From c30c45819a766c928f0767b93c532f68d99e843d Mon Sep 17 00:00:00 2001 From: kaw67872 <kawtar.laariche@iais.fraunhofer.de> Date: Fri, 13 Sep 2024 11:09:27 +0200 Subject: [PATCH] #26: add design studio js dependencies --- src/legacy/design-studio/js/chart.registry.js | 28 + .../design-studio/js/classlist-polyfill.js | 240 + src/legacy/design-studio/js/cola.js | 4891 ++++ src/legacy/design-studio/js/crossfilter.js | 3077 +++ src/legacy/design-studio/js/d3.js | 9554 ++++++++ src/legacy/design-studio/js/dagre.js | 16396 ++++++++++++++ .../design-studio/js/dc.graph.cola.worker.js | 496 + .../design-studio/js/dc.graph.dagre.worker.js | 384 + src/legacy/design-studio/js/dc.graph.js | 15617 +++++++++++++ src/legacy/design-studio/js/dc.js | 10777 +++++++++ src/legacy/design-studio/js/jquery-ui.js | 18706 ++++++++++++++++ .../design-studio/js/jquery.dataTables.js | 15307 +++++++++++++ src/legacy/design-studio/js/jquery.js | 9210 ++++++++ .../design-studio/js/jqueryui-editable.js | 5065 +++++ src/legacy/design-studio/js/lodash.js | 17084 ++++++++++++++ .../design-studio/js/promise-polyfill.js | 286 + src/legacy/design-studio/js/querystring.js | 187 + src/legacy/design-studio/js/queue.js | 106 + src/legacy/design-studio/js/tether.js | 1811 ++ src/legacy/design-studio/js/yoga-layout.js | 10202 +++++++++ 20 files changed, 139424 insertions(+) create mode 100644 src/legacy/design-studio/js/chart.registry.js create mode 100644 src/legacy/design-studio/js/classlist-polyfill.js create mode 100644 src/legacy/design-studio/js/cola.js create mode 100644 src/legacy/design-studio/js/crossfilter.js create mode 100644 src/legacy/design-studio/js/d3.js create mode 100644 src/legacy/design-studio/js/dagre.js create mode 100644 src/legacy/design-studio/js/dc.graph.cola.worker.js create mode 100644 src/legacy/design-studio/js/dc.graph.dagre.worker.js create mode 100644 src/legacy/design-studio/js/dc.graph.js create mode 100644 src/legacy/design-studio/js/dc.js create mode 100644 src/legacy/design-studio/js/jquery-ui.js create mode 100644 src/legacy/design-studio/js/jquery.dataTables.js create mode 100644 src/legacy/design-studio/js/jquery.js create mode 100644 src/legacy/design-studio/js/jqueryui-editable.js create mode 100644 src/legacy/design-studio/js/lodash.js create mode 100644 src/legacy/design-studio/js/promise-polyfill.js create mode 100644 src/legacy/design-studio/js/querystring.js create mode 100644 src/legacy/design-studio/js/queue.js create mode 100644 src/legacy/design-studio/js/tether.js create mode 100644 src/legacy/design-studio/js/yoga-layout.js diff --git a/src/legacy/design-studio/js/chart.registry.js b/src/legacy/design-studio/js/chart.registry.js new file mode 100644 index 0000000..4fcea58 --- /dev/null +++ b/src/legacy/design-studio/js/chart.registry.js @@ -0,0 +1,28 @@ +/* + This is an abstraction of dc.chartRegistry using d3.dispatch. + + https://github.com/dc-js/dc.js/blob/develop/web/docs/api-latest.md#dc.chartRegistry + + Will eventually be cleaned up and modularized, and dc.js will use it (while preserving its legacy interface). +*/ +(function() { + var chart_registry = window.chart_registry || {}; + window.chart_registry = chart_registry; + + var types = {}; + + chart_registry.create_type = function(type, constructor) { + if(!types[type]) + types[type] = {constructor: constructor, groups: {}}; + + return types[type]; + }; + + chart_registry.create_group = function(type, groupname) { + if(!types[type]) + throw new Error('chart registry type "' + type + '" not known'); + if(!types[type][groupname]) + types[type][groupname] = types[type].constructor(); + return types[type][groupname]; + }; +})(); diff --git a/src/legacy/design-studio/js/classlist-polyfill.js b/src/legacy/design-studio/js/classlist-polyfill.js new file mode 100644 index 0000000..4d608fb --- /dev/null +++ b/src/legacy/design-studio/js/classlist-polyfill.js @@ -0,0 +1,240 @@ +/* + * classList.js: Cross-browser full element.classList implementation. + * 1.1.20170427 + * + * By Eli Grey, http://eligrey.com + * License: Dedicated to the public domain. + * See https://github.com/eligrey/classList.js/blob/master/LICENSE.md + */ + +/*global self, document, DOMException */ + +/*! @source http://purl.eligrey.com/github/classList.js/blob/master/classList.js */ + +if ("document" in window.self) { + +// Full polyfill for browsers with no classList support +// Including IE < Edge missing SVGElement.classList +if (!("classList" in document.createElement("_")) + || document.createElementNS && !("classList" in document.createElementNS("http://www.w3.org/2000/svg","g"))) { + +(function (view) { + +"use strict"; + +if (!('Element' in view)) return; + +var + classListProp = "classList" + , protoProp = "prototype" + , elemCtrProto = view.Element[protoProp] + , objCtr = Object + , strTrim = String[protoProp].trim || function () { + return this.replace(/^\s+|\s+$/g, ""); + } + , arrIndexOf = Array[protoProp].indexOf || function (item) { + var + i = 0 + , len = this.length + ; + for (; i < len; i++) { + if (i in this && this[i] === item) { + return i; + } + } + return -1; + } + // Vendors: please allow content code to instantiate DOMExceptions + , DOMEx = function (type, message) { + this.name = type; + this.code = DOMException[type]; + this.message = message; + } + , checkTokenAndGetIndex = function (classList, token) { + if (token === "") { + throw new DOMEx( + "SYNTAX_ERR" + , "An invalid or illegal string was specified" + ); + } + if (/\s/.test(token)) { + throw new DOMEx( + "INVALID_CHARACTER_ERR" + , "String contains an invalid character" + ); + } + return arrIndexOf.call(classList, token); + } + , ClassList = function (elem) { + var + trimmedClasses = strTrim.call(elem.getAttribute("class") || "") + , classes = trimmedClasses ? trimmedClasses.split(/\s+/) : [] + , i = 0 + , len = classes.length + ; + for (; i < len; i++) { + this.push(classes[i]); + } + this._updateClassName = function () { + elem.setAttribute("class", this.toString()); + }; + } + , classListProto = ClassList[protoProp] = [] + , classListGetter = function () { + return new ClassList(this); + } +; +// Most DOMException implementations don't allow calling DOMException's toString() +// on non-DOMExceptions. Error's toString() is sufficient here. +DOMEx[protoProp] = Error[protoProp]; +classListProto.item = function (i) { + return this[i] || null; +}; +classListProto.contains = function (token) { + token += ""; + return checkTokenAndGetIndex(this, token) !== -1; +}; +classListProto.add = function () { + var + tokens = arguments + , i = 0 + , l = tokens.length + , token + , updated = false + ; + do { + token = tokens[i] + ""; + if (checkTokenAndGetIndex(this, token) === -1) { + this.push(token); + updated = true; + } + } + while (++i < l); + + if (updated) { + this._updateClassName(); + } +}; +classListProto.remove = function () { + var + tokens = arguments + , i = 0 + , l = tokens.length + , token + , updated = false + , index + ; + do { + token = tokens[i] + ""; + index = checkTokenAndGetIndex(this, token); + while (index !== -1) { + this.splice(index, 1); + updated = true; + index = checkTokenAndGetIndex(this, token); + } + } + while (++i < l); + + if (updated) { + this._updateClassName(); + } +}; +classListProto.toggle = function (token, force) { + token += ""; + + var + result = this.contains(token) + , method = result ? + force !== true && "remove" + : + force !== false && "add" + ; + + if (method) { + this[method](token); + } + + if (force === true || force === false) { + return force; + } else { + return !result; + } +}; +classListProto.toString = function () { + return this.join(" "); +}; + +if (objCtr.defineProperty) { + var classListPropDesc = { + get: classListGetter + , enumerable: true + , configurable: true + }; + try { + objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc); + } catch (ex) { // IE 8 doesn't support enumerable:true + // adding undefined to fight this issue https://github.com/eligrey/classList.js/issues/36 + // modernie IE8-MSW7 machine has IE8 8.0.6001.18702 and is affected + if (ex.number === undefined || ex.number === -0x7FF5EC54) { + classListPropDesc.enumerable = false; + objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc); + } + } +} else if (objCtr[protoProp].__defineGetter__) { + elemCtrProto.__defineGetter__(classListProp, classListGetter); +} + +}(window.self)); + +} + +// There is full or partial native classList support, so just check if we need +// to normalize the add/remove and toggle APIs. + +(function () { + "use strict"; + + var testElement = document.createElement("_"); + + testElement.classList.add("c1", "c2"); + + // Polyfill for IE 10/11 and Firefox <26, where classList.add and + // classList.remove exist but support only one argument at a time. + if (!testElement.classList.contains("c2")) { + var createMethod = function(method) { + var original = DOMTokenList.prototype[method]; + + DOMTokenList.prototype[method] = function(token) { + var i, len = arguments.length; + + for (i = 0; i < len; i++) { + token = arguments[i]; + original.call(this, token); + } + }; + }; + createMethod('add'); + createMethod('remove'); + } + + testElement.classList.toggle("c3", false); + + // Polyfill for IE 10 and Firefox <24, where classList.toggle does not + // support the second argument. + if (testElement.classList.contains("c3")) { + var _toggle = DOMTokenList.prototype.toggle; + + DOMTokenList.prototype.toggle = function(token, force) { + if (1 in arguments && !this.contains(token) === !force) { + return force; + } else { + return _toggle.call(this, token); + } + }; + + } + + testElement = null; +}()); + +} \ No newline at end of file diff --git a/src/legacy/design-studio/js/cola.js b/src/legacy/design-studio/js/cola.js new file mode 100644 index 0000000..f05d881 --- /dev/null +++ b/src/legacy/design-studio/js/cola.js @@ -0,0 +1,4891 @@ +var cola; +(function (cola) { + var packingOptions = { + PADDING: 10, + GOLDEN_SECTION: (1 + Math.sqrt(5)) / 2, + FLOAT_EPSILON: 0.0001, + MAX_INERATIONS: 100 + }; + // assign x, y to nodes while using box packing algorithm for disconnected graphs + function applyPacking(graphs, w, h, node_size, desired_ratio) { + if (desired_ratio === void 0) { desired_ratio = 1; } + var init_x = 0, init_y = 0, svg_width = w, svg_height = h, desired_ratio = typeof desired_ratio !== 'undefined' ? desired_ratio : 1, node_size = typeof node_size !== 'undefined' ? node_size : 0, real_width = 0, real_height = 0, min_width = 0, global_bottom = 0, line = []; + if (graphs.length == 0) + return; + /// that would take care of single nodes problem + // graphs.forEach(function (g) { + // if (g.array.length == 1) { + // g.array[0].x = 0; + // g.array[0].y = 0; + // } + // }); + calculate_bb(graphs); + apply(graphs, desired_ratio); + put_nodes_to_right_positions(graphs); + // get bounding boxes for all separate graphs + function calculate_bb(graphs) { + graphs.forEach(function (g) { + calculate_single_bb(g); + }); + function calculate_single_bb(graph) { + var min_x = Number.MAX_VALUE, min_y = Number.MAX_VALUE, max_x = 0, max_y = 0; + graph.array.forEach(function (v) { + var w = typeof v.width !== 'undefined' ? v.width : node_size; + var h = typeof v.height !== 'undefined' ? v.height : node_size; + w /= 2; + h /= 2; + max_x = Math.max(v.x + w, max_x); + min_x = Math.min(v.x - w, min_x); + max_y = Math.max(v.y + h, max_y); + min_y = Math.min(v.y - h, min_y); + }); + graph.width = max_x - min_x; + graph.height = max_y - min_y; + } + } + //function plot(data, left, right, opt_x, opt_y) { + // // plot the cost function + // var plot_svg = d3.select("body").append("svg") + // .attr("width", function () { return 2 * (right - left); }) + // .attr("height", 200); + // var x = d3.time.scale().range([0, 2 * (right - left)]); + // var xAxis = d3.svg.axis().scale(x).orient("bottom"); + // plot_svg.append("g").attr("class", "x axis") + // .attr("transform", "translate(0, 199)") + // .call(xAxis); + // var lastX = 0; + // var lastY = 0; + // var value = 0; + // for (var r = left; r < right; r += 1) { + // value = step(data, r); + // // value = 1; + // plot_svg.append("line").attr("x1", 2 * (lastX - left)) + // .attr("y1", 200 - 30 * lastY) + // .attr("x2", 2 * r - 2 * left) + // .attr("y2", 200 - 30 * value) + // .style("stroke", "rgb(6,120,155)"); + // lastX = r; + // lastY = value; + // } + // plot_svg.append("circle").attr("cx", 2 * opt_x - 2 * left).attr("cy", 200 - 30 * opt_y) + // .attr("r", 5).style('fill', "rgba(0,0,0,0.5)"); + //} + // actual assigning of position to nodes + function put_nodes_to_right_positions(graphs) { + graphs.forEach(function (g) { + // calculate current graph center: + var center = { x: 0, y: 0 }; + g.array.forEach(function (node) { + center.x += node.x; + center.y += node.y; + }); + center.x /= g.array.length; + center.y /= g.array.length; + // calculate current top left corner: + var corner = { x: center.x - g.width / 2, y: center.y - g.height / 2 }; + var offset = { x: g.x - corner.x + svg_width / 2 - real_width / 2, y: g.y - corner.y + svg_height / 2 - real_height / 2 }; + // put nodes: + g.array.forEach(function (node) { + node.x += offset.x; + node.y += offset.y; + }); + }); + } + // starts box packing algorithm + // desired ratio is 1 by default + function apply(data, desired_ratio) { + var curr_best_f = Number.POSITIVE_INFINITY; + var curr_best = 0; + data.sort(function (a, b) { return b.height - a.height; }); + min_width = data.reduce(function (a, b) { + return a.width < b.width ? a.width : b.width; + }); + var left = x1 = min_width; + var right = x2 = get_entire_width(data); + var iterationCounter = 0; + var f_x1 = Number.MAX_VALUE; + var f_x2 = Number.MAX_VALUE; + var flag = -1; // determines which among f_x1 and f_x2 to recompute + var dx = Number.MAX_VALUE; + var df = Number.MAX_VALUE; + while ((dx > min_width) || df > packingOptions.FLOAT_EPSILON) { + if (flag != 1) { + var x1 = right - (right - left) / packingOptions.GOLDEN_SECTION; + var f_x1 = step(data, x1); + } + if (flag != 0) { + var x2 = left + (right - left) / packingOptions.GOLDEN_SECTION; + var f_x2 = step(data, x2); + } + dx = Math.abs(x1 - x2); + df = Math.abs(f_x1 - f_x2); + if (f_x1 < curr_best_f) { + curr_best_f = f_x1; + curr_best = x1; + } + if (f_x2 < curr_best_f) { + curr_best_f = f_x2; + curr_best = x2; + } + if (f_x1 > f_x2) { + left = x1; + x1 = x2; + f_x1 = f_x2; + flag = 1; + } + else { + right = x2; + x2 = x1; + f_x2 = f_x1; + flag = 0; + } + if (iterationCounter++ > 100) { + break; + } + } + // plot(data, min_width, get_entire_width(data), curr_best, curr_best_f); + step(data, curr_best); + } + // one iteration of the optimization method + // (gives a proper, but not necessarily optimal packing) + function step(data, max_width) { + line = []; + real_width = 0; + real_height = 0; + global_bottom = init_y; + for (var i = 0; i < data.length; i++) { + var o = data[i]; + put_rect(o, max_width); + } + return Math.abs(get_real_ratio() - desired_ratio); + } + // looking for a position to one box + function put_rect(rect, max_width) { + var parent = undefined; + for (var i = 0; i < line.length; i++) { + if ((line[i].space_left >= rect.height) && (line[i].x + line[i].width + rect.width + packingOptions.PADDING - max_width) <= packingOptions.FLOAT_EPSILON) { + parent = line[i]; + break; + } + } + line.push(rect); + if (parent !== undefined) { + rect.x = parent.x + parent.width + packingOptions.PADDING; + rect.y = parent.bottom; + rect.space_left = rect.height; + rect.bottom = rect.y; + parent.space_left -= rect.height + packingOptions.PADDING; + parent.bottom += rect.height + packingOptions.PADDING; + } + else { + rect.y = global_bottom; + global_bottom += rect.height + packingOptions.PADDING; + rect.x = init_x; + rect.bottom = rect.y; + rect.space_left = rect.height; + } + if (rect.y + rect.height - real_height > -packingOptions.FLOAT_EPSILON) + real_height = rect.y + rect.height - init_y; + if (rect.x + rect.width - real_width > -packingOptions.FLOAT_EPSILON) + real_width = rect.x + rect.width - init_x; + } + ; + function get_entire_width(data) { + var width = 0; + data.forEach(function (d) { return width += d.width + packingOptions.PADDING; }); + return width; + } + function get_real_ratio() { + return (real_width / real_height); + } + } + cola.applyPacking = applyPacking; + /** + * connected components of graph + * returns an array of {} + */ + function separateGraphs(nodes, links) { + var marks = {}; + var ways = {}; + var graphs = []; + var clusters = 0; + for (var i = 0; i < links.length; i++) { + var link = links[i]; + var n1 = link.source; + var n2 = link.target; + if (ways[n1.index]) + ways[n1.index].push(n2); + else + ways[n1.index] = [n2]; + if (ways[n2.index]) + ways[n2.index].push(n1); + else + ways[n2.index] = [n1]; + } + for (var i = 0; i < nodes.length; i++) { + var node = nodes[i]; + if (marks[node.index]) + continue; + explore_node(node, true); + } + function explore_node(n, is_new) { + if (marks[n.index] !== undefined) + return; + if (is_new) { + clusters++; + graphs.push({ array: [] }); + } + marks[n.index] = clusters; + graphs[clusters - 1].array.push(n); + var adjacent = ways[n.index]; + if (!adjacent) + return; + for (var j = 0; j < adjacent.length; j++) { + explore_node(adjacent[j], false); + } + } + return graphs; + } + cola.separateGraphs = separateGraphs; +})(cola || (cola = {})); +var cola; +(function (cola) { + var vpsc; + (function (vpsc) { + var PositionStats = (function () { + function PositionStats(scale) { + this.scale = scale; + this.AB = 0; + this.AD = 0; + this.A2 = 0; + } + PositionStats.prototype.addVariable = function (v) { + var ai = this.scale / v.scale; + var bi = v.offset / v.scale; + var wi = v.weight; + this.AB += wi * ai * bi; + this.AD += wi * ai * v.desiredPosition; + this.A2 += wi * ai * ai; + }; + PositionStats.prototype.getPosn = function () { + return (this.AD - this.AB) / this.A2; + }; + return PositionStats; + })(); + vpsc.PositionStats = PositionStats; + var Constraint = (function () { + function Constraint(left, right, gap, equality) { + if (equality === void 0) { equality = false; } + this.left = left; + this.right = right; + this.gap = gap; + this.equality = equality; + this.active = false; + this.unsatisfiable = false; + this.left = left; + this.right = right; + this.gap = gap; + this.equality = equality; + } + Constraint.prototype.slack = function () { + return this.unsatisfiable ? Number.MAX_VALUE + : this.right.scale * this.right.position() - this.gap + - this.left.scale * this.left.position(); + }; + return Constraint; + })(); + vpsc.Constraint = Constraint; + var Variable = (function () { + function Variable(desiredPosition, weight, scale) { + if (weight === void 0) { weight = 1; } + if (scale === void 0) { scale = 1; } + this.desiredPosition = desiredPosition; + this.weight = weight; + this.scale = scale; + this.offset = 0; + } + Variable.prototype.dfdv = function () { + return 2.0 * this.weight * (this.position() - this.desiredPosition); + }; + Variable.prototype.position = function () { + return (this.block.ps.scale * this.block.posn + this.offset) / this.scale; + }; + // visit neighbours by active constraints within the same block + Variable.prototype.visitNeighbours = function (prev, f) { + var ff = function (c, next) { return c.active && prev !== next && f(c, next); }; + this.cOut.forEach(function (c) { return ff(c, c.right); }); + this.cIn.forEach(function (c) { return ff(c, c.left); }); + }; + return Variable; + })(); + vpsc.Variable = Variable; + var Block = (function () { + function Block(v) { + this.vars = []; + v.offset = 0; + this.ps = new PositionStats(v.scale); + this.addVariable(v); + } + Block.prototype.addVariable = function (v) { + v.block = this; + this.vars.push(v); + this.ps.addVariable(v); + this.posn = this.ps.getPosn(); + }; + // move the block where it needs to be to minimize cost + Block.prototype.updateWeightedPosition = function () { + this.ps.AB = this.ps.AD = this.ps.A2 = 0; + for (var i = 0, n = this.vars.length; i < n; ++i) + this.ps.addVariable(this.vars[i]); + this.posn = this.ps.getPosn(); + }; + Block.prototype.compute_lm = function (v, u, postAction) { + var _this = this; + var dfdv = v.dfdv(); + v.visitNeighbours(u, function (c, next) { + var _dfdv = _this.compute_lm(next, v, postAction); + if (next === c.right) { + dfdv += _dfdv * c.left.scale; + c.lm = _dfdv; + } + else { + dfdv += _dfdv * c.right.scale; + c.lm = -_dfdv; + } + postAction(c); + }); + return dfdv / v.scale; + }; + Block.prototype.populateSplitBlock = function (v, prev) { + var _this = this; + v.visitNeighbours(prev, function (c, next) { + next.offset = v.offset + (next === c.right ? c.gap : -c.gap); + _this.addVariable(next); + _this.populateSplitBlock(next, v); + }); + }; + // traverse the active constraint tree applying visit to each active constraint + Block.prototype.traverse = function (visit, acc, v, prev) { + var _this = this; + if (v === void 0) { v = this.vars[0]; } + if (prev === void 0) { prev = null; } + v.visitNeighbours(prev, function (c, next) { + acc.push(visit(c)); + _this.traverse(visit, acc, next, v); + }); + }; + // calculate lagrangian multipliers on constraints and + // find the active constraint in this block with the smallest lagrangian. + // if the lagrangian is negative, then the constraint is a split candidate. + Block.prototype.findMinLM = function () { + var m = null; + this.compute_lm(this.vars[0], null, function (c) { + if (!c.equality && (m === null || c.lm < m.lm)) + m = c; + }); + return m; + }; + Block.prototype.findMinLMBetween = function (lv, rv) { + this.compute_lm(lv, null, function () { }); + var m = null; + this.findPath(lv, null, rv, function (c, next) { + if (!c.equality && c.right === next && (m === null || c.lm < m.lm)) + m = c; + }); + return m; + }; + Block.prototype.findPath = function (v, prev, to, visit) { + var _this = this; + var endFound = false; + v.visitNeighbours(prev, function (c, next) { + if (!endFound && (next === to || _this.findPath(next, v, to, visit))) { + endFound = true; + visit(c, next); + } + }); + return endFound; + }; + // Search active constraint tree from u to see if there is a directed path to v. + // Returns true if path is found. + Block.prototype.isActiveDirectedPathBetween = function (u, v) { + if (u === v) + return true; + var i = u.cOut.length; + while (i--) { + var c = u.cOut[i]; + if (c.active && this.isActiveDirectedPathBetween(c.right, v)) + return true; + } + return false; + }; + // split the block into two by deactivating the specified constraint + Block.split = function (c) { + /* DEBUG + console.log("split on " + c); + console.assert(c.active, "attempt to split on inactive constraint"); + DEBUG */ + c.active = false; + return [Block.createSplitBlock(c.left), Block.createSplitBlock(c.right)]; + }; + Block.createSplitBlock = function (startVar) { + var b = new Block(startVar); + b.populateSplitBlock(startVar, null); + return b; + }; + // find a split point somewhere between the specified variables + Block.prototype.splitBetween = function (vl, vr) { + /* DEBUG + console.assert(vl.block === this); + console.assert(vr.block === this); + DEBUG */ + var c = this.findMinLMBetween(vl, vr); + if (c !== null) { + var bs = Block.split(c); + return { constraint: c, lb: bs[0], rb: bs[1] }; + } + // couldn't find a split point - for example the active path is all equality constraints + return null; + }; + Block.prototype.mergeAcross = function (b, c, dist) { + c.active = true; + for (var i = 0, n = b.vars.length; i < n; ++i) { + var v = b.vars[i]; + v.offset += dist; + this.addVariable(v); + } + this.posn = this.ps.getPosn(); + }; + Block.prototype.cost = function () { + var sum = 0, i = this.vars.length; + while (i--) { + var v = this.vars[i], d = v.position() - v.desiredPosition; + sum += d * d * v.weight; + } + return sum; + }; + return Block; + })(); + vpsc.Block = Block; + var Blocks = (function () { + function Blocks(vs) { + this.vs = vs; + var n = vs.length; + this.list = new Array(n); + while (n--) { + var b = new Block(vs[n]); + this.list[n] = b; + b.blockInd = n; + } + } + Blocks.prototype.cost = function () { + var sum = 0, i = this.list.length; + while (i--) + sum += this.list[i].cost(); + return sum; + }; + Blocks.prototype.insert = function (b) { + /* DEBUG + console.assert(!this.contains(b), "blocks error: tried to reinsert block " + b.blockInd) + DEBUG */ + b.blockInd = this.list.length; + this.list.push(b); + /* DEBUG + console.log("insert block: " + b.blockInd); + this.contains(b); + DEBUG */ + }; + Blocks.prototype.remove = function (b) { + /* DEBUG + console.log("remove block: " + b.blockInd); + console.assert(this.contains(b)); + DEBUG */ + var last = this.list.length - 1; + var swapBlock = this.list[last]; + this.list.length = last; + if (b !== swapBlock) { + this.list[b.blockInd] = swapBlock; + swapBlock.blockInd = b.blockInd; + } + }; + // merge the blocks on either side of the specified constraint, by copying the smaller block into the larger + // and deleting the smaller. + Blocks.prototype.merge = function (c) { + var l = c.left.block, r = c.right.block; + /* DEBUG + console.assert(l!==r, "attempt to merge within the same block"); + DEBUG */ + var dist = c.right.offset - c.left.offset - c.gap; + if (l.vars.length < r.vars.length) { + r.mergeAcross(l, c, dist); + this.remove(l); + } + else { + l.mergeAcross(r, c, -dist); + this.remove(r); + } + /* DEBUG + console.assert(Math.abs(c.slack()) < 1e-6, "Error: Constraint should be at equality after merge!"); + console.log("merged on " + c); + DEBUG */ + }; + Blocks.prototype.forEach = function (f) { + this.list.forEach(f); + }; + // useful, for example, after variable desired positions change. + Blocks.prototype.updateBlockPositions = function () { + this.list.forEach(function (b) { return b.updateWeightedPosition(); }); + }; + // split each block across its constraint with the minimum lagrangian + Blocks.prototype.split = function (inactive) { + var _this = this; + this.updateBlockPositions(); + this.list.forEach(function (b) { + var v = b.findMinLM(); + if (v !== null && v.lm < Solver.LAGRANGIAN_TOLERANCE) { + b = v.left.block; + Block.split(v).forEach(function (nb) { return _this.insert(nb); }); + _this.remove(b); + inactive.push(v); + } + }); + }; + return Blocks; + })(); + vpsc.Blocks = Blocks; + var Solver = (function () { + function Solver(vs, cs) { + this.vs = vs; + this.cs = cs; + this.vs = vs; + vs.forEach(function (v) { + v.cIn = [], v.cOut = []; + /* DEBUG + v.toString = () => "v" + vs.indexOf(v); + DEBUG */ + }); + this.cs = cs; + cs.forEach(function (c) { + c.left.cOut.push(c); + c.right.cIn.push(c); + /* DEBUG + c.toString = () => c.left + "+" + c.gap + "<=" + c.right + " slack=" + c.slack() + " active=" + c.active; + DEBUG */ + }); + this.inactive = cs.map(function (c) { c.active = false; return c; }); + this.bs = null; + } + Solver.prototype.cost = function () { + return this.bs.cost(); + }; + // set starting positions without changing desired positions. + // Note: it throws away any previous block structure. + Solver.prototype.setStartingPositions = function (ps) { + this.inactive = this.cs.map(function (c) { c.active = false; return c; }); + this.bs = new Blocks(this.vs); + this.bs.forEach(function (b, i) { return b.posn = ps[i]; }); + }; + Solver.prototype.setDesiredPositions = function (ps) { + this.vs.forEach(function (v, i) { return v.desiredPosition = ps[i]; }); + }; + /* DEBUG + private getId(v: Variable): number { + return this.vs.indexOf(v); + } + + // sanity check of the index integrity of the inactive list + checkInactive(): void { + var inactiveCount = 0; + this.cs.forEach(c=> { + var i = this.inactive.indexOf(c); + console.assert(!c.active && i >= 0 || c.active && i < 0, "constraint should be in the inactive list if it is not active: " + c); + if (i >= 0) { + inactiveCount++; + } else { + console.assert(c.active, "inactive constraint not found in inactive list: " + c); + } + }); + console.assert(inactiveCount === this.inactive.length, inactiveCount + " inactive constraints found, " + this.inactive.length + "in inactive list"); + } + // after every call to satisfy the following should check should pass + checkSatisfied(): void { + this.cs.forEach(c=>console.assert(c.slack() >= vpsc.Solver.ZERO_UPPERBOUND, "Error: Unsatisfied constraint! "+c)); + } + DEBUG */ + Solver.prototype.mostViolated = function () { + var minSlack = Number.MAX_VALUE, v = null, l = this.inactive, n = l.length, deletePoint = n; + for (var i = 0; i < n; ++i) { + var c = l[i]; + if (c.unsatisfiable) + continue; + var slack = c.slack(); + if (c.equality || slack < minSlack) { + minSlack = slack; + v = c; + deletePoint = i; + if (c.equality) + break; + } + } + if (deletePoint !== n && + (minSlack < Solver.ZERO_UPPERBOUND && !v.active || v.equality)) { + l[deletePoint] = l[n - 1]; + l.length = n - 1; + } + return v; + }; + // satisfy constraints by building block structure over violated constraints + // and moving the blocks to their desired positions + Solver.prototype.satisfy = function () { + if (this.bs == null) { + this.bs = new Blocks(this.vs); + } + /* DEBUG + console.log("satisfy: " + this.bs); + DEBUG */ + this.bs.split(this.inactive); + var v = null; + while ((v = this.mostViolated()) && (v.equality || v.slack() < Solver.ZERO_UPPERBOUND && !v.active)) { + var lb = v.left.block, rb = v.right.block; + /* DEBUG + console.log("most violated is: " + v); + this.bs.contains(lb); + this.bs.contains(rb); + DEBUG */ + if (lb !== rb) { + this.bs.merge(v); + } + else { + if (lb.isActiveDirectedPathBetween(v.right, v.left)) { + // cycle found! + v.unsatisfiable = true; + continue; + } + // constraint is within block, need to split first + var split = lb.splitBetween(v.left, v.right); + if (split !== null) { + this.bs.insert(split.lb); + this.bs.insert(split.rb); + this.bs.remove(lb); + this.inactive.push(split.constraint); + } + else { + /* DEBUG + console.log("unsatisfiable constraint found"); + DEBUG */ + v.unsatisfiable = true; + continue; + } + if (v.slack() >= 0) { + /* DEBUG + console.log("violated constraint indirectly satisfied: " + v); + DEBUG */ + // v was satisfied by the above split! + this.inactive.push(v); + } + else { + /* DEBUG + console.log("merge after split:"); + DEBUG */ + this.bs.merge(v); + } + } + } + /* DEBUG + this.checkSatisfied(); + DEBUG */ + }; + // repeatedly build and split block structure until we converge to an optimal solution + Solver.prototype.solve = function () { + this.satisfy(); + var lastcost = Number.MAX_VALUE, cost = this.bs.cost(); + while (Math.abs(lastcost - cost) > 0.0001) { + this.satisfy(); + lastcost = cost; + cost = this.bs.cost(); + } + return cost; + }; + Solver.LAGRANGIAN_TOLERANCE = -1e-4; + Solver.ZERO_UPPERBOUND = -1e-10; + return Solver; + })(); + vpsc.Solver = Solver; + })(vpsc = cola.vpsc || (cola.vpsc = {})); +})(cola || (cola = {})); +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + __.prototype = b.prototype; + d.prototype = new __(); +}; +var cola; +(function (cola) { + var vpsc; + (function (vpsc) { + //Based on js_es: + // + //https://github.com/vadimg/js_bintrees + // + //Copyright (C) 2011 by Vadim Graboys + // + //Permission is hereby granted, free of charge, to any person obtaining a copy + //of this software and associated documentation files (the "Software"), to deal + //in the Software without restriction, including without limitation the rights + //to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + //copies of the Software, and to permit persons to whom the Software is + //furnished to do so, subject to the following conditions: + // + //The above copyright notice and this permission notice shall be included in + //all copies or substantial portions of the Software. + // + //THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + //IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + //FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + //AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + //LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + //OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + //THE SOFTWARE. + var TreeBase = (function () { + function TreeBase() { + // returns iterator to node if found, null otherwise + this.findIter = function (data) { + var res = this._root; + var iter = this.iterator(); + while (res !== null) { + var c = this._comparator(data, res.data); + if (c === 0) { + iter._cursor = res; + return iter; + } + else { + iter._ancestors.push(res); + res = res.get_child(c > 0); + } + } + return null; + }; + } + // removes all nodes from the tree + TreeBase.prototype.clear = function () { + this._root = null; + this.size = 0; + }; + ; + // returns node data if found, null otherwise + TreeBase.prototype.find = function (data) { + var res = this._root; + while (res !== null) { + var c = this._comparator(data, res.data); + if (c === 0) { + return res.data; + } + else { + res = res.get_child(c > 0); + } + } + return null; + }; + ; + // Returns an interator to the tree node immediately before (or at) the element + TreeBase.prototype.lowerBound = function (data) { + return this._bound(data, this._comparator); + }; + ; + // Returns an interator to the tree node immediately after (or at) the element + TreeBase.prototype.upperBound = function (data) { + var cmp = this._comparator; + function reverse_cmp(a, b) { + return cmp(b, a); + } + return this._bound(data, reverse_cmp); + }; + ; + // returns null if tree is empty + TreeBase.prototype.min = function () { + var res = this._root; + if (res === null) { + return null; + } + while (res.left !== null) { + res = res.left; + } + return res.data; + }; + ; + // returns null if tree is empty + TreeBase.prototype.max = function () { + var res = this._root; + if (res === null) { + return null; + } + while (res.right !== null) { + res = res.right; + } + return res.data; + }; + ; + // returns a null iterator + // call next() or prev() to point to an element + TreeBase.prototype.iterator = function () { + return new Iterator(this); + }; + ; + // calls cb on each node's data, in order + TreeBase.prototype.each = function (cb) { + var it = this.iterator(), data; + while ((data = it.next()) !== null) { + cb(data); + } + }; + ; + // calls cb on each node's data, in reverse order + TreeBase.prototype.reach = function (cb) { + var it = this.iterator(), data; + while ((data = it.prev()) !== null) { + cb(data); + } + }; + ; + // used for lowerBound and upperBound + TreeBase.prototype._bound = function (data, cmp) { + var cur = this._root; + var iter = this.iterator(); + while (cur !== null) { + var c = this._comparator(data, cur.data); + if (c === 0) { + iter._cursor = cur; + return iter; + } + iter._ancestors.push(cur); + cur = cur.get_child(c > 0); + } + for (var i = iter._ancestors.length - 1; i >= 0; --i) { + cur = iter._ancestors[i]; + if (cmp(data, cur.data) > 0) { + iter._cursor = cur; + iter._ancestors.length = i; + return iter; + } + } + iter._ancestors.length = 0; + return iter; + }; + ; + return TreeBase; + })(); + vpsc.TreeBase = TreeBase; + var Iterator = (function () { + function Iterator(tree) { + this._tree = tree; + this._ancestors = []; + this._cursor = null; + } + Iterator.prototype.data = function () { + return this._cursor !== null ? this._cursor.data : null; + }; + ; + // if null-iterator, returns first node + // otherwise, returns next node + Iterator.prototype.next = function () { + if (this._cursor === null) { + var root = this._tree._root; + if (root !== null) { + this._minNode(root); + } + } + else { + if (this._cursor.right === null) { + // no greater node in subtree, go up to parent + // if coming from a right child, continue up the stack + var save; + do { + save = this._cursor; + if (this._ancestors.length) { + this._cursor = this._ancestors.pop(); + } + else { + this._cursor = null; + break; + } + } while (this._cursor.right === save); + } + else { + // get the next node from the subtree + this._ancestors.push(this._cursor); + this._minNode(this._cursor.right); + } + } + return this._cursor !== null ? this._cursor.data : null; + }; + ; + // if null-iterator, returns last node + // otherwise, returns previous node + Iterator.prototype.prev = function () { + if (this._cursor === null) { + var root = this._tree._root; + if (root !== null) { + this._maxNode(root); + } + } + else { + if (this._cursor.left === null) { + var save; + do { + save = this._cursor; + if (this._ancestors.length) { + this._cursor = this._ancestors.pop(); + } + else { + this._cursor = null; + break; + } + } while (this._cursor.left === save); + } + else { + this._ancestors.push(this._cursor); + this._maxNode(this._cursor.left); + } + } + return this._cursor !== null ? this._cursor.data : null; + }; + ; + Iterator.prototype._minNode = function (start) { + while (start.left !== null) { + this._ancestors.push(start); + start = start.left; + } + this._cursor = start; + }; + ; + Iterator.prototype._maxNode = function (start) { + while (start.right !== null) { + this._ancestors.push(start); + start = start.right; + } + this._cursor = start; + }; + ; + return Iterator; + })(); + vpsc.Iterator = Iterator; + var Node = (function () { + function Node(data) { + this.data = data; + this.left = null; + this.right = null; + this.red = true; + } + Node.prototype.get_child = function (dir) { + return dir ? this.right : this.left; + }; + ; + Node.prototype.set_child = function (dir, val) { + if (dir) { + this.right = val; + } + else { + this.left = val; + } + }; + ; + return Node; + })(); + var RBTree = (function (_super) { + __extends(RBTree, _super); + function RBTree(comparator) { + _super.call(this); + this._root = null; + this._comparator = comparator; + this.size = 0; + } + // returns true if inserted, false if duplicate + RBTree.prototype.insert = function (data) { + var ret = false; + if (this._root === null) { + // empty tree + this._root = new Node(data); + ret = true; + this.size++; + } + else { + var head = new Node(undefined); // fake tree root + var dir = false; + var last = false; + // setup + var gp = null; // grandparent + var ggp = head; // grand-grand-parent + var p = null; // parent + var node = this._root; + ggp.right = this._root; + // search down + while (true) { + if (node === null) { + // insert new node at the bottom + node = new Node(data); + p.set_child(dir, node); + ret = true; + this.size++; + } + else if (RBTree.is_red(node.left) && RBTree.is_red(node.right)) { + // color flip + node.red = true; + node.left.red = false; + node.right.red = false; + } + // fix red violation + if (RBTree.is_red(node) && RBTree.is_red(p)) { + var dir2 = ggp.right === gp; + if (node === p.get_child(last)) { + ggp.set_child(dir2, RBTree.single_rotate(gp, !last)); + } + else { + ggp.set_child(dir2, RBTree.double_rotate(gp, !last)); + } + } + var cmp = this._comparator(node.data, data); + // stop if found + if (cmp === 0) { + break; + } + last = dir; + dir = cmp < 0; + // update helpers + if (gp !== null) { + ggp = gp; + } + gp = p; + p = node; + node = node.get_child(dir); + } + // update root + this._root = head.right; + } + // make root black + this._root.red = false; + return ret; + }; + ; + // returns true if removed, false if not found + RBTree.prototype.remove = function (data) { + if (this._root === null) { + return false; + } + var head = new Node(undefined); // fake tree root + var node = head; + node.right = this._root; + var p = null; // parent + var gp = null; // grand parent + var found = null; // found item + var dir = true; + while (node.get_child(dir) !== null) { + var last = dir; + // update helpers + gp = p; + p = node; + node = node.get_child(dir); + var cmp = this._comparator(data, node.data); + dir = cmp > 0; + // save found node + if (cmp === 0) { + found = node; + } + // push the red node down + if (!RBTree.is_red(node) && !RBTree.is_red(node.get_child(dir))) { + if (RBTree.is_red(node.get_child(!dir))) { + var sr = RBTree.single_rotate(node, dir); + p.set_child(last, sr); + p = sr; + } + else if (!RBTree.is_red(node.get_child(!dir))) { + var sibling = p.get_child(!last); + if (sibling !== null) { + if (!RBTree.is_red(sibling.get_child(!last)) && !RBTree.is_red(sibling.get_child(last))) { + // color flip + p.red = false; + sibling.red = true; + node.red = true; + } + else { + var dir2 = gp.right === p; + if (RBTree.is_red(sibling.get_child(last))) { + gp.set_child(dir2, RBTree.double_rotate(p, last)); + } + else if (RBTree.is_red(sibling.get_child(!last))) { + gp.set_child(dir2, RBTree.single_rotate(p, last)); + } + // ensure correct coloring + var gpc = gp.get_child(dir2); + gpc.red = true; + node.red = true; + gpc.left.red = false; + gpc.right.red = false; + } + } + } + } + } + // replace and remove if found + if (found !== null) { + found.data = node.data; + p.set_child(p.right === node, node.get_child(node.left === null)); + this.size--; + } + // update root and make it black + this._root = head.right; + if (this._root !== null) { + this._root.red = false; + } + return found !== null; + }; + ; + RBTree.is_red = function (node) { + return node !== null && node.red; + }; + RBTree.single_rotate = function (root, dir) { + var save = root.get_child(!dir); + root.set_child(!dir, save.get_child(dir)); + save.set_child(dir, root); + root.red = true; + save.red = false; + return save; + }; + RBTree.double_rotate = function (root, dir) { + root.set_child(!dir, RBTree.single_rotate(root.get_child(!dir), !dir)); + return RBTree.single_rotate(root, dir); + }; + return RBTree; + })(TreeBase); + vpsc.RBTree = RBTree; + })(vpsc = cola.vpsc || (cola.vpsc = {})); +})(cola || (cola = {})); +///<reference path="vpsc.ts"/> +///<reference path="rbtree.ts"/> +var cola; +(function (cola) { + var vpsc; + (function (vpsc) { + function computeGroupBounds(g) { + g.bounds = typeof g.leaves !== "undefined" ? + g.leaves.reduce(function (r, c) { return c.bounds.union(r); }, Rectangle.empty()) : + Rectangle.empty(); + if (typeof g.groups !== "undefined") + g.bounds = g.groups.reduce(function (r, c) { return computeGroupBounds(c).union(r); }, g.bounds); + g.bounds = g.bounds.inflate(g.padding); + return g.bounds; + } + vpsc.computeGroupBounds = computeGroupBounds; + var Rectangle = (function () { + function Rectangle(x, X, y, Y) { + this.x = x; + this.X = X; + this.y = y; + this.Y = Y; + } + Rectangle.empty = function () { return new Rectangle(Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY, Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY); }; + Rectangle.prototype.cx = function () { return (this.x + this.X) / 2; }; + Rectangle.prototype.cy = function () { return (this.y + this.Y) / 2; }; + Rectangle.prototype.overlapX = function (r) { + var ux = this.cx(), vx = r.cx(); + if (ux <= vx && r.x < this.X) + return this.X - r.x; + if (vx <= ux && this.x < r.X) + return r.X - this.x; + return 0; + }; + Rectangle.prototype.overlapY = function (r) { + var uy = this.cy(), vy = r.cy(); + if (uy <= vy && r.y < this.Y) + return this.Y - r.y; + if (vy <= uy && this.y < r.Y) + return r.Y - this.y; + return 0; + }; + Rectangle.prototype.setXCentre = function (cx) { + var dx = cx - this.cx(); + this.x += dx; + this.X += dx; + }; + Rectangle.prototype.setYCentre = function (cy) { + var dy = cy - this.cy(); + this.y += dy; + this.Y += dy; + }; + Rectangle.prototype.width = function () { + return this.X - this.x; + }; + Rectangle.prototype.height = function () { + return this.Y - this.y; + }; + Rectangle.prototype.union = function (r) { + return new Rectangle(Math.min(this.x, r.x), Math.max(this.X, r.X), Math.min(this.y, r.y), Math.max(this.Y, r.Y)); + }; + /** + * return any intersection points between the given line and the sides of this rectangle + * @method lineIntersection + * @param x1 number first x coord of line + * @param y1 number first y coord of line + * @param x2 number second x coord of line + * @param y2 number second y coord of line + * @return any intersection points found + */ + Rectangle.prototype.lineIntersections = function (x1, y1, x2, y2) { + var sides = [[this.x, this.y, this.X, this.y], + [this.X, this.y, this.X, this.Y], + [this.X, this.Y, this.x, this.Y], + [this.x, this.Y, this.x, this.y]]; + var intersections = []; + for (var i = 0; i < 4; ++i) { + var r = Rectangle.lineIntersection(x1, y1, x2, y2, sides[i][0], sides[i][1], sides[i][2], sides[i][3]); + if (r !== null) + intersections.push({ x: r.x, y: r.y }); + } + return intersections; + }; + /** + * return any intersection points between a line extending from the centre of this rectangle to the given point, + * and the sides of this rectangle + * @method lineIntersection + * @param x2 number second x coord of line + * @param y2 number second y coord of line + * @return any intersection points found + */ + Rectangle.prototype.rayIntersection = function (x2, y2) { + var ints = this.lineIntersections(this.cx(), this.cy(), x2, y2); + return ints.length > 0 ? ints[0] : null; + }; + Rectangle.prototype.vertices = function () { + return [ + { x: this.x, y: this.y }, + { x: this.X, y: this.y }, + { x: this.X, y: this.Y }, + { x: this.x, y: this.Y }, + { x: this.x, y: this.y }]; + }; + Rectangle.lineIntersection = function (x1, y1, x2, y2, x3, y3, x4, y4) { + var dx12 = x2 - x1, dx34 = x4 - x3, dy12 = y2 - y1, dy34 = y4 - y3, denominator = dy34 * dx12 - dx34 * dy12; + if (denominator == 0) + return null; + var dx31 = x1 - x3, dy31 = y1 - y3, numa = dx34 * dy31 - dy34 * dx31, a = numa / denominator, numb = dx12 * dy31 - dy12 * dx31, b = numb / denominator; + if (a >= 0 && a <= 1 && b >= 0 && b <= 1) { + return { + x: x1 + a * dx12, + y: y1 + a * dy12 + }; + } + return null; + }; + Rectangle.prototype.inflate = function (pad) { + return new Rectangle(this.x - pad, this.X + pad, this.y - pad, this.Y + pad); + }; + return Rectangle; + })(); + vpsc.Rectangle = Rectangle; + function makeEdgeBetween(source, target, ah) { + var si = source.rayIntersection(target.cx(), target.cy()) || { x: source.cx(), y: source.cy() }, ti = target.rayIntersection(source.cx(), source.cy()) || { x: target.cx(), y: target.cy() }, dx = ti.x - si.x, dy = ti.y - si.y, l = Math.sqrt(dx * dx + dy * dy), al = l - ah; + return { + sourceIntersection: si, + targetIntersection: ti, + arrowStart: { x: si.x + al * dx / l, y: si.y + al * dy / l } + }; + } + vpsc.makeEdgeBetween = makeEdgeBetween; + function makeEdgeTo(s, target, ah) { + var ti = target.rayIntersection(s.x, s.y); + if (!ti) + ti = { x: target.cx(), y: target.cy() }; + var dx = ti.x - s.x, dy = ti.y - s.y, l = Math.sqrt(dx * dx + dy * dy); + return { x: ti.x - ah * dx / l, y: ti.y - ah * dy / l }; + } + vpsc.makeEdgeTo = makeEdgeTo; + var Node = (function () { + function Node(v, r, pos) { + this.v = v; + this.r = r; + this.pos = pos; + this.prev = makeRBTree(); + this.next = makeRBTree(); + } + return Node; + })(); + var Event = (function () { + function Event(isOpen, v, pos) { + this.isOpen = isOpen; + this.v = v; + this.pos = pos; + } + return Event; + })(); + function compareEvents(a, b) { + if (a.pos > b.pos) { + return 1; + } + if (a.pos < b.pos) { + return -1; + } + if (a.isOpen) { + // open must come before close + return -1; + } + if (b.isOpen) { + // open must come before close + return 1; + } + return 0; + } + function makeRBTree() { + return new vpsc.RBTree(function (a, b) { return a.pos - b.pos; }); + } + var xRect = { + getCentre: function (r) { return r.cx(); }, + getOpen: function (r) { return r.y; }, + getClose: function (r) { return r.Y; }, + getSize: function (r) { return r.width(); }, + makeRect: function (open, close, center, size) { return new Rectangle(center - size / 2, center + size / 2, open, close); }, + findNeighbours: findXNeighbours + }; + var yRect = { + getCentre: function (r) { return r.cy(); }, + getOpen: function (r) { return r.x; }, + getClose: function (r) { return r.X; }, + getSize: function (r) { return r.height(); }, + makeRect: function (open, close, center, size) { return new Rectangle(open, close, center - size / 2, center + size / 2); }, + findNeighbours: findYNeighbours + }; + function generateGroupConstraints(root, f, minSep, isContained) { + if (isContained === void 0) { isContained = false; } + var padding = root.padding, gn = typeof root.groups !== 'undefined' ? root.groups.length : 0, ln = typeof root.leaves !== 'undefined' ? root.leaves.length : 0, childConstraints = !gn ? [] + : root.groups.reduce(function (ccs, g) { return ccs.concat(generateGroupConstraints(g, f, minSep, true)); }, []), n = (isContained ? 2 : 0) + ln + gn, vs = new Array(n), rs = new Array(n), i = 0, add = function (r, v) { rs[i] = r; vs[i++] = v; }; + if (isContained) { + // if this group is contained by another, then we add two dummy vars and rectangles for the borders + var b = root.bounds, c = f.getCentre(b), s = f.getSize(b) / 2, open = f.getOpen(b), close = f.getClose(b), min = c - s + padding / 2, max = c + s - padding / 2; + root.minVar.desiredPosition = min; + add(f.makeRect(open, close, min, padding), root.minVar); + root.maxVar.desiredPosition = max; + add(f.makeRect(open, close, max, padding), root.maxVar); + } + if (ln) + root.leaves.forEach(function (l) { return add(l.bounds, l.variable); }); + if (gn) + root.groups.forEach(function (g) { + var b = g.bounds; + add(f.makeRect(f.getOpen(b), f.getClose(b), f.getCentre(b), f.getSize(b)), g.minVar); + }); + var cs = generateConstraints(rs, vs, f, minSep); + if (gn) { + vs.forEach(function (v) { v.cOut = [], v.cIn = []; }); + cs.forEach(function (c) { c.left.cOut.push(c), c.right.cIn.push(c); }); + root.groups.forEach(function (g) { + var gapAdjustment = (g.padding - f.getSize(g.bounds)) / 2; + g.minVar.cIn.forEach(function (c) { return c.gap += gapAdjustment; }); + g.minVar.cOut.forEach(function (c) { c.left = g.maxVar; c.gap += gapAdjustment; }); + }); + } + return childConstraints.concat(cs); + } + function generateConstraints(rs, vars, rect, minSep) { + var i, n = rs.length; + var N = 2 * n; + console.assert(vars.length >= n); + var events = new Array(N); + for (i = 0; i < n; ++i) { + var r = rs[i]; + var v = new Node(vars[i], r, rect.getCentre(r)); + events[i] = new Event(true, v, rect.getOpen(r)); + events[i + n] = new Event(false, v, rect.getClose(r)); + } + events.sort(compareEvents); + var cs = new Array(); + var scanline = makeRBTree(); + for (i = 0; i < N; ++i) { + var e = events[i]; + var v = e.v; + if (e.isOpen) { + scanline.insert(v); + rect.findNeighbours(v, scanline); + } + else { + // close event + scanline.remove(v); + var makeConstraint = function (l, r) { + var sep = (rect.getSize(l.r) + rect.getSize(r.r)) / 2 + minSep; + cs.push(new vpsc.Constraint(l.v, r.v, sep)); + }; + var visitNeighbours = function (forward, reverse, mkcon) { + var u, it = v[forward].iterator(); + while ((u = it[forward]()) !== null) { + mkcon(u, v); + u[reverse].remove(v); + } + }; + visitNeighbours("prev", "next", function (u, v) { return makeConstraint(u, v); }); + visitNeighbours("next", "prev", function (u, v) { return makeConstraint(v, u); }); + } + } + console.assert(scanline.size === 0); + return cs; + } + function findXNeighbours(v, scanline) { + var f = function (forward, reverse) { + var it = scanline.findIter(v); + var u; + while ((u = it[forward]()) !== null) { + var uovervX = u.r.overlapX(v.r); + if (uovervX <= 0 || uovervX <= u.r.overlapY(v.r)) { + v[forward].insert(u); + u[reverse].insert(v); + } + if (uovervX <= 0) { + break; + } + } + }; + f("next", "prev"); + f("prev", "next"); + } + function findYNeighbours(v, scanline) { + var f = function (forward, reverse) { + var u = scanline.findIter(v)[forward](); + if (u !== null && u.r.overlapX(v.r) > 0) { + v[forward].insert(u); + u[reverse].insert(v); + } + }; + f("next", "prev"); + f("prev", "next"); + } + function generateXConstraints(rs, vars) { + return generateConstraints(rs, vars, xRect, 1e-6); + } + vpsc.generateXConstraints = generateXConstraints; + function generateYConstraints(rs, vars) { + return generateConstraints(rs, vars, yRect, 1e-6); + } + vpsc.generateYConstraints = generateYConstraints; + function generateXGroupConstraints(root) { + return generateGroupConstraints(root, xRect, 1e-6); + } + vpsc.generateXGroupConstraints = generateXGroupConstraints; + function generateYGroupConstraints(root) { + return generateGroupConstraints(root, yRect, 1e-6); + } + vpsc.generateYGroupConstraints = generateYGroupConstraints; + function removeOverlaps(rs) { + var vs = rs.map(function (r) { return new vpsc.Variable(r.cx()); }); + var cs = vpsc.generateXConstraints(rs, vs); + var solver = new vpsc.Solver(vs, cs); + solver.solve(); + vs.forEach(function (v, i) { return rs[i].setXCentre(v.position()); }); + vs = rs.map(function (r) { return new vpsc.Variable(r.cy()); }); + cs = vpsc.generateYConstraints(rs, vs); + solver = new vpsc.Solver(vs, cs); + solver.solve(); + vs.forEach(function (v, i) { return rs[i].setYCentre(v.position()); }); + } + vpsc.removeOverlaps = removeOverlaps; + var IndexedVariable = (function (_super) { + __extends(IndexedVariable, _super); + function IndexedVariable(index, w) { + _super.call(this, 0, w); + this.index = index; + } + return IndexedVariable; + })(vpsc.Variable); + vpsc.IndexedVariable = IndexedVariable; + var Projection = (function () { + function Projection(nodes, groups, rootGroup, constraints, avoidOverlaps) { + var _this = this; + if (rootGroup === void 0) { rootGroup = null; } + if (constraints === void 0) { constraints = null; } + if (avoidOverlaps === void 0) { avoidOverlaps = false; } + this.nodes = nodes; + this.groups = groups; + this.rootGroup = rootGroup; + this.avoidOverlaps = avoidOverlaps; + this.variables = nodes.map(function (v, i) { + return v.variable = new IndexedVariable(i, 1); + }); + if (constraints) + this.createConstraints(constraints); + if (avoidOverlaps && rootGroup && typeof rootGroup.groups !== 'undefined') { + nodes.forEach(function (v) { + if (!v.width || !v.height) { + //If undefined, default to nothing + v.bounds = new vpsc.Rectangle(v.x, v.x, v.y, v.y); + return; + } + var w2 = v.width / 2, h2 = v.height / 2; + v.bounds = new vpsc.Rectangle(v.x - w2, v.x + w2, v.y - h2, v.y + h2); + }); + computeGroupBounds(rootGroup); + var i = nodes.length; + groups.forEach(function (g) { + _this.variables[i] = g.minVar = new IndexedVariable(i++, typeof g.stiffness !== "undefined" ? g.stiffness : 0.01); + _this.variables[i] = g.maxVar = new IndexedVariable(i++, typeof g.stiffness !== "undefined" ? g.stiffness : 0.01); + }); + } + } + Projection.prototype.createSeparation = function (c) { + return new vpsc.Constraint(this.nodes[c.left].variable, this.nodes[c.right].variable, c.gap, typeof c.equality !== "undefined" ? c.equality : false); + }; + Projection.prototype.makeFeasible = function (c) { + var _this = this; + if (!this.avoidOverlaps) + return; + var axis = 'x', dim = 'width'; + if (c.axis === 'x') + axis = 'y', dim = 'height'; + var vs = c.offsets.map(function (o) { return _this.nodes[o.node]; }).sort(function (a, b) { return a[axis] - b[axis]; }); + var p = null; + vs.forEach(function (v) { + if (p) + v[axis] = p[axis] + p[dim] + 1; + p = v; + }); + }; + Projection.prototype.createAlignment = function (c) { + var _this = this; + var u = this.nodes[c.offsets[0].node].variable; + this.makeFeasible(c); + var cs = c.axis === 'x' ? this.xConstraints : this.yConstraints; + c.offsets.slice(1).forEach(function (o) { + var v = _this.nodes[o.node].variable; + cs.push(new vpsc.Constraint(u, v, o.offset, true)); + }); + }; + Projection.prototype.createConstraints = function (constraints) { + var _this = this; + var isSep = function (c) { return typeof c.type === 'undefined' || c.type === 'separation'; }; + this.xConstraints = constraints + .filter(function (c) { return c.axis === "x" && isSep(c); }) + .map(function (c) { return _this.createSeparation(c); }); + this.yConstraints = constraints + .filter(function (c) { return c.axis === "y" && isSep(c); }) + .map(function (c) { return _this.createSeparation(c); }); + constraints + .filter(function (c) { return c.type === 'alignment'; }) + .forEach(function (c) { return _this.createAlignment(c); }); + }; + Projection.prototype.setupVariablesAndBounds = function (x0, y0, desired, getDesired) { + this.nodes.forEach(function (v, i) { + if (v.fixed) { + v.variable.weight = v.fixedWeight ? v.fixedWeight : 1000; + desired[i] = getDesired(v); + } + else { + v.variable.weight = 1; + } + var w = (v.width || 0) / 2, h = (v.height || 0) / 2; + var ix = x0[i], iy = y0[i]; + v.bounds = new Rectangle(ix - w, ix + w, iy - h, iy + h); + }); + }; + Projection.prototype.xProject = function (x0, y0, x) { + if (!this.rootGroup && !(this.avoidOverlaps || this.xConstraints)) + return; + this.project(x0, y0, x0, x, function (v) { return v.px; }, this.xConstraints, generateXGroupConstraints, function (v) { return v.bounds.setXCentre(x[v.variable.index] = v.variable.position()); }, function (g) { + var xmin = x[g.minVar.index] = g.minVar.position(); + var xmax = x[g.maxVar.index] = g.maxVar.position(); + var p2 = g.padding / 2; + g.bounds.x = xmin - p2; + g.bounds.X = xmax + p2; + }); + }; + Projection.prototype.yProject = function (x0, y0, y) { + if (!this.rootGroup && !this.yConstraints) + return; + this.project(x0, y0, y0, y, function (v) { return v.py; }, this.yConstraints, generateYGroupConstraints, function (v) { return v.bounds.setYCentre(y[v.variable.index] = v.variable.position()); }, function (g) { + var ymin = y[g.minVar.index] = g.minVar.position(); + var ymax = y[g.maxVar.index] = g.maxVar.position(); + var p2 = g.padding / 2; + g.bounds.y = ymin - p2; + ; + g.bounds.Y = ymax + p2; + }); + }; + Projection.prototype.projectFunctions = function () { + var _this = this; + return [ + function (x0, y0, x) { return _this.xProject(x0, y0, x); }, + function (x0, y0, y) { return _this.yProject(x0, y0, y); } + ]; + }; + Projection.prototype.project = function (x0, y0, start, desired, getDesired, cs, generateConstraints, updateNodeBounds, updateGroupBounds) { + this.setupVariablesAndBounds(x0, y0, desired, getDesired); + if (this.rootGroup && this.avoidOverlaps) { + computeGroupBounds(this.rootGroup); + cs = cs.concat(generateConstraints(this.rootGroup)); + } + this.solve(this.variables, cs, start, desired); + this.nodes.forEach(updateNodeBounds); + if (this.rootGroup && this.avoidOverlaps) { + this.groups.forEach(updateGroupBounds); + } + }; + Projection.prototype.solve = function (vs, cs, starting, desired) { + var solver = new vpsc.Solver(vs, cs); + solver.setStartingPositions(starting); + solver.setDesiredPositions(desired); + solver.solve(); + }; + return Projection; + })(); + vpsc.Projection = Projection; + })(vpsc = cola.vpsc || (cola.vpsc = {})); +})(cola || (cola = {})); +///<reference path="vpsc.ts"/> +///<reference path="rectangle.ts"/> +var cola; +(function (cola) { + var geom; + (function (geom) { + var Point = (function () { + function Point() { + } + return Point; + })(); + geom.Point = Point; + var LineSegment = (function () { + function LineSegment(x1, y1, x2, y2) { + this.x1 = x1; + this.y1 = y1; + this.x2 = x2; + this.y2 = y2; + } + return LineSegment; + })(); + geom.LineSegment = LineSegment; + var PolyPoint = (function (_super) { + __extends(PolyPoint, _super); + function PolyPoint() { + _super.apply(this, arguments); + } + return PolyPoint; + })(Point); + geom.PolyPoint = PolyPoint; + /** tests if a point is Left|On|Right of an infinite line. + * @param points P0, P1, and P2 + * @return >0 for P2 left of the line through P0 and P1 + * =0 for P2 on the line + * <0 for P2 right of the line + */ + function isLeft(P0, P1, P2) { + return (P1.x - P0.x) * (P2.y - P0.y) - (P2.x - P0.x) * (P1.y - P0.y); + } + geom.isLeft = isLeft; + function above(p, vi, vj) { + return isLeft(p, vi, vj) > 0; + } + function below(p, vi, vj) { + return isLeft(p, vi, vj) < 0; + } + /** + * returns the convex hull of a set of points using Andrew's monotone chain algorithm + * see: http://geomalgorithms.com/a10-_hull-1.html#Monotone%20Chain + * @param S array of points + * @return the convex hull as an array of points + */ + function ConvexHull(S) { + var P = S.slice(0).sort(function (a, b) { return a.x !== b.x ? b.x - a.x : b.y - a.y; }); + var n = S.length, i; + var minmin = 0; + var xmin = P[0].x; + for (i = 1; i < n; ++i) { + if (P[i].x !== xmin) + break; + } + var minmax = i - 1; + var H = []; + H.push(P[minmin]); // push minmin point onto stack + if (minmax === n - 1) { + if (P[minmax].y !== P[minmin].y) + H.push(P[minmax]); + } + else { + // Get the indices of points with max x-coord and min|max y-coord + var maxmin, maxmax = n - 1; + var xmax = P[n - 1].x; + for (i = n - 2; i >= 0; i--) + if (P[i].x !== xmax) + break; + maxmin = i + 1; + // Compute the lower hull on the stack H + i = minmax; + while (++i <= maxmin) { + // the lower line joins P[minmin] with P[maxmin] + if (isLeft(P[minmin], P[maxmin], P[i]) >= 0 && i < maxmin) + continue; // ignore P[i] above or on the lower line + while (H.length > 1) { + // test if P[i] is left of the line at the stack top + if (isLeft(H[H.length - 2], H[H.length - 1], P[i]) > 0) + break; // P[i] is a new hull vertex + else + H.length -= 1; // pop top point off stack + } + if (i != minmin) + H.push(P[i]); + } + // Next, compute the upper hull on the stack H above the bottom hull + if (maxmax != maxmin) + H.push(P[maxmax]); // push maxmax point onto stack + var bot = H.length; // the bottom point of the upper hull stack + i = maxmin; + while (--i >= minmax) { + // the upper line joins P[maxmax] with P[minmax] + if (isLeft(P[maxmax], P[minmax], P[i]) >= 0 && i > minmax) + continue; // ignore P[i] below or on the upper line + while (H.length > bot) { + // test if P[i] is left of the line at the stack top + if (isLeft(H[H.length - 2], H[H.length - 1], P[i]) > 0) + break; // P[i] is a new hull vertex + else + H.length -= 1; // pop top point off stack + } + if (i != minmin) + H.push(P[i]); // push P[i] onto stack + } + } + return H; + } + geom.ConvexHull = ConvexHull; + // apply f to the points in P in clockwise order around the point p + function clockwiseRadialSweep(p, P, f) { + P.slice(0).sort(function (a, b) { return Math.atan2(a.y - p.y, a.x - p.x) - Math.atan2(b.y - p.y, b.x - p.x); }).forEach(f); + } + geom.clockwiseRadialSweep = clockwiseRadialSweep; + function nextPolyPoint(p, ps) { + if (p.polyIndex === ps.length - 1) + return ps[0]; + return ps[p.polyIndex + 1]; + } + function prevPolyPoint(p, ps) { + if (p.polyIndex === 0) + return ps[ps.length - 1]; + return ps[p.polyIndex - 1]; + } + // tangent_PointPolyC(): fast binary search for tangents to a convex polygon + // Input: P = a 2D point (exterior to the polygon) + // n = number of polygon vertices + // V = array of vertices for a 2D convex polygon with V[n] = V[0] + // Output: rtan = index of rightmost tangent point V[rtan] + // ltan = index of leftmost tangent point V[ltan] + function tangent_PointPolyC(P, V) { + return { rtan: Rtangent_PointPolyC(P, V), ltan: Ltangent_PointPolyC(P, V) }; + } + // Rtangent_PointPolyC(): binary search for convex polygon right tangent + // Input: P = a 2D point (exterior to the polygon) + // n = number of polygon vertices + // V = array of vertices for a 2D convex polygon with V[n] = V[0] + // Return: index "i" of rightmost tangent point V[i] + function Rtangent_PointPolyC(P, V) { + var n = V.length - 1; + // use binary search for large convex polygons + var a, b, c; // indices for edge chain endpoints + var upA, dnC; // test for up direction of edges a and c + // rightmost tangent = maximum for the isLeft() ordering + // test if V[0] is a local maximum + if (below(P, V[1], V[0]) && !above(P, V[n - 1], V[0])) + return 0; // V[0] is the maximum tangent point + for (a = 0, b = n;;) { + if (b - a === 1) + if (above(P, V[a], V[b])) + return a; + else + return b; + c = Math.floor((a + b) / 2); // midpoint of [a,b], and 0<c<n + dnC = below(P, V[c + 1], V[c]); + if (dnC && !above(P, V[c - 1], V[c])) + return c; // V[c] is the maximum tangent point + // no max yet, so continue with the binary search + // pick one of the two subchains [a,c] or [c,b] + upA = above(P, V[a + 1], V[a]); + if (upA) { + if (dnC) + b = c; // select [a,c] + else { + if (above(P, V[a], V[c])) + b = c; // select [a,c] + else + a = c; // select [c,b] + } + } + else { + if (!dnC) + a = c; // select [c,b] + else { + if (below(P, V[a], V[c])) + b = c; // select [a,c] + else + a = c; // select [c,b] + } + } + } + } + // Ltangent_PointPolyC(): binary search for convex polygon left tangent + // Input: P = a 2D point (exterior to the polygon) + // n = number of polygon vertices + // V = array of vertices for a 2D convex polygon with V[n]=V[0] + // Return: index "i" of leftmost tangent point V[i] + function Ltangent_PointPolyC(P, V) { + var n = V.length - 1; + // use binary search for large convex polygons + var a, b, c; // indices for edge chain endpoints + var dnA, dnC; // test for down direction of edges a and c + // leftmost tangent = minimum for the isLeft() ordering + // test if V[0] is a local minimum + if (above(P, V[n - 1], V[0]) && !below(P, V[1], V[0])) + return 0; // V[0] is the minimum tangent point + for (a = 0, b = n;;) { + if (b - a === 1) + if (below(P, V[a], V[b])) + return a; + else + return b; + c = Math.floor((a + b) / 2); // midpoint of [a,b], and 0<c<n + dnC = below(P, V[c + 1], V[c]); + if (above(P, V[c - 1], V[c]) && !dnC) + return c; // V[c] is the minimum tangent point + // no min yet, so continue with the binary search + // pick one of the two subchains [a,c] or [c,b] + dnA = below(P, V[a + 1], V[a]); + if (dnA) { + if (!dnC) + b = c; // select [a,c] + else { + if (below(P, V[a], V[c])) + b = c; // select [a,c] + else + a = c; // select [c,b] + } + } + else { + if (dnC) + a = c; // select [c,b] + else { + if (above(P, V[a], V[c])) + b = c; // select [a,c] + else + a = c; // select [c,b] + } + } + } + } + // RLtangent_PolyPolyC(): get the RL tangent between two convex polygons + // Input: m = number of vertices in polygon 1 + // V = array of vertices for convex polygon 1 with V[m]=V[0] + // n = number of vertices in polygon 2 + // W = array of vertices for convex polygon 2 with W[n]=W[0] + // Output: *t1 = index of tangent point V[t1] for polygon 1 + // *t2 = index of tangent point W[t2] for polygon 2 + function tangent_PolyPolyC(V, W, t1, t2, cmp1, cmp2) { + var ix1, ix2; // search indices for polygons 1 and 2 + // first get the initial vertex on each polygon + ix1 = t1(W[0], V); // right tangent from W[0] to V + ix2 = t2(V[ix1], W); // left tangent from V[ix1] to W + // ping-pong linear search until it stabilizes + var done = false; // flag when done + while (!done) { + done = true; // assume done until... + while (true) { + if (ix1 === V.length - 1) + ix1 = 0; + if (cmp1(W[ix2], V[ix1], V[ix1 + 1])) + break; + ++ix1; // get Rtangent from W[ix2] to V + } + while (true) { + if (ix2 === 0) + ix2 = W.length - 1; + if (cmp2(V[ix1], W[ix2], W[ix2 - 1])) + break; + --ix2; // get Ltangent from V[ix1] to W + done = false; // not done if had to adjust this + } + } + return { t1: ix1, t2: ix2 }; + } + geom.tangent_PolyPolyC = tangent_PolyPolyC; + function LRtangent_PolyPolyC(V, W) { + var rl = RLtangent_PolyPolyC(W, V); + return { t1: rl.t2, t2: rl.t1 }; + } + geom.LRtangent_PolyPolyC = LRtangent_PolyPolyC; + function RLtangent_PolyPolyC(V, W) { + return tangent_PolyPolyC(V, W, Rtangent_PointPolyC, Ltangent_PointPolyC, above, below); + } + geom.RLtangent_PolyPolyC = RLtangent_PolyPolyC; + function LLtangent_PolyPolyC(V, W) { + return tangent_PolyPolyC(V, W, Ltangent_PointPolyC, Ltangent_PointPolyC, below, below); + } + geom.LLtangent_PolyPolyC = LLtangent_PolyPolyC; + function RRtangent_PolyPolyC(V, W) { + return tangent_PolyPolyC(V, W, Rtangent_PointPolyC, Rtangent_PointPolyC, above, above); + } + geom.RRtangent_PolyPolyC = RRtangent_PolyPolyC; + var BiTangent = (function () { + function BiTangent(t1, t2) { + this.t1 = t1; + this.t2 = t2; + } + return BiTangent; + })(); + geom.BiTangent = BiTangent; + var BiTangents = (function () { + function BiTangents() { + } + return BiTangents; + })(); + geom.BiTangents = BiTangents; + var TVGPoint = (function (_super) { + __extends(TVGPoint, _super); + function TVGPoint() { + _super.apply(this, arguments); + } + return TVGPoint; + })(Point); + geom.TVGPoint = TVGPoint; + var VisibilityVertex = (function () { + function VisibilityVertex(id, polyid, polyvertid, p) { + this.id = id; + this.polyid = polyid; + this.polyvertid = polyvertid; + this.p = p; + p.vv = this; + } + return VisibilityVertex; + })(); + geom.VisibilityVertex = VisibilityVertex; + var VisibilityEdge = (function () { + function VisibilityEdge(source, target) { + this.source = source; + this.target = target; + } + VisibilityEdge.prototype.length = function () { + var dx = this.source.p.x - this.target.p.x; + var dy = this.source.p.y - this.target.p.y; + return Math.sqrt(dx * dx + dy * dy); + }; + return VisibilityEdge; + })(); + geom.VisibilityEdge = VisibilityEdge; + var TangentVisibilityGraph = (function () { + function TangentVisibilityGraph(P, g0) { + this.P = P; + this.V = []; + this.E = []; + if (!g0) { + var n = P.length; + for (var i = 0; i < n; i++) { + var p = P[i]; + for (var j = 0; j < p.length; ++j) { + var pj = p[j], vv = new VisibilityVertex(this.V.length, i, j, pj); + this.V.push(vv); + if (j > 0) + this.E.push(new VisibilityEdge(p[j - 1].vv, vv)); + } + } + for (var i = 0; i < n - 1; i++) { + var Pi = P[i]; + for (var j = i + 1; j < n; j++) { + var Pj = P[j], t = geom.tangents(Pi, Pj); + for (var q in t) { + var c = t[q], source = Pi[c.t1], target = Pj[c.t2]; + this.addEdgeIfVisible(source, target, i, j); + } + } + } + } + else { + this.V = g0.V.slice(0); + this.E = g0.E.slice(0); + } + } + TangentVisibilityGraph.prototype.addEdgeIfVisible = function (u, v, i1, i2) { + if (!this.intersectsPolys(new LineSegment(u.x, u.y, v.x, v.y), i1, i2)) { + this.E.push(new VisibilityEdge(u.vv, v.vv)); + } + }; + TangentVisibilityGraph.prototype.addPoint = function (p, i1) { + var n = this.P.length; + this.V.push(new VisibilityVertex(this.V.length, n, 0, p)); + for (var i = 0; i < n; ++i) { + if (i === i1) + continue; + var poly = this.P[i], t = tangent_PointPolyC(p, poly); + this.addEdgeIfVisible(p, poly[t.ltan], i1, i); + this.addEdgeIfVisible(p, poly[t.rtan], i1, i); + } + return p.vv; + }; + TangentVisibilityGraph.prototype.intersectsPolys = function (l, i1, i2) { + for (var i = 0, n = this.P.length; i < n; ++i) { + if (i != i1 && i != i2 && intersects(l, this.P[i]).length > 0) { + return true; + } + } + return false; + }; + return TangentVisibilityGraph; + })(); + geom.TangentVisibilityGraph = TangentVisibilityGraph; + function intersects(l, P) { + var ints = []; + for (var i = 1, n = P.length; i < n; ++i) { + var int = cola.vpsc.Rectangle.lineIntersection(l.x1, l.y1, l.x2, l.y2, P[i - 1].x, P[i - 1].y, P[i].x, P[i].y); + if (int) + ints.push(int); + } + return ints; + } + function tangents(V, W) { + var m = V.length - 1, n = W.length - 1; + var bt = new BiTangents(); + for (var i = 0; i < m; ++i) { + for (var j = 0; j < n; ++j) { + var v1 = V[i == 0 ? m - 1 : i - 1]; + var v2 = V[i]; + var v3 = V[i + 1]; + var w1 = W[j == 0 ? n - 1 : j - 1]; + var w2 = W[j]; + var w3 = W[j + 1]; + var v1v2w2 = isLeft(v1, v2, w2); + var v2w1w2 = isLeft(v2, w1, w2); + var v2w2w3 = isLeft(v2, w2, w3); + var w1w2v2 = isLeft(w1, w2, v2); + var w2v1v2 = isLeft(w2, v1, v2); + var w2v2v3 = isLeft(w2, v2, v3); + if (v1v2w2 >= 0 && v2w1w2 >= 0 && v2w2w3 < 0 + && w1w2v2 >= 0 && w2v1v2 >= 0 && w2v2v3 < 0) { + bt.ll = new BiTangent(i, j); + } + else if (v1v2w2 <= 0 && v2w1w2 <= 0 && v2w2w3 > 0 + && w1w2v2 <= 0 && w2v1v2 <= 0 && w2v2v3 > 0) { + bt.rr = new BiTangent(i, j); + } + else if (v1v2w2 <= 0 && v2w1w2 > 0 && v2w2w3 <= 0 + && w1w2v2 >= 0 && w2v1v2 < 0 && w2v2v3 >= 0) { + bt.rl = new BiTangent(i, j); + } + else if (v1v2w2 >= 0 && v2w1w2 < 0 && v2w2w3 >= 0 + && w1w2v2 <= 0 && w2v1v2 > 0 && w2v2v3 <= 0) { + bt.lr = new BiTangent(i, j); + } + } + } + return bt; + } + geom.tangents = tangents; + function isPointInsidePoly(p, poly) { + for (var i = 1, n = poly.length; i < n; ++i) + if (below(poly[i - 1], poly[i], p)) + return false; + return true; + } + function isAnyPInQ(p, q) { + return !p.every(function (v) { return !isPointInsidePoly(v, q); }); + } + function polysOverlap(p, q) { + if (isAnyPInQ(p, q)) + return true; + if (isAnyPInQ(q, p)) + return true; + for (var i = 1, n = p.length; i < n; ++i) { + var v = p[i], u = p[i - 1]; + if (intersects(new LineSegment(u.x, u.y, v.x, v.y), q).length > 0) + return true; + } + return false; + } + geom.polysOverlap = polysOverlap; + })(geom = cola.geom || (cola.geom = {})); +})(cola || (cola = {})); +/** + * @module cola + */ +var cola; +(function (cola) { + /** + * Descent respects a collection of locks over nodes that should not move + * @class Locks + */ + var Locks = (function () { + function Locks() { + this.locks = {}; + } + /** + * add a lock on the node at index id + * @method add + * @param id index of node to be locked + * @param x required position for node + */ + Locks.prototype.add = function (id, x) { + /* DEBUG + if (isNaN(x[0]) || isNaN(x[1])) debugger; + DEBUG */ + this.locks[id] = x; + }; + /** + * @method clear clear all locks + */ + Locks.prototype.clear = function () { + this.locks = {}; + }; + /** + * @isEmpty + * @returns false if no locks exist + */ + Locks.prototype.isEmpty = function () { + for (var l in this.locks) + return false; + return true; + }; + /** + * perform an operation on each lock + * @apply + */ + Locks.prototype.apply = function (f) { + for (var l in this.locks) { + f(l, this.locks[l]); + } + }; + return Locks; + })(); + cola.Locks = Locks; + /** + * Uses a gradient descent approach to reduce a stress or p-stress goal function over a graph with specified ideal edge lengths or a square matrix of dissimilarities. + * The standard stress function over a graph nodes with position vectors x,y,z is (mathematica input): + * stress[x_,y_,z_,D_,w_]:=Sum[w[[i,j]] (length[x[[i]],y[[i]],z[[i]],x[[j]],y[[j]],z[[j]]]-d[[i,j]])^2,{i,Length[x]-1},{j,i+1,Length[x]}] + * where: D is a square matrix of ideal separations between nodes, w is matrix of weights for those separations + * length[x1_, y1_, z1_, x2_, y2_, z2_] = Sqrt[(x1 - x2)^2 + (y1 - y2)^2 + (z1 - z2)^2] + * below, we use wij = 1/(Dij^2) + * + * @class Descent + */ + var Descent = (function () { + /** + * @method constructor + * @param x {number[][]} initial coordinates for nodes + * @param D {number[][]} matrix of desired distances between pairs of nodes + * @param G {number[][]} [default=null] if specified, G is a matrix of weights for goal terms between pairs of nodes. + * If G[i][j] > 1 and the separation between nodes i and j is greater than their ideal distance, then there is no contribution for this pair to the goal + * If G[i][j] <= 1 then it is used as a weighting on the contribution of the variance between ideal and actual separation between i and j to the goal function + */ + function Descent(x, D, G) { + if (G === void 0) { G = null; } + this.D = D; + this.G = G; + this.threshold = 0.0001; + // Parameters for grid snap stress. + // TODO: Make a pluggable "StressTerm" class instead of this + // mess. + this.numGridSnapNodes = 0; + this.snapGridSize = 100; + this.snapStrength = 1000; + this.scaleSnapByMaxH = false; + this.random = new PseudoRandom(); + this.project = null; + this.x = x; + this.k = x.length; // dimensionality + var n = this.n = x[0].length; // number of nodes + this.H = new Array(this.k); + this.g = new Array(this.k); + this.Hd = new Array(this.k); + this.a = new Array(this.k); + this.b = new Array(this.k); + this.c = new Array(this.k); + this.d = new Array(this.k); + this.e = new Array(this.k); + this.ia = new Array(this.k); + this.ib = new Array(this.k); + this.xtmp = new Array(this.k); + this.locks = new Locks(); + this.minD = Number.MAX_VALUE; + var i = n, j; + while (i--) { + j = n; + while (--j > i) { + var d = D[i][j]; + if (d > 0 && d < this.minD) { + this.minD = d; + } + } + } + if (this.minD === Number.MAX_VALUE) + this.minD = 1; + i = this.k; + while (i--) { + this.g[i] = new Array(n); + this.H[i] = new Array(n); + j = n; + while (j--) { + this.H[i][j] = new Array(n); + } + this.Hd[i] = new Array(n); + this.a[i] = new Array(n); + this.b[i] = new Array(n); + this.c[i] = new Array(n); + this.d[i] = new Array(n); + this.e[i] = new Array(n); + this.ia[i] = new Array(n); + this.ib[i] = new Array(n); + this.xtmp[i] = new Array(n); + } + } + Descent.createSquareMatrix = function (n, f) { + var M = new Array(n); + for (var i = 0; i < n; ++i) { + M[i] = new Array(n); + for (var j = 0; j < n; ++j) { + M[i][j] = f(i, j); + } + } + return M; + }; + Descent.prototype.offsetDir = function () { + var _this = this; + var u = new Array(this.k); + var l = 0; + for (var i = 0; i < this.k; ++i) { + var x = u[i] = this.random.getNextBetween(0.01, 1) - 0.5; + l += x * x; + } + l = Math.sqrt(l); + return u.map(function (x) { return x *= _this.minD / l; }); + }; + // compute first and second derivative information storing results in this.g and this.H + Descent.prototype.computeDerivatives = function (x) { + var _this = this; + var n = this.n; + if (n < 1) + return; + var i; + /* DEBUG + for (var u: number = 0; u < n; ++u) + for (i = 0; i < this.k; ++i) + if (isNaN(x[i][u])) debugger; + DEBUG */ + var d = new Array(this.k); + var d2 = new Array(this.k); + var Huu = new Array(this.k); + var maxH = 0; + for (var u = 0; u < n; ++u) { + for (i = 0; i < this.k; ++i) + Huu[i] = this.g[i][u] = 0; + for (var v = 0; v < n; ++v) { + if (u === v) + continue; + // The following loop randomly displaces nodes that are at identical positions + var maxDisplaces = n; // avoid infinite loop in the case of numerical issues, such as huge values + while (maxDisplaces--) { + var sd2 = 0; + for (i = 0; i < this.k; ++i) { + var dx = d[i] = x[i][u] - x[i][v]; + sd2 += d2[i] = dx * dx; + } + if (sd2 > 1e-9) + break; + var rd = this.offsetDir(); + for (i = 0; i < this.k; ++i) + x[i][v] += rd[i]; + } + var l = Math.sqrt(sd2); + var D = this.D[u][v]; + var weight = this.G != null ? this.G[u][v] : 1; + if (weight > 1 && l > D || !isFinite(D)) { + for (i = 0; i < this.k; ++i) + this.H[i][u][v] = 0; + continue; + } + if (weight > 1) { + weight = 1; + } + var D2 = D * D; + var gs = 2 * weight * (l - D) / (D2 * l); + var l3 = l * l * l; + var hs = 2 * -weight / (D2 * l3); + if (!isFinite(gs)) + console.log(gs); + for (i = 0; i < this.k; ++i) { + this.g[i][u] += d[i] * gs; + Huu[i] -= this.H[i][u][v] = hs * (l3 + D * (d2[i] - sd2) + l * sd2); + } + } + for (i = 0; i < this.k; ++i) + maxH = Math.max(maxH, this.H[i][u][u] = Huu[i]); + } + // Grid snap forces + var r = this.snapGridSize / 2; + var g = this.snapGridSize; + var w = this.snapStrength; + var k = w / (r * r); + var numNodes = this.numGridSnapNodes; + //var numNodes = n; + for (var u = 0; u < numNodes; ++u) { + for (i = 0; i < this.k; ++i) { + var xiu = this.x[i][u]; + var m = xiu / g; + var f = m % 1; + var q = m - f; + var a = Math.abs(f); + var dx = (a <= 0.5) ? xiu - q * g : + (xiu > 0) ? xiu - (q + 1) * g : xiu - (q - 1) * g; + if (-r < dx && dx <= r) { + if (this.scaleSnapByMaxH) { + this.g[i][u] += maxH * k * dx; + this.H[i][u][u] += maxH * k; + } + else { + this.g[i][u] += k * dx; + this.H[i][u][u] += k; + } + } + } + } + if (!this.locks.isEmpty()) { + this.locks.apply(function (u, p) { + for (i = 0; i < _this.k; ++i) { + _this.H[i][u][u] += maxH; + _this.g[i][u] -= maxH * (p[i] - x[i][u]); + } + }); + } + /* DEBUG + for (var u: number = 0; u < n; ++u) + for (i = 0; i < this.k; ++i) { + if (isNaN(this.g[i][u])) debugger; + for (var v: number = 0; v < n; ++v) + if (isNaN(this.H[i][u][v])) debugger; + } + DEBUG */ + }; + Descent.dotProd = function (a, b) { + var x = 0, i = a.length; + while (i--) + x += a[i] * b[i]; + return x; + }; + // result r = matrix m * vector v + Descent.rightMultiply = function (m, v, r) { + var i = m.length; + while (i--) + r[i] = Descent.dotProd(m[i], v); + }; + // computes the optimal step size to take in direction d using the + // derivative information in this.g and this.H + // returns the scalar multiplier to apply to d to get the optimal step + Descent.prototype.computeStepSize = function (d) { + var numerator = 0, denominator = 0; + for (var i = 0; i < this.k; ++i) { + numerator += Descent.dotProd(this.g[i], d[i]); + Descent.rightMultiply(this.H[i], d[i], this.Hd[i]); + denominator += Descent.dotProd(d[i], this.Hd[i]); + } + if (denominator === 0 || !isFinite(denominator)) + return 0; + return 1 * numerator / denominator; + }; + Descent.prototype.reduceStress = function () { + this.computeDerivatives(this.x); + var alpha = this.computeStepSize(this.g); + for (var i = 0; i < this.k; ++i) { + this.takeDescentStep(this.x[i], this.g[i], alpha); + } + return this.computeStress(); + }; + Descent.copy = function (a, b) { + var m = a.length, n = b[0].length; + for (var i = 0; i < m; ++i) { + for (var j = 0; j < n; ++j) { + b[i][j] = a[i][j]; + } + } + }; + // takes a step of stepSize * d from x0, and then project against any constraints. + // result is returned in r. + // x0: starting positions + // r: result positions will be returned here + // d: unconstrained descent vector + // stepSize: amount to step along d + Descent.prototype.stepAndProject = function (x0, r, d, stepSize) { + Descent.copy(x0, r); + this.takeDescentStep(r[0], d[0], stepSize); + if (this.project) + this.project[0](x0[0], x0[1], r[0]); + this.takeDescentStep(r[1], d[1], stepSize); + if (this.project) + this.project[1](r[0], x0[1], r[1]); + // todo: allow projection against constraints in higher dimensions + for (var i = 2; i < this.k; i++) + this.takeDescentStep(r[i], d[i], stepSize); + // the following makes locks extra sticky... but hides the result of the projection from the consumer + //if (!this.locks.isEmpty()) { + // this.locks.apply((u, p) => { + // for (var i = 0; i < this.k; i++) { + // r[i][u] = p[i]; + // } + // }); + //} + }; + Descent.mApply = function (m, n, f) { + var i = m; + while (i-- > 0) { + var j = n; + while (j-- > 0) + f(i, j); + } + }; + Descent.prototype.matrixApply = function (f) { + Descent.mApply(this.k, this.n, f); + }; + Descent.prototype.computeNextPosition = function (x0, r) { + var _this = this; + this.computeDerivatives(x0); + var alpha = this.computeStepSize(this.g); + this.stepAndProject(x0, r, this.g, alpha); + /* DEBUG + for (var u: number = 0; u < this.n; ++u) + for (var i = 0; i < this.k; ++i) + if (isNaN(r[i][u])) debugger; + DEBUG */ + if (this.project) { + this.matrixApply(function (i, j) { return _this.e[i][j] = x0[i][j] - r[i][j]; }); + var beta = this.computeStepSize(this.e); + beta = Math.max(0.2, Math.min(beta, 1)); + this.stepAndProject(x0, r, this.e, beta); + } + }; + Descent.prototype.run = function (iterations) { + var stress = Number.MAX_VALUE, converged = false; + while (!converged && iterations-- > 0) { + var s = this.rungeKutta(); + converged = Math.abs(stress / s - 1) < this.threshold; + stress = s; + } + return stress; + }; + Descent.prototype.rungeKutta = function () { + var _this = this; + this.computeNextPosition(this.x, this.a); + Descent.mid(this.x, this.a, this.ia); + this.computeNextPosition(this.ia, this.b); + Descent.mid(this.x, this.b, this.ib); + this.computeNextPosition(this.ib, this.c); + this.computeNextPosition(this.c, this.d); + var disp = 0; + this.matrixApply(function (i, j) { + var x = (_this.a[i][j] + 2.0 * _this.b[i][j] + 2.0 * _this.c[i][j] + _this.d[i][j]) / 6.0, d = _this.x[i][j] - x; + disp += d * d; + _this.x[i][j] = x; + }); + return disp; + }; + Descent.mid = function (a, b, m) { + Descent.mApply(a.length, a[0].length, function (i, j) { + return m[i][j] = a[i][j] + (b[i][j] - a[i][j]) / 2.0; + }); + }; + Descent.prototype.takeDescentStep = function (x, d, stepSize) { + for (var i = 0; i < this.n; ++i) { + x[i] = x[i] - stepSize * d[i]; + } + }; + Descent.prototype.computeStress = function () { + var stress = 0; + for (var u = 0, nMinus1 = this.n - 1; u < nMinus1; ++u) { + for (var v = u + 1, n = this.n; v < n; ++v) { + var l = 0; + for (var i = 0; i < this.k; ++i) { + var dx = this.x[i][u] - this.x[i][v]; + l += dx * dx; + } + l = Math.sqrt(l); + var d = this.D[u][v]; + if (!isFinite(d)) + continue; + var rl = d - l; + var d2 = d * d; + stress += rl * rl / d2; + } + } + return stress; + }; + Descent.zeroDistance = 1e-10; + return Descent; + })(); + cola.Descent = Descent; + // Linear congruential pseudo random number generator + var PseudoRandom = (function () { + function PseudoRandom(seed) { + if (seed === void 0) { seed = 1; } + this.seed = seed; + this.a = 214013; + this.c = 2531011; + this.m = 2147483648; + this.range = 32767; + } + // random real between 0 and 1 + PseudoRandom.prototype.getNext = function () { + this.seed = (this.seed * this.a + this.c) % this.m; + return (this.seed >> 16) / this.range; + }; + // random real between min and max + PseudoRandom.prototype.getNextBetween = function (min, max) { + return min + this.getNext() * (max - min); + }; + return PseudoRandom; + })(); + cola.PseudoRandom = PseudoRandom; +})(cola || (cola = {})); +var cola; +(function (cola) { + var powergraph; + (function (powergraph) { + var PowerEdge = (function () { + function PowerEdge(source, target, type) { + this.source = source; + this.target = target; + this.type = type; + } + return PowerEdge; + })(); + powergraph.PowerEdge = PowerEdge; + var Configuration = (function () { + function Configuration(n, edges, linkAccessor, rootGroup) { + var _this = this; + this.linkAccessor = linkAccessor; + this.modules = new Array(n); + this.roots = []; + if (rootGroup) { + this.initModulesFromGroup(rootGroup); + } + else { + this.roots.push(new ModuleSet()); + for (var i = 0; i < n; ++i) + this.roots[0].add(this.modules[i] = new Module(i)); + } + this.R = edges.length; + edges.forEach(function (e) { + var s = _this.modules[linkAccessor.getSourceIndex(e)], t = _this.modules[linkAccessor.getTargetIndex(e)], type = linkAccessor.getType(e); + s.outgoing.add(type, t); + t.incoming.add(type, s); + }); + } + Configuration.prototype.initModulesFromGroup = function (group) { + var moduleSet = new ModuleSet(); + this.roots.push(moduleSet); + for (var i = 0; i < group.leaves.length; ++i) { + var node = group.leaves[i]; + var module = new Module(node.id); + this.modules[node.id] = module; + moduleSet.add(module); + } + if (group.groups) { + for (var j = 0; j < group.groups.length; ++j) { + var child = group.groups[j]; + // Propagate group properties (like padding, stiffness, ...) as module definition so that the generated power graph group will inherit it + var definition = {}; + for (var prop in child) + if (prop !== "leaves" && prop !== "groups" && child.hasOwnProperty(prop)) + definition[prop] = child[prop]; + // Use negative module id to avoid clashes between predefined and generated modules + moduleSet.add(new Module(-1 - j, new LinkSets(), new LinkSets(), this.initModulesFromGroup(child), definition)); + } + } + return moduleSet; + }; + // merge modules a and b keeping track of their power edges and removing the from roots + Configuration.prototype.merge = function (a, b, k) { + if (k === void 0) { k = 0; } + var inInt = a.incoming.intersection(b.incoming), outInt = a.outgoing.intersection(b.outgoing); + var children = new ModuleSet(); + children.add(a); + children.add(b); + var m = new Module(this.modules.length, outInt, inInt, children); + this.modules.push(m); + var update = function (s, i, o) { + s.forAll(function (ms, linktype) { + ms.forAll(function (n) { + var nls = n[i]; + nls.add(linktype, m); + nls.remove(linktype, a); + nls.remove(linktype, b); + a[o].remove(linktype, n); + b[o].remove(linktype, n); + }); + }); + }; + update(outInt, "incoming", "outgoing"); + update(inInt, "outgoing", "incoming"); + this.R -= inInt.count() + outInt.count(); + this.roots[k].remove(a); + this.roots[k].remove(b); + this.roots[k].add(m); + return m; + }; + Configuration.prototype.rootMerges = function (k) { + if (k === void 0) { k = 0; } + var rs = this.roots[k].modules(); + var n = rs.length; + var merges = new Array(n * (n - 1)); + var ctr = 0; + for (var i = 0, i_ = n - 1; i < i_; ++i) { + for (var j = i + 1; j < n; ++j) { + var a = rs[i], b = rs[j]; + merges[ctr] = { id: ctr, nEdges: this.nEdges(a, b), a: a, b: b }; + ctr++; + } + } + return merges; + }; + Configuration.prototype.greedyMerge = function () { + for (var i = 0; i < this.roots.length; ++i) { + // Handle single nested module case + if (this.roots[i].modules().length < 2) + continue; + // find the merge that allows for the most edges to be removed. secondary ordering based on arbitrary id (for predictability) + var ms = this.rootMerges(i).sort(function (a, b) { return a.nEdges == b.nEdges ? a.id - b.id : a.nEdges - b.nEdges; }); + var m = ms[0]; + if (m.nEdges >= this.R) + continue; + this.merge(m.a, m.b, i); + return true; + } + }; + Configuration.prototype.nEdges = function (a, b) { + var inInt = a.incoming.intersection(b.incoming), outInt = a.outgoing.intersection(b.outgoing); + return this.R - inInt.count() - outInt.count(); + }; + Configuration.prototype.getGroupHierarchy = function (retargetedEdges) { + var _this = this; + var groups = []; + var root = {}; + toGroups(this.roots[0], root, groups); + var es = this.allEdges(); + es.forEach(function (e) { + var a = _this.modules[e.source]; + var b = _this.modules[e.target]; + retargetedEdges.push(new PowerEdge(typeof a.gid === "undefined" ? e.source : groups[a.gid], typeof b.gid === "undefined" ? e.target : groups[b.gid], e.type)); + }); + return groups; + }; + Configuration.prototype.allEdges = function () { + var es = []; + Configuration.getEdges(this.roots[0], es); + return es; + }; + Configuration.getEdges = function (modules, es) { + modules.forAll(function (m) { + m.getEdges(es); + Configuration.getEdges(m.children, es); + }); + }; + return Configuration; + })(); + powergraph.Configuration = Configuration; + function toGroups(modules, group, groups) { + modules.forAll(function (m) { + if (m.isLeaf()) { + if (!group.leaves) + group.leaves = []; + group.leaves.push(m.id); + } + else { + var g = group; + m.gid = groups.length; + if (!m.isIsland() || m.isPredefined()) { + g = { id: m.gid }; + if (m.isPredefined()) + // Apply original group properties + for (var prop in m.definition) + g[prop] = m.definition[prop]; + if (!group.groups) + group.groups = []; + group.groups.push(m.gid); + groups.push(g); + } + toGroups(m.children, g, groups); + } + }); + } + var Module = (function () { + function Module(id, outgoing, incoming, children, definition) { + if (outgoing === void 0) { outgoing = new LinkSets(); } + if (incoming === void 0) { incoming = new LinkSets(); } + if (children === void 0) { children = new ModuleSet(); } + this.id = id; + this.outgoing = outgoing; + this.incoming = incoming; + this.children = children; + this.definition = definition; + } + Module.prototype.getEdges = function (es) { + var _this = this; + this.outgoing.forAll(function (ms, edgetype) { + ms.forAll(function (target) { + es.push(new PowerEdge(_this.id, target.id, edgetype)); + }); + }); + }; + Module.prototype.isLeaf = function () { + return this.children.count() === 0; + }; + Module.prototype.isIsland = function () { + return this.outgoing.count() === 0 && this.incoming.count() === 0; + }; + Module.prototype.isPredefined = function () { + return typeof this.definition !== "undefined"; + }; + return Module; + })(); + powergraph.Module = Module; + function intersection(m, n) { + var i = {}; + for (var v in m) + if (v in n) + i[v] = m[v]; + return i; + } + var ModuleSet = (function () { + function ModuleSet() { + this.table = {}; + } + ModuleSet.prototype.count = function () { + return Object.keys(this.table).length; + }; + ModuleSet.prototype.intersection = function (other) { + var result = new ModuleSet(); + result.table = intersection(this.table, other.table); + return result; + }; + ModuleSet.prototype.intersectionCount = function (other) { + return this.intersection(other).count(); + }; + ModuleSet.prototype.contains = function (id) { + return id in this.table; + }; + ModuleSet.prototype.add = function (m) { + this.table[m.id] = m; + }; + ModuleSet.prototype.remove = function (m) { + delete this.table[m.id]; + }; + ModuleSet.prototype.forAll = function (f) { + for (var mid in this.table) { + f(this.table[mid]); + } + }; + ModuleSet.prototype.modules = function () { + var vs = []; + this.forAll(function (m) { + if (!m.isPredefined()) + vs.push(m); + }); + return vs; + }; + return ModuleSet; + })(); + powergraph.ModuleSet = ModuleSet; + var LinkSets = (function () { + function LinkSets() { + this.sets = {}; + this.n = 0; + } + LinkSets.prototype.count = function () { + return this.n; + }; + LinkSets.prototype.contains = function (id) { + var result = false; + this.forAllModules(function (m) { + if (!result && m.id == id) { + result = true; + } + }); + return result; + }; + LinkSets.prototype.add = function (linktype, m) { + var s = linktype in this.sets ? this.sets[linktype] : this.sets[linktype] = new ModuleSet(); + s.add(m); + ++this.n; + }; + LinkSets.prototype.remove = function (linktype, m) { + var ms = this.sets[linktype]; + ms.remove(m); + if (ms.count() === 0) { + delete this.sets[linktype]; + } + --this.n; + }; + LinkSets.prototype.forAll = function (f) { + for (var linktype in this.sets) { + f(this.sets[linktype], linktype); + } + }; + LinkSets.prototype.forAllModules = function (f) { + this.forAll(function (ms, lt) { return ms.forAll(f); }); + }; + LinkSets.prototype.intersection = function (other) { + var result = new LinkSets(); + this.forAll(function (ms, lt) { + if (lt in other.sets) { + var i = ms.intersection(other.sets[lt]), n = i.count(); + if (n > 0) { + result.sets[lt] = i; + result.n += n; + } + } + }); + return result; + }; + return LinkSets; + })(); + powergraph.LinkSets = LinkSets; + function intersectionCount(m, n) { + return Object.keys(intersection(m, n)).length; + } + function getGroups(nodes, links, la, rootGroup) { + var n = nodes.length, c = new powergraph.Configuration(n, links, la, rootGroup); + while (c.greedyMerge()) + ; + var powerEdges = []; + var g = c.getGroupHierarchy(powerEdges); + powerEdges.forEach(function (e) { + var f = function (end) { + var g = e[end]; + if (typeof g == "number") + e[end] = nodes[g]; + }; + f("source"); + f("target"); + }); + return { groups: g, powerEdges: powerEdges }; + } + powergraph.getGroups = getGroups; + })(powergraph = cola.powergraph || (cola.powergraph = {})); +})(cola || (cola = {})); +/** + * @module cola + */ +var cola; +(function (cola) { + // compute the size of the union of two sets a and b + function unionCount(a, b) { + var u = {}; + for (var i in a) + u[i] = {}; + for (var i in b) + u[i] = {}; + return Object.keys(u).length; + } + // compute the size of the intersection of two sets a and b + function intersectionCount(a, b) { + var n = 0; + for (var i in a) + if (typeof b[i] !== 'undefined') + ++n; + return n; + } + function getNeighbours(links, la) { + var neighbours = {}; + var addNeighbours = function (u, v) { + if (typeof neighbours[u] === 'undefined') + neighbours[u] = {}; + neighbours[u][v] = {}; + }; + links.forEach(function (e) { + var u = la.getSourceIndex(e), v = la.getTargetIndex(e); + addNeighbours(u, v); + addNeighbours(v, u); + }); + return neighbours; + } + // modify the lengths of the specified links by the result of function f weighted by w + function computeLinkLengths(links, w, f, la) { + var neighbours = getNeighbours(links, la); + links.forEach(function (l) { + var a = neighbours[la.getSourceIndex(l)]; + var b = neighbours[la.getTargetIndex(l)]; + la.setLength(l, 1 + w * f(a, b)); + }); + } + /** modify the specified link lengths based on the symmetric difference of their neighbours + * @class symmetricDiffLinkLengths + */ + function symmetricDiffLinkLengths(links, la, w) { + if (w === void 0) { w = 1; } + computeLinkLengths(links, w, function (a, b) { return Math.sqrt(unionCount(a, b) - intersectionCount(a, b)); }, la); + } + cola.symmetricDiffLinkLengths = symmetricDiffLinkLengths; + /** modify the specified links lengths based on the jaccard difference between their neighbours + * @class jaccardLinkLengths + */ + function jaccardLinkLengths(links, la, w) { + if (w === void 0) { w = 1; } + computeLinkLengths(links, w, function (a, b) { + return Math.min(Object.keys(a).length, Object.keys(b).length) < 1.1 ? 0 : intersectionCount(a, b) / unionCount(a, b); + }, la); + } + cola.jaccardLinkLengths = jaccardLinkLengths; + /** generate separation constraints for all edges unless both their source and sink are in the same strongly connected component + * @class generateDirectedEdgeConstraints + */ + function generateDirectedEdgeConstraints(n, links, axis, la) { + var components = stronglyConnectedComponents(n, links, la); + var nodes = {}; + components.forEach(function (c, i) { + return c.forEach(function (v) { return nodes[v] = i; }); + }); + var constraints = []; + links.forEach(function (l) { + var ui = la.getSourceIndex(l), vi = la.getTargetIndex(l), u = nodes[ui], v = nodes[vi]; + if (u !== v) { + constraints.push({ + axis: axis, + left: ui, + right: vi, + gap: la.getMinSeparation(l) + }); + } + }); + return constraints; + } + cola.generateDirectedEdgeConstraints = generateDirectedEdgeConstraints; + /** + * Tarjan's strongly connected components algorithm for directed graphs + * returns an array of arrays of node indicies in each of the strongly connected components. + * a vertex not in a SCC of two or more nodes is it's own SCC. + * adaptation of https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm + */ + function stronglyConnectedComponents(numVertices, edges, la) { + var nodes = []; + var index = 0; + var stack = []; + var components = []; + function strongConnect(v) { + // Set the depth index for v to the smallest unused index + v.index = v.lowlink = index++; + stack.push(v); + v.onStack = true; + // Consider successors of v + for (var _i = 0, _a = v.out; _i < _a.length; _i++) { + var w = _a[_i]; + if (typeof w.index === 'undefined') { + // Successor w has not yet been visited; recurse on it + strongConnect(w); + v.lowlink = Math.min(v.lowlink, w.lowlink); + } + else if (w.onStack) { + // Successor w is in stack S and hence in the current SCC + v.lowlink = Math.min(v.lowlink, w.index); + } + } + // If v is a root node, pop the stack and generate an SCC + if (v.lowlink === v.index) { + // start a new strongly connected component + var component = []; + while (stack.length) { + w = stack.pop(); + w.onStack = false; + //add w to current strongly connected component + component.push(w); + if (w === v) + break; + } + // output the current strongly connected component + components.push(component.map(function (v) { return v.id; })); + } + } + for (var i = 0; i < numVertices; i++) { + nodes.push({ id: i, out: [] }); + } + for (var _i = 0; _i < edges.length; _i++) { + var e = edges[_i]; + var v_1 = nodes[la.getSourceIndex(e)], w = nodes[la.getTargetIndex(e)]; + v_1.out.push(w); + } + for (var _a = 0; _a < nodes.length; _a++) { + var v = nodes[_a]; + if (typeof v.index === 'undefined') + strongConnect(v); + } + return components; + } + cola.stronglyConnectedComponents = stronglyConnectedComponents; +})(cola || (cola = {})); +var PairingHeap = (function () { + // from: https://gist.github.com/nervoussystem + //{elem:object, subheaps:[array of heaps]} + function PairingHeap(elem) { + this.elem = elem; + this.subheaps = []; + } + PairingHeap.prototype.toString = function (selector) { + var str = "", needComma = false; + for (var i = 0; i < this.subheaps.length; ++i) { + var subheap = this.subheaps[i]; + if (!subheap.elem) { + needComma = false; + continue; + } + if (needComma) { + str = str + ","; + } + str = str + subheap.toString(selector); + needComma = true; + } + if (str !== "") { + str = "(" + str + ")"; + } + return (this.elem ? selector(this.elem) : "") + str; + }; + PairingHeap.prototype.forEach = function (f) { + if (!this.empty()) { + f(this.elem, this); + this.subheaps.forEach(function (s) { return s.forEach(f); }); + } + }; + PairingHeap.prototype.count = function () { + return this.empty() ? 0 : 1 + this.subheaps.reduce(function (n, h) { + return n + h.count(); + }, 0); + }; + PairingHeap.prototype.min = function () { + return this.elem; + }; + PairingHeap.prototype.empty = function () { + return this.elem == null; + }; + PairingHeap.prototype.contains = function (h) { + if (this === h) + return true; + for (var i = 0; i < this.subheaps.length; i++) { + if (this.subheaps[i].contains(h)) + return true; + } + return false; + }; + PairingHeap.prototype.isHeap = function (lessThan) { + var _this = this; + return this.subheaps.every(function (h) { return lessThan(_this.elem, h.elem) && h.isHeap(lessThan); }); + }; + PairingHeap.prototype.insert = function (obj, lessThan) { + return this.merge(new PairingHeap(obj), lessThan); + }; + PairingHeap.prototype.merge = function (heap2, lessThan) { + if (this.empty()) + return heap2; + else if (heap2.empty()) + return this; + else if (lessThan(this.elem, heap2.elem)) { + this.subheaps.push(heap2); + return this; + } + else { + heap2.subheaps.push(this); + return heap2; + } + }; + PairingHeap.prototype.removeMin = function (lessThan) { + if (this.empty()) + return null; + else + return this.mergePairs(lessThan); + }; + PairingHeap.prototype.mergePairs = function (lessThan) { + if (this.subheaps.length == 0) + return new PairingHeap(null); + else if (this.subheaps.length == 1) { + return this.subheaps[0]; + } + else { + var firstPair = this.subheaps.pop().merge(this.subheaps.pop(), lessThan); + var remaining = this.mergePairs(lessThan); + return firstPair.merge(remaining, lessThan); + } + }; + PairingHeap.prototype.decreaseKey = function (subheap, newValue, setHeapNode, lessThan) { + var newHeap = subheap.removeMin(lessThan); + //reassign subheap values to preserve tree + subheap.elem = newHeap.elem; + subheap.subheaps = newHeap.subheaps; + if (setHeapNode !== null && newHeap.elem !== null) { + setHeapNode(subheap.elem, subheap); + } + var pairingNode = new PairingHeap(newValue); + if (setHeapNode !== null) { + setHeapNode(newValue, pairingNode); + } + return this.merge(pairingNode, lessThan); + }; + return PairingHeap; +})(); +/** + * @class PriorityQueue a min priority queue backed by a pairing heap + */ +var PriorityQueue = (function () { + function PriorityQueue(lessThan) { + this.lessThan = lessThan; + } + /** + * @method top + * @return the top element (the min element as defined by lessThan) + */ + PriorityQueue.prototype.top = function () { + if (this.empty()) { + return null; + } + return this.root.elem; + }; + /** + * @method push + * put things on the heap + */ + PriorityQueue.prototype.push = function () { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i - 0] = arguments[_i]; + } + var pairingNode; + for (var i = 0, arg; arg = args[i]; ++i) { + pairingNode = new PairingHeap(arg); + this.root = this.empty() ? + pairingNode : this.root.merge(pairingNode, this.lessThan); + } + return pairingNode; + }; + /** + * @method empty + * @return true if no more elements in queue + */ + PriorityQueue.prototype.empty = function () { + return !this.root || !this.root.elem; + }; + /** + * @method isHeap check heap condition (for testing) + * @return true if queue is in valid state + */ + PriorityQueue.prototype.isHeap = function () { + return this.root.isHeap(this.lessThan); + }; + /** + * @method forEach apply f to each element of the queue + * @param f function to apply + */ + PriorityQueue.prototype.forEach = function (f) { + this.root.forEach(f); + }; + /** + * @method pop remove and return the min element from the queue + */ + PriorityQueue.prototype.pop = function () { + if (this.empty()) { + return null; + } + var obj = this.root.min(); + this.root = this.root.removeMin(this.lessThan); + return obj; + }; + /** + * @method reduceKey reduce the key value of the specified heap node + */ + PriorityQueue.prototype.reduceKey = function (heapNode, newKey, setHeapNode) { + if (setHeapNode === void 0) { setHeapNode = null; } + this.root = this.root.decreaseKey(heapNode, newKey, setHeapNode, this.lessThan); + }; + PriorityQueue.prototype.toString = function (selector) { + return this.root.toString(selector); + }; + /** + * @method count + * @return number of elements in queue + */ + PriorityQueue.prototype.count = function () { + return this.root.count(); + }; + return PriorityQueue; +})(); +///<reference path="pqueue.ts"/> +/** + * @module shortestpaths + */ +var cola; +(function (cola) { + var shortestpaths; + (function (shortestpaths) { + var Neighbour = (function () { + function Neighbour(id, distance) { + this.id = id; + this.distance = distance; + } + return Neighbour; + })(); + var Node = (function () { + function Node(id) { + this.id = id; + this.neighbours = []; + } + return Node; + })(); + var QueueEntry = (function () { + function QueueEntry(node, prev, d) { + this.node = node; + this.prev = prev; + this.d = d; + } + return QueueEntry; + })(); + /** + * calculates all-pairs shortest paths or shortest paths from a single node + * @class Calculator + * @constructor + * @param n {number} number of nodes + * @param es {Edge[]} array of edges + */ + var Calculator = (function () { + function Calculator(n, es, getSourceIndex, getTargetIndex, getLength) { + this.n = n; + this.es = es; + this.neighbours = new Array(this.n); + var i = this.n; + while (i--) + this.neighbours[i] = new Node(i); + i = this.es.length; + while (i--) { + var e = this.es[i]; + var u = getSourceIndex(e), v = getTargetIndex(e); + var d = getLength(e); + this.neighbours[u].neighbours.push(new Neighbour(v, d)); + this.neighbours[v].neighbours.push(new Neighbour(u, d)); + } + } + /** + * compute shortest paths for graph over n nodes with edges an array of source/target pairs + * edges may optionally have a length attribute. 1 is the default. + * Uses Johnson's algorithm. + * + * @method DistanceMatrix + * @return the distance matrix + */ + Calculator.prototype.DistanceMatrix = function () { + var D = new Array(this.n); + for (var i = 0; i < this.n; ++i) { + D[i] = this.dijkstraNeighbours(i); + } + return D; + }; + /** + * get shortest paths from a specified start node + * @method DistancesFromNode + * @param start node index + * @return array of path lengths + */ + Calculator.prototype.DistancesFromNode = function (start) { + return this.dijkstraNeighbours(start); + }; + Calculator.prototype.PathFromNodeToNode = function (start, end) { + return this.dijkstraNeighbours(start, end); + }; + // find shortest path from start to end, with the opportunity at + // each edge traversal to compute a custom cost based on the + // previous edge. For example, to penalise bends. + Calculator.prototype.PathFromNodeToNodeWithPrevCost = function (start, end, prevCost) { + var q = new PriorityQueue(function (a, b) { return a.d <= b.d; }), u = this.neighbours[start], qu = new QueueEntry(u, null, 0), visitedFrom = {}; + q.push(qu); + while (!q.empty()) { + qu = q.pop(); + u = qu.node; + if (u.id === end) { + break; + } + var i = u.neighbours.length; + while (i--) { + var neighbour = u.neighbours[i], v = this.neighbours[neighbour.id]; + // don't double back + if (qu.prev && v.id === qu.prev.node.id) + continue; + // don't retraverse an edge if it has already been explored + // from a lower cost route + var viduid = v.id + ',' + u.id; + if (viduid in visitedFrom && visitedFrom[viduid] <= qu.d) + continue; + var cc = qu.prev ? prevCost(qu.prev.node.id, u.id, v.id) : 0, t = qu.d + neighbour.distance + cc; + // store cost of this traversal + visitedFrom[viduid] = t; + q.push(new QueueEntry(v, qu, t)); + } + } + var path = []; + while (qu.prev) { + qu = qu.prev; + path.push(qu.node.id); + } + return path; + }; + Calculator.prototype.dijkstraNeighbours = function (start, dest) { + if (dest === void 0) { dest = -1; } + var q = new PriorityQueue(function (a, b) { return a.d <= b.d; }), i = this.neighbours.length, d = new Array(i); + while (i--) { + var node = this.neighbours[i]; + node.d = i === start ? 0 : Number.POSITIVE_INFINITY; + node.q = q.push(node); + } + while (!q.empty()) { + // console.log(q.toString(function (u) { return u.id + "=" + (u.d === Number.POSITIVE_INFINITY ? "\u221E" : u.d.toFixed(2) )})); + var u = q.pop(); + d[u.id] = u.d; + if (u.id === dest) { + var path = []; + var v = u; + while (typeof v.prev !== 'undefined') { + path.push(v.prev.id); + v = v.prev; + } + return path; + } + i = u.neighbours.length; + while (i--) { + var neighbour = u.neighbours[i]; + var v = this.neighbours[neighbour.id]; + var t = u.d + neighbour.distance; + if (u.d !== Number.MAX_VALUE && v.d > t) { + v.d = t; + v.prev = u; + q.reduceKey(v.q, v, function (e, q) { return e.q = q; }); + } + } + } + return d; + }; + return Calculator; + })(); + shortestpaths.Calculator = Calculator; + })(shortestpaths = cola.shortestpaths || (cola.shortestpaths = {})); +})(cola || (cola = {})); +///<reference path="handledisconnected.ts"/> +///<reference path="geom.ts"/> +///<reference path="descent.ts"/> +///<reference path="powergraph.ts"/> +///<reference path="linklengths.ts"/> +///<reference path="shortestpaths.ts"/> +/** + * @module cola + */ +var cola; +(function (cola) { + /** + * The layout process fires three events: + * - start: layout iterations started + * - tick: fired once per iteration, listen to this to animate + * - end: layout converged, you might like to zoom-to-fit or something at notification of this event + */ + (function (EventType) { + EventType[EventType["start"] = 0] = "start"; + EventType[EventType["tick"] = 1] = "tick"; + EventType[EventType["end"] = 2] = "end"; + })(cola.EventType || (cola.EventType = {})); + var EventType = cola.EventType; + ; + /** + * Main interface to cola layout. + * @class Layout + */ + var Layout = (function () { + function Layout() { + var _this = this; + this._canvasSize = [1, 1]; + this._linkDistance = 20; + this._defaultNodeSize = 10; + this._linkLengthCalculator = null; + this._linkType = null; + this._avoidOverlaps = false; + this._handleDisconnected = true; + this._running = false; + this._nodes = []; + this._groups = []; + this._rootGroup = null; + this._links = []; + this._constraints = []; + this._distanceMatrix = null; + this._descent = null; + this._directedLinkConstraints = null; + this._threshold = 0.01; + this._visibilityGraph = null; + this._groupCompactness = 1e-6; + // sub-class and override this property to replace with a more sophisticated eventing mechanism + this.event = null; + this.linkAccessor = { + getSourceIndex: Layout.getSourceIndex, + getTargetIndex: Layout.getTargetIndex, + setLength: Layout.setLinkLength, + getType: function (l) { return typeof _this._linkType === "function" ? _this._linkType(l) : 0; } + }; + } + // subscribe a listener to an event + // sub-class and override this method to replace with a more sophisticated eventing mechanism + Layout.prototype.on = function (e, listener) { + // override me! + if (!this.event) + this.event = {}; + if (typeof e === 'string') { + this.event[EventType[e]] = listener; + } + else { + this.event[e] = listener; + } + return this; + }; + // a function that is notified of events like "tick" + // sub-class and override this method to replace with a more sophisticated eventing mechanism + Layout.prototype.trigger = function (e) { + if (this.event && typeof this.event[e.type] !== 'undefined') { + this.event[e.type](e); + } + }; + // a function that kicks off the iteration tick loop + // it calls tick() repeatedly until tick returns true (is converged) + // subclass and override it with something fancier (e.g. dispatch tick on a timer) + Layout.prototype.kick = function () { + while (!this.tick()) + ; + }; + /** + * iterate the layout. Returns true when layout converged. + */ + Layout.prototype.tick = function () { + if (this._alpha < this._threshold) { + this._running = false; + this.trigger({ type: EventType.end, alpha: this._alpha = 0, stress: this._lastStress }); + return true; + } + var n = this._nodes.length, m = this._links.length; + var o, i; + this._descent.locks.clear(); + for (i = 0; i < n; ++i) { + o = this._nodes[i]; + if (o.fixed) { + if (typeof o.px === 'undefined' || typeof o.py === 'undefined') { + o.px = o.x; + o.py = o.y; + } + var p = [o.px, o.py]; + this._descent.locks.add(i, p); + } + } + var s1 = this._descent.rungeKutta(); + //var s1 = descent.reduceStress(); + if (s1 === 0) { + this._alpha = 0; + } + else if (typeof this._lastStress !== 'undefined') { + this._alpha = s1; //Math.abs(Math.abs(this._lastStress / s1) - 1); + } + this._lastStress = s1; + this.updateNodePositions(); + this.trigger({ type: EventType.tick, alpha: this._alpha, stress: this._lastStress }); + return false; + }; + // copy positions out of descent instance into each of the nodes' center coords + Layout.prototype.updateNodePositions = function () { + var x = this._descent.x[0], y = this._descent.x[1]; + var o, i = this._nodes.length; + while (i--) { + o = this._nodes[i]; + o.x = x[i]; + o.y = y[i]; + } + }; + Layout.prototype.nodes = function (v) { + if (!v) { + if (this._nodes.length === 0 && this._links.length > 0) { + // if we have links but no nodes, create the nodes array now with empty objects for the links to point at. + // in this case the links are expected to be numeric indices for nodes in the range 0..n-1 where n is the number of nodes + var n = 0; + this._links.forEach(function (l) { + n = Math.max(n, l.source, l.target); + }); + this._nodes = new Array(++n); + for (var i = 0; i < n; ++i) { + this._nodes[i] = {}; + } + } + return this._nodes; + } + this._nodes = v; + return this; + }; + Layout.prototype.groups = function (x) { + var _this = this; + if (!x) + return this._groups; + this._groups = x; + this._rootGroup = {}; + this._groups.forEach(function (g) { + if (typeof g.padding === "undefined") + g.padding = 1; + if (typeof g.leaves !== "undefined") + g.leaves.forEach(function (v, i) { (g.leaves[i] = _this._nodes[v]).parent = g; }); + if (typeof g.groups !== "undefined") + g.groups.forEach(function (gi, i) { (g.groups[i] = _this._groups[gi]).parent = g; }); + }); + this._rootGroup.leaves = this._nodes.filter(function (v) { return typeof v.parent === 'undefined'; }); + this._rootGroup.groups = this._groups.filter(function (g) { return typeof g.parent === 'undefined'; }); + return this; + }; + Layout.prototype.powerGraphGroups = function (f) { + var g = cola.powergraph.getGroups(this._nodes, this._links, this.linkAccessor, this._rootGroup); + this.groups(g.groups); + f(g); + return this; + }; + Layout.prototype.avoidOverlaps = function (v) { + if (!arguments.length) + return this._avoidOverlaps; + this._avoidOverlaps = v; + return this; + }; + Layout.prototype.handleDisconnected = function (v) { + if (!arguments.length) + return this._handleDisconnected; + this._handleDisconnected = v; + return this; + }; + /** + * causes constraints to be generated such that directed graphs are laid out either from left-to-right or top-to-bottom. + * a separation constraint is generated in the selected axis for each edge that is not involved in a cycle (part of a strongly connected component) + * @param axis {string} 'x' for left-to-right, 'y' for top-to-bottom + * @param minSeparation {number|link=>number} either a number specifying a minimum spacing required across all links or a function to return the minimum spacing for each link + */ + Layout.prototype.flowLayout = function (axis, minSeparation) { + if (!arguments.length) + axis = 'y'; + this._directedLinkConstraints = { + axis: axis, + getMinSeparation: typeof minSeparation === 'number' ? function () { return minSeparation; } : minSeparation + }; + return this; + }; + Layout.prototype.links = function (x) { + if (!arguments.length) + return this._links; + this._links = x; + return this; + }; + Layout.prototype.constraints = function (c) { + if (!arguments.length) + return this._constraints; + this._constraints = c; + return this; + }; + Layout.prototype.distanceMatrix = function (d) { + if (!arguments.length) + return this._distanceMatrix; + this._distanceMatrix = d; + return this; + }; + Layout.prototype.size = function (x) { + if (!x) + return this._canvasSize; + this._canvasSize = x; + return this; + }; + Layout.prototype.defaultNodeSize = function (x) { + if (!x) + return this._defaultNodeSize; + this._defaultNodeSize = x; + return this; + }; + Layout.prototype.groupCompactness = function (x) { + if (!x) + return this._groupCompactness; + this._groupCompactness = x; + return this; + }; + Layout.prototype.linkDistance = function (x) { + if (!x) { + return this._linkDistance; + } + this._linkDistance = typeof x === "function" ? x : +x; + this._linkLengthCalculator = null; + return this; + }; + Layout.prototype.linkType = function (f) { + this._linkType = f; + return this; + }; + Layout.prototype.convergenceThreshold = function (x) { + if (!x) + return this._threshold; + this._threshold = typeof x === "function" ? x : +x; + return this; + }; + Layout.prototype.alpha = function (x) { + if (!arguments.length) + return this._alpha; + else { + x = +x; + if (this._alpha) { + if (x > 0) + this._alpha = x; // we might keep it hot + else + this._alpha = 0; // or, next tick will dispatch "end" + } + else if (x > 0) { + if (!this._running) { + this._running = true; + this.trigger({ type: EventType.start, alpha: this._alpha = x }); + this.kick(); + } + } + return this; + } + }; + Layout.prototype.getLinkLength = function (link) { + return typeof this._linkDistance === "function" ? +(this._linkDistance(link)) : this._linkDistance; + }; + Layout.setLinkLength = function (link, length) { + link.length = length; + }; + Layout.prototype.getLinkType = function (link) { + return typeof this._linkType === "function" ? this._linkType(link) : 0; + }; + /** + * compute an ideal length for each link based on the graph structure around that link. + * you can use this (for example) to create extra space around hub-nodes in dense graphs. + * In particular this calculation is based on the "symmetric difference" in the neighbour sets of the source and target: + * i.e. if neighbours of source is a and neighbours of target are b then calculation is: sqrt(|a union b| - |a intersection b|) + * Actual computation based on inspection of link structure occurs in start(), so links themselves + * don't have to have been assigned before invoking this function. + * @param {number} [idealLength] the base length for an edge when its source and start have no other common neighbours (e.g. 40) + * @param {number} [w] a multiplier for the effect of the length adjustment (e.g. 0.7) + */ + Layout.prototype.symmetricDiffLinkLengths = function (idealLength, w) { + var _this = this; + if (w === void 0) { w = 1; } + this.linkDistance(function (l) { return idealLength * l.length; }); + this._linkLengthCalculator = function () { return cola.symmetricDiffLinkLengths(_this._links, _this.linkAccessor, w); }; + return this; + }; + /** + * compute an ideal length for each link based on the graph structure around that link. + * you can use this (for example) to create extra space around hub-nodes in dense graphs. + * In particular this calculation is based on the "symmetric difference" in the neighbour sets of the source and target: + * i.e. if neighbours of source is a and neighbours of target are b then calculation is: |a intersection b|/|a union b| + * Actual computation based on inspection of link structure occurs in start(), so links themselves + * don't have to have been assigned before invoking this function. + * @param {number} [idealLength] the base length for an edge when its source and start have no other common neighbours (e.g. 40) + * @param {number} [w] a multiplier for the effect of the length adjustment (e.g. 0.7) + */ + Layout.prototype.jaccardLinkLengths = function (idealLength, w) { + var _this = this; + if (w === void 0) { w = 1; } + this.linkDistance(function (l) { return idealLength * l.length; }); + this._linkLengthCalculator = function () { return cola.jaccardLinkLengths(_this._links, _this.linkAccessor, w); }; + return this; + }; + /** + * start the layout process + * @method start + * @param {number} [initialUnconstrainedIterations=0] unconstrained initial layout iterations + * @param {number} [initialUserConstraintIterations=0] initial layout iterations with user-specified constraints + * @param {number} [initialAllConstraintsIterations=0] initial layout iterations with all constraints including non-overlap + * @param {number} [gridSnapIterations=0] iterations of "grid snap", which pulls nodes towards grid cell centers - grid of size node[0].width - only really makes sense if all nodes have the same width and height + * @param [keepRunning=true] keep iterating asynchronously via the tick method + */ + Layout.prototype.start = function (initialUnconstrainedIterations, initialUserConstraintIterations, initialAllConstraintsIterations, gridSnapIterations, keepRunning) { + var _this = this; + if (initialUnconstrainedIterations === void 0) { initialUnconstrainedIterations = 0; } + if (initialUserConstraintIterations === void 0) { initialUserConstraintIterations = 0; } + if (initialAllConstraintsIterations === void 0) { initialAllConstraintsIterations = 0; } + if (gridSnapIterations === void 0) { gridSnapIterations = 0; } + if (keepRunning === void 0) { keepRunning = true; } + var i, j, n = this.nodes().length, N = n + 2 * this._groups.length, m = this._links.length, w = this._canvasSize[0], h = this._canvasSize[1]; + if (this._linkLengthCalculator) + this._linkLengthCalculator(); + var x = new Array(N), y = new Array(N); + var G = null; + var ao = this._avoidOverlaps; + this._nodes.forEach(function (v, i) { + v.index = i; + if (typeof v.x === 'undefined') { + v.x = w / 2, v.y = h / 2; + } + x[i] = v.x, y[i] = v.y; + }); + //should we do this to clearly label groups? + //this._groups.forEach((g, i) => g.groupIndex = i); + var distances; + if (this._distanceMatrix) { + // use the user specified distanceMatrix + distances = this._distanceMatrix; + } + else { + // construct an n X n distance matrix based on shortest paths through graph (with respect to edge.length). + distances = (new cola.shortestpaths.Calculator(N, this._links, Layout.getSourceIndex, Layout.getTargetIndex, function (l) { return _this.getLinkLength(l); })).DistanceMatrix(); + // G is a square matrix with G[i][j] = 1 iff there exists an edge between node i and node j + // otherwise 2. ( + G = cola.Descent.createSquareMatrix(N, function () { return 2; }); + this._links.forEach(function (l) { + if (typeof l.source == "number") + l.source = _this._nodes[l.source]; + if (typeof l.target == "number") + l.target = _this._nodes[l.target]; + }); + this._links.forEach(function (e) { + var u = Layout.getSourceIndex(e), v = Layout.getTargetIndex(e); + G[u][v] = G[v][u] = e.weight || 1; + }); + } + var D = cola.Descent.createSquareMatrix(N, function (i, j) { + return distances[i][j]; + }); + if (this._rootGroup && typeof this._rootGroup.groups !== 'undefined') { + var i = n; + var addAttraction = function (i, j, strength, idealDistance) { + G[i][j] = G[j][i] = strength; + D[i][j] = D[j][i] = idealDistance; + }; + this._groups.forEach(function (g) { + addAttraction(i, i + 1, _this._groupCompactness, 0.1); + // todo: add terms here attracting children of the group to the group dummy nodes + //if (typeof g.leaves !== 'undefined') + // g.leaves.forEach(l => { + // addAttraction(l.index, i, 1e-4, 0.1); + // addAttraction(l.index, i + 1, 1e-4, 0.1); + // }); + //if (typeof g.groups !== 'undefined') + // g.groups.forEach(g => { + // var gid = n + g.groupIndex * 2; + // addAttraction(gid, i, 0.1, 0.1); + // addAttraction(gid + 1, i, 0.1, 0.1); + // addAttraction(gid, i + 1, 0.1, 0.1); + // addAttraction(gid + 1, i + 1, 0.1, 0.1); + // }); + x[i] = 0, y[i++] = 0; + x[i] = 0, y[i++] = 0; + }); + } + else + this._rootGroup = { leaves: this._nodes, groups: [] }; + var curConstraints = this._constraints || []; + if (this._directedLinkConstraints) { + this.linkAccessor.getMinSeparation = this._directedLinkConstraints.getMinSeparation; + curConstraints = curConstraints.concat(cola.generateDirectedEdgeConstraints(n, this._links, this._directedLinkConstraints.axis, (this.linkAccessor))); + } + this.avoidOverlaps(false); + this._descent = new cola.Descent([x, y], D); + this._descent.locks.clear(); + for (var i = 0; i < n; ++i) { + var o = this._nodes[i]; + if (o.fixed) { + o.px = o.x; + o.py = o.y; + var p = [o.x, o.y]; + this._descent.locks.add(i, p); + } + } + this._descent.threshold = this._threshold; + // apply initialIterations without user constraints or nonoverlap constraints + this._descent.run(initialUnconstrainedIterations); + // apply initialIterations with user constraints but no nonoverlap constraints + if (curConstraints.length > 0) + this._descent.project = new cola.vpsc.Projection(this._nodes, this._groups, this._rootGroup, curConstraints).projectFunctions(); + this._descent.run(initialUserConstraintIterations); + this.separateOverlappingComponents(w, h); + // subsequent iterations will apply all constraints + this.avoidOverlaps(ao); + if (ao) { + this._nodes.forEach(function (v, i) { v.x = x[i], v.y = y[i]; }); + this._descent.project = new cola.vpsc.Projection(this._nodes, this._groups, this._rootGroup, curConstraints, true).projectFunctions(); + this._nodes.forEach(function (v, i) { x[i] = v.x, y[i] = v.y; }); + } + // allow not immediately connected nodes to relax apart (p-stress) + this._descent.G = G; + this._descent.run(initialAllConstraintsIterations); + if (gridSnapIterations) { + this._descent.snapStrength = 1000; + this._descent.snapGridSize = this._nodes[0].width; + this._descent.numGridSnapNodes = n; + this._descent.scaleSnapByMaxH = n != N; // if we have groups then need to scale hessian so grid forces still apply + var G0 = cola.Descent.createSquareMatrix(N, function (i, j) { + if (i >= n || j >= n) + return G[i][j]; + return 0; + }); + this._descent.G = G0; + this._descent.run(gridSnapIterations); + } + this.updateNodePositions(); + this.separateOverlappingComponents(w, h); + return keepRunning ? this.resume() : this; + }; + // recalculate nodes position for disconnected graphs + Layout.prototype.separateOverlappingComponents = function (width, height) { + var _this = this; + // recalculate nodes position for disconnected graphs + if (!this._distanceMatrix && this._handleDisconnected) { + var x = this._descent.x[0], y = this._descent.x[1]; + this._nodes.forEach(function (v, i) { v.x = x[i], v.y = y[i]; }); + var graphs = cola.separateGraphs(this._nodes, this._links); + cola.applyPacking(graphs, width, height, this._defaultNodeSize); + this._nodes.forEach(function (v, i) { + _this._descent.x[0][i] = v.x, _this._descent.x[1][i] = v.y; + if (v.bounds) { + v.bounds.setXCentre(v.x); + v.bounds.setYCentre(v.y); + } + }); + } + }; + Layout.prototype.resume = function () { + return this.alpha(0.1); + }; + Layout.prototype.stop = function () { + return this.alpha(0); + }; + /// find a visibility graph over the set of nodes. assumes all nodes have a + /// bounds property (a rectangle) and that no pair of bounds overlaps. + Layout.prototype.prepareEdgeRouting = function (nodeMargin) { + if (nodeMargin === void 0) { nodeMargin = 0; } + this._visibilityGraph = new cola.geom.TangentVisibilityGraph(this._nodes.map(function (v) { + return v.bounds.inflate(-nodeMargin).vertices(); + })); + }; + /// find a route avoiding node bounds for the given edge. + /// assumes the visibility graph has been created (by prepareEdgeRouting method) + /// and also assumes that nodes have an index property giving their position in the + /// node array. This index property is created by the start() method. + Layout.prototype.routeEdge = function (edge, draw) { + var lineData = []; + //if (d.source.id === 10 && d.target.id === 11) { + // debugger; + //} + var vg2 = new cola.geom.TangentVisibilityGraph(this._visibilityGraph.P, { V: this._visibilityGraph.V, E: this._visibilityGraph.E }), port1 = { x: edge.source.x, y: edge.source.y }, port2 = { x: edge.target.x, y: edge.target.y }, start = vg2.addPoint(port1, edge.source.index), end = vg2.addPoint(port2, edge.target.index); + vg2.addEdgeIfVisible(port1, port2, edge.source.index, edge.target.index); + if (typeof draw !== 'undefined') { + draw(vg2); + } + var sourceInd = function (e) { return e.source.id; }, targetInd = function (e) { return e.target.id; }, length = function (e) { return e.length(); }, spCalc = new cola.shortestpaths.Calculator(vg2.V.length, vg2.E, sourceInd, targetInd, length), shortestPath = spCalc.PathFromNodeToNode(start.id, end.id); + if (shortestPath.length === 1 || shortestPath.length === vg2.V.length) { + var route = cola.vpsc.makeEdgeBetween(edge.source.innerBounds, edge.target.innerBounds, 5); + lineData = [route.sourceIntersection, route.arrowStart]; + } + else { + var n = shortestPath.length - 2, p = vg2.V[shortestPath[n]].p, q = vg2.V[shortestPath[0]].p, lineData = [edge.source.innerBounds.rayIntersection(p.x, p.y)]; + for (var i = n; i >= 0; --i) + lineData.push(vg2.V[shortestPath[i]].p); + lineData.push(cola.vpsc.makeEdgeTo(q, edge.target.innerBounds, 5)); + } + //lineData.forEach((v, i) => { + // if (i > 0) { + // var u = lineData[i - 1]; + // this._nodes.forEach(function (node) { + // if (node.id === getSourceIndex(d) || node.id === getTargetIndex(d)) return; + // var ints = node.innerBounds.lineIntersections(u.x, u.y, v.x, v.y); + // if (ints.length > 0) { + // debugger; + // } + // }) + // } + //}) + return lineData; + }; + //The link source and target may be just a node index, or they may be references to nodes themselves. + Layout.getSourceIndex = function (e) { + return typeof e.source === 'number' ? e.source : e.source.index; + }; + //The link source and target may be just a node index, or they may be references to nodes themselves. + Layout.getTargetIndex = function (e) { + return typeof e.target === 'number' ? e.target : e.target.index; + }; + // Get a string ID for a given link. + Layout.linkId = function (e) { + return Layout.getSourceIndex(e) + "-" + Layout.getTargetIndex(e); + }; + // The fixed property has three bits: + // Bit 1 can be set externally (e.g., d.fixed = true) and show persist. + // Bit 2 stores the dragging state, from mousedown to mouseup. + // Bit 3 stores the hover state, from mouseover to mouseout. + // Dragend is a special case: it also clears the hover state. + Layout.dragStart = function (d) { + d.fixed |= 2; // set bit 2 + d.px = d.x, d.py = d.y; // set velocity to zero + }; + Layout.dragEnd = function (d) { + d.fixed &= ~6; // unset bits 2 and 3 + //d.fixed = 0; + }; + Layout.mouseOver = function (d) { + d.fixed |= 4; // set bit 3 + d.px = d.x, d.py = d.y; // set velocity to zero + }; + Layout.mouseOut = function (d) { + d.fixed &= ~4; // unset bit 3 + }; + return Layout; + })(); + cola.Layout = Layout; +})(cola || (cola = {})); +///<reference path="layout.ts"/> +var cola; +(function (cola) { + var LayoutAdaptor = (function (_super) { + __extends(LayoutAdaptor, _super); + function LayoutAdaptor(options) { + _super.call(this); + // take in implementation as defined by client + var self = this; + var o = options; + if (o.trigger) { + this.trigger = o.trigger; + } + if (o.kick) { + this.kick = o.kick; + } + if (o.drag) { + this.drag = o.drag; + } + if (o.on) { + this.on = o.on; + } + this.dragstart = this.dragStart = cola.Layout.dragStart; + this.dragend = this.dragEnd = cola.Layout.dragEnd; + } + // dummy functions in case not defined by client + LayoutAdaptor.prototype.trigger = function (e) { }; + ; + LayoutAdaptor.prototype.kick = function () { }; + ; + LayoutAdaptor.prototype.drag = function () { }; + ; + LayoutAdaptor.prototype.on = function (eventType, listener) { return this; }; + ; + return LayoutAdaptor; + })(cola.Layout); + cola.LayoutAdaptor = LayoutAdaptor; + /** + * provides an interface for use with any external graph system (e.g. Cytoscape.js): + */ + function adaptor(options) { + return new LayoutAdaptor(options); + } + cola.adaptor = adaptor; +})(cola || (cola = {})); +var cola; +(function (cola) { + function gridify(pgLayout, nudgeGap, margin, groupMargin) { + pgLayout.cola.start(0, 0, 0, 10, false); + var gridrouter = route(pgLayout.cola.nodes(), pgLayout.cola.groups(), margin, groupMargin); + return gridrouter.routeEdges(pgLayout.powerGraph.powerEdges, nudgeGap, function (e) { return e.source.routerNode.id; }, function (e) { return e.target.routerNode.id; }); + } + cola.gridify = gridify; + function route(nodes, groups, margin, groupMargin) { + nodes.forEach(function (d) { + d.routerNode = { + name: d.name, + bounds: d.bounds.inflate(-margin) + }; + }); + groups.forEach(function (d) { + d.routerNode = { + bounds: d.bounds.inflate(-groupMargin), + children: (typeof d.groups !== 'undefined' ? d.groups.map(function (c) { return nodes.length + c.id; }) : []) + .concat(typeof d.leaves !== 'undefined' ? d.leaves.map(function (c) { return c.index; }) : []) + }; + }); + var gridRouterNodes = nodes.concat(groups).map(function (d, i) { + d.routerNode.id = i; + return d.routerNode; + }); + return new cola.GridRouter(gridRouterNodes, { + getChildren: function (v) { return v.children; }, + getBounds: function (v) { return v.bounds; } + }, margin - groupMargin); + } + function powerGraphGridLayout(graph, size, grouppadding, margin, groupMargin) { + // compute power graph + var powerGraph; + graph.nodes.forEach(function (v, i) { return v.index = i; }); + new cola.Layout() + .avoidOverlaps(false) + .nodes(graph.nodes) + .links(graph.links) + .powerGraphGroups(function (d) { + powerGraph = d; + powerGraph.groups.forEach(function (v) { return v.padding = grouppadding; }); + }); + // construct a flat graph with dummy nodes for the groups and edges connecting group dummy nodes to their children + // power edges attached to groups are replaced with edges connected to the corresponding group dummy node + var n = graph.nodes.length; + var edges = []; + var vs = graph.nodes.slice(0); + vs.forEach(function (v, i) { return v.index = i; }); + powerGraph.groups.forEach(function (g) { + var sourceInd = g.index = g.id + n; + vs.push(g); + if (typeof g.leaves !== 'undefined') + g.leaves.forEach(function (v) { return edges.push({ source: sourceInd, target: v.index }); }); + if (typeof g.groups !== 'undefined') + g.groups.forEach(function (gg) { return edges.push({ source: sourceInd, target: gg.id + n }); }); + }); + powerGraph.powerEdges.forEach(function (e) { + edges.push({ source: e.source.index, target: e.target.index }); + }); + // layout the flat graph with dummy nodes and edges + new cola.Layout() + .size(size) + .nodes(vs) + .links(edges) + .avoidOverlaps(false) + .linkDistance(30) + .symmetricDiffLinkLengths(5) + .convergenceThreshold(1e-4) + .start(100, 0, 0, 0, false); + // final layout taking node positions from above as starting positions + // subject to group containment constraints + // and then gridifying the layout + return { + cola: new cola.Layout() + .convergenceThreshold(1e-3) + .size(size) + .avoidOverlaps(true) + .nodes(graph.nodes) + .links(graph.links) + .groupCompactness(1e-4) + .linkDistance(30) + .symmetricDiffLinkLengths(5) + .powerGraphGroups(function (d) { + powerGraph = d; + powerGraph.groups.forEach(function (v) { + v.padding = grouppadding; + }); + }).start(50, 0, 100, 0, false), + powerGraph: powerGraph + }; + } + cola.powerGraphGridLayout = powerGraphGridLayout; +})(cola || (cola = {})); +///<reference path="../extern/d3.d.ts"/> +///<reference path="layout.ts"/> +var cola; +(function (cola) { + var D3StyleLayoutAdaptor = (function (_super) { + __extends(D3StyleLayoutAdaptor, _super); + function D3StyleLayoutAdaptor() { + _super.call(this); + this.event = d3.dispatch(cola.EventType[cola.EventType.start], cola.EventType[cola.EventType.tick], cola.EventType[cola.EventType.end]); + // bit of trickyness remapping 'this' so we can reference it in the function body. + var d3layout = this; + var drag; + this.drag = function () { + if (!drag) { + var drag = d3.behavior.drag() + .origin(function (d) { return d; }) + .on("dragstart.d3adaptor", cola.Layout.dragStart) + .on("drag.d3adaptor", function (d) { + d.px = d3.event.x, d.py = d3.event.y; + d3layout.resume(); // restart annealing + }) + .on("dragend.d3adaptor", cola.Layout.dragEnd); + } + if (!arguments.length) + return drag; + // this is the context of the function, i.e. the d3 selection + this //.on("mouseover.adaptor", colaMouseover) + .call(drag); + }; + } + D3StyleLayoutAdaptor.prototype.trigger = function (e) { + var d3event = { type: cola.EventType[e.type], alpha: e.alpha, stress: e.stress }; + this.event[d3event.type](d3event); // via d3 dispatcher, e.g. event.start(e); + }; + // iterate layout using a d3.timer, which queues calls to tick repeatedly until tick returns true + D3StyleLayoutAdaptor.prototype.kick = function () { + var _this = this; + d3.timer(function () { return _super.prototype.tick.call(_this); }); + }; + // a function for binding to events on the adapter + D3StyleLayoutAdaptor.prototype.on = function (eventType, listener) { + if (typeof eventType === 'string') { + this.event.on(eventType, listener); + } + else { + this.event.on(cola.EventType[eventType], listener); + } + return this; + }; + return D3StyleLayoutAdaptor; + })(cola.Layout); + cola.D3StyleLayoutAdaptor = D3StyleLayoutAdaptor; + /** + * provides an interface for use with d3: + * - uses the d3 event system to dispatch layout events such as: + * o "start" (start layout process) + * o "tick" (after each layout iteration) + * o "end" (layout converged and complete). + * - uses the d3 timer to queue layout iterations. + * - sets up d3.behavior.drag to drag nodes + * o use `node.call(<the returned instance of Layout>.drag)` to make nodes draggable + * returns an instance of the cola.Layout itself with which the user + * can interact directly. + */ + function d3adaptor() { + return new D3StyleLayoutAdaptor(); + } + cola.d3adaptor = d3adaptor; +})(cola || (cola = {})); +/// <reference path="rectangle.ts"/> +/// <reference path="shortestpaths.ts"/> +/// <reference path="geom.ts"/> +/// <reference path="vpsc.ts"/> +var cola; +(function (cola) { + var NodeWrapper = (function () { + function NodeWrapper(id, rect, children) { + this.id = id; + this.rect = rect; + this.children = children; + this.leaf = typeof children === 'undefined' || children.length === 0; + } + return NodeWrapper; + })(); + cola.NodeWrapper = NodeWrapper; + var Vert = (function () { + function Vert(id, x, y, node, line) { + if (node === void 0) { node = null; } + if (line === void 0) { line = null; } + this.id = id; + this.x = x; + this.y = y; + this.node = node; + this.line = line; + } + return Vert; + })(); + cola.Vert = Vert; + var LongestCommonSubsequence = (function () { + function LongestCommonSubsequence(s, t) { + this.s = s; + this.t = t; + var mf = LongestCommonSubsequence.findMatch(s, t); + var tr = t.slice(0).reverse(); + var mr = LongestCommonSubsequence.findMatch(s, tr); + if (mf.length >= mr.length) { + this.length = mf.length; + this.si = mf.si; + this.ti = mf.ti; + this.reversed = false; + } + else { + this.length = mr.length; + this.si = mr.si; + this.ti = t.length - mr.ti - mr.length; + this.reversed = true; + } + } + LongestCommonSubsequence.findMatch = function (s, t) { + var m = s.length; + var n = t.length; + var match = { length: 0, si: -1, ti: -1 }; + var l = new Array(m); + for (var i = 0; i < m; i++) { + l[i] = new Array(n); + for (var j = 0; j < n; j++) + if (s[i] === t[j]) { + var v = l[i][j] = (i === 0 || j === 0) ? 1 : l[i - 1][j - 1] + 1; + if (v > match.length) { + match.length = v; + match.si = i - v + 1; + match.ti = j - v + 1; + } + ; + } + else + l[i][j] = 0; + } + return match; + }; + LongestCommonSubsequence.prototype.getSequence = function () { + return this.length >= 0 ? this.s.slice(this.si, this.si + this.length) : []; + }; + return LongestCommonSubsequence; + })(); + cola.LongestCommonSubsequence = LongestCommonSubsequence; + var GridRouter = (function () { + function GridRouter(originalnodes, accessor, groupPadding) { + var _this = this; + if (groupPadding === void 0) { groupPadding = 12; } + this.originalnodes = originalnodes; + this.groupPadding = groupPadding; + this.leaves = null; + this.nodes = originalnodes.map(function (v, i) { return new NodeWrapper(i, accessor.getBounds(v), accessor.getChildren(v)); }); + this.leaves = this.nodes.filter(function (v) { return v.leaf; }); + this.groups = this.nodes.filter(function (g) { return !g.leaf; }); + this.cols = this.getGridLines('x'); + this.rows = this.getGridLines('y'); + // create parents for each node or group that is a member of another's children + this.groups.forEach(function (v) { + return v.children.forEach(function (c) { return _this.nodes[c].parent = v; }); + }); + // root claims the remaining orphans + this.root = { children: [] }; + this.nodes.forEach(function (v) { + if (typeof v.parent === 'undefined') { + v.parent = _this.root; + _this.root.children.push(v.id); + } + // each node will have grid vertices associated with it, + // some inside the node and some on the boundary + // leaf nodes will have exactly one internal node at the center + // and four boundary nodes + // groups will have potentially many of each + v.ports = []; + }); + // nodes ordered by their position in the group hierarchy + this.backToFront = this.nodes.slice(0); + this.backToFront.sort(function (x, y) { return _this.getDepth(x) - _this.getDepth(y); }); + // compute boundary rectangles for each group + // has to be done from front to back, i.e. inside groups to outside groups + // such that each can be made large enough to enclose its interior + var frontToBackGroups = this.backToFront.slice(0).reverse().filter(function (g) { return !g.leaf; }); + frontToBackGroups.forEach(function (v) { + var r = cola.vpsc.Rectangle.empty(); + v.children.forEach(function (c) { return r = r.union(_this.nodes[c].rect); }); + v.rect = r.inflate(_this.groupPadding); + }); + var colMids = this.midPoints(this.cols.map(function (r) { return r.pos; })); + var rowMids = this.midPoints(this.rows.map(function (r) { return r.pos; })); + // setup extents of lines + var rowx = colMids[0], rowX = colMids[colMids.length - 1]; + var coly = rowMids[0], colY = rowMids[rowMids.length - 1]; + // horizontal lines + var hlines = this.rows.map(function (r) { return { x1: rowx, x2: rowX, y1: r.pos, y2: r.pos }; }) + .concat(rowMids.map(function (m) { return { x1: rowx, x2: rowX, y1: m, y2: m }; })); + // vertical lines + var vlines = this.cols.map(function (c) { return { x1: c.pos, x2: c.pos, y1: coly, y2: colY }; }) + .concat(colMids.map(function (m) { return { x1: m, x2: m, y1: coly, y2: colY }; })); + // the full set of lines + var lines = hlines.concat(vlines); + // we record the vertices associated with each line + lines.forEach(function (l) { return l.verts = []; }); + // the routing graph + this.verts = []; + this.edges = []; + // create vertices at the crossings of horizontal and vertical grid-lines + hlines.forEach(function (h) { + return vlines.forEach(function (v) { + var p = new Vert(_this.verts.length, v.x1, h.y1); + h.verts.push(p); + v.verts.push(p); + _this.verts.push(p); + // assign vertices to the nodes immediately under them + var i = _this.backToFront.length; + while (i-- > 0) { + var node = _this.backToFront[i], r = node.rect; + var dx = Math.abs(p.x - r.cx()), dy = Math.abs(p.y - r.cy()); + if (dx < r.width() / 2 && dy < r.height() / 2) { + p.node = node; + break; + } + } + }); + }); + lines.forEach(function (l, li) { + // create vertices at the intersections of nodes and lines + _this.nodes.forEach(function (v, i) { + v.rect.lineIntersections(l.x1, l.y1, l.x2, l.y2).forEach(function (intersect, j) { + //console.log(li+','+i+','+j+':'+intersect.x + ',' + intersect.y); + var p = new Vert(_this.verts.length, intersect.x, intersect.y, v, l); + _this.verts.push(p); + l.verts.push(p); + v.ports.push(p); + }); + }); + // split lines into edges joining vertices + var isHoriz = Math.abs(l.y1 - l.y2) < 0.1; + var delta = function (a, b) { return isHoriz ? b.x - a.x : b.y - a.y; }; + l.verts.sort(delta); + for (var i = 1; i < l.verts.length; i++) { + var u = l.verts[i - 1], v = l.verts[i]; + if (u.node && u.node === v.node && u.node.leaf) + continue; + _this.edges.push({ source: u.id, target: v.id, length: Math.abs(delta(u, v)) }); + } + }); + } + GridRouter.prototype.avg = function (a) { return a.reduce(function (x, y) { return x + y; }) / a.length; }; + // in the given axis, find sets of leaves overlapping in that axis + // center of each GridLine is average of all nodes in column + GridRouter.prototype.getGridLines = function (axis) { + var columns = []; + var ls = this.leaves.slice(0, this.leaves.length); + while (ls.length > 0) { + // find a column of all leaves overlapping in axis with the first leaf + var overlapping = ls.filter(function (v) { return v.rect['overlap' + axis.toUpperCase()](ls[0].rect); }); + var col = { + nodes: overlapping, + pos: this.avg(overlapping.map(function (v) { return v.rect['c' + axis](); })) + }; + columns.push(col); + col.nodes.forEach(function (v) { return ls.splice(ls.indexOf(v), 1); }); + } + columns.sort(function (a, b) { return a.pos - b.pos; }); + return columns; + }; + // get the depth of the given node in the group hierarchy + GridRouter.prototype.getDepth = function (v) { + var depth = 0; + while (v.parent !== this.root) { + depth++; + v = v.parent; + } + return depth; + }; + // medial axes between node centres and also boundary lines for the grid + GridRouter.prototype.midPoints = function (a) { + var gap = a[1] - a[0]; + var mids = [a[0] - gap / 2]; + for (var i = 1; i < a.length; i++) { + mids.push((a[i] + a[i - 1]) / 2); + } + mids.push(a[a.length - 1] + gap / 2); + return mids; + }; + // find path from v to root including both v and root + GridRouter.prototype.findLineage = function (v) { + var lineage = [v]; + do { + v = v.parent; + lineage.push(v); + } while (v !== this.root); + return lineage.reverse(); + }; + // find path connecting a and b through their lowest common ancestor + GridRouter.prototype.findAncestorPathBetween = function (a, b) { + var aa = this.findLineage(a), ba = this.findLineage(b), i = 0; + while (aa[i] === ba[i]) + i++; + // i-1 to include common ancestor only once (as first element) + return { commonAncestor: aa[i - 1], lineages: aa.slice(i).concat(ba.slice(i)) }; + }; + // when finding a path between two nodes a and b, siblings of a and b on the + // paths from a and b to their least common ancestor are obstacles + GridRouter.prototype.siblingObstacles = function (a, b) { + var _this = this; + var path = this.findAncestorPathBetween(a, b); + var lineageLookup = {}; + path.lineages.forEach(function (v) { return lineageLookup[v.id] = {}; }); + var obstacles = path.commonAncestor.children.filter(function (v) { return !(v in lineageLookup); }); + path.lineages + .filter(function (v) { return v.parent !== path.commonAncestor; }) + .forEach(function (v) { return obstacles = obstacles.concat(v.parent.children.filter(function (c) { return c !== v.id; })); }); + return obstacles.map(function (v) { return _this.nodes[v]; }); + }; + // for the given routes, extract all the segments orthogonal to the axis x + // and return all them grouped by x position + GridRouter.getSegmentSets = function (routes, x, y) { + // vsegments is a list of vertical segments sorted by x position + var vsegments = []; + for (var ei = 0; ei < routes.length; ei++) { + var route = routes[ei]; + for (var si = 0; si < route.length; si++) { + var s = route[si]; + s.edgeid = ei; + s.i = si; + var sdx = s[1][x] - s[0][x]; + if (Math.abs(sdx) < 0.1) { + vsegments.push(s); + } + } + } + vsegments.sort(function (a, b) { return a[0][x] - b[0][x]; }); + // vsegmentsets is a set of sets of segments grouped by x position + var vsegmentsets = []; + var segmentset = null; + for (var i = 0; i < vsegments.length; i++) { + var s = vsegments[i]; + if (!segmentset || Math.abs(s[0][x] - segmentset.pos) > 0.1) { + segmentset = { pos: s[0][x], segments: [] }; + vsegmentsets.push(segmentset); + } + segmentset.segments.push(s); + } + return vsegmentsets; + }; + // for all segments in this bundle create a vpsc problem such that + // each segment's x position is a variable and separation constraints + // are given by the partial order over the edges to which the segments belong + // for each pair s1,s2 of segments in the open set: + // e1 = edge of s1, e2 = edge of s2 + // if leftOf(e1,e2) create constraint s1.x + gap <= s2.x + // else if leftOf(e2,e1) create cons. s2.x + gap <= s1.x + GridRouter.nudgeSegs = function (x, y, routes, segments, leftOf, gap) { + var n = segments.length; + if (n <= 1) + return; + var vs = segments.map(function (s) { return new cola.vpsc.Variable(s[0][x]); }); + var cs = []; + for (var i = 0; i < n; i++) { + for (var j = 0; j < n; j++) { + if (i === j) + continue; + var s1 = segments[i], s2 = segments[j], e1 = s1.edgeid, e2 = s2.edgeid, lind = -1, rind = -1; + // in page coordinates (not cartesian) the notion of 'leftof' is flipped in the horizontal axis from the vertical axis + // that is, when nudging vertical segments, if they increase in the y(conj) direction the segment belonging to the + // 'left' edge actually needs to be nudged to the right + // when nudging horizontal segments, if the segments increase in the x direction + // then the 'left' segment needs to go higher, i.e. to have y pos less than that of the right + if (x == 'x') { + if (leftOf(e1, e2)) { + //console.log('s1: ' + s1[0][x] + ',' + s1[0][y] + '-' + s1[1][x] + ',' + s1[1][y]); + if (s1[0][y] < s1[1][y]) { + lind = j, rind = i; + } + else { + lind = i, rind = j; + } + } + } + else { + if (leftOf(e1, e2)) { + if (s1[0][y] < s1[1][y]) { + lind = i, rind = j; + } + else { + lind = j, rind = i; + } + } + } + if (lind >= 0) { + //console.log(x+' constraint: ' + lind + '<' + rind); + cs.push(new cola.vpsc.Constraint(vs[lind], vs[rind], gap)); + } + } + } + var solver = new cola.vpsc.Solver(vs, cs); + solver.solve(); + vs.forEach(function (v, i) { + var s = segments[i]; + var pos = v.position(); + s[0][x] = s[1][x] = pos; + var route = routes[s.edgeid]; + if (s.i > 0) + route[s.i - 1][1][x] = pos; + if (s.i < route.length - 1) + route[s.i + 1][0][x] = pos; + }); + }; + GridRouter.nudgeSegments = function (routes, x, y, leftOf, gap) { + var vsegmentsets = GridRouter.getSegmentSets(routes, x, y); + // scan the grouped (by x) segment sets to find co-linear bundles + for (var i = 0; i < vsegmentsets.length; i++) { + var ss = vsegmentsets[i]; + var events = []; + for (var j = 0; j < ss.segments.length; j++) { + var s = ss.segments[j]; + events.push({ type: 0, s: s, pos: Math.min(s[0][y], s[1][y]) }); + events.push({ type: 1, s: s, pos: Math.max(s[0][y], s[1][y]) }); + } + events.sort(function (a, b) { return a.pos - b.pos + a.type - b.type; }); + var open = []; + var openCount = 0; + events.forEach(function (e) { + if (e.type === 0) { + open.push(e.s); + openCount++; + } + else { + openCount--; + } + if (openCount == 0) { + GridRouter.nudgeSegs(x, y, routes, open, leftOf, gap); + open = []; + } + }); + } + }; + // obtain routes for the specified edges, nicely nudged apart + // warning: edge paths may be reversed such that common paths are ordered consistently within bundles! + // @param edges list of edges + // @param nudgeGap how much to space parallel edge segements + // @param source function to retrieve the index of the source node for a given edge + // @param target function to retrieve the index of the target node for a given edge + // @returns an array giving, for each edge, an array of segments, each segment a pair of points in an array + GridRouter.prototype.routeEdges = function (edges, nudgeGap, source, target) { + var _this = this; + var routePaths = edges.map(function (e) { return _this.route(source(e), target(e)); }); + var order = cola.GridRouter.orderEdges(routePaths); + var routes = routePaths.map(function (e) { return cola.GridRouter.makeSegments(e); }); + cola.GridRouter.nudgeSegments(routes, 'x', 'y', order, nudgeGap); + cola.GridRouter.nudgeSegments(routes, 'y', 'x', order, nudgeGap); + cola.GridRouter.unreverseEdges(routes, routePaths); + return routes; + }; + // path may have been reversed by the subsequence processing in orderEdges + // so now we need to restore the original order + GridRouter.unreverseEdges = function (routes, routePaths) { + routes.forEach(function (segments, i) { + var path = routePaths[i]; + if (path.reversed) { + segments.reverse(); // reverse order of segments + segments.forEach(function (segment) { + segment.reverse(); // reverse each segment + }); + } + }); + }; + GridRouter.angleBetween2Lines = function (line1, line2) { + var angle1 = Math.atan2(line1[0].y - line1[1].y, line1[0].x - line1[1].x); + var angle2 = Math.atan2(line2[0].y - line2[1].y, line2[0].x - line2[1].x); + var diff = angle1 - angle2; + if (diff > Math.PI || diff < -Math.PI) { + diff = angle2 - angle1; + } + return diff; + }; + // does the path a-b-c describe a left turn? + GridRouter.isLeft = function (a, b, c) { + return ((b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x)) <= 0; + }; + // for the given list of ordered pairs, returns a function that (efficiently) looks-up a specific pair to + // see if it exists in the list + GridRouter.getOrder = function (pairs) { + var outgoing = {}; + for (var i = 0; i < pairs.length; i++) { + var p = pairs[i]; + if (typeof outgoing[p.l] === 'undefined') + outgoing[p.l] = {}; + outgoing[p.l][p.r] = true; + } + return function (l, r) { return typeof outgoing[l] !== 'undefined' && outgoing[l][r]; }; + }; + // returns an ordering (a lookup function) that determines the correct order to nudge the + // edge paths apart to minimize crossings + GridRouter.orderEdges = function (edges) { + var edgeOrder = []; + for (var i = 0; i < edges.length - 1; i++) { + for (var j = i + 1; j < edges.length; j++) { + var e = edges[i], f = edges[j], lcs = new cola.LongestCommonSubsequence(e, f); + var u, vi, vj; + if (lcs.length === 0) + continue; // no common subpath + if (lcs.reversed) { + // if we found a common subpath but one of the edges runs the wrong way, + // then reverse f. + f.reverse(); + f.reversed = true; + lcs = new cola.LongestCommonSubsequence(e, f); + } + if ((lcs.si <= 0 || lcs.ti <= 0) && + (lcs.si + lcs.length >= e.length || lcs.ti + lcs.length >= f.length)) { + // the paths do not diverge, so make an arbitrary ordering decision + edgeOrder.push({ l: i, r: j }); + continue; + } + if (lcs.si + lcs.length >= e.length || lcs.ti + lcs.length >= f.length) { + // if the common subsequence of the + // two edges being considered goes all the way to the + // end of one (or both) of the lines then we have to + // base our ordering decision on the other end of the + // common subsequence + u = e[lcs.si + 1]; + vj = e[lcs.si - 1]; + vi = f[lcs.ti - 1]; + } + else { + u = e[lcs.si + lcs.length - 2]; + vi = e[lcs.si + lcs.length]; + vj = f[lcs.ti + lcs.length]; + } + if (GridRouter.isLeft(u, vi, vj)) { + edgeOrder.push({ l: j, r: i }); + } + else { + edgeOrder.push({ l: i, r: j }); + } + } + } + //edgeOrder.forEach(function (e) { console.log('l:' + e.l + ',r:' + e.r) }); + return cola.GridRouter.getOrder(edgeOrder); + }; + // for an orthogonal path described by a sequence of points, create a list of segments + // if consecutive segments would make a straight line they are merged into a single segment + // segments are over cloned points, not the original vertices + GridRouter.makeSegments = function (path) { + function copyPoint(p) { + return { x: p.x, y: p.y }; + } + var isStraight = function (a, b, c) { return Math.abs((b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x)) < 0.001; }; + var segments = []; + var a = copyPoint(path[0]); + for (var i = 1; i < path.length; i++) { + var b = copyPoint(path[i]), c = i < path.length - 1 ? path[i + 1] : null; + if (!c || !isStraight(a, b, c)) { + segments.push([a, b]); + a = b; + } + } + return segments; + }; + // find a route between node s and node t + // returns an array of indices to verts + GridRouter.prototype.route = function (s, t) { + var _this = this; + var source = this.nodes[s], target = this.nodes[t]; + this.obstacles = this.siblingObstacles(source, target); + var obstacleLookup = {}; + this.obstacles.forEach(function (o) { return obstacleLookup[o.id] = o; }); + this.passableEdges = this.edges.filter(function (e) { + var u = _this.verts[e.source], v = _this.verts[e.target]; + return !(u.node && u.node.id in obstacleLookup + || v.node && v.node.id in obstacleLookup); + }); + // add dummy segments linking ports inside source and target + for (var i = 1; i < source.ports.length; i++) { + var u = source.ports[0].id; + var v = source.ports[i].id; + this.passableEdges.push({ + source: u, + target: v, + length: 0 + }); + } + for (var i = 1; i < target.ports.length; i++) { + var u = target.ports[0].id; + var v = target.ports[i].id; + this.passableEdges.push({ + source: u, + target: v, + length: 0 + }); + } + var getSource = function (e) { return e.source; }, getTarget = function (e) { return e.target; }, getLength = function (e) { return e.length; }; + var shortestPathCalculator = new cola.shortestpaths.Calculator(this.verts.length, this.passableEdges, getSource, getTarget, getLength); + var bendPenalty = function (u, v, w) { + var a = _this.verts[u], b = _this.verts[v], c = _this.verts[w]; + var dx = Math.abs(c.x - a.x), dy = Math.abs(c.y - a.y); + // don't count bends from internal node edges + if (a.node === source && a.node === b.node || b.node === target && b.node === c.node) + return 0; + return dx > 1 && dy > 1 ? 1000 : 0; + }; + // get shortest path + var shortestPath = shortestPathCalculator.PathFromNodeToNodeWithPrevCost(source.ports[0].id, target.ports[0].id, bendPenalty); + // shortest path is reversed and does not include the target port + var pathPoints = shortestPath.reverse().map(function (vi) { return _this.verts[vi]; }); + pathPoints.push(this.nodes[target.id].ports[0]); + // filter out any extra end points that are inside the source or target (i.e. the dummy segments above) + return pathPoints.filter(function (v, i) { + return !(i < pathPoints.length - 1 && pathPoints[i + 1].node === source && v.node === source + || i > 0 && v.node === target && pathPoints[i - 1].node === target); + }); + }; + GridRouter.getRoutePath = function (route, cornerradius, arrowwidth, arrowheight) { + var result = { + routepath: 'M ' + route[0][0].x + ' ' + route[0][0].y + ' ', + arrowpath: '' + }; + if (route.length > 1) { + for (var i = 0; i < route.length; i++) { + var li = route[i]; + var x = li[1].x, y = li[1].y; + var dx = x - li[0].x; + var dy = y - li[0].y; + if (i < route.length - 1) { + if (Math.abs(dx) > 0) { + x -= dx / Math.abs(dx) * cornerradius; + } + else { + y -= dy / Math.abs(dy) * cornerradius; + } + result.routepath += 'L ' + x + ' ' + y + ' '; + var l = route[i + 1]; + var x0 = l[0].x, y0 = l[0].y; + var x1 = l[1].x; + var y1 = l[1].y; + dx = x1 - x0; + dy = y1 - y0; + var angle = GridRouter.angleBetween2Lines(li, l) < 0 ? 1 : 0; + //console.log(cola.GridRouter.angleBetween2Lines(li, l)) + var x2, y2; + if (Math.abs(dx) > 0) { + x2 = x0 + dx / Math.abs(dx) * cornerradius; + y2 = y0; + } + else { + x2 = x0; + y2 = y0 + dy / Math.abs(dy) * cornerradius; + } + var cx = Math.abs(x2 - x); + var cy = Math.abs(y2 - y); + result.routepath += 'A ' + cx + ' ' + cy + ' 0 0 ' + angle + ' ' + x2 + ' ' + y2 + ' '; + } + else { + var arrowtip = [x, y]; + var arrowcorner1, arrowcorner2; + if (Math.abs(dx) > 0) { + x -= dx / Math.abs(dx) * arrowheight; + arrowcorner1 = [x, y + arrowwidth]; + arrowcorner2 = [x, y - arrowwidth]; + } + else { + y -= dy / Math.abs(dy) * arrowheight; + arrowcorner1 = [x + arrowwidth, y]; + arrowcorner2 = [x - arrowwidth, y]; + } + result.routepath += 'L ' + x + ' ' + y + ' '; + if (arrowheight > 0) { + result.arrowpath = 'M ' + arrowtip[0] + ' ' + arrowtip[1] + ' L ' + arrowcorner1[0] + ' ' + arrowcorner1[1] + + ' L ' + arrowcorner2[0] + ' ' + arrowcorner2[1]; + } + } + } + } + else { + var li = route[0]; + var x = li[1].x, y = li[1].y; + var dx = x - li[0].x; + var dy = y - li[0].y; + var arrowtip = [x, y]; + var arrowcorner1, arrowcorner2; + if (Math.abs(dx) > 0) { + x -= dx / Math.abs(dx) * arrowheight; + arrowcorner1 = [x, y + arrowwidth]; + arrowcorner2 = [x, y - arrowwidth]; + } + else { + y -= dy / Math.abs(dy) * arrowheight; + arrowcorner1 = [x + arrowwidth, y]; + arrowcorner2 = [x - arrowwidth, y]; + } + result.routepath += 'L ' + x + ' ' + y + ' '; + if (arrowheight > 0) { + result.arrowpath = 'M ' + arrowtip[0] + ' ' + arrowtip[1] + ' L ' + arrowcorner1[0] + ' ' + arrowcorner1[1] + + ' L ' + arrowcorner2[0] + ' ' + arrowcorner2[1]; + } + } + return result; + }; + return GridRouter; + })(); + cola.GridRouter = GridRouter; +})(cola || (cola = {})); +/** + * Use cola to do a layout in 3D!! Yay. + * Pretty simple for the moment. + */ +var cola; +(function (cola) { + var Link3D = (function () { + function Link3D(source, target) { + this.source = source; + this.target = target; + } + Link3D.prototype.actualLength = function (x) { + var _this = this; + return Math.sqrt(x.reduce(function (c, v) { + var dx = v[_this.target] - v[_this.source]; + return c + dx * dx; + }, 0)); + }; + return Link3D; + })(); + cola.Link3D = Link3D; + var Node3D = (function () { + function Node3D(x, y, z) { + if (x === void 0) { x = 0; } + if (y === void 0) { y = 0; } + if (z === void 0) { z = 0; } + this.x = x; + this.y = y; + this.z = z; + } + return Node3D; + })(); + cola.Node3D = Node3D; + var Layout3D = (function () { + function Layout3D(nodes, links, idealLinkLength) { + var _this = this; + if (idealLinkLength === void 0) { idealLinkLength = 1; } + this.nodes = nodes; + this.links = links; + this.idealLinkLength = idealLinkLength; + this.constraints = null; + this.useJaccardLinkLengths = true; + this.result = new Array(Layout3D.k); + for (var i = 0; i < Layout3D.k; ++i) { + this.result[i] = new Array(nodes.length); + } + nodes.forEach(function (v, i) { + for (var _i = 0, _a = Layout3D.dims; _i < _a.length; _i++) { + var dim = _a[_i]; + if (typeof v[dim] == 'undefined') + v[dim] = Math.random(); + } + _this.result[0][i] = v.x; + _this.result[1][i] = v.y; + _this.result[2][i] = v.z; + }); + } + ; + Layout3D.prototype.linkLength = function (l) { + return l.actualLength(this.result); + }; + Layout3D.prototype.start = function (iterations) { + var _this = this; + if (iterations === void 0) { iterations = 100; } + var n = this.nodes.length; + var linkAccessor = new LinkAccessor(); + if (this.useJaccardLinkLengths) + cola.jaccardLinkLengths(this.links, linkAccessor, 1.5); + this.links.forEach(function (e) { return e.length *= _this.idealLinkLength; }); + // Create the distance matrix that Cola needs + var distanceMatrix = (new cola.shortestpaths.Calculator(n, this.links, function (e) { return e.source; }, function (e) { return e.target; }, function (e) { return e.length; })).DistanceMatrix(); + var D = cola.Descent.createSquareMatrix(n, function (i, j) { return distanceMatrix[i][j]; }); + // G is a square matrix with G[i][j] = 1 iff there exists an edge between node i and node j + // otherwise 2. + var G = cola.Descent.createSquareMatrix(n, function () { return 2; }); + this.links.forEach(function (_a) { + var source = _a.source, target = _a.target; + return G[source][target] = G[target][source] = 1; + }); + this.descent = new cola.Descent(this.result, D); + this.descent.threshold = 1e-3; + this.descent.G = G; + //let constraints = this.links.map(e=> <any>{ + // axis: 'y', left: e.source, right: e.target, gap: e.length*1.5 + //}); + if (this.constraints) + this.descent.project = new cola.vpsc.Projection(this.nodes, null, null, this.constraints).projectFunctions(); + for (var i = 0; i < this.nodes.length; i++) { + var v = this.nodes[i]; + if (v.fixed) { + this.descent.locks.add(i, [v.x, v.y, v.z]); + } + } + this.descent.run(iterations); + return this; + }; + Layout3D.prototype.tick = function () { + this.descent.locks.clear(); + for (var i = 0; i < this.nodes.length; i++) { + var v = this.nodes[i]; + if (v.fixed) { + this.descent.locks.add(i, [v.x, v.y, v.z]); + } + } + return this.descent.rungeKutta(); + }; + Layout3D.dims = ['x', 'y', 'z']; + Layout3D.k = Layout3D.dims.length; + return Layout3D; + })(); + cola.Layout3D = Layout3D; + var LinkAccessor = (function () { + function LinkAccessor() { + } + LinkAccessor.prototype.getSourceIndex = function (e) { return e.source; }; + LinkAccessor.prototype.getTargetIndex = function (e) { return e.target; }; + LinkAccessor.prototype.getLength = function (e) { return e.length; }; + LinkAccessor.prototype.setLength = function (e, l) { e.length = l; }; + return LinkAccessor; + })(); +})(cola || (cola = {})); +//# sourceMappingURL=cola.js.map \ No newline at end of file diff --git a/src/legacy/design-studio/js/crossfilter.js b/src/legacy/design-studio/js/crossfilter.js new file mode 100644 index 0000000..71577d3 --- /dev/null +++ b/src/legacy/design-studio/js/crossfilter.js @@ -0,0 +1,3077 @@ +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.crossfilter = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ +module.exports = require("./src/crossfilter").crossfilter; + +},{"./src/crossfilter":6}],2:[function(require,module,exports){ +(function (global){ +/** + * lodash (Custom Build) <https://lodash.com/> + * Build: `lodash modularize exports="npm" -o ./` + * Copyright jQuery Foundation and other contributors <https://jquery.org/> + * Released under MIT license <https://lodash.com/license> + * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE> + * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors + */ + +/** Used as the `TypeError` message for "Functions" methods. */ +var FUNC_ERROR_TEXT = 'Expected a function'; + +/** Used to stand-in for `undefined` hash values. */ +var HASH_UNDEFINED = '__lodash_hash_undefined__'; + +/** Used as references for various `Number` constants. */ +var INFINITY = 1 / 0; + +/** `Object#toString` result references. */ +var funcTag = '[object Function]', + genTag = '[object GeneratorFunction]', + symbolTag = '[object Symbol]'; + +/** Used to match property names within property paths. */ +var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/, + reIsPlainProp = /^\w*$/, + reLeadingDot = /^\./, + rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g; + +/** + * Used to match `RegExp` + * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns). + */ +var reRegExpChar = /[\\^$.*+?()[\]{}|]/g; + +/** Used to match backslashes in property paths. */ +var reEscapeChar = /\\(\\)?/g; + +/** Used to detect host constructors (Safari). */ +var reIsHostCtor = /^\[object .+?Constructor\]$/; + +/** Detect free variable `global` from Node.js. */ +var freeGlobal = typeof global == 'object' && global && global.Object === Object && global; + +/** Detect free variable `self`. */ +var freeSelf = typeof self == 'object' && self && self.Object === Object && self; + +/** Used as a reference to the global object. */ +var root = freeGlobal || freeSelf || Function('return this')(); + +/** + * Gets the value at `key` of `object`. + * + * @private + * @param {Object} [object] The object to query. + * @param {string} key The key of the property to get. + * @returns {*} Returns the property value. + */ +function getValue(object, key) { + return object == null ? undefined : object[key]; +} + +/** + * Checks if `value` is a host object in IE < 9. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a host object, else `false`. + */ +function isHostObject(value) { + // Many host objects are `Object` objects that can coerce to strings + // despite having improperly defined `toString` methods. + var result = false; + if (value != null && typeof value.toString != 'function') { + try { + result = !!(value + ''); + } catch (e) {} + } + return result; +} + +/** Used for built-in method references. */ +var arrayProto = Array.prototype, + funcProto = Function.prototype, + objectProto = Object.prototype; + +/** Used to detect overreaching core-js shims. */ +var coreJsData = root['__core-js_shared__']; + +/** Used to detect methods masquerading as native. */ +var maskSrcKey = (function() { + var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || ''); + return uid ? ('Symbol(src)_1.' + uid) : ''; +}()); + +/** Used to resolve the decompiled source of functions. */ +var funcToString = funcProto.toString; + +/** Used to check objects for own properties. */ +var hasOwnProperty = objectProto.hasOwnProperty; + +/** + * Used to resolve the + * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) + * of values. + */ +var objectToString = objectProto.toString; + +/** Used to detect if a method is native. */ +var reIsNative = RegExp('^' + + funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\$&') + .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' +); + +/** Built-in value references. */ +var Symbol = root.Symbol, + splice = arrayProto.splice; + +/* Built-in method references that are verified to be native. */ +var Map = getNative(root, 'Map'), + nativeCreate = getNative(Object, 'create'); + +/** Used to convert symbols to primitives and strings. */ +var symbolProto = Symbol ? Symbol.prototype : undefined, + symbolToString = symbolProto ? symbolProto.toString : undefined; + +/** + * Creates a hash object. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ +function Hash(entries) { + var index = -1, + length = entries ? entries.length : 0; + + this.clear(); + while (++index < length) { + var entry = entries[index]; + this.set(entry[0], entry[1]); + } +} + +/** + * Removes all key-value entries from the hash. + * + * @private + * @name clear + * @memberOf Hash + */ +function hashClear() { + this.__data__ = nativeCreate ? nativeCreate(null) : {}; +} + +/** + * Removes `key` and its value from the hash. + * + * @private + * @name delete + * @memberOf Hash + * @param {Object} hash The hash to modify. + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ +function hashDelete(key) { + return this.has(key) && delete this.__data__[key]; +} + +/** + * Gets the hash value for `key`. + * + * @private + * @name get + * @memberOf Hash + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ +function hashGet(key) { + var data = this.__data__; + if (nativeCreate) { + var result = data[key]; + return result === HASH_UNDEFINED ? undefined : result; + } + return hasOwnProperty.call(data, key) ? data[key] : undefined; +} + +/** + * Checks if a hash value for `key` exists. + * + * @private + * @name has + * @memberOf Hash + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ +function hashHas(key) { + var data = this.__data__; + return nativeCreate ? data[key] !== undefined : hasOwnProperty.call(data, key); +} + +/** + * Sets the hash `key` to `value`. + * + * @private + * @name set + * @memberOf Hash + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the hash instance. + */ +function hashSet(key, value) { + var data = this.__data__; + data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value; + return this; +} + +// Add methods to `Hash`. +Hash.prototype.clear = hashClear; +Hash.prototype['delete'] = hashDelete; +Hash.prototype.get = hashGet; +Hash.prototype.has = hashHas; +Hash.prototype.set = hashSet; + +/** + * Creates an list cache object. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ +function ListCache(entries) { + var index = -1, + length = entries ? entries.length : 0; + + this.clear(); + while (++index < length) { + var entry = entries[index]; + this.set(entry[0], entry[1]); + } +} + +/** + * Removes all key-value entries from the list cache. + * + * @private + * @name clear + * @memberOf ListCache + */ +function listCacheClear() { + this.__data__ = []; +} + +/** + * Removes `key` and its value from the list cache. + * + * @private + * @name delete + * @memberOf ListCache + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ +function listCacheDelete(key) { + var data = this.__data__, + index = assocIndexOf(data, key); + + if (index < 0) { + return false; + } + var lastIndex = data.length - 1; + if (index == lastIndex) { + data.pop(); + } else { + splice.call(data, index, 1); + } + return true; +} + +/** + * Gets the list cache value for `key`. + * + * @private + * @name get + * @memberOf ListCache + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ +function listCacheGet(key) { + var data = this.__data__, + index = assocIndexOf(data, key); + + return index < 0 ? undefined : data[index][1]; +} + +/** + * Checks if a list cache value for `key` exists. + * + * @private + * @name has + * @memberOf ListCache + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ +function listCacheHas(key) { + return assocIndexOf(this.__data__, key) > -1; +} + +/** + * Sets the list cache `key` to `value`. + * + * @private + * @name set + * @memberOf ListCache + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the list cache instance. + */ +function listCacheSet(key, value) { + var data = this.__data__, + index = assocIndexOf(data, key); + + if (index < 0) { + data.push([key, value]); + } else { + data[index][1] = value; + } + return this; +} + +// Add methods to `ListCache`. +ListCache.prototype.clear = listCacheClear; +ListCache.prototype['delete'] = listCacheDelete; +ListCache.prototype.get = listCacheGet; +ListCache.prototype.has = listCacheHas; +ListCache.prototype.set = listCacheSet; + +/** + * Creates a map cache object to store key-value pairs. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ +function MapCache(entries) { + var index = -1, + length = entries ? entries.length : 0; + + this.clear(); + while (++index < length) { + var entry = entries[index]; + this.set(entry[0], entry[1]); + } +} + +/** + * Removes all key-value entries from the map. + * + * @private + * @name clear + * @memberOf MapCache + */ +function mapCacheClear() { + this.__data__ = { + 'hash': new Hash, + 'map': new (Map || ListCache), + 'string': new Hash + }; +} + +/** + * Removes `key` and its value from the map. + * + * @private + * @name delete + * @memberOf MapCache + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ +function mapCacheDelete(key) { + return getMapData(this, key)['delete'](key); +} + +/** + * Gets the map value for `key`. + * + * @private + * @name get + * @memberOf MapCache + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ +function mapCacheGet(key) { + return getMapData(this, key).get(key); +} + +/** + * Checks if a map value for `key` exists. + * + * @private + * @name has + * @memberOf MapCache + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ +function mapCacheHas(key) { + return getMapData(this, key).has(key); +} + +/** + * Sets the map `key` to `value`. + * + * @private + * @name set + * @memberOf MapCache + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the map cache instance. + */ +function mapCacheSet(key, value) { + getMapData(this, key).set(key, value); + return this; +} + +// Add methods to `MapCache`. +MapCache.prototype.clear = mapCacheClear; +MapCache.prototype['delete'] = mapCacheDelete; +MapCache.prototype.get = mapCacheGet; +MapCache.prototype.has = mapCacheHas; +MapCache.prototype.set = mapCacheSet; + +/** + * Gets the index at which the `key` is found in `array` of key-value pairs. + * + * @private + * @param {Array} array The array to inspect. + * @param {*} key The key to search for. + * @returns {number} Returns the index of the matched value, else `-1`. + */ +function assocIndexOf(array, key) { + var length = array.length; + while (length--) { + if (eq(array[length][0], key)) { + return length; + } + } + return -1; +} + +/** + * The base implementation of `_.isNative` without bad shim checks. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a native function, + * else `false`. + */ +function baseIsNative(value) { + if (!isObject(value) || isMasked(value)) { + return false; + } + var pattern = (isFunction(value) || isHostObject(value)) ? reIsNative : reIsHostCtor; + return pattern.test(toSource(value)); +} + +/** + * The base implementation of `_.toString` which doesn't convert nullish + * values to empty strings. + * + * @private + * @param {*} value The value to process. + * @returns {string} Returns the string. + */ +function baseToString(value) { + // Exit early for strings to avoid a performance hit in some environments. + if (typeof value == 'string') { + return value; + } + if (isSymbol(value)) { + return symbolToString ? symbolToString.call(value) : ''; + } + var result = (value + ''); + return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result; +} + +/** + * Casts `value` to a path array if it's not one. + * + * @private + * @param {*} value The value to inspect. + * @returns {Array} Returns the cast property path array. + */ +function castPath(value) { + return isArray(value) ? value : stringToPath(value); +} + +/** + * Gets the data for `map`. + * + * @private + * @param {Object} map The map to query. + * @param {string} key The reference key. + * @returns {*} Returns the map data. + */ +function getMapData(map, key) { + var data = map.__data__; + return isKeyable(key) + ? data[typeof key == 'string' ? 'string' : 'hash'] + : data.map; +} + +/** + * Gets the native function at `key` of `object`. + * + * @private + * @param {Object} object The object to query. + * @param {string} key The key of the method to get. + * @returns {*} Returns the function if it's native, else `undefined`. + */ +function getNative(object, key) { + var value = getValue(object, key); + return baseIsNative(value) ? value : undefined; +} + +/** + * Checks if `value` is a property name and not a property path. + * + * @private + * @param {*} value The value to check. + * @param {Object} [object] The object to query keys on. + * @returns {boolean} Returns `true` if `value` is a property name, else `false`. + */ +function isKey(value, object) { + if (isArray(value)) { + return false; + } + var type = typeof value; + if (type == 'number' || type == 'symbol' || type == 'boolean' || + value == null || isSymbol(value)) { + return true; + } + return reIsPlainProp.test(value) || !reIsDeepProp.test(value) || + (object != null && value in Object(object)); +} + +/** + * Checks if `value` is suitable for use as unique object key. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is suitable, else `false`. + */ +function isKeyable(value) { + var type = typeof value; + return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean') + ? (value !== '__proto__') + : (value === null); +} + +/** + * Checks if `func` has its source masked. + * + * @private + * @param {Function} func The function to check. + * @returns {boolean} Returns `true` if `func` is masked, else `false`. + */ +function isMasked(func) { + return !!maskSrcKey && (maskSrcKey in func); +} + +/** + * Converts `string` to a property path array. + * + * @private + * @param {string} string The string to convert. + * @returns {Array} Returns the property path array. + */ +var stringToPath = memoize(function(string) { + string = toString(string); + + var result = []; + if (reLeadingDot.test(string)) { + result.push(''); + } + string.replace(rePropName, function(match, number, quote, string) { + result.push(quote ? string.replace(reEscapeChar, '$1') : (number || match)); + }); + return result; +}); + +/** + * Converts `value` to a string key if it's not a string or symbol. + * + * @private + * @param {*} value The value to inspect. + * @returns {string|symbol} Returns the key. + */ +function toKey(value) { + if (typeof value == 'string' || isSymbol(value)) { + return value; + } + var result = (value + ''); + return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result; +} + +/** + * Converts `func` to its source code. + * + * @private + * @param {Function} func The function to process. + * @returns {string} Returns the source code. + */ +function toSource(func) { + if (func != null) { + try { + return funcToString.call(func); + } catch (e) {} + try { + return (func + ''); + } catch (e) {} + } + return ''; +} + +/** + * Creates a function that memoizes the result of `func`. If `resolver` is + * provided, it determines the cache key for storing the result based on the + * arguments provided to the memoized function. By default, the first argument + * provided to the memoized function is used as the map cache key. The `func` + * is invoked with the `this` binding of the memoized function. + * + * **Note:** The cache is exposed as the `cache` property on the memoized + * function. Its creation may be customized by replacing the `_.memoize.Cache` + * constructor with one whose instances implement the + * [`Map`](http://ecma-international.org/ecma-262/7.0/#sec-properties-of-the-map-prototype-object) + * method interface of `delete`, `get`, `has`, and `set`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {Function} func The function to have its output memoized. + * @param {Function} [resolver] The function to resolve the cache key. + * @returns {Function} Returns the new memoized function. + * @example + * + * var object = { 'a': 1, 'b': 2 }; + * var other = { 'c': 3, 'd': 4 }; + * + * var values = _.memoize(_.values); + * values(object); + * // => [1, 2] + * + * values(other); + * // => [3, 4] + * + * object.a = 2; + * values(object); + * // => [1, 2] + * + * // Modify the result cache. + * values.cache.set(object, ['a', 'b']); + * values(object); + * // => ['a', 'b'] + * + * // Replace `_.memoize.Cache`. + * _.memoize.Cache = WeakMap; + */ +function memoize(func, resolver) { + if (typeof func != 'function' || (resolver && typeof resolver != 'function')) { + throw new TypeError(FUNC_ERROR_TEXT); + } + var memoized = function() { + var args = arguments, + key = resolver ? resolver.apply(this, args) : args[0], + cache = memoized.cache; + + if (cache.has(key)) { + return cache.get(key); + } + var result = func.apply(this, args); + memoized.cache = cache.set(key, result); + return result; + }; + memoized.cache = new (memoize.Cache || MapCache); + return memoized; +} + +// Assign cache to `_.memoize`. +memoize.Cache = MapCache; + +/** + * Performs a + * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * comparison between two values to determine if they are equivalent. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if the values are equivalent, else `false`. + * @example + * + * var object = { 'a': 1 }; + * var other = { 'a': 1 }; + * + * _.eq(object, object); + * // => true + * + * _.eq(object, other); + * // => false + * + * _.eq('a', 'a'); + * // => true + * + * _.eq('a', Object('a')); + * // => false + * + * _.eq(NaN, NaN); + * // => true + */ +function eq(value, other) { + return value === other || (value !== value && other !== other); +} + +/** + * Checks if `value` is classified as an `Array` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an array, else `false`. + * @example + * + * _.isArray([1, 2, 3]); + * // => true + * + * _.isArray(document.body.children); + * // => false + * + * _.isArray('abc'); + * // => false + * + * _.isArray(_.noop); + * // => false + */ +var isArray = Array.isArray; + +/** + * Checks if `value` is classified as a `Function` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a function, else `false`. + * @example + * + * _.isFunction(_); + * // => true + * + * _.isFunction(/abc/); + * // => false + */ +function isFunction(value) { + // The use of `Object#toString` avoids issues with the `typeof` operator + // in Safari 8-9 which returns 'object' for typed array and other constructors. + var tag = isObject(value) ? objectToString.call(value) : ''; + return tag == funcTag || tag == genTag; +} + +/** + * Checks if `value` is the + * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) + * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an object, else `false`. + * @example + * + * _.isObject({}); + * // => true + * + * _.isObject([1, 2, 3]); + * // => true + * + * _.isObject(_.noop); + * // => true + * + * _.isObject(null); + * // => false + */ +function isObject(value) { + var type = typeof value; + return !!value && (type == 'object' || type == 'function'); +} + +/** + * Checks if `value` is object-like. A value is object-like if it's not `null` + * and has a `typeof` result of "object". + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is object-like, else `false`. + * @example + * + * _.isObjectLike({}); + * // => true + * + * _.isObjectLike([1, 2, 3]); + * // => true + * + * _.isObjectLike(_.noop); + * // => false + * + * _.isObjectLike(null); + * // => false + */ +function isObjectLike(value) { + return !!value && typeof value == 'object'; +} + +/** + * Checks if `value` is classified as a `Symbol` primitive or object. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a symbol, else `false`. + * @example + * + * _.isSymbol(Symbol.iterator); + * // => true + * + * _.isSymbol('abc'); + * // => false + */ +function isSymbol(value) { + return typeof value == 'symbol' || + (isObjectLike(value) && objectToString.call(value) == symbolTag); +} + +/** + * Converts `value` to a string. An empty string is returned for `null` + * and `undefined` values. The sign of `-0` is preserved. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to process. + * @returns {string} Returns the string. + * @example + * + * _.toString(null); + * // => '' + * + * _.toString(-0); + * // => '-0' + * + * _.toString([1, 2, 3]); + * // => '1,2,3' + */ +function toString(value) { + return value == null ? '' : baseToString(value); +} + +/** + * This method is like `_.get` except that if the resolved value is a + * function it's invoked with the `this` binding of its parent object and + * its result is returned. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path of the property to resolve. + * @param {*} [defaultValue] The value returned for `undefined` resolved values. + * @returns {*} Returns the resolved value. + * @example + * + * var object = { 'a': [{ 'b': { 'c1': 3, 'c2': _.constant(4) } }] }; + * + * _.result(object, 'a[0].b.c1'); + * // => 3 + * + * _.result(object, 'a[0].b.c2'); + * // => 4 + * + * _.result(object, 'a[0].b.c3', 'default'); + * // => 'default' + * + * _.result(object, 'a[0].b.c3', _.constant('default')); + * // => 'default' + */ +function result(object, path, defaultValue) { + path = isKey(path, object) ? [path] : castPath(path); + + var index = -1, + length = path.length; + + // Ensure the loop is entered when path is empty. + if (!length) { + object = undefined; + length = 1; + } + while (++index < length) { + var value = object == null ? undefined : object[toKey(path[index])]; + if (value === undefined) { + index = length; + value = defaultValue; + } + object = isFunction(value) ? value.call(object) : value; + } + return object; +} + +module.exports = result; + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{}],3:[function(require,module,exports){ +module.exports={"version":"1.4.0"} +},{}],4:[function(require,module,exports){ +if (typeof Uint8Array !== "undefined") { + var crossfilter_array8 = function(n) { return new Uint8Array(n); }; + var crossfilter_array16 = function(n) { return new Uint16Array(n); }; + var crossfilter_array32 = function(n) { return new Uint32Array(n); }; + + var crossfilter_arrayLengthen = function(array, length) { + if (array.length >= length) return array; + var copy = new array.constructor(length); + copy.set(array); + return copy; + }; + + var crossfilter_arrayWiden = function(array, width) { + var copy; + switch (width) { + case 16: copy = crossfilter_array16(array.length); break; + case 32: copy = crossfilter_array32(array.length); break; + default: throw new Error("invalid array width!"); + } + copy.set(array); + return copy; + }; +} + +function crossfilter_arrayUntyped(n) { + var array = new Array(n), i = -1; + while (++i < n) array[i] = 0; + return array; +} + +function crossfilter_arrayLengthenUntyped(array, length) { + var n = array.length; + while (n < length) array[n++] = 0; + return array; +} + +function crossfilter_arrayWidenUntyped(array, width) { + if (width > 32) throw new Error("invalid array width!"); + return array; +} + +// An arbitrarily-wide array of bitmasks +function crossfilter_bitarray(n) { + this.length = n; + this.subarrays = 1; + this.width = 8; + this.masks = { + 0: 0 + } + + this[0] = crossfilter_array8(n); +} + +crossfilter_bitarray.prototype.lengthen = function(n) { + var i, len; + for (i = 0, len = this.subarrays; i < len; ++i) { + this[i] = crossfilter_arrayLengthen(this[i], n); + } + this.length = n; +}; + +// Reserve a new bit index in the array, returns {offset, one} +crossfilter_bitarray.prototype.add = function() { + var m, w, one, i, len; + + for (i = 0, len = this.subarrays; i < len; ++i) { + m = this.masks[i]; + w = this.width - (32 * i); + one = ~m & -~m; + + if (w >= 32 && !one) { + continue; + } + + if (w < 32 && (one & (1 << w))) { + // widen this subarray + this[i] = crossfilter_arrayWiden(this[i], w <<= 1); + this.width = 32 * i + w; + } + + this.masks[i] |= one; + + return { + offset: i, + one: one + }; + } + + // add a new subarray + this[this.subarrays] = crossfilter_array8(this.length); + this.masks[this.subarrays] = 1; + this.width += 8; + return { + offset: this.subarrays++, + one: 1 + }; +}; + +// Copy record from index src to index dest +crossfilter_bitarray.prototype.copy = function(dest, src) { + var i, len; + for (i = 0, len = this.subarrays; i < len; ++i) { + this[i][dest] = this[i][src]; + } +}; + +// Truncate the array to the given length +crossfilter_bitarray.prototype.truncate = function(n) { + var i, len; + for (i = 0, len = this.subarrays; i < len; ++i) { + for (var j = this.length - 1; j >= n; j--) { + this[i][j] = 0; + } + this[i].length = n; + } + this.length = n; +}; + +// Checks that all bits for the given index are 0 +crossfilter_bitarray.prototype.zero = function(n) { + var i, len; + for (i = 0, len = this.subarrays; i < len; ++i) { + if (this[i][n]) { + return false; + } + } + return true; +}; + +// Checks that all bits for the given index are 0 except for possibly one +crossfilter_bitarray.prototype.zeroExcept = function(n, offset, zero) { + var i, len; + for (i = 0, len = this.subarrays; i < len; ++i) { + if (i === offset ? this[i][n] & zero : this[i][n]) { + return false; + } + } + return true; +}; + +// Checks that all bits for the given indez are 0 except for the specified mask. +// The mask should be an array of the same size as the filter subarrays width. +crossfilter_bitarray.prototype.zeroExceptMask = function(n, mask) { + var i, len; + for (i = 0, len = this.subarrays; i < len; ++i) { + if (this[i][n] & mask[i]) { + return false; + } + } + return true; +} + +// Checks that only the specified bit is set for the given index +crossfilter_bitarray.prototype.only = function(n, offset, one) { + var i, len; + for (i = 0, len = this.subarrays; i < len; ++i) { + if (this[i][n] != (i === offset ? one : 0)) { + return false; + } + } + return true; +}; + +// Checks that only the specified bit is set for the given index except for possibly one other +crossfilter_bitarray.prototype.onlyExcept = function(n, offset, zero, onlyOffset, onlyOne) { + var mask; + var i, len; + for (i = 0, len = this.subarrays; i < len; ++i) { + mask = this[i][n]; + if (i === offset) + mask &= zero; + if (mask != (i === onlyOffset ? onlyOne : 0)) { + return false; + } + } + return true; +}; + +module.exports = { + array8: crossfilter_arrayUntyped, + array16: crossfilter_arrayUntyped, + array32: crossfilter_arrayUntyped, + arrayLengthen: crossfilter_arrayLengthenUntyped, + arrayWiden: crossfilter_arrayWidenUntyped, + bitarray: crossfilter_bitarray +}; + +},{}],5:[function(require,module,exports){ +'use strict'; + +var crossfilter_identity = require('./identity'); + +function bisect_by(f) { + + // Locate the insertion point for x in a to maintain sorted order. The + // arguments lo and hi may be used to specify a subset of the array which + // should be considered; by default the entire array is used. If x is already + // present in a, the insertion point will be before (to the left of) any + // existing entries. The return value is suitable for use as the first + // argument to `array.splice` assuming that a is already sorted. + // + // The returned insertion point i partitions the array a into two halves so + // that all v < x for v in a[lo:i] for the left side and all v >= x for v in + // a[i:hi] for the right side. + function bisectLeft(a, x, lo, hi) { + while (lo < hi) { + var mid = lo + hi >>> 1; + if (f(a[mid]) < x) lo = mid + 1; + else hi = mid; + } + return lo; + } + + // Similar to bisectLeft, but returns an insertion point which comes after (to + // the right of) any existing entries of x in a. + // + // The returned insertion point i partitions the array into two halves so that + // all v <= x for v in a[lo:i] for the left side and all v > x for v in + // a[i:hi] for the right side. + function bisectRight(a, x, lo, hi) { + while (lo < hi) { + var mid = lo + hi >>> 1; + if (x < f(a[mid])) hi = mid; + else lo = mid + 1; + } + return lo; + } + + bisectRight.right = bisectRight; + bisectRight.left = bisectLeft; + return bisectRight; +} + +module.exports = bisect_by(crossfilter_identity); +module.exports.by = bisect_by; // assign the raw function to the export as well + +},{"./identity":10}],6:[function(require,module,exports){ +'use strict'; + +var xfilterArray = require('./array'); +var xfilterFilter = require('./filter'); +var crossfilter_identity = require('./identity'); +var crossfilter_null = require('./null'); +var crossfilter_zero = require('./zero'); +var xfilterHeapselect = require('./heapselect'); +var xfilterHeap = require('./heap'); +var bisect = require('./bisect'); +var insertionsort = require('./insertionsort'); +var permute = require('./permute'); +var quicksort = require('./quicksort'); +var xfilterReduce = require('./reduce'); +var packageJson = require('./../package.json'); // require own package.json for the version field +var result = require('lodash.result'); +// expose API exports +exports.crossfilter = crossfilter; +exports.crossfilter.heap = xfilterHeap; +exports.crossfilter.heapselect = xfilterHeapselect; +exports.crossfilter.bisect = bisect; +exports.crossfilter.insertionsort = insertionsort; +exports.crossfilter.permute = permute; +exports.crossfilter.quicksort = quicksort; +exports.crossfilter.version = packageJson.version; // please note use of "package-json-versionify" transform + +function crossfilter() { + var crossfilter = { + add: add, + remove: removeData, + dimension: dimension, + groupAll: groupAll, + size: size, + all: all, + allFiltered: allFiltered, + onChange: onChange, + isElementFiltered: isElementFiltered + }; + + var data = [], // the records + n = 0, // the number of records; data.length + filters, // 1 is filtered out + filterListeners = [], // when the filters change + dataListeners = [], // when data is added + removeDataListeners = [], // when data is removed + callbacks = []; + + filters = new xfilterArray.bitarray(0); + + // Adds the specified new records to this crossfilter. + function add(newData) { + var n0 = n, + n1 = newData.length; + + // If there's actually new data to add… + // Merge the new data into the existing data. + // Lengthen the filter bitset to handle the new records. + // Notify listeners (dimensions and groups) that new data is available. + if (n1) { + data = data.concat(newData); + filters.lengthen(n += n1); + dataListeners.forEach(function(l) { l(newData, n0, n1); }); + triggerOnChange('dataAdded'); + } + + return crossfilter; + } + + // Removes all records that match the current filters. + function removeData() { + var newIndex = crossfilter_index(n, n), + removed = []; + + for (var index1 = 0, index2 = 0; index1 < n; ++index1) { + if (!filters.zero(index1)) newIndex[index1] = index2++; + else removed.push(index1); + } + + // Remove all matching records from groups. + filterListeners.forEach(function(l) { l(-1, -1, [], removed, true); }); + + // Update indexes. + removeDataListeners.forEach(function(l) { l(newIndex); }); + + // Remove old filters and data by overwriting. + for (var index3 = 0, index4 = 0; index3 < n; ++index3) { + if (!filters.zero(index3)) { + if (index3 !== index4) filters.copy(index4, index3), data[index4] = data[index3]; + ++index4; + } + } + + data.length = n = index4; + filters.truncate(index4); + triggerOnChange('dataRemoved'); + } + + // Return true if the data element at index i is filtered IN. + // Optionally, ignore the filters of any dimensions in the ignore_dimensions list. + function isElementFiltered(i, ignore_dimensions) { + var n, + d, + id, + len, + mask = Array(filters.subarrays); + for (n = 0; n < filters.subarrays; n++) { mask[n] = ~0; } + if (ignore_dimensions) { + for (d = 0, len = ignore_dimensions.length; d < len; d++) { + // The top bits of the ID are the subarray offset and the lower bits are the bit + // offset of the "one" mask. + id = ignore_dimensions[d].id(); + mask[id >> 7] &= ~(0x1 << (id & 0x3f)); + } + } + return filters.zeroExceptMask(i,mask); + } + + // Adds a new dimension with the specified value accessor function. + function dimension(value, iterable) { + + if (typeof value === 'string') { + var accessorPath = value; + value = function(d) { return result(d, accessorPath); }; + } + + var dimension = { + filter: filter, + filterExact: filterExact, + filterRange: filterRange, + filterFunction: filterFunction, + filterAll: filterAll, + top: top, + bottom: bottom, + group: group, + groupAll: groupAll, + dispose: dispose, + remove: dispose, // for backwards-compatibility + accessor: value, + id: function() { return id; } + }; + + var one, // lowest unset bit as mask, e.g., 00001000 + zero, // inverted one, e.g., 11110111 + offset, // offset into the filters arrays + id, // unique ID for this dimension (reused when dimensions are disposed) + values, // sorted, cached array + index, // value rank ↦ object id + newValues, // temporary array storing newly-added values + newIndex, // temporary array storing newly-added index + iterablesIndexCount, + newIterablesIndexCount, + iterablesIndexFilterStatus, + newIterablesIndexFilterStatus, + iterablesEmptyRows, + sort = quicksort.by(function(i) { return newValues[i]; }), + refilter = xfilterFilter.filterAll, // for recomputing filter + refilterFunction, // the custom filter function in use + indexListeners = [], // when data is added + dimensionGroups = [], + lo0 = 0, + hi0 = 0, + t = 0, + k; + + // Updating a dimension is a two-stage process. First, we must update the + // associated filters for the newly-added records. Once all dimensions have + // updated their filters, the groups are notified to update. + dataListeners.unshift(preAdd); + dataListeners.push(postAdd); + + removeDataListeners.push(removeData); + + // Add a new dimension in the filter bitmap and store the offset and bitmask. + var tmp = filters.add(); + offset = tmp.offset; + one = tmp.one; + zero = ~one; + + // Create a unique ID for the dimension + // IDs will be re-used if dimensions are disposed. + // For internal use the ID is the subarray offset shifted left 7 bits or'd with the + // bit offset of the set bit in the dimension's "one" mask. + id = (offset << 7) | (Math.log(one) / Math.log(2)); + + preAdd(data, 0, n); + postAdd(data, 0, n); + + // Incorporates the specified new records into this dimension. + // This function is responsible for updating filters, values, and index. + function preAdd(newData, n0, n1) { + + if (iterable){ + // Count all the values + t = 0; + j = 0; + k = []; + + for (var i0 = 0; i0 < newData.length; i0++) { + for(j = 0, k = value(newData[i0]); j < k.length; j++) { + t++; + } + } + + newValues = []; + newIterablesIndexCount = crossfilter_range(newData.length); + newIterablesIndexFilterStatus = crossfilter_index(t,1); + iterablesEmptyRows = []; + var unsortedIndex = crossfilter_range(t); + + for (var l = 0, index1 = 0; index1 < newData.length; index1++) { + k = value(newData[index1]) + // + if(!k.length){ + newIterablesIndexCount[index1] = 0; + iterablesEmptyRows.push(index1); + continue; + } + newIterablesIndexCount[index1] = k.length + for (j = 0; j < k.length; j++) { + newValues.push(k[j]); + unsortedIndex[l] = index1; + l++; + } + } + + // Create the Sort map used to sort both the values and the valueToData indices + var sortMap = sort(crossfilter_range(t), 0, t); + + // Use the sortMap to sort the newValues + newValues = permute(newValues, sortMap); + + + // Use the sortMap to sort the unsortedIndex map + // newIndex should be a map of sortedValue -> crossfilterData + newIndex = permute(unsortedIndex, sortMap) + + } else{ + // Permute new values into natural order using a standard sorted index. + newValues = newData.map(value); + newIndex = sort(crossfilter_range(n1), 0, n1); + newValues = permute(newValues, newIndex); + } + + if(iterable) { + n1 = t; + } + + // Bisect newValues to determine which new records are selected. + var bounds = refilter(newValues), lo1 = bounds[0], hi1 = bounds[1]; + if (refilterFunction) { + for (var index2 = 0; index2 < n1; ++index2) { + if (!refilterFunction(newValues[index2], index2)) { + filters[offset][newIndex[index2] + n0] |= one; + if(iterable) newIterablesIndexFilterStatus[index2] = 1; + } + } + } else { + for (var index3 = 0; index3 < lo1; ++index3) { + filters[offset][newIndex[index3] + n0] |= one; + if(iterable) newIterablesIndexFilterStatus[index3] = 1; + } + for (var index4 = hi1; index4 < n1; ++index4) { + filters[offset][newIndex[index4] + n0] |= one; + if(iterable) newIterablesIndexFilterStatus[index4] = 1; + } + } + + // If this dimension previously had no data, then we don't need to do the + // more expensive merge operation; use the new values and index as-is. + if (!n0) { + values = newValues; + index = newIndex; + iterablesIndexCount = newIterablesIndexCount; + iterablesIndexFilterStatus = newIterablesIndexFilterStatus; + lo0 = lo1; + hi0 = hi1; + return; + } + + + + var oldValues = values, + oldIndex = index, + oldIterablesIndexFilterStatus = iterablesIndexFilterStatus, + old_n0, + i1 = 0; + + i0 = 0; + + if(iterable){ + old_n0 = n0 + n0 = oldValues.length; + n1 = t + } + + // Otherwise, create new arrays into which to merge new and old. + values = iterable ? new Array(n0 + n1) : new Array(n); + index = iterable ? new Array(n0 + n1) : crossfilter_index(n, n); + if(iterable) iterablesIndexFilterStatus = crossfilter_index(n0 + n1, 1); + + // Concatenate the newIterablesIndexCount onto the old one. + if(iterable) { + var oldiiclength = iterablesIndexCount.length; + iterablesIndexCount = xfilterArray.arrayLengthen(iterablesIndexCount, n); + for(var j=0; j+oldiiclength < n; j++) { + iterablesIndexCount[j+oldiiclength] = newIterablesIndexCount[j]; + } + } + + // Merge the old and new sorted values, and old and new index. + var index5 = 0; + for (; i0 < n0 && i1 < n1; ++index5) { + if (oldValues[i0] < newValues[i1]) { + values[index5] = oldValues[i0]; + if(iterable) iterablesIndexFilterStatus[index5] = oldIterablesIndexFilterStatus[i0]; + index[index5] = oldIndex[i0++]; + } else { + values[index5] = newValues[i1]; + if(iterable) iterablesIndexFilterStatus[index5] = oldIterablesIndexFilterStatus[i1]; + index[index5] = newIndex[i1++] + (iterable ? old_n0 : n0); + } + } + + // Add any remaining old values. + for (; i0 < n0; ++i0, ++index5) { + values[index5] = oldValues[i0]; + if(iterable) iterablesIndexFilterStatus[index5] = oldIterablesIndexFilterStatus[i0]; + index[index5] = oldIndex[i0]; + } + + // Add any remaining new values. + for (; i1 < n1; ++i1, ++index5) { + values[index5] = newValues[i1]; + if(iterable) iterablesIndexFilterStatus[index5] = oldIterablesIndexFilterStatus[i1]; + index[index5] = newIndex[i1] + (iterable ? old_n0 : n0); + } + + // Bisect again to recompute lo0 and hi0. + bounds = refilter(values), lo0 = bounds[0], hi0 = bounds[1]; + } + + // When all filters have updated, notify index listeners of the new values. + function postAdd(newData, n0, n1) { + indexListeners.forEach(function(l) { l(newValues, newIndex, n0, n1); }); + newValues = newIndex = null; + } + + function removeData(reIndex) { + for (var i = 0, j = 0, k; i < n; ++i) { + if (!filters.zero(k = index[i])) { + if (i !== j) values[j] = values[i]; + index[j] = reIndex[k]; + ++j; + } + } + values.length = j; + while (j < n) index[j++] = 0; + + // Bisect again to recompute lo0 and hi0. + var bounds = refilter(values); + lo0 = bounds[0], hi0 = bounds[1]; + } + + // Updates the selected values based on the specified bounds [lo, hi]. + // This implementation is used by all the public filter methods. + function filterIndexBounds(bounds) { + + var lo1 = bounds[0], + hi1 = bounds[1]; + + if (refilterFunction) { + refilterFunction = null; + filterIndexFunction(function(d, i) { return lo1 <= i && i < hi1; }, bounds[0] === 0 && bounds[1] === index.length); + lo0 = lo1; + hi0 = hi1; + return dimension; + } + + var i, + j, + k, + added = [], + removed = [], + valueIndexAdded = [], + valueIndexRemoved = []; + + + // Fast incremental update based on previous lo index. + if (lo1 < lo0) { + for (i = lo1, j = Math.min(lo0, hi1); i < j; ++i) { + added.push(index[i]); + valueIndexAdded.push(i); + } + } else if (lo1 > lo0) { + for (i = lo0, j = Math.min(lo1, hi0); i < j; ++i) { + removed.push(index[i]); + valueIndexRemoved.push(i); + } + } + + // Fast incremental update based on previous hi index. + if (hi1 > hi0) { + for (i = Math.max(lo1, hi0), j = hi1; i < j; ++i) { + added.push(index[i]); + valueIndexAdded.push(i); + } + } else if (hi1 < hi0) { + for (i = Math.max(lo0, hi1), j = hi0; i < j; ++i) { + removed.push(index[i]); + valueIndexRemoved.push(i); + } + } + + if(!iterable) { + // Flip filters normally. + + for(i=0; i<added.length; i++) { + filters[offset][added[i]] ^= one; + } + + for(i=0; i<removed.length; i++) { + filters[offset][removed[i]] ^= one; + } + + } else { + // For iterables, we need to figure out if the row has been completely removed vs partially included + // Only count a row as added if it is not already being aggregated. Only count a row + // as removed if the last element being aggregated is removed. + + var newAdded = []; + var newRemoved = []; + for (i = 0; i < added.length; i++) { + iterablesIndexCount[added[i]]++ + iterablesIndexFilterStatus[valueIndexAdded[i]] = 0; + if(iterablesIndexCount[added[i]] === 1) { + filters[offset][added[i]] ^= one; + newAdded.push(added[i]); + } + } + for (i = 0; i < removed.length; i++) { + iterablesIndexCount[removed[i]]-- + iterablesIndexFilterStatus[valueIndexRemoved[i]] = 1; + if(iterablesIndexCount[removed[i]] === 0) { + filters[offset][removed[i]] ^= one; + newRemoved.push(removed[i]); + } + } + + added = newAdded; + removed = newRemoved; + + // Now handle empty rows. + if(bounds[0] === 0 && bounds[1] === index.length) { + for(i = 0; i < iterablesEmptyRows.length; i++) { + if((filters[offset][k = iterablesEmptyRows[i]] & one)) { + // Was not in the filter, so set the filter and add + filters[offset][k] ^= one; + added.push(k); + } + } + } else { + // filter in place - remove empty rows if necessary + for(i = 0; i < iterablesEmptyRows.length; i++) { + if(!(filters[offset][k = iterablesEmptyRows[i]] & one)) { + // Was in the filter, so set the filter and remove + filters[offset][k] ^= one; + removed.push(k); + } + } + } + } + + lo0 = lo1; + hi0 = hi1; + filterListeners.forEach(function(l) { l(one, offset, added, removed); }); + triggerOnChange('filtered'); + return dimension; + } + + // Filters this dimension using the specified range, value, or null. + // If the range is null, this is equivalent to filterAll. + // If the range is an array, this is equivalent to filterRange. + // Otherwise, this is equivalent to filterExact. + function filter(range) { + return range == null + ? filterAll() : Array.isArray(range) + ? filterRange(range) : typeof range === "function" + ? filterFunction(range) + : filterExact(range); + } + + // Filters this dimension to select the exact value. + function filterExact(value) { + return filterIndexBounds((refilter = xfilterFilter.filterExact(bisect, value))(values)); + } + + // Filters this dimension to select the specified range [lo, hi]. + // The lower bound is inclusive, and the upper bound is exclusive. + function filterRange(range) { + return filterIndexBounds((refilter = xfilterFilter.filterRange(bisect, range))(values)); + } + + // Clears any filters on this dimension. + function filterAll() { + return filterIndexBounds((refilter = xfilterFilter.filterAll)(values)); + } + + // Filters this dimension using an arbitrary function. + function filterFunction(f) { + refilterFunction = f; + refilter = xfilterFilter.filterAll; + + filterIndexFunction(f, false); + + lo0 = 0; + hi0 = n; + + return dimension; + } + + function filterIndexFunction(f, filterAll) { + var i, + k, + x, + added = [], + removed = [], + valueIndexAdded = [], + valueIndexRemoved = [], + indexLength = index.length; + + if(!iterable) { + for (i = 0; i < indexLength; ++i) { + if (!(filters[offset][k = index[i]] & one) ^ !!(x = f(values[i], i))) { + if (x) added.push(k); + else removed.push(k); + } + } + } + + if(iterable) { + for(i=0; i < indexLength; ++i) { + if(f(values[i], i)) { + added.push(index[i]); + valueIndexAdded.push(i); + } else { + removed.push(index[i]); + valueIndexRemoved.push(i); + } + } + } + + if(!iterable) { + for(i=0; i<added.length; i++) { + if(filters[offset][added[i]] & one) filters[offset][added[i]] &= zero; + } + + for(i=0; i<removed.length; i++) { + if(!(filters[offset][removed[i]] & one)) filters[offset][removed[i]] |= one; + } + } else { + + var newAdded = []; + var newRemoved = []; + for (i = 0; i < added.length; i++) { + // First check this particular value needs to be added + if(iterablesIndexFilterStatus[valueIndexAdded[i]] === 1) { + iterablesIndexCount[added[i]]++ + iterablesIndexFilterStatus[valueIndexAdded[i]] = 0; + if(iterablesIndexCount[added[i]] === 1) { + filters[offset][added[i]] ^= one; + newAdded.push(added[i]); + } + } + } + for (i = 0; i < removed.length; i++) { + // First check this particular value needs to be removed + if(iterablesIndexFilterStatus[valueIndexRemoved[i]] === 0) { + iterablesIndexCount[removed[i]]-- + iterablesIndexFilterStatus[valueIndexRemoved[i]] = 1; + if(iterablesIndexCount[removed[i]] === 0) { + filters[offset][removed[i]] ^= one; + newRemoved.push(removed[i]); + } + } + } + + added = newAdded; + removed = newRemoved; + + // Now handle empty rows. + if(filterAll) { + for(i = 0; i < iterablesEmptyRows.length; i++) { + if((filters[offset][k = iterablesEmptyRows[i]] & one)) { + // Was not in the filter, so set the filter and add + filters[offset][k] ^= one; + added.push(k); + } + } + } else { + // filter in place - remove empty rows if necessary + for(i = 0; i < iterablesEmptyRows.length; i++) { + if(!(filters[offset][k = iterablesEmptyRows[i]] & one)) { + // Was in the filter, so set the filter and remove + filters[offset][k] ^= one; + removed.push(k); + } + } + } + } + + filterListeners.forEach(function(l) { l(one, offset, added, removed); }); + triggerOnChange('filtered'); + } + + // Returns the top K selected records based on this dimension's order. + // Note: observes this dimension's filter, unlike group and groupAll. + function top(k, top_offset) { + var array = [], + i = hi0, + j, + toSkip = 0; + + if(top_offset && top_offset > 0) toSkip = top_offset; + + while (--i >= lo0 && k > 0) { + if (filters.zero(j = index[i])) { + if(toSkip > 0) { + //skip matching row + --toSkip; + } else { + array.push(data[j]); + --k; + } + } + } + + if(iterable){ + for(i = 0; i < iterablesEmptyRows.length && k > 0; i++) { + // Add row with empty iterable column at the end + if(filters.zero(j = iterablesEmptyRows[i])) { + if(toSkip > 0) { + //skip matching row + --toSkip; + } else { + array.push(data[j]); + --k; + } + } + } + } + + return array; + } + + // Returns the bottom K selected records based on this dimension's order. + // Note: observes this dimension's filter, unlike group and groupAll. + function bottom(k, bottom_offset) { + var array = [], + i, + j, + toSkip = 0; + + if(bottom_offset && bottom_offset > 0) toSkip = bottom_offset; + + if(iterable) { + // Add row with empty iterable column at the top + for(i = 0; i < iterablesEmptyRows.length && k > 0; i++) { + if(filters.zero(j = iterablesEmptyRows[i])) { + if(toSkip > 0) { + //skip matching row + --toSkip; + } else { + array.push(data[j]); + --k; + } + } + } + } + + i = lo0; + + while (i < hi0 && k > 0) { + if (filters.zero(j = index[i])) { + if(toSkip > 0) { + //skip matching row + --toSkip; + } else { + array.push(data[j]); + --k; + } + } + i++; + } + + return array; + } + + // Adds a new group to this dimension, using the specified key function. + function group(key) { + var group = { + top: top, + all: all, + reduce: reduce, + reduceCount: reduceCount, + reduceSum: reduceSum, + order: order, + orderNatural: orderNatural, + size: size, + dispose: dispose, + remove: dispose // for backwards-compatibility + }; + + // Ensure that this group will be removed when the dimension is removed. + dimensionGroups.push(group); + + var groups, // array of {key, value} + groupIndex, // object id ↦ group id + groupWidth = 8, + groupCapacity = crossfilter_capacity(groupWidth), + k = 0, // cardinality + select, + heap, + reduceAdd, + reduceRemove, + reduceInitial, + update = crossfilter_null, + reset = crossfilter_null, + resetNeeded = true, + groupAll = key === crossfilter_null, + n0old; + + if (arguments.length < 1) key = crossfilter_identity; + + // The group listens to the crossfilter for when any dimension changes, so + // that it can update the associated reduce values. It must also listen to + // the parent dimension for when data is added, and compute new keys. + filterListeners.push(update); + indexListeners.push(add); + removeDataListeners.push(removeData); + + // Incorporate any existing data into the grouping. + add(values, index, 0, n); + + // Incorporates the specified new values into this group. + // This function is responsible for updating groups and groupIndex. + function add(newValues, newIndex, n0, n1) { + + if(iterable) { + n0old = n0 + n0 = values.length - newValues.length + n1 = newValues.length; + } + + var oldGroups = groups, + reIndex = iterable ? [] : crossfilter_index(k, groupCapacity), + add = reduceAdd, + remove = reduceRemove, + initial = reduceInitial, + k0 = k, // old cardinality + i0 = 0, // index of old group + i1 = 0, // index of new record + j, // object id + g0, // old group + x0, // old key + x1, // new key + g, // group to add + x; // key of group to add + + // If a reset is needed, we don't need to update the reduce values. + if (resetNeeded) add = initial = crossfilter_null; + if (resetNeeded) remove = initial = crossfilter_null; + + // Reset the new groups (k is a lower bound). + // Also, make sure that groupIndex exists and is long enough. + groups = new Array(k), k = 0; + if(iterable){ + groupIndex = k0 > 1 ? groupIndex : []; + } + else{ + groupIndex = k0 > 1 ? xfilterArray.arrayLengthen(groupIndex, n) : crossfilter_index(n, groupCapacity); + } + + + // Get the first old key (x0 of g0), if it exists. + if (k0) x0 = (g0 = oldGroups[0]).key; + + // Find the first new key (x1), skipping NaN keys. + while (i1 < n1 && !((x1 = key(newValues[i1])) >= x1)) ++i1; + + // While new keys remain… + while (i1 < n1) { + + // Determine the lesser of the two current keys; new and old. + // If there are no old keys remaining, then always add the new key. + if (g0 && x0 <= x1) { + g = g0, x = x0; + + // Record the new index of the old group. + reIndex[i0] = k; + + // Retrieve the next old key. + g0 = oldGroups[++i0]; + if (g0) x0 = g0.key; + } else { + g = {key: x1, value: initial()}, x = x1; + } + + // Add the lesser group. + groups[k] = g; + + // Add any selected records belonging to the added group, while + // advancing the new key and populating the associated group index. + + while (x1 <= x) { + j = newIndex[i1] + (iterable ? n0old : n0) + + + if(iterable){ + if(groupIndex[j]){ + groupIndex[j].push(k) + } + else{ + groupIndex[j] = [k] + } + } + else{ + groupIndex[j] = k; + } + + // Always add new values to groups. Only remove when not in filter. + // This gives groups full information on data life-cycle. + g.value = add(g.value, data[j], true); + if (!filters.zeroExcept(j, offset, zero)) g.value = remove(g.value, data[j], false); + if (++i1 >= n1) break; + x1 = key(newValues[i1]); + } + + groupIncrement(); + } + + // Add any remaining old groups that were greater th1an all new keys. + // No incremental reduce is needed; these groups have no new records. + // Also record the new index of the old group. + while (i0 < k0) { + groups[reIndex[i0] = k] = oldGroups[i0++]; + groupIncrement(); + } + + + // Fill in gaps with empty arrays where there may have been rows with empty iterables + if(iterable){ + for (var index1 = 0; index1 < n; index1++) { + if(!groupIndex[index1]){ + groupIndex[index1] = []; + } + } + } + + // If we added any new groups before any old groups, + // update the group index of all the old records. + if(k > i0){ + if(iterable){ + groupIndex = permute(groupIndex, reIndex, true) + } + else{ + for (i0 = 0; i0 < n0; ++i0) { + groupIndex[i0] = reIndex[groupIndex[i0]]; + } + } + } + + // Modify the update and reset behavior based on the cardinality. + // If the cardinality is less than or equal to one, then the groupIndex + // is not needed. If the cardinality is zero, then there are no records + // and therefore no groups to update or reset. Note that we also must + // change the registered listener to point to the new method. + j = filterListeners.indexOf(update); + if (k > 1) { + update = updateMany; + reset = resetMany; + } else { + if (!k && groupAll) { + k = 1; + groups = [{key: null, value: initial()}]; + } + if (k === 1) { + update = updateOne; + reset = resetOne; + } else { + update = crossfilter_null; + reset = crossfilter_null; + } + groupIndex = null; + } + filterListeners[j] = update; + + // Count the number of added groups, + // and widen the group index as needed. + function groupIncrement() { + if(iterable){ + k++ + return + } + if (++k === groupCapacity) { + reIndex = xfilterArray.arrayWiden(reIndex, groupWidth <<= 1); + groupIndex = xfilterArray.arrayWiden(groupIndex, groupWidth); + groupCapacity = crossfilter_capacity(groupWidth); + } + } + } + + function removeData() { + if (k > 1) { + var oldK = k, + oldGroups = groups, + seenGroups = crossfilter_index(oldK, oldK); + + // Filter out non-matches by copying matching group index entries to + // the beginning of the array. + for (var i = 0, j = 0; i < n; ++i) { + if (!filters.zero(i)) { + seenGroups[groupIndex[j] = groupIndex[i]] = 1; + ++j; + } + } + + // Reassemble groups including only those groups that were referred + // to by matching group index entries. Note the new group index in + // seenGroups. + groups = [], k = 0; + for (i = 0; i < oldK; ++i) { + if (seenGroups[i]) { + seenGroups[i] = k++; + groups.push(oldGroups[i]); + } + } + + if (k > 1) { + // Reindex the group index using seenGroups to find the new index. + for (var index2 = 0; index2 < j; ++index2) groupIndex[index2] = seenGroups[groupIndex[index2]]; + } else { + groupIndex = null; + } + filterListeners[filterListeners.indexOf(update)] = k > 1 + ? (reset = resetMany, update = updateMany) + : k === 1 ? (reset = resetOne, update = updateOne) + : reset = update = crossfilter_null; + } else if (k === 1) { + if (groupAll) return; + for (var index3 = 0; index3 < n; ++index3) if (!filters.zero(index3)) return; + groups = [], k = 0; + filterListeners[filterListeners.indexOf(update)] = + update = reset = crossfilter_null; + } + } + + // Reduces the specified selected or deselected records. + // This function is only used when the cardinality is greater than 1. + // notFilter indicates a crossfilter.add/remove operation. + function updateMany(filterOne, filterOffset, added, removed, notFilter) { + + if ((filterOne === one && filterOffset === offset) || resetNeeded) return; + + var i, + j, + k, + n, + g; + + if(iterable){ + // Add the added values. + for (i = 0, n = added.length; i < n; ++i) { + if (filters.zeroExcept(k = added[i], offset, zero)) { + for (j = 0; j < groupIndex[k].length; j++) { + g = groups[groupIndex[k][j]]; + g.value = reduceAdd(g.value, data[k], false, j); + } + } + } + + // Remove the removed values. + for (i = 0, n = removed.length; i < n; ++i) { + if (filters.onlyExcept(k = removed[i], offset, zero, filterOffset, filterOne)) { + for (j = 0; j < groupIndex[k].length; j++) { + g = groups[groupIndex[k][j]]; + g.value = reduceRemove(g.value, data[k], notFilter, j); + } + } + } + return; + } + + // Add the added values. + for (i = 0, n = added.length; i < n; ++i) { + if (filters.zeroExcept(k = added[i], offset, zero)) { + g = groups[groupIndex[k]]; + g.value = reduceAdd(g.value, data[k], false); + } + } + + // Remove the removed values. + for (i = 0, n = removed.length; i < n; ++i) { + if (filters.onlyExcept(k = removed[i], offset, zero, filterOffset, filterOne)) { + g = groups[groupIndex[k]]; + g.value = reduceRemove(g.value, data[k], notFilter); + } + } + } + + // Reduces the specified selected or deselected records. + // This function is only used when the cardinality is 1. + // notFilter indicates a crossfilter.add/remove operation. + function updateOne(filterOne, filterOffset, added, removed, notFilter) { + if ((filterOne === one && filterOffset === offset) || resetNeeded) return; + + var i, + k, + n, + g = groups[0]; + + // Add the added values. + for (i = 0, n = added.length; i < n; ++i) { + if (filters.zeroExcept(k = added[i], offset, zero)) { + g.value = reduceAdd(g.value, data[k], false); + } + } + + // Remove the removed values. + for (i = 0, n = removed.length; i < n; ++i) { + if (filters.onlyExcept(k = removed[i], offset, zero, filterOffset, filterOne)) { + g.value = reduceRemove(g.value, data[k], notFilter); + } + } + } + + // Recomputes the group reduce values from scratch. + // This function is only used when the cardinality is greater than 1. + function resetMany() { + var i, + j, + g; + + // Reset all group values. + for (i = 0; i < k; ++i) { + groups[i].value = reduceInitial(); + } + + // We add all records and then remove filtered records so that reducers + // can build an 'unfiltered' view even if there are already filters in + // place on other dimensions. + if(iterable){ + for (i = 0; i < n; ++i) { + for (j = 0; j < groupIndex[i].length; j++) { + g = groups[groupIndex[i][j]]; + g.value = reduceAdd(g.value, data[i], true, j); + } + } + for (i = 0; i < n; ++i) { + if (!filters.zeroExcept(i, offset, zero)) { + for (j = 0; j < groupIndex[i].length; j++) { + g = groups[groupIndex[i][j]]; + g.value = reduceRemove(g.value, data[i], false, j); + } + } + } + return; + } + + for (i = 0; i < n; ++i) { + g = groups[groupIndex[i]]; + g.value = reduceAdd(g.value, data[i], true); + } + for (i = 0; i < n; ++i) { + if (!filters.zeroExcept(i, offset, zero)) { + g = groups[groupIndex[i]]; + g.value = reduceRemove(g.value, data[i], false); + } + } + } + + // Recomputes the group reduce values from scratch. + // This function is only used when the cardinality is 1. + function resetOne() { + var i, + g = groups[0]; + + // Reset the singleton group values. + g.value = reduceInitial(); + + // We add all records and then remove filtered records so that reducers + // can build an 'unfiltered' view even if there are already filters in + // place on other dimensions. + for (i = 0; i < n; ++i) { + g.value = reduceAdd(g.value, data[i], true); + } + + for (i = 0; i < n; ++i) { + if (!filters.zeroExcept(i, offset, zero)) { + g.value = reduceRemove(g.value, data[i], false); + } + } + } + + // Returns the array of group values, in the dimension's natural order. + function all() { + if (resetNeeded) reset(), resetNeeded = false; + return groups; + } + + // Returns a new array containing the top K group values, in reduce order. + function top(k) { + var top = select(all(), 0, groups.length, k); + return heap.sort(top, 0, top.length); + } + + // Sets the reduce behavior for this group to use the specified functions. + // This method lazily recomputes the reduce values, waiting until needed. + function reduce(add, remove, initial) { + reduceAdd = add; + reduceRemove = remove; + reduceInitial = initial; + resetNeeded = true; + return group; + } + + // A convenience method for reducing by count. + function reduceCount() { + return reduce(xfilterReduce.reduceIncrement, xfilterReduce.reduceDecrement, crossfilter_zero); + } + + // A convenience method for reducing by sum(value). + function reduceSum(value) { + return reduce(xfilterReduce.reduceAdd(value), xfilterReduce.reduceSubtract(value), crossfilter_zero); + } + + // Sets the reduce order, using the specified accessor. + function order(value) { + select = xfilterHeapselect.by(valueOf); + heap = xfilterHeap.by(valueOf); + function valueOf(d) { return value(d.value); } + return group; + } + + // A convenience method for natural ordering by reduce value. + function orderNatural() { + return order(crossfilter_identity); + } + + // Returns the cardinality of this group, irrespective of any filters. + function size() { + return k; + } + + // Removes this group and associated event listeners. + function dispose() { + var i = filterListeners.indexOf(update); + if (i >= 0) filterListeners.splice(i, 1); + i = indexListeners.indexOf(add); + if (i >= 0) indexListeners.splice(i, 1); + i = removeDataListeners.indexOf(removeData); + if (i >= 0) removeDataListeners.splice(i, 1); + return group; + } + + return reduceCount().orderNatural(); + } + + // A convenience function for generating a singleton group. + function groupAll() { + var g = group(crossfilter_null), all = g.all; + delete g.all; + delete g.top; + delete g.order; + delete g.orderNatural; + delete g.size; + g.value = function() { return all()[0].value; }; + return g; + } + + // Removes this dimension and associated groups and event listeners. + function dispose() { + dimensionGroups.forEach(function(group) { group.dispose(); }); + var i = dataListeners.indexOf(preAdd); + if (i >= 0) dataListeners.splice(i, 1); + i = dataListeners.indexOf(postAdd); + if (i >= 0) dataListeners.splice(i, 1); + i = removeDataListeners.indexOf(removeData); + if (i >= 0) removeDataListeners.splice(i, 1); + filters.masks[offset] &= zero; + return filterAll(); + } + + return dimension; + } + + // A convenience method for groupAll on a dummy dimension. + // This implementation can be optimized since it always has cardinality 1. + function groupAll() { + var group = { + reduce: reduce, + reduceCount: reduceCount, + reduceSum: reduceSum, + value: value, + dispose: dispose, + remove: dispose // for backwards-compatibility + }; + + var reduceValue, + reduceAdd, + reduceRemove, + reduceInitial, + resetNeeded = true; + + // The group listens to the crossfilter for when any dimension changes, so + // that it can update the reduce value. It must also listen to the parent + // dimension for when data is added. + filterListeners.push(update); + dataListeners.push(add); + + // For consistency; actually a no-op since resetNeeded is true. + add(data, 0, n); + + // Incorporates the specified new values into this group. + function add(newData, n0) { + var i; + + if (resetNeeded) return; + + // Cycle through all the values. + for (i = n0; i < n; ++i) { + + // Add all values all the time. + reduceValue = reduceAdd(reduceValue, data[i], true); + + // Remove the value if filtered. + if (!filters.zero(i)) { + reduceValue = reduceRemove(reduceValue, data[i], false); + } + } + } + + // Reduces the specified selected or deselected records. + function update(filterOne, filterOffset, added, removed, notFilter) { + var i, + k, + n; + + if (resetNeeded) return; + + // Add the added values. + for (i = 0, n = added.length; i < n; ++i) { + if (filters.zero(k = added[i])) { + reduceValue = reduceAdd(reduceValue, data[k], notFilter); + } + } + + // Remove the removed values. + for (i = 0, n = removed.length; i < n; ++i) { + if (filters.only(k = removed[i], filterOffset, filterOne)) { + reduceValue = reduceRemove(reduceValue, data[k], notFilter); + } + } + } + + // Recomputes the group reduce value from scratch. + function reset() { + var i; + + reduceValue = reduceInitial(); + + // Cycle through all the values. + for (i = 0; i < n; ++i) { + + // Add all values all the time. + reduceValue = reduceAdd(reduceValue, data[i], true); + + // Remove the value if it is filtered. + if (!filters.zero(i)) { + reduceValue = reduceRemove(reduceValue, data[i], false); + } + } + } + + // Sets the reduce behavior for this group to use the specified functions. + // This method lazily recomputes the reduce value, waiting until needed. + function reduce(add, remove, initial) { + reduceAdd = add; + reduceRemove = remove; + reduceInitial = initial; + resetNeeded = true; + return group; + } + + // A convenience method for reducing by count. + function reduceCount() { + return reduce(xfilterReduce.reduceIncrement, xfilterReduce.reduceDecrement, crossfilter_zero); + } + + // A convenience method for reducing by sum(value). + function reduceSum(value) { + return reduce(xfilterReduce.reduceAdd(value), xfilterReduce.reduceSubtract(value), crossfilter_zero); + } + + // Returns the computed reduce value. + function value() { + if (resetNeeded) reset(), resetNeeded = false; + return reduceValue; + } + + // Removes this group and associated event listeners. + function dispose() { + var i = filterListeners.indexOf(update); + if (i >= 0) filterListeners.splice(i); + i = dataListeners.indexOf(add); + if (i >= 0) dataListeners.splice(i); + return group; + } + + return reduceCount(); + } + + // Returns the number of records in this crossfilter, irrespective of any filters. + function size() { + return n; + } + + // Returns the raw row data contained in this crossfilter + function all(){ + return data; + } + + // Returns row data with all dimension filters applied + function allFiltered() { + var array = [], + i = 0; + + for (i = 0; i < n; i++) { + if (filters.zero(i)) { + array.push(data[i]); + } + } + + return array; + } + + function onChange(cb){ + if(typeof cb !== 'function'){ + /* eslint no-console: 0 */ + console.warn('onChange callback parameter must be a function!'); + return; + } + callbacks.push(cb); + return function(){ + callbacks.splice(callbacks.indexOf(cb), 1); + }; + } + + function triggerOnChange(eventName){ + for (var i = 0; i < callbacks.length; i++) { + callbacks[i](eventName); + } + } + + return arguments.length + ? add(arguments[0]) + : crossfilter; +} + +// Returns an array of size n, big enough to store ids up to m. +function crossfilter_index(n, m) { + return (m < 0x101 + ? xfilterArray.array8 : m < 0x10001 + ? xfilterArray.array16 + : xfilterArray.array32)(n); +} + +// Constructs a new array of size n, with sequential values from 0 to n - 1. +function crossfilter_range(n) { + var range = crossfilter_index(n, n); + for (var i = -1; ++i < n;) range[i] = i; + return range; +} + +function crossfilter_capacity(w) { + return w === 8 + ? 0x100 : w === 16 + ? 0x10000 + : 0x100000000; +} + +},{"./../package.json":3,"./array":4,"./bisect":5,"./filter":7,"./heap":8,"./heapselect":9,"./identity":10,"./insertionsort":11,"./null":12,"./permute":13,"./quicksort":14,"./reduce":15,"./zero":16,"lodash.result":2}],7:[function(require,module,exports){ +'use strict'; + +function crossfilter_filterExact(bisect, value) { + return function(values) { + var n = values.length; + return [bisect.left(values, value, 0, n), bisect.right(values, value, 0, n)]; + }; +} + +function crossfilter_filterRange(bisect, range) { + var min = range[0], + max = range[1]; + return function(values) { + var n = values.length; + return [bisect.left(values, min, 0, n), bisect.left(values, max, 0, n)]; + }; +} + +function crossfilter_filterAll(values) { + return [0, values.length]; +} + +module.exports = { + filterExact: crossfilter_filterExact, + filterRange: crossfilter_filterRange, + filterAll: crossfilter_filterAll +}; + +},{}],8:[function(require,module,exports){ +'use strict'; + +var crossfilter_identity = require('./identity'); + +function heap_by(f) { + + // Builds a binary heap within the specified array a[lo:hi]. The heap has the + // property such that the parent a[lo+i] is always less than or equal to its + // two children: a[lo+2*i+1] and a[lo+2*i+2]. + function heap(a, lo, hi) { + var n = hi - lo, + i = (n >>> 1) + 1; + while (--i > 0) sift(a, i, n, lo); + return a; + } + + // Sorts the specified array a[lo:hi] in descending order, assuming it is + // already a heap. + function sort(a, lo, hi) { + var n = hi - lo, + t; + while (--n > 0) t = a[lo], a[lo] = a[lo + n], a[lo + n] = t, sift(a, 1, n, lo); + return a; + } + + // Sifts the element a[lo+i-1] down the heap, where the heap is the contiguous + // slice of array a[lo:lo+n]. This method can also be used to update the heap + // incrementally, without incurring the full cost of reconstructing the heap. + function sift(a, i, n, lo) { + var d = a[--lo + i], + x = f(d), + child; + while ((child = i << 1) <= n) { + if (child < n && f(a[lo + child]) > f(a[lo + child + 1])) child++; + if (x <= f(a[lo + child])) break; + a[lo + i] = a[lo + child]; + i = child; + } + a[lo + i] = d; + } + + heap.sort = sort; + return heap; +} + +module.exports = heap_by(crossfilter_identity); +module.exports.by = heap_by; + +},{"./identity":10}],9:[function(require,module,exports){ +'use strict'; + +var crossfilter_identity = require('./identity'); +var xFilterHeap = require('./heap'); + +function heapselect_by(f) { + var heap = xFilterHeap.by(f); + + // Returns a new array containing the top k elements in the array a[lo:hi]. + // The returned array is not sorted, but maintains the heap property. If k is + // greater than hi - lo, then fewer than k elements will be returned. The + // order of elements in a is unchanged by this operation. + function heapselect(a, lo, hi, k) { + var queue = new Array(k = Math.min(hi - lo, k)), + min, + i, + d; + + for (i = 0; i < k; ++i) queue[i] = a[lo++]; + heap(queue, 0, k); + + if (lo < hi) { + min = f(queue[0]); + do { + if (f(d = a[lo]) > min) { + queue[0] = d; + min = f(heap(queue, 0, k)[0]); + } + } while (++lo < hi); + } + + return queue; + } + + return heapselect; +} + +module.exports = heapselect_by(crossfilter_identity); +module.exports.by = heapselect_by; // assign the raw function to the export as well + +},{"./heap":8,"./identity":10}],10:[function(require,module,exports){ +'use strict'; + +function crossfilter_identity(d) { + return d; +} + +module.exports = crossfilter_identity; + +},{}],11:[function(require,module,exports){ +'use strict'; + +var crossfilter_identity = require('./identity'); + +function insertionsort_by(f) { + + function insertionsort(a, lo, hi) { + for (var i = lo + 1; i < hi; ++i) { + for (var j = i, t = a[i], x = f(t); j > lo && f(a[j - 1]) > x; --j) { + a[j] = a[j - 1]; + } + a[j] = t; + } + return a; + } + + return insertionsort; +} + +module.exports = insertionsort_by(crossfilter_identity); +module.exports.by = insertionsort_by; + +},{"./identity":10}],12:[function(require,module,exports){ +'use strict'; + +function crossfilter_null() { + return null; +} + +module.exports = crossfilter_null; + +},{}],13:[function(require,module,exports){ +'use strict'; + +function permute(array, index, deep) { + for (var i = 0, n = index.length, copy = deep ? JSON.parse(JSON.stringify(array)) : new Array(n); i < n; ++i) { + copy[i] = array[index[i]]; + } + return copy; +} + +module.exports = permute; + +},{}],14:[function(require,module,exports){ +var crossfilter_identity = require('./identity'); +var xFilterInsertionsort = require('./insertionsort'); + +// Algorithm designed by Vladimir Yaroslavskiy. +// Implementation based on the Dart project; see NOTICE and AUTHORS for details. + +function quicksort_by(f) { + var insertionsort = xFilterInsertionsort.by(f); + + function sort(a, lo, hi) { + return (hi - lo < quicksort_sizeThreshold + ? insertionsort + : quicksort)(a, lo, hi); + } + + function quicksort(a, lo, hi) { + // Compute the two pivots by looking at 5 elements. + var sixth = (hi - lo) / 6 | 0, + i1 = lo + sixth, + i5 = hi - 1 - sixth, + i3 = lo + hi - 1 >> 1, // The midpoint. + i2 = i3 - sixth, + i4 = i3 + sixth; + + var e1 = a[i1], x1 = f(e1), + e2 = a[i2], x2 = f(e2), + e3 = a[i3], x3 = f(e3), + e4 = a[i4], x4 = f(e4), + e5 = a[i5], x5 = f(e5); + + var t; + + // Sort the selected 5 elements using a sorting network. + if (x1 > x2) t = e1, e1 = e2, e2 = t, t = x1, x1 = x2, x2 = t; + if (x4 > x5) t = e4, e4 = e5, e5 = t, t = x4, x4 = x5, x5 = t; + if (x1 > x3) t = e1, e1 = e3, e3 = t, t = x1, x1 = x3, x3 = t; + if (x2 > x3) t = e2, e2 = e3, e3 = t, t = x2, x2 = x3, x3 = t; + if (x1 > x4) t = e1, e1 = e4, e4 = t, t = x1, x1 = x4, x4 = t; + if (x3 > x4) t = e3, e3 = e4, e4 = t, t = x3, x3 = x4, x4 = t; + if (x2 > x5) t = e2, e2 = e5, e5 = t, t = x2, x2 = x5, x5 = t; + if (x2 > x3) t = e2, e2 = e3, e3 = t, t = x2, x2 = x3, x3 = t; + if (x4 > x5) t = e4, e4 = e5, e5 = t, t = x4, x4 = x5, x5 = t; + + var pivot1 = e2, pivotValue1 = x2, + pivot2 = e4, pivotValue2 = x4; + + // e2 and e4 have been saved in the pivot variables. They will be written + // back, once the partitioning is finished. + a[i1] = e1; + a[i2] = a[lo]; + a[i3] = e3; + a[i4] = a[hi - 1]; + a[i5] = e5; + + var less = lo + 1, // First element in the middle partition. + great = hi - 2; // Last element in the middle partition. + + // Note that for value comparison, <, <=, >= and > coerce to a primitive via + // Object.prototype.valueOf; == and === do not, so in order to be consistent + // with natural order (such as for Date objects), we must do two compares. + var pivotsEqual = pivotValue1 <= pivotValue2 && pivotValue1 >= pivotValue2; + if (pivotsEqual) { + + // Degenerated case where the partitioning becomes a dutch national flag + // problem. + // + // [ | < pivot | == pivot | unpartitioned | > pivot | ] + // ^ ^ ^ ^ ^ + // left less k great right + // + // a[left] and a[right] are undefined and are filled after the + // partitioning. + // + // Invariants: + // 1) for x in ]left, less[ : x < pivot. + // 2) for x in [less, k[ : x == pivot. + // 3) for x in ]great, right[ : x > pivot. + for (var k = less; k <= great; ++k) { + var ek = a[k], xk = f(ek); + if (xk < pivotValue1) { + if (k !== less) { + a[k] = a[less]; + a[less] = ek; + } + ++less; + } else if (xk > pivotValue1) { + + // Find the first element <= pivot in the range [k - 1, great] and + // put [:ek:] there. We know that such an element must exist: + // When k == less, then el3 (which is equal to pivot) lies in the + // interval. Otherwise a[k - 1] == pivot and the search stops at k-1. + // Note that in the latter case invariant 2 will be violated for a + // short amount of time. The invariant will be restored when the + // pivots are put into their final positions. + /* eslint no-constant-condition: 0 */ + while (true) { + var greatValue = f(a[great]); + if (greatValue > pivotValue1) { + great--; + // This is the only location in the while-loop where a new + // iteration is started. + continue; + } else if (greatValue < pivotValue1) { + // Triple exchange. + a[k] = a[less]; + a[less++] = a[great]; + a[great--] = ek; + break; + } else { + a[k] = a[great]; + a[great--] = ek; + // Note: if great < k then we will exit the outer loop and fix + // invariant 2 (which we just violated). + break; + } + } + } + } + } else { + + // We partition the list into three parts: + // 1. < pivot1 + // 2. >= pivot1 && <= pivot2 + // 3. > pivot2 + // + // During the loop we have: + // [ | < pivot1 | >= pivot1 && <= pivot2 | unpartitioned | > pivot2 | ] + // ^ ^ ^ ^ ^ + // left less k great right + // + // a[left] and a[right] are undefined and are filled after the + // partitioning. + // + // Invariants: + // 1. for x in ]left, less[ : x < pivot1 + // 2. for x in [less, k[ : pivot1 <= x && x <= pivot2 + // 3. for x in ]great, right[ : x > pivot2 + (function () { // isolate scope + for (var k = less; k <= great; k++) { + var ek = a[k], xk = f(ek); + if (xk < pivotValue1) { + if (k !== less) { + a[k] = a[less]; + a[less] = ek; + } + ++less; + } else { + if (xk > pivotValue2) { + while (true) { + var greatValue = f(a[great]); + if (greatValue > pivotValue2) { + great--; + if (great < k) break; + // This is the only location inside the loop where a new + // iteration is started. + continue; + } else { + // a[great] <= pivot2. + if (greatValue < pivotValue1) { + // Triple exchange. + a[k] = a[less]; + a[less++] = a[great]; + a[great--] = ek; + } else { + // a[great] >= pivot1. + a[k] = a[great]; + a[great--] = ek; + } + break; + } + } + } + } + } + })(); // isolate scope + } + + // Move pivots into their final positions. + // We shrunk the list from both sides (a[left] and a[right] have + // meaningless values in them) and now we move elements from the first + // and third partition into these locations so that we can store the + // pivots. + a[lo] = a[less - 1]; + a[less - 1] = pivot1; + a[hi - 1] = a[great + 1]; + a[great + 1] = pivot2; + + // The list is now partitioned into three partitions: + // [ < pivot1 | >= pivot1 && <= pivot2 | > pivot2 ] + // ^ ^ ^ ^ + // left less great right + + // Recursive descent. (Don't include the pivot values.) + sort(a, lo, less - 1); + sort(a, great + 2, hi); + + if (pivotsEqual) { + // All elements in the second partition are equal to the pivot. No + // need to sort them. + return a; + } + + // In theory it should be enough to call _doSort recursively on the second + // partition. + // The Android source however removes the pivot elements from the recursive + // call if the second partition is too large (more than 2/3 of the list). + if (less < i1 && great > i5) { + + (function () { // isolate scope + var lessValue, greatValue; + while ((lessValue = f(a[less])) <= pivotValue1 && lessValue >= pivotValue1) ++less; + while ((greatValue = f(a[great])) <= pivotValue2 && greatValue >= pivotValue2) --great; + + // Copy paste of the previous 3-way partitioning with adaptions. + // + // We partition the list into three parts: + // 1. == pivot1 + // 2. > pivot1 && < pivot2 + // 3. == pivot2 + // + // During the loop we have: + // [ == pivot1 | > pivot1 && < pivot2 | unpartitioned | == pivot2 ] + // ^ ^ ^ + // less k great + // + // Invariants: + // 1. for x in [ *, less[ : x == pivot1 + // 2. for x in [less, k[ : pivot1 < x && x < pivot2 + // 3. for x in ]great, * ] : x == pivot2 + for (var k = less; k <= great; k++) { + var ek = a[k], xk = f(ek); + if (xk <= pivotValue1 && xk >= pivotValue1) { + if (k !== less) { + a[k] = a[less]; + a[less] = ek; + } + less++; + } else { + if (xk <= pivotValue2 && xk >= pivotValue2) { + /* eslint no-constant-condition: 0 */ + while (true) { + greatValue = f(a[great]); + if (greatValue <= pivotValue2 && greatValue >= pivotValue2) { + great--; + if (great < k) break; + // This is the only location inside the loop where a new + // iteration is started. + continue; + } else { + // a[great] < pivot2. + if (greatValue < pivotValue1) { + // Triple exchange. + a[k] = a[less]; + a[less++] = a[great]; + a[great--] = ek; + } else { + // a[great] == pivot1. + a[k] = a[great]; + a[great--] = ek; + } + break; + } + } + } + } + } + })(); // isolate scope + + } + + // The second partition has now been cleared of pivot elements and looks + // as follows: + // [ * | > pivot1 && < pivot2 | * ] + // ^ ^ + // less great + // Sort the second partition using recursive descent. + + // The second partition looks as follows: + // [ * | >= pivot1 && <= pivot2 | * ] + // ^ ^ + // less great + // Simply sort it by recursive descent. + + return sort(a, less, great + 1); + } + + return sort; +} + +var quicksort_sizeThreshold = 32; + +module.exports = quicksort_by(crossfilter_identity); +module.exports.by = quicksort_by; + +},{"./identity":10,"./insertionsort":11}],15:[function(require,module,exports){ +'use strict'; + +function crossfilter_reduceIncrement(p) { + return p + 1; +} + +function crossfilter_reduceDecrement(p) { + return p - 1; +} + +function crossfilter_reduceAdd(f) { + return function(p, v) { + return p + +f(v); + }; +} + +function crossfilter_reduceSubtract(f) { + return function(p, v) { + return p - f(v); + }; +} + +module.exports = { + reduceIncrement: crossfilter_reduceIncrement, + reduceDecrement: crossfilter_reduceDecrement, + reduceAdd: crossfilter_reduceAdd, + reduceSubtract: crossfilter_reduceSubtract +}; + +},{}],16:[function(require,module,exports){ +'use strict'; + +function crossfilter_zero() { + return 0; +} + +module.exports = crossfilter_zero; + +},{}]},{},[1])(1) +}); \ No newline at end of file diff --git a/src/legacy/design-studio/js/d3.js b/src/legacy/design-studio/js/d3.js new file mode 100644 index 0000000..aded45c --- /dev/null +++ b/src/legacy/design-studio/js/d3.js @@ -0,0 +1,9554 @@ +!function() { + var d3 = { + version: "3.5.17" + }; + var d3_arraySlice = [].slice, d3_array = function(list) { + return d3_arraySlice.call(list); + }; + var d3_document = this.document; + function d3_documentElement(node) { + return node && (node.ownerDocument || node.document || node).documentElement; + } + function d3_window(node) { + return node && (node.ownerDocument && node.ownerDocument.defaultView || node.document && node || node.defaultView); + } + if (d3_document) { + try { + d3_array(d3_document.documentElement.childNodes)[0].nodeType; + } catch (e) { + d3_array = function(list) { + var i = list.length, array = new Array(i); + while (i--) array[i] = list[i]; + return array; + }; + } + } + if (!Date.now) Date.now = function() { + return +new Date(); + }; + if (d3_document) { + try { + d3_document.createElement("DIV").style.setProperty("opacity", 0, ""); + } catch (error) { + var d3_element_prototype = this.Element.prototype, d3_element_setAttribute = d3_element_prototype.setAttribute, d3_element_setAttributeNS = d3_element_prototype.setAttributeNS, d3_style_prototype = this.CSSStyleDeclaration.prototype, d3_style_setProperty = d3_style_prototype.setProperty; + d3_element_prototype.setAttribute = function(name, value) { + d3_element_setAttribute.call(this, name, value + ""); + }; + d3_element_prototype.setAttributeNS = function(space, local, value) { + d3_element_setAttributeNS.call(this, space, local, value + ""); + }; + d3_style_prototype.setProperty = function(name, value, priority) { + d3_style_setProperty.call(this, name, value + "", priority); + }; + } + } + d3.ascending = d3_ascending; + function d3_ascending(a, b) { + return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN; + } + d3.descending = function(a, b) { + return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN; + }; + d3.min = function(array, f) { + var i = -1, n = array.length, a, b; + if (arguments.length === 1) { + while (++i < n) if ((b = array[i]) != null && b >= b) { + a = b; + break; + } + while (++i < n) if ((b = array[i]) != null && a > b) a = b; + } else { + while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) { + a = b; + break; + } + while (++i < n) if ((b = f.call(array, array[i], i)) != null && a > b) a = b; + } + return a; + }; + d3.max = function(array, f) { + var i = -1, n = array.length, a, b; + if (arguments.length === 1) { + while (++i < n) if ((b = array[i]) != null && b >= b) { + a = b; + break; + } + while (++i < n) if ((b = array[i]) != null && b > a) a = b; + } else { + while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) { + a = b; + break; + } + while (++i < n) if ((b = f.call(array, array[i], i)) != null && b > a) a = b; + } + return a; + }; + d3.extent = function(array, f) { + var i = -1, n = array.length, a, b, c; + if (arguments.length === 1) { + while (++i < n) if ((b = array[i]) != null && b >= b) { + a = c = b; + break; + } + while (++i < n) if ((b = array[i]) != null) { + if (a > b) a = b; + if (c < b) c = b; + } + } else { + while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) { + a = c = b; + break; + } + while (++i < n) if ((b = f.call(array, array[i], i)) != null) { + if (a > b) a = b; + if (c < b) c = b; + } + } + return [ a, c ]; + }; + function d3_number(x) { + return x === null ? NaN : +x; + } + function d3_numeric(x) { + return !isNaN(x); + } + d3.sum = function(array, f) { + var s = 0, n = array.length, a, i = -1; + if (arguments.length === 1) { + while (++i < n) if (d3_numeric(a = +array[i])) s += a; + } else { + while (++i < n) if (d3_numeric(a = +f.call(array, array[i], i))) s += a; + } + return s; + }; + d3.mean = function(array, f) { + var s = 0, n = array.length, a, i = -1, j = n; + if (arguments.length === 1) { + while (++i < n) if (d3_numeric(a = d3_number(array[i]))) s += a; else --j; + } else { + while (++i < n) if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) s += a; else --j; + } + if (j) return s / j; + }; + d3.quantile = function(values, p) { + var H = (values.length - 1) * p + 1, h = Math.floor(H), v = +values[h - 1], e = H - h; + return e ? v + e * (values[h] - v) : v; + }; + d3.median = function(array, f) { + var numbers = [], n = array.length, a, i = -1; + if (arguments.length === 1) { + while (++i < n) if (d3_numeric(a = d3_number(array[i]))) numbers.push(a); + } else { + while (++i < n) if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) numbers.push(a); + } + if (numbers.length) return d3.quantile(numbers.sort(d3_ascending), .5); + }; + d3.variance = function(array, f) { + var n = array.length, m = 0, a, d, s = 0, i = -1, j = 0; + if (arguments.length === 1) { + while (++i < n) { + if (d3_numeric(a = d3_number(array[i]))) { + d = a - m; + m += d / ++j; + s += d * (a - m); + } + } + } else { + while (++i < n) { + if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) { + d = a - m; + m += d / ++j; + s += d * (a - m); + } + } + } + if (j > 1) return s / (j - 1); + }; + d3.deviation = function() { + var v = d3.variance.apply(this, arguments); + return v ? Math.sqrt(v) : v; + }; + function d3_bisector(compare) { + return { + left: function(a, x, lo, hi) { + if (arguments.length < 3) lo = 0; + if (arguments.length < 4) hi = a.length; + while (lo < hi) { + var mid = lo + hi >>> 1; + if (compare(a[mid], x) < 0) lo = mid + 1; else hi = mid; + } + return lo; + }, + right: function(a, x, lo, hi) { + if (arguments.length < 3) lo = 0; + if (arguments.length < 4) hi = a.length; + while (lo < hi) { + var mid = lo + hi >>> 1; + if (compare(a[mid], x) > 0) hi = mid; else lo = mid + 1; + } + return lo; + } + }; + } + var d3_bisect = d3_bisector(d3_ascending); + d3.bisectLeft = d3_bisect.left; + d3.bisect = d3.bisectRight = d3_bisect.right; + d3.bisector = function(f) { + return d3_bisector(f.length === 1 ? function(d, x) { + return d3_ascending(f(d), x); + } : f); + }; + d3.shuffle = function(array, i0, i1) { + if ((m = arguments.length) < 3) { + i1 = array.length; + if (m < 2) i0 = 0; + } + var m = i1 - i0, t, i; + while (m) { + i = Math.random() * m-- | 0; + t = array[m + i0], array[m + i0] = array[i + i0], array[i + i0] = t; + } + return array; + }; + d3.permute = function(array, indexes) { + var i = indexes.length, permutes = new Array(i); + while (i--) permutes[i] = array[indexes[i]]; + return permutes; + }; + d3.pairs = function(array) { + var i = 0, n = array.length - 1, p0, p1 = array[0], pairs = new Array(n < 0 ? 0 : n); + while (i < n) pairs[i] = [ p0 = p1, p1 = array[++i] ]; + return pairs; + }; + d3.transpose = function(matrix) { + if (!(n = matrix.length)) return []; + for (var i = -1, m = d3.min(matrix, d3_transposeLength), transpose = new Array(m); ++i < m; ) { + for (var j = -1, n, row = transpose[i] = new Array(n); ++j < n; ) { + row[j] = matrix[j][i]; + } + } + return transpose; + }; + function d3_transposeLength(d) { + return d.length; + } + d3.zip = function() { + return d3.transpose(arguments); + }; + d3.keys = function(map) { + var keys = []; + for (var key in map) keys.push(key); + return keys; + }; + d3.values = function(map) { + var values = []; + for (var key in map) values.push(map[key]); + return values; + }; + d3.entries = function(map) { + var entries = []; + for (var key in map) entries.push({ + key: key, + value: map[key] + }); + return entries; + }; + d3.merge = function(arrays) { + var n = arrays.length, m, i = -1, j = 0, merged, array; + while (++i < n) j += arrays[i].length; + merged = new Array(j); + while (--n >= 0) { + array = arrays[n]; + m = array.length; + while (--m >= 0) { + merged[--j] = array[m]; + } + } + return merged; + }; + var abs = Math.abs; + d3.range = function(start, stop, step) { + if (arguments.length < 3) { + step = 1; + if (arguments.length < 2) { + stop = start; + start = 0; + } + } + if ((stop - start) / step === Infinity) throw new Error("infinite range"); + var range = [], k = d3_range_integerScale(abs(step)), i = -1, j; + start *= k, stop *= k, step *= k; + if (step < 0) while ((j = start + step * ++i) > stop) range.push(j / k); else while ((j = start + step * ++i) < stop) range.push(j / k); + return range; + }; + function d3_range_integerScale(x) { + var k = 1; + while (x * k % 1) k *= 10; + return k; + } + function d3_class(ctor, properties) { + for (var key in properties) { + Object.defineProperty(ctor.prototype, key, { + value: properties[key], + enumerable: false + }); + } + } + d3.map = function(object, f) { + var map = new d3_Map(); + if (object instanceof d3_Map) { + object.forEach(function(key, value) { + map.set(key, value); + }); + } else if (Array.isArray(object)) { + var i = -1, n = object.length, o; + if (arguments.length === 1) while (++i < n) map.set(i, object[i]); else while (++i < n) map.set(f.call(object, o = object[i], i), o); + } else { + for (var key in object) map.set(key, object[key]); + } + return map; + }; + function d3_Map() { + this._ = Object.create(null); + } + var d3_map_proto = "__proto__", d3_map_zero = "\x00"; + d3_class(d3_Map, { + has: d3_map_has, + get: function(key) { + return this._[d3_map_escape(key)]; + }, + set: function(key, value) { + return this._[d3_map_escape(key)] = value; + }, + remove: d3_map_remove, + keys: d3_map_keys, + values: function() { + var values = []; + for (var key in this._) values.push(this._[key]); + return values; + }, + entries: function() { + var entries = []; + for (var key in this._) entries.push({ + key: d3_map_unescape(key), + value: this._[key] + }); + return entries; + }, + size: d3_map_size, + empty: d3_map_empty, + forEach: function(f) { + for (var key in this._) f.call(this, d3_map_unescape(key), this._[key]); + } + }); + function d3_map_escape(key) { + return (key += "") === d3_map_proto || key[0] === d3_map_zero ? d3_map_zero + key : key; + } + function d3_map_unescape(key) { + return (key += "")[0] === d3_map_zero ? key.slice(1) : key; + } + function d3_map_has(key) { + return d3_map_escape(key) in this._; + } + function d3_map_remove(key) { + return (key = d3_map_escape(key)) in this._ && delete this._[key]; + } + function d3_map_keys() { + var keys = []; + for (var key in this._) keys.push(d3_map_unescape(key)); + return keys; + } + function d3_map_size() { + var size = 0; + for (var key in this._) ++size; + return size; + } + function d3_map_empty() { + for (var key in this._) return false; + return true; + } + d3.nest = function() { + var nest = {}, keys = [], sortKeys = [], sortValues, rollup; + function map(mapType, array, depth) { + if (depth >= keys.length) return rollup ? rollup.call(nest, array) : sortValues ? array.sort(sortValues) : array; + var i = -1, n = array.length, key = keys[depth++], keyValue, object, setter, valuesByKey = new d3_Map(), values; + while (++i < n) { + if (values = valuesByKey.get(keyValue = key(object = array[i]))) { + values.push(object); + } else { + valuesByKey.set(keyValue, [ object ]); + } + } + if (mapType) { + object = mapType(); + setter = function(keyValue, values) { + object.set(keyValue, map(mapType, values, depth)); + }; + } else { + object = {}; + setter = function(keyValue, values) { + object[keyValue] = map(mapType, values, depth); + }; + } + valuesByKey.forEach(setter); + return object; + } + function entries(map, depth) { + if (depth >= keys.length) return map; + var array = [], sortKey = sortKeys[depth++]; + map.forEach(function(key, keyMap) { + array.push({ + key: key, + values: entries(keyMap, depth) + }); + }); + return sortKey ? array.sort(function(a, b) { + return sortKey(a.key, b.key); + }) : array; + } + nest.map = function(array, mapType) { + return map(mapType, array, 0); + }; + nest.entries = function(array) { + return entries(map(d3.map, array, 0), 0); + }; + nest.key = function(d) { + keys.push(d); + return nest; + }; + nest.sortKeys = function(order) { + sortKeys[keys.length - 1] = order; + return nest; + }; + nest.sortValues = function(order) { + sortValues = order; + return nest; + }; + nest.rollup = function(f) { + rollup = f; + return nest; + }; + return nest; + }; + d3.set = function(array) { + var set = new d3_Set(); + if (array) for (var i = 0, n = array.length; i < n; ++i) set.add(array[i]); + return set; + }; + function d3_Set() { + this._ = Object.create(null); + } + d3_class(d3_Set, { + has: d3_map_has, + add: function(key) { + this._[d3_map_escape(key += "")] = true; + return key; + }, + remove: d3_map_remove, + values: d3_map_keys, + size: d3_map_size, + empty: d3_map_empty, + forEach: function(f) { + for (var key in this._) f.call(this, d3_map_unescape(key)); + } + }); + d3.behavior = {}; + function d3_identity(d) { + return d; + } + d3.rebind = function(target, source) { + var i = 1, n = arguments.length, method; + while (++i < n) target[method = arguments[i]] = d3_rebind(target, source, source[method]); + return target; + }; + function d3_rebind(target, source, method) { + return function() { + var value = method.apply(source, arguments); + return value === source ? target : value; + }; + } + function d3_vendorSymbol(object, name) { + if (name in object) return name; + name = name.charAt(0).toUpperCase() + name.slice(1); + for (var i = 0, n = d3_vendorPrefixes.length; i < n; ++i) { + var prefixName = d3_vendorPrefixes[i] + name; + if (prefixName in object) return prefixName; + } + } + var d3_vendorPrefixes = [ "webkit", "ms", "moz", "Moz", "o", "O" ]; + function d3_noop() {} + d3.dispatch = function() { + var dispatch = new d3_dispatch(), i = -1, n = arguments.length; + while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch); + return dispatch; + }; + function d3_dispatch() {} + d3_dispatch.prototype.on = function(type, listener) { + var i = type.indexOf("."), name = ""; + if (i >= 0) { + name = type.slice(i + 1); + type = type.slice(0, i); + } + if (type) return arguments.length < 2 ? this[type].on(name) : this[type].on(name, listener); + if (arguments.length === 2) { + if (listener == null) for (type in this) { + if (this.hasOwnProperty(type)) this[type].on(name, null); + } + return this; + } + }; + function d3_dispatch_event(dispatch) { + var listeners = [], listenerByName = new d3_Map(); + function event() { + var z = listeners, i = -1, n = z.length, l; + while (++i < n) if (l = z[i].on) l.apply(this, arguments); + return dispatch; + } + event.on = function(name, listener) { + var l = listenerByName.get(name), i; + if (arguments.length < 2) return l && l.on; + if (l) { + l.on = null; + listeners = listeners.slice(0, i = listeners.indexOf(l)).concat(listeners.slice(i + 1)); + listenerByName.remove(name); + } + if (listener) listeners.push(listenerByName.set(name, { + on: listener + })); + return dispatch; + }; + return event; + } + d3.event = null; + function d3_eventPreventDefault() { + d3.event.preventDefault(); + } + function d3_eventSource() { + var e = d3.event, s; + while (s = e.sourceEvent) e = s; + return e; + } + function d3_eventDispatch(target) { + var dispatch = new d3_dispatch(), i = 0, n = arguments.length; + while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch); + dispatch.of = function(thiz, argumentz) { + return function(e1) { + try { + var e0 = e1.sourceEvent = d3.event; + e1.target = target; + d3.event = e1; + dispatch[e1.type].apply(thiz, argumentz); + } finally { + d3.event = e0; + } + }; + }; + return dispatch; + } + d3.requote = function(s) { + return s.replace(d3_requote_re, "\\$&"); + }; + var d3_requote_re = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g; + var d3_subclass = {}.__proto__ ? function(object, prototype) { + object.__proto__ = prototype; + } : function(object, prototype) { + for (var property in prototype) object[property] = prototype[property]; + }; + function d3_selection(groups) { + d3_subclass(groups, d3_selectionPrototype); + return groups; + } + var d3_select = function(s, n) { + return n.querySelector(s); + }, d3_selectAll = function(s, n) { + return n.querySelectorAll(s); + }, d3_selectMatches = function(n, s) { + var d3_selectMatcher = n.matches || n[d3_vendorSymbol(n, "matchesSelector")]; + d3_selectMatches = function(n, s) { + return d3_selectMatcher.call(n, s); + }; + return d3_selectMatches(n, s); + }; + if (typeof Sizzle === "function") { + d3_select = function(s, n) { + return Sizzle(s, n)[0] || null; + }; + d3_selectAll = Sizzle; + d3_selectMatches = Sizzle.matchesSelector; + } + d3.selection = function() { + return d3.select(d3_document.documentElement); + }; + var d3_selectionPrototype = d3.selection.prototype = []; + d3_selectionPrototype.select = function(selector) { + var subgroups = [], subgroup, subnode, group, node; + selector = d3_selection_selector(selector); + for (var j = -1, m = this.length; ++j < m; ) { + subgroups.push(subgroup = []); + subgroup.parentNode = (group = this[j]).parentNode; + for (var i = -1, n = group.length; ++i < n; ) { + if (node = group[i]) { + subgroup.push(subnode = selector.call(node, node.__data__, i, j)); + if (subnode && "__data__" in node) subnode.__data__ = node.__data__; + } else { + subgroup.push(null); + } + } + } + return d3_selection(subgroups); + }; + function d3_selection_selector(selector) { + return typeof selector === "function" ? selector : function() { + return d3_select(selector, this); + }; + } + d3_selectionPrototype.selectAll = function(selector) { + var subgroups = [], subgroup, node; + selector = d3_selection_selectorAll(selector); + for (var j = -1, m = this.length; ++j < m; ) { + for (var group = this[j], i = -1, n = group.length; ++i < n; ) { + if (node = group[i]) { + subgroups.push(subgroup = d3_array(selector.call(node, node.__data__, i, j))); + subgroup.parentNode = node; + } + } + } + return d3_selection(subgroups); + }; + function d3_selection_selectorAll(selector) { + return typeof selector === "function" ? selector : function() { + return d3_selectAll(selector, this); + }; + } + var d3_nsXhtml = "http://www.w3.org/1999/xhtml"; + var d3_nsPrefix = { + svg: "http://www.w3.org/2000/svg", + xhtml: d3_nsXhtml, + xlink: "http://www.w3.org/1999/xlink", + xml: "http://www.w3.org/XML/1998/namespace", + xmlns: "http://www.w3.org/2000/xmlns/" + }; + d3.ns = { + prefix: d3_nsPrefix, + qualify: function(name) { + var i = name.indexOf(":"), prefix = name; + if (i >= 0 && (prefix = name.slice(0, i)) !== "xmlns") name = name.slice(i + 1); + return d3_nsPrefix.hasOwnProperty(prefix) ? { + space: d3_nsPrefix[prefix], + local: name + } : name; + } + }; + d3_selectionPrototype.attr = function(name, value) { + if (arguments.length < 2) { + if (typeof name === "string") { + var node = this.node(); + name = d3.ns.qualify(name); + return name.local ? node.getAttributeNS(name.space, name.local) : node.getAttribute(name); + } + for (value in name) this.each(d3_selection_attr(value, name[value])); + return this; + } + return this.each(d3_selection_attr(name, value)); + }; + function d3_selection_attr(name, value) { + name = d3.ns.qualify(name); + function attrNull() { + this.removeAttribute(name); + } + function attrNullNS() { + this.removeAttributeNS(name.space, name.local); + } + function attrConstant() { + this.setAttribute(name, value); + } + function attrConstantNS() { + this.setAttributeNS(name.space, name.local, value); + } + function attrFunction() { + var x = value.apply(this, arguments); + if (x == null) this.removeAttribute(name); else this.setAttribute(name, x); + } + function attrFunctionNS() { + var x = value.apply(this, arguments); + if (x == null) this.removeAttributeNS(name.space, name.local); else this.setAttributeNS(name.space, name.local, x); + } + return value == null ? name.local ? attrNullNS : attrNull : typeof value === "function" ? name.local ? attrFunctionNS : attrFunction : name.local ? attrConstantNS : attrConstant; + } + function d3_collapse(s) { + return s.trim().replace(/\s+/g, " "); + } + d3_selectionPrototype.classed = function(name, value) { + if (arguments.length < 2) { + if (typeof name === "string") { + var node = this.node(), n = (name = d3_selection_classes(name)).length, i = -1; + if (value = node.classList) { + while (++i < n) if (!value.contains(name[i])) return false; + } else { + value = node.getAttribute("class"); + while (++i < n) if (!d3_selection_classedRe(name[i]).test(value)) return false; + } + return true; + } + for (value in name) this.each(d3_selection_classed(value, name[value])); + return this; + } + return this.each(d3_selection_classed(name, value)); + }; + function d3_selection_classedRe(name) { + return new RegExp("(?:^|\\s+)" + d3.requote(name) + "(?:\\s+|$)", "g"); + } + function d3_selection_classes(name) { + return (name + "").trim().split(/^|\s+/); + } + function d3_selection_classed(name, value) { + name = d3_selection_classes(name).map(d3_selection_classedName); + var n = name.length; + function classedConstant() { + var i = -1; + while (++i < n) name[i](this, value); + } + function classedFunction() { + var i = -1, x = value.apply(this, arguments); + while (++i < n) name[i](this, x); + } + return typeof value === "function" ? classedFunction : classedConstant; + } + function d3_selection_classedName(name) { + var re = d3_selection_classedRe(name); + return function(node, value) { + if (c = node.classList) return value ? c.add(name) : c.remove(name); + var c = node.getAttribute("class") || ""; + if (value) { + re.lastIndex = 0; + if (!re.test(c)) node.setAttribute("class", d3_collapse(c + " " + name)); + } else { + node.setAttribute("class", d3_collapse(c.replace(re, " "))); + } + }; + } + d3_selectionPrototype.style = function(name, value, priority) { + var n = arguments.length; + if (n < 3) { + if (typeof name !== "string") { + if (n < 2) value = ""; + for (priority in name) this.each(d3_selection_style(priority, name[priority], value)); + return this; + } + if (n < 2) { + var node = this.node(); + return d3_window(node).getComputedStyle(node, null).getPropertyValue(name); + } + priority = ""; + } + return this.each(d3_selection_style(name, value, priority)); + }; + function d3_selection_style(name, value, priority) { + function styleNull() { + this.style.removeProperty(name); + } + function styleConstant() { + this.style.setProperty(name, value, priority); + } + function styleFunction() { + var x = value.apply(this, arguments); + if (x == null) this.style.removeProperty(name); else this.style.setProperty(name, x, priority); + } + return value == null ? styleNull : typeof value === "function" ? styleFunction : styleConstant; + } + d3_selectionPrototype.property = function(name, value) { + if (arguments.length < 2) { + if (typeof name === "string") return this.node()[name]; + for (value in name) this.each(d3_selection_property(value, name[value])); + return this; + } + return this.each(d3_selection_property(name, value)); + }; + function d3_selection_property(name, value) { + function propertyNull() { + delete this[name]; + } + function propertyConstant() { + this[name] = value; + } + function propertyFunction() { + var x = value.apply(this, arguments); + if (x == null) delete this[name]; else this[name] = x; + } + return value == null ? propertyNull : typeof value === "function" ? propertyFunction : propertyConstant; + } + d3_selectionPrototype.text = function(value) { + return arguments.length ? this.each(typeof value === "function" ? function() { + var v = value.apply(this, arguments); + this.textContent = v == null ? "" : v; + } : value == null ? function() { + this.textContent = ""; + } : function() { + this.textContent = value; + }) : this.node().textContent; + }; + d3_selectionPrototype.html = function(value) { + return arguments.length ? this.each(typeof value === "function" ? function() { + var v = value.apply(this, arguments); + this.innerHTML = v == null ? "" : v; + } : value == null ? function() { + this.innerHTML = ""; + } : function() { + this.innerHTML = value; + }) : this.node().innerHTML; + }; + d3_selectionPrototype.append = function(name) { + name = d3_selection_creator(name); + return this.select(function() { + return this.appendChild(name.apply(this, arguments)); + }); + }; + function d3_selection_creator(name) { + function create() { + var document = this.ownerDocument, namespace = this.namespaceURI; + return namespace === d3_nsXhtml && document.documentElement.namespaceURI === d3_nsXhtml ? document.createElement(name) : document.createElementNS(namespace, name); + } + function createNS() { + return this.ownerDocument.createElementNS(name.space, name.local); + } + return typeof name === "function" ? name : (name = d3.ns.qualify(name)).local ? createNS : create; + } + d3_selectionPrototype.insert = function(name, before) { + name = d3_selection_creator(name); + before = d3_selection_selector(before); + return this.select(function() { + return this.insertBefore(name.apply(this, arguments), before.apply(this, arguments) || null); + }); + }; + d3_selectionPrototype.remove = function() { + return this.each(d3_selectionRemove); + }; + function d3_selectionRemove() { + var parent = this.parentNode; + if (parent) parent.removeChild(this); + } + d3_selectionPrototype.data = function(value, key) { + var i = -1, n = this.length, group, node; + if (!arguments.length) { + value = new Array(n = (group = this[0]).length); + while (++i < n) { + if (node = group[i]) { + value[i] = node.__data__; + } + } + return value; + } + function bind(group, groupData) { + var i, n = group.length, m = groupData.length, n0 = Math.min(n, m), updateNodes = new Array(m), enterNodes = new Array(m), exitNodes = new Array(n), node, nodeData; + if (key) { + var nodeByKeyValue = new d3_Map(), keyValues = new Array(n), keyValue; + for (i = -1; ++i < n; ) { + if (node = group[i]) { + if (nodeByKeyValue.has(keyValue = key.call(node, node.__data__, i))) { + exitNodes[i] = node; + } else { + nodeByKeyValue.set(keyValue, node); + } + keyValues[i] = keyValue; + } + } + for (i = -1; ++i < m; ) { + if (!(node = nodeByKeyValue.get(keyValue = key.call(groupData, nodeData = groupData[i], i)))) { + enterNodes[i] = d3_selection_dataNode(nodeData); + } else if (node !== true) { + updateNodes[i] = node; + node.__data__ = nodeData; + } + nodeByKeyValue.set(keyValue, true); + } + for (i = -1; ++i < n; ) { + if (i in keyValues && nodeByKeyValue.get(keyValues[i]) !== true) { + exitNodes[i] = group[i]; + } + } + } else { + for (i = -1; ++i < n0; ) { + node = group[i]; + nodeData = groupData[i]; + if (node) { + node.__data__ = nodeData; + updateNodes[i] = node; + } else { + enterNodes[i] = d3_selection_dataNode(nodeData); + } + } + for (;i < m; ++i) { + enterNodes[i] = d3_selection_dataNode(groupData[i]); + } + for (;i < n; ++i) { + exitNodes[i] = group[i]; + } + } + enterNodes.update = updateNodes; + enterNodes.parentNode = updateNodes.parentNode = exitNodes.parentNode = group.parentNode; + enter.push(enterNodes); + update.push(updateNodes); + exit.push(exitNodes); + } + var enter = d3_selection_enter([]), update = d3_selection([]), exit = d3_selection([]); + if (typeof value === "function") { + while (++i < n) { + bind(group = this[i], value.call(group, group.parentNode.__data__, i)); + } + } else { + while (++i < n) { + bind(group = this[i], value); + } + } + update.enter = function() { + return enter; + }; + update.exit = function() { + return exit; + }; + return update; + }; + function d3_selection_dataNode(data) { + return { + __data__: data + }; + } + d3_selectionPrototype.datum = function(value) { + return arguments.length ? this.property("__data__", value) : this.property("__data__"); + }; + d3_selectionPrototype.filter = function(filter) { + var subgroups = [], subgroup, group, node; + if (typeof filter !== "function") filter = d3_selection_filter(filter); + for (var j = 0, m = this.length; j < m; j++) { + subgroups.push(subgroup = []); + subgroup.parentNode = (group = this[j]).parentNode; + for (var i = 0, n = group.length; i < n; i++) { + if ((node = group[i]) && filter.call(node, node.__data__, i, j)) { + subgroup.push(node); + } + } + } + return d3_selection(subgroups); + }; + function d3_selection_filter(selector) { + return function() { + return d3_selectMatches(this, selector); + }; + } + d3_selectionPrototype.order = function() { + for (var j = -1, m = this.length; ++j < m; ) { + for (var group = this[j], i = group.length - 1, next = group[i], node; --i >= 0; ) { + if (node = group[i]) { + if (next && next !== node.nextSibling) next.parentNode.insertBefore(node, next); + next = node; + } + } + } + return this; + }; + d3_selectionPrototype.sort = function(comparator) { + comparator = d3_selection_sortComparator.apply(this, arguments); + for (var j = -1, m = this.length; ++j < m; ) this[j].sort(comparator); + return this.order(); + }; + function d3_selection_sortComparator(comparator) { + if (!arguments.length) comparator = d3_ascending; + return function(a, b) { + return a && b ? comparator(a.__data__, b.__data__) : !a - !b; + }; + } + d3_selectionPrototype.each = function(callback) { + return d3_selection_each(this, function(node, i, j) { + callback.call(node, node.__data__, i, j); + }); + }; + function d3_selection_each(groups, callback) { + for (var j = 0, m = groups.length; j < m; j++) { + for (var group = groups[j], i = 0, n = group.length, node; i < n; i++) { + if (node = group[i]) callback(node, i, j); + } + } + return groups; + } + d3_selectionPrototype.call = function(callback) { + var args = d3_array(arguments); + callback.apply(args[0] = this, args); + return this; + }; + d3_selectionPrototype.empty = function() { + return !this.node(); + }; + d3_selectionPrototype.node = function() { + for (var j = 0, m = this.length; j < m; j++) { + for (var group = this[j], i = 0, n = group.length; i < n; i++) { + var node = group[i]; + if (node) return node; + } + } + return null; + }; + d3_selectionPrototype.size = function() { + var n = 0; + d3_selection_each(this, function() { + ++n; + }); + return n; + }; + function d3_selection_enter(selection) { + d3_subclass(selection, d3_selection_enterPrototype); + return selection; + } + var d3_selection_enterPrototype = []; + d3.selection.enter = d3_selection_enter; + d3.selection.enter.prototype = d3_selection_enterPrototype; + d3_selection_enterPrototype.append = d3_selectionPrototype.append; + d3_selection_enterPrototype.empty = d3_selectionPrototype.empty; + d3_selection_enterPrototype.node = d3_selectionPrototype.node; + d3_selection_enterPrototype.call = d3_selectionPrototype.call; + d3_selection_enterPrototype.size = d3_selectionPrototype.size; + d3_selection_enterPrototype.select = function(selector) { + var subgroups = [], subgroup, subnode, upgroup, group, node; + for (var j = -1, m = this.length; ++j < m; ) { + upgroup = (group = this[j]).update; + subgroups.push(subgroup = []); + subgroup.parentNode = group.parentNode; + for (var i = -1, n = group.length; ++i < n; ) { + if (node = group[i]) { + subgroup.push(upgroup[i] = subnode = selector.call(group.parentNode, node.__data__, i, j)); + subnode.__data__ = node.__data__; + } else { + subgroup.push(null); + } + } + } + return d3_selection(subgroups); + }; + d3_selection_enterPrototype.insert = function(name, before) { + if (arguments.length < 2) before = d3_selection_enterInsertBefore(this); + return d3_selectionPrototype.insert.call(this, name, before); + }; + function d3_selection_enterInsertBefore(enter) { + var i0, j0; + return function(d, i, j) { + var group = enter[j].update, n = group.length, node; + if (j != j0) j0 = j, i0 = 0; + if (i >= i0) i0 = i + 1; + while (!(node = group[i0]) && ++i0 < n) ; + return node; + }; + } + d3.select = function(node) { + var group; + if (typeof node === "string") { + group = [ d3_select(node, d3_document) ]; + group.parentNode = d3_document.documentElement; + } else { + group = [ node ]; + group.parentNode = d3_documentElement(node); + } + return d3_selection([ group ]); + }; + d3.selectAll = function(nodes) { + var group; + if (typeof nodes === "string") { + group = d3_array(d3_selectAll(nodes, d3_document)); + group.parentNode = d3_document.documentElement; + } else { + group = d3_array(nodes); + group.parentNode = null; + } + return d3_selection([ group ]); + }; + d3_selectionPrototype.on = function(type, listener, capture) { + var n = arguments.length; + if (n < 3) { + if (typeof type !== "string") { + if (n < 2) listener = false; + for (capture in type) this.each(d3_selection_on(capture, type[capture], listener)); + return this; + } + if (n < 2) return (n = this.node()["__on" + type]) && n._; + capture = false; + } + return this.each(d3_selection_on(type, listener, capture)); + }; + function d3_selection_on(type, listener, capture) { + var name = "__on" + type, i = type.indexOf("."), wrap = d3_selection_onListener; + if (i > 0) type = type.slice(0, i); + var filter = d3_selection_onFilters.get(type); + if (filter) type = filter, wrap = d3_selection_onFilter; + function onRemove() { + var l = this[name]; + if (l) { + this.removeEventListener(type, l, l.$); + delete this[name]; + } + } + function onAdd() { + var l = wrap(listener, d3_array(arguments)); + onRemove.call(this); + this.addEventListener(type, this[name] = l, l.$ = capture); + l._ = listener; + } + function removeAll() { + var re = new RegExp("^__on([^.]+)" + d3.requote(type) + "$"), match; + for (var name in this) { + if (match = name.match(re)) { + var l = this[name]; + this.removeEventListener(match[1], l, l.$); + delete this[name]; + } + } + } + return i ? listener ? onAdd : onRemove : listener ? d3_noop : removeAll; + } + var d3_selection_onFilters = d3.map({ + mouseenter: "mouseover", + mouseleave: "mouseout" + }); + if (d3_document) { + d3_selection_onFilters.forEach(function(k) { + if ("on" + k in d3_document) d3_selection_onFilters.remove(k); + }); + } + function d3_selection_onListener(listener, argumentz) { + return function(e) { + var o = d3.event; + d3.event = e; + argumentz[0] = this.__data__; + try { + listener.apply(this, argumentz); + } finally { + d3.event = o; + } + }; + } + function d3_selection_onFilter(listener, argumentz) { + var l = d3_selection_onListener(listener, argumentz); + return function(e) { + var target = this, related = e.relatedTarget; + if (!related || related !== target && !(related.compareDocumentPosition(target) & 8)) { + l.call(target, e); + } + }; + } + var d3_event_dragSelect, d3_event_dragId = 0; + function d3_event_dragSuppress(node) { + var name = ".dragsuppress-" + ++d3_event_dragId, click = "click" + name, w = d3.select(d3_window(node)).on("touchmove" + name, d3_eventPreventDefault).on("dragstart" + name, d3_eventPreventDefault).on("selectstart" + name, d3_eventPreventDefault); + if (d3_event_dragSelect == null) { + d3_event_dragSelect = "onselectstart" in node ? false : d3_vendorSymbol(node.style, "userSelect"); + } + if (d3_event_dragSelect) { + var style = d3_documentElement(node).style, select = style[d3_event_dragSelect]; + style[d3_event_dragSelect] = "none"; + } + return function(suppressClick) { + w.on(name, null); + if (d3_event_dragSelect) style[d3_event_dragSelect] = select; + if (suppressClick) { + var off = function() { + w.on(click, null); + }; + w.on(click, function() { + d3_eventPreventDefault(); + off(); + }, true); + setTimeout(off, 0); + } + }; + } + d3.mouse = function(container) { + return d3_mousePoint(container, d3_eventSource()); + }; + var d3_mouse_bug44083 = this.navigator && /WebKit/.test(this.navigator.userAgent) ? -1 : 0; + function d3_mousePoint(container, e) { + if (e.changedTouches) e = e.changedTouches[0]; + var svg = container.ownerSVGElement || container; + if (svg.createSVGPoint) { + var point = svg.createSVGPoint(); + if (d3_mouse_bug44083 < 0) { + var window = d3_window(container); + if (window.scrollX || window.scrollY) { + svg = d3.select("body").append("svg").style({ + position: "absolute", + top: 0, + left: 0, + margin: 0, + padding: 0, + border: "none" + }, "important"); + var ctm = svg[0][0].getScreenCTM(); + d3_mouse_bug44083 = !(ctm.f || ctm.e); + svg.remove(); + } + } + if (d3_mouse_bug44083) point.x = e.pageX, point.y = e.pageY; else point.x = e.clientX, + point.y = e.clientY; + point = point.matrixTransform(container.getScreenCTM().inverse()); + return [ point.x, point.y ]; + } + var rect = container.getBoundingClientRect(); + return [ e.clientX - rect.left - container.clientLeft, e.clientY - rect.top - container.clientTop ]; + } + d3.touch = function(container, touches, identifier) { + if (arguments.length < 3) identifier = touches, touches = d3_eventSource().changedTouches; + if (touches) for (var i = 0, n = touches.length, touch; i < n; ++i) { + if ((touch = touches[i]).identifier === identifier) { + return d3_mousePoint(container, touch); + } + } + }; + d3.behavior.drag = function() { + var event = d3_eventDispatch(drag, "drag", "dragstart", "dragend"), origin = null, mousedown = dragstart(d3_noop, d3.mouse, d3_window, "mousemove", "mouseup"), touchstart = dragstart(d3_behavior_dragTouchId, d3.touch, d3_identity, "touchmove", "touchend"); + function drag() { + this.on("mousedown.drag", mousedown).on("touchstart.drag", touchstart); + } + function dragstart(id, position, subject, move, end) { + return function() { + var that = this, target = d3.event.target.correspondingElement || d3.event.target, parent = that.parentNode, dispatch = event.of(that, arguments), dragged = 0, dragId = id(), dragName = ".drag" + (dragId == null ? "" : "-" + dragId), dragOffset, dragSubject = d3.select(subject(target)).on(move + dragName, moved).on(end + dragName, ended), dragRestore = d3_event_dragSuppress(target), position0 = position(parent, dragId); + if (origin) { + dragOffset = origin.apply(that, arguments); + dragOffset = [ dragOffset.x - position0[0], dragOffset.y - position0[1] ]; + } else { + dragOffset = [ 0, 0 ]; + } + dispatch({ + type: "dragstart" + }); + function moved() { + var position1 = position(parent, dragId), dx, dy; + if (!position1) return; + dx = position1[0] - position0[0]; + dy = position1[1] - position0[1]; + dragged |= dx | dy; + position0 = position1; + dispatch({ + type: "drag", + x: position1[0] + dragOffset[0], + y: position1[1] + dragOffset[1], + dx: dx, + dy: dy + }); + } + function ended() { + if (!position(parent, dragId)) return; + dragSubject.on(move + dragName, null).on(end + dragName, null); + dragRestore(dragged); + dispatch({ + type: "dragend" + }); + } + }; + } + drag.origin = function(x) { + if (!arguments.length) return origin; + origin = x; + return drag; + }; + return d3.rebind(drag, event, "on"); + }; + function d3_behavior_dragTouchId() { + return d3.event.changedTouches[0].identifier; + } + d3.touches = function(container, touches) { + if (arguments.length < 2) touches = d3_eventSource().touches; + return touches ? d3_array(touches).map(function(touch) { + var point = d3_mousePoint(container, touch); + point.identifier = touch.identifier; + return point; + }) : []; + }; + var ε = 1e-6, ε2 = ε * ε, Ï€ = Math.PI, Ï„ = 2 * Ï€, τε = Ï„ - ε, halfÏ€ = Ï€ / 2, d3_radians = Ï€ / 180, d3_degrees = 180 / Ï€; + function d3_sgn(x) { + return x > 0 ? 1 : x < 0 ? -1 : 0; + } + function d3_cross2d(a, b, c) { + return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]); + } + function d3_acos(x) { + return x > 1 ? 0 : x < -1 ? Ï€ : Math.acos(x); + } + function d3_asin(x) { + return x > 1 ? halfÏ€ : x < -1 ? -halfÏ€ : Math.asin(x); + } + function d3_sinh(x) { + return ((x = Math.exp(x)) - 1 / x) / 2; + } + function d3_cosh(x) { + return ((x = Math.exp(x)) + 1 / x) / 2; + } + function d3_tanh(x) { + return ((x = Math.exp(2 * x)) - 1) / (x + 1); + } + function d3_haversin(x) { + return (x = Math.sin(x / 2)) * x; + } + var Ï = Math.SQRT2, Ï2 = 2, Ï4 = 4; + d3.interpolateZoom = function(p0, p1) { + var ux0 = p0[0], uy0 = p0[1], w0 = p0[2], ux1 = p1[0], uy1 = p1[1], w1 = p1[2], dx = ux1 - ux0, dy = uy1 - uy0, d2 = dx * dx + dy * dy, i, S; + if (d2 < ε2) { + S = Math.log(w1 / w0) / Ï; + i = function(t) { + return [ ux0 + t * dx, uy0 + t * dy, w0 * Math.exp(Ï * t * S) ]; + }; + } else { + var d1 = Math.sqrt(d2), b0 = (w1 * w1 - w0 * w0 + Ï4 * d2) / (2 * w0 * Ï2 * d1), b1 = (w1 * w1 - w0 * w0 - Ï4 * d2) / (2 * w1 * Ï2 * d1), r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0), r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1); + S = (r1 - r0) / Ï; + i = function(t) { + var s = t * S, coshr0 = d3_cosh(r0), u = w0 / (Ï2 * d1) * (coshr0 * d3_tanh(Ï * s + r0) - d3_sinh(r0)); + return [ ux0 + u * dx, uy0 + u * dy, w0 * coshr0 / d3_cosh(Ï * s + r0) ]; + }; + } + i.duration = S * 1e3; + return i; + }; + d3.behavior.zoom = function() { + var view = { + x: 0, + y: 0, + k: 1 + }, translate0, center0, center, size = [ 960, 500 ], scaleExtent = d3_behavior_zoomInfinity, duration = 250, zooming = 0, mousedown = "mousedown.zoom", mousemove = "mousemove.zoom", mouseup = "mouseup.zoom", mousewheelTimer, touchstart = "touchstart.zoom", touchtime, event = d3_eventDispatch(zoom, "zoomstart", "zoom", "zoomend"), x0, x1, y0, y1; + if (!d3_behavior_zoomWheel) { + d3_behavior_zoomWheel = "onwheel" in d3_document ? (d3_behavior_zoomDelta = function() { + return -d3.event.deltaY * (d3.event.deltaMode ? 120 : 1); + }, "wheel") : "onmousewheel" in d3_document ? (d3_behavior_zoomDelta = function() { + return d3.event.wheelDelta; + }, "mousewheel") : (d3_behavior_zoomDelta = function() { + return -d3.event.detail; + }, "MozMousePixelScroll"); + } + function zoom(g) { + g.on(mousedown, mousedowned).on(d3_behavior_zoomWheel + ".zoom", mousewheeled).on("dblclick.zoom", dblclicked).on(touchstart, touchstarted); + } + zoom.event = function(g) { + g.each(function() { + var dispatch = event.of(this, arguments), view1 = view; + if (d3_transitionInheritId) { + d3.select(this).transition().each("start.zoom", function() { + view = this.__chart__ || { + x: 0, + y: 0, + k: 1 + }; + zoomstarted(dispatch); + }).tween("zoom:zoom", function() { + var dx = size[0], dy = size[1], cx = center0 ? center0[0] : dx / 2, cy = center0 ? center0[1] : dy / 2, i = d3.interpolateZoom([ (cx - view.x) / view.k, (cy - view.y) / view.k, dx / view.k ], [ (cx - view1.x) / view1.k, (cy - view1.y) / view1.k, dx / view1.k ]); + return function(t) { + var l = i(t), k = dx / l[2]; + this.__chart__ = view = { + x: cx - l[0] * k, + y: cy - l[1] * k, + k: k + }; + zoomed(dispatch); + }; + }).each("interrupt.zoom", function() { + zoomended(dispatch); + }).each("end.zoom", function() { + zoomended(dispatch); + }); + } else { + this.__chart__ = view; + zoomstarted(dispatch); + zoomed(dispatch); + zoomended(dispatch); + } + }); + }; + zoom.translate = function(_) { + if (!arguments.length) return [ view.x, view.y ]; + view = { + x: +_[0], + y: +_[1], + k: view.k + }; + rescale(); + return zoom; + }; + zoom.scale = function(_) { + if (!arguments.length) return view.k; + view = { + x: view.x, + y: view.y, + k: null + }; + scaleTo(+_); + rescale(); + return zoom; + }; + zoom.scaleExtent = function(_) { + if (!arguments.length) return scaleExtent; + scaleExtent = _ == null ? d3_behavior_zoomInfinity : [ +_[0], +_[1] ]; + return zoom; + }; + zoom.center = function(_) { + if (!arguments.length) return center; + center = _ && [ +_[0], +_[1] ]; + return zoom; + }; + zoom.size = function(_) { + if (!arguments.length) return size; + size = _ && [ +_[0], +_[1] ]; + return zoom; + }; + zoom.duration = function(_) { + if (!arguments.length) return duration; + duration = +_; + return zoom; + }; + zoom.x = function(z) { + if (!arguments.length) return x1; + x1 = z; + x0 = z.copy(); + view = { + x: 0, + y: 0, + k: 1 + }; + return zoom; + }; + zoom.y = function(z) { + if (!arguments.length) return y1; + y1 = z; + y0 = z.copy(); + view = { + x: 0, + y: 0, + k: 1 + }; + return zoom; + }; + function location(p) { + return [ (p[0] - view.x) / view.k, (p[1] - view.y) / view.k ]; + } + function point(l) { + return [ l[0] * view.k + view.x, l[1] * view.k + view.y ]; + } + function scaleTo(s) { + view.k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], s)); + } + function translateTo(p, l) { + l = point(l); + view.x += p[0] - l[0]; + view.y += p[1] - l[1]; + } + function zoomTo(that, p, l, k) { + that.__chart__ = { + x: view.x, + y: view.y, + k: view.k + }; + scaleTo(Math.pow(2, k)); + translateTo(center0 = p, l); + that = d3.select(that); + if (duration > 0) that = that.transition().duration(duration); + that.call(zoom.event); + } + function rescale() { + if (x1) x1.domain(x0.range().map(function(x) { + return (x - view.x) / view.k; + }).map(x0.invert)); + if (y1) y1.domain(y0.range().map(function(y) { + return (y - view.y) / view.k; + }).map(y0.invert)); + } + function zoomstarted(dispatch) { + if (!zooming++) dispatch({ + type: "zoomstart" + }); + } + function zoomed(dispatch) { + rescale(); + dispatch({ + type: "zoom", + scale: view.k, + translate: [ view.x, view.y ] + }); + } + function zoomended(dispatch) { + if (!--zooming) dispatch({ + type: "zoomend" + }), center0 = null; + } + function mousedowned() { + var that = this, dispatch = event.of(that, arguments), dragged = 0, subject = d3.select(d3_window(that)).on(mousemove, moved).on(mouseup, ended), location0 = location(d3.mouse(that)), dragRestore = d3_event_dragSuppress(that); + d3_selection_interrupt.call(that); + zoomstarted(dispatch); + function moved() { + dragged = 1; + translateTo(d3.mouse(that), location0); + zoomed(dispatch); + } + function ended() { + subject.on(mousemove, null).on(mouseup, null); + dragRestore(dragged); + zoomended(dispatch); + } + } + function touchstarted() { + var that = this, dispatch = event.of(that, arguments), locations0 = {}, distance0 = 0, scale0, zoomName = ".zoom-" + d3.event.changedTouches[0].identifier, touchmove = "touchmove" + zoomName, touchend = "touchend" + zoomName, targets = [], subject = d3.select(that), dragRestore = d3_event_dragSuppress(that); + started(); + zoomstarted(dispatch); + subject.on(mousedown, null).on(touchstart, started); + function relocate() { + var touches = d3.touches(that); + scale0 = view.k; + touches.forEach(function(t) { + if (t.identifier in locations0) locations0[t.identifier] = location(t); + }); + return touches; + } + function started() { + var target = d3.event.target; + d3.select(target).on(touchmove, moved).on(touchend, ended); + targets.push(target); + var changed = d3.event.changedTouches; + for (var i = 0, n = changed.length; i < n; ++i) { + locations0[changed[i].identifier] = null; + } + var touches = relocate(), now = Date.now(); + if (touches.length === 1) { + if (now - touchtime < 500) { + var p = touches[0]; + zoomTo(that, p, locations0[p.identifier], Math.floor(Math.log(view.k) / Math.LN2) + 1); + d3_eventPreventDefault(); + } + touchtime = now; + } else if (touches.length > 1) { + var p = touches[0], q = touches[1], dx = p[0] - q[0], dy = p[1] - q[1]; + distance0 = dx * dx + dy * dy; + } + } + function moved() { + var touches = d3.touches(that), p0, l0, p1, l1; + d3_selection_interrupt.call(that); + for (var i = 0, n = touches.length; i < n; ++i, l1 = null) { + p1 = touches[i]; + if (l1 = locations0[p1.identifier]) { + if (l0) break; + p0 = p1, l0 = l1; + } + } + if (l1) { + var distance1 = (distance1 = p1[0] - p0[0]) * distance1 + (distance1 = p1[1] - p0[1]) * distance1, scale1 = distance0 && Math.sqrt(distance1 / distance0); + p0 = [ (p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2 ]; + l0 = [ (l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2 ]; + scaleTo(scale1 * scale0); + } + touchtime = null; + translateTo(p0, l0); + zoomed(dispatch); + } + function ended() { + if (d3.event.touches.length) { + var changed = d3.event.changedTouches; + for (var i = 0, n = changed.length; i < n; ++i) { + delete locations0[changed[i].identifier]; + } + for (var identifier in locations0) { + return void relocate(); + } + } + d3.selectAll(targets).on(zoomName, null); + subject.on(mousedown, mousedowned).on(touchstart, touchstarted); + dragRestore(); + zoomended(dispatch); + } + } + function mousewheeled() { + var dispatch = event.of(this, arguments); + if (mousewheelTimer) clearTimeout(mousewheelTimer); else d3_selection_interrupt.call(this), + translate0 = location(center0 = center || d3.mouse(this)), zoomstarted(dispatch); + mousewheelTimer = setTimeout(function() { + mousewheelTimer = null; + zoomended(dispatch); + }, 50); + d3_eventPreventDefault(); + scaleTo(Math.pow(2, d3_behavior_zoomDelta() * .002) * view.k); + translateTo(center0, translate0); + zoomed(dispatch); + } + function dblclicked() { + var p = d3.mouse(this), k = Math.log(view.k) / Math.LN2; + zoomTo(this, p, location(p), d3.event.shiftKey ? Math.ceil(k) - 1 : Math.floor(k) + 1); + } + return d3.rebind(zoom, event, "on"); + }; + var d3_behavior_zoomInfinity = [ 0, Infinity ], d3_behavior_zoomDelta, d3_behavior_zoomWheel; + d3.color = d3_color; + function d3_color() {} + d3_color.prototype.toString = function() { + return this.rgb() + ""; + }; + d3.hsl = d3_hsl; + function d3_hsl(h, s, l) { + return this instanceof d3_hsl ? void (this.h = +h, this.s = +s, this.l = +l) : arguments.length < 2 ? h instanceof d3_hsl ? new d3_hsl(h.h, h.s, h.l) : d3_rgb_parse("" + h, d3_rgb_hsl, d3_hsl) : new d3_hsl(h, s, l); + } + var d3_hslPrototype = d3_hsl.prototype = new d3_color(); + d3_hslPrototype.brighter = function(k) { + k = Math.pow(.7, arguments.length ? k : 1); + return new d3_hsl(this.h, this.s, this.l / k); + }; + d3_hslPrototype.darker = function(k) { + k = Math.pow(.7, arguments.length ? k : 1); + return new d3_hsl(this.h, this.s, k * this.l); + }; + d3_hslPrototype.rgb = function() { + return d3_hsl_rgb(this.h, this.s, this.l); + }; + function d3_hsl_rgb(h, s, l) { + var m1, m2; + h = isNaN(h) ? 0 : (h %= 360) < 0 ? h + 360 : h; + s = isNaN(s) ? 0 : s < 0 ? 0 : s > 1 ? 1 : s; + l = l < 0 ? 0 : l > 1 ? 1 : l; + m2 = l <= .5 ? l * (1 + s) : l + s - l * s; + m1 = 2 * l - m2; + function v(h) { + if (h > 360) h -= 360; else if (h < 0) h += 360; + if (h < 60) return m1 + (m2 - m1) * h / 60; + if (h < 180) return m2; + if (h < 240) return m1 + (m2 - m1) * (240 - h) / 60; + return m1; + } + function vv(h) { + return Math.round(v(h) * 255); + } + return new d3_rgb(vv(h + 120), vv(h), vv(h - 120)); + } + d3.hcl = d3_hcl; + function d3_hcl(h, c, l) { + return this instanceof d3_hcl ? void (this.h = +h, this.c = +c, this.l = +l) : arguments.length < 2 ? h instanceof d3_hcl ? new d3_hcl(h.h, h.c, h.l) : h instanceof d3_lab ? d3_lab_hcl(h.l, h.a, h.b) : d3_lab_hcl((h = d3_rgb_lab((h = d3.rgb(h)).r, h.g, h.b)).l, h.a, h.b) : new d3_hcl(h, c, l); + } + var d3_hclPrototype = d3_hcl.prototype = new d3_color(); + d3_hclPrototype.brighter = function(k) { + return new d3_hcl(this.h, this.c, Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1))); + }; + d3_hclPrototype.darker = function(k) { + return new d3_hcl(this.h, this.c, Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1))); + }; + d3_hclPrototype.rgb = function() { + return d3_hcl_lab(this.h, this.c, this.l).rgb(); + }; + function d3_hcl_lab(h, c, l) { + if (isNaN(h)) h = 0; + if (isNaN(c)) c = 0; + return new d3_lab(l, Math.cos(h *= d3_radians) * c, Math.sin(h) * c); + } + d3.lab = d3_lab; + function d3_lab(l, a, b) { + return this instanceof d3_lab ? void (this.l = +l, this.a = +a, this.b = +b) : arguments.length < 2 ? l instanceof d3_lab ? new d3_lab(l.l, l.a, l.b) : l instanceof d3_hcl ? d3_hcl_lab(l.h, l.c, l.l) : d3_rgb_lab((l = d3_rgb(l)).r, l.g, l.b) : new d3_lab(l, a, b); + } + var d3_lab_K = 18; + var d3_lab_X = .95047, d3_lab_Y = 1, d3_lab_Z = 1.08883; + var d3_labPrototype = d3_lab.prototype = new d3_color(); + d3_labPrototype.brighter = function(k) { + return new d3_lab(Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1)), this.a, this.b); + }; + d3_labPrototype.darker = function(k) { + return new d3_lab(Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1)), this.a, this.b); + }; + d3_labPrototype.rgb = function() { + return d3_lab_rgb(this.l, this.a, this.b); + }; + function d3_lab_rgb(l, a, b) { + var y = (l + 16) / 116, x = y + a / 500, z = y - b / 200; + x = d3_lab_xyz(x) * d3_lab_X; + y = d3_lab_xyz(y) * d3_lab_Y; + z = d3_lab_xyz(z) * d3_lab_Z; + return new d3_rgb(d3_xyz_rgb(3.2404542 * x - 1.5371385 * y - .4985314 * z), d3_xyz_rgb(-.969266 * x + 1.8760108 * y + .041556 * z), d3_xyz_rgb(.0556434 * x - .2040259 * y + 1.0572252 * z)); + } + function d3_lab_hcl(l, a, b) { + return l > 0 ? new d3_hcl(Math.atan2(b, a) * d3_degrees, Math.sqrt(a * a + b * b), l) : new d3_hcl(NaN, NaN, l); + } + function d3_lab_xyz(x) { + return x > .206893034 ? x * x * x : (x - 4 / 29) / 7.787037; + } + function d3_xyz_lab(x) { + return x > .008856 ? Math.pow(x, 1 / 3) : 7.787037 * x + 4 / 29; + } + function d3_xyz_rgb(r) { + return Math.round(255 * (r <= .00304 ? 12.92 * r : 1.055 * Math.pow(r, 1 / 2.4) - .055)); + } + d3.rgb = d3_rgb; + function d3_rgb(r, g, b) { + return this instanceof d3_rgb ? void (this.r = ~~r, this.g = ~~g, this.b = ~~b) : arguments.length < 2 ? r instanceof d3_rgb ? new d3_rgb(r.r, r.g, r.b) : d3_rgb_parse("" + r, d3_rgb, d3_hsl_rgb) : new d3_rgb(r, g, b); + } + function d3_rgbNumber(value) { + return new d3_rgb(value >> 16, value >> 8 & 255, value & 255); + } + function d3_rgbString(value) { + return d3_rgbNumber(value) + ""; + } + var d3_rgbPrototype = d3_rgb.prototype = new d3_color(); + d3_rgbPrototype.brighter = function(k) { + k = Math.pow(.7, arguments.length ? k : 1); + var r = this.r, g = this.g, b = this.b, i = 30; + if (!r && !g && !b) return new d3_rgb(i, i, i); + if (r && r < i) r = i; + if (g && g < i) g = i; + if (b && b < i) b = i; + return new d3_rgb(Math.min(255, r / k), Math.min(255, g / k), Math.min(255, b / k)); + }; + d3_rgbPrototype.darker = function(k) { + k = Math.pow(.7, arguments.length ? k : 1); + return new d3_rgb(k * this.r, k * this.g, k * this.b); + }; + d3_rgbPrototype.hsl = function() { + return d3_rgb_hsl(this.r, this.g, this.b); + }; + d3_rgbPrototype.toString = function() { + return "#" + d3_rgb_hex(this.r) + d3_rgb_hex(this.g) + d3_rgb_hex(this.b); + }; + function d3_rgb_hex(v) { + return v < 16 ? "0" + Math.max(0, v).toString(16) : Math.min(255, v).toString(16); + } + function d3_rgb_parse(format, rgb, hsl) { + var r = 0, g = 0, b = 0, m1, m2, color; + m1 = /([a-z]+)\((.*)\)/.exec(format = format.toLowerCase()); + if (m1) { + m2 = m1[2].split(","); + switch (m1[1]) { + case "hsl": + { + return hsl(parseFloat(m2[0]), parseFloat(m2[1]) / 100, parseFloat(m2[2]) / 100); + } + + case "rgb": + { + return rgb(d3_rgb_parseNumber(m2[0]), d3_rgb_parseNumber(m2[1]), d3_rgb_parseNumber(m2[2])); + } + } + } + if (color = d3_rgb_names.get(format)) { + return rgb(color.r, color.g, color.b); + } + if (format != null && format.charAt(0) === "#" && !isNaN(color = parseInt(format.slice(1), 16))) { + if (format.length === 4) { + r = (color & 3840) >> 4; + r = r >> 4 | r; + g = color & 240; + g = g >> 4 | g; + b = color & 15; + b = b << 4 | b; + } else if (format.length === 7) { + r = (color & 16711680) >> 16; + g = (color & 65280) >> 8; + b = color & 255; + } + } + return rgb(r, g, b); + } + function d3_rgb_hsl(r, g, b) { + var min = Math.min(r /= 255, g /= 255, b /= 255), max = Math.max(r, g, b), d = max - min, h, s, l = (max + min) / 2; + if (d) { + s = l < .5 ? d / (max + min) : d / (2 - max - min); + if (r == max) h = (g - b) / d + (g < b ? 6 : 0); else if (g == max) h = (b - r) / d + 2; else h = (r - g) / d + 4; + h *= 60; + } else { + h = NaN; + s = l > 0 && l < 1 ? 0 : h; + } + return new d3_hsl(h, s, l); + } + function d3_rgb_lab(r, g, b) { + r = d3_rgb_xyz(r); + g = d3_rgb_xyz(g); + b = d3_rgb_xyz(b); + var x = d3_xyz_lab((.4124564 * r + .3575761 * g + .1804375 * b) / d3_lab_X), y = d3_xyz_lab((.2126729 * r + .7151522 * g + .072175 * b) / d3_lab_Y), z = d3_xyz_lab((.0193339 * r + .119192 * g + .9503041 * b) / d3_lab_Z); + return d3_lab(116 * y - 16, 500 * (x - y), 200 * (y - z)); + } + function d3_rgb_xyz(r) { + return (r /= 255) <= .04045 ? r / 12.92 : Math.pow((r + .055) / 1.055, 2.4); + } + function d3_rgb_parseNumber(c) { + var f = parseFloat(c); + return c.charAt(c.length - 1) === "%" ? Math.round(f * 2.55) : f; + } + var d3_rgb_names = d3.map({ + aliceblue: 15792383, + antiquewhite: 16444375, + aqua: 65535, + aquamarine: 8388564, + azure: 15794175, + beige: 16119260, + bisque: 16770244, + black: 0, + blanchedalmond: 16772045, + blue: 255, + blueviolet: 9055202, + brown: 10824234, + burlywood: 14596231, + cadetblue: 6266528, + chartreuse: 8388352, + chocolate: 13789470, + coral: 16744272, + cornflowerblue: 6591981, + cornsilk: 16775388, + crimson: 14423100, + cyan: 65535, + darkblue: 139, + darkcyan: 35723, + darkgoldenrod: 12092939, + darkgray: 11119017, + darkgreen: 25600, + darkgrey: 11119017, + darkkhaki: 12433259, + darkmagenta: 9109643, + darkolivegreen: 5597999, + darkorange: 16747520, + darkorchid: 10040012, + darkred: 9109504, + darksalmon: 15308410, + darkseagreen: 9419919, + darkslateblue: 4734347, + darkslategray: 3100495, + darkslategrey: 3100495, + darkturquoise: 52945, + darkviolet: 9699539, + deeppink: 16716947, + deepskyblue: 49151, + dimgray: 6908265, + dimgrey: 6908265, + dodgerblue: 2003199, + firebrick: 11674146, + floralwhite: 16775920, + forestgreen: 2263842, + fuchsia: 16711935, + gainsboro: 14474460, + ghostwhite: 16316671, + gold: 16766720, + goldenrod: 14329120, + gray: 8421504, + green: 32768, + greenyellow: 11403055, + grey: 8421504, + honeydew: 15794160, + hotpink: 16738740, + indianred: 13458524, + indigo: 4915330, + ivory: 16777200, + khaki: 15787660, + lavender: 15132410, + lavenderblush: 16773365, + lawngreen: 8190976, + lemonchiffon: 16775885, + lightblue: 11393254, + lightcoral: 15761536, + lightcyan: 14745599, + lightgoldenrodyellow: 16448210, + lightgray: 13882323, + lightgreen: 9498256, + lightgrey: 13882323, + lightpink: 16758465, + lightsalmon: 16752762, + lightseagreen: 2142890, + lightskyblue: 8900346, + lightslategray: 7833753, + lightslategrey: 7833753, + lightsteelblue: 11584734, + lightyellow: 16777184, + lime: 65280, + limegreen: 3329330, + linen: 16445670, + magenta: 16711935, + maroon: 8388608, + mediumaquamarine: 6737322, + mediumblue: 205, + mediumorchid: 12211667, + mediumpurple: 9662683, + mediumseagreen: 3978097, + mediumslateblue: 8087790, + mediumspringgreen: 64154, + mediumturquoise: 4772300, + mediumvioletred: 13047173, + midnightblue: 1644912, + mintcream: 16121850, + mistyrose: 16770273, + moccasin: 16770229, + navajowhite: 16768685, + navy: 128, + oldlace: 16643558, + olive: 8421376, + olivedrab: 7048739, + orange: 16753920, + orangered: 16729344, + orchid: 14315734, + palegoldenrod: 15657130, + palegreen: 10025880, + paleturquoise: 11529966, + palevioletred: 14381203, + papayawhip: 16773077, + peachpuff: 16767673, + peru: 13468991, + pink: 16761035, + plum: 14524637, + powderblue: 11591910, + purple: 8388736, + rebeccapurple: 6697881, + red: 16711680, + rosybrown: 12357519, + royalblue: 4286945, + saddlebrown: 9127187, + salmon: 16416882, + sandybrown: 16032864, + seagreen: 3050327, + seashell: 16774638, + sienna: 10506797, + silver: 12632256, + skyblue: 8900331, + slateblue: 6970061, + slategray: 7372944, + slategrey: 7372944, + snow: 16775930, + springgreen: 65407, + steelblue: 4620980, + tan: 13808780, + teal: 32896, + thistle: 14204888, + tomato: 16737095, + turquoise: 4251856, + violet: 15631086, + wheat: 16113331, + white: 16777215, + whitesmoke: 16119285, + yellow: 16776960, + yellowgreen: 10145074 + }); + d3_rgb_names.forEach(function(key, value) { + d3_rgb_names.set(key, d3_rgbNumber(value)); + }); + function d3_functor(v) { + return typeof v === "function" ? v : function() { + return v; + }; + } + d3.functor = d3_functor; + d3.xhr = d3_xhrType(d3_identity); + function d3_xhrType(response) { + return function(url, mimeType, callback) { + if (arguments.length === 2 && typeof mimeType === "function") callback = mimeType, + mimeType = null; + return d3_xhr(url, mimeType, response, callback); + }; + } + function d3_xhr(url, mimeType, response, callback) { + var xhr = {}, dispatch = d3.dispatch("beforesend", "progress", "load", "error"), headers = {}, request = new XMLHttpRequest(), responseType = null; + if (this.XDomainRequest && !("withCredentials" in request) && /^(http(s)?:)?\/\//.test(url)) request = new XDomainRequest(); + "onload" in request ? request.onload = request.onerror = respond : request.onreadystatechange = function() { + request.readyState > 3 && respond(); + }; + function respond() { + var status = request.status, result; + if (!status && d3_xhrHasResponse(request) || status >= 200 && status < 300 || status === 304) { + try { + result = response.call(xhr, request); + } catch (e) { + dispatch.error.call(xhr, e); + return; + } + dispatch.load.call(xhr, result); + } else { + dispatch.error.call(xhr, request); + } + } + request.onprogress = function(event) { + var o = d3.event; + d3.event = event; + try { + dispatch.progress.call(xhr, request); + } finally { + d3.event = o; + } + }; + xhr.header = function(name, value) { + name = (name + "").toLowerCase(); + if (arguments.length < 2) return headers[name]; + if (value == null) delete headers[name]; else headers[name] = value + ""; + return xhr; + }; + xhr.mimeType = function(value) { + if (!arguments.length) return mimeType; + mimeType = value == null ? null : value + ""; + return xhr; + }; + xhr.responseType = function(value) { + if (!arguments.length) return responseType; + responseType = value; + return xhr; + }; + xhr.response = function(value) { + response = value; + return xhr; + }; + [ "get", "post" ].forEach(function(method) { + xhr[method] = function() { + return xhr.send.apply(xhr, [ method ].concat(d3_array(arguments))); + }; + }); + xhr.send = function(method, data, callback) { + if (arguments.length === 2 && typeof data === "function") callback = data, data = null; + request.open(method, url, true); + if (mimeType != null && !("accept" in headers)) headers["accept"] = mimeType + ",*/*"; + if (request.setRequestHeader) for (var name in headers) request.setRequestHeader(name, headers[name]); + if (mimeType != null && request.overrideMimeType) request.overrideMimeType(mimeType); + if (responseType != null) request.responseType = responseType; + if (callback != null) xhr.on("error", callback).on("load", function(request) { + callback(null, request); + }); + dispatch.beforesend.call(xhr, request); + request.send(data == null ? null : data); + return xhr; + }; + xhr.abort = function() { + request.abort(); + return xhr; + }; + d3.rebind(xhr, dispatch, "on"); + return callback == null ? xhr : xhr.get(d3_xhr_fixCallback(callback)); + } + function d3_xhr_fixCallback(callback) { + return callback.length === 1 ? function(error, request) { + callback(error == null ? request : null); + } : callback; + } + function d3_xhrHasResponse(request) { + var type = request.responseType; + return type && type !== "text" ? request.response : request.responseText; + } + d3.dsv = function(delimiter, mimeType) { + var reFormat = new RegExp('["' + delimiter + "\n]"), delimiterCode = delimiter.charCodeAt(0); + function dsv(url, row, callback) { + if (arguments.length < 3) callback = row, row = null; + var xhr = d3_xhr(url, mimeType, row == null ? response : typedResponse(row), callback); + xhr.row = function(_) { + return arguments.length ? xhr.response((row = _) == null ? response : typedResponse(_)) : row; + }; + return xhr; + } + function response(request) { + return dsv.parse(request.responseText); + } + function typedResponse(f) { + return function(request) { + return dsv.parse(request.responseText, f); + }; + } + dsv.parse = function(text, f) { + var o; + return dsv.parseRows(text, function(row, i) { + if (o) return o(row, i - 1); + var a = new Function("d", "return {" + row.map(function(name, i) { + return JSON.stringify(name) + ": d[" + i + "]"; + }).join(",") + "}"); + o = f ? function(row, i) { + return f(a(row), i); + } : a; + }); + }; + dsv.parseRows = function(text, f) { + var EOL = {}, EOF = {}, rows = [], N = text.length, I = 0, n = 0, t, eol; + function token() { + if (I >= N) return EOF; + if (eol) return eol = false, EOL; + var j = I; + if (text.charCodeAt(j) === 34) { + var i = j; + while (i++ < N) { + if (text.charCodeAt(i) === 34) { + if (text.charCodeAt(i + 1) !== 34) break; + ++i; + } + } + I = i + 2; + var c = text.charCodeAt(i + 1); + if (c === 13) { + eol = true; + if (text.charCodeAt(i + 2) === 10) ++I; + } else if (c === 10) { + eol = true; + } + return text.slice(j + 1, i).replace(/""/g, '"'); + } + while (I < N) { + var c = text.charCodeAt(I++), k = 1; + if (c === 10) eol = true; else if (c === 13) { + eol = true; + if (text.charCodeAt(I) === 10) ++I, ++k; + } else if (c !== delimiterCode) continue; + return text.slice(j, I - k); + } + return text.slice(j); + } + while ((t = token()) !== EOF) { + var a = []; + while (t !== EOL && t !== EOF) { + a.push(t); + t = token(); + } + if (f && (a = f(a, n++)) == null) continue; + rows.push(a); + } + return rows; + }; + dsv.format = function(rows) { + if (Array.isArray(rows[0])) return dsv.formatRows(rows); + var fieldSet = new d3_Set(), fields = []; + rows.forEach(function(row) { + for (var field in row) { + if (!fieldSet.has(field)) { + fields.push(fieldSet.add(field)); + } + } + }); + return [ fields.map(formatValue).join(delimiter) ].concat(rows.map(function(row) { + return fields.map(function(field) { + return formatValue(row[field]); + }).join(delimiter); + })).join("\n"); + }; + dsv.formatRows = function(rows) { + return rows.map(formatRow).join("\n"); + }; + function formatRow(row) { + return row.map(formatValue).join(delimiter); + } + function formatValue(text) { + return reFormat.test(text) ? '"' + text.replace(/\"/g, '""') + '"' : text; + } + return dsv; + }; + d3.csv = d3.dsv(",", "text/csv"); + d3.tsv = d3.dsv(" ", "text/tab-separated-values"); + var d3_timer_queueHead, d3_timer_queueTail, d3_timer_interval, d3_timer_timeout, d3_timer_frame = this[d3_vendorSymbol(this, "requestAnimationFrame")] || function(callback) { + setTimeout(callback, 17); + }; + d3.timer = function() { + d3_timer.apply(this, arguments); + }; + function d3_timer(callback, delay, then) { + var n = arguments.length; + if (n < 2) delay = 0; + if (n < 3) then = Date.now(); + var time = then + delay, timer = { + c: callback, + t: time, + n: null + }; + if (d3_timer_queueTail) d3_timer_queueTail.n = timer; else d3_timer_queueHead = timer; + d3_timer_queueTail = timer; + if (!d3_timer_interval) { + d3_timer_timeout = clearTimeout(d3_timer_timeout); + d3_timer_interval = 1; + d3_timer_frame(d3_timer_step); + } + return timer; + } + function d3_timer_step() { + var now = d3_timer_mark(), delay = d3_timer_sweep() - now; + if (delay > 24) { + if (isFinite(delay)) { + clearTimeout(d3_timer_timeout); + d3_timer_timeout = setTimeout(d3_timer_step, delay); + } + d3_timer_interval = 0; + } else { + d3_timer_interval = 1; + d3_timer_frame(d3_timer_step); + } + } + d3.timer.flush = function() { + d3_timer_mark(); + d3_timer_sweep(); + }; + function d3_timer_mark() { + var now = Date.now(), timer = d3_timer_queueHead; + while (timer) { + if (now >= timer.t && timer.c(now - timer.t)) timer.c = null; + timer = timer.n; + } + return now; + } + function d3_timer_sweep() { + var t0, t1 = d3_timer_queueHead, time = Infinity; + while (t1) { + if (t1.c) { + if (t1.t < time) time = t1.t; + t1 = (t0 = t1).n; + } else { + t1 = t0 ? t0.n = t1.n : d3_timer_queueHead = t1.n; + } + } + d3_timer_queueTail = t0; + return time; + } + function d3_format_precision(x, p) { + return p - (x ? Math.ceil(Math.log(x) / Math.LN10) : 1); + } + d3.round = function(x, n) { + return n ? Math.round(x * (n = Math.pow(10, n))) / n : Math.round(x); + }; + var d3_formatPrefixes = [ "y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y" ].map(d3_formatPrefix); + d3.formatPrefix = function(value, precision) { + var i = 0; + if (value = +value) { + if (value < 0) value *= -1; + if (precision) value = d3.round(value, d3_format_precision(value, precision)); + i = 1 + Math.floor(1e-12 + Math.log(value) / Math.LN10); + i = Math.max(-24, Math.min(24, Math.floor((i - 1) / 3) * 3)); + } + return d3_formatPrefixes[8 + i / 3]; + }; + function d3_formatPrefix(d, i) { + var k = Math.pow(10, abs(8 - i) * 3); + return { + scale: i > 8 ? function(d) { + return d / k; + } : function(d) { + return d * k; + }, + symbol: d + }; + } + function d3_locale_numberFormat(locale) { + var locale_decimal = locale.decimal, locale_thousands = locale.thousands, locale_grouping = locale.grouping, locale_currency = locale.currency, formatGroup = locale_grouping && locale_thousands ? function(value, width) { + var i = value.length, t = [], j = 0, g = locale_grouping[0], length = 0; + while (i > 0 && g > 0) { + if (length + g + 1 > width) g = Math.max(1, width - length); + t.push(value.substring(i -= g, i + g)); + if ((length += g + 1) > width) break; + g = locale_grouping[j = (j + 1) % locale_grouping.length]; + } + return t.reverse().join(locale_thousands); + } : d3_identity; + return function(specifier) { + var match = d3_format_re.exec(specifier), fill = match[1] || " ", align = match[2] || ">", sign = match[3] || "-", symbol = match[4] || "", zfill = match[5], width = +match[6], comma = match[7], precision = match[8], type = match[9], scale = 1, prefix = "", suffix = "", integer = false, exponent = true; + if (precision) precision = +precision.substring(1); + if (zfill || fill === "0" && align === "=") { + zfill = fill = "0"; + align = "="; + } + switch (type) { + case "n": + comma = true; + type = "g"; + break; + + case "%": + scale = 100; + suffix = "%"; + type = "f"; + break; + + case "p": + scale = 100; + suffix = "%"; + type = "r"; + break; + + case "b": + case "o": + case "x": + case "X": + if (symbol === "#") prefix = "0" + type.toLowerCase(); + + case "c": + exponent = false; + + case "d": + integer = true; + precision = 0; + break; + + case "s": + scale = -1; + type = "r"; + break; + } + if (symbol === "$") prefix = locale_currency[0], suffix = locale_currency[1]; + if (type == "r" && !precision) type = "g"; + if (precision != null) { + if (type == "g") precision = Math.max(1, Math.min(21, precision)); else if (type == "e" || type == "f") precision = Math.max(0, Math.min(20, precision)); + } + type = d3_format_types.get(type) || d3_format_typeDefault; + var zcomma = zfill && comma; + return function(value) { + var fullSuffix = suffix; + if (integer && value % 1) return ""; + var negative = value < 0 || value === 0 && 1 / value < 0 ? (value = -value, "-") : sign === "-" ? "" : sign; + if (scale < 0) { + var unit = d3.formatPrefix(value, precision); + value = unit.scale(value); + fullSuffix = unit.symbol + suffix; + } else { + value *= scale; + } + value = type(value, precision); + var i = value.lastIndexOf("."), before, after; + if (i < 0) { + var j = exponent ? value.lastIndexOf("e") : -1; + if (j < 0) before = value, after = ""; else before = value.substring(0, j), after = value.substring(j); + } else { + before = value.substring(0, i); + after = locale_decimal + value.substring(i + 1); + } + if (!zfill && comma) before = formatGroup(before, Infinity); + var length = prefix.length + before.length + after.length + (zcomma ? 0 : negative.length), padding = length < width ? new Array(length = width - length + 1).join(fill) : ""; + if (zcomma) before = formatGroup(padding + before, padding.length ? width - after.length : Infinity); + negative += prefix; + value = before + after; + return (align === "<" ? negative + value + padding : align === ">" ? padding + negative + value : align === "^" ? padding.substring(0, length >>= 1) + negative + value + padding.substring(length) : negative + (zcomma ? value : padding + value)) + fullSuffix; + }; + }; + } + var d3_format_re = /(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i; + var d3_format_types = d3.map({ + b: function(x) { + return x.toString(2); + }, + c: function(x) { + return String.fromCharCode(x); + }, + o: function(x) { + return x.toString(8); + }, + x: function(x) { + return x.toString(16); + }, + X: function(x) { + return x.toString(16).toUpperCase(); + }, + g: function(x, p) { + return x.toPrecision(p); + }, + e: function(x, p) { + return x.toExponential(p); + }, + f: function(x, p) { + return x.toFixed(p); + }, + r: function(x, p) { + return (x = d3.round(x, d3_format_precision(x, p))).toFixed(Math.max(0, Math.min(20, d3_format_precision(x * (1 + 1e-15), p)))); + } + }); + function d3_format_typeDefault(x) { + return x + ""; + } + var d3_time = d3.time = {}, d3_date = Date; + function d3_date_utc() { + this._ = new Date(arguments.length > 1 ? Date.UTC.apply(this, arguments) : arguments[0]); + } + d3_date_utc.prototype = { + getDate: function() { + return this._.getUTCDate(); + }, + getDay: function() { + return this._.getUTCDay(); + }, + getFullYear: function() { + return this._.getUTCFullYear(); + }, + getHours: function() { + return this._.getUTCHours(); + }, + getMilliseconds: function() { + return this._.getUTCMilliseconds(); + }, + getMinutes: function() { + return this._.getUTCMinutes(); + }, + getMonth: function() { + return this._.getUTCMonth(); + }, + getSeconds: function() { + return this._.getUTCSeconds(); + }, + getTime: function() { + return this._.getTime(); + }, + getTimezoneOffset: function() { + return 0; + }, + valueOf: function() { + return this._.valueOf(); + }, + setDate: function() { + d3_time_prototype.setUTCDate.apply(this._, arguments); + }, + setDay: function() { + d3_time_prototype.setUTCDay.apply(this._, arguments); + }, + setFullYear: function() { + d3_time_prototype.setUTCFullYear.apply(this._, arguments); + }, + setHours: function() { + d3_time_prototype.setUTCHours.apply(this._, arguments); + }, + setMilliseconds: function() { + d3_time_prototype.setUTCMilliseconds.apply(this._, arguments); + }, + setMinutes: function() { + d3_time_prototype.setUTCMinutes.apply(this._, arguments); + }, + setMonth: function() { + d3_time_prototype.setUTCMonth.apply(this._, arguments); + }, + setSeconds: function() { + d3_time_prototype.setUTCSeconds.apply(this._, arguments); + }, + setTime: function() { + d3_time_prototype.setTime.apply(this._, arguments); + } + }; + var d3_time_prototype = Date.prototype; + function d3_time_interval(local, step, number) { + function round(date) { + var d0 = local(date), d1 = offset(d0, 1); + return date - d0 < d1 - date ? d0 : d1; + } + function ceil(date) { + step(date = local(new d3_date(date - 1)), 1); + return date; + } + function offset(date, k) { + step(date = new d3_date(+date), k); + return date; + } + function range(t0, t1, dt) { + var time = ceil(t0), times = []; + if (dt > 1) { + while (time < t1) { + if (!(number(time) % dt)) times.push(new Date(+time)); + step(time, 1); + } + } else { + while (time < t1) times.push(new Date(+time)), step(time, 1); + } + return times; + } + function range_utc(t0, t1, dt) { + try { + d3_date = d3_date_utc; + var utc = new d3_date_utc(); + utc._ = t0; + return range(utc, t1, dt); + } finally { + d3_date = Date; + } + } + local.floor = local; + local.round = round; + local.ceil = ceil; + local.offset = offset; + local.range = range; + var utc = local.utc = d3_time_interval_utc(local); + utc.floor = utc; + utc.round = d3_time_interval_utc(round); + utc.ceil = d3_time_interval_utc(ceil); + utc.offset = d3_time_interval_utc(offset); + utc.range = range_utc; + return local; + } + function d3_time_interval_utc(method) { + return function(date, k) { + try { + d3_date = d3_date_utc; + var utc = new d3_date_utc(); + utc._ = date; + return method(utc, k)._; + } finally { + d3_date = Date; + } + }; + } + d3_time.year = d3_time_interval(function(date) { + date = d3_time.day(date); + date.setMonth(0, 1); + return date; + }, function(date, offset) { + date.setFullYear(date.getFullYear() + offset); + }, function(date) { + return date.getFullYear(); + }); + d3_time.years = d3_time.year.range; + d3_time.years.utc = d3_time.year.utc.range; + d3_time.day = d3_time_interval(function(date) { + var day = new d3_date(2e3, 0); + day.setFullYear(date.getFullYear(), date.getMonth(), date.getDate()); + return day; + }, function(date, offset) { + date.setDate(date.getDate() + offset); + }, function(date) { + return date.getDate() - 1; + }); + d3_time.days = d3_time.day.range; + d3_time.days.utc = d3_time.day.utc.range; + d3_time.dayOfYear = function(date) { + var year = d3_time.year(date); + return Math.floor((date - year - (date.getTimezoneOffset() - year.getTimezoneOffset()) * 6e4) / 864e5); + }; + [ "sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday" ].forEach(function(day, i) { + i = 7 - i; + var interval = d3_time[day] = d3_time_interval(function(date) { + (date = d3_time.day(date)).setDate(date.getDate() - (date.getDay() + i) % 7); + return date; + }, function(date, offset) { + date.setDate(date.getDate() + Math.floor(offset) * 7); + }, function(date) { + var day = d3_time.year(date).getDay(); + return Math.floor((d3_time.dayOfYear(date) + (day + i) % 7) / 7) - (day !== i); + }); + d3_time[day + "s"] = interval.range; + d3_time[day + "s"].utc = interval.utc.range; + d3_time[day + "OfYear"] = function(date) { + var day = d3_time.year(date).getDay(); + return Math.floor((d3_time.dayOfYear(date) + (day + i) % 7) / 7); + }; + }); + d3_time.week = d3_time.sunday; + d3_time.weeks = d3_time.sunday.range; + d3_time.weeks.utc = d3_time.sunday.utc.range; + d3_time.weekOfYear = d3_time.sundayOfYear; + function d3_locale_timeFormat(locale) { + var locale_dateTime = locale.dateTime, locale_date = locale.date, locale_time = locale.time, locale_periods = locale.periods, locale_days = locale.days, locale_shortDays = locale.shortDays, locale_months = locale.months, locale_shortMonths = locale.shortMonths; + function d3_time_format(template) { + var n = template.length; + function format(date) { + var string = [], i = -1, j = 0, c, p, f; + while (++i < n) { + if (template.charCodeAt(i) === 37) { + string.push(template.slice(j, i)); + if ((p = d3_time_formatPads[c = template.charAt(++i)]) != null) c = template.charAt(++i); + if (f = d3_time_formats[c]) c = f(date, p == null ? c === "e" ? " " : "0" : p); + string.push(c); + j = i + 1; + } + } + string.push(template.slice(j, i)); + return string.join(""); + } + format.parse = function(string) { + var d = { + y: 1900, + m: 0, + d: 1, + H: 0, + M: 0, + S: 0, + L: 0, + Z: null + }, i = d3_time_parse(d, template, string, 0); + if (i != string.length) return null; + if ("p" in d) d.H = d.H % 12 + d.p * 12; + var localZ = d.Z != null && d3_date !== d3_date_utc, date = new (localZ ? d3_date_utc : d3_date)(); + if ("j" in d) date.setFullYear(d.y, 0, d.j); else if ("W" in d || "U" in d) { + if (!("w" in d)) d.w = "W" in d ? 1 : 0; + date.setFullYear(d.y, 0, 1); + date.setFullYear(d.y, 0, "W" in d ? (d.w + 6) % 7 + d.W * 7 - (date.getDay() + 5) % 7 : d.w + d.U * 7 - (date.getDay() + 6) % 7); + } else date.setFullYear(d.y, d.m, d.d); + date.setHours(d.H + (d.Z / 100 | 0), d.M + d.Z % 100, d.S, d.L); + return localZ ? date._ : date; + }; + format.toString = function() { + return template; + }; + return format; + } + function d3_time_parse(date, template, string, j) { + var c, p, t, i = 0, n = template.length, m = string.length; + while (i < n) { + if (j >= m) return -1; + c = template.charCodeAt(i++); + if (c === 37) { + t = template.charAt(i++); + p = d3_time_parsers[t in d3_time_formatPads ? template.charAt(i++) : t]; + if (!p || (j = p(date, string, j)) < 0) return -1; + } else if (c != string.charCodeAt(j++)) { + return -1; + } + } + return j; + } + d3_time_format.utc = function(template) { + var local = d3_time_format(template); + function format(date) { + try { + d3_date = d3_date_utc; + var utc = new d3_date(); + utc._ = date; + return local(utc); + } finally { + d3_date = Date; + } + } + format.parse = function(string) { + try { + d3_date = d3_date_utc; + var date = local.parse(string); + return date && date._; + } finally { + d3_date = Date; + } + }; + format.toString = local.toString; + return format; + }; + d3_time_format.multi = d3_time_format.utc.multi = d3_time_formatMulti; + var d3_time_periodLookup = d3.map(), d3_time_dayRe = d3_time_formatRe(locale_days), d3_time_dayLookup = d3_time_formatLookup(locale_days), d3_time_dayAbbrevRe = d3_time_formatRe(locale_shortDays), d3_time_dayAbbrevLookup = d3_time_formatLookup(locale_shortDays), d3_time_monthRe = d3_time_formatRe(locale_months), d3_time_monthLookup = d3_time_formatLookup(locale_months), d3_time_monthAbbrevRe = d3_time_formatRe(locale_shortMonths), d3_time_monthAbbrevLookup = d3_time_formatLookup(locale_shortMonths); + locale_periods.forEach(function(p, i) { + d3_time_periodLookup.set(p.toLowerCase(), i); + }); + var d3_time_formats = { + a: function(d) { + return locale_shortDays[d.getDay()]; + }, + A: function(d) { + return locale_days[d.getDay()]; + }, + b: function(d) { + return locale_shortMonths[d.getMonth()]; + }, + B: function(d) { + return locale_months[d.getMonth()]; + }, + c: d3_time_format(locale_dateTime), + d: function(d, p) { + return d3_time_formatPad(d.getDate(), p, 2); + }, + e: function(d, p) { + return d3_time_formatPad(d.getDate(), p, 2); + }, + H: function(d, p) { + return d3_time_formatPad(d.getHours(), p, 2); + }, + I: function(d, p) { + return d3_time_formatPad(d.getHours() % 12 || 12, p, 2); + }, + j: function(d, p) { + return d3_time_formatPad(1 + d3_time.dayOfYear(d), p, 3); + }, + L: function(d, p) { + return d3_time_formatPad(d.getMilliseconds(), p, 3); + }, + m: function(d, p) { + return d3_time_formatPad(d.getMonth() + 1, p, 2); + }, + M: function(d, p) { + return d3_time_formatPad(d.getMinutes(), p, 2); + }, + p: function(d) { + return locale_periods[+(d.getHours() >= 12)]; + }, + S: function(d, p) { + return d3_time_formatPad(d.getSeconds(), p, 2); + }, + U: function(d, p) { + return d3_time_formatPad(d3_time.sundayOfYear(d), p, 2); + }, + w: function(d) { + return d.getDay(); + }, + W: function(d, p) { + return d3_time_formatPad(d3_time.mondayOfYear(d), p, 2); + }, + x: d3_time_format(locale_date), + X: d3_time_format(locale_time), + y: function(d, p) { + return d3_time_formatPad(d.getFullYear() % 100, p, 2); + }, + Y: function(d, p) { + return d3_time_formatPad(d.getFullYear() % 1e4, p, 4); + }, + Z: d3_time_zone, + "%": function() { + return "%"; + } + }; + var d3_time_parsers = { + a: d3_time_parseWeekdayAbbrev, + A: d3_time_parseWeekday, + b: d3_time_parseMonthAbbrev, + B: d3_time_parseMonth, + c: d3_time_parseLocaleFull, + d: d3_time_parseDay, + e: d3_time_parseDay, + H: d3_time_parseHour24, + I: d3_time_parseHour24, + j: d3_time_parseDayOfYear, + L: d3_time_parseMilliseconds, + m: d3_time_parseMonthNumber, + M: d3_time_parseMinutes, + p: d3_time_parseAmPm, + S: d3_time_parseSeconds, + U: d3_time_parseWeekNumberSunday, + w: d3_time_parseWeekdayNumber, + W: d3_time_parseWeekNumberMonday, + x: d3_time_parseLocaleDate, + X: d3_time_parseLocaleTime, + y: d3_time_parseYear, + Y: d3_time_parseFullYear, + Z: d3_time_parseZone, + "%": d3_time_parseLiteralPercent + }; + function d3_time_parseWeekdayAbbrev(date, string, i) { + d3_time_dayAbbrevRe.lastIndex = 0; + var n = d3_time_dayAbbrevRe.exec(string.slice(i)); + return n ? (date.w = d3_time_dayAbbrevLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; + } + function d3_time_parseWeekday(date, string, i) { + d3_time_dayRe.lastIndex = 0; + var n = d3_time_dayRe.exec(string.slice(i)); + return n ? (date.w = d3_time_dayLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; + } + function d3_time_parseMonthAbbrev(date, string, i) { + d3_time_monthAbbrevRe.lastIndex = 0; + var n = d3_time_monthAbbrevRe.exec(string.slice(i)); + return n ? (date.m = d3_time_monthAbbrevLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; + } + function d3_time_parseMonth(date, string, i) { + d3_time_monthRe.lastIndex = 0; + var n = d3_time_monthRe.exec(string.slice(i)); + return n ? (date.m = d3_time_monthLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; + } + function d3_time_parseLocaleFull(date, string, i) { + return d3_time_parse(date, d3_time_formats.c.toString(), string, i); + } + function d3_time_parseLocaleDate(date, string, i) { + return d3_time_parse(date, d3_time_formats.x.toString(), string, i); + } + function d3_time_parseLocaleTime(date, string, i) { + return d3_time_parse(date, d3_time_formats.X.toString(), string, i); + } + function d3_time_parseAmPm(date, string, i) { + var n = d3_time_periodLookup.get(string.slice(i, i += 2).toLowerCase()); + return n == null ? -1 : (date.p = n, i); + } + return d3_time_format; + } + var d3_time_formatPads = { + "-": "", + _: " ", + "0": "0" + }, d3_time_numberRe = /^\s*\d+/, d3_time_percentRe = /^%/; + function d3_time_formatPad(value, fill, width) { + var sign = value < 0 ? "-" : "", string = (sign ? -value : value) + "", length = string.length; + return sign + (length < width ? new Array(width - length + 1).join(fill) + string : string); + } + function d3_time_formatRe(names) { + return new RegExp("^(?:" + names.map(d3.requote).join("|") + ")", "i"); + } + function d3_time_formatLookup(names) { + var map = new d3_Map(), i = -1, n = names.length; + while (++i < n) map.set(names[i].toLowerCase(), i); + return map; + } + function d3_time_parseWeekdayNumber(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 1)); + return n ? (date.w = +n[0], i + n[0].length) : -1; + } + function d3_time_parseWeekNumberSunday(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i)); + return n ? (date.U = +n[0], i + n[0].length) : -1; + } + function d3_time_parseWeekNumberMonday(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i)); + return n ? (date.W = +n[0], i + n[0].length) : -1; + } + function d3_time_parseFullYear(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 4)); + return n ? (date.y = +n[0], i + n[0].length) : -1; + } + function d3_time_parseYear(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 2)); + return n ? (date.y = d3_time_expandYear(+n[0]), i + n[0].length) : -1; + } + function d3_time_parseZone(date, string, i) { + return /^[+-]\d{4}$/.test(string = string.slice(i, i + 5)) ? (date.Z = -string, + i + 5) : -1; + } + function d3_time_expandYear(d) { + return d + (d > 68 ? 1900 : 2e3); + } + function d3_time_parseMonthNumber(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 2)); + return n ? (date.m = n[0] - 1, i + n[0].length) : -1; + } + function d3_time_parseDay(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 2)); + return n ? (date.d = +n[0], i + n[0].length) : -1; + } + function d3_time_parseDayOfYear(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 3)); + return n ? (date.j = +n[0], i + n[0].length) : -1; + } + function d3_time_parseHour24(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 2)); + return n ? (date.H = +n[0], i + n[0].length) : -1; + } + function d3_time_parseMinutes(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 2)); + return n ? (date.M = +n[0], i + n[0].length) : -1; + } + function d3_time_parseSeconds(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 2)); + return n ? (date.S = +n[0], i + n[0].length) : -1; + } + function d3_time_parseMilliseconds(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 3)); + return n ? (date.L = +n[0], i + n[0].length) : -1; + } + function d3_time_zone(d) { + var z = d.getTimezoneOffset(), zs = z > 0 ? "-" : "+", zh = abs(z) / 60 | 0, zm = abs(z) % 60; + return zs + d3_time_formatPad(zh, "0", 2) + d3_time_formatPad(zm, "0", 2); + } + function d3_time_parseLiteralPercent(date, string, i) { + d3_time_percentRe.lastIndex = 0; + var n = d3_time_percentRe.exec(string.slice(i, i + 1)); + return n ? i + n[0].length : -1; + } + function d3_time_formatMulti(formats) { + var n = formats.length, i = -1; + while (++i < n) formats[i][0] = this(formats[i][0]); + return function(date) { + var i = 0, f = formats[i]; + while (!f[1](date)) f = formats[++i]; + return f[0](date); + }; + } + d3.locale = function(locale) { + return { + numberFormat: d3_locale_numberFormat(locale), + timeFormat: d3_locale_timeFormat(locale) + }; + }; + var d3_locale_enUS = d3.locale({ + decimal: ".", + thousands: ",", + grouping: [ 3 ], + currency: [ "$", "" ], + dateTime: "%a %b %e %X %Y", + date: "%m/%d/%Y", + time: "%H:%M:%S", + periods: [ "AM", "PM" ], + days: [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ], + shortDays: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ], + months: [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ], + shortMonths: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ] + }); + d3.format = d3_locale_enUS.numberFormat; + d3.geo = {}; + function d3_adder() {} + d3_adder.prototype = { + s: 0, + t: 0, + add: function(y) { + d3_adderSum(y, this.t, d3_adderTemp); + d3_adderSum(d3_adderTemp.s, this.s, this); + if (this.s) this.t += d3_adderTemp.t; else this.s = d3_adderTemp.t; + }, + reset: function() { + this.s = this.t = 0; + }, + valueOf: function() { + return this.s; + } + }; + var d3_adderTemp = new d3_adder(); + function d3_adderSum(a, b, o) { + var x = o.s = a + b, bv = x - a, av = x - bv; + o.t = a - av + (b - bv); + } + d3.geo.stream = function(object, listener) { + if (object && d3_geo_streamObjectType.hasOwnProperty(object.type)) { + d3_geo_streamObjectType[object.type](object, listener); + } else { + d3_geo_streamGeometry(object, listener); + } + }; + function d3_geo_streamGeometry(geometry, listener) { + if (geometry && d3_geo_streamGeometryType.hasOwnProperty(geometry.type)) { + d3_geo_streamGeometryType[geometry.type](geometry, listener); + } + } + var d3_geo_streamObjectType = { + Feature: function(feature, listener) { + d3_geo_streamGeometry(feature.geometry, listener); + }, + FeatureCollection: function(object, listener) { + var features = object.features, i = -1, n = features.length; + while (++i < n) d3_geo_streamGeometry(features[i].geometry, listener); + } + }; + var d3_geo_streamGeometryType = { + Sphere: function(object, listener) { + listener.sphere(); + }, + Point: function(object, listener) { + object = object.coordinates; + listener.point(object[0], object[1], object[2]); + }, + MultiPoint: function(object, listener) { + var coordinates = object.coordinates, i = -1, n = coordinates.length; + while (++i < n) object = coordinates[i], listener.point(object[0], object[1], object[2]); + }, + LineString: function(object, listener) { + d3_geo_streamLine(object.coordinates, listener, 0); + }, + MultiLineString: function(object, listener) { + var coordinates = object.coordinates, i = -1, n = coordinates.length; + while (++i < n) d3_geo_streamLine(coordinates[i], listener, 0); + }, + Polygon: function(object, listener) { + d3_geo_streamPolygon(object.coordinates, listener); + }, + MultiPolygon: function(object, listener) { + var coordinates = object.coordinates, i = -1, n = coordinates.length; + while (++i < n) d3_geo_streamPolygon(coordinates[i], listener); + }, + GeometryCollection: function(object, listener) { + var geometries = object.geometries, i = -1, n = geometries.length; + while (++i < n) d3_geo_streamGeometry(geometries[i], listener); + } + }; + function d3_geo_streamLine(coordinates, listener, closed) { + var i = -1, n = coordinates.length - closed, coordinate; + listener.lineStart(); + while (++i < n) coordinate = coordinates[i], listener.point(coordinate[0], coordinate[1], coordinate[2]); + listener.lineEnd(); + } + function d3_geo_streamPolygon(coordinates, listener) { + var i = -1, n = coordinates.length; + listener.polygonStart(); + while (++i < n) d3_geo_streamLine(coordinates[i], listener, 1); + listener.polygonEnd(); + } + d3.geo.area = function(object) { + d3_geo_areaSum = 0; + d3.geo.stream(object, d3_geo_area); + return d3_geo_areaSum; + }; + var d3_geo_areaSum, d3_geo_areaRingSum = new d3_adder(); + var d3_geo_area = { + sphere: function() { + d3_geo_areaSum += 4 * Ï€; + }, + point: d3_noop, + lineStart: d3_noop, + lineEnd: d3_noop, + polygonStart: function() { + d3_geo_areaRingSum.reset(); + d3_geo_area.lineStart = d3_geo_areaRingStart; + }, + polygonEnd: function() { + var area = 2 * d3_geo_areaRingSum; + d3_geo_areaSum += area < 0 ? 4 * Ï€ + area : area; + d3_geo_area.lineStart = d3_geo_area.lineEnd = d3_geo_area.point = d3_noop; + } + }; + function d3_geo_areaRingStart() { + var λ00, φ00, λ0, cosφ0, sinφ0; + d3_geo_area.point = function(λ, φ) { + d3_geo_area.point = nextPoint; + λ0 = (λ00 = λ) * d3_radians, cosφ0 = Math.cos(φ = (φ00 = φ) * d3_radians / 2 + Ï€ / 4), + sinφ0 = Math.sin(φ); + }; + function nextPoint(λ, φ) { + λ *= d3_radians; + φ = φ * d3_radians / 2 + Ï€ / 4; + var dλ = λ - λ0, sdλ = dλ >= 0 ? 1 : -1, adλ = sdλ * dλ, cosφ = Math.cos(φ), sinφ = Math.sin(φ), k = sinφ0 * sinφ, u = cosφ0 * cosφ + k * Math.cos(adλ), v = k * sdλ * Math.sin(adλ); + d3_geo_areaRingSum.add(Math.atan2(v, u)); + λ0 = λ, cosφ0 = cosφ, sinφ0 = sinφ; + } + d3_geo_area.lineEnd = function() { + nextPoint(λ00, φ00); + }; + } + function d3_geo_cartesian(spherical) { + var λ = spherical[0], φ = spherical[1], cosφ = Math.cos(φ); + return [ cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ) ]; + } + function d3_geo_cartesianDot(a, b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; + } + function d3_geo_cartesianCross(a, b) { + return [ a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0] ]; + } + function d3_geo_cartesianAdd(a, b) { + a[0] += b[0]; + a[1] += b[1]; + a[2] += b[2]; + } + function d3_geo_cartesianScale(vector, k) { + return [ vector[0] * k, vector[1] * k, vector[2] * k ]; + } + function d3_geo_cartesianNormalize(d) { + var l = Math.sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]); + d[0] /= l; + d[1] /= l; + d[2] /= l; + } + function d3_geo_spherical(cartesian) { + return [ Math.atan2(cartesian[1], cartesian[0]), d3_asin(cartesian[2]) ]; + } + function d3_geo_sphericalEqual(a, b) { + return abs(a[0] - b[0]) < ε && abs(a[1] - b[1]) < ε; + } + d3.geo.bounds = function() { + var λ0, φ0, λ1, φ1, λ_, λ__, φ__, p0, dλSum, ranges, range; + var bound = { + point: point, + lineStart: lineStart, + lineEnd: lineEnd, + polygonStart: function() { + bound.point = ringPoint; + bound.lineStart = ringStart; + bound.lineEnd = ringEnd; + dλSum = 0; + d3_geo_area.polygonStart(); + }, + polygonEnd: function() { + d3_geo_area.polygonEnd(); + bound.point = point; + bound.lineStart = lineStart; + bound.lineEnd = lineEnd; + if (d3_geo_areaRingSum < 0) λ0 = -(λ1 = 180), φ0 = -(φ1 = 90); else if (dλSum > ε) φ1 = 90; else if (dλSum < -ε) φ0 = -90; + range[0] = λ0, range[1] = λ1; + } + }; + function point(λ, φ) { + ranges.push(range = [ λ0 = λ, λ1 = λ ]); + if (φ < φ0) φ0 = φ; + if (φ > φ1) φ1 = φ; + } + function linePoint(λ, φ) { + var p = d3_geo_cartesian([ λ * d3_radians, φ * d3_radians ]); + if (p0) { + var normal = d3_geo_cartesianCross(p0, p), equatorial = [ normal[1], -normal[0], 0 ], inflection = d3_geo_cartesianCross(equatorial, normal); + d3_geo_cartesianNormalize(inflection); + inflection = d3_geo_spherical(inflection); + var dλ = λ - λ_, s = dλ > 0 ? 1 : -1, λi = inflection[0] * d3_degrees * s, antimeridian = abs(dλ) > 180; + if (antimeridian ^ (s * λ_ < λi && λi < s * λ)) { + var φi = inflection[1] * d3_degrees; + if (φi > φ1) φ1 = φi; + } else if (λi = (λi + 360) % 360 - 180, antimeridian ^ (s * λ_ < λi && λi < s * λ)) { + var φi = -inflection[1] * d3_degrees; + if (φi < φ0) φ0 = φi; + } else { + if (φ < φ0) φ0 = φ; + if (φ > φ1) φ1 = φ; + } + if (antimeridian) { + if (λ < λ_) { + if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ; + } else { + if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ; + } + } else { + if (λ1 >= λ0) { + if (λ < λ0) λ0 = λ; + if (λ > λ1) λ1 = λ; + } else { + if (λ > λ_) { + if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ; + } else { + if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ; + } + } + } + } else { + point(λ, φ); + } + p0 = p, λ_ = λ; + } + function lineStart() { + bound.point = linePoint; + } + function lineEnd() { + range[0] = λ0, range[1] = λ1; + bound.point = point; + p0 = null; + } + function ringPoint(λ, φ) { + if (p0) { + var dλ = λ - λ_; + dλSum += abs(dλ) > 180 ? dλ + (dλ > 0 ? 360 : -360) : dλ; + } else λ__ = λ, φ__ = φ; + d3_geo_area.point(λ, φ); + linePoint(λ, φ); + } + function ringStart() { + d3_geo_area.lineStart(); + } + function ringEnd() { + ringPoint(λ__, φ__); + d3_geo_area.lineEnd(); + if (abs(dλSum) > ε) λ0 = -(λ1 = 180); + range[0] = λ0, range[1] = λ1; + p0 = null; + } + function angle(λ0, λ1) { + return (λ1 -= λ0) < 0 ? λ1 + 360 : λ1; + } + function compareRanges(a, b) { + return a[0] - b[0]; + } + function withinRange(x, range) { + return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x; + } + return function(feature) { + φ1 = λ1 = -(λ0 = φ0 = Infinity); + ranges = []; + d3.geo.stream(feature, bound); + var n = ranges.length; + if (n) { + ranges.sort(compareRanges); + for (var i = 1, a = ranges[0], b, merged = [ a ]; i < n; ++i) { + b = ranges[i]; + if (withinRange(b[0], a) || withinRange(b[1], a)) { + if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1]; + if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0]; + } else { + merged.push(a = b); + } + } + var best = -Infinity, dλ; + for (var n = merged.length - 1, i = 0, a = merged[n], b; i <= n; a = b, ++i) { + b = merged[i]; + if ((dλ = angle(a[1], b[0])) > best) best = dλ, λ0 = b[0], λ1 = a[1]; + } + } + ranges = range = null; + return λ0 === Infinity || φ0 === Infinity ? [ [ NaN, NaN ], [ NaN, NaN ] ] : [ [ λ0, φ0 ], [ λ1, φ1 ] ]; + }; + }(); + d3.geo.centroid = function(object) { + d3_geo_centroidW0 = d3_geo_centroidW1 = d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 = d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 = d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0; + d3.geo.stream(object, d3_geo_centroid); + var x = d3_geo_centroidX2, y = d3_geo_centroidY2, z = d3_geo_centroidZ2, m = x * x + y * y + z * z; + if (m < ε2) { + x = d3_geo_centroidX1, y = d3_geo_centroidY1, z = d3_geo_centroidZ1; + if (d3_geo_centroidW1 < ε) x = d3_geo_centroidX0, y = d3_geo_centroidY0, z = d3_geo_centroidZ0; + m = x * x + y * y + z * z; + if (m < ε2) return [ NaN, NaN ]; + } + return [ Math.atan2(y, x) * d3_degrees, d3_asin(z / Math.sqrt(m)) * d3_degrees ]; + }; + var d3_geo_centroidW0, d3_geo_centroidW1, d3_geo_centroidX0, d3_geo_centroidY0, d3_geo_centroidZ0, d3_geo_centroidX1, d3_geo_centroidY1, d3_geo_centroidZ1, d3_geo_centroidX2, d3_geo_centroidY2, d3_geo_centroidZ2; + var d3_geo_centroid = { + sphere: d3_noop, + point: d3_geo_centroidPoint, + lineStart: d3_geo_centroidLineStart, + lineEnd: d3_geo_centroidLineEnd, + polygonStart: function() { + d3_geo_centroid.lineStart = d3_geo_centroidRingStart; + }, + polygonEnd: function() { + d3_geo_centroid.lineStart = d3_geo_centroidLineStart; + } + }; + function d3_geo_centroidPoint(λ, φ) { + λ *= d3_radians; + var cosφ = Math.cos(φ *= d3_radians); + d3_geo_centroidPointXYZ(cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ)); + } + function d3_geo_centroidPointXYZ(x, y, z) { + ++d3_geo_centroidW0; + d3_geo_centroidX0 += (x - d3_geo_centroidX0) / d3_geo_centroidW0; + d3_geo_centroidY0 += (y - d3_geo_centroidY0) / d3_geo_centroidW0; + d3_geo_centroidZ0 += (z - d3_geo_centroidZ0) / d3_geo_centroidW0; + } + function d3_geo_centroidLineStart() { + var x0, y0, z0; + d3_geo_centroid.point = function(λ, φ) { + λ *= d3_radians; + var cosφ = Math.cos(φ *= d3_radians); + x0 = cosφ * Math.cos(λ); + y0 = cosφ * Math.sin(λ); + z0 = Math.sin(φ); + d3_geo_centroid.point = nextPoint; + d3_geo_centroidPointXYZ(x0, y0, z0); + }; + function nextPoint(λ, φ) { + λ *= d3_radians; + var cosφ = Math.cos(φ *= d3_radians), x = cosφ * Math.cos(λ), y = cosφ * Math.sin(λ), z = Math.sin(φ), w = Math.atan2(Math.sqrt((w = y0 * z - z0 * y) * w + (w = z0 * x - x0 * z) * w + (w = x0 * y - y0 * x) * w), x0 * x + y0 * y + z0 * z); + d3_geo_centroidW1 += w; + d3_geo_centroidX1 += w * (x0 + (x0 = x)); + d3_geo_centroidY1 += w * (y0 + (y0 = y)); + d3_geo_centroidZ1 += w * (z0 + (z0 = z)); + d3_geo_centroidPointXYZ(x0, y0, z0); + } + } + function d3_geo_centroidLineEnd() { + d3_geo_centroid.point = d3_geo_centroidPoint; + } + function d3_geo_centroidRingStart() { + var λ00, φ00, x0, y0, z0; + d3_geo_centroid.point = function(λ, φ) { + λ00 = λ, φ00 = φ; + d3_geo_centroid.point = nextPoint; + λ *= d3_radians; + var cosφ = Math.cos(φ *= d3_radians); + x0 = cosφ * Math.cos(λ); + y0 = cosφ * Math.sin(λ); + z0 = Math.sin(φ); + d3_geo_centroidPointXYZ(x0, y0, z0); + }; + d3_geo_centroid.lineEnd = function() { + nextPoint(λ00, φ00); + d3_geo_centroid.lineEnd = d3_geo_centroidLineEnd; + d3_geo_centroid.point = d3_geo_centroidPoint; + }; + function nextPoint(λ, φ) { + λ *= d3_radians; + var cosφ = Math.cos(φ *= d3_radians), x = cosφ * Math.cos(λ), y = cosφ * Math.sin(λ), z = Math.sin(φ), cx = y0 * z - z0 * y, cy = z0 * x - x0 * z, cz = x0 * y - y0 * x, m = Math.sqrt(cx * cx + cy * cy + cz * cz), u = x0 * x + y0 * y + z0 * z, v = m && -d3_acos(u) / m, w = Math.atan2(m, u); + d3_geo_centroidX2 += v * cx; + d3_geo_centroidY2 += v * cy; + d3_geo_centroidZ2 += v * cz; + d3_geo_centroidW1 += w; + d3_geo_centroidX1 += w * (x0 + (x0 = x)); + d3_geo_centroidY1 += w * (y0 + (y0 = y)); + d3_geo_centroidZ1 += w * (z0 + (z0 = z)); + d3_geo_centroidPointXYZ(x0, y0, z0); + } + } + function d3_geo_compose(a, b) { + function compose(x, y) { + return x = a(x, y), b(x[0], x[1]); + } + if (a.invert && b.invert) compose.invert = function(x, y) { + return x = b.invert(x, y), x && a.invert(x[0], x[1]); + }; + return compose; + } + function d3_true() { + return true; + } + function d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener) { + var subject = [], clip = []; + segments.forEach(function(segment) { + if ((n = segment.length - 1) <= 0) return; + var n, p0 = segment[0], p1 = segment[n]; + if (d3_geo_sphericalEqual(p0, p1)) { + listener.lineStart(); + for (var i = 0; i < n; ++i) listener.point((p0 = segment[i])[0], p0[1]); + listener.lineEnd(); + return; + } + var a = new d3_geo_clipPolygonIntersection(p0, segment, null, true), b = new d3_geo_clipPolygonIntersection(p0, null, a, false); + a.o = b; + subject.push(a); + clip.push(b); + a = new d3_geo_clipPolygonIntersection(p1, segment, null, false); + b = new d3_geo_clipPolygonIntersection(p1, null, a, true); + a.o = b; + subject.push(a); + clip.push(b); + }); + clip.sort(compare); + d3_geo_clipPolygonLinkCircular(subject); + d3_geo_clipPolygonLinkCircular(clip); + if (!subject.length) return; + for (var i = 0, entry = clipStartInside, n = clip.length; i < n; ++i) { + clip[i].e = entry = !entry; + } + var start = subject[0], points, point; + while (1) { + var current = start, isSubject = true; + while (current.v) if ((current = current.n) === start) return; + points = current.z; + listener.lineStart(); + do { + current.v = current.o.v = true; + if (current.e) { + if (isSubject) { + for (var i = 0, n = points.length; i < n; ++i) listener.point((point = points[i])[0], point[1]); + } else { + interpolate(current.x, current.n.x, 1, listener); + } + current = current.n; + } else { + if (isSubject) { + points = current.p.z; + for (var i = points.length - 1; i >= 0; --i) listener.point((point = points[i])[0], point[1]); + } else { + interpolate(current.x, current.p.x, -1, listener); + } + current = current.p; + } + current = current.o; + points = current.z; + isSubject = !isSubject; + } while (!current.v); + listener.lineEnd(); + } + } + function d3_geo_clipPolygonLinkCircular(array) { + if (!(n = array.length)) return; + var n, i = 0, a = array[0], b; + while (++i < n) { + a.n = b = array[i]; + b.p = a; + a = b; + } + a.n = b = array[0]; + b.p = a; + } + function d3_geo_clipPolygonIntersection(point, points, other, entry) { + this.x = point; + this.z = points; + this.o = other; + this.e = entry; + this.v = false; + this.n = this.p = null; + } + function d3_geo_clip(pointVisible, clipLine, interpolate, clipStart) { + return function(rotate, listener) { + var line = clipLine(listener), rotatedClipStart = rotate.invert(clipStart[0], clipStart[1]); + var clip = { + point: point, + lineStart: lineStart, + lineEnd: lineEnd, + polygonStart: function() { + clip.point = pointRing; + clip.lineStart = ringStart; + clip.lineEnd = ringEnd; + segments = []; + polygon = []; + }, + polygonEnd: function() { + clip.point = point; + clip.lineStart = lineStart; + clip.lineEnd = lineEnd; + segments = d3.merge(segments); + var clipStartInside = d3_geo_pointInPolygon(rotatedClipStart, polygon); + if (segments.length) { + if (!polygonStarted) listener.polygonStart(), polygonStarted = true; + d3_geo_clipPolygon(segments, d3_geo_clipSort, clipStartInside, interpolate, listener); + } else if (clipStartInside) { + if (!polygonStarted) listener.polygonStart(), polygonStarted = true; + listener.lineStart(); + interpolate(null, null, 1, listener); + listener.lineEnd(); + } + if (polygonStarted) listener.polygonEnd(), polygonStarted = false; + segments = polygon = null; + }, + sphere: function() { + listener.polygonStart(); + listener.lineStart(); + interpolate(null, null, 1, listener); + listener.lineEnd(); + listener.polygonEnd(); + } + }; + function point(λ, φ) { + var point = rotate(λ, φ); + if (pointVisible(λ = point[0], φ = point[1])) listener.point(λ, φ); + } + function pointLine(λ, φ) { + var point = rotate(λ, φ); + line.point(point[0], point[1]); + } + function lineStart() { + clip.point = pointLine; + line.lineStart(); + } + function lineEnd() { + clip.point = point; + line.lineEnd(); + } + var segments; + var buffer = d3_geo_clipBufferListener(), ringListener = clipLine(buffer), polygonStarted = false, polygon, ring; + function pointRing(λ, φ) { + ring.push([ λ, φ ]); + var point = rotate(λ, φ); + ringListener.point(point[0], point[1]); + } + function ringStart() { + ringListener.lineStart(); + ring = []; + } + function ringEnd() { + pointRing(ring[0][0], ring[0][1]); + ringListener.lineEnd(); + var clean = ringListener.clean(), ringSegments = buffer.buffer(), segment, n = ringSegments.length; + ring.pop(); + polygon.push(ring); + ring = null; + if (!n) return; + if (clean & 1) { + segment = ringSegments[0]; + var n = segment.length - 1, i = -1, point; + if (n > 0) { + if (!polygonStarted) listener.polygonStart(), polygonStarted = true; + listener.lineStart(); + while (++i < n) listener.point((point = segment[i])[0], point[1]); + listener.lineEnd(); + } + return; + } + if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift())); + segments.push(ringSegments.filter(d3_geo_clipSegmentLength1)); + } + return clip; + }; + } + function d3_geo_clipSegmentLength1(segment) { + return segment.length > 1; + } + function d3_geo_clipBufferListener() { + var lines = [], line; + return { + lineStart: function() { + lines.push(line = []); + }, + point: function(λ, φ) { + line.push([ λ, φ ]); + }, + lineEnd: d3_noop, + buffer: function() { + var buffer = lines; + lines = []; + line = null; + return buffer; + }, + rejoin: function() { + if (lines.length > 1) lines.push(lines.pop().concat(lines.shift())); + } + }; + } + function d3_geo_clipSort(a, b) { + return ((a = a.x)[0] < 0 ? a[1] - halfÏ€ - ε : halfÏ€ - a[1]) - ((b = b.x)[0] < 0 ? b[1] - halfÏ€ - ε : halfÏ€ - b[1]); + } + var d3_geo_clipAntimeridian = d3_geo_clip(d3_true, d3_geo_clipAntimeridianLine, d3_geo_clipAntimeridianInterpolate, [ -Ï€, -Ï€ / 2 ]); + function d3_geo_clipAntimeridianLine(listener) { + var λ0 = NaN, φ0 = NaN, sλ0 = NaN, clean; + return { + lineStart: function() { + listener.lineStart(); + clean = 1; + }, + point: function(λ1, φ1) { + var sλ1 = λ1 > 0 ? Ï€ : -Ï€, dλ = abs(λ1 - λ0); + if (abs(dλ - Ï€) < ε) { + listener.point(λ0, φ0 = (φ0 + φ1) / 2 > 0 ? halfÏ€ : -halfÏ€); + listener.point(sλ0, φ0); + listener.lineEnd(); + listener.lineStart(); + listener.point(sλ1, φ0); + listener.point(λ1, φ0); + clean = 0; + } else if (sλ0 !== sλ1 && dλ >= Ï€) { + if (abs(λ0 - sλ0) < ε) λ0 -= sλ0 * ε; + if (abs(λ1 - sλ1) < ε) λ1 -= sλ1 * ε; + φ0 = d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1); + listener.point(sλ0, φ0); + listener.lineEnd(); + listener.lineStart(); + listener.point(sλ1, φ0); + clean = 0; + } + listener.point(λ0 = λ1, φ0 = φ1); + sλ0 = sλ1; + }, + lineEnd: function() { + listener.lineEnd(); + λ0 = φ0 = NaN; + }, + clean: function() { + return 2 - clean; + } + }; + } + function d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1) { + var cosφ0, cosφ1, sinλ0_λ1 = Math.sin(λ0 - λ1); + return abs(sinλ0_λ1) > ε ? Math.atan((Math.sin(φ0) * (cosφ1 = Math.cos(φ1)) * Math.sin(λ1) - Math.sin(φ1) * (cosφ0 = Math.cos(φ0)) * Math.sin(λ0)) / (cosφ0 * cosφ1 * sinλ0_λ1)) : (φ0 + φ1) / 2; + } + function d3_geo_clipAntimeridianInterpolate(from, to, direction, listener) { + var φ; + if (from == null) { + φ = direction * halfÏ€; + listener.point(-Ï€, φ); + listener.point(0, φ); + listener.point(Ï€, φ); + listener.point(Ï€, 0); + listener.point(Ï€, -φ); + listener.point(0, -φ); + listener.point(-Ï€, -φ); + listener.point(-Ï€, 0); + listener.point(-Ï€, φ); + } else if (abs(from[0] - to[0]) > ε) { + var s = from[0] < to[0] ? Ï€ : -Ï€; + φ = direction * s / 2; + listener.point(-s, φ); + listener.point(0, φ); + listener.point(s, φ); + } else { + listener.point(to[0], to[1]); + } + } + function d3_geo_pointInPolygon(point, polygon) { + var meridian = point[0], parallel = point[1], meridianNormal = [ Math.sin(meridian), -Math.cos(meridian), 0 ], polarAngle = 0, winding = 0; + d3_geo_areaRingSum.reset(); + for (var i = 0, n = polygon.length; i < n; ++i) { + var ring = polygon[i], m = ring.length; + if (!m) continue; + var point0 = ring[0], λ0 = point0[0], φ0 = point0[1] / 2 + Ï€ / 4, sinφ0 = Math.sin(φ0), cosφ0 = Math.cos(φ0), j = 1; + while (true) { + if (j === m) j = 0; + point = ring[j]; + var λ = point[0], φ = point[1] / 2 + Ï€ / 4, sinφ = Math.sin(φ), cosφ = Math.cos(φ), dλ = λ - λ0, sdλ = dλ >= 0 ? 1 : -1, adλ = sdλ * dλ, antimeridian = adλ > Ï€, k = sinφ0 * sinφ; + d3_geo_areaRingSum.add(Math.atan2(k * sdλ * Math.sin(adλ), cosφ0 * cosφ + k * Math.cos(adλ))); + polarAngle += antimeridian ? dλ + sdλ * Ï„ : dλ; + if (antimeridian ^ λ0 >= meridian ^ λ >= meridian) { + var arc = d3_geo_cartesianCross(d3_geo_cartesian(point0), d3_geo_cartesian(point)); + d3_geo_cartesianNormalize(arc); + var intersection = d3_geo_cartesianCross(meridianNormal, arc); + d3_geo_cartesianNormalize(intersection); + var φarc = (antimeridian ^ dλ >= 0 ? -1 : 1) * d3_asin(intersection[2]); + if (parallel > φarc || parallel === φarc && (arc[0] || arc[1])) { + winding += antimeridian ^ dλ >= 0 ? 1 : -1; + } + } + if (!j++) break; + λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ, point0 = point; + } + } + return (polarAngle < -ε || polarAngle < ε && d3_geo_areaRingSum < -ε) ^ winding & 1; + } + function d3_geo_clipCircle(radius) { + var cr = Math.cos(radius), smallRadius = cr > 0, notHemisphere = abs(cr) > ε, interpolate = d3_geo_circleInterpolate(radius, 6 * d3_radians); + return d3_geo_clip(visible, clipLine, interpolate, smallRadius ? [ 0, -radius ] : [ -Ï€, radius - Ï€ ]); + function visible(λ, φ) { + return Math.cos(λ) * Math.cos(φ) > cr; + } + function clipLine(listener) { + var point0, c0, v0, v00, clean; + return { + lineStart: function() { + v00 = v0 = false; + clean = 1; + }, + point: function(λ, φ) { + var point1 = [ λ, φ ], point2, v = visible(λ, φ), c = smallRadius ? v ? 0 : code(λ, φ) : v ? code(λ + (λ < 0 ? Ï€ : -Ï€), φ) : 0; + if (!point0 && (v00 = v0 = v)) listener.lineStart(); + if (v !== v0) { + point2 = intersect(point0, point1); + if (d3_geo_sphericalEqual(point0, point2) || d3_geo_sphericalEqual(point1, point2)) { + point1[0] += ε; + point1[1] += ε; + v = visible(point1[0], point1[1]); + } + } + if (v !== v0) { + clean = 0; + if (v) { + listener.lineStart(); + point2 = intersect(point1, point0); + listener.point(point2[0], point2[1]); + } else { + point2 = intersect(point0, point1); + listener.point(point2[0], point2[1]); + listener.lineEnd(); + } + point0 = point2; + } else if (notHemisphere && point0 && smallRadius ^ v) { + var t; + if (!(c & c0) && (t = intersect(point1, point0, true))) { + clean = 0; + if (smallRadius) { + listener.lineStart(); + listener.point(t[0][0], t[0][1]); + listener.point(t[1][0], t[1][1]); + listener.lineEnd(); + } else { + listener.point(t[1][0], t[1][1]); + listener.lineEnd(); + listener.lineStart(); + listener.point(t[0][0], t[0][1]); + } + } + } + if (v && (!point0 || !d3_geo_sphericalEqual(point0, point1))) { + listener.point(point1[0], point1[1]); + } + point0 = point1, v0 = v, c0 = c; + }, + lineEnd: function() { + if (v0) listener.lineEnd(); + point0 = null; + }, + clean: function() { + return clean | (v00 && v0) << 1; + } + }; + } + function intersect(a, b, two) { + var pa = d3_geo_cartesian(a), pb = d3_geo_cartesian(b); + var n1 = [ 1, 0, 0 ], n2 = d3_geo_cartesianCross(pa, pb), n2n2 = d3_geo_cartesianDot(n2, n2), n1n2 = n2[0], determinant = n2n2 - n1n2 * n1n2; + if (!determinant) return !two && a; + var c1 = cr * n2n2 / determinant, c2 = -cr * n1n2 / determinant, n1xn2 = d3_geo_cartesianCross(n1, n2), A = d3_geo_cartesianScale(n1, c1), B = d3_geo_cartesianScale(n2, c2); + d3_geo_cartesianAdd(A, B); + var u = n1xn2, w = d3_geo_cartesianDot(A, u), uu = d3_geo_cartesianDot(u, u), t2 = w * w - uu * (d3_geo_cartesianDot(A, A) - 1); + if (t2 < 0) return; + var t = Math.sqrt(t2), q = d3_geo_cartesianScale(u, (-w - t) / uu); + d3_geo_cartesianAdd(q, A); + q = d3_geo_spherical(q); + if (!two) return q; + var λ0 = a[0], λ1 = b[0], φ0 = a[1], φ1 = b[1], z; + if (λ1 < λ0) z = λ0, λ0 = λ1, λ1 = z; + var δλ = λ1 - λ0, polar = abs(δλ - Ï€) < ε, meridian = polar || δλ < ε; + if (!polar && φ1 < φ0) z = φ0, φ0 = φ1, φ1 = z; + if (meridian ? polar ? φ0 + φ1 > 0 ^ q[1] < (abs(q[0] - λ0) < ε ? φ0 : φ1) : φ0 <= q[1] && q[1] <= φ1 : δλ > Ï€ ^ (λ0 <= q[0] && q[0] <= λ1)) { + var q1 = d3_geo_cartesianScale(u, (-w + t) / uu); + d3_geo_cartesianAdd(q1, A); + return [ q, d3_geo_spherical(q1) ]; + } + } + function code(λ, φ) { + var r = smallRadius ? radius : Ï€ - radius, code = 0; + if (λ < -r) code |= 1; else if (λ > r) code |= 2; + if (φ < -r) code |= 4; else if (φ > r) code |= 8; + return code; + } + } + function d3_geom_clipLine(x0, y0, x1, y1) { + return function(line) { + var a = line.a, b = line.b, ax = a.x, ay = a.y, bx = b.x, by = b.y, t0 = 0, t1 = 1, dx = bx - ax, dy = by - ay, r; + r = x0 - ax; + if (!dx && r > 0) return; + r /= dx; + if (dx < 0) { + if (r < t0) return; + if (r < t1) t1 = r; + } else if (dx > 0) { + if (r > t1) return; + if (r > t0) t0 = r; + } + r = x1 - ax; + if (!dx && r < 0) return; + r /= dx; + if (dx < 0) { + if (r > t1) return; + if (r > t0) t0 = r; + } else if (dx > 0) { + if (r < t0) return; + if (r < t1) t1 = r; + } + r = y0 - ay; + if (!dy && r > 0) return; + r /= dy; + if (dy < 0) { + if (r < t0) return; + if (r < t1) t1 = r; + } else if (dy > 0) { + if (r > t1) return; + if (r > t0) t0 = r; + } + r = y1 - ay; + if (!dy && r < 0) return; + r /= dy; + if (dy < 0) { + if (r > t1) return; + if (r > t0) t0 = r; + } else if (dy > 0) { + if (r < t0) return; + if (r < t1) t1 = r; + } + if (t0 > 0) line.a = { + x: ax + t0 * dx, + y: ay + t0 * dy + }; + if (t1 < 1) line.b = { + x: ax + t1 * dx, + y: ay + t1 * dy + }; + return line; + }; + } + var d3_geo_clipExtentMAX = 1e9; + d3.geo.clipExtent = function() { + var x0, y0, x1, y1, stream, clip, clipExtent = { + stream: function(output) { + if (stream) stream.valid = false; + stream = clip(output); + stream.valid = true; + return stream; + }, + extent: function(_) { + if (!arguments.length) return [ [ x0, y0 ], [ x1, y1 ] ]; + clip = d3_geo_clipExtent(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]); + if (stream) stream.valid = false, stream = null; + return clipExtent; + } + }; + return clipExtent.extent([ [ 0, 0 ], [ 960, 500 ] ]); + }; + function d3_geo_clipExtent(x0, y0, x1, y1) { + return function(listener) { + var listener_ = listener, bufferListener = d3_geo_clipBufferListener(), clipLine = d3_geom_clipLine(x0, y0, x1, y1), segments, polygon, ring; + var clip = { + point: point, + lineStart: lineStart, + lineEnd: lineEnd, + polygonStart: function() { + listener = bufferListener; + segments = []; + polygon = []; + clean = true; + }, + polygonEnd: function() { + listener = listener_; + segments = d3.merge(segments); + var clipStartInside = insidePolygon([ x0, y1 ]), inside = clean && clipStartInside, visible = segments.length; + if (inside || visible) { + listener.polygonStart(); + if (inside) { + listener.lineStart(); + interpolate(null, null, 1, listener); + listener.lineEnd(); + } + if (visible) { + d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener); + } + listener.polygonEnd(); + } + segments = polygon = ring = null; + } + }; + function insidePolygon(p) { + var wn = 0, n = polygon.length, y = p[1]; + for (var i = 0; i < n; ++i) { + for (var j = 1, v = polygon[i], m = v.length, a = v[0], b; j < m; ++j) { + b = v[j]; + if (a[1] <= y) { + if (b[1] > y && d3_cross2d(a, b, p) > 0) ++wn; + } else { + if (b[1] <= y && d3_cross2d(a, b, p) < 0) --wn; + } + a = b; + } + } + return wn !== 0; + } + function interpolate(from, to, direction, listener) { + var a = 0, a1 = 0; + if (from == null || (a = corner(from, direction)) !== (a1 = corner(to, direction)) || comparePoints(from, to) < 0 ^ direction > 0) { + do { + listener.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0); + } while ((a = (a + direction + 4) % 4) !== a1); + } else { + listener.point(to[0], to[1]); + } + } + function pointVisible(x, y) { + return x0 <= x && x <= x1 && y0 <= y && y <= y1; + } + function point(x, y) { + if (pointVisible(x, y)) listener.point(x, y); + } + var x__, y__, v__, x_, y_, v_, first, clean; + function lineStart() { + clip.point = linePoint; + if (polygon) polygon.push(ring = []); + first = true; + v_ = false; + x_ = y_ = NaN; + } + function lineEnd() { + if (segments) { + linePoint(x__, y__); + if (v__ && v_) bufferListener.rejoin(); + segments.push(bufferListener.buffer()); + } + clip.point = point; + if (v_) listener.lineEnd(); + } + function linePoint(x, y) { + x = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, x)); + y = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, y)); + var v = pointVisible(x, y); + if (polygon) ring.push([ x, y ]); + if (first) { + x__ = x, y__ = y, v__ = v; + first = false; + if (v) { + listener.lineStart(); + listener.point(x, y); + } + } else { + if (v && v_) listener.point(x, y); else { + var l = { + a: { + x: x_, + y: y_ + }, + b: { + x: x, + y: y + } + }; + if (clipLine(l)) { + if (!v_) { + listener.lineStart(); + listener.point(l.a.x, l.a.y); + } + listener.point(l.b.x, l.b.y); + if (!v) listener.lineEnd(); + clean = false; + } else if (v) { + listener.lineStart(); + listener.point(x, y); + clean = false; + } + } + } + x_ = x, y_ = y, v_ = v; + } + return clip; + }; + function corner(p, direction) { + return abs(p[0] - x0) < ε ? direction > 0 ? 0 : 3 : abs(p[0] - x1) < ε ? direction > 0 ? 2 : 1 : abs(p[1] - y0) < ε ? direction > 0 ? 1 : 0 : direction > 0 ? 3 : 2; + } + function compare(a, b) { + return comparePoints(a.x, b.x); + } + function comparePoints(a, b) { + var ca = corner(a, 1), cb = corner(b, 1); + return ca !== cb ? ca - cb : ca === 0 ? b[1] - a[1] : ca === 1 ? a[0] - b[0] : ca === 2 ? a[1] - b[1] : b[0] - a[0]; + } + } + function d3_geo_conic(projectAt) { + var φ0 = 0, φ1 = Ï€ / 3, m = d3_geo_projectionMutator(projectAt), p = m(φ0, φ1); + p.parallels = function(_) { + if (!arguments.length) return [ φ0 / Ï€ * 180, φ1 / Ï€ * 180 ]; + return m(φ0 = _[0] * Ï€ / 180, φ1 = _[1] * Ï€ / 180); + }; + return p; + } + function d3_geo_conicEqualArea(φ0, φ1) { + var sinφ0 = Math.sin(φ0), n = (sinφ0 + Math.sin(φ1)) / 2, C = 1 + sinφ0 * (2 * n - sinφ0), Ï0 = Math.sqrt(C) / n; + function forward(λ, φ) { + var Ï = Math.sqrt(C - 2 * n * Math.sin(φ)) / n; + return [ Ï * Math.sin(λ *= n), Ï0 - Ï * Math.cos(λ) ]; + } + forward.invert = function(x, y) { + var Ï0_y = Ï0 - y; + return [ Math.atan2(x, Ï0_y) / n, d3_asin((C - (x * x + Ï0_y * Ï0_y) * n * n) / (2 * n)) ]; + }; + return forward; + } + (d3.geo.conicEqualArea = function() { + return d3_geo_conic(d3_geo_conicEqualArea); + }).raw = d3_geo_conicEqualArea; + d3.geo.albers = function() { + return d3.geo.conicEqualArea().rotate([ 96, 0 ]).center([ -.6, 38.7 ]).parallels([ 29.5, 45.5 ]).scale(1070); + }; + d3.geo.albersUsa = function() { + var lower48 = d3.geo.albers(); + var alaska = d3.geo.conicEqualArea().rotate([ 154, 0 ]).center([ -2, 58.5 ]).parallels([ 55, 65 ]); + var hawaii = d3.geo.conicEqualArea().rotate([ 157, 0 ]).center([ -3, 19.9 ]).parallels([ 8, 18 ]); + var point, pointStream = { + point: function(x, y) { + point = [ x, y ]; + } + }, lower48Point, alaskaPoint, hawaiiPoint; + function albersUsa(coordinates) { + var x = coordinates[0], y = coordinates[1]; + point = null; + (lower48Point(x, y), point) || (alaskaPoint(x, y), point) || hawaiiPoint(x, y); + return point; + } + albersUsa.invert = function(coordinates) { + var k = lower48.scale(), t = lower48.translate(), x = (coordinates[0] - t[0]) / k, y = (coordinates[1] - t[1]) / k; + return (y >= .12 && y < .234 && x >= -.425 && x < -.214 ? alaska : y >= .166 && y < .234 && x >= -.214 && x < -.115 ? hawaii : lower48).invert(coordinates); + }; + albersUsa.stream = function(stream) { + var lower48Stream = lower48.stream(stream), alaskaStream = alaska.stream(stream), hawaiiStream = hawaii.stream(stream); + return { + point: function(x, y) { + lower48Stream.point(x, y); + alaskaStream.point(x, y); + hawaiiStream.point(x, y); + }, + sphere: function() { + lower48Stream.sphere(); + alaskaStream.sphere(); + hawaiiStream.sphere(); + }, + lineStart: function() { + lower48Stream.lineStart(); + alaskaStream.lineStart(); + hawaiiStream.lineStart(); + }, + lineEnd: function() { + lower48Stream.lineEnd(); + alaskaStream.lineEnd(); + hawaiiStream.lineEnd(); + }, + polygonStart: function() { + lower48Stream.polygonStart(); + alaskaStream.polygonStart(); + hawaiiStream.polygonStart(); + }, + polygonEnd: function() { + lower48Stream.polygonEnd(); + alaskaStream.polygonEnd(); + hawaiiStream.polygonEnd(); + } + }; + }; + albersUsa.precision = function(_) { + if (!arguments.length) return lower48.precision(); + lower48.precision(_); + alaska.precision(_); + hawaii.precision(_); + return albersUsa; + }; + albersUsa.scale = function(_) { + if (!arguments.length) return lower48.scale(); + lower48.scale(_); + alaska.scale(_ * .35); + hawaii.scale(_); + return albersUsa.translate(lower48.translate()); + }; + albersUsa.translate = function(_) { + if (!arguments.length) return lower48.translate(); + var k = lower48.scale(), x = +_[0], y = +_[1]; + lower48Point = lower48.translate(_).clipExtent([ [ x - .455 * k, y - .238 * k ], [ x + .455 * k, y + .238 * k ] ]).stream(pointStream).point; + alaskaPoint = alaska.translate([ x - .307 * k, y + .201 * k ]).clipExtent([ [ x - .425 * k + ε, y + .12 * k + ε ], [ x - .214 * k - ε, y + .234 * k - ε ] ]).stream(pointStream).point; + hawaiiPoint = hawaii.translate([ x - .205 * k, y + .212 * k ]).clipExtent([ [ x - .214 * k + ε, y + .166 * k + ε ], [ x - .115 * k - ε, y + .234 * k - ε ] ]).stream(pointStream).point; + return albersUsa; + }; + return albersUsa.scale(1070); + }; + var d3_geo_pathAreaSum, d3_geo_pathAreaPolygon, d3_geo_pathArea = { + point: d3_noop, + lineStart: d3_noop, + lineEnd: d3_noop, + polygonStart: function() { + d3_geo_pathAreaPolygon = 0; + d3_geo_pathArea.lineStart = d3_geo_pathAreaRingStart; + }, + polygonEnd: function() { + d3_geo_pathArea.lineStart = d3_geo_pathArea.lineEnd = d3_geo_pathArea.point = d3_noop; + d3_geo_pathAreaSum += abs(d3_geo_pathAreaPolygon / 2); + } + }; + function d3_geo_pathAreaRingStart() { + var x00, y00, x0, y0; + d3_geo_pathArea.point = function(x, y) { + d3_geo_pathArea.point = nextPoint; + x00 = x0 = x, y00 = y0 = y; + }; + function nextPoint(x, y) { + d3_geo_pathAreaPolygon += y0 * x - x0 * y; + x0 = x, y0 = y; + } + d3_geo_pathArea.lineEnd = function() { + nextPoint(x00, y00); + }; + } + var d3_geo_pathBoundsX0, d3_geo_pathBoundsY0, d3_geo_pathBoundsX1, d3_geo_pathBoundsY1; + var d3_geo_pathBounds = { + point: d3_geo_pathBoundsPoint, + lineStart: d3_noop, + lineEnd: d3_noop, + polygonStart: d3_noop, + polygonEnd: d3_noop + }; + function d3_geo_pathBoundsPoint(x, y) { + if (x < d3_geo_pathBoundsX0) d3_geo_pathBoundsX0 = x; + if (x > d3_geo_pathBoundsX1) d3_geo_pathBoundsX1 = x; + if (y < d3_geo_pathBoundsY0) d3_geo_pathBoundsY0 = y; + if (y > d3_geo_pathBoundsY1) d3_geo_pathBoundsY1 = y; + } + function d3_geo_pathBuffer() { + var pointCircle = d3_geo_pathBufferCircle(4.5), buffer = []; + var stream = { + point: point, + lineStart: function() { + stream.point = pointLineStart; + }, + lineEnd: lineEnd, + polygonStart: function() { + stream.lineEnd = lineEndPolygon; + }, + polygonEnd: function() { + stream.lineEnd = lineEnd; + stream.point = point; + }, + pointRadius: function(_) { + pointCircle = d3_geo_pathBufferCircle(_); + return stream; + }, + result: function() { + if (buffer.length) { + var result = buffer.join(""); + buffer = []; + return result; + } + } + }; + function point(x, y) { + buffer.push("M", x, ",", y, pointCircle); + } + function pointLineStart(x, y) { + buffer.push("M", x, ",", y); + stream.point = pointLine; + } + function pointLine(x, y) { + buffer.push("L", x, ",", y); + } + function lineEnd() { + stream.point = point; + } + function lineEndPolygon() { + buffer.push("Z"); + } + return stream; + } + function d3_geo_pathBufferCircle(radius) { + return "m0," + radius + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius + "a" + radius + "," + radius + " 0 1,1 0," + 2 * radius + "z"; + } + var d3_geo_pathCentroid = { + point: d3_geo_pathCentroidPoint, + lineStart: d3_geo_pathCentroidLineStart, + lineEnd: d3_geo_pathCentroidLineEnd, + polygonStart: function() { + d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidRingStart; + }, + polygonEnd: function() { + d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint; + d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidLineStart; + d3_geo_pathCentroid.lineEnd = d3_geo_pathCentroidLineEnd; + } + }; + function d3_geo_pathCentroidPoint(x, y) { + d3_geo_centroidX0 += x; + d3_geo_centroidY0 += y; + ++d3_geo_centroidZ0; + } + function d3_geo_pathCentroidLineStart() { + var x0, y0; + d3_geo_pathCentroid.point = function(x, y) { + d3_geo_pathCentroid.point = nextPoint; + d3_geo_pathCentroidPoint(x0 = x, y0 = y); + }; + function nextPoint(x, y) { + var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy); + d3_geo_centroidX1 += z * (x0 + x) / 2; + d3_geo_centroidY1 += z * (y0 + y) / 2; + d3_geo_centroidZ1 += z; + d3_geo_pathCentroidPoint(x0 = x, y0 = y); + } + } + function d3_geo_pathCentroidLineEnd() { + d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint; + } + function d3_geo_pathCentroidRingStart() { + var x00, y00, x0, y0; + d3_geo_pathCentroid.point = function(x, y) { + d3_geo_pathCentroid.point = nextPoint; + d3_geo_pathCentroidPoint(x00 = x0 = x, y00 = y0 = y); + }; + function nextPoint(x, y) { + var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy); + d3_geo_centroidX1 += z * (x0 + x) / 2; + d3_geo_centroidY1 += z * (y0 + y) / 2; + d3_geo_centroidZ1 += z; + z = y0 * x - x0 * y; + d3_geo_centroidX2 += z * (x0 + x); + d3_geo_centroidY2 += z * (y0 + y); + d3_geo_centroidZ2 += z * 3; + d3_geo_pathCentroidPoint(x0 = x, y0 = y); + } + d3_geo_pathCentroid.lineEnd = function() { + nextPoint(x00, y00); + }; + } + function d3_geo_pathContext(context) { + var pointRadius = 4.5; + var stream = { + point: point, + lineStart: function() { + stream.point = pointLineStart; + }, + lineEnd: lineEnd, + polygonStart: function() { + stream.lineEnd = lineEndPolygon; + }, + polygonEnd: function() { + stream.lineEnd = lineEnd; + stream.point = point; + }, + pointRadius: function(_) { + pointRadius = _; + return stream; + }, + result: d3_noop + }; + function point(x, y) { + context.moveTo(x + pointRadius, y); + context.arc(x, y, pointRadius, 0, Ï„); + } + function pointLineStart(x, y) { + context.moveTo(x, y); + stream.point = pointLine; + } + function pointLine(x, y) { + context.lineTo(x, y); + } + function lineEnd() { + stream.point = point; + } + function lineEndPolygon() { + context.closePath(); + } + return stream; + } + function d3_geo_resample(project) { + var δ2 = .5, cosMinDistance = Math.cos(30 * d3_radians), maxDepth = 16; + function resample(stream) { + return (maxDepth ? resampleRecursive : resampleNone)(stream); + } + function resampleNone(stream) { + return d3_geo_transformPoint(stream, function(x, y) { + x = project(x, y); + stream.point(x[0], x[1]); + }); + } + function resampleRecursive(stream) { + var λ00, φ00, x00, y00, a00, b00, c00, λ0, x0, y0, a0, b0, c0; + var resample = { + point: point, + lineStart: lineStart, + lineEnd: lineEnd, + polygonStart: function() { + stream.polygonStart(); + resample.lineStart = ringStart; + }, + polygonEnd: function() { + stream.polygonEnd(); + resample.lineStart = lineStart; + } + }; + function point(x, y) { + x = project(x, y); + stream.point(x[0], x[1]); + } + function lineStart() { + x0 = NaN; + resample.point = linePoint; + stream.lineStart(); + } + function linePoint(λ, φ) { + var c = d3_geo_cartesian([ λ, φ ]), p = project(λ, φ); + resampleLineTo(x0, y0, λ0, a0, b0, c0, x0 = p[0], y0 = p[1], λ0 = λ, a0 = c[0], b0 = c[1], c0 = c[2], maxDepth, stream); + stream.point(x0, y0); + } + function lineEnd() { + resample.point = point; + stream.lineEnd(); + } + function ringStart() { + lineStart(); + resample.point = ringPoint; + resample.lineEnd = ringEnd; + } + function ringPoint(λ, φ) { + linePoint(λ00 = λ, φ00 = φ), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0; + resample.point = linePoint; + } + function ringEnd() { + resampleLineTo(x0, y0, λ0, a0, b0, c0, x00, y00, λ00, a00, b00, c00, maxDepth, stream); + resample.lineEnd = lineEnd; + lineEnd(); + } + return resample; + } + function resampleLineTo(x0, y0, λ0, a0, b0, c0, x1, y1, λ1, a1, b1, c1, depth, stream) { + var dx = x1 - x0, dy = y1 - y0, d2 = dx * dx + dy * dy; + if (d2 > 4 * δ2 && depth--) { + var a = a0 + a1, b = b0 + b1, c = c0 + c1, m = Math.sqrt(a * a + b * b + c * c), φ2 = Math.asin(c /= m), λ2 = abs(abs(c) - 1) < ε || abs(λ0 - λ1) < ε ? (λ0 + λ1) / 2 : Math.atan2(b, a), p = project(λ2, φ2), x2 = p[0], y2 = p[1], dx2 = x2 - x0, dy2 = y2 - y0, dz = dy * dx2 - dx * dy2; + if (dz * dz / d2 > δ2 || abs((dx * dx2 + dy * dy2) / d2 - .5) > .3 || a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) { + resampleLineTo(x0, y0, λ0, a0, b0, c0, x2, y2, λ2, a /= m, b /= m, c, depth, stream); + stream.point(x2, y2); + resampleLineTo(x2, y2, λ2, a, b, c, x1, y1, λ1, a1, b1, c1, depth, stream); + } + } + } + resample.precision = function(_) { + if (!arguments.length) return Math.sqrt(δ2); + maxDepth = (δ2 = _ * _) > 0 && 16; + return resample; + }; + return resample; + } + d3.geo.path = function() { + var pointRadius = 4.5, projection, context, projectStream, contextStream, cacheStream; + function path(object) { + if (object) { + if (typeof pointRadius === "function") contextStream.pointRadius(+pointRadius.apply(this, arguments)); + if (!cacheStream || !cacheStream.valid) cacheStream = projectStream(contextStream); + d3.geo.stream(object, cacheStream); + } + return contextStream.result(); + } + path.area = function(object) { + d3_geo_pathAreaSum = 0; + d3.geo.stream(object, projectStream(d3_geo_pathArea)); + return d3_geo_pathAreaSum; + }; + path.centroid = function(object) { + d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 = d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 = d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0; + d3.geo.stream(object, projectStream(d3_geo_pathCentroid)); + return d3_geo_centroidZ2 ? [ d3_geo_centroidX2 / d3_geo_centroidZ2, d3_geo_centroidY2 / d3_geo_centroidZ2 ] : d3_geo_centroidZ1 ? [ d3_geo_centroidX1 / d3_geo_centroidZ1, d3_geo_centroidY1 / d3_geo_centroidZ1 ] : d3_geo_centroidZ0 ? [ d3_geo_centroidX0 / d3_geo_centroidZ0, d3_geo_centroidY0 / d3_geo_centroidZ0 ] : [ NaN, NaN ]; + }; + path.bounds = function(object) { + d3_geo_pathBoundsX1 = d3_geo_pathBoundsY1 = -(d3_geo_pathBoundsX0 = d3_geo_pathBoundsY0 = Infinity); + d3.geo.stream(object, projectStream(d3_geo_pathBounds)); + return [ [ d3_geo_pathBoundsX0, d3_geo_pathBoundsY0 ], [ d3_geo_pathBoundsX1, d3_geo_pathBoundsY1 ] ]; + }; + path.projection = function(_) { + if (!arguments.length) return projection; + projectStream = (projection = _) ? _.stream || d3_geo_pathProjectStream(_) : d3_identity; + return reset(); + }; + path.context = function(_) { + if (!arguments.length) return context; + contextStream = (context = _) == null ? new d3_geo_pathBuffer() : new d3_geo_pathContext(_); + if (typeof pointRadius !== "function") contextStream.pointRadius(pointRadius); + return reset(); + }; + path.pointRadius = function(_) { + if (!arguments.length) return pointRadius; + pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_); + return path; + }; + function reset() { + cacheStream = null; + return path; + } + return path.projection(d3.geo.albersUsa()).context(null); + }; + function d3_geo_pathProjectStream(project) { + var resample = d3_geo_resample(function(x, y) { + return project([ x * d3_degrees, y * d3_degrees ]); + }); + return function(stream) { + return d3_geo_projectionRadians(resample(stream)); + }; + } + d3.geo.transform = function(methods) { + return { + stream: function(stream) { + var transform = new d3_geo_transform(stream); + for (var k in methods) transform[k] = methods[k]; + return transform; + } + }; + }; + function d3_geo_transform(stream) { + this.stream = stream; + } + d3_geo_transform.prototype = { + point: function(x, y) { + this.stream.point(x, y); + }, + sphere: function() { + this.stream.sphere(); + }, + lineStart: function() { + this.stream.lineStart(); + }, + lineEnd: function() { + this.stream.lineEnd(); + }, + polygonStart: function() { + this.stream.polygonStart(); + }, + polygonEnd: function() { + this.stream.polygonEnd(); + } + }; + function d3_geo_transformPoint(stream, point) { + return { + point: point, + sphere: function() { + stream.sphere(); + }, + lineStart: function() { + stream.lineStart(); + }, + lineEnd: function() { + stream.lineEnd(); + }, + polygonStart: function() { + stream.polygonStart(); + }, + polygonEnd: function() { + stream.polygonEnd(); + } + }; + } + d3.geo.projection = d3_geo_projection; + d3.geo.projectionMutator = d3_geo_projectionMutator; + function d3_geo_projection(project) { + return d3_geo_projectionMutator(function() { + return project; + })(); + } + function d3_geo_projectionMutator(projectAt) { + var project, rotate, projectRotate, projectResample = d3_geo_resample(function(x, y) { + x = project(x, y); + return [ x[0] * k + δx, δy - x[1] * k ]; + }), k = 150, x = 480, y = 250, λ = 0, φ = 0, δλ = 0, δφ = 0, δγ = 0, δx, δy, preclip = d3_geo_clipAntimeridian, postclip = d3_identity, clipAngle = null, clipExtent = null, stream; + function projection(point) { + point = projectRotate(point[0] * d3_radians, point[1] * d3_radians); + return [ point[0] * k + δx, δy - point[1] * k ]; + } + function invert(point) { + point = projectRotate.invert((point[0] - δx) / k, (δy - point[1]) / k); + return point && [ point[0] * d3_degrees, point[1] * d3_degrees ]; + } + projection.stream = function(output) { + if (stream) stream.valid = false; + stream = d3_geo_projectionRadians(preclip(rotate, projectResample(postclip(output)))); + stream.valid = true; + return stream; + }; + projection.clipAngle = function(_) { + if (!arguments.length) return clipAngle; + preclip = _ == null ? (clipAngle = _, d3_geo_clipAntimeridian) : d3_geo_clipCircle((clipAngle = +_) * d3_radians); + return invalidate(); + }; + projection.clipExtent = function(_) { + if (!arguments.length) return clipExtent; + clipExtent = _; + postclip = _ ? d3_geo_clipExtent(_[0][0], _[0][1], _[1][0], _[1][1]) : d3_identity; + return invalidate(); + }; + projection.scale = function(_) { + if (!arguments.length) return k; + k = +_; + return reset(); + }; + projection.translate = function(_) { + if (!arguments.length) return [ x, y ]; + x = +_[0]; + y = +_[1]; + return reset(); + }; + projection.center = function(_) { + if (!arguments.length) return [ λ * d3_degrees, φ * d3_degrees ]; + λ = _[0] % 360 * d3_radians; + φ = _[1] % 360 * d3_radians; + return reset(); + }; + projection.rotate = function(_) { + if (!arguments.length) return [ δλ * d3_degrees, δφ * d3_degrees, δγ * d3_degrees ]; + δλ = _[0] % 360 * d3_radians; + δφ = _[1] % 360 * d3_radians; + δγ = _.length > 2 ? _[2] % 360 * d3_radians : 0; + return reset(); + }; + d3.rebind(projection, projectResample, "precision"); + function reset() { + projectRotate = d3_geo_compose(rotate = d3_geo_rotation(δλ, δφ, δγ), project); + var center = project(λ, φ); + δx = x - center[0] * k; + δy = y + center[1] * k; + return invalidate(); + } + function invalidate() { + if (stream) stream.valid = false, stream = null; + return projection; + } + return function() { + project = projectAt.apply(this, arguments); + projection.invert = project.invert && invert; + return reset(); + }; + } + function d3_geo_projectionRadians(stream) { + return d3_geo_transformPoint(stream, function(x, y) { + stream.point(x * d3_radians, y * d3_radians); + }); + } + function d3_geo_equirectangular(λ, φ) { + return [ λ, φ ]; + } + (d3.geo.equirectangular = function() { + return d3_geo_projection(d3_geo_equirectangular); + }).raw = d3_geo_equirectangular.invert = d3_geo_equirectangular; + d3.geo.rotation = function(rotate) { + rotate = d3_geo_rotation(rotate[0] % 360 * d3_radians, rotate[1] * d3_radians, rotate.length > 2 ? rotate[2] * d3_radians : 0); + function forward(coordinates) { + coordinates = rotate(coordinates[0] * d3_radians, coordinates[1] * d3_radians); + return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates; + } + forward.invert = function(coordinates) { + coordinates = rotate.invert(coordinates[0] * d3_radians, coordinates[1] * d3_radians); + return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates; + }; + return forward; + }; + function d3_geo_identityRotation(λ, φ) { + return [ λ > Ï€ ? λ - Ï„ : λ < -Ï€ ? λ + Ï„ : λ, φ ]; + } + d3_geo_identityRotation.invert = d3_geo_equirectangular; + function d3_geo_rotation(δλ, δφ, δγ) { + return δλ ? δφ || δγ ? d3_geo_compose(d3_geo_rotationλ(δλ), d3_geo_rotationφγ(δφ, δγ)) : d3_geo_rotationλ(δλ) : δφ || δγ ? d3_geo_rotationφγ(δφ, δγ) : d3_geo_identityRotation; + } + function d3_geo_forwardRotationλ(δλ) { + return function(λ, φ) { + return λ += δλ, [ λ > Ï€ ? λ - Ï„ : λ < -Ï€ ? λ + Ï„ : λ, φ ]; + }; + } + function d3_geo_rotationλ(δλ) { + var rotation = d3_geo_forwardRotationλ(δλ); + rotation.invert = d3_geo_forwardRotationλ(-δλ); + return rotation; + } + function d3_geo_rotationφγ(δφ, δγ) { + var cosδφ = Math.cos(δφ), sinδφ = Math.sin(δφ), cosδγ = Math.cos(δγ), sinδγ = Math.sin(δγ); + function rotation(λ, φ) { + var cosφ = Math.cos(φ), x = Math.cos(λ) * cosφ, y = Math.sin(λ) * cosφ, z = Math.sin(φ), k = z * cosδφ + x * sinδφ; + return [ Math.atan2(y * cosδγ - k * sinδγ, x * cosδφ - z * sinδφ), d3_asin(k * cosδγ + y * sinδγ) ]; + } + rotation.invert = function(λ, φ) { + var cosφ = Math.cos(φ), x = Math.cos(λ) * cosφ, y = Math.sin(λ) * cosφ, z = Math.sin(φ), k = z * cosδγ - y * sinδγ; + return [ Math.atan2(y * cosδγ + z * sinδγ, x * cosδφ + k * sinδφ), d3_asin(k * cosδφ - x * sinδφ) ]; + }; + return rotation; + } + d3.geo.circle = function() { + var origin = [ 0, 0 ], angle, precision = 6, interpolate; + function circle() { + var center = typeof origin === "function" ? origin.apply(this, arguments) : origin, rotate = d3_geo_rotation(-center[0] * d3_radians, -center[1] * d3_radians, 0).invert, ring = []; + interpolate(null, null, 1, { + point: function(x, y) { + ring.push(x = rotate(x, y)); + x[0] *= d3_degrees, x[1] *= d3_degrees; + } + }); + return { + type: "Polygon", + coordinates: [ ring ] + }; + } + circle.origin = function(x) { + if (!arguments.length) return origin; + origin = x; + return circle; + }; + circle.angle = function(x) { + if (!arguments.length) return angle; + interpolate = d3_geo_circleInterpolate((angle = +x) * d3_radians, precision * d3_radians); + return circle; + }; + circle.precision = function(_) { + if (!arguments.length) return precision; + interpolate = d3_geo_circleInterpolate(angle * d3_radians, (precision = +_) * d3_radians); + return circle; + }; + return circle.angle(90); + }; + function d3_geo_circleInterpolate(radius, precision) { + var cr = Math.cos(radius), sr = Math.sin(radius); + return function(from, to, direction, listener) { + var step = direction * precision; + if (from != null) { + from = d3_geo_circleAngle(cr, from); + to = d3_geo_circleAngle(cr, to); + if (direction > 0 ? from < to : from > to) from += direction * Ï„; + } else { + from = radius + direction * Ï„; + to = radius - .5 * step; + } + for (var point, t = from; direction > 0 ? t > to : t < to; t -= step) { + listener.point((point = d3_geo_spherical([ cr, -sr * Math.cos(t), -sr * Math.sin(t) ]))[0], point[1]); + } + }; + } + function d3_geo_circleAngle(cr, point) { + var a = d3_geo_cartesian(point); + a[0] -= cr; + d3_geo_cartesianNormalize(a); + var angle = d3_acos(-a[1]); + return ((-a[2] < 0 ? -angle : angle) + 2 * Math.PI - ε) % (2 * Math.PI); + } + d3.geo.distance = function(a, b) { + var Δλ = (b[0] - a[0]) * d3_radians, φ0 = a[1] * d3_radians, φ1 = b[1] * d3_radians, sinΔλ = Math.sin(Δλ), cosΔλ = Math.cos(Δλ), sinφ0 = Math.sin(φ0), cosφ0 = Math.cos(φ0), sinφ1 = Math.sin(φ1), cosφ1 = Math.cos(φ1), t; + return Math.atan2(Math.sqrt((t = cosφ1 * sinΔλ) * t + (t = cosφ0 * sinφ1 - sinφ0 * cosφ1 * cosΔλ) * t), sinφ0 * sinφ1 + cosφ0 * cosφ1 * cosΔλ); + }; + d3.geo.graticule = function() { + var x1, x0, X1, X0, y1, y0, Y1, Y0, dx = 10, dy = dx, DX = 90, DY = 360, x, y, X, Y, precision = 2.5; + function graticule() { + return { + type: "MultiLineString", + coordinates: lines() + }; + } + function lines() { + return d3.range(Math.ceil(X0 / DX) * DX, X1, DX).map(X).concat(d3.range(Math.ceil(Y0 / DY) * DY, Y1, DY).map(Y)).concat(d3.range(Math.ceil(x0 / dx) * dx, x1, dx).filter(function(x) { + return abs(x % DX) > ε; + }).map(x)).concat(d3.range(Math.ceil(y0 / dy) * dy, y1, dy).filter(function(y) { + return abs(y % DY) > ε; + }).map(y)); + } + graticule.lines = function() { + return lines().map(function(coordinates) { + return { + type: "LineString", + coordinates: coordinates + }; + }); + }; + graticule.outline = function() { + return { + type: "Polygon", + coordinates: [ X(X0).concat(Y(Y1).slice(1), X(X1).reverse().slice(1), Y(Y0).reverse().slice(1)) ] + }; + }; + graticule.extent = function(_) { + if (!arguments.length) return graticule.minorExtent(); + return graticule.majorExtent(_).minorExtent(_); + }; + graticule.majorExtent = function(_) { + if (!arguments.length) return [ [ X0, Y0 ], [ X1, Y1 ] ]; + X0 = +_[0][0], X1 = +_[1][0]; + Y0 = +_[0][1], Y1 = +_[1][1]; + if (X0 > X1) _ = X0, X0 = X1, X1 = _; + if (Y0 > Y1) _ = Y0, Y0 = Y1, Y1 = _; + return graticule.precision(precision); + }; + graticule.minorExtent = function(_) { + if (!arguments.length) return [ [ x0, y0 ], [ x1, y1 ] ]; + x0 = +_[0][0], x1 = +_[1][0]; + y0 = +_[0][1], y1 = +_[1][1]; + if (x0 > x1) _ = x0, x0 = x1, x1 = _; + if (y0 > y1) _ = y0, y0 = y1, y1 = _; + return graticule.precision(precision); + }; + graticule.step = function(_) { + if (!arguments.length) return graticule.minorStep(); + return graticule.majorStep(_).minorStep(_); + }; + graticule.majorStep = function(_) { + if (!arguments.length) return [ DX, DY ]; + DX = +_[0], DY = +_[1]; + return graticule; + }; + graticule.minorStep = function(_) { + if (!arguments.length) return [ dx, dy ]; + dx = +_[0], dy = +_[1]; + return graticule; + }; + graticule.precision = function(_) { + if (!arguments.length) return precision; + precision = +_; + x = d3_geo_graticuleX(y0, y1, 90); + y = d3_geo_graticuleY(x0, x1, precision); + X = d3_geo_graticuleX(Y0, Y1, 90); + Y = d3_geo_graticuleY(X0, X1, precision); + return graticule; + }; + return graticule.majorExtent([ [ -180, -90 + ε ], [ 180, 90 - ε ] ]).minorExtent([ [ -180, -80 - ε ], [ 180, 80 + ε ] ]); + }; + function d3_geo_graticuleX(y0, y1, dy) { + var y = d3.range(y0, y1 - ε, dy).concat(y1); + return function(x) { + return y.map(function(y) { + return [ x, y ]; + }); + }; + } + function d3_geo_graticuleY(x0, x1, dx) { + var x = d3.range(x0, x1 - ε, dx).concat(x1); + return function(y) { + return x.map(function(x) { + return [ x, y ]; + }); + }; + } + function d3_source(d) { + return d.source; + } + function d3_target(d) { + return d.target; + } + d3.geo.greatArc = function() { + var source = d3_source, source_, target = d3_target, target_; + function greatArc() { + return { + type: "LineString", + coordinates: [ source_ || source.apply(this, arguments), target_ || target.apply(this, arguments) ] + }; + } + greatArc.distance = function() { + return d3.geo.distance(source_ || source.apply(this, arguments), target_ || target.apply(this, arguments)); + }; + greatArc.source = function(_) { + if (!arguments.length) return source; + source = _, source_ = typeof _ === "function" ? null : _; + return greatArc; + }; + greatArc.target = function(_) { + if (!arguments.length) return target; + target = _, target_ = typeof _ === "function" ? null : _; + return greatArc; + }; + greatArc.precision = function() { + return arguments.length ? greatArc : 0; + }; + return greatArc; + }; + d3.geo.interpolate = function(source, target) { + return d3_geo_interpolate(source[0] * d3_radians, source[1] * d3_radians, target[0] * d3_radians, target[1] * d3_radians); + }; + function d3_geo_interpolate(x0, y0, x1, y1) { + var cy0 = Math.cos(y0), sy0 = Math.sin(y0), cy1 = Math.cos(y1), sy1 = Math.sin(y1), kx0 = cy0 * Math.cos(x0), ky0 = cy0 * Math.sin(x0), kx1 = cy1 * Math.cos(x1), ky1 = cy1 * Math.sin(x1), d = 2 * Math.asin(Math.sqrt(d3_haversin(y1 - y0) + cy0 * cy1 * d3_haversin(x1 - x0))), k = 1 / Math.sin(d); + var interpolate = d ? function(t) { + var B = Math.sin(t *= d) * k, A = Math.sin(d - t) * k, x = A * kx0 + B * kx1, y = A * ky0 + B * ky1, z = A * sy0 + B * sy1; + return [ Math.atan2(y, x) * d3_degrees, Math.atan2(z, Math.sqrt(x * x + y * y)) * d3_degrees ]; + } : function() { + return [ x0 * d3_degrees, y0 * d3_degrees ]; + }; + interpolate.distance = d; + return interpolate; + } + d3.geo.length = function(object) { + d3_geo_lengthSum = 0; + d3.geo.stream(object, d3_geo_length); + return d3_geo_lengthSum; + }; + var d3_geo_lengthSum; + var d3_geo_length = { + sphere: d3_noop, + point: d3_noop, + lineStart: d3_geo_lengthLineStart, + lineEnd: d3_noop, + polygonStart: d3_noop, + polygonEnd: d3_noop + }; + function d3_geo_lengthLineStart() { + var λ0, sinφ0, cosφ0; + d3_geo_length.point = function(λ, φ) { + λ0 = λ * d3_radians, sinφ0 = Math.sin(φ *= d3_radians), cosφ0 = Math.cos(φ); + d3_geo_length.point = nextPoint; + }; + d3_geo_length.lineEnd = function() { + d3_geo_length.point = d3_geo_length.lineEnd = d3_noop; + }; + function nextPoint(λ, φ) { + var sinφ = Math.sin(φ *= d3_radians), cosφ = Math.cos(φ), t = abs((λ *= d3_radians) - λ0), cosΔλ = Math.cos(t); + d3_geo_lengthSum += Math.atan2(Math.sqrt((t = cosφ * Math.sin(t)) * t + (t = cosφ0 * sinφ - sinφ0 * cosφ * cosΔλ) * t), sinφ0 * sinφ + cosφ0 * cosφ * cosΔλ); + λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ; + } + } + function d3_geo_azimuthal(scale, angle) { + function azimuthal(λ, φ) { + var cosλ = Math.cos(λ), cosφ = Math.cos(φ), k = scale(cosλ * cosφ); + return [ k * cosφ * Math.sin(λ), k * Math.sin(φ) ]; + } + azimuthal.invert = function(x, y) { + var Ï = Math.sqrt(x * x + y * y), c = angle(Ï), sinc = Math.sin(c), cosc = Math.cos(c); + return [ Math.atan2(x * sinc, Ï * cosc), Math.asin(Ï && y * sinc / Ï) ]; + }; + return azimuthal; + } + var d3_geo_azimuthalEqualArea = d3_geo_azimuthal(function(cosλcosφ) { + return Math.sqrt(2 / (1 + cosλcosφ)); + }, function(Ï) { + return 2 * Math.asin(Ï / 2); + }); + (d3.geo.azimuthalEqualArea = function() { + return d3_geo_projection(d3_geo_azimuthalEqualArea); + }).raw = d3_geo_azimuthalEqualArea; + var d3_geo_azimuthalEquidistant = d3_geo_azimuthal(function(cosλcosφ) { + var c = Math.acos(cosλcosφ); + return c && c / Math.sin(c); + }, d3_identity); + (d3.geo.azimuthalEquidistant = function() { + return d3_geo_projection(d3_geo_azimuthalEquidistant); + }).raw = d3_geo_azimuthalEquidistant; + function d3_geo_conicConformal(φ0, φ1) { + var cosφ0 = Math.cos(φ0), t = function(φ) { + return Math.tan(Ï€ / 4 + φ / 2); + }, n = φ0 === φ1 ? Math.sin(φ0) : Math.log(cosφ0 / Math.cos(φ1)) / Math.log(t(φ1) / t(φ0)), F = cosφ0 * Math.pow(t(φ0), n) / n; + if (!n) return d3_geo_mercator; + function forward(λ, φ) { + if (F > 0) { + if (φ < -halfÏ€ + ε) φ = -halfÏ€ + ε; + } else { + if (φ > halfÏ€ - ε) φ = halfÏ€ - ε; + } + var Ï = F / Math.pow(t(φ), n); + return [ Ï * Math.sin(n * λ), F - Ï * Math.cos(n * λ) ]; + } + forward.invert = function(x, y) { + var Ï0_y = F - y, Ï = d3_sgn(n) * Math.sqrt(x * x + Ï0_y * Ï0_y); + return [ Math.atan2(x, Ï0_y) / n, 2 * Math.atan(Math.pow(F / Ï, 1 / n)) - halfÏ€ ]; + }; + return forward; + } + (d3.geo.conicConformal = function() { + return d3_geo_conic(d3_geo_conicConformal); + }).raw = d3_geo_conicConformal; + function d3_geo_conicEquidistant(φ0, φ1) { + var cosφ0 = Math.cos(φ0), n = φ0 === φ1 ? Math.sin(φ0) : (cosφ0 - Math.cos(φ1)) / (φ1 - φ0), G = cosφ0 / n + φ0; + if (abs(n) < ε) return d3_geo_equirectangular; + function forward(λ, φ) { + var Ï = G - φ; + return [ Ï * Math.sin(n * λ), G - Ï * Math.cos(n * λ) ]; + } + forward.invert = function(x, y) { + var Ï0_y = G - y; + return [ Math.atan2(x, Ï0_y) / n, G - d3_sgn(n) * Math.sqrt(x * x + Ï0_y * Ï0_y) ]; + }; + return forward; + } + (d3.geo.conicEquidistant = function() { + return d3_geo_conic(d3_geo_conicEquidistant); + }).raw = d3_geo_conicEquidistant; + var d3_geo_gnomonic = d3_geo_azimuthal(function(cosλcosφ) { + return 1 / cosλcosφ; + }, Math.atan); + (d3.geo.gnomonic = function() { + return d3_geo_projection(d3_geo_gnomonic); + }).raw = d3_geo_gnomonic; + function d3_geo_mercator(λ, φ) { + return [ λ, Math.log(Math.tan(Ï€ / 4 + φ / 2)) ]; + } + d3_geo_mercator.invert = function(x, y) { + return [ x, 2 * Math.atan(Math.exp(y)) - halfÏ€ ]; + }; + function d3_geo_mercatorProjection(project) { + var m = d3_geo_projection(project), scale = m.scale, translate = m.translate, clipExtent = m.clipExtent, clipAuto; + m.scale = function() { + var v = scale.apply(m, arguments); + return v === m ? clipAuto ? m.clipExtent(null) : m : v; + }; + m.translate = function() { + var v = translate.apply(m, arguments); + return v === m ? clipAuto ? m.clipExtent(null) : m : v; + }; + m.clipExtent = function(_) { + var v = clipExtent.apply(m, arguments); + if (v === m) { + if (clipAuto = _ == null) { + var k = Ï€ * scale(), t = translate(); + clipExtent([ [ t[0] - k, t[1] - k ], [ t[0] + k, t[1] + k ] ]); + } + } else if (clipAuto) { + v = null; + } + return v; + }; + return m.clipExtent(null); + } + (d3.geo.mercator = function() { + return d3_geo_mercatorProjection(d3_geo_mercator); + }).raw = d3_geo_mercator; + var d3_geo_orthographic = d3_geo_azimuthal(function() { + return 1; + }, Math.asin); + (d3.geo.orthographic = function() { + return d3_geo_projection(d3_geo_orthographic); + }).raw = d3_geo_orthographic; + var d3_geo_stereographic = d3_geo_azimuthal(function(cosλcosφ) { + return 1 / (1 + cosλcosφ); + }, function(Ï) { + return 2 * Math.atan(Ï); + }); + (d3.geo.stereographic = function() { + return d3_geo_projection(d3_geo_stereographic); + }).raw = d3_geo_stereographic; + function d3_geo_transverseMercator(λ, φ) { + return [ Math.log(Math.tan(Ï€ / 4 + φ / 2)), -λ ]; + } + d3_geo_transverseMercator.invert = function(x, y) { + return [ -y, 2 * Math.atan(Math.exp(x)) - halfÏ€ ]; + }; + (d3.geo.transverseMercator = function() { + var projection = d3_geo_mercatorProjection(d3_geo_transverseMercator), center = projection.center, rotate = projection.rotate; + projection.center = function(_) { + return _ ? center([ -_[1], _[0] ]) : (_ = center(), [ _[1], -_[0] ]); + }; + projection.rotate = function(_) { + return _ ? rotate([ _[0], _[1], _.length > 2 ? _[2] + 90 : 90 ]) : (_ = rotate(), + [ _[0], _[1], _[2] - 90 ]); + }; + return rotate([ 0, 0, 90 ]); + }).raw = d3_geo_transverseMercator; + d3.geom = {}; + function d3_geom_pointX(d) { + return d[0]; + } + function d3_geom_pointY(d) { + return d[1]; + } + d3.geom.hull = function(vertices) { + var x = d3_geom_pointX, y = d3_geom_pointY; + if (arguments.length) return hull(vertices); + function hull(data) { + if (data.length < 3) return []; + var fx = d3_functor(x), fy = d3_functor(y), i, n = data.length, points = [], flippedPoints = []; + for (i = 0; i < n; i++) { + points.push([ +fx.call(this, data[i], i), +fy.call(this, data[i], i), i ]); + } + points.sort(d3_geom_hullOrder); + for (i = 0; i < n; i++) flippedPoints.push([ points[i][0], -points[i][1] ]); + var upper = d3_geom_hullUpper(points), lower = d3_geom_hullUpper(flippedPoints); + var skipLeft = lower[0] === upper[0], skipRight = lower[lower.length - 1] === upper[upper.length - 1], polygon = []; + for (i = upper.length - 1; i >= 0; --i) polygon.push(data[points[upper[i]][2]]); + for (i = +skipLeft; i < lower.length - skipRight; ++i) polygon.push(data[points[lower[i]][2]]); + return polygon; + } + hull.x = function(_) { + return arguments.length ? (x = _, hull) : x; + }; + hull.y = function(_) { + return arguments.length ? (y = _, hull) : y; + }; + return hull; + }; + function d3_geom_hullUpper(points) { + var n = points.length, hull = [ 0, 1 ], hs = 2; + for (var i = 2; i < n; i++) { + while (hs > 1 && d3_cross2d(points[hull[hs - 2]], points[hull[hs - 1]], points[i]) <= 0) --hs; + hull[hs++] = i; + } + return hull.slice(0, hs); + } + function d3_geom_hullOrder(a, b) { + return a[0] - b[0] || a[1] - b[1]; + } + d3.geom.polygon = function(coordinates) { + d3_subclass(coordinates, d3_geom_polygonPrototype); + return coordinates; + }; + var d3_geom_polygonPrototype = d3.geom.polygon.prototype = []; + d3_geom_polygonPrototype.area = function() { + var i = -1, n = this.length, a, b = this[n - 1], area = 0; + while (++i < n) { + a = b; + b = this[i]; + area += a[1] * b[0] - a[0] * b[1]; + } + return area * .5; + }; + d3_geom_polygonPrototype.centroid = function(k) { + var i = -1, n = this.length, x = 0, y = 0, a, b = this[n - 1], c; + if (!arguments.length) k = -1 / (6 * this.area()); + while (++i < n) { + a = b; + b = this[i]; + c = a[0] * b[1] - b[0] * a[1]; + x += (a[0] + b[0]) * c; + y += (a[1] + b[1]) * c; + } + return [ x * k, y * k ]; + }; + d3_geom_polygonPrototype.clip = function(subject) { + var input, closed = d3_geom_polygonClosed(subject), i = -1, n = this.length - d3_geom_polygonClosed(this), j, m, a = this[n - 1], b, c, d; + while (++i < n) { + input = subject.slice(); + subject.length = 0; + b = this[i]; + c = input[(m = input.length - closed) - 1]; + j = -1; + while (++j < m) { + d = input[j]; + if (d3_geom_polygonInside(d, a, b)) { + if (!d3_geom_polygonInside(c, a, b)) { + subject.push(d3_geom_polygonIntersect(c, d, a, b)); + } + subject.push(d); + } else if (d3_geom_polygonInside(c, a, b)) { + subject.push(d3_geom_polygonIntersect(c, d, a, b)); + } + c = d; + } + if (closed) subject.push(subject[0]); + a = b; + } + return subject; + }; + function d3_geom_polygonInside(p, a, b) { + return (b[0] - a[0]) * (p[1] - a[1]) < (b[1] - a[1]) * (p[0] - a[0]); + } + function d3_geom_polygonIntersect(c, d, a, b) { + var x1 = c[0], x3 = a[0], x21 = d[0] - x1, x43 = b[0] - x3, y1 = c[1], y3 = a[1], y21 = d[1] - y1, y43 = b[1] - y3, ua = (x43 * (y1 - y3) - y43 * (x1 - x3)) / (y43 * x21 - x43 * y21); + return [ x1 + ua * x21, y1 + ua * y21 ]; + } + function d3_geom_polygonClosed(coordinates) { + var a = coordinates[0], b = coordinates[coordinates.length - 1]; + return !(a[0] - b[0] || a[1] - b[1]); + } + var d3_geom_voronoiEdges, d3_geom_voronoiCells, d3_geom_voronoiBeaches, d3_geom_voronoiBeachPool = [], d3_geom_voronoiFirstCircle, d3_geom_voronoiCircles, d3_geom_voronoiCirclePool = []; + function d3_geom_voronoiBeach() { + d3_geom_voronoiRedBlackNode(this); + this.edge = this.site = this.circle = null; + } + function d3_geom_voronoiCreateBeach(site) { + var beach = d3_geom_voronoiBeachPool.pop() || new d3_geom_voronoiBeach(); + beach.site = site; + return beach; + } + function d3_geom_voronoiDetachBeach(beach) { + d3_geom_voronoiDetachCircle(beach); + d3_geom_voronoiBeaches.remove(beach); + d3_geom_voronoiBeachPool.push(beach); + d3_geom_voronoiRedBlackNode(beach); + } + function d3_geom_voronoiRemoveBeach(beach) { + var circle = beach.circle, x = circle.x, y = circle.cy, vertex = { + x: x, + y: y + }, previous = beach.P, next = beach.N, disappearing = [ beach ]; + d3_geom_voronoiDetachBeach(beach); + var lArc = previous; + while (lArc.circle && abs(x - lArc.circle.x) < ε && abs(y - lArc.circle.cy) < ε) { + previous = lArc.P; + disappearing.unshift(lArc); + d3_geom_voronoiDetachBeach(lArc); + lArc = previous; + } + disappearing.unshift(lArc); + d3_geom_voronoiDetachCircle(lArc); + var rArc = next; + while (rArc.circle && abs(x - rArc.circle.x) < ε && abs(y - rArc.circle.cy) < ε) { + next = rArc.N; + disappearing.push(rArc); + d3_geom_voronoiDetachBeach(rArc); + rArc = next; + } + disappearing.push(rArc); + d3_geom_voronoiDetachCircle(rArc); + var nArcs = disappearing.length, iArc; + for (iArc = 1; iArc < nArcs; ++iArc) { + rArc = disappearing[iArc]; + lArc = disappearing[iArc - 1]; + d3_geom_voronoiSetEdgeEnd(rArc.edge, lArc.site, rArc.site, vertex); + } + lArc = disappearing[0]; + rArc = disappearing[nArcs - 1]; + rArc.edge = d3_geom_voronoiCreateEdge(lArc.site, rArc.site, null, vertex); + d3_geom_voronoiAttachCircle(lArc); + d3_geom_voronoiAttachCircle(rArc); + } + function d3_geom_voronoiAddBeach(site) { + var x = site.x, directrix = site.y, lArc, rArc, dxl, dxr, node = d3_geom_voronoiBeaches._; + while (node) { + dxl = d3_geom_voronoiLeftBreakPoint(node, directrix) - x; + if (dxl > ε) node = node.L; else { + dxr = x - d3_geom_voronoiRightBreakPoint(node, directrix); + if (dxr > ε) { + if (!node.R) { + lArc = node; + break; + } + node = node.R; + } else { + if (dxl > -ε) { + lArc = node.P; + rArc = node; + } else if (dxr > -ε) { + lArc = node; + rArc = node.N; + } else { + lArc = rArc = node; + } + break; + } + } + } + var newArc = d3_geom_voronoiCreateBeach(site); + d3_geom_voronoiBeaches.insert(lArc, newArc); + if (!lArc && !rArc) return; + if (lArc === rArc) { + d3_geom_voronoiDetachCircle(lArc); + rArc = d3_geom_voronoiCreateBeach(lArc.site); + d3_geom_voronoiBeaches.insert(newArc, rArc); + newArc.edge = rArc.edge = d3_geom_voronoiCreateEdge(lArc.site, newArc.site); + d3_geom_voronoiAttachCircle(lArc); + d3_geom_voronoiAttachCircle(rArc); + return; + } + if (!rArc) { + newArc.edge = d3_geom_voronoiCreateEdge(lArc.site, newArc.site); + return; + } + d3_geom_voronoiDetachCircle(lArc); + d3_geom_voronoiDetachCircle(rArc); + var lSite = lArc.site, ax = lSite.x, ay = lSite.y, bx = site.x - ax, by = site.y - ay, rSite = rArc.site, cx = rSite.x - ax, cy = rSite.y - ay, d = 2 * (bx * cy - by * cx), hb = bx * bx + by * by, hc = cx * cx + cy * cy, vertex = { + x: (cy * hb - by * hc) / d + ax, + y: (bx * hc - cx * hb) / d + ay + }; + d3_geom_voronoiSetEdgeEnd(rArc.edge, lSite, rSite, vertex); + newArc.edge = d3_geom_voronoiCreateEdge(lSite, site, null, vertex); + rArc.edge = d3_geom_voronoiCreateEdge(site, rSite, null, vertex); + d3_geom_voronoiAttachCircle(lArc); + d3_geom_voronoiAttachCircle(rArc); + } + function d3_geom_voronoiLeftBreakPoint(arc, directrix) { + var site = arc.site, rfocx = site.x, rfocy = site.y, pby2 = rfocy - directrix; + if (!pby2) return rfocx; + var lArc = arc.P; + if (!lArc) return -Infinity; + site = lArc.site; + var lfocx = site.x, lfocy = site.y, plby2 = lfocy - directrix; + if (!plby2) return lfocx; + var hl = lfocx - rfocx, aby2 = 1 / pby2 - 1 / plby2, b = hl / plby2; + if (aby2) return (-b + Math.sqrt(b * b - 2 * aby2 * (hl * hl / (-2 * plby2) - lfocy + plby2 / 2 + rfocy - pby2 / 2))) / aby2 + rfocx; + return (rfocx + lfocx) / 2; + } + function d3_geom_voronoiRightBreakPoint(arc, directrix) { + var rArc = arc.N; + if (rArc) return d3_geom_voronoiLeftBreakPoint(rArc, directrix); + var site = arc.site; + return site.y === directrix ? site.x : Infinity; + } + function d3_geom_voronoiCell(site) { + this.site = site; + this.edges = []; + } + d3_geom_voronoiCell.prototype.prepare = function() { + var halfEdges = this.edges, iHalfEdge = halfEdges.length, edge; + while (iHalfEdge--) { + edge = halfEdges[iHalfEdge].edge; + if (!edge.b || !edge.a) halfEdges.splice(iHalfEdge, 1); + } + halfEdges.sort(d3_geom_voronoiHalfEdgeOrder); + return halfEdges.length; + }; + function d3_geom_voronoiCloseCells(extent) { + var x0 = extent[0][0], x1 = extent[1][0], y0 = extent[0][1], y1 = extent[1][1], x2, y2, x3, y3, cells = d3_geom_voronoiCells, iCell = cells.length, cell, iHalfEdge, halfEdges, nHalfEdges, start, end; + while (iCell--) { + cell = cells[iCell]; + if (!cell || !cell.prepare()) continue; + halfEdges = cell.edges; + nHalfEdges = halfEdges.length; + iHalfEdge = 0; + while (iHalfEdge < nHalfEdges) { + end = halfEdges[iHalfEdge].end(), x3 = end.x, y3 = end.y; + start = halfEdges[++iHalfEdge % nHalfEdges].start(), x2 = start.x, y2 = start.y; + if (abs(x3 - x2) > ε || abs(y3 - y2) > ε) { + halfEdges.splice(iHalfEdge, 0, new d3_geom_voronoiHalfEdge(d3_geom_voronoiCreateBorderEdge(cell.site, end, abs(x3 - x0) < ε && y1 - y3 > ε ? { + x: x0, + y: abs(x2 - x0) < ε ? y2 : y1 + } : abs(y3 - y1) < ε && x1 - x3 > ε ? { + x: abs(y2 - y1) < ε ? x2 : x1, + y: y1 + } : abs(x3 - x1) < ε && y3 - y0 > ε ? { + x: x1, + y: abs(x2 - x1) < ε ? y2 : y0 + } : abs(y3 - y0) < ε && x3 - x0 > ε ? { + x: abs(y2 - y0) < ε ? x2 : x0, + y: y0 + } : null), cell.site, null)); + ++nHalfEdges; + } + } + } + } + function d3_geom_voronoiHalfEdgeOrder(a, b) { + return b.angle - a.angle; + } + function d3_geom_voronoiCircle() { + d3_geom_voronoiRedBlackNode(this); + this.x = this.y = this.arc = this.site = this.cy = null; + } + function d3_geom_voronoiAttachCircle(arc) { + var lArc = arc.P, rArc = arc.N; + if (!lArc || !rArc) return; + var lSite = lArc.site, cSite = arc.site, rSite = rArc.site; + if (lSite === rSite) return; + var bx = cSite.x, by = cSite.y, ax = lSite.x - bx, ay = lSite.y - by, cx = rSite.x - bx, cy = rSite.y - by; + var d = 2 * (ax * cy - ay * cx); + if (d >= -ε2) return; + var ha = ax * ax + ay * ay, hc = cx * cx + cy * cy, x = (cy * ha - ay * hc) / d, y = (ax * hc - cx * ha) / d, cy = y + by; + var circle = d3_geom_voronoiCirclePool.pop() || new d3_geom_voronoiCircle(); + circle.arc = arc; + circle.site = cSite; + circle.x = x + bx; + circle.y = cy + Math.sqrt(x * x + y * y); + circle.cy = cy; + arc.circle = circle; + var before = null, node = d3_geom_voronoiCircles._; + while (node) { + if (circle.y < node.y || circle.y === node.y && circle.x <= node.x) { + if (node.L) node = node.L; else { + before = node.P; + break; + } + } else { + if (node.R) node = node.R; else { + before = node; + break; + } + } + } + d3_geom_voronoiCircles.insert(before, circle); + if (!before) d3_geom_voronoiFirstCircle = circle; + } + function d3_geom_voronoiDetachCircle(arc) { + var circle = arc.circle; + if (circle) { + if (!circle.P) d3_geom_voronoiFirstCircle = circle.N; + d3_geom_voronoiCircles.remove(circle); + d3_geom_voronoiCirclePool.push(circle); + d3_geom_voronoiRedBlackNode(circle); + arc.circle = null; + } + } + function d3_geom_voronoiClipEdges(extent) { + var edges = d3_geom_voronoiEdges, clip = d3_geom_clipLine(extent[0][0], extent[0][1], extent[1][0], extent[1][1]), i = edges.length, e; + while (i--) { + e = edges[i]; + if (!d3_geom_voronoiConnectEdge(e, extent) || !clip(e) || abs(e.a.x - e.b.x) < ε && abs(e.a.y - e.b.y) < ε) { + e.a = e.b = null; + edges.splice(i, 1); + } + } + } + function d3_geom_voronoiConnectEdge(edge, extent) { + var vb = edge.b; + if (vb) return true; + var va = edge.a, x0 = extent[0][0], x1 = extent[1][0], y0 = extent[0][1], y1 = extent[1][1], lSite = edge.l, rSite = edge.r, lx = lSite.x, ly = lSite.y, rx = rSite.x, ry = rSite.y, fx = (lx + rx) / 2, fy = (ly + ry) / 2, fm, fb; + if (ry === ly) { + if (fx < x0 || fx >= x1) return; + if (lx > rx) { + if (!va) va = { + x: fx, + y: y0 + }; else if (va.y >= y1) return; + vb = { + x: fx, + y: y1 + }; + } else { + if (!va) va = { + x: fx, + y: y1 + }; else if (va.y < y0) return; + vb = { + x: fx, + y: y0 + }; + } + } else { + fm = (lx - rx) / (ry - ly); + fb = fy - fm * fx; + if (fm < -1 || fm > 1) { + if (lx > rx) { + if (!va) va = { + x: (y0 - fb) / fm, + y: y0 + }; else if (va.y >= y1) return; + vb = { + x: (y1 - fb) / fm, + y: y1 + }; + } else { + if (!va) va = { + x: (y1 - fb) / fm, + y: y1 + }; else if (va.y < y0) return; + vb = { + x: (y0 - fb) / fm, + y: y0 + }; + } + } else { + if (ly < ry) { + if (!va) va = { + x: x0, + y: fm * x0 + fb + }; else if (va.x >= x1) return; + vb = { + x: x1, + y: fm * x1 + fb + }; + } else { + if (!va) va = { + x: x1, + y: fm * x1 + fb + }; else if (va.x < x0) return; + vb = { + x: x0, + y: fm * x0 + fb + }; + } + } + } + edge.a = va; + edge.b = vb; + return true; + } + function d3_geom_voronoiEdge(lSite, rSite) { + this.l = lSite; + this.r = rSite; + this.a = this.b = null; + } + function d3_geom_voronoiCreateEdge(lSite, rSite, va, vb) { + var edge = new d3_geom_voronoiEdge(lSite, rSite); + d3_geom_voronoiEdges.push(edge); + if (va) d3_geom_voronoiSetEdgeEnd(edge, lSite, rSite, va); + if (vb) d3_geom_voronoiSetEdgeEnd(edge, rSite, lSite, vb); + d3_geom_voronoiCells[lSite.i].edges.push(new d3_geom_voronoiHalfEdge(edge, lSite, rSite)); + d3_geom_voronoiCells[rSite.i].edges.push(new d3_geom_voronoiHalfEdge(edge, rSite, lSite)); + return edge; + } + function d3_geom_voronoiCreateBorderEdge(lSite, va, vb) { + var edge = new d3_geom_voronoiEdge(lSite, null); + edge.a = va; + edge.b = vb; + d3_geom_voronoiEdges.push(edge); + return edge; + } + function d3_geom_voronoiSetEdgeEnd(edge, lSite, rSite, vertex) { + if (!edge.a && !edge.b) { + edge.a = vertex; + edge.l = lSite; + edge.r = rSite; + } else if (edge.l === rSite) { + edge.b = vertex; + } else { + edge.a = vertex; + } + } + function d3_geom_voronoiHalfEdge(edge, lSite, rSite) { + var va = edge.a, vb = edge.b; + this.edge = edge; + this.site = lSite; + this.angle = rSite ? Math.atan2(rSite.y - lSite.y, rSite.x - lSite.x) : edge.l === lSite ? Math.atan2(vb.x - va.x, va.y - vb.y) : Math.atan2(va.x - vb.x, vb.y - va.y); + } + d3_geom_voronoiHalfEdge.prototype = { + start: function() { + return this.edge.l === this.site ? this.edge.a : this.edge.b; + }, + end: function() { + return this.edge.l === this.site ? this.edge.b : this.edge.a; + } + }; + function d3_geom_voronoiRedBlackTree() { + this._ = null; + } + function d3_geom_voronoiRedBlackNode(node) { + node.U = node.C = node.L = node.R = node.P = node.N = null; + } + d3_geom_voronoiRedBlackTree.prototype = { + insert: function(after, node) { + var parent, grandpa, uncle; + if (after) { + node.P = after; + node.N = after.N; + if (after.N) after.N.P = node; + after.N = node; + if (after.R) { + after = after.R; + while (after.L) after = after.L; + after.L = node; + } else { + after.R = node; + } + parent = after; + } else if (this._) { + after = d3_geom_voronoiRedBlackFirst(this._); + node.P = null; + node.N = after; + after.P = after.L = node; + parent = after; + } else { + node.P = node.N = null; + this._ = node; + parent = null; + } + node.L = node.R = null; + node.U = parent; + node.C = true; + after = node; + while (parent && parent.C) { + grandpa = parent.U; + if (parent === grandpa.L) { + uncle = grandpa.R; + if (uncle && uncle.C) { + parent.C = uncle.C = false; + grandpa.C = true; + after = grandpa; + } else { + if (after === parent.R) { + d3_geom_voronoiRedBlackRotateLeft(this, parent); + after = parent; + parent = after.U; + } + parent.C = false; + grandpa.C = true; + d3_geom_voronoiRedBlackRotateRight(this, grandpa); + } + } else { + uncle = grandpa.L; + if (uncle && uncle.C) { + parent.C = uncle.C = false; + grandpa.C = true; + after = grandpa; + } else { + if (after === parent.L) { + d3_geom_voronoiRedBlackRotateRight(this, parent); + after = parent; + parent = after.U; + } + parent.C = false; + grandpa.C = true; + d3_geom_voronoiRedBlackRotateLeft(this, grandpa); + } + } + parent = after.U; + } + this._.C = false; + }, + remove: function(node) { + if (node.N) node.N.P = node.P; + if (node.P) node.P.N = node.N; + node.N = node.P = null; + var parent = node.U, sibling, left = node.L, right = node.R, next, red; + if (!left) next = right; else if (!right) next = left; else next = d3_geom_voronoiRedBlackFirst(right); + if (parent) { + if (parent.L === node) parent.L = next; else parent.R = next; + } else { + this._ = next; + } + if (left && right) { + red = next.C; + next.C = node.C; + next.L = left; + left.U = next; + if (next !== right) { + parent = next.U; + next.U = node.U; + node = next.R; + parent.L = node; + next.R = right; + right.U = next; + } else { + next.U = parent; + parent = next; + node = next.R; + } + } else { + red = node.C; + node = next; + } + if (node) node.U = parent; + if (red) return; + if (node && node.C) { + node.C = false; + return; + } + do { + if (node === this._) break; + if (node === parent.L) { + sibling = parent.R; + if (sibling.C) { + sibling.C = false; + parent.C = true; + d3_geom_voronoiRedBlackRotateLeft(this, parent); + sibling = parent.R; + } + if (sibling.L && sibling.L.C || sibling.R && sibling.R.C) { + if (!sibling.R || !sibling.R.C) { + sibling.L.C = false; + sibling.C = true; + d3_geom_voronoiRedBlackRotateRight(this, sibling); + sibling = parent.R; + } + sibling.C = parent.C; + parent.C = sibling.R.C = false; + d3_geom_voronoiRedBlackRotateLeft(this, parent); + node = this._; + break; + } + } else { + sibling = parent.L; + if (sibling.C) { + sibling.C = false; + parent.C = true; + d3_geom_voronoiRedBlackRotateRight(this, parent); + sibling = parent.L; + } + if (sibling.L && sibling.L.C || sibling.R && sibling.R.C) { + if (!sibling.L || !sibling.L.C) { + sibling.R.C = false; + sibling.C = true; + d3_geom_voronoiRedBlackRotateLeft(this, sibling); + sibling = parent.L; + } + sibling.C = parent.C; + parent.C = sibling.L.C = false; + d3_geom_voronoiRedBlackRotateRight(this, parent); + node = this._; + break; + } + } + sibling.C = true; + node = parent; + parent = parent.U; + } while (!node.C); + if (node) node.C = false; + } + }; + function d3_geom_voronoiRedBlackRotateLeft(tree, node) { + var p = node, q = node.R, parent = p.U; + if (parent) { + if (parent.L === p) parent.L = q; else parent.R = q; + } else { + tree._ = q; + } + q.U = parent; + p.U = q; + p.R = q.L; + if (p.R) p.R.U = p; + q.L = p; + } + function d3_geom_voronoiRedBlackRotateRight(tree, node) { + var p = node, q = node.L, parent = p.U; + if (parent) { + if (parent.L === p) parent.L = q; else parent.R = q; + } else { + tree._ = q; + } + q.U = parent; + p.U = q; + p.L = q.R; + if (p.L) p.L.U = p; + q.R = p; + } + function d3_geom_voronoiRedBlackFirst(node) { + while (node.L) node = node.L; + return node; + } + function d3_geom_voronoi(sites, bbox) { + var site = sites.sort(d3_geom_voronoiVertexOrder).pop(), x0, y0, circle; + d3_geom_voronoiEdges = []; + d3_geom_voronoiCells = new Array(sites.length); + d3_geom_voronoiBeaches = new d3_geom_voronoiRedBlackTree(); + d3_geom_voronoiCircles = new d3_geom_voronoiRedBlackTree(); + while (true) { + circle = d3_geom_voronoiFirstCircle; + if (site && (!circle || site.y < circle.y || site.y === circle.y && site.x < circle.x)) { + if (site.x !== x0 || site.y !== y0) { + d3_geom_voronoiCells[site.i] = new d3_geom_voronoiCell(site); + d3_geom_voronoiAddBeach(site); + x0 = site.x, y0 = site.y; + } + site = sites.pop(); + } else if (circle) { + d3_geom_voronoiRemoveBeach(circle.arc); + } else { + break; + } + } + if (bbox) d3_geom_voronoiClipEdges(bbox), d3_geom_voronoiCloseCells(bbox); + var diagram = { + cells: d3_geom_voronoiCells, + edges: d3_geom_voronoiEdges + }; + d3_geom_voronoiBeaches = d3_geom_voronoiCircles = d3_geom_voronoiEdges = d3_geom_voronoiCells = null; + return diagram; + } + function d3_geom_voronoiVertexOrder(a, b) { + return b.y - a.y || b.x - a.x; + } + d3.geom.voronoi = function(points) { + var x = d3_geom_pointX, y = d3_geom_pointY, fx = x, fy = y, clipExtent = d3_geom_voronoiClipExtent; + if (points) return voronoi(points); + function voronoi(data) { + var polygons = new Array(data.length), x0 = clipExtent[0][0], y0 = clipExtent[0][1], x1 = clipExtent[1][0], y1 = clipExtent[1][1]; + d3_geom_voronoi(sites(data), clipExtent).cells.forEach(function(cell, i) { + var edges = cell.edges, site = cell.site, polygon = polygons[i] = edges.length ? edges.map(function(e) { + var s = e.start(); + return [ s.x, s.y ]; + }) : site.x >= x0 && site.x <= x1 && site.y >= y0 && site.y <= y1 ? [ [ x0, y1 ], [ x1, y1 ], [ x1, y0 ], [ x0, y0 ] ] : []; + polygon.point = data[i]; + }); + return polygons; + } + function sites(data) { + return data.map(function(d, i) { + return { + x: Math.round(fx(d, i) / ε) * ε, + y: Math.round(fy(d, i) / ε) * ε, + i: i + }; + }); + } + voronoi.links = function(data) { + return d3_geom_voronoi(sites(data)).edges.filter(function(edge) { + return edge.l && edge.r; + }).map(function(edge) { + return { + source: data[edge.l.i], + target: data[edge.r.i] + }; + }); + }; + voronoi.triangles = function(data) { + var triangles = []; + d3_geom_voronoi(sites(data)).cells.forEach(function(cell, i) { + var site = cell.site, edges = cell.edges.sort(d3_geom_voronoiHalfEdgeOrder), j = -1, m = edges.length, e0, s0, e1 = edges[m - 1].edge, s1 = e1.l === site ? e1.r : e1.l; + while (++j < m) { + e0 = e1; + s0 = s1; + e1 = edges[j].edge; + s1 = e1.l === site ? e1.r : e1.l; + if (i < s0.i && i < s1.i && d3_geom_voronoiTriangleArea(site, s0, s1) < 0) { + triangles.push([ data[i], data[s0.i], data[s1.i] ]); + } + } + }); + return triangles; + }; + voronoi.x = function(_) { + return arguments.length ? (fx = d3_functor(x = _), voronoi) : x; + }; + voronoi.y = function(_) { + return arguments.length ? (fy = d3_functor(y = _), voronoi) : y; + }; + voronoi.clipExtent = function(_) { + if (!arguments.length) return clipExtent === d3_geom_voronoiClipExtent ? null : clipExtent; + clipExtent = _ == null ? d3_geom_voronoiClipExtent : _; + return voronoi; + }; + voronoi.size = function(_) { + if (!arguments.length) return clipExtent === d3_geom_voronoiClipExtent ? null : clipExtent && clipExtent[1]; + return voronoi.clipExtent(_ && [ [ 0, 0 ], _ ]); + }; + return voronoi; + }; + var d3_geom_voronoiClipExtent = [ [ -1e6, -1e6 ], [ 1e6, 1e6 ] ]; + function d3_geom_voronoiTriangleArea(a, b, c) { + return (a.x - c.x) * (b.y - a.y) - (a.x - b.x) * (c.y - a.y); + } + d3.geom.delaunay = function(vertices) { + return d3.geom.voronoi().triangles(vertices); + }; + d3.geom.quadtree = function(points, x1, y1, x2, y2) { + var x = d3_geom_pointX, y = d3_geom_pointY, compat; + if (compat = arguments.length) { + x = d3_geom_quadtreeCompatX; + y = d3_geom_quadtreeCompatY; + if (compat === 3) { + y2 = y1; + x2 = x1; + y1 = x1 = 0; + } + return quadtree(points); + } + function quadtree(data) { + var d, fx = d3_functor(x), fy = d3_functor(y), xs, ys, i, n, x1_, y1_, x2_, y2_; + if (x1 != null) { + x1_ = x1, y1_ = y1, x2_ = x2, y2_ = y2; + } else { + x2_ = y2_ = -(x1_ = y1_ = Infinity); + xs = [], ys = []; + n = data.length; + if (compat) for (i = 0; i < n; ++i) { + d = data[i]; + if (d.x < x1_) x1_ = d.x; + if (d.y < y1_) y1_ = d.y; + if (d.x > x2_) x2_ = d.x; + if (d.y > y2_) y2_ = d.y; + xs.push(d.x); + ys.push(d.y); + } else for (i = 0; i < n; ++i) { + var x_ = +fx(d = data[i], i), y_ = +fy(d, i); + if (x_ < x1_) x1_ = x_; + if (y_ < y1_) y1_ = y_; + if (x_ > x2_) x2_ = x_; + if (y_ > y2_) y2_ = y_; + xs.push(x_); + ys.push(y_); + } + } + var dx = x2_ - x1_, dy = y2_ - y1_; + if (dx > dy) y2_ = y1_ + dx; else x2_ = x1_ + dy; + function insert(n, d, x, y, x1, y1, x2, y2) { + if (isNaN(x) || isNaN(y)) return; + if (n.leaf) { + var nx = n.x, ny = n.y; + if (nx != null) { + if (abs(nx - x) + abs(ny - y) < .01) { + insertChild(n, d, x, y, x1, y1, x2, y2); + } else { + var nPoint = n.point; + n.x = n.y = n.point = null; + insertChild(n, nPoint, nx, ny, x1, y1, x2, y2); + insertChild(n, d, x, y, x1, y1, x2, y2); + } + } else { + n.x = x, n.y = y, n.point = d; + } + } else { + insertChild(n, d, x, y, x1, y1, x2, y2); + } + } + function insertChild(n, d, x, y, x1, y1, x2, y2) { + var xm = (x1 + x2) * .5, ym = (y1 + y2) * .5, right = x >= xm, below = y >= ym, i = below << 1 | right; + n.leaf = false; + n = n.nodes[i] || (n.nodes[i] = d3_geom_quadtreeNode()); + if (right) x1 = xm; else x2 = xm; + if (below) y1 = ym; else y2 = ym; + insert(n, d, x, y, x1, y1, x2, y2); + } + var root = d3_geom_quadtreeNode(); + root.add = function(d) { + insert(root, d, +fx(d, ++i), +fy(d, i), x1_, y1_, x2_, y2_); + }; + root.visit = function(f) { + d3_geom_quadtreeVisit(f, root, x1_, y1_, x2_, y2_); + }; + root.find = function(point) { + return d3_geom_quadtreeFind(root, point[0], point[1], x1_, y1_, x2_, y2_); + }; + i = -1; + if (x1 == null) { + while (++i < n) { + insert(root, data[i], xs[i], ys[i], x1_, y1_, x2_, y2_); + } + --i; + } else data.forEach(root.add); + xs = ys = data = d = null; + return root; + } + quadtree.x = function(_) { + return arguments.length ? (x = _, quadtree) : x; + }; + quadtree.y = function(_) { + return arguments.length ? (y = _, quadtree) : y; + }; + quadtree.extent = function(_) { + if (!arguments.length) return x1 == null ? null : [ [ x1, y1 ], [ x2, y2 ] ]; + if (_ == null) x1 = y1 = x2 = y2 = null; else x1 = +_[0][0], y1 = +_[0][1], x2 = +_[1][0], + y2 = +_[1][1]; + return quadtree; + }; + quadtree.size = function(_) { + if (!arguments.length) return x1 == null ? null : [ x2 - x1, y2 - y1 ]; + if (_ == null) x1 = y1 = x2 = y2 = null; else x1 = y1 = 0, x2 = +_[0], y2 = +_[1]; + return quadtree; + }; + return quadtree; + }; + function d3_geom_quadtreeCompatX(d) { + return d.x; + } + function d3_geom_quadtreeCompatY(d) { + return d.y; + } + function d3_geom_quadtreeNode() { + return { + leaf: true, + nodes: [], + point: null, + x: null, + y: null + }; + } + function d3_geom_quadtreeVisit(f, node, x1, y1, x2, y2) { + if (!f(node, x1, y1, x2, y2)) { + var sx = (x1 + x2) * .5, sy = (y1 + y2) * .5, children = node.nodes; + if (children[0]) d3_geom_quadtreeVisit(f, children[0], x1, y1, sx, sy); + if (children[1]) d3_geom_quadtreeVisit(f, children[1], sx, y1, x2, sy); + if (children[2]) d3_geom_quadtreeVisit(f, children[2], x1, sy, sx, y2); + if (children[3]) d3_geom_quadtreeVisit(f, children[3], sx, sy, x2, y2); + } + } + function d3_geom_quadtreeFind(root, x, y, x0, y0, x3, y3) { + var minDistance2 = Infinity, closestPoint; + (function find(node, x1, y1, x2, y2) { + if (x1 > x3 || y1 > y3 || x2 < x0 || y2 < y0) return; + if (point = node.point) { + var point, dx = x - node.x, dy = y - node.y, distance2 = dx * dx + dy * dy; + if (distance2 < minDistance2) { + var distance = Math.sqrt(minDistance2 = distance2); + x0 = x - distance, y0 = y - distance; + x3 = x + distance, y3 = y + distance; + closestPoint = point; + } + } + var children = node.nodes, xm = (x1 + x2) * .5, ym = (y1 + y2) * .5, right = x >= xm, below = y >= ym; + for (var i = below << 1 | right, j = i + 4; i < j; ++i) { + if (node = children[i & 3]) switch (i & 3) { + case 0: + find(node, x1, y1, xm, ym); + break; + + case 1: + find(node, xm, y1, x2, ym); + break; + + case 2: + find(node, x1, ym, xm, y2); + break; + + case 3: + find(node, xm, ym, x2, y2); + break; + } + } + })(root, x0, y0, x3, y3); + return closestPoint; + } + d3.interpolateRgb = d3_interpolateRgb; + function d3_interpolateRgb(a, b) { + a = d3.rgb(a); + b = d3.rgb(b); + var ar = a.r, ag = a.g, ab = a.b, br = b.r - ar, bg = b.g - ag, bb = b.b - ab; + return function(t) { + return "#" + d3_rgb_hex(Math.round(ar + br * t)) + d3_rgb_hex(Math.round(ag + bg * t)) + d3_rgb_hex(Math.round(ab + bb * t)); + }; + } + d3.interpolateObject = d3_interpolateObject; + function d3_interpolateObject(a, b) { + var i = {}, c = {}, k; + for (k in a) { + if (k in b) { + i[k] = d3_interpolate(a[k], b[k]); + } else { + c[k] = a[k]; + } + } + for (k in b) { + if (!(k in a)) { + c[k] = b[k]; + } + } + return function(t) { + for (k in i) c[k] = i[k](t); + return c; + }; + } + d3.interpolateNumber = d3_interpolateNumber; + function d3_interpolateNumber(a, b) { + a = +a, b = +b; + return function(t) { + return a * (1 - t) + b * t; + }; + } + d3.interpolateString = d3_interpolateString; + function d3_interpolateString(a, b) { + var bi = d3_interpolate_numberA.lastIndex = d3_interpolate_numberB.lastIndex = 0, am, bm, bs, i = -1, s = [], q = []; + a = a + "", b = b + ""; + while ((am = d3_interpolate_numberA.exec(a)) && (bm = d3_interpolate_numberB.exec(b))) { + if ((bs = bm.index) > bi) { + bs = b.slice(bi, bs); + if (s[i]) s[i] += bs; else s[++i] = bs; + } + if ((am = am[0]) === (bm = bm[0])) { + if (s[i]) s[i] += bm; else s[++i] = bm; + } else { + s[++i] = null; + q.push({ + i: i, + x: d3_interpolateNumber(am, bm) + }); + } + bi = d3_interpolate_numberB.lastIndex; + } + if (bi < b.length) { + bs = b.slice(bi); + if (s[i]) s[i] += bs; else s[++i] = bs; + } + return s.length < 2 ? q[0] ? (b = q[0].x, function(t) { + return b(t) + ""; + }) : function() { + return b; + } : (b = q.length, function(t) { + for (var i = 0, o; i < b; ++i) s[(o = q[i]).i] = o.x(t); + return s.join(""); + }); + } + var d3_interpolate_numberA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g, d3_interpolate_numberB = new RegExp(d3_interpolate_numberA.source, "g"); + d3.interpolate = d3_interpolate; + function d3_interpolate(a, b) { + var i = d3.interpolators.length, f; + while (--i >= 0 && !(f = d3.interpolators[i](a, b))) ; + return f; + } + d3.interpolators = [ function(a, b) { + var t = typeof b; + return (t === "string" ? d3_rgb_names.has(b.toLowerCase()) || /^(#|rgb\(|hsl\()/i.test(b) ? d3_interpolateRgb : d3_interpolateString : b instanceof d3_color ? d3_interpolateRgb : Array.isArray(b) ? d3_interpolateArray : t === "object" && isNaN(b) ? d3_interpolateObject : d3_interpolateNumber)(a, b); + } ]; + d3.interpolateArray = d3_interpolateArray; + function d3_interpolateArray(a, b) { + var x = [], c = [], na = a.length, nb = b.length, n0 = Math.min(a.length, b.length), i; + for (i = 0; i < n0; ++i) x.push(d3_interpolate(a[i], b[i])); + for (;i < na; ++i) c[i] = a[i]; + for (;i < nb; ++i) c[i] = b[i]; + return function(t) { + for (i = 0; i < n0; ++i) c[i] = x[i](t); + return c; + }; + } + var d3_ease_default = function() { + return d3_identity; + }; + var d3_ease = d3.map({ + linear: d3_ease_default, + poly: d3_ease_poly, + quad: function() { + return d3_ease_quad; + }, + cubic: function() { + return d3_ease_cubic; + }, + sin: function() { + return d3_ease_sin; + }, + exp: function() { + return d3_ease_exp; + }, + circle: function() { + return d3_ease_circle; + }, + elastic: d3_ease_elastic, + back: d3_ease_back, + bounce: function() { + return d3_ease_bounce; + } + }); + var d3_ease_mode = d3.map({ + "in": d3_identity, + out: d3_ease_reverse, + "in-out": d3_ease_reflect, + "out-in": function(f) { + return d3_ease_reflect(d3_ease_reverse(f)); + } + }); + d3.ease = function(name) { + var i = name.indexOf("-"), t = i >= 0 ? name.slice(0, i) : name, m = i >= 0 ? name.slice(i + 1) : "in"; + t = d3_ease.get(t) || d3_ease_default; + m = d3_ease_mode.get(m) || d3_identity; + return d3_ease_clamp(m(t.apply(null, d3_arraySlice.call(arguments, 1)))); + }; + function d3_ease_clamp(f) { + return function(t) { + return t <= 0 ? 0 : t >= 1 ? 1 : f(t); + }; + } + function d3_ease_reverse(f) { + return function(t) { + return 1 - f(1 - t); + }; + } + function d3_ease_reflect(f) { + return function(t) { + return .5 * (t < .5 ? f(2 * t) : 2 - f(2 - 2 * t)); + }; + } + function d3_ease_quad(t) { + return t * t; + } + function d3_ease_cubic(t) { + return t * t * t; + } + function d3_ease_cubicInOut(t) { + if (t <= 0) return 0; + if (t >= 1) return 1; + var t2 = t * t, t3 = t2 * t; + return 4 * (t < .5 ? t3 : 3 * (t - t2) + t3 - .75); + } + function d3_ease_poly(e) { + return function(t) { + return Math.pow(t, e); + }; + } + function d3_ease_sin(t) { + return 1 - Math.cos(t * halfÏ€); + } + function d3_ease_exp(t) { + return Math.pow(2, 10 * (t - 1)); + } + function d3_ease_circle(t) { + return 1 - Math.sqrt(1 - t * t); + } + function d3_ease_elastic(a, p) { + var s; + if (arguments.length < 2) p = .45; + if (arguments.length) s = p / Ï„ * Math.asin(1 / a); else a = 1, s = p / 4; + return function(t) { + return 1 + a * Math.pow(2, -10 * t) * Math.sin((t - s) * Ï„ / p); + }; + } + function d3_ease_back(s) { + if (!s) s = 1.70158; + return function(t) { + return t * t * ((s + 1) * t - s); + }; + } + function d3_ease_bounce(t) { + return t < 1 / 2.75 ? 7.5625 * t * t : t < 2 / 2.75 ? 7.5625 * (t -= 1.5 / 2.75) * t + .75 : t < 2.5 / 2.75 ? 7.5625 * (t -= 2.25 / 2.75) * t + .9375 : 7.5625 * (t -= 2.625 / 2.75) * t + .984375; + } + d3.interpolateHcl = d3_interpolateHcl; + function d3_interpolateHcl(a, b) { + a = d3.hcl(a); + b = d3.hcl(b); + var ah = a.h, ac = a.c, al = a.l, bh = b.h - ah, bc = b.c - ac, bl = b.l - al; + if (isNaN(bc)) bc = 0, ac = isNaN(ac) ? b.c : ac; + if (isNaN(bh)) bh = 0, ah = isNaN(ah) ? b.h : ah; else if (bh > 180) bh -= 360; else if (bh < -180) bh += 360; + return function(t) { + return d3_hcl_lab(ah + bh * t, ac + bc * t, al + bl * t) + ""; + }; + } + d3.interpolateHsl = d3_interpolateHsl; + function d3_interpolateHsl(a, b) { + a = d3.hsl(a); + b = d3.hsl(b); + var ah = a.h, as = a.s, al = a.l, bh = b.h - ah, bs = b.s - as, bl = b.l - al; + if (isNaN(bs)) bs = 0, as = isNaN(as) ? b.s : as; + if (isNaN(bh)) bh = 0, ah = isNaN(ah) ? b.h : ah; else if (bh > 180) bh -= 360; else if (bh < -180) bh += 360; + return function(t) { + return d3_hsl_rgb(ah + bh * t, as + bs * t, al + bl * t) + ""; + }; + } + d3.interpolateLab = d3_interpolateLab; + function d3_interpolateLab(a, b) { + a = d3.lab(a); + b = d3.lab(b); + var al = a.l, aa = a.a, ab = a.b, bl = b.l - al, ba = b.a - aa, bb = b.b - ab; + return function(t) { + return d3_lab_rgb(al + bl * t, aa + ba * t, ab + bb * t) + ""; + }; + } + d3.interpolateRound = d3_interpolateRound; + function d3_interpolateRound(a, b) { + b -= a; + return function(t) { + return Math.round(a + b * t); + }; + } + d3.transform = function(string) { + var g = d3_document.createElementNS(d3.ns.prefix.svg, "g"); + return (d3.transform = function(string) { + if (string != null) { + g.setAttribute("transform", string); + var t = g.transform.baseVal.consolidate(); + } + return new d3_transform(t ? t.matrix : d3_transformIdentity); + })(string); + }; + function d3_transform(m) { + var r0 = [ m.a, m.b ], r1 = [ m.c, m.d ], kx = d3_transformNormalize(r0), kz = d3_transformDot(r0, r1), ky = d3_transformNormalize(d3_transformCombine(r1, r0, -kz)) || 0; + if (r0[0] * r1[1] < r1[0] * r0[1]) { + r0[0] *= -1; + r0[1] *= -1; + kx *= -1; + kz *= -1; + } + this.rotate = (kx ? Math.atan2(r0[1], r0[0]) : Math.atan2(-r1[0], r1[1])) * d3_degrees; + this.translate = [ m.e, m.f ]; + this.scale = [ kx, ky ]; + this.skew = ky ? Math.atan2(kz, ky) * d3_degrees : 0; + } + d3_transform.prototype.toString = function() { + return "translate(" + this.translate + ")rotate(" + this.rotate + ")skewX(" + this.skew + ")scale(" + this.scale + ")"; + }; + function d3_transformDot(a, b) { + return a[0] * b[0] + a[1] * b[1]; + } + function d3_transformNormalize(a) { + var k = Math.sqrt(d3_transformDot(a, a)); + if (k) { + a[0] /= k; + a[1] /= k; + } + return k; + } + function d3_transformCombine(a, b, k) { + a[0] += k * b[0]; + a[1] += k * b[1]; + return a; + } + var d3_transformIdentity = { + a: 1, + b: 0, + c: 0, + d: 1, + e: 0, + f: 0 + }; + d3.interpolateTransform = d3_interpolateTransform; + function d3_interpolateTransformPop(s) { + return s.length ? s.pop() + "," : ""; + } + function d3_interpolateTranslate(ta, tb, s, q) { + if (ta[0] !== tb[0] || ta[1] !== tb[1]) { + var i = s.push("translate(", null, ",", null, ")"); + q.push({ + i: i - 4, + x: d3_interpolateNumber(ta[0], tb[0]) + }, { + i: i - 2, + x: d3_interpolateNumber(ta[1], tb[1]) + }); + } else if (tb[0] || tb[1]) { + s.push("translate(" + tb + ")"); + } + } + function d3_interpolateRotate(ra, rb, s, q) { + if (ra !== rb) { + if (ra - rb > 180) rb += 360; else if (rb - ra > 180) ra += 360; + q.push({ + i: s.push(d3_interpolateTransformPop(s) + "rotate(", null, ")") - 2, + x: d3_interpolateNumber(ra, rb) + }); + } else if (rb) { + s.push(d3_interpolateTransformPop(s) + "rotate(" + rb + ")"); + } + } + function d3_interpolateSkew(wa, wb, s, q) { + if (wa !== wb) { + q.push({ + i: s.push(d3_interpolateTransformPop(s) + "skewX(", null, ")") - 2, + x: d3_interpolateNumber(wa, wb) + }); + } else if (wb) { + s.push(d3_interpolateTransformPop(s) + "skewX(" + wb + ")"); + } + } + function d3_interpolateScale(ka, kb, s, q) { + if (ka[0] !== kb[0] || ka[1] !== kb[1]) { + var i = s.push(d3_interpolateTransformPop(s) + "scale(", null, ",", null, ")"); + q.push({ + i: i - 4, + x: d3_interpolateNumber(ka[0], kb[0]) + }, { + i: i - 2, + x: d3_interpolateNumber(ka[1], kb[1]) + }); + } else if (kb[0] !== 1 || kb[1] !== 1) { + s.push(d3_interpolateTransformPop(s) + "scale(" + kb + ")"); + } + } + function d3_interpolateTransform(a, b) { + var s = [], q = []; + a = d3.transform(a), b = d3.transform(b); + d3_interpolateTranslate(a.translate, b.translate, s, q); + d3_interpolateRotate(a.rotate, b.rotate, s, q); + d3_interpolateSkew(a.skew, b.skew, s, q); + d3_interpolateScale(a.scale, b.scale, s, q); + a = b = null; + return function(t) { + var i = -1, n = q.length, o; + while (++i < n) s[(o = q[i]).i] = o.x(t); + return s.join(""); + }; + } + function d3_uninterpolateNumber(a, b) { + b = (b -= a = +a) || 1 / b; + return function(x) { + return (x - a) / b; + }; + } + function d3_uninterpolateClamp(a, b) { + b = (b -= a = +a) || 1 / b; + return function(x) { + return Math.max(0, Math.min(1, (x - a) / b)); + }; + } + d3.layout = {}; + d3.layout.bundle = function() { + return function(links) { + var paths = [], i = -1, n = links.length; + while (++i < n) paths.push(d3_layout_bundlePath(links[i])); + return paths; + }; + }; + function d3_layout_bundlePath(link) { + var start = link.source, end = link.target, lca = d3_layout_bundleLeastCommonAncestor(start, end), points = [ start ]; + while (start !== lca) { + start = start.parent; + points.push(start); + } + var k = points.length; + while (end !== lca) { + points.splice(k, 0, end); + end = end.parent; + } + return points; + } + function d3_layout_bundleAncestors(node) { + var ancestors = [], parent = node.parent; + while (parent != null) { + ancestors.push(node); + node = parent; + parent = parent.parent; + } + ancestors.push(node); + return ancestors; + } + function d3_layout_bundleLeastCommonAncestor(a, b) { + if (a === b) return a; + var aNodes = d3_layout_bundleAncestors(a), bNodes = d3_layout_bundleAncestors(b), aNode = aNodes.pop(), bNode = bNodes.pop(), sharedNode = null; + while (aNode === bNode) { + sharedNode = aNode; + aNode = aNodes.pop(); + bNode = bNodes.pop(); + } + return sharedNode; + } + d3.layout.chord = function() { + var chord = {}, chords, groups, matrix, n, padding = 0, sortGroups, sortSubgroups, sortChords; + function relayout() { + var subgroups = {}, groupSums = [], groupIndex = d3.range(n), subgroupIndex = [], k, x, x0, i, j; + chords = []; + groups = []; + k = 0, i = -1; + while (++i < n) { + x = 0, j = -1; + while (++j < n) { + x += matrix[i][j]; + } + groupSums.push(x); + subgroupIndex.push(d3.range(n)); + k += x; + } + if (sortGroups) { + groupIndex.sort(function(a, b) { + return sortGroups(groupSums[a], groupSums[b]); + }); + } + if (sortSubgroups) { + subgroupIndex.forEach(function(d, i) { + d.sort(function(a, b) { + return sortSubgroups(matrix[i][a], matrix[i][b]); + }); + }); + } + k = (Ï„ - padding * n) / k; + x = 0, i = -1; + while (++i < n) { + x0 = x, j = -1; + while (++j < n) { + var di = groupIndex[i], dj = subgroupIndex[di][j], v = matrix[di][dj], a0 = x, a1 = x += v * k; + subgroups[di + "-" + dj] = { + index: di, + subindex: dj, + startAngle: a0, + endAngle: a1, + value: v + }; + } + groups[di] = { + index: di, + startAngle: x0, + endAngle: x, + value: groupSums[di] + }; + x += padding; + } + i = -1; + while (++i < n) { + j = i - 1; + while (++j < n) { + var source = subgroups[i + "-" + j], target = subgroups[j + "-" + i]; + if (source.value || target.value) { + chords.push(source.value < target.value ? { + source: target, + target: source + } : { + source: source, + target: target + }); + } + } + } + if (sortChords) resort(); + } + function resort() { + chords.sort(function(a, b) { + return sortChords((a.source.value + a.target.value) / 2, (b.source.value + b.target.value) / 2); + }); + } + chord.matrix = function(x) { + if (!arguments.length) return matrix; + n = (matrix = x) && matrix.length; + chords = groups = null; + return chord; + }; + chord.padding = function(x) { + if (!arguments.length) return padding; + padding = x; + chords = groups = null; + return chord; + }; + chord.sortGroups = function(x) { + if (!arguments.length) return sortGroups; + sortGroups = x; + chords = groups = null; + return chord; + }; + chord.sortSubgroups = function(x) { + if (!arguments.length) return sortSubgroups; + sortSubgroups = x; + chords = null; + return chord; + }; + chord.sortChords = function(x) { + if (!arguments.length) return sortChords; + sortChords = x; + if (chords) resort(); + return chord; + }; + chord.chords = function() { + if (!chords) relayout(); + return chords; + }; + chord.groups = function() { + if (!groups) relayout(); + return groups; + }; + return chord; + }; + d3.layout.force = function() { + var force = {}, event = d3.dispatch("start", "tick", "end"), timer, size = [ 1, 1 ], drag, alpha, friction = .9, linkDistance = d3_layout_forceLinkDistance, linkStrength = d3_layout_forceLinkStrength, charge = -30, chargeDistance2 = d3_layout_forceChargeDistance2, gravity = .1, theta2 = .64, nodes = [], links = [], distances, strengths, charges; + function repulse(node) { + return function(quad, x1, _, x2) { + if (quad.point !== node) { + var dx = quad.cx - node.x, dy = quad.cy - node.y, dw = x2 - x1, dn = dx * dx + dy * dy; + if (dw * dw / theta2 < dn) { + if (dn < chargeDistance2) { + var k = quad.charge / dn; + node.px -= dx * k; + node.py -= dy * k; + } + return true; + } + if (quad.point && dn && dn < chargeDistance2) { + var k = quad.pointCharge / dn; + node.px -= dx * k; + node.py -= dy * k; + } + } + return !quad.charge; + }; + } + force.tick = function() { + if ((alpha *= .99) < .005) { + timer = null; + event.end({ + type: "end", + alpha: alpha = 0 + }); + return true; + } + var n = nodes.length, m = links.length, q, i, o, s, t, l, k, x, y; + for (i = 0; i < m; ++i) { + o = links[i]; + s = o.source; + t = o.target; + x = t.x - s.x; + y = t.y - s.y; + if (l = x * x + y * y) { + l = alpha * strengths[i] * ((l = Math.sqrt(l)) - distances[i]) / l; + x *= l; + y *= l; + t.x -= x * (k = s.weight + t.weight ? s.weight / (s.weight + t.weight) : .5); + t.y -= y * k; + s.x += x * (k = 1 - k); + s.y += y * k; + } + } + if (k = alpha * gravity) { + x = size[0] / 2; + y = size[1] / 2; + i = -1; + if (k) while (++i < n) { + o = nodes[i]; + o.x += (x - o.x) * k; + o.y += (y - o.y) * k; + } + } + if (charge) { + d3_layout_forceAccumulate(q = d3.geom.quadtree(nodes), alpha, charges); + i = -1; + while (++i < n) { + if (!(o = nodes[i]).fixed) { + q.visit(repulse(o)); + } + } + } + i = -1; + while (++i < n) { + o = nodes[i]; + if (o.fixed) { + o.x = o.px; + o.y = o.py; + } else { + o.x -= (o.px - (o.px = o.x)) * friction; + o.y -= (o.py - (o.py = o.y)) * friction; + } + } + event.tick({ + type: "tick", + alpha: alpha + }); + }; + force.nodes = function(x) { + if (!arguments.length) return nodes; + nodes = x; + return force; + }; + force.links = function(x) { + if (!arguments.length) return links; + links = x; + return force; + }; + force.size = function(x) { + if (!arguments.length) return size; + size = x; + return force; + }; + force.linkDistance = function(x) { + if (!arguments.length) return linkDistance; + linkDistance = typeof x === "function" ? x : +x; + return force; + }; + force.distance = force.linkDistance; + force.linkStrength = function(x) { + if (!arguments.length) return linkStrength; + linkStrength = typeof x === "function" ? x : +x; + return force; + }; + force.friction = function(x) { + if (!arguments.length) return friction; + friction = +x; + return force; + }; + force.charge = function(x) { + if (!arguments.length) return charge; + charge = typeof x === "function" ? x : +x; + return force; + }; + force.chargeDistance = function(x) { + if (!arguments.length) return Math.sqrt(chargeDistance2); + chargeDistance2 = x * x; + return force; + }; + force.gravity = function(x) { + if (!arguments.length) return gravity; + gravity = +x; + return force; + }; + force.theta = function(x) { + if (!arguments.length) return Math.sqrt(theta2); + theta2 = x * x; + return force; + }; + force.alpha = function(x) { + if (!arguments.length) return alpha; + x = +x; + if (alpha) { + if (x > 0) { + alpha = x; + } else { + timer.c = null, timer.t = NaN, timer = null; + event.end({ + type: "end", + alpha: alpha = 0 + }); + } + } else if (x > 0) { + event.start({ + type: "start", + alpha: alpha = x + }); + timer = d3_timer(force.tick); + } + return force; + }; + force.start = function() { + var i, n = nodes.length, m = links.length, w = size[0], h = size[1], neighbors, o; + for (i = 0; i < n; ++i) { + (o = nodes[i]).index = i; + o.weight = 0; + } + for (i = 0; i < m; ++i) { + o = links[i]; + if (typeof o.source == "number") o.source = nodes[o.source]; + if (typeof o.target == "number") o.target = nodes[o.target]; + ++o.source.weight; + ++o.target.weight; + } + for (i = 0; i < n; ++i) { + o = nodes[i]; + if (isNaN(o.x)) o.x = position("x", w); + if (isNaN(o.y)) o.y = position("y", h); + if (isNaN(o.px)) o.px = o.x; + if (isNaN(o.py)) o.py = o.y; + } + distances = []; + if (typeof linkDistance === "function") for (i = 0; i < m; ++i) distances[i] = +linkDistance.call(this, links[i], i); else for (i = 0; i < m; ++i) distances[i] = linkDistance; + strengths = []; + if (typeof linkStrength === "function") for (i = 0; i < m; ++i) strengths[i] = +linkStrength.call(this, links[i], i); else for (i = 0; i < m; ++i) strengths[i] = linkStrength; + charges = []; + if (typeof charge === "function") for (i = 0; i < n; ++i) charges[i] = +charge.call(this, nodes[i], i); else for (i = 0; i < n; ++i) charges[i] = charge; + function position(dimension, size) { + if (!neighbors) { + neighbors = new Array(n); + for (j = 0; j < n; ++j) { + neighbors[j] = []; + } + for (j = 0; j < m; ++j) { + var o = links[j]; + neighbors[o.source.index].push(o.target); + neighbors[o.target.index].push(o.source); + } + } + var candidates = neighbors[i], j = -1, l = candidates.length, x; + while (++j < l) if (!isNaN(x = candidates[j][dimension])) return x; + return Math.random() * size; + } + return force.resume(); + }; + force.resume = function() { + return force.alpha(.1); + }; + force.stop = function() { + return force.alpha(0); + }; + force.drag = function() { + if (!drag) drag = d3.behavior.drag().origin(d3_identity).on("dragstart.force", d3_layout_forceDragstart).on("drag.force", dragmove).on("dragend.force", d3_layout_forceDragend); + if (!arguments.length) return drag; + this.on("mouseover.force", d3_layout_forceMouseover).on("mouseout.force", d3_layout_forceMouseout).call(drag); + }; + function dragmove(d) { + d.px = d3.event.x, d.py = d3.event.y; + force.resume(); + } + return d3.rebind(force, event, "on"); + }; + function d3_layout_forceDragstart(d) { + d.fixed |= 2; + } + function d3_layout_forceDragend(d) { + d.fixed &= ~6; + } + function d3_layout_forceMouseover(d) { + d.fixed |= 4; + d.px = d.x, d.py = d.y; + } + function d3_layout_forceMouseout(d) { + d.fixed &= ~4; + } + function d3_layout_forceAccumulate(quad, alpha, charges) { + var cx = 0, cy = 0; + quad.charge = 0; + if (!quad.leaf) { + var nodes = quad.nodes, n = nodes.length, i = -1, c; + while (++i < n) { + c = nodes[i]; + if (c == null) continue; + d3_layout_forceAccumulate(c, alpha, charges); + quad.charge += c.charge; + cx += c.charge * c.cx; + cy += c.charge * c.cy; + } + } + if (quad.point) { + if (!quad.leaf) { + quad.point.x += Math.random() - .5; + quad.point.y += Math.random() - .5; + } + var k = alpha * charges[quad.point.index]; + quad.charge += quad.pointCharge = k; + cx += k * quad.point.x; + cy += k * quad.point.y; + } + quad.cx = cx / quad.charge; + quad.cy = cy / quad.charge; + } + var d3_layout_forceLinkDistance = 20, d3_layout_forceLinkStrength = 1, d3_layout_forceChargeDistance2 = Infinity; + d3.layout.hierarchy = function() { + var sort = d3_layout_hierarchySort, children = d3_layout_hierarchyChildren, value = d3_layout_hierarchyValue; + function hierarchy(root) { + var stack = [ root ], nodes = [], node; + root.depth = 0; + while ((node = stack.pop()) != null) { + nodes.push(node); + if ((childs = children.call(hierarchy, node, node.depth)) && (n = childs.length)) { + var n, childs, child; + while (--n >= 0) { + stack.push(child = childs[n]); + child.parent = node; + child.depth = node.depth + 1; + } + if (value) node.value = 0; + node.children = childs; + } else { + if (value) node.value = +value.call(hierarchy, node, node.depth) || 0; + delete node.children; + } + } + d3_layout_hierarchyVisitAfter(root, function(node) { + var childs, parent; + if (sort && (childs = node.children)) childs.sort(sort); + if (value && (parent = node.parent)) parent.value += node.value; + }); + return nodes; + } + hierarchy.sort = function(x) { + if (!arguments.length) return sort; + sort = x; + return hierarchy; + }; + hierarchy.children = function(x) { + if (!arguments.length) return children; + children = x; + return hierarchy; + }; + hierarchy.value = function(x) { + if (!arguments.length) return value; + value = x; + return hierarchy; + }; + hierarchy.revalue = function(root) { + if (value) { + d3_layout_hierarchyVisitBefore(root, function(node) { + if (node.children) node.value = 0; + }); + d3_layout_hierarchyVisitAfter(root, function(node) { + var parent; + if (!node.children) node.value = +value.call(hierarchy, node, node.depth) || 0; + if (parent = node.parent) parent.value += node.value; + }); + } + return root; + }; + return hierarchy; + }; + function d3_layout_hierarchyRebind(object, hierarchy) { + d3.rebind(object, hierarchy, "sort", "children", "value"); + object.nodes = object; + object.links = d3_layout_hierarchyLinks; + return object; + } + function d3_layout_hierarchyVisitBefore(node, callback) { + var nodes = [ node ]; + while ((node = nodes.pop()) != null) { + callback(node); + if ((children = node.children) && (n = children.length)) { + var n, children; + while (--n >= 0) nodes.push(children[n]); + } + } + } + function d3_layout_hierarchyVisitAfter(node, callback) { + var nodes = [ node ], nodes2 = []; + while ((node = nodes.pop()) != null) { + nodes2.push(node); + if ((children = node.children) && (n = children.length)) { + var i = -1, n, children; + while (++i < n) nodes.push(children[i]); + } + } + while ((node = nodes2.pop()) != null) { + callback(node); + } + } + function d3_layout_hierarchyChildren(d) { + return d.children; + } + function d3_layout_hierarchyValue(d) { + return d.value; + } + function d3_layout_hierarchySort(a, b) { + return b.value - a.value; + } + function d3_layout_hierarchyLinks(nodes) { + return d3.merge(nodes.map(function(parent) { + return (parent.children || []).map(function(child) { + return { + source: parent, + target: child + }; + }); + })); + } + d3.layout.partition = function() { + var hierarchy = d3.layout.hierarchy(), size = [ 1, 1 ]; + function position(node, x, dx, dy) { + var children = node.children; + node.x = x; + node.y = node.depth * dy; + node.dx = dx; + node.dy = dy; + if (children && (n = children.length)) { + var i = -1, n, c, d; + dx = node.value ? dx / node.value : 0; + while (++i < n) { + position(c = children[i], x, d = c.value * dx, dy); + x += d; + } + } + } + function depth(node) { + var children = node.children, d = 0; + if (children && (n = children.length)) { + var i = -1, n; + while (++i < n) d = Math.max(d, depth(children[i])); + } + return 1 + d; + } + function partition(d, i) { + var nodes = hierarchy.call(this, d, i); + position(nodes[0], 0, size[0], size[1] / depth(nodes[0])); + return nodes; + } + partition.size = function(x) { + if (!arguments.length) return size; + size = x; + return partition; + }; + return d3_layout_hierarchyRebind(partition, hierarchy); + }; + d3.layout.pie = function() { + var value = Number, sort = d3_layout_pieSortByValue, startAngle = 0, endAngle = Ï„, padAngle = 0; + function pie(data) { + var n = data.length, values = data.map(function(d, i) { + return +value.call(pie, d, i); + }), a = +(typeof startAngle === "function" ? startAngle.apply(this, arguments) : startAngle), da = (typeof endAngle === "function" ? endAngle.apply(this, arguments) : endAngle) - a, p = Math.min(Math.abs(da) / n, +(typeof padAngle === "function" ? padAngle.apply(this, arguments) : padAngle)), pa = p * (da < 0 ? -1 : 1), sum = d3.sum(values), k = sum ? (da - n * pa) / sum : 0, index = d3.range(n), arcs = [], v; + if (sort != null) index.sort(sort === d3_layout_pieSortByValue ? function(i, j) { + return values[j] - values[i]; + } : function(i, j) { + return sort(data[i], data[j]); + }); + index.forEach(function(i) { + arcs[i] = { + data: data[i], + value: v = values[i], + startAngle: a, + endAngle: a += v * k + pa, + padAngle: p + }; + }); + return arcs; + } + pie.value = function(_) { + if (!arguments.length) return value; + value = _; + return pie; + }; + pie.sort = function(_) { + if (!arguments.length) return sort; + sort = _; + return pie; + }; + pie.startAngle = function(_) { + if (!arguments.length) return startAngle; + startAngle = _; + return pie; + }; + pie.endAngle = function(_) { + if (!arguments.length) return endAngle; + endAngle = _; + return pie; + }; + pie.padAngle = function(_) { + if (!arguments.length) return padAngle; + padAngle = _; + return pie; + }; + return pie; + }; + var d3_layout_pieSortByValue = {}; + d3.layout.stack = function() { + var values = d3_identity, order = d3_layout_stackOrderDefault, offset = d3_layout_stackOffsetZero, out = d3_layout_stackOut, x = d3_layout_stackX, y = d3_layout_stackY; + function stack(data, index) { + if (!(n = data.length)) return data; + var series = data.map(function(d, i) { + return values.call(stack, d, i); + }); + var points = series.map(function(d) { + return d.map(function(v, i) { + return [ x.call(stack, v, i), y.call(stack, v, i) ]; + }); + }); + var orders = order.call(stack, points, index); + series = d3.permute(series, orders); + points = d3.permute(points, orders); + var offsets = offset.call(stack, points, index); + var m = series[0].length, n, i, j, o; + for (j = 0; j < m; ++j) { + out.call(stack, series[0][j], o = offsets[j], points[0][j][1]); + for (i = 1; i < n; ++i) { + out.call(stack, series[i][j], o += points[i - 1][j][1], points[i][j][1]); + } + } + return data; + } + stack.values = function(x) { + if (!arguments.length) return values; + values = x; + return stack; + }; + stack.order = function(x) { + if (!arguments.length) return order; + order = typeof x === "function" ? x : d3_layout_stackOrders.get(x) || d3_layout_stackOrderDefault; + return stack; + }; + stack.offset = function(x) { + if (!arguments.length) return offset; + offset = typeof x === "function" ? x : d3_layout_stackOffsets.get(x) || d3_layout_stackOffsetZero; + return stack; + }; + stack.x = function(z) { + if (!arguments.length) return x; + x = z; + return stack; + }; + stack.y = function(z) { + if (!arguments.length) return y; + y = z; + return stack; + }; + stack.out = function(z) { + if (!arguments.length) return out; + out = z; + return stack; + }; + return stack; + }; + function d3_layout_stackX(d) { + return d.x; + } + function d3_layout_stackY(d) { + return d.y; + } + function d3_layout_stackOut(d, y0, y) { + d.y0 = y0; + d.y = y; + } + var d3_layout_stackOrders = d3.map({ + "inside-out": function(data) { + var n = data.length, i, j, max = data.map(d3_layout_stackMaxIndex), sums = data.map(d3_layout_stackReduceSum), index = d3.range(n).sort(function(a, b) { + return max[a] - max[b]; + }), top = 0, bottom = 0, tops = [], bottoms = []; + for (i = 0; i < n; ++i) { + j = index[i]; + if (top < bottom) { + top += sums[j]; + tops.push(j); + } else { + bottom += sums[j]; + bottoms.push(j); + } + } + return bottoms.reverse().concat(tops); + }, + reverse: function(data) { + return d3.range(data.length).reverse(); + }, + "default": d3_layout_stackOrderDefault + }); + var d3_layout_stackOffsets = d3.map({ + silhouette: function(data) { + var n = data.length, m = data[0].length, sums = [], max = 0, i, j, o, y0 = []; + for (j = 0; j < m; ++j) { + for (i = 0, o = 0; i < n; i++) o += data[i][j][1]; + if (o > max) max = o; + sums.push(o); + } + for (j = 0; j < m; ++j) { + y0[j] = (max - sums[j]) / 2; + } + return y0; + }, + wiggle: function(data) { + var n = data.length, x = data[0], m = x.length, i, j, k, s1, s2, s3, dx, o, o0, y0 = []; + y0[0] = o = o0 = 0; + for (j = 1; j < m; ++j) { + for (i = 0, s1 = 0; i < n; ++i) s1 += data[i][j][1]; + for (i = 0, s2 = 0, dx = x[j][0] - x[j - 1][0]; i < n; ++i) { + for (k = 0, s3 = (data[i][j][1] - data[i][j - 1][1]) / (2 * dx); k < i; ++k) { + s3 += (data[k][j][1] - data[k][j - 1][1]) / dx; + } + s2 += s3 * data[i][j][1]; + } + y0[j] = o -= s1 ? s2 / s1 * dx : 0; + if (o < o0) o0 = o; + } + for (j = 0; j < m; ++j) y0[j] -= o0; + return y0; + }, + expand: function(data) { + var n = data.length, m = data[0].length, k = 1 / n, i, j, o, y0 = []; + for (j = 0; j < m; ++j) { + for (i = 0, o = 0; i < n; i++) o += data[i][j][1]; + if (o) for (i = 0; i < n; i++) data[i][j][1] /= o; else for (i = 0; i < n; i++) data[i][j][1] = k; + } + for (j = 0; j < m; ++j) y0[j] = 0; + return y0; + }, + zero: d3_layout_stackOffsetZero + }); + function d3_layout_stackOrderDefault(data) { + return d3.range(data.length); + } + function d3_layout_stackOffsetZero(data) { + var j = -1, m = data[0].length, y0 = []; + while (++j < m) y0[j] = 0; + return y0; + } + function d3_layout_stackMaxIndex(array) { + var i = 1, j = 0, v = array[0][1], k, n = array.length; + for (;i < n; ++i) { + if ((k = array[i][1]) > v) { + j = i; + v = k; + } + } + return j; + } + function d3_layout_stackReduceSum(d) { + return d.reduce(d3_layout_stackSum, 0); + } + function d3_layout_stackSum(p, d) { + return p + d[1]; + } + d3.layout.histogram = function() { + var frequency = true, valuer = Number, ranger = d3_layout_histogramRange, binner = d3_layout_histogramBinSturges; + function histogram(data, i) { + var bins = [], values = data.map(valuer, this), range = ranger.call(this, values, i), thresholds = binner.call(this, range, values, i), bin, i = -1, n = values.length, m = thresholds.length - 1, k = frequency ? 1 : 1 / n, x; + while (++i < m) { + bin = bins[i] = []; + bin.dx = thresholds[i + 1] - (bin.x = thresholds[i]); + bin.y = 0; + } + if (m > 0) { + i = -1; + while (++i < n) { + x = values[i]; + if (x >= range[0] && x <= range[1]) { + bin = bins[d3.bisect(thresholds, x, 1, m) - 1]; + bin.y += k; + bin.push(data[i]); + } + } + } + return bins; + } + histogram.value = function(x) { + if (!arguments.length) return valuer; + valuer = x; + return histogram; + }; + histogram.range = function(x) { + if (!arguments.length) return ranger; + ranger = d3_functor(x); + return histogram; + }; + histogram.bins = function(x) { + if (!arguments.length) return binner; + binner = typeof x === "number" ? function(range) { + return d3_layout_histogramBinFixed(range, x); + } : d3_functor(x); + return histogram; + }; + histogram.frequency = function(x) { + if (!arguments.length) return frequency; + frequency = !!x; + return histogram; + }; + return histogram; + }; + function d3_layout_histogramBinSturges(range, values) { + return d3_layout_histogramBinFixed(range, Math.ceil(Math.log(values.length) / Math.LN2 + 1)); + } + function d3_layout_histogramBinFixed(range, n) { + var x = -1, b = +range[0], m = (range[1] - b) / n, f = []; + while (++x <= n) f[x] = m * x + b; + return f; + } + function d3_layout_histogramRange(values) { + return [ d3.min(values), d3.max(values) ]; + } + d3.layout.pack = function() { + var hierarchy = d3.layout.hierarchy().sort(d3_layout_packSort), padding = 0, size = [ 1, 1 ], radius; + function pack(d, i) { + var nodes = hierarchy.call(this, d, i), root = nodes[0], w = size[0], h = size[1], r = radius == null ? Math.sqrt : typeof radius === "function" ? radius : function() { + return radius; + }; + root.x = root.y = 0; + d3_layout_hierarchyVisitAfter(root, function(d) { + d.r = +r(d.value); + }); + d3_layout_hierarchyVisitAfter(root, d3_layout_packSiblings); + if (padding) { + var dr = padding * (radius ? 1 : Math.max(2 * root.r / w, 2 * root.r / h)) / 2; + d3_layout_hierarchyVisitAfter(root, function(d) { + d.r += dr; + }); + d3_layout_hierarchyVisitAfter(root, d3_layout_packSiblings); + d3_layout_hierarchyVisitAfter(root, function(d) { + d.r -= dr; + }); + } + d3_layout_packTransform(root, w / 2, h / 2, radius ? 1 : 1 / Math.max(2 * root.r / w, 2 * root.r / h)); + return nodes; + } + pack.size = function(_) { + if (!arguments.length) return size; + size = _; + return pack; + }; + pack.radius = function(_) { + if (!arguments.length) return radius; + radius = _ == null || typeof _ === "function" ? _ : +_; + return pack; + }; + pack.padding = function(_) { + if (!arguments.length) return padding; + padding = +_; + return pack; + }; + return d3_layout_hierarchyRebind(pack, hierarchy); + }; + function d3_layout_packSort(a, b) { + return a.value - b.value; + } + function d3_layout_packInsert(a, b) { + var c = a._pack_next; + a._pack_next = b; + b._pack_prev = a; + b._pack_next = c; + c._pack_prev = b; + } + function d3_layout_packSplice(a, b) { + a._pack_next = b; + b._pack_prev = a; + } + function d3_layout_packIntersects(a, b) { + var dx = b.x - a.x, dy = b.y - a.y, dr = a.r + b.r; + return .999 * dr * dr > dx * dx + dy * dy; + } + function d3_layout_packSiblings(node) { + if (!(nodes = node.children) || !(n = nodes.length)) return; + var nodes, xMin = Infinity, xMax = -Infinity, yMin = Infinity, yMax = -Infinity, a, b, c, i, j, k, n; + function bound(node) { + xMin = Math.min(node.x - node.r, xMin); + xMax = Math.max(node.x + node.r, xMax); + yMin = Math.min(node.y - node.r, yMin); + yMax = Math.max(node.y + node.r, yMax); + } + nodes.forEach(d3_layout_packLink); + a = nodes[0]; + a.x = -a.r; + a.y = 0; + bound(a); + if (n > 1) { + b = nodes[1]; + b.x = b.r; + b.y = 0; + bound(b); + if (n > 2) { + c = nodes[2]; + d3_layout_packPlace(a, b, c); + bound(c); + d3_layout_packInsert(a, c); + a._pack_prev = c; + d3_layout_packInsert(c, b); + b = a._pack_next; + for (i = 3; i < n; i++) { + d3_layout_packPlace(a, b, c = nodes[i]); + var isect = 0, s1 = 1, s2 = 1; + for (j = b._pack_next; j !== b; j = j._pack_next, s1++) { + if (d3_layout_packIntersects(j, c)) { + isect = 1; + break; + } + } + if (isect == 1) { + for (k = a._pack_prev; k !== j._pack_prev; k = k._pack_prev, s2++) { + if (d3_layout_packIntersects(k, c)) { + break; + } + } + } + if (isect) { + if (s1 < s2 || s1 == s2 && b.r < a.r) d3_layout_packSplice(a, b = j); else d3_layout_packSplice(a = k, b); + i--; + } else { + d3_layout_packInsert(a, c); + b = c; + bound(c); + } + } + } + } + var cx = (xMin + xMax) / 2, cy = (yMin + yMax) / 2, cr = 0; + for (i = 0; i < n; i++) { + c = nodes[i]; + c.x -= cx; + c.y -= cy; + cr = Math.max(cr, c.r + Math.sqrt(c.x * c.x + c.y * c.y)); + } + node.r = cr; + nodes.forEach(d3_layout_packUnlink); + } + function d3_layout_packLink(node) { + node._pack_next = node._pack_prev = node; + } + function d3_layout_packUnlink(node) { + delete node._pack_next; + delete node._pack_prev; + } + function d3_layout_packTransform(node, x, y, k) { + var children = node.children; + node.x = x += k * node.x; + node.y = y += k * node.y; + node.r *= k; + if (children) { + var i = -1, n = children.length; + while (++i < n) d3_layout_packTransform(children[i], x, y, k); + } + } + function d3_layout_packPlace(a, b, c) { + var db = a.r + c.r, dx = b.x - a.x, dy = b.y - a.y; + if (db && (dx || dy)) { + var da = b.r + c.r, dc = dx * dx + dy * dy; + da *= da; + db *= db; + var x = .5 + (db - da) / (2 * dc), y = Math.sqrt(Math.max(0, 2 * da * (db + dc) - (db -= dc) * db - da * da)) / (2 * dc); + c.x = a.x + x * dx + y * dy; + c.y = a.y + x * dy - y * dx; + } else { + c.x = a.x + db; + c.y = a.y; + } + } + d3.layout.tree = function() { + var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ], nodeSize = null; + function tree(d, i) { + var nodes = hierarchy.call(this, d, i), root0 = nodes[0], root1 = wrapTree(root0); + d3_layout_hierarchyVisitAfter(root1, firstWalk), root1.parent.m = -root1.z; + d3_layout_hierarchyVisitBefore(root1, secondWalk); + if (nodeSize) d3_layout_hierarchyVisitBefore(root0, sizeNode); else { + var left = root0, right = root0, bottom = root0; + d3_layout_hierarchyVisitBefore(root0, function(node) { + if (node.x < left.x) left = node; + if (node.x > right.x) right = node; + if (node.depth > bottom.depth) bottom = node; + }); + var tx = separation(left, right) / 2 - left.x, kx = size[0] / (right.x + separation(right, left) / 2 + tx), ky = size[1] / (bottom.depth || 1); + d3_layout_hierarchyVisitBefore(root0, function(node) { + node.x = (node.x + tx) * kx; + node.y = node.depth * ky; + }); + } + return nodes; + } + function wrapTree(root0) { + var root1 = { + A: null, + children: [ root0 ] + }, queue = [ root1 ], node1; + while ((node1 = queue.pop()) != null) { + for (var children = node1.children, child, i = 0, n = children.length; i < n; ++i) { + queue.push((children[i] = child = { + _: children[i], + parent: node1, + children: (child = children[i].children) && child.slice() || [], + A: null, + a: null, + z: 0, + m: 0, + c: 0, + s: 0, + t: null, + i: i + }).a = child); + } + } + return root1.children[0]; + } + function firstWalk(v) { + var children = v.children, siblings = v.parent.children, w = v.i ? siblings[v.i - 1] : null; + if (children.length) { + d3_layout_treeShift(v); + var midpoint = (children[0].z + children[children.length - 1].z) / 2; + if (w) { + v.z = w.z + separation(v._, w._); + v.m = v.z - midpoint; + } else { + v.z = midpoint; + } + } else if (w) { + v.z = w.z + separation(v._, w._); + } + v.parent.A = apportion(v, w, v.parent.A || siblings[0]); + } + function secondWalk(v) { + v._.x = v.z + v.parent.m; + v.m += v.parent.m; + } + function apportion(v, w, ancestor) { + if (w) { + var vip = v, vop = v, vim = w, vom = vip.parent.children[0], sip = vip.m, sop = vop.m, sim = vim.m, som = vom.m, shift; + while (vim = d3_layout_treeRight(vim), vip = d3_layout_treeLeft(vip), vim && vip) { + vom = d3_layout_treeLeft(vom); + vop = d3_layout_treeRight(vop); + vop.a = v; + shift = vim.z + sim - vip.z - sip + separation(vim._, vip._); + if (shift > 0) { + d3_layout_treeMove(d3_layout_treeAncestor(vim, v, ancestor), v, shift); + sip += shift; + sop += shift; + } + sim += vim.m; + sip += vip.m; + som += vom.m; + sop += vop.m; + } + if (vim && !d3_layout_treeRight(vop)) { + vop.t = vim; + vop.m += sim - sop; + } + if (vip && !d3_layout_treeLeft(vom)) { + vom.t = vip; + vom.m += sip - som; + ancestor = v; + } + } + return ancestor; + } + function sizeNode(node) { + node.x *= size[0]; + node.y = node.depth * size[1]; + } + tree.separation = function(x) { + if (!arguments.length) return separation; + separation = x; + return tree; + }; + tree.size = function(x) { + if (!arguments.length) return nodeSize ? null : size; + nodeSize = (size = x) == null ? sizeNode : null; + return tree; + }; + tree.nodeSize = function(x) { + if (!arguments.length) return nodeSize ? size : null; + nodeSize = (size = x) == null ? null : sizeNode; + return tree; + }; + return d3_layout_hierarchyRebind(tree, hierarchy); + }; + function d3_layout_treeSeparation(a, b) { + return a.parent == b.parent ? 1 : 2; + } + function d3_layout_treeLeft(v) { + var children = v.children; + return children.length ? children[0] : v.t; + } + function d3_layout_treeRight(v) { + var children = v.children, n; + return (n = children.length) ? children[n - 1] : v.t; + } + function d3_layout_treeMove(wm, wp, shift) { + var change = shift / (wp.i - wm.i); + wp.c -= change; + wp.s += shift; + wm.c += change; + wp.z += shift; + wp.m += shift; + } + function d3_layout_treeShift(v) { + var shift = 0, change = 0, children = v.children, i = children.length, w; + while (--i >= 0) { + w = children[i]; + w.z += shift; + w.m += shift; + shift += w.s + (change += w.c); + } + } + function d3_layout_treeAncestor(vim, v, ancestor) { + return vim.a.parent === v.parent ? vim.a : ancestor; + } + d3.layout.cluster = function() { + var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ], nodeSize = false; + function cluster(d, i) { + var nodes = hierarchy.call(this, d, i), root = nodes[0], previousNode, x = 0; + d3_layout_hierarchyVisitAfter(root, function(node) { + var children = node.children; + if (children && children.length) { + node.x = d3_layout_clusterX(children); + node.y = d3_layout_clusterY(children); + } else { + node.x = previousNode ? x += separation(node, previousNode) : 0; + node.y = 0; + previousNode = node; + } + }); + var left = d3_layout_clusterLeft(root), right = d3_layout_clusterRight(root), x0 = left.x - separation(left, right) / 2, x1 = right.x + separation(right, left) / 2; + d3_layout_hierarchyVisitAfter(root, nodeSize ? function(node) { + node.x = (node.x - root.x) * size[0]; + node.y = (root.y - node.y) * size[1]; + } : function(node) { + node.x = (node.x - x0) / (x1 - x0) * size[0]; + node.y = (1 - (root.y ? node.y / root.y : 1)) * size[1]; + }); + return nodes; + } + cluster.separation = function(x) { + if (!arguments.length) return separation; + separation = x; + return cluster; + }; + cluster.size = function(x) { + if (!arguments.length) return nodeSize ? null : size; + nodeSize = (size = x) == null; + return cluster; + }; + cluster.nodeSize = function(x) { + if (!arguments.length) return nodeSize ? size : null; + nodeSize = (size = x) != null; + return cluster; + }; + return d3_layout_hierarchyRebind(cluster, hierarchy); + }; + function d3_layout_clusterY(children) { + return 1 + d3.max(children, function(child) { + return child.y; + }); + } + function d3_layout_clusterX(children) { + return children.reduce(function(x, child) { + return x + child.x; + }, 0) / children.length; + } + function d3_layout_clusterLeft(node) { + var children = node.children; + return children && children.length ? d3_layout_clusterLeft(children[0]) : node; + } + function d3_layout_clusterRight(node) { + var children = node.children, n; + return children && (n = children.length) ? d3_layout_clusterRight(children[n - 1]) : node; + } + d3.layout.treemap = function() { + var hierarchy = d3.layout.hierarchy(), round = Math.round, size = [ 1, 1 ], padding = null, pad = d3_layout_treemapPadNull, sticky = false, stickies, mode = "squarify", ratio = .5 * (1 + Math.sqrt(5)); + function scale(children, k) { + var i = -1, n = children.length, child, area; + while (++i < n) { + area = (child = children[i]).value * (k < 0 ? 0 : k); + child.area = isNaN(area) || area <= 0 ? 0 : area; + } + } + function squarify(node) { + var children = node.children; + if (children && children.length) { + var rect = pad(node), row = [], remaining = children.slice(), child, best = Infinity, score, u = mode === "slice" ? rect.dx : mode === "dice" ? rect.dy : mode === "slice-dice" ? node.depth & 1 ? rect.dy : rect.dx : Math.min(rect.dx, rect.dy), n; + scale(remaining, rect.dx * rect.dy / node.value); + row.area = 0; + while ((n = remaining.length) > 0) { + row.push(child = remaining[n - 1]); + row.area += child.area; + if (mode !== "squarify" || (score = worst(row, u)) <= best) { + remaining.pop(); + best = score; + } else { + row.area -= row.pop().area; + position(row, u, rect, false); + u = Math.min(rect.dx, rect.dy); + row.length = row.area = 0; + best = Infinity; + } + } + if (row.length) { + position(row, u, rect, true); + row.length = row.area = 0; + } + children.forEach(squarify); + } + } + function stickify(node) { + var children = node.children; + if (children && children.length) { + var rect = pad(node), remaining = children.slice(), child, row = []; + scale(remaining, rect.dx * rect.dy / node.value); + row.area = 0; + while (child = remaining.pop()) { + row.push(child); + row.area += child.area; + if (child.z != null) { + position(row, child.z ? rect.dx : rect.dy, rect, !remaining.length); + row.length = row.area = 0; + } + } + children.forEach(stickify); + } + } + function worst(row, u) { + var s = row.area, r, rmax = 0, rmin = Infinity, i = -1, n = row.length; + while (++i < n) { + if (!(r = row[i].area)) continue; + if (r < rmin) rmin = r; + if (r > rmax) rmax = r; + } + s *= s; + u *= u; + return s ? Math.max(u * rmax * ratio / s, s / (u * rmin * ratio)) : Infinity; + } + function position(row, u, rect, flush) { + var i = -1, n = row.length, x = rect.x, y = rect.y, v = u ? round(row.area / u) : 0, o; + if (u == rect.dx) { + if (flush || v > rect.dy) v = rect.dy; + while (++i < n) { + o = row[i]; + o.x = x; + o.y = y; + o.dy = v; + x += o.dx = Math.min(rect.x + rect.dx - x, v ? round(o.area / v) : 0); + } + o.z = true; + o.dx += rect.x + rect.dx - x; + rect.y += v; + rect.dy -= v; + } else { + if (flush || v > rect.dx) v = rect.dx; + while (++i < n) { + o = row[i]; + o.x = x; + o.y = y; + o.dx = v; + y += o.dy = Math.min(rect.y + rect.dy - y, v ? round(o.area / v) : 0); + } + o.z = false; + o.dy += rect.y + rect.dy - y; + rect.x += v; + rect.dx -= v; + } + } + function treemap(d) { + var nodes = stickies || hierarchy(d), root = nodes[0]; + root.x = root.y = 0; + if (root.value) root.dx = size[0], root.dy = size[1]; else root.dx = root.dy = 0; + if (stickies) hierarchy.revalue(root); + scale([ root ], root.dx * root.dy / root.value); + (stickies ? stickify : squarify)(root); + if (sticky) stickies = nodes; + return nodes; + } + treemap.size = function(x) { + if (!arguments.length) return size; + size = x; + return treemap; + }; + treemap.padding = function(x) { + if (!arguments.length) return padding; + function padFunction(node) { + var p = x.call(treemap, node, node.depth); + return p == null ? d3_layout_treemapPadNull(node) : d3_layout_treemapPad(node, typeof p === "number" ? [ p, p, p, p ] : p); + } + function padConstant(node) { + return d3_layout_treemapPad(node, x); + } + var type; + pad = (padding = x) == null ? d3_layout_treemapPadNull : (type = typeof x) === "function" ? padFunction : type === "number" ? (x = [ x, x, x, x ], + padConstant) : padConstant; + return treemap; + }; + treemap.round = function(x) { + if (!arguments.length) return round != Number; + round = x ? Math.round : Number; + return treemap; + }; + treemap.sticky = function(x) { + if (!arguments.length) return sticky; + sticky = x; + stickies = null; + return treemap; + }; + treemap.ratio = function(x) { + if (!arguments.length) return ratio; + ratio = x; + return treemap; + }; + treemap.mode = function(x) { + if (!arguments.length) return mode; + mode = x + ""; + return treemap; + }; + return d3_layout_hierarchyRebind(treemap, hierarchy); + }; + function d3_layout_treemapPadNull(node) { + return { + x: node.x, + y: node.y, + dx: node.dx, + dy: node.dy + }; + } + function d3_layout_treemapPad(node, padding) { + var x = node.x + padding[3], y = node.y + padding[0], dx = node.dx - padding[1] - padding[3], dy = node.dy - padding[0] - padding[2]; + if (dx < 0) { + x += dx / 2; + dx = 0; + } + if (dy < 0) { + y += dy / 2; + dy = 0; + } + return { + x: x, + y: y, + dx: dx, + dy: dy + }; + } + d3.random = { + normal: function(µ, σ) { + var n = arguments.length; + if (n < 2) σ = 1; + if (n < 1) µ = 0; + return function() { + var x, y, r; + do { + x = Math.random() * 2 - 1; + y = Math.random() * 2 - 1; + r = x * x + y * y; + } while (!r || r > 1); + return µ + σ * x * Math.sqrt(-2 * Math.log(r) / r); + }; + }, + logNormal: function() { + var random = d3.random.normal.apply(d3, arguments); + return function() { + return Math.exp(random()); + }; + }, + bates: function(m) { + var random = d3.random.irwinHall(m); + return function() { + return random() / m; + }; + }, + irwinHall: function(m) { + return function() { + for (var s = 0, j = 0; j < m; j++) s += Math.random(); + return s; + }; + } + }; + d3.scale = {}; + function d3_scaleExtent(domain) { + var start = domain[0], stop = domain[domain.length - 1]; + return start < stop ? [ start, stop ] : [ stop, start ]; + } + function d3_scaleRange(scale) { + return scale.rangeExtent ? scale.rangeExtent() : d3_scaleExtent(scale.range()); + } + function d3_scale_bilinear(domain, range, uninterpolate, interpolate) { + var u = uninterpolate(domain[0], domain[1]), i = interpolate(range[0], range[1]); + return function(x) { + return i(u(x)); + }; + } + function d3_scale_nice(domain, nice) { + var i0 = 0, i1 = domain.length - 1, x0 = domain[i0], x1 = domain[i1], dx; + if (x1 < x0) { + dx = i0, i0 = i1, i1 = dx; + dx = x0, x0 = x1, x1 = dx; + } + domain[i0] = nice.floor(x0); + domain[i1] = nice.ceil(x1); + return domain; + } + function d3_scale_niceStep(step) { + return step ? { + floor: function(x) { + return Math.floor(x / step) * step; + }, + ceil: function(x) { + return Math.ceil(x / step) * step; + } + } : d3_scale_niceIdentity; + } + var d3_scale_niceIdentity = { + floor: d3_identity, + ceil: d3_identity + }; + function d3_scale_polylinear(domain, range, uninterpolate, interpolate) { + var u = [], i = [], j = 0, k = Math.min(domain.length, range.length) - 1; + if (domain[k] < domain[0]) { + domain = domain.slice().reverse(); + range = range.slice().reverse(); + } + while (++j <= k) { + u.push(uninterpolate(domain[j - 1], domain[j])); + i.push(interpolate(range[j - 1], range[j])); + } + return function(x) { + var j = d3.bisect(domain, x, 1, k) - 1; + return i[j](u[j](x)); + }; + } + d3.scale.linear = function() { + return d3_scale_linear([ 0, 1 ], [ 0, 1 ], d3_interpolate, false); + }; + function d3_scale_linear(domain, range, interpolate, clamp) { + var output, input; + function rescale() { + var linear = Math.min(domain.length, range.length) > 2 ? d3_scale_polylinear : d3_scale_bilinear, uninterpolate = clamp ? d3_uninterpolateClamp : d3_uninterpolateNumber; + output = linear(domain, range, uninterpolate, interpolate); + input = linear(range, domain, uninterpolate, d3_interpolate); + return scale; + } + function scale(x) { + return output(x); + } + scale.invert = function(y) { + return input(y); + }; + scale.domain = function(x) { + if (!arguments.length) return domain; + domain = x.map(Number); + return rescale(); + }; + scale.range = function(x) { + if (!arguments.length) return range; + range = x; + return rescale(); + }; + scale.rangeRound = function(x) { + return scale.range(x).interpolate(d3_interpolateRound); + }; + scale.clamp = function(x) { + if (!arguments.length) return clamp; + clamp = x; + return rescale(); + }; + scale.interpolate = function(x) { + if (!arguments.length) return interpolate; + interpolate = x; + return rescale(); + }; + scale.ticks = function(m) { + return d3_scale_linearTicks(domain, m); + }; + scale.tickFormat = function(m, format) { + return d3_scale_linearTickFormat(domain, m, format); + }; + scale.nice = function(m) { + d3_scale_linearNice(domain, m); + return rescale(); + }; + scale.copy = function() { + return d3_scale_linear(domain, range, interpolate, clamp); + }; + return rescale(); + } + function d3_scale_linearRebind(scale, linear) { + return d3.rebind(scale, linear, "range", "rangeRound", "interpolate", "clamp"); + } + function d3_scale_linearNice(domain, m) { + d3_scale_nice(domain, d3_scale_niceStep(d3_scale_linearTickRange(domain, m)[2])); + d3_scale_nice(domain, d3_scale_niceStep(d3_scale_linearTickRange(domain, m)[2])); + return domain; + } + function d3_scale_linearTickRange(domain, m) { + if (m == null) m = 10; + var extent = d3_scaleExtent(domain), span = extent[1] - extent[0], step = Math.pow(10, Math.floor(Math.log(span / m) / Math.LN10)), err = m / span * step; + if (err <= .15) step *= 10; else if (err <= .35) step *= 5; else if (err <= .75) step *= 2; + extent[0] = Math.ceil(extent[0] / step) * step; + extent[1] = Math.floor(extent[1] / step) * step + step * .5; + extent[2] = step; + return extent; + } + function d3_scale_linearTicks(domain, m) { + return d3.range.apply(d3, d3_scale_linearTickRange(domain, m)); + } + function d3_scale_linearTickFormat(domain, m, format) { + var range = d3_scale_linearTickRange(domain, m); + if (format) { + var match = d3_format_re.exec(format); + match.shift(); + if (match[8] === "s") { + var prefix = d3.formatPrefix(Math.max(abs(range[0]), abs(range[1]))); + if (!match[7]) match[7] = "." + d3_scale_linearPrecision(prefix.scale(range[2])); + match[8] = "f"; + format = d3.format(match.join("")); + return function(d) { + return format(prefix.scale(d)) + prefix.symbol; + }; + } + if (!match[7]) match[7] = "." + d3_scale_linearFormatPrecision(match[8], range); + format = match.join(""); + } else { + format = ",." + d3_scale_linearPrecision(range[2]) + "f"; + } + return d3.format(format); + } + var d3_scale_linearFormatSignificant = { + s: 1, + g: 1, + p: 1, + r: 1, + e: 1 + }; + function d3_scale_linearPrecision(value) { + return -Math.floor(Math.log(value) / Math.LN10 + .01); + } + function d3_scale_linearFormatPrecision(type, range) { + var p = d3_scale_linearPrecision(range[2]); + return type in d3_scale_linearFormatSignificant ? Math.abs(p - d3_scale_linearPrecision(Math.max(abs(range[0]), abs(range[1])))) + +(type !== "e") : p - (type === "%") * 2; + } + d3.scale.log = function() { + return d3_scale_log(d3.scale.linear().domain([ 0, 1 ]), 10, true, [ 1, 10 ]); + }; + function d3_scale_log(linear, base, positive, domain) { + function log(x) { + return (positive ? Math.log(x < 0 ? 0 : x) : -Math.log(x > 0 ? 0 : -x)) / Math.log(base); + } + function pow(x) { + return positive ? Math.pow(base, x) : -Math.pow(base, -x); + } + function scale(x) { + return linear(log(x)); + } + scale.invert = function(x) { + return pow(linear.invert(x)); + }; + scale.domain = function(x) { + if (!arguments.length) return domain; + positive = x[0] >= 0; + linear.domain((domain = x.map(Number)).map(log)); + return scale; + }; + scale.base = function(_) { + if (!arguments.length) return base; + base = +_; + linear.domain(domain.map(log)); + return scale; + }; + scale.nice = function() { + var niced = d3_scale_nice(domain.map(log), positive ? Math : d3_scale_logNiceNegative); + linear.domain(niced); + domain = niced.map(pow); + return scale; + }; + scale.ticks = function() { + var extent = d3_scaleExtent(domain), ticks = [], u = extent[0], v = extent[1], i = Math.floor(log(u)), j = Math.ceil(log(v)), n = base % 1 ? 2 : base; + if (isFinite(j - i)) { + if (positive) { + for (;i < j; i++) for (var k = 1; k < n; k++) ticks.push(pow(i) * k); + ticks.push(pow(i)); + } else { + ticks.push(pow(i)); + for (;i++ < j; ) for (var k = n - 1; k > 0; k--) ticks.push(pow(i) * k); + } + for (i = 0; ticks[i] < u; i++) {} + for (j = ticks.length; ticks[j - 1] > v; j--) {} + ticks = ticks.slice(i, j); + } + return ticks; + }; + scale.tickFormat = function(n, format) { + if (!arguments.length) return d3_scale_logFormat; + if (arguments.length < 2) format = d3_scale_logFormat; else if (typeof format !== "function") format = d3.format(format); + var k = Math.max(1, base * n / scale.ticks().length); + return function(d) { + var i = d / pow(Math.round(log(d))); + if (i * base < base - .5) i *= base; + return i <= k ? format(d) : ""; + }; + }; + scale.copy = function() { + return d3_scale_log(linear.copy(), base, positive, domain); + }; + return d3_scale_linearRebind(scale, linear); + } + var d3_scale_logFormat = d3.format(".0e"), d3_scale_logNiceNegative = { + floor: function(x) { + return -Math.ceil(-x); + }, + ceil: function(x) { + return -Math.floor(-x); + } + }; + d3.scale.pow = function() { + return d3_scale_pow(d3.scale.linear(), 1, [ 0, 1 ]); + }; + function d3_scale_pow(linear, exponent, domain) { + var powp = d3_scale_powPow(exponent), powb = d3_scale_powPow(1 / exponent); + function scale(x) { + return linear(powp(x)); + } + scale.invert = function(x) { + return powb(linear.invert(x)); + }; + scale.domain = function(x) { + if (!arguments.length) return domain; + linear.domain((domain = x.map(Number)).map(powp)); + return scale; + }; + scale.ticks = function(m) { + return d3_scale_linearTicks(domain, m); + }; + scale.tickFormat = function(m, format) { + return d3_scale_linearTickFormat(domain, m, format); + }; + scale.nice = function(m) { + return scale.domain(d3_scale_linearNice(domain, m)); + }; + scale.exponent = function(x) { + if (!arguments.length) return exponent; + powp = d3_scale_powPow(exponent = x); + powb = d3_scale_powPow(1 / exponent); + linear.domain(domain.map(powp)); + return scale; + }; + scale.copy = function() { + return d3_scale_pow(linear.copy(), exponent, domain); + }; + return d3_scale_linearRebind(scale, linear); + } + function d3_scale_powPow(e) { + return function(x) { + return x < 0 ? -Math.pow(-x, e) : Math.pow(x, e); + }; + } + d3.scale.sqrt = function() { + return d3.scale.pow().exponent(.5); + }; + d3.scale.ordinal = function() { + return d3_scale_ordinal([], { + t: "range", + a: [ [] ] + }); + }; + function d3_scale_ordinal(domain, ranger) { + var index, range, rangeBand; + function scale(x) { + return range[((index.get(x) || (ranger.t === "range" ? index.set(x, domain.push(x)) : NaN)) - 1) % range.length]; + } + function steps(start, step) { + return d3.range(domain.length).map(function(i) { + return start + step * i; + }); + } + scale.domain = function(x) { + if (!arguments.length) return domain; + domain = []; + index = new d3_Map(); + var i = -1, n = x.length, xi; + while (++i < n) if (!index.has(xi = x[i])) index.set(xi, domain.push(xi)); + return scale[ranger.t].apply(scale, ranger.a); + }; + scale.range = function(x) { + if (!arguments.length) return range; + range = x; + rangeBand = 0; + ranger = { + t: "range", + a: arguments + }; + return scale; + }; + scale.rangePoints = function(x, padding) { + if (arguments.length < 2) padding = 0; + var start = x[0], stop = x[1], step = domain.length < 2 ? (start = (start + stop) / 2, + 0) : (stop - start) / (domain.length - 1 + padding); + range = steps(start + step * padding / 2, step); + rangeBand = 0; + ranger = { + t: "rangePoints", + a: arguments + }; + return scale; + }; + scale.rangeRoundPoints = function(x, padding) { + if (arguments.length < 2) padding = 0; + var start = x[0], stop = x[1], step = domain.length < 2 ? (start = stop = Math.round((start + stop) / 2), + 0) : (stop - start) / (domain.length - 1 + padding) | 0; + range = steps(start + Math.round(step * padding / 2 + (stop - start - (domain.length - 1 + padding) * step) / 2), step); + rangeBand = 0; + ranger = { + t: "rangeRoundPoints", + a: arguments + }; + return scale; + }; + scale.rangeBands = function(x, padding, outerPadding) { + if (arguments.length < 2) padding = 0; + if (arguments.length < 3) outerPadding = padding; + var reverse = x[1] < x[0], start = x[reverse - 0], stop = x[1 - reverse], step = (stop - start) / (domain.length - padding + 2 * outerPadding); + range = steps(start + step * outerPadding, step); + if (reverse) range.reverse(); + rangeBand = step * (1 - padding); + ranger = { + t: "rangeBands", + a: arguments + }; + return scale; + }; + scale.rangeRoundBands = function(x, padding, outerPadding) { + if (arguments.length < 2) padding = 0; + if (arguments.length < 3) outerPadding = padding; + var reverse = x[1] < x[0], start = x[reverse - 0], stop = x[1 - reverse], step = Math.floor((stop - start) / (domain.length - padding + 2 * outerPadding)); + range = steps(start + Math.round((stop - start - (domain.length - padding) * step) / 2), step); + if (reverse) range.reverse(); + rangeBand = Math.round(step * (1 - padding)); + ranger = { + t: "rangeRoundBands", + a: arguments + }; + return scale; + }; + scale.rangeBand = function() { + return rangeBand; + }; + scale.rangeExtent = function() { + return d3_scaleExtent(ranger.a[0]); + }; + scale.copy = function() { + return d3_scale_ordinal(domain, ranger); + }; + return scale.domain(domain); + } + d3.scale.category10 = function() { + return d3.scale.ordinal().range(d3_category10); + }; + d3.scale.category20 = function() { + return d3.scale.ordinal().range(d3_category20); + }; + d3.scale.category20b = function() { + return d3.scale.ordinal().range(d3_category20b); + }; + d3.scale.category20c = function() { + return d3.scale.ordinal().range(d3_category20c); + }; + var d3_category10 = [ 2062260, 16744206, 2924588, 14034728, 9725885, 9197131, 14907330, 8355711, 12369186, 1556175 ].map(d3_rgbString); + var d3_category20 = [ 2062260, 11454440, 16744206, 16759672, 2924588, 10018698, 14034728, 16750742, 9725885, 12955861, 9197131, 12885140, 14907330, 16234194, 8355711, 13092807, 12369186, 14408589, 1556175, 10410725 ].map(d3_rgbString); + var d3_category20b = [ 3750777, 5395619, 7040719, 10264286, 6519097, 9216594, 11915115, 13556636, 9202993, 12426809, 15186514, 15190932, 8666169, 11356490, 14049643, 15177372, 8077683, 10834324, 13528509, 14589654 ].map(d3_rgbString); + var d3_category20c = [ 3244733, 7057110, 10406625, 13032431, 15095053, 16616764, 16625259, 16634018, 3253076, 7652470, 10607003, 13101504, 7695281, 10394312, 12369372, 14342891, 6513507, 9868950, 12434877, 14277081 ].map(d3_rgbString); + d3.scale.quantile = function() { + return d3_scale_quantile([], []); + }; + function d3_scale_quantile(domain, range) { + var thresholds; + function rescale() { + var k = 0, q = range.length; + thresholds = []; + while (++k < q) thresholds[k - 1] = d3.quantile(domain, k / q); + return scale; + } + function scale(x) { + if (!isNaN(x = +x)) return range[d3.bisect(thresholds, x)]; + } + scale.domain = function(x) { + if (!arguments.length) return domain; + domain = x.map(d3_number).filter(d3_numeric).sort(d3_ascending); + return rescale(); + }; + scale.range = function(x) { + if (!arguments.length) return range; + range = x; + return rescale(); + }; + scale.quantiles = function() { + return thresholds; + }; + scale.invertExtent = function(y) { + y = range.indexOf(y); + return y < 0 ? [ NaN, NaN ] : [ y > 0 ? thresholds[y - 1] : domain[0], y < thresholds.length ? thresholds[y] : domain[domain.length - 1] ]; + }; + scale.copy = function() { + return d3_scale_quantile(domain, range); + }; + return rescale(); + } + d3.scale.quantize = function() { + return d3_scale_quantize(0, 1, [ 0, 1 ]); + }; + function d3_scale_quantize(x0, x1, range) { + var kx, i; + function scale(x) { + return range[Math.max(0, Math.min(i, Math.floor(kx * (x - x0))))]; + } + function rescale() { + kx = range.length / (x1 - x0); + i = range.length - 1; + return scale; + } + scale.domain = function(x) { + if (!arguments.length) return [ x0, x1 ]; + x0 = +x[0]; + x1 = +x[x.length - 1]; + return rescale(); + }; + scale.range = function(x) { + if (!arguments.length) return range; + range = x; + return rescale(); + }; + scale.invertExtent = function(y) { + y = range.indexOf(y); + y = y < 0 ? NaN : y / kx + x0; + return [ y, y + 1 / kx ]; + }; + scale.copy = function() { + return d3_scale_quantize(x0, x1, range); + }; + return rescale(); + } + d3.scale.threshold = function() { + return d3_scale_threshold([ .5 ], [ 0, 1 ]); + }; + function d3_scale_threshold(domain, range) { + function scale(x) { + if (x <= x) return range[d3.bisect(domain, x)]; + } + scale.domain = function(_) { + if (!arguments.length) return domain; + domain = _; + return scale; + }; + scale.range = function(_) { + if (!arguments.length) return range; + range = _; + return scale; + }; + scale.invertExtent = function(y) { + y = range.indexOf(y); + return [ domain[y - 1], domain[y] ]; + }; + scale.copy = function() { + return d3_scale_threshold(domain, range); + }; + return scale; + } + d3.scale.identity = function() { + return d3_scale_identity([ 0, 1 ]); + }; + function d3_scale_identity(domain) { + function identity(x) { + return +x; + } + identity.invert = identity; + identity.domain = identity.range = function(x) { + if (!arguments.length) return domain; + domain = x.map(identity); + return identity; + }; + identity.ticks = function(m) { + return d3_scale_linearTicks(domain, m); + }; + identity.tickFormat = function(m, format) { + return d3_scale_linearTickFormat(domain, m, format); + }; + identity.copy = function() { + return d3_scale_identity(domain); + }; + return identity; + } + d3.svg = {}; + function d3_zero() { + return 0; + } + d3.svg.arc = function() { + var innerRadius = d3_svg_arcInnerRadius, outerRadius = d3_svg_arcOuterRadius, cornerRadius = d3_zero, padRadius = d3_svg_arcAuto, startAngle = d3_svg_arcStartAngle, endAngle = d3_svg_arcEndAngle, padAngle = d3_svg_arcPadAngle; + function arc() { + var r0 = Math.max(0, +innerRadius.apply(this, arguments)), r1 = Math.max(0, +outerRadius.apply(this, arguments)), a0 = startAngle.apply(this, arguments) - halfÏ€, a1 = endAngle.apply(this, arguments) - halfÏ€, da = Math.abs(a1 - a0), cw = a0 > a1 ? 0 : 1; + if (r1 < r0) rc = r1, r1 = r0, r0 = rc; + if (da >= τε) return circleSegment(r1, cw) + (r0 ? circleSegment(r0, 1 - cw) : "") + "Z"; + var rc, cr, rp, ap, p0 = 0, p1 = 0, x0, y0, x1, y1, x2, y2, x3, y3, path = []; + if (ap = (+padAngle.apply(this, arguments) || 0) / 2) { + rp = padRadius === d3_svg_arcAuto ? Math.sqrt(r0 * r0 + r1 * r1) : +padRadius.apply(this, arguments); + if (!cw) p1 *= -1; + if (r1) p1 = d3_asin(rp / r1 * Math.sin(ap)); + if (r0) p0 = d3_asin(rp / r0 * Math.sin(ap)); + } + if (r1) { + x0 = r1 * Math.cos(a0 + p1); + y0 = r1 * Math.sin(a0 + p1); + x1 = r1 * Math.cos(a1 - p1); + y1 = r1 * Math.sin(a1 - p1); + var l1 = Math.abs(a1 - a0 - 2 * p1) <= Ï€ ? 0 : 1; + if (p1 && d3_svg_arcSweep(x0, y0, x1, y1) === cw ^ l1) { + var h1 = (a0 + a1) / 2; + x0 = r1 * Math.cos(h1); + y0 = r1 * Math.sin(h1); + x1 = y1 = null; + } + } else { + x0 = y0 = 0; + } + if (r0) { + x2 = r0 * Math.cos(a1 - p0); + y2 = r0 * Math.sin(a1 - p0); + x3 = r0 * Math.cos(a0 + p0); + y3 = r0 * Math.sin(a0 + p0); + var l0 = Math.abs(a0 - a1 + 2 * p0) <= Ï€ ? 0 : 1; + if (p0 && d3_svg_arcSweep(x2, y2, x3, y3) === 1 - cw ^ l0) { + var h0 = (a0 + a1) / 2; + x2 = r0 * Math.cos(h0); + y2 = r0 * Math.sin(h0); + x3 = y3 = null; + } + } else { + x2 = y2 = 0; + } + if (da > ε && (rc = Math.min(Math.abs(r1 - r0) / 2, +cornerRadius.apply(this, arguments))) > .001) { + cr = r0 < r1 ^ cw ? 0 : 1; + var rc1 = rc, rc0 = rc; + if (da < Ï€) { + var oc = x3 == null ? [ x2, y2 ] : x1 == null ? [ x0, y0 ] : d3_geom_polygonIntersect([ x0, y0 ], [ x3, y3 ], [ x1, y1 ], [ x2, y2 ]), ax = x0 - oc[0], ay = y0 - oc[1], bx = x1 - oc[0], by = y1 - oc[1], kc = 1 / Math.sin(Math.acos((ax * bx + ay * by) / (Math.sqrt(ax * ax + ay * ay) * Math.sqrt(bx * bx + by * by))) / 2), lc = Math.sqrt(oc[0] * oc[0] + oc[1] * oc[1]); + rc0 = Math.min(rc, (r0 - lc) / (kc - 1)); + rc1 = Math.min(rc, (r1 - lc) / (kc + 1)); + } + if (x1 != null) { + var t30 = d3_svg_arcCornerTangents(x3 == null ? [ x2, y2 ] : [ x3, y3 ], [ x0, y0 ], r1, rc1, cw), t12 = d3_svg_arcCornerTangents([ x1, y1 ], [ x2, y2 ], r1, rc1, cw); + if (rc === rc1) { + path.push("M", t30[0], "A", rc1, ",", rc1, " 0 0,", cr, " ", t30[1], "A", r1, ",", r1, " 0 ", 1 - cw ^ d3_svg_arcSweep(t30[1][0], t30[1][1], t12[1][0], t12[1][1]), ",", cw, " ", t12[1], "A", rc1, ",", rc1, " 0 0,", cr, " ", t12[0]); + } else { + path.push("M", t30[0], "A", rc1, ",", rc1, " 0 1,", cr, " ", t12[0]); + } + } else { + path.push("M", x0, ",", y0); + } + if (x3 != null) { + var t03 = d3_svg_arcCornerTangents([ x0, y0 ], [ x3, y3 ], r0, -rc0, cw), t21 = d3_svg_arcCornerTangents([ x2, y2 ], x1 == null ? [ x0, y0 ] : [ x1, y1 ], r0, -rc0, cw); + if (rc === rc0) { + path.push("L", t21[0], "A", rc0, ",", rc0, " 0 0,", cr, " ", t21[1], "A", r0, ",", r0, " 0 ", cw ^ d3_svg_arcSweep(t21[1][0], t21[1][1], t03[1][0], t03[1][1]), ",", 1 - cw, " ", t03[1], "A", rc0, ",", rc0, " 0 0,", cr, " ", t03[0]); + } else { + path.push("L", t21[0], "A", rc0, ",", rc0, " 0 0,", cr, " ", t03[0]); + } + } else { + path.push("L", x2, ",", y2); + } + } else { + path.push("M", x0, ",", y0); + if (x1 != null) path.push("A", r1, ",", r1, " 0 ", l1, ",", cw, " ", x1, ",", y1); + path.push("L", x2, ",", y2); + if (x3 != null) path.push("A", r0, ",", r0, " 0 ", l0, ",", 1 - cw, " ", x3, ",", y3); + } + path.push("Z"); + return path.join(""); + } + function circleSegment(r1, cw) { + return "M0," + r1 + "A" + r1 + "," + r1 + " 0 1," + cw + " 0," + -r1 + "A" + r1 + "," + r1 + " 0 1," + cw + " 0," + r1; + } + arc.innerRadius = function(v) { + if (!arguments.length) return innerRadius; + innerRadius = d3_functor(v); + return arc; + }; + arc.outerRadius = function(v) { + if (!arguments.length) return outerRadius; + outerRadius = d3_functor(v); + return arc; + }; + arc.cornerRadius = function(v) { + if (!arguments.length) return cornerRadius; + cornerRadius = d3_functor(v); + return arc; + }; + arc.padRadius = function(v) { + if (!arguments.length) return padRadius; + padRadius = v == d3_svg_arcAuto ? d3_svg_arcAuto : d3_functor(v); + return arc; + }; + arc.startAngle = function(v) { + if (!arguments.length) return startAngle; + startAngle = d3_functor(v); + return arc; + }; + arc.endAngle = function(v) { + if (!arguments.length) return endAngle; + endAngle = d3_functor(v); + return arc; + }; + arc.padAngle = function(v) { + if (!arguments.length) return padAngle; + padAngle = d3_functor(v); + return arc; + }; + arc.centroid = function() { + var r = (+innerRadius.apply(this, arguments) + +outerRadius.apply(this, arguments)) / 2, a = (+startAngle.apply(this, arguments) + +endAngle.apply(this, arguments)) / 2 - halfÏ€; + return [ Math.cos(a) * r, Math.sin(a) * r ]; + }; + return arc; + }; + var d3_svg_arcAuto = "auto"; + function d3_svg_arcInnerRadius(d) { + return d.innerRadius; + } + function d3_svg_arcOuterRadius(d) { + return d.outerRadius; + } + function d3_svg_arcStartAngle(d) { + return d.startAngle; + } + function d3_svg_arcEndAngle(d) { + return d.endAngle; + } + function d3_svg_arcPadAngle(d) { + return d && d.padAngle; + } + function d3_svg_arcSweep(x0, y0, x1, y1) { + return (x0 - x1) * y0 - (y0 - y1) * x0 > 0 ? 0 : 1; + } + function d3_svg_arcCornerTangents(p0, p1, r1, rc, cw) { + var x01 = p0[0] - p1[0], y01 = p0[1] - p1[1], lo = (cw ? rc : -rc) / Math.sqrt(x01 * x01 + y01 * y01), ox = lo * y01, oy = -lo * x01, x1 = p0[0] + ox, y1 = p0[1] + oy, x2 = p1[0] + ox, y2 = p1[1] + oy, x3 = (x1 + x2) / 2, y3 = (y1 + y2) / 2, dx = x2 - x1, dy = y2 - y1, d2 = dx * dx + dy * dy, r = r1 - rc, D = x1 * y2 - x2 * y1, d = (dy < 0 ? -1 : 1) * Math.sqrt(Math.max(0, r * r * d2 - D * D)), cx0 = (D * dy - dx * d) / d2, cy0 = (-D * dx - dy * d) / d2, cx1 = (D * dy + dx * d) / d2, cy1 = (-D * dx + dy * d) / d2, dx0 = cx0 - x3, dy0 = cy0 - y3, dx1 = cx1 - x3, dy1 = cy1 - y3; + if (dx0 * dx0 + dy0 * dy0 > dx1 * dx1 + dy1 * dy1) cx0 = cx1, cy0 = cy1; + return [ [ cx0 - ox, cy0 - oy ], [ cx0 * r1 / r, cy0 * r1 / r ] ]; + } + function d3_svg_line(projection) { + var x = d3_geom_pointX, y = d3_geom_pointY, defined = d3_true, interpolate = d3_svg_lineLinear, interpolateKey = interpolate.key, tension = .7; + function line(data) { + var segments = [], points = [], i = -1, n = data.length, d, fx = d3_functor(x), fy = d3_functor(y); + function segment() { + segments.push("M", interpolate(projection(points), tension)); + } + while (++i < n) { + if (defined.call(this, d = data[i], i)) { + points.push([ +fx.call(this, d, i), +fy.call(this, d, i) ]); + } else if (points.length) { + segment(); + points = []; + } + } + if (points.length) segment(); + return segments.length ? segments.join("") : null; + } + line.x = function(_) { + if (!arguments.length) return x; + x = _; + return line; + }; + line.y = function(_) { + if (!arguments.length) return y; + y = _; + return line; + }; + line.defined = function(_) { + if (!arguments.length) return defined; + defined = _; + return line; + }; + line.interpolate = function(_) { + if (!arguments.length) return interpolateKey; + if (typeof _ === "function") interpolateKey = interpolate = _; else interpolateKey = (interpolate = d3_svg_lineInterpolators.get(_) || d3_svg_lineLinear).key; + return line; + }; + line.tension = function(_) { + if (!arguments.length) return tension; + tension = _; + return line; + }; + return line; + } + d3.svg.line = function() { + return d3_svg_line(d3_identity); + }; + var d3_svg_lineInterpolators = d3.map({ + linear: d3_svg_lineLinear, + "linear-closed": d3_svg_lineLinearClosed, + step: d3_svg_lineStep, + "step-before": d3_svg_lineStepBefore, + "step-after": d3_svg_lineStepAfter, + basis: d3_svg_lineBasis, + "basis-open": d3_svg_lineBasisOpen, + "basis-closed": d3_svg_lineBasisClosed, + bundle: d3_svg_lineBundle, + cardinal: d3_svg_lineCardinal, + "cardinal-open": d3_svg_lineCardinalOpen, + "cardinal-closed": d3_svg_lineCardinalClosed, + monotone: d3_svg_lineMonotone + }); + d3_svg_lineInterpolators.forEach(function(key, value) { + value.key = key; + value.closed = /-closed$/.test(key); + }); + function d3_svg_lineLinear(points) { + return points.length > 1 ? points.join("L") : points + "Z"; + } + function d3_svg_lineLinearClosed(points) { + return points.join("L") + "Z"; + } + function d3_svg_lineStep(points) { + var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ]; + while (++i < n) path.push("H", (p[0] + (p = points[i])[0]) / 2, "V", p[1]); + if (n > 1) path.push("H", p[0]); + return path.join(""); + } + function d3_svg_lineStepBefore(points) { + var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ]; + while (++i < n) path.push("V", (p = points[i])[1], "H", p[0]); + return path.join(""); + } + function d3_svg_lineStepAfter(points) { + var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ]; + while (++i < n) path.push("H", (p = points[i])[0], "V", p[1]); + return path.join(""); + } + function d3_svg_lineCardinalOpen(points, tension) { + return points.length < 4 ? d3_svg_lineLinear(points) : points[1] + d3_svg_lineHermite(points.slice(1, -1), d3_svg_lineCardinalTangents(points, tension)); + } + function d3_svg_lineCardinalClosed(points, tension) { + return points.length < 3 ? d3_svg_lineLinearClosed(points) : points[0] + d3_svg_lineHermite((points.push(points[0]), + points), d3_svg_lineCardinalTangents([ points[points.length - 2] ].concat(points, [ points[1] ]), tension)); + } + function d3_svg_lineCardinal(points, tension) { + return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineCardinalTangents(points, tension)); + } + function d3_svg_lineHermite(points, tangents) { + if (tangents.length < 1 || points.length != tangents.length && points.length != tangents.length + 2) { + return d3_svg_lineLinear(points); + } + var quad = points.length != tangents.length, path = "", p0 = points[0], p = points[1], t0 = tangents[0], t = t0, pi = 1; + if (quad) { + path += "Q" + (p[0] - t0[0] * 2 / 3) + "," + (p[1] - t0[1] * 2 / 3) + "," + p[0] + "," + p[1]; + p0 = points[1]; + pi = 2; + } + if (tangents.length > 1) { + t = tangents[1]; + p = points[pi]; + pi++; + path += "C" + (p0[0] + t0[0]) + "," + (p0[1] + t0[1]) + "," + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1]; + for (var i = 2; i < tangents.length; i++, pi++) { + p = points[pi]; + t = tangents[i]; + path += "S" + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1]; + } + } + if (quad) { + var lp = points[pi]; + path += "Q" + (p[0] + t[0] * 2 / 3) + "," + (p[1] + t[1] * 2 / 3) + "," + lp[0] + "," + lp[1]; + } + return path; + } + function d3_svg_lineCardinalTangents(points, tension) { + var tangents = [], a = (1 - tension) / 2, p0, p1 = points[0], p2 = points[1], i = 1, n = points.length; + while (++i < n) { + p0 = p1; + p1 = p2; + p2 = points[i]; + tangents.push([ a * (p2[0] - p0[0]), a * (p2[1] - p0[1]) ]); + } + return tangents; + } + function d3_svg_lineBasis(points) { + if (points.length < 3) return d3_svg_lineLinear(points); + var i = 1, n = points.length, pi = points[0], x0 = pi[0], y0 = pi[1], px = [ x0, x0, x0, (pi = points[1])[0] ], py = [ y0, y0, y0, pi[1] ], path = [ x0, ",", y0, "L", d3_svg_lineDot4(d3_svg_lineBasisBezier3, px), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, py) ]; + points.push(points[n - 1]); + while (++i <= n) { + pi = points[i]; + px.shift(); + px.push(pi[0]); + py.shift(); + py.push(pi[1]); + d3_svg_lineBasisBezier(path, px, py); + } + points.pop(); + path.push("L", pi); + return path.join(""); + } + function d3_svg_lineBasisOpen(points) { + if (points.length < 4) return d3_svg_lineLinear(points); + var path = [], i = -1, n = points.length, pi, px = [ 0 ], py = [ 0 ]; + while (++i < 3) { + pi = points[i]; + px.push(pi[0]); + py.push(pi[1]); + } + path.push(d3_svg_lineDot4(d3_svg_lineBasisBezier3, px) + "," + d3_svg_lineDot4(d3_svg_lineBasisBezier3, py)); + --i; + while (++i < n) { + pi = points[i]; + px.shift(); + px.push(pi[0]); + py.shift(); + py.push(pi[1]); + d3_svg_lineBasisBezier(path, px, py); + } + return path.join(""); + } + function d3_svg_lineBasisClosed(points) { + var path, i = -1, n = points.length, m = n + 4, pi, px = [], py = []; + while (++i < 4) { + pi = points[i % n]; + px.push(pi[0]); + py.push(pi[1]); + } + path = [ d3_svg_lineDot4(d3_svg_lineBasisBezier3, px), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, py) ]; + --i; + while (++i < m) { + pi = points[i % n]; + px.shift(); + px.push(pi[0]); + py.shift(); + py.push(pi[1]); + d3_svg_lineBasisBezier(path, px, py); + } + return path.join(""); + } + function d3_svg_lineBundle(points, tension) { + var n = points.length - 1; + if (n) { + var x0 = points[0][0], y0 = points[0][1], dx = points[n][0] - x0, dy = points[n][1] - y0, i = -1, p, t; + while (++i <= n) { + p = points[i]; + t = i / n; + p[0] = tension * p[0] + (1 - tension) * (x0 + t * dx); + p[1] = tension * p[1] + (1 - tension) * (y0 + t * dy); + } + } + return d3_svg_lineBasis(points); + } + function d3_svg_lineDot4(a, b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]; + } + var d3_svg_lineBasisBezier1 = [ 0, 2 / 3, 1 / 3, 0 ], d3_svg_lineBasisBezier2 = [ 0, 1 / 3, 2 / 3, 0 ], d3_svg_lineBasisBezier3 = [ 0, 1 / 6, 2 / 3, 1 / 6 ]; + function d3_svg_lineBasisBezier(path, x, y) { + path.push("C", d3_svg_lineDot4(d3_svg_lineBasisBezier1, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier1, y), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, y), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, y)); + } + function d3_svg_lineSlope(p0, p1) { + return (p1[1] - p0[1]) / (p1[0] - p0[0]); + } + function d3_svg_lineFiniteDifferences(points) { + var i = 0, j = points.length - 1, m = [], p0 = points[0], p1 = points[1], d = m[0] = d3_svg_lineSlope(p0, p1); + while (++i < j) { + m[i] = (d + (d = d3_svg_lineSlope(p0 = p1, p1 = points[i + 1]))) / 2; + } + m[i] = d; + return m; + } + function d3_svg_lineMonotoneTangents(points) { + var tangents = [], d, a, b, s, m = d3_svg_lineFiniteDifferences(points), i = -1, j = points.length - 1; + while (++i < j) { + d = d3_svg_lineSlope(points[i], points[i + 1]); + if (abs(d) < ε) { + m[i] = m[i + 1] = 0; + } else { + a = m[i] / d; + b = m[i + 1] / d; + s = a * a + b * b; + if (s > 9) { + s = d * 3 / Math.sqrt(s); + m[i] = s * a; + m[i + 1] = s * b; + } + } + } + i = -1; + while (++i <= j) { + s = (points[Math.min(j, i + 1)][0] - points[Math.max(0, i - 1)][0]) / (6 * (1 + m[i] * m[i])); + tangents.push([ s || 0, m[i] * s || 0 ]); + } + return tangents; + } + function d3_svg_lineMonotone(points) { + return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineMonotoneTangents(points)); + } + d3.svg.line.radial = function() { + var line = d3_svg_line(d3_svg_lineRadial); + line.radius = line.x, delete line.x; + line.angle = line.y, delete line.y; + return line; + }; + function d3_svg_lineRadial(points) { + var point, i = -1, n = points.length, r, a; + while (++i < n) { + point = points[i]; + r = point[0]; + a = point[1] - halfÏ€; + point[0] = r * Math.cos(a); + point[1] = r * Math.sin(a); + } + return points; + } + function d3_svg_area(projection) { + var x0 = d3_geom_pointX, x1 = d3_geom_pointX, y0 = 0, y1 = d3_geom_pointY, defined = d3_true, interpolate = d3_svg_lineLinear, interpolateKey = interpolate.key, interpolateReverse = interpolate, L = "L", tension = .7; + function area(data) { + var segments = [], points0 = [], points1 = [], i = -1, n = data.length, d, fx0 = d3_functor(x0), fy0 = d3_functor(y0), fx1 = x0 === x1 ? function() { + return x; + } : d3_functor(x1), fy1 = y0 === y1 ? function() { + return y; + } : d3_functor(y1), x, y; + function segment() { + segments.push("M", interpolate(projection(points1), tension), L, interpolateReverse(projection(points0.reverse()), tension), "Z"); + } + while (++i < n) { + if (defined.call(this, d = data[i], i)) { + points0.push([ x = +fx0.call(this, d, i), y = +fy0.call(this, d, i) ]); + points1.push([ +fx1.call(this, d, i), +fy1.call(this, d, i) ]); + } else if (points0.length) { + segment(); + points0 = []; + points1 = []; + } + } + if (points0.length) segment(); + return segments.length ? segments.join("") : null; + } + area.x = function(_) { + if (!arguments.length) return x1; + x0 = x1 = _; + return area; + }; + area.x0 = function(_) { + if (!arguments.length) return x0; + x0 = _; + return area; + }; + area.x1 = function(_) { + if (!arguments.length) return x1; + x1 = _; + return area; + }; + area.y = function(_) { + if (!arguments.length) return y1; + y0 = y1 = _; + return area; + }; + area.y0 = function(_) { + if (!arguments.length) return y0; + y0 = _; + return area; + }; + area.y1 = function(_) { + if (!arguments.length) return y1; + y1 = _; + return area; + }; + area.defined = function(_) { + if (!arguments.length) return defined; + defined = _; + return area; + }; + area.interpolate = function(_) { + if (!arguments.length) return interpolateKey; + if (typeof _ === "function") interpolateKey = interpolate = _; else interpolateKey = (interpolate = d3_svg_lineInterpolators.get(_) || d3_svg_lineLinear).key; + interpolateReverse = interpolate.reverse || interpolate; + L = interpolate.closed ? "M" : "L"; + return area; + }; + area.tension = function(_) { + if (!arguments.length) return tension; + tension = _; + return area; + }; + return area; + } + d3_svg_lineStepBefore.reverse = d3_svg_lineStepAfter; + d3_svg_lineStepAfter.reverse = d3_svg_lineStepBefore; + d3.svg.area = function() { + return d3_svg_area(d3_identity); + }; + d3.svg.area.radial = function() { + var area = d3_svg_area(d3_svg_lineRadial); + area.radius = area.x, delete area.x; + area.innerRadius = area.x0, delete area.x0; + area.outerRadius = area.x1, delete area.x1; + area.angle = area.y, delete area.y; + area.startAngle = area.y0, delete area.y0; + area.endAngle = area.y1, delete area.y1; + return area; + }; + d3.svg.chord = function() { + var source = d3_source, target = d3_target, radius = d3_svg_chordRadius, startAngle = d3_svg_arcStartAngle, endAngle = d3_svg_arcEndAngle; + function chord(d, i) { + var s = subgroup(this, source, d, i), t = subgroup(this, target, d, i); + return "M" + s.p0 + arc(s.r, s.p1, s.a1 - s.a0) + (equals(s, t) ? curve(s.r, s.p1, s.r, s.p0) : curve(s.r, s.p1, t.r, t.p0) + arc(t.r, t.p1, t.a1 - t.a0) + curve(t.r, t.p1, s.r, s.p0)) + "Z"; + } + function subgroup(self, f, d, i) { + var subgroup = f.call(self, d, i), r = radius.call(self, subgroup, i), a0 = startAngle.call(self, subgroup, i) - halfÏ€, a1 = endAngle.call(self, subgroup, i) - halfÏ€; + return { + r: r, + a0: a0, + a1: a1, + p0: [ r * Math.cos(a0), r * Math.sin(a0) ], + p1: [ r * Math.cos(a1), r * Math.sin(a1) ] + }; + } + function equals(a, b) { + return a.a0 == b.a0 && a.a1 == b.a1; + } + function arc(r, p, a) { + return "A" + r + "," + r + " 0 " + +(a > Ï€) + ",1 " + p; + } + function curve(r0, p0, r1, p1) { + return "Q 0,0 " + p1; + } + chord.radius = function(v) { + if (!arguments.length) return radius; + radius = d3_functor(v); + return chord; + }; + chord.source = function(v) { + if (!arguments.length) return source; + source = d3_functor(v); + return chord; + }; + chord.target = function(v) { + if (!arguments.length) return target; + target = d3_functor(v); + return chord; + }; + chord.startAngle = function(v) { + if (!arguments.length) return startAngle; + startAngle = d3_functor(v); + return chord; + }; + chord.endAngle = function(v) { + if (!arguments.length) return endAngle; + endAngle = d3_functor(v); + return chord; + }; + return chord; + }; + function d3_svg_chordRadius(d) { + return d.radius; + } + d3.svg.diagonal = function() { + var source = d3_source, target = d3_target, projection = d3_svg_diagonalProjection; + function diagonal(d, i) { + var p0 = source.call(this, d, i), p3 = target.call(this, d, i), m = (p0.y + p3.y) / 2, p = [ p0, { + x: p0.x, + y: m + }, { + x: p3.x, + y: m + }, p3 ]; + p = p.map(projection); + return "M" + p[0] + "C" + p[1] + " " + p[2] + " " + p[3]; + } + diagonal.source = function(x) { + if (!arguments.length) return source; + source = d3_functor(x); + return diagonal; + }; + diagonal.target = function(x) { + if (!arguments.length) return target; + target = d3_functor(x); + return diagonal; + }; + diagonal.projection = function(x) { + if (!arguments.length) return projection; + projection = x; + return diagonal; + }; + return diagonal; + }; + function d3_svg_diagonalProjection(d) { + return [ d.x, d.y ]; + } + d3.svg.diagonal.radial = function() { + var diagonal = d3.svg.diagonal(), projection = d3_svg_diagonalProjection, projection_ = diagonal.projection; + diagonal.projection = function(x) { + return arguments.length ? projection_(d3_svg_diagonalRadialProjection(projection = x)) : projection; + }; + return diagonal; + }; + function d3_svg_diagonalRadialProjection(projection) { + return function() { + var d = projection.apply(this, arguments), r = d[0], a = d[1] - halfÏ€; + return [ r * Math.cos(a), r * Math.sin(a) ]; + }; + } + d3.svg.symbol = function() { + var type = d3_svg_symbolType, size = d3_svg_symbolSize; + function symbol(d, i) { + return (d3_svg_symbols.get(type.call(this, d, i)) || d3_svg_symbolCircle)(size.call(this, d, i)); + } + symbol.type = function(x) { + if (!arguments.length) return type; + type = d3_functor(x); + return symbol; + }; + symbol.size = function(x) { + if (!arguments.length) return size; + size = d3_functor(x); + return symbol; + }; + return symbol; + }; + function d3_svg_symbolSize() { + return 64; + } + function d3_svg_symbolType() { + return "circle"; + } + function d3_svg_symbolCircle(size) { + var r = Math.sqrt(size / Ï€); + return "M0," + r + "A" + r + "," + r + " 0 1,1 0," + -r + "A" + r + "," + r + " 0 1,1 0," + r + "Z"; + } + var d3_svg_symbols = d3.map({ + circle: d3_svg_symbolCircle, + cross: function(size) { + var r = Math.sqrt(size / 5) / 2; + return "M" + -3 * r + "," + -r + "H" + -r + "V" + -3 * r + "H" + r + "V" + -r + "H" + 3 * r + "V" + r + "H" + r + "V" + 3 * r + "H" + -r + "V" + r + "H" + -3 * r + "Z"; + }, + diamond: function(size) { + var ry = Math.sqrt(size / (2 * d3_svg_symbolTan30)), rx = ry * d3_svg_symbolTan30; + return "M0," + -ry + "L" + rx + ",0" + " 0," + ry + " " + -rx + ",0" + "Z"; + }, + square: function(size) { + var r = Math.sqrt(size) / 2; + return "M" + -r + "," + -r + "L" + r + "," + -r + " " + r + "," + r + " " + -r + "," + r + "Z"; + }, + "triangle-down": function(size) { + var rx = Math.sqrt(size / d3_svg_symbolSqrt3), ry = rx * d3_svg_symbolSqrt3 / 2; + return "M0," + ry + "L" + rx + "," + -ry + " " + -rx + "," + -ry + "Z"; + }, + "triangle-up": function(size) { + var rx = Math.sqrt(size / d3_svg_symbolSqrt3), ry = rx * d3_svg_symbolSqrt3 / 2; + return "M0," + -ry + "L" + rx + "," + ry + " " + -rx + "," + ry + "Z"; + } + }); + d3.svg.symbolTypes = d3_svg_symbols.keys(); + var d3_svg_symbolSqrt3 = Math.sqrt(3), d3_svg_symbolTan30 = Math.tan(30 * d3_radians); + d3_selectionPrototype.transition = function(name) { + var id = d3_transitionInheritId || ++d3_transitionId, ns = d3_transitionNamespace(name), subgroups = [], subgroup, node, transition = d3_transitionInherit || { + time: Date.now(), + ease: d3_ease_cubicInOut, + delay: 0, + duration: 250 + }; + for (var j = -1, m = this.length; ++j < m; ) { + subgroups.push(subgroup = []); + for (var group = this[j], i = -1, n = group.length; ++i < n; ) { + if (node = group[i]) d3_transitionNode(node, i, ns, id, transition); + subgroup.push(node); + } + } + return d3_transition(subgroups, ns, id); + }; + d3_selectionPrototype.interrupt = function(name) { + return this.each(name == null ? d3_selection_interrupt : d3_selection_interruptNS(d3_transitionNamespace(name))); + }; + var d3_selection_interrupt = d3_selection_interruptNS(d3_transitionNamespace()); + function d3_selection_interruptNS(ns) { + return function() { + var lock, activeId, active; + if ((lock = this[ns]) && (active = lock[activeId = lock.active])) { + active.timer.c = null; + active.timer.t = NaN; + if (--lock.count) delete lock[activeId]; else delete this[ns]; + lock.active += .5; + active.event && active.event.interrupt.call(this, this.__data__, active.index); + } + }; + } + function d3_transition(groups, ns, id) { + d3_subclass(groups, d3_transitionPrototype); + groups.namespace = ns; + groups.id = id; + return groups; + } + var d3_transitionPrototype = [], d3_transitionId = 0, d3_transitionInheritId, d3_transitionInherit; + d3_transitionPrototype.call = d3_selectionPrototype.call; + d3_transitionPrototype.empty = d3_selectionPrototype.empty; + d3_transitionPrototype.node = d3_selectionPrototype.node; + d3_transitionPrototype.size = d3_selectionPrototype.size; + d3.transition = function(selection, name) { + return selection && selection.transition ? d3_transitionInheritId ? selection.transition(name) : selection : d3.selection().transition(selection); + }; + d3.transition.prototype = d3_transitionPrototype; + d3_transitionPrototype.select = function(selector) { + var id = this.id, ns = this.namespace, subgroups = [], subgroup, subnode, node; + selector = d3_selection_selector(selector); + for (var j = -1, m = this.length; ++j < m; ) { + subgroups.push(subgroup = []); + for (var group = this[j], i = -1, n = group.length; ++i < n; ) { + if ((node = group[i]) && (subnode = selector.call(node, node.__data__, i, j))) { + if ("__data__" in node) subnode.__data__ = node.__data__; + d3_transitionNode(subnode, i, ns, id, node[ns][id]); + subgroup.push(subnode); + } else { + subgroup.push(null); + } + } + } + return d3_transition(subgroups, ns, id); + }; + d3_transitionPrototype.selectAll = function(selector) { + var id = this.id, ns = this.namespace, subgroups = [], subgroup, subnodes, node, subnode, transition; + selector = d3_selection_selectorAll(selector); + for (var j = -1, m = this.length; ++j < m; ) { + for (var group = this[j], i = -1, n = group.length; ++i < n; ) { + if (node = group[i]) { + transition = node[ns][id]; + subnodes = selector.call(node, node.__data__, i, j); + subgroups.push(subgroup = []); + for (var k = -1, o = subnodes.length; ++k < o; ) { + if (subnode = subnodes[k]) d3_transitionNode(subnode, k, ns, id, transition); + subgroup.push(subnode); + } + } + } + } + return d3_transition(subgroups, ns, id); + }; + d3_transitionPrototype.filter = function(filter) { + var subgroups = [], subgroup, group, node; + if (typeof filter !== "function") filter = d3_selection_filter(filter); + for (var j = 0, m = this.length; j < m; j++) { + subgroups.push(subgroup = []); + for (var group = this[j], i = 0, n = group.length; i < n; i++) { + if ((node = group[i]) && filter.call(node, node.__data__, i, j)) { + subgroup.push(node); + } + } + } + return d3_transition(subgroups, this.namespace, this.id); + }; + d3_transitionPrototype.tween = function(name, tween) { + var id = this.id, ns = this.namespace; + if (arguments.length < 2) return this.node()[ns][id].tween.get(name); + return d3_selection_each(this, tween == null ? function(node) { + node[ns][id].tween.remove(name); + } : function(node) { + node[ns][id].tween.set(name, tween); + }); + }; + function d3_transition_tween(groups, name, value, tween) { + var id = groups.id, ns = groups.namespace; + return d3_selection_each(groups, typeof value === "function" ? function(node, i, j) { + node[ns][id].tween.set(name, tween(value.call(node, node.__data__, i, j))); + } : (value = tween(value), function(node) { + node[ns][id].tween.set(name, value); + })); + } + d3_transitionPrototype.attr = function(nameNS, value) { + if (arguments.length < 2) { + for (value in nameNS) this.attr(value, nameNS[value]); + return this; + } + var interpolate = nameNS == "transform" ? d3_interpolateTransform : d3_interpolate, name = d3.ns.qualify(nameNS); + function attrNull() { + this.removeAttribute(name); + } + function attrNullNS() { + this.removeAttributeNS(name.space, name.local); + } + function attrTween(b) { + return b == null ? attrNull : (b += "", function() { + var a = this.getAttribute(name), i; + return a !== b && (i = interpolate(a, b), function(t) { + this.setAttribute(name, i(t)); + }); + }); + } + function attrTweenNS(b) { + return b == null ? attrNullNS : (b += "", function() { + var a = this.getAttributeNS(name.space, name.local), i; + return a !== b && (i = interpolate(a, b), function(t) { + this.setAttributeNS(name.space, name.local, i(t)); + }); + }); + } + return d3_transition_tween(this, "attr." + nameNS, value, name.local ? attrTweenNS : attrTween); + }; + d3_transitionPrototype.attrTween = function(nameNS, tween) { + var name = d3.ns.qualify(nameNS); + function attrTween(d, i) { + var f = tween.call(this, d, i, this.getAttribute(name)); + return f && function(t) { + this.setAttribute(name, f(t)); + }; + } + function attrTweenNS(d, i) { + var f = tween.call(this, d, i, this.getAttributeNS(name.space, name.local)); + return f && function(t) { + this.setAttributeNS(name.space, name.local, f(t)); + }; + } + return this.tween("attr." + nameNS, name.local ? attrTweenNS : attrTween); + }; + d3_transitionPrototype.style = function(name, value, priority) { + var n = arguments.length; + if (n < 3) { + if (typeof name !== "string") { + if (n < 2) value = ""; + for (priority in name) this.style(priority, name[priority], value); + return this; + } + priority = ""; + } + function styleNull() { + this.style.removeProperty(name); + } + function styleString(b) { + return b == null ? styleNull : (b += "", function() { + var a = d3_window(this).getComputedStyle(this, null).getPropertyValue(name), i; + return a !== b && (i = d3_interpolate(a, b), function(t) { + this.style.setProperty(name, i(t), priority); + }); + }); + } + return d3_transition_tween(this, "style." + name, value, styleString); + }; + d3_transitionPrototype.styleTween = function(name, tween, priority) { + if (arguments.length < 3) priority = ""; + function styleTween(d, i) { + var f = tween.call(this, d, i, d3_window(this).getComputedStyle(this, null).getPropertyValue(name)); + return f && function(t) { + this.style.setProperty(name, f(t), priority); + }; + } + return this.tween("style." + name, styleTween); + }; + d3_transitionPrototype.text = function(value) { + return d3_transition_tween(this, "text", value, d3_transition_text); + }; + function d3_transition_text(b) { + if (b == null) b = ""; + return function() { + this.textContent = b; + }; + } + d3_transitionPrototype.remove = function() { + var ns = this.namespace; + return this.each("end.transition", function() { + var p; + if (this[ns].count < 2 && (p = this.parentNode)) p.removeChild(this); + }); + }; + d3_transitionPrototype.ease = function(value) { + var id = this.id, ns = this.namespace; + if (arguments.length < 1) return this.node()[ns][id].ease; + if (typeof value !== "function") value = d3.ease.apply(d3, arguments); + return d3_selection_each(this, function(node) { + node[ns][id].ease = value; + }); + }; + d3_transitionPrototype.delay = function(value) { + var id = this.id, ns = this.namespace; + if (arguments.length < 1) return this.node()[ns][id].delay; + return d3_selection_each(this, typeof value === "function" ? function(node, i, j) { + node[ns][id].delay = +value.call(node, node.__data__, i, j); + } : (value = +value, function(node) { + node[ns][id].delay = value; + })); + }; + d3_transitionPrototype.duration = function(value) { + var id = this.id, ns = this.namespace; + if (arguments.length < 1) return this.node()[ns][id].duration; + return d3_selection_each(this, typeof value === "function" ? function(node, i, j) { + node[ns][id].duration = Math.max(1, value.call(node, node.__data__, i, j)); + } : (value = Math.max(1, value), function(node) { + node[ns][id].duration = value; + })); + }; + d3_transitionPrototype.each = function(type, listener) { + var id = this.id, ns = this.namespace; + if (arguments.length < 2) { + var inherit = d3_transitionInherit, inheritId = d3_transitionInheritId; + try { + d3_transitionInheritId = id; + d3_selection_each(this, function(node, i, j) { + d3_transitionInherit = node[ns][id]; + type.call(node, node.__data__, i, j); + }); + } finally { + d3_transitionInherit = inherit; + d3_transitionInheritId = inheritId; + } + } else { + d3_selection_each(this, function(node) { + var transition = node[ns][id]; + (transition.event || (transition.event = d3.dispatch("start", "end", "interrupt"))).on(type, listener); + }); + } + return this; + }; + d3_transitionPrototype.transition = function() { + var id0 = this.id, id1 = ++d3_transitionId, ns = this.namespace, subgroups = [], subgroup, group, node, transition; + for (var j = 0, m = this.length; j < m; j++) { + subgroups.push(subgroup = []); + for (var group = this[j], i = 0, n = group.length; i < n; i++) { + if (node = group[i]) { + transition = node[ns][id0]; + d3_transitionNode(node, i, ns, id1, { + time: transition.time, + ease: transition.ease, + delay: transition.delay + transition.duration, + duration: transition.duration + }); + } + subgroup.push(node); + } + } + return d3_transition(subgroups, ns, id1); + }; + function d3_transitionNamespace(name) { + return name == null ? "__transition__" : "__transition_" + name + "__"; + } + function d3_transitionNode(node, i, ns, id, inherit) { + var lock = node[ns] || (node[ns] = { + active: 0, + count: 0 + }), transition = lock[id], time, timer, duration, ease, tweens; + function schedule(elapsed) { + var delay = transition.delay; + timer.t = delay + time; + if (delay <= elapsed) return start(elapsed - delay); + timer.c = start; + } + function start(elapsed) { + var activeId = lock.active, active = lock[activeId]; + if (active) { + active.timer.c = null; + active.timer.t = NaN; + --lock.count; + delete lock[activeId]; + active.event && active.event.interrupt.call(node, node.__data__, active.index); + } + for (var cancelId in lock) { + if (+cancelId < id) { + var cancel = lock[cancelId]; + cancel.timer.c = null; + cancel.timer.t = NaN; + --lock.count; + delete lock[cancelId]; + } + } + timer.c = tick; + d3_timer(function() { + if (timer.c && tick(elapsed || 1)) { + timer.c = null; + timer.t = NaN; + } + return 1; + }, 0, time); + lock.active = id; + transition.event && transition.event.start.call(node, node.__data__, i); + tweens = []; + transition.tween.forEach(function(key, value) { + if (value = value.call(node, node.__data__, i)) { + tweens.push(value); + } + }); + ease = transition.ease; + duration = transition.duration; + } + function tick(elapsed) { + var t = elapsed / duration, e = ease(t), n = tweens.length; + while (n > 0) { + tweens[--n].call(node, e); + } + if (t >= 1) { + transition.event && transition.event.end.call(node, node.__data__, i); + if (--lock.count) delete lock[id]; else delete node[ns]; + return 1; + } + } + if (!transition) { + time = inherit.time; + timer = d3_timer(schedule, 0, time); + transition = lock[id] = { + tween: new d3_Map(), + time: time, + timer: timer, + delay: inherit.delay, + duration: inherit.duration, + ease: inherit.ease, + index: i + }; + inherit = null; + ++lock.count; + } + } + d3.svg.axis = function() { + var scale = d3.scale.linear(), orient = d3_svg_axisDefaultOrient, innerTickSize = 6, outerTickSize = 6, tickPadding = 3, tickArguments_ = [ 10 ], tickValues = null, tickFormat_; + function axis(g) { + g.each(function() { + var g = d3.select(this); + var scale0 = this.__chart__ || scale, scale1 = this.__chart__ = scale.copy(); + var ticks = tickValues == null ? scale1.ticks ? scale1.ticks.apply(scale1, tickArguments_) : scale1.domain() : tickValues, tickFormat = tickFormat_ == null ? scale1.tickFormat ? scale1.tickFormat.apply(scale1, tickArguments_) : d3_identity : tickFormat_, tick = g.selectAll(".tick").data(ticks, scale1), tickEnter = tick.enter().insert("g", ".domain").attr("class", "tick").style("opacity", ε), tickExit = d3.transition(tick.exit()).style("opacity", ε).remove(), tickUpdate = d3.transition(tick.order()).style("opacity", 1), tickSpacing = Math.max(innerTickSize, 0) + tickPadding, tickTransform; + var range = d3_scaleRange(scale1), path = g.selectAll(".domain").data([ 0 ]), pathUpdate = (path.enter().append("path").attr("class", "domain"), + d3.transition(path)); + tickEnter.append("line"); + tickEnter.append("text"); + var lineEnter = tickEnter.select("line"), lineUpdate = tickUpdate.select("line"), text = tick.select("text").text(tickFormat), textEnter = tickEnter.select("text"), textUpdate = tickUpdate.select("text"), sign = orient === "top" || orient === "left" ? -1 : 1, x1, x2, y1, y2; + if (orient === "bottom" || orient === "top") { + tickTransform = d3_svg_axisX, x1 = "x", y1 = "y", x2 = "x2", y2 = "y2"; + text.attr("dy", sign < 0 ? "0em" : ".71em").style("text-anchor", "middle"); + pathUpdate.attr("d", "M" + range[0] + "," + sign * outerTickSize + "V0H" + range[1] + "V" + sign * outerTickSize); + } else { + tickTransform = d3_svg_axisY, x1 = "y", y1 = "x", x2 = "y2", y2 = "x2"; + text.attr("dy", ".32em").style("text-anchor", sign < 0 ? "end" : "start"); + pathUpdate.attr("d", "M" + sign * outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + sign * outerTickSize); + } + lineEnter.attr(y2, sign * innerTickSize); + textEnter.attr(y1, sign * tickSpacing); + lineUpdate.attr(x2, 0).attr(y2, sign * innerTickSize); + textUpdate.attr(x1, 0).attr(y1, sign * tickSpacing); + if (scale1.rangeBand) { + var x = scale1, dx = x.rangeBand() / 2; + scale0 = scale1 = function(d) { + return x(d) + dx; + }; + } else if (scale0.rangeBand) { + scale0 = scale1; + } else { + tickExit.call(tickTransform, scale1, scale0); + } + tickEnter.call(tickTransform, scale0, scale1); + tickUpdate.call(tickTransform, scale1, scale1); + }); + } + axis.scale = function(x) { + if (!arguments.length) return scale; + scale = x; + return axis; + }; + axis.orient = function(x) { + if (!arguments.length) return orient; + orient = x in d3_svg_axisOrients ? x + "" : d3_svg_axisDefaultOrient; + return axis; + }; + axis.ticks = function() { + if (!arguments.length) return tickArguments_; + tickArguments_ = d3_array(arguments); + return axis; + }; + axis.tickValues = function(x) { + if (!arguments.length) return tickValues; + tickValues = x; + return axis; + }; + axis.tickFormat = function(x) { + if (!arguments.length) return tickFormat_; + tickFormat_ = x; + return axis; + }; + axis.tickSize = function(x) { + var n = arguments.length; + if (!n) return innerTickSize; + innerTickSize = +x; + outerTickSize = +arguments[n - 1]; + return axis; + }; + axis.innerTickSize = function(x) { + if (!arguments.length) return innerTickSize; + innerTickSize = +x; + return axis; + }; + axis.outerTickSize = function(x) { + if (!arguments.length) return outerTickSize; + outerTickSize = +x; + return axis; + }; + axis.tickPadding = function(x) { + if (!arguments.length) return tickPadding; + tickPadding = +x; + return axis; + }; + axis.tickSubdivide = function() { + return arguments.length && axis; + }; + return axis; + }; + var d3_svg_axisDefaultOrient = "bottom", d3_svg_axisOrients = { + top: 1, + right: 1, + bottom: 1, + left: 1 + }; + function d3_svg_axisX(selection, x0, x1) { + selection.attr("transform", function(d) { + var v0 = x0(d); + return "translate(" + (isFinite(v0) ? v0 : x1(d)) + ",0)"; + }); + } + function d3_svg_axisY(selection, y0, y1) { + selection.attr("transform", function(d) { + var v0 = y0(d); + return "translate(0," + (isFinite(v0) ? v0 : y1(d)) + ")"; + }); + } + d3.svg.brush = function() { + var event = d3_eventDispatch(brush, "brushstart", "brush", "brushend"), x = null, y = null, xExtent = [ 0, 0 ], yExtent = [ 0, 0 ], xExtentDomain, yExtentDomain, xClamp = true, yClamp = true, resizes = d3_svg_brushResizes[0]; + function brush(g) { + g.each(function() { + var g = d3.select(this).style("pointer-events", "all").style("-webkit-tap-highlight-color", "rgba(0,0,0,0)").on("mousedown.brush", brushstart).on("touchstart.brush", brushstart); + var background = g.selectAll(".background").data([ 0 ]); + background.enter().append("rect").attr("class", "background").style("visibility", "hidden").style("cursor", "crosshair"); + g.selectAll(".extent").data([ 0 ]).enter().append("rect").attr("class", "extent").style("cursor", "move"); + var resize = g.selectAll(".resize").data(resizes, d3_identity); + resize.exit().remove(); + resize.enter().append("g").attr("class", function(d) { + return "resize " + d; + }).style("cursor", function(d) { + return d3_svg_brushCursor[d]; + }).append("rect").attr("x", function(d) { + return /[ew]$/.test(d) ? -3 : null; + }).attr("y", function(d) { + return /^[ns]/.test(d) ? -3 : null; + }).attr("width", 6).attr("height", 6).style("visibility", "hidden"); + resize.style("display", brush.empty() ? "none" : null); + var gUpdate = d3.transition(g), backgroundUpdate = d3.transition(background), range; + if (x) { + range = d3_scaleRange(x); + backgroundUpdate.attr("x", range[0]).attr("width", range[1] - range[0]); + redrawX(gUpdate); + } + if (y) { + range = d3_scaleRange(y); + backgroundUpdate.attr("y", range[0]).attr("height", range[1] - range[0]); + redrawY(gUpdate); + } + redraw(gUpdate); + }); + } + brush.event = function(g) { + g.each(function() { + var event_ = event.of(this, arguments), extent1 = { + x: xExtent, + y: yExtent, + i: xExtentDomain, + j: yExtentDomain + }, extent0 = this.__chart__ || extent1; + this.__chart__ = extent1; + if (d3_transitionInheritId) { + d3.select(this).transition().each("start.brush", function() { + xExtentDomain = extent0.i; + yExtentDomain = extent0.j; + xExtent = extent0.x; + yExtent = extent0.y; + event_({ + type: "brushstart" + }); + }).tween("brush:brush", function() { + var xi = d3_interpolateArray(xExtent, extent1.x), yi = d3_interpolateArray(yExtent, extent1.y); + xExtentDomain = yExtentDomain = null; + return function(t) { + xExtent = extent1.x = xi(t); + yExtent = extent1.y = yi(t); + event_({ + type: "brush", + mode: "resize" + }); + }; + }).each("end.brush", function() { + xExtentDomain = extent1.i; + yExtentDomain = extent1.j; + event_({ + type: "brush", + mode: "resize" + }); + event_({ + type: "brushend" + }); + }); + } else { + event_({ + type: "brushstart" + }); + event_({ + type: "brush", + mode: "resize" + }); + event_({ + type: "brushend" + }); + } + }); + }; + function redraw(g) { + g.selectAll(".resize").attr("transform", function(d) { + return "translate(" + xExtent[+/e$/.test(d)] + "," + yExtent[+/^s/.test(d)] + ")"; + }); + } + function redrawX(g) { + g.select(".extent").attr("x", xExtent[0]); + g.selectAll(".extent,.n>rect,.s>rect").attr("width", xExtent[1] - xExtent[0]); + } + function redrawY(g) { + g.select(".extent").attr("y", yExtent[0]); + g.selectAll(".extent,.e>rect,.w>rect").attr("height", yExtent[1] - yExtent[0]); + } + function brushstart() { + var target = this, eventTarget = d3.select(d3.event.target), event_ = event.of(target, arguments), g = d3.select(target), resizing = eventTarget.datum(), resizingX = !/^(n|s)$/.test(resizing) && x, resizingY = !/^(e|w)$/.test(resizing) && y, dragging = eventTarget.classed("extent"), dragRestore = d3_event_dragSuppress(target), center, origin = d3.mouse(target), offset; + var w = d3.select(d3_window(target)).on("keydown.brush", keydown).on("keyup.brush", keyup); + if (d3.event.changedTouches) { + w.on("touchmove.brush", brushmove).on("touchend.brush", brushend); + } else { + w.on("mousemove.brush", brushmove).on("mouseup.brush", brushend); + } + g.interrupt().selectAll("*").interrupt(); + if (dragging) { + origin[0] = xExtent[0] - origin[0]; + origin[1] = yExtent[0] - origin[1]; + } else if (resizing) { + var ex = +/w$/.test(resizing), ey = +/^n/.test(resizing); + offset = [ xExtent[1 - ex] - origin[0], yExtent[1 - ey] - origin[1] ]; + origin[0] = xExtent[ex]; + origin[1] = yExtent[ey]; + } else if (d3.event.altKey) center = origin.slice(); + g.style("pointer-events", "none").selectAll(".resize").style("display", null); + d3.select("body").style("cursor", eventTarget.style("cursor")); + event_({ + type: "brushstart" + }); + brushmove(); + function keydown() { + if (d3.event.keyCode == 32) { + if (!dragging) { + center = null; + origin[0] -= xExtent[1]; + origin[1] -= yExtent[1]; + dragging = 2; + } + d3_eventPreventDefault(); + } + } + function keyup() { + if (d3.event.keyCode == 32 && dragging == 2) { + origin[0] += xExtent[1]; + origin[1] += yExtent[1]; + dragging = 0; + d3_eventPreventDefault(); + } + } + function brushmove() { + var point = d3.mouse(target), moved = false; + if (offset) { + point[0] += offset[0]; + point[1] += offset[1]; + } + if (!dragging) { + if (d3.event.altKey) { + if (!center) center = [ (xExtent[0] + xExtent[1]) / 2, (yExtent[0] + yExtent[1]) / 2 ]; + origin[0] = xExtent[+(point[0] < center[0])]; + origin[1] = yExtent[+(point[1] < center[1])]; + } else center = null; + } + if (resizingX && move1(point, x, 0)) { + redrawX(g); + moved = true; + } + if (resizingY && move1(point, y, 1)) { + redrawY(g); + moved = true; + } + if (moved) { + redraw(g); + event_({ + type: "brush", + mode: dragging ? "move" : "resize" + }); + } + } + function move1(point, scale, i) { + var range = d3_scaleRange(scale), r0 = range[0], r1 = range[1], position = origin[i], extent = i ? yExtent : xExtent, size = extent[1] - extent[0], min, max; + if (dragging) { + r0 -= position; + r1 -= size + position; + } + min = (i ? yClamp : xClamp) ? Math.max(r0, Math.min(r1, point[i])) : point[i]; + if (dragging) { + max = (min += position) + size; + } else { + if (center) position = Math.max(r0, Math.min(r1, 2 * center[i] - min)); + if (position < min) { + max = min; + min = position; + } else { + max = position; + } + } + if (extent[0] != min || extent[1] != max) { + if (i) yExtentDomain = null; else xExtentDomain = null; + extent[0] = min; + extent[1] = max; + return true; + } + } + function brushend() { + brushmove(); + g.style("pointer-events", "all").selectAll(".resize").style("display", brush.empty() ? "none" : null); + d3.select("body").style("cursor", null); + w.on("mousemove.brush", null).on("mouseup.brush", null).on("touchmove.brush", null).on("touchend.brush", null).on("keydown.brush", null).on("keyup.brush", null); + dragRestore(); + event_({ + type: "brushend" + }); + } + } + brush.x = function(z) { + if (!arguments.length) return x; + x = z; + resizes = d3_svg_brushResizes[!x << 1 | !y]; + return brush; + }; + brush.y = function(z) { + if (!arguments.length) return y; + y = z; + resizes = d3_svg_brushResizes[!x << 1 | !y]; + return brush; + }; + brush.clamp = function(z) { + if (!arguments.length) return x && y ? [ xClamp, yClamp ] : x ? xClamp : y ? yClamp : null; + if (x && y) xClamp = !!z[0], yClamp = !!z[1]; else if (x) xClamp = !!z; else if (y) yClamp = !!z; + return brush; + }; + brush.extent = function(z) { + var x0, x1, y0, y1, t; + if (!arguments.length) { + if (x) { + if (xExtentDomain) { + x0 = xExtentDomain[0], x1 = xExtentDomain[1]; + } else { + x0 = xExtent[0], x1 = xExtent[1]; + if (x.invert) x0 = x.invert(x0), x1 = x.invert(x1); + if (x1 < x0) t = x0, x0 = x1, x1 = t; + } + } + if (y) { + if (yExtentDomain) { + y0 = yExtentDomain[0], y1 = yExtentDomain[1]; + } else { + y0 = yExtent[0], y1 = yExtent[1]; + if (y.invert) y0 = y.invert(y0), y1 = y.invert(y1); + if (y1 < y0) t = y0, y0 = y1, y1 = t; + } + } + return x && y ? [ [ x0, y0 ], [ x1, y1 ] ] : x ? [ x0, x1 ] : y && [ y0, y1 ]; + } + if (x) { + x0 = z[0], x1 = z[1]; + if (y) x0 = x0[0], x1 = x1[0]; + xExtentDomain = [ x0, x1 ]; + if (x.invert) x0 = x(x0), x1 = x(x1); + if (x1 < x0) t = x0, x0 = x1, x1 = t; + if (x0 != xExtent[0] || x1 != xExtent[1]) xExtent = [ x0, x1 ]; + } + if (y) { + y0 = z[0], y1 = z[1]; + if (x) y0 = y0[1], y1 = y1[1]; + yExtentDomain = [ y0, y1 ]; + if (y.invert) y0 = y(y0), y1 = y(y1); + if (y1 < y0) t = y0, y0 = y1, y1 = t; + if (y0 != yExtent[0] || y1 != yExtent[1]) yExtent = [ y0, y1 ]; + } + return brush; + }; + brush.clear = function() { + if (!brush.empty()) { + xExtent = [ 0, 0 ], yExtent = [ 0, 0 ]; + xExtentDomain = yExtentDomain = null; + } + return brush; + }; + brush.empty = function() { + return !!x && xExtent[0] == xExtent[1] || !!y && yExtent[0] == yExtent[1]; + }; + return d3.rebind(brush, event, "on"); + }; + var d3_svg_brushCursor = { + n: "ns-resize", + e: "ew-resize", + s: "ns-resize", + w: "ew-resize", + nw: "nwse-resize", + ne: "nesw-resize", + se: "nwse-resize", + sw: "nesw-resize" + }; + var d3_svg_brushResizes = [ [ "n", "e", "s", "w", "nw", "ne", "se", "sw" ], [ "e", "w" ], [ "n", "s" ], [] ]; + var d3_time_format = d3_time.format = d3_locale_enUS.timeFormat; + var d3_time_formatUtc = d3_time_format.utc; + var d3_time_formatIso = d3_time_formatUtc("%Y-%m-%dT%H:%M:%S.%LZ"); + d3_time_format.iso = Date.prototype.toISOString && +new Date("2000-01-01T00:00:00.000Z") ? d3_time_formatIsoNative : d3_time_formatIso; + function d3_time_formatIsoNative(date) { + return date.toISOString(); + } + d3_time_formatIsoNative.parse = function(string) { + var date = new Date(string); + return isNaN(date) ? null : date; + }; + d3_time_formatIsoNative.toString = d3_time_formatIso.toString; + d3_time.second = d3_time_interval(function(date) { + return new d3_date(Math.floor(date / 1e3) * 1e3); + }, function(date, offset) { + date.setTime(date.getTime() + Math.floor(offset) * 1e3); + }, function(date) { + return date.getSeconds(); + }); + d3_time.seconds = d3_time.second.range; + d3_time.seconds.utc = d3_time.second.utc.range; + d3_time.minute = d3_time_interval(function(date) { + return new d3_date(Math.floor(date / 6e4) * 6e4); + }, function(date, offset) { + date.setTime(date.getTime() + Math.floor(offset) * 6e4); + }, function(date) { + return date.getMinutes(); + }); + d3_time.minutes = d3_time.minute.range; + d3_time.minutes.utc = d3_time.minute.utc.range; + d3_time.hour = d3_time_interval(function(date) { + var timezone = date.getTimezoneOffset() / 60; + return new d3_date((Math.floor(date / 36e5 - timezone) + timezone) * 36e5); + }, function(date, offset) { + date.setTime(date.getTime() + Math.floor(offset) * 36e5); + }, function(date) { + return date.getHours(); + }); + d3_time.hours = d3_time.hour.range; + d3_time.hours.utc = d3_time.hour.utc.range; + d3_time.month = d3_time_interval(function(date) { + date = d3_time.day(date); + date.setDate(1); + return date; + }, function(date, offset) { + date.setMonth(date.getMonth() + offset); + }, function(date) { + return date.getMonth(); + }); + d3_time.months = d3_time.month.range; + d3_time.months.utc = d3_time.month.utc.range; + function d3_time_scale(linear, methods, format) { + function scale(x) { + return linear(x); + } + scale.invert = function(x) { + return d3_time_scaleDate(linear.invert(x)); + }; + scale.domain = function(x) { + if (!arguments.length) return linear.domain().map(d3_time_scaleDate); + linear.domain(x); + return scale; + }; + function tickMethod(extent, count) { + var span = extent[1] - extent[0], target = span / count, i = d3.bisect(d3_time_scaleSteps, target); + return i == d3_time_scaleSteps.length ? [ methods.year, d3_scale_linearTickRange(extent.map(function(d) { + return d / 31536e6; + }), count)[2] ] : !i ? [ d3_time_scaleMilliseconds, d3_scale_linearTickRange(extent, count)[2] ] : methods[target / d3_time_scaleSteps[i - 1] < d3_time_scaleSteps[i] / target ? i - 1 : i]; + } + scale.nice = function(interval, skip) { + var domain = scale.domain(), extent = d3_scaleExtent(domain), method = interval == null ? tickMethod(extent, 10) : typeof interval === "number" && tickMethod(extent, interval); + if (method) interval = method[0], skip = method[1]; + function skipped(date) { + return !isNaN(date) && !interval.range(date, d3_time_scaleDate(+date + 1), skip).length; + } + return scale.domain(d3_scale_nice(domain, skip > 1 ? { + floor: function(date) { + while (skipped(date = interval.floor(date))) date = d3_time_scaleDate(date - 1); + return date; + }, + ceil: function(date) { + while (skipped(date = interval.ceil(date))) date = d3_time_scaleDate(+date + 1); + return date; + } + } : interval)); + }; + scale.ticks = function(interval, skip) { + var extent = d3_scaleExtent(scale.domain()), method = interval == null ? tickMethod(extent, 10) : typeof interval === "number" ? tickMethod(extent, interval) : !interval.range && [ { + range: interval + }, skip ]; + if (method) interval = method[0], skip = method[1]; + return interval.range(extent[0], d3_time_scaleDate(+extent[1] + 1), skip < 1 ? 1 : skip); + }; + scale.tickFormat = function() { + return format; + }; + scale.copy = function() { + return d3_time_scale(linear.copy(), methods, format); + }; + return d3_scale_linearRebind(scale, linear); + } + function d3_time_scaleDate(t) { + return new Date(t); + } + var d3_time_scaleSteps = [ 1e3, 5e3, 15e3, 3e4, 6e4, 3e5, 9e5, 18e5, 36e5, 108e5, 216e5, 432e5, 864e5, 1728e5, 6048e5, 2592e6, 7776e6, 31536e6 ]; + var d3_time_scaleLocalMethods = [ [ d3_time.second, 1 ], [ d3_time.second, 5 ], [ d3_time.second, 15 ], [ d3_time.second, 30 ], [ d3_time.minute, 1 ], [ d3_time.minute, 5 ], [ d3_time.minute, 15 ], [ d3_time.minute, 30 ], [ d3_time.hour, 1 ], [ d3_time.hour, 3 ], [ d3_time.hour, 6 ], [ d3_time.hour, 12 ], [ d3_time.day, 1 ], [ d3_time.day, 2 ], [ d3_time.week, 1 ], [ d3_time.month, 1 ], [ d3_time.month, 3 ], [ d3_time.year, 1 ] ]; + var d3_time_scaleLocalFormat = d3_time_format.multi([ [ ".%L", function(d) { + return d.getMilliseconds(); + } ], [ ":%S", function(d) { + return d.getSeconds(); + } ], [ "%I:%M", function(d) { + return d.getMinutes(); + } ], [ "%I %p", function(d) { + return d.getHours(); + } ], [ "%a %d", function(d) { + return d.getDay() && d.getDate() != 1; + } ], [ "%b %d", function(d) { + return d.getDate() != 1; + } ], [ "%B", function(d) { + return d.getMonth(); + } ], [ "%Y", d3_true ] ]); + var d3_time_scaleMilliseconds = { + range: function(start, stop, step) { + return d3.range(Math.ceil(start / step) * step, +stop, step).map(d3_time_scaleDate); + }, + floor: d3_identity, + ceil: d3_identity + }; + d3_time_scaleLocalMethods.year = d3_time.year; + d3_time.scale = function() { + return d3_time_scale(d3.scale.linear(), d3_time_scaleLocalMethods, d3_time_scaleLocalFormat); + }; + var d3_time_scaleUtcMethods = d3_time_scaleLocalMethods.map(function(m) { + return [ m[0].utc, m[1] ]; + }); + var d3_time_scaleUtcFormat = d3_time_formatUtc.multi([ [ ".%L", function(d) { + return d.getUTCMilliseconds(); + } ], [ ":%S", function(d) { + return d.getUTCSeconds(); + } ], [ "%I:%M", function(d) { + return d.getUTCMinutes(); + } ], [ "%I %p", function(d) { + return d.getUTCHours(); + } ], [ "%a %d", function(d) { + return d.getUTCDay() && d.getUTCDate() != 1; + } ], [ "%b %d", function(d) { + return d.getUTCDate() != 1; + } ], [ "%B", function(d) { + return d.getUTCMonth(); + } ], [ "%Y", d3_true ] ]); + d3_time_scaleUtcMethods.year = d3_time.year.utc; + d3_time.scale.utc = function() { + return d3_time_scale(d3.scale.linear(), d3_time_scaleUtcMethods, d3_time_scaleUtcFormat); + }; + d3.text = d3_xhrType(function(request) { + return request.responseText; + }); + d3.json = function(url, callback) { + return d3_xhr(url, "application/json", d3_json, callback); + }; + function d3_json(request) { + return JSON.parse(request.responseText); + } + d3.html = function(url, callback) { + return d3_xhr(url, "text/html", d3_html, callback); + }; + function d3_html(request) { + var range = d3_document.createRange(); + range.selectNode(d3_document.body); + return range.createContextualFragment(request.responseText); + } + d3.xml = d3_xhrType(function(request) { + return request.responseXML; + }); + if (typeof define === "function" && define.amd) this.d3 = d3, define(d3); else if (typeof module === "object" && module.exports) module.exports = d3; else this.d3 = d3; +}(); \ No newline at end of file diff --git a/src/legacy/design-studio/js/dagre.js b/src/legacy/design-studio/js/dagre.js new file mode 100644 index 0000000..830997b --- /dev/null +++ b/src/legacy/design-studio/js/dagre.js @@ -0,0 +1,16396 @@ +!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.dagre=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ +/* +Copyright (c) 2012-2014 Chris Pettitt + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +module.exports = { + graphlib: require("./lib/graphlib"), + + layout: require("./lib/layout"), + debug: require("./lib/debug"), + util: { + time: require("./lib/util").time, + notime: require("./lib/util").notime + }, + version: require("./lib/version") +}; + +},{"./lib/debug":6,"./lib/graphlib":7,"./lib/layout":9,"./lib/util":29,"./lib/version":30}],2:[function(require,module,exports){ +"use strict"; + +var _ = require("./lodash"), + greedyFAS = require("./greedy-fas"); + +module.exports = { + run: run, + undo: undo +}; + +function run(g) { + var fas = (g.graph().acyclicer === "greedy" + ? greedyFAS(g, weightFn(g)) + : dfsFAS(g)); + _.each(fas, function(e) { + var label = g.edge(e); + g.removeEdge(e); + label.forwardName = e.name; + label.reversed = true; + g.setEdge(e.w, e.v, label, _.uniqueId("rev")); + }); + + function weightFn(g) { + return function(e) { + return g.edge(e).weight; + }; + } +} + +function dfsFAS(g) { + var fas = [], + stack = {}, + visited = {}; + + function dfs(v) { + if (_.has(visited, v)) { + return; + } + visited[v] = true; + stack[v] = true; + _.each(g.outEdges(v), function(e) { + if (_.has(stack, e.w)) { + fas.push(e); + } else { + dfs(e.w); + } + }); + delete stack[v]; + } + + _.each(g.nodes(), dfs); + return fas; +} + +function undo(g) { + _.each(g.edges(), function(e) { + var label = g.edge(e); + if (label.reversed) { + g.removeEdge(e); + + var forwardName = label.forwardName; + delete label.reversed; + delete label.forwardName; + g.setEdge(e.w, e.v, label, forwardName); + } + }); +} + +},{"./greedy-fas":8,"./lodash":10}],3:[function(require,module,exports){ +var _ = require("./lodash"), + util = require("./util"); + +module.exports = addBorderSegments; + +function addBorderSegments(g) { + function dfs(v) { + var children = g.children(v), + node = g.node(v); + if (children.length) { + _.each(children, dfs); + } + + if (_.has(node, "minRank")) { + node.borderLeft = []; + node.borderRight = []; + for (var rank = node.minRank, maxRank = node.maxRank + 1; + rank < maxRank; + ++rank) { + addBorderNode(g, "borderLeft", "_bl", v, node, rank); + addBorderNode(g, "borderRight", "_br", v, node, rank); + } + } + } + + _.each(g.children(), dfs); +} + +function addBorderNode(g, prop, prefix, sg, sgNode, rank) { + var label = { width: 0, height: 0, rank: rank, borderType: prop }, + prev = sgNode[prop][rank - 1], + curr = util.addDummyNode(g, "border", label, prefix); + sgNode[prop][rank] = curr; + g.setParent(curr, sg); + if (prev) { + g.setEdge(prev, curr, { weight: 1 }); + } +} + +},{"./lodash":10,"./util":29}],4:[function(require,module,exports){ +"use strict"; + +var _ = require("./lodash"); + +module.exports = { + adjust: adjust, + undo: undo +}; + +function adjust(g) { + var rankDir = g.graph().rankdir.toLowerCase(); + if (rankDir === "lr" || rankDir === "rl") { + swapWidthHeight(g); + } +} + +function undo(g) { + var rankDir = g.graph().rankdir.toLowerCase(); + if (rankDir === "bt" || rankDir === "rl") { + reverseY(g); + } + + if (rankDir === "lr" || rankDir === "rl") { + swapXY(g); + swapWidthHeight(g); + } +} + +function swapWidthHeight(g) { + _.each(g.nodes(), function(v) { swapWidthHeightOne(g.node(v)); }); + _.each(g.edges(), function(e) { swapWidthHeightOne(g.edge(e)); }); +} + +function swapWidthHeightOne(attrs) { + var w = attrs.width; + attrs.width = attrs.height; + attrs.height = w; +} + +function reverseY(g) { + _.each(g.nodes(), function(v) { reverseYOne(g.node(v)); }); + + _.each(g.edges(), function(e) { + var edge = g.edge(e); + _.each(edge.points, reverseYOne); + if (_.has(edge, "y")) { + reverseYOne(edge); + } + }); +} + +function reverseYOne(attrs) { + attrs.y = -attrs.y; +} + +function swapXY(g) { + _.each(g.nodes(), function(v) { swapXYOne(g.node(v)); }); + + _.each(g.edges(), function(e) { + var edge = g.edge(e); + _.each(edge.points, swapXYOne); + if (_.has(edge, "x")) { + swapXYOne(edge); + } + }); +} + +function swapXYOne(attrs) { + var x = attrs.x; + attrs.x = attrs.y; + attrs.y = x; +} + +},{"./lodash":10}],5:[function(require,module,exports){ +/* + * Simple doubly linked list implementation derived from Cormen, et al., + * "Introduction to Algorithms". + */ + +module.exports = List; + +function List() { + var sentinel = {}; + sentinel._next = sentinel._prev = sentinel; + this._sentinel = sentinel; +} + +List.prototype.dequeue = function() { + var sentinel = this._sentinel, + entry = sentinel._prev; + if (entry !== sentinel) { + unlink(entry); + return entry; + } +}; + +List.prototype.enqueue = function(entry) { + var sentinel = this._sentinel; + if (entry._prev && entry._next) { + unlink(entry); + } + entry._next = sentinel._next; + sentinel._next._prev = entry; + sentinel._next = entry; + entry._prev = sentinel; +}; + +List.prototype.toString = function() { + var strs = [], + sentinel = this._sentinel, + curr = sentinel._prev; + while (curr !== sentinel) { + strs.push(JSON.stringify(curr, filterOutLinks)); + curr = curr._prev; + } + return "[" + strs.join(", ") + "]"; +}; + +function unlink(entry) { + entry._prev._next = entry._next; + entry._next._prev = entry._prev; + delete entry._next; + delete entry._prev; +} + +function filterOutLinks(k, v) { + if (k !== "_next" && k !== "_prev") { + return v; + } +} + +},{}],6:[function(require,module,exports){ +var _ = require("./lodash"), + util = require("./util"), + Graph = require("./graphlib").Graph; + +module.exports = { + debugOrdering: debugOrdering +}; + +/* istanbul ignore next */ +function debugOrdering(g) { + var layerMatrix = util.buildLayerMatrix(g); + + var h = new Graph({ compound: true, multigraph: true }).setGraph({}); + + _.each(g.nodes(), function(v) { + h.setNode(v, { label: v }); + h.setParent(v, "layer" + g.node(v).rank); + }); + + _.each(g.edges(), function(e) { + h.setEdge(e.v, e.w, {}, e.name); + }); + + _.each(layerMatrix, function(layer, i) { + var layerV = "layer" + i; + h.setNode(layerV, { rank: "same" }); + _.reduce(layer, function(u, v) { + h.setEdge(u, v, { style: "invis" }); + return v; + }); + }); + + return h; +} + +},{"./graphlib":7,"./lodash":10,"./util":29}],7:[function(require,module,exports){ +/* global window */ + +var graphlib; + +if (typeof require === "function") { + try { + graphlib = require("graphlib"); + } catch (e) {} +} + +if (!graphlib) { + graphlib = window.graphlib; +} + +module.exports = graphlib; + +},{"graphlib":31}],8:[function(require,module,exports){ +var _ = require("./lodash"), + Graph = require("./graphlib").Graph, + List = require("./data/list"); + +/* + * A greedy heuristic for finding a feedback arc set for a graph. A feedback + * arc set is a set of edges that can be removed to make a graph acyclic. + * The algorithm comes from: P. Eades, X. Lin, and W. F. Smyth, "A fast and + * effective heuristic for the feedback arc set problem." This implementation + * adjusts that from the paper to allow for weighted edges. + */ +module.exports = greedyFAS; + +var DEFAULT_WEIGHT_FN = _.constant(1); + +function greedyFAS(g, weightFn) { + if (g.nodeCount() <= 1) { + return []; + } + var state = buildState(g, weightFn || DEFAULT_WEIGHT_FN); + var results = doGreedyFAS(state.graph, state.buckets, state.zeroIdx); + + // Expand multi-edges + return _.flatten(_.map(results, function(e) { + return g.outEdges(e.v, e.w); + }), true); +} + +function doGreedyFAS(g, buckets, zeroIdx) { + var results = [], + sources = buckets[buckets.length - 1], + sinks = buckets[0]; + + var entry; + while (g.nodeCount()) { + while ((entry = sinks.dequeue())) { removeNode(g, buckets, zeroIdx, entry); } + while ((entry = sources.dequeue())) { removeNode(g, buckets, zeroIdx, entry); } + if (g.nodeCount()) { + for (var i = buckets.length - 2; i > 0; --i) { + entry = buckets[i].dequeue(); + if (entry) { + results = results.concat(removeNode(g, buckets, zeroIdx, entry, true)); + break; + } + } + } + } + + return results; +} + +function removeNode(g, buckets, zeroIdx, entry, collectPredecessors) { + var results = collectPredecessors ? [] : undefined; + + _.each(g.inEdges(entry.v), function(edge) { + var weight = g.edge(edge), + uEntry = g.node(edge.v); + + if (collectPredecessors) { + results.push({ v: edge.v, w: edge.w }); + } + + uEntry.out -= weight; + assignBucket(buckets, zeroIdx, uEntry); + }); + + _.each(g.outEdges(entry.v), function(edge) { + var weight = g.edge(edge), + w = edge.w, + wEntry = g.node(w); + wEntry["in"] -= weight; + assignBucket(buckets, zeroIdx, wEntry); + }); + + g.removeNode(entry.v); + + return results; +} + +function buildState(g, weightFn) { + var fasGraph = new Graph(), + maxIn = 0, + maxOut = 0; + + _.each(g.nodes(), function(v) { + fasGraph.setNode(v, { v: v, "in": 0, out: 0 }); + }); + + // Aggregate weights on nodes, but also sum the weights across multi-edges + // into a single edge for the fasGraph. + _.each(g.edges(), function(e) { + var prevWeight = fasGraph.edge(e.v, e.w) || 0, + weight = weightFn(e), + edgeWeight = prevWeight + weight; + fasGraph.setEdge(e.v, e.w, edgeWeight); + maxOut = Math.max(maxOut, fasGraph.node(e.v).out += weight); + maxIn = Math.max(maxIn, fasGraph.node(e.w)["in"] += weight); + }); + + var buckets = _.range(maxOut + maxIn + 3).map(function() { return new List(); }); + var zeroIdx = maxIn + 1; + + _.each(fasGraph.nodes(), function(v) { + assignBucket(buckets, zeroIdx, fasGraph.node(v)); + }); + + return { graph: fasGraph, buckets: buckets, zeroIdx: zeroIdx }; +} + +function assignBucket(buckets, zeroIdx, entry) { + if (!entry.out) { + buckets[0].enqueue(entry); + } else if (!entry["in"]) { + buckets[buckets.length - 1].enqueue(entry); + } else { + buckets[entry.out - entry["in"] + zeroIdx].enqueue(entry); + } +} + +},{"./data/list":5,"./graphlib":7,"./lodash":10}],9:[function(require,module,exports){ +"use strict"; + +var _ = require("./lodash"), + acyclic = require("./acyclic"), + normalize = require("./normalize"), + rank = require("./rank"), + normalizeRanks = require("./util").normalizeRanks, + parentDummyChains = require("./parent-dummy-chains"), + removeEmptyRanks = require("./util").removeEmptyRanks, + nestingGraph = require("./nesting-graph"), + addBorderSegments = require("./add-border-segments"), + coordinateSystem = require("./coordinate-system"), + order = require("./order"), + position = require("./position"), + util = require("./util"), + Graph = require("./graphlib").Graph; + +module.exports = layout; + +function layout(g, opts) { + var time = opts && opts.debugTiming ? util.time : util.notime; + time("layout", function() { + var layoutGraph = time(" buildLayoutGraph", + function() { return buildLayoutGraph(g); }); + time(" runLayout", function() { runLayout(layoutGraph, time); }); + time(" updateInputGraph", function() { updateInputGraph(g, layoutGraph); }); + }); +} + +function runLayout(g, time) { + time(" makeSpaceForEdgeLabels", function() { makeSpaceForEdgeLabels(g); }); + time(" removeSelfEdges", function() { removeSelfEdges(g); }); + time(" acyclic", function() { acyclic.run(g); }); + time(" nestingGraph.run", function() { nestingGraph.run(g); }); + time(" rank", function() { rank(util.asNonCompoundGraph(g)); }); + time(" injectEdgeLabelProxies", function() { injectEdgeLabelProxies(g); }); + time(" removeEmptyRanks", function() { removeEmptyRanks(g); }); + time(" nestingGraph.cleanup", function() { nestingGraph.cleanup(g); }); + time(" normalizeRanks", function() { normalizeRanks(g); }); + time(" assignRankMinMax", function() { assignRankMinMax(g); }); + time(" removeEdgeLabelProxies", function() { removeEdgeLabelProxies(g); }); + time(" normalize.run", function() { normalize.run(g); }); + time(" parentDummyChains", function() { parentDummyChains(g); }); + time(" addBorderSegments", function() { addBorderSegments(g); }); + time(" order", function() { order(g); }); + time(" insertSelfEdges", function() { insertSelfEdges(g); }); + time(" adjustCoordinateSystem", function() { coordinateSystem.adjust(g); }); + time(" position", function() { position(g); }); + time(" positionSelfEdges", function() { positionSelfEdges(g); }); + time(" removeBorderNodes", function() { removeBorderNodes(g); }); + time(" normalize.undo", function() { normalize.undo(g); }); + time(" fixupEdgeLabelCoords", function() { fixupEdgeLabelCoords(g); }); + time(" undoCoordinateSystem", function() { coordinateSystem.undo(g); }); + time(" translateGraph", function() { translateGraph(g); }); + time(" assignNodeIntersects", function() { assignNodeIntersects(g); }); + time(" reversePoints", function() { reversePointsForReversedEdges(g); }); + time(" acyclic.undo", function() { acyclic.undo(g); }); +} + +/* + * Copies final layout information from the layout graph back to the input + * graph. This process only copies whitelisted attributes from the layout graph + * to the input graph, so it serves as a good place to determine what + * attributes can influence layout. + */ +function updateInputGraph(inputGraph, layoutGraph) { + _.each(inputGraph.nodes(), function(v) { + var inputLabel = inputGraph.node(v), + layoutLabel = layoutGraph.node(v); + + if (inputLabel) { + inputLabel.x = layoutLabel.x; + inputLabel.y = layoutLabel.y; + + if (layoutGraph.children(v).length) { + inputLabel.width = layoutLabel.width; + inputLabel.height = layoutLabel.height; + } + } + }); + + _.each(inputGraph.edges(), function(e) { + var inputLabel = inputGraph.edge(e), + layoutLabel = layoutGraph.edge(e); + + inputLabel.points = layoutLabel.points; + if (_.has(layoutLabel, "x")) { + inputLabel.x = layoutLabel.x; + inputLabel.y = layoutLabel.y; + } + }); + + inputGraph.graph().width = layoutGraph.graph().width; + inputGraph.graph().height = layoutGraph.graph().height; +} + +var graphNumAttrs = ["nodesep", "edgesep", "ranksep", "marginx", "marginy"], + graphDefaults = { ranksep: 50, edgesep: 20, nodesep: 50, rankdir: "tb" }, + graphAttrs = ["acyclicer", "ranker", "rankdir", "align"], + nodeNumAttrs = ["width", "height"], + nodeDefaults = { width: 0, height: 0 }, + edgeNumAttrs = ["minlen", "weight", "width", "height", "labeloffset"], + edgeDefaults = { + minlen: 1, weight: 1, width: 0, height: 0, + labeloffset: 10, labelpos: "r" + }, + edgeAttrs = ["labelpos"]; + +/* + * Constructs a new graph from the input graph, which can be used for layout. + * This process copies only whitelisted attributes from the input graph to the + * layout graph. Thus this function serves as a good place to determine what + * attributes can influence layout. + */ +function buildLayoutGraph(inputGraph) { + var g = new Graph({ multigraph: true, compound: true }), + graph = canonicalize(inputGraph.graph()); + + g.setGraph(_.merge({}, + graphDefaults, + selectNumberAttrs(graph, graphNumAttrs), + _.pick(graph, graphAttrs))); + + _.each(inputGraph.nodes(), function(v) { + var node = canonicalize(inputGraph.node(v)); + g.setNode(v, _.defaults(selectNumberAttrs(node, nodeNumAttrs), nodeDefaults)); + g.setParent(v, inputGraph.parent(v)); + }); + + _.each(inputGraph.edges(), function(e) { + var edge = canonicalize(inputGraph.edge(e)); + g.setEdge(e, _.merge({}, + edgeDefaults, + selectNumberAttrs(edge, edgeNumAttrs), + _.pick(edge, edgeAttrs))); + }); + + return g; +} + +/* + * This idea comes from the Gansner paper: to account for edge labels in our + * layout we split each rank in half by doubling minlen and halving ranksep. + * Then we can place labels at these mid-points between nodes. + * + * We also add some minimal padding to the width to push the label for the edge + * away from the edge itself a bit. + */ +function makeSpaceForEdgeLabels(g) { + var graph = g.graph(); + graph.ranksep /= 2; + _.each(g.edges(), function(e) { + var edge = g.edge(e); + edge.minlen *= 2; + if (edge.labelpos.toLowerCase() !== "c") { + if (graph.rankdir === "TB" || graph.rankdir === "BT") { + edge.width += edge.labeloffset; + } else { + edge.height += edge.labeloffset; + } + } + }); +} + +/* + * Creates temporary dummy nodes that capture the rank in which each edge's + * label is going to, if it has one of non-zero width and height. We do this + * so that we can safely remove empty ranks while preserving balance for the + * label's position. + */ +function injectEdgeLabelProxies(g) { + _.each(g.edges(), function(e) { + var edge = g.edge(e); + if (edge.width && edge.height) { + var v = g.node(e.v), + w = g.node(e.w), + label = { rank: (w.rank - v.rank) / 2 + v.rank, e: e }; + util.addDummyNode(g, "edge-proxy", label, "_ep"); + } + }); +} + +function assignRankMinMax(g) { + var maxRank = 0; + _.each(g.nodes(), function(v) { + var node = g.node(v); + if (node.borderTop) { + node.minRank = g.node(node.borderTop).rank; + node.maxRank = g.node(node.borderBottom).rank; + maxRank = _.max(maxRank, node.maxRank); + } + }); + g.graph().maxRank = maxRank; +} + +function removeEdgeLabelProxies(g) { + _.each(g.nodes(), function(v) { + var node = g.node(v); + if (node.dummy === "edge-proxy") { + g.edge(node.e).labelRank = node.rank; + g.removeNode(v); + } + }); +} + +function translateGraph(g) { + var minX = Number.POSITIVE_INFINITY, + maxX = 0, + minY = Number.POSITIVE_INFINITY, + maxY = 0, + graphLabel = g.graph(), + marginX = graphLabel.marginx || 0, + marginY = graphLabel.marginy || 0; + + function getExtremes(attrs) { + var x = attrs.x, + y = attrs.y, + w = attrs.width, + h = attrs.height; + minX = Math.min(minX, x - w / 2); + maxX = Math.max(maxX, x + w / 2); + minY = Math.min(minY, y - h / 2); + maxY = Math.max(maxY, y + h / 2); + } + + _.each(g.nodes(), function(v) { getExtremes(g.node(v)); }); + _.each(g.edges(), function(e) { + var edge = g.edge(e); + if (_.has(edge, "x")) { + getExtremes(edge); + } + }); + + minX -= marginX; + minY -= marginY; + + _.each(g.nodes(), function(v) { + var node = g.node(v); + node.x -= minX; + node.y -= minY; + }); + + _.each(g.edges(), function(e) { + var edge = g.edge(e); + _.each(edge.points, function(p) { + p.x -= minX; + p.y -= minY; + }); + if (_.has(edge, "x")) { edge.x -= minX; } + if (_.has(edge, "y")) { edge.y -= minY; } + }); + + graphLabel.width = maxX - minX + marginX; + graphLabel.height = maxY - minY + marginY; +} + +function assignNodeIntersects(g) { + _.each(g.edges(), function(e) { + var edge = g.edge(e), + nodeV = g.node(e.v), + nodeW = g.node(e.w), + p1, p2; + if (!edge.points) { + edge.points = []; + p1 = nodeW; + p2 = nodeV; + } else { + p1 = edge.points[0]; + p2 = edge.points[edge.points.length - 1]; + } + edge.points.unshift(util.intersectRect(nodeV, p1)); + edge.points.push(util.intersectRect(nodeW, p2)); + }); +} + +function fixupEdgeLabelCoords(g) { + _.each(g.edges(), function(e) { + var edge = g.edge(e); + if (_.has(edge, "x")) { + if (edge.labelpos === "l" || edge.labelpos === "r") { + edge.width -= edge.labeloffset; + } + switch (edge.labelpos) { + case "l": edge.x -= edge.width / 2 + edge.labeloffset; break; + case "r": edge.x += edge.width / 2 + edge.labeloffset; break; + } + } + }); +} + +function reversePointsForReversedEdges(g) { + _.each(g.edges(), function(e) { + var edge = g.edge(e); + if (edge.reversed) { + edge.points.reverse(); + } + }); +} + +function removeBorderNodes(g) { + _.each(g.nodes(), function(v) { + if (g.children(v).length) { + var node = g.node(v), + t = g.node(node.borderTop), + b = g.node(node.borderBottom), + l = g.node(_.last(node.borderLeft)), + r = g.node(_.last(node.borderRight)); + + node.width = Math.abs(r.x - l.x); + node.height = Math.abs(b.y - t.y); + node.x = l.x + node.width / 2; + node.y = t.y + node.height / 2; + } + }); + + _.each(g.nodes(), function(v) { + if (g.node(v).dummy === "border") { + g.removeNode(v); + } + }); +} + +function removeSelfEdges(g) { + _.each(g.edges(), function(e) { + if (e.v === e.w) { + var node = g.node(e.v); + if (!node.selfEdges) { + node.selfEdges = []; + } + node.selfEdges.push({ e: e, label: g.edge(e) }); + g.removeEdge(e); + } + }); +} + +function insertSelfEdges(g) { + var layers = util.buildLayerMatrix(g); + _.each(layers, function(layer) { + var orderShift = 0; + _.each(layer, function(v, i) { + var node = g.node(v); + node.order = i + orderShift; + _.each(node.selfEdges, function(selfEdge) { + util.addDummyNode(g, "selfedge", { + width: selfEdge.label.width, + height: selfEdge.label.height, + rank: node.rank, + order: i + (++orderShift), + e: selfEdge.e, + label: selfEdge.label + }, "_se"); + }); + delete node.selfEdges; + }); + }); +} + +function positionSelfEdges(g) { + _.each(g.nodes(), function(v) { + var node = g.node(v); + if (node.dummy === "selfedge") { + var selfNode = g.node(node.e.v), + x = selfNode.x + selfNode.width / 2, + y = selfNode.y, + dx = node.x - x, + dy = selfNode.height / 2; + g.setEdge(node.e, node.label); + g.removeNode(v); + node.label.points = [ + { x: x + 2 * dx / 3, y: y - dy }, + { x: x + 5 * dx / 6, y: y - dy }, + { x: x + dx , y: y }, + { x: x + 5 * dx / 6, y: y + dy }, + { x: x + 2 * dx / 3, y: y + dy }, + ]; + node.label.x = node.x; + node.label.y = node.y; + } + }); +} + +function selectNumberAttrs(obj, attrs) { + return _.mapValues(_.pick(obj, attrs), Number); +} + +function canonicalize(attrs) { + var newAttrs = {}; + _.each(attrs, function(v, k) { + newAttrs[k.toLowerCase()] = v; + }); + return newAttrs; +} + +},{"./acyclic":2,"./add-border-segments":3,"./coordinate-system":4,"./graphlib":7,"./lodash":10,"./nesting-graph":11,"./normalize":12,"./order":17,"./parent-dummy-chains":22,"./position":24,"./rank":26,"./util":29}],10:[function(require,module,exports){ +/* global window */ + +var lodash; + +if (typeof require === "function") { + try { + lodash = require("lodash"); + } catch (e) {} +} + +if (!lodash) { + lodash = window._; +} + +module.exports = lodash; + +},{"lodash":51}],11:[function(require,module,exports){ +var _ = require("./lodash"), + util = require("./util"); + +module.exports = { + run: run, + cleanup: cleanup +}; + +/* + * A nesting graph creates dummy nodes for the tops and bottoms of subgraphs, + * adds appropriate edges to ensure that all cluster nodes are placed between + * these boundries, and ensures that the graph is connected. + * + * In addition we ensure, through the use of the minlen property, that nodes + * and subgraph border nodes to not end up on the same rank. + * + * Preconditions: + * + * 1. Input graph is a DAG + * 2. Nodes in the input graph has a minlen attribute + * + * Postconditions: + * + * 1. Input graph is connected. + * 2. Dummy nodes are added for the tops and bottoms of subgraphs. + * 3. The minlen attribute for nodes is adjusted to ensure nodes do not + * get placed on the same rank as subgraph border nodes. + * + * The nesting graph idea comes from Sander, "Layout of Compound Directed + * Graphs." + */ +function run(g) { + var root = util.addDummyNode(g, "root", {}, "_root"), + depths = treeDepths(g), + height = _.max(depths) - 1, + nodeSep = 2 * height + 1; + + g.graph().nestingRoot = root; + + // Multiply minlen by nodeSep to align nodes on non-border ranks. + _.each(g.edges(), function(e) { g.edge(e).minlen *= nodeSep; }); + + // Calculate a weight that is sufficient to keep subgraphs vertically compact + var weight = sumWeights(g) + 1; + + // Create border nodes and link them up + _.each(g.children(), function(child) { + dfs(g, root, nodeSep, weight, height, depths, child); + }); + + // Save the multiplier for node layers for later removal of empty border + // layers. + g.graph().nodeRankFactor = nodeSep; +} + +function dfs(g, root, nodeSep, weight, height, depths, v) { + var children = g.children(v); + if (!children.length) { + if (v !== root) { + g.setEdge(root, v, { weight: 0, minlen: nodeSep }); + } + return; + } + + var top = util.addBorderNode(g, "_bt"), + bottom = util.addBorderNode(g, "_bb"), + label = g.node(v); + + g.setParent(top, v); + label.borderTop = top; + g.setParent(bottom, v); + label.borderBottom = bottom; + + _.each(children, function(child) { + dfs(g, root, nodeSep, weight, height, depths, child); + + var childNode = g.node(child), + childTop = childNode.borderTop ? childNode.borderTop : child, + childBottom = childNode.borderBottom ? childNode.borderBottom : child, + thisWeight = childNode.borderTop ? weight : 2 * weight, + minlen = childTop !== childBottom ? 1 : height - depths[v] + 1; + + g.setEdge(top, childTop, { + weight: thisWeight, + minlen: minlen, + nestingEdge: true + }); + + g.setEdge(childBottom, bottom, { + weight: thisWeight, + minlen: minlen, + nestingEdge: true + }); + }); + + if (!g.parent(v)) { + g.setEdge(root, top, { weight: 0, minlen: height + depths[v] }); + } +} + +function treeDepths(g) { + var depths = {}; + function dfs(v, depth) { + var children = g.children(v); + if (children && children.length) { + _.each(children, function(child) { + dfs(child, depth + 1); + }); + } + depths[v] = depth; + } + _.each(g.children(), function(v) { dfs(v, 1); }); + return depths; +} + +function sumWeights(g) { + return _.reduce(g.edges(), function(acc, e) { + return acc + g.edge(e).weight; + }, 0); +} + +function cleanup(g) { + var graphLabel = g.graph(); + g.removeNode(graphLabel.nestingRoot); + delete graphLabel.nestingRoot; + _.each(g.edges(), function(e) { + var edge = g.edge(e); + if (edge.nestingEdge) { + g.removeEdge(e); + } + }); +} + +},{"./lodash":10,"./util":29}],12:[function(require,module,exports){ +"use strict"; + +var _ = require("./lodash"), + util = require("./util"); + +module.exports = { + run: run, + undo: undo +}; + +/* + * Breaks any long edges in the graph into short segments that span 1 layer + * each. This operation is undoable with the denormalize function. + * + * Pre-conditions: + * + * 1. The input graph is a DAG. + * 2. Each node in the graph has a "rank" property. + * + * Post-condition: + * + * 1. All edges in the graph have a length of 1. + * 2. Dummy nodes are added where edges have been split into segments. + * 3. The graph is augmented with a "dummyChains" attribute which contains + * the first dummy in each chain of dummy nodes produced. + */ +function run(g) { + g.graph().dummyChains = []; + _.each(g.edges(), function(edge) { normalizeEdge(g, edge); }); +} + +function normalizeEdge(g, e) { + var v = e.v, + vRank = g.node(v).rank, + w = e.w, + wRank = g.node(w).rank, + name = e.name, + edgeLabel = g.edge(e), + labelRank = edgeLabel.labelRank; + + if (wRank === vRank + 1) return; + + g.removeEdge(e); + + var dummy, attrs, i; + for (i = 0, ++vRank; vRank < wRank; ++i, ++vRank) { + edgeLabel.points = []; + attrs = { + width: 0, height: 0, + edgeLabel: edgeLabel, edgeObj: e, + rank: vRank + }; + dummy = util.addDummyNode(g, "edge", attrs, "_d"); + if (vRank === labelRank) { + attrs.width = edgeLabel.width; + attrs.height = edgeLabel.height; + attrs.dummy = "edge-label"; + attrs.labelpos = edgeLabel.labelpos; + } + g.setEdge(v, dummy, { weight: edgeLabel.weight }, name); + if (i === 0) { + g.graph().dummyChains.push(dummy); + } + v = dummy; + } + + g.setEdge(v, w, { weight: edgeLabel.weight }, name); +} + +function undo(g) { + _.each(g.graph().dummyChains, function(v) { + var node = g.node(v), + origLabel = node.edgeLabel, + w; + g.setEdge(node.edgeObj, origLabel); + while (node.dummy) { + w = g.successors(v)[0]; + g.removeNode(v); + origLabel.points.push({ x: node.x, y: node.y }); + if (node.dummy === "edge-label") { + origLabel.x = node.x; + origLabel.y = node.y; + origLabel.width = node.width; + origLabel.height = node.height; + } + v = w; + node = g.node(v); + } + }); +} + +},{"./lodash":10,"./util":29}],13:[function(require,module,exports){ +var _ = require("../lodash"); + +module.exports = addSubgraphConstraints; + +function addSubgraphConstraints(g, cg, vs) { + var prev = {}, + rootPrev; + + _.each(vs, function(v) { + var child = g.parent(v), + parent, + prevChild; + while (child) { + parent = g.parent(child); + if (parent) { + prevChild = prev[parent]; + prev[parent] = child; + } else { + prevChild = rootPrev; + rootPrev = child; + } + if (prevChild && prevChild !== child) { + cg.setEdge(prevChild, child); + return; + } + child = parent; + } + }); + + /* + function dfs(v) { + var children = v ? g.children(v) : g.children(); + if (children.length) { + var min = Number.POSITIVE_INFINITY, + subgraphs = []; + _.each(children, function(child) { + var childMin = dfs(child); + if (g.children(child).length) { + subgraphs.push({ v: child, order: childMin }); + } + min = Math.min(min, childMin); + }); + _.reduce(_.sortBy(subgraphs, "order"), function(prev, curr) { + cg.setEdge(prev.v, curr.v); + return curr; + }); + return min; + } + return g.node(v).order; + } + dfs(undefined); + */ +} + +},{"../lodash":10}],14:[function(require,module,exports){ +var _ = require("../lodash"); + +module.exports = barycenter; + +function barycenter(g, movable) { + return _.map(movable, function(v) { + var inV = g.inEdges(v); + if (!inV.length) { + return { v: v }; + } else { + var result = _.reduce(inV, function(acc, e) { + var edge = g.edge(e), + nodeU = g.node(e.v); + return { + sum: acc.sum + (edge.weight * nodeU.order), + weight: acc.weight + edge.weight + }; + }, { sum: 0, weight: 0 }); + + return { + v: v, + barycenter: result.sum / result.weight, + weight: result.weight + }; + } + }); +} + + +},{"../lodash":10}],15:[function(require,module,exports){ +var _ = require("../lodash"), + Graph = require("../graphlib").Graph; + +module.exports = buildLayerGraph; + +/* + * Constructs a graph that can be used to sort a layer of nodes. The graph will + * contain all base and subgraph nodes from the request layer in their original + * hierarchy and any edges that are incident on these nodes and are of the type + * requested by the "relationship" parameter. + * + * Nodes from the requested rank that do not have parents are assigned a root + * node in the output graph, which is set in the root graph attribute. This + * makes it easy to walk the hierarchy of movable nodes during ordering. + * + * Pre-conditions: + * + * 1. Input graph is a DAG + * 2. Base nodes in the input graph have a rank attribute + * 3. Subgraph nodes in the input graph has minRank and maxRank attributes + * 4. Edges have an assigned weight + * + * Post-conditions: + * + * 1. Output graph has all nodes in the movable rank with preserved + * hierarchy. + * 2. Root nodes in the movable layer are made children of the node + * indicated by the root attribute of the graph. + * 3. Non-movable nodes incident on movable nodes, selected by the + * relationship parameter, are included in the graph (without hierarchy). + * 4. Edges incident on movable nodes, selected by the relationship + * parameter, are added to the output graph. + * 5. The weights for copied edges are aggregated as need, since the output + * graph is not a multi-graph. + */ +function buildLayerGraph(g, rank, relationship) { + var root = createRootNode(g), + result = new Graph({ compound: true }).setGraph({ root: root }) + .setDefaultNodeLabel(function(v) { return g.node(v); }); + + _.each(g.nodes(), function(v) { + var node = g.node(v), + parent = g.parent(v); + + if (node.rank === rank || node.minRank <= rank && rank <= node.maxRank) { + result.setNode(v); + result.setParent(v, parent || root); + + // This assumes we have only short edges! + _.each(g[relationship](v), function(e) { + var u = e.v === v ? e.w : e.v, + edge = result.edge(u, v), + weight = !_.isUndefined(edge) ? edge.weight : 0; + result.setEdge(u, v, { weight: g.edge(e).weight + weight }); + }); + + if (_.has(node, "minRank")) { + result.setNode(v, { + borderLeft: node.borderLeft[rank], + borderRight: node.borderRight[rank] + }); + } + } + }); + + return result; +} + +function createRootNode(g) { + var v; + while (g.hasNode((v = _.uniqueId("_root")))); + return v; +} + +},{"../graphlib":7,"../lodash":10}],16:[function(require,module,exports){ +"use strict"; + +var _ = require("../lodash"); + +module.exports = crossCount; + +/* + * A function that takes a layering (an array of layers, each with an array of + * ordererd nodes) and a graph and returns a weighted crossing count. + * + * Pre-conditions: + * + * 1. Input graph must be simple (not a multigraph), directed, and include + * only simple edges. + * 2. Edges in the input graph must have assigned weights. + * + * Post-conditions: + * + * 1. The graph and layering matrix are left unchanged. + * + * This algorithm is derived from Barth, et al., "Bilayer Cross Counting." + */ +function crossCount(g, layering) { + var cc = 0; + for (var i = 1; i < layering.length; ++i) { + cc += twoLayerCrossCount(g, layering[i-1], layering[i]); + } + return cc; +} + +function twoLayerCrossCount(g, northLayer, southLayer) { + // Sort all of the edges between the north and south layers by their position + // in the north layer and then the south. Map these edges to the position of + // their head in the south layer. + var southPos = _.zipObject(southLayer, + _.map(southLayer, function (v, i) { return i; })); + var southEntries = _.flatten(_.map(northLayer, function(v) { + return _.chain(g.outEdges(v)) + .map(function(e) { + return { pos: southPos[e.w], weight: g.edge(e).weight }; + }) + .sortBy("pos") + .value(); + }), true); + + // Build the accumulator tree + var firstIndex = 1; + while (firstIndex < southLayer.length) firstIndex <<= 1; + var treeSize = 2 * firstIndex - 1; + firstIndex -= 1; + var tree = _.map(new Array(treeSize), function() { return 0; }); + + // Calculate the weighted crossings + var cc = 0; + _.each(southEntries.forEach(function(entry) { + var index = entry.pos + firstIndex; + tree[index] += entry.weight; + var weightSum = 0; + while (index > 0) { + if (index % 2) { + weightSum += tree[index + 1]; + } + index = (index - 1) >> 1; + tree[index] += entry.weight; + } + cc += entry.weight * weightSum; + })); + + return cc; +} + +},{"../lodash":10}],17:[function(require,module,exports){ +"use strict"; + +var _ = require("../lodash"), + initOrder = require("./init-order"), + crossCount = require("./cross-count"), + sortSubgraph = require("./sort-subgraph"), + buildLayerGraph = require("./build-layer-graph"), + addSubgraphConstraints = require("./add-subgraph-constraints"), + Graph = require("../graphlib").Graph, + util = require("../util"); + +module.exports = order; + +/* + * Applies heuristics to minimize edge crossings in the graph and sets the best + * order solution as an order attribute on each node. + * + * Pre-conditions: + * + * 1. Graph must be DAG + * 2. Graph nodes must be objects with a "rank" attribute + * 3. Graph edges must have the "weight" attribute + * + * Post-conditions: + * + * 1. Graph nodes will have an "order" attribute based on the results of the + * algorithm. + */ +function order(g) { + var maxRank = util.maxRank(g), + downLayerGraphs = buildLayerGraphs(g, _.range(1, maxRank + 1), "inEdges"), + upLayerGraphs = buildLayerGraphs(g, _.range(maxRank - 1, -1, -1), "outEdges"); + + var layering = initOrder(g); + assignOrder(g, layering); + + var bestCC = Number.POSITIVE_INFINITY, + best; + + for (var i = 0, lastBest = 0; lastBest < 4; ++i, ++lastBest) { + sweepLayerGraphs(i % 2 ? downLayerGraphs : upLayerGraphs, i % 4 >= 2); + + layering = util.buildLayerMatrix(g); + var cc = crossCount(g, layering); + if (cc < bestCC) { + lastBest = 0; + best = _.cloneDeep(layering); + bestCC = cc; + } + } + + assignOrder(g, best); +} + +function buildLayerGraphs(g, ranks, relationship) { + return _.map(ranks, function(rank) { + return buildLayerGraph(g, rank, relationship); + }); +} + +function sweepLayerGraphs(layerGraphs, biasRight) { + var cg = new Graph(); + _.each(layerGraphs, function(lg) { + var root = lg.graph().root; + var sorted = sortSubgraph(lg, root, cg, biasRight); + _.each(sorted.vs, function(v, i) { + lg.node(v).order = i; + }); + addSubgraphConstraints(lg, cg, sorted.vs); + }); +} + +function assignOrder(g, layering) { + _.each(layering, function(layer) { + _.each(layer, function(v, i) { + g.node(v).order = i; + }); + }); +} + +},{"../graphlib":7,"../lodash":10,"../util":29,"./add-subgraph-constraints":13,"./build-layer-graph":15,"./cross-count":16,"./init-order":18,"./sort-subgraph":20}],18:[function(require,module,exports){ +"use strict"; + +var _ = require("../lodash"); + +module.exports = initOrder; + +/* + * Assigns an initial order value for each node by performing a DFS search + * starting from nodes in the first rank. Nodes are assigned an order in their + * rank as they are first visited. + * + * This approach comes from Gansner, et al., "A Technique for Drawing Directed + * Graphs." + * + * Returns a layering matrix with an array per layer and each layer sorted by + * the order of its nodes. + */ +function initOrder(g) { + var visited = {}, + simpleNodes = _.filter(g.nodes(), function(v) { + return !g.children(v).length; + }), + maxRank = _.max(_.map(simpleNodes, function(v) { return g.node(v).rank; })), + layers = _.map(_.range(maxRank + 1), function() { return []; }); + + function dfs(v) { + if (_.has(visited, v)) return; + visited[v] = true; + var node = g.node(v); + layers[node.rank].push(v); + _.each(g.successors(v), dfs); + } + + var orderedVs = _.sortBy(simpleNodes, function(v) { return g.node(v).rank; }); + _.each(orderedVs, dfs); + + return layers; +} + +},{"../lodash":10}],19:[function(require,module,exports){ +"use strict"; + +var _ = require("../lodash"); + +module.exports = resolveConflicts; + +/* + * Given a list of entries of the form {v, barycenter, weight} and a + * constraint graph this function will resolve any conflicts between the + * constraint graph and the barycenters for the entries. If the barycenters for + * an entry would violate a constraint in the constraint graph then we coalesce + * the nodes in the conflict into a new node that respects the contraint and + * aggregates barycenter and weight information. + * + * This implementation is based on the description in Forster, "A Fast and + * Simple Hueristic for Constrained Two-Level Crossing Reduction," thought it + * differs in some specific details. + * + * Pre-conditions: + * + * 1. Each entry has the form {v, barycenter, weight}, or if the node has + * no barycenter, then {v}. + * + * Returns: + * + * A new list of entries of the form {vs, i, barycenter, weight}. The list + * `vs` may either be a singleton or it may be an aggregation of nodes + * ordered such that they do not violate constraints from the constraint + * graph. The property `i` is the lowest original index of any of the + * elements in `vs`. + */ +function resolveConflicts(entries, cg) { + var mappedEntries = {}; + _.each(entries, function(entry, i) { + var tmp = mappedEntries[entry.v] = { + indegree: 0, + "in": [], + out: [], + vs: [entry.v], + i: i + }; + if (!_.isUndefined(entry.barycenter)) { + tmp.barycenter = entry.barycenter; + tmp.weight = entry.weight; + } + }); + + _.each(cg.edges(), function(e) { + var entryV = mappedEntries[e.v], + entryW = mappedEntries[e.w]; + if (!_.isUndefined(entryV) && !_.isUndefined(entryW)) { + entryW.indegree++; + entryV.out.push(mappedEntries[e.w]); + } + }); + + var sourceSet = _.filter(mappedEntries, function(entry) { + return !entry.indegree; + }); + + return doResolveConflicts(sourceSet); +} + +function doResolveConflicts(sourceSet) { + var entries = []; + + function handleIn(vEntry) { + return function(uEntry) { + if (uEntry.merged) { + return; + } + if (_.isUndefined(uEntry.barycenter) || + _.isUndefined(vEntry.barycenter) || + uEntry.barycenter >= vEntry.barycenter) { + mergeEntries(vEntry, uEntry); + } + }; + } + + function handleOut(vEntry) { + return function(wEntry) { + wEntry["in"].push(vEntry); + if (--wEntry.indegree === 0) { + sourceSet.push(wEntry); + } + }; + } + + while (sourceSet.length) { + var entry = sourceSet.pop(); + entries.push(entry); + _.each(entry["in"].reverse(), handleIn(entry)); + _.each(entry.out, handleOut(entry)); + } + + return _.chain(entries) + .filter(function(entry) { return !entry.merged; }) + .map(function(entry) { + return _.pick(entry, ["vs", "i", "barycenter", "weight"]); + }) + .value(); +} + +function mergeEntries(target, source) { + var sum = 0, + weight = 0; + + if (target.weight) { + sum += target.barycenter * target.weight; + weight += target.weight; + } + + if (source.weight) { + sum += source.barycenter * source.weight; + weight += source.weight; + } + + target.vs = source.vs.concat(target.vs); + target.barycenter = sum / weight; + target.weight = weight; + target.i = Math.min(source.i, target.i); + source.merged = true; +} + +},{"../lodash":10}],20:[function(require,module,exports){ +var _ = require("../lodash"), + barycenter = require("./barycenter"), + resolveConflicts = require("./resolve-conflicts"), + sort = require("./sort"); + +module.exports = sortSubgraph; + +function sortSubgraph(g, v, cg, biasRight) { + var movable = g.children(v), + node = g.node(v), + bl = node ? node.borderLeft : undefined, + br = node ? node.borderRight: undefined, + subgraphs = {}; + + if (bl) { + movable = _.filter(movable, function(w) { + return w !== bl && w !== br; + }); + } + + var barycenters = barycenter(g, movable); + _.each(barycenters, function(entry) { + if (g.children(entry.v).length) { + var subgraphResult = sortSubgraph(g, entry.v, cg, biasRight); + subgraphs[entry.v] = subgraphResult; + if (_.has(subgraphResult, "barycenter")) { + mergeBarycenters(entry, subgraphResult); + } + } + }); + + var entries = resolveConflicts(barycenters, cg); + expandSubgraphs(entries, subgraphs); + + var result = sort(entries, biasRight); + + if (bl) { + result.vs = _.flatten([bl, result.vs, br], true); + if (g.predecessors(bl).length) { + var blPred = g.node(g.predecessors(bl)[0]), + brPred = g.node(g.predecessors(br)[0]); + if (!_.has(result, "barycenter")) { + result.barycenter = 0; + result.weight = 0; + } + result.barycenter = (result.barycenter * result.weight + + blPred.order + brPred.order) / (result.weight + 2); + result.weight += 2; + } + } + + return result; +} + +function expandSubgraphs(entries, subgraphs) { + _.each(entries, function(entry) { + entry.vs = _.flatten(entry.vs.map(function(v) { + if (subgraphs[v]) { + return subgraphs[v].vs; + } + return v; + }), true); + }); +} + +function mergeBarycenters(target, other) { + if (!_.isUndefined(target.barycenter)) { + target.barycenter = (target.barycenter * target.weight + + other.barycenter * other.weight) / + (target.weight + other.weight); + target.weight += other.weight; + } else { + target.barycenter = other.barycenter; + target.weight = other.weight; + } +} + +},{"../lodash":10,"./barycenter":14,"./resolve-conflicts":19,"./sort":21}],21:[function(require,module,exports){ +var _ = require("../lodash"), + util = require("../util"); + +module.exports = sort; + +function sort(entries, biasRight) { + var parts = util.partition(entries, function(entry) { + return _.has(entry, "barycenter"); + }); + var sortable = parts.lhs, + unsortable = _.sortBy(parts.rhs, function(entry) { return -entry.i; }), + vs = [], + sum = 0, + weight = 0, + vsIndex = 0; + + sortable.sort(compareWithBias(!!biasRight)); + + vsIndex = consumeUnsortable(vs, unsortable, vsIndex); + + _.each(sortable, function (entry) { + vsIndex += entry.vs.length; + vs.push(entry.vs); + sum += entry.barycenter * entry.weight; + weight += entry.weight; + vsIndex = consumeUnsortable(vs, unsortable, vsIndex); + }); + + var result = { vs: _.flatten(vs, true) }; + if (weight) { + result.barycenter = sum / weight; + result.weight = weight; + } + return result; +} + +function consumeUnsortable(vs, unsortable, index) { + var last; + while (unsortable.length && (last = _.last(unsortable)).i <= index) { + unsortable.pop(); + vs.push(last.vs); + index++; + } + return index; +} + +function compareWithBias(bias) { + return function(entryV, entryW) { + if (entryV.barycenter < entryW.barycenter) { + return -1; + } else if (entryV.barycenter > entryW.barycenter) { + return 1; + } + + return !bias ? entryV.i - entryW.i : entryW.i - entryV.i; + }; +} + +},{"../lodash":10,"../util":29}],22:[function(require,module,exports){ +var _ = require("./lodash"); + +module.exports = parentDummyChains; + +function parentDummyChains(g) { + var postorderNums = postorder(g); + + _.each(g.graph().dummyChains, function(v) { + var node = g.node(v), + edgeObj = node.edgeObj, + pathData = findPath(g, postorderNums, edgeObj.v, edgeObj.w), + path = pathData.path, + lca = pathData.lca, + pathIdx = 0, + pathV = path[pathIdx], + ascending = true; + + while (v !== edgeObj.w) { + node = g.node(v); + + if (ascending) { + while ((pathV = path[pathIdx]) !== lca && + g.node(pathV).maxRank < node.rank) { + pathIdx++; + } + + if (pathV === lca) { + ascending = false; + } + } + + if (!ascending) { + while (pathIdx < path.length - 1 && + g.node(pathV = path[pathIdx + 1]).minRank <= node.rank) { + pathIdx++; + } + pathV = path[pathIdx]; + } + + g.setParent(v, pathV); + v = g.successors(v)[0]; + } + }); +} + +// Find a path from v to w through the lowest common ancestor (LCA). Return the +// full path and the LCA. +function findPath(g, postorderNums, v, w) { + var vPath = [], + wPath = [], + low = Math.min(postorderNums[v].low, postorderNums[w].low), + lim = Math.max(postorderNums[v].lim, postorderNums[w].lim), + parent, + lca; + + // Traverse up from v to find the LCA + parent = v; + do { + parent = g.parent(parent); + vPath.push(parent); + } while (parent && + (postorderNums[parent].low > low || lim > postorderNums[parent].lim)); + lca = parent; + + // Traverse from w to LCA + parent = w; + while ((parent = g.parent(parent)) !== lca) { + wPath.push(parent); + } + + return { path: vPath.concat(wPath.reverse()), lca: lca }; +} + +function postorder(g) { + var result = {}, + lim = 0; + + function dfs(v) { + var low = lim; + _.each(g.children(v), dfs); + result[v] = { low: low, lim: lim++ }; + } + _.each(g.children(), dfs); + + return result; +} + +},{"./lodash":10}],23:[function(require,module,exports){ +"use strict"; + +var _ = require("../lodash"), + Graph = require("../graphlib").Graph, + util = require("../util"); + +/* + * This module provides coordinate assignment based on Brandes and Köpf, "Fast + * and Simple Horizontal Coordinate Assignment." + */ + +module.exports = { + positionX: positionX, + findType1Conflicts: findType1Conflicts, + findType2Conflicts: findType2Conflicts, + addConflict: addConflict, + hasConflict: hasConflict, + verticalAlignment: verticalAlignment, + horizontalCompaction: horizontalCompaction, + alignCoordinates: alignCoordinates, + findSmallestWidthAlignment: findSmallestWidthAlignment, + balance: balance +}; + +/* + * Marks all edges in the graph with a type-1 conflict with the "type1Conflict" + * property. A type-1 conflict is one where a non-inner segment crosses an + * inner segment. An inner segment is an edge with both incident nodes marked + * with the "dummy" property. + * + * This algorithm scans layer by layer, starting with the second, for type-1 + * conflicts between the current layer and the previous layer. For each layer + * it scans the nodes from left to right until it reaches one that is incident + * on an inner segment. It then scans predecessors to determine if they have + * edges that cross that inner segment. At the end a final scan is done for all + * nodes on the current rank to see if they cross the last visited inner + * segment. + * + * This algorithm (safely) assumes that a dummy node will only be incident on a + * single node in the layers being scanned. + */ +function findType1Conflicts(g, layering) { + var conflicts = {}; + + function visitLayer(prevLayer, layer) { + var + // last visited node in the previous layer that is incident on an inner + // segment. + k0 = 0, + // Tracks the last node in this layer scanned for crossings with a type-1 + // segment. + scanPos = 0, + prevLayerLength = prevLayer.length, + lastNode = _.last(layer); + + _.each(layer, function(v, i) { + var w = findOtherInnerSegmentNode(g, v), + k1 = w ? g.node(w).order : prevLayerLength; + + if (w || v === lastNode) { + _.each(layer.slice(scanPos, i +1), function(scanNode) { + _.each(g.predecessors(scanNode), function(u) { + var uLabel = g.node(u), + uPos = uLabel.order; + if ((uPos < k0 || k1 < uPos) && + !(uLabel.dummy && g.node(scanNode).dummy)) { + addConflict(conflicts, u, scanNode); + } + }); + }); + scanPos = i + 1; + k0 = k1; + } + }); + + return layer; + } + + _.reduce(layering, visitLayer); + return conflicts; +} + +function findType2Conflicts(g, layering) { + var conflicts = {}; + + function scan(south, southPos, southEnd, prevNorthBorder, nextNorthBorder) { + var v; + _.each(_.range(southPos, southEnd), function(i) { + v = south[i]; + if (g.node(v).dummy) { + _.each(g.predecessors(v), function(u) { + var uNode = g.node(u); + if (uNode.dummy && + (uNode.order < prevNorthBorder || uNode.order > nextNorthBorder)) { + addConflict(conflicts, u, v); + } + }); + } + }); + } + + + function visitLayer(north, south) { + var prevNorthPos = -1, + nextNorthPos, + southPos = 0; + + _.each(south, function(v, southLookahead) { + if (g.node(v).dummy === "border") { + var predecessors = g.predecessors(v); + if (predecessors.length) { + nextNorthPos = g.node(predecessors[0]).order; + scan(south, southPos, southLookahead, prevNorthPos, nextNorthPos); + southPos = southLookahead; + prevNorthPos = nextNorthPos; + } + } + scan(south, southPos, south.length, nextNorthPos, north.length); + }); + + return south; + } + + _.reduce(layering, visitLayer); + return conflicts; +} + +function findOtherInnerSegmentNode(g, v) { + if (g.node(v).dummy) { + return _.find(g.predecessors(v), function(u) { + return g.node(u).dummy; + }); + } +} + +function addConflict(conflicts, v, w) { + if (v > w) { + var tmp = v; + v = w; + w = tmp; + } + + var conflictsV = conflicts[v]; + if (!conflictsV) { + conflicts[v] = conflictsV = {}; + } + conflictsV[w] = true; +} + +function hasConflict(conflicts, v, w) { + if (v > w) { + var tmp = v; + v = w; + w = tmp; + } + return _.has(conflicts[v], w); +} + +/* + * Try to align nodes into vertical "blocks" where possible. This algorithm + * attempts to align a node with one of its median neighbors. If the edge + * connecting a neighbor is a type-1 conflict then we ignore that possibility. + * If a previous node has already formed a block with a node after the node + * we're trying to form a block with, we also ignore that possibility - our + * blocks would be split in that scenario. + */ +function verticalAlignment(g, layering, conflicts, neighborFn) { + var root = {}, + align = {}, + pos = {}; + + // We cache the position here based on the layering because the graph and + // layering may be out of sync. The layering matrix is manipulated to + // generate different extreme alignments. + _.each(layering, function(layer) { + _.each(layer, function(v, order) { + root[v] = v; + align[v] = v; + pos[v] = order; + }); + }); + + _.each(layering, function(layer) { + var prevIdx = -1; + _.each(layer, function(v) { + var ws = neighborFn(v); + if (ws.length) { + ws = _.sortBy(ws, function(w) { return pos[w]; }); + var mp = (ws.length - 1) / 2; + for (var i = Math.floor(mp), il = Math.ceil(mp); i <= il; ++i) { + var w = ws[i]; + if (align[v] === v && + prevIdx < pos[w] && + !hasConflict(conflicts, v, w)) { + align[w] = v; + align[v] = root[v] = root[w]; + prevIdx = pos[w]; + } + } + } + }); + }); + + return { root: root, align: align }; +} + +function horizontalCompaction(g, layering, root, align, reverseSep) { + // This portion of the algorithm differs from BK due to a number of problems. + // Instead of their algorithm we construct a new block graph and do two + // sweeps. The first sweep places blocks with the smallest possible + // coordinates. The second sweep removes unused space by moving blocks to the + // greatest coordinates without violating separation. + var xs = {}, + blockG = buildBlockGraph(g, layering, root, reverseSep); + + // First pass, assign smallest coordinates via DFS + var visited = {}; + function pass1(v) { + if (!_.has(visited, v)) { + visited[v] = true; + xs[v] = _.reduce(blockG.inEdges(v), function(max, e) { + pass1(e.v); + return Math.max(max, xs[e.v] + blockG.edge(e)); + }, 0); + } + } + _.each(blockG.nodes(), pass1); + + var borderType = reverseSep ? "borderLeft" : "borderRight"; + function pass2(v) { + if (visited[v] !== 2) { + visited[v]++; + var node = g.node(v); + var min = _.reduce(blockG.outEdges(v), function(min, e) { + pass2(e.w); + return Math.min(min, xs[e.w] - blockG.edge(e)); + }, Number.POSITIVE_INFINITY); + if (min !== Number.POSITIVE_INFINITY && node.borderType !== borderType) { + xs[v] = Math.max(xs[v], min); + } + } + } + _.each(blockG.nodes(), pass2); + + // Assign x coordinates to all nodes + _.each(align, function(v) { + xs[v] = xs[root[v]]; + }); + + return xs; +} + + +function buildBlockGraph(g, layering, root, reverseSep) { + var blockGraph = new Graph(), + graphLabel = g.graph(), + sepFn = sep(graphLabel.nodesep, graphLabel.edgesep, reverseSep); + + _.each(layering, function(layer) { + var u; + _.each(layer, function(v) { + var vRoot = root[v]; + blockGraph.setNode(vRoot); + if (u) { + var uRoot = root[u], + prevMax = blockGraph.edge(uRoot, vRoot); + blockGraph.setEdge(uRoot, vRoot, Math.max(sepFn(g, v, u), prevMax || 0)); + } + u = v; + }); + }); + + return blockGraph; +} + +/* + * Returns the alignment that has the smallest width of the given alignments. + */ +function findSmallestWidthAlignment(g, xss) { + return _.min(xss, function(xs) { + var min = _.min(xs, function(x, v) { return x - width(g, v) / 2; }), + max = _.max(xs, function(x, v) { return x + width(g, v) / 2; }); + return max - min; + }); +} + +/* + * Align the coordinates of each of the layout alignments such that + * left-biased alignments have their minimum coordinate at the same point as + * the minimum coordinate of the smallest width alignment and right-biased + * alignments have their maximum coordinate at the same point as the maximum + * coordinate of the smallest width alignment. + */ +function alignCoordinates(xss, alignTo) { + var alignToMin = _.min(alignTo), + alignToMax = _.max(alignTo); + + _.each(["u", "d"], function(vert) { + _.each(["l", "r"], function(horiz) { + var alignment = vert + horiz, + xs = xss[alignment], + delta; + if (xs === alignTo) return; + + delta = horiz === "l" ? alignToMin - _.min(xs) : alignToMax - _.max(xs); + + if (delta) { + xss[alignment] = _.mapValues(xs, function(x) { return x + delta; }); + } + }); + }); +} + +function balance(xss, align) { + return _.mapValues(xss.ul, function(ignore, v) { + if (align) { + return xss[align.toLowerCase()][v]; + } else { + var xs = _.sortBy(_.pluck(xss, v)); + return (xs[1] + xs[2]) / 2; + } + }); +} + +function positionX(g) { + var layering = util.buildLayerMatrix(g), + conflicts = _.merge(findType1Conflicts(g, layering), + findType2Conflicts(g, layering)); + + var xss = {}, + adjustedLayering; + _.each(["u", "d"], function(vert) { + adjustedLayering = vert === "u" ? layering : _.values(layering).reverse(); + _.each(["l", "r"], function(horiz) { + if (horiz === "r") { + adjustedLayering = _.map(adjustedLayering, function(inner) { + return _.values(inner).reverse(); + }); + } + + var neighborFn = _.bind(vert === "u" ? g.predecessors : g.successors, g); + var align = verticalAlignment(g, adjustedLayering, conflicts, neighborFn); + var xs = horizontalCompaction(g, adjustedLayering, + align.root, align.align, + horiz === "r"); + if (horiz === "r") { + xs = _.mapValues(xs, function(x) { return -x; }); + } + xss[vert + horiz] = xs; + }); + }); + + var smallestWidth = findSmallestWidthAlignment(g, xss); + alignCoordinates(xss, smallestWidth); + return balance(xss, g.graph().align); +} + +function sep(nodeSep, edgeSep, reverseSep) { + return function(g, v, w) { + var vLabel = g.node(v), + wLabel = g.node(w), + sum = 0, + delta; + + sum += vLabel.width / 2; + if (_.has(vLabel, "labelpos")) { + switch (vLabel.labelpos.toLowerCase()) { + case "l": delta = -vLabel.width / 2; break; + case "r": delta = vLabel.width / 2; break; + } + } + if (delta) { + sum += reverseSep ? delta : -delta; + } + delta = 0; + + sum += (vLabel.dummy ? edgeSep : nodeSep) / 2; + sum += (wLabel.dummy ? edgeSep : nodeSep) / 2; + + sum += wLabel.width / 2; + if (_.has(wLabel, "labelpos")) { + switch (wLabel.labelpos.toLowerCase()) { + case "l": delta = wLabel.width / 2; break; + case "r": delta = -wLabel.width / 2; break; + } + } + if (delta) { + sum += reverseSep ? delta : -delta; + } + delta = 0; + + return sum; + }; +} + +function width(g, v) { + return g.node(v).width; +} + +},{"../graphlib":7,"../lodash":10,"../util":29}],24:[function(require,module,exports){ +"use strict"; + +var _ = require("../lodash"), + util = require("../util"), + positionX = require("./bk").positionX; + +module.exports = position; + +function position(g) { + g = util.asNonCompoundGraph(g); + + positionY(g); + _.each(positionX(g), function(x, v) { + g.node(v).x = x; + }); +} + +function positionY(g) { + var layering = util.buildLayerMatrix(g), + rankSep = g.graph().ranksep, + prevY = 0; + _.each(layering, function(layer) { + var maxHeight = _.max(_.map(layer, function(v) { return g.node(v).height; })); + _.each(layer, function(v) { + g.node(v).y = prevY + maxHeight / 2; + }); + prevY += maxHeight + rankSep; + }); +} + + +},{"../lodash":10,"../util":29,"./bk":23}],25:[function(require,module,exports){ +"use strict"; + +var _ = require("../lodash"), + Graph = require("../graphlib").Graph, + slack = require("./util").slack; + +module.exports = feasibleTree; + +/* + * Constructs a spanning tree with tight edges and adjusted the input node's + * ranks to achieve this. A tight edge is one that is has a length that matches + * its "minlen" attribute. + * + * The basic structure for this function is derived from Gansner, et al., "A + * Technique for Drawing Directed Graphs." + * + * Pre-conditions: + * + * 1. Graph must be a DAG. + * 2. Graph must be connected. + * 3. Graph must have at least one node. + * 5. Graph nodes must have been previously assigned a "rank" property that + * respects the "minlen" property of incident edges. + * 6. Graph edges must have a "minlen" property. + * + * Post-conditions: + * + * - Graph nodes will have their rank adjusted to ensure that all edges are + * tight. + * + * Returns a tree (undirected graph) that is constructed using only "tight" + * edges. + */ +function feasibleTree(g) { + var t = new Graph({ directed: false }); + + // Choose arbitrary node from which to start our tree + var start = g.nodes()[0], + size = g.nodeCount(); + t.setNode(start, {}); + + var edge, delta; + while (tightTree(t, g) < size) { + edge = findMinSlackEdge(t, g); + delta = t.hasNode(edge.v) ? slack(g, edge) : -slack(g, edge); + shiftRanks(t, g, delta); + } + + return t; +} + +/* + * Finds a maximal tree of tight edges and returns the number of nodes in the + * tree. + */ +function tightTree(t, g) { + function dfs(v) { + _.each(g.nodeEdges(v), function(e) { + var edgeV = e.v, + w = (v === edgeV) ? e.w : edgeV; + if (!t.hasNode(w) && !slack(g, e)) { + t.setNode(w, {}); + t.setEdge(v, w, {}); + dfs(w); + } + }); + } + + _.each(t.nodes(), dfs); + return t.nodeCount(); +} + +/* + * Finds the edge with the smallest slack that is incident on tree and returns + * it. + */ +function findMinSlackEdge(t, g) { + return _.min(g.edges(), function(e) { + if (t.hasNode(e.v) !== t.hasNode(e.w)) { + return slack(g, e); + } + }); +} + +function shiftRanks(t, g, delta) { + _.each(t.nodes(), function(v) { + g.node(v).rank += delta; + }); +} + +},{"../graphlib":7,"../lodash":10,"./util":28}],26:[function(require,module,exports){ +"use strict"; + +var rankUtil = require("./util"), + longestPath = rankUtil.longestPath, + feasibleTree = require("./feasible-tree"), + networkSimplex = require("./network-simplex"); + +module.exports = rank; + +/* + * Assigns a rank to each node in the input graph that respects the "minlen" + * constraint specified on edges between nodes. + * + * This basic structure is derived from Gansner, et al., "A Technique for + * Drawing Directed Graphs." + * + * Pre-conditions: + * + * 1. Graph must be a connected DAG + * 2. Graph nodes must be objects + * 3. Graph edges must have "weight" and "minlen" attributes + * + * Post-conditions: + * + * 1. Graph nodes will have a "rank" attribute based on the results of the + * algorithm. Ranks can start at any index (including negative), we'll + * fix them up later. + */ +function rank(g) { + switch(g.graph().ranker) { + case "network-simplex": networkSimplexRanker(g); break; + case "tight-tree": tightTreeRanker(g); break; + case "longest-path": longestPathRanker(g); break; + default: networkSimplexRanker(g); + } +} + +// A fast and simple ranker, but results are far from optimal. +var longestPathRanker = longestPath; + +function tightTreeRanker(g) { + longestPath(g); + feasibleTree(g); +} + +function networkSimplexRanker(g) { + networkSimplex(g); +} + +},{"./feasible-tree":25,"./network-simplex":27,"./util":28}],27:[function(require,module,exports){ +"use strict"; + +var _ = require("../lodash"), + feasibleTree = require("./feasible-tree"), + slack = require("./util").slack, + initRank = require("./util").longestPath, + preorder = require("../graphlib").alg.preorder, + postorder = require("../graphlib").alg.postorder, + simplify = require("../util").simplify; + +module.exports = networkSimplex; + +// Expose some internals for testing purposes +networkSimplex.initLowLimValues = initLowLimValues; +networkSimplex.initCutValues = initCutValues; +networkSimplex.calcCutValue = calcCutValue; +networkSimplex.leaveEdge = leaveEdge; +networkSimplex.enterEdge = enterEdge; +networkSimplex.exchangeEdges = exchangeEdges; + +/* + * The network simplex algorithm assigns ranks to each node in the input graph + * and iteratively improves the ranking to reduce the length of edges. + * + * Preconditions: + * + * 1. The input graph must be a DAG. + * 2. All nodes in the graph must have an object value. + * 3. All edges in the graph must have "minlen" and "weight" attributes. + * + * Postconditions: + * + * 1. All nodes in the graph will have an assigned "rank" attribute that has + * been optimized by the network simplex algorithm. Ranks start at 0. + * + * + * A rough sketch of the algorithm is as follows: + * + * 1. Assign initial ranks to each node. We use the longest path algorithm, + * which assigns ranks to the lowest position possible. In general this + * leads to very wide bottom ranks and unnecessarily long edges. + * 2. Construct a feasible tight tree. A tight tree is one such that all + * edges in the tree have no slack (difference between length of edge + * and minlen for the edge). This by itself greatly improves the assigned + * rankings by shorting edges. + * 3. Iteratively find edges that have negative cut values. Generally a + * negative cut value indicates that the edge could be removed and a new + * tree edge could be added to produce a more compact graph. + * + * Much of the algorithms here are derived from Gansner, et al., "A Technique + * for Drawing Directed Graphs." The structure of the file roughly follows the + * structure of the overall algorithm. + */ +function networkSimplex(g) { + g = simplify(g); + initRank(g); + var t = feasibleTree(g); + initLowLimValues(t); + initCutValues(t, g); + + var e, f; + while ((e = leaveEdge(t))) { + f = enterEdge(t, g, e); + exchangeEdges(t, g, e, f); + } +} + +/* + * Initializes cut values for all edges in the tree. + */ +function initCutValues(t, g) { + var vs = postorder(t, t.nodes()); + vs = vs.slice(0, vs.length - 1); + _.each(vs, function(v) { + assignCutValue(t, g, v); + }); +} + +function assignCutValue(t, g, child) { + var childLab = t.node(child), + parent = childLab.parent; + t.edge(child, parent).cutvalue = calcCutValue(t, g, child); +} + +/* + * Given the tight tree, its graph, and a child in the graph calculate and + * return the cut value for the edge between the child and its parent. + */ +function calcCutValue(t, g, child) { + var childLab = t.node(child), + parent = childLab.parent, + // True if the child is on the tail end of the edge in the directed graph + childIsTail = true, + // The graph's view of the tree edge we're inspecting + graphEdge = g.edge(child, parent), + // The accumulated cut value for the edge between this node and its parent + cutValue = 0; + + if (!graphEdge) { + childIsTail = false; + graphEdge = g.edge(parent, child); + } + + cutValue = graphEdge.weight; + + _.each(g.nodeEdges(child), function(e) { + var isOutEdge = e.v === child, + other = isOutEdge ? e.w : e.v; + + if (other !== parent) { + var pointsToHead = isOutEdge === childIsTail, + otherWeight = g.edge(e).weight; + + cutValue += pointsToHead ? otherWeight : -otherWeight; + if (isTreeEdge(t, child, other)) { + var otherCutValue = t.edge(child, other).cutvalue; + cutValue += pointsToHead ? -otherCutValue : otherCutValue; + } + } + }); + + return cutValue; +} + +function initLowLimValues(tree, root) { + if (arguments.length < 2) { + root = tree.nodes()[0]; + } + dfsAssignLowLim(tree, {}, 1, root); +} + +function dfsAssignLowLim(tree, visited, nextLim, v, parent) { + var low = nextLim, + label = tree.node(v); + + visited[v] = true; + _.each(tree.neighbors(v), function(w) { + if (!_.has(visited, w)) { + nextLim = dfsAssignLowLim(tree, visited, nextLim, w, v); + } + }); + + label.low = low; + label.lim = nextLim++; + if (parent) { + label.parent = parent; + } else { + // TODO should be able to remove this when we incrementally update low lim + delete label.parent; + } + + return nextLim; +} + +function leaveEdge(tree) { + return _.find(tree.edges(), function(e) { + return tree.edge(e).cutvalue < 0; + }); +} + +function enterEdge(t, g, edge) { + var v = edge.v, + w = edge.w; + + // For the rest of this function we assume that v is the tail and w is the + // head, so if we don't have this edge in the graph we should flip it to + // match the correct orientation. + if (!g.hasEdge(v, w)) { + v = edge.w; + w = edge.v; + } + + var vLabel = t.node(v), + wLabel = t.node(w), + tailLabel = vLabel, + flip = false; + + // If the root is in the tail of the edge then we need to flip the logic that + // checks for the head and tail nodes in the candidates function below. + if (vLabel.lim > wLabel.lim) { + tailLabel = wLabel; + flip = true; + } + + var candidates = _.filter(g.edges(), function(edge) { + return flip === isDescendant(t, t.node(edge.v), tailLabel) && + flip !== isDescendant(t, t.node(edge.w), tailLabel); + }); + + return _.min(candidates, function(edge) { return slack(g, edge); }); +} + +function exchangeEdges(t, g, e, f) { + var v = e.v, + w = e.w; + t.removeEdge(v, w); + t.setEdge(f.v, f.w, {}); + initLowLimValues(t); + initCutValues(t, g); + updateRanks(t, g); +} + +function updateRanks(t, g) { + var root = _.find(t.nodes(), function(v) { return !g.node(v).parent; }), + vs = preorder(t, root); + vs = vs.slice(1); + _.each(vs, function(v) { + var parent = t.node(v).parent, + edge = g.edge(v, parent), + flipped = false; + + if (!edge) { + edge = g.edge(parent, v); + flipped = true; + } + + g.node(v).rank = g.node(parent).rank + (flipped ? edge.minlen : -edge.minlen); + }); +} + +/* + * Returns true if the edge is in the tree. + */ +function isTreeEdge(tree, u, v) { + return tree.hasEdge(u, v); +} + +/* + * Returns true if the specified node is descendant of the root node per the + * assigned low and lim attributes in the tree. + */ +function isDescendant(tree, vLabel, rootLabel) { + return rootLabel.low <= vLabel.lim && vLabel.lim <= rootLabel.lim; +} + +},{"../graphlib":7,"../lodash":10,"../util":29,"./feasible-tree":25,"./util":28}],28:[function(require,module,exports){ +"use strict"; + +var _ = require("../lodash"); + +module.exports = { + longestPath: longestPath, + slack: slack +}; + +/* + * Initializes ranks for the input graph using the longest path algorithm. This + * algorithm scales well and is fast in practice, it yields rather poor + * solutions. Nodes are pushed to the lowest layer possible, leaving the bottom + * ranks wide and leaving edges longer than necessary. However, due to its + * speed, this algorithm is good for getting an initial ranking that can be fed + * into other algorithms. + * + * This algorithm does not normalize layers because it will be used by other + * algorithms in most cases. If using this algorithm directly, be sure to + * run normalize at the end. + * + * Pre-conditions: + * + * 1. Input graph is a DAG. + * 2. Input graph node labels can be assigned properties. + * + * Post-conditions: + * + * 1. Each node will be assign an (unnormalized) "rank" property. + */ +function longestPath(g) { + var visited = {}; + + function dfs(v) { + var label = g.node(v); + if (_.has(visited, v)) { + return label.rank; + } + visited[v] = true; + + var rank = _.min(_.map(g.outEdges(v), function(e) { + return dfs(e.w) - g.edge(e).minlen; + })); + + if (rank === Number.POSITIVE_INFINITY) { + rank = 0; + } + + return (label.rank = rank); + } + + _.each(g.sources(), dfs); +} + +/* + * Returns the amount of slack for the given edge. The slack is defined as the + * difference between the length of the edge and its minimum length. + */ +function slack(g, e) { + return g.node(e.w).rank - g.node(e.v).rank - g.edge(e).minlen; +} + +},{"../lodash":10}],29:[function(require,module,exports){ +"use strict"; + +var _ = require("./lodash"), + Graph = require("./graphlib").Graph; + +module.exports = { + addDummyNode: addDummyNode, + simplify: simplify, + asNonCompoundGraph: asNonCompoundGraph, + successorWeights: successorWeights, + predecessorWeights: predecessorWeights, + intersectRect: intersectRect, + buildLayerMatrix: buildLayerMatrix, + normalizeRanks: normalizeRanks, + removeEmptyRanks: removeEmptyRanks, + addBorderNode: addBorderNode, + maxRank: maxRank, + partition: partition, + time: time, + notime: notime +}; + +/* + * Adds a dummy node to the graph and return v. + */ +function addDummyNode(g, type, attrs, name) { + var v; + do { + v = _.uniqueId(name); + } while (g.hasNode(v)); + + attrs.dummy = type; + g.setNode(v, attrs); + return v; +} + +/* + * Returns a new graph with only simple edges. Handles aggregation of data + * associated with multi-edges. + */ +function simplify(g) { + var simplified = new Graph().setGraph(g.graph()); + _.each(g.nodes(), function(v) { simplified.setNode(v, g.node(v)); }); + _.each(g.edges(), function(e) { + var simpleLabel = simplified.edge(e.v, e.w) || { weight: 0, minlen: 1 }, + label = g.edge(e); + simplified.setEdge(e.v, e.w, { + weight: simpleLabel.weight + label.weight, + minlen: Math.max(simpleLabel.minlen, label.minlen) + }); + }); + return simplified; +} + +function asNonCompoundGraph(g) { + var simplified = new Graph({ multigraph: g.isMultigraph() }).setGraph(g.graph()); + _.each(g.nodes(), function(v) { + if (!g.children(v).length) { + simplified.setNode(v, g.node(v)); + } + }); + _.each(g.edges(), function(e) { + simplified.setEdge(e, g.edge(e)); + }); + return simplified; +} + +function successorWeights(g) { + var weightMap = _.map(g.nodes(), function(v) { + var sucs = {}; + _.each(g.outEdges(v), function(e) { + sucs[e.w] = (sucs[e.w] || 0) + g.edge(e).weight; + }); + return sucs; + }); + return _.zipObject(g.nodes(), weightMap); +} + +function predecessorWeights(g) { + var weightMap = _.map(g.nodes(), function(v) { + var preds = {}; + _.each(g.inEdges(v), function(e) { + preds[e.v] = (preds[e.v] || 0) + g.edge(e).weight; + }); + return preds; + }); + return _.zipObject(g.nodes(), weightMap); +} + +/* + * Finds where a line starting at point ({x, y}) would intersect a rectangle + * ({x, y, width, height}) if it were pointing at the rectangle's center. + */ +function intersectRect(rect, point) { + var x = rect.x; + var y = rect.y; + + // Rectangle intersection algorithm from: + // http://math.stackexchange.com/questions/108113/find-edge-between-two-boxes + var dx = point.x - x; + var dy = point.y - y; + var w = rect.width / 2; + var h = rect.height / 2; + + if (!dx && !dy) { + throw new Error("Not possible to find intersection inside of the rectangle"); + } + + var sx, sy; + if (Math.abs(dy) * w > Math.abs(dx) * h) { + // Intersection is top or bottom of rect. + if (dy < 0) { + h = -h; + } + sx = h * dx / dy; + sy = h; + } else { + // Intersection is left or right of rect. + if (dx < 0) { + w = -w; + } + sx = w; + sy = w * dy / dx; + } + + return { x: x + sx, y: y + sy }; +} + +/* + * Given a DAG with each node assigned "rank" and "order" properties, this + * function will produce a matrix with the ids of each node. + */ +function buildLayerMatrix(g) { + var layering = _.map(_.range(maxRank(g) + 1), function() { return []; }); + _.each(g.nodes(), function(v) { + var node = g.node(v), + rank = node.rank; + if (!_.isUndefined(rank)) { + layering[rank][node.order] = v; + } + }); + return layering; +} + +/* + * Adjusts the ranks for all nodes in the graph such that all nodes v have + * rank(v) >= 0 and at least one node w has rank(w) = 0. + */ +function normalizeRanks(g) { + var min = _.min(_.map(g.nodes(), function(v) { return g.node(v).rank; })); + _.each(g.nodes(), function(v) { + var node = g.node(v); + if (_.has(node, "rank")) { + node.rank -= min; + } + }); +} + +function removeEmptyRanks(g) { + // Ranks may not start at 0, so we need to offset them + var offset = _.min(_.map(g.nodes(), function(v) { return g.node(v).rank; })); + + var layers = []; + _.each(g.nodes(), function(v) { + var rank = g.node(v).rank - offset; + if (!layers[rank]) { + layers[rank] = []; + } + layers[rank].push(v); + }); + + var delta = 0, + nodeRankFactor = g.graph().nodeRankFactor; + _.each(layers, function(vs, i) { + if (_.isUndefined(vs) && i % nodeRankFactor !== 0) { + --delta; + } else if (delta) { + _.each(vs, function(v) { g.node(v).rank += delta; }); + } + }); +} + +function addBorderNode(g, prefix, rank, order) { + var node = { + width: 0, + height: 0 + }; + if (arguments.length >= 4) { + node.rank = rank; + node.order = order; + } + return addDummyNode(g, "border", node, prefix); +} + +function maxRank(g) { + return _.max(_.map(g.nodes(), function(v) { + var rank = g.node(v).rank; + if (!_.isUndefined(rank)) { + return rank; + } + })); +} + +/* + * Partition a collection into two groups: `lhs` and `rhs`. If the supplied + * function returns true for an entry it goes into `lhs`. Otherwise it goes + * into `rhs. + */ +function partition(collection, fn) { + var result = { lhs: [], rhs: [] }; + _.each(collection, function(value) { + if (fn(value)) { + result.lhs.push(value); + } else { + result.rhs.push(value); + } + }); + return result; +} + +/* + * Returns a new function that wraps `fn` with a timer. The wrapper logs the + * time it takes to execute the function. + */ +function time(name, fn) { + var start = _.now(); + try { + return fn(); + } finally { + console.log(name + " time: " + (_.now() - start) + "ms"); + } +} + +function notime(name, fn) { + return fn(); +} + +},{"./graphlib":7,"./lodash":10}],30:[function(require,module,exports){ +module.exports = "0.7.4"; + +},{}],31:[function(require,module,exports){ +/** + * Copyright (c) 2014, Chris Pettitt + * 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. Neither the name of the copyright holder 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 HOLDER 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. + */ + +var lib = require("./lib"); + +module.exports = { + Graph: lib.Graph, + json: require("./lib/json"), + alg: require("./lib/alg"), + version: lib.version +}; + +},{"./lib":47,"./lib/alg":38,"./lib/json":48}],32:[function(require,module,exports){ +var _ = require("../lodash"); + +module.exports = components; + +function components(g) { + var visited = {}, + cmpts = [], + cmpt; + + function dfs(v) { + if (_.has(visited, v)) return; + visited[v] = true; + cmpt.push(v); + _.each(g.successors(v), dfs); + _.each(g.predecessors(v), dfs); + } + + _.each(g.nodes(), function(v) { + cmpt = []; + dfs(v); + if (cmpt.length) { + cmpts.push(cmpt); + } + }); + + return cmpts; +} + +},{"../lodash":49}],33:[function(require,module,exports){ +var _ = require("../lodash"); + +module.exports = dfs; + +/* + * A helper that preforms a pre- or post-order traversal on the input graph + * and returns the nodes in the order they were visited. This algorithm treats + * the input as undirected. + * + * Order must be one of "pre" or "post". + */ +function dfs(g, vs, order) { + if (!_.isArray(vs)) { + vs = [vs]; + } + + var acc = [], + visited = {}; + _.each(vs, function(v) { + if (!g.hasNode(v)) { + throw new Error("Graph does not have node: " + v); + } + + doDfs(g, v, order === "post", visited, acc); + }); + return acc; +} + +function doDfs(g, v, postorder, visited, acc) { + if (!_.has(visited, v)) { + visited[v] = true; + + if (!postorder) { acc.push(v); } + _.each(g.neighbors(v), function(w) { + doDfs(g, w, postorder, visited, acc); + }); + if (postorder) { acc.push(v); } + } +} + +},{"../lodash":49}],34:[function(require,module,exports){ +var dijkstra = require("./dijkstra"), + _ = require("../lodash"); + +module.exports = dijkstraAll; + +function dijkstraAll(g, weightFunc, edgeFunc) { + return _.transform(g.nodes(), function(acc, v) { + acc[v] = dijkstra(g, v, weightFunc, edgeFunc); + }, {}); +} + +},{"../lodash":49,"./dijkstra":35}],35:[function(require,module,exports){ +var _ = require("../lodash"), + PriorityQueue = require("../data/priority-queue"); + +module.exports = dijkstra; + +var DEFAULT_WEIGHT_FUNC = _.constant(1); + +function dijkstra(g, source, weightFn, edgeFn) { + return runDijkstra(g, String(source), + weightFn || DEFAULT_WEIGHT_FUNC, + edgeFn || function(v) { return g.outEdges(v); }); +} + +function runDijkstra(g, source, weightFn, edgeFn) { + var results = {}, + pq = new PriorityQueue(), + v, vEntry; + + var updateNeighbors = function(edge) { + var w = edge.v !== v ? edge.v : edge.w, + wEntry = results[w], + weight = weightFn(edge), + distance = vEntry.distance + weight; + + if (weight < 0) { + throw new Error("dijkstra does not allow negative edge weights. " + + "Bad edge: " + edge + " Weight: " + weight); + } + + if (distance < wEntry.distance) { + wEntry.distance = distance; + wEntry.predecessor = v; + pq.decrease(w, distance); + } + }; + + g.nodes().forEach(function(v) { + var distance = v === source ? 0 : Number.POSITIVE_INFINITY; + results[v] = { distance: distance }; + pq.add(v, distance); + }); + + while (pq.size() > 0) { + v = pq.removeMin(); + vEntry = results[v]; + if (vEntry.distance === Number.POSITIVE_INFINITY) { + break; + } + + edgeFn(v).forEach(updateNeighbors); + } + + return results; +} + +},{"../data/priority-queue":45,"../lodash":49}],36:[function(require,module,exports){ +var _ = require("../lodash"), + tarjan = require("./tarjan"); + +module.exports = findCycles; + +function findCycles(g) { + return _.filter(tarjan(g), function(cmpt) { + return cmpt.length > 1 || (cmpt.length === 1 && g.hasEdge(cmpt[0], cmpt[0])); + }); +} + +},{"../lodash":49,"./tarjan":43}],37:[function(require,module,exports){ +var _ = require("../lodash"); + +module.exports = floydWarshall; + +var DEFAULT_WEIGHT_FUNC = _.constant(1); + +function floydWarshall(g, weightFn, edgeFn) { + return runFloydWarshall(g, + weightFn || DEFAULT_WEIGHT_FUNC, + edgeFn || function(v) { return g.outEdges(v); }); +} + +function runFloydWarshall(g, weightFn, edgeFn) { + var results = {}, + nodes = g.nodes(); + + nodes.forEach(function(v) { + results[v] = {}; + results[v][v] = { distance: 0 }; + nodes.forEach(function(w) { + if (v !== w) { + results[v][w] = { distance: Number.POSITIVE_INFINITY }; + } + }); + edgeFn(v).forEach(function(edge) { + var w = edge.v === v ? edge.w : edge.v, + d = weightFn(edge); + results[v][w] = { distance: d, predecessor: v }; + }); + }); + + nodes.forEach(function(k) { + var rowK = results[k]; + nodes.forEach(function(i) { + var rowI = results[i]; + nodes.forEach(function(j) { + var ik = rowI[k]; + var kj = rowK[j]; + var ij = rowI[j]; + var altDistance = ik.distance + kj.distance; + if (altDistance < ij.distance) { + ij.distance = altDistance; + ij.predecessor = kj.predecessor; + } + }); + }); + }); + + return results; +} + +},{"../lodash":49}],38:[function(require,module,exports){ +module.exports = { + components: require("./components"), + dijkstra: require("./dijkstra"), + dijkstraAll: require("./dijkstra-all"), + findCycles: require("./find-cycles"), + floydWarshall: require("./floyd-warshall"), + isAcyclic: require("./is-acyclic"), + postorder: require("./postorder"), + preorder: require("./preorder"), + prim: require("./prim"), + tarjan: require("./tarjan"), + topsort: require("./topsort") +}; + +},{"./components":32,"./dijkstra":35,"./dijkstra-all":34,"./find-cycles":36,"./floyd-warshall":37,"./is-acyclic":39,"./postorder":40,"./preorder":41,"./prim":42,"./tarjan":43,"./topsort":44}],39:[function(require,module,exports){ +var topsort = require("./topsort"); + +module.exports = isAcyclic; + +function isAcyclic(g) { + try { + topsort(g); + } catch (e) { + if (e instanceof topsort.CycleException) { + return false; + } + throw e; + } + return true; +} + +},{"./topsort":44}],40:[function(require,module,exports){ +var dfs = require("./dfs"); + +module.exports = postorder; + +function postorder(g, vs) { + return dfs(g, vs, "post"); +} + +},{"./dfs":33}],41:[function(require,module,exports){ +var dfs = require("./dfs"); + +module.exports = preorder; + +function preorder(g, vs) { + return dfs(g, vs, "pre"); +} + +},{"./dfs":33}],42:[function(require,module,exports){ +var _ = require("../lodash"), + Graph = require("../graph"), + PriorityQueue = require("../data/priority-queue"); + +module.exports = prim; + +function prim(g, weightFunc) { + var result = new Graph(), + parents = {}, + pq = new PriorityQueue(), + v; + + function updateNeighbors(edge) { + var w = edge.v === v ? edge.w : edge.v, + pri = pq.priority(w); + if (pri !== undefined) { + var edgeWeight = weightFunc(edge); + if (edgeWeight < pri) { + parents[w] = v; + pq.decrease(w, edgeWeight); + } + } + } + + if (g.nodeCount() === 0) { + return result; + } + + _.each(g.nodes(), function(v) { + pq.add(v, Number.POSITIVE_INFINITY); + result.setNode(v); + }); + + // Start from an arbitrary node + pq.decrease(g.nodes()[0], 0); + + var init = false; + while (pq.size() > 0) { + v = pq.removeMin(); + if (_.has(parents, v)) { + result.setEdge(v, parents[v]); + } else if (init) { + throw new Error("Input graph is not connected: " + g); + } else { + init = true; + } + + g.nodeEdges(v).forEach(updateNeighbors); + } + + return result; +} + +},{"../data/priority-queue":45,"../graph":46,"../lodash":49}],43:[function(require,module,exports){ +var _ = require("../lodash"); + +module.exports = tarjan; + +function tarjan(g) { + var index = 0, + stack = [], + visited = {}, // node id -> { onStack, lowlink, index } + results = []; + + function dfs(v) { + var entry = visited[v] = { + onStack: true, + lowlink: index, + index: index++ + }; + stack.push(v); + + g.successors(v).forEach(function(w) { + if (!_.has(visited, w)) { + dfs(w); + entry.lowlink = Math.min(entry.lowlink, visited[w].lowlink); + } else if (visited[w].onStack) { + entry.lowlink = Math.min(entry.lowlink, visited[w].index); + } + }); + + if (entry.lowlink === entry.index) { + var cmpt = [], + w; + do { + w = stack.pop(); + visited[w].onStack = false; + cmpt.push(w); + } while (v !== w); + results.push(cmpt); + } + } + + g.nodes().forEach(function(v) { + if (!_.has(visited, v)) { + dfs(v); + } + }); + + return results; +} + +},{"../lodash":49}],44:[function(require,module,exports){ +var _ = require("../lodash"); + +module.exports = topsort; +topsort.CycleException = CycleException; + +function topsort(g) { + var visited = {}, + stack = {}, + results = []; + + function visit(node) { + if (_.has(stack, node)) { + throw new CycleException(); + } + + if (!_.has(visited, node)) { + stack[node] = true; + visited[node] = true; + _.each(g.predecessors(node), visit); + delete stack[node]; + results.push(node); + } + } + + _.each(g.sinks(), visit); + + if (_.size(visited) !== g.nodeCount()) { + throw new CycleException(); + } + + return results; +} + +function CycleException() {} + +},{"../lodash":49}],45:[function(require,module,exports){ +var _ = require("../lodash"); + +module.exports = PriorityQueue; + +/** + * A min-priority queue data structure. This algorithm is derived from Cormen, + * et al., "Introduction to Algorithms". The basic idea of a min-priority + * queue is that you can efficiently (in O(1) time) get the smallest key in + * the queue. Adding and removing elements takes O(log n) time. A key can + * have its priority decreased in O(log n) time. + */ +function PriorityQueue() { + this._arr = []; + this._keyIndices = {}; +} + +/** + * Returns the number of elements in the queue. Takes `O(1)` time. + */ +PriorityQueue.prototype.size = function() { + return this._arr.length; +}; + +/** + * Returns the keys that are in the queue. Takes `O(n)` time. + */ +PriorityQueue.prototype.keys = function() { + return this._arr.map(function(x) { return x.key; }); +}; + +/** + * Returns `true` if **key** is in the queue and `false` if not. + */ +PriorityQueue.prototype.has = function(key) { + return _.has(this._keyIndices, key); +}; + +/** + * Returns the priority for **key**. If **key** is not present in the queue + * then this function returns `undefined`. Takes `O(1)` time. + * + * @param {Object} key + */ +PriorityQueue.prototype.priority = function(key) { + var index = this._keyIndices[key]; + if (index !== undefined) { + return this._arr[index].priority; + } +}; + +/** + * Returns the key for the minimum element in this queue. If the queue is + * empty this function throws an Error. Takes `O(1)` time. + */ +PriorityQueue.prototype.min = function() { + if (this.size() === 0) { + throw new Error("Queue underflow"); + } + return this._arr[0].key; +}; + +/** + * Inserts a new key into the priority queue. If the key already exists in + * the queue this function returns `false`; otherwise it will return `true`. + * Takes `O(n)` time. + * + * @param {Object} key the key to add + * @param {Number} priority the initial priority for the key + */ +PriorityQueue.prototype.add = function(key, priority) { + var keyIndices = this._keyIndices; + key = String(key); + if (!_.has(keyIndices, key)) { + var arr = this._arr; + var index = arr.length; + keyIndices[key] = index; + arr.push({key: key, priority: priority}); + this._decrease(index); + return true; + } + return false; +}; + +/** + * Removes and returns the smallest key in the queue. Takes `O(log n)` time. + */ +PriorityQueue.prototype.removeMin = function() { + this._swap(0, this._arr.length - 1); + var min = this._arr.pop(); + delete this._keyIndices[min.key]; + this._heapify(0); + return min.key; +}; + +/** + * Decreases the priority for **key** to **priority**. If the new priority is + * greater than the previous priority, this function will throw an Error. + * + * @param {Object} key the key for which to raise priority + * @param {Number} priority the new priority for the key + */ +PriorityQueue.prototype.decrease = function(key, priority) { + var index = this._keyIndices[key]; + if (priority > this._arr[index].priority) { + throw new Error("New priority is greater than current priority. " + + "Key: " + key + " Old: " + this._arr[index].priority + " New: " + priority); + } + this._arr[index].priority = priority; + this._decrease(index); +}; + +PriorityQueue.prototype._heapify = function(i) { + var arr = this._arr; + var l = 2 * i, + r = l + 1, + largest = i; + if (l < arr.length) { + largest = arr[l].priority < arr[largest].priority ? l : largest; + if (r < arr.length) { + largest = arr[r].priority < arr[largest].priority ? r : largest; + } + if (largest !== i) { + this._swap(i, largest); + this._heapify(largest); + } + } +}; + +PriorityQueue.prototype._decrease = function(index) { + var arr = this._arr; + var priority = arr[index].priority; + var parent; + while (index !== 0) { + parent = index >> 1; + if (arr[parent].priority < priority) { + break; + } + this._swap(index, parent); + index = parent; + } +}; + +PriorityQueue.prototype._swap = function(i, j) { + var arr = this._arr; + var keyIndices = this._keyIndices; + var origArrI = arr[i]; + var origArrJ = arr[j]; + arr[i] = origArrJ; + arr[j] = origArrI; + keyIndices[origArrJ.key] = i; + keyIndices[origArrI.key] = j; +}; + +},{"../lodash":49}],46:[function(require,module,exports){ +"use strict"; + +var _ = require("./lodash"); + +module.exports = Graph; + +var DEFAULT_EDGE_NAME = "\x00", + GRAPH_NODE = "\x00", + EDGE_KEY_DELIM = "\x01"; + +// Implementation notes: +// +// * Node id query functions should return string ids for the nodes +// * Edge id query functions should return an "edgeObj", edge object, that is +// composed of enough information to uniquely identify an edge: {v, w, name}. +// * Internally we use an "edgeId", a stringified form of the edgeObj, to +// reference edges. This is because we need a performant way to look these +// edges up and, object properties, which have string keys, are the closest +// we're going to get to a performant hashtable in JavaScript. + +function Graph(opts) { + this._isDirected = _.has(opts, "directed") ? opts.directed : true; + this._isMultigraph = _.has(opts, "multigraph") ? opts.multigraph : false; + this._isCompound = _.has(opts, "compound") ? opts.compound : false; + + // Label for the graph itself + this._label = undefined; + + // Defaults to be set when creating a new node + this._defaultNodeLabelFn = _.constant(undefined); + + // Defaults to be set when creating a new edge + this._defaultEdgeLabelFn = _.constant(undefined); + + // v -> label + this._nodes = {}; + + if (this._isCompound) { + // v -> parent + this._parent = {}; + + // v -> children + this._children = {}; + this._children[GRAPH_NODE] = {}; + } + + // v -> edgeObj + this._in = {}; + + // u -> v -> Number + this._preds = {}; + + // v -> edgeObj + this._out = {}; + + // v -> w -> Number + this._sucs = {}; + + // e -> edgeObj + this._edgeObjs = {}; + + // e -> label + this._edgeLabels = {}; +} + +/* Number of nodes in the graph. Should only be changed by the implementation. */ +Graph.prototype._nodeCount = 0; + +/* Number of edges in the graph. Should only be changed by the implementation. */ +Graph.prototype._edgeCount = 0; + + +/* === Graph functions ========= */ + +Graph.prototype.isDirected = function() { + return this._isDirected; +}; + +Graph.prototype.isMultigraph = function() { + return this._isMultigraph; +}; + +Graph.prototype.isCompound = function() { + return this._isCompound; +}; + +Graph.prototype.setGraph = function(label) { + this._label = label; + return this; +}; + +Graph.prototype.graph = function() { + return this._label; +}; + + +/* === Node functions ========== */ + +Graph.prototype.setDefaultNodeLabel = function(newDefault) { + if (!_.isFunction(newDefault)) { + newDefault = _.constant(newDefault); + } + this._defaultNodeLabelFn = newDefault; + return this; +}; + +Graph.prototype.nodeCount = function() { + return this._nodeCount; +}; + +Graph.prototype.nodes = function() { + return _.keys(this._nodes); +}; + +Graph.prototype.sources = function() { + return _.filter(this.nodes(), function(v) { + return _.isEmpty(this._in[v]); + }, this); +}; + +Graph.prototype.sinks = function() { + return _.filter(this.nodes(), function(v) { + return _.isEmpty(this._out[v]); + }, this); +}; + +Graph.prototype.setNodes = function(vs, value) { + var args = arguments; + _.each(vs, function(v) { + if (args.length > 1) { + this.setNode(v, value); + } else { + this.setNode(v); + } + }, this); + return this; +}; + +Graph.prototype.setNode = function(v, value) { + if (_.has(this._nodes, v)) { + if (arguments.length > 1) { + this._nodes[v] = value; + } + return this; + } + + this._nodes[v] = arguments.length > 1 ? value : this._defaultNodeLabelFn(v); + if (this._isCompound) { + this._parent[v] = GRAPH_NODE; + this._children[v] = {}; + this._children[GRAPH_NODE][v] = true; + } + this._in[v] = {}; + this._preds[v] = {}; + this._out[v] = {}; + this._sucs[v] = {}; + ++this._nodeCount; + return this; +}; + +Graph.prototype.node = function(v) { + return this._nodes[v]; +}; + +Graph.prototype.hasNode = function(v) { + return _.has(this._nodes, v); +}; + +Graph.prototype.removeNode = function(v) { + var self = this; + if (_.has(this._nodes, v)) { + var removeEdge = function(e) { self.removeEdge(self._edgeObjs[e]); }; + delete this._nodes[v]; + if (this._isCompound) { + this._removeFromParentsChildList(v); + delete this._parent[v]; + _.each(this.children(v), function(child) { + this.setParent(child); + }, this); + delete this._children[v]; + } + _.each(_.keys(this._in[v]), removeEdge); + delete this._in[v]; + delete this._preds[v]; + _.each(_.keys(this._out[v]), removeEdge); + delete this._out[v]; + delete this._sucs[v]; + --this._nodeCount; + } + return this; +}; + +Graph.prototype.setParent = function(v, parent) { + if (!this._isCompound) { + throw new Error("Cannot set parent in a non-compound graph"); + } + + if (_.isUndefined(parent)) { + parent = GRAPH_NODE; + } else { + // Coerce parent to string + parent += ""; + for (var ancestor = parent; + !_.isUndefined(ancestor); + ancestor = this.parent(ancestor)) { + if (ancestor === v) { + throw new Error("Setting " + parent+ " as parent of " + v + + " would create create a cycle"); + } + } + + this.setNode(parent); + } + + this.setNode(v); + this._removeFromParentsChildList(v); + this._parent[v] = parent; + this._children[parent][v] = true; + return this; +}; + +Graph.prototype._removeFromParentsChildList = function(v) { + delete this._children[this._parent[v]][v]; +}; + +Graph.prototype.parent = function(v) { + if (this._isCompound) { + var parent = this._parent[v]; + if (parent !== GRAPH_NODE) { + return parent; + } + } +}; + +Graph.prototype.children = function(v) { + if (_.isUndefined(v)) { + v = GRAPH_NODE; + } + + if (this._isCompound) { + var children = this._children[v]; + if (children) { + return _.keys(children); + } + } else if (v === GRAPH_NODE) { + return this.nodes(); + } else if (this.hasNode(v)) { + return []; + } +}; + +Graph.prototype.predecessors = function(v) { + var predsV = this._preds[v]; + if (predsV) { + return _.keys(predsV); + } +}; + +Graph.prototype.successors = function(v) { + var sucsV = this._sucs[v]; + if (sucsV) { + return _.keys(sucsV); + } +}; + +Graph.prototype.neighbors = function(v) { + var preds = this.predecessors(v); + if (preds) { + return _.union(preds, this.successors(v)); + } +}; + +/* === Edge functions ========== */ + +Graph.prototype.setDefaultEdgeLabel = function(newDefault) { + if (!_.isFunction(newDefault)) { + newDefault = _.constant(newDefault); + } + this._defaultEdgeLabelFn = newDefault; + return this; +}; + +Graph.prototype.edgeCount = function() { + return this._edgeCount; +}; + +Graph.prototype.edges = function() { + return _.values(this._edgeObjs); +}; + +Graph.prototype.setPath = function(vs, value) { + var self = this, + args = arguments; + _.reduce(vs, function(v, w) { + if (args.length > 1) { + self.setEdge(v, w, value); + } else { + self.setEdge(v, w); + } + return w; + }); + return this; +}; + +/* + * setEdge(v, w, [value, [name]]) + * setEdge({ v, w, [name] }, [value]) + */ +Graph.prototype.setEdge = function() { + var v, w, name, value, + valueSpecified = false; + + if (_.isPlainObject(arguments[0])) { + v = arguments[0].v; + w = arguments[0].w; + name = arguments[0].name; + if (arguments.length === 2) { + value = arguments[1]; + valueSpecified = true; + } + } else { + v = arguments[0]; + w = arguments[1]; + name = arguments[3]; + if (arguments.length > 2) { + value = arguments[2]; + valueSpecified = true; + } + } + + v = "" + v; + w = "" + w; + if (!_.isUndefined(name)) { + name = "" + name; + } + + var e = edgeArgsToId(this._isDirected, v, w, name); + if (_.has(this._edgeLabels, e)) { + if (valueSpecified) { + this._edgeLabels[e] = value; + } + return this; + } + + if (!_.isUndefined(name) && !this._isMultigraph) { + throw new Error("Cannot set a named edge when isMultigraph = false"); + } + + // It didn't exist, so we need to create it. + // First ensure the nodes exist. + this.setNode(v); + this.setNode(w); + + this._edgeLabels[e] = valueSpecified ? value : this._defaultEdgeLabelFn(v, w, name); + + var edgeObj = edgeArgsToObj(this._isDirected, v, w, name); + // Ensure we add undirected edges in a consistent way. + v = edgeObj.v; + w = edgeObj.w; + + Object.freeze(edgeObj); + this._edgeObjs[e] = edgeObj; + incrementOrInitEntry(this._preds[w], v); + incrementOrInitEntry(this._sucs[v], w); + this._in[w][e] = edgeObj; + this._out[v][e] = edgeObj; + this._edgeCount++; + return this; +}; + +Graph.prototype.edge = function(v, w, name) { + var e = (arguments.length === 1 + ? edgeObjToId(this._isDirected, arguments[0]) + : edgeArgsToId(this._isDirected, v, w, name)); + return this._edgeLabels[e]; +}; + +Graph.prototype.hasEdge = function(v, w, name) { + var e = (arguments.length === 1 + ? edgeObjToId(this._isDirected, arguments[0]) + : edgeArgsToId(this._isDirected, v, w, name)); + return _.has(this._edgeLabels, e); +}; + +Graph.prototype.removeEdge = function(v, w, name) { + var e = (arguments.length === 1 + ? edgeObjToId(this._isDirected, arguments[0]) + : edgeArgsToId(this._isDirected, v, w, name)), + edge = this._edgeObjs[e]; + if (edge) { + v = edge.v; + w = edge.w; + delete this._edgeLabels[e]; + delete this._edgeObjs[e]; + decrementOrRemoveEntry(this._preds[w], v); + decrementOrRemoveEntry(this._sucs[v], w); + delete this._in[w][e]; + delete this._out[v][e]; + this._edgeCount--; + } + return this; +}; + +Graph.prototype.inEdges = function(v, u) { + var inV = this._in[v]; + if (inV) { + var edges = _.values(inV); + if (!u) { + return edges; + } + return _.filter(edges, function(edge) { return edge.v === u; }); + } +}; + +Graph.prototype.outEdges = function(v, w) { + var outV = this._out[v]; + if (outV) { + var edges = _.values(outV); + if (!w) { + return edges; + } + return _.filter(edges, function(edge) { return edge.w === w; }); + } +}; + +Graph.prototype.nodeEdges = function(v, w) { + var inEdges = this.inEdges(v, w); + if (inEdges) { + return inEdges.concat(this.outEdges(v, w)); + } +}; + +function incrementOrInitEntry(map, k) { + if (_.has(map, k)) { + map[k]++; + } else { + map[k] = 1; + } +} + +function decrementOrRemoveEntry(map, k) { + if (!--map[k]) { delete map[k]; } +} + +function edgeArgsToId(isDirected, v, w, name) { + if (!isDirected && v > w) { + var tmp = v; + v = w; + w = tmp; + } + return v + EDGE_KEY_DELIM + w + EDGE_KEY_DELIM + + (_.isUndefined(name) ? DEFAULT_EDGE_NAME : name); +} + +function edgeArgsToObj(isDirected, v, w, name) { + if (!isDirected && v > w) { + var tmp = v; + v = w; + w = tmp; + } + var edgeObj = { v: v, w: w }; + if (name) { + edgeObj.name = name; + } + return edgeObj; +} + +function edgeObjToId(isDirected, edgeObj) { + return edgeArgsToId(isDirected, edgeObj.v, edgeObj.w, edgeObj.name); +} + +},{"./lodash":49}],47:[function(require,module,exports){ +// Includes only the "core" of graphlib +module.exports = { + Graph: require("./graph"), + version: require("./version") +}; + +},{"./graph":46,"./version":50}],48:[function(require,module,exports){ +var _ = require("./lodash"), + Graph = require("./graph"); + +module.exports = { + write: write, + read: read +}; + +function write(g) { + var json = { + options: { + directed: g.isDirected(), + multigraph: g.isMultigraph(), + compound: g.isCompound() + }, + nodes: writeNodes(g), + edges: writeEdges(g) + }; + if (!_.isUndefined(g.graph())) { + json.value = _.clone(g.graph()); + } + return json; +} + +function writeNodes(g) { + return _.map(g.nodes(), function(v) { + var nodeValue = g.node(v), + parent = g.parent(v), + node = { v: v }; + if (!_.isUndefined(nodeValue)) { + node.value = nodeValue; + } + if (!_.isUndefined(parent)) { + node.parent = parent; + } + return node; + }); +} + +function writeEdges(g) { + return _.map(g.edges(), function(e) { + var edgeValue = g.edge(e), + edge = { v: e.v, w: e.w }; + if (!_.isUndefined(e.name)) { + edge.name = e.name; + } + if (!_.isUndefined(edgeValue)) { + edge.value = edgeValue; + } + return edge; + }); +} + +function read(json) { + var g = new Graph(json.options).setGraph(json.value); + _.each(json.nodes, function(entry) { + g.setNode(entry.v, entry.value); + if (entry.parent) { + g.setParent(entry.v, entry.parent); + } + }); + _.each(json.edges, function(entry) { + g.setEdge({ v: entry.v, w: entry.w, name: entry.name }, entry.value); + }); + return g; +} + +},{"./graph":46,"./lodash":49}],49:[function(require,module,exports){ +module.exports=require(10) +},{"/Users/cpettitt/projects/dagre/lib/lodash.js":10,"lodash":51}],50:[function(require,module,exports){ +module.exports = '1.0.5'; + +},{}],51:[function(require,module,exports){ +(function (global){ +/** + * @license + * lodash 3.10.0 (Custom Build) <https://lodash.com/> + * Build: `lodash modern -d -o ./index.js` + * Copyright 2012-2015 The Dojo Foundation <http://dojofoundation.org/> + * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE> + * Copyright 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors + * Available under MIT license <https://lodash.com/license> + */ +;(function() { + + /** Used as a safe reference for `undefined` in pre-ES5 environments. */ + var undefined; + + /** Used as the semantic version number. */ + var VERSION = '3.10.0'; + + /** Used to compose bitmasks for wrapper metadata. */ + var BIND_FLAG = 1, + BIND_KEY_FLAG = 2, + CURRY_BOUND_FLAG = 4, + CURRY_FLAG = 8, + CURRY_RIGHT_FLAG = 16, + PARTIAL_FLAG = 32, + PARTIAL_RIGHT_FLAG = 64, + ARY_FLAG = 128, + REARG_FLAG = 256; + + /** Used as default options for `_.trunc`. */ + var DEFAULT_TRUNC_LENGTH = 30, + DEFAULT_TRUNC_OMISSION = '...'; + + /** Used to detect when a function becomes hot. */ + var HOT_COUNT = 150, + HOT_SPAN = 16; + + /** Used as the size to enable large array optimizations. */ + var LARGE_ARRAY_SIZE = 200; + + /** Used to indicate the type of lazy iteratees. */ + var LAZY_FILTER_FLAG = 1, + LAZY_MAP_FLAG = 2; + + /** Used as the `TypeError` message for "Functions" methods. */ + var FUNC_ERROR_TEXT = 'Expected a function'; + + /** Used as the internal argument placeholder. */ + var PLACEHOLDER = '__lodash_placeholder__'; + + /** `Object#toString` result references. */ + var argsTag = '[object Arguments]', + arrayTag = '[object Array]', + boolTag = '[object Boolean]', + dateTag = '[object Date]', + errorTag = '[object Error]', + funcTag = '[object Function]', + mapTag = '[object Map]', + numberTag = '[object Number]', + objectTag = '[object Object]', + regexpTag = '[object RegExp]', + setTag = '[object Set]', + stringTag = '[object String]', + weakMapTag = '[object WeakMap]'; + + var arrayBufferTag = '[object ArrayBuffer]', + float32Tag = '[object Float32Array]', + float64Tag = '[object Float64Array]', + int8Tag = '[object Int8Array]', + int16Tag = '[object Int16Array]', + int32Tag = '[object Int32Array]', + uint8Tag = '[object Uint8Array]', + uint8ClampedTag = '[object Uint8ClampedArray]', + uint16Tag = '[object Uint16Array]', + uint32Tag = '[object Uint32Array]'; + + /** Used to match empty string literals in compiled template source. */ + var reEmptyStringLeading = /\b__p \+= '';/g, + reEmptyStringMiddle = /\b(__p \+=) '' \+/g, + reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g; + + /** Used to match HTML entities and HTML characters. */ + var reEscapedHtml = /&(?:amp|lt|gt|quot|#39|#96);/g, + reUnescapedHtml = /[&<>"'`]/g, + reHasEscapedHtml = RegExp(reEscapedHtml.source), + reHasUnescapedHtml = RegExp(reUnescapedHtml.source); + + /** Used to match template delimiters. */ + var reEscape = /<%-([\s\S]+?)%>/g, + reEvaluate = /<%([\s\S]+?)%>/g, + reInterpolate = /<%=([\s\S]+?)%>/g; + + /** Used to match property names within property paths. */ + var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\n\\]|\\.)*?\1)\]/, + reIsPlainProp = /^\w*$/, + rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\n\\]|\\.)*?)\2)\]/g; + + /** + * Used to match `RegExp` [syntax characters](http://ecma-international.org/ecma-262/6.0/#sec-patterns) + * and those outlined by [`EscapeRegExpPattern`](http://ecma-international.org/ecma-262/6.0/#sec-escaperegexppattern). + */ + var reRegExpChars = /^[:!,]|[\\^$.*+?()[\]{}|\/]|(^[0-9a-fA-Fnrtuvx])|([\n\r\u2028\u2029])/g, + reHasRegExpChars = RegExp(reRegExpChars.source); + + /** Used to match [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks). */ + var reComboMark = /[\u0300-\u036f\ufe20-\ufe23]/g; + + /** Used to match backslashes in property paths. */ + var reEscapeChar = /\\(\\)?/g; + + /** Used to match [ES template delimiters](http://ecma-international.org/ecma-262/6.0/#sec-template-literal-lexical-components). */ + var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g; + + /** Used to match `RegExp` flags from their coerced string values. */ + var reFlags = /\w*$/; + + /** Used to detect hexadecimal string values. */ + var reHasHexPrefix = /^0[xX]/; + + /** Used to detect host constructors (Safari > 5). */ + var reIsHostCtor = /^\[object .+?Constructor\]$/; + + /** Used to detect unsigned integer values. */ + var reIsUint = /^\d+$/; + + /** Used to match latin-1 supplementary letters (excluding mathematical operators). */ + var reLatin1 = /[\xc0-\xd6\xd8-\xde\xdf-\xf6\xf8-\xff]/g; + + /** Used to ensure capturing order of template delimiters. */ + var reNoMatch = /($^)/; + + /** Used to match unescaped characters in compiled string literals. */ + var reUnescapedString = /['\n\r\u2028\u2029\\]/g; + + /** Used to match words to create compound words. */ + var reWords = (function() { + var upper = '[A-Z\\xc0-\\xd6\\xd8-\\xde]', + lower = '[a-z\\xdf-\\xf6\\xf8-\\xff]+'; + + return RegExp(upper + '+(?=' + upper + lower + ')|' + upper + '?' + lower + '|' + upper + '+|[0-9]+', 'g'); + }()); + + /** Used to assign default `context` object properties. */ + var contextProps = [ + 'Array', 'ArrayBuffer', 'Date', 'Error', 'Float32Array', 'Float64Array', + 'Function', 'Int8Array', 'Int16Array', 'Int32Array', 'Math', 'Number', + 'Object', 'RegExp', 'Set', 'String', '_', 'clearTimeout', 'isFinite', + 'parseFloat', 'parseInt', 'setTimeout', 'TypeError', 'Uint8Array', + 'Uint8ClampedArray', 'Uint16Array', 'Uint32Array', 'WeakMap' + ]; + + /** Used to make template sourceURLs easier to identify. */ + var templateCounter = -1; + + /** Used to identify `toStringTag` values of typed arrays. */ + var typedArrayTags = {}; + typedArrayTags[float32Tag] = typedArrayTags[float64Tag] = + typedArrayTags[int8Tag] = typedArrayTags[int16Tag] = + typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] = + typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] = + typedArrayTags[uint32Tag] = true; + typedArrayTags[argsTag] = typedArrayTags[arrayTag] = + typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] = + typedArrayTags[dateTag] = typedArrayTags[errorTag] = + typedArrayTags[funcTag] = typedArrayTags[mapTag] = + typedArrayTags[numberTag] = typedArrayTags[objectTag] = + typedArrayTags[regexpTag] = typedArrayTags[setTag] = + typedArrayTags[stringTag] = typedArrayTags[weakMapTag] = false; + + /** Used to identify `toStringTag` values supported by `_.clone`. */ + var cloneableTags = {}; + cloneableTags[argsTag] = cloneableTags[arrayTag] = + cloneableTags[arrayBufferTag] = cloneableTags[boolTag] = + cloneableTags[dateTag] = cloneableTags[float32Tag] = + cloneableTags[float64Tag] = cloneableTags[int8Tag] = + cloneableTags[int16Tag] = cloneableTags[int32Tag] = + cloneableTags[numberTag] = cloneableTags[objectTag] = + cloneableTags[regexpTag] = cloneableTags[stringTag] = + cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] = + cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true; + cloneableTags[errorTag] = cloneableTags[funcTag] = + cloneableTags[mapTag] = cloneableTags[setTag] = + cloneableTags[weakMapTag] = false; + + /** Used to map latin-1 supplementary letters to basic latin letters. */ + var deburredLetters = { + '\xc0': 'A', '\xc1': 'A', '\xc2': 'A', '\xc3': 'A', '\xc4': 'A', '\xc5': 'A', + '\xe0': 'a', '\xe1': 'a', '\xe2': 'a', '\xe3': 'a', '\xe4': 'a', '\xe5': 'a', + '\xc7': 'C', '\xe7': 'c', + '\xd0': 'D', '\xf0': 'd', + '\xc8': 'E', '\xc9': 'E', '\xca': 'E', '\xcb': 'E', + '\xe8': 'e', '\xe9': 'e', '\xea': 'e', '\xeb': 'e', + '\xcC': 'I', '\xcd': 'I', '\xce': 'I', '\xcf': 'I', + '\xeC': 'i', '\xed': 'i', '\xee': 'i', '\xef': 'i', + '\xd1': 'N', '\xf1': 'n', + '\xd2': 'O', '\xd3': 'O', '\xd4': 'O', '\xd5': 'O', '\xd6': 'O', '\xd8': 'O', + '\xf2': 'o', '\xf3': 'o', '\xf4': 'o', '\xf5': 'o', '\xf6': 'o', '\xf8': 'o', + '\xd9': 'U', '\xda': 'U', '\xdb': 'U', '\xdc': 'U', + '\xf9': 'u', '\xfa': 'u', '\xfb': 'u', '\xfc': 'u', + '\xdd': 'Y', '\xfd': 'y', '\xff': 'y', + '\xc6': 'Ae', '\xe6': 'ae', + '\xde': 'Th', '\xfe': 'th', + '\xdf': 'ss' + }; + + /** Used to map characters to HTML entities. */ + var htmlEscapes = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''', + '`': '`' + }; + + /** Used to map HTML entities to characters. */ + var htmlUnescapes = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + ''': "'", + '`': '`' + }; + + /** Used to determine if values are of the language type `Object`. */ + var objectTypes = { + 'function': true, + 'object': true + }; + + /** Used to escape characters for inclusion in compiled regexes. */ + var regexpEscapes = { + '0': 'x30', '1': 'x31', '2': 'x32', '3': 'x33', '4': 'x34', + '5': 'x35', '6': 'x36', '7': 'x37', '8': 'x38', '9': 'x39', + 'A': 'x41', 'B': 'x42', 'C': 'x43', 'D': 'x44', 'E': 'x45', 'F': 'x46', + 'a': 'x61', 'b': 'x62', 'c': 'x63', 'd': 'x64', 'e': 'x65', 'f': 'x66', + 'n': 'x6e', 'r': 'x72', 't': 'x74', 'u': 'x75', 'v': 'x76', 'x': 'x78' + }; + + /** Used to escape characters for inclusion in compiled string literals. */ + var stringEscapes = { + '\\': '\\', + "'": "'", + '\n': 'n', + '\r': 'r', + '\u2028': 'u2028', + '\u2029': 'u2029' + }; + + /** Detect free variable `exports`. */ + var freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports; + + /** Detect free variable `module`. */ + var freeModule = objectTypes[typeof module] && module && !module.nodeType && module; + + /** Detect free variable `global` from Node.js. */ + var freeGlobal = freeExports && freeModule && typeof global == 'object' && global && global.Object && global; + + /** Detect free variable `self`. */ + var freeSelf = objectTypes[typeof self] && self && self.Object && self; + + /** Detect free variable `window`. */ + var freeWindow = objectTypes[typeof window] && window && window.Object && window; + + /** Detect the popular CommonJS extension `module.exports`. */ + var moduleExports = freeModule && freeModule.exports === freeExports && freeExports; + + /** + * Used as a reference to the global object. + * + * The `this` value is used if it's the global object to avoid Greasemonkey's + * restricted `window` object, otherwise the `window` object is used. + */ + var root = freeGlobal || ((freeWindow !== (this && this.window)) && freeWindow) || freeSelf || this; + + /*--------------------------------------------------------------------------*/ + + /** + * The base implementation of `compareAscending` which compares values and + * sorts them in ascending order without guaranteeing a stable sort. + * + * @private + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {number} Returns the sort order indicator for `value`. + */ + function baseCompareAscending(value, other) { + if (value !== other) { + var valIsNull = value === null, + valIsUndef = value === undefined, + valIsReflexive = value === value; + + var othIsNull = other === null, + othIsUndef = other === undefined, + othIsReflexive = other === other; + + if ((value > other && !othIsNull) || !valIsReflexive || + (valIsNull && !othIsUndef && othIsReflexive) || + (valIsUndef && othIsReflexive)) { + return 1; + } + if ((value < other && !valIsNull) || !othIsReflexive || + (othIsNull && !valIsUndef && valIsReflexive) || + (othIsUndef && valIsReflexive)) { + return -1; + } + } + return 0; + } + + /** + * The base implementation of `_.findIndex` and `_.findLastIndex` without + * support for callback shorthands and `this` binding. + * + * @private + * @param {Array} array The array to search. + * @param {Function} predicate The function invoked per iteration. + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {number} Returns the index of the matched value, else `-1`. + */ + function baseFindIndex(array, predicate, fromRight) { + var length = array.length, + index = fromRight ? length : -1; + + while ((fromRight ? index-- : ++index < length)) { + if (predicate(array[index], index, array)) { + return index; + } + } + return -1; + } + + /** + * The base implementation of `_.indexOf` without support for binary searches. + * + * @private + * @param {Array} array The array to search. + * @param {*} value The value to search for. + * @param {number} fromIndex The index to search from. + * @returns {number} Returns the index of the matched value, else `-1`. + */ + function baseIndexOf(array, value, fromIndex) { + if (value !== value) { + return indexOfNaN(array, fromIndex); + } + var index = fromIndex - 1, + length = array.length; + + while (++index < length) { + if (array[index] === value) { + return index; + } + } + return -1; + } + + /** + * The base implementation of `_.isFunction` without support for environments + * with incorrect `typeof` results. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + */ + function baseIsFunction(value) { + // Avoid a Chakra JIT bug in compatibility modes of IE 11. + // See https://github.com/jashkenas/underscore/issues/1621 for more details. + return typeof value == 'function' || false; + } + + /** + * Converts `value` to a string if it's not one. An empty string is returned + * for `null` or `undefined` values. + * + * @private + * @param {*} value The value to process. + * @returns {string} Returns the string. + */ + function baseToString(value) { + return value == null ? '' : (value + ''); + } + + /** + * Used by `_.trim` and `_.trimLeft` to get the index of the first character + * of `string` that is not found in `chars`. + * + * @private + * @param {string} string The string to inspect. + * @param {string} chars The characters to find. + * @returns {number} Returns the index of the first character not found in `chars`. + */ + function charsLeftIndex(string, chars) { + var index = -1, + length = string.length; + + while (++index < length && chars.indexOf(string.charAt(index)) > -1) {} + return index; + } + + /** + * Used by `_.trim` and `_.trimRight` to get the index of the last character + * of `string` that is not found in `chars`. + * + * @private + * @param {string} string The string to inspect. + * @param {string} chars The characters to find. + * @returns {number} Returns the index of the last character not found in `chars`. + */ + function charsRightIndex(string, chars) { + var index = string.length; + + while (index-- && chars.indexOf(string.charAt(index)) > -1) {} + return index; + } + + /** + * Used by `_.sortBy` to compare transformed elements of a collection and stable + * sort them in ascending order. + * + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @returns {number} Returns the sort order indicator for `object`. + */ + function compareAscending(object, other) { + return baseCompareAscending(object.criteria, other.criteria) || (object.index - other.index); + } + + /** + * Used by `_.sortByOrder` to compare multiple properties of a value to another + * and stable sort them. + * + * If `orders` is unspecified, all valuess are sorted in ascending order. Otherwise, + * a value is sorted in ascending order if its corresponding order is "asc", and + * descending if "desc". + * + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @param {boolean[]} orders The order to sort by for each property. + * @returns {number} Returns the sort order indicator for `object`. + */ + function compareMultiple(object, other, orders) { + var index = -1, + objCriteria = object.criteria, + othCriteria = other.criteria, + length = objCriteria.length, + ordersLength = orders.length; + + while (++index < length) { + var result = baseCompareAscending(objCriteria[index], othCriteria[index]); + if (result) { + if (index >= ordersLength) { + return result; + } + var order = orders[index]; + return result * ((order === 'asc' || order === true) ? 1 : -1); + } + } + // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications + // that causes it, under certain circumstances, to provide the same value for + // `object` and `other`. See https://github.com/jashkenas/underscore/pull/1247 + // for more details. + // + // This also ensures a stable sort in V8 and other engines. + // See https://code.google.com/p/v8/issues/detail?id=90 for more details. + return object.index - other.index; + } + + /** + * Used by `_.deburr` to convert latin-1 supplementary letters to basic latin letters. + * + * @private + * @param {string} letter The matched letter to deburr. + * @returns {string} Returns the deburred letter. + */ + function deburrLetter(letter) { + return deburredLetters[letter]; + } + + /** + * Used by `_.escape` to convert characters to HTML entities. + * + * @private + * @param {string} chr The matched character to escape. + * @returns {string} Returns the escaped character. + */ + function escapeHtmlChar(chr) { + return htmlEscapes[chr]; + } + + /** + * Used by `_.escapeRegExp` to escape characters for inclusion in compiled regexes. + * + * @private + * @param {string} chr The matched character to escape. + * @param {string} leadingChar The capture group for a leading character. + * @param {string} whitespaceChar The capture group for a whitespace character. + * @returns {string} Returns the escaped character. + */ + function escapeRegExpChar(chr, leadingChar, whitespaceChar) { + if (leadingChar) { + chr = regexpEscapes[chr]; + } else if (whitespaceChar) { + chr = stringEscapes[chr]; + } + return '\\' + chr; + } + + /** + * Used by `_.template` to escape characters for inclusion in compiled string literals. + * + * @private + * @param {string} chr The matched character to escape. + * @returns {string} Returns the escaped character. + */ + function escapeStringChar(chr) { + return '\\' + stringEscapes[chr]; + } + + /** + * Gets the index at which the first occurrence of `NaN` is found in `array`. + * + * @private + * @param {Array} array The array to search. + * @param {number} fromIndex The index to search from. + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {number} Returns the index of the matched `NaN`, else `-1`. + */ + function indexOfNaN(array, fromIndex, fromRight) { + var length = array.length, + index = fromIndex + (fromRight ? 0 : -1); + + while ((fromRight ? index-- : ++index < length)) { + var other = array[index]; + if (other !== other) { + return index; + } + } + return -1; + } + + /** + * Checks if `value` is object-like. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is object-like, else `false`. + */ + function isObjectLike(value) { + return !!value && typeof value == 'object'; + } + + /** + * Used by `trimmedLeftIndex` and `trimmedRightIndex` to determine if a + * character code is whitespace. + * + * @private + * @param {number} charCode The character code to inspect. + * @returns {boolean} Returns `true` if `charCode` is whitespace, else `false`. + */ + function isSpace(charCode) { + return ((charCode <= 160 && (charCode >= 9 && charCode <= 13) || charCode == 32 || charCode == 160) || charCode == 5760 || charCode == 6158 || + (charCode >= 8192 && (charCode <= 8202 || charCode == 8232 || charCode == 8233 || charCode == 8239 || charCode == 8287 || charCode == 12288 || charCode == 65279))); + } + + /** + * Replaces all `placeholder` elements in `array` with an internal placeholder + * and returns an array of their indexes. + * + * @private + * @param {Array} array The array to modify. + * @param {*} placeholder The placeholder to replace. + * @returns {Array} Returns the new array of placeholder indexes. + */ + function replaceHolders(array, placeholder) { + var index = -1, + length = array.length, + resIndex = -1, + result = []; + + while (++index < length) { + if (array[index] === placeholder) { + array[index] = PLACEHOLDER; + result[++resIndex] = index; + } + } + return result; + } + + /** + * An implementation of `_.uniq` optimized for sorted arrays without support + * for callback shorthands and `this` binding. + * + * @private + * @param {Array} array The array to inspect. + * @param {Function} [iteratee] The function invoked per iteration. + * @returns {Array} Returns the new duplicate-value-free array. + */ + function sortedUniq(array, iteratee) { + var seen, + index = -1, + length = array.length, + resIndex = -1, + result = []; + + while (++index < length) { + var value = array[index], + computed = iteratee ? iteratee(value, index, array) : value; + + if (!index || seen !== computed) { + seen = computed; + result[++resIndex] = value; + } + } + return result; + } + + /** + * Used by `_.trim` and `_.trimLeft` to get the index of the first non-whitespace + * character of `string`. + * + * @private + * @param {string} string The string to inspect. + * @returns {number} Returns the index of the first non-whitespace character. + */ + function trimmedLeftIndex(string) { + var index = -1, + length = string.length; + + while (++index < length && isSpace(string.charCodeAt(index))) {} + return index; + } + + /** + * Used by `_.trim` and `_.trimRight` to get the index of the last non-whitespace + * character of `string`. + * + * @private + * @param {string} string The string to inspect. + * @returns {number} Returns the index of the last non-whitespace character. + */ + function trimmedRightIndex(string) { + var index = string.length; + + while (index-- && isSpace(string.charCodeAt(index))) {} + return index; + } + + /** + * Used by `_.unescape` to convert HTML entities to characters. + * + * @private + * @param {string} chr The matched character to unescape. + * @returns {string} Returns the unescaped character. + */ + function unescapeHtmlChar(chr) { + return htmlUnescapes[chr]; + } + + /*--------------------------------------------------------------------------*/ + + /** + * Create a new pristine `lodash` function using the given `context` object. + * + * @static + * @memberOf _ + * @category Utility + * @param {Object} [context=root] The context object. + * @returns {Function} Returns a new `lodash` function. + * @example + * + * _.mixin({ 'foo': _.constant('foo') }); + * + * var lodash = _.runInContext(); + * lodash.mixin({ 'bar': lodash.constant('bar') }); + * + * _.isFunction(_.foo); + * // => true + * _.isFunction(_.bar); + * // => false + * + * lodash.isFunction(lodash.foo); + * // => false + * lodash.isFunction(lodash.bar); + * // => true + * + * // using `context` to mock `Date#getTime` use in `_.now` + * var mock = _.runInContext({ + * 'Date': function() { + * return { 'getTime': getTimeMock }; + * } + * }); + * + * // or creating a suped-up `defer` in Node.js + * var defer = _.runInContext({ 'setTimeout': setImmediate }).defer; + */ + function runInContext(context) { + // Avoid issues with some ES3 environments that attempt to use values, named + // after built-in constructors like `Object`, for the creation of literals. + // ES5 clears this up by stating that literals must use built-in constructors. + // See https://es5.github.io/#x11.1.5 for more details. + context = context ? _.defaults(root.Object(), context, _.pick(root, contextProps)) : root; + + /** Native constructor references. */ + var Array = context.Array, + Date = context.Date, + Error = context.Error, + Function = context.Function, + Math = context.Math, + Number = context.Number, + Object = context.Object, + RegExp = context.RegExp, + String = context.String, + TypeError = context.TypeError; + + /** Used for native method references. */ + var arrayProto = Array.prototype, + objectProto = Object.prototype, + stringProto = String.prototype; + + /** Used to resolve the decompiled source of functions. */ + var fnToString = Function.prototype.toString; + + /** Used to check objects for own properties. */ + var hasOwnProperty = objectProto.hasOwnProperty; + + /** Used to generate unique IDs. */ + var idCounter = 0; + + /** + * Used to resolve the [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring) + * of values. + */ + var objToString = objectProto.toString; + + /** Used to restore the original `_` reference in `_.noConflict`. */ + var oldDash = root._; + + /** Used to detect if a method is native. */ + var reIsNative = RegExp('^' + + fnToString.call(hasOwnProperty).replace(/[\\^$.*+?()[\]{}|]/g, '\\$&') + .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' + ); + + /** Native method references. */ + var ArrayBuffer = context.ArrayBuffer, + clearTimeout = context.clearTimeout, + parseFloat = context.parseFloat, + pow = Math.pow, + propertyIsEnumerable = objectProto.propertyIsEnumerable, + Set = getNative(context, 'Set'), + setTimeout = context.setTimeout, + splice = arrayProto.splice, + Uint8Array = context.Uint8Array, + WeakMap = getNative(context, 'WeakMap'); + + /* Native method references for those with the same name as other `lodash` methods. */ + var nativeCeil = Math.ceil, + nativeCreate = getNative(Object, 'create'), + nativeFloor = Math.floor, + nativeIsArray = getNative(Array, 'isArray'), + nativeIsFinite = context.isFinite, + nativeKeys = getNative(Object, 'keys'), + nativeMax = Math.max, + nativeMin = Math.min, + nativeNow = getNative(Date, 'now'), + nativeParseInt = context.parseInt, + nativeRandom = Math.random; + + /** Used as references for `-Infinity` and `Infinity`. */ + var NEGATIVE_INFINITY = Number.NEGATIVE_INFINITY, + POSITIVE_INFINITY = Number.POSITIVE_INFINITY; + + /** Used as references for the maximum length and index of an array. */ + var MAX_ARRAY_LENGTH = 4294967295, + MAX_ARRAY_INDEX = MAX_ARRAY_LENGTH - 1, + HALF_MAX_ARRAY_LENGTH = MAX_ARRAY_LENGTH >>> 1; + + /** + * Used as the [maximum length](http://ecma-international.org/ecma-262/6.0/#sec-number.max_safe_integer) + * of an array-like value. + */ + var MAX_SAFE_INTEGER = 9007199254740991; + + /** Used to store function metadata. */ + var metaMap = WeakMap && new WeakMap; + + /** Used to lookup unminified function names. */ + var realNames = {}; + + /*------------------------------------------------------------------------*/ + + /** + * Creates a `lodash` object which wraps `value` to enable implicit chaining. + * Methods that operate on and return arrays, collections, and functions can + * be chained together. Methods that retrieve a single value or may return a + * primitive value will automatically end the chain returning the unwrapped + * value. Explicit chaining may be enabled using `_.chain`. The execution of + * chained methods is lazy, that is, execution is deferred until `_#value` + * is implicitly or explicitly called. + * + * Lazy evaluation allows several methods to support shortcut fusion. Shortcut + * fusion is an optimization strategy which merge iteratee calls; this can help + * to avoid the creation of intermediate data structures and greatly reduce the + * number of iteratee executions. + * + * Chaining is supported in custom builds as long as the `_#value` method is + * directly or indirectly included in the build. + * + * In addition to lodash methods, wrappers have `Array` and `String` methods. + * + * The wrapper `Array` methods are: + * `concat`, `join`, `pop`, `push`, `reverse`, `shift`, `slice`, `sort`, + * `splice`, and `unshift` + * + * The wrapper `String` methods are: + * `replace` and `split` + * + * The wrapper methods that support shortcut fusion are: + * `compact`, `drop`, `dropRight`, `dropRightWhile`, `dropWhile`, `filter`, + * `first`, `initial`, `last`, `map`, `pluck`, `reject`, `rest`, `reverse`, + * `slice`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`, `toArray`, + * and `where` + * + * The chainable wrapper methods are: + * `after`, `ary`, `assign`, `at`, `before`, `bind`, `bindAll`, `bindKey`, + * `callback`, `chain`, `chunk`, `commit`, `compact`, `concat`, `constant`, + * `countBy`, `create`, `curry`, `debounce`, `defaults`, `defaultsDeep`, + * `defer`, `delay`, `difference`, `drop`, `dropRight`, `dropRightWhile`, + * `dropWhile`, `fill`, `filter`, `flatten`, `flattenDeep`, `flow`, `flowRight`, + * `forEach`, `forEachRight`, `forIn`, `forInRight`, `forOwn`, `forOwnRight`, + * `functions`, `groupBy`, `indexBy`, `initial`, `intersection`, `invert`, + * `invoke`, `keys`, `keysIn`, `map`, `mapKeys`, `mapValues`, `matches`, + * `matchesProperty`, `memoize`, `merge`, `method`, `methodOf`, `mixin`, + * `modArgs`, `negate`, `omit`, `once`, `pairs`, `partial`, `partialRight`, + * `partition`, `pick`, `plant`, `pluck`, `property`, `propertyOf`, `pull`, + * `pullAt`, `push`, `range`, `rearg`, `reject`, `remove`, `rest`, `restParam`, + * `reverse`, `set`, `shuffle`, `slice`, `sort`, `sortBy`, `sortByAll`, + * `sortByOrder`, `splice`, `spread`, `take`, `takeRight`, `takeRightWhile`, + * `takeWhile`, `tap`, `throttle`, `thru`, `times`, `toArray`, `toPlainObject`, + * `transform`, `union`, `uniq`, `unshift`, `unzip`, `unzipWith`, `values`, + * `valuesIn`, `where`, `without`, `wrap`, `xor`, `zip`, `zipObject`, `zipWith` + * + * The wrapper methods that are **not** chainable by default are: + * `add`, `attempt`, `camelCase`, `capitalize`, `ceil`, `clone`, `cloneDeep`, + * `deburr`, `endsWith`, `escape`, `escapeRegExp`, `every`, `find`, `findIndex`, + * `findKey`, `findLast`, `findLastIndex`, `findLastKey`, `findWhere`, `first`, + * `floor`, `get`, `gt`, `gte`, `has`, `identity`, `includes`, `indexOf`, + * `inRange`, `isArguments`, `isArray`, `isBoolean`, `isDate`, `isElement`, + * `isEmpty`, `isEqual`, `isError`, `isFinite` `isFunction`, `isMatch`, + * `isNative`, `isNaN`, `isNull`, `isNumber`, `isObject`, `isPlainObject`, + * `isRegExp`, `isString`, `isUndefined`, `isTypedArray`, `join`, `kebabCase`, + * `last`, `lastIndexOf`, `lt`, `lte`, `max`, `min`, `noConflict`, `noop`, + * `now`, `pad`, `padLeft`, `padRight`, `parseInt`, `pop`, `random`, `reduce`, + * `reduceRight`, `repeat`, `result`, `round`, `runInContext`, `shift`, `size`, + * `snakeCase`, `some`, `sortedIndex`, `sortedLastIndex`, `startCase`, + * `startsWith`, `sum`, `template`, `trim`, `trimLeft`, `trimRight`, `trunc`, + * `unescape`, `uniqueId`, `value`, and `words` + * + * The wrapper method `sample` will return a wrapped value when `n` is provided, + * otherwise an unwrapped value is returned. + * + * @name _ + * @constructor + * @category Chain + * @param {*} value The value to wrap in a `lodash` instance. + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * var wrapped = _([1, 2, 3]); + * + * // returns an unwrapped value + * wrapped.reduce(function(total, n) { + * return total + n; + * }); + * // => 6 + * + * // returns a wrapped value + * var squares = wrapped.map(function(n) { + * return n * n; + * }); + * + * _.isArray(squares); + * // => false + * + * _.isArray(squares.value()); + * // => true + */ + function lodash(value) { + if (isObjectLike(value) && !isArray(value) && !(value instanceof LazyWrapper)) { + if (value instanceof LodashWrapper) { + return value; + } + if (hasOwnProperty.call(value, '__chain__') && hasOwnProperty.call(value, '__wrapped__')) { + return wrapperClone(value); + } + } + return new LodashWrapper(value); + } + + /** + * The function whose prototype all chaining wrappers inherit from. + * + * @private + */ + function baseLodash() { + // No operation performed. + } + + /** + * The base constructor for creating `lodash` wrapper objects. + * + * @private + * @param {*} value The value to wrap. + * @param {boolean} [chainAll] Enable chaining for all wrapper methods. + * @param {Array} [actions=[]] Actions to peform to resolve the unwrapped value. + */ + function LodashWrapper(value, chainAll, actions) { + this.__wrapped__ = value; + this.__actions__ = actions || []; + this.__chain__ = !!chainAll; + } + + /** + * An object environment feature flags. + * + * @static + * @memberOf _ + * @type Object + */ + var support = lodash.support = {}; + + /** + * By default, the template delimiters used by lodash are like those in + * embedded Ruby (ERB). Change the following template settings to use + * alternative delimiters. + * + * @static + * @memberOf _ + * @type Object + */ + lodash.templateSettings = { + + /** + * Used to detect `data` property values to be HTML-escaped. + * + * @memberOf _.templateSettings + * @type RegExp + */ + 'escape': reEscape, + + /** + * Used to detect code to be evaluated. + * + * @memberOf _.templateSettings + * @type RegExp + */ + 'evaluate': reEvaluate, + + /** + * Used to detect `data` property values to inject. + * + * @memberOf _.templateSettings + * @type RegExp + */ + 'interpolate': reInterpolate, + + /** + * Used to reference the data object in the template text. + * + * @memberOf _.templateSettings + * @type string + */ + 'variable': '', + + /** + * Used to import variables into the compiled template. + * + * @memberOf _.templateSettings + * @type Object + */ + 'imports': { + + /** + * A reference to the `lodash` function. + * + * @memberOf _.templateSettings.imports + * @type Function + */ + '_': lodash + } + }; + + /*------------------------------------------------------------------------*/ + + /** + * Creates a lazy wrapper object which wraps `value` to enable lazy evaluation. + * + * @private + * @param {*} value The value to wrap. + */ + function LazyWrapper(value) { + this.__wrapped__ = value; + this.__actions__ = []; + this.__dir__ = 1; + this.__filtered__ = false; + this.__iteratees__ = []; + this.__takeCount__ = POSITIVE_INFINITY; + this.__views__ = []; + } + + /** + * Creates a clone of the lazy wrapper object. + * + * @private + * @name clone + * @memberOf LazyWrapper + * @returns {Object} Returns the cloned `LazyWrapper` object. + */ + function lazyClone() { + var result = new LazyWrapper(this.__wrapped__); + result.__actions__ = arrayCopy(this.__actions__); + result.__dir__ = this.__dir__; + result.__filtered__ = this.__filtered__; + result.__iteratees__ = arrayCopy(this.__iteratees__); + result.__takeCount__ = this.__takeCount__; + result.__views__ = arrayCopy(this.__views__); + return result; + } + + /** + * Reverses the direction of lazy iteration. + * + * @private + * @name reverse + * @memberOf LazyWrapper + * @returns {Object} Returns the new reversed `LazyWrapper` object. + */ + function lazyReverse() { + if (this.__filtered__) { + var result = new LazyWrapper(this); + result.__dir__ = -1; + result.__filtered__ = true; + } else { + result = this.clone(); + result.__dir__ *= -1; + } + return result; + } + + /** + * Extracts the unwrapped value from its lazy wrapper. + * + * @private + * @name value + * @memberOf LazyWrapper + * @returns {*} Returns the unwrapped value. + */ + function lazyValue() { + var array = this.__wrapped__.value(), + dir = this.__dir__, + isArr = isArray(array), + isRight = dir < 0, + arrLength = isArr ? array.length : 0, + view = getView(0, arrLength, this.__views__), + start = view.start, + end = view.end, + length = end - start, + index = isRight ? end : (start - 1), + iteratees = this.__iteratees__, + iterLength = iteratees.length, + resIndex = 0, + takeCount = nativeMin(length, this.__takeCount__); + + if (!isArr || arrLength < LARGE_ARRAY_SIZE || (arrLength == length && takeCount == length)) { + return baseWrapperValue((isRight && isArr) ? array.reverse() : array, this.__actions__); + } + var result = []; + + outer: + while (length-- && resIndex < takeCount) { + index += dir; + + var iterIndex = -1, + value = array[index]; + + while (++iterIndex < iterLength) { + var data = iteratees[iterIndex], + iteratee = data.iteratee, + type = data.type, + computed = iteratee(value); + + if (type == LAZY_MAP_FLAG) { + value = computed; + } else if (!computed) { + if (type == LAZY_FILTER_FLAG) { + continue outer; + } else { + break outer; + } + } + } + result[resIndex++] = value; + } + return result; + } + + /*------------------------------------------------------------------------*/ + + /** + * Creates a cache object to store key/value pairs. + * + * @private + * @static + * @name Cache + * @memberOf _.memoize + */ + function MapCache() { + this.__data__ = {}; + } + + /** + * Removes `key` and its value from the cache. + * + * @private + * @name delete + * @memberOf _.memoize.Cache + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed successfully, else `false`. + */ + function mapDelete(key) { + return this.has(key) && delete this.__data__[key]; + } + + /** + * Gets the cached value for `key`. + * + * @private + * @name get + * @memberOf _.memoize.Cache + * @param {string} key The key of the value to get. + * @returns {*} Returns the cached value. + */ + function mapGet(key) { + return key == '__proto__' ? undefined : this.__data__[key]; + } + + /** + * Checks if a cached value for `key` exists. + * + * @private + * @name has + * @memberOf _.memoize.Cache + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function mapHas(key) { + return key != '__proto__' && hasOwnProperty.call(this.__data__, key); + } + + /** + * Sets `value` to `key` of the cache. + * + * @private + * @name set + * @memberOf _.memoize.Cache + * @param {string} key The key of the value to cache. + * @param {*} value The value to cache. + * @returns {Object} Returns the cache object. + */ + function mapSet(key, value) { + if (key != '__proto__') { + this.__data__[key] = value; + } + return this; + } + + /*------------------------------------------------------------------------*/ + + /** + * + * Creates a cache object to store unique values. + * + * @private + * @param {Array} [values] The values to cache. + */ + function SetCache(values) { + var length = values ? values.length : 0; + + this.data = { 'hash': nativeCreate(null), 'set': new Set }; + while (length--) { + this.push(values[length]); + } + } + + /** + * Checks if `value` is in `cache` mimicking the return signature of + * `_.indexOf` by returning `0` if the value is found, else `-1`. + * + * @private + * @param {Object} cache The cache to search. + * @param {*} value The value to search for. + * @returns {number} Returns `0` if `value` is found, else `-1`. + */ + function cacheIndexOf(cache, value) { + var data = cache.data, + result = (typeof value == 'string' || isObject(value)) ? data.set.has(value) : data.hash[value]; + + return result ? 0 : -1; + } + + /** + * Adds `value` to the cache. + * + * @private + * @name push + * @memberOf SetCache + * @param {*} value The value to cache. + */ + function cachePush(value) { + var data = this.data; + if (typeof value == 'string' || isObject(value)) { + data.set.add(value); + } else { + data.hash[value] = true; + } + } + + /*------------------------------------------------------------------------*/ + + /** + * Creates a new array joining `array` with `other`. + * + * @private + * @param {Array} array The array to join. + * @param {Array} other The other array to join. + * @returns {Array} Returns the new concatenated array. + */ + function arrayConcat(array, other) { + var index = -1, + length = array.length, + othIndex = -1, + othLength = other.length, + result = Array(length + othLength); + + while (++index < length) { + result[index] = array[index]; + } + while (++othIndex < othLength) { + result[index++] = other[othIndex]; + } + return result; + } + + /** + * Copies the values of `source` to `array`. + * + * @private + * @param {Array} source The array to copy values from. + * @param {Array} [array=[]] The array to copy values to. + * @returns {Array} Returns `array`. + */ + function arrayCopy(source, array) { + var index = -1, + length = source.length; + + array || (array = Array(length)); + while (++index < length) { + array[index] = source[index]; + } + return array; + } + + /** + * A specialized version of `_.forEach` for arrays without support for callback + * shorthands and `this` binding. + * + * @private + * @param {Array} array The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns `array`. + */ + function arrayEach(array, iteratee) { + var index = -1, + length = array.length; + + while (++index < length) { + if (iteratee(array[index], index, array) === false) { + break; + } + } + return array; + } + + /** + * A specialized version of `_.forEachRight` for arrays without support for + * callback shorthands and `this` binding. + * + * @private + * @param {Array} array The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns `array`. + */ + function arrayEachRight(array, iteratee) { + var length = array.length; + + while (length--) { + if (iteratee(array[length], length, array) === false) { + break; + } + } + return array; + } + + /** + * A specialized version of `_.every` for arrays without support for callback + * shorthands and `this` binding. + * + * @private + * @param {Array} array The array to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {boolean} Returns `true` if all elements pass the predicate check, + * else `false`. + */ + function arrayEvery(array, predicate) { + var index = -1, + length = array.length; + + while (++index < length) { + if (!predicate(array[index], index, array)) { + return false; + } + } + return true; + } + + /** + * A specialized version of `baseExtremum` for arrays which invokes `iteratee` + * with one argument: (value). + * + * @private + * @param {Array} array The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {Function} comparator The function used to compare values. + * @param {*} exValue The initial extremum value. + * @returns {*} Returns the extremum value. + */ + function arrayExtremum(array, iteratee, comparator, exValue) { + var index = -1, + length = array.length, + computed = exValue, + result = computed; + + while (++index < length) { + var value = array[index], + current = +iteratee(value); + + if (comparator(current, computed)) { + computed = current; + result = value; + } + } + return result; + } + + /** + * A specialized version of `_.filter` for arrays without support for callback + * shorthands and `this` binding. + * + * @private + * @param {Array} array The array to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {Array} Returns the new filtered array. + */ + function arrayFilter(array, predicate) { + var index = -1, + length = array.length, + resIndex = -1, + result = []; + + while (++index < length) { + var value = array[index]; + if (predicate(value, index, array)) { + result[++resIndex] = value; + } + } + return result; + } + + /** + * A specialized version of `_.map` for arrays without support for callback + * shorthands and `this` binding. + * + * @private + * @param {Array} array The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns the new mapped array. + */ + function arrayMap(array, iteratee) { + var index = -1, + length = array.length, + result = Array(length); + + while (++index < length) { + result[index] = iteratee(array[index], index, array); + } + return result; + } + + /** + * Appends the elements of `values` to `array`. + * + * @private + * @param {Array} array The array to modify. + * @param {Array} values The values to append. + * @returns {Array} Returns `array`. + */ + function arrayPush(array, values) { + var index = -1, + length = values.length, + offset = array.length; + + while (++index < length) { + array[offset + index] = values[index]; + } + return array; + } + + /** + * A specialized version of `_.reduce` for arrays without support for callback + * shorthands and `this` binding. + * + * @private + * @param {Array} array The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {*} [accumulator] The initial value. + * @param {boolean} [initFromArray] Specify using the first element of `array` + * as the initial value. + * @returns {*} Returns the accumulated value. + */ + function arrayReduce(array, iteratee, accumulator, initFromArray) { + var index = -1, + length = array.length; + + if (initFromArray && length) { + accumulator = array[++index]; + } + while (++index < length) { + accumulator = iteratee(accumulator, array[index], index, array); + } + return accumulator; + } + + /** + * A specialized version of `_.reduceRight` for arrays without support for + * callback shorthands and `this` binding. + * + * @private + * @param {Array} array The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {*} [accumulator] The initial value. + * @param {boolean} [initFromArray] Specify using the last element of `array` + * as the initial value. + * @returns {*} Returns the accumulated value. + */ + function arrayReduceRight(array, iteratee, accumulator, initFromArray) { + var length = array.length; + if (initFromArray && length) { + accumulator = array[--length]; + } + while (length--) { + accumulator = iteratee(accumulator, array[length], length, array); + } + return accumulator; + } + + /** + * A specialized version of `_.some` for arrays without support for callback + * shorthands and `this` binding. + * + * @private + * @param {Array} array The array to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {boolean} Returns `true` if any element passes the predicate check, + * else `false`. + */ + function arraySome(array, predicate) { + var index = -1, + length = array.length; + + while (++index < length) { + if (predicate(array[index], index, array)) { + return true; + } + } + return false; + } + + /** + * A specialized version of `_.sum` for arrays without support for callback + * shorthands and `this` binding.. + * + * @private + * @param {Array} array The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {number} Returns the sum. + */ + function arraySum(array, iteratee) { + var length = array.length, + result = 0; + + while (length--) { + result += +iteratee(array[length]) || 0; + } + return result; + } + + /** + * Used by `_.defaults` to customize its `_.assign` use. + * + * @private + * @param {*} objectValue The destination object property value. + * @param {*} sourceValue The source object property value. + * @returns {*} Returns the value to assign to the destination object. + */ + function assignDefaults(objectValue, sourceValue) { + return objectValue === undefined ? sourceValue : objectValue; + } + + /** + * Used by `_.template` to customize its `_.assign` use. + * + * **Note:** This function is like `assignDefaults` except that it ignores + * inherited property values when checking if a property is `undefined`. + * + * @private + * @param {*} objectValue The destination object property value. + * @param {*} sourceValue The source object property value. + * @param {string} key The key associated with the object and source values. + * @param {Object} object The destination object. + * @returns {*} Returns the value to assign to the destination object. + */ + function assignOwnDefaults(objectValue, sourceValue, key, object) { + return (objectValue === undefined || !hasOwnProperty.call(object, key)) + ? sourceValue + : objectValue; + } + + /** + * A specialized version of `_.assign` for customizing assigned values without + * support for argument juggling, multiple sources, and `this` binding `customizer` + * functions. + * + * @private + * @param {Object} object The destination object. + * @param {Object} source The source object. + * @param {Function} customizer The function to customize assigned values. + * @returns {Object} Returns `object`. + */ + function assignWith(object, source, customizer) { + var index = -1, + props = keys(source), + length = props.length; + + while (++index < length) { + var key = props[index], + value = object[key], + result = customizer(value, source[key], key, object, source); + + if ((result === result ? (result !== value) : (value === value)) || + (value === undefined && !(key in object))) { + object[key] = result; + } + } + return object; + } + + /** + * The base implementation of `_.assign` without support for argument juggling, + * multiple sources, and `customizer` functions. + * + * @private + * @param {Object} object The destination object. + * @param {Object} source The source object. + * @returns {Object} Returns `object`. + */ + function baseAssign(object, source) { + return source == null + ? object + : baseCopy(source, keys(source), object); + } + + /** + * The base implementation of `_.at` without support for string collections + * and individual key arguments. + * + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {number[]|string[]} props The property names or indexes of elements to pick. + * @returns {Array} Returns the new array of picked elements. + */ + function baseAt(collection, props) { + var index = -1, + isNil = collection == null, + isArr = !isNil && isArrayLike(collection), + length = isArr ? collection.length : 0, + propsLength = props.length, + result = Array(propsLength); + + while(++index < propsLength) { + var key = props[index]; + if (isArr) { + result[index] = isIndex(key, length) ? collection[key] : undefined; + } else { + result[index] = isNil ? undefined : collection[key]; + } + } + return result; + } + + /** + * Copies properties of `source` to `object`. + * + * @private + * @param {Object} source The object to copy properties from. + * @param {Array} props The property names to copy. + * @param {Object} [object={}] The object to copy properties to. + * @returns {Object} Returns `object`. + */ + function baseCopy(source, props, object) { + object || (object = {}); + + var index = -1, + length = props.length; + + while (++index < length) { + var key = props[index]; + object[key] = source[key]; + } + return object; + } + + /** + * The base implementation of `_.callback` which supports specifying the + * number of arguments to provide to `func`. + * + * @private + * @param {*} [func=_.identity] The value to convert to a callback. + * @param {*} [thisArg] The `this` binding of `func`. + * @param {number} [argCount] The number of arguments to provide to `func`. + * @returns {Function} Returns the callback. + */ + function baseCallback(func, thisArg, argCount) { + var type = typeof func; + if (type == 'function') { + return thisArg === undefined + ? func + : bindCallback(func, thisArg, argCount); + } + if (func == null) { + return identity; + } + if (type == 'object') { + return baseMatches(func); + } + return thisArg === undefined + ? property(func) + : baseMatchesProperty(func, thisArg); + } + + /** + * The base implementation of `_.clone` without support for argument juggling + * and `this` binding `customizer` functions. + * + * @private + * @param {*} value The value to clone. + * @param {boolean} [isDeep] Specify a deep clone. + * @param {Function} [customizer] The function to customize cloning values. + * @param {string} [key] The key of `value`. + * @param {Object} [object] The object `value` belongs to. + * @param {Array} [stackA=[]] Tracks traversed source objects. + * @param {Array} [stackB=[]] Associates clones with source counterparts. + * @returns {*} Returns the cloned value. + */ + function baseClone(value, isDeep, customizer, key, object, stackA, stackB) { + var result; + if (customizer) { + result = object ? customizer(value, key, object) : customizer(value); + } + if (result !== undefined) { + return result; + } + if (!isObject(value)) { + return value; + } + var isArr = isArray(value); + if (isArr) { + result = initCloneArray(value); + if (!isDeep) { + return arrayCopy(value, result); + } + } else { + var tag = objToString.call(value), + isFunc = tag == funcTag; + + if (tag == objectTag || tag == argsTag || (isFunc && !object)) { + result = initCloneObject(isFunc ? {} : value); + if (!isDeep) { + return baseAssign(result, value); + } + } else { + return cloneableTags[tag] + ? initCloneByTag(value, tag, isDeep) + : (object ? value : {}); + } + } + // Check for circular references and return its corresponding clone. + stackA || (stackA = []); + stackB || (stackB = []); + + var length = stackA.length; + while (length--) { + if (stackA[length] == value) { + return stackB[length]; + } + } + // Add the source value to the stack of traversed objects and associate it with its clone. + stackA.push(value); + stackB.push(result); + + // Recursively populate clone (susceptible to call stack limits). + (isArr ? arrayEach : baseForOwn)(value, function(subValue, key) { + result[key] = baseClone(subValue, isDeep, customizer, key, value, stackA, stackB); + }); + return result; + } + + /** + * The base implementation of `_.create` without support for assigning + * properties to the created object. + * + * @private + * @param {Object} prototype The object to inherit from. + * @returns {Object} Returns the new object. + */ + var baseCreate = (function() { + function object() {} + return function(prototype) { + if (isObject(prototype)) { + object.prototype = prototype; + var result = new object; + object.prototype = undefined; + } + return result || {}; + }; + }()); + + /** + * The base implementation of `_.delay` and `_.defer` which accepts an index + * of where to slice the arguments to provide to `func`. + * + * @private + * @param {Function} func The function to delay. + * @param {number} wait The number of milliseconds to delay invocation. + * @param {Object} args The arguments provide to `func`. + * @returns {number} Returns the timer id. + */ + function baseDelay(func, wait, args) { + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + return setTimeout(function() { func.apply(undefined, args); }, wait); + } + + /** + * The base implementation of `_.difference` which accepts a single array + * of values to exclude. + * + * @private + * @param {Array} array The array to inspect. + * @param {Array} values The values to exclude. + * @returns {Array} Returns the new array of filtered values. + */ + function baseDifference(array, values) { + var length = array ? array.length : 0, + result = []; + + if (!length) { + return result; + } + var index = -1, + indexOf = getIndexOf(), + isCommon = indexOf == baseIndexOf, + cache = (isCommon && values.length >= LARGE_ARRAY_SIZE) ? createCache(values) : null, + valuesLength = values.length; + + if (cache) { + indexOf = cacheIndexOf; + isCommon = false; + values = cache; + } + outer: + while (++index < length) { + var value = array[index]; + + if (isCommon && value === value) { + var valuesIndex = valuesLength; + while (valuesIndex--) { + if (values[valuesIndex] === value) { + continue outer; + } + } + result.push(value); + } + else if (indexOf(values, value, 0) < 0) { + result.push(value); + } + } + return result; + } + + /** + * The base implementation of `_.forEach` without support for callback + * shorthands and `this` binding. + * + * @private + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array|Object|string} Returns `collection`. + */ + var baseEach = createBaseEach(baseForOwn); + + /** + * The base implementation of `_.forEachRight` without support for callback + * shorthands and `this` binding. + * + * @private + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array|Object|string} Returns `collection`. + */ + var baseEachRight = createBaseEach(baseForOwnRight, true); + + /** + * The base implementation of `_.every` without support for callback + * shorthands and `this` binding. + * + * @private + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {boolean} Returns `true` if all elements pass the predicate check, + * else `false` + */ + function baseEvery(collection, predicate) { + var result = true; + baseEach(collection, function(value, index, collection) { + result = !!predicate(value, index, collection); + return result; + }); + return result; + } + + /** + * Gets the extremum value of `collection` invoking `iteratee` for each value + * in `collection` to generate the criterion by which the value is ranked. + * The `iteratee` is invoked with three arguments: (value, index|key, collection). + * + * @private + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {Function} comparator The function used to compare values. + * @param {*} exValue The initial extremum value. + * @returns {*} Returns the extremum value. + */ + function baseExtremum(collection, iteratee, comparator, exValue) { + var computed = exValue, + result = computed; + + baseEach(collection, function(value, index, collection) { + var current = +iteratee(value, index, collection); + if (comparator(current, computed) || (current === exValue && current === result)) { + computed = current; + result = value; + } + }); + return result; + } + + /** + * The base implementation of `_.fill` without an iteratee call guard. + * + * @private + * @param {Array} array The array to fill. + * @param {*} value The value to fill `array` with. + * @param {number} [start=0] The start position. + * @param {number} [end=array.length] The end position. + * @returns {Array} Returns `array`. + */ + function baseFill(array, value, start, end) { + var length = array.length; + + start = start == null ? 0 : (+start || 0); + if (start < 0) { + start = -start > length ? 0 : (length + start); + } + end = (end === undefined || end > length) ? length : (+end || 0); + if (end < 0) { + end += length; + } + length = start > end ? 0 : (end >>> 0); + start >>>= 0; + + while (start < length) { + array[start++] = value; + } + return array; + } + + /** + * The base implementation of `_.filter` without support for callback + * shorthands and `this` binding. + * + * @private + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {Array} Returns the new filtered array. + */ + function baseFilter(collection, predicate) { + var result = []; + baseEach(collection, function(value, index, collection) { + if (predicate(value, index, collection)) { + result.push(value); + } + }); + return result; + } + + /** + * The base implementation of `_.find`, `_.findLast`, `_.findKey`, and `_.findLastKey`, + * without support for callback shorthands and `this` binding, which iterates + * over `collection` using the provided `eachFunc`. + * + * @private + * @param {Array|Object|string} collection The collection to search. + * @param {Function} predicate The function invoked per iteration. + * @param {Function} eachFunc The function to iterate over `collection`. + * @param {boolean} [retKey] Specify returning the key of the found element + * instead of the element itself. + * @returns {*} Returns the found element or its key, else `undefined`. + */ + function baseFind(collection, predicate, eachFunc, retKey) { + var result; + eachFunc(collection, function(value, key, collection) { + if (predicate(value, key, collection)) { + result = retKey ? key : value; + return false; + } + }); + return result; + } + + /** + * The base implementation of `_.flatten` with added support for restricting + * flattening and specifying the start index. + * + * @private + * @param {Array} array The array to flatten. + * @param {boolean} [isDeep] Specify a deep flatten. + * @param {boolean} [isStrict] Restrict flattening to arrays-like objects. + * @param {Array} [result=[]] The initial result value. + * @returns {Array} Returns the new flattened array. + */ + function baseFlatten(array, isDeep, isStrict, result) { + result || (result = []); + + var index = -1, + length = array.length; + + while (++index < length) { + var value = array[index]; + if (isObjectLike(value) && isArrayLike(value) && + (isStrict || isArray(value) || isArguments(value))) { + if (isDeep) { + // Recursively flatten arrays (susceptible to call stack limits). + baseFlatten(value, isDeep, isStrict, result); + } else { + arrayPush(result, value); + } + } else if (!isStrict) { + result[result.length] = value; + } + } + return result; + } + + /** + * The base implementation of `baseForIn` and `baseForOwn` which iterates + * over `object` properties returned by `keysFunc` invoking `iteratee` for + * each property. Iteratee functions may exit iteration early by explicitly + * returning `false`. + * + * @private + * @param {Object} object The object to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {Function} keysFunc The function to get the keys of `object`. + * @returns {Object} Returns `object`. + */ + var baseFor = createBaseFor(); + + /** + * This function is like `baseFor` except that it iterates over properties + * in the opposite order. + * + * @private + * @param {Object} object The object to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {Function} keysFunc The function to get the keys of `object`. + * @returns {Object} Returns `object`. + */ + var baseForRight = createBaseFor(true); + + /** + * The base implementation of `_.forIn` without support for callback + * shorthands and `this` binding. + * + * @private + * @param {Object} object The object to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Object} Returns `object`. + */ + function baseForIn(object, iteratee) { + return baseFor(object, iteratee, keysIn); + } + + /** + * The base implementation of `_.forOwn` without support for callback + * shorthands and `this` binding. + * + * @private + * @param {Object} object The object to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Object} Returns `object`. + */ + function baseForOwn(object, iteratee) { + return baseFor(object, iteratee, keys); + } + + /** + * The base implementation of `_.forOwnRight` without support for callback + * shorthands and `this` binding. + * + * @private + * @param {Object} object The object to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Object} Returns `object`. + */ + function baseForOwnRight(object, iteratee) { + return baseForRight(object, iteratee, keys); + } + + /** + * The base implementation of `_.functions` which creates an array of + * `object` function property names filtered from those provided. + * + * @private + * @param {Object} object The object to inspect. + * @param {Array} props The property names to filter. + * @returns {Array} Returns the new array of filtered property names. + */ + function baseFunctions(object, props) { + var index = -1, + length = props.length, + resIndex = -1, + result = []; + + while (++index < length) { + var key = props[index]; + if (isFunction(object[key])) { + result[++resIndex] = key; + } + } + return result; + } + + /** + * The base implementation of `get` without support for string paths + * and default values. + * + * @private + * @param {Object} object The object to query. + * @param {Array} path The path of the property to get. + * @param {string} [pathKey] The key representation of path. + * @returns {*} Returns the resolved value. + */ + function baseGet(object, path, pathKey) { + if (object == null) { + return; + } + if (pathKey !== undefined && pathKey in toObject(object)) { + path = [pathKey]; + } + var index = 0, + length = path.length; + + while (object != null && index < length) { + object = object[path[index++]]; + } + return (index && index == length) ? object : undefined; + } + + /** + * The base implementation of `_.isEqual` without support for `this` binding + * `customizer` functions. + * + * @private + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @param {Function} [customizer] The function to customize comparing values. + * @param {boolean} [isLoose] Specify performing partial comparisons. + * @param {Array} [stackA] Tracks traversed `value` objects. + * @param {Array} [stackB] Tracks traversed `other` objects. + * @returns {boolean} Returns `true` if the values are equivalent, else `false`. + */ + function baseIsEqual(value, other, customizer, isLoose, stackA, stackB) { + if (value === other) { + return true; + } + if (value == null || other == null || (!isObject(value) && !isObjectLike(other))) { + return value !== value && other !== other; + } + return baseIsEqualDeep(value, other, baseIsEqual, customizer, isLoose, stackA, stackB); + } + + /** + * A specialized version of `baseIsEqual` for arrays and objects which performs + * deep comparisons and tracks traversed objects enabling objects with circular + * references to be compared. + * + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @param {Function} equalFunc The function to determine equivalents of values. + * @param {Function} [customizer] The function to customize comparing objects. + * @param {boolean} [isLoose] Specify performing partial comparisons. + * @param {Array} [stackA=[]] Tracks traversed `value` objects. + * @param {Array} [stackB=[]] Tracks traversed `other` objects. + * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. + */ + function baseIsEqualDeep(object, other, equalFunc, customizer, isLoose, stackA, stackB) { + var objIsArr = isArray(object), + othIsArr = isArray(other), + objTag = arrayTag, + othTag = arrayTag; + + if (!objIsArr) { + objTag = objToString.call(object); + if (objTag == argsTag) { + objTag = objectTag; + } else if (objTag != objectTag) { + objIsArr = isTypedArray(object); + } + } + if (!othIsArr) { + othTag = objToString.call(other); + if (othTag == argsTag) { + othTag = objectTag; + } else if (othTag != objectTag) { + othIsArr = isTypedArray(other); + } + } + var objIsObj = objTag == objectTag, + othIsObj = othTag == objectTag, + isSameTag = objTag == othTag; + + if (isSameTag && !(objIsArr || objIsObj)) { + return equalByTag(object, other, objTag); + } + if (!isLoose) { + var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'), + othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__'); + + if (objIsWrapped || othIsWrapped) { + return equalFunc(objIsWrapped ? object.value() : object, othIsWrapped ? other.value() : other, customizer, isLoose, stackA, stackB); + } + } + if (!isSameTag) { + return false; + } + // Assume cyclic values are equal. + // For more information on detecting circular references see https://es5.github.io/#JO. + stackA || (stackA = []); + stackB || (stackB = []); + + var length = stackA.length; + while (length--) { + if (stackA[length] == object) { + return stackB[length] == other; + } + } + // Add `object` and `other` to the stack of traversed objects. + stackA.push(object); + stackB.push(other); + + var result = (objIsArr ? equalArrays : equalObjects)(object, other, equalFunc, customizer, isLoose, stackA, stackB); + + stackA.pop(); + stackB.pop(); + + return result; + } + + /** + * The base implementation of `_.isMatch` without support for callback + * shorthands and `this` binding. + * + * @private + * @param {Object} object The object to inspect. + * @param {Array} matchData The propery names, values, and compare flags to match. + * @param {Function} [customizer] The function to customize comparing objects. + * @returns {boolean} Returns `true` if `object` is a match, else `false`. + */ + function baseIsMatch(object, matchData, customizer) { + var index = matchData.length, + length = index, + noCustomizer = !customizer; + + if (object == null) { + return !length; + } + object = toObject(object); + while (index--) { + var data = matchData[index]; + if ((noCustomizer && data[2]) + ? data[1] !== object[data[0]] + : !(data[0] in object) + ) { + return false; + } + } + while (++index < length) { + data = matchData[index]; + var key = data[0], + objValue = object[key], + srcValue = data[1]; + + if (noCustomizer && data[2]) { + if (objValue === undefined && !(key in object)) { + return false; + } + } else { + var result = customizer ? customizer(objValue, srcValue, key) : undefined; + if (!(result === undefined ? baseIsEqual(srcValue, objValue, customizer, true) : result)) { + return false; + } + } + } + return true; + } + + /** + * The base implementation of `_.map` without support for callback shorthands + * and `this` binding. + * + * @private + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns the new mapped array. + */ + function baseMap(collection, iteratee) { + var index = -1, + result = isArrayLike(collection) ? Array(collection.length) : []; + + baseEach(collection, function(value, key, collection) { + result[++index] = iteratee(value, key, collection); + }); + return result; + } + + /** + * The base implementation of `_.matches` which does not clone `source`. + * + * @private + * @param {Object} source The object of property values to match. + * @returns {Function} Returns the new function. + */ + function baseMatches(source) { + var matchData = getMatchData(source); + if (matchData.length == 1 && matchData[0][2]) { + var key = matchData[0][0], + value = matchData[0][1]; + + return function(object) { + if (object == null) { + return false; + } + return object[key] === value && (value !== undefined || (key in toObject(object))); + }; + } + return function(object) { + return baseIsMatch(object, matchData); + }; + } + + /** + * The base implementation of `_.matchesProperty` which does not clone `srcValue`. + * + * @private + * @param {string} path The path of the property to get. + * @param {*} srcValue The value to compare. + * @returns {Function} Returns the new function. + */ + function baseMatchesProperty(path, srcValue) { + var isArr = isArray(path), + isCommon = isKey(path) && isStrictComparable(srcValue), + pathKey = (path + ''); + + path = toPath(path); + return function(object) { + if (object == null) { + return false; + } + var key = pathKey; + object = toObject(object); + if ((isArr || !isCommon) && !(key in object)) { + object = path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1)); + if (object == null) { + return false; + } + key = last(path); + object = toObject(object); + } + return object[key] === srcValue + ? (srcValue !== undefined || (key in object)) + : baseIsEqual(srcValue, object[key], undefined, true); + }; + } + + /** + * The base implementation of `_.merge` without support for argument juggling, + * multiple sources, and `this` binding `customizer` functions. + * + * @private + * @param {Object} object The destination object. + * @param {Object} source The source object. + * @param {Function} [customizer] The function to customize merged values. + * @param {Array} [stackA=[]] Tracks traversed source objects. + * @param {Array} [stackB=[]] Associates values with source counterparts. + * @returns {Object} Returns `object`. + */ + function baseMerge(object, source, customizer, stackA, stackB) { + if (!isObject(object)) { + return object; + } + var isSrcArr = isArrayLike(source) && (isArray(source) || isTypedArray(source)), + props = isSrcArr ? undefined : keys(source); + + arrayEach(props || source, function(srcValue, key) { + if (props) { + key = srcValue; + srcValue = source[key]; + } + if (isObjectLike(srcValue)) { + stackA || (stackA = []); + stackB || (stackB = []); + baseMergeDeep(object, source, key, baseMerge, customizer, stackA, stackB); + } + else { + var value = object[key], + result = customizer ? customizer(value, srcValue, key, object, source) : undefined, + isCommon = result === undefined; + + if (isCommon) { + result = srcValue; + } + if ((result !== undefined || (isSrcArr && !(key in object))) && + (isCommon || (result === result ? (result !== value) : (value === value)))) { + object[key] = result; + } + } + }); + return object; + } + + /** + * A specialized version of `baseMerge` for arrays and objects which performs + * deep merges and tracks traversed objects enabling objects with circular + * references to be merged. + * + * @private + * @param {Object} object The destination object. + * @param {Object} source The source object. + * @param {string} key The key of the value to merge. + * @param {Function} mergeFunc The function to merge values. + * @param {Function} [customizer] The function to customize merged values. + * @param {Array} [stackA=[]] Tracks traversed source objects. + * @param {Array} [stackB=[]] Associates values with source counterparts. + * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. + */ + function baseMergeDeep(object, source, key, mergeFunc, customizer, stackA, stackB) { + var length = stackA.length, + srcValue = source[key]; + + while (length--) { + if (stackA[length] == srcValue) { + object[key] = stackB[length]; + return; + } + } + var value = object[key], + result = customizer ? customizer(value, srcValue, key, object, source) : undefined, + isCommon = result === undefined; + + if (isCommon) { + result = srcValue; + if (isArrayLike(srcValue) && (isArray(srcValue) || isTypedArray(srcValue))) { + result = isArray(value) + ? value + : (isArrayLike(value) ? arrayCopy(value) : []); + } + else if (isPlainObject(srcValue) || isArguments(srcValue)) { + result = isArguments(value) + ? toPlainObject(value) + : (isPlainObject(value) ? value : {}); + } + else { + isCommon = false; + } + } + // Add the source value to the stack of traversed objects and associate + // it with its merged value. + stackA.push(srcValue); + stackB.push(result); + + if (isCommon) { + // Recursively merge objects and arrays (susceptible to call stack limits). + object[key] = mergeFunc(result, srcValue, customizer, stackA, stackB); + } else if (result === result ? (result !== value) : (value === value)) { + object[key] = result; + } + } + + /** + * The base implementation of `_.property` without support for deep paths. + * + * @private + * @param {string} key The key of the property to get. + * @returns {Function} Returns the new function. + */ + function baseProperty(key) { + return function(object) { + return object == null ? undefined : object[key]; + }; + } + + /** + * A specialized version of `baseProperty` which supports deep paths. + * + * @private + * @param {Array|string} path The path of the property to get. + * @returns {Function} Returns the new function. + */ + function basePropertyDeep(path) { + var pathKey = (path + ''); + path = toPath(path); + return function(object) { + return baseGet(object, path, pathKey); + }; + } + + /** + * The base implementation of `_.pullAt` without support for individual + * index arguments and capturing the removed elements. + * + * @private + * @param {Array} array The array to modify. + * @param {number[]} indexes The indexes of elements to remove. + * @returns {Array} Returns `array`. + */ + function basePullAt(array, indexes) { + var length = array ? indexes.length : 0; + while (length--) { + var index = indexes[length]; + if (index != previous && isIndex(index)) { + var previous = index; + splice.call(array, index, 1); + } + } + return array; + } + + /** + * The base implementation of `_.random` without support for argument juggling + * and returning floating-point numbers. + * + * @private + * @param {number} min The minimum possible value. + * @param {number} max The maximum possible value. + * @returns {number} Returns the random number. + */ + function baseRandom(min, max) { + return min + nativeFloor(nativeRandom() * (max - min + 1)); + } + + /** + * The base implementation of `_.reduce` and `_.reduceRight` without support + * for callback shorthands and `this` binding, which iterates over `collection` + * using the provided `eachFunc`. + * + * @private + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {*} accumulator The initial value. + * @param {boolean} initFromCollection Specify using the first or last element + * of `collection` as the initial value. + * @param {Function} eachFunc The function to iterate over `collection`. + * @returns {*} Returns the accumulated value. + */ + function baseReduce(collection, iteratee, accumulator, initFromCollection, eachFunc) { + eachFunc(collection, function(value, index, collection) { + accumulator = initFromCollection + ? (initFromCollection = false, value) + : iteratee(accumulator, value, index, collection); + }); + return accumulator; + } + + /** + * The base implementation of `setData` without support for hot loop detection. + * + * @private + * @param {Function} func The function to associate metadata with. + * @param {*} data The metadata. + * @returns {Function} Returns `func`. + */ + var baseSetData = !metaMap ? identity : function(func, data) { + metaMap.set(func, data); + return func; + }; + + /** + * The base implementation of `_.slice` without an iteratee call guard. + * + * @private + * @param {Array} array The array to slice. + * @param {number} [start=0] The start position. + * @param {number} [end=array.length] The end position. + * @returns {Array} Returns the slice of `array`. + */ + function baseSlice(array, start, end) { + var index = -1, + length = array.length; + + start = start == null ? 0 : (+start || 0); + if (start < 0) { + start = -start > length ? 0 : (length + start); + } + end = (end === undefined || end > length) ? length : (+end || 0); + if (end < 0) { + end += length; + } + length = start > end ? 0 : ((end - start) >>> 0); + start >>>= 0; + + var result = Array(length); + while (++index < length) { + result[index] = array[index + start]; + } + return result; + } + + /** + * The base implementation of `_.some` without support for callback shorthands + * and `this` binding. + * + * @private + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {boolean} Returns `true` if any element passes the predicate check, + * else `false`. + */ + function baseSome(collection, predicate) { + var result; + + baseEach(collection, function(value, index, collection) { + result = predicate(value, index, collection); + return !result; + }); + return !!result; + } + + /** + * The base implementation of `_.sortBy` which uses `comparer` to define + * the sort order of `array` and replaces criteria objects with their + * corresponding values. + * + * @private + * @param {Array} array The array to sort. + * @param {Function} comparer The function to define sort order. + * @returns {Array} Returns `array`. + */ + function baseSortBy(array, comparer) { + var length = array.length; + + array.sort(comparer); + while (length--) { + array[length] = array[length].value; + } + return array; + } + + /** + * The base implementation of `_.sortByOrder` without param guards. + * + * @private + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function[]|Object[]|string[]} iteratees The iteratees to sort by. + * @param {boolean[]} orders The sort orders of `iteratees`. + * @returns {Array} Returns the new sorted array. + */ + function baseSortByOrder(collection, iteratees, orders) { + var callback = getCallback(), + index = -1; + + iteratees = arrayMap(iteratees, function(iteratee) { return callback(iteratee); }); + + var result = baseMap(collection, function(value) { + var criteria = arrayMap(iteratees, function(iteratee) { return iteratee(value); }); + return { 'criteria': criteria, 'index': ++index, 'value': value }; + }); + + return baseSortBy(result, function(object, other) { + return compareMultiple(object, other, orders); + }); + } + + /** + * The base implementation of `_.sum` without support for callback shorthands + * and `this` binding. + * + * @private + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {number} Returns the sum. + */ + function baseSum(collection, iteratee) { + var result = 0; + baseEach(collection, function(value, index, collection) { + result += +iteratee(value, index, collection) || 0; + }); + return result; + } + + /** + * The base implementation of `_.uniq` without support for callback shorthands + * and `this` binding. + * + * @private + * @param {Array} array The array to inspect. + * @param {Function} [iteratee] The function invoked per iteration. + * @returns {Array} Returns the new duplicate-value-free array. + */ + function baseUniq(array, iteratee) { + var index = -1, + indexOf = getIndexOf(), + length = array.length, + isCommon = indexOf == baseIndexOf, + isLarge = isCommon && length >= LARGE_ARRAY_SIZE, + seen = isLarge ? createCache() : null, + result = []; + + if (seen) { + indexOf = cacheIndexOf; + isCommon = false; + } else { + isLarge = false; + seen = iteratee ? [] : result; + } + outer: + while (++index < length) { + var value = array[index], + computed = iteratee ? iteratee(value, index, array) : value; + + if (isCommon && value === value) { + var seenIndex = seen.length; + while (seenIndex--) { + if (seen[seenIndex] === computed) { + continue outer; + } + } + if (iteratee) { + seen.push(computed); + } + result.push(value); + } + else if (indexOf(seen, computed, 0) < 0) { + if (iteratee || isLarge) { + seen.push(computed); + } + result.push(value); + } + } + return result; + } + + /** + * The base implementation of `_.values` and `_.valuesIn` which creates an + * array of `object` property values corresponding to the property names + * of `props`. + * + * @private + * @param {Object} object The object to query. + * @param {Array} props The property names to get values for. + * @returns {Object} Returns the array of property values. + */ + function baseValues(object, props) { + var index = -1, + length = props.length, + result = Array(length); + + while (++index < length) { + result[index] = object[props[index]]; + } + return result; + } + + /** + * The base implementation of `_.dropRightWhile`, `_.dropWhile`, `_.takeRightWhile`, + * and `_.takeWhile` without support for callback shorthands and `this` binding. + * + * @private + * @param {Array} array The array to query. + * @param {Function} predicate The function invoked per iteration. + * @param {boolean} [isDrop] Specify dropping elements instead of taking them. + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Array} Returns the slice of `array`. + */ + function baseWhile(array, predicate, isDrop, fromRight) { + var length = array.length, + index = fromRight ? length : -1; + + while ((fromRight ? index-- : ++index < length) && predicate(array[index], index, array)) {} + return isDrop + ? baseSlice(array, (fromRight ? 0 : index), (fromRight ? index + 1 : length)) + : baseSlice(array, (fromRight ? index + 1 : 0), (fromRight ? length : index)); + } + + /** + * The base implementation of `wrapperValue` which returns the result of + * performing a sequence of actions on the unwrapped `value`, where each + * successive action is supplied the return value of the previous. + * + * @private + * @param {*} value The unwrapped value. + * @param {Array} actions Actions to peform to resolve the unwrapped value. + * @returns {*} Returns the resolved value. + */ + function baseWrapperValue(value, actions) { + var result = value; + if (result instanceof LazyWrapper) { + result = result.value(); + } + var index = -1, + length = actions.length; + + while (++index < length) { + var action = actions[index]; + result = action.func.apply(action.thisArg, arrayPush([result], action.args)); + } + return result; + } + + /** + * Performs a binary search of `array` to determine the index at which `value` + * should be inserted into `array` in order to maintain its sort order. + * + * @private + * @param {Array} array The sorted array to inspect. + * @param {*} value The value to evaluate. + * @param {boolean} [retHighest] Specify returning the highest qualified index. + * @returns {number} Returns the index at which `value` should be inserted + * into `array`. + */ + function binaryIndex(array, value, retHighest) { + var low = 0, + high = array ? array.length : low; + + if (typeof value == 'number' && value === value && high <= HALF_MAX_ARRAY_LENGTH) { + while (low < high) { + var mid = (low + high) >>> 1, + computed = array[mid]; + + if ((retHighest ? (computed <= value) : (computed < value)) && computed !== null) { + low = mid + 1; + } else { + high = mid; + } + } + return high; + } + return binaryIndexBy(array, value, identity, retHighest); + } + + /** + * This function is like `binaryIndex` except that it invokes `iteratee` for + * `value` and each element of `array` to compute their sort ranking. The + * iteratee is invoked with one argument; (value). + * + * @private + * @param {Array} array The sorted array to inspect. + * @param {*} value The value to evaluate. + * @param {Function} iteratee The function invoked per iteration. + * @param {boolean} [retHighest] Specify returning the highest qualified index. + * @returns {number} Returns the index at which `value` should be inserted + * into `array`. + */ + function binaryIndexBy(array, value, iteratee, retHighest) { + value = iteratee(value); + + var low = 0, + high = array ? array.length : 0, + valIsNaN = value !== value, + valIsNull = value === null, + valIsUndef = value === undefined; + + while (low < high) { + var mid = nativeFloor((low + high) / 2), + computed = iteratee(array[mid]), + isDef = computed !== undefined, + isReflexive = computed === computed; + + if (valIsNaN) { + var setLow = isReflexive || retHighest; + } else if (valIsNull) { + setLow = isReflexive && isDef && (retHighest || computed != null); + } else if (valIsUndef) { + setLow = isReflexive && (retHighest || isDef); + } else if (computed == null) { + setLow = false; + } else { + setLow = retHighest ? (computed <= value) : (computed < value); + } + if (setLow) { + low = mid + 1; + } else { + high = mid; + } + } + return nativeMin(high, MAX_ARRAY_INDEX); + } + + /** + * A specialized version of `baseCallback` which only supports `this` binding + * and specifying the number of arguments to provide to `func`. + * + * @private + * @param {Function} func The function to bind. + * @param {*} thisArg The `this` binding of `func`. + * @param {number} [argCount] The number of arguments to provide to `func`. + * @returns {Function} Returns the callback. + */ + function bindCallback(func, thisArg, argCount) { + if (typeof func != 'function') { + return identity; + } + if (thisArg === undefined) { + return func; + } + switch (argCount) { + case 1: return function(value) { + return func.call(thisArg, value); + }; + case 3: return function(value, index, collection) { + return func.call(thisArg, value, index, collection); + }; + case 4: return function(accumulator, value, index, collection) { + return func.call(thisArg, accumulator, value, index, collection); + }; + case 5: return function(value, other, key, object, source) { + return func.call(thisArg, value, other, key, object, source); + }; + } + return function() { + return func.apply(thisArg, arguments); + }; + } + + /** + * Creates a clone of the given array buffer. + * + * @private + * @param {ArrayBuffer} buffer The array buffer to clone. + * @returns {ArrayBuffer} Returns the cloned array buffer. + */ + function bufferClone(buffer) { + var result = new ArrayBuffer(buffer.byteLength), + view = new Uint8Array(result); + + view.set(new Uint8Array(buffer)); + return result; + } + + /** + * Creates an array that is the composition of partially applied arguments, + * placeholders, and provided arguments into a single array of arguments. + * + * @private + * @param {Array|Object} args The provided arguments. + * @param {Array} partials The arguments to prepend to those provided. + * @param {Array} holders The `partials` placeholder indexes. + * @returns {Array} Returns the new array of composed arguments. + */ + function composeArgs(args, partials, holders) { + var holdersLength = holders.length, + argsIndex = -1, + argsLength = nativeMax(args.length - holdersLength, 0), + leftIndex = -1, + leftLength = partials.length, + result = Array(leftLength + argsLength); + + while (++leftIndex < leftLength) { + result[leftIndex] = partials[leftIndex]; + } + while (++argsIndex < holdersLength) { + result[holders[argsIndex]] = args[argsIndex]; + } + while (argsLength--) { + result[leftIndex++] = args[argsIndex++]; + } + return result; + } + + /** + * This function is like `composeArgs` except that the arguments composition + * is tailored for `_.partialRight`. + * + * @private + * @param {Array|Object} args The provided arguments. + * @param {Array} partials The arguments to append to those provided. + * @param {Array} holders The `partials` placeholder indexes. + * @returns {Array} Returns the new array of composed arguments. + */ + function composeArgsRight(args, partials, holders) { + var holdersIndex = -1, + holdersLength = holders.length, + argsIndex = -1, + argsLength = nativeMax(args.length - holdersLength, 0), + rightIndex = -1, + rightLength = partials.length, + result = Array(argsLength + rightLength); + + while (++argsIndex < argsLength) { + result[argsIndex] = args[argsIndex]; + } + var offset = argsIndex; + while (++rightIndex < rightLength) { + result[offset + rightIndex] = partials[rightIndex]; + } + while (++holdersIndex < holdersLength) { + result[offset + holders[holdersIndex]] = args[argsIndex++]; + } + return result; + } + + /** + * Creates a `_.countBy`, `_.groupBy`, `_.indexBy`, or `_.partition` function. + * + * @private + * @param {Function} setter The function to set keys and values of the accumulator object. + * @param {Function} [initializer] The function to initialize the accumulator object. + * @returns {Function} Returns the new aggregator function. + */ + function createAggregator(setter, initializer) { + return function(collection, iteratee, thisArg) { + var result = initializer ? initializer() : {}; + iteratee = getCallback(iteratee, thisArg, 3); + + if (isArray(collection)) { + var index = -1, + length = collection.length; + + while (++index < length) { + var value = collection[index]; + setter(result, value, iteratee(value, index, collection), collection); + } + } else { + baseEach(collection, function(value, key, collection) { + setter(result, value, iteratee(value, key, collection), collection); + }); + } + return result; + }; + } + + /** + * Creates a `_.assign`, `_.defaults`, or `_.merge` function. + * + * @private + * @param {Function} assigner The function to assign values. + * @returns {Function} Returns the new assigner function. + */ + function createAssigner(assigner) { + return restParam(function(object, sources) { + var index = -1, + length = object == null ? 0 : sources.length, + customizer = length > 2 ? sources[length - 2] : undefined, + guard = length > 2 ? sources[2] : undefined, + thisArg = length > 1 ? sources[length - 1] : undefined; + + if (typeof customizer == 'function') { + customizer = bindCallback(customizer, thisArg, 5); + length -= 2; + } else { + customizer = typeof thisArg == 'function' ? thisArg : undefined; + length -= (customizer ? 1 : 0); + } + if (guard && isIterateeCall(sources[0], sources[1], guard)) { + customizer = length < 3 ? undefined : customizer; + length = 1; + } + while (++index < length) { + var source = sources[index]; + if (source) { + assigner(object, source, customizer); + } + } + return object; + }); + } + + /** + * Creates a `baseEach` or `baseEachRight` function. + * + * @private + * @param {Function} eachFunc The function to iterate over a collection. + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Function} Returns the new base function. + */ + function createBaseEach(eachFunc, fromRight) { + return function(collection, iteratee) { + var length = collection ? getLength(collection) : 0; + if (!isLength(length)) { + return eachFunc(collection, iteratee); + } + var index = fromRight ? length : -1, + iterable = toObject(collection); + + while ((fromRight ? index-- : ++index < length)) { + if (iteratee(iterable[index], index, iterable) === false) { + break; + } + } + return collection; + }; + } + + /** + * Creates a base function for `_.forIn` or `_.forInRight`. + * + * @private + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Function} Returns the new base function. + */ + function createBaseFor(fromRight) { + return function(object, iteratee, keysFunc) { + var iterable = toObject(object), + props = keysFunc(object), + length = props.length, + index = fromRight ? length : -1; + + while ((fromRight ? index-- : ++index < length)) { + var key = props[index]; + if (iteratee(iterable[key], key, iterable) === false) { + break; + } + } + return object; + }; + } + + /** + * Creates a function that wraps `func` and invokes it with the `this` + * binding of `thisArg`. + * + * @private + * @param {Function} func The function to bind. + * @param {*} [thisArg] The `this` binding of `func`. + * @returns {Function} Returns the new bound function. + */ + function createBindWrapper(func, thisArg) { + var Ctor = createCtorWrapper(func); + + function wrapper() { + var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func; + return fn.apply(thisArg, arguments); + } + return wrapper; + } + + /** + * Creates a `Set` cache object to optimize linear searches of large arrays. + * + * @private + * @param {Array} [values] The values to cache. + * @returns {null|Object} Returns the new cache object if `Set` is supported, else `null`. + */ + function createCache(values) { + return (nativeCreate && Set) ? new SetCache(values) : null; + } + + /** + * Creates a function that produces compound words out of the words in a + * given string. + * + * @private + * @param {Function} callback The function to combine each word. + * @returns {Function} Returns the new compounder function. + */ + function createCompounder(callback) { + return function(string) { + var index = -1, + array = words(deburr(string)), + length = array.length, + result = ''; + + while (++index < length) { + result = callback(result, array[index], index); + } + return result; + }; + } + + /** + * Creates a function that produces an instance of `Ctor` regardless of + * whether it was invoked as part of a `new` expression or by `call` or `apply`. + * + * @private + * @param {Function} Ctor The constructor to wrap. + * @returns {Function} Returns the new wrapped function. + */ + function createCtorWrapper(Ctor) { + return function() { + // Use a `switch` statement to work with class constructors. + // See http://ecma-international.org/ecma-262/6.0/#sec-ecmascript-function-objects-call-thisargument-argumentslist + // for more details. + var args = arguments; + switch (args.length) { + case 0: return new Ctor; + case 1: return new Ctor(args[0]); + case 2: return new Ctor(args[0], args[1]); + case 3: return new Ctor(args[0], args[1], args[2]); + case 4: return new Ctor(args[0], args[1], args[2], args[3]); + case 5: return new Ctor(args[0], args[1], args[2], args[3], args[4]); + case 6: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5]); + case 7: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5], args[6]); + } + var thisBinding = baseCreate(Ctor.prototype), + result = Ctor.apply(thisBinding, args); + + // Mimic the constructor's `return` behavior. + // See https://es5.github.io/#x13.2.2 for more details. + return isObject(result) ? result : thisBinding; + }; + } + + /** + * Creates a `_.curry` or `_.curryRight` function. + * + * @private + * @param {boolean} flag The curry bit flag. + * @returns {Function} Returns the new curry function. + */ + function createCurry(flag) { + function curryFunc(func, arity, guard) { + if (guard && isIterateeCall(func, arity, guard)) { + arity = undefined; + } + var result = createWrapper(func, flag, undefined, undefined, undefined, undefined, undefined, arity); + result.placeholder = curryFunc.placeholder; + return result; + } + return curryFunc; + } + + /** + * Creates a `_.defaults` or `_.defaultsDeep` function. + * + * @private + * @param {Function} assigner The function to assign values. + * @param {Function} customizer The function to customize assigned values. + * @returns {Function} Returns the new defaults function. + */ + function createDefaults(assigner, customizer) { + return restParam(function(args) { + var object = args[0]; + if (object == null) { + return object; + } + args.push(customizer); + return assigner.apply(undefined, args); + }); + } + + /** + * Creates a `_.max` or `_.min` function. + * + * @private + * @param {Function} comparator The function used to compare values. + * @param {*} exValue The initial extremum value. + * @returns {Function} Returns the new extremum function. + */ + function createExtremum(comparator, exValue) { + return function(collection, iteratee, thisArg) { + if (thisArg && isIterateeCall(collection, iteratee, thisArg)) { + iteratee = undefined; + } + iteratee = getCallback(iteratee, thisArg, 3); + if (iteratee.length == 1) { + collection = isArray(collection) ? collection : toIterable(collection); + var result = arrayExtremum(collection, iteratee, comparator, exValue); + if (!(collection.length && result === exValue)) { + return result; + } + } + return baseExtremum(collection, iteratee, comparator, exValue); + }; + } + + /** + * Creates a `_.find` or `_.findLast` function. + * + * @private + * @param {Function} eachFunc The function to iterate over a collection. + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Function} Returns the new find function. + */ + function createFind(eachFunc, fromRight) { + return function(collection, predicate, thisArg) { + predicate = getCallback(predicate, thisArg, 3); + if (isArray(collection)) { + var index = baseFindIndex(collection, predicate, fromRight); + return index > -1 ? collection[index] : undefined; + } + return baseFind(collection, predicate, eachFunc); + }; + } + + /** + * Creates a `_.findIndex` or `_.findLastIndex` function. + * + * @private + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Function} Returns the new find function. + */ + function createFindIndex(fromRight) { + return function(array, predicate, thisArg) { + if (!(array && array.length)) { + return -1; + } + predicate = getCallback(predicate, thisArg, 3); + return baseFindIndex(array, predicate, fromRight); + }; + } + + /** + * Creates a `_.findKey` or `_.findLastKey` function. + * + * @private + * @param {Function} objectFunc The function to iterate over an object. + * @returns {Function} Returns the new find function. + */ + function createFindKey(objectFunc) { + return function(object, predicate, thisArg) { + predicate = getCallback(predicate, thisArg, 3); + return baseFind(object, predicate, objectFunc, true); + }; + } + + /** + * Creates a `_.flow` or `_.flowRight` function. + * + * @private + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Function} Returns the new flow function. + */ + function createFlow(fromRight) { + return function() { + var wrapper, + length = arguments.length, + index = fromRight ? length : -1, + leftIndex = 0, + funcs = Array(length); + + while ((fromRight ? index-- : ++index < length)) { + var func = funcs[leftIndex++] = arguments[index]; + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + if (!wrapper && LodashWrapper.prototype.thru && getFuncName(func) == 'wrapper') { + wrapper = new LodashWrapper([], true); + } + } + index = wrapper ? -1 : length; + while (++index < length) { + func = funcs[index]; + + var funcName = getFuncName(func), + data = funcName == 'wrapper' ? getData(func) : undefined; + + if (data && isLaziable(data[0]) && data[1] == (ARY_FLAG | CURRY_FLAG | PARTIAL_FLAG | REARG_FLAG) && !data[4].length && data[9] == 1) { + wrapper = wrapper[getFuncName(data[0])].apply(wrapper, data[3]); + } else { + wrapper = (func.length == 1 && isLaziable(func)) ? wrapper[funcName]() : wrapper.thru(func); + } + } + return function() { + var args = arguments, + value = args[0]; + + if (wrapper && args.length == 1 && isArray(value) && value.length >= LARGE_ARRAY_SIZE) { + return wrapper.plant(value).value(); + } + var index = 0, + result = length ? funcs[index].apply(this, args) : value; + + while (++index < length) { + result = funcs[index].call(this, result); + } + return result; + }; + }; + } + + /** + * Creates a function for `_.forEach` or `_.forEachRight`. + * + * @private + * @param {Function} arrayFunc The function to iterate over an array. + * @param {Function} eachFunc The function to iterate over a collection. + * @returns {Function} Returns the new each function. + */ + function createForEach(arrayFunc, eachFunc) { + return function(collection, iteratee, thisArg) { + return (typeof iteratee == 'function' && thisArg === undefined && isArray(collection)) + ? arrayFunc(collection, iteratee) + : eachFunc(collection, bindCallback(iteratee, thisArg, 3)); + }; + } + + /** + * Creates a function for `_.forIn` or `_.forInRight`. + * + * @private + * @param {Function} objectFunc The function to iterate over an object. + * @returns {Function} Returns the new each function. + */ + function createForIn(objectFunc) { + return function(object, iteratee, thisArg) { + if (typeof iteratee != 'function' || thisArg !== undefined) { + iteratee = bindCallback(iteratee, thisArg, 3); + } + return objectFunc(object, iteratee, keysIn); + }; + } + + /** + * Creates a function for `_.forOwn` or `_.forOwnRight`. + * + * @private + * @param {Function} objectFunc The function to iterate over an object. + * @returns {Function} Returns the new each function. + */ + function createForOwn(objectFunc) { + return function(object, iteratee, thisArg) { + if (typeof iteratee != 'function' || thisArg !== undefined) { + iteratee = bindCallback(iteratee, thisArg, 3); + } + return objectFunc(object, iteratee); + }; + } + + /** + * Creates a function for `_.mapKeys` or `_.mapValues`. + * + * @private + * @param {boolean} [isMapKeys] Specify mapping keys instead of values. + * @returns {Function} Returns the new map function. + */ + function createObjectMapper(isMapKeys) { + return function(object, iteratee, thisArg) { + var result = {}; + iteratee = getCallback(iteratee, thisArg, 3); + + baseForOwn(object, function(value, key, object) { + var mapped = iteratee(value, key, object); + key = isMapKeys ? mapped : key; + value = isMapKeys ? value : mapped; + result[key] = value; + }); + return result; + }; + } + + /** + * Creates a function for `_.padLeft` or `_.padRight`. + * + * @private + * @param {boolean} [fromRight] Specify padding from the right. + * @returns {Function} Returns the new pad function. + */ + function createPadDir(fromRight) { + return function(string, length, chars) { + string = baseToString(string); + return (fromRight ? string : '') + createPadding(string, length, chars) + (fromRight ? '' : string); + }; + } + + /** + * Creates a `_.partial` or `_.partialRight` function. + * + * @private + * @param {boolean} flag The partial bit flag. + * @returns {Function} Returns the new partial function. + */ + function createPartial(flag) { + var partialFunc = restParam(function(func, partials) { + var holders = replaceHolders(partials, partialFunc.placeholder); + return createWrapper(func, flag, undefined, partials, holders); + }); + return partialFunc; + } + + /** + * Creates a function for `_.reduce` or `_.reduceRight`. + * + * @private + * @param {Function} arrayFunc The function to iterate over an array. + * @param {Function} eachFunc The function to iterate over a collection. + * @returns {Function} Returns the new each function. + */ + function createReduce(arrayFunc, eachFunc) { + return function(collection, iteratee, accumulator, thisArg) { + var initFromArray = arguments.length < 3; + return (typeof iteratee == 'function' && thisArg === undefined && isArray(collection)) + ? arrayFunc(collection, iteratee, accumulator, initFromArray) + : baseReduce(collection, getCallback(iteratee, thisArg, 4), accumulator, initFromArray, eachFunc); + }; + } + + /** + * Creates a function that wraps `func` and invokes it with optional `this` + * binding of, partial application, and currying. + * + * @private + * @param {Function|string} func The function or method name to reference. + * @param {number} bitmask The bitmask of flags. See `createWrapper` for more details. + * @param {*} [thisArg] The `this` binding of `func`. + * @param {Array} [partials] The arguments to prepend to those provided to the new function. + * @param {Array} [holders] The `partials` placeholder indexes. + * @param {Array} [partialsRight] The arguments to append to those provided to the new function. + * @param {Array} [holdersRight] The `partialsRight` placeholder indexes. + * @param {Array} [argPos] The argument positions of the new function. + * @param {number} [ary] The arity cap of `func`. + * @param {number} [arity] The arity of `func`. + * @returns {Function} Returns the new wrapped function. + */ + function createHybridWrapper(func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity) { + var isAry = bitmask & ARY_FLAG, + isBind = bitmask & BIND_FLAG, + isBindKey = bitmask & BIND_KEY_FLAG, + isCurry = bitmask & CURRY_FLAG, + isCurryBound = bitmask & CURRY_BOUND_FLAG, + isCurryRight = bitmask & CURRY_RIGHT_FLAG, + Ctor = isBindKey ? undefined : createCtorWrapper(func); + + function wrapper() { + // Avoid `arguments` object use disqualifying optimizations by + // converting it to an array before providing it to other functions. + var length = arguments.length, + index = length, + args = Array(length); + + while (index--) { + args[index] = arguments[index]; + } + if (partials) { + args = composeArgs(args, partials, holders); + } + if (partialsRight) { + args = composeArgsRight(args, partialsRight, holdersRight); + } + if (isCurry || isCurryRight) { + var placeholder = wrapper.placeholder, + argsHolders = replaceHolders(args, placeholder); + + length -= argsHolders.length; + if (length < arity) { + var newArgPos = argPos ? arrayCopy(argPos) : undefined, + newArity = nativeMax(arity - length, 0), + newsHolders = isCurry ? argsHolders : undefined, + newHoldersRight = isCurry ? undefined : argsHolders, + newPartials = isCurry ? args : undefined, + newPartialsRight = isCurry ? undefined : args; + + bitmask |= (isCurry ? PARTIAL_FLAG : PARTIAL_RIGHT_FLAG); + bitmask &= ~(isCurry ? PARTIAL_RIGHT_FLAG : PARTIAL_FLAG); + + if (!isCurryBound) { + bitmask &= ~(BIND_FLAG | BIND_KEY_FLAG); + } + var newData = [func, bitmask, thisArg, newPartials, newsHolders, newPartialsRight, newHoldersRight, newArgPos, ary, newArity], + result = createHybridWrapper.apply(undefined, newData); + + if (isLaziable(func)) { + setData(result, newData); + } + result.placeholder = placeholder; + return result; + } + } + var thisBinding = isBind ? thisArg : this, + fn = isBindKey ? thisBinding[func] : func; + + if (argPos) { + args = reorder(args, argPos); + } + if (isAry && ary < args.length) { + args.length = ary; + } + if (this && this !== root && this instanceof wrapper) { + fn = Ctor || createCtorWrapper(func); + } + return fn.apply(thisBinding, args); + } + return wrapper; + } + + /** + * Creates the padding required for `string` based on the given `length`. + * The `chars` string is truncated if the number of characters exceeds `length`. + * + * @private + * @param {string} string The string to create padding for. + * @param {number} [length=0] The padding length. + * @param {string} [chars=' '] The string used as padding. + * @returns {string} Returns the pad for `string`. + */ + function createPadding(string, length, chars) { + var strLength = string.length; + length = +length; + + if (strLength >= length || !nativeIsFinite(length)) { + return ''; + } + var padLength = length - strLength; + chars = chars == null ? ' ' : (chars + ''); + return repeat(chars, nativeCeil(padLength / chars.length)).slice(0, padLength); + } + + /** + * Creates a function that wraps `func` and invokes it with the optional `this` + * binding of `thisArg` and the `partials` prepended to those provided to + * the wrapper. + * + * @private + * @param {Function} func The function to partially apply arguments to. + * @param {number} bitmask The bitmask of flags. See `createWrapper` for more details. + * @param {*} thisArg The `this` binding of `func`. + * @param {Array} partials The arguments to prepend to those provided to the new function. + * @returns {Function} Returns the new bound function. + */ + function createPartialWrapper(func, bitmask, thisArg, partials) { + var isBind = bitmask & BIND_FLAG, + Ctor = createCtorWrapper(func); + + function wrapper() { + // Avoid `arguments` object use disqualifying optimizations by + // converting it to an array before providing it `func`. + var argsIndex = -1, + argsLength = arguments.length, + leftIndex = -1, + leftLength = partials.length, + args = Array(leftLength + argsLength); + + while (++leftIndex < leftLength) { + args[leftIndex] = partials[leftIndex]; + } + while (argsLength--) { + args[leftIndex++] = arguments[++argsIndex]; + } + var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func; + return fn.apply(isBind ? thisArg : this, args); + } + return wrapper; + } + + /** + * Creates a `_.ceil`, `_.floor`, or `_.round` function. + * + * @private + * @param {string} methodName The name of the `Math` method to use when rounding. + * @returns {Function} Returns the new round function. + */ + function createRound(methodName) { + var func = Math[methodName]; + return function(number, precision) { + precision = precision === undefined ? 0 : (+precision || 0); + if (precision) { + precision = pow(10, precision); + return func(number * precision) / precision; + } + return func(number); + }; + } + + /** + * Creates a `_.sortedIndex` or `_.sortedLastIndex` function. + * + * @private + * @param {boolean} [retHighest] Specify returning the highest qualified index. + * @returns {Function} Returns the new index function. + */ + function createSortedIndex(retHighest) { + return function(array, value, iteratee, thisArg) { + var callback = getCallback(iteratee); + return (iteratee == null && callback === baseCallback) + ? binaryIndex(array, value, retHighest) + : binaryIndexBy(array, value, callback(iteratee, thisArg, 1), retHighest); + }; + } + + /** + * Creates a function that either curries or invokes `func` with optional + * `this` binding and partially applied arguments. + * + * @private + * @param {Function|string} func The function or method name to reference. + * @param {number} bitmask The bitmask of flags. + * The bitmask may be composed of the following flags: + * 1 - `_.bind` + * 2 - `_.bindKey` + * 4 - `_.curry` or `_.curryRight` of a bound function + * 8 - `_.curry` + * 16 - `_.curryRight` + * 32 - `_.partial` + * 64 - `_.partialRight` + * 128 - `_.rearg` + * 256 - `_.ary` + * @param {*} [thisArg] The `this` binding of `func`. + * @param {Array} [partials] The arguments to be partially applied. + * @param {Array} [holders] The `partials` placeholder indexes. + * @param {Array} [argPos] The argument positions of the new function. + * @param {number} [ary] The arity cap of `func`. + * @param {number} [arity] The arity of `func`. + * @returns {Function} Returns the new wrapped function. + */ + function createWrapper(func, bitmask, thisArg, partials, holders, argPos, ary, arity) { + var isBindKey = bitmask & BIND_KEY_FLAG; + if (!isBindKey && typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + var length = partials ? partials.length : 0; + if (!length) { + bitmask &= ~(PARTIAL_FLAG | PARTIAL_RIGHT_FLAG); + partials = holders = undefined; + } + length -= (holders ? holders.length : 0); + if (bitmask & PARTIAL_RIGHT_FLAG) { + var partialsRight = partials, + holdersRight = holders; + + partials = holders = undefined; + } + var data = isBindKey ? undefined : getData(func), + newData = [func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity]; + + if (data) { + mergeData(newData, data); + bitmask = newData[1]; + arity = newData[9]; + } + newData[9] = arity == null + ? (isBindKey ? 0 : func.length) + : (nativeMax(arity - length, 0) || 0); + + if (bitmask == BIND_FLAG) { + var result = createBindWrapper(newData[0], newData[2]); + } else if ((bitmask == PARTIAL_FLAG || bitmask == (BIND_FLAG | PARTIAL_FLAG)) && !newData[4].length) { + result = createPartialWrapper.apply(undefined, newData); + } else { + result = createHybridWrapper.apply(undefined, newData); + } + var setter = data ? baseSetData : setData; + return setter(result, newData); + } + + /** + * A specialized version of `baseIsEqualDeep` for arrays with support for + * partial deep comparisons. + * + * @private + * @param {Array} array The array to compare. + * @param {Array} other The other array to compare. + * @param {Function} equalFunc The function to determine equivalents of values. + * @param {Function} [customizer] The function to customize comparing arrays. + * @param {boolean} [isLoose] Specify performing partial comparisons. + * @param {Array} [stackA] Tracks traversed `value` objects. + * @param {Array} [stackB] Tracks traversed `other` objects. + * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`. + */ + function equalArrays(array, other, equalFunc, customizer, isLoose, stackA, stackB) { + var index = -1, + arrLength = array.length, + othLength = other.length; + + if (arrLength != othLength && !(isLoose && othLength > arrLength)) { + return false; + } + // Ignore non-index properties. + while (++index < arrLength) { + var arrValue = array[index], + othValue = other[index], + result = customizer ? customizer(isLoose ? othValue : arrValue, isLoose ? arrValue : othValue, index) : undefined; + + if (result !== undefined) { + if (result) { + continue; + } + return false; + } + // Recursively compare arrays (susceptible to call stack limits). + if (isLoose) { + if (!arraySome(other, function(othValue) { + return arrValue === othValue || equalFunc(arrValue, othValue, customizer, isLoose, stackA, stackB); + })) { + return false; + } + } else if (!(arrValue === othValue || equalFunc(arrValue, othValue, customizer, isLoose, stackA, stackB))) { + return false; + } + } + return true; + } + + /** + * A specialized version of `baseIsEqualDeep` for comparing objects of + * the same `toStringTag`. + * + * **Note:** This function only supports comparing values with tags of + * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`. + * + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @param {string} tag The `toStringTag` of the objects to compare. + * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. + */ + function equalByTag(object, other, tag) { + switch (tag) { + case boolTag: + case dateTag: + // Coerce dates and booleans to numbers, dates to milliseconds and booleans + // to `1` or `0` treating invalid dates coerced to `NaN` as not equal. + return +object == +other; + + case errorTag: + return object.name == other.name && object.message == other.message; + + case numberTag: + // Treat `NaN` vs. `NaN` as equal. + return (object != +object) + ? other != +other + : object == +other; + + case regexpTag: + case stringTag: + // Coerce regexes to strings and treat strings primitives and string + // objects as equal. See https://es5.github.io/#x15.10.6.4 for more details. + return object == (other + ''); + } + return false; + } + + /** + * A specialized version of `baseIsEqualDeep` for objects with support for + * partial deep comparisons. + * + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @param {Function} equalFunc The function to determine equivalents of values. + * @param {Function} [customizer] The function to customize comparing values. + * @param {boolean} [isLoose] Specify performing partial comparisons. + * @param {Array} [stackA] Tracks traversed `value` objects. + * @param {Array} [stackB] Tracks traversed `other` objects. + * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. + */ + function equalObjects(object, other, equalFunc, customizer, isLoose, stackA, stackB) { + var objProps = keys(object), + objLength = objProps.length, + othProps = keys(other), + othLength = othProps.length; + + if (objLength != othLength && !isLoose) { + return false; + } + var index = objLength; + while (index--) { + var key = objProps[index]; + if (!(isLoose ? key in other : hasOwnProperty.call(other, key))) { + return false; + } + } + var skipCtor = isLoose; + while (++index < objLength) { + key = objProps[index]; + var objValue = object[key], + othValue = other[key], + result = customizer ? customizer(isLoose ? othValue : objValue, isLoose? objValue : othValue, key) : undefined; + + // Recursively compare objects (susceptible to call stack limits). + if (!(result === undefined ? equalFunc(objValue, othValue, customizer, isLoose, stackA, stackB) : result)) { + return false; + } + skipCtor || (skipCtor = key == 'constructor'); + } + if (!skipCtor) { + var objCtor = object.constructor, + othCtor = other.constructor; + + // Non `Object` object instances with different constructors are not equal. + if (objCtor != othCtor && + ('constructor' in object && 'constructor' in other) && + !(typeof objCtor == 'function' && objCtor instanceof objCtor && + typeof othCtor == 'function' && othCtor instanceof othCtor)) { + return false; + } + } + return true; + } + + /** + * Gets the appropriate "callback" function. If the `_.callback` method is + * customized this function returns the custom method, otherwise it returns + * the `baseCallback` function. If arguments are provided the chosen function + * is invoked with them and its result is returned. + * + * @private + * @returns {Function} Returns the chosen function or its result. + */ + function getCallback(func, thisArg, argCount) { + var result = lodash.callback || callback; + result = result === callback ? baseCallback : result; + return argCount ? result(func, thisArg, argCount) : result; + } + + /** + * Gets metadata for `func`. + * + * @private + * @param {Function} func The function to query. + * @returns {*} Returns the metadata for `func`. + */ + var getData = !metaMap ? noop : function(func) { + return metaMap.get(func); + }; + + /** + * Gets the name of `func`. + * + * @private + * @param {Function} func The function to query. + * @returns {string} Returns the function name. + */ + function getFuncName(func) { + var result = func.name, + array = realNames[result], + length = array ? array.length : 0; + + while (length--) { + var data = array[length], + otherFunc = data.func; + if (otherFunc == null || otherFunc == func) { + return data.name; + } + } + return result; + } + + /** + * Gets the appropriate "indexOf" function. If the `_.indexOf` method is + * customized this function returns the custom method, otherwise it returns + * the `baseIndexOf` function. If arguments are provided the chosen function + * is invoked with them and its result is returned. + * + * @private + * @returns {Function|number} Returns the chosen function or its result. + */ + function getIndexOf(collection, target, fromIndex) { + var result = lodash.indexOf || indexOf; + result = result === indexOf ? baseIndexOf : result; + return collection ? result(collection, target, fromIndex) : result; + } + + /** + * Gets the "length" property value of `object`. + * + * **Note:** This function is used to avoid a [JIT bug](https://bugs.webkit.org/show_bug.cgi?id=142792) + * that affects Safari on at least iOS 8.1-8.3 ARM64. + * + * @private + * @param {Object} object The object to query. + * @returns {*} Returns the "length" value. + */ + var getLength = baseProperty('length'); + + /** + * Gets the propery names, values, and compare flags of `object`. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the match data of `object`. + */ + function getMatchData(object) { + var result = pairs(object), + length = result.length; + + while (length--) { + result[length][2] = isStrictComparable(result[length][1]); + } + return result; + } + + /** + * Gets the native function at `key` of `object`. + * + * @private + * @param {Object} object The object to query. + * @param {string} key The key of the method to get. + * @returns {*} Returns the function if it's native, else `undefined`. + */ + function getNative(object, key) { + var value = object == null ? undefined : object[key]; + return isNative(value) ? value : undefined; + } + + /** + * Gets the view, applying any `transforms` to the `start` and `end` positions. + * + * @private + * @param {number} start The start of the view. + * @param {number} end The end of the view. + * @param {Array} transforms The transformations to apply to the view. + * @returns {Object} Returns an object containing the `start` and `end` + * positions of the view. + */ + function getView(start, end, transforms) { + var index = -1, + length = transforms.length; + + while (++index < length) { + var data = transforms[index], + size = data.size; + + switch (data.type) { + case 'drop': start += size; break; + case 'dropRight': end -= size; break; + case 'take': end = nativeMin(end, start + size); break; + case 'takeRight': start = nativeMax(start, end - size); break; + } + } + return { 'start': start, 'end': end }; + } + + /** + * Initializes an array clone. + * + * @private + * @param {Array} array The array to clone. + * @returns {Array} Returns the initialized clone. + */ + function initCloneArray(array) { + var length = array.length, + result = new array.constructor(length); + + // Add array properties assigned by `RegExp#exec`. + if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) { + result.index = array.index; + result.input = array.input; + } + return result; + } + + /** + * Initializes an object clone. + * + * @private + * @param {Object} object The object to clone. + * @returns {Object} Returns the initialized clone. + */ + function initCloneObject(object) { + var Ctor = object.constructor; + if (!(typeof Ctor == 'function' && Ctor instanceof Ctor)) { + Ctor = Object; + } + return new Ctor; + } + + /** + * Initializes an object clone based on its `toStringTag`. + * + * **Note:** This function only supports cloning values with tags of + * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`. + * + * @private + * @param {Object} object The object to clone. + * @param {string} tag The `toStringTag` of the object to clone. + * @param {boolean} [isDeep] Specify a deep clone. + * @returns {Object} Returns the initialized clone. + */ + function initCloneByTag(object, tag, isDeep) { + var Ctor = object.constructor; + switch (tag) { + case arrayBufferTag: + return bufferClone(object); + + case boolTag: + case dateTag: + return new Ctor(+object); + + case float32Tag: case float64Tag: + case int8Tag: case int16Tag: case int32Tag: + case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag: + var buffer = object.buffer; + return new Ctor(isDeep ? bufferClone(buffer) : buffer, object.byteOffset, object.length); + + case numberTag: + case stringTag: + return new Ctor(object); + + case regexpTag: + var result = new Ctor(object.source, reFlags.exec(object)); + result.lastIndex = object.lastIndex; + } + return result; + } + + /** + * Invokes the method at `path` on `object`. + * + * @private + * @param {Object} object The object to query. + * @param {Array|string} path The path of the method to invoke. + * @param {Array} args The arguments to invoke the method with. + * @returns {*} Returns the result of the invoked method. + */ + function invokePath(object, path, args) { + if (object != null && !isKey(path, object)) { + path = toPath(path); + object = path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1)); + path = last(path); + } + var func = object == null ? object : object[path]; + return func == null ? undefined : func.apply(object, args); + } + + /** + * Checks if `value` is array-like. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is array-like, else `false`. + */ + function isArrayLike(value) { + return value != null && isLength(getLength(value)); + } + + /** + * Checks if `value` is a valid array-like index. + * + * @private + * @param {*} value The value to check. + * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index. + * @returns {boolean} Returns `true` if `value` is a valid index, else `false`. + */ + function isIndex(value, length) { + value = (typeof value == 'number' || reIsUint.test(value)) ? +value : -1; + length = length == null ? MAX_SAFE_INTEGER : length; + return value > -1 && value % 1 == 0 && value < length; + } + + /** + * Checks if the provided arguments are from an iteratee call. + * + * @private + * @param {*} value The potential iteratee value argument. + * @param {*} index The potential iteratee index or key argument. + * @param {*} object The potential iteratee object argument. + * @returns {boolean} Returns `true` if the arguments are from an iteratee call, else `false`. + */ + function isIterateeCall(value, index, object) { + if (!isObject(object)) { + return false; + } + var type = typeof index; + if (type == 'number' + ? (isArrayLike(object) && isIndex(index, object.length)) + : (type == 'string' && index in object)) { + var other = object[index]; + return value === value ? (value === other) : (other !== other); + } + return false; + } + + /** + * Checks if `value` is a property name and not a property path. + * + * @private + * @param {*} value The value to check. + * @param {Object} [object] The object to query keys on. + * @returns {boolean} Returns `true` if `value` is a property name, else `false`. + */ + function isKey(value, object) { + var type = typeof value; + if ((type == 'string' && reIsPlainProp.test(value)) || type == 'number') { + return true; + } + if (isArray(value)) { + return false; + } + var result = !reIsDeepProp.test(value); + return result || (object != null && value in toObject(object)); + } + + /** + * Checks if `func` has a lazy counterpart. + * + * @private + * @param {Function} func The function to check. + * @returns {boolean} Returns `true` if `func` has a lazy counterpart, else `false`. + */ + function isLaziable(func) { + var funcName = getFuncName(func); + if (!(funcName in LazyWrapper.prototype)) { + return false; + } + var other = lodash[funcName]; + if (func === other) { + return true; + } + var data = getData(other); + return !!data && func === data[0]; + } + + /** + * Checks if `value` is a valid array-like length. + * + * **Note:** This function is based on [`ToLength`](http://ecma-international.org/ecma-262/6.0/#sec-tolength). + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a valid length, else `false`. + */ + function isLength(value) { + return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER; + } + + /** + * Checks if `value` is suitable for strict equality comparisons, i.e. `===`. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` if suitable for strict + * equality comparisons, else `false`. + */ + function isStrictComparable(value) { + return value === value && !isObject(value); + } + + /** + * Merges the function metadata of `source` into `data`. + * + * Merging metadata reduces the number of wrappers required to invoke a function. + * This is possible because methods like `_.bind`, `_.curry`, and `_.partial` + * may be applied regardless of execution order. Methods like `_.ary` and `_.rearg` + * augment function arguments, making the order in which they are executed important, + * preventing the merging of metadata. However, we make an exception for a safe + * common case where curried functions have `_.ary` and or `_.rearg` applied. + * + * @private + * @param {Array} data The destination metadata. + * @param {Array} source The source metadata. + * @returns {Array} Returns `data`. + */ + function mergeData(data, source) { + var bitmask = data[1], + srcBitmask = source[1], + newBitmask = bitmask | srcBitmask, + isCommon = newBitmask < ARY_FLAG; + + var isCombo = + (srcBitmask == ARY_FLAG && bitmask == CURRY_FLAG) || + (srcBitmask == ARY_FLAG && bitmask == REARG_FLAG && data[7].length <= source[8]) || + (srcBitmask == (ARY_FLAG | REARG_FLAG) && bitmask == CURRY_FLAG); + + // Exit early if metadata can't be merged. + if (!(isCommon || isCombo)) { + return data; + } + // Use source `thisArg` if available. + if (srcBitmask & BIND_FLAG) { + data[2] = source[2]; + // Set when currying a bound function. + newBitmask |= (bitmask & BIND_FLAG) ? 0 : CURRY_BOUND_FLAG; + } + // Compose partial arguments. + var value = source[3]; + if (value) { + var partials = data[3]; + data[3] = partials ? composeArgs(partials, value, source[4]) : arrayCopy(value); + data[4] = partials ? replaceHolders(data[3], PLACEHOLDER) : arrayCopy(source[4]); + } + // Compose partial right arguments. + value = source[5]; + if (value) { + partials = data[5]; + data[5] = partials ? composeArgsRight(partials, value, source[6]) : arrayCopy(value); + data[6] = partials ? replaceHolders(data[5], PLACEHOLDER) : arrayCopy(source[6]); + } + // Use source `argPos` if available. + value = source[7]; + if (value) { + data[7] = arrayCopy(value); + } + // Use source `ary` if it's smaller. + if (srcBitmask & ARY_FLAG) { + data[8] = data[8] == null ? source[8] : nativeMin(data[8], source[8]); + } + // Use source `arity` if one is not provided. + if (data[9] == null) { + data[9] = source[9]; + } + // Use source `func` and merge bitmasks. + data[0] = source[0]; + data[1] = newBitmask; + + return data; + } + + /** + * Used by `_.defaultsDeep` to customize its `_.merge` use. + * + * @private + * @param {*} objectValue The destination object property value. + * @param {*} sourceValue The source object property value. + * @returns {*} Returns the value to assign to the destination object. + */ + function mergeDefaults(objectValue, sourceValue) { + return objectValue === undefined ? sourceValue : merge(objectValue, sourceValue, mergeDefaults); + } + + /** + * A specialized version of `_.pick` which picks `object` properties specified + * by `props`. + * + * @private + * @param {Object} object The source object. + * @param {string[]} props The property names to pick. + * @returns {Object} Returns the new object. + */ + function pickByArray(object, props) { + object = toObject(object); + + var index = -1, + length = props.length, + result = {}; + + while (++index < length) { + var key = props[index]; + if (key in object) { + result[key] = object[key]; + } + } + return result; + } + + /** + * A specialized version of `_.pick` which picks `object` properties `predicate` + * returns truthy for. + * + * @private + * @param {Object} object The source object. + * @param {Function} predicate The function invoked per iteration. + * @returns {Object} Returns the new object. + */ + function pickByCallback(object, predicate) { + var result = {}; + baseForIn(object, function(value, key, object) { + if (predicate(value, key, object)) { + result[key] = value; + } + }); + return result; + } + + /** + * Reorder `array` according to the specified indexes where the element at + * the first index is assigned as the first element, the element at + * the second index is assigned as the second element, and so on. + * + * @private + * @param {Array} array The array to reorder. + * @param {Array} indexes The arranged array indexes. + * @returns {Array} Returns `array`. + */ + function reorder(array, indexes) { + var arrLength = array.length, + length = nativeMin(indexes.length, arrLength), + oldArray = arrayCopy(array); + + while (length--) { + var index = indexes[length]; + array[length] = isIndex(index, arrLength) ? oldArray[index] : undefined; + } + return array; + } + + /** + * Sets metadata for `func`. + * + * **Note:** If this function becomes hot, i.e. is invoked a lot in a short + * period of time, it will trip its breaker and transition to an identity function + * to avoid garbage collection pauses in V8. See [V8 issue 2070](https://code.google.com/p/v8/issues/detail?id=2070) + * for more details. + * + * @private + * @param {Function} func The function to associate metadata with. + * @param {*} data The metadata. + * @returns {Function} Returns `func`. + */ + var setData = (function() { + var count = 0, + lastCalled = 0; + + return function(key, value) { + var stamp = now(), + remaining = HOT_SPAN - (stamp - lastCalled); + + lastCalled = stamp; + if (remaining > 0) { + if (++count >= HOT_COUNT) { + return key; + } + } else { + count = 0; + } + return baseSetData(key, value); + }; + }()); + + /** + * A fallback implementation of `Object.keys` which creates an array of the + * own enumerable property names of `object`. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + */ + function shimKeys(object) { + var props = keysIn(object), + propsLength = props.length, + length = propsLength && object.length; + + var allowIndexes = !!length && isLength(length) && + (isArray(object) || isArguments(object)); + + var index = -1, + result = []; + + while (++index < propsLength) { + var key = props[index]; + if ((allowIndexes && isIndex(key, length)) || hasOwnProperty.call(object, key)) { + result.push(key); + } + } + return result; + } + + /** + * Converts `value` to an array-like object if it's not one. + * + * @private + * @param {*} value The value to process. + * @returns {Array|Object} Returns the array-like object. + */ + function toIterable(value) { + if (value == null) { + return []; + } + if (!isArrayLike(value)) { + return values(value); + } + return isObject(value) ? value : Object(value); + } + + /** + * Converts `value` to an object if it's not one. + * + * @private + * @param {*} value The value to process. + * @returns {Object} Returns the object. + */ + function toObject(value) { + return isObject(value) ? value : Object(value); + } + + /** + * Converts `value` to property path array if it's not one. + * + * @private + * @param {*} value The value to process. + * @returns {Array} Returns the property path array. + */ + function toPath(value) { + if (isArray(value)) { + return value; + } + var result = []; + baseToString(value).replace(rePropName, function(match, number, quote, string) { + result.push(quote ? string.replace(reEscapeChar, '$1') : (number || match)); + }); + return result; + } + + /** + * Creates a clone of `wrapper`. + * + * @private + * @param {Object} wrapper The wrapper to clone. + * @returns {Object} Returns the cloned wrapper. + */ + function wrapperClone(wrapper) { + return wrapper instanceof LazyWrapper + ? wrapper.clone() + : new LodashWrapper(wrapper.__wrapped__, wrapper.__chain__, arrayCopy(wrapper.__actions__)); + } + + /*------------------------------------------------------------------------*/ + + /** + * Creates an array of elements split into groups the length of `size`. + * If `collection` can't be split evenly, the final chunk will be the remaining + * elements. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to process. + * @param {number} [size=1] The length of each chunk. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {Array} Returns the new array containing chunks. + * @example + * + * _.chunk(['a', 'b', 'c', 'd'], 2); + * // => [['a', 'b'], ['c', 'd']] + * + * _.chunk(['a', 'b', 'c', 'd'], 3); + * // => [['a', 'b', 'c'], ['d']] + */ + function chunk(array, size, guard) { + if (guard ? isIterateeCall(array, size, guard) : size == null) { + size = 1; + } else { + size = nativeMax(nativeFloor(size) || 1, 1); + } + var index = 0, + length = array ? array.length : 0, + resIndex = -1, + result = Array(nativeCeil(length / size)); + + while (index < length) { + result[++resIndex] = baseSlice(array, index, (index += size)); + } + return result; + } + + /** + * Creates an array with all falsey values removed. The values `false`, `null`, + * `0`, `""`, `undefined`, and `NaN` are falsey. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to compact. + * @returns {Array} Returns the new array of filtered values. + * @example + * + * _.compact([0, 1, false, 2, '', 3]); + * // => [1, 2, 3] + */ + function compact(array) { + var index = -1, + length = array ? array.length : 0, + resIndex = -1, + result = []; + + while (++index < length) { + var value = array[index]; + if (value) { + result[++resIndex] = value; + } + } + return result; + } + + /** + * Creates an array of unique `array` values not included in the other + * provided arrays using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) + * for equality comparisons. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to inspect. + * @param {...Array} [values] The arrays of values to exclude. + * @returns {Array} Returns the new array of filtered values. + * @example + * + * _.difference([1, 2, 3], [4, 2]); + * // => [1, 3] + */ + var difference = restParam(function(array, values) { + return (isObjectLike(array) && isArrayLike(array)) + ? baseDifference(array, baseFlatten(values, false, true)) + : []; + }); + + /** + * Creates a slice of `array` with `n` elements dropped from the beginning. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to query. + * @param {number} [n=1] The number of elements to drop. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.drop([1, 2, 3]); + * // => [2, 3] + * + * _.drop([1, 2, 3], 2); + * // => [3] + * + * _.drop([1, 2, 3], 5); + * // => [] + * + * _.drop([1, 2, 3], 0); + * // => [1, 2, 3] + */ + function drop(array, n, guard) { + var length = array ? array.length : 0; + if (!length) { + return []; + } + if (guard ? isIterateeCall(array, n, guard) : n == null) { + n = 1; + } + return baseSlice(array, n < 0 ? 0 : n); + } + + /** + * Creates a slice of `array` with `n` elements dropped from the end. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to query. + * @param {number} [n=1] The number of elements to drop. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.dropRight([1, 2, 3]); + * // => [1, 2] + * + * _.dropRight([1, 2, 3], 2); + * // => [1] + * + * _.dropRight([1, 2, 3], 5); + * // => [] + * + * _.dropRight([1, 2, 3], 0); + * // => [1, 2, 3] + */ + function dropRight(array, n, guard) { + var length = array ? array.length : 0; + if (!length) { + return []; + } + if (guard ? isIterateeCall(array, n, guard) : n == null) { + n = 1; + } + n = length - (+n || 0); + return baseSlice(array, 0, n < 0 ? 0 : n); + } + + /** + * Creates a slice of `array` excluding elements dropped from the end. + * Elements are dropped until `predicate` returns falsey. The predicate is + * bound to `thisArg` and invoked with three arguments: (value, index, array). + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that match the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to query. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.dropRightWhile([1, 2, 3], function(n) { + * return n > 1; + * }); + * // => [1] + * + * var users = [ + * { 'user': 'barney', 'active': true }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': false } + * ]; + * + * // using the `_.matches` callback shorthand + * _.pluck(_.dropRightWhile(users, { 'user': 'pebbles', 'active': false }), 'user'); + * // => ['barney', 'fred'] + * + * // using the `_.matchesProperty` callback shorthand + * _.pluck(_.dropRightWhile(users, 'active', false), 'user'); + * // => ['barney'] + * + * // using the `_.property` callback shorthand + * _.pluck(_.dropRightWhile(users, 'active'), 'user'); + * // => ['barney', 'fred', 'pebbles'] + */ + function dropRightWhile(array, predicate, thisArg) { + return (array && array.length) + ? baseWhile(array, getCallback(predicate, thisArg, 3), true, true) + : []; + } + + /** + * Creates a slice of `array` excluding elements dropped from the beginning. + * Elements are dropped until `predicate` returns falsey. The predicate is + * bound to `thisArg` and invoked with three arguments: (value, index, array). + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to query. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.dropWhile([1, 2, 3], function(n) { + * return n < 3; + * }); + * // => [3] + * + * var users = [ + * { 'user': 'barney', 'active': false }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': true } + * ]; + * + * // using the `_.matches` callback shorthand + * _.pluck(_.dropWhile(users, { 'user': 'barney', 'active': false }), 'user'); + * // => ['fred', 'pebbles'] + * + * // using the `_.matchesProperty` callback shorthand + * _.pluck(_.dropWhile(users, 'active', false), 'user'); + * // => ['pebbles'] + * + * // using the `_.property` callback shorthand + * _.pluck(_.dropWhile(users, 'active'), 'user'); + * // => ['barney', 'fred', 'pebbles'] + */ + function dropWhile(array, predicate, thisArg) { + return (array && array.length) + ? baseWhile(array, getCallback(predicate, thisArg, 3), true) + : []; + } + + /** + * Fills elements of `array` with `value` from `start` up to, but not + * including, `end`. + * + * **Note:** This method mutates `array`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to fill. + * @param {*} value The value to fill `array` with. + * @param {number} [start=0] The start position. + * @param {number} [end=array.length] The end position. + * @returns {Array} Returns `array`. + * @example + * + * var array = [1, 2, 3]; + * + * _.fill(array, 'a'); + * console.log(array); + * // => ['a', 'a', 'a'] + * + * _.fill(Array(3), 2); + * // => [2, 2, 2] + * + * _.fill([4, 6, 8], '*', 1, 2); + * // => [4, '*', 8] + */ + function fill(array, value, start, end) { + var length = array ? array.length : 0; + if (!length) { + return []; + } + if (start && typeof start != 'number' && isIterateeCall(array, value, start)) { + start = 0; + end = length; + } + return baseFill(array, value, start, end); + } + + /** + * This method is like `_.find` except that it returns the index of the first + * element `predicate` returns truthy for instead of the element itself. + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to search. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {number} Returns the index of the found element, else `-1`. + * @example + * + * var users = [ + * { 'user': 'barney', 'active': false }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': true } + * ]; + * + * _.findIndex(users, function(chr) { + * return chr.user == 'barney'; + * }); + * // => 0 + * + * // using the `_.matches` callback shorthand + * _.findIndex(users, { 'user': 'fred', 'active': false }); + * // => 1 + * + * // using the `_.matchesProperty` callback shorthand + * _.findIndex(users, 'active', false); + * // => 0 + * + * // using the `_.property` callback shorthand + * _.findIndex(users, 'active'); + * // => 2 + */ + var findIndex = createFindIndex(); + + /** + * This method is like `_.findIndex` except that it iterates over elements + * of `collection` from right to left. + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to search. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {number} Returns the index of the found element, else `-1`. + * @example + * + * var users = [ + * { 'user': 'barney', 'active': true }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': false } + * ]; + * + * _.findLastIndex(users, function(chr) { + * return chr.user == 'pebbles'; + * }); + * // => 2 + * + * // using the `_.matches` callback shorthand + * _.findLastIndex(users, { 'user': 'barney', 'active': true }); + * // => 0 + * + * // using the `_.matchesProperty` callback shorthand + * _.findLastIndex(users, 'active', false); + * // => 2 + * + * // using the `_.property` callback shorthand + * _.findLastIndex(users, 'active'); + * // => 0 + */ + var findLastIndex = createFindIndex(true); + + /** + * Gets the first element of `array`. + * + * @static + * @memberOf _ + * @alias head + * @category Array + * @param {Array} array The array to query. + * @returns {*} Returns the first element of `array`. + * @example + * + * _.first([1, 2, 3]); + * // => 1 + * + * _.first([]); + * // => undefined + */ + function first(array) { + return array ? array[0] : undefined; + } + + /** + * Flattens a nested array. If `isDeep` is `true` the array is recursively + * flattened, otherwise it is only flattened a single level. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to flatten. + * @param {boolean} [isDeep] Specify a deep flatten. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {Array} Returns the new flattened array. + * @example + * + * _.flatten([1, [2, 3, [4]]]); + * // => [1, 2, 3, [4]] + * + * // using `isDeep` + * _.flatten([1, [2, 3, [4]]], true); + * // => [1, 2, 3, 4] + */ + function flatten(array, isDeep, guard) { + var length = array ? array.length : 0; + if (guard && isIterateeCall(array, isDeep, guard)) { + isDeep = false; + } + return length ? baseFlatten(array, isDeep) : []; + } + + /** + * Recursively flattens a nested array. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to recursively flatten. + * @returns {Array} Returns the new flattened array. + * @example + * + * _.flattenDeep([1, [2, 3, [4]]]); + * // => [1, 2, 3, 4] + */ + function flattenDeep(array) { + var length = array ? array.length : 0; + return length ? baseFlatten(array, true) : []; + } + + /** + * Gets the index at which the first occurrence of `value` is found in `array` + * using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) + * for equality comparisons. If `fromIndex` is negative, it is used as the offset + * from the end of `array`. If `array` is sorted providing `true` for `fromIndex` + * performs a faster binary search. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to search. + * @param {*} value The value to search for. + * @param {boolean|number} [fromIndex=0] The index to search from or `true` + * to perform a binary search on a sorted array. + * @returns {number} Returns the index of the matched value, else `-1`. + * @example + * + * _.indexOf([1, 2, 1, 2], 2); + * // => 1 + * + * // using `fromIndex` + * _.indexOf([1, 2, 1, 2], 2, 2); + * // => 3 + * + * // performing a binary search + * _.indexOf([1, 1, 2, 2], 2, true); + * // => 2 + */ + function indexOf(array, value, fromIndex) { + var length = array ? array.length : 0; + if (!length) { + return -1; + } + if (typeof fromIndex == 'number') { + fromIndex = fromIndex < 0 ? nativeMax(length + fromIndex, 0) : fromIndex; + } else if (fromIndex) { + var index = binaryIndex(array, value); + if (index < length && + (value === value ? (value === array[index]) : (array[index] !== array[index]))) { + return index; + } + return -1; + } + return baseIndexOf(array, value, fromIndex || 0); + } + + /** + * Gets all but the last element of `array`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to query. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.initial([1, 2, 3]); + * // => [1, 2] + */ + function initial(array) { + return dropRight(array, 1); + } + + /** + * Creates an array of unique values that are included in all of the provided + * arrays using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) + * for equality comparisons. + * + * @static + * @memberOf _ + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @returns {Array} Returns the new array of shared values. + * @example + * _.intersection([1, 2], [4, 2], [2, 1]); + * // => [2] + */ + var intersection = restParam(function(arrays) { + var othLength = arrays.length, + othIndex = othLength, + caches = Array(length), + indexOf = getIndexOf(), + isCommon = indexOf == baseIndexOf, + result = []; + + while (othIndex--) { + var value = arrays[othIndex] = isArrayLike(value = arrays[othIndex]) ? value : []; + caches[othIndex] = (isCommon && value.length >= 120) ? createCache(othIndex && value) : null; + } + var array = arrays[0], + index = -1, + length = array ? array.length : 0, + seen = caches[0]; + + outer: + while (++index < length) { + value = array[index]; + if ((seen ? cacheIndexOf(seen, value) : indexOf(result, value, 0)) < 0) { + var othIndex = othLength; + while (--othIndex) { + var cache = caches[othIndex]; + if ((cache ? cacheIndexOf(cache, value) : indexOf(arrays[othIndex], value, 0)) < 0) { + continue outer; + } + } + if (seen) { + seen.push(value); + } + result.push(value); + } + } + return result; + }); + + /** + * Gets the last element of `array`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to query. + * @returns {*} Returns the last element of `array`. + * @example + * + * _.last([1, 2, 3]); + * // => 3 + */ + function last(array) { + var length = array ? array.length : 0; + return length ? array[length - 1] : undefined; + } + + /** + * This method is like `_.indexOf` except that it iterates over elements of + * `array` from right to left. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to search. + * @param {*} value The value to search for. + * @param {boolean|number} [fromIndex=array.length-1] The index to search from + * or `true` to perform a binary search on a sorted array. + * @returns {number} Returns the index of the matched value, else `-1`. + * @example + * + * _.lastIndexOf([1, 2, 1, 2], 2); + * // => 3 + * + * // using `fromIndex` + * _.lastIndexOf([1, 2, 1, 2], 2, 2); + * // => 1 + * + * // performing a binary search + * _.lastIndexOf([1, 1, 2, 2], 2, true); + * // => 3 + */ + function lastIndexOf(array, value, fromIndex) { + var length = array ? array.length : 0; + if (!length) { + return -1; + } + var index = length; + if (typeof fromIndex == 'number') { + index = (fromIndex < 0 ? nativeMax(length + fromIndex, 0) : nativeMin(fromIndex || 0, length - 1)) + 1; + } else if (fromIndex) { + index = binaryIndex(array, value, true) - 1; + var other = array[index]; + if (value === value ? (value === other) : (other !== other)) { + return index; + } + return -1; + } + if (value !== value) { + return indexOfNaN(array, index, true); + } + while (index--) { + if (array[index] === value) { + return index; + } + } + return -1; + } + + /** + * Removes all provided values from `array` using + * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) + * for equality comparisons. + * + * **Note:** Unlike `_.without`, this method mutates `array`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to modify. + * @param {...*} [values] The values to remove. + * @returns {Array} Returns `array`. + * @example + * + * var array = [1, 2, 3, 1, 2, 3]; + * + * _.pull(array, 2, 3); + * console.log(array); + * // => [1, 1] + */ + function pull() { + var args = arguments, + array = args[0]; + + if (!(array && array.length)) { + return array; + } + var index = 0, + indexOf = getIndexOf(), + length = args.length; + + while (++index < length) { + var fromIndex = 0, + value = args[index]; + + while ((fromIndex = indexOf(array, value, fromIndex)) > -1) { + splice.call(array, fromIndex, 1); + } + } + return array; + } + + /** + * Removes elements from `array` corresponding to the given indexes and returns + * an array of the removed elements. Indexes may be specified as an array of + * indexes or as individual arguments. + * + * **Note:** Unlike `_.at`, this method mutates `array`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to modify. + * @param {...(number|number[])} [indexes] The indexes of elements to remove, + * specified as individual indexes or arrays of indexes. + * @returns {Array} Returns the new array of removed elements. + * @example + * + * var array = [5, 10, 15, 20]; + * var evens = _.pullAt(array, 1, 3); + * + * console.log(array); + * // => [5, 15] + * + * console.log(evens); + * // => [10, 20] + */ + var pullAt = restParam(function(array, indexes) { + indexes = baseFlatten(indexes); + + var result = baseAt(array, indexes); + basePullAt(array, indexes.sort(baseCompareAscending)); + return result; + }); + + /** + * Removes all elements from `array` that `predicate` returns truthy for + * and returns an array of the removed elements. The predicate is bound to + * `thisArg` and invoked with three arguments: (value, index, array). + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * **Note:** Unlike `_.filter`, this method mutates `array`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to modify. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {Array} Returns the new array of removed elements. + * @example + * + * var array = [1, 2, 3, 4]; + * var evens = _.remove(array, function(n) { + * return n % 2 == 0; + * }); + * + * console.log(array); + * // => [1, 3] + * + * console.log(evens); + * // => [2, 4] + */ + function remove(array, predicate, thisArg) { + var result = []; + if (!(array && array.length)) { + return result; + } + var index = -1, + indexes = [], + length = array.length; + + predicate = getCallback(predicate, thisArg, 3); + while (++index < length) { + var value = array[index]; + if (predicate(value, index, array)) { + result.push(value); + indexes.push(index); + } + } + basePullAt(array, indexes); + return result; + } + + /** + * Gets all but the first element of `array`. + * + * @static + * @memberOf _ + * @alias tail + * @category Array + * @param {Array} array The array to query. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.rest([1, 2, 3]); + * // => [2, 3] + */ + function rest(array) { + return drop(array, 1); + } + + /** + * Creates a slice of `array` from `start` up to, but not including, `end`. + * + * **Note:** This method is used instead of `Array#slice` to support node + * lists in IE < 9 and to ensure dense arrays are returned. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to slice. + * @param {number} [start=0] The start position. + * @param {number} [end=array.length] The end position. + * @returns {Array} Returns the slice of `array`. + */ + function slice(array, start, end) { + var length = array ? array.length : 0; + if (!length) { + return []; + } + if (end && typeof end != 'number' && isIterateeCall(array, start, end)) { + start = 0; + end = length; + } + return baseSlice(array, start, end); + } + + /** + * Uses a binary search to determine the lowest index at which `value` should + * be inserted into `array` in order to maintain its sort order. If an iteratee + * function is provided it is invoked for `value` and each element of `array` + * to compute their sort ranking. The iteratee is bound to `thisArg` and + * invoked with one argument; (value). + * + * If a property name is provided for `iteratee` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `iteratee` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The sorted array to inspect. + * @param {*} value The value to evaluate. + * @param {Function|Object|string} [iteratee=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {number} Returns the index at which `value` should be inserted + * into `array`. + * @example + * + * _.sortedIndex([30, 50], 40); + * // => 1 + * + * _.sortedIndex([4, 4, 5, 5], 5); + * // => 2 + * + * var dict = { 'data': { 'thirty': 30, 'forty': 40, 'fifty': 50 } }; + * + * // using an iteratee function + * _.sortedIndex(['thirty', 'fifty'], 'forty', function(word) { + * return this.data[word]; + * }, dict); + * // => 1 + * + * // using the `_.property` callback shorthand + * _.sortedIndex([{ 'x': 30 }, { 'x': 50 }], { 'x': 40 }, 'x'); + * // => 1 + */ + var sortedIndex = createSortedIndex(); + + /** + * This method is like `_.sortedIndex` except that it returns the highest + * index at which `value` should be inserted into `array` in order to + * maintain its sort order. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The sorted array to inspect. + * @param {*} value The value to evaluate. + * @param {Function|Object|string} [iteratee=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {number} Returns the index at which `value` should be inserted + * into `array`. + * @example + * + * _.sortedLastIndex([4, 4, 5, 5], 5); + * // => 4 + */ + var sortedLastIndex = createSortedIndex(true); + + /** + * Creates a slice of `array` with `n` elements taken from the beginning. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to query. + * @param {number} [n=1] The number of elements to take. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.take([1, 2, 3]); + * // => [1] + * + * _.take([1, 2, 3], 2); + * // => [1, 2] + * + * _.take([1, 2, 3], 5); + * // => [1, 2, 3] + * + * _.take([1, 2, 3], 0); + * // => [] + */ + function take(array, n, guard) { + var length = array ? array.length : 0; + if (!length) { + return []; + } + if (guard ? isIterateeCall(array, n, guard) : n == null) { + n = 1; + } + return baseSlice(array, 0, n < 0 ? 0 : n); + } + + /** + * Creates a slice of `array` with `n` elements taken from the end. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to query. + * @param {number} [n=1] The number of elements to take. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.takeRight([1, 2, 3]); + * // => [3] + * + * _.takeRight([1, 2, 3], 2); + * // => [2, 3] + * + * _.takeRight([1, 2, 3], 5); + * // => [1, 2, 3] + * + * _.takeRight([1, 2, 3], 0); + * // => [] + */ + function takeRight(array, n, guard) { + var length = array ? array.length : 0; + if (!length) { + return []; + } + if (guard ? isIterateeCall(array, n, guard) : n == null) { + n = 1; + } + n = length - (+n || 0); + return baseSlice(array, n < 0 ? 0 : n); + } + + /** + * Creates a slice of `array` with elements taken from the end. Elements are + * taken until `predicate` returns falsey. The predicate is bound to `thisArg` + * and invoked with three arguments: (value, index, array). + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to query. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.takeRightWhile([1, 2, 3], function(n) { + * return n > 1; + * }); + * // => [2, 3] + * + * var users = [ + * { 'user': 'barney', 'active': true }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': false } + * ]; + * + * // using the `_.matches` callback shorthand + * _.pluck(_.takeRightWhile(users, { 'user': 'pebbles', 'active': false }), 'user'); + * // => ['pebbles'] + * + * // using the `_.matchesProperty` callback shorthand + * _.pluck(_.takeRightWhile(users, 'active', false), 'user'); + * // => ['fred', 'pebbles'] + * + * // using the `_.property` callback shorthand + * _.pluck(_.takeRightWhile(users, 'active'), 'user'); + * // => [] + */ + function takeRightWhile(array, predicate, thisArg) { + return (array && array.length) + ? baseWhile(array, getCallback(predicate, thisArg, 3), false, true) + : []; + } + + /** + * Creates a slice of `array` with elements taken from the beginning. Elements + * are taken until `predicate` returns falsey. The predicate is bound to + * `thisArg` and invoked with three arguments: (value, index, array). + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to query. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.takeWhile([1, 2, 3], function(n) { + * return n < 3; + * }); + * // => [1, 2] + * + * var users = [ + * { 'user': 'barney', 'active': false }, + * { 'user': 'fred', 'active': false}, + * { 'user': 'pebbles', 'active': true } + * ]; + * + * // using the `_.matches` callback shorthand + * _.pluck(_.takeWhile(users, { 'user': 'barney', 'active': false }), 'user'); + * // => ['barney'] + * + * // using the `_.matchesProperty` callback shorthand + * _.pluck(_.takeWhile(users, 'active', false), 'user'); + * // => ['barney', 'fred'] + * + * // using the `_.property` callback shorthand + * _.pluck(_.takeWhile(users, 'active'), 'user'); + * // => [] + */ + function takeWhile(array, predicate, thisArg) { + return (array && array.length) + ? baseWhile(array, getCallback(predicate, thisArg, 3)) + : []; + } + + /** + * Creates an array of unique values, in order, from all of the provided arrays + * using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) + * for equality comparisons. + * + * @static + * @memberOf _ + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @returns {Array} Returns the new array of combined values. + * @example + * + * _.union([1, 2], [4, 2], [2, 1]); + * // => [1, 2, 4] + */ + var union = restParam(function(arrays) { + return baseUniq(baseFlatten(arrays, false, true)); + }); + + /** + * Creates a duplicate-free version of an array, using + * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) + * for equality comparisons, in which only the first occurence of each element + * is kept. Providing `true` for `isSorted` performs a faster search algorithm + * for sorted arrays. If an iteratee function is provided it is invoked for + * each element in the array to generate the criterion by which uniqueness + * is computed. The `iteratee` is bound to `thisArg` and invoked with three + * arguments: (value, index, array). + * + * If a property name is provided for `iteratee` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `iteratee` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @alias unique + * @category Array + * @param {Array} array The array to inspect. + * @param {boolean} [isSorted] Specify the array is sorted. + * @param {Function|Object|string} [iteratee] The function invoked per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Array} Returns the new duplicate-value-free array. + * @example + * + * _.uniq([2, 1, 2]); + * // => [2, 1] + * + * // using `isSorted` + * _.uniq([1, 1, 2], true); + * // => [1, 2] + * + * // using an iteratee function + * _.uniq([1, 2.5, 1.5, 2], function(n) { + * return this.floor(n); + * }, Math); + * // => [1, 2.5] + * + * // using the `_.property` callback shorthand + * _.uniq([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x'); + * // => [{ 'x': 1 }, { 'x': 2 }] + */ + function uniq(array, isSorted, iteratee, thisArg) { + var length = array ? array.length : 0; + if (!length) { + return []; + } + if (isSorted != null && typeof isSorted != 'boolean') { + thisArg = iteratee; + iteratee = isIterateeCall(array, isSorted, thisArg) ? undefined : isSorted; + isSorted = false; + } + var callback = getCallback(); + if (!(iteratee == null && callback === baseCallback)) { + iteratee = callback(iteratee, thisArg, 3); + } + return (isSorted && getIndexOf() == baseIndexOf) + ? sortedUniq(array, iteratee) + : baseUniq(array, iteratee); + } + + /** + * This method is like `_.zip` except that it accepts an array of grouped + * elements and creates an array regrouping the elements to their pre-zip + * configuration. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array of grouped elements to process. + * @returns {Array} Returns the new array of regrouped elements. + * @example + * + * var zipped = _.zip(['fred', 'barney'], [30, 40], [true, false]); + * // => [['fred', 30, true], ['barney', 40, false]] + * + * _.unzip(zipped); + * // => [['fred', 'barney'], [30, 40], [true, false]] + */ + function unzip(array) { + if (!(array && array.length)) { + return []; + } + var index = -1, + length = 0; + + array = arrayFilter(array, function(group) { + if (isArrayLike(group)) { + length = nativeMax(group.length, length); + return true; + } + }); + var result = Array(length); + while (++index < length) { + result[index] = arrayMap(array, baseProperty(index)); + } + return result; + } + + /** + * This method is like `_.unzip` except that it accepts an iteratee to specify + * how regrouped values should be combined. The `iteratee` is bound to `thisArg` + * and invoked with four arguments: (accumulator, value, index, group). + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array of grouped elements to process. + * @param {Function} [iteratee] The function to combine regrouped values. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Array} Returns the new array of regrouped elements. + * @example + * + * var zipped = _.zip([1, 2], [10, 20], [100, 200]); + * // => [[1, 10, 100], [2, 20, 200]] + * + * _.unzipWith(zipped, _.add); + * // => [3, 30, 300] + */ + function unzipWith(array, iteratee, thisArg) { + var length = array ? array.length : 0; + if (!length) { + return []; + } + var result = unzip(array); + if (iteratee == null) { + return result; + } + iteratee = bindCallback(iteratee, thisArg, 4); + return arrayMap(result, function(group) { + return arrayReduce(group, iteratee, undefined, true); + }); + } + + /** + * Creates an array excluding all provided values using + * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) + * for equality comparisons. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to filter. + * @param {...*} [values] The values to exclude. + * @returns {Array} Returns the new array of filtered values. + * @example + * + * _.without([1, 2, 1, 3], 1, 2); + * // => [3] + */ + var without = restParam(function(array, values) { + return isArrayLike(array) + ? baseDifference(array, values) + : []; + }); + + /** + * Creates an array of unique values that is the [symmetric difference](https://en.wikipedia.org/wiki/Symmetric_difference) + * of the provided arrays. + * + * @static + * @memberOf _ + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @returns {Array} Returns the new array of values. + * @example + * + * _.xor([1, 2], [4, 2]); + * // => [1, 4] + */ + function xor() { + var index = -1, + length = arguments.length; + + while (++index < length) { + var array = arguments[index]; + if (isArrayLike(array)) { + var result = result + ? arrayPush(baseDifference(result, array), baseDifference(array, result)) + : array; + } + } + return result ? baseUniq(result) : []; + } + + /** + * Creates an array of grouped elements, the first of which contains the first + * elements of the given arrays, the second of which contains the second elements + * of the given arrays, and so on. + * + * @static + * @memberOf _ + * @category Array + * @param {...Array} [arrays] The arrays to process. + * @returns {Array} Returns the new array of grouped elements. + * @example + * + * _.zip(['fred', 'barney'], [30, 40], [true, false]); + * // => [['fred', 30, true], ['barney', 40, false]] + */ + var zip = restParam(unzip); + + /** + * The inverse of `_.pairs`; this method returns an object composed from arrays + * of property names and values. Provide either a single two dimensional array, + * e.g. `[[key1, value1], [key2, value2]]` or two arrays, one of property names + * and one of corresponding values. + * + * @static + * @memberOf _ + * @alias object + * @category Array + * @param {Array} props The property names. + * @param {Array} [values=[]] The property values. + * @returns {Object} Returns the new object. + * @example + * + * _.zipObject([['fred', 30], ['barney', 40]]); + * // => { 'fred': 30, 'barney': 40 } + * + * _.zipObject(['fred', 'barney'], [30, 40]); + * // => { 'fred': 30, 'barney': 40 } + */ + function zipObject(props, values) { + var index = -1, + length = props ? props.length : 0, + result = {}; + + if (length && !values && !isArray(props[0])) { + values = []; + } + while (++index < length) { + var key = props[index]; + if (values) { + result[key] = values[index]; + } else if (key) { + result[key[0]] = key[1]; + } + } + return result; + } + + /** + * This method is like `_.zip` except that it accepts an iteratee to specify + * how grouped values should be combined. The `iteratee` is bound to `thisArg` + * and invoked with four arguments: (accumulator, value, index, group). + * + * @static + * @memberOf _ + * @category Array + * @param {...Array} [arrays] The arrays to process. + * @param {Function} [iteratee] The function to combine grouped values. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Array} Returns the new array of grouped elements. + * @example + * + * _.zipWith([1, 2], [10, 20], [100, 200], _.add); + * // => [111, 222] + */ + var zipWith = restParam(function(arrays) { + var length = arrays.length, + iteratee = length > 2 ? arrays[length - 2] : undefined, + thisArg = length > 1 ? arrays[length - 1] : undefined; + + if (length > 2 && typeof iteratee == 'function') { + length -= 2; + } else { + iteratee = (length > 1 && typeof thisArg == 'function') ? (--length, thisArg) : undefined; + thisArg = undefined; + } + arrays.length = length; + return unzipWith(arrays, iteratee, thisArg); + }); + + /*------------------------------------------------------------------------*/ + + /** + * Creates a `lodash` object that wraps `value` with explicit method + * chaining enabled. + * + * @static + * @memberOf _ + * @category Chain + * @param {*} value The value to wrap. + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36 }, + * { 'user': 'fred', 'age': 40 }, + * { 'user': 'pebbles', 'age': 1 } + * ]; + * + * var youngest = _.chain(users) + * .sortBy('age') + * .map(function(chr) { + * return chr.user + ' is ' + chr.age; + * }) + * .first() + * .value(); + * // => 'pebbles is 1' + */ + function chain(value) { + var result = lodash(value); + result.__chain__ = true; + return result; + } + + /** + * This method invokes `interceptor` and returns `value`. The interceptor is + * bound to `thisArg` and invoked with one argument; (value). The purpose of + * this method is to "tap into" a method chain in order to perform operations + * on intermediate results within the chain. + * + * @static + * @memberOf _ + * @category Chain + * @param {*} value The value to provide to `interceptor`. + * @param {Function} interceptor The function to invoke. + * @param {*} [thisArg] The `this` binding of `interceptor`. + * @returns {*} Returns `value`. + * @example + * + * _([1, 2, 3]) + * .tap(function(array) { + * array.pop(); + * }) + * .reverse() + * .value(); + * // => [2, 1] + */ + function tap(value, interceptor, thisArg) { + interceptor.call(thisArg, value); + return value; + } + + /** + * This method is like `_.tap` except that it returns the result of `interceptor`. + * + * @static + * @memberOf _ + * @category Chain + * @param {*} value The value to provide to `interceptor`. + * @param {Function} interceptor The function to invoke. + * @param {*} [thisArg] The `this` binding of `interceptor`. + * @returns {*} Returns the result of `interceptor`. + * @example + * + * _(' abc ') + * .chain() + * .trim() + * .thru(function(value) { + * return [value]; + * }) + * .value(); + * // => ['abc'] + */ + function thru(value, interceptor, thisArg) { + return interceptor.call(thisArg, value); + } + + /** + * Enables explicit method chaining on the wrapper object. + * + * @name chain + * @memberOf _ + * @category Chain + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36 }, + * { 'user': 'fred', 'age': 40 } + * ]; + * + * // without explicit chaining + * _(users).first(); + * // => { 'user': 'barney', 'age': 36 } + * + * // with explicit chaining + * _(users).chain() + * .first() + * .pick('user') + * .value(); + * // => { 'user': 'barney' } + */ + function wrapperChain() { + return chain(this); + } + + /** + * Executes the chained sequence and returns the wrapped result. + * + * @name commit + * @memberOf _ + * @category Chain + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * var array = [1, 2]; + * var wrapped = _(array).push(3); + * + * console.log(array); + * // => [1, 2] + * + * wrapped = wrapped.commit(); + * console.log(array); + * // => [1, 2, 3] + * + * wrapped.last(); + * // => 3 + * + * console.log(array); + * // => [1, 2, 3] + */ + function wrapperCommit() { + return new LodashWrapper(this.value(), this.__chain__); + } + + /** + * Creates a new array joining a wrapped array with any additional arrays + * and/or values. + * + * @name concat + * @memberOf _ + * @category Chain + * @param {...*} [values] The values to concatenate. + * @returns {Array} Returns the new concatenated array. + * @example + * + * var array = [1]; + * var wrapped = _(array).concat(2, [3], [[4]]); + * + * console.log(wrapped.value()); + * // => [1, 2, 3, [4]] + * + * console.log(array); + * // => [1] + */ + var wrapperConcat = restParam(function(values) { + values = baseFlatten(values); + return this.thru(function(array) { + return arrayConcat(isArray(array) ? array : [toObject(array)], values); + }); + }); + + /** + * Creates a clone of the chained sequence planting `value` as the wrapped value. + * + * @name plant + * @memberOf _ + * @category Chain + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * var array = [1, 2]; + * var wrapped = _(array).map(function(value) { + * return Math.pow(value, 2); + * }); + * + * var other = [3, 4]; + * var otherWrapped = wrapped.plant(other); + * + * otherWrapped.value(); + * // => [9, 16] + * + * wrapped.value(); + * // => [1, 4] + */ + function wrapperPlant(value) { + var result, + parent = this; + + while (parent instanceof baseLodash) { + var clone = wrapperClone(parent); + if (result) { + previous.__wrapped__ = clone; + } else { + result = clone; + } + var previous = clone; + parent = parent.__wrapped__; + } + previous.__wrapped__ = value; + return result; + } + + /** + * Reverses the wrapped array so the first element becomes the last, the + * second element becomes the second to last, and so on. + * + * **Note:** This method mutates the wrapped array. + * + * @name reverse + * @memberOf _ + * @category Chain + * @returns {Object} Returns the new reversed `lodash` wrapper instance. + * @example + * + * var array = [1, 2, 3]; + * + * _(array).reverse().value() + * // => [3, 2, 1] + * + * console.log(array); + * // => [3, 2, 1] + */ + function wrapperReverse() { + var value = this.__wrapped__; + + var interceptor = function(value) { + return (wrapped && wrapped.__dir__ < 0) ? value : value.reverse(); + }; + if (value instanceof LazyWrapper) { + var wrapped = value; + if (this.__actions__.length) { + wrapped = new LazyWrapper(this); + } + wrapped = wrapped.reverse(); + wrapped.__actions__.push({ 'func': thru, 'args': [interceptor], 'thisArg': undefined }); + return new LodashWrapper(wrapped, this.__chain__); + } + return this.thru(interceptor); + } + + /** + * Produces the result of coercing the unwrapped value to a string. + * + * @name toString + * @memberOf _ + * @category Chain + * @returns {string} Returns the coerced string value. + * @example + * + * _([1, 2, 3]).toString(); + * // => '1,2,3' + */ + function wrapperToString() { + return (this.value() + ''); + } + + /** + * Executes the chained sequence to extract the unwrapped value. + * + * @name value + * @memberOf _ + * @alias run, toJSON, valueOf + * @category Chain + * @returns {*} Returns the resolved unwrapped value. + * @example + * + * _([1, 2, 3]).value(); + * // => [1, 2, 3] + */ + function wrapperValue() { + return baseWrapperValue(this.__wrapped__, this.__actions__); + } + + /*------------------------------------------------------------------------*/ + + /** + * Creates an array of elements corresponding to the given keys, or indexes, + * of `collection`. Keys may be specified as individual arguments or as arrays + * of keys. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {...(number|number[]|string|string[])} [props] The property names + * or indexes of elements to pick, specified individually or in arrays. + * @returns {Array} Returns the new array of picked elements. + * @example + * + * _.at(['a', 'b', 'c'], [0, 2]); + * // => ['a', 'c'] + * + * _.at(['barney', 'fred', 'pebbles'], 0, 2); + * // => ['barney', 'pebbles'] + */ + var at = restParam(function(collection, props) { + return baseAt(collection, baseFlatten(props)); + }); + + /** + * Creates an object composed of keys generated from the results of running + * each element of `collection` through `iteratee`. The corresponding value + * of each key is the number of times the key was returned by `iteratee`. + * The `iteratee` is bound to `thisArg` and invoked with three arguments: + * (value, index|key, collection). + * + * If a property name is provided for `iteratee` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `iteratee` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [iteratee=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Object} Returns the composed aggregate object. + * @example + * + * _.countBy([4.3, 6.1, 6.4], function(n) { + * return Math.floor(n); + * }); + * // => { '4': 1, '6': 2 } + * + * _.countBy([4.3, 6.1, 6.4], function(n) { + * return this.floor(n); + * }, Math); + * // => { '4': 1, '6': 2 } + * + * _.countBy(['one', 'two', 'three'], 'length'); + * // => { '3': 2, '5': 1 } + */ + var countBy = createAggregator(function(result, value, key) { + hasOwnProperty.call(result, key) ? ++result[key] : (result[key] = 1); + }); + + /** + * Checks if `predicate` returns truthy for **all** elements of `collection`. + * The predicate is bound to `thisArg` and invoked with three arguments: + * (value, index|key, collection). + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @alias all + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {boolean} Returns `true` if all elements pass the predicate check, + * else `false`. + * @example + * + * _.every([true, 1, null, 'yes'], Boolean); + * // => false + * + * var users = [ + * { 'user': 'barney', 'active': false }, + * { 'user': 'fred', 'active': false } + * ]; + * + * // using the `_.matches` callback shorthand + * _.every(users, { 'user': 'barney', 'active': false }); + * // => false + * + * // using the `_.matchesProperty` callback shorthand + * _.every(users, 'active', false); + * // => true + * + * // using the `_.property` callback shorthand + * _.every(users, 'active'); + * // => false + */ + function every(collection, predicate, thisArg) { + var func = isArray(collection) ? arrayEvery : baseEvery; + if (thisArg && isIterateeCall(collection, predicate, thisArg)) { + predicate = undefined; + } + if (typeof predicate != 'function' || thisArg !== undefined) { + predicate = getCallback(predicate, thisArg, 3); + } + return func(collection, predicate); + } + + /** + * Iterates over elements of `collection`, returning an array of all elements + * `predicate` returns truthy for. The predicate is bound to `thisArg` and + * invoked with three arguments: (value, index|key, collection). + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @alias select + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {Array} Returns the new filtered array. + * @example + * + * _.filter([4, 5, 6], function(n) { + * return n % 2 == 0; + * }); + * // => [4, 6] + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': true }, + * { 'user': 'fred', 'age': 40, 'active': false } + * ]; + * + * // using the `_.matches` callback shorthand + * _.pluck(_.filter(users, { 'age': 36, 'active': true }), 'user'); + * // => ['barney'] + * + * // using the `_.matchesProperty` callback shorthand + * _.pluck(_.filter(users, 'active', false), 'user'); + * // => ['fred'] + * + * // using the `_.property` callback shorthand + * _.pluck(_.filter(users, 'active'), 'user'); + * // => ['barney'] + */ + function filter(collection, predicate, thisArg) { + var func = isArray(collection) ? arrayFilter : baseFilter; + predicate = getCallback(predicate, thisArg, 3); + return func(collection, predicate); + } + + /** + * Iterates over elements of `collection`, returning the first element + * `predicate` returns truthy for. The predicate is bound to `thisArg` and + * invoked with three arguments: (value, index|key, collection). + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @alias detect + * @category Collection + * @param {Array|Object|string} collection The collection to search. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {*} Returns the matched element, else `undefined`. + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': true }, + * { 'user': 'fred', 'age': 40, 'active': false }, + * { 'user': 'pebbles', 'age': 1, 'active': true } + * ]; + * + * _.result(_.find(users, function(chr) { + * return chr.age < 40; + * }), 'user'); + * // => 'barney' + * + * // using the `_.matches` callback shorthand + * _.result(_.find(users, { 'age': 1, 'active': true }), 'user'); + * // => 'pebbles' + * + * // using the `_.matchesProperty` callback shorthand + * _.result(_.find(users, 'active', false), 'user'); + * // => 'fred' + * + * // using the `_.property` callback shorthand + * _.result(_.find(users, 'active'), 'user'); + * // => 'barney' + */ + var find = createFind(baseEach); + + /** + * This method is like `_.find` except that it iterates over elements of + * `collection` from right to left. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to search. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {*} Returns the matched element, else `undefined`. + * @example + * + * _.findLast([1, 2, 3, 4], function(n) { + * return n % 2 == 1; + * }); + * // => 3 + */ + var findLast = createFind(baseEachRight, true); + + /** + * Performs a deep comparison between each element in `collection` and the + * source object, returning the first element that has equivalent property + * values. + * + * **Note:** This method supports comparing arrays, booleans, `Date` objects, + * numbers, `Object` objects, regexes, and strings. Objects are compared by + * their own, not inherited, enumerable properties. For comparing a single + * own or inherited property value see `_.matchesProperty`. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to search. + * @param {Object} source The object of property values to match. + * @returns {*} Returns the matched element, else `undefined`. + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': true }, + * { 'user': 'fred', 'age': 40, 'active': false } + * ]; + * + * _.result(_.findWhere(users, { 'age': 36, 'active': true }), 'user'); + * // => 'barney' + * + * _.result(_.findWhere(users, { 'age': 40, 'active': false }), 'user'); + * // => 'fred' + */ + function findWhere(collection, source) { + return find(collection, baseMatches(source)); + } + + /** + * Iterates over elements of `collection` invoking `iteratee` for each element. + * The `iteratee` is bound to `thisArg` and invoked with three arguments: + * (value, index|key, collection). Iteratee functions may exit iteration early + * by explicitly returning `false`. + * + * **Note:** As with other "Collections" methods, objects with a "length" property + * are iterated like arrays. To avoid this behavior `_.forIn` or `_.forOwn` + * may be used for object iteration. + * + * @static + * @memberOf _ + * @alias each + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Array|Object|string} Returns `collection`. + * @example + * + * _([1, 2]).forEach(function(n) { + * console.log(n); + * }).value(); + * // => logs each value from left to right and returns the array + * + * _.forEach({ 'a': 1, 'b': 2 }, function(n, key) { + * console.log(n, key); + * }); + * // => logs each value-key pair and returns the object (iteration order is not guaranteed) + */ + var forEach = createForEach(arrayEach, baseEach); + + /** + * This method is like `_.forEach` except that it iterates over elements of + * `collection` from right to left. + * + * @static + * @memberOf _ + * @alias eachRight + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Array|Object|string} Returns `collection`. + * @example + * + * _([1, 2]).forEachRight(function(n) { + * console.log(n); + * }).value(); + * // => logs each value from right to left and returns the array + */ + var forEachRight = createForEach(arrayEachRight, baseEachRight); + + /** + * Creates an object composed of keys generated from the results of running + * each element of `collection` through `iteratee`. The corresponding value + * of each key is an array of the elements responsible for generating the key. + * The `iteratee` is bound to `thisArg` and invoked with three arguments: + * (value, index|key, collection). + * + * If a property name is provided for `iteratee` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `iteratee` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [iteratee=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Object} Returns the composed aggregate object. + * @example + * + * _.groupBy([4.2, 6.1, 6.4], function(n) { + * return Math.floor(n); + * }); + * // => { '4': [4.2], '6': [6.1, 6.4] } + * + * _.groupBy([4.2, 6.1, 6.4], function(n) { + * return this.floor(n); + * }, Math); + * // => { '4': [4.2], '6': [6.1, 6.4] } + * + * // using the `_.property` callback shorthand + * _.groupBy(['one', 'two', 'three'], 'length'); + * // => { '3': ['one', 'two'], '5': ['three'] } + */ + var groupBy = createAggregator(function(result, value, key) { + if (hasOwnProperty.call(result, key)) { + result[key].push(value); + } else { + result[key] = [value]; + } + }); + + /** + * Checks if `value` is in `collection` using + * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) + * for equality comparisons. If `fromIndex` is negative, it is used as the offset + * from the end of `collection`. + * + * @static + * @memberOf _ + * @alias contains, include + * @category Collection + * @param {Array|Object|string} collection The collection to search. + * @param {*} target The value to search for. + * @param {number} [fromIndex=0] The index to search from. + * @param- {Object} [guard] Enables use as a callback for functions like `_.reduce`. + * @returns {boolean} Returns `true` if a matching element is found, else `false`. + * @example + * + * _.includes([1, 2, 3], 1); + * // => true + * + * _.includes([1, 2, 3], 1, 2); + * // => false + * + * _.includes({ 'user': 'fred', 'age': 40 }, 'fred'); + * // => true + * + * _.includes('pebbles', 'eb'); + * // => true + */ + function includes(collection, target, fromIndex, guard) { + var length = collection ? getLength(collection) : 0; + if (!isLength(length)) { + collection = values(collection); + length = collection.length; + } + if (typeof fromIndex != 'number' || (guard && isIterateeCall(target, fromIndex, guard))) { + fromIndex = 0; + } else { + fromIndex = fromIndex < 0 ? nativeMax(length + fromIndex, 0) : (fromIndex || 0); + } + return (typeof collection == 'string' || !isArray(collection) && isString(collection)) + ? (fromIndex <= length && collection.indexOf(target, fromIndex) > -1) + : (!!length && getIndexOf(collection, target, fromIndex) > -1); + } + + /** + * Creates an object composed of keys generated from the results of running + * each element of `collection` through `iteratee`. The corresponding value + * of each key is the last element responsible for generating the key. The + * iteratee function is bound to `thisArg` and invoked with three arguments: + * (value, index|key, collection). + * + * If a property name is provided for `iteratee` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `iteratee` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [iteratee=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Object} Returns the composed aggregate object. + * @example + * + * var keyData = [ + * { 'dir': 'left', 'code': 97 }, + * { 'dir': 'right', 'code': 100 } + * ]; + * + * _.indexBy(keyData, 'dir'); + * // => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } } + * + * _.indexBy(keyData, function(object) { + * return String.fromCharCode(object.code); + * }); + * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } } + * + * _.indexBy(keyData, function(object) { + * return this.fromCharCode(object.code); + * }, String); + * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } } + */ + var indexBy = createAggregator(function(result, value, key) { + result[key] = value; + }); + + /** + * Invokes the method at `path` of each element in `collection`, returning + * an array of the results of each invoked method. Any additional arguments + * are provided to each invoked method. If `methodName` is a function it is + * invoked for, and `this` bound to, each element in `collection`. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Array|Function|string} path The path of the method to invoke or + * the function invoked per iteration. + * @param {...*} [args] The arguments to invoke the method with. + * @returns {Array} Returns the array of results. + * @example + * + * _.invoke([[5, 1, 7], [3, 2, 1]], 'sort'); + * // => [[1, 5, 7], [1, 2, 3]] + * + * _.invoke([123, 456], String.prototype.split, ''); + * // => [['1', '2', '3'], ['4', '5', '6']] + */ + var invoke = restParam(function(collection, path, args) { + var index = -1, + isFunc = typeof path == 'function', + isProp = isKey(path), + result = isArrayLike(collection) ? Array(collection.length) : []; + + baseEach(collection, function(value) { + var func = isFunc ? path : ((isProp && value != null) ? value[path] : undefined); + result[++index] = func ? func.apply(value, args) : invokePath(value, path, args); + }); + return result; + }); + + /** + * Creates an array of values by running each element in `collection` through + * `iteratee`. The `iteratee` is bound to `thisArg` and invoked with three + * arguments: (value, index|key, collection). + * + * If a property name is provided for `iteratee` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `iteratee` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * Many lodash methods are guarded to work as iteratees for methods like + * `_.every`, `_.filter`, `_.map`, `_.mapValues`, `_.reject`, and `_.some`. + * + * The guarded methods are: + * `ary`, `callback`, `chunk`, `clone`, `create`, `curry`, `curryRight`, + * `drop`, `dropRight`, `every`, `fill`, `flatten`, `invert`, `max`, `min`, + * `parseInt`, `slice`, `sortBy`, `take`, `takeRight`, `template`, `trim`, + * `trimLeft`, `trimRight`, `trunc`, `random`, `range`, `sample`, `some`, + * `sum`, `uniq`, and `words` + * + * @static + * @memberOf _ + * @alias collect + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [iteratee=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Array} Returns the new mapped array. + * @example + * + * function timesThree(n) { + * return n * 3; + * } + * + * _.map([1, 2], timesThree); + * // => [3, 6] + * + * _.map({ 'a': 1, 'b': 2 }, timesThree); + * // => [3, 6] (iteration order is not guaranteed) + * + * var users = [ + * { 'user': 'barney' }, + * { 'user': 'fred' } + * ]; + * + * // using the `_.property` callback shorthand + * _.map(users, 'user'); + * // => ['barney', 'fred'] + */ + function map(collection, iteratee, thisArg) { + var func = isArray(collection) ? arrayMap : baseMap; + iteratee = getCallback(iteratee, thisArg, 3); + return func(collection, iteratee); + } + + /** + * Creates an array of elements split into two groups, the first of which + * contains elements `predicate` returns truthy for, while the second of which + * contains elements `predicate` returns falsey for. The predicate is bound + * to `thisArg` and invoked with three arguments: (value, index|key, collection). + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {Array} Returns the array of grouped elements. + * @example + * + * _.partition([1, 2, 3], function(n) { + * return n % 2; + * }); + * // => [[1, 3], [2]] + * + * _.partition([1.2, 2.3, 3.4], function(n) { + * return this.floor(n) % 2; + * }, Math); + * // => [[1.2, 3.4], [2.3]] + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': false }, + * { 'user': 'fred', 'age': 40, 'active': true }, + * { 'user': 'pebbles', 'age': 1, 'active': false } + * ]; + * + * var mapper = function(array) { + * return _.pluck(array, 'user'); + * }; + * + * // using the `_.matches` callback shorthand + * _.map(_.partition(users, { 'age': 1, 'active': false }), mapper); + * // => [['pebbles'], ['barney', 'fred']] + * + * // using the `_.matchesProperty` callback shorthand + * _.map(_.partition(users, 'active', false), mapper); + * // => [['barney', 'pebbles'], ['fred']] + * + * // using the `_.property` callback shorthand + * _.map(_.partition(users, 'active'), mapper); + * // => [['fred'], ['barney', 'pebbles']] + */ + var partition = createAggregator(function(result, value, key) { + result[key ? 0 : 1].push(value); + }, function() { return [[], []]; }); + + /** + * Gets the property value of `path` from all elements in `collection`. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Array|string} path The path of the property to pluck. + * @returns {Array} Returns the property values. + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36 }, + * { 'user': 'fred', 'age': 40 } + * ]; + * + * _.pluck(users, 'user'); + * // => ['barney', 'fred'] + * + * var userIndex = _.indexBy(users, 'user'); + * _.pluck(userIndex, 'age'); + * // => [36, 40] (iteration order is not guaranteed) + */ + function pluck(collection, path) { + return map(collection, property(path)); + } + + /** + * Reduces `collection` to a value which is the accumulated result of running + * each element in `collection` through `iteratee`, where each successive + * invocation is supplied the return value of the previous. If `accumulator` + * is not provided the first element of `collection` is used as the initial + * value. The `iteratee` is bound to `thisArg` and invoked with four arguments: + * (accumulator, value, index|key, collection). + * + * Many lodash methods are guarded to work as iteratees for methods like + * `_.reduce`, `_.reduceRight`, and `_.transform`. + * + * The guarded methods are: + * `assign`, `defaults`, `defaultsDeep`, `includes`, `merge`, `sortByAll`, + * and `sortByOrder` + * + * @static + * @memberOf _ + * @alias foldl, inject + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {*} [accumulator] The initial value. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {*} Returns the accumulated value. + * @example + * + * _.reduce([1, 2], function(total, n) { + * return total + n; + * }); + * // => 3 + * + * _.reduce({ 'a': 1, 'b': 2 }, function(result, n, key) { + * result[key] = n * 3; + * return result; + * }, {}); + * // => { 'a': 3, 'b': 6 } (iteration order is not guaranteed) + */ + var reduce = createReduce(arrayReduce, baseEach); + + /** + * This method is like `_.reduce` except that it iterates over elements of + * `collection` from right to left. + * + * @static + * @memberOf _ + * @alias foldr + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {*} [accumulator] The initial value. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {*} Returns the accumulated value. + * @example + * + * var array = [[0, 1], [2, 3], [4, 5]]; + * + * _.reduceRight(array, function(flattened, other) { + * return flattened.concat(other); + * }, []); + * // => [4, 5, 2, 3, 0, 1] + */ + var reduceRight = createReduce(arrayReduceRight, baseEachRight); + + /** + * The opposite of `_.filter`; this method returns the elements of `collection` + * that `predicate` does **not** return truthy for. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {Array} Returns the new filtered array. + * @example + * + * _.reject([1, 2, 3, 4], function(n) { + * return n % 2 == 0; + * }); + * // => [1, 3] + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': false }, + * { 'user': 'fred', 'age': 40, 'active': true } + * ]; + * + * // using the `_.matches` callback shorthand + * _.pluck(_.reject(users, { 'age': 40, 'active': true }), 'user'); + * // => ['barney'] + * + * // using the `_.matchesProperty` callback shorthand + * _.pluck(_.reject(users, 'active', false), 'user'); + * // => ['fred'] + * + * // using the `_.property` callback shorthand + * _.pluck(_.reject(users, 'active'), 'user'); + * // => ['barney'] + */ + function reject(collection, predicate, thisArg) { + var func = isArray(collection) ? arrayFilter : baseFilter; + predicate = getCallback(predicate, thisArg, 3); + return func(collection, function(value, index, collection) { + return !predicate(value, index, collection); + }); + } + + /** + * Gets a random element or `n` random elements from a collection. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to sample. + * @param {number} [n] The number of elements to sample. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {*} Returns the random sample(s). + * @example + * + * _.sample([1, 2, 3, 4]); + * // => 2 + * + * _.sample([1, 2, 3, 4], 2); + * // => [3, 1] + */ + function sample(collection, n, guard) { + if (guard ? isIterateeCall(collection, n, guard) : n == null) { + collection = toIterable(collection); + var length = collection.length; + return length > 0 ? collection[baseRandom(0, length - 1)] : undefined; + } + var index = -1, + result = toArray(collection), + length = result.length, + lastIndex = length - 1; + + n = nativeMin(n < 0 ? 0 : (+n || 0), length); + while (++index < n) { + var rand = baseRandom(index, lastIndex), + value = result[rand]; + + result[rand] = result[index]; + result[index] = value; + } + result.length = n; + return result; + } + + /** + * Creates an array of shuffled values, using a version of the + * [Fisher-Yates shuffle](https://en.wikipedia.org/wiki/Fisher-Yates_shuffle). + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to shuffle. + * @returns {Array} Returns the new shuffled array. + * @example + * + * _.shuffle([1, 2, 3, 4]); + * // => [4, 1, 3, 2] + */ + function shuffle(collection) { + return sample(collection, POSITIVE_INFINITY); + } + + /** + * Gets the size of `collection` by returning its length for array-like + * values or the number of own enumerable properties for objects. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to inspect. + * @returns {number} Returns the size of `collection`. + * @example + * + * _.size([1, 2, 3]); + * // => 3 + * + * _.size({ 'a': 1, 'b': 2 }); + * // => 2 + * + * _.size('pebbles'); + * // => 7 + */ + function size(collection) { + var length = collection ? getLength(collection) : 0; + return isLength(length) ? length : keys(collection).length; + } + + /** + * Checks if `predicate` returns truthy for **any** element of `collection`. + * The function returns as soon as it finds a passing value and does not iterate + * over the entire collection. The predicate is bound to `thisArg` and invoked + * with three arguments: (value, index|key, collection). + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @alias any + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {boolean} Returns `true` if any element passes the predicate check, + * else `false`. + * @example + * + * _.some([null, 0, 'yes', false], Boolean); + * // => true + * + * var users = [ + * { 'user': 'barney', 'active': true }, + * { 'user': 'fred', 'active': false } + * ]; + * + * // using the `_.matches` callback shorthand + * _.some(users, { 'user': 'barney', 'active': false }); + * // => false + * + * // using the `_.matchesProperty` callback shorthand + * _.some(users, 'active', false); + * // => true + * + * // using the `_.property` callback shorthand + * _.some(users, 'active'); + * // => true + */ + function some(collection, predicate, thisArg) { + var func = isArray(collection) ? arraySome : baseSome; + if (thisArg && isIterateeCall(collection, predicate, thisArg)) { + predicate = undefined; + } + if (typeof predicate != 'function' || thisArg !== undefined) { + predicate = getCallback(predicate, thisArg, 3); + } + return func(collection, predicate); + } + + /** + * Creates an array of elements, sorted in ascending order by the results of + * running each element in a collection through `iteratee`. This method performs + * a stable sort, that is, it preserves the original sort order of equal elements. + * The `iteratee` is bound to `thisArg` and invoked with three arguments: + * (value, index|key, collection). + * + * If a property name is provided for `iteratee` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `iteratee` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [iteratee=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Array} Returns the new sorted array. + * @example + * + * _.sortBy([1, 2, 3], function(n) { + * return Math.sin(n); + * }); + * // => [3, 1, 2] + * + * _.sortBy([1, 2, 3], function(n) { + * return this.sin(n); + * }, Math); + * // => [3, 1, 2] + * + * var users = [ + * { 'user': 'fred' }, + * { 'user': 'pebbles' }, + * { 'user': 'barney' } + * ]; + * + * // using the `_.property` callback shorthand + * _.pluck(_.sortBy(users, 'user'), 'user'); + * // => ['barney', 'fred', 'pebbles'] + */ + function sortBy(collection, iteratee, thisArg) { + if (collection == null) { + return []; + } + if (thisArg && isIterateeCall(collection, iteratee, thisArg)) { + iteratee = undefined; + } + var index = -1; + iteratee = getCallback(iteratee, thisArg, 3); + + var result = baseMap(collection, function(value, key, collection) { + return { 'criteria': iteratee(value, key, collection), 'index': ++index, 'value': value }; + }); + return baseSortBy(result, compareAscending); + } + + /** + * This method is like `_.sortBy` except that it can sort by multiple iteratees + * or property names. + * + * If a property name is provided for an iteratee the created `_.property` + * style callback returns the property value of the given element. + * + * If an object is provided for an iteratee the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {...(Function|Function[]|Object|Object[]|string|string[])} iteratees + * The iteratees to sort by, specified as individual values or arrays of values. + * @returns {Array} Returns the new sorted array. + * @example + * + * var users = [ + * { 'user': 'fred', 'age': 48 }, + * { 'user': 'barney', 'age': 36 }, + * { 'user': 'fred', 'age': 42 }, + * { 'user': 'barney', 'age': 34 } + * ]; + * + * _.map(_.sortByAll(users, ['user', 'age']), _.values); + * // => [['barney', 34], ['barney', 36], ['fred', 42], ['fred', 48]] + * + * _.map(_.sortByAll(users, 'user', function(chr) { + * return Math.floor(chr.age / 10); + * }), _.values); + * // => [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 42]] + */ + var sortByAll = restParam(function(collection, iteratees) { + if (collection == null) { + return []; + } + var guard = iteratees[2]; + if (guard && isIterateeCall(iteratees[0], iteratees[1], guard)) { + iteratees.length = 1; + } + return baseSortByOrder(collection, baseFlatten(iteratees), []); + }); + + /** + * This method is like `_.sortByAll` except that it allows specifying the + * sort orders of the iteratees to sort by. If `orders` is unspecified, all + * values are sorted in ascending order. Otherwise, a value is sorted in + * ascending order if its corresponding order is "asc", and descending if "desc". + * + * If a property name is provided for an iteratee the created `_.property` + * style callback returns the property value of the given element. + * + * If an object is provided for an iteratee the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function[]|Object[]|string[]} iteratees The iteratees to sort by. + * @param {boolean[]} [orders] The sort orders of `iteratees`. + * @param- {Object} [guard] Enables use as a callback for functions like `_.reduce`. + * @returns {Array} Returns the new sorted array. + * @example + * + * var users = [ + * { 'user': 'fred', 'age': 48 }, + * { 'user': 'barney', 'age': 34 }, + * { 'user': 'fred', 'age': 42 }, + * { 'user': 'barney', 'age': 36 } + * ]; + * + * // sort by `user` in ascending order and by `age` in descending order + * _.map(_.sortByOrder(users, ['user', 'age'], ['asc', 'desc']), _.values); + * // => [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 42]] + */ + function sortByOrder(collection, iteratees, orders, guard) { + if (collection == null) { + return []; + } + if (guard && isIterateeCall(iteratees, orders, guard)) { + orders = undefined; + } + if (!isArray(iteratees)) { + iteratees = iteratees == null ? [] : [iteratees]; + } + if (!isArray(orders)) { + orders = orders == null ? [] : [orders]; + } + return baseSortByOrder(collection, iteratees, orders); + } + + /** + * Performs a deep comparison between each element in `collection` and the + * source object, returning an array of all elements that have equivalent + * property values. + * + * **Note:** This method supports comparing arrays, booleans, `Date` objects, + * numbers, `Object` objects, regexes, and strings. Objects are compared by + * their own, not inherited, enumerable properties. For comparing a single + * own or inherited property value see `_.matchesProperty`. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to search. + * @param {Object} source The object of property values to match. + * @returns {Array} Returns the new filtered array. + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': false, 'pets': ['hoppy'] }, + * { 'user': 'fred', 'age': 40, 'active': true, 'pets': ['baby puss', 'dino'] } + * ]; + * + * _.pluck(_.where(users, { 'age': 36, 'active': false }), 'user'); + * // => ['barney'] + * + * _.pluck(_.where(users, { 'pets': ['dino'] }), 'user'); + * // => ['fred'] + */ + function where(collection, source) { + return filter(collection, baseMatches(source)); + } + + /*------------------------------------------------------------------------*/ + + /** + * Gets the number of milliseconds that have elapsed since the Unix epoch + * (1 January 1970 00:00:00 UTC). + * + * @static + * @memberOf _ + * @category Date + * @example + * + * _.defer(function(stamp) { + * console.log(_.now() - stamp); + * }, _.now()); + * // => logs the number of milliseconds it took for the deferred function to be invoked + */ + var now = nativeNow || function() { + return new Date().getTime(); + }; + + /*------------------------------------------------------------------------*/ + + /** + * The opposite of `_.before`; this method creates a function that invokes + * `func` once it is called `n` or more times. + * + * @static + * @memberOf _ + * @category Function + * @param {number} n The number of calls before `func` is invoked. + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new restricted function. + * @example + * + * var saves = ['profile', 'settings']; + * + * var done = _.after(saves.length, function() { + * console.log('done saving!'); + * }); + * + * _.forEach(saves, function(type) { + * asyncSave({ 'type': type, 'complete': done }); + * }); + * // => logs 'done saving!' after the two async saves have completed + */ + function after(n, func) { + if (typeof func != 'function') { + if (typeof n == 'function') { + var temp = n; + n = func; + func = temp; + } else { + throw new TypeError(FUNC_ERROR_TEXT); + } + } + n = nativeIsFinite(n = +n) ? n : 0; + return function() { + if (--n < 1) { + return func.apply(this, arguments); + } + }; + } + + /** + * Creates a function that accepts up to `n` arguments ignoring any + * additional arguments. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to cap arguments for. + * @param {number} [n=func.length] The arity cap. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {Function} Returns the new function. + * @example + * + * _.map(['6', '8', '10'], _.ary(parseInt, 1)); + * // => [6, 8, 10] + */ + function ary(func, n, guard) { + if (guard && isIterateeCall(func, n, guard)) { + n = undefined; + } + n = (func && n == null) ? func.length : nativeMax(+n || 0, 0); + return createWrapper(func, ARY_FLAG, undefined, undefined, undefined, undefined, n); + } + + /** + * Creates a function that invokes `func`, with the `this` binding and arguments + * of the created function, while it is called less than `n` times. Subsequent + * calls to the created function return the result of the last `func` invocation. + * + * @static + * @memberOf _ + * @category Function + * @param {number} n The number of calls at which `func` is no longer invoked. + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new restricted function. + * @example + * + * jQuery('#add').on('click', _.before(5, addContactToList)); + * // => allows adding up to 4 contacts to the list + */ + function before(n, func) { + var result; + if (typeof func != 'function') { + if (typeof n == 'function') { + var temp = n; + n = func; + func = temp; + } else { + throw new TypeError(FUNC_ERROR_TEXT); + } + } + return function() { + if (--n > 0) { + result = func.apply(this, arguments); + } + if (n <= 1) { + func = undefined; + } + return result; + }; + } + + /** + * Creates a function that invokes `func` with the `this` binding of `thisArg` + * and prepends any additional `_.bind` arguments to those provided to the + * bound function. + * + * The `_.bind.placeholder` value, which defaults to `_` in monolithic builds, + * may be used as a placeholder for partially applied arguments. + * + * **Note:** Unlike native `Function#bind` this method does not set the "length" + * property of bound functions. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to bind. + * @param {*} thisArg The `this` binding of `func`. + * @param {...*} [partials] The arguments to be partially applied. + * @returns {Function} Returns the new bound function. + * @example + * + * var greet = function(greeting, punctuation) { + * return greeting + ' ' + this.user + punctuation; + * }; + * + * var object = { 'user': 'fred' }; + * + * var bound = _.bind(greet, object, 'hi'); + * bound('!'); + * // => 'hi fred!' + * + * // using placeholders + * var bound = _.bind(greet, object, _, '!'); + * bound('hi'); + * // => 'hi fred!' + */ + var bind = restParam(function(func, thisArg, partials) { + var bitmask = BIND_FLAG; + if (partials.length) { + var holders = replaceHolders(partials, bind.placeholder); + bitmask |= PARTIAL_FLAG; + } + return createWrapper(func, bitmask, thisArg, partials, holders); + }); + + /** + * Binds methods of an object to the object itself, overwriting the existing + * method. Method names may be specified as individual arguments or as arrays + * of method names. If no method names are provided all enumerable function + * properties, own and inherited, of `object` are bound. + * + * **Note:** This method does not set the "length" property of bound functions. + * + * @static + * @memberOf _ + * @category Function + * @param {Object} object The object to bind and assign the bound methods to. + * @param {...(string|string[])} [methodNames] The object method names to bind, + * specified as individual method names or arrays of method names. + * @returns {Object} Returns `object`. + * @example + * + * var view = { + * 'label': 'docs', + * 'onClick': function() { + * console.log('clicked ' + this.label); + * } + * }; + * + * _.bindAll(view); + * jQuery('#docs').on('click', view.onClick); + * // => logs 'clicked docs' when the element is clicked + */ + var bindAll = restParam(function(object, methodNames) { + methodNames = methodNames.length ? baseFlatten(methodNames) : functions(object); + + var index = -1, + length = methodNames.length; + + while (++index < length) { + var key = methodNames[index]; + object[key] = createWrapper(object[key], BIND_FLAG, object); + } + return object; + }); + + /** + * Creates a function that invokes the method at `object[key]` and prepends + * any additional `_.bindKey` arguments to those provided to the bound function. + * + * This method differs from `_.bind` by allowing bound functions to reference + * methods that may be redefined or don't yet exist. + * See [Peter Michaux's article](http://peter.michaux.ca/articles/lazy-function-definition-pattern) + * for more details. + * + * The `_.bindKey.placeholder` value, which defaults to `_` in monolithic + * builds, may be used as a placeholder for partially applied arguments. + * + * @static + * @memberOf _ + * @category Function + * @param {Object} object The object the method belongs to. + * @param {string} key The key of the method. + * @param {...*} [partials] The arguments to be partially applied. + * @returns {Function} Returns the new bound function. + * @example + * + * var object = { + * 'user': 'fred', + * 'greet': function(greeting, punctuation) { + * return greeting + ' ' + this.user + punctuation; + * } + * }; + * + * var bound = _.bindKey(object, 'greet', 'hi'); + * bound('!'); + * // => 'hi fred!' + * + * object.greet = function(greeting, punctuation) { + * return greeting + 'ya ' + this.user + punctuation; + * }; + * + * bound('!'); + * // => 'hiya fred!' + * + * // using placeholders + * var bound = _.bindKey(object, 'greet', _, '!'); + * bound('hi'); + * // => 'hiya fred!' + */ + var bindKey = restParam(function(object, key, partials) { + var bitmask = BIND_FLAG | BIND_KEY_FLAG; + if (partials.length) { + var holders = replaceHolders(partials, bindKey.placeholder); + bitmask |= PARTIAL_FLAG; + } + return createWrapper(key, bitmask, object, partials, holders); + }); + + /** + * Creates a function that accepts one or more arguments of `func` that when + * called either invokes `func` returning its result, if all `func` arguments + * have been provided, or returns a function that accepts one or more of the + * remaining `func` arguments, and so on. The arity of `func` may be specified + * if `func.length` is not sufficient. + * + * The `_.curry.placeholder` value, which defaults to `_` in monolithic builds, + * may be used as a placeholder for provided arguments. + * + * **Note:** This method does not set the "length" property of curried functions. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to curry. + * @param {number} [arity=func.length] The arity of `func`. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {Function} Returns the new curried function. + * @example + * + * var abc = function(a, b, c) { + * return [a, b, c]; + * }; + * + * var curried = _.curry(abc); + * + * curried(1)(2)(3); + * // => [1, 2, 3] + * + * curried(1, 2)(3); + * // => [1, 2, 3] + * + * curried(1, 2, 3); + * // => [1, 2, 3] + * + * // using placeholders + * curried(1)(_, 3)(2); + * // => [1, 2, 3] + */ + var curry = createCurry(CURRY_FLAG); + + /** + * This method is like `_.curry` except that arguments are applied to `func` + * in the manner of `_.partialRight` instead of `_.partial`. + * + * The `_.curryRight.placeholder` value, which defaults to `_` in monolithic + * builds, may be used as a placeholder for provided arguments. + * + * **Note:** This method does not set the "length" property of curried functions. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to curry. + * @param {number} [arity=func.length] The arity of `func`. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {Function} Returns the new curried function. + * @example + * + * var abc = function(a, b, c) { + * return [a, b, c]; + * }; + * + * var curried = _.curryRight(abc); + * + * curried(3)(2)(1); + * // => [1, 2, 3] + * + * curried(2, 3)(1); + * // => [1, 2, 3] + * + * curried(1, 2, 3); + * // => [1, 2, 3] + * + * // using placeholders + * curried(3)(1, _)(2); + * // => [1, 2, 3] + */ + var curryRight = createCurry(CURRY_RIGHT_FLAG); + + /** + * Creates a debounced function that delays invoking `func` until after `wait` + * milliseconds have elapsed since the last time the debounced function was + * invoked. The debounced function comes with a `cancel` method to cancel + * delayed invocations. Provide an options object to indicate that `func` + * should be invoked on the leading and/or trailing edge of the `wait` timeout. + * Subsequent calls to the debounced function return the result of the last + * `func` invocation. + * + * **Note:** If `leading` and `trailing` options are `true`, `func` is invoked + * on the trailing edge of the timeout only if the the debounced function is + * invoked more than once during the `wait` timeout. + * + * See [David Corbacho's article](http://drupalmotion.com/article/debounce-and-throttle-visual-explanation) + * for details over the differences between `_.debounce` and `_.throttle`. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to debounce. + * @param {number} [wait=0] The number of milliseconds to delay. + * @param {Object} [options] The options object. + * @param {boolean} [options.leading=false] Specify invoking on the leading + * edge of the timeout. + * @param {number} [options.maxWait] The maximum time `func` is allowed to be + * delayed before it is invoked. + * @param {boolean} [options.trailing=true] Specify invoking on the trailing + * edge of the timeout. + * @returns {Function} Returns the new debounced function. + * @example + * + * // avoid costly calculations while the window size is in flux + * jQuery(window).on('resize', _.debounce(calculateLayout, 150)); + * + * // invoke `sendMail` when the click event is fired, debouncing subsequent calls + * jQuery('#postbox').on('click', _.debounce(sendMail, 300, { + * 'leading': true, + * 'trailing': false + * })); + * + * // ensure `batchLog` is invoked once after 1 second of debounced calls + * var source = new EventSource('/stream'); + * jQuery(source).on('message', _.debounce(batchLog, 250, { + * 'maxWait': 1000 + * })); + * + * // cancel a debounced call + * var todoChanges = _.debounce(batchLog, 1000); + * Object.observe(models.todo, todoChanges); + * + * Object.observe(models, function(changes) { + * if (_.find(changes, { 'user': 'todo', 'type': 'delete'})) { + * todoChanges.cancel(); + * } + * }, ['delete']); + * + * // ...at some point `models.todo` is changed + * models.todo.completed = true; + * + * // ...before 1 second has passed `models.todo` is deleted + * // which cancels the debounced `todoChanges` call + * delete models.todo; + */ + function debounce(func, wait, options) { + var args, + maxTimeoutId, + result, + stamp, + thisArg, + timeoutId, + trailingCall, + lastCalled = 0, + maxWait = false, + trailing = true; + + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + wait = wait < 0 ? 0 : (+wait || 0); + if (options === true) { + var leading = true; + trailing = false; + } else if (isObject(options)) { + leading = !!options.leading; + maxWait = 'maxWait' in options && nativeMax(+options.maxWait || 0, wait); + trailing = 'trailing' in options ? !!options.trailing : trailing; + } + + function cancel() { + if (timeoutId) { + clearTimeout(timeoutId); + } + if (maxTimeoutId) { + clearTimeout(maxTimeoutId); + } + lastCalled = 0; + maxTimeoutId = timeoutId = trailingCall = undefined; + } + + function complete(isCalled, id) { + if (id) { + clearTimeout(id); + } + maxTimeoutId = timeoutId = trailingCall = undefined; + if (isCalled) { + lastCalled = now(); + result = func.apply(thisArg, args); + if (!timeoutId && !maxTimeoutId) { + args = thisArg = undefined; + } + } + } + + function delayed() { + var remaining = wait - (now() - stamp); + if (remaining <= 0 || remaining > wait) { + complete(trailingCall, maxTimeoutId); + } else { + timeoutId = setTimeout(delayed, remaining); + } + } + + function maxDelayed() { + complete(trailing, timeoutId); + } + + function debounced() { + args = arguments; + stamp = now(); + thisArg = this; + trailingCall = trailing && (timeoutId || !leading); + + if (maxWait === false) { + var leadingCall = leading && !timeoutId; + } else { + if (!maxTimeoutId && !leading) { + lastCalled = stamp; + } + var remaining = maxWait - (stamp - lastCalled), + isCalled = remaining <= 0 || remaining > maxWait; + + if (isCalled) { + if (maxTimeoutId) { + maxTimeoutId = clearTimeout(maxTimeoutId); + } + lastCalled = stamp; + result = func.apply(thisArg, args); + } + else if (!maxTimeoutId) { + maxTimeoutId = setTimeout(maxDelayed, remaining); + } + } + if (isCalled && timeoutId) { + timeoutId = clearTimeout(timeoutId); + } + else if (!timeoutId && wait !== maxWait) { + timeoutId = setTimeout(delayed, wait); + } + if (leadingCall) { + isCalled = true; + result = func.apply(thisArg, args); + } + if (isCalled && !timeoutId && !maxTimeoutId) { + args = thisArg = undefined; + } + return result; + } + debounced.cancel = cancel; + return debounced; + } + + /** + * Defers invoking the `func` until the current call stack has cleared. Any + * additional arguments are provided to `func` when it is invoked. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to defer. + * @param {...*} [args] The arguments to invoke the function with. + * @returns {number} Returns the timer id. + * @example + * + * _.defer(function(text) { + * console.log(text); + * }, 'deferred'); + * // logs 'deferred' after one or more milliseconds + */ + var defer = restParam(function(func, args) { + return baseDelay(func, 1, args); + }); + + /** + * Invokes `func` after `wait` milliseconds. Any additional arguments are + * provided to `func` when it is invoked. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to delay. + * @param {number} wait The number of milliseconds to delay invocation. + * @param {...*} [args] The arguments to invoke the function with. + * @returns {number} Returns the timer id. + * @example + * + * _.delay(function(text) { + * console.log(text); + * }, 1000, 'later'); + * // => logs 'later' after one second + */ + var delay = restParam(function(func, wait, args) { + return baseDelay(func, wait, args); + }); + + /** + * Creates a function that returns the result of invoking the provided + * functions with the `this` binding of the created function, where each + * successive invocation is supplied the return value of the previous. + * + * @static + * @memberOf _ + * @category Function + * @param {...Function} [funcs] Functions to invoke. + * @returns {Function} Returns the new function. + * @example + * + * function square(n) { + * return n * n; + * } + * + * var addSquare = _.flow(_.add, square); + * addSquare(1, 2); + * // => 9 + */ + var flow = createFlow(); + + /** + * This method is like `_.flow` except that it creates a function that + * invokes the provided functions from right to left. + * + * @static + * @memberOf _ + * @alias backflow, compose + * @category Function + * @param {...Function} [funcs] Functions to invoke. + * @returns {Function} Returns the new function. + * @example + * + * function square(n) { + * return n * n; + * } + * + * var addSquare = _.flowRight(square, _.add); + * addSquare(1, 2); + * // => 9 + */ + var flowRight = createFlow(true); + + /** + * Creates a function that memoizes the result of `func`. If `resolver` is + * provided it determines the cache key for storing the result based on the + * arguments provided to the memoized function. By default, the first argument + * provided to the memoized function is coerced to a string and used as the + * cache key. The `func` is invoked with the `this` binding of the memoized + * function. + * + * **Note:** The cache is exposed as the `cache` property on the memoized + * function. Its creation may be customized by replacing the `_.memoize.Cache` + * constructor with one whose instances implement the [`Map`](http://ecma-international.org/ecma-262/6.0/#sec-properties-of-the-map-prototype-object) + * method interface of `get`, `has`, and `set`. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to have its output memoized. + * @param {Function} [resolver] The function to resolve the cache key. + * @returns {Function} Returns the new memoizing function. + * @example + * + * var upperCase = _.memoize(function(string) { + * return string.toUpperCase(); + * }); + * + * upperCase('fred'); + * // => 'FRED' + * + * // modifying the result cache + * upperCase.cache.set('fred', 'BARNEY'); + * upperCase('fred'); + * // => 'BARNEY' + * + * // replacing `_.memoize.Cache` + * var object = { 'user': 'fred' }; + * var other = { 'user': 'barney' }; + * var identity = _.memoize(_.identity); + * + * identity(object); + * // => { 'user': 'fred' } + * identity(other); + * // => { 'user': 'fred' } + * + * _.memoize.Cache = WeakMap; + * var identity = _.memoize(_.identity); + * + * identity(object); + * // => { 'user': 'fred' } + * identity(other); + * // => { 'user': 'barney' } + */ + function memoize(func, resolver) { + if (typeof func != 'function' || (resolver && typeof resolver != 'function')) { + throw new TypeError(FUNC_ERROR_TEXT); + } + var memoized = function() { + var args = arguments, + key = resolver ? resolver.apply(this, args) : args[0], + cache = memoized.cache; + + if (cache.has(key)) { + return cache.get(key); + } + var result = func.apply(this, args); + memoized.cache = cache.set(key, result); + return result; + }; + memoized.cache = new memoize.Cache; + return memoized; + } + + /** + * Creates a function that runs each argument through a corresponding + * transform function. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to wrap. + * @param {...(Function|Function[])} [transforms] The functions to transform + * arguments, specified as individual functions or arrays of functions. + * @returns {Function} Returns the new function. + * @example + * + * function doubled(n) { + * return n * 2; + * } + * + * function square(n) { + * return n * n; + * } + * + * var modded = _.modArgs(function(x, y) { + * return [x, y]; + * }, square, doubled); + * + * modded(1, 2); + * // => [1, 4] + * + * modded(5, 10); + * // => [25, 20] + */ + var modArgs = restParam(function(func, transforms) { + transforms = baseFlatten(transforms); + if (typeof func != 'function' || !arrayEvery(transforms, baseIsFunction)) { + throw new TypeError(FUNC_ERROR_TEXT); + } + var length = transforms.length; + return restParam(function(args) { + var index = nativeMin(args.length, length); + while (index--) { + args[index] = transforms[index](args[index]); + } + return func.apply(this, args); + }); + }); + + /** + * Creates a function that negates the result of the predicate `func`. The + * `func` predicate is invoked with the `this` binding and arguments of the + * created function. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} predicate The predicate to negate. + * @returns {Function} Returns the new function. + * @example + * + * function isEven(n) { + * return n % 2 == 0; + * } + * + * _.filter([1, 2, 3, 4, 5, 6], _.negate(isEven)); + * // => [1, 3, 5] + */ + function negate(predicate) { + if (typeof predicate != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + return function() { + return !predicate.apply(this, arguments); + }; + } + + /** + * Creates a function that is restricted to invoking `func` once. Repeat calls + * to the function return the value of the first call. The `func` is invoked + * with the `this` binding and arguments of the created function. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new restricted function. + * @example + * + * var initialize = _.once(createApplication); + * initialize(); + * initialize(); + * // `initialize` invokes `createApplication` once + */ + function once(func) { + return before(2, func); + } + + /** + * Creates a function that invokes `func` with `partial` arguments prepended + * to those provided to the new function. This method is like `_.bind` except + * it does **not** alter the `this` binding. + * + * The `_.partial.placeholder` value, which defaults to `_` in monolithic + * builds, may be used as a placeholder for partially applied arguments. + * + * **Note:** This method does not set the "length" property of partially + * applied functions. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to partially apply arguments to. + * @param {...*} [partials] The arguments to be partially applied. + * @returns {Function} Returns the new partially applied function. + * @example + * + * var greet = function(greeting, name) { + * return greeting + ' ' + name; + * }; + * + * var sayHelloTo = _.partial(greet, 'hello'); + * sayHelloTo('fred'); + * // => 'hello fred' + * + * // using placeholders + * var greetFred = _.partial(greet, _, 'fred'); + * greetFred('hi'); + * // => 'hi fred' + */ + var partial = createPartial(PARTIAL_FLAG); + + /** + * This method is like `_.partial` except that partially applied arguments + * are appended to those provided to the new function. + * + * The `_.partialRight.placeholder` value, which defaults to `_` in monolithic + * builds, may be used as a placeholder for partially applied arguments. + * + * **Note:** This method does not set the "length" property of partially + * applied functions. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to partially apply arguments to. + * @param {...*} [partials] The arguments to be partially applied. + * @returns {Function} Returns the new partially applied function. + * @example + * + * var greet = function(greeting, name) { + * return greeting + ' ' + name; + * }; + * + * var greetFred = _.partialRight(greet, 'fred'); + * greetFred('hi'); + * // => 'hi fred' + * + * // using placeholders + * var sayHelloTo = _.partialRight(greet, 'hello', _); + * sayHelloTo('fred'); + * // => 'hello fred' + */ + var partialRight = createPartial(PARTIAL_RIGHT_FLAG); + + /** + * Creates a function that invokes `func` with arguments arranged according + * to the specified indexes where the argument value at the first index is + * provided as the first argument, the argument value at the second index is + * provided as the second argument, and so on. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to rearrange arguments for. + * @param {...(number|number[])} indexes The arranged argument indexes, + * specified as individual indexes or arrays of indexes. + * @returns {Function} Returns the new function. + * @example + * + * var rearged = _.rearg(function(a, b, c) { + * return [a, b, c]; + * }, 2, 0, 1); + * + * rearged('b', 'c', 'a') + * // => ['a', 'b', 'c'] + * + * var map = _.rearg(_.map, [1, 0]); + * map(function(n) { + * return n * 3; + * }, [1, 2, 3]); + * // => [3, 6, 9] + */ + var rearg = restParam(function(func, indexes) { + return createWrapper(func, REARG_FLAG, undefined, undefined, undefined, baseFlatten(indexes)); + }); + + /** + * Creates a function that invokes `func` with the `this` binding of the + * created function and arguments from `start` and beyond provided as an array. + * + * **Note:** This method is based on the [rest parameter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters). + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to apply a rest parameter to. + * @param {number} [start=func.length-1] The start position of the rest parameter. + * @returns {Function} Returns the new function. + * @example + * + * var say = _.restParam(function(what, names) { + * return what + ' ' + _.initial(names).join(', ') + + * (_.size(names) > 1 ? ', & ' : '') + _.last(names); + * }); + * + * say('hello', 'fred', 'barney', 'pebbles'); + * // => 'hello fred, barney, & pebbles' + */ + function restParam(func, start) { + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + start = nativeMax(start === undefined ? (func.length - 1) : (+start || 0), 0); + return function() { + var args = arguments, + index = -1, + length = nativeMax(args.length - start, 0), + rest = Array(length); + + while (++index < length) { + rest[index] = args[start + index]; + } + switch (start) { + case 0: return func.call(this, rest); + case 1: return func.call(this, args[0], rest); + case 2: return func.call(this, args[0], args[1], rest); + } + var otherArgs = Array(start + 1); + index = -1; + while (++index < start) { + otherArgs[index] = args[index]; + } + otherArgs[start] = rest; + return func.apply(this, otherArgs); + }; + } + + /** + * Creates a function that invokes `func` with the `this` binding of the created + * function and an array of arguments much like [`Function#apply`](https://es5.github.io/#x15.3.4.3). + * + * **Note:** This method is based on the [spread operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator). + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to spread arguments over. + * @returns {Function} Returns the new function. + * @example + * + * var say = _.spread(function(who, what) { + * return who + ' says ' + what; + * }); + * + * say(['fred', 'hello']); + * // => 'fred says hello' + * + * // with a Promise + * var numbers = Promise.all([ + * Promise.resolve(40), + * Promise.resolve(36) + * ]); + * + * numbers.then(_.spread(function(x, y) { + * return x + y; + * })); + * // => a Promise of 76 + */ + function spread(func) { + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + return function(array) { + return func.apply(this, array); + }; + } + + /** + * Creates a throttled function that only invokes `func` at most once per + * every `wait` milliseconds. The throttled function comes with a `cancel` + * method to cancel delayed invocations. Provide an options object to indicate + * that `func` should be invoked on the leading and/or trailing edge of the + * `wait` timeout. Subsequent calls to the throttled function return the + * result of the last `func` call. + * + * **Note:** If `leading` and `trailing` options are `true`, `func` is invoked + * on the trailing edge of the timeout only if the the throttled function is + * invoked more than once during the `wait` timeout. + * + * See [David Corbacho's article](http://drupalmotion.com/article/debounce-and-throttle-visual-explanation) + * for details over the differences between `_.throttle` and `_.debounce`. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to throttle. + * @param {number} [wait=0] The number of milliseconds to throttle invocations to. + * @param {Object} [options] The options object. + * @param {boolean} [options.leading=true] Specify invoking on the leading + * edge of the timeout. + * @param {boolean} [options.trailing=true] Specify invoking on the trailing + * edge of the timeout. + * @returns {Function} Returns the new throttled function. + * @example + * + * // avoid excessively updating the position while scrolling + * jQuery(window).on('scroll', _.throttle(updatePosition, 100)); + * + * // invoke `renewToken` when the click event is fired, but not more than once every 5 minutes + * jQuery('.interactive').on('click', _.throttle(renewToken, 300000, { + * 'trailing': false + * })); + * + * // cancel a trailing throttled call + * jQuery(window).on('popstate', throttled.cancel); + */ + function throttle(func, wait, options) { + var leading = true, + trailing = true; + + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + if (options === false) { + leading = false; + } else if (isObject(options)) { + leading = 'leading' in options ? !!options.leading : leading; + trailing = 'trailing' in options ? !!options.trailing : trailing; + } + return debounce(func, wait, { 'leading': leading, 'maxWait': +wait, 'trailing': trailing }); + } + + /** + * Creates a function that provides `value` to the wrapper function as its + * first argument. Any additional arguments provided to the function are + * appended to those provided to the wrapper function. The wrapper is invoked + * with the `this` binding of the created function. + * + * @static + * @memberOf _ + * @category Function + * @param {*} value The value to wrap. + * @param {Function} wrapper The wrapper function. + * @returns {Function} Returns the new function. + * @example + * + * var p = _.wrap(_.escape, function(func, text) { + * return '<p>' + func(text) + '</p>'; + * }); + * + * p('fred, barney, & pebbles'); + * // => '<p>fred, barney, & pebbles</p>' + */ + function wrap(value, wrapper) { + wrapper = wrapper == null ? identity : wrapper; + return createWrapper(wrapper, PARTIAL_FLAG, undefined, [value], []); + } + + /*------------------------------------------------------------------------*/ + + /** + * Creates a clone of `value`. If `isDeep` is `true` nested objects are cloned, + * otherwise they are assigned by reference. If `customizer` is provided it is + * invoked to produce the cloned values. If `customizer` returns `undefined` + * cloning is handled by the method instead. The `customizer` is bound to + * `thisArg` and invoked with two argument; (value [, index|key, object]). + * + * **Note:** This method is loosely based on the + * [structured clone algorithm](http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm). + * The enumerable properties of `arguments` objects and objects created by + * constructors other than `Object` are cloned to plain `Object` objects. An + * empty object is returned for uncloneable values such as functions, DOM nodes, + * Maps, Sets, and WeakMaps. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to clone. + * @param {boolean} [isDeep] Specify a deep clone. + * @param {Function} [customizer] The function to customize cloning values. + * @param {*} [thisArg] The `this` binding of `customizer`. + * @returns {*} Returns the cloned value. + * @example + * + * var users = [ + * { 'user': 'barney' }, + * { 'user': 'fred' } + * ]; + * + * var shallow = _.clone(users); + * shallow[0] === users[0]; + * // => true + * + * var deep = _.clone(users, true); + * deep[0] === users[0]; + * // => false + * + * // using a customizer callback + * var el = _.clone(document.body, function(value) { + * if (_.isElement(value)) { + * return value.cloneNode(false); + * } + * }); + * + * el === document.body + * // => false + * el.nodeName + * // => BODY + * el.childNodes.length; + * // => 0 + */ + function clone(value, isDeep, customizer, thisArg) { + if (isDeep && typeof isDeep != 'boolean' && isIterateeCall(value, isDeep, customizer)) { + isDeep = false; + } + else if (typeof isDeep == 'function') { + thisArg = customizer; + customizer = isDeep; + isDeep = false; + } + return typeof customizer == 'function' + ? baseClone(value, isDeep, bindCallback(customizer, thisArg, 1)) + : baseClone(value, isDeep); + } + + /** + * Creates a deep clone of `value`. If `customizer` is provided it is invoked + * to produce the cloned values. If `customizer` returns `undefined` cloning + * is handled by the method instead. The `customizer` is bound to `thisArg` + * and invoked with two argument; (value [, index|key, object]). + * + * **Note:** This method is loosely based on the + * [structured clone algorithm](http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm). + * The enumerable properties of `arguments` objects and objects created by + * constructors other than `Object` are cloned to plain `Object` objects. An + * empty object is returned for uncloneable values such as functions, DOM nodes, + * Maps, Sets, and WeakMaps. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to deep clone. + * @param {Function} [customizer] The function to customize cloning values. + * @param {*} [thisArg] The `this` binding of `customizer`. + * @returns {*} Returns the deep cloned value. + * @example + * + * var users = [ + * { 'user': 'barney' }, + * { 'user': 'fred' } + * ]; + * + * var deep = _.cloneDeep(users); + * deep[0] === users[0]; + * // => false + * + * // using a customizer callback + * var el = _.cloneDeep(document.body, function(value) { + * if (_.isElement(value)) { + * return value.cloneNode(true); + * } + * }); + * + * el === document.body + * // => false + * el.nodeName + * // => BODY + * el.childNodes.length; + * // => 20 + */ + function cloneDeep(value, customizer, thisArg) { + return typeof customizer == 'function' + ? baseClone(value, true, bindCallback(customizer, thisArg, 1)) + : baseClone(value, true); + } + + /** + * Checks if `value` is greater than `other`. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if `value` is greater than `other`, else `false`. + * @example + * + * _.gt(3, 1); + * // => true + * + * _.gt(3, 3); + * // => false + * + * _.gt(1, 3); + * // => false + */ + function gt(value, other) { + return value > other; + } + + /** + * Checks if `value` is greater than or equal to `other`. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if `value` is greater than or equal to `other`, else `false`. + * @example + * + * _.gte(3, 1); + * // => true + * + * _.gte(3, 3); + * // => true + * + * _.gte(1, 3); + * // => false + */ + function gte(value, other) { + return value >= other; + } + + /** + * Checks if `value` is classified as an `arguments` object. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isArguments(function() { return arguments; }()); + * // => true + * + * _.isArguments([1, 2, 3]); + * // => false + */ + function isArguments(value) { + return isObjectLike(value) && isArrayLike(value) && + hasOwnProperty.call(value, 'callee') && !propertyIsEnumerable.call(value, 'callee'); + } + + /** + * Checks if `value` is classified as an `Array` object. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isArray([1, 2, 3]); + * // => true + * + * _.isArray(function() { return arguments; }()); + * // => false + */ + var isArray = nativeIsArray || function(value) { + return isObjectLike(value) && isLength(value.length) && objToString.call(value) == arrayTag; + }; + + /** + * Checks if `value` is classified as a boolean primitive or object. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isBoolean(false); + * // => true + * + * _.isBoolean(null); + * // => false + */ + function isBoolean(value) { + return value === true || value === false || (isObjectLike(value) && objToString.call(value) == boolTag); + } + + /** + * Checks if `value` is classified as a `Date` object. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isDate(new Date); + * // => true + * + * _.isDate('Mon April 23 2012'); + * // => false + */ + function isDate(value) { + return isObjectLike(value) && objToString.call(value) == dateTag; + } + + /** + * Checks if `value` is a DOM element. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a DOM element, else `false`. + * @example + * + * _.isElement(document.body); + * // => true + * + * _.isElement('<body>'); + * // => false + */ + function isElement(value) { + return !!value && value.nodeType === 1 && isObjectLike(value) && !isPlainObject(value); + } + + /** + * Checks if `value` is empty. A value is considered empty unless it is an + * `arguments` object, array, string, or jQuery-like collection with a length + * greater than `0` or an object with own enumerable properties. + * + * @static + * @memberOf _ + * @category Lang + * @param {Array|Object|string} value The value to inspect. + * @returns {boolean} Returns `true` if `value` is empty, else `false`. + * @example + * + * _.isEmpty(null); + * // => true + * + * _.isEmpty(true); + * // => true + * + * _.isEmpty(1); + * // => true + * + * _.isEmpty([1, 2, 3]); + * // => false + * + * _.isEmpty({ 'a': 1 }); + * // => false + */ + function isEmpty(value) { + if (value == null) { + return true; + } + if (isArrayLike(value) && (isArray(value) || isString(value) || isArguments(value) || + (isObjectLike(value) && isFunction(value.splice)))) { + return !value.length; + } + return !keys(value).length; + } + + /** + * Performs a deep comparison between two values to determine if they are + * equivalent. If `customizer` is provided it is invoked to compare values. + * If `customizer` returns `undefined` comparisons are handled by the method + * instead. The `customizer` is bound to `thisArg` and invoked with three + * arguments: (value, other [, index|key]). + * + * **Note:** This method supports comparing arrays, booleans, `Date` objects, + * numbers, `Object` objects, regexes, and strings. Objects are compared by + * their own, not inherited, enumerable properties. Functions and DOM nodes + * are **not** supported. Provide a customizer function to extend support + * for comparing other values. + * + * @static + * @memberOf _ + * @alias eq + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @param {Function} [customizer] The function to customize value comparisons. + * @param {*} [thisArg] The `this` binding of `customizer`. + * @returns {boolean} Returns `true` if the values are equivalent, else `false`. + * @example + * + * var object = { 'user': 'fred' }; + * var other = { 'user': 'fred' }; + * + * object == other; + * // => false + * + * _.isEqual(object, other); + * // => true + * + * // using a customizer callback + * var array = ['hello', 'goodbye']; + * var other = ['hi', 'goodbye']; + * + * _.isEqual(array, other, function(value, other) { + * if (_.every([value, other], RegExp.prototype.test, /^h(?:i|ello)$/)) { + * return true; + * } + * }); + * // => true + */ + function isEqual(value, other, customizer, thisArg) { + customizer = typeof customizer == 'function' ? bindCallback(customizer, thisArg, 3) : undefined; + var result = customizer ? customizer(value, other) : undefined; + return result === undefined ? baseIsEqual(value, other, customizer) : !!result; + } + + /** + * Checks if `value` is an `Error`, `EvalError`, `RangeError`, `ReferenceError`, + * `SyntaxError`, `TypeError`, or `URIError` object. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an error object, else `false`. + * @example + * + * _.isError(new Error); + * // => true + * + * _.isError(Error); + * // => false + */ + function isError(value) { + return isObjectLike(value) && typeof value.message == 'string' && objToString.call(value) == errorTag; + } + + /** + * Checks if `value` is a finite primitive number. + * + * **Note:** This method is based on [`Number.isFinite`](http://ecma-international.org/ecma-262/6.0/#sec-number.isfinite). + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a finite number, else `false`. + * @example + * + * _.isFinite(10); + * // => true + * + * _.isFinite('10'); + * // => false + * + * _.isFinite(true); + * // => false + * + * _.isFinite(Object(10)); + * // => false + * + * _.isFinite(Infinity); + * // => false + */ + function isFinite(value) { + return typeof value == 'number' && nativeIsFinite(value); + } + + /** + * Checks if `value` is classified as a `Function` object. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isFunction(_); + * // => true + * + * _.isFunction(/abc/); + * // => false + */ + function isFunction(value) { + // The use of `Object#toString` avoids issues with the `typeof` operator + // in older versions of Chrome and Safari which return 'function' for regexes + // and Safari 8 equivalents which return 'object' for typed array constructors. + return isObject(value) && objToString.call(value) == funcTag; + } + + /** + * Checks if `value` is the [language type](https://es5.github.io/#x8) of `Object`. + * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an object, else `false`. + * @example + * + * _.isObject({}); + * // => true + * + * _.isObject([1, 2, 3]); + * // => true + * + * _.isObject(1); + * // => false + */ + function isObject(value) { + // Avoid a V8 JIT bug in Chrome 19-20. + // See https://code.google.com/p/v8/issues/detail?id=2291 for more details. + var type = typeof value; + return !!value && (type == 'object' || type == 'function'); + } + + /** + * Performs a deep comparison between `object` and `source` to determine if + * `object` contains equivalent property values. If `customizer` is provided + * it is invoked to compare values. If `customizer` returns `undefined` + * comparisons are handled by the method instead. The `customizer` is bound + * to `thisArg` and invoked with three arguments: (value, other, index|key). + * + * **Note:** This method supports comparing properties of arrays, booleans, + * `Date` objects, numbers, `Object` objects, regexes, and strings. Functions + * and DOM nodes are **not** supported. Provide a customizer function to extend + * support for comparing other values. + * + * @static + * @memberOf _ + * @category Lang + * @param {Object} object The object to inspect. + * @param {Object} source The object of property values to match. + * @param {Function} [customizer] The function to customize value comparisons. + * @param {*} [thisArg] The `this` binding of `customizer`. + * @returns {boolean} Returns `true` if `object` is a match, else `false`. + * @example + * + * var object = { 'user': 'fred', 'age': 40 }; + * + * _.isMatch(object, { 'age': 40 }); + * // => true + * + * _.isMatch(object, { 'age': 36 }); + * // => false + * + * // using a customizer callback + * var object = { 'greeting': 'hello' }; + * var source = { 'greeting': 'hi' }; + * + * _.isMatch(object, source, function(value, other) { + * return _.every([value, other], RegExp.prototype.test, /^h(?:i|ello)$/) || undefined; + * }); + * // => true + */ + function isMatch(object, source, customizer, thisArg) { + customizer = typeof customizer == 'function' ? bindCallback(customizer, thisArg, 3) : undefined; + return baseIsMatch(object, getMatchData(source), customizer); + } + + /** + * Checks if `value` is `NaN`. + * + * **Note:** This method is not the same as [`isNaN`](https://es5.github.io/#x15.1.2.4) + * which returns `true` for `undefined` and other non-numeric values. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`. + * @example + * + * _.isNaN(NaN); + * // => true + * + * _.isNaN(new Number(NaN)); + * // => true + * + * isNaN(undefined); + * // => true + * + * _.isNaN(undefined); + * // => false + */ + function isNaN(value) { + // An `NaN` primitive is the only value that is not equal to itself. + // Perform the `toStringTag` check first to avoid errors with some host objects in IE. + return isNumber(value) && value != +value; + } + + /** + * Checks if `value` is a native function. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a native function, else `false`. + * @example + * + * _.isNative(Array.prototype.push); + * // => true + * + * _.isNative(_); + * // => false + */ + function isNative(value) { + if (value == null) { + return false; + } + if (isFunction(value)) { + return reIsNative.test(fnToString.call(value)); + } + return isObjectLike(value) && reIsHostCtor.test(value); + } + + /** + * Checks if `value` is `null`. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is `null`, else `false`. + * @example + * + * _.isNull(null); + * // => true + * + * _.isNull(void 0); + * // => false + */ + function isNull(value) { + return value === null; + } + + /** + * Checks if `value` is classified as a `Number` primitive or object. + * + * **Note:** To exclude `Infinity`, `-Infinity`, and `NaN`, which are classified + * as numbers, use the `_.isFinite` method. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isNumber(8.4); + * // => true + * + * _.isNumber(NaN); + * // => true + * + * _.isNumber('8.4'); + * // => false + */ + function isNumber(value) { + return typeof value == 'number' || (isObjectLike(value) && objToString.call(value) == numberTag); + } + + /** + * Checks if `value` is a plain object, that is, an object created by the + * `Object` constructor or one with a `[[Prototype]]` of `null`. + * + * **Note:** This method assumes objects created by the `Object` constructor + * have no inherited enumerable properties. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a plain object, else `false`. + * @example + * + * function Foo() { + * this.a = 1; + * } + * + * _.isPlainObject(new Foo); + * // => false + * + * _.isPlainObject([1, 2, 3]); + * // => false + * + * _.isPlainObject({ 'x': 0, 'y': 0 }); + * // => true + * + * _.isPlainObject(Object.create(null)); + * // => true + */ + function isPlainObject(value) { + var Ctor; + + // Exit early for non `Object` objects. + if (!(isObjectLike(value) && objToString.call(value) == objectTag && !isArguments(value)) || + (!hasOwnProperty.call(value, 'constructor') && (Ctor = value.constructor, typeof Ctor == 'function' && !(Ctor instanceof Ctor)))) { + return false; + } + // IE < 9 iterates inherited properties before own properties. If the first + // iterated property is an object's own property then there are no inherited + // enumerable properties. + var result; + // In most environments an object's own properties are iterated before + // its inherited properties. If the last iterated property is an object's + // own property then there are no inherited enumerable properties. + baseForIn(value, function(subValue, key) { + result = key; + }); + return result === undefined || hasOwnProperty.call(value, result); + } + + /** + * Checks if `value` is classified as a `RegExp` object. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isRegExp(/abc/); + * // => true + * + * _.isRegExp('/abc/'); + * // => false + */ + function isRegExp(value) { + return isObject(value) && objToString.call(value) == regexpTag; + } + + /** + * Checks if `value` is classified as a `String` primitive or object. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isString('abc'); + * // => true + * + * _.isString(1); + * // => false + */ + function isString(value) { + return typeof value == 'string' || (isObjectLike(value) && objToString.call(value) == stringTag); + } + + /** + * Checks if `value` is classified as a typed array. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isTypedArray(new Uint8Array); + * // => true + * + * _.isTypedArray([]); + * // => false + */ + function isTypedArray(value) { + return isObjectLike(value) && isLength(value.length) && !!typedArrayTags[objToString.call(value)]; + } + + /** + * Checks if `value` is `undefined`. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`. + * @example + * + * _.isUndefined(void 0); + * // => true + * + * _.isUndefined(null); + * // => false + */ + function isUndefined(value) { + return value === undefined; + } + + /** + * Checks if `value` is less than `other`. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if `value` is less than `other`, else `false`. + * @example + * + * _.lt(1, 3); + * // => true + * + * _.lt(3, 3); + * // => false + * + * _.lt(3, 1); + * // => false + */ + function lt(value, other) { + return value < other; + } + + /** + * Checks if `value` is less than or equal to `other`. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if `value` is less than or equal to `other`, else `false`. + * @example + * + * _.lte(1, 3); + * // => true + * + * _.lte(3, 3); + * // => true + * + * _.lte(3, 1); + * // => false + */ + function lte(value, other) { + return value <= other; + } + + /** + * Converts `value` to an array. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to convert. + * @returns {Array} Returns the converted array. + * @example + * + * (function() { + * return _.toArray(arguments).slice(1); + * }(1, 2, 3)); + * // => [2, 3] + */ + function toArray(value) { + var length = value ? getLength(value) : 0; + if (!isLength(length)) { + return values(value); + } + if (!length) { + return []; + } + return arrayCopy(value); + } + + /** + * Converts `value` to a plain object flattening inherited enumerable + * properties of `value` to own properties of the plain object. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to convert. + * @returns {Object} Returns the converted plain object. + * @example + * + * function Foo() { + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.assign({ 'a': 1 }, new Foo); + * // => { 'a': 1, 'b': 2 } + * + * _.assign({ 'a': 1 }, _.toPlainObject(new Foo)); + * // => { 'a': 1, 'b': 2, 'c': 3 } + */ + function toPlainObject(value) { + return baseCopy(value, keysIn(value)); + } + + /*------------------------------------------------------------------------*/ + + /** + * Recursively merges own enumerable properties of the source object(s), that + * don't resolve to `undefined` into the destination object. Subsequent sources + * overwrite property assignments of previous sources. If `customizer` is + * provided it is invoked to produce the merged values of the destination and + * source properties. If `customizer` returns `undefined` merging is handled + * by the method instead. The `customizer` is bound to `thisArg` and invoked + * with five arguments: (objectValue, sourceValue, key, object, source). + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @param {Function} [customizer] The function to customize assigned values. + * @param {*} [thisArg] The `this` binding of `customizer`. + * @returns {Object} Returns `object`. + * @example + * + * var users = { + * 'data': [{ 'user': 'barney' }, { 'user': 'fred' }] + * }; + * + * var ages = { + * 'data': [{ 'age': 36 }, { 'age': 40 }] + * }; + * + * _.merge(users, ages); + * // => { 'data': [{ 'user': 'barney', 'age': 36 }, { 'user': 'fred', 'age': 40 }] } + * + * // using a customizer callback + * var object = { + * 'fruits': ['apple'], + * 'vegetables': ['beet'] + * }; + * + * var other = { + * 'fruits': ['banana'], + * 'vegetables': ['carrot'] + * }; + * + * _.merge(object, other, function(a, b) { + * if (_.isArray(a)) { + * return a.concat(b); + * } + * }); + * // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot'] } + */ + var merge = createAssigner(baseMerge); + + /** + * Assigns own enumerable properties of source object(s) to the destination + * object. Subsequent sources overwrite property assignments of previous sources. + * If `customizer` is provided it is invoked to produce the assigned values. + * The `customizer` is bound to `thisArg` and invoked with five arguments: + * (objectValue, sourceValue, key, object, source). + * + * **Note:** This method mutates `object` and is based on + * [`Object.assign`](http://ecma-international.org/ecma-262/6.0/#sec-object.assign). + * + * @static + * @memberOf _ + * @alias extend + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @param {Function} [customizer] The function to customize assigned values. + * @param {*} [thisArg] The `this` binding of `customizer`. + * @returns {Object} Returns `object`. + * @example + * + * _.assign({ 'user': 'barney' }, { 'age': 40 }, { 'user': 'fred' }); + * // => { 'user': 'fred', 'age': 40 } + * + * // using a customizer callback + * var defaults = _.partialRight(_.assign, function(value, other) { + * return _.isUndefined(value) ? other : value; + * }); + * + * defaults({ 'user': 'barney' }, { 'age': 36 }, { 'user': 'fred' }); + * // => { 'user': 'barney', 'age': 36 } + */ + var assign = createAssigner(function(object, source, customizer) { + return customizer + ? assignWith(object, source, customizer) + : baseAssign(object, source); + }); + + /** + * Creates an object that inherits from the given `prototype` object. If a + * `properties` object is provided its own enumerable properties are assigned + * to the created object. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} prototype The object to inherit from. + * @param {Object} [properties] The properties to assign to the object. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {Object} Returns the new object. + * @example + * + * function Shape() { + * this.x = 0; + * this.y = 0; + * } + * + * function Circle() { + * Shape.call(this); + * } + * + * Circle.prototype = _.create(Shape.prototype, { + * 'constructor': Circle + * }); + * + * var circle = new Circle; + * circle instanceof Circle; + * // => true + * + * circle instanceof Shape; + * // => true + */ + function create(prototype, properties, guard) { + var result = baseCreate(prototype); + if (guard && isIterateeCall(prototype, properties, guard)) { + properties = undefined; + } + return properties ? baseAssign(result, properties) : result; + } + + /** + * Assigns own enumerable properties of source object(s) to the destination + * object for all destination properties that resolve to `undefined`. Once a + * property is set, additional values of the same property are ignored. + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @returns {Object} Returns `object`. + * @example + * + * _.defaults({ 'user': 'barney' }, { 'age': 36 }, { 'user': 'fred' }); + * // => { 'user': 'barney', 'age': 36 } + */ + var defaults = createDefaults(assign, assignDefaults); + + /** + * This method is like `_.defaults` except that it recursively assigns + * default properties. + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @returns {Object} Returns `object`. + * @example + * + * _.defaultsDeep({ 'user': { 'name': 'barney' } }, { 'user': { 'name': 'fred', 'age': 36 } }); + * // => { 'user': { 'name': 'barney', 'age': 36 } } + * + */ + var defaultsDeep = createDefaults(merge, mergeDefaults); + + /** + * This method is like `_.find` except that it returns the key of the first + * element `predicate` returns truthy for instead of the element itself. + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to search. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {string|undefined} Returns the key of the matched element, else `undefined`. + * @example + * + * var users = { + * 'barney': { 'age': 36, 'active': true }, + * 'fred': { 'age': 40, 'active': false }, + * 'pebbles': { 'age': 1, 'active': true } + * }; + * + * _.findKey(users, function(chr) { + * return chr.age < 40; + * }); + * // => 'barney' (iteration order is not guaranteed) + * + * // using the `_.matches` callback shorthand + * _.findKey(users, { 'age': 1, 'active': true }); + * // => 'pebbles' + * + * // using the `_.matchesProperty` callback shorthand + * _.findKey(users, 'active', false); + * // => 'fred' + * + * // using the `_.property` callback shorthand + * _.findKey(users, 'active'); + * // => 'barney' + */ + var findKey = createFindKey(baseForOwn); + + /** + * This method is like `_.findKey` except that it iterates over elements of + * a collection in the opposite order. + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to search. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {string|undefined} Returns the key of the matched element, else `undefined`. + * @example + * + * var users = { + * 'barney': { 'age': 36, 'active': true }, + * 'fred': { 'age': 40, 'active': false }, + * 'pebbles': { 'age': 1, 'active': true } + * }; + * + * _.findLastKey(users, function(chr) { + * return chr.age < 40; + * }); + * // => returns `pebbles` assuming `_.findKey` returns `barney` + * + * // using the `_.matches` callback shorthand + * _.findLastKey(users, { 'age': 36, 'active': true }); + * // => 'barney' + * + * // using the `_.matchesProperty` callback shorthand + * _.findLastKey(users, 'active', false); + * // => 'fred' + * + * // using the `_.property` callback shorthand + * _.findLastKey(users, 'active'); + * // => 'pebbles' + */ + var findLastKey = createFindKey(baseForOwnRight); + + /** + * Iterates over own and inherited enumerable properties of an object invoking + * `iteratee` for each property. The `iteratee` is bound to `thisArg` and invoked + * with three arguments: (value, key, object). Iteratee functions may exit + * iteration early by explicitly returning `false`. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Object} Returns `object`. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.forIn(new Foo, function(value, key) { + * console.log(key); + * }); + * // => logs 'a', 'b', and 'c' (iteration order is not guaranteed) + */ + var forIn = createForIn(baseFor); + + /** + * This method is like `_.forIn` except that it iterates over properties of + * `object` in the opposite order. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Object} Returns `object`. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.forInRight(new Foo, function(value, key) { + * console.log(key); + * }); + * // => logs 'c', 'b', and 'a' assuming `_.forIn ` logs 'a', 'b', and 'c' + */ + var forInRight = createForIn(baseForRight); + + /** + * Iterates over own enumerable properties of an object invoking `iteratee` + * for each property. The `iteratee` is bound to `thisArg` and invoked with + * three arguments: (value, key, object). Iteratee functions may exit iteration + * early by explicitly returning `false`. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Object} Returns `object`. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.forOwn(new Foo, function(value, key) { + * console.log(key); + * }); + * // => logs 'a' and 'b' (iteration order is not guaranteed) + */ + var forOwn = createForOwn(baseForOwn); + + /** + * This method is like `_.forOwn` except that it iterates over properties of + * `object` in the opposite order. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Object} Returns `object`. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.forOwnRight(new Foo, function(value, key) { + * console.log(key); + * }); + * // => logs 'b' and 'a' assuming `_.forOwn` logs 'a' and 'b' + */ + var forOwnRight = createForOwn(baseForOwnRight); + + /** + * Creates an array of function property names from all enumerable properties, + * own and inherited, of `object`. + * + * @static + * @memberOf _ + * @alias methods + * @category Object + * @param {Object} object The object to inspect. + * @returns {Array} Returns the new array of property names. + * @example + * + * _.functions(_); + * // => ['after', 'ary', 'assign', ...] + */ + function functions(object) { + return baseFunctions(object, keysIn(object)); + } + + /** + * Gets the property value at `path` of `object`. If the resolved value is + * `undefined` the `defaultValue` is used in its place. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path of the property to get. + * @param {*} [defaultValue] The value returned if the resolved value is `undefined`. + * @returns {*} Returns the resolved value. + * @example + * + * var object = { 'a': [{ 'b': { 'c': 3 } }] }; + * + * _.get(object, 'a[0].b.c'); + * // => 3 + * + * _.get(object, ['a', '0', 'b', 'c']); + * // => 3 + * + * _.get(object, 'a.b.c', 'default'); + * // => 'default' + */ + function get(object, path, defaultValue) { + var result = object == null ? undefined : baseGet(object, toPath(path), path + ''); + return result === undefined ? defaultValue : result; + } + + /** + * Checks if `path` is a direct property. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path to check. + * @returns {boolean} Returns `true` if `path` is a direct property, else `false`. + * @example + * + * var object = { 'a': { 'b': { 'c': 3 } } }; + * + * _.has(object, 'a'); + * // => true + * + * _.has(object, 'a.b.c'); + * // => true + * + * _.has(object, ['a', 'b', 'c']); + * // => true + */ + function has(object, path) { + if (object == null) { + return false; + } + var result = hasOwnProperty.call(object, path); + if (!result && !isKey(path)) { + path = toPath(path); + object = path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1)); + if (object == null) { + return false; + } + path = last(path); + result = hasOwnProperty.call(object, path); + } + return result || (isLength(object.length) && isIndex(path, object.length) && + (isArray(object) || isArguments(object))); + } + + /** + * Creates an object composed of the inverted keys and values of `object`. + * If `object` contains duplicate values, subsequent values overwrite property + * assignments of previous values unless `multiValue` is `true`. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to invert. + * @param {boolean} [multiValue] Allow multiple values per key. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {Object} Returns the new inverted object. + * @example + * + * var object = { 'a': 1, 'b': 2, 'c': 1 }; + * + * _.invert(object); + * // => { '1': 'c', '2': 'b' } + * + * // with `multiValue` + * _.invert(object, true); + * // => { '1': ['a', 'c'], '2': ['b'] } + */ + function invert(object, multiValue, guard) { + if (guard && isIterateeCall(object, multiValue, guard)) { + multiValue = undefined; + } + var index = -1, + props = keys(object), + length = props.length, + result = {}; + + while (++index < length) { + var key = props[index], + value = object[key]; + + if (multiValue) { + if (hasOwnProperty.call(result, value)) { + result[value].push(key); + } else { + result[value] = [key]; + } + } + else { + result[value] = key; + } + } + return result; + } + + /** + * Creates an array of the own enumerable property names of `object`. + * + * **Note:** Non-object values are coerced to objects. See the + * [ES spec](http://ecma-international.org/ecma-262/6.0/#sec-object.keys) + * for more details. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.keys(new Foo); + * // => ['a', 'b'] (iteration order is not guaranteed) + * + * _.keys('hi'); + * // => ['0', '1'] + */ + var keys = !nativeKeys ? shimKeys : function(object) { + var Ctor = object == null ? undefined : object.constructor; + if ((typeof Ctor == 'function' && Ctor.prototype === object) || + (typeof object != 'function' && isArrayLike(object))) { + return shimKeys(object); + } + return isObject(object) ? nativeKeys(object) : []; + }; + + /** + * Creates an array of the own and inherited enumerable property names of `object`. + * + * **Note:** Non-object values are coerced to objects. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.keysIn(new Foo); + * // => ['a', 'b', 'c'] (iteration order is not guaranteed) + */ + function keysIn(object) { + if (object == null) { + return []; + } + if (!isObject(object)) { + object = Object(object); + } + var length = object.length; + length = (length && isLength(length) && + (isArray(object) || isArguments(object)) && length) || 0; + + var Ctor = object.constructor, + index = -1, + isProto = typeof Ctor == 'function' && Ctor.prototype === object, + result = Array(length), + skipIndexes = length > 0; + + while (++index < length) { + result[index] = (index + ''); + } + for (var key in object) { + if (!(skipIndexes && isIndex(key, length)) && + !(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) { + result.push(key); + } + } + return result; + } + + /** + * The opposite of `_.mapValues`; this method creates an object with the + * same values as `object` and keys generated by running each own enumerable + * property of `object` through `iteratee`. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function|Object|string} [iteratee=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Object} Returns the new mapped object. + * @example + * + * _.mapKeys({ 'a': 1, 'b': 2 }, function(value, key) { + * return key + value; + * }); + * // => { 'a1': 1, 'b2': 2 } + */ + var mapKeys = createObjectMapper(true); + + /** + * Creates an object with the same keys as `object` and values generated by + * running each own enumerable property of `object` through `iteratee`. The + * iteratee function is bound to `thisArg` and invoked with three arguments: + * (value, key, object). + * + * If a property name is provided for `iteratee` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `iteratee` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function|Object|string} [iteratee=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Object} Returns the new mapped object. + * @example + * + * _.mapValues({ 'a': 1, 'b': 2 }, function(n) { + * return n * 3; + * }); + * // => { 'a': 3, 'b': 6 } + * + * var users = { + * 'fred': { 'user': 'fred', 'age': 40 }, + * 'pebbles': { 'user': 'pebbles', 'age': 1 } + * }; + * + * // using the `_.property` callback shorthand + * _.mapValues(users, 'age'); + * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed) + */ + var mapValues = createObjectMapper(); + + /** + * The opposite of `_.pick`; this method creates an object composed of the + * own and inherited enumerable properties of `object` that are not omitted. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The source object. + * @param {Function|...(string|string[])} [predicate] The function invoked per + * iteration or property names to omit, specified as individual property + * names or arrays of property names. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {Object} Returns the new object. + * @example + * + * var object = { 'user': 'fred', 'age': 40 }; + * + * _.omit(object, 'age'); + * // => { 'user': 'fred' } + * + * _.omit(object, _.isNumber); + * // => { 'user': 'fred' } + */ + var omit = restParam(function(object, props) { + if (object == null) { + return {}; + } + if (typeof props[0] != 'function') { + var props = arrayMap(baseFlatten(props), String); + return pickByArray(object, baseDifference(keysIn(object), props)); + } + var predicate = bindCallback(props[0], props[1], 3); + return pickByCallback(object, function(value, key, object) { + return !predicate(value, key, object); + }); + }); + + /** + * Creates a two dimensional array of the key-value pairs for `object`, + * e.g. `[[key1, value1], [key2, value2]]`. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the new array of key-value pairs. + * @example + * + * _.pairs({ 'barney': 36, 'fred': 40 }); + * // => [['barney', 36], ['fred', 40]] (iteration order is not guaranteed) + */ + function pairs(object) { + object = toObject(object); + + var index = -1, + props = keys(object), + length = props.length, + result = Array(length); + + while (++index < length) { + var key = props[index]; + result[index] = [key, object[key]]; + } + return result; + } + + /** + * Creates an object composed of the picked `object` properties. Property + * names may be specified as individual arguments or as arrays of property + * names. If `predicate` is provided it is invoked for each property of `object` + * picking the properties `predicate` returns truthy for. The predicate is + * bound to `thisArg` and invoked with three arguments: (value, key, object). + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The source object. + * @param {Function|...(string|string[])} [predicate] The function invoked per + * iteration or property names to pick, specified as individual property + * names or arrays of property names. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {Object} Returns the new object. + * @example + * + * var object = { 'user': 'fred', 'age': 40 }; + * + * _.pick(object, 'user'); + * // => { 'user': 'fred' } + * + * _.pick(object, _.isString); + * // => { 'user': 'fred' } + */ + var pick = restParam(function(object, props) { + if (object == null) { + return {}; + } + return typeof props[0] == 'function' + ? pickByCallback(object, bindCallback(props[0], props[1], 3)) + : pickByArray(object, baseFlatten(props)); + }); + + /** + * This method is like `_.get` except that if the resolved value is a function + * it is invoked with the `this` binding of its parent object and its result + * is returned. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path of the property to resolve. + * @param {*} [defaultValue] The value returned if the resolved value is `undefined`. + * @returns {*} Returns the resolved value. + * @example + * + * var object = { 'a': [{ 'b': { 'c1': 3, 'c2': _.constant(4) } }] }; + * + * _.result(object, 'a[0].b.c1'); + * // => 3 + * + * _.result(object, 'a[0].b.c2'); + * // => 4 + * + * _.result(object, 'a.b.c', 'default'); + * // => 'default' + * + * _.result(object, 'a.b.c', _.constant('default')); + * // => 'default' + */ + function result(object, path, defaultValue) { + var result = object == null ? undefined : object[path]; + if (result === undefined) { + if (object != null && !isKey(path, object)) { + path = toPath(path); + object = path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1)); + result = object == null ? undefined : object[last(path)]; + } + result = result === undefined ? defaultValue : result; + } + return isFunction(result) ? result.call(object) : result; + } + + /** + * Sets the property value of `path` on `object`. If a portion of `path` + * does not exist it is created. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to augment. + * @param {Array|string} path The path of the property to set. + * @param {*} value The value to set. + * @returns {Object} Returns `object`. + * @example + * + * var object = { 'a': [{ 'b': { 'c': 3 } }] }; + * + * _.set(object, 'a[0].b.c', 4); + * console.log(object.a[0].b.c); + * // => 4 + * + * _.set(object, 'x[0].y.z', 5); + * console.log(object.x[0].y.z); + * // => 5 + */ + function set(object, path, value) { + if (object == null) { + return object; + } + var pathKey = (path + ''); + path = (object[pathKey] != null || isKey(path, object)) ? [pathKey] : toPath(path); + + var index = -1, + length = path.length, + lastIndex = length - 1, + nested = object; + + while (nested != null && ++index < length) { + var key = path[index]; + if (isObject(nested)) { + if (index == lastIndex) { + nested[key] = value; + } else if (nested[key] == null) { + nested[key] = isIndex(path[index + 1]) ? [] : {}; + } + } + nested = nested[key]; + } + return object; + } + + /** + * An alternative to `_.reduce`; this method transforms `object` to a new + * `accumulator` object which is the result of running each of its own enumerable + * properties through `iteratee`, with each invocation potentially mutating + * the `accumulator` object. The `iteratee` is bound to `thisArg` and invoked + * with four arguments: (accumulator, value, key, object). Iteratee functions + * may exit iteration early by explicitly returning `false`. + * + * @static + * @memberOf _ + * @category Object + * @param {Array|Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {*} [accumulator] The custom accumulator value. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {*} Returns the accumulated value. + * @example + * + * _.transform([2, 3, 4], function(result, n) { + * result.push(n *= n); + * return n % 2 == 0; + * }); + * // => [4, 9] + * + * _.transform({ 'a': 1, 'b': 2 }, function(result, n, key) { + * result[key] = n * 3; + * }); + * // => { 'a': 3, 'b': 6 } + */ + function transform(object, iteratee, accumulator, thisArg) { + var isArr = isArray(object) || isTypedArray(object); + iteratee = getCallback(iteratee, thisArg, 4); + + if (accumulator == null) { + if (isArr || isObject(object)) { + var Ctor = object.constructor; + if (isArr) { + accumulator = isArray(object) ? new Ctor : []; + } else { + accumulator = baseCreate(isFunction(Ctor) ? Ctor.prototype : undefined); + } + } else { + accumulator = {}; + } + } + (isArr ? arrayEach : baseForOwn)(object, function(value, index, object) { + return iteratee(accumulator, value, index, object); + }); + return accumulator; + } + + /** + * Creates an array of the own enumerable property values of `object`. + * + * **Note:** Non-object values are coerced to objects. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property values. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.values(new Foo); + * // => [1, 2] (iteration order is not guaranteed) + * + * _.values('hi'); + * // => ['h', 'i'] + */ + function values(object) { + return baseValues(object, keys(object)); + } + + /** + * Creates an array of the own and inherited enumerable property values + * of `object`. + * + * **Note:** Non-object values are coerced to objects. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property values. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.valuesIn(new Foo); + * // => [1, 2, 3] (iteration order is not guaranteed) + */ + function valuesIn(object) { + return baseValues(object, keysIn(object)); + } + + /*------------------------------------------------------------------------*/ + + /** + * Checks if `n` is between `start` and up to but not including, `end`. If + * `end` is not specified it is set to `start` with `start` then set to `0`. + * + * @static + * @memberOf _ + * @category Number + * @param {number} n The number to check. + * @param {number} [start=0] The start of the range. + * @param {number} end The end of the range. + * @returns {boolean} Returns `true` if `n` is in the range, else `false`. + * @example + * + * _.inRange(3, 2, 4); + * // => true + * + * _.inRange(4, 8); + * // => true + * + * _.inRange(4, 2); + * // => false + * + * _.inRange(2, 2); + * // => false + * + * _.inRange(1.2, 2); + * // => true + * + * _.inRange(5.2, 4); + * // => false + */ + function inRange(value, start, end) { + start = +start || 0; + if (end === undefined) { + end = start; + start = 0; + } else { + end = +end || 0; + } + return value >= nativeMin(start, end) && value < nativeMax(start, end); + } + + /** + * Produces a random number between `min` and `max` (inclusive). If only one + * argument is provided a number between `0` and the given number is returned. + * If `floating` is `true`, or either `min` or `max` are floats, a floating-point + * number is returned instead of an integer. + * + * @static + * @memberOf _ + * @category Number + * @param {number} [min=0] The minimum possible value. + * @param {number} [max=1] The maximum possible value. + * @param {boolean} [floating] Specify returning a floating-point number. + * @returns {number} Returns the random number. + * @example + * + * _.random(0, 5); + * // => an integer between 0 and 5 + * + * _.random(5); + * // => also an integer between 0 and 5 + * + * _.random(5, true); + * // => a floating-point number between 0 and 5 + * + * _.random(1.2, 5.2); + * // => a floating-point number between 1.2 and 5.2 + */ + function random(min, max, floating) { + if (floating && isIterateeCall(min, max, floating)) { + max = floating = undefined; + } + var noMin = min == null, + noMax = max == null; + + if (floating == null) { + if (noMax && typeof min == 'boolean') { + floating = min; + min = 1; + } + else if (typeof max == 'boolean') { + floating = max; + noMax = true; + } + } + if (noMin && noMax) { + max = 1; + noMax = false; + } + min = +min || 0; + if (noMax) { + max = min; + min = 0; + } else { + max = +max || 0; + } + if (floating || min % 1 || max % 1) { + var rand = nativeRandom(); + return nativeMin(min + (rand * (max - min + parseFloat('1e-' + ((rand + '').length - 1)))), max); + } + return baseRandom(min, max); + } + + /*------------------------------------------------------------------------*/ + + /** + * Converts `string` to [camel case](https://en.wikipedia.org/wiki/CamelCase). + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the camel cased string. + * @example + * + * _.camelCase('Foo Bar'); + * // => 'fooBar' + * + * _.camelCase('--foo-bar'); + * // => 'fooBar' + * + * _.camelCase('__foo_bar__'); + * // => 'fooBar' + */ + var camelCase = createCompounder(function(result, word, index) { + word = word.toLowerCase(); + return result + (index ? (word.charAt(0).toUpperCase() + word.slice(1)) : word); + }); + + /** + * Capitalizes the first character of `string`. + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to capitalize. + * @returns {string} Returns the capitalized string. + * @example + * + * _.capitalize('fred'); + * // => 'Fred' + */ + function capitalize(string) { + string = baseToString(string); + return string && (string.charAt(0).toUpperCase() + string.slice(1)); + } + + /** + * Deburrs `string` by converting [latin-1 supplementary letters](https://en.wikipedia.org/wiki/Latin-1_Supplement_(Unicode_block)#Character_table) + * to basic latin letters and removing [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks). + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to deburr. + * @returns {string} Returns the deburred string. + * @example + * + * _.deburr('déjà vu'); + * // => 'deja vu' + */ + function deburr(string) { + string = baseToString(string); + return string && string.replace(reLatin1, deburrLetter).replace(reComboMark, ''); + } + + /** + * Checks if `string` ends with the given target string. + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to search. + * @param {string} [target] The string to search for. + * @param {number} [position=string.length] The position to search from. + * @returns {boolean} Returns `true` if `string` ends with `target`, else `false`. + * @example + * + * _.endsWith('abc', 'c'); + * // => true + * + * _.endsWith('abc', 'b'); + * // => false + * + * _.endsWith('abc', 'b', 2); + * // => true + */ + function endsWith(string, target, position) { + string = baseToString(string); + target = (target + ''); + + var length = string.length; + position = position === undefined + ? length + : nativeMin(position < 0 ? 0 : (+position || 0), length); + + position -= target.length; + return position >= 0 && string.indexOf(target, position) == position; + } + + /** + * Converts the characters "&", "<", ">", '"', "'", and "\`", in `string` to + * their corresponding HTML entities. + * + * **Note:** No other characters are escaped. To escape additional characters + * use a third-party library like [_he_](https://mths.be/he). + * + * Though the ">" character is escaped for symmetry, characters like + * ">" and "/" don't need escaping in HTML and have no special meaning + * unless they're part of a tag or unquoted attribute value. + * See [Mathias Bynens's article](https://mathiasbynens.be/notes/ambiguous-ampersands) + * (under "semi-related fun fact") for more details. + * + * Backticks are escaped because in Internet Explorer < 9, they can break out + * of attribute values or HTML comments. See [#59](https://html5sec.org/#59), + * [#102](https://html5sec.org/#102), [#108](https://html5sec.org/#108), and + * [#133](https://html5sec.org/#133) of the [HTML5 Security Cheatsheet](https://html5sec.org/) + * for more details. + * + * When working with HTML you should always [quote attribute values](http://wonko.com/post/html-escaping) + * to reduce XSS vectors. + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to escape. + * @returns {string} Returns the escaped string. + * @example + * + * _.escape('fred, barney, & pebbles'); + * // => 'fred, barney, & pebbles' + */ + function escape(string) { + // Reset `lastIndex` because in IE < 9 `String#replace` does not. + string = baseToString(string); + return (string && reHasUnescapedHtml.test(string)) + ? string.replace(reUnescapedHtml, escapeHtmlChar) + : string; + } + + /** + * Escapes the `RegExp` special characters "\", "/", "^", "$", ".", "|", "?", + * "*", "+", "(", ")", "[", "]", "{" and "}" in `string`. + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to escape. + * @returns {string} Returns the escaped string. + * @example + * + * _.escapeRegExp('[lodash](https://lodash.com/)'); + * // => '\[lodash\]\(https:\/\/lodash\.com\/\)' + */ + function escapeRegExp(string) { + string = baseToString(string); + return (string && reHasRegExpChars.test(string)) + ? string.replace(reRegExpChars, escapeRegExpChar) + : (string || '(?:)'); + } + + /** + * Converts `string` to [kebab case](https://en.wikipedia.org/wiki/Letter_case#Special_case_styles). + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the kebab cased string. + * @example + * + * _.kebabCase('Foo Bar'); + * // => 'foo-bar' + * + * _.kebabCase('fooBar'); + * // => 'foo-bar' + * + * _.kebabCase('__foo_bar__'); + * // => 'foo-bar' + */ + var kebabCase = createCompounder(function(result, word, index) { + return result + (index ? '-' : '') + word.toLowerCase(); + }); + + /** + * Pads `string` on the left and right sides if it's shorter than `length`. + * Padding characters are truncated if they can't be evenly divided by `length`. + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to pad. + * @param {number} [length=0] The padding length. + * @param {string} [chars=' '] The string used as padding. + * @returns {string} Returns the padded string. + * @example + * + * _.pad('abc', 8); + * // => ' abc ' + * + * _.pad('abc', 8, '_-'); + * // => '_-abc_-_' + * + * _.pad('abc', 3); + * // => 'abc' + */ + function pad(string, length, chars) { + string = baseToString(string); + length = +length; + + var strLength = string.length; + if (strLength >= length || !nativeIsFinite(length)) { + return string; + } + var mid = (length - strLength) / 2, + leftLength = nativeFloor(mid), + rightLength = nativeCeil(mid); + + chars = createPadding('', rightLength, chars); + return chars.slice(0, leftLength) + string + chars; + } + + /** + * Pads `string` on the left side if it's shorter than `length`. Padding + * characters are truncated if they exceed `length`. + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to pad. + * @param {number} [length=0] The padding length. + * @param {string} [chars=' '] The string used as padding. + * @returns {string} Returns the padded string. + * @example + * + * _.padLeft('abc', 6); + * // => ' abc' + * + * _.padLeft('abc', 6, '_-'); + * // => '_-_abc' + * + * _.padLeft('abc', 3); + * // => 'abc' + */ + var padLeft = createPadDir(); + + /** + * Pads `string` on the right side if it's shorter than `length`. Padding + * characters are truncated if they exceed `length`. + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to pad. + * @param {number} [length=0] The padding length. + * @param {string} [chars=' '] The string used as padding. + * @returns {string} Returns the padded string. + * @example + * + * _.padRight('abc', 6); + * // => 'abc ' + * + * _.padRight('abc', 6, '_-'); + * // => 'abc_-_' + * + * _.padRight('abc', 3); + * // => 'abc' + */ + var padRight = createPadDir(true); + + /** + * Converts `string` to an integer of the specified radix. If `radix` is + * `undefined` or `0`, a `radix` of `10` is used unless `value` is a hexadecimal, + * in which case a `radix` of `16` is used. + * + * **Note:** This method aligns with the [ES5 implementation](https://es5.github.io/#E) + * of `parseInt`. + * + * @static + * @memberOf _ + * @category String + * @param {string} string The string to convert. + * @param {number} [radix] The radix to interpret `value` by. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {number} Returns the converted integer. + * @example + * + * _.parseInt('08'); + * // => 8 + * + * _.map(['6', '08', '10'], _.parseInt); + * // => [6, 8, 10] + */ + function parseInt(string, radix, guard) { + // Firefox < 21 and Opera < 15 follow ES3 for `parseInt`. + // Chrome fails to trim leading <BOM> whitespace characters. + // See https://code.google.com/p/v8/issues/detail?id=3109 for more details. + if (guard ? isIterateeCall(string, radix, guard) : radix == null) { + radix = 0; + } else if (radix) { + radix = +radix; + } + string = trim(string); + return nativeParseInt(string, radix || (reHasHexPrefix.test(string) ? 16 : 10)); + } + + /** + * Repeats the given string `n` times. + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to repeat. + * @param {number} [n=0] The number of times to repeat the string. + * @returns {string} Returns the repeated string. + * @example + * + * _.repeat('*', 3); + * // => '***' + * + * _.repeat('abc', 2); + * // => 'abcabc' + * + * _.repeat('abc', 0); + * // => '' + */ + function repeat(string, n) { + var result = ''; + string = baseToString(string); + n = +n; + if (n < 1 || !string || !nativeIsFinite(n)) { + return result; + } + // Leverage the exponentiation by squaring algorithm for a faster repeat. + // See https://en.wikipedia.org/wiki/Exponentiation_by_squaring for more details. + do { + if (n % 2) { + result += string; + } + n = nativeFloor(n / 2); + string += string; + } while (n); + + return result; + } + + /** + * Converts `string` to [snake case](https://en.wikipedia.org/wiki/Snake_case). + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the snake cased string. + * @example + * + * _.snakeCase('Foo Bar'); + * // => 'foo_bar' + * + * _.snakeCase('fooBar'); + * // => 'foo_bar' + * + * _.snakeCase('--foo-bar'); + * // => 'foo_bar' + */ + var snakeCase = createCompounder(function(result, word, index) { + return result + (index ? '_' : '') + word.toLowerCase(); + }); + + /** + * Converts `string` to [start case](https://en.wikipedia.org/wiki/Letter_case#Stylistic_or_specialised_usage). + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the start cased string. + * @example + * + * _.startCase('--foo-bar'); + * // => 'Foo Bar' + * + * _.startCase('fooBar'); + * // => 'Foo Bar' + * + * _.startCase('__foo_bar__'); + * // => 'Foo Bar' + */ + var startCase = createCompounder(function(result, word, index) { + return result + (index ? ' ' : '') + (word.charAt(0).toUpperCase() + word.slice(1)); + }); + + /** + * Checks if `string` starts with the given target string. + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to search. + * @param {string} [target] The string to search for. + * @param {number} [position=0] The position to search from. + * @returns {boolean} Returns `true` if `string` starts with `target`, else `false`. + * @example + * + * _.startsWith('abc', 'a'); + * // => true + * + * _.startsWith('abc', 'b'); + * // => false + * + * _.startsWith('abc', 'b', 1); + * // => true + */ + function startsWith(string, target, position) { + string = baseToString(string); + position = position == null + ? 0 + : nativeMin(position < 0 ? 0 : (+position || 0), string.length); + + return string.lastIndexOf(target, position) == position; + } + + /** + * Creates a compiled template function that can interpolate data properties + * in "interpolate" delimiters, HTML-escape interpolated data properties in + * "escape" delimiters, and execute JavaScript in "evaluate" delimiters. Data + * properties may be accessed as free variables in the template. If a setting + * object is provided it takes precedence over `_.templateSettings` values. + * + * **Note:** In the development build `_.template` utilizes + * [sourceURLs](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl) + * for easier debugging. + * + * For more information on precompiling templates see + * [lodash's custom builds documentation](https://lodash.com/custom-builds). + * + * For more information on Chrome extension sandboxes see + * [Chrome's extensions documentation](https://developer.chrome.com/extensions/sandboxingEval). + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The template string. + * @param {Object} [options] The options object. + * @param {RegExp} [options.escape] The HTML "escape" delimiter. + * @param {RegExp} [options.evaluate] The "evaluate" delimiter. + * @param {Object} [options.imports] An object to import into the template as free variables. + * @param {RegExp} [options.interpolate] The "interpolate" delimiter. + * @param {string} [options.sourceURL] The sourceURL of the template's compiled source. + * @param {string} [options.variable] The data object variable name. + * @param- {Object} [otherOptions] Enables the legacy `options` param signature. + * @returns {Function} Returns the compiled template function. + * @example + * + * // using the "interpolate" delimiter to create a compiled template + * var compiled = _.template('hello <%= user %>!'); + * compiled({ 'user': 'fred' }); + * // => 'hello fred!' + * + * // using the HTML "escape" delimiter to escape data property values + * var compiled = _.template('<b><%- value %></b>'); + * compiled({ 'value': '<script>' }); + * // => '<b><script></b>' + * + * // using the "evaluate" delimiter to execute JavaScript and generate HTML + * var compiled = _.template('<% _.forEach(users, function(user) { %><li><%- user %></li><% }); %>'); + * compiled({ 'users': ['fred', 'barney'] }); + * // => '<li>fred</li><li>barney</li>' + * + * // using the internal `print` function in "evaluate" delimiters + * var compiled = _.template('<% print("hello " + user); %>!'); + * compiled({ 'user': 'barney' }); + * // => 'hello barney!' + * + * // using the ES delimiter as an alternative to the default "interpolate" delimiter + * var compiled = _.template('hello ${ user }!'); + * compiled({ 'user': 'pebbles' }); + * // => 'hello pebbles!' + * + * // using custom template delimiters + * _.templateSettings.interpolate = /{{([\s\S]+?)}}/g; + * var compiled = _.template('hello {{ user }}!'); + * compiled({ 'user': 'mustache' }); + * // => 'hello mustache!' + * + * // using backslashes to treat delimiters as plain text + * var compiled = _.template('<%= "\\<%- value %\\>" %>'); + * compiled({ 'value': 'ignored' }); + * // => '<%- value %>' + * + * // using the `imports` option to import `jQuery` as `jq` + * var text = '<% jq.each(users, function(user) { %><li><%- user %></li><% }); %>'; + * var compiled = _.template(text, { 'imports': { 'jq': jQuery } }); + * compiled({ 'users': ['fred', 'barney'] }); + * // => '<li>fred</li><li>barney</li>' + * + * // using the `sourceURL` option to specify a custom sourceURL for the template + * var compiled = _.template('hello <%= user %>!', { 'sourceURL': '/basic/greeting.jst' }); + * compiled(data); + * // => find the source of "greeting.jst" under the Sources tab or Resources panel of the web inspector + * + * // using the `variable` option to ensure a with-statement isn't used in the compiled template + * var compiled = _.template('hi <%= data.user %>!', { 'variable': 'data' }); + * compiled.source; + * // => function(data) { + * // var __t, __p = ''; + * // __p += 'hi ' + ((__t = ( data.user )) == null ? '' : __t) + '!'; + * // return __p; + * // } + * + * // using the `source` property to inline compiled templates for meaningful + * // line numbers in error messages and a stack trace + * fs.writeFileSync(path.join(cwd, 'jst.js'), '\ + * var JST = {\ + * "main": ' + _.template(mainText).source + '\ + * };\ + * '); + */ + function template(string, options, otherOptions) { + // Based on John Resig's `tmpl` implementation (http://ejohn.org/blog/javascript-micro-templating/) + // and Laura Doktorova's doT.js (https://github.com/olado/doT). + var settings = lodash.templateSettings; + + if (otherOptions && isIterateeCall(string, options, otherOptions)) { + options = otherOptions = undefined; + } + string = baseToString(string); + options = assignWith(baseAssign({}, otherOptions || options), settings, assignOwnDefaults); + + var imports = assignWith(baseAssign({}, options.imports), settings.imports, assignOwnDefaults), + importsKeys = keys(imports), + importsValues = baseValues(imports, importsKeys); + + var isEscaping, + isEvaluating, + index = 0, + interpolate = options.interpolate || reNoMatch, + source = "__p += '"; + + // Compile the regexp to match each delimiter. + var reDelimiters = RegExp( + (options.escape || reNoMatch).source + '|' + + interpolate.source + '|' + + (interpolate === reInterpolate ? reEsTemplate : reNoMatch).source + '|' + + (options.evaluate || reNoMatch).source + '|$' + , 'g'); + + // Use a sourceURL for easier debugging. + var sourceURL = '//# sourceURL=' + + ('sourceURL' in options + ? options.sourceURL + : ('lodash.templateSources[' + (++templateCounter) + ']') + ) + '\n'; + + string.replace(reDelimiters, function(match, escapeValue, interpolateValue, esTemplateValue, evaluateValue, offset) { + interpolateValue || (interpolateValue = esTemplateValue); + + // Escape characters that can't be included in string literals. + source += string.slice(index, offset).replace(reUnescapedString, escapeStringChar); + + // Replace delimiters with snippets. + if (escapeValue) { + isEscaping = true; + source += "' +\n__e(" + escapeValue + ") +\n'"; + } + if (evaluateValue) { + isEvaluating = true; + source += "';\n" + evaluateValue + ";\n__p += '"; + } + if (interpolateValue) { + source += "' +\n((__t = (" + interpolateValue + ")) == null ? '' : __t) +\n'"; + } + index = offset + match.length; + + // The JS engine embedded in Adobe products requires returning the `match` + // string in order to produce the correct `offset` value. + return match; + }); + + source += "';\n"; + + // If `variable` is not specified wrap a with-statement around the generated + // code to add the data object to the top of the scope chain. + var variable = options.variable; + if (!variable) { + source = 'with (obj) {\n' + source + '\n}\n'; + } + // Cleanup code by stripping empty strings. + source = (isEvaluating ? source.replace(reEmptyStringLeading, '') : source) + .replace(reEmptyStringMiddle, '$1') + .replace(reEmptyStringTrailing, '$1;'); + + // Frame code as the function body. + source = 'function(' + (variable || 'obj') + ') {\n' + + (variable + ? '' + : 'obj || (obj = {});\n' + ) + + "var __t, __p = ''" + + (isEscaping + ? ', __e = _.escape' + : '' + ) + + (isEvaluating + ? ', __j = Array.prototype.join;\n' + + "function print() { __p += __j.call(arguments, '') }\n" + : ';\n' + ) + + source + + 'return __p\n}'; + + var result = attempt(function() { + return Function(importsKeys, sourceURL + 'return ' + source).apply(undefined, importsValues); + }); + + // Provide the compiled function's source by its `toString` method or + // the `source` property as a convenience for inlining compiled templates. + result.source = source; + if (isError(result)) { + throw result; + } + return result; + } + + /** + * Removes leading and trailing whitespace or specified characters from `string`. + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to trim. + * @param {string} [chars=whitespace] The characters to trim. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {string} Returns the trimmed string. + * @example + * + * _.trim(' abc '); + * // => 'abc' + * + * _.trim('-_-abc-_-', '_-'); + * // => 'abc' + * + * _.map([' foo ', ' bar '], _.trim); + * // => ['foo', 'bar'] + */ + function trim(string, chars, guard) { + var value = string; + string = baseToString(string); + if (!string) { + return string; + } + if (guard ? isIterateeCall(value, chars, guard) : chars == null) { + return string.slice(trimmedLeftIndex(string), trimmedRightIndex(string) + 1); + } + chars = (chars + ''); + return string.slice(charsLeftIndex(string, chars), charsRightIndex(string, chars) + 1); + } + + /** + * Removes leading whitespace or specified characters from `string`. + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to trim. + * @param {string} [chars=whitespace] The characters to trim. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {string} Returns the trimmed string. + * @example + * + * _.trimLeft(' abc '); + * // => 'abc ' + * + * _.trimLeft('-_-abc-_-', '_-'); + * // => 'abc-_-' + */ + function trimLeft(string, chars, guard) { + var value = string; + string = baseToString(string); + if (!string) { + return string; + } + if (guard ? isIterateeCall(value, chars, guard) : chars == null) { + return string.slice(trimmedLeftIndex(string)); + } + return string.slice(charsLeftIndex(string, (chars + ''))); + } + + /** + * Removes trailing whitespace or specified characters from `string`. + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to trim. + * @param {string} [chars=whitespace] The characters to trim. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {string} Returns the trimmed string. + * @example + * + * _.trimRight(' abc '); + * // => ' abc' + * + * _.trimRight('-_-abc-_-', '_-'); + * // => '-_-abc' + */ + function trimRight(string, chars, guard) { + var value = string; + string = baseToString(string); + if (!string) { + return string; + } + if (guard ? isIterateeCall(value, chars, guard) : chars == null) { + return string.slice(0, trimmedRightIndex(string) + 1); + } + return string.slice(0, charsRightIndex(string, (chars + '')) + 1); + } + + /** + * Truncates `string` if it's longer than the given maximum string length. + * The last characters of the truncated string are replaced with the omission + * string which defaults to "...". + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to truncate. + * @param {Object|number} [options] The options object or maximum string length. + * @param {number} [options.length=30] The maximum string length. + * @param {string} [options.omission='...'] The string to indicate text is omitted. + * @param {RegExp|string} [options.separator] The separator pattern to truncate to. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {string} Returns the truncated string. + * @example + * + * _.trunc('hi-diddly-ho there, neighborino'); + * // => 'hi-diddly-ho there, neighbo...' + * + * _.trunc('hi-diddly-ho there, neighborino', 24); + * // => 'hi-diddly-ho there, n...' + * + * _.trunc('hi-diddly-ho there, neighborino', { + * 'length': 24, + * 'separator': ' ' + * }); + * // => 'hi-diddly-ho there,...' + * + * _.trunc('hi-diddly-ho there, neighborino', { + * 'length': 24, + * 'separator': /,? +/ + * }); + * // => 'hi-diddly-ho there...' + * + * _.trunc('hi-diddly-ho there, neighborino', { + * 'omission': ' [...]' + * }); + * // => 'hi-diddly-ho there, neig [...]' + */ + function trunc(string, options, guard) { + if (guard && isIterateeCall(string, options, guard)) { + options = undefined; + } + var length = DEFAULT_TRUNC_LENGTH, + omission = DEFAULT_TRUNC_OMISSION; + + if (options != null) { + if (isObject(options)) { + var separator = 'separator' in options ? options.separator : separator; + length = 'length' in options ? (+options.length || 0) : length; + omission = 'omission' in options ? baseToString(options.omission) : omission; + } else { + length = +options || 0; + } + } + string = baseToString(string); + if (length >= string.length) { + return string; + } + var end = length - omission.length; + if (end < 1) { + return omission; + } + var result = string.slice(0, end); + if (separator == null) { + return result + omission; + } + if (isRegExp(separator)) { + if (string.slice(end).search(separator)) { + var match, + newEnd, + substring = string.slice(0, end); + + if (!separator.global) { + separator = RegExp(separator.source, (reFlags.exec(separator) || '') + 'g'); + } + separator.lastIndex = 0; + while ((match = separator.exec(substring))) { + newEnd = match.index; + } + result = result.slice(0, newEnd == null ? end : newEnd); + } + } else if (string.indexOf(separator, end) != end) { + var index = result.lastIndexOf(separator); + if (index > -1) { + result = result.slice(0, index); + } + } + return result + omission; + } + + /** + * The inverse of `_.escape`; this method converts the HTML entities + * `&`, `<`, `>`, `"`, `'`, and ``` in `string` to their + * corresponding characters. + * + * **Note:** No other HTML entities are unescaped. To unescape additional HTML + * entities use a third-party library like [_he_](https://mths.be/he). + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to unescape. + * @returns {string} Returns the unescaped string. + * @example + * + * _.unescape('fred, barney, & pebbles'); + * // => 'fred, barney, & pebbles' + */ + function unescape(string) { + string = baseToString(string); + return (string && reHasEscapedHtml.test(string)) + ? string.replace(reEscapedHtml, unescapeHtmlChar) + : string; + } + + /** + * Splits `string` into an array of its words. + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to inspect. + * @param {RegExp|string} [pattern] The pattern to match words. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {Array} Returns the words of `string`. + * @example + * + * _.words('fred, barney, & pebbles'); + * // => ['fred', 'barney', 'pebbles'] + * + * _.words('fred, barney, & pebbles', /[^, ]+/g); + * // => ['fred', 'barney', '&', 'pebbles'] + */ + function words(string, pattern, guard) { + if (guard && isIterateeCall(string, pattern, guard)) { + pattern = undefined; + } + string = baseToString(string); + return string.match(pattern || reWords) || []; + } + + /*------------------------------------------------------------------------*/ + + /** + * Attempts to invoke `func`, returning either the result or the caught error + * object. Any additional arguments are provided to `func` when it is invoked. + * + * @static + * @memberOf _ + * @category Utility + * @param {Function} func The function to attempt. + * @returns {*} Returns the `func` result or error object. + * @example + * + * // avoid throwing errors for invalid selectors + * var elements = _.attempt(function(selector) { + * return document.querySelectorAll(selector); + * }, '>_>'); + * + * if (_.isError(elements)) { + * elements = []; + * } + */ + var attempt = restParam(function(func, args) { + try { + return func.apply(undefined, args); + } catch(e) { + return isError(e) ? e : new Error(e); + } + }); + + /** + * Creates a function that invokes `func` with the `this` binding of `thisArg` + * and arguments of the created function. If `func` is a property name the + * created callback returns the property value for a given element. If `func` + * is an object the created callback returns `true` for elements that contain + * the equivalent object properties, otherwise it returns `false`. + * + * @static + * @memberOf _ + * @alias iteratee + * @category Utility + * @param {*} [func=_.identity] The value to convert to a callback. + * @param {*} [thisArg] The `this` binding of `func`. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {Function} Returns the callback. + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36 }, + * { 'user': 'fred', 'age': 40 } + * ]; + * + * // wrap to create custom callback shorthands + * _.callback = _.wrap(_.callback, function(callback, func, thisArg) { + * var match = /^(.+?)__([gl]t)(.+)$/.exec(func); + * if (!match) { + * return callback(func, thisArg); + * } + * return function(object) { + * return match[2] == 'gt' + * ? object[match[1]] > match[3] + * : object[match[1]] < match[3]; + * }; + * }); + * + * _.filter(users, 'age__gt36'); + * // => [{ 'user': 'fred', 'age': 40 }] + */ + function callback(func, thisArg, guard) { + if (guard && isIterateeCall(func, thisArg, guard)) { + thisArg = undefined; + } + return isObjectLike(func) + ? matches(func) + : baseCallback(func, thisArg); + } + + /** + * Creates a function that returns `value`. + * + * @static + * @memberOf _ + * @category Utility + * @param {*} value The value to return from the new function. + * @returns {Function} Returns the new function. + * @example + * + * var object = { 'user': 'fred' }; + * var getter = _.constant(object); + * + * getter() === object; + * // => true + */ + function constant(value) { + return function() { + return value; + }; + } + + /** + * This method returns the first argument provided to it. + * + * @static + * @memberOf _ + * @category Utility + * @param {*} value Any value. + * @returns {*} Returns `value`. + * @example + * + * var object = { 'user': 'fred' }; + * + * _.identity(object) === object; + * // => true + */ + function identity(value) { + return value; + } + + /** + * Creates a function that performs a deep comparison between a given object + * and `source`, returning `true` if the given object has equivalent property + * values, else `false`. + * + * **Note:** This method supports comparing arrays, booleans, `Date` objects, + * numbers, `Object` objects, regexes, and strings. Objects are compared by + * their own, not inherited, enumerable properties. For comparing a single + * own or inherited property value see `_.matchesProperty`. + * + * @static + * @memberOf _ + * @category Utility + * @param {Object} source The object of property values to match. + * @returns {Function} Returns the new function. + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': true }, + * { 'user': 'fred', 'age': 40, 'active': false } + * ]; + * + * _.filter(users, _.matches({ 'age': 40, 'active': false })); + * // => [{ 'user': 'fred', 'age': 40, 'active': false }] + */ + function matches(source) { + return baseMatches(baseClone(source, true)); + } + + /** + * Creates a function that compares the property value of `path` on a given + * object to `value`. + * + * **Note:** This method supports comparing arrays, booleans, `Date` objects, + * numbers, `Object` objects, regexes, and strings. Objects are compared by + * their own, not inherited, enumerable properties. + * + * @static + * @memberOf _ + * @category Utility + * @param {Array|string} path The path of the property to get. + * @param {*} srcValue The value to match. + * @returns {Function} Returns the new function. + * @example + * + * var users = [ + * { 'user': 'barney' }, + * { 'user': 'fred' } + * ]; + * + * _.find(users, _.matchesProperty('user', 'fred')); + * // => { 'user': 'fred' } + */ + function matchesProperty(path, srcValue) { + return baseMatchesProperty(path, baseClone(srcValue, true)); + } + + /** + * Creates a function that invokes the method at `path` on a given object. + * Any additional arguments are provided to the invoked method. + * + * @static + * @memberOf _ + * @category Utility + * @param {Array|string} path The path of the method to invoke. + * @param {...*} [args] The arguments to invoke the method with. + * @returns {Function} Returns the new function. + * @example + * + * var objects = [ + * { 'a': { 'b': { 'c': _.constant(2) } } }, + * { 'a': { 'b': { 'c': _.constant(1) } } } + * ]; + * + * _.map(objects, _.method('a.b.c')); + * // => [2, 1] + * + * _.invoke(_.sortBy(objects, _.method(['a', 'b', 'c'])), 'a.b.c'); + * // => [1, 2] + */ + var method = restParam(function(path, args) { + return function(object) { + return invokePath(object, path, args); + }; + }); + + /** + * The opposite of `_.method`; this method creates a function that invokes + * the method at a given path on `object`. Any additional arguments are + * provided to the invoked method. + * + * @static + * @memberOf _ + * @category Utility + * @param {Object} object The object to query. + * @param {...*} [args] The arguments to invoke the method with. + * @returns {Function} Returns the new function. + * @example + * + * var array = _.times(3, _.constant), + * object = { 'a': array, 'b': array, 'c': array }; + * + * _.map(['a[2]', 'c[0]'], _.methodOf(object)); + * // => [2, 0] + * + * _.map([['a', '2'], ['c', '0']], _.methodOf(object)); + * // => [2, 0] + */ + var methodOf = restParam(function(object, args) { + return function(path) { + return invokePath(object, path, args); + }; + }); + + /** + * Adds all own enumerable function properties of a source object to the + * destination object. If `object` is a function then methods are added to + * its prototype as well. + * + * **Note:** Use `_.runInContext` to create a pristine `lodash` function to + * avoid conflicts caused by modifying the original. + * + * @static + * @memberOf _ + * @category Utility + * @param {Function|Object} [object=lodash] The destination object. + * @param {Object} source The object of functions to add. + * @param {Object} [options] The options object. + * @param {boolean} [options.chain=true] Specify whether the functions added + * are chainable. + * @returns {Function|Object} Returns `object`. + * @example + * + * function vowels(string) { + * return _.filter(string, function(v) { + * return /[aeiou]/i.test(v); + * }); + * } + * + * _.mixin({ 'vowels': vowels }); + * _.vowels('fred'); + * // => ['e'] + * + * _('fred').vowels().value(); + * // => ['e'] + * + * _.mixin({ 'vowels': vowels }, { 'chain': false }); + * _('fred').vowels(); + * // => ['e'] + */ + function mixin(object, source, options) { + if (options == null) { + var isObj = isObject(source), + props = isObj ? keys(source) : undefined, + methodNames = (props && props.length) ? baseFunctions(source, props) : undefined; + + if (!(methodNames ? methodNames.length : isObj)) { + methodNames = false; + options = source; + source = object; + object = this; + } + } + if (!methodNames) { + methodNames = baseFunctions(source, keys(source)); + } + var chain = true, + index = -1, + isFunc = isFunction(object), + length = methodNames.length; + + if (options === false) { + chain = false; + } else if (isObject(options) && 'chain' in options) { + chain = options.chain; + } + while (++index < length) { + var methodName = methodNames[index], + func = source[methodName]; + + object[methodName] = func; + if (isFunc) { + object.prototype[methodName] = (function(func) { + return function() { + var chainAll = this.__chain__; + if (chain || chainAll) { + var result = object(this.__wrapped__), + actions = result.__actions__ = arrayCopy(this.__actions__); + + actions.push({ 'func': func, 'args': arguments, 'thisArg': object }); + result.__chain__ = chainAll; + return result; + } + return func.apply(object, arrayPush([this.value()], arguments)); + }; + }(func)); + } + } + return object; + } + + /** + * Reverts the `_` variable to its previous value and returns a reference to + * the `lodash` function. + * + * @static + * @memberOf _ + * @category Utility + * @returns {Function} Returns the `lodash` function. + * @example + * + * var lodash = _.noConflict(); + */ + function noConflict() { + root._ = oldDash; + return this; + } + + /** + * A no-operation function that returns `undefined` regardless of the + * arguments it receives. + * + * @static + * @memberOf _ + * @category Utility + * @example + * + * var object = { 'user': 'fred' }; + * + * _.noop(object) === undefined; + * // => true + */ + function noop() { + // No operation performed. + } + + /** + * Creates a function that returns the property value at `path` on a + * given object. + * + * @static + * @memberOf _ + * @category Utility + * @param {Array|string} path The path of the property to get. + * @returns {Function} Returns the new function. + * @example + * + * var objects = [ + * { 'a': { 'b': { 'c': 2 } } }, + * { 'a': { 'b': { 'c': 1 } } } + * ]; + * + * _.map(objects, _.property('a.b.c')); + * // => [2, 1] + * + * _.pluck(_.sortBy(objects, _.property(['a', 'b', 'c'])), 'a.b.c'); + * // => [1, 2] + */ + function property(path) { + return isKey(path) ? baseProperty(path) : basePropertyDeep(path); + } + + /** + * The opposite of `_.property`; this method creates a function that returns + * the property value at a given path on `object`. + * + * @static + * @memberOf _ + * @category Utility + * @param {Object} object The object to query. + * @returns {Function} Returns the new function. + * @example + * + * var array = [0, 1, 2], + * object = { 'a': array, 'b': array, 'c': array }; + * + * _.map(['a[2]', 'c[0]'], _.propertyOf(object)); + * // => [2, 0] + * + * _.map([['a', '2'], ['c', '0']], _.propertyOf(object)); + * // => [2, 0] + */ + function propertyOf(object) { + return function(path) { + return baseGet(object, toPath(path), path + ''); + }; + } + + /** + * Creates an array of numbers (positive and/or negative) progressing from + * `start` up to, but not including, `end`. If `end` is not specified it is + * set to `start` with `start` then set to `0`. If `end` is less than `start` + * a zero-length range is created unless a negative `step` is specified. + * + * @static + * @memberOf _ + * @category Utility + * @param {number} [start=0] The start of the range. + * @param {number} end The end of the range. + * @param {number} [step=1] The value to increment or decrement by. + * @returns {Array} Returns the new array of numbers. + * @example + * + * _.range(4); + * // => [0, 1, 2, 3] + * + * _.range(1, 5); + * // => [1, 2, 3, 4] + * + * _.range(0, 20, 5); + * // => [0, 5, 10, 15] + * + * _.range(0, -4, -1); + * // => [0, -1, -2, -3] + * + * _.range(1, 4, 0); + * // => [1, 1, 1] + * + * _.range(0); + * // => [] + */ + function range(start, end, step) { + if (step && isIterateeCall(start, end, step)) { + end = step = undefined; + } + start = +start || 0; + step = step == null ? 1 : (+step || 0); + + if (end == null) { + end = start; + start = 0; + } else { + end = +end || 0; + } + // Use `Array(length)` so engines like Chakra and V8 avoid slower modes. + // See https://youtu.be/XAqIpGU8ZZk#t=17m25s for more details. + var index = -1, + length = nativeMax(nativeCeil((end - start) / (step || 1)), 0), + result = Array(length); + + while (++index < length) { + result[index] = start; + start += step; + } + return result; + } + + /** + * Invokes the iteratee function `n` times, returning an array of the results + * of each invocation. The `iteratee` is bound to `thisArg` and invoked with + * one argument; (index). + * + * @static + * @memberOf _ + * @category Utility + * @param {number} n The number of times to invoke `iteratee`. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Array} Returns the array of results. + * @example + * + * var diceRolls = _.times(3, _.partial(_.random, 1, 6, false)); + * // => [3, 6, 4] + * + * _.times(3, function(n) { + * mage.castSpell(n); + * }); + * // => invokes `mage.castSpell(n)` three times with `n` of `0`, `1`, and `2` + * + * _.times(3, function(n) { + * this.cast(n); + * }, mage); + * // => also invokes `mage.castSpell(n)` three times + */ + function times(n, iteratee, thisArg) { + n = nativeFloor(n); + + // Exit early to avoid a JSC JIT bug in Safari 8 + // where `Array(0)` is treated as `Array(1)`. + if (n < 1 || !nativeIsFinite(n)) { + return []; + } + var index = -1, + result = Array(nativeMin(n, MAX_ARRAY_LENGTH)); + + iteratee = bindCallback(iteratee, thisArg, 1); + while (++index < n) { + if (index < MAX_ARRAY_LENGTH) { + result[index] = iteratee(index); + } else { + iteratee(index); + } + } + return result; + } + + /** + * Generates a unique ID. If `prefix` is provided the ID is appended to it. + * + * @static + * @memberOf _ + * @category Utility + * @param {string} [prefix] The value to prefix the ID with. + * @returns {string} Returns the unique ID. + * @example + * + * _.uniqueId('contact_'); + * // => 'contact_104' + * + * _.uniqueId(); + * // => '105' + */ + function uniqueId(prefix) { + var id = ++idCounter; + return baseToString(prefix) + id; + } + + /*------------------------------------------------------------------------*/ + + /** + * Adds two numbers. + * + * @static + * @memberOf _ + * @category Math + * @param {number} augend The first number to add. + * @param {number} addend The second number to add. + * @returns {number} Returns the sum. + * @example + * + * _.add(6, 4); + * // => 10 + */ + function add(augend, addend) { + return (+augend || 0) + (+addend || 0); + } + + /** + * Calculates `n` rounded up to `precision`. + * + * @static + * @memberOf _ + * @category Math + * @param {number} n The number to round up. + * @param {number} [precision=0] The precision to round up to. + * @returns {number} Returns the rounded up number. + * @example + * + * _.ceil(4.006); + * // => 5 + * + * _.ceil(6.004, 2); + * // => 6.01 + * + * _.ceil(6040, -2); + * // => 6100 + */ + var ceil = createRound('ceil'); + + /** + * Calculates `n` rounded down to `precision`. + * + * @static + * @memberOf _ + * @category Math + * @param {number} n The number to round down. + * @param {number} [precision=0] The precision to round down to. + * @returns {number} Returns the rounded down number. + * @example + * + * _.floor(4.006); + * // => 4 + * + * _.floor(0.046, 2); + * // => 0.04 + * + * _.floor(4060, -2); + * // => 4000 + */ + var floor = createRound('floor'); + + /** + * Gets the maximum value of `collection`. If `collection` is empty or falsey + * `-Infinity` is returned. If an iteratee function is provided it is invoked + * for each value in `collection` to generate the criterion by which the value + * is ranked. The `iteratee` is bound to `thisArg` and invoked with three + * arguments: (value, index, collection). + * + * If a property name is provided for `iteratee` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `iteratee` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Math + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [iteratee] The function invoked per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {*} Returns the maximum value. + * @example + * + * _.max([4, 2, 8, 6]); + * // => 8 + * + * _.max([]); + * // => -Infinity + * + * var users = [ + * { 'user': 'barney', 'age': 36 }, + * { 'user': 'fred', 'age': 40 } + * ]; + * + * _.max(users, function(chr) { + * return chr.age; + * }); + * // => { 'user': 'fred', 'age': 40 } + * + * // using the `_.property` callback shorthand + * _.max(users, 'age'); + * // => { 'user': 'fred', 'age': 40 } + */ + var max = createExtremum(gt, NEGATIVE_INFINITY); + + /** + * Gets the minimum value of `collection`. If `collection` is empty or falsey + * `Infinity` is returned. If an iteratee function is provided it is invoked + * for each value in `collection` to generate the criterion by which the value + * is ranked. The `iteratee` is bound to `thisArg` and invoked with three + * arguments: (value, index, collection). + * + * If a property name is provided for `iteratee` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `iteratee` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Math + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [iteratee] The function invoked per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {*} Returns the minimum value. + * @example + * + * _.min([4, 2, 8, 6]); + * // => 2 + * + * _.min([]); + * // => Infinity + * + * var users = [ + * { 'user': 'barney', 'age': 36 }, + * { 'user': 'fred', 'age': 40 } + * ]; + * + * _.min(users, function(chr) { + * return chr.age; + * }); + * // => { 'user': 'barney', 'age': 36 } + * + * // using the `_.property` callback shorthand + * _.min(users, 'age'); + * // => { 'user': 'barney', 'age': 36 } + */ + var min = createExtremum(lt, POSITIVE_INFINITY); + + /** + * Calculates `n` rounded to `precision`. + * + * @static + * @memberOf _ + * @category Math + * @param {number} n The number to round. + * @param {number} [precision=0] The precision to round to. + * @returns {number} Returns the rounded number. + * @example + * + * _.round(4.006); + * // => 4 + * + * _.round(4.006, 2); + * // => 4.01 + * + * _.round(4060, -2); + * // => 4100 + */ + var round = createRound('round'); + + /** + * Gets the sum of the values in `collection`. + * + * @static + * @memberOf _ + * @category Math + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [iteratee] The function invoked per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {number} Returns the sum. + * @example + * + * _.sum([4, 6]); + * // => 10 + * + * _.sum({ 'a': 4, 'b': 6 }); + * // => 10 + * + * var objects = [ + * { 'n': 4 }, + * { 'n': 6 } + * ]; + * + * _.sum(objects, function(object) { + * return object.n; + * }); + * // => 10 + * + * // using the `_.property` callback shorthand + * _.sum(objects, 'n'); + * // => 10 + */ + function sum(collection, iteratee, thisArg) { + if (thisArg && isIterateeCall(collection, iteratee, thisArg)) { + iteratee = undefined; + } + iteratee = getCallback(iteratee, thisArg, 3); + return iteratee.length == 1 + ? arraySum(isArray(collection) ? collection : toIterable(collection), iteratee) + : baseSum(collection, iteratee); + } + + /*------------------------------------------------------------------------*/ + + // Ensure wrappers are instances of `baseLodash`. + lodash.prototype = baseLodash.prototype; + + LodashWrapper.prototype = baseCreate(baseLodash.prototype); + LodashWrapper.prototype.constructor = LodashWrapper; + + LazyWrapper.prototype = baseCreate(baseLodash.prototype); + LazyWrapper.prototype.constructor = LazyWrapper; + + // Add functions to the `Map` cache. + MapCache.prototype['delete'] = mapDelete; + MapCache.prototype.get = mapGet; + MapCache.prototype.has = mapHas; + MapCache.prototype.set = mapSet; + + // Add functions to the `Set` cache. + SetCache.prototype.push = cachePush; + + // Assign cache to `_.memoize`. + memoize.Cache = MapCache; + + // Add functions that return wrapped values when chaining. + lodash.after = after; + lodash.ary = ary; + lodash.assign = assign; + lodash.at = at; + lodash.before = before; + lodash.bind = bind; + lodash.bindAll = bindAll; + lodash.bindKey = bindKey; + lodash.callback = callback; + lodash.chain = chain; + lodash.chunk = chunk; + lodash.compact = compact; + lodash.constant = constant; + lodash.countBy = countBy; + lodash.create = create; + lodash.curry = curry; + lodash.curryRight = curryRight; + lodash.debounce = debounce; + lodash.defaults = defaults; + lodash.defaultsDeep = defaultsDeep; + lodash.defer = defer; + lodash.delay = delay; + lodash.difference = difference; + lodash.drop = drop; + lodash.dropRight = dropRight; + lodash.dropRightWhile = dropRightWhile; + lodash.dropWhile = dropWhile; + lodash.fill = fill; + lodash.filter = filter; + lodash.flatten = flatten; + lodash.flattenDeep = flattenDeep; + lodash.flow = flow; + lodash.flowRight = flowRight; + lodash.forEach = forEach; + lodash.forEachRight = forEachRight; + lodash.forIn = forIn; + lodash.forInRight = forInRight; + lodash.forOwn = forOwn; + lodash.forOwnRight = forOwnRight; + lodash.functions = functions; + lodash.groupBy = groupBy; + lodash.indexBy = indexBy; + lodash.initial = initial; + lodash.intersection = intersection; + lodash.invert = invert; + lodash.invoke = invoke; + lodash.keys = keys; + lodash.keysIn = keysIn; + lodash.map = map; + lodash.mapKeys = mapKeys; + lodash.mapValues = mapValues; + lodash.matches = matches; + lodash.matchesProperty = matchesProperty; + lodash.memoize = memoize; + lodash.merge = merge; + lodash.method = method; + lodash.methodOf = methodOf; + lodash.mixin = mixin; + lodash.modArgs = modArgs; + lodash.negate = negate; + lodash.omit = omit; + lodash.once = once; + lodash.pairs = pairs; + lodash.partial = partial; + lodash.partialRight = partialRight; + lodash.partition = partition; + lodash.pick = pick; + lodash.pluck = pluck; + lodash.property = property; + lodash.propertyOf = propertyOf; + lodash.pull = pull; + lodash.pullAt = pullAt; + lodash.range = range; + lodash.rearg = rearg; + lodash.reject = reject; + lodash.remove = remove; + lodash.rest = rest; + lodash.restParam = restParam; + lodash.set = set; + lodash.shuffle = shuffle; + lodash.slice = slice; + lodash.sortBy = sortBy; + lodash.sortByAll = sortByAll; + lodash.sortByOrder = sortByOrder; + lodash.spread = spread; + lodash.take = take; + lodash.takeRight = takeRight; + lodash.takeRightWhile = takeRightWhile; + lodash.takeWhile = takeWhile; + lodash.tap = tap; + lodash.throttle = throttle; + lodash.thru = thru; + lodash.times = times; + lodash.toArray = toArray; + lodash.toPlainObject = toPlainObject; + lodash.transform = transform; + lodash.union = union; + lodash.uniq = uniq; + lodash.unzip = unzip; + lodash.unzipWith = unzipWith; + lodash.values = values; + lodash.valuesIn = valuesIn; + lodash.where = where; + lodash.without = without; + lodash.wrap = wrap; + lodash.xor = xor; + lodash.zip = zip; + lodash.zipObject = zipObject; + lodash.zipWith = zipWith; + + // Add aliases. + lodash.backflow = flowRight; + lodash.collect = map; + lodash.compose = flowRight; + lodash.each = forEach; + lodash.eachRight = forEachRight; + lodash.extend = assign; + lodash.iteratee = callback; + lodash.methods = functions; + lodash.object = zipObject; + lodash.select = filter; + lodash.tail = rest; + lodash.unique = uniq; + + // Add functions to `lodash.prototype`. + mixin(lodash, lodash); + + /*------------------------------------------------------------------------*/ + + // Add functions that return unwrapped values when chaining. + lodash.add = add; + lodash.attempt = attempt; + lodash.camelCase = camelCase; + lodash.capitalize = capitalize; + lodash.ceil = ceil; + lodash.clone = clone; + lodash.cloneDeep = cloneDeep; + lodash.deburr = deburr; + lodash.endsWith = endsWith; + lodash.escape = escape; + lodash.escapeRegExp = escapeRegExp; + lodash.every = every; + lodash.find = find; + lodash.findIndex = findIndex; + lodash.findKey = findKey; + lodash.findLast = findLast; + lodash.findLastIndex = findLastIndex; + lodash.findLastKey = findLastKey; + lodash.findWhere = findWhere; + lodash.first = first; + lodash.floor = floor; + lodash.get = get; + lodash.gt = gt; + lodash.gte = gte; + lodash.has = has; + lodash.identity = identity; + lodash.includes = includes; + lodash.indexOf = indexOf; + lodash.inRange = inRange; + lodash.isArguments = isArguments; + lodash.isArray = isArray; + lodash.isBoolean = isBoolean; + lodash.isDate = isDate; + lodash.isElement = isElement; + lodash.isEmpty = isEmpty; + lodash.isEqual = isEqual; + lodash.isError = isError; + lodash.isFinite = isFinite; + lodash.isFunction = isFunction; + lodash.isMatch = isMatch; + lodash.isNaN = isNaN; + lodash.isNative = isNative; + lodash.isNull = isNull; + lodash.isNumber = isNumber; + lodash.isObject = isObject; + lodash.isPlainObject = isPlainObject; + lodash.isRegExp = isRegExp; + lodash.isString = isString; + lodash.isTypedArray = isTypedArray; + lodash.isUndefined = isUndefined; + lodash.kebabCase = kebabCase; + lodash.last = last; + lodash.lastIndexOf = lastIndexOf; + lodash.lt = lt; + lodash.lte = lte; + lodash.max = max; + lodash.min = min; + lodash.noConflict = noConflict; + lodash.noop = noop; + lodash.now = now; + lodash.pad = pad; + lodash.padLeft = padLeft; + lodash.padRight = padRight; + lodash.parseInt = parseInt; + lodash.random = random; + lodash.reduce = reduce; + lodash.reduceRight = reduceRight; + lodash.repeat = repeat; + lodash.result = result; + lodash.round = round; + lodash.runInContext = runInContext; + lodash.size = size; + lodash.snakeCase = snakeCase; + lodash.some = some; + lodash.sortedIndex = sortedIndex; + lodash.sortedLastIndex = sortedLastIndex; + lodash.startCase = startCase; + lodash.startsWith = startsWith; + lodash.sum = sum; + lodash.template = template; + lodash.trim = trim; + lodash.trimLeft = trimLeft; + lodash.trimRight = trimRight; + lodash.trunc = trunc; + lodash.unescape = unescape; + lodash.uniqueId = uniqueId; + lodash.words = words; + + // Add aliases. + lodash.all = every; + lodash.any = some; + lodash.contains = includes; + lodash.eq = isEqual; + lodash.detect = find; + lodash.foldl = reduce; + lodash.foldr = reduceRight; + lodash.head = first; + lodash.include = includes; + lodash.inject = reduce; + + mixin(lodash, (function() { + var source = {}; + baseForOwn(lodash, function(func, methodName) { + if (!lodash.prototype[methodName]) { + source[methodName] = func; + } + }); + return source; + }()), false); + + /*------------------------------------------------------------------------*/ + + // Add functions capable of returning wrapped and unwrapped values when chaining. + lodash.sample = sample; + + lodash.prototype.sample = function(n) { + if (!this.__chain__ && n == null) { + return sample(this.value()); + } + return this.thru(function(value) { + return sample(value, n); + }); + }; + + /*------------------------------------------------------------------------*/ + + /** + * The semantic version number. + * + * @static + * @memberOf _ + * @type string + */ + lodash.VERSION = VERSION; + + // Assign default placeholders. + arrayEach(['bind', 'bindKey', 'curry', 'curryRight', 'partial', 'partialRight'], function(methodName) { + lodash[methodName].placeholder = lodash; + }); + + // Add `LazyWrapper` methods for `_.drop` and `_.take` variants. + arrayEach(['drop', 'take'], function(methodName, index) { + LazyWrapper.prototype[methodName] = function(n) { + var filtered = this.__filtered__; + if (filtered && !index) { + return new LazyWrapper(this); + } + n = n == null ? 1 : nativeMax(nativeFloor(n) || 0, 0); + + var result = this.clone(); + if (filtered) { + result.__takeCount__ = nativeMin(result.__takeCount__, n); + } else { + result.__views__.push({ 'size': n, 'type': methodName + (result.__dir__ < 0 ? 'Right' : '') }); + } + return result; + }; + + LazyWrapper.prototype[methodName + 'Right'] = function(n) { + return this.reverse()[methodName](n).reverse(); + }; + }); + + // Add `LazyWrapper` methods that accept an `iteratee` value. + arrayEach(['filter', 'map', 'takeWhile'], function(methodName, index) { + var type = index + 1, + isFilter = type != LAZY_MAP_FLAG; + + LazyWrapper.prototype[methodName] = function(iteratee, thisArg) { + var result = this.clone(); + result.__iteratees__.push({ 'iteratee': getCallback(iteratee, thisArg, 1), 'type': type }); + result.__filtered__ = result.__filtered__ || isFilter; + return result; + }; + }); + + // Add `LazyWrapper` methods for `_.first` and `_.last`. + arrayEach(['first', 'last'], function(methodName, index) { + var takeName = 'take' + (index ? 'Right' : ''); + + LazyWrapper.prototype[methodName] = function() { + return this[takeName](1).value()[0]; + }; + }); + + // Add `LazyWrapper` methods for `_.initial` and `_.rest`. + arrayEach(['initial', 'rest'], function(methodName, index) { + var dropName = 'drop' + (index ? '' : 'Right'); + + LazyWrapper.prototype[methodName] = function() { + return this.__filtered__ ? new LazyWrapper(this) : this[dropName](1); + }; + }); + + // Add `LazyWrapper` methods for `_.pluck` and `_.where`. + arrayEach(['pluck', 'where'], function(methodName, index) { + var operationName = index ? 'filter' : 'map', + createCallback = index ? baseMatches : property; + + LazyWrapper.prototype[methodName] = function(value) { + return this[operationName](createCallback(value)); + }; + }); + + LazyWrapper.prototype.compact = function() { + return this.filter(identity); + }; + + LazyWrapper.prototype.reject = function(predicate, thisArg) { + predicate = getCallback(predicate, thisArg, 1); + return this.filter(function(value) { + return !predicate(value); + }); + }; + + LazyWrapper.prototype.slice = function(start, end) { + start = start == null ? 0 : (+start || 0); + + var result = this; + if (result.__filtered__ && (start > 0 || end < 0)) { + return new LazyWrapper(result); + } + if (start < 0) { + result = result.takeRight(-start); + } else if (start) { + result = result.drop(start); + } + if (end !== undefined) { + end = (+end || 0); + result = end < 0 ? result.dropRight(-end) : result.take(end - start); + } + return result; + }; + + LazyWrapper.prototype.takeRightWhile = function(predicate, thisArg) { + return this.reverse().takeWhile(predicate, thisArg).reverse(); + }; + + LazyWrapper.prototype.toArray = function() { + return this.take(POSITIVE_INFINITY); + }; + + // Add `LazyWrapper` methods to `lodash.prototype`. + baseForOwn(LazyWrapper.prototype, function(func, methodName) { + var checkIteratee = /^(?:filter|map|reject)|While$/.test(methodName), + retUnwrapped = /^(?:first|last)$/.test(methodName), + lodashFunc = lodash[retUnwrapped ? ('take' + (methodName == 'last' ? 'Right' : '')) : methodName]; + + if (!lodashFunc) { + return; + } + lodash.prototype[methodName] = function() { + var args = retUnwrapped ? [1] : arguments, + chainAll = this.__chain__, + value = this.__wrapped__, + isHybrid = !!this.__actions__.length, + isLazy = value instanceof LazyWrapper, + iteratee = args[0], + useLazy = isLazy || isArray(value); + + if (useLazy && checkIteratee && typeof iteratee == 'function' && iteratee.length != 1) { + // Avoid lazy use if the iteratee has a "length" value other than `1`. + isLazy = useLazy = false; + } + var interceptor = function(value) { + return (retUnwrapped && chainAll) + ? lodashFunc(value, 1)[0] + : lodashFunc.apply(undefined, arrayPush([value], args)); + }; + + var action = { 'func': thru, 'args': [interceptor], 'thisArg': undefined }, + onlyLazy = isLazy && !isHybrid; + + if (retUnwrapped && !chainAll) { + if (onlyLazy) { + value = value.clone(); + value.__actions__.push(action); + return func.call(value); + } + return lodashFunc.call(undefined, this.value())[0]; + } + if (!retUnwrapped && useLazy) { + value = onlyLazy ? value : new LazyWrapper(this); + var result = func.apply(value, args); + result.__actions__.push(action); + return new LodashWrapper(result, chainAll); + } + return this.thru(interceptor); + }; + }); + + // Add `Array` and `String` methods to `lodash.prototype`. + arrayEach(['join', 'pop', 'push', 'replace', 'shift', 'sort', 'splice', 'split', 'unshift'], function(methodName) { + var func = (/^(?:replace|split)$/.test(methodName) ? stringProto : arrayProto)[methodName], + chainName = /^(?:push|sort|unshift)$/.test(methodName) ? 'tap' : 'thru', + retUnwrapped = /^(?:join|pop|replace|shift)$/.test(methodName); + + lodash.prototype[methodName] = function() { + var args = arguments; + if (retUnwrapped && !this.__chain__) { + return func.apply(this.value(), args); + } + return this[chainName](function(value) { + return func.apply(value, args); + }); + }; + }); + + // Map minified function names to their real names. + baseForOwn(LazyWrapper.prototype, function(func, methodName) { + var lodashFunc = lodash[methodName]; + if (lodashFunc) { + var key = lodashFunc.name, + names = realNames[key] || (realNames[key] = []); + + names.push({ 'name': methodName, 'func': lodashFunc }); + } + }); + + realNames[createHybridWrapper(undefined, BIND_KEY_FLAG).name] = [{ 'name': 'wrapper', 'func': undefined }]; + + // Add functions to the lazy wrapper. + LazyWrapper.prototype.clone = lazyClone; + LazyWrapper.prototype.reverse = lazyReverse; + LazyWrapper.prototype.value = lazyValue; + + // Add chaining functions to the `lodash` wrapper. + lodash.prototype.chain = wrapperChain; + lodash.prototype.commit = wrapperCommit; + lodash.prototype.concat = wrapperConcat; + lodash.prototype.plant = wrapperPlant; + lodash.prototype.reverse = wrapperReverse; + lodash.prototype.toString = wrapperToString; + lodash.prototype.run = lodash.prototype.toJSON = lodash.prototype.valueOf = lodash.prototype.value = wrapperValue; + + // Add function aliases to the `lodash` wrapper. + lodash.prototype.collect = lodash.prototype.map; + lodash.prototype.head = lodash.prototype.first; + lodash.prototype.select = lodash.prototype.filter; + lodash.prototype.tail = lodash.prototype.rest; + + return lodash; + } + + /*--------------------------------------------------------------------------*/ + + // Export lodash. + var _ = runInContext(); + + // Some AMD build optimizers like r.js check for condition patterns like the following: + if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) { + // Expose lodash to the global object when an AMD loader is present to avoid + // errors in cases where lodash is loaded by a script tag and not intended + // as an AMD module. See http://requirejs.org/docs/errors.html#mismatch for + // more details. + root._ = _; + + // Define as an anonymous module so, through path mapping, it can be + // referenced as the "underscore" module. + define(function() { + return _; + }); + } + // Check for `exports` after `define` in case a build optimizer adds an `exports` object. + else if (freeExports && freeModule) { + // Export for Node.js or RingoJS. + if (moduleExports) { + (freeModule.exports = _)._ = _; + } + // Export for Rhino with CommonJS support. + else { + freeExports._ = _; + } + } + else { + // Export for a browser or Rhino. + root._ = _; + } +}.call(this)); + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{}]},{},[1])(1) +}); \ No newline at end of file diff --git a/src/legacy/design-studio/js/dc.graph.cola.worker.js b/src/legacy/design-studio/js/dc.graph.cola.worker.js new file mode 100644 index 0000000..3cc6899 --- /dev/null +++ b/src/legacy/design-studio/js/dc.graph.cola.worker.js @@ -0,0 +1,496 @@ +/*! + * dc.graph 0.6.0 + * http://dc-js.github.io/dc.graph.js/ + * Copyright 2015-2016 AT&T Intellectual Property & the dc.graph.js Developers + * https://github.com/dc-js/dc.graph.js/blob/master/AUTHORS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +/** + * The entire dc.graph.js library is scoped under the **dc_graph** name space. It does not introduce + * anything else into the global name space. + * + * Like in dc.js and most libraries built on d3, most `dc_graph` functions are designed to allow function chaining, meaning they return the current chart + * instance whenever it is appropriate. The getter forms of functions do not participate in function + * chaining because they return values that are not the chart. + * @namespace dc_graph + * @version 0.6.0 + * @example + * // Example chaining + * chart.width(600) + * .height(400) + * .nodeDimension(nodeDim) + * .nodeGroup(nodeGroup); + */ + +var dc_graph = { + version: '0.6.0', + constants: { + CHART_CLASS: 'dc-graph' + } +}; + +function get_original(x) { + return x.orig; +} + +function identity(x) { + return x; +}; + +var property = function (defaultValue, unwrap) { + if(unwrap === undefined) + unwrap = get_original; + else if(unwrap === false) + unwrap = identity; + var value = defaultValue, react = null; + var cascade = []; + var ret = function (_) { + if (!arguments.length) { + return value; + } + if(react) + react(_); + value = _; + return this; + }; + ret.cascade = function (n, f) { + for(var i = 0; i<cascade.length; ++i) { + if(cascade[i].n === n) { + if(f) + cascade[i].f = f; + else delete cascade[i]; + return ret; + } else if(cascade[i].n > n) { + cascade.splice(i, 0, {n: n, f: f}); + return ret; + } + } + cascade.push({n: n, f: f}); + return ret; + }; + ret._eval = function(o, n) { + if(n===0 || !cascade.length) + return dc_graph.functor_wrap(ret(), unwrap)(o); + else { + var last = cascade[n-1]; + return last.f(o, function() { + return ret._eval(o, n-1); + }); + } + }; + ret.eval = function(o) { + return ret._eval(o, cascade.length); + }; + ret.react = function(_) { + if (!arguments.length) { + return react; + } + react = _; + return this; + }; + return ret; +}; + +function deprecated_property(message, defaultValue) { + var prop = property(defaultValue); + var ret = function() { + if(arguments.length) { + console.warn(message); + prop.apply(property, arguments); + return this; + } + return prop(); + }; + ['cascade', '_eval', 'eval', 'react'].forEach(function(method) { + ret[method] = prop[method]; + }); + return ret; +} + +// http://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript +function uuid() { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8); + return v.toString(16); + }); +} + +// create or re-use objects in a map, delete the ones that were not reused +function regenerate_objects(preserved, list, need, key, assign, create, destroy) { + if(!create) create = function(k, o) { }; + if(!destroy) destroy = function(k) { }; + var keep = {}; + function wrap(o) { + var k = key(o); + if(!preserved[k]) + create(k, preserved[k] = {}, o); + var o1 = preserved[k]; + assign(o1, o); + keep[k] = true; + return o1; + } + var wlist = list.map(wrap); + if(need) + need.forEach(function(k) { + if(!preserved[k]) + create(k, preserved[k] = {}, null); + keep[k] = true; + wlist.push(preserved[k]); + }); + // delete any objects from last round that are no longer used + for(var k in preserved) + if(!keep[k]) { + destroy(k, preserved[k]); + delete preserved[k]; + } + return wlist; +} + +/** + * `dc_graph.graphviz_attrs defines a basic set of attributes which layout engines should + * implement - although these are not required, they make it easier for clients and + * behaviors (like expand_collapse) to work with multiple layout engines. + * + * these attributes are {@link http://www.graphviz.org/doc/info/attrs.html from graphviz} + * @class graphviz_attrs + * @memberof dc_graph + * @return {Object} + **/ +dc_graph.graphviz_attrs = function() { + return { + /** + * Direction to draw ranks. + * @method rankdir + * @memberof dc_graph.graphviz_attrs + * @instance + * @param {String} [rankdir='TB'] 'TB', 'LR', 'BT', or 'RL' + **/ + rankdir: property('TB'), + /** + * Spacing in between nodes in the same rank. + * @method nodesep + * @memberof dc_graph.graphviz_attrs + * @instance + * @param {String} [nodesep=40] + **/ + nodesep: property(40), + /** + * Spacing in between ranks. + * @method ranksep + * @memberof dc_graph.graphviz_attrs + * @instance + * @param {String} [ranksep=40] + **/ + ranksep: property(40) + }; +}; + +/** + * `dc_graph.cola_layout` is an adaptor for cola.js layouts in dc.graph.js + * @class cola_layout + * @memberof dc_graph + * @param {String} [id=uuid()] - Unique identifier + * @return {dc_graph.cola_layout} + **/ +dc_graph.cola_layout = function(id) { + var _layoutId = id || uuid(); + var _d3cola = null; + var _dispatch = d3.dispatch('tick', 'start', 'end'); + var _flowLayout; + // node and edge objects shared with cola.js, preserved from one iteration + // to the next (as long as the object is still in the layout) + var _nodes = {}, _edges = {}; + + function init(options) { + // width, height, handleDisconnected, lengthStrategy, baseLength, flowLayout, tickSize + _d3cola = cola.d3adaptor() + .avoidOverlaps(true) + .size([options.width, options.height]) + .handleDisconnected(options.handleDisconnected); + if(_d3cola.tickSize) // non-standard + _d3cola.tickSize(options.tickSize); + + switch(options.lengthStrategy) { + case 'symmetric': + _d3cola.symmetricDiffLinkLengths(options.baseLength); + break; + case 'jaccard': + _d3cola.jaccardLinkLengths(options.baseLength); + break; + case 'individual': + _d3cola.linkDistance(function(e) { + return e.dcg_edgeLength || options.baseLength; + }); + break; + case 'none': + default: + } + if(options.flowLayout) { + _d3cola.flowLayout(options.flowLayout.axis, options.flowLayout.minSeparation); + } + } + + function data(nodes, edges, constraints, options) { + var wnodes = regenerate_objects(_nodes, nodes, null, function(v) { + return v.dcg_nodeKey; + }, function(v1, v) { + v1.dcg_nodeKey = v.dcg_nodeKey; + v1.width = v.width; + v1.height = v.height; + v1.fixed = !!v.dcg_nodeFixed; + + if(v1.fixed && typeof v.dcg_nodeFixed === 'object') { + v1.x = v.dcg_nodeFixed.x; + v1.y = v.dcg_nodeFixed.y; + } + else { + // should we support e.g. null to unset x,y? + if(v.x !== undefined) + v1.x = v.x; + if(v.y !== undefined) + v1.y = v.y; + } + }); + var wedges = regenerate_objects(_edges, edges, null, function(e) { + return e.dcg_edgeKey; + }, function(e1, e) { + e1.dcg_edgeKey = e.dcg_edgeKey; + // cola edges can work with indices or with object references + // but it will replace indices with object references + e1.source = _nodes[e.dcg_edgeSource]; + e1.target = _nodes[e.dcg_edgeTarget]; + e1.dcg_edgeLength = e.dcg_edgeLength; + }); + + // cola needs each node object to have an index property + wnodes.forEach(function(v, i) { + v.index = i; + }); + + var groups = null; + if(options.groupConnected) { + var components = cola.separateGraphs(wnodes, wedges); + groups = components.map(function(g) { + return {leaves: g.array.map(function(n) { return n.index; })}; + }); + } + + function dispatchState(event) { + _dispatch[event]( + wnodes, + wedges.map(function(e) { + return {dcg_edgeKey: e.dcg_edgeKey}; + }) + ); + } + _d3cola.on('tick', /* _tick = */ function() { + dispatchState('tick'); + }).on('start', function() { + _dispatch.start(); + }).on('end', /* _done = */ function() { + dispatchState('end'); + }); + _d3cola.nodes(wnodes) + .links(wedges) + .constraints(constraints) + .groups(groups); + } + + function start(options) { + _d3cola.start(options.initialUnconstrainedIterations, + options.initialUserConstraintIterations, + options.initialAllConstraintsIterations, + options.gridSnapIterations); + } + + function stop() { + _d3cola.stop(); + } + + var graphviz = dc_graph.graphviz_attrs(), graphviz_keys = Object.keys(graphviz); + graphviz.rankdir(null); + + var engine = Object.assign(graphviz, { + layoutAlgorithm: function() { + return 'cola'; + }, + layoutId: function() { + return _layoutId; + }, + supportsWebworker: function() { + return true; + }, + needsStage: function(stage) { // stopgap until we have engine chaining + return stage === 'ports' || stage === 'edgepos'; + }, + parent: property(null), + on: function(event, f) { + _dispatch.on(event, f); + return this; + }, + init: function(options) { + this.optionNames().forEach(function(option) { + options[option] = options[option] || this[option](); + }.bind(this)); + init(options); + return this; + }, + data: function(nodes, edges, constraints, options) { + data(nodes, edges, constraints, options); + }, + start: function(options) { + start(options); + }, + stop: function() { + stop(); + }, + optionNames: function() { + return ['handleDisconnected', 'lengthStrategy', 'baseLength', 'flowLayout', 'tickSize'] + .concat(graphviz_keys); + }, + populateLayoutNode: function() {}, + populateLayoutEdge: function() {}, + /** + * Instructs cola.js to fit the connected components. + * @method handleDisconnected + * @memberof dc_graph.cola_layout + * @instance + * @param {Boolean} [handleDisconnected=true] + * @return {Boolean} + * @return {dc_graph.cola_layout} + **/ + handleDisconnected: property(true), + /** + * Currently, three strategies are supported for specifying the lengths of edges: + * * 'individual' - uses the `edgeLength` for each edge. If it returns falsy, uses the + * `baseLength` + * * 'symmetric', 'jaccard' - compute the edge length based on the graph structure around + * the edge. See + * {@link https://github.com/tgdwyer/WebCola/wiki/link-lengths the cola.js wiki} + * for more details. + * 'none' - no edge lengths will be specified + * @method lengthStrategy + * @memberof dc_graph.cola_layout + * @instance + * @param {Function|String} [lengthStrategy='symmetric'] + * @return {Function|String} + * @return {dc_graph.cola_layout} + **/ + lengthStrategy: property('symmetric'), + /** + * Gets or sets the default edge length (in pixels) when the `.lengthStrategy` is + * 'individual', and the base value to be multiplied for 'symmetric' and 'jaccard' edge + * lengths. + * @method baseLength + * @memberof dc_graph.cola_layout + * @instance + * @param {Number} [baseLength=30] + * @return {Number} + * @return {dc_graph.cola_layout} + **/ + baseLength: property(30), + /** + * If `flowLayout` is set, it determines the axis and separation for + * {@link http://marvl.infotech.monash.edu/webcola/doc/classes/cola.layout.html#flowlayout cola flow layout}. + * If it is not set, `flowLayout` will be calculated from the {@link dc_graph.graphviz_attrs#rankdir rankdir} + * and {@link dc_graph.graphviz_attrs#ranksep ranksep}; if `rankdir` is also null (the + * default for cola layout), then there will be no flow. + * @method flowLayout + * @memberof dc_graph.cola_layout + * @instance + * @param {Object} [flowLayout=null] + * @example + * // No flow (default) + * chart.flowLayout(null) + * // flow in x with min separation 200 + * chart.flowLayout({axis: 'x', minSeparation: 200}) + **/ + flowLayout: function(flow) { + if(!arguments.length) { + if(_flowLayout) + return _flowLayout; + var dir = engine.rankdir(); + switch(dir) { + case 'LR': return {axis: 'x', minSeparation: engine.ranksep() + engine.parent().nodeRadius()*2}; + case 'TB': return {axis: 'y', minSeparation: engine.ranksep() + engine.parent().nodeRadius()*2}; + default: return null; // RL, BT do not appear to be possible (negative separation) (?) + } + } + _flowLayout = flow; + return this; + }, + tickSize: property(1) + }); + return engine; +}; + +dc_graph.cola_layout.scripts = ['d3.js', 'cola.js']; + +var _layouts; + +function postResponse(event, layoutId) { + return function() { + var message = { + response: event, + layoutId: layoutId + }; + message.args = Array.prototype.slice.call(arguments); + postMessage(message); + }; +} + +onmessage = function(e) { + var args = e.data.args; + switch(e.data.command) { + case 'init': + // find a function under dc_graph that has `scripts` + var layout_name; + for(var name in dc_graph) { + if(typeof dc_graph[name] === 'function' && dc_graph[name].scripts) + layout_name = name; + } + if(!_layouts) { + _layouts = {}; + importScripts.apply(null, dc_graph[layout_name].scripts); + } + + _layouts[args.layoutId] = dc_graph[layout_name]() + .on('tick', postResponse('tick', args.layoutId)) + .on('start', postResponse('start', args.layoutId)) + .on('end', postResponse('end', args.layoutId)) + .init(args.options); + break; + case 'data': + if(_layouts) + _layouts[args.layoutId].data(args.nodes, args.edges, args.constraints, args.options); + break; + case 'start': + // if(args.initialOnly) { + // if(args.showLayoutSteps) + // _tick(); + // _done(); + // } + // else + _layouts[args.layoutId].start(args.options); + break; + case 'stop': + if(_layouts) + _layouts[args.layoutId].stop(); + break; + } +}; + + +//# sourceMappingURL=dc.graph.cola.worker.js.map \ No newline at end of file diff --git a/src/legacy/design-studio/js/dc.graph.dagre.worker.js b/src/legacy/design-studio/js/dc.graph.dagre.worker.js new file mode 100644 index 0000000..199ffcf --- /dev/null +++ b/src/legacy/design-studio/js/dc.graph.dagre.worker.js @@ -0,0 +1,384 @@ +/*! + * dc.graph 0.6.0 + * http://dc-js.github.io/dc.graph.js/ + * Copyright 2015-2016 AT&T Intellectual Property & the dc.graph.js Developers + * https://github.com/dc-js/dc.graph.js/blob/master/AUTHORS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +/** + * The entire dc.graph.js library is scoped under the **dc_graph** name space. It does not introduce + * anything else into the global name space. + * + * Like in dc.js and most libraries built on d3, most `dc_graph` functions are designed to allow function chaining, meaning they return the current chart + * instance whenever it is appropriate. The getter forms of functions do not participate in function + * chaining because they return values that are not the chart. + * @namespace dc_graph + * @version 0.6.0 + * @example + * // Example chaining + * chart.width(600) + * .height(400) + * .nodeDimension(nodeDim) + * .nodeGroup(nodeGroup); + */ + +var dc_graph = { + version: '0.6.0', + constants: { + CHART_CLASS: 'dc-graph' + } +}; + +function get_original(x) { + return x.orig; +} + +function identity(x) { + return x; +}; + +var property = function (defaultValue, unwrap) { + if(unwrap === undefined) + unwrap = get_original; + else if(unwrap === false) + unwrap = identity; + var value = defaultValue, react = null; + var cascade = []; + var ret = function (_) { + if (!arguments.length) { + return value; + } + if(react) + react(_); + value = _; + return this; + }; + ret.cascade = function (n, f) { + for(var i = 0; i<cascade.length; ++i) { + if(cascade[i].n === n) { + if(f) + cascade[i].f = f; + else delete cascade[i]; + return ret; + } else if(cascade[i].n > n) { + cascade.splice(i, 0, {n: n, f: f}); + return ret; + } + } + cascade.push({n: n, f: f}); + return ret; + }; + ret._eval = function(o, n) { + if(n===0 || !cascade.length) + return dc_graph.functor_wrap(ret(), unwrap)(o); + else { + var last = cascade[n-1]; + return last.f(o, function() { + return ret._eval(o, n-1); + }); + } + }; + ret.eval = function(o) { + return ret._eval(o, cascade.length); + }; + ret.react = function(_) { + if (!arguments.length) { + return react; + } + react = _; + return this; + }; + return ret; +}; + +function deprecated_property(message, defaultValue) { + var prop = property(defaultValue); + var ret = function() { + if(arguments.length) { + console.warn(message); + prop.apply(property, arguments); + return this; + } + return prop(); + }; + ['cascade', '_eval', 'eval', 'react'].forEach(function(method) { + ret[method] = prop[method]; + }); + return ret; +} + +// http://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript +function uuid() { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8); + return v.toString(16); + }); +} + +// create or re-use objects in a map, delete the ones that were not reused +function regenerate_objects(preserved, list, need, key, assign, create, destroy) { + if(!create) create = function(k, o) { }; + if(!destroy) destroy = function(k) { }; + var keep = {}; + function wrap(o) { + var k = key(o); + if(!preserved[k]) + create(k, preserved[k] = {}, o); + var o1 = preserved[k]; + assign(o1, o); + keep[k] = true; + return o1; + } + var wlist = list.map(wrap); + if(need) + need.forEach(function(k) { + if(!preserved[k]) + create(k, preserved[k] = {}, null); + keep[k] = true; + wlist.push(preserved[k]); + }); + // delete any objects from last round that are no longer used + for(var k in preserved) + if(!keep[k]) { + destroy(k, preserved[k]); + delete preserved[k]; + } + return wlist; +} + +/** + * `dc_graph.graphviz_attrs defines a basic set of attributes which layout engines should + * implement - although these are not required, they make it easier for clients and + * behaviors (like expand_collapse) to work with multiple layout engines. + * + * these attributes are {@link http://www.graphviz.org/doc/info/attrs.html from graphviz} + * @class graphviz_attrs + * @memberof dc_graph + * @return {Object} + **/ +dc_graph.graphviz_attrs = function() { + return { + /** + * Direction to draw ranks. + * @method rankdir + * @memberof dc_graph.graphviz_attrs + * @instance + * @param {String} [rankdir='TB'] 'TB', 'LR', 'BT', or 'RL' + **/ + rankdir: property('TB'), + /** + * Spacing in between nodes in the same rank. + * @method nodesep + * @memberof dc_graph.graphviz_attrs + * @instance + * @param {String} [nodesep=40] + **/ + nodesep: property(40), + /** + * Spacing in between ranks. + * @method ranksep + * @memberof dc_graph.graphviz_attrs + * @instance + * @param {String} [ranksep=40] + **/ + ranksep: property(40) + }; +}; + +/** + * `dc_graph.dagre_layout` is an adaptor for dagre.js layouts in dc.graph.js + * + * In addition to the below layout attributes, `dagre_layout` also implements the attributes from + * {@link dc_graph.graphviz_attrs graphviz_attrs} + * @class dagre_layout + * @memberof dc_graph + * @param {String} [id=uuid()] - Unique identifier + * @return {dc_graph.dagre_layout} + **/ +dc_graph.dagre_layout = function(id) { + var _layoutId = id || uuid(); + var _dagreGraph = null, _tick, _done; + var _dispatch = d3.dispatch('tick', 'start', 'end'); + // node and edge objects preserved from one iteration + // to the next (as long as the object is still in the layout) + var _nodes = {}, _edges = {}; + + function init(options) { + // Create a new directed graph + _dagreGraph = new dagre.graphlib.Graph({multigraph: true}); + + // Set an object for the graph label + _dagreGraph.setGraph({rankdir: options.rankdir, nodesep: options.nodesep, ranksep: options.ranksep}); + + // Default to assigning a new object as a label for each new edge. + _dagreGraph.setDefaultEdgeLabel(function() { return {}; }); + } + + function data(nodes, edges, constraints, options) { + var wnodes = regenerate_objects(_nodes, nodes, null, function(v) { + return v.dcg_nodeKey; + }, function(v1, v) { + v1.dcg_nodeKey = v.dcg_nodeKey; + v1.width = v.width; + v1.height = v.height; + /* + dagre does not seem to accept input positions + if(v.dcg_nodeFixed) { + v1.x = v.dcg_nodeFixed.x; + v1.y = v.dcg_nodeFixed.y; + } + */ + }, function(k, o) { + _dagreGraph.setNode(k, o); + }, function(k) { + _dagreGraph.removeNode(k); + }); + var wedges = regenerate_objects(_edges, edges, null, function(e) { + return e.dcg_edgeKey; + }, function(e1, e) { + e1.dcg_edgeKey = e.dcg_edgeKey; + e1.dcg_edgeSource = e.dcg_edgeSource; + e1.dcg_edgeTarget = e.dcg_edgeTarget; + }, function(k, o, e) { + _dagreGraph.setEdge(e.dcg_edgeSource, e.dcg_edgeTarget, o); + }, function(k, e) { + _dagreGraph.removeEdge(e.dcg_edgeSource, e.dcg_edgeTarget, e.dcg_edgeKey); + }); + + function dispatchState(event) { + _dispatch[event]( + wnodes, + wedges.map(function(e) { + return {dcg_edgeKey: e.dcg_edgeKey}; + }) + ); + } + _tick = function() { + dispatchState('tick'); + }; + _done = function() { + dispatchState('end'); + }; + } + + function start(options) { + _dispatch.start(); + dagre.layout(_dagreGraph); + _done(); + } + + function stop() { + } + + var graphviz = dc_graph.graphviz_attrs(), graphviz_keys = Object.keys(graphviz); + return Object.assign(graphviz, { + layoutAlgorithm: function() { + return 'dagre'; + }, + layoutId: function() { + return _layoutId; + }, + supportsWebworker: function() { + return true; + }, + needsStage: function(stage) { // stopgap until we have engine chaining + return stage === 'ports' || stage === 'edgepos'; + }, + on: function(event, f) { + _dispatch.on(event, f); + return this; + }, + init: function(options) { + this.optionNames().forEach(function(option) { + options[option] = options[option] || this[option](); + }.bind(this)); + init(options); + return this; + }, + data: function(nodes, edges, constraints, options) { + data(nodes, edges, constraints, options); + }, + start: function(options) { + start(options); + }, + stop: function() { + stop(); + }, + optionNames: function() { + return graphviz_keys; + }, + populateLayoutNode: function() {}, + populateLayoutEdge: function() {} + }); +}; + +dc_graph.dagre_layout.scripts = ['d3.js', 'dagre.js']; + +var _layouts; + +function postResponse(event, layoutId) { + return function() { + var message = { + response: event, + layoutId: layoutId + }; + message.args = Array.prototype.slice.call(arguments); + postMessage(message); + }; +} + +onmessage = function(e) { + var args = e.data.args; + switch(e.data.command) { + case 'init': + // find a function under dc_graph that has `scripts` + var layout_name; + for(var name in dc_graph) { + if(typeof dc_graph[name] === 'function' && dc_graph[name].scripts) + layout_name = name; + } + if(!_layouts) { + _layouts = {}; + importScripts.apply(null, dc_graph[layout_name].scripts); + } + + _layouts[args.layoutId] = dc_graph[layout_name]() + .on('tick', postResponse('tick', args.layoutId)) + .on('start', postResponse('start', args.layoutId)) + .on('end', postResponse('end', args.layoutId)) + .init(args.options); + break; + case 'data': + if(_layouts) + _layouts[args.layoutId].data(args.nodes, args.edges, args.constraints, args.options); + break; + case 'start': + // if(args.initialOnly) { + // if(args.showLayoutSteps) + // _tick(); + // _done(); + // } + // else + _layouts[args.layoutId].start(args.options); + break; + case 'stop': + if(_layouts) + _layouts[args.layoutId].stop(); + break; + } +}; + + +//# sourceMappingURL=dc.graph.dagre.worker.js.map \ No newline at end of file diff --git a/src/legacy/design-studio/js/dc.graph.js b/src/legacy/design-studio/js/dc.graph.js new file mode 100644 index 0000000..6de9384 --- /dev/null +++ b/src/legacy/design-studio/js/dc.graph.js @@ -0,0 +1,15617 @@ +/*! + * dc.graph 0.9.5 + * http://dc-js.github.io/dc.graph.js/ + * Copyright 2015-2019 AT&T Intellectual Property & the dc.graph.js Developers + * https://github.com/dc-js/dc.graph.js/blob/master/AUTHORS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +(function() { function _dc_graph(d3, crossfilter, dc) { +'use strict'; + +/** + * The entire dc.graph.js library is scoped under the **dc_graph** name space. It does not introduce + * anything else into the global name space. + * + * Like in dc.js and most libraries built on d3, most `dc_graph` functions are designed to allow function chaining, meaning they return the current diagram + * instance whenever it is appropriate. The getter forms of functions do not participate in function + * chaining because they return values that are not the diagram. + * @namespace dc_graph + * @version 0.9.5 + * @example + * // Example chaining + * diagram.width(600) + * .height(400) + * .nodeDimension(nodeDim) + * .nodeGroup(nodeGroup); + */ + +var dc_graph = { + version: '0.9.5', + constants: { + CHART_CLASS: 'dc-graph' + } +}; + +function get_original(x) { + return x.orig; +} + +function identity(x) { + return x; +}; + +var property = function (defaultValue, unwrap) { + if(unwrap === undefined) + unwrap = get_original; + else if(unwrap === false) + unwrap = identity; + var value = defaultValue, react = null; + var cascade = []; + var ret = function (_) { + if (!arguments.length) { + return value; + } + if(react) + react(_); + value = _; + return this; + }; + ret.cascade = function (n, f) { + for(var i = 0; i<cascade.length; ++i) { + if(cascade[i].n === n) { + if(f) + cascade[i].f = f; + else cascade.splice(i, 1); + return ret; + } else if(cascade[i].n > n) { + cascade.splice(i, 0, {n: n, f: f}); + return ret; + } + } + cascade.push({n: n, f: f}); + return ret; + }; + ret._eval = function(o, n) { + if(n===0 || !cascade.length) + return dc_graph.functor_wrap(ret(), unwrap)(o); + else { + var last = cascade[n-1]; + return last.f(o, function() { + return ret._eval(o, n-1); + }); + } + }; + ret.eval = function(o) { + return ret._eval(o, cascade.length); + }; + ret.react = function(_) { + if (!arguments.length) { + return react; + } + react = _; + return this; + }; + return ret; +}; + +function named_children() { + var _children = {}; + var f = function(id, object) { + if(arguments.length === 1) + return _children[id]; + if(f.reject) { + var reject = f.reject(id, object); + if(reject) { + console.groupCollapsed(reject); + console.trace(); + console.groupEnd(); + return this; + } + } + // do not notify unnecessarily + if(_children[id] === object) + return this; + if(_children[id]) + _children[id].parent(null); + _children[id] = object; + if(object) + object.parent(this); + return this; + }; + f.enum = function() { + return Object.keys(_children); + }; + f.nameOf = function(o) { + var found = Object.entries(_children).find(function(kv) { + return kv[1] == o; + }); + return found ? found[0] : null; + }; + return f; +} + +function deprecated_property(message, defaultValue) { + var prop = property(defaultValue); + var ret = function() { + if(arguments.length) { + console.warn(message); + prop.apply(property, arguments); + return this; + } + return prop(); + }; + ['cascade', '_eval', 'eval', 'react'].forEach(function(method) { + ret[method] = prop[method]; + }); + return ret; +} + +function onetime_trace(level, message) { + var said = false; + return function() { + if(said) + return; + if(level === 'trace') { + console.groupCollapsed(message); + console.trace(); + console.groupEnd(); + } + else + console[level](message); + said = true; + }; +} + +function deprecation_warning(message) { + return onetime_trace('warn', message); +} + +function trace_function(level, message, f) { + var dep = onetime_trace(level, message); + return function() { + dep(); + return f.apply(this, arguments); + }; +} +function deprecate_function(message, f) { + return trace_function('warn', message, f); +} + +// http://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript +function uuid() { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8); + return v.toString(16); + }); +} + +function is_ie() { + var ua = window.navigator.userAgent; + + return(ua.indexOf('MSIE ') > 0 || + ua.indexOf('Trident/') > 0 || + ua.indexOf('Edge/') > 0); +} + +function is_safari() { + return /^((?!chrome|android).)*safari/i.test(navigator.userAgent); +} + +// polyfill Object.assign for IE +// it's just too useful to do without +if (typeof Object.assign != 'function') { + // Must be writable: true, enumerable: false, configurable: true + Object.defineProperty(Object, "assign", { + value: function assign(target, varArgs) { // .length of function is 2 + 'use strict'; + if (target == null) { // TypeError if undefined or null + throw new TypeError('Cannot convert undefined or null to object'); + } + + var to = Object(target); + + for (var index = 1; index < arguments.length; index++) { + var nextSource = arguments[index]; + + if (nextSource != null) { // Skip over if undefined or null + for (var nextKey in nextSource) { + // Avoid bugs when hasOwnProperty is shadowed + if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { + to[nextKey] = nextSource[nextKey]; + } + } + } + } + return to; + }, + writable: true, + configurable: true + }); +} + + +// https://tc39.github.io/ecma262/#sec-array.prototype.includes +if (!Array.prototype.includes) { + Object.defineProperty(Array.prototype, 'includes', { + value: function(valueToFind, fromIndex) { + + if (this == null) { + throw new TypeError('"this" is null or not defined'); + } + + // 1. Let O be ? ToObject(this value). + var o = Object(this); + + // 2. Let len be ? ToLength(? Get(O, "length")). + var len = o.length >>> 0; + + // 3. If len is 0, return false. + if (len === 0) { + return false; + } + + // 4. Let n be ? ToInteger(fromIndex). + // (If fromIndex is undefined, this step produces the value 0.) + var n = fromIndex | 0; + + // 5. If n >= 0, then + // a. Let k be n. + // 6. Else n < 0, + // a. Let k be len + n. + // b. If k < 0, let k be 0. + var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0); + + function sameValueZero(x, y) { + return x === y || (typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y)); + } + + // 7. Repeat, while k < len + while (k < len) { + // a. Let elementK be the result of ? Get(O, ! ToString(k)). + // b. If SameValueZero(valueToFind, elementK) is true, return true. + if (sameValueZero(o[k], valueToFind)) { + return true; + } + // c. Increase k by 1. + k++; + } + + // 8. Return false + return false; + } + }); +} + +if (!Object.entries) { + Object.entries = function( obj ){ + var ownProps = Object.keys( obj ), + i = ownProps.length, + resArray = new Array(i); // preallocate the Array + while (i--) + resArray[i] = [ownProps[i], obj[ownProps[i]]]; + return resArray; + }; +} + +// https://github.com/KhaledElAnsari/Object.values +Object.values = Object.values ? Object.values : function(obj) { + var allowedTypes = ["[object String]", "[object Object]", "[object Array]", "[object Function]"]; + var objType = Object.prototype.toString.call(obj); + + if(obj === null || typeof obj === "undefined") { + throw new TypeError("Cannot convert undefined or null to object"); + } else if(!~allowedTypes.indexOf(objType)) { + return []; + } else { + // if ES6 is supported + if (Object.keys) { + return Object.keys(obj).map(function (key) { + return obj[key]; + }); + } + + var result = []; + for (var prop in obj) { + if (obj.hasOwnProperty(prop)) { + result.push(obj[prop]); + } + } + + return result; + } +}; + +function getBBoxNoThrow(elem) { + // firefox seems to have issues with some of my texts + // just catch for now + try { + return elem.getBBox(); + } catch(xep) { + return {x: 0, y: 0, width:0, height: 0}; + } +} + +function property_if(pred, curr) { + return function(o, last) { + return pred(o) ? curr(o) : last(); + }; +} + +function property_interpolate(value, curr) { + return function(o, last) { + return d3.interpolate(last(o), curr(o))(value(o)); + }; +} + +function multiply_properties(pred, props, blend) { + var props2 = {}; + for(var p in props) + props2[p] = blend(pred, param(props[p])); + return props2; +} + +function conditional_properties(pred, props) { + return multiply_properties(pred, props, property_if); +} + +function node_edge_conditions(npred, epred, props) { + var nprops = {}, eprops = {}, badprops = []; + for(var p in props) { + if(/^node/.test(p)) + nprops[p] = props[p]; + else if(/^edge/.test(p)) + eprops[p] = props[p]; + else badprops.push(p); + } + if(badprops.length) + console.error('only know how to deal with properties that start with "node" or "edge"', badprops); + var props2 = npred ? conditional_properties(npred, nprops) : {}; + if(epred) + Object.assign(props2, conditional_properties(epred, eprops)); + return props2; +} + +function cascade(parent) { + return function(level, add, props) { + for(var p in props) { + if(!parent[p]) + throw new Error('unknown attribute ' + p); + parent[p].cascade(level, add ? props[p] : null); + } + return parent; + }; +} + +function compose(f, g) { + return function() { + return f(g.apply(null, arguments)); + }; +} + +// version of d3.functor that optionally wraps the function with another +// one, if the parameter is a function +dc_graph.functor_wrap = function (v, wrap) { + if(typeof v === "function") { + return wrap ? function(x) { + return v(wrap(x)); + } : v; + } + else return function() { + return v; + }; +}; + +// we want to allow either values or functions to be passed to specify parameters. +// if a function, the function needs a preprocessor to extract the original key/value +// pair from the wrapper object we put it in. +function param(v) { + return dc_graph.functor_wrap(v, get_original); +} + +// http://jsperf.com/cloning-an-object/101 +function clone(obj) { + var target = {}; + for(var i in obj) { + if(obj.hasOwnProperty(i)) { + target[i] = obj[i]; + } + } + return target; +} + +// because i don't think we need to bind edge point data (yet!) +var bez_cmds = { + 1: 'L', 2: 'Q', 3: 'C' +}; + +function generate_path(pts, bezDegree, close) { + var cats = ['M', pts[0].x, ',', pts[0].y], remain = bezDegree; + var hasNaN = false; + for(var i = 1; i < pts.length; ++i) { + if(isNaN(pts[i].x) || isNaN(pts[i].y)) + hasNaN = true; + cats.push(remain===bezDegree ? bez_cmds[bezDegree] : ' ', pts[i].x, ',', pts[i].y); + if(--remain===0) + remain = bezDegree; + } + if(remain!=bezDegree) + console.log("warning: pts.length didn't match bezian degree", pts, bezDegree); + if(close) + cats.push('Z'); + return cats.join(''); +} + +// for IE (do we care really?) +Math.hypot = Math.hypot || function() { + var y = 0; + var length = arguments.length; + + for (var i = 0; i < length; i++) { + if (arguments[i] === Infinity || arguments[i] === -Infinity) { + return Infinity; + } + y += arguments[i] * arguments[i]; + } + return Math.sqrt(y); +}; + +// outputs the array with adjacent identical lines collapsed to one +function uniq(a) { + var ret = []; + a.forEach(function(x, i) { + if(i === 0 || x !== a[i-1]) + ret.push(x); + }); + return ret; +} + +// https://tc39.github.io/ecma262/#sec-array.prototype.find +if (!Array.prototype.find) { + Object.defineProperty(Array.prototype, 'find', { + value: function(predicate) { + // 1. Let O be ? ToObject(this value). + if (this == null) { + throw new TypeError('"this" is null or not defined'); + } + + var o = Object(this); + + // 2. Let len be ? ToLength(? Get(O, "length")). + var len = o.length >>> 0; + + // 3. If IsCallable(predicate) is false, throw a TypeError exception. + if (typeof predicate !== 'function') { + throw new TypeError('predicate must be a function'); + } + + // 4. If thisArg was supplied, let T be thisArg; else let T be undefined. + var thisArg = arguments[1]; + + // 5. Let k be 0. + var k = 0; + + // 6. Repeat, while k < len + while (k < len) { + // a. Let Pk be ! ToString(k). + // b. Let kValue be ? Get(O, Pk). + // c. Let testResult be ToBoolean(? Call(predicate, T, << kValue, k, O >>)). + // d. If testResult is true, return kValue. + var kValue = o[k]; + if (predicate.call(thisArg, kValue, k, o)) { + return kValue; + } + // e. Increase k by 1. + k++; + } + + // 7. Return undefined. + return undefined; + } + }); +} + +var script_path = function() { + var _path; + return function() { + if(_path === undefined) { + // adapted from http://stackoverflow.com/a/18283141/676195 + _path = null; // only try once + var filename = 'dc.graph.js'; + var scripts = document.getElementsByTagName('script'); + if (scripts && scripts.length > 0) { + for (var i in scripts) { + if (scripts[i].src && scripts[i].src.match(new RegExp(filename+'$'))) { + _path = scripts[i].src.replace(new RegExp('(.*)'+filename+'$'), '$1'); + break; + } + } + } + } + return _path; + }; +}(); + +dc_graph.event_coords = function(diagram) { + var bound = diagram.root().node().getBoundingClientRect(); + return diagram.invertCoord([d3.event.clientX - bound.left, + d3.event.clientY - bound.top]); +}; + +function promise_identity(x) { + return Promise.resolve(x); +} + +// http://stackoverflow.com/questions/7044944/jquery-javascript-to-detect-os-without-a-plugin +var is_a_mac = navigator.platform.toUpperCase().indexOf('MAC')!==-1; + +// https://stackoverflow.com/questions/16863917/check-if-class-exists-somewhere-in-parent-vanilla-js +function ancestor_has_class(element, classname) { + if(d3.select(element).classed(classname)) + return true; + return element.parentElement && ancestor_has_class(element.parentElement, classname); +} + +if (typeof SVGElement.prototype.contains == 'undefined') { + SVGElement.prototype.contains = HTMLDivElement.prototype.contains; +} + +// arguably depth first search is a stupid algorithm to modularize - +// there are many, many interesting moments to insert a behavior +// and those end up being almost bigger than the function itself + +// this is an argument for providing a graph API which could make it +// easy to just write a recursive function instead of using this +dc_graph.depth_first_traversal = function(callbacks) { // {[init, root, row, tree, place, sib, push, pop, skip,] finish, nodeid, sourceid, targetid} + return function(nodes, edges) { + callbacks.init && callbacks.init(); + if(callbacks.tree) + edges = edges.filter(function(e) { return callbacks.tree(e); }); + var indegree = {}; + var outmap = edges.reduce(function(m, e) { + var tail = callbacks.sourceid(e), + head = callbacks.targetid(e); + if(!m[tail]) m[tail] = []; + m[tail].push(e); + indegree[head] = (indegree[head] || 0) + 1; + return m; + }, {}); + var nmap = nodes.reduce(function(m, n) { + var key = callbacks.nodeid(n); + m[key] = n; + return m; + }, {}); + + var rows = []; + var placed = {}; + function place_tree(n, r) { + var key = callbacks.nodeid(n); + if(placed[key]) { + callbacks.skip && callbacks.skip(n, indegree[key]); + return; + } + if(!rows[r]) + rows[r] = []; + callbacks.place && callbacks.place(n, r, rows[r]); + rows[r].push(n); + placed[key] = true; + if(outmap[key]) + outmap[key].forEach(function(e, ei) { + var target = nmap[callbacks.targetid(e)]; + if(ei && callbacks.sib) + callbacks.sib(false, nmap[callbacks.targetid(outmap[key][ei-1])], target); + callbacks.push && callbacks.push(); + place_tree(target, r+1); + }); + callbacks.pop && callbacks.pop(n); + } + + var roots; + if(callbacks.root) + roots = nodes.filter(function(n) { return callbacks.root(n); }); + else { + roots = nodes.filter(function(n) { return !indegree[callbacks.nodeid(n)]; }); + if(nodes.length && !roots.length) // all nodes are in a cycle + roots = [nodes[0]]; + } + roots.forEach(function(n, ni) { + if(ni && callbacks.sib) + callbacks.sib(true, roots[ni-1], n); + callbacks.push && callbacks.push(); + place_tree(n, callbacks.row && callbacks.row(n) || 0); + }); + callbacks.finish(rows); + }; +}; + +// basically, see if it's any simpler if we start from scratch +// (well, of course it's simpler because we have less callbacks) +// same caveats as above +dc_graph.undirected_dfs = function(callbacks) { // {[comp, node], nodeid, sourceid, targetid} + return function(nodes, edges) { + var adjacencies = edges.reduce(function(m, e) { + var tail = callbacks.sourceid(e), + head = callbacks.targetid(e); + if(!m[tail]) m[tail] = []; + if(!m[head]) m[head] = []; + m[tail].push(head); + m[head].push(tail); + return m; + }, {}); + var nmap = nodes.reduce(function(m, n) { + var key = callbacks.nodeid(n); + m[key] = n; + return m; + }, {}); + var found = {}; + function recurse(n) { + var nid = callbacks.nodeid(n); + callbacks.node(compid, n); + found[nid] = true; + if(adjacencies[nid]) + adjacencies[nid].forEach(function(adj) { + if(!found[adj]) + recurse(nmap[adj]); + }); + } + var compid = 0; + nodes.forEach(function(n) { + if(!found[callbacks.nodeid(n)]) { + callbacks.comp && callbacks.comp(compid); + recurse(n); + ++compid; + } + }); + }; +}; + +// create or re-use objects in a map, delete the ones that were not reused +function regenerate_objects(preserved, list, need, key, assign, create, destroy) { + if(!create) create = function(k, o) { }; + if(!destroy) destroy = function(k) { }; + var keep = {}; + function wrap(o) { + var k = key(o); + if(!preserved[k]) + create(k, preserved[k] = {}, o); + var o1 = preserved[k]; + assign(o1, o); + keep[k] = true; + return o1; + } + var wlist = list.map(wrap); + if(need) + need.forEach(function(k) { + if(!preserved[k]) { // hasn't been created, needs to be + create(k, preserved[k] = {}, null); + assign(preserved[k], null); + } + if(!keep[k]) { // wasn't in list, should be + wlist.push(preserved[k]); + keep[k] = true; + } + }); + // delete any objects from last round that are no longer used + for(var k in preserved) + if(!keep[k]) { + destroy(k, preserved[k]); + delete preserved[k]; + } + return wlist; +} + +function point_on_ellipse(A, B, dx, dy) { + var tansq = Math.tan(Math.atan2(dy, dx)); + tansq = tansq*tansq; // why is this not just dy*dy/dx*dx ? ? + var ret = {x: A*B/Math.sqrt(B*B + A*A*tansq), y: A*B/Math.sqrt(A*A + B*B/tansq)}; + if(dx<0) + ret.x = -ret.x; + if(dy<0) + ret.y = -ret.y; + return ret; +} + +var eps = 0.0000001; +function between(a, b, c) { + return a-eps <= b && b <= c+eps; +} + +// Adapted from http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect/1968345#1968345 +function segment_intersection(x1,y1,x2,y2, x3,y3,x4,y4) { + var x=((x1*y2-y1*x2)*(x3-x4)-(x1-x2)*(x3*y4-y3*x4)) / + ((x1-x2)*(y3-y4)-(y1-y2)*(x3-x4)); + var y=((x1*y2-y1*x2)*(y3-y4)-(y1-y2)*(x3*y4-y3*x4)) / + ((x1-x2)*(y3-y4)-(y1-y2)*(x3-x4)); + if (isNaN(x)||isNaN(y)) { + return false; + } else { + if (x1>=x2) { + if (!between(x2, x, x1)) {return false;} + } else { + if (!between(x1, x, x2)) {return false;} + } + if (y1>=y2) { + if (!between(y2, y, y1)) {return false;} + } else { + if (!between(y1, y, y2)) {return false;} + } + if (x3>=x4) { + if (!between(x4, x, x3)) {return false;} + } else { + if (!between(x3, x, x4)) {return false;} + } + if (y3>=y4) { + if (!between(y4, y, y3)) {return false;} + } else { + if (!between(y3, y, y4)) {return false;} + } + } + return {x: x, y: y}; +} + + +function point_on_polygon(points, x0, y0, x1, y1) { + for(var i = 0; i < points.length; ++i) { + var next = i===points.length-1 ? 0 : i+1; + var isect = segment_intersection(points[i].x, points[i].y, points[next].x, points[next].y, + x0, y0, x1, y1); + if(isect) + return isect; + } + return null; +} + +// as many as we can get from +// http://www.graphviz.org/doc/info/shapes.html +dc_graph.shape_presets = { + egg: { + // not really: an ovoid should be two half-ellipses stuck together + // https://en.wikipedia.org/wiki/Oval + generator: 'polygon', + preset: function() { + return {sides: 100, distortion: -0.25}; + } + }, + triangle: { + generator: 'polygon', + preset: function() { + return {sides: 3}; + } + }, + rectangle: { + generator: 'polygon', + preset: function() { + return {sides: 4}; + } + }, + diamond: { + generator: 'polygon', + preset: function() { + return {sides: 4, rotation: 45}; + } + }, + trapezium: { + generator: 'polygon', + preset: function() { + return {sides: 4, distortion: -0.5}; + } + }, + parallelogram: { + generator: 'polygon', + preset: function() { + return {sides: 4, skew: 0.5}; + } + }, + pentagon: { + generator: 'polygon', + preset: function() { + return {sides: 5}; + } + }, + hexagon: { + generator: 'polygon', + preset: function() { + return {sides: 6}; + } + }, + septagon: { + generator: 'polygon', + preset: function() { + return {sides: 7}; + } + }, + octagon: { + generator: 'polygon', + preset: function() { + return {sides: 8}; + } + }, + invtriangle: { + generator: 'polygon', + preset: function() { + return {sides: 3, rotation: 180}; + } + }, + invtrapezium: { + generator: 'polygon', + preset: function() { + return {sides: 4, distortion: 0.5}; + } + }, + square: { + generator: 'polygon', + preset: function() { + return { + sides: 4, + regular: true + }; + } + }, + plain: { + generator: 'rounded-rect', + preset: function() { + return { + noshape: true + }; + } + }, + house: { + generator: 'elaborated-rect', + preset: function() { + return { + get_points: function(rx, ry) { + return [ + {x: rx, y: ry*2/3}, + {x: rx, y: -ry/2}, + {x: 0, y: -ry}, + {x: -rx, y: -ry/2}, + {x: -rx, y: ry*2/3} + ]; + }, + minrx: 30 + }; + } + }, + invhouse: { + generator: 'elaborated-rect', + preset: function() { + return { + get_points: function(rx, ry) { + return [ + {x: rx, y: ry/2}, + {x: rx, y: -ry*2/3}, + {x: -rx, y: -ry*2/3}, + {x: -rx, y: ry/2}, + {x: 0, y: ry} + ]; + }, + minrx: 30 + }; + } + }, + rarrow: { + generator: 'elaborated-rect', + preset: function() { + return { + get_points: function(rx, ry) { + return [ + {x: rx, y: ry}, + {x: rx, y: ry*1.5}, + {x: rx + ry*1.5, y: 0}, + {x: rx, y: -ry*1.5}, + {x: rx, y: -ry}, + {x: -rx, y: -ry}, + {x: -rx, y: ry} + ]; + }, + minrx: 30 + }; + } + }, + larrow: { + generator: 'elaborated-rect', + preset: function() { + return { + get_points: function(rx, ry) { + return [ + {x: -rx, y: ry}, + {x: -rx, y: ry*1.5}, + {x: -rx - ry*1.5, y: 0}, + {x: -rx, y: -ry*1.5}, + {x: -rx, y: -ry}, + {x: rx, y: -ry}, + {x: rx, y: ry} + ]; + }, + minrx: 30 + }; + } + }, + rpromoter: { + generator: 'elaborated-rect', + preset: function() { + return { + get_points: function(rx, ry) { + return [ + {x: rx, y: ry}, + {x: rx, y: ry*1.5}, + {x: rx + ry*1.5, y: 0}, + {x: rx, y: -ry*1.5}, + {x: rx, y: -ry}, + {x: -rx, y: -ry}, + {x: -rx, y: ry*1.5}, + {x: 0, y: ry*1.5}, + {x: 0, y: ry}, + ]; + }, + minrx: 30 + }; + } + }, + lpromoter: { + generator: 'elaborated-rect', + preset: function() { + return { + get_points: function(rx, ry) { + return [ + {x: -rx, y: ry}, + {x: -rx, y: ry*1.5}, + {x: -rx - ry*1.5, y: 0}, + {x: -rx, y: -ry*1.5}, + {x: -rx, y: -ry}, + {x: rx, y: -ry}, + {x: rx, y: ry*1.5}, + {x: 0, y: ry*1.5}, + {x: 0, y: ry} + ]; + }, + minrx: 30 + }; + } + }, + cds: { + generator: 'elaborated-rect', + preset: function() { + return { + get_points: function(rx, ry) { + return [ + {x: rx, y: ry}, + {x: rx + ry, y: 0}, + {x: rx, y: -ry}, + {x: -rx, y: -ry}, + {x: -rx, y: ry} + ]; + }, + minrx: 30 + }; + } + }, +}; + +dc_graph.shape_presets.box = dc_graph.shape_presets.rect = dc_graph.shape_presets.rectangle; + +dc_graph.available_shapes = function() { + var shapes = Object.keys(dc_graph.shape_presets); + return shapes.slice(0, shapes.length-1); // not including polygon +}; + +var default_shape = {shape: 'ellipse'}; + +function normalize_shape_def(diagram, n) { + var def = diagram.nodeShape.eval(n); + if(!def) + return default_shape; + if(typeof def === 'string') + return {shape: def}; + return def; +} + +function elaborate_shape(diagram, def) { + var shape = def.shape, def2 = Object.assign({}, def); + delete def2.shape; + if(shape === 'random') { + var available = dc_graph.available_shapes(); // could include diagram.shape !== ellipse, polygon + shape = available[Math.floor(Math.random()*available.length)]; + } + else if(diagram.shape.enum().indexOf(shape) !== -1) + return diagram.shape(shape).elaborate({shape: shape}, def2); + if(!dc_graph.shape_presets[shape]) { + console.warn('unknown shape ', shape); + return default_shape; + } + var preset = dc_graph.shape_presets[shape].preset(def2); + preset.shape = dc_graph.shape_presets[shape].generator; + return diagram.shape(preset.shape).elaborate(preset, def2); +} + +function infer_shape(diagram) { + return function(n) { + var def = normalize_shape_def(diagram, n); + n.dcg_shape = elaborate_shape(diagram, def); + n.dcg_shape.abstract = def; + }; +} + +function shape_changed(diagram) { + return function(n) { + var def = normalize_shape_def(diagram, n); + var old = n.dcg_shape.abstract; + if(def.shape !== old.shape) + return true; + else if(def.shape === 'polygon') { + return def.shape.sides !== old.sides || def.shape.skew !== old.skew || + def.shape.distortion !== old.distortion || def.shape.rotation !== old.rotation; + } + else return false; + }; +} + +function node_label_padding(diagram, n) { + var nlp = diagram.nodeLabelPadding.eval(n); + if(typeof nlp === 'number' || typeof nlp === 'string') + return {x: +nlp, y: +nlp}; + else return nlp; +} + +function fit_shape(shape, diagram) { + return function(content) { + content.each(function(n) { + var bbox = null; + if((!shape.useTextSize || shape.useTextSize(n.dcg_shape)) && diagram.nodeFitLabel.eval(n)) { + bbox = getBBoxNoThrow(this); + bbox = {x: bbox.x, y: bbox.y, width: bbox.width, height: bbox.height}; + var padding; + var content = diagram.nodeContent.eval(n); + if(content && diagram.content(content).padding) + padding = diagram.content(content).padding(n); + else { + var padding2 = node_label_padding(diagram, n); + padding = { + x: padding2.x*2, + y: padding2.y*2 + }; + } + bbox.width += padding.x; + bbox.height += padding.y; + n.bbox = bbox; + } + var r = 0, radii; + if(!shape.useRadius || shape.useRadius(n.dcg_shape)) + r = diagram.nodeRadius.eval(n); + if(bbox && bbox.width && bbox.height || shape.useTextSize && !shape.useTextSize(n.dcg_shape)) + radii = shape.calc_radii(n, r, bbox); + else + radii = {rx: r, ry: r}; + n.dcg_rx = radii.rx; + n.dcg_ry = radii.ry; + + var w = radii.rx*2, h = radii.ry*2; + // fixme: this is only consistent if regular || !squeeze + // but we'd need to calculate polygon first in order to find out + // (not a bad idea, just no time right now) + if(w<h) w = h; + + if(!shape.usePaddingAndStroke || shape.usePaddingAndStroke(n.dcg_shape)) { + var pands = diagram.nodePadding.eval(n) + diagram.nodeStrokeWidth.eval(n); + w += pands; + h += pands; + } + n.cola.width = w; + n.cola.height = h; + }); + }; +} + +function ellipse_attrs(diagram) { + return { + rx: function(n) { return n.dcg_rx; }, + ry: function(n) { return n.dcg_ry; } + }; +} + +function polygon_attrs(diagram, n) { + return { + d: function(n) { + var rx = n.dcg_rx, ry = n.dcg_ry, + def = n.dcg_shape, + sides = def.sides || 4, + skew = def.skew || 0, + distortion = def.distortion || 0, + rotation = def.rotation || 0, + align = (sides%2 ? 0 : 0.5), // even-sided horizontal top, odd pointy top + angles = []; + rotation = rotation/360 + 0.25; // start at y axis not x + for(var i = 0; i<sides; ++i) { + var theta = -((i+align)/sides + rotation)*Math.PI*2; // svg is up-negative + angles.push({x: Math.cos(theta), y: Math.sin(theta)}); + } + var yext = d3.extent(angles, function(theta) { return theta.y; }); + if(def.regular) + rx = ry = Math.max(rx, ry); + else if(rx < ry && !def.squeeze) + rx = ry; + else + ry = ry / Math.min(-yext[0], yext[1]); + n.dcg_points = angles.map(function(theta) { + var x = rx*theta.x, + y = ry*theta.y; + x *= 1 + distortion*((ry-y)/ry - 1); + x -= skew*y/2; + return {x: x, y: y}; + }); + return generate_path(n.dcg_points, 1, true); + } + }; +} + +function binary_search(f, a, b) { + var patience = 100; + if(f(a).val >= 0) + throw new Error("f(a) must be less than 0"); + if(f(b).val <= 0) + throw new Error("f(b) must be greater than 0"); + while(true) { + if(!--patience) + throw new Error("patience ran out"); + var c = (a+b)/2, + f_c = f(c), fv = f_c.val; + if(Math.abs(fv) < 0.5) + return f_c; + if(fv > 0) + b = c; + else + a = c; + } +} + +function draw_edge_to_shapes(diagram, e, sx, sy, tx, ty, + neighbor, dir, offset, source_padding, target_padding) { + var deltaX, deltaY, + sp, tp, points, bezDegree, + headAng, retPath; + if(!neighbor) { + sp = e.sourcePort.pos; + tp = e.targetPort.pos; + if(!sp) sp = {x: 0, y: 0}; + if(!tp) tp = {x: 0, y: 0}; + points = [{ + x: sx + sp.x, + y: sy + sp.y + }, { + x: tx + tp.x, + y: ty + tp.y + }]; + bezDegree = 1; + } + else { + var p_on_s = function(node, ang) { + return diagram.shape(node.dcg_shape.shape).intersect_vec(node, Math.cos(ang)*1000, Math.sin(ang)*1000); + }; + var compare_dist = function(node, port0, goal) { + return function(ang) { + var port = p_on_s(node, ang); + if(!port) + return { + port: {x: 0, y: 0}, + val: 0, + ang: ang + }; + else + return { + port: port, + val: Math.hypot(port.x - port0.x, port.y - port0.y) - goal, + ang: ang + }; + }; + }; + var srcang = Math.atan2(neighbor.sourcePort.y, neighbor.sourcePort.x), + tarang = Math.atan2(neighbor.targetPort.y, neighbor.targetPort.x); + var bss, bst; + + // don't like this but throwing is unacceptable + try { + bss = binary_search(compare_dist(e.source, neighbor.sourcePort, offset), + srcang, srcang + 2 * dir * offset / source_padding); + } + catch(x) { + bss = {ang: srcang, port: neighbor.sourcePort}; + } + try { + bst = binary_search(compare_dist(e.target, neighbor.targetPort, offset), + tarang, tarang - 2 * dir * offset / source_padding); + } + catch(x) { + bst = {ang: tarang, port: neighbor.targetPort}; + } + + sp = bss.port; + tp = bst.port; + var sdist = Math.hypot(sp.x, sp.y), + tdist = Math.hypot(tp.x, tp.y), + c1dist = sdist+source_padding/2, + c2dist = tdist+target_padding/2; + var c1X = sx + c1dist * Math.cos(bss.ang), + c1Y = sy + c1dist * Math.sin(bss.ang), + c2X = tx + c2dist * Math.cos(bst.ang), + c2Y = ty + c2dist * Math.sin(bst.ang); + points = [ + {x: sx + sp.x, y: sy + sp.y}, + {x: c1X, y: c1Y}, + {x: c2X, y: c2Y}, + {x: tx + tp.x, y: ty + tp.y} + ]; + bezDegree = 3; + } + return { + sourcePort: sp, + targetPort: tp, + points: points, + bezDegree: bezDegree + }; +} + +function is_one_segment(path) { + return path.bezDegree === 1 && path.points.length === 2 || + path.bezDegree === 3 && path.points.length === 4; +} + +function as_bezier3(path) { + var p = path.points; + if(path.bezDegree === 3) return p; + else if(path.bezDegree === 1) + return [ + { + x: p[0].x, + y: p[0].y + }, + { + x: p[0].x + (p[1].x - p[0].x)/3, + y: p[0].y + (p[1].y - p[0].y)/3 + }, + { + x: p[0].x + 2*(p[1].x - p[0].x)/3, + y: p[0].y + 2*(p[1].y - p[0].y)/3 + }, + { + x: p[1].x, + y: p[1].y + } + ]; + else throw new Error('unknown bezDegree ' + path.bezDegree); +} + +// from https://www.jasondavies.com/animated-bezier/ +function interpolate(d, p) { + var r = []; + for (var i=1; i<d.length; i++) { + var d0 = d[i-1], d1 = d[i]; + r.push({x: d0.x + (d1.x - d0.x) * p, y: d0.y + (d1.y - d0.y) * p}); + } + return r; +} + +function getLevels(points, t_) { + var x = [points]; + for (var i=1; i<points.length; i++) { + x.push(interpolate(x[x.length-1], t_)); + } + return x; +} + +// get a point on a bezier segment, where 0 <= t <= 1 +function bezier_point(points, t_) { + var q = getLevels(points, t_); + return q[q.length-1][0]; +} + +// from https://stackoverflow.com/questions/8369488/splitting-a-bezier-curve#8405756 +// somewhat redundant with the above but different objective +function split_bezier(p, t) { + var x1 = p[0].x, y1 = p[0].y, + x2 = p[1].x, y2 = p[1].y, + x3 = p[2].x, y3 = p[2].y, + x4 = p[3].x, y4 = p[3].y, + + x12 = (x2-x1)*t+x1, + y12 = (y2-y1)*t+y1, + + x23 = (x3-x2)*t+x2, + y23 = (y3-y2)*t+y2, + + x34 = (x4-x3)*t+x3, + y34 = (y4-y3)*t+y3, + + x123 = (x23-x12)*t+x12, + y123 = (y23-y12)*t+y12, + + x234 = (x34-x23)*t+x23, + y234 = (y34-y23)*t+y23, + + x1234 = (x234-x123)*t+x123, + y1234 = (y234-y123)*t+y123; + + return [ + [{x: x1, y: y1}, {x: x12, y: y12}, {x: x123, y: y123}, {x: x1234, y: y1234}], + [{x: x1234, y: y1234}, {x: x234, y: y234}, {x: x34, y: y34}, {x: x4, y: y4}] + ]; +} +function split_bezier_n(p, n) { + var ret = []; + while(n > 1) { + var parts = split_bezier(p, 1/n); + ret.push(parts[0][0], parts[0][1], parts[0][2]); + p = parts[1]; + --n; + } + ret.push.apply(ret, p); + return ret; +} + +// binary search for a point along a bezier that is a certain distance from one of the end points +// return the bezier cut at that point. +function chop_bezier(points, end, dist) { + var EPS = 0.1, dist2 = dist*dist; + var ref, dir, segment; + if(end === 'head') { + ref = points[points.length-1]; + segment = points.slice(points.length-4); + dir = -1; + } else { + ref = points[0]; + segment = points.slice(0, 4); + dir = 1; + } + var parts, d2, t = 0.5, dt = 0.5, dx, dy; + do { + parts = split_bezier(segment, t); + dx = ref.x - parts[1][0].x; + dy = ref.y - parts[1][0].y; + d2 = dx*dx + dy*dy; + dt /= 2; + if(d2 > dist2) + t -= dt*dir; + else + t += dt*dir; + //console.log('dist', dist, 'dir', dir, 'd', d, 't', t, 'dt', dt); + } + while(dt > 0.0000001 && Math.abs(d2 - dist2) > EPS); + points = points.slice(); + if(end === 'head') + return points.slice(0, points.length-4).concat(parts[0]); + else + return parts[1].concat(points.slice(4)); +} + +function angle_between_points(p0, p1) { + return Math.atan2(p1.y - p0.y, p1.x - p0.x); +} + +dc_graph.no_shape = function() { + var _shape = { + parent: property(null), + elaborate: function(preset, def) { + return Object.assign(preset, def); + }, + useTextSize: function() { return false; }, + useRadius: function() { return false; }, + usePaddingAndStroke: function() { return false; }, + intersect_vec: function(n, deltaX, deltaY) { + return {x: 0, y: 0}; + }, + calc_radii: function(n, ry, bbox) { + return {rx: 0, ry: 0}; + }, + create: function(nodeEnter) { + }, + replace: function(nodeChanged) { + }, + update: function(node) { + } + }; + return _shape; +}; + +dc_graph.ellipse_shape = function() { + var _shape = { + parent: property(null), + elaborate: function(preset, def) { + return Object.assign(preset, def); + }, + intersect_vec: function(n, deltaX, deltaY) { + return point_on_ellipse(n.dcg_rx, n.dcg_ry, deltaX, deltaY); + }, + calc_radii: function(n, ry, bbox) { + // make sure we can fit height in r + ry = Math.max(ry, bbox.height/2 + 5); + var rx = bbox.width/2; + + // solve (x/A)^2 + (y/B)^2) = 1 for A, with B=r, to fit text in ellipse + // http://stackoverflow.com/a/433438/676195 + var y_over_B = bbox.height/2/ry; + rx = rx/Math.sqrt(1 - y_over_B*y_over_B); + rx = Math.max(rx, ry); + + return {rx: rx, ry: ry}; + }, + create: function(nodeEnter) { + nodeEnter.insert('ellipse', ':first-child') + .attr('class', 'node-shape'); + }, + update: function(node) { + node.select('ellipse.node-shape') + .attr(ellipse_attrs(_shape.parent())); + } + }; + return _shape; +}; + +dc_graph.polygon_shape = function() { + var _shape = { + parent: property(null), + elaborate: function(preset, def) { + return Object.assign(preset, def); + }, + intersect_vec: function(n, deltaX, deltaY) { + return point_on_polygon(n.dcg_points, 0, 0, deltaX, deltaY); + }, + calc_radii: function(n, ry, bbox) { + // make sure we can fit height in r + ry = Math.max(ry, bbox.height/2 + 5); + var rx = bbox.width/2; + + // this is cribbed from graphviz but there is much i don't understand + // and any errors are mine + // https://github.com/ellson/graphviz/blob/6acd566eab716c899ef3c4ddc87eceb9b428b627/lib/common/shapes.c#L1996 + rx = rx*Math.sqrt(2)/Math.cos(Math.PI/(n.dcg_shape.sides||4)); + + return {rx: rx, ry: ry}; + }, + create: function(nodeEnter) { + nodeEnter.insert('path', ':first-child') + .attr('class', 'node-shape'); + }, + update: function(node) { + node.select('path.node-shape') + .attr(polygon_attrs(_shape.parent())); + } + }; + return _shape; +}; + +dc_graph.rounded_rectangle_shape = function() { + var _shape = { + parent: property(null), + elaborate: function(preset, def) { + preset = Object.assign({rx: 10, ry: 10}, preset); + return Object.assign(preset, def); + }, + intersect_vec: function(n, deltaX, deltaY) { + var points = [ + {x: n.dcg_rx, y: n.dcg_ry}, + {x: n.dcg_rx, y: -n.dcg_ry}, + {x: -n.dcg_rx, y: -n.dcg_ry}, + {x: -n.dcg_rx, y: n.dcg_ry} + ]; + return point_on_polygon(points, 0, 0, deltaX, deltaY); // not rounded + }, + useRadius: function(shape) { + return !shape.noshape; + }, + calc_radii: function(n, ry, bbox) { + var fity = bbox.height/2; + // fixme: fudge to make sure text is not too tall for node + if(!n.dcg_shape.noshape) + fity += 5; + return { + rx: bbox.width / 2, + ry: Math.max(ry, fity) + }; + }, + create: function(nodeEnter) { + nodeEnter.filter(function(n) { + return !n.dcg_shape.noshape; + }).insert('rect', ':first-child') + .attr('class', 'node-shape'); + }, + update: function(node) { + node.select('rect.node-shape') + .attr({ + x: function(n) { + return -n.dcg_rx; + }, + y: function(n) { + return -n.dcg_ry; + }, + width: function(n) { + return 2*n.dcg_rx; + }, + height: function(n) { + return 2*n.dcg_ry; + }, + rx: function(n) { + return n.dcg_shape.rx + 'px'; + }, + ry: function(n) { + return n.dcg_shape.ry + 'px'; + } + }); + } + }; + return _shape; +}; + +// this is not all that accurate - idea is that arrows, houses, etc, are rectangles +// in terms of sizing, but elaborated drawing & clipping. refine until done. +dc_graph.elaborated_rectangle_shape = function() { + var _shape = dc_graph.rounded_rectangle_shape(); + _shape.intersect_vec = function(n, deltaX, deltaY) { + var points = n.dcg_shape.get_points(n.dcg_rx, n.dcg_ry); + return point_on_polygon(points, 0, 0, deltaX, deltaY); + }; + delete _shape.useRadius; + var orig_radii = _shape.calc_radii; + _shape.calc_radii = function(n, ry, bbox) { + var ret = orig_radii(n, ry, bbox); + return { + rx: Math.max(ret.rx, n.dcg_shape.minrx), + ry: ret.ry + }; + }; + _shape.create = function(nodeEnter) { + nodeEnter.insert('path', ':first-child') + .attr('class', 'node-shape'); + }; + _shape.update = function(node) { + node.select('path.node-shape') + .attr('d', function(n) { + return generate_path(n.dcg_shape.get_points(n.dcg_rx, n.dcg_ry), 1, true); + }); + }; + return _shape; +}; + + +function offsetx(ofsx) { + return function(p) { + return {x: p.x + ofsx, y: p.y}; + }; +} + +dc_graph.builtin_arrows = { + box: function(open, side) { + if(!open) return { + frontRef: [8,0], + drawFunction: function(marker, ofs, stemWidth) { + marker.append('rect') + .attr({ + x: ofs[0], + y: side==='right' ? -stemWidth/2 : -4, + width: 8, + height: side ? 4+stemWidth/2 : 8, + 'stroke-width': 0 + }); + } + }; + else return { + frontRef: [8,0], + drawFunction: function(marker, ofs, stemWidth) { + marker.append('rect') + .attr({ + x: ofs[0] + 0.5, + y: side==='right' ? 0 : -3.5, + width: 7, + height: side ? 3.5 : 7, + 'stroke-width': 1, + fill: 'none' + }); + if(side) + marker.append('svg:path') + .attr({ + d: ['M', ofs[0], 0, 'h',8].join(' '), + 'stroke-width': stemWidth, + fill: 'none' + }); + } + }; + }, + curve: function(open, side) { + return { + stems: [true,false], + kernstems: [0, 0.25], + frontRef: [8,0], + drawFunction: function(marker, ofs, stemWidth) { + var instrs = []; + instrs.push('M', (side==='left' ? 7.5 : 4) + ofs[0], side==='left' ? stemWidth/2 : 3.5); + if(side==='left') + instrs.push('v', -stemWidth/2); + instrs.push('A', 3.5, 3.5, 0, 0, 0, + (side==='right' ? 7.5 : 4) + ofs[0], side==='right' ? 0 : -3.5); + if(side==='right') + instrs.push('v', -stemWidth/2); + marker.append('svg:path') + .attr({ + d: instrs.join(' '), + 'stroke-width': 1, + fill: 'none' + }); + marker.append('svg:path') + .attr({ + d: ['M', 7 + ofs[0], 0, + 'h -7'].join(' '), + 'stroke-width': stemWidth, + fill: 'none' + }); + } + }; + }, + icurve: function(open, side) { + return { + stems: [false,true], + kernstems: [0.25,0], + frontRef: [8,0], + drawFunction: function(marker, ofs, stemWidth) { + var instrs = []; + instrs.push('M', (side==='left' ? 0.5 : 4) + ofs[0], side==='left' ? stemWidth/2 : 3.5); + if(side==='left') + instrs.push('v', -stemWidth/2); + instrs.push('A', 3.5, 3.5, 0, 0, 1, + (side==='right' ? 0.5 : 4) + ofs[0], side==='right' ? 0 : -3.5); + if(side==='right') + instrs.push('v', -stemWidth/2); + marker.append('svg:path') + .attr({ + d: instrs.join(' '), + 'stroke-width': 1, + fill: 'none' + }); + marker.append('svg:path') + .attr({ + d: ['M', 1 + ofs[0], 0, + 'h 7'].join(' '), + 'stroke-width': stemWidth, + fill: 'none' + }); + } + }; + }, + diamond: function(open, side) { + if(!open) return { + frontRef: [side ? 11.25 : 12, 0], + backRef: [side ? 0.75 : 0, 0], + viewBox: [0, -4, 12, 8], + stems: [!!side, !!side], + kernstems: function(stemWidth) { + return [side ? 0 : .75*stemWidth, side ? 0 : .75*stemWidth]; + }, + drawFunction: function(marker, ofs, stemWidth) { + var upoints = [{x: 0, y: 0}]; + if(side !== 'left') + upoints.push({x: 6, y: 4}); + else + upoints.push({x: 6, y: -4}); + upoints.push({x: 12, y: 0}); + if(!side) + upoints.push({x: 6, y: -4}); + var points = upoints.map(offsetx(ofs[0])); + marker.append('svg:path') + .attr({ + d: generate_path(points, 1, true), + 'stroke-width': 0 + }); + if(side) { + marker.append('svg:path') + .attr({ + d: ['M', 0.75 + ofs[0], 0, + 'h 10.5'].join(' '), + 'stroke-width': stemWidth, + fill: 'none' + }); + } + } + }; + else return { + frontRef: [side ? 11.25 : 12, 0], + backRef: [side ? 0.75 : 0, 0], + viewBox: [0, -4, 12, 8], + stems: [!!side, !!side], + kernstems: function(stemWidth) { + return [side ? 0 : .75*stemWidth, side ? 0 : .75*stemWidth]; + }, + drawFunction: function(marker, ofs, stemWidth) { + var upoints = [{x: 0.9, y: 0}]; + if(side !== 'left') + upoints.push({x: 6, y: 3.4}); + else + upoints.push({x: 6, y: -3.4}); + upoints.push({x: 11.1, y: 0}); + if(!side) + upoints.push({x: 6, y: -3.4}); + var points = upoints.map(offsetx(ofs[0])); + marker.append('svg:path') + .attr({ + d: generate_path(points, 1, !side), + 'stroke-width': 1, + fill: 'none' + }); + if(side) { + marker.append('svg:path') + .attr({ + d: ['M', 0.75 + ofs[0], 0, + 'h 10.5'].join(' '), + 'stroke-width': stemWidth, + fill: 'none' + }); + } + } + }; + }, + dot: function(open, side) { + if(!open) return { + frontRef: [8,0], + stems: [!!side, !!side], + drawFunction: function(marker, ofs, stemWidth) { + if(side) { + marker.append('svg:path') + .attr({ + d: ['M', ofs[0], 0, + 'A', 4, 4, 0, 0, side==='left'?1:0, 8 + ofs[0], 0].join(' '), + 'stroke-width': 0 + }); + marker.append('svg:path') + .attr({ + d: ['M', ofs[0], 0, + 'h 8'].join(' '), + 'stroke-width': stemWidth, + fill: 'none' + }); + } + else { + marker.append('svg:circle') + .attr('r', 4) + .attr('cx', 4 + ofs[0]) + .attr('cy', 0) + .attr('stroke-width', '0px'); + } + } + }; + else return { + frontRef: [8,0], + stems: [!!side, !!side], + drawFunction: function(marker, ofs, stemWidth) { + if(side) { + marker.append('svg:path') + .attr({ + d: ['M', 0.5 + ofs[0], 0, + 'A', 3.5, 3.5, 0, 0, side==='left'?1:0, 7.5 + ofs[0], 0].join(' '), + 'stroke-width': 1, + fill: 'none' + }); + marker.append('svg:path') + .attr({ + d: ['M', ofs[0], 0, + 'h 8'].join(' '), + 'stroke-width': stemWidth, + fill: 'none' + }); + } else { + marker.append('svg:circle') + .attr('r', 3.5) + .attr('cx', 4 + ofs[0]) + .attr('cy', 0) + .attr('fill', 'none') + .attr('stroke-width', '1px'); + } + } + }; + }, + normal: function(open, side) { + if(!open) return { + frontRef: [side ? 8-4/3 : 8, 0], + viewBox: [0, -3, 8, 6], + kernstems: function(stemWidth) { + return [0,stemWidth*4/3]; + }, + drawFunction: function(marker, ofs, stemWidth) { + var upoints = []; + if(side === 'left') + upoints.push({x: 0, y: 0}); + else + upoints.push({x: 0, y: 3}); + switch(side) { + case 'left': + upoints.push({x: 8 - stemWidth*4/3, y: -stemWidth/2}); + break; + case 'right': + upoints.push({x: 8 - stemWidth*4/3, y: stemWidth/2}); + break; + default: + upoints.push({x: 8, y: 0}); + } + if(side === 'right') + upoints.push({x: 0, y: 0}); + else + upoints.push({x: 0, y: -3}); + var points = upoints.map(offsetx(ofs[0])); + marker.append('svg:path') + .attr('d', generate_path(points, 1, true)) + .attr('stroke-width', '0px'); + if(side) { + marker.append('svg:path') + .attr({ + d: ['M', ofs[0], 0, + 'h', 8-4*stemWidth/3].join(' '), + 'stroke-width': stemWidth, + fill: 'none' + }); + } + } + }; + else return { + frontRef: [side ? 8-4/3 : 8, 0], + viewBox: [0, -3, 8, 6], + kernstems: function(stemWidth) { + return [0,stemWidth*4/3]; + }, + drawFunction: function(marker, ofs, stemWidth) { + var upoints = []; + if(!side) { + upoints = [ + {x: 0.5, y: 2.28}, + {x: 6.57, y: 0}, + {x: 0.5, y: -2.28} + ]; + } else { + upoints = [ + {x: 0.5, y: 0}, + {x: 0.5, y: side === 'left' ? -2.28 : 2.28}, + {x: 8-4/3, y: 0} + ]; + } + var points = upoints.map(offsetx(ofs[0])); + marker.append('svg:path') + .attr({ + d: generate_path(points, 1, !side), + 'stroke-width': 1, + fill: 'none' + }); + if(side) { + marker.append('svg:path') + .attr({ + d: ['M', ofs[0], 0, + 'h', 8-4/3].join(' '), + 'stroke-width': stemWidth, + fill: 'none' + }); + } + } + }; + }, + inv: function(open, side) { + if(!open) return { + frontRef: [8,0], + backRef: [side ? 4/3 : 0, 0], + viewBox: [0, -3, 8, 6], + kernstems: function(stemWidth) { + return [stemWidth*4/3,0]; + }, + drawFunction: function(marker, ofs, stemWidth) { + var upoints = []; + if(side === 'left') + upoints.push({x: 8, y: 0}); + else + upoints.push({x: 8, y: 3}); + switch(side) { + case 'left': + upoints.push({x: stemWidth*4/3, y: -stemWidth/2}); + break; + case 'right': + upoints.push({x: stemWidth*4/3, y: stemWidth/2}); + break; + default: + upoints.push({x: 0, y: 0}); + } + if(side === 'right') + upoints.push({x: 8, y: 0}); + else + upoints.push({x: 8, y: -3}); + var points = upoints.map(offsetx(ofs[0])); + marker.append('svg:path') + .attr('d', generate_path(points, 1, true)) + .attr('stroke-width', '0px'); + if(side) { + marker.append('svg:path') + .attr({ + d: ['M', 4*stemWidth/3 + ofs[0], 0, + 'h', 8-4*stemWidth/3].join(' '), + 'stroke-width': stemWidth, + fill: 'none' + }); + } + } + }; + else return { + frontRef: [8,0], + backRef: [side ? 4/3 : 0, 0], + viewBox: [0, -3, 8, 6], + kernstems: function(stemWidth) { + return [stemWidth*4/3,0]; + }, + drawFunction: function(marker, ofs, stemWidth) { + var upoints = []; + if(!side) { + upoints = [ + {x: 7.5, y: 2.28}, + {x: 1.43, y: 0}, + {x: 7.5, y: -2.28} + ]; + } else { + upoints = [ + {x: 7.5, y: 0}, + {x: 7.5, y: side === 'left' ? -2.28 : 2.28}, + {x: 1.43, y: 0} + ]; + } + var points = upoints.map(offsetx(ofs[0])); + marker.append('svg:path') + .attr({ + d: generate_path(points, 1, !side), + 'stroke-width': 1, + fill: 'none' + }); + if(side) { + marker.append('svg:path') + .attr({ + d: ['M', 4*stemWidth/3 + ofs[0], 0, + 'h', 8-4/3].join(' '), + 'stroke-width': stemWidth, + fill: 'none' + }); + } + } + }; + }, + tee: function(open, side) { + return { + frontRef: [5,0], + viewBox: [0, -5, 5, 10], + stems: [true,false], + drawFunction: function(marker, ofs, stemWidth) { + var b = side === 'right' ? 0 : -5, + t = side === 'left' ? 0 : 5; + var points = [ + {x: 2, y: t}, + {x: 5, y: t}, + {x: 5, y: b}, + {x: 2, y: b} + ].map(offsetx(ofs[0])); + marker.append('svg:path') + .attr('d', generate_path(points, 1, true)) + .attr('stroke-width', '0px'); + marker.append('svg:path') + .attr('d', ['M', ofs[0], 0, 'h', 5].join(' ')) + .attr('stroke-width', stemWidth) + .attr('fill', 'none'); + } + }; + }, + vee: function(open, side) { + return { + stems: [true,false], + kernstems: function(stemWidth) { + return [0,stemWidth]; + }, + drawFunction: function(marker, ofs, stemWidth) { + var upoints = [ + {x: 0, y: -5}, + {x: 10, y: 0}, + {x: 0, y: 5}, + {x: 5, y: 0} + ]; + if(side==='right') + upoints.splice(0, 1, + {x: 5, y: -stemWidth/2}, + {x: 10, y: -stemWidth/2}); + else if(side==='left') + upoints.splice(2, 1, + {x: 10, y: stemWidth/2}, + {x: 5, y: stemWidth/2}); + var points = upoints.map(offsetx(ofs[0])); + marker.append('svg:path') + .attr('d', generate_path(points, 1, true)) + .attr('stroke-width', '0px'); + marker.append('svg:path') + .attr('d', ['M', ofs[0]+5, 0, 'h',-5].join(' ')) + .attr('stroke-width', stemWidth); + } + }; + }, + crow: function(open, side) { + return { + stems: [false,true], + kernstems: function(stemWidth) { + return [stemWidth,0]; + }, + drawFunction: function(marker, ofs, stemWidth) { + var upoints = [ + {x: 10, y: -5}, + {x: 0, y: 0}, + {x: 10, y: 5}, + {x: 5, y: 0} + ]; + if(side==='right') + upoints.splice(0, 1, + {x: 5, y: -stemWidth/2}, + {x: 0, y: -stemWidth/2}); + else if(side==='left') + upoints.splice(2, 1, + {x: 0, y: stemWidth/2}, + {x: 5, y: stemWidth/2}); + var points = upoints.map(offsetx(ofs[0])); + marker.append('svg:path') + .attr('d', generate_path(points, 1, true)) + .attr('stroke-width', '0px'); + marker.append('svg:path') + .attr('d', ['M', ofs[0]+5, 0, 'h',5].join(' ')) + .attr('stroke-width', stemWidth); + } + }; + } +}; + +function arrow_def(arrdefs, shape, open, side) { + return arrdefs[shape](open, side); +} + +function arrow_parts(arrdefs, desc) { + // graphviz appears to use a real parser for this + var parts = []; + while(desc && desc.length) { + var mods = /^o?(?:l|r)?/.exec(desc); + var open = false, side = null; + if(mods[0]) { + mods = mods[0]; + desc = desc.slice(mods.length); + open = mods[0] === 'o'; + switch(mods[mods.length-1]) { + case 'l': + side='left'; + break; + case 'r': + side='right'; + } + } + var ok = false; + for(var aname in arrdefs) + if(desc.substring(0, aname.length) === aname) { + ok = true; + parts.push(arrow_def(arrdefs, aname, open, side)); + desc = desc.slice(aname.length); + break; + } + if(!ok) { + console.warn("couldn't find arrow name in " + desc); + break; + } + } + return parts; +} + +function union_viewbox(vb1, vb2) { + var left = Math.min(vb1[0], vb2[0]), + bottom = Math.min(vb1[1], vb2[1]), + right = Math.max(vb1[0] + vb1[2], vb2[0] + vb2[2]), + top = Math.max(vb1[1] + vb1[3], vb2[1] + vb2[3]); + return [left, bottom, right - left, top - bottom]; +} + +function subtract_points(p1, p2) { + return [p1[0] - p2[0], p1[1] - p2[1]]; +} + +function add_points(p1, p2) { + return [p1[0] + p2[0], p1[1] + p2[1]]; +} + +function mult_point(p, s) { + return p.map(function(x) { return x*s; }); +} + +function defaulted(def) { + return function(x) { + return x || def; + }; +} + +var view_box = defaulted([0, -5, 10, 10]), + front_ref = defaulted([10, 0]), + back_ref = defaulted([0, 0]); + +function arrow_offsets(parts, stemWidth) { + var frontRef = null, backRef = null; + return parts.map(function(p, i) { + var fr = front_ref(p.frontRef).slice(), + br = back_ref(p.backRef).slice(); + if(p.kernstems) { + var kernstems = p.kernstems; + if(typeof kernstems === 'function') + kernstems = kernstems(stemWidth); + if(i !== 0 && kernstems[1]) { + var last = parts[i-1]; + if(last.stems && last.stems[0]) + fr[0] -= kernstems[1]; + } + if(kernstems[0]) { + var kern = false; + if(i === parts.length-1) + kern = true; + else { + var next = parts[i+1]; + if(next.stems && next.stems[1]) + kern = true; + } + if(kern) + br[0] += kernstems[0]; + } + } + if(i === 0) { + frontRef = fr; + backRef = br; + return {backRef: backRef, offset: [0, 0]}; + } else { + var ofs = subtract_points(backRef, fr); + backRef = add_points(br, ofs); + return {backRef: backRef, offset: ofs}; + } + }); +} + +function arrow_bounds(parts, stemWidth) { + var viewBox = null, offsets = arrow_offsets(parts, stemWidth); + parts.forEach(function(p, i) { + var vb = view_box(p.viewBox); + var ofs = offsets[i].offset; + if(!viewBox) + viewBox = vb.slice(); + else + viewBox = union_viewbox(viewBox, [vb[0] + ofs[0], vb[1] + ofs[1], vb[2], vb[3]]); + }); + return {offsets: offsets, viewBox: viewBox}; +} + +function arrow_length(parts, stemWidth) { + if(!parts.length) + return 0; + var offsets = arrow_offsets(parts, stemWidth); + return front_ref(parts[0].frontRef)[0] - offsets[parts.length-1].backRef[0]; +} + + +function scaled_arrow_lengths(diagram, e) { + var arrowSize = diagram.edgeArrowSize.eval(e), + stemWidth = diagram.edgeStrokeWidth.eval(e) / arrowSize; + var headLength = arrowSize * + (arrow_length(arrow_parts(diagram.arrows(), diagram.edgeArrowhead.eval(e)), stemWidth) + + diagram.nodeStrokeWidth.eval(e.target) / 2), + tailLength = arrowSize * + (arrow_length(arrow_parts(diagram.arrows(), diagram.edgeArrowtail.eval(e)), stemWidth) + + diagram.nodeStrokeWidth.eval(e.source) / 2); + return {headLength: headLength, tailLength: tailLength}; +} + +function clip_path_to_arrows(headLength, tailLength, path) { + var points0 = as_bezier3(path), + points = chop_bezier(points0, 'head', headLength); + return { + bezDegree: 3, + points: chop_bezier(points, 'tail', tailLength), + sourcePort: path.sourcePort, + targetPort: path.targetPort + }; +} + +function place_arrows_on_spline(diagram, e, points) { + var alengths = scaled_arrow_lengths(diagram, e); + var path0 = { + points: points, + bezDegree: 3 + }; + var path = clip_path_to_arrows(alengths.headLength, alengths.tailLength, path0); + return { + path: path, + full: path0, + orienthead: angle_between_points(path.points[path.points.length-1], path0.points[path0.points.length-1]) + 'rad', //calculate_arrowhead_orientation(e.cola.points, 'head'), + orienttail: angle_between_points(path.points[0], path0.points[0]) + 'rad' //calculate_arrowhead_orientation(e.cola.points, 'tail') + }; +} + + +// determine pre-transition orientation that won't spin a lot going to new orientation +function unsurprising_orient(oldorient, neworient) { + var oldang = +oldorient.slice(0, -3), + newang = +neworient.slice(0, -3); + if(Math.abs(oldang - newang) > Math.PI) { + if(newang > oldang) + oldang += 2*Math.PI; + else oldang -= 2*Math.PI; + } + return oldang; +} + + +function edgeArrow(diagram, arrdefs, e, kind, desc) { + var id = diagram.arrowId(e, kind); + var strokeOfs, edgeStroke; + function arrow_sig() { + return desc + '-' + strokeOfs + '-' + edgeStroke; + } + if(desc) { + strokeOfs = diagram.nodeStrokeWidth.eval(kind==='tail' ? e.source : e.target)/2; + edgeStroke = diagram.edgeStroke.eval(e); + if(e[kind + 'ArrowLast'] === arrow_sig()) + return id; + } + var parts = arrow_parts(arrdefs, desc), + marker = diagram.addOrRemoveDef(id, !!parts.length, 'svg:marker'); + + if(parts.length) { + var arrowSize = diagram.edgeArrowSize.eval(e), + stemWidth = diagram.edgeStrokeWidth.eval(e) / arrowSize, + bounds = arrow_bounds(parts, stemWidth), + frontRef = front_ref(parts[0].frontRef); + bounds.viewBox[0] -= strokeOfs/arrowSize; + bounds.viewBox[3] += strokeOfs/arrowSize; + marker + .attr('viewBox', bounds.viewBox.join(' ')) + .attr('refX', frontRef[0]) + .attr('refY', frontRef[1]) + .attr('markerUnits', 'userSpaceOnUse') + .attr('markerWidth', bounds.viewBox[2]*arrowSize) + .attr('markerHeight', bounds.viewBox[3]*arrowSize) + .attr('stroke', edgeStroke) + .attr('fill', edgeStroke); + marker.html(null); + parts.forEach(function(p, i) { + marker + .call(p.drawFunction, + add_points([-strokeOfs/arrowSize,0], bounds.offsets[i].offset), + stemWidth); + }); + } + e[kind + 'ArrowLast'] = arrow_sig(); + return desc ? id : null; +} + +dc_graph.text_contents = function() { + var _contents = { + parent: property(null), + update: function(container) { + var text = container.selectAll('text.node-label') + .data(function(n) { return [n]; }); + text.enter().append('text') + .attr('class', 'node-label'); + var tspan = text.selectAll('tspan').data(function(n) { + var lines = _contents.parent().nodeLabel.eval(n); + if(!lines) + return []; + else if(typeof lines === 'string') + lines = [lines]; + var lineHeight = _contents.parent().nodeLineHeight(); + var first = 0.5 - ((lines.length - 1) * lineHeight + 1)/2; + // IE, Edge, and Safari do not seem to support + // dominant-baseline: central although they say they do + if(is_ie() || is_safari()) + first += 0.3; + return lines.map(function(line, i) { return {node: n, line: line, yofs: (i==0 ? first : lineHeight) + 'em'}; }); + }); + tspan.enter().append('tspan'); + tspan.attr({ + 'text-anchor': 'start', + 'text-decoration': function(line) { + return _contents.parent().nodeLabelDecoration.eval(line.node); + }, + x: 0 + }).html(function(s) { return s.line; }); + text + .each(function(n) { + n.xofs = 0; + }) + .filter(function(n) { + return _contents.parent().nodeLabelAlignment.eval(n) !== 'center'; + }) + .each(function(n) { + var bbox = getBBoxNoThrow(this); + n.bbox = {x: bbox.x, y: bbox.y, width: bbox.width, height: bbox.height}; + switch(_contents.parent().nodeLabelAlignment.eval(n)) { + case 'left': n.xofs = -n.bbox.width/2; + break; + case 'right': n.xofs = n.bbox.width/2; + break; + } + }) + .selectAll('tspan'); + tspan.attr({ + 'text-anchor': function(s) { + switch(_contents.parent().nodeLabelAlignment.eval(s.node)) { + case 'left': return 'start'; + case 'center': return 'middle'; + case 'right': return 'end'; + } + return null; + }, + x: function(s) { + return s.node.xofs; + }, + dy: function(d) { return d.yofs; } + }); + + tspan.exit().remove(); + text + .attr('fill', _contents.parent().nodeLabelFill.eval); + }, + textbox: function(container) { + var bbox = getBBoxNoThrow(this.selectContent(container).node()); + return {x: bbox.x, y: bbox.y, width: bbox.width, height: bbox.height}; + }, + selectContent: function(container) { + return container.select('text.node-label'); + }, + selectText: function(container) { + return this.selectContent(container); + } + }; + return _contents; +}; + +dc_graph.with_icon_contents = function(contents, width, height) { + var _contents = { + parent: property(null).react(function(parent) { + contents.parent(parent); + }), + padding: function(n) { + var padding = node_label_padding(_contents.parent(), n); + return { + x: padding.x * 3, + y: padding.y * 3 + }; + }, + update: function(container) { + var g = container.selectAll('g.with-icon') + .data(function(n) { return [n]; }); + var gEnter = g.enter(); + gEnter.append('g') + .attr('class', 'with-icon') + .append('image').attr({ + class: 'icon', + width: width + 'px', + height: height + 'px' + }); + g.call(contents.update); + contents.selectContent(g) + .attr('transform', 'translate(' + width/2 + ')'); + g.selectAll('image.icon').attr({ + href: _contents.parent().nodeIcon.eval, + x: function(n) { + var totwid = width + contents.textbox(d3.select(this.parentNode)).width; + return -totwid/2 - node_label_padding(_contents.parent(), n).x; + }, + y: -height/2 + }); + }, + textbox: function(container) { + var box = contents.textbox(container); + box.x += width/2; + return box; + }, + selectContent: function(container) { + return container.select('g.with-icon'); + }, + selectText: function(container) { + return this.selectContent(container).select('text.node-label'); + } + }; + return _contents; +}; + + +/** + * `dc_graph.diagram` is a dc.js-compatible network visualization component. It registers in + * the dc.js chart registry and its nodes and edges are generated from crossfilter groups. It + * logically derives from the dc.js + * {@link https://github.com/dc-js/dc.js/blob/develop/web/docs/api-latest.md#dc.baseMixin baseMixin}, + * but it does not physically derive from it since so much is different about network + * visualization versus conventional charts. + * @class diagram + * @memberof dc_graph + * @param {String|node} parent - Any valid + * {@link https://github.com/mbostock/d3/wiki/Selections#selecting-elements d3 single selector} + * specifying a dom block element such as a div; or a dom element. + * @param {String} [chartGroup] - The name of the dc.js chart group this diagram instance + * should be placed in. Filter interaction with a diagram will only trigger events and redraws + * within the diagram's group. + * @return {dc_graph.diagram} + **/ +dc_graph.diagram = function (parent, chartGroup) { + // different enough from regular dc charts that we don't use dc.baseMixin + // but attempt to implement most of that interface, copying some of the most basic stuff + var _diagram = dc.marginMixin({}); + _diagram.__dcFlag__ = dc.utils.uniqueId(); + _diagram.margins({left: 10, top: 10, right: 10, bottom: 10}); + var _dispatch = d3.dispatch('preDraw', 'data', 'end', 'start', 'render', 'drawn', 'receivedLayout', 'transitionsStarted', 'zoomed', 'reset'); + var _nodes = {}, _edges = {}; // hold state between runs + var _ports = {}; // id = node|edge/id/name + var _clusters = {}; + var _nodePorts; // ports sorted by node id + var _stats = {}; + var _nodes_snapshot, _edges_snapshot; + var _arrows = {}; + var _running = false; // for detecting concurrency issues + var _anchor, _chartGroup; + var _animateZoom; + + var _minWidth = 200; + var _defaultWidthCalc = function (element) { + var width = element && element.getBoundingClientRect && element.getBoundingClientRect().width; + return (width && width > _minWidth) ? width : _minWidth; + }; + var _widthCalc = _defaultWidthCalc; + + var _minHeight = 200; + var _defaultHeightCalc = function (element) { + var height = element && element.getBoundingClientRect && element.getBoundingClientRect().height; + return (height && height > _minHeight) ? height : _minHeight; + }; + var _heightCalc = _defaultHeightCalc; + var _width, _height, _lastWidth, _lastHeight; + + function deprecate_layout_algo_parameter(name) { + return function(value) { + if(!_diagram.layoutEngine()) + _diagram.layoutAlgorithm('cola', true); + var engine = _diagram.layoutEngine(); + if(engine.getEngine) + engine = engine.getEngine(); + if(engine[name]) { + console.warn('property is deprecated, call on layout engine instead: dc_graph.diagram.%c' + name, + 'font-weight: bold'); + if(!arguments.length) + return engine[name](); + engine[name](value); + } else { + console.warn('property is deprecated, and is not supported for Warning: dc_graph.diagram.<b>' + name + '</b> is deprecated, and it is not supported for the "' + engine.layoutAlgorithm() + '" layout algorithm: ignored.'); + if(!arguments.length) + return null; + } + return this; + }; + } + + /** + * Set or get the height attribute of the diagram. If a value is given, then the diagram is + * returned for method chaining. If no value is given, then the current value of the height + * attribute will be returned. + * + * The width and height are applied to the SVG element generated by the diagram on render, or + * when `resizeSvg` is called. + * + * If the value is falsy or a function, the height will be calculated the first time it is + * needed, using the provided function or default height calculator, and then cached. The + * default calculator uses the client rect of the element specified when constructing the chart, + * with a minimum of `minHeight`. A custom calculator will be passed the element. + * + * If the value is `'auto'`, the height will be calculated every time the diagram is drawn, and + * it will not be set on the `<svg>` element. Instead, the element will be pinned to the same + * rectangle as its containing div using CSS. + * + * @method height + * @memberof dc_graph.diagram + * @instance + * @param {Number} [height=200] + * @return {Number} + * @return {dc_graph.diagram} + **/ + _diagram.height = function (height) { + if (!arguments.length) { + if (!dc.utils.isNumber(_height)) { + _lastHeight = _heightCalc(_diagram.root().node()); + if(_height === 'auto') // 'auto' => calculate every time + return _lastHeight; + // null/undefined => calculate once only + _height = _lastHeight; + } + return _height; + } + if(dc.utils.isNumber(height) || !height || height === 'auto') + _height = height; + else if(typeof height === 'function') { + _heightCalc = height; + _height = undefined; + } + else throw new Error("don't know what to do with height type " + typeof height + " value " + height); + return _diagram; + }; + _diagram.minHeight = function(height) { + if(!arguments.length) + return _minHeight; + _minHeight = height; + return _diagram; + }; + /** + * Set or get the width attribute of the diagram. If a value is given, then the diagram is + * returned for method chaining. If no value is given, then the current value of the width + * attribute will be returned. + * + * The width and height are applied to the SVG element generated by the diagram on render, or + * when `resizeSvg` is called. + * + * If the value is falsy or a function, the width will be calculated the first time it is + * needed, using the provided function or default width calculator, and then cached. The default + * calculator uses the client rect of the element specified when constructing the chart, with a + * minimum of `minWidth`. A custom calculator will be passed the element. + * + * If the value is `'auto'`, the width will be calculated every time the diagram is drawn, and + * it will not be set on the `<svg>` element. Instead, the element will be pinned to the same + * rectangle as its containing div using CSS. + * + * @method width + * @memberof dc_graph.diagram + * @instance + * @param {Number} [width=200] + * @return {Number} + * @return {dc_graph.diagram} + **/ + _diagram.width = function (width) { + if (!arguments.length) { + if (!dc.utils.isNumber(_width)) { + _lastWidth = _widthCalc(_diagram.root().node()); + if(_width === 'auto') // 'auto' => calculate every time + return _lastWidth; + // null/undefined => calculate once only + _width = _lastWidth; + } + return _width; + } + if(dc.utils.isNumber(width) || !width || width === 'auto') + _width = width; + else if(typeof width === 'function') { + _widthCalc = width; + _width = undefined; + } + else throw new Error("don't know what to do with width type " + typeof width + " value " + width); + return _diagram; + }; + _diagram.minWidth = function(width) { + if(!arguments.length) + return _minWidth; + _minWidth = width; + return _diagram; + }; + + /** + * Get or set the root element, which is usually the parent div. Normally the root is set + * when the diagram is constructed; setting it later may have unexpected consequences. + * @method root + * @memberof dc_graph.diagram + * @instance + * @param {node} [root=null] + * @return {node} + * @return {dc_graph.diagram} + **/ + _diagram.root = property(null).react(function(e) { + if(e.empty()) + console.log('Warning: parent selector ' + parent + " doesn't seem to exist"); + }); + + /** + * Get or set whether mouse wheel rotation or touchpad gestures will zoom the diagram, and + * whether dragging on the background pans the diagram. + * @method mouseZoomable + * @memberof dc_graph.diagram + * @instance + * @param {Boolean} [mouseZoomable=true] + * @return {Boolean} + * @return {dc_graph.diagram} + **/ + _diagram.mouseZoomable = property(true); + + _diagram.zoomExtent = property([.1, 2]); + + /** + * Whether zooming should only be enabled when the alt key is pressed. + * @method altKeyZoom + * @memberof dc_graph.diagram + * @instance + * @param {Boolean} [altKeyZoom=true] + * @return {Boolean} + * @return {dc_graph.diagram} + **/ + _diagram.modKeyZoom = _diagram.altKeyZoom = property(false); + + /** + * Set or get the fitting strategy for the canvas, which affects how the translate + * and scale get calculated when `autoZoom` is triggered. + * + * * `'default'` - simulates the preserveAspectRatio behavior of `xMidYMid meet`, but + * with margins - the content is stretched or squished in the more constrained + * direction, and centered in the other direction + * * `'vertical'` - fits the canvas vertically (with vertical margins) and centers + * it horizontally. If the canvas is taller than the viewport, it will meet + * vertically and there will be blank areas to the left and right. If the canvas + * is wider than the viewport, it will be sliced. + * * `'horizontal'` - fits the canvas horizontally (with horizontal margins) and + * centers it vertically. If the canvas is wider than the viewport, it will meet + * horizontally and there will be blank areas above and below. If the canvas is + * taller than the viewport, it will be sliced. + * + * Other options + * * `null` - no attempt is made to fit the content in the viewport + * * `'zoom'` - does not scale the content, but attempts to bring as much content + * into view as possible, using using the same algorithm as `restrictPan` + * * `'align_{tlbrc}[2]'` - does not scale; aligns up to two sides or centers them + * @method fitStrategy + * @memberof dc_graph.diagram + * @instance + * @param {String} [fitStrategy='default'] + * @return {String} + * @return {dc_graph.diagram} + **/ + _diagram.fitStrategy = property('default'); + + /** + * Do not allow panning (scrolling) to push the diagram out of the viewable area, if there + * is space for it to be shown. */ + _diagram.restrictPan = property(false); + + /** + * Auto-zoom behavior. + * * `'always'` - zoom every time layout happens + * * `'once'` - zoom the next time layout happens + * * `null` - manual, call `zoomToFit` to fit + * @method autoZoom + * @memberof dc_graph.diagram + * @instance + * @param {String} [autoZoom=null] + * @return {String} + * @return {dc_graph.diagram} + **/ + _diagram.autoZoom = property(null); + _diagram.zoomToFit = function(animate) { + // if(!(_nodeLayer && _edgeLayer)) + // return; + auto_zoom(animate); + }; + _diagram.zoomDuration = property(500); + + /** + * Set or get the crossfilter dimension which represents the nodes (vertices) in the + * diagram. Typically there will be a crossfilter instance for the nodes, and another for + * the edges. + * + * *Dimensions are included on the diagram for similarity to dc.js, however the diagram + * itself does not use them - but {@link dc_graph.filter_selection filter_selection} will.* + * @method nodeDimension + * @memberof dc_graph.diagram + * @instance + * @param {crossfilter.dimension} [nodeDimension] + * @return {crossfilter.dimension} + * @return {dc_graph.diagram} + **/ + _diagram.nodeDimension = property(); + + /** + * Set or get the crossfilter group which is the data source for the nodes in the + * diagram. The diagram will use the group's `.all()` method to get an array of `{key, + * value}` pairs, where the key is a unique identifier, and the value is usually an object + * containing the node's attributes. All accessors work with these key/value pairs. + * + * If the group is changed or returns different values, the next call to `.redraw()` will + * reflect the changes incrementally. + * + * It is possible to pass another object with the same `.all()` interface instead of a + * crossfilter group. + * @method nodeGroup + * @memberof dc_graph.diagram + * @instance + * @param {crossfilter.group} [nodeGroup] + * @return {crossfilter.group} + * @return {dc_graph.diagram} + **/ + _diagram.nodeGroup = property(); + + /** + * Set or get the crossfilter dimension which represents the edges in the + * diagram. Typically there will be a crossfilter instance for the nodes, and another for + * the edges. + * + * *Dimensions are included on the diagram for similarity to dc.js, however the diagram + * itself does not use them - but {@link dc_graph.filter_selection filter_selection} will.* + * @method edgeDimension + * @memberof dc_graph.diagram + * @instance + * @param {crossfilter.dimension} [edgeDimension] + * @return {crossfilter.dimension} + * @return {dc_graph.diagram} + **/ + _diagram.edgeDimension = property(); + + /** + * Set or get the crossfilter group which is the data source for the edges in the + * diagram. See `.nodeGroup` above for the way data is loaded from a crossfilter group. + * + * The values in the key/value pairs returned by `diagram.edgeGroup().all()` need to + * support, at a minimum, the {@link dc_graph.diagram#nodeSource nodeSource} and + * {@link dc_graph.diagram#nodeTarget nodeTarget}, which should return the same + * keys as the {@link dc_graph.diagram#nodeKey nodeKey} + * + * @method edgeGroup + * @memberof dc_graph.diagram + * @instance + * @param {crossfilter.group} [edgeGroup] + * @return {crossfilter.group} + * @return {dc_graph.diagram} + **/ + _diagram.edgeGroup = property(); + + _diagram.edgesInFront = property(false); + + /** + * Set or get the function which will be used to retrieve the unique key for each node. By + * default, this accesses the `key` field of the object passed to it. The keys should match + * the keys returned by the {@link dc_graph.diagram#edgeSource edgeSource} and + * {@link dc_graph.diagram#edgeTarget edgeTarget}. + * + * @method nodeKey + * @memberof dc_graph.diagram + * @instance + * @param {Function} [nodeKey=function(kv) { return kv.key }] + * @return {Function} + * @return {dc_graph.diagram} + **/ + _diagram.nodeKey = _diagram.nodeKeyAccessor = property(function(kv) { + return kv.key; + }); + + /** + * Set or get the function which will be used to retrieve the unique key for each edge. By + * default, this accesses the `key` field of the object passed to it. + * + * @method edgeKey + * @memberof dc_graph.diagram + * @instance + * @param {Function} [edgeKey=function(kv) { return kv.key }] + * @return {Function} + * @return {dc_graph.diagram} + **/ + _diagram.edgeKey = _diagram.edgeKeyAccessor = property(function(kv) { + return kv.key; + }); + + /** + * Set or get the function which will be used to retrieve the source (origin/tail) key of + * the edge objects. The key must equal the key returned by the `.nodeKey` for one of the + * nodes; if it does not, or if the node is currently filtered out, the edge will not be + * displayed. By default, looks for `.value.sourcename`. + * + * @method edgeSource + * @memberof dc_graph.diagram + * @instance + * @param {Function} [edgeSource=function(kv) { return kv.value.sourcename; }] + * @return {Function} + * @return {dc_graph.diagram} + **/ + _diagram.edgeSource = _diagram.sourceAccessor = property(function(kv) { + return kv.value.sourcename; + }); + + /** + * Set or get the function which will be used to retrieve the target (destination/head) key + * of the edge objects. The key must equal the key returned by the + * {@link dc_graph.diagram#nodeKey nodeKey} for one of the nodes; if it does not, or if the node + * is currently filtered out, the edge will not be displayed. By default, looks for + * `.value.targetname`. + * @method edgeTarget + * @memberof dc_graph.diagram + * @instance + * @param {Function} [edgeTarget=function(kv) { return kv.value.targetname; }] + * @return {Function} + * @return {dc_graph.diagram} + **/ + _diagram.edgeTarget = _diagram.targetAccessor = property(function(kv) { + return kv.value.targetname; + }); + + _diagram.portDimension = property(null); + _diagram.portGroup = property(null); + _diagram.portNodeKey = property(null); + _diagram.portEdgeKey = property(null); + _diagram.portName = property(null); + _diagram.portStyleName = property(null); + _diagram.portElastic = property(true); + + _diagram.portStyle = named_children(); + + _diagram.portBounds = property(null); // position limits, in radians + + _diagram.edgeSourcePortName = property(null); + _diagram.edgeTargetPortName = property(null); + + /** + * Set or get the crossfilter dimension which represents the edges in the + * diagram. Typically there will be a crossfilter instance for the nodes, and another for + * the edges. + * + * *As with node and edge dimensions, the diagram will itself not filter on cluster dimensions; + * this is included for symmetry, and for modes which may want to filter clusters.* + * @method clusterDimension + * @memberof dc_graph.diagram + * @instance + * @param {crossfilter.dimension} [clusterDimension] + * @return {crossfilter.dimension} + * @return {dc_graph.diagram} + **/ + _diagram.clusterDimension = property(null); + + /** + * Set or get the crossfilter group which is the data source for clusters in the + * diagram. + * + * The key/value pairs returned by `diagram.clusterGroup().all()` need to support, at a minimum, + * the {@link dc_graph.diagram#clusterKey clusterKey} and {@link dc_graph.diagram#clusterParent clusterParent} + * accessors, which should return keys in this group. + * + * @method clusterGroup + * @memberof dc_graph.diagram + * @instance + * @param {crossfilter.group} [clusterGroup] + * @return {crossfilter.group} + * @return {dc_graph.diagram} + **/ + _diagram.clusterGroup = property(null); + + // cluster accessors + /** + * Set or get the function which will be used to retrieve the unique key for each cluster. By + * default, this accesses the `key` field of the object passed to it. + * + * @method clusterKey + * @memberof dc_graph.diagram + * @instance + * @param {Function} [clusterKey=function(kv) { return kv.key }] + * @return {Function} + * @return {dc_graph.diagram} + **/ + _diagram.clusterKey = property(dc.pluck('key')); + + /** + * Set or get the function which will be used to retrieve the key of the parent of a cluster, + * which is another cluster. + * + * @method clusterParent + * @memberof dc_graph.diagram + * @instance + * @param {Function} [clusterParent=function(kv) { return kv.key }] + * @return {Function} + * @return {dc_graph.diagram} + **/ + _diagram.clusterParent = property(null); + + /** + * Set or get the function which will be used to retrieve the padding, in pixels, around a cluster. + * + * **To be implemented.** If a single value is returned, it will be used on all sides; if two + * values are returned they will be interpreted as the vertical and horizontal padding. + * + * @method clusterPadding + * @memberof dc_graph.diagram + * @instance + * @param {Function} [clusterPadding=function(kv) { return kv.key }] + * @return {Function} + * @return {dc_graph.diagram} + **/ + _diagram.clusterPadding = property(8); + + // node accessor + /** + * Set or get the function which will be used to retrieve the parent cluster of a node, or + * `null` if the node is not in a cluster. + * + * @method nodeParentCluster + * @memberof dc_graph.diagram + * @instance + * @param {Function} [nodeParentCluster=function(kv) { return kv.key }] + * @return {Function} + * @return {dc_graph.diagram} + **/ + _diagram.nodeParentCluster = property(null); + + /** + * Set or get the function which will be used to retrieve the radius, in pixels, for each + * node. This determines the height of nodes,and if `nodeFitLabel` is false, the width too. + * @method nodeRadius + * @memberof dc_graph.diagram + * @instance + * @param {Function|Number} [nodeRadius=25] + * @return {Function|Number} + * @return {dc_graph.diagram} + **/ + _diagram.nodeRadius = _diagram.nodeRadiusAccessor = property(25); + + /** + * Set or get the function which will be used to retrieve the stroke width, in pixels, for + * drawing the outline of each node. According to the SVG specification, the outline will + * be drawn half on top of the fill, and half outside. Default: 1 + * @method nodeStrokeWidth + * @memberof dc_graph.diagram + * @instance + * @param {Function|Number} [nodeStrokeWidth=1] + * @return {Function|Number} + * @return {dc_graph.diagram} + **/ + _diagram.nodeStrokeWidth = _diagram.nodeStrokeWidthAccessor = property(1); + + /** + * Set or get the function which will be used to retrieve the stroke color for the outline + * of each node. + * @method nodeStroke + * @memberof dc_graph.diagram + * @instance + * @param {Function|String} [nodeStroke='black'] + * @return {Function|String} + * @return {dc_graph.diagram} + **/ + _diagram.nodeStroke = _diagram.nodeStrokeAccessor = property('black'); + + _diagram.nodeStrokeDashArray = property(null); + + /** + * If set, the value returned from `nodeFill` will be processed through this + * {@link https://github.com/mbostock/d3/wiki/Scales d3.scale} + * to return the fill color. If falsy, uses the identity function (no scale). + * @method nodeFillScale + * @memberof dc_graph.diagram + * @instance + * @param {Function|d3.scale} [nodeFillScale] + * @return {Function|d3.scale} + * @return {dc_graph.diagram} + **/ + _diagram.nodeFillScale = property(null); + + /** + * Set or get the function which will be used to retrieve the fill color for the body of each + * node. + * @method nodeFill + * @memberof dc_graph.diagram + * @instance + * @param {Function|String} [nodeFill='white'] + * @return {Function|String} + * @return {dc_graph.diagram} + **/ + _diagram.nodeFill = _diagram.nodeFillAccessor = property('white'); + + /** + * Set or get the function which will be used to retrieve the opacity of each node. + * @method nodeOpacity + * @memberof dc_graph.diagram + * @instance + * @param {Function|Number} [nodeOpacity=1] + * @return {Function|Number} + * @return {dc_graph.diagram} + **/ + _diagram.nodeOpacity = property(1); + + /** + * Set or get the padding or minimum distance, in pixels, for a node. (Will be distributed + * to both sides of the node.) + * @method nodePadding + * @memberof dc_graph.diagram + * @instance + * @param {Function|Number} [nodePadding=6] + * @return {Function|Number} + * @return {dc_graph.diagram} + **/ + _diagram.nodePadding = property(6); + + + /** + * Set or get the padding, in pixels, for a node's label. If an object, should contain fields + * `x` and `y`. If a number, will be applied to both x and y. + * @method nodeLabelPadding + * @memberof dc_graph.diagram + * @instance + * @param {Function|Number|Object} [nodeLabelPadding=0] + * @return {Function|Number} + * @return {dc_graph.diagram} + **/ + _diagram.nodeLabelPadding = property(0); + + /** + * Set or get the line height for nodes with multiple lines of text, in ems. + * @method nodeLineHeight + * @memberof dc_graph.diagram + * @instance + * @param {Function|Number} [nodeLineHeight=1] + * @return {Function|Number} + * @return {dc_graph.diagram} + **/ + _diagram.nodeLineHeight = property(1); + + /** + * Set or get the function which will be used to retrieve the label text to display in each + * node. By default, looks for a field `label` or `name` inside the `value` field. + * @method nodeLabel + * @memberof dc_graph.diagram + * @instance + * @param {Function|String} [nodeLabel] + * @return {Function|String} + * @example + * // Default behavior + * diagram.nodeLabel(function(kv) { + * return kv.value.label || kv.value.name; + * }); + * @return {dc_graph.diagram} + **/ + _diagram.nodeLabel = _diagram.nodeLabelAccessor = property(function(kv) { + return kv.value.label || kv.value.name; + }); + + _diagram.nodeLabelAlignment = property('center'); + _diagram.nodeLabelDecoration = property(null); + + /** + * Set or get the function which will be used to retrieve the label fill color. Default: null + * @method nodeLabelFill + * @memberof dc_graph.diagram + * @instance + * @param {Function|String} [nodeLabelFill=null] + * @return {Function|String} + * @return {dc_graph.diagram} + **/ + _diagram.nodeLabelFill = _diagram.nodeLabelFillAccessor = property(null); + + /** + * Whether to fit the node shape around the label + * @method nodeFitLabel + * @memberof dc_graph.diagram + * @instance + * @param {Function|Boolean} [nodeFitLabel=true] + * @return {Function|Boolean} + * @return {dc_graph.diagram} + **/ + _diagram.nodeFitLabel = _diagram.nodeFitLabelAccessor = property(true); + + /** + * The shape to use for drawing each node, specified as an object with at least the field + * `shape`. The names of shapes are mostly taken + * [from graphviz](http://www.graphviz.org/doc/info/shapes.html); currently ellipse, egg, + * triangle, rectangle, diamond, trapezium, parallelogram, pentagon, hexagon, septagon, octagon, + * invtriangle, invtrapezium, square, polygon are supported. + * + * If `shape = polygon`: + * * `sides`: number of sides for a polygon + * @method nodeShape + * @memberof dc_graph.diagram + * @instance + * @param {Function|Object} [nodeShape={shape: 'ellipse'}] + * @return {Function|Object} + * @return {dc_graph.diagram} + * @example + * // set shape to diamond or parallelogram based on flag + * diagram.nodeShape(function(kv) { + * return {shape: kv.value.flag ? 'diamond' : 'parallelogram'}; + * }); + **/ + _diagram.nodeShape = property(default_shape); + + // for defining custom (and standard) shapes + _diagram.shape = named_children(); + + _diagram.shape('nothing', dc_graph.no_shape()); + _diagram.shape('ellipse', dc_graph.ellipse_shape()); + _diagram.shape('polygon', dc_graph.polygon_shape()); + _diagram.shape('rounded-rect', dc_graph.rounded_rectangle_shape()); + _diagram.shape('elaborated-rect', dc_graph.elaborated_rectangle_shape()); + + _diagram.nodeContent = property('text'); + _diagram.content = named_children(); + _diagram.content('text', dc_graph.text_contents()); + + // really looks like these should reside in an open namespace - this used only by an extension + // but it's no less real than any other computed property + _diagram.nodeIcon = property(null); + + /** + * Set or get the function which will be used to retrieve the node title, usually rendered + * as a tooltip. By default, uses the key of the node. + * @method nodeTitle + * @memberof dc_graph.diagram + * @instance + * @param {Function|String} [nodeTitle] + * @return {Function|String} + * @example + * // Default behavior + * diagram.nodeTitle(function(kv) { + * return _diagram.nodeKey()(kv); + * }); + * @return {dc_graph.diagram} + **/ + _diagram.nodeTitle = _diagram.nodeTitleAccessor = property(function(kv) { + return _diagram.nodeKey()(kv); + }); + + /** + * By default, nodes are added to the layout in the order that `.nodeGroup().all()` returns + * them. If specified, `.nodeOrdering` provides an accessor that returns a key to sort the + * nodes on. *It would be better not to rely on ordering to affect layout, but it may + * affect the layout in some cases.* + * @method nodeOrdering + * @memberof dc_graph.diagram + * @instance + * @param {Function} [nodeOrdering=null] + * @return {Function} + * @return {dc_graph.diagram} + **/ + _diagram.nodeOrdering = property(null); + + /** + * Specify an accessor that returns an {x,y} coordinate for a node that should be + * {@link https://github.com/tgdwyer/WebCola/wiki/Fixed-Node-Positions fixed in place}, + * and returns falsy for other nodes. + * @method nodeFixed + * @memberof dc_graph.diagram + * @instance + * @param {Function|Object} [nodeFixed=null] + * @return {Function|Object} + * @return {dc_graph.diagram} + **/ + _diagram.nodeFixed = _diagram.nodeFixedAccessor = property(null); + + + /** + * Set or get the function which will be used to retrieve the stroke color for the edges. + * @method edgeStroke + * @memberof dc_graph.diagram + * @instance + * @param {Function|String} [edgeStroke='black'] + * @return {Function|String} + * @return {dc_graph.diagram} + **/ + _diagram.edgeStroke = _diagram.edgeStrokeAccessor = property('black'); + + /** + * Set or get the function which will be used to retrieve the stroke width for the edges. + * @method edgeStrokeWidth + * @memberof dc_graph.diagram + * @instance + * @param {Function|Number} [edgeStrokeWidth=1] + * @return {Function|Number} + * @return {dc_graph.diagram} + **/ + _diagram.edgeStrokeWidth = _diagram.edgeStrokeWidthAccessor = property(1); + + _diagram.edgeStrokeDashArray = property(null); + + /** + * Set or get the function which will be used to retrieve the edge opacity, a number from 0 + * to 1. + * @method edgeOpacity + * @memberof dc_graph.diagram + * @instance + * @param {Function|Number} [edgeOpacity=1] + * @return {Function|Number} + * @return {dc_graph.diagram} + **/ + _diagram.edgeOpacity = _diagram.edgeOpacityAccessor = property(1); + + /** + * Set or get the function which will be used to retrieve the edge label text. The label is + * displayed when an edge is hovered over. By default, uses the `edgeKey`. + * @method edgeLabel + * @memberof dc_graph.diagram + * @instance + * @param {Function|String} [edgeLabel] + * @example + * // Default behavior + * diagram.edgeLabel(function(e) { + * return _diagram.edgeKey()(e); + * }); + * @return {Function|String} + * @return {dc_graph.diagram} + **/ + _diagram.edgeLabel = _diagram.edgeLabelAccessor = property(function(e) { + return _diagram.edgeKey()(e); + }); + // vertical spacing when there are multiple lines of edge label + _diagram.edgeLabelSpacing = property(12); + + /** + * Set or get the function which will be used to retrieve the name of the arrowhead to use + * for the target/ head/destination of the edge. Arrow symbols can be specified with + * `.defineArrow()`. Return null to display no arrowhead. + * @method edgeArrowhead + * @memberof dc_graph.diagram + * @instance + * @param {Function|String} [edgeArrowhead='vee'] + * @return {Function|String} + * @return {dc_graph.diagram} + **/ + _diagram.edgeArrowhead = _diagram.edgeArrowheadAccessor = property('vee'); + + /** + * Set or get the function which will be used to retrieve the name of the arrow tail to use + * for the tail/source of the edge. Arrow symbols can be specified with + * `.defineArrow()`. Return null to display no arrowtail. + * @method edgeArrowtail + * @memberof dc_graph.diagram + * @instance + * @param {Function|String} [edgeArrowtail=null] + * @return {Function|String} + * @return {dc_graph.diagram} + **/ + _diagram.edgeArrowtail = _diagram.edgeArrowtailAccessor = property(null); + + /** + * Multiplier for arrow size. + * @method edgeArrowSize + * @memberof dc_graph.diagram + * @instance + * @param {Function|Number} [edgeArrowSize=1] + * @return {Function|Number} + * @return {dc_graph.diagram} + **/ + _diagram.edgeArrowSize = property(1); + + /** + * To draw an edge but not have it affect the layout, specify a function which returns + * false for that edge. By default, will return false if the `notLayout` field of the edge + * value is truthy, true otherwise. + * @method edgeIsLayout + * @memberof dc_graph.diagram + * @instance + * @param {Function|Boolean} [edgeIsLayout] + * @example + * // Default behavior + * diagram.edgeIsLayout(function(kv) { + * return !kv.value.notLayout; + * }); + * @return {Function|Boolean} + * @return {dc_graph.diagram} + **/ + _diagram.edgeIsLayout = _diagram.edgeIsLayoutAccessor = property(function(kv) { + return !kv.value.notLayout; + }); + + // if false, don't draw or layout the edge. this is not documented because it seems like + // the interface could be better and this combined with edgeIsLayout. (currently there is + // no way to layout but not draw an edge.) + _diagram.edgeIsShown = property(true); + + /** + * Currently, three strategies are supported for specifying the lengths of edges: + * * 'individual' - uses the `edgeLength` for each edge. If it returns falsy, uses the + * `baseLength` + * * 'symmetric', 'jaccard' - compute the edge length based on the graph structure around + * the edge. See + * {@link https://github.com/tgdwyer/WebCola/wiki/link-lengths the cola.js wiki} + * for more details. + * 'none' - no edge lengths will be specified + * + * **Deprecated**: Use {@link dc_graph.cola_layout#lengthStrategy cola_layout.lengthStrategy} instead. + * @method lengthStrategy + * @memberof dc_graph.diagram + * @instance + * @param {Function|String} [lengthStrategy='symmetric'] + * @return {Function|String} + * @return {dc_graph.diagram} + **/ + _diagram.lengthStrategy = deprecate_layout_algo_parameter('lengthStrategy'); + + /** + * When the `.lengthStrategy` is 'individual', this accessor will be used to read the + * length of each edge. By default, reads the `distance` field of the edge. If the + * distance is falsy, uses the `baseLength`. + * @method edgeLength + * @memberof dc_graph.diagram + * @instance + * @param {Function|Number} [edgeLength] + * @example + * // Default behavior + * diagram.edgeLength(function(kv) { + * return kv.value.distance; + * }); + * @return {Function|Number} + * @return {dc_graph.diagram} + **/ + _diagram.edgeLength = _diagram.edgeDistanceAccessor = property(function(kv) { + return kv.value.distance; + }); + + /** + * This should be equivalent to rankdir and ranksep in the dagre/graphviz nomenclature, but for + * now it is separate. + * + * **Deprecated**: use {@link dc_graph.cola_layout#flowLayout cola_layout.flowLayout} instead. + * @method flowLayout + * @memberof dc_graph.diagram + * @instance + * @param {Object} [flowLayout] + * @example + * // No flow (default) + * diagram.flowLayout(null) + * // flow in x with min separation 200 + * diagram.flowLayout({axis: 'x', minSeparation: 200}) + **/ + _diagram.flowLayout = deprecate_layout_algo_parameter('flowLayout'); + + /** + * Direction to draw ranks. Currently for dagre and expand_collapse, but I think cola could be + * generated from graphviz-style since it is more general. + * + * **Deprecated**: use {@link dc_graph.dagre_layout#rankdir dagre_layout.rankdir} instead. + * @method rankdir + * @memberof dc_graph.diagram + * @instance + * @param {String} [rankdir] + **/ + _diagram.rankdir = deprecate_layout_algo_parameter('rankdir'); + + /** + * Gets or sets the default edge length (in pixels) when the `.lengthStrategy` is + * 'individual', and the base value to be multiplied for 'symmetric' and 'jaccard' edge + * lengths. + * + * **Deprecated**: use {@link dc_graph.cola_layout#baseLength cola_layout.baseLength} instead. + * @method baseLength + * @memberof dc_graph.diagram + * @instance + * @param {Number} [baseLength] + * @return {Number} + * @return {dc_graph.diagram} + **/ + _diagram.baseLength = deprecate_layout_algo_parameter('baseLength'); + + /** + * Gets or sets the transition duration, the length of time each change to the diagram will + * be animated. + * @method transitionDuration + * @memberof dc_graph.diagram + * @instance + * @param {Number} [transitionDuration=500] + * @return {Number} + * @return {dc_graph.diagram} + **/ + _diagram.transitionDuration = property(500); + + /** + * How transitions should be split into separate animations to emphasize + * the delete, modify, and insert operations: + * * `none`: modify and insert operations animate at the same time + * * `modins`: modify operations happen before inserts + * * `insmod`: insert operations happen before modifies + * + * Deletions always happen before/during layout computation. + * @method stageTransitions + * @memberof dc_graph.diagram + * @instance + * @param {String} [stageTransitions='none'] + * @return {String} + * @return {dc_graph.diagram} + **/ + _diagram.stageTransitions = property('none'); + + /** + * The delete transition happens simultaneously with layout, which can take longer + * than the transition duration. Delaying it can bring it closer to the other + * staged transitions. + * @method deleteDelay + * @memberof dc_graph.diagram + * @instance + * @param {Number} [deleteDelay=0] + * @return {Number} + * @return {dc_graph.diagram} + **/ + _diagram.deleteDelay = property(0); + + /** + * Whether to put connected components each in their own group, to stabilize layout. + * @method groupConnected + * @memberof dc_graph.diagram + * @instance + * @param {String} [groupConnected=false] + * @return {String} + * @return {dc_graph.diagram} + **/ + _diagram.groupConnected = deprecate_layout_algo_parameter('groupConnected'); + + /** + * Gets or sets the maximum time spent doing layout for a render or redraw. Set to 0 for no + * limit. + * @method timeLimit + * @memberof dc_graph.diagram + * @instance + * @param {Function|Number} [timeLimit=0] + * @return {Function|Number} + * @return {dc_graph.diagram} + **/ + _diagram.timeLimit = property(0); + + /** + * Gets or sets a function which will be called with the current nodes and edges on each + * redraw in order to derive new layout constraints. The constraints are built from scratch + * on each redraw. + * + * This can be used to generate alignment (rank) or axis constraints. By default, no + * constraints will be added, although cola.js uses constraints internally to implement + * flow and overlap prevention. See + * {@link https://github.com/tgdwyer/WebCola/wiki/Constraints the cola.js wiki} + * for more details. + * + * For convenience, dc.graph.js implements a other constraints on top of those implemented + * by cola.js: + * * 'ordering' - the nodes will be ordered on the specified `axis` according to the keys + * returned by the `ordering` function, by creating separation constraints using the + * specified `gap`. + * * 'circle' - (experimental) the nodes will be placed in a circle using "wheel" + * edge lengths similar to those described in + * {@link http://www.csse.monash.edu.au/~tdwyer/Dwyer2009FastConstraints.pdf Scalable, Versatile, and Simple Constrained Graph Layout} + * *Although this is not as performant or stable as might be desired, it may work for + * simple cases. In particular, it should use edge length *constraints*, which don't yet + * exist in cola.js.* + * + * Because it is tedious to write code to generate constraints for a graph, **dc.graph.js** + * also includes a {@link #dc_graph+constraint_pattern constraint generator} to produce + * this constrain function, specifying the constraints themselves in a graph. + * @method constrain + * @memberof dc_graph.diagram + * @instance + * @param {Function} [constrain] + * @return {Function} + * @return {dc_graph.diagram} + **/ + _diagram.constrain = property(function(nodes, edges) { + return []; + }); + + /** + * If there are multiple edges between the same two nodes, start them this many pixels away + * from the original so they don't overlap. + * @method parallelEdgeOffset + * @memberof dc_graph.diagram + * @instance + * @param {Number} [parallelEdgeOffset=10] + * @return {Number} + * @return {dc_graph.diagram} + **/ + _diagram.parallelEdgeOffset = property(10); + + /** + * By default, edges are added to the layout in the order that `.edgeGroup().all()` returns + * them. If specified, `.edgeOrdering` provides an accessor that returns a key to sort the + * edges on. + * + * *It would be better not to rely on ordering to affect layout, but it may affect the + * layout in some cases. (Probably less than node ordering, but it does affect which + * parallel edge is which.)* + * @method edgeOrdering + * @memberof dc_graph.diagram + * @instance + * @param {Function} [edgeOrdering=null] + * @return {Function} + * @return {dc_graph.diagram} + **/ + _diagram.edgeOrdering = property(null); + + _diagram.edgeSort = property(null); + + _diagram.cascade = cascade(_diagram); + + /** + * Currently there are some bugs when the same instance of cola.js is used multiple + * times. (In particular, overlaps between nodes may not be eliminated + * {@link https://github.com/tgdwyer/WebCola/issues/118 if cola is not reinitialized} + * This flag can be set true to construct a new cola layout object on each redraw. However, + * layout seems to be more stable if this is set false, so hopefully this will be fixed + * soon. + * @method initLayoutOnRedraw + * @memberof dc_graph.diagram + * @instance + * @param {Boolean} [initLayoutOnRedraw=false] + * @return {Boolean} + * @return {dc_graph.diagram} + **/ + _diagram.initLayoutOnRedraw = property(false); + + /** + * Whether to perform layout when the data is unchanged from the last redraw. + * @method layoutUnchanged + * @memberof dc_graph.diagram + * @instance + * @param {Boolean} [layoutUnchanged=false] + * @return {Boolean} + * @return {dc_graph.diagram} + **/ + _diagram.layoutUnchanged = property(false); + + /** + * When `layoutUnchanged` is false, this will force layout to happen again. This may be needed + * when changing a parameter but not changing the topology of the graph. (Yes, probably should + * not be necessary.) + * @method relayout + * @memberof dc_graph.diagram + * @instance + * @return {dc_graph.diagram} + **/ + _diagram.relayout = function() { + _nodes_snapshot = _edges_snapshot = null; + return this; + }; + + /** + * Function to call to generate an initial layout. Takes (diagram, nodes, edges) + * + * **Deprecated**: The only layout that was using this was `tree_positions` and it never + * worked as an initialization step for cola, as was originally intended. Now that + * `tree_layout` is a layout algorithm, this should go away. + * + * In the future, there will be support for chaining layout algorithms. But that will be a + * matter of composing them into a super-algorithm, not a special step like this was. + * @method initialLayout + * @memberof dc_graph.diagram + * @instance + * @param {Function} [initialLayout=null] + * @return {Function} + * @return {dc_graph.diagram} + **/ + _diagram.initialLayout = deprecated_property('initialLayout is deprecated - use layout algorithms instead', null); + + _diagram.initialOnly = deprecated_property('initialOnly is deprecated - see the initialLayout deprecation notice in the documentation', false); + + /** + * By default, all nodes are included, and edges are only included if both end-nodes are + * visible. If `.induceNodes` is set, then only nodes which have at least one edge will be + * shown. + * @method induceNodes + * @memberof dc_graph.diagram + * @instance + * @param {Boolean} [induceNodes=false] + * @return {Boolean} + * @return {dc_graph.diagram} + **/ + _diagram.induceNodes = property(false); + + /** + * If this flag is true, the positions of nodes and will be updated while layout is + * iterating. If false, the positions will only be updated once layout has + * stabilized. Note: this may not be compatible with transitionDuration. + * @method showLayoutSteps + * @memberof dc_graph.diagram + * @instance + * @param {Boolean} [showLayoutSteps=false] + * @return {Boolean} + * @return {dc_graph.diagram} + **/ + _diagram.showLayoutSteps = property(false); + + /** + * Assigns a legend object which will be displayed within the same SVG element and + * according to the visual encoding of this diagram. + * @method legend + * @memberof dc_graph.diagram + * @instance + * @param {Object} [legend=null] + * @return {Object} + * @return {dc_graph.diagram} + **/ + // (pre-deprecated; see below) + + /** + * Specifies another kind of child layer or interface. For example, this can + * be used to display tooltips on nodes using `dc_graph.tip`. + + * The child needs to support a `parent` method, the diagram to modify. + * @method child + * @memberof dc_graph.diagram + * @instance + * @param {String} [id] - the name of the child to modify or add + * @param {Object} [object] - the child object to add, or null to remove + * @example + * // Display tooltips on node hover, via the d3-tip library + * var tip = dc_graph.tip() + * tip.content(function(n, k) { + * // you can do an asynchronous call here, e.g. d3.json, if you need + * // to fetch data to show the tooltip - just call k() with the content + * k("This is <em>" + n.orig.value.name + "</em>"); + * }); + * diagram.child('tip', tip); + * @return {dc_graph.diagram} + **/ + _diagram.mode = _diagram.child = named_children(); + + _diagram.mode.reject = function(id, object) { + var rtype = _diagram.renderer().rendererType(); + if(!object) + return false; // null is always a valid mode for any renderer + if(!object.supportsRenderer) + console.log('could not check if "' + id + '" is compatible with ' + rtype); + else if(!object.supportsRenderer(rtype)) + return 'not installing "' + id + '" because it is not compatible with renderer ' + rtype; + return false; + }; + + _diagram.legend = deprecate_function(".legend() is deprecated; use .child() for more control & multiple legends", function(_) { + if(!arguments.length) + return _diagram.child('node-legend'); + _diagram.child('node-legend', _); + return _diagram; + }); + + /** + * Specify 'cola' (the default) or 'dagre' as the Layout Algorithm and it will replace the + * back-end. + * + * **Deprecated**: use {@link dc_graph.diagram#layoutEngine diagram.layoutEngine} with the engine + * object instead + * @method layoutAlgorithm + * @memberof dc_graph.diagram + * @instance + * @param {String} [algo='cola'] - the name of the layout algorithm to use + * @example + * // use dagre for layout + * diagram.layoutAlgorithm('dagre'); + * @return {dc_graph.diagram} + **/ + _diagram.layoutAlgorithm = function(value, skipWarning) { + if(!arguments.length) + return _diagram.layoutEngine() ? _diagram.layoutEngine().layoutAlgorithm() : 'cola'; + if(!skipWarning) + console.warn('dc.graph.diagram.layoutAlgorithm is deprecated - pass the layout engine object to dc_graph.diagram.layoutEngine instead'); + + var engine; + switch(value) { + case 'cola': + engine = dc_graph.cola_layout(); + break; + case 'dagre': + engine = dc_graph.dagre_layout(); + } + engine = dc_graph.webworker_layout(engine); + _diagram.layoutEngine(engine); + return this; + }; + + /** + * The layout engine determines positions of nodes and edges. + * @method layoutEngine + * @memberof dc_graph.diagram + * @instance + * @param {Object} [engine=null] - the layout engine to use + * @example + * // use cola with no webworker + * diagram.layoutEngine(dc_graph.cola_layout()); + * // use dagre with a webworker + * diagram.layoutEngine(dc_graph.webworker_layout(dc_graph.dagre_layout())); + **/ + _diagram.layoutEngine = property(null).react(function(val) { + if(val && val.parent) + val.parent(_diagram); + if(_diagram.renderer().isRendered()) { + // remove any calculated points, if engine did that + Object.keys(_edges).forEach(function(k) { + _edges[k].cola.points = null; + }); + // initialize engine + initLayout(val); + } + }); + + _diagram.renderer = property(dc_graph.render_svg().parent(_diagram)).react(function(r) { + if(_diagram.renderer()) + _diagram.renderer().parent(null); + r.parent(_diagram); + }); + + // S-spline any edges that are not going in this direction + _diagram.enforceEdgeDirection = property(null); + + _diagram.tickSize = deprecate_layout_algo_parameter('tickSize'); + + + _diagram.uniqueId = function() { + return _diagram.anchorName().replace(/[ .#=\[\]"]/g, '-'); + }; + + _diagram.edgeId = function(e) { + return 'edge-' + _diagram.edgeKey.eval(e).replace(/[^\w-_]/g, '-'); + }; + + _diagram.arrowId = function(e, kind) { + return 'arrow-' + kind + '-' + _diagram.uniqueId() + '-' + _diagram.edgeId(e); + }; + _diagram.textpathId = function(e) { + return 'textpath-' + _diagram.uniqueId() + '-' + _diagram.edgeId(e); + }; + + // this kind of begs a (meta)graph ADT + // instead of munging this into the diagram + _diagram.getNode = function(id) { + return _nodes[id] ? _nodes[id].orig : null; + }; + + _diagram.getWholeNode = function(id) { + return _nodes[id] ? _nodes[id] : null; + }; + + _diagram.getEdge = function(id) { + return _edges[id] ? _edges[id].orig : null; + }; + + _diagram.getWholeEdge = function(id) { + return _edges[id] ? _edges[id] : null; + }; + + // again, awful, we need an ADT + _diagram.getPort = function(nid, eid, name) { + return _ports[port_name(nid, eid, name)]; + }; + + _diagram.nodePorts = function() { + return _nodePorts; + }; + + _diagram.getWholeCluster = function(id) { + return _clusters[id] || null; + }; + + /** + * Instructs cola.js to fit the connected components. + * + * **Deprecated**: Use + * {@link dc_graph.cola_layout#handleDisconnected cola_layout.handleDisconnected} instead. + * @method handleDisconnected + * @memberof dc_graph.diagram + * @instance + * @param {Boolean} [handleDisconnected=true] + * @return {Boolean} + * @return {dc_graph.diagram} + **/ + _diagram.handleDisconnected = deprecate_layout_algo_parameter('handleDisconnected'); + + function initLayout(engine) { + if(!_diagram.layoutEngine()) + _diagram.layoutAlgorithm('cola', true); + (engine || _diagram.layoutEngine()).init({ + width: _diagram.width(), + height: _diagram.height() + }); + } + + _diagram.forEachChild = function(node, children, idf, f) { + children.enum().forEach(function(key) { + f(children(key), + node.filter(function(n) { return idf(n) === key; })); + }); + }; + _diagram.forEachShape = function(node, f) { + _diagram.forEachChild(node, _diagram.shape, function(n) { return n.dcg_shape.shape; }, f); + }; + _diagram.forEachContent = function(node, f) { + _diagram.forEachChild(node, _diagram.content, _diagram.nodeContent.eval, f); + }; + + function has_source_and_target(e) { + return !!e.source && !!e.target; + } + + // three stages: delete before layout, and modify & insert split the transitionDuration + _diagram.stagedDuration = function() { + return (_diagram.stageTransitions() !== 'none') ? + _diagram.transitionDuration() / 2 : + _diagram.transitionDuration(); + }; + + _diagram.stagedDelay = function(is_enter) { + return _diagram.stageTransitions() === 'none' || + _diagram.stageTransitions() === 'modins' === !is_enter ? + 0 : + _diagram.transitionDuration() / 2; + }; + + _diagram.isRunning = function() { + return _running; + }; + + function svg_specific(name) { + return trace_function('trace', name + '() is specific to the SVG renderer', function() { + return _diagram.renderer()[name].apply(this, arguments); + }); + } + + function call_on_renderer(name) { + return trace_function('trace', 'calling ' + name + '() on renderer', function() { + return _diagram.renderer()[name].apply(this, arguments); + }); + } + + _diagram.svg = svg_specific('svg'); + _diagram.g = svg_specific('g'); + _diagram.select = svg_specific('select'); + _diagram.selectAll = svg_specific('selectAll'); + _diagram.addOrRemoveDef = svg_specific('addOrRemoveDef'); + _diagram.selectAllNodes = svg_specific('selectAllNodes'); + _diagram.selectAllEdges = svg_specific('selectAllEdges'); + _diagram.selectNodePortsOfStyle = svg_specific('selectNodePortsOfStyle'); + _diagram.zoom = svg_specific('zoom'); + _diagram.translate = svg_specific('translate'); + _diagram.scale = svg_specific('scale'); + + function renderer_specific(name) { + return trace_function('trace', name + '() will have renderer-specific arguments', function() { + return _diagram.renderer()[name].apply(this, arguments); + }); + } + _diagram.renderNode = svg_specific('renderNode'); + _diagram.renderEdge = svg_specific('renderEdge'); + _diagram.redrawNode = svg_specific('redrawNode'); + _diagram.redrawEdge = svg_specific('redrawEdge'); + _diagram.reposition = call_on_renderer('reposition'); + + + /** + * Standard dc.js + * {@link https://github.com/dc-js/dc.js/blob/develop/web/docs/api-latest.md#dc.baseMixin baseMixin} + * method. Computes a new layout based on the nodes and edges in the edge groups, and + * displays the diagram. To the extent possible, the diagram will minimize changes in + * positions from the previous layout. `.render()` must be called the first time, and + * `.redraw()` can be called after that. + * + * `.redraw()` will be triggered by changes to the filters in any other charts in the same + * dc.js chart group. + * + * Unlike in dc.js, `redraw` executes asynchronously, because drawing can be computationally + * intensive, and the diagram will be drawn multiple times if + * {@link #dc_graph.diagram+showLayoutSteps showLayoutSteps} + * is enabled. Watch the {@link #dc_graph.diagram+on 'end'} event to know when layout is + * complete. + * @method redraw + * @memberof dc_graph.diagram + * @instance + * @return {dc_graph.diagram} + **/ + var _needsRedraw = false; + _diagram.redraw = function () { + // since dc.js can receive UI events and trigger redraws whenever it wants, + // and cola absolutely will not tolerate being poked while it's doing layout, + // we need to guard the startLayout call. + if(_running) { + _needsRedraw = true; + return this; + } + else return _diagram.startLayout(); + }; + + /** + * Standard dc.js + * {@link https://github.com/dc-js/dc.js/blob/develop/web/docs/api-latest.md#dc.baseMixin baseMixin} + * method. Erases any existing SVG elements and draws the diagram from scratch. `.render()` + * must be called the first time, and `.redraw()` can be called after that. + * @method render + * @memberof dc_graph.diagram + * @instance + * @return {dc_graph.diagram} + **/ + _diagram.render = function() { + if(_diagram.renderer().isRendered()) + _dispatch.reset(); + if(!_diagram.initLayoutOnRedraw()) + initLayout(); + + _nodes = {}; + _edges = {}; + _ports = {}; + _clusters = {}; + + // start out with 1:1 zoom + _diagram.x(d3.scale.linear() + .domain([0, _diagram.width()]) + .range([0, _diagram.width()])); + _diagram.y(d3.scale.linear() + .domain([0, _diagram.height()]) + .range([0, _diagram.height()])); + _diagram.renderer().initializeDrawing(); + _dispatch.render(); + _diagram.redraw(); + return this; + }; + + _diagram.refresh = call_on_renderer('refresh'); + + _diagram.width_is_automatic = function() { + return _width === 'auto'; + }; + + _diagram.height_is_automatic = function() { + return _height === 'auto'; + }; + + function detect_size_change() { + var oldWidth = _lastWidth, oldHeight = _lastHeight; + var newWidth = _diagram.width(), newHeight = _diagram.height(); + if(oldWidth !== newWidth || oldHeight !== newHeight) + _diagram.renderer().rezoom(oldWidth, oldHeight, newWidth, newHeight); + } + + _diagram.startLayout = function () { + var nodes = _diagram.nodeGroup().all(); + var edges = _diagram.edgeGroup().all(); + var ports = _diagram.portGroup() ? _diagram.portGroup().all() : []; + var clusters = _diagram.clusterGroup() ? _diagram.clusterGroup().all() : []; + if(_running) { + throw new Error('dc_graph.diagram.redraw already running!'); + } + _running = true; + + if(_diagram.width_is_automatic() || _diagram.height_is_automatic()) + detect_size_change(); + else + _diagram.renderer().resize(); + + if(_diagram.initLayoutOnRedraw()) + initLayout(); + _diagram.layoutEngine().stop(); + _dispatch.preDraw(); + + // ordering shouldn't matter, but we support ordering in case it does + if(_diagram.nodeOrdering()) { + nodes = crossfilter.quicksort.by(_diagram.nodeOrdering())(nodes.slice(0), 0, nodes.length); + } + if(_diagram.edgeOrdering()) { + edges = crossfilter.quicksort.by(_diagram.edgeOrdering())(edges.slice(0), 0, edges.length); + } + + var wnodes = regenerate_objects(_nodes, nodes, null, function(v) { + return _diagram.nodeKey()(v); + }, function(v1, v) { + v1.orig = v; + v1.cola = v1.cola || {}; + v1.cola.dcg_nodeKey = _diagram.nodeKey.eval(v1); + v1.cola.dcg_nodeParentCluster = _diagram.nodeParentCluster.eval(v1); + _diagram.layoutEngine().populateLayoutNode(v1.cola, v1); + }); + var wedges = regenerate_objects(_edges, edges, null, function(e) { + return _diagram.edgeKey()(e); + }, function(e1, e) { + e1.orig = e; + e1.cola = e1.cola || {}; + e1.cola.dcg_edgeKey = _diagram.edgeKey.eval(e1); + e1.cola.dcg_edgeSource = _diagram.edgeSource.eval(e1); + e1.cola.dcg_edgeTarget = _diagram.edgeTarget.eval(e1); + e1.source = _nodes[e1.cola.dcg_edgeSource]; + e1.target = _nodes[e1.cola.dcg_edgeTarget]; + e1.sourcePort = e1.sourcePort || {}; + e1.targetPort = e1.targetPort || {}; + _diagram.layoutEngine().populateLayoutEdge(e1.cola, e1); + }); + + // remove edges that don't have both end nodes + wedges = wedges.filter(has_source_and_target); + + // remove self-edges (since we can't draw them - will be option later) + wedges = wedges.filter(function(e) { return e.source !== e.target; }); + + wedges = wedges.filter(_diagram.edgeIsShown.eval); + + // now we know which ports should exist + var needports = wedges.map(function(e) { + if(_diagram.edgeSourcePortName.eval(e)) + return port_name(_diagram.edgeSource.eval(e), null, _diagram.edgeSourcePortName.eval(e)); + else return port_name(null, _diagram.edgeKey.eval(e), 'source'); + }); + needports = needports.concat(wedges.map(function(e) { + if(_diagram.edgeTargetPortName.eval(e)) + return port_name(_diagram.edgeTarget.eval(e), null, _diagram.edgeTargetPortName.eval(e)); + else return port_name(null, _diagram.edgeKey.eval(e), 'target'); + })); + // remove any invalid ports so they don't crash in confusing ways later + ports = ports.filter(function(p) { + return _diagram.portNodeKey() && _diagram.portNodeKey()(p) || + _diagram.portEdgeKey() && _diagram.portEdgeKey()(p); + }); + var wports = regenerate_objects(_ports, ports, needports, function(p) { + return port_name(_diagram.portNodeKey() && _diagram.portNodeKey()(p), + _diagram.portEdgeKey() && _diagram.portEdgeKey()(p), + _diagram.portName()(p)); + }, function(p1, p) { + p1.orig = p; + if(p1.named) + p1.edges = []; + }, function(k, p) { + console.assert(k, 'should have screened out invalid ports'); + // it's dumb to parse the id we just created. as usual, i blame the lack of metagraphs + var parse = split_port_name(k); + if(parse.nodeKey) { + p.node = _nodes[parse.nodeKey]; + p.named = true; + } + else { + var e = _edges[parse.edgeKey]; + p.node = e[parse.name]; + p.edges = [e]; + p.named = false; + } + p.name = parse.name; + }); + // remove any ports where the end-node was not found, to avoid crashing elsewhere + wports = wports.filter(function(p) { return p.node; }); + + // find all edges for named ports + wedges.forEach(function(e) { + var name = _diagram.edgeSourcePortName.eval(e); + if(name) + _ports[port_name(_diagram.nodeKey.eval(e.source), null, name)].edges.push(e); + name = _diagram.edgeTargetPortName.eval(e); + if(name) + _ports[port_name(_diagram.nodeKey.eval(e.target), null, name)].edges.push(e); + }); + + // optionally, delete nodes that have no edges + if(_diagram.induceNodes()) { + var keeps = {}; + wedges.forEach(function(e) { + keeps[e.cola.dcg_edgeSource] = true; + keeps[e.cola.dcg_edgeTarget] = true; + }); + wnodes = wnodes.filter(function(n) { return keeps[n.cola.dcg_nodeKey]; }); + for(var k in _nodes) + if(!keeps[k]) + delete _nodes[k]; + } + + var needclusters = d3.set(wnodes.map(function(n) { + return _diagram.nodeParentCluster.eval(n); + }).filter(identity)).values(); + + var wclusters = regenerate_objects(_clusters, clusters, needclusters, function(c) { + return _diagram.clusterKey()(c); + }, function(c1, c) { // assign + c1.orig = c; + c1.cola = c1.cola || { + dcg_clusterKey: _diagram.clusterKey.eval(c1), + dcg_clusterParent: _diagram.clusterParent.eval(c1) + }; + }, function(k, c) { // create + }); + + wnodes.forEach(function(v, i) { + v.index = i; + }); + + // announce new data + _dispatch.data(_diagram, _nodes, wnodes, _edges, wedges, _ports, wports); + _stats = {nnodes: wnodes.length, nedges: wedges.length}; + + // fixed nodes may have been affected by .data() so calculate now + wnodes.forEach(function(v) { + if(_diagram.nodeFixed()) + v.cola.dcg_nodeFixed = _diagram.nodeFixed.eval(v); + }); + + // annotate parallel edges so we can draw them specially + if(_diagram.parallelEdgeOffset()) { + var em = new Array(wnodes.length); + for(var i = 0; i < wnodes.length; ++i) + em[i] = new Array(i); + wedges.forEach(function(e) { + e.pos = e.pos || {}; + var min, max, minattr, maxattr; + if(e.source.index < e.target.index) { + min = e.source.index; max = e.target.index; + minattr = 'edgeSourcePortName'; maxattr = 'edgeTargetPortName'; + } else { + max = e.source.index; min = e.target.index; + maxattr = 'edgeSourcePortName'; minattr = 'edgeTargetPortName'; + } + var minport = _diagram[minattr].eval(e) || 'no port', + maxport = _diagram[maxattr].eval(e) || 'no port'; + em[max][min] = em[max][min] || {}; + em[max][min][maxport] = em[max][min][maxport] || {}; + e.parallel = em[max][min][maxport][minport] = em[max][min][maxport][minport] || { + rev: [], + edges: [] + }; + e.parallel.edges.push(e); + e.parallel.rev.push(min !== e.source.index); + }); + } + + var drawState = _diagram.renderer().startRedraw(_dispatch, wnodes, wedges); + + // really we should have layout chaining like in the good old Dynagraph days + // the ordering of this and the previous 4 statements is somewhat questionable + if(_diagram.initialLayout()) + _diagram.initialLayout()(_diagram, wnodes, wedges); + + // no layout if the topology and layout parameters haven't changed + var skip_layout = false; + if(!_diagram.layoutUnchanged()) { + var nodes_snapshot = JSON.stringify(wnodes.map(function(n) { + return {orig: get_original(n), cola: {dcg_nodeFixed: n.cola.dcg_nodeFixed}}; + })); + var edges_snapshot = JSON.stringify(wedges.map(function(e) { + return {orig: get_original(e), cola: e.cola}; + })); + if(nodes_snapshot === _nodes_snapshot && edges_snapshot === _edges_snapshot) + skip_layout = true; + _nodes_snapshot = nodes_snapshot; + _edges_snapshot = edges_snapshot; + } + + // edge lengths may be affected by node sizes + wedges.forEach(function(e) { + e.cola.dcg_edgeLength = _diagram.edgeLength.eval(e); + }); + + // cola constraints always use indices, but node references + // are more friendly, so translate those + + // i am not satisfied with this constraint generation api... + // https://github.com/dc-js/dc.graph.js/issues/10 + var constraints = _diagram.constrain()(_diagram, wnodes, wedges); + + // warn if there are any loops (before changing names to indices) + // it would be better to do this in webcola + // (for one thing, this duplicates logic in rectangle.ts) + // but by that time it has lost the names of things, + // so the output would be difficult to use + var constraints_by_left = constraints.reduce(function(p, c) { + if(c.type) { + switch(c.type) { + case 'alignment': + var left = c.offsets[0].node; + p[left] = p[left] || []; + c.offsets.slice(1).forEach(function(o) { + p[left].push({node: o.node, in_constraint: c}); + }); + break; + } + } else if(c.axis) { + p[c.left] = p[c.left] || []; + p[c.left].push({node: c.right, in_constraint: c}); + } + return p; + }, {}); + var touched = {}; + function find_constraint_loops(con, stack) { + var left = con.node; + stack = stack || []; + var loop = stack.find(function(con) { return con.node === left; }); + stack = stack.concat([con]); + if(loop) + console.warn('found a loop in constraints', stack); + if(touched[left]) + return; + touched[left] = true; + if(!constraints_by_left[left]) + return; + constraints_by_left[left].forEach(function(right) { + find_constraint_loops(right, stack); + }); + } + Object.keys(constraints_by_left).forEach(function(left) { + if(!touched[left]) + find_constraint_loops({node: left, in_constraint: null}); + }); + + // translate references from names to indices (ugly) + var invalid_constraints = []; + constraints.forEach(function(c) { + if(c.type) { + switch(c.type) { + case 'alignment': + c.offsets.forEach(function(o) { + o.node = _nodes[o.node].index; + }); + break; + case 'circle': + c.nodes.forEach(function(n) { + n.node = _nodes[n.node].index; + }); + break; + } + } else if(c.axis && c.left && c.right) { + c.left = _nodes[c.left].index; + c.right = _nodes[c.right].index; + } + else invalid_constraints.push(c); + }); + + if(invalid_constraints.length) + console.warn(invalid_constraints.length + ' invalid constraints', invalid_constraints); + + // pseudo-cola.js features + + // 1. non-layout edges are drawn but not told to cola.js + var layout_edges = wedges.filter(_diagram.edgeIsLayout.eval); + var nonlayout_edges = wedges.filter(function(x) { + return !_diagram.edgeIsLayout.eval(x); + }); + + // 2. type=circle constraints + var circle_constraints = constraints.filter(function(c) { + return c.type === 'circle'; + }); + constraints = constraints.filter(function(c) { + return c.type !== 'circle'; + }); + circle_constraints.forEach(function(c) { + var R = (c.distance || _diagram.baseLength()*4) / (2*Math.sin(Math.PI/c.nodes.length)); + var nindices = c.nodes.map(function(x) { return x.node; }); + var namef = function(i) { + return _diagram.nodeKey.eval(wnodes[i]); + }; + var wheel = dc_graph.wheel_edges(namef, nindices, R) + .map(function(e) { + var e1 = {internal: e}; + e1.source = _nodes[e.sourcename]; + e1.target = _nodes[e.targetname]; + return e1; + }); + layout_edges = layout_edges.concat(wheel); + }); + + // 3. ordered alignment + var ordered_constraints = constraints.filter(function(c) { + return c.type === 'ordering'; + }); + constraints = constraints.filter(function(c) { + return c.type !== 'ordering'; + }); + ordered_constraints.forEach(function(c) { + var sorted = c.nodes.map(function(n) { return _nodes[n]; }); + if(c.ordering) { + var sort = crossfilter.quicksort.by(param(c.ordering)); + sorted = sort(sorted, 0, sorted.length); + } + var left; + sorted.forEach(function(n, i) { + if(i===0) + left = n; + else { + constraints.push({ + left: left.index, + right: (left = n).index, + axis: c.axis, + gap: c.gap + }); + } + }); + }); + if(skip_layout) { + _running = false; + // init_node_ports? + _diagram.renderer().draw(drawState, true); + _diagram.renderer().drawPorts(drawState); + _diagram.renderer().fireTSEvent(_dispatch, drawState); + check_zoom(drawState); + return this; + } + var startTime = Date.now(); + + function populate_cola(rnodes, redges, rclusters) { + rnodes.forEach(function(rn) { + var n = _nodes[rn.dcg_nodeKey]; + if(!n) { + console.warn('received node "' + rn.dcg_nodeKey + '" that we did not send, ignored'); + return; + } + n.cola.x = rn.x; + n.cola.y = rn.y; + n.cola.z = rn.z; + }); + redges.forEach(function(re) { + var e = _edges[re.dcg_edgeKey]; + if(!e) { + console.warn('received edge "' + re.dcg_edgeKey + '" that we did not send, ignored'); + return; + } + if(re.points) + e.cola.points = re.points; + }); + wclusters.forEach(function(c) { + c.cola.bounds = null; + }); + if(rclusters) + rclusters.forEach(function(rc) { + var c = _clusters[rc.dcg_clusterKey]; + if(!c) { + console.warn('received cluster "' + rc.dcg_clusterKey + '" that we did not send, ignored'); + return; + } + if(rc.bounds) + c.cola.bounds = rc.bounds; + }); + } + _diagram.layoutEngine() + .on('tick.diagram', function(nodes, edges, clusters) { + var elapsed = Date.now() - startTime; + if(!_diagram.initialOnly()) + populate_cola(nodes, edges, clusters); + if(_diagram.showLayoutSteps()) { + init_node_ports(_nodes, wports); + _dispatch.receivedLayout(_diagram, _nodes, wnodes, _edges, wedges, _ports, wports); + propagate_port_positions(_nodes, wedges, _ports); + _diagram.renderer().draw(drawState, true); + _diagram.renderer().drawPorts(drawState); + // should do this only once + _diagram.renderer().fireTSEvent(_dispatch, drawState); + } + if(_needsRedraw || _diagram.timeLimit() && elapsed > _diagram.timeLimit()) { + console.log('cancelled'); + _diagram.layoutEngine().stop(); + } + }) + .on('end.diagram', function(nodes, edges, clusters) { + if(!_diagram.showLayoutSteps()) { + if(!_diagram.initialOnly()) + populate_cola(nodes, edges, clusters); + init_node_ports(_nodes, wports); + _dispatch.receivedLayout(_diagram, _nodes, wnodes, _edges, wedges, _ports, wports); + propagate_port_positions(_nodes, wedges, _ports); + _diagram.renderer().draw(drawState, true); + _diagram.renderer().drawPorts(drawState); + _diagram.renderer().fireTSEvent(_dispatch, drawState); + } + else _diagram.layoutDone(true); + check_zoom(drawState); + }) + .on('start.diagram', function() { + console.log('algo ' + _diagram.layoutEngine().layoutAlgorithm() + ' started.'); + _dispatch.start(); + }); + + if(_diagram.initialOnly()) + _diagram.layoutEngine().dispatch().end(wnodes, wedges); + else { + _dispatch.start(); // cola doesn't seem to fire this itself? + var engine = _diagram.layoutEngine(); + engine.data( + { width: _diagram.width(), height: _diagram.height() }, + wnodes.map(function(v) { + var lv = Object.assign({}, v.cola, v.dcg_shape); + if(engine.annotateNode) + engine.annotateNode(lv, v); + else if(engine.extractNodeAttrs) + Object.keys(engine.extractNodeAttrs()).forEach(function(key) { + lv[key] = engine.extractNodeAttrs()[key](v.orig); + }); + return lv; + }), + layout_edges.map(function(e) { + var le = e.cola; + if(engine.annotateEdge) + engine.annotateEdge(le, e); + else if(engine.extractEdgeAttrs) + Object.keys(engine.extractEdgeAttrs()).forEach(function(key) { + le[key] = engine.extractEdgeAttrs()[key](e.orig); + }); + return le; + }), + wclusters.map(function(c) { + return c.cola; + }), + constraints + ); + engine.start(); + } + return this; + }; + + function check_zoom(drawState) { + var do_zoom, animate = true; + if(_diagram.width_is_automatic() || _diagram.height_is_automatic()) + detect_size_change(); + switch(_diagram.autoZoom()) { + case 'always-skipanimonce': + animate = false; + _diagram.autoZoom('always'); + case 'always': + do_zoom = true; + break; + case 'once-noanim': + animate = false; + case 'once': + do_zoom = true; + _diagram.autoZoom(null); + break; + default: + do_zoom = false; + } + calc_bounds(drawState); + if(do_zoom) + auto_zoom(animate); + } + + function norm(v) { + var len = Math.hypot(v[0], v[1]); + return [v[0]/len, v[1]/len]; + } + function edge_vec(n, e) { + var dy = e.target.cola.y - e.source.cola.y, + dx = e.target.cola.x - e.source.cola.x; + if(dy === 0 && dx === 0) + return [1, 0]; + if(e.source !== n) + dy = -dy, dx = -dx; + if(e.parallel && e.parallel.edges.length > 1 && e.source.index > e.target.index) + dy = -dy, dx = -dx; + return norm([dx, dy]); + } + function init_node_ports(nodes, wports) { + _nodePorts = {}; + // assemble port-lists for nodes, again because we don't have a metagraph. + wports.forEach(function(p) { + var nid = _diagram.nodeKey.eval(p.node); + var np = _nodePorts[nid] = _nodePorts[nid] || []; + np.push(p); + }); + for(var nid in _nodePorts) { + var n = nodes[nid], + nports = _nodePorts[nid]; + // initial positions: use average of edge vectors, if any, or existing position + nports.forEach(function(p) { + if(_diagram.portElastic.eval(p) && p.edges.length) { + var vecs = p.edges.map(edge_vec.bind(null, n)); + p.vec = [ + d3.sum(vecs, function(v) { return v[0]; })/vecs.length, + d3.sum(vecs, function(v) { return v[1]; })/vecs.length + ]; + } else p.vec = p.vec || undefined; + p.pos = null; + }); + } + } + function propagate_port_positions(nodes, wedges, ports) { + // make sure we have projected vectors to positions + for(var nid in _nodePorts) { + var n = nodes[nid]; + _nodePorts[nid].forEach(function(p) { + if(!p.pos) + project_port(_diagram, n, p); + }); + } + + // propagate port positions to edge endpoints + wedges.forEach(function(e) { + var name = _diagram.edgeSourcePortName.eval(e); + e.sourcePort.pos = name ? ports[port_name(_diagram.nodeKey.eval(e.source), null, name)].pos : + ports[port_name(null, _diagram.edgeKey.eval(e), 'source')].pos; + name = _diagram.edgeTargetPortName.eval(e); + e.targetPort.pos = name ? ports[port_name(_diagram.nodeKey.eval(e.target), null, name)].pos : + ports[port_name(null, _diagram.edgeKey.eval(e), 'target')].pos; + console.assert(e.sourcePort.pos && e.targetPort.pos); + }); + } + + _diagram.requestRefresh = function(durationOverride) { + window.requestAnimationFrame(function() { + var transdur; + if(durationOverride !== undefined) { + transdur = _diagram.transitionDuration(); + _diagram.transitionDuration(durationOverride); + } + _diagram.renderer().refresh(); + if(durationOverride !== undefined) + _diagram.transitionDuration(transdur); + }); + }; + + _diagram.layoutDone = function(happens) { + _dispatch.end(happens); + _running = false; + if(_needsRedraw) { + _needsRedraw = false; + window.setTimeout(function() { + if(!_diagram.isRunning()) // someone else may already have started + _diagram.redraw(); + }, 0); + } + }; + + function enforce_path_direction(path, spos, tpos) { + var points = path.points, first = points[0], last = points[points.length-1]; + switch(_diagram.enforceEdgeDirection()) { + case 'LR': + if(spos.x >= tpos.x) { + var dx = first.x - last.x; + return { + points: [ + first, + {x: first.x + dx, y: first.y - dx/2}, + {x: last.x - dx, y: last.y - dx/2}, + last + ], + bezDegree: 3, + sourcePort: path.sourcePort, + targetPort: path.targetPort + }; + } + break; + case 'TB': + if(spos.y >= tpos.y) { + var dy = first.y - last.y; + return { + points: [ + first, + {x: first.x + dy/2, y: first.y + dy}, + {x: last.x + dy/2, y: last.y - dy}, + last + ], + bezDegree: 3, + sourcePort: path.sourcePort, + targetPort: path.targetPort + }; + } + break; + } + return path; + } + _diagram.calcEdgePath = function(e, age, sx, sy, tx, ty) { + var parallel = e.parallel; + var source = e.source, target = e.target; + if(parallel.edges.length > 1 && e.source.index > e.target.index) { + var t; + t = target; target = source; source = t; + t = tx; tx = sx; sx = t; + t = ty; ty = sy; sy = t; + } + var source_padding = source.dcg_ry + + _diagram.nodeStrokeWidth.eval(source) / 2, + target_padding = target.dcg_ry + + _diagram.nodeStrokeWidth.eval(target) / 2; + for(var p = 0; p < parallel.edges.length; ++p) { + // alternate parallel edges over, then under + var dir = (!!(p%2) === (sx < tx)) ? -1 : 1, + port = Math.floor((p+1)/2), + last = port > 0 ? parallel.edges[p > 2 ? p - 2 : 0].pos[age].path : null; + var path = draw_edge_to_shapes(_diagram, e, sx, sy, tx, ty, + last, dir, _diagram.parallelEdgeOffset(), + source_padding, target_padding + ); + if(parallel.edges.length > 1 && parallel.rev[p]) + path.points.reverse(); + if(_diagram.enforceEdgeDirection()) + path = enforce_path_direction(path, source.cola, target.cola); + var path0 = { + points: path.points, + bezDegree: path.bezDegree + }; + var alengths = scaled_arrow_lengths(_diagram, parallel.edges[p]); + path = clip_path_to_arrows(alengths.headLength, alengths.tailLength, path); + var points = path.points, points0 = path0.points; + parallel.edges[p].pos[age] = { + path: path, + full: path0, + orienthead: angle_between_points(points[points.length-1], points0[points0.length-1]) + 'rad', + orienttail: angle_between_points(points[0], points0[0]) + 'rad' + }; + } + }; + + function node_bounds(n) { + var bounds = {left: n.cola.x - n.dcg_rx, top: n.cola.y - n.dcg_ry, + right: n.cola.x + n.dcg_rx, bottom: n.cola.y + n.dcg_ry}; + if(_diagram.portStyle.enum().length) { + var ports = _nodePorts[_diagram.nodeKey.eval(n)]; + if(ports) + ports.forEach(function(p) { + var portStyle =_diagram.portStyleName.eval(p); + if(!portStyle || !_diagram.portStyle(portStyle)) + return; + var pb = _diagram.portStyle(portStyle).portBounds(p); + pb.left += n.cola.x; pb.top += n.cola.y; + pb.right += n.cola.x; pb.bottom += n.cola.y; + bounds = union_bounds(bounds, pb); + }); + } + return bounds; + } + + function union_bounds(b1, b2) { + return { + left: Math.min(b1.left, b2.left), + top: Math.min(b1.top, b2.top), + right: Math.max(b1.right, b2.right), + bottom: Math.max(b1.bottom, b2.bottom) + }; + } + + function point_to_bounds(p) { + return { + left: p.x, + top: p.y, + right: p.x, + bottom: p.y + }; + } + + function edge_bounds(e) { + // assumption: edge must have some points + var points = e.pos.new.path.points; + return points.map(point_to_bounds).reduce(union_bounds); + } + + _diagram.calculateBounds = function(ndata, edata) { + // assumption: there can be no edges without nodes + var bounds = ndata.map(node_bounds).reduce(union_bounds); + return edata.map(edge_bounds).reduce(union_bounds, bounds); + }; + var _bounds; + function calc_bounds(drawState) { + if((_diagram.fitStrategy() || _diagram.restrictPan())) { + _bounds = _diagram.renderer().calculateBounds(drawState); + } + } + + function auto_zoom(animate) { + if(_diagram.fitStrategy()) { + if(!_bounds) + return; + var vwidth = _bounds.right - _bounds.left, vheight = _bounds.bottom - _bounds.top, + swidth = _diagram.width() - _diagram.margins().left - _diagram.margins().right, + sheight = _diagram.height() - _diagram.margins().top - _diagram.margins().bottom; + var fitS = _diagram.fitStrategy(), translate = [0,0], scale = 1; + if(['default', 'vertical', 'horizontal'].indexOf(fitS) >= 0) { + var sAR = sheight / swidth, vAR = vheight / vwidth, + vrl = vAR<sAR, // view aspect ratio is less (wider) + amv = (fitS === 'default') ? !vrl : (fitS === 'vertical'); // align margins vertically + scale = amv ? sheight / vheight : swidth / vwidth; + scale = Math.max(_diagram.zoomExtent()[0], Math.min(_diagram.zoomExtent()[1], scale)); + translate = [_diagram.margins().left - _bounds.left*scale + (swidth - vwidth*scale) / 2, + _diagram.margins().top - _bounds.top*scale + (sheight - vheight*scale) / 2]; + } + else if(typeof fitS === 'string' && fitS.match(/^align_/)) { + var sides = fitS.split('_')[1].toLowerCase().split(''); + if(sides.length > 2) + throw new Error("align_ expecting 0-2 sides, not " + sides.length); + var bounds = margined_bounds(); + translate = _diagram.renderer().translate(); + scale = _diagram.renderer().scale(); + var vertalign = false, horzalign = false; + sides.forEach(function(s) { + switch(s) { + case 'l': + translate[0] = align_left(translate, bounds.left); + horzalign = true; + break; + case 't': + translate[1] = align_top(translate, bounds.top); + vertalign = true; + break; + case 'r': + translate[0] = align_right(translate, bounds.right); + horzalign = true; + break; + case 'b': + translate[1] = align_bottom(translate, bounds.bottom); + vertalign = true; + break; + case 'c': // handled below + break; + default: + throw new Error("align_ expecting l t r b or c, not '" + s + "'"); + } + }); + if(sides.includes('c')) { + if(!horzalign) + translate[0] = center_horizontally(translate, bounds); + if(!vertalign) + translate[1] = center_vertically(translate, bounds); + } + } + else if(fitS === 'zoom') { + scale = _diagram.renderer().scale(); + translate = bring_in_bounds(_diagram.renderer().translate()); + } + else + throw new Error('unknown fitStrategy type ' + typeof fitS); + + _animateZoom = animate; + _diagram.renderer().translate(translate).scale(scale).commitTranslateScale(); + _animateZoom = false; + } + } + function namespace_event_reducer(msg_fun) { + return function(p, ev) { + var namespace = {}; + p[ev] = function(ns) { + return namespace[ns] = namespace[ns] || onetime_trace('trace', msg_fun(ns, ev)); + }; + return p; + }; + } + var renderer_specific_events = ['drawn', 'transitionsStarted', 'zoomed'] + .reduce(namespace_event_reducer(function(ns, ev) { + return 'subscribing "' + ns + '" to event "' + ev + '" which takes renderer-specific parameters'; + }), {}); + var inconsistent_arguments = ['end'] + .reduce(namespace_event_reducer(function(ns, ev) { + return 'subscribing "' + ns + '" to event "' + ev + '" which may receive inconsistent arguments'; + }), {}); + + /** + * Standard dc.js + * {@link https://github.com/dc-js/dc.js/blob/develop/web/docs/api-latest.md#dc.baseMixin baseMixin} + * method. Attaches an event handler to the diagram. The currently supported events are + * * `start()` - layout is starting + * * `drawn(nodes, edges)` - the node and edge elements have been rendered to the screen + * and can be modified through the passed d3 selections. + * * `end()` - diagram layout has completed. + * @method on + * @memberof dc_graph.diagram + * @instance + * @param {String} [event] - the event to subscribe to + * @param {Function} [f] - the event handler + * @return {dc_graph.diagram} + **/ + _diagram.on = function(event, f) { + if(arguments.length === 1) + return _dispatch.on(event); + var evns = event.split('.'), + warning = renderer_specific_events[evns[0]] || inconsistent_arguments[evns[0]]; + if(warning) + warning(evns[1] || '')(); + _dispatch.on(event, f); + return this; + }; + + /** + * Returns an object with current statistics on graph layout. + * * `nnodes` - number of nodes displayed + * * `nedges` - number of edges displayed + * @method getStats + * @memberof dc_graph.diagram + * @instance + * @return {} + * @return {dc_graph.diagram} + **/ + _diagram.getStats = function() { + return _stats; + }; + + /** + * Standard dc.js + * {@link https://github.com/dc-js/dc.js/blob/develop/web/docs/api-latest.md#dc.baseMixin baseMixin} + * method. Gets or sets the x scale. + * @method x + * @memberof dc_graph.diagram + * @instance + * @param {d3.scale} [scale] + * @return {d3.scale} + * @return {dc_graph.diagram} + + **/ + _diagram.x = property(null); + + /** + * Standard dc.js + * {@link https://github.com/dc-js/dc.js/blob/develop/web/docs/api-latest.md#dc.baseMixin baseMixin} + * method. Gets or sets the y scale. + * @method y + * @memberof dc_graph.diagram + * @instance + * @param {d3.scale} [scale] + * @return {d3.scale} + * @return {dc_graph.diagram} + + **/ + _diagram.y = property(null); + + /** + * Standard dc.js + * {@link https://github.com/dc-js/dc.js/blob/develop/web/docs/api-latest.md#dc.baseMixin baseMixin} + * method. Causes all charts in the chart group to be redrawn. + * @method redrawGroup + * @memberof dc_graph.diagram + * @instance + * @return {dc_graph.diagram} + **/ + _diagram.redrawGroup = function () { + dc.redrawAll(_chartGroup); + }; + + /** + * Standard dc.js + * {@link https://github.com/dc-js/dc.js/blob/develop/web/docs/api-latest.md#dc.baseMixin baseMixin} + * method. Causes all charts in the chart group to be rendered. + * @method renderGroup + * @memberof dc_graph.diagram + * @instance + * @return {dc_graph.diagram} + **/ + _diagram.renderGroup = function () { + dc.renderAll(_chartGroup); + }; + + /** + * Creates an svg marker definition for drawing edge arrow tails or heads. + * + * Sorry, this is not currently documented - please see + * [arrows.js](https://github.com/dc-js/dc.graph.js/blob/develop/src/arrows.js) + * for examples + * @return {dc_graph.diagram} + **/ + _diagram.defineArrow = function(name, defn) { + if(typeof defn !== 'function') + throw new Error('sorry, defineArrow no longer takes specific shape parameters, and the parameters have changed too much to convert them. it takes a name and a function returning a definition - please look at arrows.js for new format'); + _arrows[name] = defn; + return _diagram; + }; + + // hmm + _diagram.arrows = function() { + return _arrows; + }; + + Object.keys(dc_graph.builtin_arrows).forEach(function(aname) { + var defn = dc_graph.builtin_arrows[aname]; + _diagram.defineArrow(aname, defn); + }); + + function margined_bounds() { + var bounds = _bounds || {left: 0, top: 0, right: 0, bottom: 0}; + var scale = _diagram.renderer().scale(); + return { + left: bounds.left - _diagram.margins().left/scale, + top: bounds.top - _diagram.margins().top/scale, + right: bounds.right + _diagram.margins().right/scale, + bottom: bounds.bottom + _diagram.margins().bottom/scale + }; + } + + // with thanks to comments in https://github.com/d3/d3/issues/1084 + function align_left(translate, x) { + return translate[0] - _diagram.x()(x) + _diagram.x().range()[0]; + } + function align_top(translate, y) { + return translate[1] - _diagram.y()(y) + _diagram.y().range()[0]; + } + function align_right(translate, x) { + return translate[0] - _diagram.x()(x) + _diagram.x().range()[1]; + } + function align_bottom(translate, y) { + return translate[1] - _diagram.y()(y) + _diagram.y().range()[1];; + } + function center_horizontally(translate, bounds) { + return (align_left(translate, bounds.left) + align_right(translate, bounds.right))/2; + } + function center_vertically(translate, bounds) { + return (align_top(translate, bounds.top) + align_bottom(translate, bounds.bottom))/2; + } + + function bring_in_bounds(translate) { + var xDomain = _diagram.x().domain(), yDomain = _diagram.y().domain(); + var bounds = margined_bounds(); + var less1 = bounds.left < xDomain[0], less2 = bounds.right < xDomain[1], + lessExt = (bounds.right - bounds.left) < (xDomain[1] - xDomain[0]); + var align, nothing = 0; + if(less1 && less2) + if(lessExt) + align = 'left'; + else + align = 'right'; + else if(!less1 && !less2) + if(lessExt) + align = 'right'; + else + align = 'left'; + switch(align) { + case 'left': + translate[0] = align_left(translate, bounds.left); + break; + case 'right': + translate[0] = align_right(translate, bounds.right); + break; + default: + ++nothing; + } + less1 = bounds.top < yDomain[0]; less2 = bounds.bottom < yDomain[1]; + lessExt = (bounds.bottom - bounds.top) < (yDomain[1] - yDomain[0]); + if(less1 && less2) + if(lessExt) + align = 'top'; + else + align = 'bottom'; + else if(!less1 && !less2) + if(lessExt) + align = 'bottom'; + else + align = 'top'; + switch(align) { + case 'top': + translate[1] = align_top(translate, bounds.top); + break; + case 'bottom': + translate[1] = align_bottom(translate, bounds.bottom); + break; + default: + ++nothing; + } + return translate; + + } + + _diagram.doZoom = function() { + if(_diagram.width_is_automatic() || _diagram.height_is_automatic()) + detect_size_change(); + var translate, scale = d3.event.scale; + if(_diagram.restrictPan()) + _diagram.renderer().translate(translate = bring_in_bounds(d3.event.translate)); + else translate = d3.event.translate; + _diagram.renderer().globalTransform(translate, scale, _animateZoom); + _dispatch.zoomed(translate, scale, _diagram.x().domain(), _diagram.y().domain()); + }; + + _diagram.invertCoord = function(clientCoord) { + return [ + _diagram.x().invert(clientCoord[0]), + _diagram.y().invert(clientCoord[1]) + ]; + }; + + /** + * Set the root SVGElement to either be any valid [d3 single + * selector](https://github.com/mbostock/d3/wiki/Selections#selecting-elements) specifying a dom + * block element such as a div; or a dom element or d3 selection. This class is called + * internally on diagram initialization, but be called again to relocate the diagram. However, it + * will orphan any previously created SVGElements. + * @method anchor + * @memberof dc_graph.diagram + * @instance + * @param {anchorSelector|anchorNode|d3.selection} [parent] + * @param {String} [chartGroup] + * @return {String|node|d3.selection} + * @return {dc_graph.diagram} + */ + _diagram.anchor = function(parent, chartGroup) { + if (!arguments.length) { + return _anchor; + } + if (parent) { + if (parent.select && parent.classed) { // detect d3 selection + _anchor = parent.node(); + } else { + _anchor = parent; + } + _diagram.root(d3.select(_anchor)); + _diagram.root().classed(dc_graph.constants.CHART_CLASS, true); + dc.registerChart(_diagram, chartGroup); + } else { + throw new dc.errors.BadArgumentException('parent must be defined'); + } + _chartGroup = chartGroup; + return _diagram; + }; + + /** + * Returns the internal numeric ID of the chart. + * @method chartID + * @memberof dc.baseMixin + * @instance + * @returns {String} + */ + _diagram.chartID = function () { + return _diagram.__dcFlag__; + }; + + /** + * Returns the DOM id for the chart's anchored location. + * @method anchorName + * @memberof dc_graph.diagram + * @instance + * @return {String} + */ + _diagram.anchorName = function () { + var a = _diagram.anchor(); + if (a && a.id) { + return a.id; + } + if (a && a.replace) { + return a.replace('#', ''); + } + return 'dc-graph' + _diagram.chartID(); + }; + + return _diagram.anchor(parent, chartGroup); +}; + +dc_graph.render_svg = function() { + var _svg = null, _defs = null, _g = null, _nodeLayer = null, _edgeLayer = null; + var _animating = false; // do not refresh during animations + var _zoom; + var _renderer = {}; + + _renderer.rendererType = function() { + return 'svg'; + }; + + _renderer.parent = property(null); + + _renderer.renderNode = _renderer._enterNode = function(nodeEnter) { + if(_renderer.parent().nodeTitle()) + nodeEnter.append('title'); + nodeEnter.each(infer_shape(_renderer.parent())); + _renderer.parent().forEachShape(nodeEnter, function(shape, node) { + node.call(shape.create); + }); + return _renderer; + }; + _renderer.redrawNode = _renderer._updateNode = function(node) { + var changedShape = node.filter(shape_changed(_renderer.parent())); + changedShape.selectAll('.node-shape').remove(); + changedShape.each(infer_shape(_renderer.parent())); + _renderer.parent().forEachShape(changedShape, function(shape, node) { + node.call(shape.create); + }); + node.select('title') + .text(_renderer.parent().nodeTitle.eval); + _renderer.parent().forEachContent(node, function(contentType, node) { + node.call(contentType.update); + _renderer.parent().forEachShape(contentType.selectContent(node), function(shape, content) { + content + .call(fit_shape(shape, _renderer.parent())); + }); + }); + _renderer.parent().forEachShape(node, function(shape, node) { + node.call(shape.update); + }); + node.select('.node-shape') + .attr({ + stroke: _renderer.parent().nodeStroke.eval, + 'stroke-width': _renderer.parent().nodeStrokeWidth.eval, + 'stroke-dasharray': _renderer.parent().nodeStrokeDashArray.eval, + fill: compose(_renderer.parent().nodeFillScale() || identity, _renderer.parent().nodeFill.eval) + }); + return _renderer; + }; + _renderer.redrawEdge = _renderer._updateEdge = function(edge, edgeArrows) { + edge + .attr('stroke', _renderer.parent().edgeStroke.eval) + .attr('stroke-width', _renderer.parent().edgeStrokeWidth.eval) + .attr('stroke-dasharray', _renderer.parent().edgeStrokeDashArray.eval); + edgeArrows + .attr('marker-end', function(e) { + var name = _renderer.parent().edgeArrowhead.eval(e), + id = edgeArrow(_renderer.parent(), _renderer.parent().arrows(), e, 'head', name); + return id ? 'url(#' + id + ')' : null; + }) + .attr('marker-start', function(e) { + var name = _renderer.parent().edgeArrowtail.eval(e), + arrow_id = edgeArrow(_renderer.parent(), _renderer.parent().arrows(), e, 'tail', name); + return name ? 'url(#' + arrow_id + ')' : null; + }) + .each(function(e) { + var fillEdgeStroke = _renderer.parent().edgeStroke.eval(e); + _renderer.selectAll('#' + _renderer.parent().arrowId(e, 'head')) + .attr('fill', _renderer.parent().edgeStroke.eval(e)); + _renderer.selectAll('#' + _renderer.parent().arrowId(e, 'tail')) + .attr('fill', _renderer.parent().edgeStroke.eval(e)); + }); + }; + + _renderer.selectAllNodes = function(selector) { + selector = selector || '.node'; + return _nodeLayer && _nodeLayer.selectAll(selector).filter(function(n) { + return !n.deleted; + }) || d3.selectAll('.foo-this-does-not-exist'); + }; + + _renderer.selectAllEdges = function(selector) { + selector = selector || '.edge'; + return _edgeLayer && _edgeLayer.selectAll(selector).filter(function(e) { + return !e.deleted; + }) || d3.selectAll('.foo-this-does-not-exist'); + }; + + _renderer.selectAllDefs = function(selector) { + return _defs && _defs.selectAll(selector).filter(function(def) { + return !def.deleted; + }) || d3.selectAll('.foo-this-does-not-exist'); + }; + + _renderer.resize = function(w, h) { + if(_svg) { + _svg.attr('width', w || (_renderer.parent().width_is_automatic() ? '100%' : _renderer.parent().width())) + .attr('height', h || (_renderer.parent().height_is_automatic() ? '100%' : _renderer.parent().height())); + } + return _renderer; + }; + + _renderer.rezoom = function(oldWidth, oldHeight, newWidth, newHeight) { + var scale = _zoom.scale(), translate = _zoom.translate(); + _zoom.scale(1).translate([0,0]); + var xDomain = _renderer.parent().x().domain(), yDomain = _renderer.parent().y().domain(); + _renderer.parent().x() + .domain([xDomain[0], xDomain[0] + (xDomain[1] - xDomain[0])*newWidth/oldWidth]) + .range([0, newWidth]); + _renderer.parent().y() + .domain([yDomain[0], yDomain[0] + (yDomain[1] - yDomain[0])*newHeight/oldHeight]) + .range([0, newHeight]); + _zoom + .x(_renderer.parent().x()).y(_renderer.parent().y()) + .translate(translate).scale(scale); + }; + + _renderer.globalTransform = function(pos, scale, animate) { + // _translate = pos; + // _scale = scale; + var obj = _g; + if(animate) + obj = _g.transition().duration(_renderer.parent().zoomDuration()); + obj.attr('transform', 'translate(' + pos + ')' + ' scale(' + scale + ')'); + }; + + _renderer.translate = function(_) { + if(!arguments.length) + return _zoom.translate(); + _zoom.translate(_); + return this; + }; + + _renderer.scale = function(_) { + if(!arguments.length) + return _zoom ? _zoom.scale() : 1; + _zoom.scale(_); + return this; + }; + + // argh + _renderer.commitTranslateScale = function() { + _zoom.event(_svg); + }; + + _renderer.zoom = function(_) { + if(!arguments.length) + return _zoom; + _zoom = _; // is this a good idea? + return _renderer; + }; + + _renderer.startRedraw = function(dispatch, wnodes, wedges) { + // create edge SVG elements + var edge = _edgeLayer.selectAll('.edge') + .data(wedges, _renderer.parent().edgeKey.eval); + var edgeEnter = edge.enter().append('svg:path') + .attr({ + class: 'edge', + id: _renderer.parent().edgeId, + opacity: 0 + }) + .each(function(e) { + e.deleted = false; + }); + edge.exit().each(function(e) { + e.deleted = true; + }).transition() + .duration(_renderer.parent().stagedDuration()) + .delay(_renderer.parent().deleteDelay()) + .attr('opacity', 0) + .remove(); + + var edgeArrows = _edgeLayer.selectAll('.edge-arrows') + .data(wedges, _renderer.parent().edgeKey.eval); + var edgeArrowsEnter = edgeArrows.enter().append('svg:path') + .attr({ + class: 'edge-arrows', + id: function(d) { + return _renderer.parent().edgeId(d) + '-arrows'; + }, + fill: 'none', + opacity: 0 + }); + edgeArrows.exit().transition() + .duration(_renderer.parent().stagedDuration()) + .delay(_renderer.parent().deleteDelay()) + .attr('opacity', 0) + .remove() + .each('end.delarrow', function(e) { + edgeArrow(_renderer.parent(), _renderer.parent().arrows(), e, 'head', null); + edgeArrow(_renderer.parent(), _renderer.parent().arrows(), e, 'tail', null); + }); + + if(_renderer.parent().edgeSort()) { + edge.sort(function(a, b) { + var as = _renderer.parent().edgeSort.eval(a), bs = _renderer.parent().edgeSort.eval(b); + return as < bs ? -1 : bs < as ? 1 : 0; + }); + } + + // another wider copy of the edge just for hover events + var edgeHover = _edgeLayer.selectAll('.edge-hover') + .data(wedges, _renderer.parent().edgeKey.eval); + var edgeHoverEnter = edgeHover.enter().append('svg:path') + .attr('class', 'edge-hover') + .attr('opacity', 0) + .attr('fill', 'none') + .attr('stroke', 'green') + .attr('stroke-width', 10) + .on('mouseover.diagram', function(e) { + _renderer.select('#' + _renderer.parent().edgeId(e) + '-label') + .attr('visibility', 'visible'); + }) + .on('mouseout.diagram', function(e) { + _renderer.select('#' + _renderer.parent().edgeId(e) + '-label') + .attr('visibility', 'hidden'); + }); + edgeHover.exit().remove(); + + var edgeLabels = _edgeLayer.selectAll('g.edge-label-wrapper') + .data(wedges, _renderer.parent().edgeKey.eval); + var edgeLabelsEnter = edgeLabels.enter() + .append('g') + .attr('class', 'edge-label-wrapper') + .attr('visibility', 'hidden') + .attr('id', function(e) { + return _renderer.parent().edgeId(e) + '-label'; + }); + var textPaths = _defs.selectAll('path.edge-label-path') + .data(wedges, _renderer.parent().textpathId); + var textPathsEnter = textPaths.enter() + .append('svg:path').attr({ + class: 'edge-label-path', + id: _renderer.parent().textpathId + }); + edgeLabels.exit().transition() + .duration(_renderer.parent().stagedDuration()) + .delay(_renderer.parent().deleteDelay()) + .attr('opacity', 0).remove(); + + // create node SVG elements + var node = _nodeLayer.selectAll('.node') + .data(wnodes, _renderer.parent().nodeKey.eval); + var nodeEnter = node.enter().append('g') + .attr('class', 'node') + .attr('opacity', '0') // don't show until has layout + .each(function(n) { + n.deleted = false; + }); + // .call(_d3cola.drag); + + _renderer.renderNode(nodeEnter); + + node.exit().each(function(n) { + n.deleted = true; + }).transition() + .duration(_renderer.parent().stagedDuration()) + .delay(_renderer.parent().deleteDelay()) + .attr('opacity', 0) + .remove(); + + dispatch.drawn(node, edge, edgeHover); + + var drawState = { + node: node, + nodeEnter: nodeEnter, + edge: edge, + edgeEnter: edgeEnter, + edgeHover: edgeHover, + edgeHoverEnter: edgeHoverEnter, + edgeLabels: edgeLabels, + edgeLabelsEnter: edgeLabelsEnter, + edgeArrows: edgeArrows, + edgeArrowsEnter: edgeArrowsEnter, + textPaths: textPaths, + textPathsEnter: textPathsEnter + }; + + _refresh(drawState); + + return drawState; + }; + + function _refresh(drawState) { + _renderer.redrawEdge(drawState.edge, drawState.edgeArrows); + _renderer.redrawNode(drawState.node); + _renderer.drawPorts(drawState); + } + + _renderer.refresh = function(node, edge, edgeHover, edgeLabels, textPaths) { + if(_animating) + return this; // but what about changed attributes? + node = node || _renderer.selectAllNodes(); + edge = edge || _renderer.selectAllEdges(); + var edgeArrows = _renderer.selectAllEdges('.edge-arrows'); + _refresh({node: node, edge: edge, edgeArrows: edgeArrows}); + + edgeHover = edgeHover || _renderer.selectAllEdges('.edge-hover'); + edgeLabels = edgeLabels || _renderer.selectAllEdges('.edge-label-wrapper'); + textPaths = textPaths || _renderer.selectAllDefs('path.edge-label-path'); + var nullSel = d3.select(null); // no enters + draw(node, nullSel, edge, nullSel, edgeHover, nullSel, edgeLabels, nullSel, edgeArrows, nullSel, textPaths, nullSel, false); + return this; + }; + + _renderer.reposition = function(node, edge) { + node + .attr('transform', function (n) { + return 'translate(' + n.cola.x + ',' + n.cola.y + ')'; + }); + // reset edge ports + edge.each(function(e) { + e.pos.new = null; + e.pos.old = null; + _renderer.parent().calcEdgePath(e, 'new', e.source.cola.x, e.source.cola.y, e.target.cola.x, e.target.cola.y); + if(_renderer.parent().edgeArrowhead.eval(e)) + _renderer.select('#' + _renderer.parent().arrowId(e, 'head')) + .attr('orient', function() { + return e.pos.new.orienthead; + }); + if(_renderer.parent().edgeArrowtail.eval(e)) + _renderer.select('#' + _renderer.parent().arrowId(e, 'tail')) + .attr('orient', function() { + return e.pos.new.orienttail; + }); + }) + .attr('d', generate_edge_path('new')); + return this; + }; + + function generate_edge_path(age, full) { + var field = full ? 'full' : 'path'; + return function(e) { + var path = e.pos[age][field]; + return generate_path(path.points, path.bezDegree); + }; + }; + + function generate_edge_label_path(age) { + return function(e) { + var path = e.pos[age].path; + var points = path.points[path.points.length-1].x < path.points[0].x ? + path.points.slice(0).reverse() : path.points; + return generate_path(points, path.bezDegree); + }; + }; + + function with_rad(f) { + return function() { + return f.apply(this, arguments) + 'rad'; + }; + } + + function unsurprising_orient_rad(oldorient, neworient) { + return with_rad(unsurprising_orient)(oldorient, neworient); + } + + function has_source_and_target(e) { + return !!e.source && !!e.target; + } + + _renderer.draw = function(drawState, animatePositions) { + draw(drawState.node, drawState.nodeEnter, + drawState.edge, drawState.edgeEnter, + drawState.edgeHover, drawState.edgeHoverEnter, + drawState.edgeLabels, drawState.edgeLabelsEnter, + drawState.edgeArrows, drawState.edgeArrowsEnter, + drawState.textPaths, drawState.textPathsEnter, + animatePositions); + }; + + function draw(node, nodeEnter, edge, edgeEnter, edgeHover, edgeHoverEnter, + edgeLabels, edgeLabelsEnter, edgeArrows, edgeArrowsEnter, + textPaths, textPathsEnter, animatePositions) { + console.assert(edge.data().every(has_source_and_target)); + + var nodeEntered = {}; + nodeEnter + .each(function(n) { + nodeEntered[_renderer.parent().nodeKey.eval(n)] = true; + }) + .attr('transform', function (n) { + // start new nodes at their final position + return 'translate(' + n.cola.x + ',' + n.cola.y + ')'; + }); + var ntrans = node + .transition() + .duration(_renderer.parent().stagedDuration()) + .delay(function(n) { + return _renderer.parent().stagedDelay(nodeEntered[_renderer.parent().nodeKey.eval(n)]); + }) + .attr('opacity', _renderer.parent().nodeOpacity.eval); + if(animatePositions) + ntrans + .attr('transform', function (n) { + return 'translate(' + n.cola.x + ',' + n.cola.y + ')'; + }) + .each('end.record', function(n) { + n.prevX = n.cola.x; + n.prevY = n.cola.y; + }); + + // recalculate edge positions + edge.each(function(e) { + e.pos.new = null; + }); + edge.each(function(e) { + if(e.cola.points) { + e.pos.new = place_arrows_on_spline(_renderer.parent(), e, e.cola.points); + } + else { + if(!e.pos.old) + _renderer.parent().calcEdgePath(e, 'old', e.source.prevX || e.source.cola.x, e.source.prevY || e.source.cola.y, + e.target.prevX || e.target.cola.x, e.target.prevY || e.target.cola.y); + if(!e.pos.new) + _renderer.parent().calcEdgePath(e, 'new', e.source.cola.x, e.source.cola.y, e.target.cola.x, e.target.cola.y); + } + if(e.pos.old) { + if(e.pos.old.path.bezDegree !== e.pos.new.path.bezDegree || + e.pos.old.path.points.length !== e.pos.new.path.points.length) { + //console.log('old', e.pos.old.path.points.length, 'new', e.pos.new.path.points.length); + if(is_one_segment(e.pos.old.path)) { + e.pos.new.path.points = as_bezier3(e.pos.new.path); + e.pos.old.path.points = split_bezier_n(as_bezier3(e.pos.old.path), + (e.pos.new.path.points.length-1)/3); + e.pos.old.path.bezDegree = e.pos.new.bezDegree = 3; + } + else if(is_one_segment(e.pos.new.path)) { + e.pos.old.path.points = as_bezier3(e.pos.old.path); + e.pos.new.path.points = split_bezier_n(as_bezier3(e.pos.new.path), + (e.pos.old.path.points.length-1)/3); + e.pos.old.path.bezDegree = e.pos.new.bezDegree = 3; + } + else console.warn("don't know how to interpolate two multi-segments"); + } + } + else + e.pos.old = e.pos.new; + }); + + var edgeEntered = {}; + edgeEnter + .each(function(e) { + edgeEntered[_renderer.parent().edgeKey.eval(e)] = true; + }) + .attr('d', generate_edge_path(_renderer.parent().stageTransitions() === 'modins' ? 'new' : 'old')); + + edgeArrowsEnter + .each(function(e) { + // if staging transitions, just fade new edges in at new position + // else start new edges at old positions of nodes, if any, else new positions + var age = _renderer.parent().stageTransitions() === 'modins' ? 'new' : 'old'; + if(_renderer.parent().edgeArrowhead.eval(e)) + _renderer.select('#' + _renderer.parent().arrowId(e, 'head')) + .attr('orient', function() { + return e.pos[age].orienthead; + }); + if(_renderer.parent().edgeArrowtail.eval(e)) + _renderer.select('#' + _renderer.parent().arrowId(e, 'tail')) + .attr('orient', function() { + return e.pos[age].orienttail; + }); + }) + .attr('d', generate_edge_path(_renderer.parent().stageTransitions() === 'modins' ? 'new' : 'old', true)); + + edgeArrows + .each(function(e) { + if(_renderer.parent().edgeArrowhead.eval(e)) + _renderer.select('#' + _renderer.parent().arrowId(e, 'head')) + .attr('orient', unsurprising_orient_rad(e.pos.old.orienthead, e.pos.new.orienthead)) + .transition().duration(_renderer.parent().stagedDuration()) + .delay(_renderer.parent().stagedDelay(false)) + .attr('orient', function() { + return e.pos.new.orienthead; + }); + if(_renderer.parent().edgeArrowtail.eval(e)) + _renderer.select('#' + _renderer.parent().arrowId(e, 'tail')) + .attr('orient', unsurprising_orient_rad(e.pos.old.orienttail, e.pos.new.orienttail)) + .transition().duration(_renderer.parent().stagedDuration()) + .delay(_renderer.parent().stagedDelay(false)) + .attr('orient', function() { + return e.pos.new.orienttail; + }); + }); + + var etrans = edge + .transition() + .duration(_renderer.parent().stagedDuration()) + .delay(function(e) { + return _renderer.parent().stagedDelay(edgeEntered[_renderer.parent().edgeKey.eval(e)]); + }) + .attr('opacity', _renderer.parent().edgeOpacity.eval); + var arrowtrans = edgeArrows + .transition() + .duration(_renderer.parent().stagedDuration()) + .delay(function(e) { + return _renderer.parent().stagedDelay(edgeEntered[_renderer.parent().edgeKey.eval(e)]); + }) + .attr('opacity', _renderer.parent().edgeOpacity.eval); + (animatePositions ? etrans : edge) + .attr('d', function(e) { + var when = _renderer.parent().stageTransitions() === 'insmod' && + edgeEntered[_renderer.parent().edgeKey.eval(e)] ? 'old' : 'new'; + return generate_edge_path(when)(e); + }); + (animatePositions ? arrowtrans : edgeArrows) + .attr('d', function(e) { + var when = _renderer.parent().stageTransitions() === 'insmod' && + edgeEntered[_renderer.parent().edgeKey.eval(e)] ? 'old' : 'new'; + return generate_edge_path(when, true)(e); + }); + var elabels = edgeLabels + .selectAll('text').data(function(e) { + var labels = _renderer.parent().edgeLabel.eval(e); + if(!labels) + return []; + else if(typeof labels === 'string') + return [labels]; + else return labels; + }); + elabels.enter() + .append('text') + .attr({ + 'class': 'edge-label', + 'text-anchor': 'middle', + dy: function(_, i) { + return i * _renderer.parent().edgeLabelSpacing.eval(this.parentNode) -2; + } + }) + .append('textPath') + .attr('startOffset', '50%'); + elabels + .select('textPath') + .html(function(t) { return t; }) + .attr('opacity', function() { + return _renderer.parent().edgeOpacity.eval(d3.select(this.parentNode.parentNode).datum()); + }) + .attr('xlink:href', function(e) { + var id = _renderer.parent().textpathId(d3.select(this.parentNode.parentNode).datum()); + // angular on firefox needs absolute paths for fragments + return window.location.href.split('#')[0] + '#' + id; + }); + textPathsEnter + .attr('d', generate_edge_label_path(_renderer.parent().stageTransitions() === 'modins' ? 'new' : 'old')); + var textTrans = textPaths.transition() + .duration(_renderer.parent().stagedDuration()) + .delay(function(e) { + return _renderer.parent().stagedDelay(edgeEntered[_renderer.parent().edgeKey.eval(e)]); + }); + if(animatePositions) + textTrans + .attr('d', function(e) { + var when = _renderer.parent().stageTransitions() === 'insmod' && + edgeEntered[_renderer.parent().edgeKey.eval(e)] ? 'old' : 'new'; + return generate_edge_label_path(when)(e); + }); + if(_renderer.parent().stageTransitions() === 'insmod' && animatePositions) { + // inserted edges transition twice in insmod mode + if(_renderer.parent().stagedDuration() >= 50) { + etrans = etrans.transition() + .duration(_renderer.parent().stagedDuration()) + .attr('d', generate_edge_path('new')); + textTrans = textTrans.transition() + .duration(_renderer.parent().stagedDuration()) + .attr('d', generate_edge_label_path('new')); + arrowtrans.transition() + .duration(_renderer.parent().stagedDuration()) + .attr('d', generate_edge_path('new', true)); + } else { + // if transitions are too short, we run into various problems, + // from transitions not completing to objects not found + // so don't try to chain in that case + // this also helped once: d3.timer.flush(); + etrans + .attr('d', generate_edge_path('new')); + textTrans + .attr('d', generate_edge_path('new')); + arrowtrans + .attr('d', generate_edge_path('new', true)); + } + } + + // signal layout done when all transitions complete + // because otherwise client might start another layout and lock the processor + _animating = true; + if(!_renderer.parent().showLayoutSteps()) + endall([ntrans, etrans, textTrans], + function() { + _animating = false; + _renderer.parent().layoutDone(true); + }); + + if(animatePositions) + edgeHover.attr('d', generate_edge_path('new')); + + edge.each(function(e) { + e.pos.old = e.pos.new; + }); + } + + // wait on multiple transitions, adapted from + // http://stackoverflow.com/questions/10692100/invoke-a-callback-at-the-end-of-a-transition + function endall(transitions, callback) { + if (transitions.every(function(transition) { return transition.size() === 0; })) + callback(); + var n = 0; + transitions.forEach(function(transition) { + transition + .each(function() { ++n; }) + .each('end.all', function() { if (!--n) callback(); }); + }); + } + + _renderer.isRendered = function() { + return !!_svg; + }; + + _renderer.initializeDrawing = function () { + _renderer.resetSvg(); + _g = _svg.append('g') + .attr('class', 'draw'); + + var layers = ['edge-layer', 'node-layer']; + if(_renderer.parent().edgesInFront()) + layers.reverse(); + _g.selectAll('g').data(layers) + .enter().append('g') + .attr('class', function(l) { return l; }); + _edgeLayer = _g.selectAll('g.edge-layer'); + _nodeLayer = _g.selectAll('g.node-layer'); + return this; + }; + + + /** + * Standard dc.js + * {@link https://github.com/dc-js/dc.js/blob/develop/web/docs/api-latest.md#dc.baseMixin baseMixin} + * method. Execute a d3 single selection in the diagram's scope using the given selector + * and return the d3 selection. Roughly the same as + * ```js + * d3.select('#diagram-id').select(selector) + * ``` + * Since this function returns a d3 selection, it is not chainable. (However, d3 selection + * calls can be chained after it.) + * @method select + * @memberof dc_graph.diagram + * @instance + * @param {String} [selector] + * @return {d3.selection} + * @return {dc_graph.diagram} + **/ + _renderer.select = function (s) { + return _renderer.parent().root().select(s); + }; + + /** + * Standard dc.js + * {@link https://github.com/dc-js/dc.js/blob/develop/web/docs/api-latest.md#dc.baseMixin baseMixin} + * method. Selects all elements that match the d3 single selector in the diagram's scope, + * and return the d3 selection. Roughly the same as + * + * ```js + * d3.select('#diagram-id').selectAll(selector) + * ``` + * + * Since this function returns a d3 selection, it is not chainable. (However, d3 selection + * calls can be chained after it.) + * @method selectAll + * @memberof dc_graph.diagram + * @instance + * @param {String} [selector] + * @return {d3.selection} + * @return {dc_graph.diagram} + **/ + _renderer.selectAll = function (s) { + return _renderer.parent().root() ? _renderer.parent().root().selectAll(s) : null; + }; + + _renderer.selectNodePortsOfStyle = function(node, style) { + return node.selectAll('g.port').filter(function(p) { + return _renderer.parent().portStyleName.eval(p) === style; + }); + }; + + _renderer.drawPorts = function(drawState) { + var nodePorts = _renderer.parent().nodePorts(); + if(!nodePorts) + return; + _renderer.parent().portStyle.enum().forEach(function(style) { + var nodePorts2 = {}; + for(var nid in nodePorts) + nodePorts2[nid] = nodePorts[nid].filter(function(p) { + return _renderer.parent().portStyleName.eval(p) === style; + }); + var port = _renderer.selectNodePortsOfStyle(drawState.node, style); + _renderer.parent().portStyle(style).drawPorts(port, nodePorts2, drawState.node); + }); + }; + + _renderer.fireTSEvent = function(dispatch, drawState) { + dispatch.transitionsStarted(drawState.node, drawState.edge, drawState.edgeHover); + }; + + _renderer.calculateBounds = function(drawState) { + if(!drawState.node.size()) + return null; + return _renderer.parent().calculateBounds(drawState.node.data(), drawState.edge.data()); + }; + + /** + * Standard dc.js + * {@link https://github.com/dc-js/dc.js/blob/develop/web/docs/api-latest.md#dc.baseMixin baseMixin} + * method. Returns the top `svg` element for this specific diagram. You can also pass in a new + * svg element, but setting the svg element on a diagram may have unexpected consequences. + * @method svg + * @memberof dc_graph.diagram + * @instance + * @param {d3.selection} [selection] + * @return {d3.selection} + * @return {dc_graph.diagram} + **/ + _renderer.svg = function (_) { + if (!arguments.length) { + return _svg; + } + _svg = _; + return _renderer; + }; + + /** + * Returns the top `g` element for this specific diagram. This method is usually used to + * retrieve the g element in order to overlay custom svg drawing + * programatically. **Caution**: The root g element is usually generated internally, and + * resetting it might produce unpredictable results. + * @method g + * @memberof dc_graph.diagram + * @instance + * @param {d3.selection} [selection] + * @return {d3.selection} + * @return {dc_graph.diagram} + + **/ + _renderer.g = function (_) { + if (!arguments.length) { + return _g; + } + _g = _; + return _renderer; + }; + + + /** + * Standard dc.js + * {@link https://github.com/dc-js/dc.js/blob/develop/web/docs/api-latest.md#dc.baseMixin baseMixin} + * method. Remove the diagram's SVG elements from the dom and recreate the container SVG + * element. + * @method resetSvg + * @memberof dc_graph.diagram + * @instance + * @return {dc_graph.diagram} + **/ + _renderer.resetSvg = function () { + // we might be re-initialized in a div, in which case + // we already have an <svg> element to delete + var svg = _svg || _renderer.select('svg'); + svg.remove(); + _svg = null; + //_renderer.parent().x(null).y(null); + return generateSvg(); + }; + + _renderer.addOrRemoveDef = function(id, whether, tag, onEnter) { + var data = whether ? [0] : []; + var sel = _defs.selectAll('#' + id).data(data); + + var selEnter = sel + .enter().append(tag) + .attr('id', id); + if(selEnter.size() && onEnter) + selEnter.call(onEnter); + sel.exit().remove(); + return sel; + }; + + function enableZoom() { + _svg.call(_zoom); + _svg.on('dblclick.zoom', null); + } + function disableZoom() { + _svg.on('.zoom', null); + } + + function generateSvg() { + _svg = _renderer.parent().root().append('svg'); + _renderer.resize(); + + _defs = _svg.append('svg:defs'); + + _zoom = d3.behavior.zoom() + .on('zoom.diagram', _renderer.parent().doZoom) + .x(_renderer.parent().x()).y(_renderer.parent().y()) + .scaleExtent(_renderer.parent().zoomExtent()); + if(_renderer.parent().mouseZoomable()) { + var mod, mods; + var brush = _renderer.parent().child('brush'); + if((mod = _renderer.parent().modKeyZoom())) { + if (Array.isArray (mod)) + mods = mod.slice (); + else if (typeof mod === "string") + mods = [mod]; + else + mods = ['Alt']; + var mouseDown = false, modDown = false, zoomEnabled = false; + _svg.on('mousedown.modkey-zoom', function() { + mouseDown = true; + }).on('mouseup.modkey-zoom', function() { + mouseDown = false; + if(!mouseDown && !modDown && zoomEnabled) { + zoomEnabled = false; + disableZoom(); + if(brush) + brush.activate(); + } + }); + d3.select(document) + .on('keydown.modkey-zoom-' + _renderer.parent().anchorName(), function() { + if(mods.indexOf (d3.event.key) > -1) { + modDown = true; + if(!mouseDown) { + zoomEnabled = true; + enableZoom(); + if(brush) + brush.deactivate(); + } + } + }) + .on('keyup.modkey-zoom-' + _renderer.parent().anchorName(), function() { + if(mods.indexOf (d3.event.key) > -1) { + modDown = false; + if(!mouseDown) { + zoomEnabled = false; + disableZoom(); + if(brush) + brush.activate(); + } + } + }); + } + else enableZoom(); + } + + return _svg; + } + + _renderer.animating = function() { + return _animating; + }; + + return _renderer; +}; + + +dc_graph.render_webgl = function() { + //var _svg = null, _defs = null, _g = null, _nodeLayer = null, _edgeLayer = null; + var _camera, _scene, _webgl_renderer; + var _directionalLight, _ambientLight; + var _controls; + var _sphereGeometry; + var _nodes = {}, _edges = {}; + var _animating = false; // do not refresh during animations + var _renderer = {}; + + _renderer.rendererType = function() { + return 'webgl'; + }; + + _renderer.parent = property(null); + + _renderer.isRendered = function() { + return !!_camera; + }; + + _renderer.resize = function(w, h) { + return _renderer; + }; + + _renderer.rezoom = function(oldWidth, oldHeight, newWidth, newHeight) { + return _renderer; + }; + + _renderer.globalTransform = function(pos, scale, animate) { + return _renderer; + }; + + _renderer.translate = function(_) { + if(!arguments.length) + return [0,0]; + return _renderer; + }; + + _renderer.scale = function(_) { + if(!arguments.length) + return 1; + return _renderer; + }; + + // argh + _renderer.commitTranslateScale = function() { + }; + + _renderer.initializeDrawing = function () { + if(_scene) // just treat it as a redraw + return _renderer; + + _camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10000); + _camera.up = new THREE.Vector3(0, 0, 1); + + _scene = new THREE.Scene(); + + _sphereGeometry = new THREE.SphereBufferGeometry(10, 32, 32); + + _directionalLight = new THREE.DirectionalLight(0xffffff, 1); + _directionalLight.position.set(-1, -1, 1).normalize(); + _scene.add(_directionalLight); + + _ambientLight = new THREE.AmbientLight(0xaaaaaa); + _scene.add(_ambientLight); + + _webgl_renderer = new THREE.WebGLRenderer({ antialias: true }); + _webgl_renderer.setPixelRatio(window.devicePixelRatio); + var boundRect = _renderer.parent().root().node().getBoundingClientRect(); + _webgl_renderer.setSize(boundRect.width, boundRect.height); + _renderer.parent().root().node().appendChild(_webgl_renderer.domElement); + + _controls = new THREE.OrbitControls(_camera, _webgl_renderer.domElement); + _controls.minDistance = 300; + _controls.maxDistance = 1000; + return _renderer; + }; + + _renderer.startRedraw = function(dispatch, wnodes, wedges) { + wnodes.forEach(infer_shape(_renderer.parent())); + var rnodes = regenerate_objects(_nodes, wnodes, null, function(n) { + return _renderer.parent().nodeKey.eval(n); + }, function(rn, n) { + rn.wnode = n; + }, null, function(wnode, rnode) { + _scene.remove(rnode.mesh); + //rnode.mesh.dispose(); + rnode.material.dispose(); + }); + var redges = regenerate_objects(_edges, wedges, null, function(e) { + return _renderer.parent().edgeKey.eval(e); + }, function(re, e) { + re.wedge = e; + }, null, function(wedge, redge) { + _scene.remove(redge.mesh); + //redge.mesh.dispose(); + redge.geometry.dispose(); + redge.material.dispose(); + }); + animate(); + return {wnodes: wnodes, wedges: wedges, rnodes: rnodes, redges: redges}; + }; + + function color_to_int(color) { + // it better be 6 byte hex RGB + if(color.length !== 7 || color[0] !== '#') { + console.warn("don't know how to use color " + color); + color = '#888888'; + } + return parseInt(color.slice(1), 16); + } + _renderer.color_to_int = color_to_int; + + _renderer.draw = function(drawState, animatePositions) { + drawState.wedges.forEach(function(e) { + if(!e.pos.old) + _renderer.parent().calcEdgePath(e, 'old', e.source.prevX || e.source.cola.x, e.source.prevY || e.source.cola.y, + e.target.prevX || e.target.cola.x, e.target.prevY || e.target.cola.y); + if(!e.pos.new) + _renderer.parent().calcEdgePath(e, 'new', e.source.cola.x, e.source.cola.y, e.target.cola.x, e.target.cola.y); + }); + + var MULT = _renderer.multiplier(); + drawState.rnodes.forEach(function(rn) { + var color = _renderer.parent().nodeFill.eval(rn.wnode); + var add = false; + if(!rn.mesh) { + add = true; + if(_renderer.parent().nodeFillScale()) + color = _renderer.parent().nodeFillScale()(color); + var cint = color_to_int(color); + rn.material = new THREE.MeshLambertMaterial({color: cint}); + rn.mesh = new THREE.Mesh(_sphereGeometry, rn.material); + rn.mesh.name = _renderer.parent().nodeKey.eval(rn.wnode); + } + rn.mesh.position.x = rn.wnode.cola.x * MULT; + rn.mesh.position.y = -rn.wnode.cola.y * MULT; + rn.mesh.position.z = rn.wnode.cola.z * MULT || 0; + if(add) + _scene.add(rn.mesh); + }); + + var xext = d3.extent(drawState.wnodes, function(n) { return n.cola.x * MULT; }), + yext = d3.extent(drawState.wnodes, function(n) { return -n.cola.y * MULT; }), + zext = d3.extent(drawState.wnodes, function(n) { return n.cola.z * MULT || 0; }); + var cx = (xext[0] + xext[1])/2, + cy = (yext[0] + yext[1])/2, + cz = (zext[0] + zext[1])/2; + + drawState.center = [cx, cy, cz]; + drawState.extents = [xext, yext, zext]; + _controls.target.set(cx, cy, cz); + _controls.update(); + + var vertices = []; + drawState.redges.forEach(function(re) { + if(!re.wedge.source || !re.wedge.target) + return; + var a = re.wedge.source.cola, b = re.wedge.target.cola; + var add = false; + var width = _renderer.parent().edgeStrokeWidth.eval(re.wedge); + if(!re.mesh) { + add = true; + var color = _renderer.parent().edgeStroke.eval(re.wedge); + var cint = color_to_int(color); + re.material = new THREE.MeshLambertMaterial({ color: cint }); + re.curve = new THREE.LineCurve3( + new THREE.Vector3(a.x*MULT, -a.y*MULT, a.z*MULT || 0), + new THREE.Vector3(b.x*MULT, -b.y*MULT, b.z*MULT || 0)); + re.geometry = new THREE.TubeBufferGeometry(re.curve, 20, width/2, 8, false); + re.mesh = new THREE.Mesh(re.geometry, re.material); + re.mesh.name = _renderer.parent().edgeKey.eval(re.wedge); + } else { + re.curve = new THREE.LineCurve3( + new THREE.Vector3(a.x*MULT, -a.y*MULT, a.z*MULT || 0), + new THREE.Vector3(b.x*MULT, -b.y*MULT, b.z*MULT || 0)); + re.geometry.dispose(); + re.geometry = new THREE.TubeBufferGeometry(re.curve, 20, width/2, 8, false); + re.mesh.geometry = re.geometry; + } + if(add) + _scene.add(re.mesh); + }); + _animating = false; + _renderer.parent().layoutDone(true); + return _renderer; + }; + + function animate() { + window.requestAnimationFrame(animate); + render(); + } + + function render() { + _webgl_renderer.render(_scene, _camera); + } + + _renderer.drawPorts = function(drawState) { + var nodePorts = _renderer.parent().nodePorts(); + if(!nodePorts) + return; + _renderer.parent().portStyle.enum().forEach(function(style) { + var nodePorts2 = {}; + for(var nid in nodePorts) + nodePorts2[nid] = nodePorts[nid].filter(function(p) { + return _renderer.parent().portStyleName.eval(p) === style; + }); + // not implemented + var port = _renderer.selectNodePortsOfStyle(drawState.node, style); + //_renderer.parent().portStyle(style).drawPorts(port, nodePorts2, drawState.node); + }); + }; + + _renderer.fireTSEvent = function(dispatch, drawState) { + dispatch.transitionsStarted(_scene, drawState); + }; + + _renderer.calculateBounds = function(drawState) { + if(!drawState.wnodes.length) + return null; + return _renderer.parent().calculateBounds(drawState.wnodes, drawState.wedges); + }; + + _renderer.refresh = function(node, edge, edgeHover, edgeLabels, textPaths) { + if(_animating) + return _renderer; // but what about changed attributes? + return _renderer; + }; + + _renderer.reposition = function(node, edge) { + return _renderer; + }; + + function has_source_and_target(e) { + return !!e.source && !!e.target; + } + + _renderer.animating = function() { + return _animating; + }; + + _renderer.multiplier = property(3); + + return _renderer; +}; + + +dc_graph.spawn_engine = function(layout, args, worker) { + args = args || {}; + worker = worker && !!window.Worker; + var engine = dc_graph.engines.instantiate(layout, args, worker); + if(!engine) { + console.warn('layout engine ' + layout + ' not found; using default ' + dc_graph._default_engine); + engine = dc_graph.engines.instantiate(dc_graph._default_engine, args, worker); + } + return engine; +}; + +dc_graph._engines = [ + { + name: 'dagre', + params: ['rankdir'], + instantiate: function() { + return dc_graph.dagre_layout(); + } + }, + { + name: 'd3force', + instantiate: function() { + return dc_graph.d3_force_layout(); + } + }, + { + name: 'd3v4force', + instantiate: function() { + return dc_graph.d3v4_force_layout(); + } + }, + { + name: 'tree', + instantiate: function() { + return dc_graph.tree_layout(); + } + }, + { + names: ['circo', 'dot', 'neato', 'osage', 'twopi', 'fdp'], + instantiate: function(layout, args) { + return dc_graph.graphviz_layout(null, layout, args.server); + } + }, + { + name: 'cola', + params: ['lengthStrategy'], + instantiate: function() { + return dc_graph.cola_layout(); + } + }, + { + name: 'manual', + instantiate: function() { + return dc_graph.manual_layout(); + } + }, + { + name: 'flexbox', + instantiate: function() { + return dc_graph.flexbox_layout(); + } + }, + { + name: 'layered', + instantiate: function() { + return dc_graph.layered_layout(); + } + } +]; +dc_graph._default_engine = 'cola'; + +dc_graph.engines = { + entry_pred: function(layoutName) { + return function(e) { + return e.name && e.name === layoutName || e.names && e.names.includes(layoutName); + }; + }, + get: function(layoutName) { + return dc_graph._engines.find(this.entry_pred(layoutName)); + }, + instantiate: function(layout, args, worker) { + var entry = this.get(layout); + if(!entry) + return null; + var engine = entry.instantiate(layout, args), + params = entry.params || []; + params.forEach(function(p) { + if(args[p]) + engine[p](args[p]); + }); + if(engine.supportsWebworker && engine.supportsWebworker() && worker) + engine = dc_graph.webworker_layout(engine); + return engine; + }, + available: function() { + return dc_graph._engines.reduce(function(avail, entry) { + return avail.concat(entry.name ? [entry.name] : entry.names); + }, []); + }, + unregister: function(layoutName) { + // meh. this is a bit much. there is such a thing as making the api too "easy". + var i = dc_graph._engines.findIndex(this.entry_pred(layoutName)); + var remove = false; + if(i < 0) + return false; + var entry = dc_graph._engines[i]; + if(entry.name === layoutName) + remove = true; + else { + var j = entry.names.indexOf(layoutName); + if(j >= 0) + entry.names.splice(j, 1); + else + console.warn('search for engine failed', layoutName); + if(entry.names.length === 0) + remove = true; + } + if(remove) + dc_graph._engines.splice(i, 1); + return true; + }, + register: function(entry) { + var that = this; + if(!entry.instantiate) { + console.error('engine definition needs instantiate: function(layout, args) { ... }'); + return this; + } + if(entry.name) + this.unregister(entry.name); + else if(entry.names) + entry.names.forEach(function(layoutName) { + that.unregister(layoutName); + }); + else { + console.error('engine definition needs name or names[]'); + return this; + } + dc_graph._engines.push(entry); + return this; + } +}; + +var _workers = {}; +var NUMBER_RESULTS = 3; +function create_worker(layoutAlgorithm) { + if(!_workers[layoutAlgorithm]) { + var worker = _workers[layoutAlgorithm] = { + worker: new Worker(script_path() + 'dc.graph.' + layoutAlgorithm + '.worker.js'), + layouts: {} + }; + worker.worker.onmessage = function(e) { + var layoutId = e.data.layoutId; + if(!worker.layouts[layoutId]) + throw new Error('layoutId "' + layoutId + '" unknown!'); + var engine = worker.layouts[layoutId].getEngine(); + if(e.data.args.length > NUMBER_RESULTS && engine.processExtraWorkerResults) + engine.processExtraWorkerResults.apply(engine, e.data.args.slice(NUMBER_RESULTS)); + worker.layouts[layoutId].dispatch()[e.data.response].apply(null, e.data.args); + }; + } + return _workers[layoutAlgorithm]; +} + +dc_graph.webworker_layout = function(layoutEngine) { + var _tick, _done, _dispatch = d3.dispatch('init', 'start', 'tick', 'end'); + var _worker = create_worker(layoutEngine.layoutAlgorithm()); + var engine = {}; + _worker.layouts[layoutEngine.layoutId()] = engine; + + engine.parent = function(parent) { + if(layoutEngine.parent) + layoutEngine.parent(parent); + }; + engine.init = function(options) { + options = layoutEngine.optionNames().reduce( + function(options, option) { + options[option] = layoutEngine[option](); + return options; + }, options); + if(layoutEngine.propagateOptions) + layoutEngine.propagateOptions(options); + _worker.worker.postMessage({ + command: 'init', + args: { + layoutId: layoutEngine.layoutId(), + options: options + } + }); + return this; + }; + engine.data = function(graph, nodes, edges, clusters, constraints) { + _worker.worker.postMessage({ + command: 'data', + args: { + layoutId: layoutEngine.layoutId(), + graph: graph, + nodes: nodes, + edges: edges, + clusters: clusters, + constraints: constraints + } + }); + }; + engine.start = function() { + _worker.worker.postMessage({ + command: 'start', + args: { + layoutId: layoutEngine.layoutId() + } + }); + }; + engine.stop = function() { + _worker.worker.postMessage({ + command: 'stop', + args: { + layoutId: layoutEngine.layoutId() + } + }); + return this; + }; + // stopgap while layout options are still on diagram + engine.getEngine = function() { + return layoutEngine; + }; + // somewhat sketchy - do we want this object to be transparent or not? + var passthroughs = ['layoutAlgorithm', 'populateLayoutNode', 'populateLayoutEdge', + 'rankdir', 'ranksep']; + passthroughs.concat(layoutEngine.optionNames(), + layoutEngine.passThru ? layoutEngine.passThru() : []).forEach(function(name) { + engine[name] = function() { + var ret = layoutEngine[name].apply(layoutEngine, arguments); + return arguments.length ? this : ret; + }; + }); + engine.on = function(event, f) { + if(arguments.length === 1) + return _dispatch.on(event); + _dispatch.on(event, f); + return this; + }; + engine.dispatch = function() { + return _dispatch; + }; + return engine; +}; + +/** + * `dc_graph.graphviz_attrs defines a basic set of attributes which layout engines should + * implement - although these are not required, they make it easier for clients and + * modes (like expand_collapse) to work with multiple layout engines. + * + * these attributes are {@link http://www.graphviz.org/doc/info/attrs.html from graphviz} + * @class graphviz_attrs + * @memberof dc_graph + * @return {Object} + **/ +dc_graph.graphviz_attrs = function() { + return { + /** + * Direction to draw ranks. + * @method rankdir + * @memberof dc_graph.graphviz_attrs + * @instance + * @param {String} [rankdir='TB'] 'TB', 'LR', 'BT', or 'RL' + **/ + rankdir: property('TB'), + /** + * Spacing in between nodes in the same rank. + * @method nodesep + * @memberof dc_graph.graphviz_attrs + * @instance + * @param {String} [nodesep=40] + **/ + nodesep: property(40), + /** + * Spacing in between ranks. + * @method ranksep + * @memberof dc_graph.graphviz_attrs + * @instance + * @param {String} [ranksep=40] + **/ + ranksep: property(40) + }; +}; + +// graphlib-dot seems to wrap nodes in an extra {value} +// actually this is quite a common problem with generic libs +function nvalue(n) { + return n.value.value ? n.value.value : n.value; +} + +// apply standard accessors to a diagram in order to style it as graphviz would +// this is a work in progress +dc_graph.apply_graphviz_accessors = function(diagram) { + diagram + .nodeLabel(function(n) { + var label = nvalue(n).label; + if(label === undefined) + label = n.key; + return label && label.split(/\n|\\n/); + }) + .nodeRadius(function(n) { + // should do width & height instead, #25 + return nvalue(n).radius || 25; + }) + .nodeShape(function(n) { return nvalue(n).shape; }) + .nodeFill(function(n) { return nvalue(n).fillcolor || 'white'; }) + .nodeOpacity(function(n) { + // not standard gv + return nvalue(n).opacity || 1; + }) + .nodeLabelFill(function(n) { return nvalue(n).fontcolor || 'black'; }) + .nodeTitle(function(n) { + return (nvalue(n).htmltip || nvalue(n).jsontip) ? null : + nvalue(n).tooltip !== undefined ? + nvalue(n).tooltip : + diagram.nodeLabel()(n); + }) + .nodeStrokeWidth(function(n) { + // it is debatable whether a point === a pixel but they are close + // https://graphicdesign.stackexchange.com/questions/199/point-vs-pixel-what-is-the-difference + var penwidth = nvalue(n).penwidth; + return penwidth !== undefined ? +penwidth : 1; + }) + .edgeLabel(function(e) { return e.value.label ? e.value.label.split(/\n|\\n/) : ''; }) + .edgeStroke(function(e) { return e.value.color || 'black'; }) + .edgeOpacity(function(e) { + // not standard gv + return e.value.opacity || 1; + }) + .edgeArrowSize(function(e) { + return e.value.arrowsize || 1; + }) + // need directedness to default these correctly, see #106 + .edgeArrowhead(function(e) { + var head = e.value.arrowhead; + return head !== undefined ? head : 'vee'; + }) + .edgeArrowtail(function(e) { + var tail = e.value.arrowtail; + return tail !== undefined ? tail : null; + }) + .edgeStrokeDashArray(function(e) { + switch(e.value.style) { + case 'dotted': + return [1,5]; + } + return null; + }); + var draw_clusters = diagram.child('draw-clusters'); + if(draw_clusters) { + draw_clusters + .clusterStroke(function(c) { + return c.value.color || 'black'; + }) + .clusterFill(function(c) { + return c.value.style === 'filled' ? c.value.fillcolor || c.value.color || c.value.bgcolor : null; + }) + .clusterLabel(function(c) { + return c.value.label; + }); + } +}; + +dc_graph.snapshot_graphviz = function(diagram) { + var xDomain = diagram.x().domain(), yDomain = diagram.y().domain(); + return { + nodes: diagram.nodeGroup().all().map(function(n) { + return diagram.getWholeNode(n.key); + }) + .filter(function(x) { return x; }) + .map(function(n) { + return { + key: diagram.nodeKey.eval(n), + label: diagram.nodeLabel.eval(n), + fillcolor: diagram.nodeFillScale()(diagram.nodeFill.eval(n)), + penwidth: diagram.nodeStrokeWidth.eval(n), + // not supported as input, see dc.graph.js#25 + // width: n.cola.dcg_rx*2, + // height: n.cola.dcg_ry*2, + + // not graphviz attributes + // until we have w/h + radius: diagram.nodeRadius.eval(n), + // does not seem to exist in gv + opacity: diagram.nodeOpacity.eval(n), + // should be pos + x: n.cola.x, + y: n.cola.y + }; + }), + edges: diagram.edgeGroup().all().map(function(e) { + return diagram.getWholeEdge(e.key); + }).map(function(e) { + return { + key: diagram.edgeKey.eval(e), + source: diagram.edgeSource.eval(e), + target: diagram.edgeTarget.eval(e), + color: diagram.edgeStroke.eval(e), + arrowsize: diagram.edgeArrowSize.eval(e), + opacity: diagram.edgeOpacity.eval(e), + // should support dir, see dc.graph.js#106 + arrowhead: diagram.edgeArrowhead.eval(e), + arrowtail: diagram.edgeArrowtail.eval(e) + }; + }), + bounds: { + left: xDomain[0], + top: yDomain[0], + right: xDomain[1], + bottom: yDomain[1] + } + }; +}; + +/** + * `dc_graph.cola_layout` is an adaptor for cola.js layouts in dc.graph.js + * @class cola_layout + * @memberof dc_graph + * @param {String} [id=uuid()] - Unique identifier + * @return {dc_graph.cola_layout} + **/ +dc_graph.cola_layout = function(id) { + var _layoutId = id || uuid(); + var _d3cola = null; + var _setcola_nodes; + var _dispatch = d3.dispatch('tick', 'start', 'end'); + var _flowLayout; + // node and edge objects shared with cola.js, preserved from one iteration + // to the next (as long as the object is still in the layout) + var _nodes = {}, _edges = {}; + var _options; + + function init(options) { + _options = options; + _d3cola = cola.d3adaptor() + .avoidOverlaps(true) + .size([options.width, options.height]) + .handleDisconnected(options.handleDisconnected); + + if(_d3cola.tickSize) // non-standard + _d3cola.tickSize(options.tickSize); + + switch(options.lengthStrategy) { + case 'symmetric': + _d3cola.symmetricDiffLinkLengths(options.baseLength); + break; + case 'jaccard': + _d3cola.jaccardLinkLengths(options.baseLength); + break; + case 'individual': + _d3cola.linkDistance(function(e) { + return e.dcg_edgeLength || options.baseLength; + }); + break; + case 'none': + default: + } + if(options.flowLayout) { + _d3cola.flowLayout(options.flowLayout.axis, options.flowLayout.minSeparation); + } + } + + function data(nodes, edges, clusters, constraints) { + var wnodes = regenerate_objects(_nodes, nodes, null, function(v) { + return v.dcg_nodeKey; + }, function(v1, v) { + v1.dcg_nodeKey = v.dcg_nodeKey; + v1.dcg_nodeParentCluster = v.dcg_nodeParentCluster; + v1.width = v.width; + v1.height = v.height; + v1.fixed = !!v.dcg_nodeFixed; + _options.nodeAttrs.forEach(function(key) { + v1[key] = v[key]; + }); + + if(v1.fixed && typeof v.dcg_nodeFixed === 'object') { + v1.x = v.dcg_nodeFixed.x; + v1.y = v.dcg_nodeFixed.y; + } + else { + // should we support e.g. null to unset x,y? + if(v.x !== undefined) + v1.x = v.x; + if(v.y !== undefined) + v1.y = v.y; + } + }); + var wedges = regenerate_objects(_edges, edges, null, function(e) { + return e.dcg_edgeKey; + }, function(e1, e) { + e1.dcg_edgeKey = e.dcg_edgeKey; + // cola edges can work with indices or with object references + // but it will replace indices with object references + e1.source = _nodes[e.dcg_edgeSource]; + e1.target = _nodes[e.dcg_edgeTarget]; + e1.dcg_edgeLength = e.dcg_edgeLength; + _options.edgeAttrs.forEach(function(key) { + e1[key] = e[key]; + }); + }); + + // cola needs each node object to have an index property + wnodes.forEach(function(v, i) { + v.index = i; + }); + + var groups = null; + if(engine.groupConnected()) { + var components = cola.separateGraphs(wnodes, wedges); + groups = components.map(function(g) { + return { + dcg_autoGroup: true, + leaves: g.array.map(function(n) { return n.index; }) + }; + }); + } else if(clusters) { + var G = {}; + groups = clusters.filter(function(c) { + return /^cluster/.test(c.dcg_clusterKey); + }).map(function(c, i) { + return G[c.dcg_clusterKey] = { + dcg_clusterKey: c.dcg_clusterKey, + index: i, + groups: [], + leaves: [] + }; + }); + clusters.forEach(function(c) { + if(c.dcg_clusterParent && G[c.dcg_clusterParent]) + G[c.dcg_clusterParent].groups.push(G[c.dcg_clusterKey].index); + }); + wnodes.forEach(function(n, i) { + if(n.dcg_nodeParentCluster && G[n.dcg_nodeParentCluster]) + G[n.dcg_nodeParentCluster].leaves.push(i); + }); + } + + function dispatchState(event) { + // clean up extra setcola annotations + wnodes.forEach(function(n) { + Object.keys(n).forEach(function(key) { + if(/^get/.test(key) && typeof n[key] === 'function') + delete n[key]; + }); + }); + _dispatch[event]( + wnodes, + wedges.map(function(e) { + return {dcg_edgeKey: e.dcg_edgeKey}; + }), + groups.filter(function(g) { + return !g.dcg_autoGroup; + }).map(function(g) { + g = Object.assign({}, g); + g.bounds = { + left: g.bounds.x, + top: g.bounds.y, + right: g.bounds.X, + bottom: g.bounds.Y + }; + return g; + }), + _setcola_nodes + ); + } + _d3cola.on('tick', /* _tick = */ function() { + dispatchState('tick'); + }).on('start', function() { + _dispatch.start(); + }).on('end', /* _done = */ function() { + dispatchState('end'); + }); + + if(_options.setcolaSpec && typeof setcola !== 'undefined') { + console.log('generating setcola constrains'); + var setcola_result = setcola + .nodes(wnodes) + .links(wedges) + .constraints(_options.setcolaSpec) + .gap(10) //default value is 10, can be customized in setcolaSpec + .layout(); + + _setcola_nodes = setcola_result.nodes.filter(function(n) { return n._cid; }); + _d3cola.nodes(setcola_result.nodes) + .links(setcola_result.links) + .constraints(setcola_result.constraints) + .groups(groups); + } else { + _d3cola.nodes(wnodes) + .links(wedges) + .constraints(constraints) + .groups(groups); + } + + } + + function start() { + _d3cola.start(engine.unconstrainedIterations(), + engine.userConstraintIterations(), + engine.allConstraintsIterations(), + engine.gridSnapIterations()); + } + + function stop() { + if(_d3cola) + _d3cola.stop(); + } + + var graphviz = dc_graph.graphviz_attrs(), graphviz_keys = Object.keys(graphviz); + graphviz.rankdir(null); + + var engine = Object.assign(graphviz, { + layoutAlgorithm: function() { + return 'cola'; + }, + layoutId: function() { + return _layoutId; + }, + supportsWebworker: function() { + return true; + }, + parent: property(null), + on: function(event, f) { + if(arguments.length === 1) + return _dispatch.on(event); + _dispatch.on(event, f); + return this; + }, + init: function(options) { + this.optionNames().forEach(function(option) { + options[option] = options[option] || this[option](); + }.bind(this)); + this.propagateOptions(options); + init(options); + return this; + }, + data: function(graph, nodes, edges, clusters, constraints) { + data(nodes, edges, clusters, constraints); + }, + start: function() { + start(); + }, + stop: function() { + stop(); + }, + optionNames: function() { + return ['handleDisconnected', 'lengthStrategy', 'baseLength', 'flowLayout', + 'tickSize', 'groupConnected', 'setcolaSpec', 'setcolaNodes'] + .concat(graphviz_keys); + }, + passThru: function() { + return ['extractNodeAttrs', 'extractEdgeAttrs']; + }, + propagateOptions: function(options) { + if(!options.nodeAttrs) + options.nodeAttrs = Object.keys(engine.extractNodeAttrs()); + if(!options.edgeAttrs) + options.edgeAttrs = Object.keys(engine.extractEdgeAttrs()); + }, + populateLayoutNode: function() {}, + populateLayoutEdge: function() {}, + /** + * Instructs cola.js to fit the connected components. + * @method handleDisconnected + * @memberof dc_graph.cola_layout + * @instance + * @param {Boolean} [handleDisconnected=true] + * @return {Boolean} + * @return {dc_graph.cola_layout} + **/ + handleDisconnected: property(true), + /** + * Currently, three strategies are supported for specifying the lengths of edges: + * * 'individual' - uses the `edgeLength` for each edge. If it returns falsy, uses the + * `baseLength` + * * 'symmetric', 'jaccard' - compute the edge length based on the graph structure around + * the edge. See + * {@link https://github.com/tgdwyer/WebCola/wiki/link-lengths the cola.js wiki} + * for more details. + * 'none' - no edge lengths will be specified + * @method lengthStrategy + * @memberof dc_graph.cola_layout + * @instance + * @param {Function|String} [lengthStrategy='symmetric'] + * @return {Function|String} + * @return {dc_graph.cola_layout} + **/ + lengthStrategy: property('symmetric'), + /** + * Gets or sets the default edge length (in pixels) when the `.lengthStrategy` is + * 'individual', and the base value to be multiplied for 'symmetric' and 'jaccard' edge + * lengths. + * @method baseLength + * @memberof dc_graph.cola_layout + * @instance + * @param {Number} [baseLength=30] + * @return {Number} + * @return {dc_graph.cola_layout} + **/ + baseLength: property(30), + /** + * If `flowLayout` is set, it determines the axis and separation for + * {@link http://marvl.infotech.monash.edu/webcola/doc/classes/cola.layout.html#flowlayout cola flow layout}. + * If it is not set, `flowLayout` will be calculated from the {@link dc_graph.graphviz_attrs#rankdir rankdir} + * and {@link dc_graph.graphviz_attrs#ranksep ranksep}; if `rankdir` is also null (the + * default for cola layout), then there will be no flow. + * @method flowLayout + * @memberof dc_graph.cola_layout + * @instance + * @param {Object} [flowLayout=null] + * @example + * // No flow (default) + * diagram.flowLayout(null) + * // flow in x with min separation 200 + * diagram.flowLayout({axis: 'x', minSeparation: 200}) + **/ + flowLayout: function(flow) { + if(!arguments.length) { + if(_flowLayout) + return _flowLayout; + var dir = engine.rankdir(); + switch(dir) { + case 'LR': return {axis: 'x', minSeparation: engine.ranksep() + engine.parent().nodeRadius()*2}; + case 'TB': return {axis: 'y', minSeparation: engine.ranksep() + engine.parent().nodeRadius()*2}; + default: return null; // RL, BT do not appear to be possible (negative separation) (?) + } + } + _flowLayout = flow; + return this; + }, + unconstrainedIterations: property(10), + userConstraintIterations: property(20), + allConstraintsIterations: property(20), + gridSnapIterations: property(0), + tickSize: property(1), + groupConnected: property(false), + setcolaSpec: property(null), + setcolaNodes: function() { + return _setcola_nodes; + }, + extractNodeAttrs: property({}), // {attr: function(node)} + extractEdgeAttrs: property({}), + processExtraWorkerResults: function(setcolaNodes) { + _setcola_nodes = setcolaNodes; + } + }); + return engine; +}; + +dc_graph.cola_layout.scripts = ['d3.js', 'cola.js']; +dc_graph.cola_layout.optional_scripts = ['setcola.js']; + +/** + * `dc_graph.dagre_layout` is an adaptor for dagre.js layouts in dc.graph.js + * + * In addition to the below layout attributes, `dagre_layout` also implements the attributes from + * {@link dc_graph.graphviz_attrs graphviz_attrs} + * @class dagre_layout + * @memberof dc_graph + * @param {String} [id=uuid()] - Unique identifier + * @return {dc_graph.dagre_layout} + **/ +dc_graph.dagre_layout = function(id) { + var _layoutId = id || uuid(); + var _dagreGraph = null, _tick, _done; + var _dispatch = d3.dispatch('tick', 'start', 'end'); + // node and edge objects preserved from one iteration + // to the next (as long as the object is still in the layout) + var _nodes = {}, _edges = {}; + + function init(options) { + // Create a new directed graph + _dagreGraph = new dagre.graphlib.Graph({multigraph: true, compound: true}); + + // Set an object for the graph label + _dagreGraph.setGraph({rankdir: options.rankdir, nodesep: options.nodesep, ranksep: options.ranksep}); + + // Default to assigning a new object as a label for each new edge. + _dagreGraph.setDefaultEdgeLabel(function() { return {}; }); + } + + function data(nodes, edges, clusters) { + var wnodes = regenerate_objects(_nodes, nodes, null, function(v) { + return v.dcg_nodeKey; + }, function(v1, v) { + v1.dcg_nodeKey = v.dcg_nodeKey; + v1.width = v.width; + v1.height = v.height; + /* + dagre does not seem to accept input positions + if(v.dcg_nodeFixed) { + v1.x = v.dcg_nodeFixed.x; + v1.y = v.dcg_nodeFixed.y; + } + */ + }, function(k, o) { + _dagreGraph.setNode(k, o); + }, function(k) { + _dagreGraph.removeNode(k); + }); + var wedges = regenerate_objects(_edges, edges, null, function(e) { + return e.dcg_edgeKey; + }, function(e1, e) { + e1.dcg_edgeKey = e.dcg_edgeKey; + e1.dcg_edgeSource = e.dcg_edgeSource; + e1.dcg_edgeTarget = e.dcg_edgeTarget; + }, function(k, o, e) { + _dagreGraph.setEdge(e.dcg_edgeSource, e.dcg_edgeTarget, o); + }, function(k, e) { + _dagreGraph.removeEdge(e.dcg_edgeSource, e.dcg_edgeTarget, e.dcg_edgeKey); + }); + clusters = clusters.filter(function(c) { + return /^cluster/.test(c.dcg_clusterKey); + }); + clusters.forEach(function(c) { + _dagreGraph.setNode(c.dcg_clusterKey, c); + }); + clusters.forEach(function(c) { + if(c.dcg_clusterParent) + _dagreGraph.setParent(c.dcg_clusterKey, c.dcg_clusterParent); + }); + nodes.forEach(function(n) { + if(n.dcg_nodeParentCluster) + _dagreGraph.setParent(n.dcg_nodeKey, n.dcg_nodeParentCluster); + }); + + function dispatchState(event) { + _dispatch[event]( + wnodes, + wedges.map(function(e) { + return {dcg_edgeKey: e.dcg_edgeKey}; + }), + clusters.map(function(c) { + var c = Object.assign({}, _dagreGraph.node(c.dcg_clusterKey)); + c.bounds = { + left: c.x - c.width/2, + top: c.y - c.height/2, + right: c.x + c.width/2, + bottom: c.y + c.height/2 + }; + return c; + }) + ); + } + _tick = function() { + dispatchState('tick'); + }; + _done = function() { + dispatchState('end'); + }; + } + + function start(options) { + _dispatch.start(); + dagre.layout(_dagreGraph); + _done(); + } + + function stop() { + } + + var graphviz = dc_graph.graphviz_attrs(), graphviz_keys = Object.keys(graphviz); + return Object.assign(graphviz, { + layoutAlgorithm: function() { + return 'dagre'; + }, + layoutId: function() { + return _layoutId; + }, + supportsWebworker: function() { + return true; + }, + on: function(event, f) { + if(arguments.length === 1) + return _dispatch.on(event); + _dispatch.on(event, f); + return this; + }, + init: function(options) { + this.optionNames().forEach(function(option) { + options[option] = options[option] || this[option](); + }.bind(this)); + init(options); + return this; + }, + data: function(graph, nodes, edges, clusters) { + data(nodes, edges, clusters); + }, + start: function() { + start(); + }, + stop: function() { + stop(); + }, + optionNames: function() { + return graphviz_keys; + }, + populateLayoutNode: function() {}, + populateLayoutEdge: function() {} + }); +}; + +dc_graph.dagre_layout.scripts = ['d3.js', 'dagre.js']; + +/** + * `dc_graph.tree_layout` is a very simple and not very bright tree layout. It can draw any DAG, but + * tries to position the nodes as a tree. + * @class tree_layout + * @memberof dc_graph + * @param {String} [id=uuid()] - Unique identifier + * @return {dc_graph.tree_layout} + **/ +dc_graph.tree_layout = function(id) { + var _layoutId = id || uuid(); + var _dispatch = d3.dispatch('tick', 'start', 'end'); + var _dfs; + + function init(options) { + var x; + var nodeWidth = d3.functor(options.nodeWidth); + function best_dist(left, right) { + return (nodeWidth(left) + nodeWidth(right)) / 2; + } + _dfs = dc_graph.depth_first_traversal({ + nodeid: function(n) { + return n.dcg_nodeKey; + }, + sourceid: function(n) { + return n.dcg_edgeSource; + }, + targetid: function(n) { + return n.dcg_edgeTarget; + }, + init: function() { + x = options.offsetX; + }, + row: function(n) { + return n.dcg_rank; + }, + place: function(n, r, row) { + if(row.length) { + var left = row[row.length-1]; + var g = (nodeWidth(left) + nodeWidth(n)) / 2; + x = Math.max(x, left.left_x + g); + } + n.left_x = x; + n.hit_ins = 1; + n.y = r*options.gapY + options.offsetY; + }, + sib: function(isroot, left, right) { + var g = best_dist(left, right); + if(isroot) g = g*1.5; + x += g; + }, + pop: function(n) { + n.x = (n.left_x + x)/2; + }, + skip: function(n, indegree) { + // rolling average of in-neighbor x positions + n.x = (n.hit_ins*n.x + x)/++n.hit_ins; + if(n.hit_ins === indegree) + delete n.hit_ins; + }, + finish: function(rows) { + // this is disgusting. patch up any places where nodes overlap by scanning + // right far enough to find the space, then fill from left to right at the + // minimum gap + rows.forEach(function(row) { + var sort = row.sort(function(a, b) { return a.x - b.x; }); + var badi = null, badl = null, want; + for(var i=0; i<sort.length-1; ++i) { + var left = sort[i], right = sort[i+1]; + if(!badi) { + if(right.x - left.x < best_dist(left, right)) { + badi = i; + badl = left.x; + want = best_dist(left, right); + } // else still not bad + } else { + want += best_dist(left, right); + if(i < sort.length - 2 && right.x < badl + want) + continue; // still bad + else { + if(badi>0) + --badi; // might want to use more left + var l, limit; + if(i < sort.length - 2) { // found space before right + var extra = right.x - (badl + want); + l = sort[badi].x + extra/2; + limit = i+1; + } else { + l = Math.max(sort[badi].x, badl - best_dist(sort[badi], sort[badi+1]) - (want - right.x + badl)/2); + limit = sort.length; + } + for(var j = badi+1; j<limit; ++j) { + l += best_dist(sort[j-1], sort[j]); + sort[j].x = l; + } + badi = badl = want = null; + } + } + } + }); + } + }); + } + + var _nodes, _edges; + function data(nodes, edges) { + _nodes = nodes; + _edges = edges; + } + + function start() { + _dfs(_nodes, _edges); + _dispatch.end(_nodes, _edges); + } + + function stop() { + } + + var layout = { + layoutAlgorithm: function() { + return 'tree'; + }, + layoutId: function() { + return _layoutId; + }, + supportsWebworker: function() { + return false; + }, + on: function(event, f) { + if(arguments.length === 1) + return _dispatch.on(event); + _dispatch.on(event, f); + return this; + }, + init: function(options) { + this.optionNames().forEach(function(option) { + options[option] = options[option] || this[option](); + }.bind(this)); + init(options); + return this; + }, + data: function(graph, nodes, edges) { + data(nodes, edges); + }, + start: function() { + start(); + }, + stop: function() { + stop(); + }, + optionNames: function() { + return ['nodeWidth', 'offsetX', 'offsetY', 'rowFunction', 'gapY']; + }, + populateLayoutNode: function(layout, node) { + if(this.rowFunction()) + layout.dcg_rank = this.rowFunction.eval(node); + }, + populateLayoutEdge: function() {}, + nodeWidth: property(function(n) { return n.width; }), + offsetX: property(30), + offsetY: property(30), + rowFunction: property(null), + gapY: property(100) + }; + return layout; +}; + +dc_graph.tree_layout.scripts = []; + +/** + * `dc_graph.graphviz_layout` is an adaptor for viz.js (graphviz) layouts in dc.graph.js + * + * In addition to the below layout attributes, `graphviz_layout` also implements the attributes from + * {@link dc_graph.graphviz_attrs graphviz_attrs} + * @class graphviz_layout + * @memberof dc_graph + * @param {String} [id=uuid()] - Unique identifier + * @return {dc_graph.graphviz_layout} + **/ +dc_graph.graphviz_layout = function(id, layout, server) { + var _layoutId = id || uuid(); + var _dispatch = d3.dispatch('tick', 'start', 'end'); + var _dotInput, _dotString; + + function init(options) { + } + + function encode_name(name) { + return name.replace(/^%/, '%'); + } + function decode_name(name) { + return name.replace(/^%/, '%'); + } + function stringize_property(prop, value) { + return [prop, '"' + value + '"'].join('='); + } + function stringize_properties(props) { + return '[' + props.join(', ') + ']'; + } + function data(nodes, edges, clusters) { + if(_dotInput) { + _dotString = _dotInput; + return; + } + var lines = []; + var directed = layout !== 'neato'; + lines.push((directed ? 'digraph' : 'graph') + ' g {'); + lines.push('graph ' + stringize_properties([ + stringize_property('nodesep', graphviz.nodesep()/72), + stringize_property('ranksep', graphviz.ranksep()/72), + stringize_property('rankdir', graphviz.rankdir()) + ])); + var cluster_nodes = {}; + nodes.forEach(function(n) { + var cl = n.dcg_nodeParentCluster; + if(cl) { + cluster_nodes[cl] = cluster_nodes[cl] || []; + cluster_nodes[cl].push(n.dcg_nodeKey); + } + }); + var cluster_children = {}, tops = []; + clusters.forEach(function(c) { + var p = c.dcg_clusterParent; + if(p) { + cluster_children[p] = cluster_children[p] || []; + cluster_children[p].push(c.dcg_clusterKey); + } else tops.push(c.dcg_clusterKey); + }); + + function print_subgraph(i, c) { + var indent = ' '.repeat(i*2); + lines.push(indent + 'subgraph "' + c + '" {'); + if(cluster_children[c]) + cluster_children[c].forEach(print_subgraph.bind(null, i+1)); + lines.push(indent + ' ' + cluster_nodes[c].join(' ')); + lines.push(indent + '}'); + } + tops.forEach(print_subgraph.bind(null, 1)); + + lines = lines.concat(nodes.map(function(v) { + var props = [ + stringize_property('width', v.width/72), + stringize_property('height', v.height/72), + stringize_property('fixedsize', 'shape'), + stringize_property('shape', v.abstract.shape) + ]; + if(v.dcg_nodeFixed) + props.push(stringize_property('pos', [ + v.dcg_nodeFixed.x, + 1000-v.dcg_nodeFixed.y + ].join(','))); + return ' "' + encode_name(v.dcg_nodeKey) + '" ' + stringize_properties(props); + })); + lines = lines.concat(edges.map(function(e) { + return ' "' + encode_name(e.dcg_edgeSource) + (directed ? '" -> "' : '" -- "') + + encode_name(e.dcg_edgeTarget) + '" ' + stringize_properties([ + stringize_property('id', encode_name(e.dcg_edgeKey)), + stringize_property('arrowhead', 'none'), + stringize_property('arrowtail', 'none') + ]); + })); + lines.push('}'); + lines.push(''); + _dotString = lines.join('\n'); + } + + function process_response(error, result) { + if(error) { + console.warn("graphviz layout failed: ", error); + return; + } + _dispatch.start(); + var bb = result.bb.split(',').map(function(x) { return +x; }); + var nodes = (result.objects || []).filter(function(n) { + return n.pos; // remove non-nodes like clusters + }).map(function(n) { + var pos = n.pos.split(','); + if(isNaN(pos[0]) || isNaN(pos[1])) { + console.warn('got a NaN position from graphviz'); + pos[0] = pos[1] = 0; + } + return { + dcg_nodeKey: decode_name(n.name), + x: +pos[0], + y: bb[3] - pos[1] + }; + }); + var clusters = (result.objects || []).filter(function(n) { + return /^cluster/.test(n.name); + }); + clusters.forEach(function(c) { + c.dcg_clusterKey = c.name; + + // gv: llx, lly, urx, ury, up-positive + var cbb = c.bb.split(',').map(function(s) { return +s; }); + c.bounds = {left: cbb[0], top: bb[3] - cbb[3], + right: cbb[2], bottom: bb[3] - cbb[1]}; + }); + var edges = (result.edges || []).map(function(e) { + var e2 = { + dcg_edgeKey: decode_name(e.id || 'n' + e._gvid) + }; + if(e._draw_) { + var directive = e._draw_.find(function(d) { return d.op && d.points; }); + e2.points = directive.points.map(function(p) { return {x: p[0], y: bb[3] - p[1]}; }); + } + return e2; + }); + _dispatch.end(nodes, edges, clusters); + } + + function start() { + if(server) { + d3.json(server) + .header("Content-type", "application/x-www-form-urlencoded") + .post('layouttool=' + layout + '&' + encodeURIComponent(_dotString), process_response); + } + else { + var result = Viz(_dotString, {format: 'json', engine: layout, totalMemory: 1 << 25}); + result = JSON.parse(result); + process_response(null, result); + } + } + + function stop() { + } + + var graphviz = dc_graph.graphviz_attrs(), graphviz_keys = Object.keys(graphviz); + return Object.assign(graphviz, { + layoutAlgorithm: function() { + return layout; + }, + layoutId: function() { + return _layoutId; + }, + supportsWebworker: function() { + return false; + }, + on: function(event, f) { + if(arguments.length === 1) + return _dispatch.on(event); + _dispatch.on(event, f); + return this; + }, + init: function(options) { + this.optionNames().forEach(function(option) { + options[option] = options[option] || this[option](); + }.bind(this)); + init(options); + return this; + }, + data: function(graph, nodes, edges, clusters) { + data(nodes, edges, clusters); + }, + dotInput: function(text) { + _dotInput = text; + return this; + }, + start: function() { + start(); + }, + stop: function() { + stop(); + }, + optionNames: function() { + return graphviz_keys; + }, + populateLayoutNode: function() {}, + populateLayoutEdge: function() {} + }); +} + + +/** + * `dc_graph.d3_force_layout` is an adaptor for d3-force layouts in dc.graph.js + * @class d3_force_layout + * @memberof dc_graph + * @param {String} [id=uuid()] - Unique identifier + * @return {dc_graph.d3_force_layout} + **/ +dc_graph.d3_force_layout = function(id) { + var _layoutId = id || uuid(); + var _simulation = null; // d3-force simulation + var _dispatch = d3.dispatch('tick', 'start', 'end'); + // node and edge objects shared with d3-force, preserved from one iteration + // to the next (as long as the object is still in the layout) + var _nodes = {}, _edges = {}; + var _wnodes = [], _wedges = []; + var _options = null; + var _paths = null; + + function init(options) { + _options = options; + + _simulation = d3.layout.force() + .size([options.width, options.height]); + if(options.linkDistance) { + if(typeof options.linkDistance === 'number') + _simulation.linkDistance(options.linkDistance); + else if(options.linkDistance === 'auto') + _simulation.linkDistance(function(e) { + return e.dcg_edgeLength; + }); + } + + _simulation.on('tick', /* _tick = */ function() { + dispatchState('tick'); + }).on('start', function() { + _dispatch.start(); + }).on('end', /* _done = */ function() { + dispatchState('end'); + }); + } + + function dispatchState(event) { + _dispatch[event]( + _wnodes, + _wedges.map(function(e) { + return {dcg_edgeKey: e.dcg_edgeKey}; + }) + ); + } + + function data(nodes, edges, constraints) { + var nodeIDs = {}; + nodes.forEach(function(d, i) { + nodeIDs[d.dcg_nodeKey] = i; + }); + + _wnodes = regenerate_objects(_nodes, nodes, null, function(v) { + return v.dcg_nodeKey; + }, function(v1, v) { + v1.dcg_nodeKey = v.dcg_nodeKey; + v1.width = v.width; + v1.height = v.height; + v1.id = v.dcg_nodeKey; + if(v.dcg_nodeFixed) { + v1.fixed = true; + v1.x = v.dcg_nodeFixed.x; + v1.y = v.dcg_nodeFixed.y; + } else v1.fixed = false; + }); + + _wedges = regenerate_objects(_edges, edges, null, function(e) { + return e.dcg_edgeKey; + }, function(e1, e) { + e1.dcg_edgeKey = e.dcg_edgeKey; + // cola edges can work with indices or with object references + // but it will replace indices with object references + e1.source = _nodes[e.dcg_edgeSource]; + e1.source.id = nodeIDs[e1.source.dcg_nodeKey]; + e1.target = _nodes[e.dcg_edgeTarget]; + e1.target.id = nodeIDs[e1.target.dcg_nodeKey]; + e1.dcg_edgeLength = e.dcg_edgeLength; + }); + + _simulation.nodes(_wnodes); + _simulation.links(_wedges); + } + + function start() { + installForces(); + runSimulation(_options.iterations); + } + + function stop() { + if(_simulation) + _simulation.stop(); + } + + function savePositions() { + var data = {}; + Object.keys(_nodes).forEach(function(key) { + data[key] = {x: _nodes[key].x, y: _nodes[key].y}; + }); + return data; + } + + function restorePositions(data) { + Object.keys(data).forEach(function(key) { + if(_nodes[key]) { + _nodes[key].fixed = false; + _nodes[key].x = data[key].x; + _nodes[key].y = data[key].y; + } + }); + } + + function installForces() { + if(_paths === null) + _simulation.gravity(_options.gravityStrength) + .charge(_options.initialCharge); + else { + if(_options.fixOffPathNodes) { + var nodesOnPath = d3.set(); // nodes on path + _paths.forEach(function(path) { + path.forEach(function(nid) { + nodesOnPath.add(nid); + }); + }); + + // fix nodes not on paths + Object.keys(_nodes).forEach(function(key) { + if(!nodesOnPath.has(key)) { + _nodes[key].fixed = true; + } else { + _nodes[key].fixed = false; + } + }); + } + + // enlarge charge force to separate nodes on paths + _simulation.charge(_options.chargeForce); + } + }; + + function runSimulation(iterations) { + if(!iterations) { + dispatchState('end'); + return; + } + _simulation.start(); + for (var i = 0; i < 300; ++i) { + _simulation.tick(); + if(_paths) + applyPathAngleForces(); + } + _simulation.stop(); + } + + function applyPathAngleForces() { + function _dot(v1, v2) { return v1.x*v2.x + v1.y*v2.y; }; + function _len(v) { return Math.sqrt(v.x*v.x + v.y*v.y); }; + function _angle(v1, v2) { + var a = _dot(v1, v2) / (_len(v1)*_len(v2)); + a = Math.min(a, 1); + a = Math.max(a, -1); + return Math.acos(a); + }; + // perpendicular unit length vector + function _pVec(v) { + var xx = -v.y/v.x, yy = 1; + var length = _len({x: xx, y: yy}); + return {x: xx/length, y: yy/length}; + }; + + function updateNode(node, angle, pVec, alpha) { + node.x += pVec.x*(Math.PI-angle)*alpha; + node.y += pVec.y*(Math.PI-angle)*alpha; + } + + _paths.forEach(function(path) { + if(path.length < 3) return; // at least 3 nodes (and 2 edges): A->B->C + for(var i = 1; i < path.length-1; ++i) { + var current = _nodes[path[i]]; + var prev = _nodes[path[i-1]]; + var next = _nodes[path[i+1]]; + + // calculate the angle + var vPrev = {x: prev.x - current.x, y: prev.y - current.y}; + var vNext = {x: next.x - current.x, y: next.y - current.y}; + + var angle = _angle(vPrev, vNext); // angle in [0, PI] + + var pvecPrev = _pVec(vPrev); + var pvecNext = _pVec(vNext); + + // make sure the perpendicular vector is in the + // direction that makes the angle more towards 180 degree + // 1. calculate the middle point of node 'prev' and 'next' + var mid = {x: (prev.x+next.x)/2.0, y: (prev.y+next.y)/2.0}; + // 2. calculate the vectors: 'prev' pointing to 'mid', 'next' pointing to 'mid' + var prev_mid = {x: mid.x-prev.x, y: mid.y-prev.y}; + var next_mid = {x: mid.x-next.x, y: mid.y-next.y}; + // 3. the 'correct' vector: the angle between pvec and prev_mid(next_mid) should + // be an obtuse angle + pvecPrev = _angle(prev_mid, pvecPrev) >= Math.PI/2.0 ? pvecPrev : {x: -pvecPrev.x, y: -pvecPrev.y}; + pvecNext = _angle(next_mid, pvecNext) >= Math.PI/2.0 ? pvecNext : {x: -pvecNext.x, y: -pvecNext.y}; + + // modify positions of prev and next + updateNode(prev, angle, pvecPrev, _options.angleForce); + updateNode(next, angle, pvecNext, _options.angleForce); + } + + }); + } + + var graphviz = dc_graph.graphviz_attrs(), graphviz_keys = Object.keys(graphviz); + + var engine = Object.assign(graphviz, { + layoutAlgorithm: function() { + return 'd3-force'; + }, + layoutId: function() { + return _layoutId; + }, + supportsWebworker: function() { + return true; + }, + parent: property(null), + on: function(event, f) { + if(arguments.length === 1) + return _dispatch.on(event); + _dispatch.on(event, f); + return this; + }, + init: function(options) { + this.optionNames().forEach(function(option) { + options[option] = options[option] || this[option](); + }.bind(this)); + init(options); + return this; + }, + data: function(graph, nodes, edges, constraints) { + data(nodes, edges, constraints); + }, + start: function() { + start(); + }, + stop: function() { + stop(); + }, + paths: function(paths) { + _paths = paths; + }, + savePositions: savePositions, + restorePositions: restorePositions, + optionNames: function() { + return ['iterations', 'angleForce', 'chargeForce', 'gravityStrength', + 'initialCharge', 'linkDistance', 'fixOffPathNodes'] + .concat(graphviz_keys); + }, + iterations: property(300), + angleForce: property(0.02), + chargeForce: property(-500), + gravityStrength: property(1.0), + initialCharge: property(-400), + linkDistance: property(20), + fixOffPathNodes: property(false), + populateLayoutNode: function() {}, + populateLayoutEdge: function() {} + }); + return engine; +}; + +dc_graph.d3_force_layout.scripts = ['d3.js']; + +/** + * `dc_graph.d3v4_force_layout` is an adaptor for d3-force version 4 layouts in dc.graph.js + * @class d3v4_force_layout + * @memberof dc_graph + * @param {String} [id=uuid()] - Unique identifier + * @return {dc_graph.d3v4_force_layout} + **/ +dc_graph.d3v4_force_layout = function(id) { + var _layoutId = id || uuid(); + var _simulation = null; // d3-force simulation + var _dispatch = d3.dispatch('tick', 'start', 'end'); + // node and edge objects shared with d3-force, preserved from one iteration + // to the next (as long as the object is still in the layout) + var _nodes = {}, _edges = {}; + var _wnodes = [], _wedges = []; + var _options = null; + var _paths = null; + + function init(options) { + _options = options; + + _simulation = d3v4.forceSimulation() + .force('link', d3v4.forceLink()) + .force('center', d3v4.forceCenter(options.width / 2, options.height / 2)) + .force('gravityX', d3v4.forceX(options.width / 2).strength(_options.gravityStrength)) + .force('gravityY', d3v4.forceY(options.height / 2).strength(_options.gravityStrength)) + .force('collision', d3v4.forceCollide(_options.collisionRadius)) + .force('charge', d3v4.forceManyBody()) + .stop(); + } + + function dispatchState(event) { + _dispatch[event]( + _wnodes, + _wedges.map(function(e) { + return {dcg_edgeKey: e.dcg_edgeKey}; + }) + ); + } + + function data(nodes, edges) { + var nodeIDs = {}; + nodes.forEach(function(d, i) { + nodeIDs[d.dcg_nodeKey] = i; + }); + + _wnodes = regenerate_objects(_nodes, nodes, null, function(v) { + return v.dcg_nodeKey; + }, function(v1, v) { + v1.dcg_nodeKey = v.dcg_nodeKey; + v1.width = v.width; + v1.height = v.height; + v1.id = v.dcg_nodeKey; + if(v.dcg_nodeFixed) { + v1.fx = v.dcg_nodeFixed.x; + v1.fy = v.dcg_nodeFixed.y; + } else v1.fx = v1.fy = null; + }); + + _wedges = regenerate_objects(_edges, edges, null, function(e) { + return e.dcg_edgeKey; + }, function(e1, e) { + e1.dcg_edgeKey = e.dcg_edgeKey; + e1.source = nodeIDs[_nodes[e.dcg_edgeSource].dcg_nodeKey]; + e1.target = nodeIDs[_nodes[e.dcg_edgeTarget].dcg_nodeKey]; + e1.dcg_edgeLength = e.dcg_edgeLength; + }); + + _simulation.force('straighten', null); + _simulation.nodes(_wnodes); + _simulation.force('link').links(_wedges); + } + + function start() { + _dispatch.start(); + installForces(_paths); + runSimulation(_options.iterations); + } + + function stop() { + // not running asynchronously, no _simulation.stop(); + } + + function savePositions() { + var data = {}; + Object.keys(_nodes).forEach(function(key) { + data[key] = {x: _nodes[key].x, y: _nodes[key].y}; + }); + return data; + } + function restorePositions(data) { + Object.keys(data).forEach(function(key) { + if(_nodes[key]) { + _nodes[key].fx = data[key].x; + _nodes[key].fy = data[key].y; + } + }); + } + function installForces(paths) { + if(paths) + paths = paths.filter(function(path) { + return path.nodes.every(function(nk) { return _nodes[nk]; }); + }); + if(paths === null || !paths.length) { + _simulation.force('charge').strength(_options.initialCharge); + } else { + var nodesOnPath; + if(_options.fixOffPathNodes) { + nodesOnPath = d3.set(); + paths.forEach(function(path) { + path.nodes.forEach(function(nid) { + nodesOnPath.add(nid); + }); + }); + } + + // fix nodes not on paths + Object.keys(_nodes).forEach(function(key) { + if(_options.fixOffPathNodes && !nodesOnPath.has(key)) { + _nodes[key].fx = _nodes[key].x; + _nodes[key].fy = _nodes[key].y; + } else { + _nodes[key].fx = null; + _nodes[key].fy = null; + } + }); + + _simulation.force('charge').strength(_options.chargeForce); + _simulation.force('straighten', d3v4.forceStraightenPaths() + .id(function(n) { return n.dcg_nodeKey; }) + .angleForce(_options.angleForce) + .pathNodes(function(p) { return p.nodes; }) + .pathStrength(function(p) { return p.strength; }) + .paths(paths)); + } + }; + + function runSimulation(iterations) { + _simulation.alpha(1); + for (var i = 0; i < iterations; ++i) { + _simulation.tick(); + dispatchState('tick'); + } + dispatchState('end'); + } + + var graphviz = dc_graph.graphviz_attrs(), graphviz_keys = Object.keys(graphviz); + + var engine = Object.assign(graphviz, { + layoutAlgorithm: function() { + return 'd3v4-force'; + }, + layoutId: function() { + return _layoutId; + }, + supportsWebworker: function() { + return true; + }, + parent: property(null), + on: function(event, f) { + if(arguments.length === 1) + return _dispatch.on(event); + _dispatch.on(event, f); + return this; + }, + init: function(options) { + this.optionNames().forEach(function(option) { + options[option] = options[option] || this[option](); + }.bind(this)); + init(options); + return this; + }, + data: function(graph, nodes, edges, constraints) { + data(nodes, edges, constraints); + }, + start: function() { + start(); + }, + stop: function() { + stop(); + }, + paths: function(paths) { + _paths = paths; + }, + savePositions: savePositions, + restorePositions: restorePositions, + optionNames: function() { + return ['iterations', 'angleForce', 'chargeForce', 'gravityStrength', 'collisionRadius', + 'initialCharge', 'fixOffPathNodes'] + .concat(graphviz_keys); + }, + iterations: property(300), + angleForce: property(0.01), + chargeForce: property(-600), + gravityStrength: property(0.3), + collisionRadius: property(8), + initialCharge: property(-100), + fixOffPathNodes: property(false), + populateLayoutNode: function() {}, + populateLayoutEdge: function() {} + }); + engine.pathStraightenForce = engine.angleForce; + return engine; +}; + +dc_graph.d3v4_force_layout.scripts = ['d3.js', 'd3v4-force.js']; + +/** + * `dc_graph.flexbox_layout` lays out nodes in accordance with the + * {@link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox flexbox layout algorithm}. + * Nodes fit into a containment hierarchy based on their keys; edges do not affect the layout but + * are drawn from node to node. + * + * Since the flexbox algorithm is not ordinarily available in SVG, this class uses the + * {@link https://npmjs.com/package/css-layout css-layout} + * package. (It does not currently support css-layout's successor + * {@link https://github.com/facebook/yoga yoga} but that should be straightforward to add if + * there is interest.) + * + * Unlike conventional graph layout, where positions are determined based on a few attributes and + * the topological structure of the eedges, flexbox layout is determined based on the node hierarchy + * and a large number of attributes on the nodes. See css-layout's + * {@link https://npmjs.com/package/css-layout#supported-attributes Supported Attributes} + * for a list of those attributes, and see below to understand how the hierarchy is inferred from + * node keys. + * + * `flexbox_layout` does not require all internal nodes to be specified. The node keys are parsed as + * "addresses" or paths (arrays of strings) and the tree is built from those paths. Wherever a + * node's path terminates is where that node's data will be applied. + * + * Since flexbox supports a vast number of attributes, we don't attempt to create accessors for + * every one. Instead, any attributes in the node data are copied which match the names of flexbox + * attributes. + * + * @class flexbox_layout + * @memberof dc_graph + * @param {String} [id=uuid()] - Unique identifier + * @return {dc_graph.flexbox_layout} + **/ +dc_graph.flexbox_layout = function(id, options) { + var _layoutId = id || uuid(); + options = options || {algo: 'yoga-layout'}; + var _dispatch = d3.dispatch('tick', 'start', 'end'); + + var _graph, _tree, _nodes = {}, _wnodes; + + function init(options) { + } + // like d3.nest but address can be of arbitrary (and different) length + // probably less efficient too + function add_node(adhead, adtail, n, tree) { + tree.address = adhead.slice(); + tree.children = tree.children || {}; + if(!adtail.length) { + tree.node = n; + return; + } + var t = tree.children[adtail[0]] = tree.children[adtail[0]] || {}; + adhead.push(adtail.shift()); + add_node(adhead, adtail, n, t); + } + function all_keys(tree) { + var key = _engine.addressToKey()(tree.address); + return Array.prototype.concat.apply([key], Object.keys(tree.children || {}).map(function(k) { + return all_keys(tree.children[k]); + })); + } + function data(graph, nodes) { + _graph = graph; + _tree = {address: [], children: {}}; + nodes.forEach(function(n) { + var ad = _engine.keyToAddress()(n.dcg_nodeKey); + add_node([], ad, n, _tree); + }); + var need = all_keys(_tree); + _wnodes = nodes; + } + function ensure_inner_nodes(tree) { + if(!tree.node) + tree.node = {dcg_nodeKey: tree.address.length ? tree.address[tree.address.length-1] : null}; + Object.values(tree.children).forEach(ensure_inner_nodes); + } + var yoga_constants = { + alignItems: { + stretch: yogaLayout.ALIGN_STRETCH, + 'flex-start': yogaLayout.ALIGN_FLEX_START, + center: yogaLayout.ALIGN_CENTER, + 'flex-end': yogaLayout.ALIGN_FLEX_END, + baseline: yogaLayout.ALIGN_BASELINE + }, + alignSelf: { + stretch: yogaLayout.ALIGN_STRETCH, + 'flex-start': yogaLayout.ALIGN_FLEX_START, + center: yogaLayout.ALIGN_CENTER, + 'flex-end': yogaLayout.ALIGN_FLEX_END, + baseline: yogaLayout.ALIGN_BASELINE + }, + alignContent: { + 'flex-start': yogaLayout.ALIGN_FLEX_START, + 'flex-end': yogaLayout.ALIGN_FLEX_END, + stretch: yogaLayout.ALIGN_STRETCH, + center: yogaLayout.ALIGN_CENTER, + 'space-between': yogaLayout.ALIGN_SPACE_BETWEEN, + 'space-around': yogaLayout.ALIGN_SPACE_AROUND + }, + flexDirection: { + column: yogaLayout.FLEX_DIRECTION_COLUMN, + 'column-reverse': yogaLayout.FLEX_DIRECTION_COLUMN_REVERSE, + row: yogaLayout.FLEX_DIRECTION_ROW, + 'row-reverse': yogaLayout.FLEX_DIRECTION_ROW_REVERSE + }, + justifyContent: { + 'flex-start': yogaLayout.JUSTIFY_FLEX_START, + center: yogaLayout.JUSTIFY_CENTER, + 'flex-end': yogaLayout.JUSTIFY_FLEX_END, + 'space-between': yogaLayout.JUSTIFY_SPACE_BETWEEN, + 'space-around': yogaLayout.JUSTIFY_SPACE_AROUND, + 'space-evenly': yogaLayout.JUSTIFY_SPACE_EVENLY + } + }; + function set_yoga_attr(flexnode, attr, value) { + var fname = 'set' + attr.charAt(0).toUpperCase() + attr.slice(1); + if(typeof flexnode[fname] !== 'function') + throw new Error('Could not set yoga attr "' + attr + '" (' + fname + ')'); + if(yoga_constants[attr]) + value = yoga_constants[attr][value]; + flexnode['set' + attr.charAt(0).toUpperCase() + attr.slice(1)](value); + } + function get_yoga_attr(flexnode, attr) { + var fname = 'getComputed' + attr.charAt(0).toUpperCase() + attr.slice(1); + if(typeof flexnode[fname] !== 'function') + throw new Error('Could not get yoga attr "' + attr + '" (' + fname + ')'); + return flexnode[fname](); + } + var internal_attrs = ['sort', 'order', 'dcg_nodeKey', 'dcg_nodeParentCluster', 'shape', 'abstract', 'rx', 'ry', 'x', 'y', 'z'], + skip_on_parents = ['width', 'height']; + function create_flextree(attrs, tree) { + var flexnode; + switch(options.algo) { + case 'css-layout': + flexnode = {name: _engine.addressToKey()(tree.address), style: {}}; + break; + case 'yoga-layout': + flexnode = yogaLayout.Node.create(); + break; + } + var attrs2 = Object.assign({}, attrs); + var isParent = Object.keys(tree.children).length; + if(tree.node) + Object.assign(attrs, tree.node); + for(var attr in attrs) { + if(internal_attrs.includes(attr)) + continue; + if(isParent && skip_on_parents.includes(attr)) + continue; + var value = attrs[attr]; + if(typeof value === 'function') + value = value(tree.node); + switch(options.algo) { + case 'css-layout': + flexnode.style[attr] = value; + break; + case 'yoga-layout': + set_yoga_attr(flexnode, attr, value); + break; + } + } + if(isParent) { + var children = Object.values(tree.children) + .sort(attrs.sort) + .map(function(c) { return c.address[c.address.length-1]; }) + .map(function(key) { + return create_flextree(Object.assign({}, attrs2), tree.children[key]); + }); + switch(options.algo) { + case 'css-layout': + flexnode.children = children; + break; + case 'yoga-layout': + children.forEach(function(child, i) { + flexnode.insertChild(child, i); + }); + break; + } + } + tree.flexnode = flexnode; + return flexnode; + } + function apply_layout(offset, tree) { + var left, top, width, height; + switch(options.algo) { + case 'css-layout': + if(_engine.logStuff()) + console.log(tree.node.dcg_nodeKey + ': '+ JSON.stringify(tree.flexnode.layout)); + left = tree.flexnode.layout.left; width = tree.flexnode.layout.width; + top = tree.flexnode.layout.top; height = tree.flexnode.layout.height; + break; + case 'yoga-layout': + left = get_yoga_attr(tree.flexnode, 'left'); width = get_yoga_attr(tree.flexnode, 'width'); + top = get_yoga_attr(tree.flexnode, 'top'); height = get_yoga_attr(tree.flexnode, 'height'); + break; + } + tree.node.x = offset.x + left + width/2; + tree.node.y = offset.y + top + height/2; + Object.keys(tree.children) + .map(function(key) { return tree.children[key]; }) + .forEach(function(child) { + apply_layout({x: offset.x + left, y: offset.y + top}, child); + }); + } + function dispatchState(wnodes, wedges, event) { + _dispatch[event]( + wnodes, + wedges.map(function(e) { + return {dcg_edgeKey: e.dcg_edgeKey}; + }) + ); + } + function start() { + var defaults = { + sort: function(a, b) { + return d3.ascending(a.node.dcg_nodeKey, b.node.dcg_nodeKey); + } + }; + ensure_inner_nodes(_tree); + var flexTree = create_flextree(defaults, _tree); + switch(options.algo) { + case 'css-layout': + flexTree.style.width = _graph.width; + flexTree.style.height = _graph.height; + break; + case 'yoga-layout': + set_yoga_attr(flexTree, 'width', _graph.width); + set_yoga_attr(flexTree, 'height', _graph.height); + break; + } + if(_engine.logStuff()) + console.log(JSON.stringify(flexTree, null, 2)); + switch(options.algo) { + case 'css-layout': + computeLayout(flexTree); + break; + case 'yoga-layout': + flexTree.calculateLayout(); + break; + } + apply_layout({x: 0, y: 0}, _tree); + dispatchState(_wnodes, [], 'end'); + } + function stop() { + } + + // currently dc.graph populates the "cola" (really "layout") member with the attributes + // needed for layout and does not pass in the original data. flexbox has a huge number of attributes + // and it might be more appropriate for it to look at the original data. + // (Especially because it also computes some attributes based on data.) + var supportedAttributes = [ + 'width', 'height', // positive number + 'minWidth', 'minHeight', // positive number + 'maxWidth', 'maxHeight', // positive number + 'left', 'right', 'top', 'bottom', // number + 'margin', 'marginLeft', 'marginRight', 'marginTop', 'marginBottom', // number + 'padding', 'paddingLeft', 'paddingRight', 'paddingTop', 'paddingBottom', // positive number + 'borderWidth', 'borderLeftWidth', 'borderRightWidth', 'borderTopWidth', 'borderBottomWidth', // positive number + 'flexDirection', // 'column', 'row' + 'justifyContent', // 'flex-start', 'center', 'flex-end', 'space-between', 'space-around' + 'alignItems', 'alignSelf', // 'flex-start', 'center', 'flex-end', 'stretch' + 'flex', // positive number + 'flexWrap', // 'wrap', 'nowrap' + 'position' // 'relative', 'absolute' + ]; + + var _engine = { + layoutAlgorithm: function() { + return 'cola'; + }, + layoutId: function() { + return _layoutId; + }, + supportsWebworker: function() { + return true; + }, + parent: property(null), + on: function(event, f) { + if(arguments.length === 1) + return _dispatch.on(event); + _dispatch.on(event, f); + return this; + }, + init: function(options) { + this.optionNames().forEach(function(option) { + options[option] = options[option] || this[option](); + }.bind(this)); + init(options); + return this; + }, + data: function(graph, nodes) { + data(graph, nodes); + }, + start: function() { + start(); + }, + stop: function() { + stop(); + }, + optionNames: function() { + return []; + }, + populateLayoutNode: function(n1, n) { + ['sort', 'order'].concat(supportedAttributes).forEach(function(attr) { + if(n.orig.value[attr]) + n1[attr] = n.orig.value[attr]; + }); + }, + populateLayoutEdge: function() {}, + /** + * This function constructs a node key string from an "address". An address is an array of + * strings identifying the path from the root to the node. + * + * By default, it joins the address with commas. + * @method addressToKey + * @memberof dc_graph.flexbox_layout + * @instance + * @param {Function} [addressToKey = function(ad) { return ad.join(','); }] + * @return {Function} + * @return {dc_graph.flexbox_layout} + **/ + addressToKey: property(function(ad) { return ad.join(','); }), + /** + * This function constructs an "address" from a node key string. An address is an array of + * strings identifying the path from the root to the node. + * + * By default, it splits the key by its commas. + * @method keyToAddress + * @memberof dc_graph.flexbox_layout + * @instance + * @param {Function} [keyToAddress = function(nid) { return nid.split(','); }] + * @return {Function} + * @return {dc_graph.flexbox_layout} + **/ + keyToAddress: property(function(nid) { return nid.split(','); }), + yogaConstants: function() { + // in case any are missing, they can be added + // please file PRs for any missing constants! + return yoga_constants; + }, + logStuff: property(false) + }; + return _engine; +}; + +dc_graph.flexbox_layout.scripts = ['css-layout.js']; + +dc_graph.manual_layout = function(id) { + var _layoutId = id || uuid(); + var _dispatch = d3.dispatch('tick', 'start', 'end'); + + var _wnodes; + + function init(options) { + } + function data(nodes) { + _wnodes = nodes; + } + function dispatchState(wnodes, wedges, event) { + _dispatch[event]( + wnodes, + wedges.map(function(e) { + return {dcg_edgeKey: e.dcg_edgeKey}; + }) + ); + } + function start() { + dispatchState(_wnodes, [], 'end'); + } + function stop() { + } + + var _engine = { + layoutAlgorithm: function() { + return 'manual'; + }, + layoutId: function() { + return _layoutId; + }, + supportsWebworker: function() { + return false; + }, + parent: property(null), + on: function(event, f) { + if(arguments.length === 1) + return _dispatch.on(event); + _dispatch.on(event, f); + return this; + }, + init: function(options) { + this.optionNames().forEach(function(option) { + options[option] = options[option] || this[option](); + }.bind(this)); + init(options); + return this; + }, + data: function(graph, nodes, edges) { + data(nodes); + }, + start: function() { + start(); + }, + stop: function() { + stop(); + }, + optionNames: function() { + return []; + }, + populateLayoutNode: function(n1, n) { + ['x', 'y'].forEach(function(attr) { + if(n.orig.value[attr] !== undefined) + n1[attr] = n.orig.value[attr]; + }); + }, + populateLayoutEdge: function() {}, + addressToKey: property(function(ad) { return ad.join(','); }), + keyToAddress: property(function(nid) { return nid.split(','); }) + }; + return _engine; +}; + +dc_graph.manual_layout.scripts = ['css-layout.js']; + +/** + * `dc_graph.layered_layout` produces 3D layered layouts, utilizing another layout + * that supports fixed nodes and position hints for the layers + * @class layered_layout + * @memberof dc_graph + * @param {String} [id=uuid()] - Unique identifier + * @return {dc_graph.layered_layout} + **/ +dc_graph.layered_layout = function(id) { + var _layoutId = id || uuid(); + var _dispatch = d3.dispatch('tick', 'start', 'end'); + var _supergraph, _subgraphs; + var _layers; + var _options = null; + + function init(options) { + _options = options; + + } + + function data(nodes, edges, constraints) { + _supergraph = dc_graph.supergraph({nodes: nodes, edges: edges}, { + nodeKey: function(n) { return n.dcg_nodeKey; }, + edgeKey: function(n) { return n.dcg_edgeKey; }, + nodeValue: function(n) { return n; }, + edgeValue: function(e) { return e; }, + edgeSource: function(e) { return e.dcg_edgeSource; }, + edgeTarget: function(e) { return e.dcg_edgeTarget; } + }); + + // every node belongs natively in one rank + var nranks = _supergraph.nodes().reduce(function(p, n) { + var rank = engine.layerAccessor()(n.value()); + p[rank] = p[rank] || []; + p[rank].push(n); + return p; + }, {}); + var eranks = Object.keys(nranks).reduce(function(p, r) { + p[r] = []; + return p; + }, {}); + + // nodes are shadowed into any layers to which they are adjacent + // edges are induced from the native&shadow nodes in each layer + _supergraph.edges().forEach(function(e) { + var srank = engine.layerAccessor()(e.source().value()), + trank = engine.layerAccessor()(e.target().value()); + if(srank == trank) { + eranks[srank].push(e); + return; + } + nranks[trank].push(e.source()); + eranks[trank].push(e); + nranks[srank].push(e.target()); + eranks[srank].push(e); + }); + + // produce a subgraph for each layer + _subgraphs = Object.keys(nranks).reduce(function(p, r) { + p[r] = _supergraph.subgraph( + nranks[r].map(function(n) { return n.key(); }), + eranks[r].map(function(e) { return e.key(); })); + return p; + }, {}); + + // start from the most populous layer + var max = null; + Object.keys(nranks).forEach(function(r) { + if(max === null || + _subgraphs[r].nodes().length > _subgraphs[max].nodes().length) + max = +r; + }); + + // travel up and down from there, each time fixing the nodes from the last layer + var ranks = Object.keys(nranks).map(function(r) { return +r; }).sort(); + _layers = ranks.map(function(r) { + return { + rank: r, + z: -r * engine.layerSeparationZ() + }; + }); + var mi = ranks.indexOf(max); + var ups = ranks.slice(mi+1), downs = ranks.slice(0, mi).reverse(); + layout_layer(max, -1).then(function(layout) { + Promise.all([ + layout_layers(layout, max, ups), + layout_layers(layout, max, downs) + ]).then(function() { + _dispatch.end( + _supergraph.nodes().map(function(n) { return n.value(); }), + _supergraph.edges().map(function(e) { return e.value(); })); + }); + }); + } + + function layout_layers(layout, last, layers) { + if(layers.length === 0) + return Promise.resolve(layout); + var curr = layers.shift(); + return layout_layer(curr, last).then(function(layout) { + return layout_layers(layout, curr, layers); + }); + } + + function layout_layer(r, last) { + _subgraphs[r].nodes().forEach(function(n) { + if(engine.layerAccessor()(n.value()) !== r && + n.value().x !== undefined && + n.value().y !== undefined) + n.value().dcg_nodeFixed = { + x: n.value().x, + y: n.value().y + }; + else n.value().dcg_nodeFixed = null; + }); + var subengine = engine.engineFactory()(); + subengine.init(_options); + subengine.data( + {}, + _subgraphs[r].nodes().map(function(n) { + return n.value(); + }), + _subgraphs[r].edges().map(function(e) { + return e.value(); + })); + return promise_layout(r, subengine); + } + + function promise_layout(r, subengine) { + // stopgap - engine.start() should return a promise + return new Promise(function(resolve, reject) { + subengine.on('end', function(nodes, edges) { + resolve({nodes: nodes, edges: edges}); + }); + subengine.start(); + }).then(function(layout) { + // copy positions back into the subgraph (and hence supergraph) + layout.nodes.forEach(function(ln) { + var n = _subgraphs[r].node(ln.dcg_nodeKey); + // do not copy positions for shadow nodes + if(engine.layerAccessor()(n.value()) !== r) + return; + n.value().x = ln.x; + n.value().y = ln.y; + n.value().z = -r * engine.layerSeparationZ(); // lowest rank at top + }); + return layout; + }); + } + + function start() { + _dispatch.start(); + } + + function stop() { + } + + var graphviz = dc_graph.graphviz_attrs(), graphviz_keys = Object.keys(graphviz); + + var engine = Object.assign(graphviz, { + layoutAlgorithm: function() { + return 'layered'; + }, + layoutId: function() { + return _layoutId; + }, + supportsWebworker: function() { + return false; + }, + parent: property(null), + on: function(event, f) { + if(arguments.length === 1) + return _dispatch.on(event); + _dispatch.on(event, f); + return this; + }, + init: function(options) { + this.optionNames().forEach(function(option) { + options[option] = options[option] || this[option](); + }.bind(this)); + init(options); + return this; + }, + data: function(graph, nodes, edges, constraints) { + data(nodes, edges, constraints); + }, + start: function() { + start(); + }, + stop: function() { + stop(); + }, + optionNames: function() { + return [] + .concat(graphviz_keys); + }, + engineFactory: property(null), + layerAccessor: property(null), + layerSeparationZ: property(50), + layers: function() { + return _layers; + }, + populateLayoutNode: function() {}, + populateLayoutEdge: function() {}, + extractNodeAttrs: property({}), // {attr: function(node)} + extractEdgeAttrs: property({}) + }); + return engine; +}; + + + +function port_name(nodeId, edgeId, portName) { + if(!(nodeId || edgeId)) + return null; // must have one key or the other + if(nodeId) nodeId = nodeId.replace(/\//g, '%2F'); + if(edgeId) edgeId = edgeId.replace(/\//g, '%2F'); + return (nodeId ? 'node/' + nodeId : 'edge/' + edgeId) + '/' + portName; +}; +function split_port_name(portname) { + var parts = portname.split('/'); + console.assert(parts.length === 3); + parts = parts.map(function(p) { + return p.replace(/%2F/g, '/'); + }); + if(parts[0] === 'node') + return { + nodeKey: parts[1], + name: parts[2] + }; + else return { + edgeKey: parts[1], + name: parts[2] + }; +} +function project_port(diagram, n, p) { + if(!p.vec) { + console.assert(!p.edges.length); + throw new Error("port has not been placed, maybe install place_ports? " + p.name); + } + p.pos = diagram.shape(n.dcg_shape.shape).intersect_vec(n, p.vec[0]*1000, p.vec[1]*1000); +} + +dc_graph.place_ports = function() { + function received_layout(diagram, nodes, wnodes, edges, wedges, ports, wports) { + var node_ports = diagram.nodePorts(); + + function is_ccw(u, v) { + return u[0]*v[1] - u[1]*v[0] > 0; + } + function in_bounds(v, bounds) { + // assume bounds are ccw + return is_ccw(bounds[0], v) && is_ccw(v, bounds[1]); + } + function clip(v, bounds) { + if(is_ccw(v, bounds[0])) + return bounds[0]; + else if(is_ccw(bounds[1], v)) + return bounds[1]; + else return v; + } + function a_to_v(a) { + return [Math.cos(a), Math.sin(a)]; + } + function v_to_a(v) { + return Math.atan2(v[1], v[0]); + } + function distance(p, p2) { + return Math.hypot(p2.pos.x - p.pos.x, p2.pos.y - p.pos.y); + } + function misses(p, p2) { + var dist = distance(p, p2); + var misses = dist > _mode.minDistance(); + return misses; + } + function rand_within(a, b) { + return a + Math.random()*(b-a); + } + // calculate port positions + for(var nid in node_ports) { + var n = nodes[nid], + nports = node_ports[nid]; + + // make sure that we have vector and angle bounds for any ports with specification + nports.forEach(function(p) { + var bounds = p.orig && diagram.portBounds.eval(p) || [0, 2*Math.PI]; + if(Array.isArray(bounds[0])) { + p.vbounds = bounds; + p.abounds = bounds.map(v_to_a); + } + else { + p.vbounds = bounds.map(a_to_v); + p.abounds = bounds; + } + if(p.abounds[0] > p.abounds[1]) + p.abounds[1] += 2*Math.PI; + console.assert(p.orig || p.vec, 'unplaced unspecified port'); + }); + + // determine which ports satisfy bounds or are unplaced + var inside = [], outside = [], unplaced = []; + nports.forEach(function(p) { + if(!p.vec) + unplaced.push(p); + else if(p.vbounds && !in_bounds(p.vec, p.vbounds)) + outside.push(p); + else + inside.push(p); + }); + + // shunt outside ports into their bounds + outside.forEach(function(p) { + p.vec = clip(p.vec, p.vbounds); + inside.push(p); + }); + + // for all unplaced ports that share a bounds, evenly distribute them within those bounds. + // assume that bounds are disjoint. + var boundses = {}, boundports = {}; + unplaced.forEach(function(p) { + var boundskey = p.abounds.map(function(x) { return x.toFixed(3); }).join(','); + boundses[boundskey] = p.abounds; + boundports[boundskey] = boundports[boundskey] || []; + boundports[boundskey].push(p); + }); + for(var b in boundports) { + var bounds = boundses[b], bports = boundports[b]; + if(bports.length === 1) + bports[0].vec = a_to_v((bounds[0] + bounds[1])/2); + else { + var slice = (bounds[1] - bounds[0]) / (boundports[b].length - 1); + boundports[b].forEach(function(p, i) { + p.vec = a_to_v(bounds[0] + i*slice); + }); + } + } + inside = inside.concat(unplaced); + unplaced = []; + + // determine positions of all satisfied + inside.forEach(function(p) { + project_port(diagram, n, p); + }); + + // detect any existing collisions, unplace the one without edges or second one + for(var i = 0; i < inside.length; ++i) { + var x = inside[i]; + if(unplaced.includes(x)) + continue; + for(var j = i+1; j < inside.length; ++j) { + var y = inside[j]; + if(unplaced.includes(y)) + continue; + if(!misses(x, y)) { + if(!x.edges.length) { + unplaced.push(x); + continue; + } + else + unplaced.push(y); + } + } + } + inside = inside.filter(function(p) { return !unplaced.includes(p); }); + + // place any remaining by trying random spots within the range until it misses all or we give up + var patience = _mode.patience(), maxdist = 0, maxvec; + while(unplaced.length) { + var p = unplaced[0]; + p.vec = a_to_v(rand_within(p.abounds[0], p.abounds[1])); + project_port(diagram, n, p); + var mindist = d3.min(inside, function(p2) { return distance(p, p2); }); + if(mindist > maxdist) { + maxdist = mindist; + maxvec = p.vec; + } + if(!patience-- || mindist > _mode.minDistance()) { + if(patience<0) { + console.warn('ran out of patience placing a port'); + p.vec = maxvec; + project_port(diagram, n, p); + } + inside.push(p); + unplaced.shift(); + patience = _mode.patience(); + maxdist = 0; + } + } + } + }; + var _mode = { + parent: property(null).react(function(p) { + if(p) { + p.on('receivedLayout.place-ports', received_layout); + } else if(_mode.parent()) + _mode.parent().on('receivedLayout.place-ports', null); + }), + // minimum distance between ports + minDistance: property(20), + // number of random places to try when resolving collision + patience: property(20) + }; + + return _mode; +}; + +dc_graph.grid = function() { + var _gridLayer = null; + var _translate, _scale, _xDomain, _yDomain; + + function draw(diagram, node, edge, ehover) { + //infer_and_draw(diagram); + } + + function remove(diagram, node, edge, ehover) { + if(_gridLayer) + _gridLayer.remove(); + } + + function draw(diagram) { + _gridLayer = diagram.g().selectAll('g.grid-layer').data([0]); + _gridLayer.enter().append('g').attr('class', 'grid-layer'); + var ofs = _mode.wholeOnLines() ? 0 : 0.5; + var vline_data = _scale >= _mode.threshold() ? d3.range(Math.floor(_xDomain[0]), Math.ceil(_xDomain[1]) + 1) : []; + var vlines = _gridLayer.selectAll('line.grid-line.vertical') + .data(vline_data, function(d) { return d - ofs; }); + vlines.exit().remove(); + vlines.enter().append('line') + .attr({ + class: 'grid-line vertical', + x1: function(d) { return d - ofs; }, + x2: function(d) { return d - ofs; } + }); + vlines.attr({ + 'stroke-width': 1/_scale, + y1: _yDomain[0], + y2: _yDomain[1] + }); + var hline_data = _scale >= _mode.threshold() ? d3.range(Math.floor(_yDomain[0]), Math.ceil(_yDomain[1]) + 1) : []; + var hlines = _gridLayer.selectAll('line.grid-line.horizontal') + .data(hline_data, function(d) { return d - ofs; }); + hlines.exit().remove(); + hlines.enter().append('line') + .attr({ + class: 'grid-line horizontal', + y1: function(d) { return d - ofs; }, + y2: function(d) { return d - ofs; } + }); + hlines.attr({ + 'stroke-width': 1/_scale, + x1: _xDomain[0], + x2: _xDomain[1] + }); + } + + function on_zoom(translate, scale, xDomain, yDomain) { + _translate = translate; + _scale = scale; + _xDomain = xDomain, + _yDomain = yDomain; + draw(_mode.parent()); + } + + function infer_and_draw(diagram) { + _translate = diagram.translate(); + _scale = diagram.scale(); + _xDomain = diagram.x().domain(); + _yDomain = diagram.y().domain(); + draw(diagram); + } + + var _mode = dc_graph.mode('highlight-paths', { + draw: draw, + remove: remove, + parent: function(p) { + if(p) { + p.on('zoomed.grid', on_zoom); + infer_and_draw(p); + } + } + }); + + _mode.threshold = property(4); + _mode.wholeOnLines = property(true); + + return _mode; +}; + + + +dc_graph.annotate_layers = function() { + // svg-specific + var _drawLayer; + // wegl-specific + var _planes = []; + var _planeGeometry; + var _mode = dc_graph.mode('annotate-layers', { + laterDraw: true, + renderers: ['svg', 'webgl'], + draw: draw, + remove: remove + }); + function draw(diagram) { + var rendererType = _mode.parent().renderer().rendererType(); + var engine = _mode.parent().layoutEngine(); + if(rendererType === 'svg') { + if(engine.layoutAlgorithm() === 'cola' && + engine.setcolaSpec() && engine.setcolaNodes()) { + _drawLayer = _mode.parent().select('g.draw').selectAll('g.divider-layer').data([0]); + _drawLayer.enter().append('g').attr('class', 'divider-layer'); + var boundary_nodes = engine.setcolaNodes().filter(function(n) { + return /^sort_order_boundary/.test(n.name); + }); + var lines = _drawLayer.selectAll('line.divider').data(boundary_nodes); + lines.exit().remove(); + lines.enter().append('line') + .attr('class', 'divider'); + lines.attr({ + stroke: _mode.stroke(), + 'stroke-width': _mode.strokeWidth(), + 'stroke-dasharray': _mode.strokeDashArray(), + x1: -5000, + y1: function(n) { + return n.y; + }, + x2: 5000, + y2: function(n) { + return n.y; + } + }); + } + } else if(rendererType === 'webgl') { + var MULT = _mode.parent().renderer().multiplier(); + var scene = arguments[1], drawState = arguments[2]; + if(engine.layoutAlgorithm() === 'layered' && engine.layers()) { + var width = drawState.extents[0][1] - drawState.extents[0][0] + _mode.planePadding()*MULT*2, + height = drawState.extents[1][1] - drawState.extents[1][0] + _mode.planePadding()*MULT*2; + var delGeom; + var shape = new THREE.Shape(); + shape.moveTo(0, 0); + shape.lineTo(0, height); + shape.lineTo(width, height); + shape.lineTo(width, 0); + shape.lineTo(0, 0); + if(_planeGeometry) + delGeom = _planeGeometry; + _planeGeometry = new THREE.ShapeBufferGeometry(shape); + + var layers = engine.layers(); + if(layers.length < _planes.length) { + for(var i = layers.length; i < _planes.length; ++i) + scene.remove(_planes[i].mesh); + _planes = _planes.slice(0, layers.length); + } + layers.forEach(function(layer, i) { + if(!_planes[i]) + _planes[i] = Object.assign({}, layer); + if(_planes[i].mesh) + scene.remove(_planes[i].mesh); + var mesh = _planes[i].mesh = new THREE.Mesh(_planeGeometry, new THREE.MeshStandardMaterial({ + opacity: _mode.planeOpacity(), + transparent: true, + color: _mode.parent().renderer().color_to_int(_mode.planeColor()), + side: THREE.DoubleSide + })); + mesh.position.set(drawState.extents[0][0] - _mode.planePadding()*MULT, + drawState.extents[1][0] - _mode.planePadding()*MULT, + layer.z * MULT); + scene.add(mesh); + }); + if(delGeom) + delGeom.dispose(); + } + } else throw new Error("annotate_layers doesn't know how to work with renderer " + rendererType); + } + function remove() { + if(_drawLayer) + _drawLayer.remove(); + } + + // line properties for svg + _mode.stroke = property('black'); + _mode.strokeWidth = property(2); + _mode.strokeDashArray = property([5,5]); + + // plane properties + _mode.planePadding = property(5); + _mode.planeOpacity = property(0.2); + _mode.planeColor = property('#ffffdd'); + return _mode; +}; + +dc_graph.troubleshoot = function() { + var _debugLayer = null; + var _translate, _scale = 1, _xDomain, _yDomain; + + function draw(diagram, node, edge, ehover) { + if(!_debugLayer) + _debugLayer = diagram.g().append('g').attr({ + class: 'troubleshoot', + 'pointer-events': 'none' + }); + var centers = node.data().map(function(n) { + return { + x: n.cola.x, + y: n.cola.y + }; + }); + var crosshairs = _debugLayer.selectAll('path.nodecenter').data(centers); + crosshairs.exit().remove(); + crosshairs.enter().append('path').attr('class', 'nodecenter'); + crosshairs.attr({ + d: function(c) { + return 'M' + (c.x - _mode.xhairWidth()/2) + ',' + c.y + ' h' + _mode.xhairWidth() + + ' M' + c.x + ',' + (c.y - _mode.xhairHeight()/2) + ' v' + _mode.xhairHeight(); + }, + opacity: _mode.xhairOpacity() !== null ? _mode.xhairOpacity() : _mode.opacity(), + stroke: _mode.xhairColor(), + 'stroke-width': 1/_scale + }); + function cola_point(n) { + return {x: n.cola.x, y: n.cola.y}; + } + var colabounds = node.data().map(function(n) { + return boundary(cola_point(n), n.cola.width, n.cola.height); + }); + var colaboundary = _debugLayer.selectAll('path.colaboundary').data(colabounds); + draw_corners(colaboundary, 'colaboundary', _mode.boundsColor()); + + var textbounds = node.data().map(function(n) { + if(!n.bbox || (!n.bbox.width && !n.bbox.height)) + return null; + return boundary(cola_point(n), n.bbox.width, n.bbox.height); + }).filter(function(n) { return !!n; }); + var textboundary = _debugLayer.selectAll('path.textboundary').data(textbounds); + draw_corners(textboundary, 'textboundary', _mode.boundsColor()); + + var radiibounds = node.data().map(function(n) { + if(typeof n.dcg_rx !== 'number') + return null; + return boundary(cola_point(n), n.dcg_rx*2, n.dcg_ry*2); + }).filter(function(n) { return !!n; }); + var radiiboundary = _debugLayer.selectAll('path.radiiboundary').data(radiibounds); + draw_corners(radiiboundary, 'radiiboundary', _mode.boundsColor()); + + diagram.addOrRemoveDef('debug-orient-marker-head', + true, + 'svg:marker', + orient_marker.bind(null, _mode.arrowHeadColor())); + diagram.addOrRemoveDef('debug-orient-marker-tail', + true, + 'svg:marker', + orient_marker.bind(null, _mode.arrowTailColor())); + var heads = _mode.arrowLength() ? edge.data().map(function(e) { + return {pos: e.pos.new.path.points[e.pos.new.path.points.length-1], orient: e.pos.new.orienthead}; + }) : []; + var headOrients = _debugLayer.selectAll('line.heads').data(heads); + draw_arrow_orient(headOrients, 'heads', _mode.arrowHeadColor(), '#debug-orient-marker-head'); + + var tails = _mode.arrowLength() ? edge.data().map(function(e) { + return {pos: e.pos.new.path.points[0], orient: e.pos.new.orienttail}; + }) : []; + var tailOrients = _debugLayer.selectAll('line.tails').data(tails); + draw_arrow_orient(tailOrients, 'tails', _mode.arrowTailColor(), '#debug-orient-marker-tail'); + + var headpts = Array.prototype.concat.apply([], edge.data().map(function(e) { + var arrowSize = diagram.edgeArrowSize.eval(e); + return edge_arrow_points( + diagram.arrows(), + diagram.edgeArrowhead.eval(e), + arrowSize, + diagram.edgeStrokeWidth.eval(e) / arrowSize, + unrad(e.pos.new.orienthead), + e.pos.new.full.points[e.pos.new.full.points.length-1], + diagram.nodeStrokeWidth.eval(e.target) + ); + })); + var hp = _debugLayer.selectAll('path.head-point').data(headpts); + draw_x(hp, 'head-point', _mode.arrowHeadColor()); + + var tailpts = Array.prototype.concat.apply([], edge.data().map(function(e) { + var arrowSize = diagram.edgeArrowSize.eval(e); + return edge_arrow_points( + diagram.arrows(), + diagram.edgeArrowtail.eval(e), + arrowSize, + diagram.edgeStrokeWidth.eval(e) / arrowSize, + unrad(e.pos.new.orienttail), + e.pos.new.full.points[0], + diagram.nodeStrokeWidth.eval(e.source) + ); + })); + var tp = _debugLayer.selectAll('path.tail-point').data(tailpts); + draw_x(tp, 'tail-point', _mode.arrowTailColor()); + + var domain = _debugLayer.selectAll('rect.domain').data([0]); + domain.enter().append('rect'); + var xd = _mode.parent().x().domain(), yd = _mode.parent().y().domain(); + domain.attr({ + class: 'domain', + fill: 'none', + opacity: _mode.domainOpacity(), + stroke: _mode.domainColor(), + 'stroke-width': _mode.domainStrokeWidth()/_scale, + x: xd[0], + y: yd[0], + width: xd[1] - xd[0], + height: yd[1] - yd[0] + }); + } + function on_zoom(translate, scale, xDomain, yDomain) { + _translate = translate; + _scale = scale; + _xDomain = xDomain; + _yDomain = yDomain; + draw(_mode.parent(), _mode.parent().selectAllNodes(), _mode.parent().selectAllEdges()); + } + + function boundary(point, wid, hei) { + return { + left: point.x - wid/2, + top: point.y - hei/2, + right: point.x + wid/2, + bottom: point.y + hei/2 + }; + }; + function bound_tick(x, y, dx, dy) { + return 'M' + x + ',' + (y + dy) + ' v' + -dy + ' h' + dx; + } + function corners(bounds) { + return [ + bound_tick(bounds.left, bounds.top, _mode.boundsWidth(), _mode.boundsHeight()), + bound_tick(bounds.right, bounds.top, -_mode.boundsWidth(), _mode.boundsHeight()), + bound_tick(bounds.right, bounds.bottom, -_mode.boundsWidth(), -_mode.boundsHeight()), + bound_tick(bounds.left, bounds.bottom, _mode.boundsWidth(), -_mode.boundsHeight()), + ].join(' '); + } + function draw_corners(binding, classname, color) { + binding.exit().remove(); + binding.enter().append('path').attr('class', classname); + binding.attr({ + d: corners, + opacity: _mode.boundsOpacity() !== null ? _mode.boundsOpacity() : _mode.opacity(), + stroke: color, + 'stroke-width': 1/_scale, + fill: 'none' + }); + } + function unrad(orient) { + return +orient.replace('rad',''); + } + function draw_arrow_orient(binding, classname, color, markerUrl) { + binding.exit().remove(); + binding.enter().append('line').attr('class', classname); + binding.attr({ + x1: function(d) { return d.pos.x; }, + y1: function(d) { return d.pos.y; }, + x2: function(d) { return d.pos.x - Math.cos(unrad(d.orient))*_mode.arrowLength(); }, + y2: function(d) { return d.pos.y - Math.sin(unrad(d.orient))*_mode.arrowLength(); }, + stroke: color, + 'stroke-width': _mode.arrowStrokeWidth()/_scale, + opacity: _mode.arrowOpacity() !== null ? _mode.arrowOpacity() : _mode.opacity(), + 'marker-end': 'url(' + markerUrl + ')' + }); + } + function orient_marker(color, markerEnter) { + markerEnter + .attr({ + viewBox: '0 -3 3 6', + refX: 3, + refY: 0, + orient: 'auto' + }); + markerEnter.append('path') + .attr('stroke', color) + .attr('fill', 'none') + .attr('d', 'M0,3 L3,0 L0,-3'); + } + function edge_arrow_points(arrows, defn, arrowSize, stemWidth, orient, endp, strokeWidth) { + var parts = arrow_parts(arrows, defn), + offsets = arrow_offsets(parts, stemWidth), + xunit = [Math.cos(orient), Math.sin(orient)]; + endp = [endp.x, endp.y]; + if(!parts.length) + return [[endp[0] - xunit[0]*strokeWidth/2, + endp[1] - xunit[1]*strokeWidth/2]]; + var globofs = add_points( + [-strokeWidth/arrowSize/2,0], + mult_point(front_ref(parts[0].frontRef), -1)); + var pts = offsets.map(function(ofs, i) { + return mult_point([ + globofs, + front_ref(parts[i].frontRef), + ofs.offset + ].reduce(add_points), arrowSize); + }); + pts.push(mult_point([ + globofs, + back_ref(parts[parts.length-1].backRef), + offsets[parts.length-1].offset + ].reduce(add_points), arrowSize)); + return pts.map(function(p) { + return add_points( + endp, + [p[0]*xunit[0] - p[1]*xunit[1], p[0]*xunit[1] + p[1]*xunit[0]] + ); + }); + } + + + function draw_x(binding, classname, color) { + var xw = _mode.xWidth()/2, xh = _mode.xHeight()/2; + binding.exit().remove(); + binding.enter().append('path').attr('class', classname); + binding.attr({ + d: function(pos) { + return [[[-xw,-xh],[xw,xh]], [[xw,-xh], [-xw,xh]]].map(function(seg) { + return 'M' + seg.map(function(p) { + return (pos[0] + p[0]) + ',' + (pos[1] + p[1]); + }).join(' L'); + }).join(' '); + }, + 'stroke-width': 2/_scale, + stroke: color, + opacity: _mode.xOpacity() + }); + } + function remove(diagram, node, edge, ehover) { + if(_debugLayer) + _debugLayer.remove(); + } + + var _mode = dc_graph.mode('highlight-paths', { + laterDraw: true, + draw: draw, + remove: remove, + parent: function(p) { + if(p) { + _translate = p.translate(); + _scale = p.scale(); + p.on('zoomed.troubleshoot', on_zoom); + } + else if(_mode.parent()) + _mode.parent().on('zoomed.troubleshoot', null); + } + }); + _mode.opacity = property(0.75); + + _mode.xhairOpacity = property(null); + _mode.xhairWidth = property(10); + _mode.xhairHeight = property(10); + _mode.xhairColor = property('blue'); + + _mode.boundsOpacity = property(null); + _mode.boundsWidth = property(10); + _mode.boundsHeight = property(10); + _mode.boundsColor = property('green'); + + _mode.arrowOpacity = property(null); + _mode.arrowStrokeWidth = property(3); + _mode.arrowColor = _mode.arrowHeadColor = property('darkorange'); + _mode.arrowTailColor = property('red'); + _mode.arrowLength = property(100); + + _mode.xWidth = property(1); + _mode.xHeight = property(1); + _mode.xOpacity = property(0.8); + + _mode.domainOpacity = property(0.6); + _mode.domainColor = property('darkorange'); + _mode.domainStrokeWidth = property(4); + + return _mode; +}; + + + dc_graph.validate = function(title) { + function falsy(objects, accessor, what, who) { + var f = objects.filter(function(o) { + return !accessor(o); + }); + return f.length ? + [what + ' is empty for ' + f.length + ' of ' + objects.length + ' ' + who, f] : + null; + } + function build_index(objects, accessor) { + return objects.reduce(function(m, o) { + m[accessor(o)] = o; + return m; + }, {}); + } + function not_found(index, objects, accessor, what, where, who) { + var nf = objects.filter(function(o) { + return !index[accessor(o)]; + }).map(function(o) { + return {key: accessor(o), value: o}; + }); + return nf.length ? + [what + ' was not found in ' + where, Object.keys(index), 'for ' + nf.length + ' of ' + objects.length + ' ' + who, nf] : + null; + } + function validate() { + var diagram = _mode.parent(); + var nodes = diagram.nodeGroup().all(), + edges = diagram.edgeGroup().all(), + ports = diagram.portGroup() ? diagram.portGroup().all() : []; + var errors = []; + + function check(error) { + if(error) + errors.push(error); + } + + check(falsy(nodes, diagram.nodeKey(), 'nodeKey', 'nodes')); + check(falsy(edges, diagram.edgeSource(), 'edgeSource', 'edges')); + check(falsy(edges, diagram.edgeTarget(), 'edgeTarget', 'edges')); + + var contentTypes = d3.set(diagram.content.enum()); + var ct = dc_graph.functor_wrap(diagram.nodeContent()); + var noContentNodes = nodes.filter(function(kv) { + return !contentTypes.has(ct(kv)); + }); + if(noContentNodes.length) + errors.push(['there are ' + noContentNodes.length + ' nodes with nodeContent not matching any content', noContentNodes]); + + var nindex = build_index(nodes, diagram.nodeKey()), + eindex = build_index(edges, diagram.edgeKey()); + check(not_found(nindex, edges, diagram.edgeSource(), 'edgeSource', 'nodes', 'edges')); + check(not_found(nindex, edges, diagram.edgeTarget(), 'edgeTarget', 'nodes', 'edges')); + + check(falsy(ports, function(p) { + return diagram.portNodeKey() && diagram.portNodeKey()(p) || + diagram.portEdgeKey() && diagram.portEdgeKey()(p); + }, 'portNodeKey||portEdgeKey', 'ports')); + + var named_ports = !diagram.portNodeKey() && [] || ports.filter(function(p) { + return diagram.portNodeKey()(p); + }); + var anonymous_ports = !diagram.portEdgeKey() && [] || ports.filter(function(p) { + return diagram.portEdgeKey()(p); + }); + check(not_found(nindex, named_ports, diagram.portNodeKey(), 'portNodeKey', 'nodes', 'ports')); + check(not_found(eindex, anonymous_ports, diagram.portEdgeKey(), 'portEdgeKey', 'edges', 'ports')); + + if(diagram.portName()) { + var pindex = build_index(named_ports, function(p) { + return diagram.portNodeKey()(p) + ' - ' + diagram.portName()(p); + }); + if(diagram.edgeSourcePortName()) + check(not_found(pindex, edges, function(e) { + return diagram.edgeSource()(e) + ' - ' + d3.functor(diagram.edgeSourcePortName())(e); + }, 'edgeSourcePortName', 'ports', 'edges')); + if(diagram.edgeTargetPortName()) + check(not_found(pindex, edges, function(e) { + return diagram.edgeTarget()(e) + ' - ' + d3.functor(diagram.edgeTargetPortName())(e); + }, 'edgeTargetPortName', 'ports', 'edges')); + } + + function count_text() { + return nodes.length + ' nodes, ' + edges.length + ' edges, ' + ports.length + ' ports'; + } + if(errors.length) { + console.warn('validation of ' + title + ' failed with ' + count_text() + ':'); + errors.forEach(function(err) { + console.warn.apply(console, err); + }); + } + else + console.log('validation of ' + title + ' succeeded with ' + count_text() + '.'); + } + var _mode = { + parent: property(null).react(function(p) { + if(p) + p.on('data.validate', validate); + else + _mode.parent().on('data.validate', null); + }) + }; + + return _mode; +}; + +/** +## Legend + +The dc_graph.legend shows labeled examples of nodes & edges, within the frame of a dc_graph.diagram. +**/ +dc_graph.legend = function(legend_namespace) { + legend_namespace = legend_namespace || 'node-legend'; + var _items, _included = []; + var _dispatch = d3.dispatch('filtered'); + var _totals, _counts; + + var _svg_renderer; + + function apply_filter() { + if(_legend.dimension()) { + if(_legend.isTagDimension()) { + _legend.dimension().filterFunction(function(ks) { + return !_included.length || ks.filter(function(k) { + return _included.includes(k); + }).length; + }); + } else { + _legend.dimension().filterFunction(function(k) { + return !_included.length || _included.includes(k); + }); + } + _legend.parent().redraw(); + } + } + + var _legend = dc_graph.mode(legend_namespace, { + renderers: ['svg', 'webgl'], + draw: redraw, + remove: function() {}, + parent: function(p) { + if(p) { + p + .on('render.' + legend_namespace, render) + .on('data.' + legend_namespace, on_data); + } + else { + _legend.parent() + .on('render.' + legend_namespace, null) + .on('data.' + legend_namespace, null); + } + } + }); + + /** + #### .type([value]) + Set or get the handler for the specific type of item to be displayed. Default: dc_graph.legend.node_legend() + **/ + _legend.type = property(dc_graph.legend.node_legend()); + + /** + #### .x([value]) + Set or get x coordinate for legend widget. Default: 0. + **/ + _legend.x = property(0); + + /** + #### .y([value]) + Set or get y coordinate for legend widget. Default: 0. + **/ + _legend.y = property(0); + + /** + #### .gap([value]) + Set or get gap between legend items. Default: 5. + **/ + _legend.gap = property(5); + + /** + #### .itemWidth([value]) + Set or get width to reserve for legend item. Default: 30. + **/ + _legend.itemWidth = _legend.nodeWidth = property(40); + + /** + #### .itemHeight([value]) + Set or get height to reserve for legend item. Default: 30. + **/ + _legend.itemHeight = _legend.nodeHeight = property(40); + + _legend.dyLabel = property('0.3em'); + + _legend.omitEmpty = property(false); + + /** + #### .noLabel([value]) + Remove item labels, since legend labels are displayed outside of the items. Default: true + **/ + _legend.noLabel = property(true); + + _legend.counter = property(null); + + _legend.replaceFilter = function(filter) { + if(filter && filter.length === 1) + _included = filter[0]; + else + _included = []; + return _legend; + }; + + _legend.filters = function() { + return _included; + }; + + _legend.on = function(type, f) { + _dispatch.on(type, f); + return _legend; + }; + + /** + #### .exemplars([object]) + Specifies an object where the keys are the names of items to add to the legend, and the values are + objects which will be passed to the accessors of the attached diagram in order to determine the + drawing attributes. Alternately, if the key needs to be specified separately from the name, the + function can take an array of {name, key, value} objects. + **/ + _legend.exemplars = property({}); + + function on_data(diagram, nodes, wnodes, edges, wedges, ports, wports) { + if(_legend.counter()) + _counts = _legend.counter()(wnodes.map(get_original), wedges.map(get_original), wports.map(get_original)); + } + + _legend.redraw = deprecate_function("dc_graph.legend is an ordinary mode now; redraw will go away soon", redraw); + function redraw() { + var legend = (_svg_renderer || _legend.parent()).svg() + .selectAll('g.dc-graph-legend.' + legend_namespace) + .data([0]); + legend.enter().append('g') + .attr('class', 'dc-graph-legend ' + legend_namespace) + .attr('transform', 'translate(' + _legend.x() + ',' + _legend.y() + ')'); + + var items = !_legend.omitEmpty() || !_counts ? _items : _items.filter(function(i) { + return _included.length && !_included.includes(i.orig.key) || _counts[i.orig.key]; + }); + var item = legend.selectAll(_legend.type().itemSelector()) + .data(items, function(n) { return n.name; }); + item.exit().remove(); + var itemEnter = _legend.type().create(_legend.parent(), item.enter(), _legend.itemWidth(), _legend.itemHeight()); + itemEnter.append('text') + .attr('dy', _legend.dyLabel()) + .attr('class', 'legend-label'); + item + .attr('transform', function(n, i) { + return 'translate(' + _legend.itemWidth()/2 + ',' + (_legend.itemHeight() + _legend.gap())*(i+0.5) + ')'; + }); + item.select('text.legend-label') + .attr('transform', 'translate(' + (_legend.itemWidth()/2+_legend.gap()) + ',0)') + .attr('pointer-events', _legend.dimension() ? 'auto' : 'none') + .text(function(d) { + return d.name + (_legend.counter() && _counts ? (' (' + (_counts[d.orig.key] || 0) + (_counts[d.orig.key] !== _totals[d.orig.key] ? '/' + (_totals[d.orig.key] || 0) : '') + ')') : ''); + }); + _legend.type().draw(_svg_renderer || _legend.parent(), itemEnter, item); + if(_legend.noLabel()) + item.selectAll(_legend.type().labelSelector()).remove(); + + if(_legend.dropdown()) { + var caret = item.selectAll('text.dropdown-caret').data(function(x) { return [x]; }); + caret + .enter().append('text') + .attr('dy', '0.3em') + .attr('font-size', '75%') + .attr('fill', 'blue') + .attr('class', 'dropdown-caret') + .style('visibility', 'hidden') + .html(' ▼'); + caret + .attr('dx', function(d) { + return (_legend.itemWidth()/2+_legend.gap()) + getBBoxNoThrow(d3.select(this.parentNode).select('text.legend-label').node()).width; + }) + .on('mouseenter.' + legend_namespace, function(n) { + var rect = this.getBoundingClientRect(); + var key = _legend.parent().nodeKey.eval(n); + _legend.dropdown() + .show(key, rect.x, rect.y); + }); + item + .on('mouseenter.' + legend_namespace, function(d) { + if(_counts && _counts[d.orig.key]) { + d3.select(this).selectAll('.dropdown-caret') + .style('visibility', 'visible'); + } + }) + .on('mouseleave.' + legend_namespace, function(d) { + d3.select(this).selectAll('.dropdown-caret') + .style('visibility', 'hidden'); + }); + } + + if(_legend.dimension()) { + item.attr('cursor', 'pointer') + .on('click.' + legend_namespace, function(d) { + var key = _legend.parent().nodeKey.eval(d); + if(!_included.length) + _included = _items.map(_legend.parent().nodeKey.eval); + if(_included.includes(key)) + _included = _included.filter(function(x) { return x !== key; }); + else + _included.push(key); + apply_filter(); + _dispatch.filtered(_legend, key); + if(_svg_renderer) + window.setTimeout(redraw, 250); + }); + } else { + item.attr('cursor', 'auto') + .on('click.' + legend_namespace, null); + } + item.transition().duration(1000) + .attr('opacity', function(d) { + return (!_included.length || _included.includes(_legend.parent().nodeKey.eval(d))) ? 1 : 0.25; + }); + }; + + _legend.countBaseline = function() { + if(_legend.counter()) + _totals = _legend.counter()( + _legend.parent().nodeGroup().all(), + _legend.parent().edgeGroup().all(), + _legend.parent().portGroup() && _legend.parent().portGroup().all()); + }; + + _legend.render = deprecate_function("dc_graph.legend is an ordinary mode now; render will go away soon", render); + function render() { + if(_legend.parent().renderer().rendererType() !== 'svg') { + _svg_renderer = dc_graph.render_svg(); + _svg_renderer.parent(_legend.parent()) + .svg(_legend.parent().root().append('svg') + .style({ + position: 'absolute', + left: 0, top: 0, + width: '100%', height: '100%', + fill: 'wheat', + 'pointer-events': 'none' + })); + } + + + var exemplars = _legend.exemplars(); + _legend.countBaseline(); + if(exemplars instanceof Array) { + _items = exemplars.map(function(v) { return {name: v.name, orig: {key: v.key, value: v.value}, cola: {}}; }); + } + else { + _items = []; + for(var item in exemplars) + _items.push({name: item, orig: {key: item, value: exemplars[item]}, cola: {}}); + } + redraw(); + }; + + _legend.dropdown = property(null).react(function(v) { + if(!!v !== !!_legend.dropdown() && _legend.parent() && (_svg_renderer || _legend.parent()).svg()) + window.setTimeout(_legend.redraw, 0); + }); + + /* enables filtering */ + _legend.dimension = property(null) + .react(function(v) { + if(!v) { + _included = []; + apply_filter(); + } + }); + _legend.isTagDimension = property(false); + + return _legend; +}; + + +dc_graph.legend.node_legend = function() { + return { + itemSelector: function() { + return '.node'; + }, + labelSelector: function() { + return '.node-label'; + }, + create: function(diagram, selection) { + return selection.append('g') + .attr('class', 'node'); + }, + draw: function(renderer, itemEnter, item) { + renderer + .renderNode(itemEnter) + .redrawNode(item); + } + }; +}; + +dc_graph.legend.edge_legend = function() { + var _type = { + itemSelector: function() { + return '.edge-container'; + }, + labelSelector: function() { + return '.edge-label'; + }, + create: function(diagram, selection, w, h) { + var edgeEnter = selection.append('g') + .attr('class', 'edge-container') + .attr('opacity', 0); + edgeEnter + .append('rect') + .attr({ + x: -w/2, + y: -h/2, + width: w, + height: h, + fill: 'green', + opacity: 0 + }); + edgeEnter + .selectAll('circle') + .data([-1, 1]) + .enter() + .append('circle') + .attr({ + r: _type.fakeNodeRadius(), + fill: 'none', + stroke: 'black', + "stroke-dasharray": "4,4", + opacity: 0.15, + transform: function(d) { + return 'translate(' + [d * _type.length() / 2, 0].join(',') + ')'; + } + }); + var edgex = _type.length()/2 - _type.fakeNodeRadius(); + edgeEnter.append('svg:path') + .attr({ + class: 'edge', + id: function(d) { return d.name; }, + d: 'M' + -edgex + ',0 L' + edgex + ',0', + opacity: diagram.edgeOpacity.eval + }); + + return edgeEnter; + }, + fakeNodeRadius: property(10), + length: property(50), + draw: function(renderer, itemEnter, item) { + renderer.redrawEdge(itemEnter.select('path.edge'), renderer.selectAllEdges('.edge-arrows')); + } + }; + return _type; +}; + +dc_graph.legend.symbol_legend = function(symbolScale) { + return { + itemSelector: function() { + return '.symbol'; + }, + labelSelector: function() { + return '.symbol-label'; + }, + create: function(diagram, selection, w, h) { + var symbolEnter = selection.append('g') + .attr('class', 'symbol'); + return symbolEnter; + }, + draw: function(renderer, symbolEnter, symbol) { + symbolEnter.append('text') + .html(function(d) { + return symbolScale(d.orig.key); + }); + return symbolEnter; + } + }; +}; + +/** + * In cola.js there are three factors which influence the positions of nodes: + * * *edge length* suggestions, controlled by the + * {@link #dc_graph.diagram+lengthStrategy lengthStrategy}, + * {@link #dc_graph.diagram+baseLength baseLength}, and + * {@link #dc_graph.diagram+edgeLength edgeLength} parameters in dc.graph.js + * * *automatic constraints* based on the global edge flow direction (`cola.flowLayout`) and overlap + * avoidance parameters (`cola.avoidOverlaps`) + * * *manual constraints* such as alignment, inequality and equality constraints in a dimension/axis. + * + * Generally when the + * {@link https://github.com/tgdwyer/WebCola/wiki/Constraints cola.js documentation mentions constraints}, + * it means the manual constraints. + * + * dc.graph.js allows generation of manual constraints using + * {@link #dc_graph.diagram+constrain diagram.constrain} but it can be tedious to write these + * functions because it usually means looping over the nodes and edges multiple times to + * determine what classes or types of nodes to apply constraints to, and which edges should + * take additional constraints. + * + * This utility creates a constraint generator function from a *pattern*, a graph where: + * 1. Nodes represent *types* or classes of layout nodes, annotated with a specification + * of how to match the nodes belonging each type. + * 2. Edges represent *rules* to generate constraints. There are two kinds of rules: + * <ol type='a'> + * <li>To generate additional constraints on edges besides the built-in ones, create a rules + * between two different types. The rule will apply to any edges in the layout which match the + * source and target types, and generate simple "left/right" constraints. (Note that "left" and + * "right" in this context refer to sides of an inequality constraint `left + gap <= right`) + * <li>To generate constraints on a set of nodes, such as alignment, ordering, or circle + * constraints, create a rule from a type to itself, a self edge. + * </ol> + * (It is also conceivable to want constraints between individual nodes which don't + * have edges between them. This is not directly supported at this time; right now the workaround + * is to create the edge but not draw it, e.g. by setting its {@link #dc_graph.diagram+edgeOpacity} + * to zero. If you have a use-case for this, please + * {@link https://github.com/dc-js/dc.graph.js/issues/new file an issue}. + * + * The pattern syntax is an embedded domain specific language designed to be terse without + * restricting its power. As such, there are complicated rules for defaulting and inferring + * parameters from other parameters. Since most users will want the simplest form, this document + * will start from the highest level and then show how to use more complicated forms in order to + * gain more control. + * + * Then we'll build back up from the ground up and show how inference works. + * @class constraint_pattern + * @memberof dc_graph + * @param {dc_graph.diagram} diagram - the diagram to pull attributes from, mostly to determine + * the keys of nodes and edge sources and targets + * @param {Object} pattern - a graph which defines the constraints to be generated + * @return {Function} + */ +dc_graph.constraint_pattern = function(pattern) { + var types = {}, rules = []; + + pattern.nodes.forEach(function(n) { + var id = n.id; + var type = types[id] || (types[id] = {}); + // partitions could be done more efficiently; this is POC + if(n.partition) { + var partition = n.partition; + var value = n.value || n.id; + if(n.all || n.typename) { + type.match = n.extract ? + function(n2) { return n.extract(n2.value[partition]); } : + function(n2) { return n2.value[partition]; }; + type.typename = n.typename || function(n2) { return partition + '=' + n2.value[partition]; }; + } + else + type.match = function(n2) { return n2.value[partition] === value; }; + } + else if(n.match) + type.match = n.match; + else throw new Error("couldn't determine matcher for type " + JSON.stringify(n)); + }); + pattern.edges.forEach(function(e) { + if(e.disable) + return; + var rule = {source: e.source, target: e.target}; + rule.produce = typeof e.produce === 'function' ? e.produce : function() { + return clone(e.produce); + }; + ['listname', 'wrap', 'reverse'].forEach(function(k) { + if(e[k] !== undefined) rule[k] = e[k]; + }); + rules.push(rule); + }); + + return function(diagram, nodes, edges) { + var constraints = []; + var members = {}; + nodes.forEach(function(n) { + var key = diagram.nodeKey.eval(n); + for(var t in types) { + var type = types[t], value = type.match(n.orig); + if(value) { + var tname = type.typename ? type.typename(t, value) : t; + if(!members[tname]) + members[tname] = { + nodes: [], // original ordering + whether: {} // boolean + }; + members[tname].nodes.push(key); + members[tname].whether[key] = true; + } + } + }); + // traversal of rules could be more efficient, again POC + var edge_rules = rules.filter(function(r) { + return r.source !== r.target; + }); + var type_rules = rules.filter(function(r) { + return r.source === r.target; + }); + edges.forEach(function(e) { + var source = diagram.edgeSource.eval(e), + target = diagram.edgeTarget.eval(e); + edge_rules.forEach(function(r) { + if(members[r.source] && members[r.source].whether[source] && + members[r.target] && members[r.target].whether[target]) { + var constraint = r.produce(members, nodes, edges); + if(r.reverse) { + constraint.left = target; + constraint.right = source; + } + else { + constraint.left = source; + constraint.right = target; + } + constraints.push(constraint); + } + }); + }); + type_rules.forEach(function(r) { + if(!members[r.source]) + return; + var constraint = r.produce(), + listname = r.listname || r.produce.listname || 'nodes', + wrap = r.wrap || r.produce.wrap || function(x) { return x; }; + constraint[listname] = members[r.source].nodes.map(wrap); + constraints.push(constraint); + }); + return constraints; + }; +}; + +// constraint generation convenience functions +dc_graph.gap_y = function(gap, equality) { + return { + axis: 'y', + gap: gap, + equality: !!equality + }; +}; +dc_graph.gap_x = function(gap, equality) { + return { + axis: 'x', + gap: gap, + equality: !!equality + }; +}; + +function align_f(axis) { + var ret = function() { + return { + type: 'alignment', + axis: axis + }; + }; + ret.listname = 'offsets'; + ret.wrap = function(x) { return {node: x, offset: 0}; }; + return ret; +} + +dc_graph.align_y = function() { + return align_f('y'); +}; +dc_graph.align_x = function() { + return align_f('x'); +}; + +dc_graph.order_x = function(gap, ordering) { + return { + type: 'ordering', + axis: 'x', + gap: 60, + ordering: ordering + }; +}; +dc_graph.order_y = function(gap, ordering) { + return { + type: 'ordering', + axis: 'y', + gap: 60, + ordering: ordering + }; +}; + +// this naive tree-drawer is paraphrased from memory from dot +dc_graph.tree_positions = function(rootf, rowf, treef, ofsx, ofsy, nwidth, ygap) { + console.warn('dc_graph.tree_positions is deprecated; use the layout engine tree_layout instead'); + if(rootf || treef) { + console.warn('dc_graph.tree_positions: rootf and treef are ignored'); + } + var x; + nwidth = d3.functor(nwidth); + function best_dist(left, right) { + return (nwidth(left) + nwidth(right)) / 2; + } + var dfs = dc_graph.depth_first_traversal({ + nodeid: function(n) { + return n.cola.dcg_nodeKey; + }, + sourceid: function(n) { + return n.cola.dcg_edgeSource; + }, + targetid: function(n) { + return n.cola.dcg_edgeTarget; + }, + init: function() { + x = ofsx; + }, + row: function(n) { + return rowf(n.orig); + }, + place: function(n, r, row) { + if(row.length) { + var left = row[row.length-1]; + var g = (nwidth(left) + nwidth(n)) / 2; + x = Math.max(x, left.left_x + g); + } + n.left_x = x; + n.hit_ins = 1; + n.cola.y = r*ygap + ofsy; + }, + sib: function(isroot, left, right) { + var g = best_dist(left, right); + if(isroot) g = g*1.5; + x += g; + }, + pop: function(n) { + n.cola.x = (n.left_x + x)/2; + }, + skip: function(n, indegree) { + // rolling average of in-neighbor x positions + n.cola.x = (n.hit_ins*n.cola.x + x)/++n.hit_ins; + if(n.hit_ins === indegree) + delete n.hit_ins; + }, + finish: function(rows) { + // this is disgusting. patch up any places where nodes overlap by scanning + // right far enough to find the space, then fill from left to right at the + // minimum gap + rows.forEach(function(row) { + var sort = row.sort(function(a, b) { return a.cola.x - b.cola.x; }); + var badi = null, badl = null, want; + for(var i=0; i<sort.length-1; ++i) { + var left = sort[i], right = sort[i+1]; + if(!badi) { + if(right.cola.x - left.cola.x < best_dist(left, right)) { + badi = i; + badl = left.cola.x; + want = best_dist(left, right); + } // else still not bad + } else { + want += best_dist(left, right); + if(i < sort.length - 2 && right.cola.x < badl + want) + continue; // still bad + else { + if(badi>0) + --badi; // might want to use more left + var l, limit; + if(i < sort.length - 2) { // found space before right + var extra = right.cola.x - (badl + want); + l = sort[badi].cola.x + extra/2; + limit = i+1; + } else { + l = Math.max(sort[badi].cola.x, badl - best_dist(sort[badi], sort[badi+1]) - (want - right.cola.x + badl)/2); + limit = sort.length; + } + for(var j = badi+1; j<limit; ++j) { + l += best_dist(sort[j-1], sort[j]); + sort[j].cola.x = l; + } + badi = badl = want = null; + } + } + } + }); + } + }); + + return function(diagram, nodes, edges) { + return dfs(nodes, edges); + }; +}; + + +// this naive tree-drawer is paraphrased from memory from dot +dc_graph.tree_constraints = function(rootf, treef, xgap, ygap) { + console.warn('dc_graph.tree_constraints is deprecated - it never worked right and may not be a good idea'); + return function(diagram, nodes, edges) { + var constraints = []; + var x = 0; + var dfs = dc_graph.depth_first_traversal({ + root: rootf, + tree: treef, + place: function(n, r, row) { + if(row.length) { + var last = row[row.length-1]; + constraints.push({ + left: diagram.nodeKey.eval(last), + right: diagram.nodeKey.eval(n), + axis: 'x', + gap: x-last.foo_x, + equality: true + }); + } + n.foo_x = x; + // n.cola.x = x; + // n.cola.y = r*ygap; + }, + sib: function() { + x += xgap; + } + }); + dfs(diagram, nodes, edges); + return constraints; + }; +}; + +dc_graph.mode = function(event_namespace, options) { + var _mode = {}; + var _eventName = options.laterDraw ? 'transitionsStarted' : 'drawn'; + var draw = options.draw, remove = options.remove; + var supported_renderers = options.renderers || ['svg']; + + if(!draw) { + console.warn('behavior.add_behavior has been replaced by mode.draw'); + draw = options.add_behavior; + } + if(!remove) { + console.warn('behavior.remove_behavior has been replaced by mode.remove'); + remove = options.remove_behavior; + } + + /** + #### .parent([object]) + Assigns this mode to a diagram. + **/ + _mode.parent = property(null) + .react(function(p) { + var diagram; + if(p) { + var first = true; + diagram = p; + p.on(_eventName + '.' + event_namespace, function() { + var args2 = [diagram].concat(Array.prototype.slice.call(arguments)); + draw.apply(null, args2); + if(first && options.first) { + options.first.apply(null, args2); + first = false; + } + else if(options.rest) + options.rest.apply(null, args2); + }); + p.on('reset.' + event_namespace, function() { + var rend = diagram.renderer(), + node = rend.selectAllNodes ? rend.selectAllNodes() : null, + edge = rend.selectAllEdges ? rend.selectAllEdges() : null, + edgeHover = rend.selectAllEdges ? rend.selectAllEdges('.edge-hover') : null; + remove(diagram, node, edge, edgeHover); + }); + } + else if(_mode.parent()) { + diagram = _mode.parent(); + diagram.on(_eventName + '.' + event_namespace, function(node, edge, ehover) { + remove(diagram, node, edge, ehover); + diagram.on(_eventName + '.' + event_namespace, null); + }); + } + options.parent && options.parent(p); + }); + + _mode.supportsRenderer = function(rendererType) { + return supported_renderers.includes(rendererType); + }; + + return _mode; +}; + +dc_graph.behavior = deprecate_function('dc_graph.behavior has been renamed dc_graph.mode', dc_graph.mode); + +/** + * Asynchronous [d3.tip](https://github.com/Caged/d3-tip) support for dc.graph.js + * + * Add tooltips to the nodes and edges of a graph using an asynchronous callback to get + * the html to show. + * + * Optional - requires separately loading the d3.tip script and CSS (which are included in + * dc.graph.js in `web/js/d3-tip/index.js` and `web/css/d3-tip/example-styles.css`) + * + * @class tip + * @memberof dc_graph + * @return {Object} + **/ +dc_graph.tip = function(options) { + options = options || {}; + var _namespace = options.namespace || 'tip'; + var _d3tip = null; + var _showTimeout, _hideTimeout; + var _dispatch = d3.dispatch('tipped'); + + function init(parent) { + if(!_d3tip) { + _d3tip = d3.tip() + .attr('class', options.class || 'd3-tip') + .html(function(d) { return "<span>" + d + "</span>"; }) + .direction(_mode.direction()); + if(_mode.offset()) + _d3tip.offset(_mode.offset()); + parent.svg().call(_d3tip); + } + } + function fetch_and_show_content(d) { + if(_mode.disabled() || _mode.selection().exclude && _mode.selection().exclude(d3.event.target)) { + hide_tip.call(this); + return; + } + var target = this, + next = function() { + _mode.content()(d, function(content) { + _d3tip.show.call(target, content, target); + d3.select('div.d3-tip') + .selectAll('a.tip-link') + .on('click.' + _namespace, function() { + d3.event.preventDefault(); + if(_mode.linkCallback()) + _mode.linkCallback()(this.id); + }); + _dispatch.tipped(d); + }); + }; + if(_hideTimeout) + window.clearTimeout(_hideTimeout); + if(_mode.delay()) { + window.clearTimeout(_showTimeout); + _showTimeout = window.setTimeout(next, _mode.delay()); + } + else next(); + } + + function check_hide_tip() { + if(d3.event.relatedTarget && + (!_mode.selection().exclude || !_mode.selection().exclude(d3.event.target)) && + (this && this.contains(d3.event.relatedTarget) || // do not hide when mouse is still over a child + _mode.clickable() && d3.event.relatedTarget.classList.contains('d3-tip'))) + return false; + return true; + } + + function preempt_tip() { + if(_showTimeout) { + window.clearTimeout(_showTimeout); + _showTimeout = null; + } + } + + function hide_tip() { + if(!check_hide_tip.apply(this)) + return; + preempt_tip(); + _d3tip.hide(); + } + + function hide_tip_delay() { + if(!check_hide_tip.apply(this)) + return; + preempt_tip(); + if(_mode.hideDelay()) + _hideTimeout = window.setTimeout(function () { + _d3tip.hide(); + }, _mode.hideDelay()); + else + _d3tip.hide(); + } + + function draw(diagram, node, edge, ehover) { + init(diagram); + _mode.programmatic() || _mode.selection().select(diagram, node, edge, ehover) + .on('mouseover.' + _namespace, fetch_and_show_content) + .on('mouseout.' + _namespace, hide_tip_delay); + if(_mode.clickable()) { + d3.select('div.d3-tip') + .on('mouseover.' + _namespace, function() { + if(_hideTimeout) + window.clearTimeout(_hideTimeout); + }) + .on('mouseout.' + _namespace, hide_tip_delay); + } + } + function remove(diagram, node, edge, ehover) { + _mode.programmatic() || _mode.selection().select(diagram, node, edge, ehover) + .on('mouseover.' + _namespace, null) + .on('mouseout.' + _namespace, null); + } + + var _mode = dc_graph.mode(_namespace, { + draw: draw, + remove: remove, + laterDraw: true + }); + /** + * Specify the direction for tooltips. Currently supports the + * [cardinal and intercardinal directions](https://en.wikipedia.org/wiki/Points_of_the_compass) supported by + * [d3.tip.direction](https://github.com/Caged/d3-tip/blob/master/docs/positioning-tooltips.md#tipdirection): + * `'n'`, `'ne'`, `'e'`, etc. + * @name direction + * @memberof dc_graph.tip + * @instance + * @param {String} [direction='n'] + * @return {String} + * @return {dc_graph.tip} + **/ + _mode.direction = property('n'); + + /** + * Specifies the function to generate content for the tooltip. This function has the + * signature `function(d, k)`, where `d` is the datum of the thing being hovered over, + * and `k` is a continuation. The function should fetch the content, asynchronously if + * needed, and then pass html forward to `k`. + * @name content + * @memberof dc_graph.tip + * @instance + * @param {Function} [content] + * @return {Function} + * @example + * // Default mode: assume it's a node, show node title + * var tip = dc_graph.tip().content(function(n, k) { + * k(_mode.parent() ? _mode.parent().nodeTitle.eval(n) : ''); + * }); + **/ + _mode.content = property(function(n, k) { + k(_mode.parent() ? _mode.parent().nodeTitle.eval(n) : ''); + }); + + _mode.on = function(event, f) { + return _dispatch.on(event, f); + }; + + _mode.disabled = property(false); + _mode.programmatic = property(false); + + _mode.displayTip = function(filter, n, cb) { + if(typeof filter !== 'function') { + var d = filter; + filter = function(d2) { return d2 === d; }; + } + var found = _mode.selection().select(_mode.parent(), _mode.parent().selectAllNodes(), _mode.parent().selectAllEdges(), null) + .filter(filter); + if(found.size() > 0) { + var action = fetch_and_show_content; + // we need to flatten e.g. for ports, which will have nested selections + // .nodes() does this better in D3v4 + var flattened = found.reduce(function(p, v) { + return p.concat(v); + }, []); + var which = (n || 0) % flattened.length; + action.call(flattened[which], d3.select(flattened[which]).datum()); + d = d3.select(flattened[which]).datum(); + if(cb) + cb(d); + if(_mode.programmatic()) + found.on('mouseout.' + _namespace, hide_tip_delay); + } + return _mode; + }; + + _mode.hideTip = function(delay) { + if(_d3tip) { + if(delay) + hide_tip_delay(); + else + hide_tip(); + } + return _mode; + }; + _mode.selection = property(dc_graph.tip.select_node_and_edge()); + _mode.showDelay = _mode.delay = property(0); + _mode.hideDelay = property(200); + _mode.offset = property(null); + _mode.clickable = property(false); + _mode.linkCallback = property(null); + + return _mode; +}; + +/** + * Generates a handler which can be passed to `tip.content` to produce a table of the + * attributes and values of the hovered object. + * + * @name table + * @memberof dc_graph.tip + * @instance + * @return {Function} + * @example + * // show all the attributes and values in the node and edge objects + * var tip = dc_graph.tip(); + * tip.content(dc_graph.tip.table()); + **/ +dc_graph.tip.table = function() { + var gen = function(d, k) { + d = gen.fetch()(d); + if(!d) + return; // don't display tooltip if no content + var data, keys; + if(Array.isArray(d)) + data = d; + else if(typeof d === 'number' || typeof d === 'string') + data = [d]; + else { // object + data = keys = Object.keys(d).filter(d3.functor(gen.filter())) + .filter(function(k) { + return d[k] !== undefined; + }); + } + var table = d3.select(document.createElement('table')); + var rows = table.selectAll('tr').data(data); + var rowsEnter = rows.enter().append('tr'); + rowsEnter.append('td').text(function(item) { + if(keys && typeof item === 'string') + return item; + return JSON.stringify(item); + }); + if(keys) + rowsEnter.append('td').text(function(item) { + return JSON.stringify(d[item]); + }); + k(table.node().outerHTML); // optimizing for clarity over speed (?) + }; + gen.filter = property(true); + gen.fetch = property(function(d) { + return d.orig.value; + }); + return gen; +}; + +dc_graph.tip.json_table = function() { + var table = dc_graph.tip.table().fetch(function(d) { + var jsontip = table.json()(d); + if(!jsontip) return null; + try { + return JSON.parse(jsontip); + } catch(xep) { + return [jsontip]; + } + }); + table.json = property(function(d) { + return (d.orig.value.value || d.orig.value).jsontip; + }); + return table; +}; + +dc_graph.tip.html_or_json_table = function() { + var json_table = dc_graph.tip.json_table(); + var gen = function(d, k) { + var html = gen.html()(d); + if(html) + k(html); + else + json_table(d, k); + }; + gen.json = json_table.json; + gen.html = property(function(d) { + return (d.orig.value.value || d.orig.value).htmltip; + }); + return gen; +}; + +dc_graph.tip.select_node_and_edge = function() { + return { + select: function(diagram, node, edge, ehover) { + // hack to merge selections, not supported d3v3 + var selection = diagram.selectAll('.foo-this-does-not-exist'); + selection[0] = node[0].concat(ehover ? ehover[0] : []); + return selection; + }, + exclude: function(element) { + return ancestor_has_class(element, 'port'); + } + }; +}; + +dc_graph.tip.select_node = function() { + return { + select: function(diagram, node, edge, ehover) { + return node; + }, + exclude: function(element) { + return ancestor_has_class(element, 'port'); + } + }; +}; + +dc_graph.tip.select_edge = function() { + return { + select: function(diagram, node, edge, ehover) { + return edge; + } + }; +}; + +dc_graph.tip.select_port = function() { + return { + select: function(diagram, node, edge, ehover) { + return node.selectAll('g.port'); + } + }; +}; + +dc_graph.dropdown = function() { + dc_graph.dropdown.unique_id = (dc_graph.dropdown.unique_id || 16) + 1; + var _dropdown = { + id: 'id' + dc_graph.dropdown.unique_id, + parent: property(null), + show: function(key, x, y) { + var dropdown = _dropdown.parent().root() + .selectAll('div.dropdown.' + _dropdown.id).data([0]); + var dropdownEnter = dropdown + .enter().append('div') + .attr('class', 'dropdown ' + _dropdown.id); + dropdown + .style('visibility', 'visible') + .style('left', x + 'px') + .style('top', y + 'px'); + var capture; + var hides = _dropdown.hideOn().split('|'); + var selects = _dropdown.selectOn().split('|'); + if(hides.includes('leave')) + dropdown.on('mouseleave', function() { + dropdown.style('visibility', 'hidden'); + }); + else if(hides.includes('clickout')) { + var diagram = _dropdown.parent(); + capture = diagram.svg().append('rect') + .attr('x', 0) + .attr('y', 0) + .attr('width', diagram.width()) + .attr('height', diagram.height()) + .attr('opacity', 0) + .on('click', function() { + capture.remove(); + dropdown.style('visibility', 'hidden'); + }); + } + var container = dropdown; + if(_dropdown.scrollHeight()) { + var height = _dropdown.scrollHeight(); + if(typeof height === 'number') + height = height + 'px'; + dropdown + .style('max-height', height) + .property('scrollTop', 0); + dropdownEnter + .style('overflow-y', 'auto') + .append('div') + .attr('class', 'scroller'); + container = dropdown.selectAll('div.scroller'); + } + var values = _dropdown.fetchValues()(key, function(values) { + var items = container + .selectAll('div.dropdown-item').data(values); + items + .enter().append('div') + .attr('class', 'dropdown-item'); + items.exit().remove(); + var select_event = null; + if(selects.includes('click')) + select_event = 'click'; + else if(selects.includes('hover')) + select_event = 'mouseenter'; + items + .text(function(item) { return _dropdown.itemText()(item); }); + if(select_event) { + items + .on(select_event + '.select', function(d) { + _dropdown.itemSelected()(d); + }); + } + if(hides.includes('clickitem')) { + items + .on('click.hide', function(d) { + capture.remove(); + dropdown.style('visibility', 'hidden'); + }); + } + }); + }, + hideOn: property('clickout|clickitem'), + selectOn: property('click'), + height: property(10), + itemText: property(function(x) { return x; }), + itemSelected: property(function() {}), + fetchValues: property(function(key, k) { k([]); }), + scrollHeight: property('12em') + }; + return _dropdown; +}; + +dc_graph.keyboard = function() { + var _input_anchor, _dispatch = d3.dispatch('keydown', 'keyup'); + + function keydown() { + _dispatch.keydown(); + } + function keyup() { + _dispatch.keyup(); + } + function draw(diagram) { + _input_anchor = diagram.svg().selectAll('a#dcgraph-keyboard').data([1]); + _input_anchor.enter() + .insert('a', ':first-child').attr({ + id: 'dcgraph-keyboard', + href: '#' + }); + _input_anchor.on('keydown.keyboard', keydown); + _input_anchor.on('keyup.keyboard', keyup); + + // grab focus whenever svg is interacted with (?) + diagram.svg().on('mouseup.keyboard', function() { + _mode.focus(); + }); + } + function remove(diagram) { + _input_anchor.remove(); + } + var _mode = dc_graph.mode('brush', { + draw: draw, + remove: remove + }); + + _mode.on = function(event, f) { + if(arguments.length === 1) + return _dispatch.on(event); + _dispatch.on(event, f); + return this; + }; + + _mode.focus = function() { + if(!_mode.disableFocus()) { + _input_anchor.node().focus && _input_anchor.node().focus(); + } + }; + + _mode.disableFocus = property(false); + + return _mode; +}; + +// adapted from +// http://stackoverflow.com/questions/9308938/inline-text-editing-in-svg/#26644652 + +dc_graph.edit_text = function(parent, options) { + var foreign = parent.append('foreignObject').attr({ + height: '100%', + width: '100%' // don't wrap + }); + var padding = options.padding !== undefined ? options.padding : 2; + function reposition() { + var pos; + switch(options.align) { + case 'left': + pos = [options.box.x-padding, options.box.y-padding]; + break; + default: + case 'center': + pos = [ + options.box.x + (options.box.width - textdiv.node().offsetWidth)/2, + options.box.y + (options.box.height - textdiv.node().offsetHeight)/2 + ]; + break; + } + foreign.attr('transform', 'translate(' + pos.join(' ') + ')'); + } + var textdiv = foreign.append('xhtml:div'); + var text = options.text || "type on me"; + textdiv.text(text).attr({ + contenteditable: true, + width: 'auto', + class: options.class || null + }).style({ + display: 'inline-block', + 'background-color': 'white', + padding: padding + 'px' + }); + + function stopProp() { + d3.event.stopPropagation(); + } + foreign + .on('mousedown.edit-text', stopProp) + .on('mousemove.edit-text', stopProp) + .on('mouseup.edit-text', stopProp) + .on('dblclick.edit-text', stopProp); + + function accept() { + options.accept && options.accept(textdiv.text()); + textdiv.on('blur.edit-text', null); + foreign.remove(); + options.finally && options.finally(); + } + function cancel() { + options.cancel && options.cancel(); + textdiv.on('blur.edit-text', null); + foreign.remove(); + options.finally && options.finally(); + } + + textdiv.on('keydown.edit-text', function() { + if(d3.event.keyCode===13) { + d3.event.preventDefault(); + } + }).on('keyup.edit-text', function() { + if(d3.event.keyCode===13) { + accept(); + } else if(d3.event.keyCode===27) { + cancel(); + } + reposition(); + }).on('blur.edit-text', cancel); + reposition(); + textdiv.node().focus(); + + var range = document.createRange(); + if(options.selectText) { + range.selectNodeContents(textdiv.node()); + } else { + range.setStart(textdiv.node(), 1); + range.setEnd(textdiv.node(), 1); + } + var sel = window.getSelection(); + sel.removeAllRanges(); + sel.addRange(range); +}; + +/** + * `dc_graph.brush` is a {@link dc_graph.mode mode} providing a simple wrapper over + * [d3.svg.brush](https://github.com/d3/d3-3.x-api-reference/blob/master/SVG-Controls.md#brush) + * @class brush + * @memberof dc_graph + * @return {dc_graph.brush} + **/ +dc_graph.brush = function() { + var _brush = null, _gBrush, _dispatch = d3.dispatch('brushstart', 'brushmove', 'brushend'); + + function brushstart() { + _dispatch.brushstart(); + } + function brushmove() { + var ext = _brush.extent(); + _dispatch.brushmove(ext); + } + function brushend() { + _dispatch.brushend(); + _gBrush.call(_brush.clear()); + } + function install_brush(diagram) { + if(!_brush) { + _brush = d3.svg.brush() + .x(diagram.x()).y(diagram.y()) + .on('brushstart.brush-mode', brushstart) + .on('brush.brush-mode', brushmove) + .on('brushend.brush-mode', brushend); + } + if(!_gBrush) { + _gBrush = diagram.svg().insert('g', ':first-child') + .attr('class', 'brush') + .call(_brush); + } + } + function remove_brush() { + if(_gBrush) { + _gBrush.remove(); + _gBrush = null; + } + } + var _mode = dc_graph.mode('brush', { + draw: function() {}, + remove: remove_brush + }); + + /** + * Subscribe to a brush event, currently `brushstart`, `brushmove`, or `brushend` + * @method on + * @memberof dc_graph.brush + * @instance + * @param {String} event the name of the event; please namespace with `'namespace.event'` + * @param {Function} [f] the handler function; if omitted, returns the current handler + * @return {dc_graph.brush} + * @return {Function} + **/ + _mode.on = function(event, f) { + if(arguments.length === 1) + return _dispatch.on(event); + _dispatch.on(event, f); + return this; + }; + /** + * Add the brush to the parent diagram's SVG + * @method activate + * @memberof dc_graph.brush + * @instance + * @return {dc_graph.brush} + **/ + _mode.activate = function() { + install_brush(_mode.parent()); + return this; + }; + /** + * Remove the brush from the parent diagram's SVG + * @method deactivate + * @memberof dc_graph.brush + * @instance + * @return {dc_graph.brush} + **/ + _mode.deactivate = function() { + remove_brush(); + return this; + }; + /** + * Retrieve whether the brush is currently active + * @method isActive + * @memberof dc_graph.brush + * @instance + * @return {Boolean} + **/ + _mode.isActive = function () { + return !!_gBrush; + }; + + return _mode; +}; + +dc_graph.select_things = function(things_group, things_name, thinginess) { + var _selected = [], _oldSelected; + var _mousedownThing = null; + + var contains_predicate = thinginess.keysEqual ? + function(k1) { + return function(k2) { + return thinginess.keysEqual(k1, k2); + }; + } : + function(k1) { + return function(k2) { + return k1 === k2; + }; + }; + function contains(array, key) { + return !!_selected.find(contains_predicate(key)); + } + function isUnion(event) { + return event.shiftKey; + } + function isToggle(event) { + return is_a_mac ? event.metaKey : event.ctrlKey; + } + function add_array(array, key) { + return contains(array, key) ? array : array.concat([key]); + } + function toggle_array(array, key) { + return contains(array, key) ? array.filter(function(x) { return x != key; }) : array.concat([key]); + } + + function selection_changed(diagram) { + return function(selection, refresh) { + if(refresh === undefined) + refresh = true; + _selected = selection; + if(refresh) + diagram.requestRefresh(); + }; + } + var _have_bce = false; + function background_click_event(diagram, v) { + // we seem to have nodes-background interrupting edges-background by reinstalling uselessly + if(_have_bce === v) + return; + diagram.svg().on('click.' + things_name, v ? function(t) { + if(d3.event.target === this) + things_group.set_changed([]); + } : null); + _have_bce = v; + } + function brushstart() { + if(isUnion(d3.event.sourceEvent) || isToggle(d3.event.sourceEvent)) + _oldSelected = _selected.slice(); + else { + _oldSelected = []; + things_group.set_changed([]); + } + } + function brushmove(ext) { + if(!thinginess.intersectRect) + return; + var rectSelect = thinginess.intersectRect(ext); + var newSelected; + if(isUnion(d3.event.sourceEvent)) + newSelected = rectSelect.reduce(add_array, _oldSelected); + else if(isToggle(d3.event.sourceEvent)) + newSelected = rectSelect.reduce(toggle_array, _oldSelected); + else + newSelected = rectSelect; + things_group.set_changed(newSelected); + } + + function draw(diagram, node, edge) { + var condition = _mode.noneIsAll() ? function(t) { + return !_selected.length || contains(_selected, thinginess.key(t)); + } : function(t) { + return contains(_selected, thinginess.key(t)); + }; + thinginess.applyStyles(condition); + + thinginess.clickables(diagram, node, edge).on('mousedown.' + things_name, function(t) { + _mousedownThing = t; + }); + + thinginess.clickables(diagram, node, edge).on('mouseup.' + things_name, function(t) { + if(thinginess.excludeClick && thinginess.excludeClick(d3.event.target)) + return; + // it's only a click if the same target was mousedown & mouseup + // but we can't use click event because things may have been reordered + if(_mousedownThing !== t) + return; + var key = thinginess.key(t), newSelected; + if(_mode.multipleSelect()) { + if(isUnion(d3.event)) + newSelected = add_array(_selected, key); + else if(isToggle(d3.event)) + newSelected = toggle_array(_selected, key); + } + if(!newSelected) + newSelected = [key]; + things_group.set_changed(newSelected); + }); + + if(_mode.multipleSelect()) { + var brush_mode = diagram.child('brush'); + brush_mode.activate(); + } + else + background_click_event(diagram, _mode.clickBackgroundClears()); + + if(_mode.autoCropSelection()) { + // drop any selected which no longer exist in the diagram + var present = thinginess.clickables(diagram, node, edge).data().map(thinginess.key); + var now_selected = _selected.filter(function(k) { return contains(present, k); }); + if(_selected.length !== now_selected.length) + things_group.set_changed(now_selected, false); + } + } + + function remove(diagram, node, edge) { + thinginess.clickables(diagram, node, edge).on('click.' + things_name, null); + diagram.svg().on('click.' + things_name, null); + thinginess.removeStyles(); + } + + var _mode = dc_graph.mode(things_name, { + draw: draw, + remove: remove, + parent: function(p) { + things_group.on('set_changed.' + things_name, p ? selection_changed(p) : null); + if(p && _mode.multipleSelect()) { + var brush_mode = p.child('brush'); + if(!brush_mode) { + brush_mode = dc_graph.brush(); + p.child('brush', brush_mode); + } + brush_mode + .on('brushstart.' + things_name, brushstart) + .on('brushmove.' + things_name, brushmove); + } + }, + laterDraw: thinginess.laterDraw || false + }); + + _mode.multipleSelect = property(true); + _mode.clickBackgroundClears = property(true, false).react(function(v) { + if(!_mode.multipleSelect() && _mode.parent()) + background_click_event(_mode.parent(), v); + }); + _mode.noneIsAll = property(false); + // if you're replacing the data, you probably want the selection not to be preserved when a thing + // with the same key re-appears later (true). however, if you're filtering dc.js-style, you + // probably want filters to be independent between diagrams (false) + _mode.autoCropSelection = property(true); + // if you want to do the cool things select_things can do + _mode.thinginess = function() { + return thinginess; + }; + return _mode; +}; + +dc_graph.select_things_group = function(brushgroup, type) { + window.chart_registry.create_type(type, function() { + return d3.dispatch('set_changed'); + }); + + return window.chart_registry.create_group(type, brushgroup); +}; + +dc_graph.select_nodes = function(props, options) { + options = options || {}; + var select_nodes_group = dc_graph.select_things_group(options.select_nodes_group || 'select-nodes-group', 'select-nodes'); + + var thinginess = { + intersectRect: function(ext) { + return _mode.parent().selectAllNodes().data().filter(function(n) { + return n && ext[0][0] < n.cola.x && n.cola.x < ext[1][0] && + ext[0][1] < n.cola.y && n.cola.y < ext[1][1]; + }).map(this.key); + }, + clickables: function(diagram, node, edge) { + return node; + }, + excludeClick: function(element) { + return ancestor_has_class(element, 'port'); + }, + key: function(n) { + return _mode.parent().nodeKey.eval(n); + }, + applyStyles: function(pred) { + _mode.parent().cascade(50, true, node_edge_conditions(pred, null, props)); + }, + removeStyles: function() { + _mode.parent().cascade(50, false, props); + } + }; + var _mode = dc_graph.select_things(select_nodes_group, 'select-nodes', thinginess); + return _mode; +}; + +dc_graph.select_edges = function(props, options) { + options = options || {}; + var select_edges_group = dc_graph.select_things_group(options.select_edges_group || 'select-edges-group', 'select-edges'); + var thinginess = { + intersectRect: function(ext) { + return this.clickables().data().filter(function(e) { + // this nonsense because another select_things may have invalidated the edge positions (!!) + var sp = { + x: e.source.cola.x + e.sourcePort.pos.x, + y: e.source.cola.y + e.sourcePort.pos.y + }, + tp = { + x: e.target.cola.x + e.targetPort.pos.x, + y: e.target.cola.y + e.targetPort.pos.y + }; + return [sp, tp].some(function(p) { + return ext[0][0] < p.x && p.x < ext[1][0] && + ext[0][1] < p.y && p.y < ext[1][1]; + }); + }).map(this.key); + }, + clickables: function() { + return _mode.parent().selectAllEdges('.edge-hover'); + }, + key: function(e) { + return _mode.parent().edgeKey.eval(e); + }, + applyStyles: function(pred) { + _mode.parent().cascade(50, true, node_edge_conditions(null, pred, props)); + }, + removeStyles: function() { + _mode.parent().cascade(50, false, props); + } + }; + var _mode = dc_graph.select_things(select_edges_group, 'select-edges', thinginess); + return _mode; +}; + +dc_graph.select_ports = function(props, options) { + options = options || {}; + var port_style = options.portStyle || 'symbols'; + var select_ports_group = dc_graph.select_things_group(options.select_ports_group || 'select-ports-group', 'select-ports'); + var thinginess = { + laterDraw: true, + intersectRect: null, // multiple selection not supported for now + clickables: function() { + return _mode.parent().selectAllNodes('g.port'); + }, + key: function(p) { + // this scheme also won't work with multiselect + return p.named ? + {node: _mode.parent().nodeKey.eval(p.node), name: p.name} : + {edge: _mode.parent().edgeKey.eval(p.edges[0]), name: p.name}; + }, + applyStyles: function(pred) { + _mode.parent().portStyle(port_style).cascade(50, true, conditional_properties(pred, props)); + }, + removeStyles: function() { + _mode.parent().portStyle(port_style).cascade(50, false, props); + }, + keysEqual: function(k1, k2) { + return k1.name === k2.name && (k1.node ? k1.node === k2.node : k1.edge === k2.edge); + } + }; + var _mode = dc_graph.select_things(select_ports_group, 'select-ports', thinginess); + return _mode; +}; + +dc_graph.move_nodes = function(options) { + options = options || {}; + var select_nodes_group = dc_graph.select_things_group(options.select_nodes_group || 'select-nodes-group', 'select-nodes'); + var fix_nodes_group = dc_graph.fix_nodes_group('fix-nodes-group'); + var _selected = [], _startPos = null, _downNode, _moveStarted; + var _brush, _drawGraphs, _selectNodes, _restoreBackgroundClick; + var _maybeSelect = null; + + function isUnion(event) { + return event.shiftKey; + } + function isToggle(event) { + return is_a_mac ? event.metaKey : event.ctrlKey; + } + + function selection_changed(diagram) { + return function(selection, refresh) { + if(refresh === undefined) + refresh = true; + _selected = selection; + }; + } + function for_each_selected(f, selected) { + selected = selected || _selected; + selected.forEach(function(key) { + var n = _mode.parent().getWholeNode(key); + f(n, key); + }); + } + function draw(diagram, node, edge) { + node.on('mousedown.move-nodes', function(n) { + // Need a more general way for modes to say "I got this" + if(_drawGraphs && _drawGraphs.usePorts() && _drawGraphs.usePorts().eventPort()) + return; + _startPos = dc_graph.event_coords(diagram); + _downNode = d3.select(this); + // if the node under the mouse is not in the selection, need to + // make that node selected + var key = diagram.nodeKey.eval(n); + var selected = _selected; + if(_selected.indexOf(key)<0) { + selected = [key]; + _maybeSelect = key; + } + else _maybeSelect = null; + for_each_selected(function(n) { + n.original_position = [n.cola.x, n.cola.y]; + }, selected); + if(_brush) + _brush.deactivate(); + }); + function mouse_move() { + if(_startPos) { + if(!(d3.event.buttons & 1)) { + mouse_up(); + return; + } + if(_maybeSelect) + select_nodes_group.set_changed([_maybeSelect]); + var pos = dc_graph.event_coords(diagram); + var dx = pos[0] - _startPos[0], + dy = pos[1] - _startPos[1]; + if(!_moveStarted && Math.hypot(dx, dy) > _mode.dragSize()) { + _moveStarted = true; + // prevent click event for this node setting selection just to this + if(_downNode) + _downNode.style('pointer-events', 'none'); + } + if(_moveStarted) { + for_each_selected(function(n) { + n.cola.x = n.original_position[0] + dx; + n.cola.y = n.original_position[1] + dy; + }); + diagram.reposition(node, edge); + } + } + } + function mouse_up() { + if(_startPos) { + if(_moveStarted) { + _moveStarted = false; + if(_downNode) { + _downNode.style('pointer-events', null); + _downNode = null; + } + var fixes = []; + for_each_selected(function(n, id) { + fixes.push({ + id: id, + pos: {x: n.cola.x, y: n.cola.y} + }); + }); + fix_nodes_group.request_fixes(fixes); + } + if(_brush) + _brush.activate(); + _startPos = null; + } + } + node + .on('mousemove.move-nodes', mouse_move) + .on('mouseup.move-nodes', mouse_up); + diagram.svg() + .on('mousemove.move-nodes', mouse_move) + .on('mouseup.move-nodes', mouse_up); + } + + function remove(diagram, node, edge) { + node.on('mousedown.move-nodes', null); + node.on('mousemove.move-nodes', null); + node.on('mouseup.move-nodes', null); + } + + var _mode = dc_graph.mode('move-nodes', { + draw: draw, + remove: remove, + parent: function(p) { + select_nodes_group.on('set_changed.move-nodes', p ? selection_changed(p) : null); + if(p) { + _brush = p.child('brush'); + _drawGraphs = p.child('draw-graphs'); + _selectNodes = p.child('select-nodes'); + } + else _brush = _drawGraphs = _selectNodes = null; + } + }); + + // minimum distance that is considered a drag, not a click + _mode.dragSize = property(5); + + return _mode; +}; + +dc_graph.fix_nodes = function(options) { + options = options || {}; + var fix_nodes_group = dc_graph.fix_nodes_group('fix-nodes-group'); + var _fixedPosTag = options.fixedPosTag || 'fixedPos'; + var _fixes = [], _nodes, _wnodes, _edges, _wedges; + + var _execute = { + nodeid: function(n) { + return _mode.parent().nodeKey.eval(n); + }, + sourceid: function(e) { + return _mode.parent().edgeSource.eval(e); + }, + targetid: function(e) { + return _mode.parent().edgeTarget.eval(e); + }, + get_fix: function(n) { + return _mode.parent().nodeFixed.eval(n); + }, + fix_node: function(n, pos) { + n[_fixedPosTag] = pos; + }, + unfix_node: function(n) { + n[_fixedPosTag] = null; + }, + clear_fixes: function() { + _fixes = {}; + }, + register_fix: function(id, pos) { + _fixes[id] = pos; + } + }; + + function request_fixes(fixes) { + _mode.strategy().request_fixes(_execute, fixes); + tell_then_set(find_changes()).then(function() { + _mode.parent().redraw(); + }); + } + function new_node(nid, n, pos) { + _mode.strategy().new_node(_execute, nid, n, pos); + } + function new_edge(eid, sourceid, targetid) { + var source = _nodes[sourceid], target = _nodes[targetid]; + _mode.strategy().new_edge(_execute, eid, source, target); + } + function find_changes() { + var changes = []; + _wnodes.forEach(function(n) { + var key = _mode.parent().nodeKey.eval(n), + fixPos = _fixes[key], + oldFixed = n.orig.value[_fixedPosTag], + changed = false; + if(oldFixed) { + if(!fixPos || fixPos.x !== oldFixed.x || fixPos.y !== oldFixed.y) + changed = true; + } + else changed = fixPos; + if(changed) + changes.push({n: n, fixed: fixPos ? {x: fixPos.x, y: fixPos.y} : null}); + }); + return changes; + } + function execute_change(n, fixed) { + if(fixed) + _execute.fix_node(n.orig.value, fixed); + else + _execute.unfix_node(n.orig.value); + } + function tell_then_set(changes) { + var callback = _mode.fixNode() || function(n, pos) { return Promise.resolve(pos); }; + var promises = changes.map(function(change) { + var key = _mode.parent().nodeKey.eval(change.n); + return callback(key, change.fixed) + .then(function(fixed) { + execute_change(change.n, fixed); + }); + }); + return Promise.all(promises); + } + function set_changes(changes) { + changes.forEach(function(change) { + execute_change(change.n, change.fixed); + }); + } + function tell_changes(changes) { + var callback = _mode.fixNode() || function(n, pos) { return Promise.resolve(pos); }; + var promises = changes.map(function(change) { + var key = _mode.parent().nodeKey.eval(change.n); + return callback(key, change.fixed); + }); + return Promise.all(promises); + } + function fix_all_nodes(tell) { + if(tell === undefined) + tell = true; + var changes = _wnodes.map(function(n) { + return {n: n, fixed: {x: n.cola.x, y: n.cola.y}}; + }); + if(tell) + return tell_then_set(changes); + else { + set_changes(changes); + return Promise.resolve(undefined); + } + } + function clear_fixes() { + _mode.strategy().clear_all_fixes && _mode.strategy().clear_all_fixes(); + _execute.clear_fixes(); + } + function on_data(diagram, nodes, wnodes, edges, wedges, ports, wports) { + _nodes = nodes; + _wnodes = wnodes; + _edges = edges; + _wedges = wedges; + if(_mode.strategy().on_data) { + _mode.strategy().on_data(_execute, nodes, wnodes, edges, wedges, ports, wports); // ghastly + var changes = find_changes(); + set_changes(changes); + // can't wait for backend to acknowledge/approve so just set then blast + if(_mode.reportOverridesAsynchronously()) + tell_changes(changes); // dangling promise + } + } + + var _mode = { + parent: property(null).react(function(p) { + fix_nodes_group + .on('request_fixes.fix-nodes', p ? request_fixes : null) + .on('new_node.fix_nodes', p ? new_node : null) + .on('new_edge.fix_nodes', p ? new_edge : null); + if(p) { + p.on('data.fix-nodes', on_data); + } else if(_mode.parent()) + _mode.parent().on('data.fix-nodes', null); + }), + // callback for setting & fixing node position + fixNode: property(null), + // save/load may want to nail everything / start from scratch + // (should probably be automatic though) + fixAllNodes: fix_all_nodes, + clearFixes: clear_fixes, + strategy: property(dc_graph.fix_nodes.strategy.fix_last()), + reportOverridesAsynchronously: property(true) + }; + + return _mode; +}; + +dc_graph.fix_nodes.strategy = {}; +dc_graph.fix_nodes.strategy.fix_last = function() { + return { + request_fixes: function(exec, fixes) { + exec.clear_fixes(); + fixes.forEach(function(fix) { + exec.register_fix(fix.id, fix.pos); + }); + }, + new_node: function(exec, nid, n, pos) { + exec.fix_node(n, pos); + }, + new_edge: function(exec, eid, source, target) { + exec.unfix_node(source.orig.value); + exec.unfix_node(target.orig.value); + } + }; +}; +dc_graph.fix_nodes.strategy.last_N_per_component = function(maxf) { + maxf = maxf || 1; + var _age = 0; + var _allFixes = {}; + return { + clear_all_fixes: function() { + _allFixes = {}; + }, + request_fixes: function(exec, fixes) { + ++_age; + fixes.forEach(function(fix) { + _allFixes[fix.id] = {id: fix.id, age: _age, pos: fix.pos}; + }); + }, + new_node: function(exec, nid, n, pos) { + ++_age; + _allFixes[nid] = {id: nid, age: _age, pos: pos}; + exec.fix_node(n, pos); + }, + new_edge: function() {}, + on_data: function(exec, nodes, wnodes, edges, wedges, ports, wports) { + ++_age; + // add any existing fixes as requests + wnodes.forEach(function(n) { + var nid = exec.nodeid(n), pos = exec.get_fix(n); + if(pos && !_allFixes[nid]) + _allFixes[nid] = {id: nid, age: _age, pos: pos}; + }); + // determine components + var components = []; + var dfs = dc_graph.undirected_dfs({ + nodeid: exec.nodeid, + sourceid: exec.sourceid, + targetid: exec.targetid, + comp: function() { + components.push([]); + }, + node: function(compid, n) { + components[compid].push(n); + } + }); + dfs(wnodes, wedges); + // start from scratch + exec.clear_fixes(); + // keep or produce enough fixed nodes per component + components.forEach(function(comp, i) { + var oldcomps = comp.reduce(function(cc, n) { + if(n.last_component) { + var counts = cc[n.last_component] = cc[n.last_component] || { + total: 0, + fixed: 0 + }; + counts.total++; + if(_allFixes[exec.nodeid(n)]) + counts.fixed++; + } + return cc; + }, {}); + var fixed_by_size = Object.keys(oldcomps).reduce(function(ff, compid) { + if(oldcomps[compid].fixed) + ff.push({compid: +compid, total: oldcomps[compid].total, fixed: oldcomps[compid].fixed}); + return ff; + }, []).sort(function(coa, cob) { + return cob.total - coa.total; + }); + var largest_fixed = fixed_by_size.length && fixed_by_size[0].compid; + var fixes = comp.filter(function(n) { + return !n.last_component || n.last_component === largest_fixed; + }).map(function(n) { + return _allFixes[exec.nodeid(n)]; + }).filter(function(fix) { + return fix; + }); + if(fixes.length > maxf) { + fixes.sort(function(f1, f2) { + return f2.age - f1.age; + }); + fixes = fixes.slice(0, maxf); + } + fixes.forEach(function(fix) { + exec.register_fix(fix.id, fix.pos); + }); + var kept = fixes.reduce(function(m, fix) { + m[fix.id] = true; + return m; + }, {}); + comp.forEach(function(n) { + var nid = exec.nodeid(n); + if(!kept[nid]) + _allFixes[nid] = null; + n.last_component = i+1; + }); + }); + } + }; +}; + +dc_graph.fix_nodes_group = function(brushgroup) { + window.chart_registry.create_type('fix-nodes', function() { + return d3.dispatch('request_fixes', 'new_node', 'new_edge'); + }); + + return window.chart_registry.create_group('fix-nodes', brushgroup); +}; + +dc_graph.filter_selection = function(things_group, things_name) { + things_name = things_name || 'select-nodes'; + var select_nodes_group = dc_graph.select_things_group(things_group || 'select-nodes-group', things_name); + + function selection_changed(diagram) { + return function(selection) { + if(selection.length) { + var set = d3.set(selection); + _mode.dimensionAccessor()(diagram).filterFunction(function(k) { + return set.has(k); + }); + } else _mode.dimensionAccessor()(diagram).filter(null); + diagram.redrawGroup(); + }; + } + + var _mode = { + parent: property(null).react(function(p) { + select_nodes_group.on('set_changed.filter-selection-' + things_name, p ? selection_changed(p) : null); + }) + }; + _mode.dimensionAccessor = property(function(diagram) { + return diagram.nodeDimension(); + }); + return _mode; +}; + +dc_graph.delete_things = function(things_group, mode_name, id_tag) { + id_tag = id_tag || 'id'; + var _deleteKey = is_a_mac ? 'Backspace' : 'Delete'; + var _keyboard, _selected = []; + function selection_changed(selection) { + _selected = selection; + } + function row_id(r) { + return r[id_tag]; + } + function delete_selection(selection) { + if(!_mode.crossfilterAccessor()) + throw new Error('need crossfilterAccessor'); + if(!_mode.dimensionAccessor()) + throw new Error('need dimensionAccessor'); + selection = selection || _selected; + if(selection.length === 0) + return Promise.resolve([]); + var promise = _mode.preDelete() ? _mode.preDelete()(selection) : Promise.resolve(selection); + if(_mode.onDelete()) + promise = promise.then(_mode.onDelete()); + return promise.then(function(selection) { + if(selection && selection.length) { + var crossfilter = _mode.crossfilterAccessor()(_mode.parent()), + dimension = _mode.dimensionAccessor()(_mode.parent()); + var all = crossfilter.all().slice(), n = all.length; + dimension.filter(null); + crossfilter.remove(); + var filtered = all.filter(function(r) { + return selection.indexOf(row_id(r)) === -1; + }); + if(all.length !== filtered.length + selection.length) + console.warn('size after deletion is not previous size minus selection size', + filtered.map(row_id), all.map(row_id), selection); + crossfilter.add(filtered); + + _mode.parent().redrawGroup(); + } + return true; + }); + } + function draw(diagram) { + _keyboard.on('keyup.' + mode_name, function() { + if(d3.event.code === _deleteKey) + delete_selection(); + }); + } + function remove(diagram) { + } + var _mode = dc_graph.mode(mode_name, { + draw: draw, + remove: remove, + parent: function(p) { + things_group.on('set_changed.' + mode_name, selection_changed); + if(p) { + _keyboard = p.child('keyboard'); + if(!_keyboard) + p.child('keyboard', _keyboard = dc_graph.keyboard()); + } + } + }); + _mode.preDelete = property(null); + _mode.onDelete = property(null); + _mode.crossfilterAccessor = property(null); + _mode.dimensionAccessor = property(null); + _mode.deleteSelection = delete_selection; + return _mode; +}; + +dc_graph.delete_nodes = function(id_tag, options) { + options = options || {}; + var select_nodes_group = dc_graph.select_things_group(options.select_nodes_group || 'select-nodes-group', 'select-nodes'); + var select_edges_group = dc_graph.select_things_group(options.select_edges_group || 'select-edges-group', 'select-edges'); + var _mode = dc_graph.delete_things(select_nodes_group, 'delete-nodes', id_tag); + + _mode.preDelete(function(nodes) { + // request a delete of all attached edges, using the delete edges mode + // kind of horrible + var diagram = _mode.parent(); + var deleteEdgesMode = diagram.child('delete-edges'); + if(!deleteEdgesMode) + return null; // reject if we can't delete the edges + // it is likely that the delete_edges mode is listening to the same keyup event we + // are. introduce a pause to let it process the delete key now, deleting any selected edges. + // then select any remaining edges connected to the selected nodes and delete those. + // + // more evidence that modes need to be able to say "i got this", or that we should have + // batch deletion. otoh, given the current behavior, delete_nodes deferring to delete_edges + // makes about as much sense as anything + return Promise.resolve(undefined).then(function() { + var deleteEdges = diagram.edgeGroup().all().filter(function(e) { + return nodes.indexOf(diagram.edgeSource()(e)) !== -1 || + nodes.indexOf(diagram.edgeTarget()(e)) !== -1; + }).map(diagram.edgeKey()); + select_edges_group.set_changed(deleteEdges); + return deleteEdgesMode.deleteSelection().then(function() { + return nodes; + }); + }); + }); + return _mode; +}; + +dc_graph.label_things = function(options) { + options = options || {}; + var select_things_group = dc_graph.select_things_group(options.select_group, options.select_type), + label_things_group = dc_graph.label_things_group(options.label_group, options.label_type); + var _selected = []; + var _keyboard, _selectThings; + + function selection_changed_listener(diagram) { + return function(selection) { + _selected = selection; + }; + } + + function grab_focus() { + _keyboard.focus(); + } + + function edit_label_listener(diagram) { + return function(thing, eventOptions) { + var box = options.thing_box(thing); + options.hide_thing_label(thing, true); + dc_graph.edit_text( + diagram.g(), + { + text: eventOptions.text || options.thing_label(thing) || options.default_label, + align: options.align, + class: options.class, + box: box, + selectText: eventOptions.selectText, + accept: function(text) { + return options.accept(thing, text); + }, + finally: function() { + options.hide_thing_label(thing, false); + grab_focus(); + } + }); + }; + } + + function edit_selection(node, edge, eventOptions) { + // less than ideal interface. + // what if there are other things? can i blame the missing metagraph? + var thing = options.find_thing(_selected[0], node, edge); + if(thing.empty()) { + console.error("couldn't find thing '" + _selected[0] + "'!"); + return; + } + if(thing.size()>1) { + console.error("found too many things for '" + _selected[0] + "' (" + thing.size() + ")!"); + return; + } + label_things_group.edit_label(thing, eventOptions); + } + function draw(diagram, node, edge) { + _keyboard.on('keyup.' + options.label_type, function() { + if(_selected.length) { + // printable characters should start edit + if(d3.event.key.length !== 1) + return; + edit_selection(node, edge, {text: d3.event.key, selectText: false}); + } + }); + if(_selectThings) + _selectThings.thinginess().clickables(diagram, node, edge).on('dblclick.' + options.label_type, function() { + edit_selection(node, edge, {selectText: true}); + }); + } + + function remove(diagram, node, edge) { + } + + var _mode = dc_graph.mode(options.label_type, { + draw: draw, + remove: remove, + parent: function(p) { + select_things_group.on('set_changed.' + options.label_type, p ? selection_changed_listener(p) : null); + label_things_group.on('edit_label.' + options.label_type, p ? edit_label_listener(p) : null); + if(p) { + _keyboard = p.child('keyboard'); + if(!_keyboard) + p.child('keyboard', _keyboard = dc_graph.keyboard()); + _selectThings = p.child(options.select_type); + } + } + }); + _mode.editSelection = function(eventOptions) { + edit_selection(_mode.parent().selectAllNodes(), _mode.parent().selectAllEdges(), eventOptions); + }; + return _mode; +}; + +dc_graph.label_things_group = function(brushgroup, type) { + window.chart_registry.create_type(type, function() { + return d3.dispatch('edit_label'); + }); + + return window.chart_registry.create_group(type, brushgroup); +}; + +dc_graph.label_nodes = function(options) { + options = options || {}; + var _labelTag = options.labelTag || 'label'; + options.select_group = options.select_group || 'select-nodes-group'; + options.select_type = options.select_type || 'select-nodes'; + options.label_group = options.label_group || 'label-nodes-group'; + options.label_type = options.label_type || 'label-nodes'; + options.default_label = "node name"; + + options.find_thing = function(key, node, edge) { + return node.filter(function(n) { + return _mode.parent().nodeKey.eval(n) === key; + }); + }; + options.hide_thing_label = function(node, whether) { + var contents = _mode.parent().content(_mode.parent().nodeContent.eval(node.datum())); + contents.selectText(node).attr('visibility', whether ? 'hidden' : 'visible'); + }; + options.thing_box = function(node, eventOptions) { + var contents = _mode.parent().content(_mode.parent().nodeContent.eval(node.datum())), + box = contents.textbox(node); + box.x += node.datum().cola.x; + box.y += node.datum().cola.y; + return box; + }; + options.thing_label = function(node) { + return _mode.parent().nodeLabel.eval(node.datum()); + }; + options.accept = function(node, text) { + var callback = _mode.changeNodeLabel() ? + _mode.changeNodeLabel()(_mode.parent().nodeKey.eval(node.datum()), text) : + Promise.resolve(text); + return callback.then(function(text2) { + var n = node.datum(); + n.orig.value[_labelTag] = text2; + _mode.parent().redrawGroup(); + }); + }; + + var _mode = dc_graph.label_things(options); + _mode.changeNodeLabel = property(null); + return _mode; +}; + +dc_graph.label_edges = function(options) { + options = options || {}; + var _labelTag = options.labelTag || 'label'; + options.select_group = options.select_group || 'select-edges-group'; + options.select_type = options.select_type || 'select-edges'; + options.label_group = options.label_group || 'label-edges-group'; + options.label_type = options.label_type || 'label-edges'; + options.default_label = "edge name"; + + options.find_thing = function(key, node, edge) { + return edge.filter(function(e) { + return _mode.parent().edgeKey.eval(e) === key; + }); + }; + options.hide_thing_label = function(edge, whether) { + var label = _mode.parent().selectAll('#' + _mode.parent().edgeId(edge.datum()) + '-label textPath'); + label.attr('visibility', whether ? 'hidden' : 'visible'); + }; + options.thing_box = function(edge, eventOptions) { + var points = edge.datum().pos.new.path.points, + x = (points[0].x + points[1].x)/2, + y = (points[0].y + points[1].y)/2; + return {x: x, y: y-10, width:0, height: 20}; + }; + options.thing_label = function(edge) { + return _mode.parent().edgeLabel.eval(edge.datum()); + }; + options.accept = function(edge, text) { + var callback = _mode.changeEdgeLabel() ? + _mode.changeEdgeLabel()(_mode.parent().edgeKey.eval(edge.datum()), text) : + Promise.resolve(text); + return callback.then(function(text2) { + var e = edge.datum(); + e.orig.value[_labelTag] = text2; + _mode.parent().redrawGroup(); + }); + }; + + var _mode = dc_graph.label_things(options); + _mode.changeEdgeLabel = property(null); + return _mode; +}; + +dc_graph.register_highlight_things_group = function(thingsgroup) { + window.chart_registry.create_type('highlight-things', function() { + return d3.dispatch('highlight'); + }); + + return window.chart_registry.create_group('highlight-things', thingsgroup); +}; + +dc_graph.highlight_things = function(includeprops, excludeprops, modename, groupname, cascbase) { + var highlight_things_group = dc_graph.register_highlight_things_group(groupname || 'highlight-things-group'); + var _active, _nodeset = {}, _edgeset = {}; + cascbase = cascbase || 150; + + function highlight(nodeset, edgeset) { + _active = nodeset || edgeset; + _nodeset = nodeset || {}; + _edgeset = edgeset || {}; + _mode.parent().requestRefresh(_mode.durationOverride()); + } + function draw(diagram) { + diagram.cascade(cascbase, true, node_edge_conditions( + function(n) { + return _nodeset[_mode.parent().nodeKey.eval(n)]; + }, function(e) { + return _edgeset[_mode.parent().edgeKey.eval(e)]; + }, includeprops)); + diagram.cascade(cascbase+10, true, node_edge_conditions( + function(n) { + return _active && !_nodeset[_mode.parent().nodeKey.eval(n)]; + }, function(e) { + return _active && !_edgeset[_mode.parent().edgeKey.eval(e)]; + }, excludeprops)); + } + function remove(diagram) { + diagram.cascade(cascbase, false, includeprops); + diagram.cascade(cascbase + 10, false, excludeprops); + } + var _mode = dc_graph.mode(modename, { + draw: draw, + remove: remove, + parent: function(p) { + highlight_things_group.on('highlight.' + modename, p ? highlight : null); + } + }); + _mode.durationOverride = property(undefined); + return _mode; +}; + +dc_graph.register_highlight_neighbors_group = function(neighborsgroup) { + window.chart_registry.create_type('highlight-neighbors', function() { + return d3.dispatch('highlight_node'); + }); + + return window.chart_registry.create_group('highlight-neighbors', neighborsgroup); +}; + +dc_graph.highlight_neighbors = function(includeprops, excludeprops, neighborsgroup, thingsgroup) { + var highlight_neighbors_group = dc_graph.register_highlight_neighbors_group(neighborsgroup || 'highlight-neighbors-group'); + var highlight_things_group = dc_graph.register_highlight_things_group(thingsgroup || 'highlight-things-group'); + + function highlight_node(nodeid) { + var diagram = _mode.parent(); + var nodeset = {}, edgeset = {}; + if(nodeid) { + nodeset[nodeid] = true; + _mode.parent().selectAllEdges().each(function(e) { + if(diagram.nodeKey.eval(e.source) === nodeid) { + edgeset[diagram.edgeKey.eval(e)] = true; + nodeset[diagram.nodeKey.eval(e.target)] = true; + } + if(diagram.nodeKey.eval(e.target) === nodeid) { + edgeset[diagram.edgeKey.eval(e)] = true; + nodeset[diagram.nodeKey.eval(e.source)] = true; + } + }); + highlight_things_group.highlight(nodeset, edgeset); + } + else highlight_things_group.highlight(null, null); + } + function draw(diagram, node, edge) { + node + .on('mouseover.highlight-neighbors', function(n) { + highlight_neighbors_group.highlight_node(_mode.parent().nodeKey.eval(n)); + }) + .on('mouseout.highlight-neighbors', function(n) { + highlight_neighbors_group.highlight_node(null); + }); + } + + function remove(diagram, node, edge) { + node + .on('mouseover.highlight-neighbors', null) + .on('mouseout.highlight-neighbors', null); + highlight_neighbors_group.highlight_node(null); + } + + var _mode = dc_graph.mode('highlight-neighbors', { + draw: draw, + remove: function(diagram, node, edge) { + remove(diagram, node, edge); + }, + parent: function(p) { + highlight_neighbors_group.on('highlight_node.highlight-neighbors', p ? highlight_node : null); + if(p && !p.child('highlight-things')) + p.child('highlight-things', + dc_graph.highlight_things(includeprops, excludeprops) + .durationOverride(_mode.durationOverride())); + } + }); + _mode.durationOverride = property(undefined); + return _mode; +}; + + +dc_graph.highlight_radius = function(options) { + options = options || {}; + var select_nodes_group = dc_graph.select_things_group(options.select_nodes_group || 'select-nodes-group', 'select-nodes'); + var highlight_things_group = dc_graph.register_highlight_things_group(options.highlight_things_group || 'highlight-things-group'); + var _graph, _selection = []; + + function recurse(n, r, nodeset, edgeset) { + nodeset[n.key()] = true; + if(r) { + n.outs().filter(function(e) { + return !edgeset[e.key()]; + }).forEach(function(e) { + edgeset[e.key()] = true; + recurse(e.target(), r-1, nodeset, edgeset); + }); + n.ins().filter(function(e) { + return !edgeset[e.key()]; + }).forEach(function(e) { + edgeset[e.key()] = true; + recurse(e.source(), r-1, nodeset, edgeset); + }); + } + } + function selection_changed(nodes) { + _selection = nodes; + console.assert(_graph); + var nodeset = {}, edgeset = {}; + nodes.forEach(function(nkey) { + recurse(_graph.node(nkey), _mode.radius(), nodeset, edgeset); + }); + if(!Object.keys(nodeset).length && !Object.keys(edgeset).length) + nodeset = edgeset = null; + highlight_things_group.highlight(nodeset, edgeset); + } + + function on_data(diagram, nodes, wnodes, edges, wedges, ports, wports) { + _graph = metagraph.graph(wnodes, wedges, { + nodeKey: diagram.nodeKey.eval, + edgeKey: diagram.edgeKey.eval, + edgeSource: diagram.edgeSource.eval, + edgeTarget: diagram.edgeTarget.eval + }); + var sel2 = _selection.filter(function(nk) { + return !!_graph.node(nk); + }); + if(sel2.length < _selection.length) + window.setTimeout(function() { + select_nodes_group.set_changed(sel2); + }, 0); + } + var _mode = { + parent: function(p) { + if(p) { + p.on('data.highlight-radius', on_data); + } else if(_mode.parent()) + _mode.parent().on('data.highlight-radius', null); + select_nodes_group.on('set_changed.highlight-radius', selection_changed); + } + }; + _mode.radius = property(1); + return _mode; +}; + +dc_graph.register_highlight_paths_group = function(pathsgroup) { + window.chart_registry.create_type('highlight-paths', function() { + return d3.dispatch('paths_changed', 'hover_changed', 'select_changed'); + }); + + return window.chart_registry.create_group('highlight-paths', pathsgroup); +}; + +dc_graph.highlight_paths = function(pathprops, hoverprops, selectprops, pathsgroup) { + var highlight_paths_group = dc_graph.register_highlight_paths_group(pathsgroup || 'highlight-paths-group'); + pathprops = pathprops || {}; + hoverprops = hoverprops || {}; + selectprops = selectprops || {}; + var node_on_paths = {}, edge_on_paths = {}, selected = null, hoverpaths = null; + var _anchor; + + function refresh() { + if(_mode.doRedraw()) + _mode.parent().relayout().redraw(); + else + _mode.parent().refresh(); + } + + function paths_changed(nop, eop) { + selected = hoverpaths = null; + // it would be difficult to check if no change, but at least check if changing from empty to empty + if(Object.keys(node_on_paths).length === 0 && Object.keys(nop).length === 0 && + Object.keys(edge_on_paths).length === 0 && Object.keys(eop).length === 0) + return; + node_on_paths = nop; + edge_on_paths = eop; + refresh(); + } + + function hover_changed(hp) { + if(hp !== hoverpaths) { + hoverpaths = hp; + refresh(); + } + } + + function select_changed(sp) { + if(sp !== selected) { + selected = sp; + refresh(); + } + } + + function clear_all_highlights() { + node_on_paths = {}; + edge_on_paths = {}; + } + + function contains_path(paths) { + return function(path) { + return paths.indexOf(path)>=0; + }; + } + + // sigh + function doesnt_contain_path(paths) { + var cp = contains_path(paths); + return function(path) { + return !cp(path); + }; + } + + function intersect_paths(pathsA, pathsB) { + if(!pathsA || !pathsB) + return false; + return pathsA.some(contains_path(pathsB)); + } + + function toggle_paths(pathsA, pathsB) { + if(!pathsA) + return pathsB; + else if(!pathsB) + return pathsA; + if(pathsB.every(contains_path(pathsA))) + return pathsA.filter(doesnt_contain_path(pathsB)); + else return pathsA.concat(pathsB.filter(doesnt_contain_path(pathsA))); + } + + function draw(diagram, node, edge, ehover) { + diagram + .cascade(200, true, node_edge_conditions(function(n) { + return !!node_on_paths[diagram.nodeKey.eval(n)]; + }, function(e) { + return !!edge_on_paths[diagram.edgeKey.eval(e)]; + }, pathprops)) + .cascade(300, true, node_edge_conditions(function(n) { + return intersect_paths(node_on_paths[diagram.nodeKey.eval(n)], selected); + }, function(e) { + return intersect_paths(edge_on_paths[diagram.edgeKey.eval(e)], selected); + }, selectprops)) + .cascade(400, true, node_edge_conditions(function(n) { + return intersect_paths(node_on_paths[diagram.nodeKey.eval(n)], hoverpaths); + }, function(e) { + return intersect_paths(edge_on_paths[diagram.edgeKey.eval(e)], hoverpaths); + }, hoverprops)); + + node + .on('mouseover.highlight-paths', function(n) { + highlight_paths_group.hover_changed(node_on_paths[diagram.nodeKey.eval(n)] || null); + }) + .on('mouseout.highlight-paths', function(n) { + highlight_paths_group.hover_changed(null); + }) + .on('click.highlight-paths', function(n) { + highlight_paths_group.select_changed(toggle_paths(selected, node_on_paths[diagram.nodeKey.eval(n)])); + }); + + + ehover + .on('mouseover.highlight-paths', function(e) { + highlight_paths_group.hover_changed(edge_on_paths[diagram.edgeKey.eval(e)] || null); + }) + .on('mouseout.highlight-paths', function(e) { + highlight_paths_group.hover_changed(null); + }) + .on('click.highlight-paths', function(n) { + highlight_paths_group.select_changed(toggle_paths(selected, edge_on_paths[diagram.nodeKey.eval(n)])); + }); + } + + function remove(diagram, node, edge, ehover) { + node + .on('mouseover.highlight-paths', null) + .on('mouseout.highlight-paths', null) + .on('click.highlight-paths', null); + ehover + .on('mouseover.highlight-paths', null) + .on('mouseout.highlight-paths', null) + .on('click.highlight-paths', null); + clear_all_highlights(); + diagram + .cascade(200, false, pathprops) + .cascade(300, false, selectprops) + .cascade(400, false, hoverprops); + } + + var _mode = dc_graph.mode('highlight-paths', { + draw: draw, + remove: function(diagram, node, edge, ehover) { + remove(diagram, node, edge, ehover); + return this; + }, + parent: function(p) { + if(p) + _anchor = p.anchorName(); + // else we should have received anchor earlier + highlight_paths_group.on('paths_changed.highlight-paths-' + _anchor, p ? paths_changed : null); + highlight_paths_group.on('hover_changed.highlight-paths-' + _anchor, p ? hover_changed : null); + highlight_paths_group.on('select_changed.highlight-paths-' + _anchor, p ? select_changed : null); + } + }); + + // whether to do relayout & redraw (true) or just refresh (false) + _mode.doRedraw = property(false); + + return _mode; +}; + + +dc_graph.spline_paths = function(pathreader, pathprops, hoverprops, selectprops, pathsgroup) { + var highlight_paths_group = dc_graph.register_highlight_paths_group(pathsgroup || 'highlight-paths-group'); + pathprops = pathprops || {}; + hoverprops = hoverprops || {}; + var _paths = null, _hoverpaths = null, _selected = null; + var _anchor; + var _layer = null; + var _savedPositions = null; + + function paths_changed(nop, eop, paths) { + _paths = paths; + + var engine = _mode.parent().layoutEngine(), + localPaths = paths.filter(pathIsPresent); + if(localPaths.length) { + var nidpaths = localPaths.map(function(lpath) { + var strength = pathreader.pathStrength.eval(lpath); + if(typeof strength !== 'number') + strength = 1; + if(_selected && _selected.indexOf(lpath) !== -1) + strength *= _mode.selectedStrength(); + return { + nodes: path_keys(lpath), + strength: strength + }; + }); + engine.paths(nidpaths); + } else { + engine.paths(null); + if(_savedPositions) + engine.restorePositions(_savedPositions); + } + if(_selected) + _selected = _selected.filter(function(p) { return localPaths.indexOf(p) !== -1; }); + _mode.parent().redraw(); + } + + function select_changed(sp) { + if(sp !== _selected) { + _selected = sp; + paths_changed(null, null, _paths); + } + } + + function path_keys(path, unique) { + unique = unique !== false; + var keys = pathreader.elementList.eval(path).filter(function(elem) { + return pathreader.elementType.eval(elem) === 'node'; + }).map(function(elem) { + return pathreader.nodeKey.eval(elem); + }); + return unique ? uniq(keys) : keys; + } + + // check if entire path is present in this view + function pathIsPresent(path) { + return pathreader.elementList.eval(path).every(function(element) { + return pathreader.elementType.eval(element) !== 'node' || + _mode.parent().getWholeNode(pathreader.nodeKey.eval(element)); + }); + } + + // get the positions of nodes on path + function getNodePositions(path, old) { + return path_keys(path, false).map(function(key) { + var node = _mode.parent().getWholeNode(key); + return {x: old && node.prevX !== undefined ? node.prevX : node.cola.x, + y: old && node.prevY !== undefined ? node.prevY : node.cola.y}; + }); + }; + + // insert fake nodes to avoid sharp turns + function insertDummyNodes(path_coord) { + function _distance(node1, node2) { + return Math.sqrt(Math.pow((node1.x-node2.x),2) + Math.pow((node1.y-node2.y),2)); + } + + var new_path_coord = []; + + for(var i = 0; i < path_coord.length; i ++) { + if (i-1 >= 0 && i+1 < path_coord.length) { + if (path_coord[i-1].x === path_coord[i+1].x && + path_coord[i-1].y === path_coord[i+1].y ) { + // insert node when the previous and next nodes are the same + var x1 = path_coord[i-1].x, y1 = path_coord[i-1].y; + var x2 = path_coord[i].x, y2 = path_coord[i].y; + var dx = x1 - x2, dy = y1 - y2; + + var v1 = dy / Math.sqrt(dx*dx + dy*dy); + var v2 = - dx / Math.sqrt(dx*dx + dy*dy); + + var insert_p1 = {'x': null, 'y': null}; + var insert_p2 = {'x': null, 'y': null}; + + var offset = 10; + + insert_p1.x = (x1+x2)/2.0 + offset*v1; + insert_p1.y = (y1+y2)/2.0 + offset*v2; + + insert_p2.x = (x1+x2)/2.0 - offset*v1; + insert_p2.y = (y1+y2)/2.0 - offset*v2; + + new_path_coord.push(insert_p1); + new_path_coord.push(path_coord[i]); + new_path_coord.push(insert_p2); + } else if (_distance(path_coord[i-1], path_coord[i+1]) < pathprops.nearNodesDistance){ + // insert node when the previous and next nodes are very close + // first node + var x1 = path_coord[i-1].x, y1 = path_coord[i-1].y; + var x2 = path_coord[i].x, y2 = path_coord[i].y; + var dx = x1 - x2, dy = y1 - y2; + + var v1 = dy / Math.sqrt(dx*dx + dy*dy); + var v2 = - dx / Math.sqrt(dx*dx + dy*dy); + + var insert_p1 = {'x': null, 'y': null}; + + var offset = 10; + + insert_p1.x = (x1+x2)/2.0 + offset*v1; + insert_p1.y = (y1+y2)/2.0 + offset*v2; + + // second node + x1 = path_coord[i].x; + y1 = path_coord[i].y; + x2 = path_coord[i+1].x; + y2 = path_coord[i+1].y; + dx = x1 - x2; + dy = y1 - y2; + + v1 = dy / Math.sqrt(dx*dx + dy*dy); + v2 = - dx / Math.sqrt(dx*dx + dy*dy); + + var insert_p2 = {'x': null, 'y': null}; + + insert_p2.x = (x1+x2)/2.0 + offset*v1; + insert_p2.y = (y1+y2)/2.0 + offset*v2; + + new_path_coord.push(insert_p1); + new_path_coord.push(path_coord[i]); + new_path_coord.push(insert_p2); + + } + else { + new_path_coord.push(path_coord[i]); + } + } else { + new_path_coord.push(path_coord[i]); + } + } + return new_path_coord; + } + + // helper functions + var vecDot = function(v0, v1) { return v0.x*v1.x+v0.y*v1.y; }; + var vecMag = function(v) { return Math.sqrt(v.x*v.x + v.y*v.y); }; + var l2Dist = function(p1, p2) { + return Math.sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y)); + }; + + function drawCardinalSpline(points, lineTension, avoidSharpTurn, angleThreshold) { + var c = lineTension || 0; + avoidSharpTurn = avoidSharpTurn !== false; + angleThreshold = angleThreshold || 0.02; + + // get the path without self loops + var path_list = [points[0]]; + for(var i = 1; i < points.length; i ++) { + if(l2Dist(points[i], path_list[path_list.length-1]) > 1e-6) { + path_list.push(points[i]); + } + } + + // repeat first and last node + points = [path_list[0]]; + points = points.concat(path_list); + points.push(path_list[path_list.length-1]); + + // a segment is a list of three points: [c0, c1, p1], + // representing the coordinates in "C x0,y0,x1,y1,x,y" in svg:path + var segments = []; // control points + for(var i = 1; i < points.length-2; i ++) { + // generate svg:path + var m_0_x = (1-c)*(points[i+1].x - points[i-1].x)/2; + var m_0_y = (1-c)*(points[i+1].y - points[i-1].y)/2; + + var m_1_x = (1-c)*(points[i+2].x - points[i].x)/2; + var m_1_y = (1-c)*(points[i+2].y - points[i].y)/2; + + var p0 = points[i]; + var p1 = points[i+1]; + var c0 = p0; + if(i !== 1) { + c0 = {x: p0.x+(m_0_x/3), y:p0.y+(m_0_y/3)}; + } + var c1 = p1; + if(i !== points.length-3) { + c1 = {x: p1.x-(m_1_x/3), y:p1.y-(m_1_y/3)}; + } + + // detect special case by calculating the angle + if(avoidSharpTurn) { + var v0 = {x:points[i-1].x - points[i].x, y:points[i-1].y - points[i].y}; + var v1 = {x:points[i+1].x - points[i].x, y:points[i+1].y - points[i].y}; + var acosValue = vecDot(v0,v1) / (vecMag(v0)*vecMag(v1)); + acosValue = Math.max(-1, Math.min(1, acosValue)); + var angle = Math.acos( acosValue ); + + if(angle <= angleThreshold ){ + var m_x = (1-c)*(points[i].x - points[i-1].x)/2; + var m_y = (1-c)*(points[i].y - points[i-1].y)/2; + var k = 2; + + var cp1 = {x: p0.x+k*(-m_y/3), y:p0.y+k*(m_x/3)}; + var cp2 = {x: p0.x-k*(-m_y/3), y:p0.y-k*(m_x/3)}; + // CP_1CP_2 + var vCP = {x: cp1.x-cp2.x, y:cp1.y-cp2.y}; // vector cp1->cp2 + var vPN = {x: points[i-2].x - points[i+2].x, y:points[i-2].y-points[i+2].y}; // vector Previous->Next + if(vecDot(vCP, vPN) > 0) { + c0 = cp1; + segments[segments.length-1][1] = cp2; + } else { + c0 = cp2; + segments[segments.length-1][1] = cp1; + } + } + } + + segments.push([c0,c1,p1]); + } + + var path_d = "M"+points[0].x+","+points[0].y; + for(var i = 0; i < segments.length; i ++) { + var s = segments[i]; + path_d += "C"+s[0].x+","+s[0].y; + path_d += ","+s[1].x+","+s[1].y; + path_d += ","+s[2].x+","+s[2].y; + } + return path_d; + } + + function drawDedicatedLoops(points, lineTension, avoidSharpTurn, angleThreshold) { + // get loops as segments + var p1 = 0, p2 = 1; + var seg_list = []; // (start, end) + while(p1 < points.length-1 && p2 < points.length) { + if(l2Dist(points[p1], points[p2]) < 1e-6) { + var repeated = points[p2]; + while(p2 < points.length && l2Dist(points[p2], repeated) < 1e-6) p2++; + seg_list.push({'start': Math.max(0, p1-1), 'end': Math.min(points.length-1, p2)}); + p1 = p2; + p2 = p1+1; + } else { + p1++; + p2++; + } + } + + var loopCurves = ""; + for(var i = 0; i < seg_list.length; i ++) { + var segment = seg_list[i]; + var loopCount = segment.end - segment.start - 2; + var anchorPoint = points[segment.start+1]; + + // the vector from previous node to next node + var vec_pre_next = { + x: points[segment.end].x-points[segment.start].x, + y: points[segment.end].y-points[segment.start].y + }; + + // when previous node and next node are the same node, we need to handle + // them differently. + // e.g. for a loop segment A->B->B->A, we use the perpendicular vector perp_AB + // instead of vector AA(which is vec_pre_next in this case). + if(vecMag(vec_pre_next) == 0) { + vec_pre_next = { + x: -(points[segment.end].y-anchorPoint.y), + y: points[segment.end].x-anchorPoint.x + }; + } + + // unit length vector + var vec_pre_next_unit = { + x: vec_pre_next.x / vecMag(vec_pre_next), + y: vec_pre_next.y / vecMag(vec_pre_next) + }; + var vec_pre_next_perp = { + x: -vec_pre_next.y / vecMag(vec_pre_next), + y: vec_pre_next.x / vecMag(vec_pre_next) + }; + + var insertP; + for(var j = 0; j < loopCount; j ++) { + var c1,c2,c3,c4; + + // change the control points every time this loop appears + var cp_k = 15+2*j; + + // calculate c1 and c4, their tangent match the tangent at anchorPoint + c1 = { + x: anchorPoint.x + cp_k*vec_pre_next_unit.x, + y: anchorPoint.y + cp_k*vec_pre_next_unit.y + }; + + c4 = { + x: anchorPoint.x - cp_k*vec_pre_next_unit.x, + y: anchorPoint.y - cp_k*vec_pre_next_unit.y + }; + + // change the location of inserted virtual point every time this loop appears + var control_k = 25+5*j; + var insertP1 = { + x: anchorPoint.x+vec_pre_next_perp.x*control_k, + y: anchorPoint.y+vec_pre_next_perp.y*control_k + }; + var insertP2 = { + x: anchorPoint.x-vec_pre_next_perp.x*control_k, + y: anchorPoint.y-vec_pre_next_perp.y*control_k + }; + var vec_i_to_next = { + x: points[segment.end].x - anchorPoint.x, + y: points[segment.end].y - anchorPoint.y + }; + var vec_i_to_insert = { + x: insertP1.x - anchorPoint.x, + y: insertP1.y - anchorPoint.y + }; + insertP = insertP1; + if(vecDot(vec_i_to_insert, vec_i_to_next) > 0) { + insertP = insertP2; + } + + // calculate c2 and c3 based on insertP + c2 = { + x: insertP.x + cp_k*vec_pre_next_unit.x, + y: insertP.y + cp_k*vec_pre_next_unit.y + }; + + c3 = { + x: insertP.x - cp_k*vec_pre_next_unit.x, + y: insertP.y - cp_k*vec_pre_next_unit.y + }; + + var curve = "M"+anchorPoint.x+","+anchorPoint.y; + curve += "C"+c1.x+","+c1.y+","+c2.x+","+c2.y+","+insertP.x+","+insertP.y; + curve += "C"+c3.x+","+c3.y+","+c4.x+","+c4.y+","+anchorPoint.x+","+anchorPoint.y; + + loopCurves += curve; + } + } + return loopCurves; + } + + // convert original path data into <d> + function genPath(originalPoints, old, lineTension, avoidSharpTurn, angleThreshold) { + // get coordinates + var path_coord = getNodePositions(originalPoints, old); + if(path_coord.length < 2) return ""; + + var result = ""; + // process the points and treat them differently: + // 1. sub-path without self loop + result += drawCardinalSpline(path_coord, lineTension, avoidSharpTurn, angleThreshold); + + // 2. a list of loop segments + result += drawDedicatedLoops(path_coord, lineTension, avoidSharpTurn, angleThreshold); + + return result; + } + + // draw the spline for paths + function drawSpline(paths) { + if(paths === null) { + _savedPositions = _mode.parent().layoutEngine().savePositions(); + return; + } + + paths = paths.filter(pathIsPresent); + var hoverpaths = _hoverpaths || [], + selected = _selected || []; + + // edge spline + var edge = _layer.selectAll(".spline-edge").data(paths, function(path) { return path_keys(path).join(','); }); + edge.exit().remove(); + var edgeEnter = edge.enter().append("svg:path") + .attr('class', 'spline-edge') + .attr('id', function(d, i) { return "spline-path-"+i; }) + .attr('stroke-width', pathprops.edgeStrokeWidth || 1) + .attr('fill', 'none') + .attr('d', function(d) { return genPath(d, true, pathprops.lineTension, _mode.avoidSharpTurns()); }); + edge + .attr('stroke', function(p) { + return selected.indexOf(p) !== -1 && selectprops.edgeStroke || + hoverpaths.indexOf(p) !== -1 && hoverprops.edgeStroke || + pathprops.edgeStroke || 'black'; + }) + .attr('opacity', function(p) { + return selected.indexOf(p) !== -1 && selectprops.edgeOpacity || + hoverpaths.indexOf(p) !== -1 && hoverprops.edgeOpacity || + pathprops.edgeOpacity || 1; + }); + function path_order(p) { + return hoverpaths.indexOf(p) !== -1 ? 2 : + selected.indexOf(p) !== -1 ? 1 : + 0; + } + edge.sort(function(a, b) { + return path_order(a) - path_order(b); + }); + _layer.selectAll('.spline-edge-hover') + .each(function() {this.parentNode.appendChild(this);}); + edge.transition().duration(_mode.parent().transitionDuration()) + .attr('d', function(d) { return genPath(d, false, pathprops.lineTension, _mode.avoidSharpTurns()); }); + + // another wider copy of the edge just for hover events + var edgeHover = _layer.selectAll('.spline-edge-hover') + .data(paths, function(path) { return path_keys(path).join(','); }); + edgeHover.exit().remove(); + var edgeHoverEnter = edgeHover.enter().append('svg:path') + .attr('class', 'spline-edge-hover') + .attr('d', function(d) { return genPath(d, true, pathprops.lineTension, _mode.avoidSharpTurns()); }) + .attr('opacity', 0) + .attr('stroke', 'green') + .attr('stroke-width', (pathprops.edgeStrokeWidth || 1) + 4) + .attr('fill', 'none') + .on('mouseover.spline-paths', function(d) { + highlight_paths_group.hover_changed([d]); + }) + .on('mouseout.spline-paths', function(d) { + highlight_paths_group.hover_changed(null); + }) + .on('click.spline-paths', function(d) { + var selected = _selected && _selected.slice(0) || [], + i = selected.indexOf(d); + if(i !== -1) + selected.splice(i, 1); + else if(d3.event.shiftKey) + selected.push(d); + else + selected = [d]; + highlight_paths_group.select_changed(selected); + }); + edgeHover.transition().duration(_mode.parent().transitionDuration()) + .attr('d', function(d) { return genPath(d, false, pathprops.lineTension, _mode.avoidSharpTurns()); }); + }; + + function draw(diagram, node, edge, ehover) { + _layer = _mode.parent().select('g.draw').selectAll('g.spline-layer').data([0]); + _layer.enter().append('g').attr('class', 'spline-layer'); + + drawSpline(_paths); + } + + function remove(diagram, node, edge, ehover) { + } + + var _mode = dc_graph.mode('draw-spline-paths', { + laterDraw: true, + draw: draw, + remove: function(diagram, node, edge, ehover) { + remove(diagram, node, edge, ehover); + return this; + }, + parent: function(p) { + if(p) + _anchor = p.anchorName(); + highlight_paths_group + .on('paths_changed.draw-spline-paths-' + _anchor, p ? paths_changed : null) + .on('select_changed.draw-spline-paths-' + _anchor, p ? select_changed : null) + .on('hover_changed.draw-spline-paths-' + _anchor, p ? function(hpaths) { + _hoverpaths = hpaths; + drawSpline(_paths); + } : null); + } + }); + _mode.selectedStrength = property(1); + _mode.avoidSharpTurns = property(true); + + return _mode; +}; + +dc_graph.draw_spline_paths = deprecate_function("draw_spline_paths has been renamed spline_paths, please update", dc_graph.spline_paths); + +dc_graph.draw_clusters = function() { + + function apply_bounds(rect) { + rect.attr({ + x: function(c) { + return c.cola.bounds.left; + }, + y: function(c) { + return c.cola.bounds.top; + }, + width: function(c) { + return c.cola.bounds.right - c.cola.bounds.left; + }, + height: function(c) { + return c.cola.bounds.bottom - c.cola.bounds.top; + } + }); + } + function draw(diagram) { + if(!diagram.clusterGroup()) + return; + var clayer = diagram.g().selectAll('g.cluster-layer').data([0]); + clayer.enter().insert('g', ':first-child') + .attr('class', 'cluster-layer'); + var clusters = diagram.clusterGroup().all().map(function(kv) { + return _mode.parent().getWholeCluster(kv.key); + }).filter(function(c) { + return c && c.cola.bounds; + }); + var rects = clayer.selectAll('rect.cluster') + .data(clusters, function(c) { return c.orig.key; }); + rects.exit().remove(); + rects.enter().append('rect') + .attr({ + class: 'cluster', + opacity: 0, + stroke: _mode.clusterStroke.eval, + 'stroke-width': _mode.clusterStrokeWidth.eval, + fill: function(c) { + return _mode.clusterFill.eval(c) || 'none'; + } + }) + .call(apply_bounds); + rects.transition() + .duration(_mode.parent().stagedDuration()) + .attr('opacity', _mode.clusterOpacity.eval) + .call(apply_bounds); + } + function remove(diagram, node, edge, ehover) { + } + var _mode = dc_graph.mode('draw-clusters', { + laterDraw: true, + draw: draw, + remove: remove + }); + _mode.clusterOpacity = property(0.25); + _mode.clusterStroke = property('black'); + _mode.clusterStrokeWidth = property(1); + _mode.clusterFill = property(null); + _mode.clusterLabel = property(null); + _mode.clusterLabelFill = property('black'); + _mode.clusterLabelAlignment = property(['bottom','right']); + + return _mode; +}; + + +dc_graph.expand_collapse = function(options) { + if(typeof options === 'function') { + options = { + get_degree: arguments[0], + expand: arguments[1], + collapse: arguments[2], + dirs: arguments[3] + }; + } + var _keyboard, _overNode, _overDir, _overEdge, _expanded = {}; + var expanded_highlight_group = dc_graph.register_highlight_things_group(options.expanded_highlight_group || 'expanded-highlight-group'); + var collapse_highlight_group = dc_graph.register_highlight_things_group(options.collapse_highlight_group || 'collapse-highlight-group'); + var hide_highlight_group = dc_graph.register_highlight_things_group(options.hide_highlight_group || 'hide-highlight-group'); + options.dirs = options.dirs || ['both']; + options.dirs.forEach(function(dir) { + _expanded[dir] = {}; + }); + options.hideKey = options.hideKey || 'Alt'; + options.linkKey = options.linkKey || (is_a_mac ? 'Meta' : 'Control'); + if(options.dirs.length > 2) + throw new Error('there are only two directions to expand in'); + + var _gradients_added = {}; + function add_gradient_def(color, diagram) { + if(_gradients_added[color]) + return; + _gradients_added[color] = true; + diagram.addOrRemoveDef('spike-gradient-' + color, true, 'linearGradient', function(gradient) { + gradient.attr({ + x1: '0%', + y1: '0%', + x2: '100%', + y2: '0%', + spreadMethod: 'pad' + }); + gradient.selectAll('stop').data([[0, color, 1], [100, color, '0']]) + .enter().append('stop').attr({ + offset: function(d) { + return d[0] + '%'; + }, + 'stop-color': function(d) { + return d[1]; + }, + 'stop-opacity': function(d) { + return d[2]; + } + }); + }); + } + + function visible_edges(diagram, edge, dir, key) { + var fil; + switch(dir) { + case 'out': + fil = function(e) { + return diagram.edgeSource.eval(e) === key; + }; + break; + case 'in': + fil = function(e) { + return diagram.edgeTarget.eval(e) === key; + }; + break; + case 'both': + fil = function(e) { + return diagram.edgeSource.eval(e) === key || diagram.edgeTarget.eval(e) === key; + }; + break; + } + return edge.filter(fil).data(); + } + + function spike_directioner(rankdir, dir, N) { + if(dir==='both') + return function(i) { + return Math.PI * (2 * i / N - 0.5); + }; + else { + var sweep = (N-1)*Math.PI/N, ofs; + switch(rankdir) { + case 'LR': + ofs = 0; + break; + case 'TB': + ofs = Math.PI/2; + break; + case 'RL': + ofs = Math.PI; + break; + case 'BT': + ofs = -Math.PI/2; + break; + } + if(dir === 'in') + ofs += Math.PI; + return function(i) { + return ofs + sweep * (-.5 + (N > 1 ? i / (N-1) : 0)); // avoid 0/0 + }; + } + } + + function draw_stubs(diagram, node, edge, n, spikes) { + if(n && _expanded[spikes.dir][diagram.nodeKey.eval(n)]) + spikes = null; + var spike = node + .selectAll('g.spikes') + .data(function(n2) { + return spikes && n === n2 ? + [n2] : []; + }); + spike.exit().remove(); + spike + .enter().insert('g', ':first-child') + .classed('spikes', true); + var rect = spike + .selectAll('rect.spike') + .data(function(n) { + var key = diagram.nodeKey.eval(n); + var dir = spikes.dir, + N = spikes.n, + af = spike_directioner(diagram.layoutEngine().rankdir(), dir, N), + ret = Array(N); + for(var i = 0; i<N; ++i) { + var a = af(i); + ret[i] = { + a: a * 180 / Math.PI, + x: Math.cos(a) * n.dcg_rx*.9, + y: Math.sin(a) * n.dcg_ry*.9, + edge: spikes.invisible ? spikes.invisible[i] : null + }; + } + return ret; + }); + rect + .enter().append('rect') + .classed('spike', true) + .attr({ + width: 25, + height: 3, + fill: function(s) { + var color = s.edge ? dc_graph.functor_wrap(diagram.edgeStroke())(s.edge) : 'black'; + add_gradient_def(color, diagram); + return 'url(#spike-gradient-' + color + ')'; + }, + rx: 1, + ry: 1, + x: 0, + y: 0 + }); + rect.attr('transform', function(d) { + return 'translate(' + d.x + ',' + d.y + ') rotate(' + d.a + ')'; + }); + rect.exit().remove(); + } + + function clear_stubs(diagram, node, edge) { + draw_stubs(diagram, node, edge, null, null); + } + + function zonedir(diagram, event, dirs, n) { + if(dirs.length === 1) // we assume it's ['out', 'in'] + return dirs[0]; + var bound = diagram.root().node().getBoundingClientRect(); + var invert = diagram.invertCoord([event.clientX - bound.left,event.clientY - bound.top]), + x = invert[0], + y = invert[1]; + switch(diagram.layoutEngine().rankdir()) { + case 'TB': + return y > n.cola.y ? 'out' : 'in'; + case 'BT': + return y < n.cola.y ? 'out' : 'in'; + case 'LR': + return x > n.cola.x ? 'out' : 'in'; + case 'RL': + return x < n.cola.x ? 'out' : 'in'; + } + throw new Error('unknown rankdir ' + diagram.layoutEngine().rankdir()); + } + + function detect_key(key) { + switch(key) { + case 'Alt': + return d3.event.altKey; + case 'Meta': + return d3.event.metaKey; + case 'Shift': + return d3.event.shiftKey; + case 'Control': + return d3.event.ctrlKey; + } + return false; + } + + function highlight_hiding_node(diagram, n, edge) { + var nk = diagram.nodeKey.eval(n); + var hide_nodes_set = {}, hide_edges_set = {}; + hide_nodes_set[nk] = true; + edge.each(function(e) { + if(diagram.edgeSource.eval(e) === nk || diagram.edgeTarget.eval(e) === nk) + hide_edges_set[diagram.edgeKey.eval(e)] = true; + }); + hide_highlight_group.highlight(hide_nodes_set, hide_edges_set); + } + function highlight_hiding_edge(diagram, e) { + var hide_edges_set = {}; + hide_edges_set[diagram.edgeKey.eval(e)] = true; + hide_highlight_group.highlight({}, hide_edges_set); + } + + function highlight_collapse(diagram, n, node, edge, dir) { + var nk = diagram.nodeKey.eval(n); + var p; + if(options.get_edges) + p = Promise.resolve(options.get_edges(nk, dir)); + else + p = Promise.resolve(options.get_degree(nk, dir)); + p.then(function(de) { + var degree, edges; + if(typeof de === 'number') + degree = de; + else { + edges = de; + degree = edges.length; + } + var spikes = { + dir: dir, + visible: visible_edges(diagram, edge, dir, nk) + }; + spikes.n = Math.max(0, degree - spikes.visible.length); // be tolerant of inconsistencies + if(edges) { + var shown = spikes.visible.reduce(function(p, e) { + p[diagram.edgeKey.eval(e)] = true; + return p; + }, {}); + spikes.invisible = edges.filter(function(e) { return !shown[diagram.edgeKey()(e)]; }); + } + draw_stubs(diagram, node, edge, n, spikes); + var collapse_nodes_set = {}, collapse_edges_set = {}; + if(_expanded[dir][nk] && options.collapsibles) { + var clps = options.collapsibles(nk, dir); + collapse_nodes_set = clps.nodes; + collapse_edges_set = clps.edges; + } + collapse_highlight_group.highlight(collapse_nodes_set, collapse_edges_set); + }); + } + + function draw(diagram, node, edge, ehover) { + function over_node(n) { + var dir = zonedir(diagram, d3.event, options.dirs, n); + _overNode = n; + _overDir = dir; + if(options.hideNode && detect_key(options.hideKey)) + highlight_hiding_node(diagram, n, edge); + else if(_mode.nodeURL.eval(_overNode) && detect_key(options.linkKey)) { + diagram.selectAllNodes() + .filter(function(n) { + return n === _overNode; + }).attr('cursor', 'pointer'); + diagram.requestRefresh(0); + } + else + highlight_collapse(diagram, n, node, edge, dir); + } + function leave_node(n) { + diagram.selectAllNodes() + .filter(function(n) { + return n === _overNode; + }).attr('cursor', null); + _overNode = null; + clear_stubs(diagram, node, edge); + collapse_highlight_group.highlight({}, {}); + hide_highlight_group.highlight({}, {}); + } + function click_node(n) { + var nk = diagram.nodeKey.eval(n); + if(options.hideNode && detect_key(options.hideKey)) + options.hideNode(nk); + else if(detect_key(options.linkKey)) { + if(_mode.nodeURL.eval(n) && _mode.urlOpener) + _mode.urlOpener()(_mode, n, _mode.nodeURL.eval(n)); + } else { + clear_stubs(diagram, node, edge); + var dir = zonedir(diagram, d3.event, options.dirs, n); + expand(dir, nk, !_expanded[dir][nk]); + } + } + + function enter_edge(e) { + _overEdge = e; + if(options.hideEdge && detect_key(options.hideKey)) + highlight_hiding_edge(diagram, e); + } + function leave_edge(e) { + _overEdge = null; + hide_highlight_group.highlight({}, {}); + } + function click_edge(e) { + if(options.hideEdge && detect_key(options.hideKey)) + options.hideEdge(diagram.edgeKey.eval(e)); + } + + node + .on('mouseenter.expand-collapse', over_node) + .on('mousemove.expand-collapse', over_node) + .on('mouseout.expand-collapse', leave_node) + .on('click.expand-collapse', click_node) + .on('dblclick.expand-collapse', click_node); + + ehover + .on('mouseenter.expand-collapse', enter_edge) + .on('mouseout.expand-collapse', leave_edge) + .on('click.expand-collapse', click_edge); + + _keyboard + .on('keydown.expand-collapse', function() { + if(d3.event.key === options.hideKey && (_overNode && options.hideNode || _overEdge && options.hideEdge)) { + if(_overNode) + highlight_hiding_node(diagram, _overNode, edge); + if(_overEdge) + highlight_hiding_edge(diagram, _overEdge); + clear_stubs(diagram, node, edge); + collapse_highlight_group.highlight({}, {}); + } + else if(d3.event.key === options.linkKey && _overNode) { + if(_overNode && _mode.nodeURL.eval(_overNode)) { + diagram.selectAllNodes() + .filter(function(n) { + return n === _overNode; + }).attr('cursor', 'pointer'); + } + hide_highlight_group.highlight({}, {}); + clear_stubs(diagram, node, edge); + collapse_highlight_group.highlight({}, {}); + } + }) + .on('keyup.expand_collapse', function() { + if((d3.event.key === options.hideKey || d3.event.key === options.linkKey) && (_overNode || _overEdge)) { + hide_highlight_group.highlight({}, {}); + if(_overNode) { + highlight_collapse(diagram, _overNode, node, edge, _overDir); + if(_mode.nodeURL.eval(_overNode)) { + diagram.selectAllNodes() + .filter(function(n) { + return n === _overNode; + }).attr('cursor', null); + } + } + } + }); + diagram.cascade(97, true, conditional_properties( + function(n) { + return n === _overNode && n.orig.value.value && n.orig.value.value.URL; + }, + { + nodeLabelDecoration: 'underline' + } + )); + } + + function remove(diagram, node, edge, ehover) { + node + .on('mouseenter.expand-collapse', null) + .on('mousemove.expand-collapse', null) + .on('mouseout.expand-collapse', null) + .on('click.expand-collapse', null) + .on('dblclick.expand-collapse', null); + ehover + .on('mouseenter.expand-collapse', null) + .on('mouseout.expand-collapse', null) + .on('click.expand-collapse', null); + clear_stubs(diagram, node, edge); + } + + function expand(dir, nk, whether) { + if(dir === 'both' && !_expanded.both) + options.dirs.forEach(function(dir2) { + _expanded[dir2][nk] = whether; + }); + else + _expanded[dir][nk] = whether; + var bothmap; + if(_expanded.both) + bothmap = _expanded.both; + else { + bothmap = Object.keys(_expanded.in).filter(function(nk2) { + return _expanded.in[nk2] && _expanded.out[nk2]; + }).reduce(function(p, v) { + p[v] = true; + return p; + }, {}); + } + expanded_highlight_group.highlight(bothmap, {}); + if(dir === 'both' && !_expanded.both) + options.dirs.forEach(function(dir2, i) { + if(whether) + options.expand(nk, dir2, i !== options.dirs.length-1); + else + options.collapse(nk, dir2, i !== options.dirs.length-1); + }); + else { + if(whether) + options.expand(nk, dir); + else + options.collapse(nk, dir); + } + } + + function expandNodes(nks) { + var map = nks.reduce(function(p, v) { + p[v] = true; + return p; + }, {}); + options.dirs.forEach(function(dir) { + _expanded[dir] = Object.assign({}, map); + }); + expanded_highlight_group.highlight(map, {}); + options.expandedNodes(map); + } + + var _mode = dc_graph.mode('expand-collapse', { + draw: draw, + remove: remove, + parent: function(p) { + if(p) { + _keyboard = p.child('keyboard'); + if(!_keyboard) + p.child('keyboard', _keyboard = dc_graph.keyboard()); + } + } + }); + + _mode.expand = expand; + _mode.expandNodes = expandNodes; + _mode.clickableLinks = deprecated_property("warning - clickableLinks doesn't belong in collapse_expand and will be moved", false); + _mode.nodeURL = property(function(n) { + return n.value && n.value.value && n.value.value.URL; + }); + _mode.urlTargetWindow = property('dcgraphlink'); + _mode.urlOpener = property(dc_graph.expand_collapse.default_url_opener); + return _mode; +}; + +dc_graph.expand_collapse.default_url_opener = function(mode, node, url) { + window.open(mode.nodeURL.eval(node), mode.urlTargetWindow()); +}; + +dc_graph.expand_collapse.shown_hidden = function(opts) { + var options = Object.assign({ + nodeKey: function(n) { return n.key; }, // this one is raw rows, others are post-crossfilter-group + edgeKey: function(e) { return e.key; }, + edgeSource: function(e) { return e.value.source; }, + edgeTarget: function(e) { return e.value.target; } + }, opts); + var _nodeShown = {}, _nodeHidden = {}; + + // independent dimension on keys so that the diagram dimension will observe it + var _filter = options.nodeCrossfilter.dimension(options.nodeKey); + function apply_filter() { + _filter.filterFunction(function(nk) { + return _nodeShown[nk]; + }); + } + function adjacent_edges(nk) { + return options.edgeGroup.all().filter(function(e) { + return options.edgeSource(e) === nk || options.edgeTarget(e) === nk; + }); + } + function adjacent_nodes(nk) { + return adjacent_edges(nk).map(function(e) { + return options.edgeSource(e) === nk ? options.edgeTarget(e) : options.edgeSource(e); + }); + } + function adjacencies(nk) { + return adjacent_edges(nk).map(function(e) { + return options.edgeSource(e) === nk ? [e,options.edgeTarget(e)] : [e,options.edgeSource(e)]; + }); + } + function out_edges(nk) { + return options.edgeGroup.all().filter(function(e) { + return options.edgeSource(e) === nk; + }); + } + function in_edges(nk) { + return options.edgeGroup.all().filter(function(e) { + return options.edgeTarget(e) === nk; + }); + } + function is_collapsible(n1, n2) { + return options.edgeGroup.all().every(function(e2) { + var n3; + if(options.edgeSource(e2) === n2) + n3 = options.edgeTarget(e2); + else if(options.edgeTarget(e2) === n2) + n3 = options.edgeSource(e2); + return !n3 || n3 === n1 || !_nodeShown[n3]; + }); + } + apply_filter(); + var _strategy = {}; + if(options.directional) + Object.assign(_strategy, { + get_degree: function(nk, dir) { + switch(dir) { + case 'out': return out_edges(nk).length; + case 'in': return in_edges(nk).length; + default: throw new Error('unknown direction ' + dir); + } + }, + expand: function(nk, dir, skip_draw) { + _nodeShown[nk] = true; + switch(dir) { + case 'out': + out_edges(nk).forEach(function(e) { + if(!_nodeHidden[options.edgeTarget(e)]) + _nodeShown[options.edgeTarget(e)] = true; + }); + break; + case 'in': + in_edges(nk).forEach(function(e) { + if(!_nodeHidden[options.edgeSource(e)]) + _nodeShown[options.edgeSource(e)] = true; + }); + break; + default: throw new Error('unknown direction ' + dir); + } + if(!skip_draw) { + apply_filter(); + dc.redrawAll(); + } + }, + expandedNodes: function(_) { + if(!arguments.length) + throw new Error('not supported'); // should not be called + var that = this; + _nodeShown = {}; + Object.keys(_).forEach(function(nk) { + that.expand(nk, 'out', true); + that.expand(nk, 'in', true); + }); + apply_filter(); + dc.redrawAll(); + return this; + }, + collapsibles: function(nk, dir) { + var nodes = {}, edges = {}; + (dir === 'out' ? out_edges(nk) : in_edges(nk)).forEach(function(e) { + var n2 = dir === 'out' ? options.edgeTarget(e) : options.edgeSource(e); + if(is_collapsible(nk, n2)) { + nodes[n2] = true; + adjacent_edges(n2).forEach(function(e) { + edges[options.edgeKey(e)] = true; + }); + } + }); + return {nodes: nodes, edges: edges}; + }, + collapse: function(nk, dir) { + Object.keys(this.collapsibles(nk, dir).nodes).forEach(function(nk) { + _nodeShown[nk] = false; + }); + apply_filter(); + dc.redrawAll(); + }, + hideNode: function(nk) { + _nodeHidden[nk] = true; + _nodeShown[nk] = false; + apply_filter(); + dc.redrawAll(); + }, + dirs: ['out', 'in'] + }); + else + Object.assign(_strategy, { + get_degree: function(nk) { + return adjacent_edges(nk).length; + }, + expand: function(nk) { + _nodeShown[nk] = true; + adjacent_nodes(nk).forEach(function(nk) { + if(!_nodeHidden[nk]) + _nodeShown[nk] = true; + }); + apply_filter(); + dc.redrawAll(); + }, + expandedNodes: function(_) { + if(!arguments.length) + throw new Error('not supported'); // should not be called + var that = this; + _nodeShown = {}; + Object.keys(_).forEach(function(nk) { + that.expand(nk); + }); + return this; + }, + collapsibles: function(nk, dir) { + var nodes = {}, edges = {}; + adjacencies(nk).forEach(function(adj) { + var e = adj[0], n2 = adj[1]; + if(is_collapsible(nk, n2)) { + nodes[n2] = true; + edges[options.edgeKey(e)] = true; + } + }); + return {nodes: nodes, edges: edges}; + }, + collapse: function(nk, dir) { + Object.keys(_strategy.collapsibles(nk, dir).nodes).forEach(function(nk) { + _nodeShown[nk] = false; + }); + apply_filter(); + dc.redrawAll(); + }, + hideNode: function(nk) { + _nodeHidden[nk] = true; + _nodeShown[nk] = false; + apply_filter(); + dc.redrawAll(); + } + }); + return _strategy; +}; + +dc_graph.expand_collapse.expanded_hidden = function(opts) { + var options = Object.assign({ + nodeKey: function(n) { return n.key; }, + edgeKey: function(e) { return e.key; }, + edgeSource: function(e) { return e.value.source; }, + edgeTarget: function(e) { return e.value.target; } + }, opts); + var _nodeExpanded = {}, _nodeHidden = {}, _edgeHidden = {}; + + // independent dimension on keys so that the diagram dimension will observe it + var _nodeDim = options.nodeCrossfilter.dimension(options.nodeKey), + _edgeDim = options.edgeCrossfilter && options.edgeCrossfilter.dimension(options.edgeRawKey); + + function get_shown(expanded) { + return Object.keys(expanded).reduce(function(p, nk) { + p[nk] = true; + adjacent_nodes(nk).forEach(function(nk2) { + if(!_nodeHidden[nk2]) + p[nk2] = true; + }); + return p; + }, {}); + } + function apply_filter() { + var _shown = get_shown(_nodeExpanded); + _nodeDim.filterFunction(function(nk) { + return _shown[nk]; + }); + _edgeDim && _edgeDim.filterFunction(function(ek) { + return !_edgeHidden[ek]; + }); + } + function adjacent_edges(nk) { + return options.edgeGroup.all().filter(function(e) { + return options.edgeSource(e) === nk || options.edgeTarget(e) === nk; + }); + } + // function out_edges(nk) { + // return options.edgeGroup.all().filter(function(e) { + // return options.edgeSource(e) === nk; + // }); + // } + // function in_edges(nk) { + // return options.edgeGroup.all().filter(function(e) { + // return options.edgeTarget(e) === nk; + // }); + // } + function adjacent_nodes(nk) { + return adjacent_edges(nk).map(function(e) { + return options.edgeSource(e) === nk ? options.edgeTarget(e) : options.edgeSource(e); + }); + } + + apply_filter(); + var _strategy = { + get_degree: function(nk) { + return adjacent_edges(nk).length; + }, + get_edges: function(nk) { + return adjacent_edges(nk); + }, + expand: function(nk) { + _nodeExpanded[nk] = true; + apply_filter(); + dc.redrawAll(); + }, + expandedNodes: function(_) { + if(!arguments.length) + return _nodeExpanded; + _nodeExpanded = _; + apply_filter(); + dc.redrawAll(); + return this; + }, + collapsibles: function(nk, dir) { + var whatif = Object.assign({}, _nodeExpanded); + delete whatif[nk]; + var shown = get_shown(_nodeExpanded), would = get_shown(whatif); + var going = Object.keys(shown) + .filter(function(nk2) { return !would[nk2]; }) + .reduce(function(p, v) { + p[v] = true; + return p; + }, {}); + return { + nodes: going, + edges: options.edgeGroup.all().filter(function(e) { + return going[options.edgeSource(e)] || going[options.edgeTarget(e)]; + }).reduce(function(p, e) { + p[options.edgeKey(e)] = true; + return p; + }, {}) + }; + }, + collapse: function(nk, collapsible) { + delete _nodeExpanded[nk]; + apply_filter(); + dc.redrawAll(); + }, + hideNode: function(nk) { + _nodeHidden[nk] = true; + this.collapse(nk); // in case + }, + hideEdge: function(ek) { + if(!options.edgeCrossfilter) + console.warn('expanded_hidden needs edgeCrossfilter to hide edges'); + _edgeHidden[ek] = true; + apply_filter(); + dc.redrawAll(); + } + }; + return _strategy; +}; + +dc_graph.draw_graphs = function(options) { + var select_nodes_group = dc_graph.select_things_group(options.select_nodes_group || 'select-nodes-group', 'select-nodes'), + select_edges_group = dc_graph.select_things_group(options.select_edges_group || 'select-edges-group', 'select-edges'), + label_nodes_group = dc_graph.label_things_group('label-nodes-group', 'label-nodes'), + label_edges_group = dc_graph.label_things_group('label-edges-group', 'label-edges'), + fix_nodes_group = dc_graph.fix_nodes_group('fix-nodes-group'); + var _nodeIdTag = options.idTag || 'id', + _edgeIdTag = options.edgeIdTag || _nodeIdTag, + _sourceTag = options.sourceTag || 'source', + _targetTag = options.targetTag || 'target', + _nodeLabelTag = options.labelTag || 'label', + _edgeLabelTag = options.edgeLabelTag || _nodeLabelTag; + + var _sourceDown = null, _targetMove = null, _targetValid = false, _edgeLayer = null, _hintData = [], _crossout; + + function update_hint() { + var data = _hintData.filter(function(h) { + return h.source && h.target; + }); + var line = _edgeLayer.selectAll('line.hint-edge').data(data); + line.exit().remove(); + line.enter().append('line') + .attr('class', 'hint-edge') + .style({ + fill: 'none', + stroke: 'black', + 'pointer-events': 'none' + }); + + line.attr({ + x1: function(n) { return n.source.x; }, + y1: function(n) { return n.source.y; }, + x2: function(n) { return n.target.x; }, + y2: function(n) { return n.target.y; } + }); + } + + function port_pos(p) { + var style = _mode.parent().portStyle(_mode.parent().portStyleName.eval(p)); + var pos = style.portPosition(p); + pos.x += p.node.cola.x; + pos.y += p.node.cola.y; + return pos; + } + + function update_crossout() { + var data; + if(_crossout) { + if(_mode.usePorts()) + data = [port_pos(_crossout)]; + else + data = [{x: _crossout.node.cola.x, y: _crossout.node.cola.y}]; + } + else data = []; + + var size = _mode.crossSize(), wid = _mode.crossWidth(); + var cross = _edgeLayer.selectAll('polygon.graph-draw-crossout').data(data); + cross.exit().remove(); + cross.enter().append('polygon') + .attr('class', 'graph-draw-crossout'); + cross + .attr('points', function(d) { + var x = d.x, y = d.y; + return [ + [x-size/2, y+size/2], [x-size/2+wid, y+size/2], [x, y+wid/2], + [x+size/2-wid, y+size/2], [x+size/2, y+size/2], [x+wid/2, y], + [x+size/2, y-size/2], [x+size/2-wid, y-size/2], [x, y-wid/2], + [x-size/2+wid, y-size/2], [x-size/2, y-size/2], [x-wid/2, y] + ] + .map(function(p) { return p.join(','); }) + .join(' '); + }); + } + function erase_hint() { + _hintData = []; + _targetValid = false; + _sourceDown = _targetMove = null; + update_hint(); + } + + function create_node(diagram, pos, data) { + if(!_mode.nodeCrossfilter()) + throw new Error('need nodeCrossfilter'); + var node, callback = _mode.addNode() || promise_identity; + if(data) + node = data; + else { + node = {}; + node[_nodeIdTag] = uuid(); + node[_nodeLabelTag] = ''; + } + if(pos) + fix_nodes_group.new_node(node[_nodeIdTag], node, {x: pos[0], y: pos[1]}); + callback(node).then(function(node2) { + if(!node2) + return; + _mode.nodeCrossfilter().add([node2]); + diagram.redrawGroup(); + select_nodes_group.set_changed([node2[_nodeIdTag]]); + }); + } + + function create_edge(diagram, source, target) { + if(!_mode.edgeCrossfilter()) + throw new Error('need edgeCrossfilter'); + var edge = {}, callback = _mode.addEdge() || promise_identity; + edge[_edgeIdTag] = uuid(); + edge[_edgeLabelTag] = ''; + if(_mode.conduct().detectReversedEdge && _mode.conduct().detectReversedEdge(edge, source.port, target.port)) { + edge[_sourceTag] = target.node.orig.key; + edge[_targetTag] = source.node.orig.key; + var t; + t = source; source = target; target = t; + } else { + edge[_sourceTag] = source.node.orig.key; + edge[_targetTag] = target.node.orig.key; + } + callback(edge, source.port, target.port).then(function(edge2) { + if(!edge2) + return; + fix_nodes_group.new_edge(edge[_edgeIdTag], edge2[_sourceTag], edge2[_targetTag]); + _mode.edgeCrossfilter().add([edge2]); + select_nodes_group.set_changed([], false); + select_edges_group.set_changed([edge2[_edgeIdTag]], false); + diagram.redrawGroup(); + }); + } + + function check_invalid_drag(coords) { + var msg; + if(!(d3.event.buttons & 1)) { + // mouse button was released but we missed it + _crossout = null; + if(_mode.conduct().cancelDragEdge) + _mode.conduct().cancelDragEdge(_sourceDown); + erase_hint(); + update_crossout(); + return true; + } + if(!_sourceDown.started && Math.hypot(coords[0] - _hintData[0].source.x, coords[1] - _hintData[0].source.y) > _mode.dragSize()) { + if(_mode.conduct().startDragEdge) { + if(_mode.conduct().startDragEdge(_sourceDown)) { + _sourceDown.started = true; + } else { + if(_mode.conduct().invalidSourceMessage) { + msg = _mode.conduct().invalidSourceMessage(_sourceDown); + console.log(msg); + if(options.negativeTip) { + options.negativeTip + .content(function(_, k) { k(msg); }) + .displayTip(_mode.usePorts() ? _sourceDown.port : _sourceDown.node); + } + } + erase_hint(); + return true; + } + } + } + return false; + } + + function draw(diagram, node, edge, ehover) { + var select_nodes = diagram.child('select-nodes'); + if(select_nodes) { + if(_mode.clickCreatesNodes()) + select_nodes.clickBackgroundClears(false); + } + node + .on('mousedown.draw-graphs', function(n) { + d3.event.stopPropagation(); + if(!_mode.dragCreatesEdges()) + return; + if(options.tipsDisable) + options.tipsDisable.forEach(function(tip) { + tip + .hideTip() + .disabled(true); + }); + if(_mode.usePorts()) { + var activePort; + if(typeof _mode.usePorts() === 'object' && _mode.usePorts().eventPort) + activePort = _mode.usePorts().eventPort(); + else activePort = diagram.getPort(diagram.nodeKey.eval(n), null, 'out') + || diagram.getPort(diagram.nodeKey.eval(n), null, 'in'); + if(!activePort) + return; + _sourceDown = {node: n, port: activePort}; + _hintData = [{source: port_pos(activePort)}]; + } else { + _sourceDown = {node: n}; + _hintData = [{source: {x: _sourceDown.node.cola.x, y: _sourceDown.node.cola.y}}]; + } + }) + .on('mousemove.draw-graphs', function(n) { + var msg; + d3.event.stopPropagation(); + if(_sourceDown) { + var coords = dc_graph.event_coords(diagram); + if(check_invalid_drag(coords)) + return; + var oldTarget = _targetMove; + if(n === _sourceDown.node) { + _mode.conduct().invalidTargetMessage && + console.log(_mode.conduct().invalidTargetMessage(_sourceDown, _sourceDown)); + _targetMove = null; + _hintData[0].target = null; + } + else if(_mode.usePorts()) { + var activePort; + if(typeof _mode.usePorts() === 'object' && _mode.usePorts().eventPort) + activePort = _mode.usePorts().eventPort(); + else activePort = diagram.getPort(diagram.nodeKey.eval(n), null, 'in') + || diagram.getPort(diagram.nodeKey.eval(n), null, 'out'); + if(activePort) + _targetMove = {node: n, port: activePort}; + else + _targetMove = null; + } else if(!_targetMove || n !== _targetMove.node) { + _targetMove = {node: n}; + } + if(_mode.conduct().changeDragTarget) { + var change; + if(_mode.usePorts()) { + var oldPort = oldTarget && oldTarget.port, + newPort = _targetMove && _targetMove.port; + change = oldPort !== newPort; + } else { + var oldNode = oldTarget && oldTarget.node, + newNode = _targetMove && _targetMove.node; + change = oldNode !== newNode; + } + if(change) + if(_mode.conduct().changeDragTarget(_sourceDown, _targetMove)) { + _crossout = null; + if(options.negativeTip) + options.negativeTip.hideTip(); + msg = _mode.conduct().validTargetMessage && _mode.conduct().validTargetMessage() || + 'matches'; + if(options.positiveTip) { + options.positiveTip + .content(function(_, k) { k(msg); }) + .displayTip(_mode.usePorts() ? _targetMove.port : _targetMove.node); + } + _targetValid = true; + } else { + _crossout = _mode.usePorts() ? + _targetMove && _targetMove.port : + _targetMove && _targetMove.node; + if(_targetMove && _mode.conduct().invalidTargetMessage) { + if(options.positiveTip) + options.positiveTip.hideTip(); + msg = _mode.conduct().invalidTargetMessage(_sourceDown, _targetMove); + console.log(msg); + if(options.negativeTip) { + options.negativeTip + .content(function(_, k) { k(msg); }) + .displayTip(_mode.usePorts() ? _targetMove.port : _targetMove.node); + } + } + _targetValid = false; + } + } else _targetValid = true; + if(_targetMove) { + if(_targetMove.port) + _hintData[0].target = port_pos(activePort); + else + _hintData[0].target = {x: n.cola.x, y: n.cola.y}; + } + else { + _hintData[0].target = {x: coords[0], y: coords[1]}; + } + update_hint(); + update_crossout(); + } + }) + .on('mouseup.draw-graphs', function(n) { + _crossout = null; + if(options.negativeTip) + options.negativeTip.hideTip(true); + if(options.positiveTip) + options.positiveTip.hideTip(true); + if(options.tipsDisable) + options.tipsDisable.forEach(function(tip) { + tip.disabled(false); + }); + // allow keyboard mode to hear this one (again, we need better cooperation) + // d3.event.stopPropagation(); + if(_sourceDown && _targetValid) { + var finishPromise; + if(_mode.conduct().finishDragEdge) + finishPromise = _mode.conduct().finishDragEdge(_sourceDown, _targetMove); + else finishPromise = Promise.resolve(true); + var source = _sourceDown, target = _targetMove; + finishPromise.then(function(ok) { + if(ok) + create_edge(diagram, source, target); + }); + } + else if(_sourceDown) { + if(_mode.conduct().cancelDragEdge) + _mode.conduct().cancelDragEdge(_sourceDown); + } + erase_hint(); + update_crossout(); + }); + diagram.svg() + .on('mousedown.draw-graphs', function() { + _sourceDown = null; + }) + .on('mousemove.draw-graphs', function() { + var data = []; + if(_sourceDown) { // drawing edge + var coords = dc_graph.event_coords(diagram); + _crossout = null; + if(check_invalid_drag(coords)) + return; + if(_mode.conduct().dragCanvas) + _mode.conduct().dragCanvas(_sourceDown, coords); + if(_mode.conduct().changeDragTarget && _targetMove) + _mode.conduct().changeDragTarget(_sourceDown, null); + _targetMove = null; + _hintData[0].target = {x: coords[0], y: coords[1]}; + update_hint(); + update_crossout(); + } + }) + .on('mouseup.draw-graphs', function() { + _crossout = null; + if(options.negativeTip) + options.negativeTip.hideTip(true); + if(options.positiveTip) + options.positiveTip.hideTip(true); + if(options.tipsDisable) + options.tipsDisable.forEach(function(tip) { + tip.disabled(false); + }); + if(_sourceDown) { // drag-edge + if(_mode.conduct().cancelDragEdge) + _mode.conduct().cancelDragEdge(_sourceDown); + erase_hint(); + } else { // click-node + if(d3.event.target === this && _mode.clickCreatesNodes()) + create_node(diagram, dc_graph.event_coords(diagram)); + } + update_crossout(); + }); + if(!_edgeLayer) + _edgeLayer = diagram.g().append('g').attr('class', 'draw-graphs'); + } + + function remove(diagram, node, edge, ehover) { + node + .on('mousedown.draw-graphs', null) + .on('mousemove.draw-graphs', null) + .on('mouseup.draw-graphs', null); + diagram.svg() + .on('mousedown.draw-graphs', null) + .on('mousemove.draw-graphs', null) + .on('mouseup.draw-graphs', null); + } + + var _mode = dc_graph.mode('highlight-paths', { + draw: draw, + remove: remove + }); + + // update the data source/destination + _mode.nodeCrossfilter = property(options.nodeCrossfilter); + _mode.edgeCrossfilter = property(options.edgeCrossfilter); + + // modeal options + _mode.usePorts = property(null); + _mode.clickCreatesNodes = property(true); + _mode.dragCreatesEdges = property(true); + _mode.dragSize = property(5); + + // draw attributes of indicator for failed edge + _mode.crossSize = property(15); + _mode.crossWidth = property(5); + + // really this is a behavior or strategy + _mode.conduct = property({}); + + // callbacks to modify data as it's being added + // as of 0.6, function returns a promise of the new data + _mode.addNode = property(null); // node -> promise(node2) + _mode.addEdge = property(null); // edge, sourceport, targetport -> promise(edge2) + + // or, if you want to drive.. + _mode.createNode = function(pos, data) { + create_node(_mode.parent(), pos, data); + }; + + return _mode; +}; + + +dc_graph.match_ports = function(diagram, symbolPorts) { + var _ports, _wports, _wedges, _validTargets; + diagram.on('data.match-ports', function(diagram, nodes, wnodes, edges, wedges, ports, wports) { + _ports = ports; + _wports = wports; + _wedges = wedges; + }); + diagram.on('transitionsStarted.match-ports', function() { + symbolPorts.enableHover(true); + }); + function change_state(ports, state) { + return ports.map(function(p) { + p.state = state; + return diagram.portNodeKey.eval(p); + }); + } + function reset_ports(source) { + var nids = change_state(_validTargets, 'small'); + source.port.state = 'small'; + nids.push(diagram.portNodeKey.eval(source.port)); + symbolPorts.animateNodes(nids); + } + function has_parallel(sourcePort, targetPort) { + return _wedges.some(function(e) { + return sourcePort.edges.indexOf(e) >= 0 && targetPort.edges.indexOf(e) >= 0; + }); + } + function is_valid(sourcePort, targetPort) { + return (_strategy.allowParallel() || !has_parallel(sourcePort, targetPort)) + && _strategy.isValid()(sourcePort, targetPort); + } + function why_invalid(sourcePort, targetPort) { + return !_strategy.allowParallel() && has_parallel(sourcePort, targetPort) && "can't connect two edges between the same two ports" || + _strategy.whyInvalid()(sourcePort, targetPort); + } + var _strategy = { + isValid: property(function(sourcePort, targetPort) { + return targetPort !== sourcePort && targetPort.name === sourcePort.name; + }), + whyInvalid: property(function(sourcePort, targetPort) { + return targetPort === sourcePort && "can't connect port to itself" || + targetPort.name !== sourcePort.name && "must connect ports of the same type"; + }), + allowParallel: property(false), + hoverPort: function(port) { + if(port) { + _validTargets = _wports.filter(is_valid.bind(null, port)); + if(_validTargets.length) + return change_state(_validTargets, 'shimmer-medium'); + } else if(_validTargets) + return change_state(_validTargets, 'small'); + return null; + }, + startDragEdge: function(source) { + _validTargets = _wports.filter(is_valid.bind(null, source.port)); + var nids = change_state(_validTargets, 'shimmer'); + if(_validTargets.length) { + symbolPorts.enableHover(false); + source.port.state = 'large'; + nids.push(diagram.portNodeKey.eval(source.port)); + symbolPorts.animateNodes(nids); + } + console.log('valid targets', nids); + return _validTargets.length !== 0; + }, + invalidSourceMessage: function(source) { + return "no valid matches for this port"; + }, + changeDragTarget: function(source, target) { + var nids, valid = target && is_valid(source.port, target.port), before; + if(valid) { + nids = change_state(_validTargets, 'small'); + target.port.state = 'large'; // it's one of the valid + } + else { + nids = change_state(_validTargets, 'small'); + before = symbolPorts.animateNodes(nids); + nids = change_state(_validTargets, 'shimmer'); + } + symbolPorts.animateNodes(nids, before); + return valid; + }, + validTargetMessage: function(source, target) { + return "it's a match!"; + }, + invalidTargetMessage: function(source, target) { + return why_invalid(source.port, target.port); + }, + finishDragEdge: function(source, target) { + symbolPorts.enableHover(true); + reset_ports(source); + return Promise.resolve(is_valid(source.port, target.port)); + }, + cancelDragEdge: function(source) { + symbolPorts.enableHover(true); + reset_ports(source); + return true; + } + }; + return _strategy; +}; + +dc_graph.match_opposites = function(diagram, deleteProps, options) { + options = Object.assign({ + multiplier: 2, + ease: d3.ease('cubic') + }, options); + var _ports, _wports, _wedges, _validTargets; + + diagram.cascade(100, true, multiply_properties(function(e) { + return options.ease(e.deleting || 0); + }, deleteProps, property_interpolate)); + diagram.on('data.match-opposites', function(diagram, nodes, wnodes, edges, wedges, ports, wports) { + _ports = ports; + _wports = wports; + _wedges = wedges; + }); + function port_pos(p) { + return { x: p.node.cola.x + p.pos.x, y: p.node.cola.y + p.pos.y }; + } + function is_valid(sourcePort, targetPort) { + return (_strategy.allowParallel() || !_wedges.some(function(e) { + return sourcePort.edges.indexOf(e) >= 0 && targetPort.edges.indexOf(e) >= 0; + })) && _strategy.isValid()(sourcePort, targetPort); + } + function reset_deletables(source, targets) { + targets.forEach(function(p) { + p.edges.forEach(function(e) { + e.deleting = 0; + }); + }); + if(source) + source.port.edges.forEach(function(e) { + e.deleting = 0; + }); + } + var _strategy = { + isValid: property(function(sourcePort, targetPort) { + // draw_graphs is already enforcing this, but this makes more sense and i use xor any chance i get + return (diagram.portName.eval(sourcePort) === 'in') ^ (diagram.portName.eval(targetPort) === 'in'); + }), + allowParallel: property(false), + hoverPort: function(port) { + // could be called by draw_graphs when node is hovered, isn't + }, + startDragEdge: function(source) { + _validTargets = _wports.filter(is_valid.bind(null, source.port)); + console.log('valid targets', _validTargets.map(diagram.portNodeKey.eval)); + return _validTargets.length !== 0; + }, + dragCanvas: function(source, coords) { + var closest = _validTargets.map(function(p) { + var ppos = port_pos(p); + return { + distance: Math.hypot(coords[0] - ppos.x, coords[1] - ppos.y), + port: p + }; + }).sort(function(a, b) { + return a.distance - b.distance; + }); + var cpos = port_pos(closest[0].port), spos = port_pos(source.port); + closest.forEach(function(c) { + c.port.edges.forEach(function(e) { + e.deleting = 1 - options.multiplier * c.distance / Math.hypot(cpos.x - spos.x, cpos.y - spos.y); + }); + }); + source.port.edges.forEach(function(e) { + e.deleting = 1 - options.multiplier * closest[0].distance / Math.hypot(cpos.x - spos.x, cpos.y - spos.y); + }); + diagram.requestRefresh(0); + }, + changeDragTarget: function(source, target) { + var valid = target && is_valid(source.port, target.port); + if(valid) { + target.port.edges.forEach(function(e) { + e.deleting = 1; + }); + source.port.edges.forEach(function(e) { + e.deleting = 1; + }); + reset_deletables(null, _validTargets.filter(function(p) { + return p !== target.port; + })); + diagram.requestRefresh(0); + } + return valid; + }, + finishDragEdge: function(source, target) { + if(is_valid(source.port, target.port)) { + reset_deletables(null, _validTargets.filter(function(p) { + return p !== target.port; + })); + if(options.delete_edges) { + var edgeKeys = source.port.edges.map(diagram.edgeKey.eval).concat(target.port.edges.map(diagram.edgeKey.eval)); + return options.delete_edges.deleteSelection(edgeKeys); + } + return Promise.resolve(true); + } + reset_deletables(source, _validTargets || []); + return Promise.resolve(false); + }, + cancelDragEdge: function(source) { + reset_deletables(source, _validTargets || []); + return true; + }, + detectReversedEdge: function(edge, sourcePort, targetPort) { + return diagram.portName.eval(sourcePort) === 'in'; + } + }; + return _strategy; +}; + +dc_graph.wildcard_ports = function(options) { + var diagram = options.diagram, + get_type = options.get_type || function(p) { return p.orig.value.type; }, + set_type = options.set_type || function(p, src) { p.orig.value.type = src.orig.value.type; }, + get_name = options.get_name || function(p) { return p.orig.value.name; }, + is_wild = options.is_wild || function(p) { return p.orig.value.wild; }, + update_ports = options.update_ports || function() {}, + get_linked = options.get_linked || function() { return []; }; + function linked_ports(n, port) { + if(!diagram) + return []; + var nid = diagram.nodeKey.eval(n); + var name = get_name(port); + var links = get_linked(n) || []; + var found = links.find(function(set) { + return set.includes(name); + }); + if(!found) return []; + return found.filter(function(link) { return link !== name; }).map(function(link) { + return diagram.getPort(nid, null, link); + }); + } + function no_edges(ports) { + return ports.every(function(lp) { + return lp.edges.length === 0; + }); + } + return { + isValid: function(p1, p2) { + return get_type(p1) === null ^ get_type(p2) === null || + get_type(p1) !== null && get_type(p1) === get_type(p2); + }, + whyInvalid: function(p1, p2) { + return get_type(p1) === null && get_type(p2) === null && "can't connect wildcard to wildcard" || + get_type(p1) !== get_type(p2) && "the types of ports must match"; + }, + copyLinked: function(n, port) { + linked_ports(n, port).forEach(function(lp) { + set_type(lp, port); + }); + }, + copyType: function(e, sport, tport) { + if(get_type(sport) === null) { + set_type(sport, tport); + this.copyLinked(sport.node, sport); + update_ports(); + } else if(get_type(tport) === null) { + set_type(tport, sport); + this.copyLinked(tport.node, tport); + update_ports(); + } + return Promise.resolve(e); + }, + resetTypes: function(edges) { + // backward compatibility: this used to take diagram as + // first arg, which was wrong + var dia = diagram; + if(arguments.length === 2) { + dia = arguments[0]; + edges = arguments[1]; + } + edges.forEach(function(eid) { + var e = dia.getWholeEdge(eid), + spname = dia.edgeSourcePortName.eval(e), + tpname = dia.edgeTargetPortName.eval(e); + var update = false; + var p = dia.getPort(dia.nodeKey.eval(e.source), null, spname); + var linked = linked_ports(e.source, p); + if(is_wild(p) && p.edges.length === 1 && no_edges(linked)) { + set_type(p, null); + linked.forEach(function(lp) { + set_type(lp, null); + update = true; + }); + } + p = dia.getPort(dia.nodeKey.eval(e.target), null, tpname); + linked = linked_ports(e.target, p); + if(is_wild(p) && p.edges.length === 1 && no_edges(linked)) { + set_type(p, null); + linked.forEach(function(lp) { + set_type(lp, null); + update = true; + }); + } + if(update) + update_ports(); + }); + return Promise.resolve(edges); + } + }; +}; + +dc_graph.symbol_port_style = function() { + var _style = {}; + var _nodePorts, _node; + var _drawConduct; + + _style.symbolScale = property(null); + _style.colorScale = property(d3.scale.ordinal().range( + // colorbrewer light qualitative scale + d3.shuffle(['#8dd3c7','#ffffb3','#bebada','#fb8072','#80b1d3','#fdb462', + '#b3de69','#fccde5','#d9d9d9','#bc80bd','#ccebc5','#ffed6f']))); + + function name_or_edge(p) { + return p.named ? p.name : _style.parent().edgeKey.eval(p.edges[0]); + } + _style.symbol = _style.portSymbol = property(name_or_edge, false); // non standard properties taking "outer datum" + _style.color = _style.portColor = property(name_or_edge, false); + _style.outline = property(dc_graph.symbol_port_style.outline.circle()); + _style.content = property(dc_graph.symbol_port_style.content.d3symbol()); + _style.smallRadius = _style.portRadius = property(7); + _style.mediumRadius = _style.portHoverNodeRadius = property(10); + _style.largeRadius = _style.portHoverPortRadius = property(14); + _style.displacement = _style.portDisplacement = property(2); + _style.outlineFillScale = _style.portBackgroundScale = property(null); + _style.outlineFill = _style.portBackgroundFill = property(null); + _style.outlineStroke = _style.portBackgroundStroke = property(null); + _style.outlineStrokeWidth = _style.portBackgroundStrokeWidth = property(null); + _style.padding = _style.portPadding = property(2); + _style.label = _style.portLabel = _style.portText = property(function(p) { + return p.name; + }); + _style.portLabelPadding = property({x: 5, y: 5}); + _style.cascade = cascade(_style); + + _style.portPosition = function(p) { + var l = Math.hypot(p.pos.x, p.pos.y), + u = {x: p.pos.x / l, y: p.pos.y / l}, + disp = _style.displacement.eval(p); + return {x: p.pos.x + disp * u.x, y: p.pos.y + disp * u.y}; + }; + + _style.portBounds = function(p) { + var R = _style.largeRadius.eval(p), + pos = _style.portPosition(p); + return { + left: pos.x - R/2, + top: pos.y - R/2, + right: pos.x + R/2, + bottom: pos.y + R/2 + }; + }; + + function symbol_fill(p) { + var symcolor = _style.color.eval(p); + return symcolor ? + (_style.colorScale() ? _style.colorScale()(symcolor) : symcolor) : + 'none'; + } + function port_transform(p) { + var pos = _style.portPosition(p); + return 'translate(' + pos.x + ',' + pos.y + ')'; + } + function port_symbol(p) { + if(!_style.symbolScale()) + _style.symbolScale(d3.scale.ordinal().range(d3.shuffle(_style.content().enum()))); + var symname = _style.symbol.eval(p); + return symname && (_style.symbolScale() ? _style.symbolScale()(symname) : symname); + } + function is_left(p) { + return p.vec[0] < 0; + } + function hover_radius(p) { + switch(p.state) { + case 'large': + return _style.largeRadius.eval(p); + case 'medium': + return _style.mediumRadius.eval(p); + case 'small': + default: + return _style.smallRadius.eval(p); + } + } + function shimmer_radius(p) { + return /-medium$/.test(p.state) ? + _style.mediumRadius.eval(p) : + _style.largeRadius.eval(p); + } + // fall back to node aesthetics if not defined for port + function outline_fill(p) { + var scale, fill; + if(_style.outlineFill.eval(p)) { + scale = _style.outlineFillScale() || identity; + fill = _style.outlineFill.eval(p); + } + else { + scale = _style.parent().nodeFillScale() || identity; + fill = _style.parent().nodeFill.eval(p.node); + } + return fill === 'none' ? 'none' : scale(fill); + } + function outline_stroke(p) { + return _style.outlineStroke.eval(p) || _style.parent().nodeStroke.eval(p.node); + } + function outline_stroke_width(p) { + var sw = _style.outlineStrokeWidth.eval(p); + return typeof sw === 'number' ? sw : _style.parent().nodeStrokeWidth.eval(p.node); + } + _style.animateNodes = function(nids, before) { + var setn = d3.set(nids); + var node = _node + .filter(function(n) { + return setn.has(_style.parent().nodeKey.eval(n)); + }); + var symbol = _style.parent().selectNodePortsOfStyle(node, _style.parent().portStyle.nameOf(this)); + var shimmer = symbol.filter(function(p) { return /^shimmer/.test(p.state); }), + nonshimmer = symbol.filter(function(p) { return !/^shimmer/.test(p.state); }); + if(shimmer.size()) { + if(before) + before.each('end', repeat); + else repeat(); + } + + function repeat() { + var shimin = shimmer.transition() + .duration(1000) + .ease("bounce"); + shimin.selectAll('.port-outline') + .call(_style.outline().draw(function(p) { + return shimmer_radius(p) + _style.portPadding.eval(p); + })); + shimin.selectAll('.port-symbol') + .call(_style.content().draw(port_symbol, shimmer_radius)); + var shimout = shimin.transition() + .duration(1000) + .ease('sin'); + shimout.selectAll('.port-outline') + .call(_style.outline().draw(function(p) { + return _style.smallRadius.eval(p) + _style.portPadding.eval(p); + })); + shimout.selectAll('.port-symbol') + .call(_style.content().draw(port_symbol, _style.smallRadius.eval)); + shimout.each("end", repeat); + } + + var trans = nonshimmer.transition() + .duration(250); + trans.selectAll('.port-outline') + .call(_style.outline().draw(function(p) { + return hover_radius(p) + _style.portPadding.eval(p); + })); + trans.selectAll('.port-symbol') + .call(_style.content().draw(port_symbol, hover_radius)); + + function text_showing(p) { + return p.state === 'large' || p.state === 'medium'; + } + trans.selectAll('text.port-label') + .attr({ + opacity: function(p) { + return text_showing(p) ? 1 : 0; + }, + 'pointer-events': function(p) { + return text_showing(p) ? 'auto' : 'none'; + } + }); + trans.selectAll('rect.port-label-background') + .attr('opacity', function(p) { + return text_showing(p) ? 1 : 0; + }); + // bring all nodes which have labels showing to the front + _node.filter(function(n) { + var ports = _nodePorts[_style.parent().nodeKey.eval(n)]; + return ports && ports.some(text_showing); + }).each(function() { + this.parentNode.appendChild(this); + }); + // bring all active ports to the front + symbol.filter(function(p) { + return p.state !== 'small'; + }).each(function() { + this.parentNode.appendChild(this); + }); + return trans; + }; + _style.eventPort = function() { + var parent = d3.select(d3.event.target.parentNode); + if(d3.event.target.parentNode.tagName === 'g' && parent.classed('port')) + return parent.datum(); + return null; + }; + _style.drawPorts = function(ports, nodePorts, node) { + _nodePorts = nodePorts; _node = node; + var port = ports.data(function(n) { + return nodePorts[_style.parent().nodeKey.eval(n)] || []; + }, name_or_edge); + port.exit().remove(); + var portEnter = port.enter().append('g') + .attr({ + class: 'port', + transform: port_transform + }); + port.transition('port-position') + .duration(_style.parent().stagedDuration()) + .delay(_style.parent().stagedDelay(false)) // need to account for enters as well + .attr({ + transform: port_transform + }); + + var outline = port.selectAll('.port-outline').data(function(p) { + return outline_fill(p) !== 'none' ? [p] : []; + }); + outline.exit().remove(); + var outlineEnter = outline.enter().append(_style.outline().tag()) + .attr({ + class: 'port-outline', + fill: outline_fill, + 'stroke-width': outline_stroke_width, + stroke: outline_stroke + }); + if(_style.outline().init) + outlineEnter.call(_style.outline().init); + outlineEnter + .call(_style.outline().draw(function(p) { + return _style.smallRadius.eval(p) + _style.portPadding.eval(p); + })); + // only position and size are animated (?) - anyway these are not on the node + // and they are typically used to indicate selection which should be fast + outline + .attr({ + fill: outline_fill, + 'stroke-width': outline_stroke_width, + stroke: outline_stroke + }); + outline.transition() + .duration(_style.parent().stagedDuration()) + .delay(_style.parent().stagedDelay(false)) // need to account for enters as well + .call(_style.outline().draw(function(p) { + return _style.smallRadius.eval(p) + _style.portPadding.eval(p); + })); + + var symbolEnter = portEnter.append(_style.content().tag()) + .attr('class', 'port-symbol') + .call(_style.content().draw(port_symbol, _style.smallRadius.eval)); + + var symbol = port.select('.port-symbol'); + symbol.attr('fill', symbol_fill); + symbol.transition() + .duration(_style.parent().stagedDuration()) + .delay(_style.parent().stagedDelay(false)) // need to account for enters as well + .call(_style.content().draw(port_symbol, _style.smallRadius.eval)); + + var label = port.selectAll('text.port-label').data(function(p) { + return _style.portLabel.eval(p) ? [p] : []; + }); + label.exit().remove(); + var labelEnter = label.enter(); + labelEnter.append('rect') + .attr({ + class: 'port-label-background', + 'pointer-events': 'none' + }); + labelEnter.append('text') + .attr({ + class: 'port-label', + 'dominant-baseline': 'middle', + 'pointer-events': 'none', + cursor: 'default', + opacity: 0 + }); + label + .each(function(p) { + p.offset = (is_left(p) ? -1 : 1) * (_style.largeRadius.eval(p) + _style.portPadding.eval(p)); + }) + .attr({ + 'text-anchor': function(p) { + return is_left(p) ? 'end' : 'start'; + }, + transform: function(p) { + return 'translate(' + p.offset + ',0)'; + } + }) + .text(_style.portLabel.eval) + .each(function(p) { + p.bbox = getBBoxNoThrow(this); + }); + port.selectAll('rect.port-label-background') + .attr({ + x: function(p) { + return (p.offset < 0 ? p.offset - p.bbox.width : p.offset) - _style.portLabelPadding.eval(p).x; + }, + y: function(p) { + return -p.bbox.height/2 - _style.portLabelPadding.eval(p).y; + }, + width: function(p) { + return p.bbox.width + 2*_style.portLabelPadding.eval(p).x; + }, + height: function(p) { + return p.bbox.height + 2*_style.portLabelPadding.eval(p).y; + }, + fill: 'white', + opacity: 0 + }); + return _style; + }; + + _style.enableHover = function(whether) { + if(!_drawConduct) { + if(_style.parent()) { + var draw = _style.parent().child('draw-graphs'); + if(draw) + _drawConduct = draw.conduct(); + } + } + var namespace = 'grow-ports-' + _style.parent().portStyle.nameOf(this); + if(whether) { + _node.on('mouseover.' + namespace, function(n) { + var nid = _style.parent().nodeKey.eval(n); + var activePort = _style.eventPort(); + if(_nodePorts[nid]) + _nodePorts[nid].forEach(function(p) { + p.state = p === activePort ? 'large' : activePort ? 'small' : 'medium'; + }); + var nids = _drawConduct && _drawConduct.hoverPort(activePort) || []; + nids.push(nid); + _style.animateNodes(nids); + }); + _node.on('mouseout.' + namespace, function(n) { + var nid = _style.parent().nodeKey.eval(n); + if(_nodePorts[nid]) + _nodePorts[nid].forEach(function(p) { + p.state = 'small'; + }); + var nids = _drawConduct && _drawConduct.hoverPort(null) || []; + nids.push(nid); + _style.animateNodes(nids); + }); + } else { + _node.on('mouseover.' + namespace, null); + _node.on('mouseout.' + namespace, null); + } + return _style; + }; + + _style.parent = property(null); + return _style; +}; + +dc_graph.symbol_port_style.outline = {}; +dc_graph.symbol_port_style.outline.circle = function() { + return { + tag: function() { + return 'circle'; + }, + draw: function(rf) { + return function(outlines) { + outlines.attr('r', function(p) { return rf(p); }); + }; + } + }; +}; +dc_graph.symbol_port_style.outline.square = function() { + return { + tag: function() { + return 'rect'; + }, + init: function(outlines) { + // crispEdges can make outline off-center from symbols + // outlines.attr('shape-rendering', 'crispEdges'); + }, + draw: function(rf) { + return function(outlines) { + outlines.attr({ + x: function(p) { return -rf(p); }, + y: function(p) { return -rf(p); }, + width: function(p) { return 2*rf(p); }, + height: function(p) { return 2*rf(p); } + }); + }; + } + }; +}; +dc_graph.symbol_port_style.outline.arrow = function() { + // offset needed for body in order to keep centroid at 0,0 + var left_portion = 3/4 - Math.PI/8; + var _outline = { + tag: function() { + return 'path'; + }, + init: function(outlines) { + //outlines.attr('shape-rendering', 'crispEdges'); + }, + draw: function(rf) { + return function(outlines) { + outlines.attr('d', function(p) { + var r = rf(p); + if(!_outline.outie() || _outline.outie()(p.orig)) + return 'M' + -left_portion*r + ',' + -r + ' h' + r + + ' l' + r + ',' + r + ' l' + -r + ',' + r + + ' h' + -r + + ' a' + r + ',' + r + ' 0 1,1 0,' + -2*r; + else + return 'M' + -(2-left_portion)*r + ',' + -r + ' h' + 2*r + + ' a' + r + ',' + r + ' 0 1,1 0,' + 2*r + + ' h' + -2*r + + ' l' + r + ',' + -r + ' l' + -r + ',' + -r; + }); + }; + }, + outie: property(null) + }; + return _outline; +}; + +dc_graph.symbol_port_style.content = {}; +dc_graph.symbol_port_style.content.d3symbol = function() { + var _symbol = { + tag: function() { + return 'path'; + }, + enum: function() { + return d3.svg.symbolTypes; + }, + draw: function(symf, rf) { + return function(symbols) { + symbols.attr('d', function(p) { + var sym = symf(p), r = rf(p); + return sym ? d3.svg.symbol() + .type(sym) + .size(r*r) + () : ''; + }); + symbols.attr('transform', function(p) { + switch(symf(p)) { + case 'triangle-up': + return 'translate(0, -1)'; + case 'triangle-down': + return 'translate(0, 1)'; + default: return null; + } + }); + }; + } + }; + return _symbol; +}; +dc_graph.symbol_port_style.content.letter = function() { + var _symbol = { + tag: function() { + return 'text'; + }, + enum: function() { + return d3.range(65, 91).map(String.fromCharCode); + }, + draw: function(symf, rf) { + return function(symbols) { + symbols.text(symf) + .attr({ + 'dominant-baseline': 'middle', + 'text-anchor': 'middle' + }); + symbols.each(function(p) { + if(!p.symbol_size) + p.symbol_size = getBBoxNoThrow(this); + }); + symbols.attr('transform', function(p) { + return 'scale(' + (2*rf(p)/p.symbol_size.height) + + ') translate(' + [0,2].join(',') + ')'; + }); + }; + } + }; + return _symbol; +}; + +function process_dot(callback, error, text) { + if(error) { + callback(error, null); + return; + } + var nodes, edges, node_cluster = {}, clusters = []; + if(graphlibDot.parse) { // graphlib-dot 1.1.0 (where did i get it from?) + var digraph = graphlibDot.parse(text); + + var nodeNames = digraph.nodes(); + nodes = new Array(nodeNames.length); + nodeNames.forEach(function (name, i) { + var node = nodes[i] = digraph._nodes[nodeNames[i]]; + node.id = i; + node.name = name; + }); + + var edgeNames = digraph.edges(); + edges = []; + edgeNames.forEach(function(e) { + var edge = digraph._edges[e]; + edges.push(Object.assign({}, edge.value, { + source: digraph._nodes[edge.u].id, + target: digraph._nodes[edge.v].id, + sourcename: edge.u, + targetname: edge.v + })); + }); + // TODO: if this version exists in the wild, look at how it does subgraphs/clusters + } else { // graphlib-dot 0.6 + digraph = graphlibDot.read(text); + + nodeNames = digraph.nodes(); + nodes = new Array(nodeNames.length); + nodeNames.forEach(function (name, i) { + var node = nodes[i] = digraph._nodes[nodeNames[i]]; + node.id = i; + node.name = name; + }); + + edges = []; + digraph.edges().forEach(function(e) { + edges.push(Object.assign({}, digraph.edge(e.v, e.w), { + source: digraph._nodes[e.v].id, + target: digraph._nodes[e.w].id, + sourcename: e.v, + targetname: e.w + })); + }); + + // iterative bfs for variety (recursion would work just as well) + var cluster_names = {}; + var queue = digraph.children().map(function(c) { return Object.assign({parent: null, key: c}, digraph.node(c)); }); + while(queue.length) { + var item = queue.shift(), + children = digraph.children(item.key); + if(children.length) { + clusters.push(item); + cluster_names[item.key] = true; + } + else + node_cluster[item.key] = item.parent; + queue = queue.concat(children.map(function(c) { return {parent: item.key, key: c}; })); + } + // clusters as nodes not currently supported + nodes = nodes.filter(function(n) { + return !cluster_names[n.name]; + }); + } + var graph = {nodes: nodes, links: edges, node_cluster: node_cluster, clusters: clusters}; + callback(null, graph); +} + +function process_dsv(callback, error, data) { + if(error) { + callback(error, null); + return; + } + var keys = Object.keys(data[0]); + var source = keys[0], target = keys[1]; + var nodes = d3.set(data.map(function(r) { return r[source]; })); + data.forEach(function(r) { + nodes.add(r[target]); + }); + nodes = nodes.values().map(function(k) { return {name: k}; }); + callback(null, { + nodes: nodes, + links: data.map(function(r, i) { + return { + key: i, + sourcename: r[source], + targetname: r[target] + }; + }) + }); +} + +dc_graph.file_formats = [ + { + exts: 'json', + mimes: 'application/json', + from_url: d3.json, + from_text: function(text, callback) { + callback(null, JSON.parse(text)); + } + }, + { + exts: ['gv', 'dot'], + mimes: 'text/vnd.graphviz', + from_url: function(url, callback) { + d3.text(url, process_dot.bind(null, callback)); + }, + from_text: function(text, callback) { + process_dot(callback, null, text); + } + }, + { + exts: 'psv', + mimes: 'text/psv', + from_url: function(url, callback) { + d3.dsv('|', 'text/plain')(url, process_dsv.bind(null, callback)); + }, + from_text: function(text, callback) { + process_dsv(callback, null, d3.dsv('|').parse(text)); + } + }, + { + exts: 'csv', + mimes: 'text/csv', + from_url: function(url, callback) { + d3.csv(url, process_dsv.bind(null, callback)); + }, + from_text: function(text, callback) { + process_dsv(callback, null, d3.csv.parse(text)); + } + } +]; + +dc_graph.match_file_format = function(filename) { + return dc_graph.file_formats.find(function(format) { + var exts = format.exts; + if(!Array.isArray(exts)) + exts = [exts]; + return exts.find(function(ext) { + return new RegExp('\.' + ext + '$').test(filename); + }); + }); +}; + +dc_graph.match_mime_type = function(mime) { + return dc_graph.file_formats.find(function(format) { + var mimes = format.mimes; + if(!Array.isArray(mimes)) + mimes = [mimes]; + return mimes.includes(mime); + }); +}; + +function unknown_format_error(filename) { + var spl = filename.split('.'); + if(spl.length) + return new Error('do not know how to process graph file extension ' + spl[spl.length-1]); + else + return new Error('need file extension to process graph file automatically, filename ' + filename); +} + +function unknown_mime_error(mime) { + return new Error('do not know how to process mime type ' + mime); +} + +// load a graph from various formats and return the data in consistent {nodes, links} format +dc_graph.load_graph = function() { + // ignore any query parameters for checking extension + function ignore_query(file) { + if(!file) + return null; + return file.replace(/\?.*/, ''); + } + var file1, file2, callback; + file1 = arguments[0]; + if(arguments.length===3) { + file2 = arguments[1]; + callback = arguments[2]; + } + else if(arguments.length===2) { + callback = arguments[1]; + } + else throw new Error('need two or three arguments'); + + if(file2) { + // this is not general - really titan-specific + queue() + .defer(d3.json, file1) + .defer(d3.json, file2) + .await(function(error, nodes, edges) { + if(error) + callback(error, null); + else + callback(null, {nodes: nodes.results, edges: edges.results}); + }); + } + else { + var format; + if(/^data:/.test(file1)) { + var parts = file1.slice(5).split(/,(.+)/); + format = dc_graph.match_mime_type(parts[0]); + if(format) + format.from_text(parts[1], callback); + else callback(unknown_mime_error(parts[0])); + } else { + var file1noq = ignore_query(file1); + format = dc_graph.match_file_format(file1noq); + if(format) + format.from_url(file1, callback); + else callback(unknown_format_error(file1noq)); + } + } +}; + +dc_graph.load_graph_text = function(text, filename, callback) { + var format = dc_graph.match_file_format(filename); + if(format) + format.from_text(text, callback); + else callback(unknown_format_error(filename)); +}; + +dc_graph.data_url = function(data) { + return 'data:application/json,' + JSON.stringify(data); +}; + +function can_get_graph_from_this(data) { + return (data.nodes || data.vertices) && (data.edges || data.links); +} + +// general-purpose reader of various json-based graph formats +// (esp but not limited to titan graph database-like formats) +// this could be generalized a lot +dc_graph.munge_graph = function(data, nodekeyattr, sourceattr, targetattr) { + // we want data = {nodes, edges} and the field names for keys; find those in common json formats + var nodes, edges, nka = nodekeyattr || "name", + sa = sourceattr || "sourcename", ta = targetattr || "targetname"; + + if(!can_get_graph_from_this(data)) { + var wrappers = ['database', 'response']; + var wi = wrappers.findIndex(function(f) { return data[f] && can_get_graph_from_this(data[f]); }); + if(wi<0) + throw new Error("couldn't find the data!"); + data = data[wrappers[wi]]; + } + edges = data.edges || data.links; + nodes = data.nodes || data.vertices; + + function find_attr(o, attrs) { + return attrs.filter(function(a) { return !!o[a]; }); + } + + //var edgekeyattr = "id"; + var edge0 = edges[0]; + if(edge0[sa] === undefined) { + var sourceattrs = sourceattr ? [sourceattr] : ['source_ecomp_uid', "node1", "source", "tail"], + targetattrs = targetattr ? [targetattr] : ['target_ecomp_uid', "node2", "target", "head"]; + //var edgekeyattrs = ['id', '_id', 'ecomp_uid']; + var edgewrappers = ['edge']; + if(edge0.node0 && edge0.node1) { // specific conflict here + sa = 'node0'; + ta = 'node1'; + } + else { + var candidates = find_attr(edge0, sourceattrs); + if(!candidates.length) { + wi = edgewrappers.findIndex(function(w) { + return edge0[w] && find_attr(edge0[w], sourceattrs).length; + }); + if(wi<0) { + if(sourceattr) + throw new Error('sourceattr ' + sa + " didn't work"); + else + throw new Error("didn't find any source attr"); + } + edges = edges.map(function(e) { return e[edgewrappers[wi]]; }); + edge0 = edges[0]; + candidates = find_attr(edge0, sourceattrs); + } + if(candidates.length > 1) + console.warn('found more than one possible source attr', candidates); + sa = candidates[0]; + + candidates = find_attr(edge0, targetattrs); + if(!candidates.length) { + if(targetattr && !edge0[targetattr]) + throw new Error('targetattr ' + ta + " didn't work"); + else + throw new Error("didn't find any target attr"); + } + if(candidates.length > 1) + console.warn('found more than one possible target attr', candidates); + ta = candidates[0]; + + /* + // we're currently assembling our own edgeid + candidates = find_attr(edge0, edgekeyattrs); + if(!candidates.length) + throw new Error("didn't find any edge key"); + if(candidates.length > 1) + console.warn('found more than one edge key attr', candidates); + edgekeyattr = candidates[0]; + */ + } + } + var node0 = nodes[0]; + if(node0[nka] === undefined) { + var nodekeyattrs = nodekeyattr ? [nodekeyattr] : ['ecomp_uid', 'id', '_id', 'key']; + var nodewrappers = ['vertex']; + candidates = find_attr(node0, nodekeyattrs); + if(!candidates.length) { + wi = nodewrappers.findIndex(function(w) { + return node0[w] && find_attr(node0[w], nodekeyattrs).length; + }); + if(wi<0) { + if(nodekeyattr) + throw new Error('nodekeyattr ' + nka + " didn't work"); + else + throw new Error("couldn't find the node data"); + } + nodes = nodes.map(function(n) { return n[nodewrappers[wi]]; }); + node0 = nodes[0]; + candidates = find_attr(node0, nodekeyattrs); + } + if(candidates.length > 1) + console.warn('found more than one possible node key attr', candidates); + nka = candidates[0]; + } + + return { + nodes: nodes, + edges: edges, + nodekeyattr: nka, + sourceattr: sa, + targetattr: ta + }; +} + +/** + * `dc_graph.flat_group` implements a + * ["fake crossfilter group"](https://github.com/dc-js/dc.js/wiki/FAQ#fake-groups) + * for the case of a group which is 1:1 with the rows of the data array. + * + * Although `dc_graph` can be used with aggregated or reduced data, typically the nodes and edges + * are rows of two data arrays, and each row has a column which contains the unique identifier for + * the node or edge. + * + * @namespace flat_group + * @memberof dc_graph + * @type {{}} +**/ + +dc_graph.flat_group = (function() { + var reduce_01 = { + add: function(p, v) { return v; }, + remove: function() { return null; }, + init: function() { return null; } + }; + // now we only really want to see the non-null values, so make a fake group + function non_null(group) { + return { + all: function() { + return group.all().filter(function(kv) { + return kv.value !== null; + }); + } + }; + } + + function dim_group(ndx, id_accessor) { + var dimension = ndx.dimension(id_accessor); + return { + crossfilter: ndx, + dimension: dimension, + group: non_null(dimension.group().reduce(reduce_01.add, + reduce_01.remove, + reduce_01.init)) + }; + } + + return { + /** + * Create a crossfilter, dimension, and flat group. Returns an object containing all three. + * + * 1. If `source` is an array, create a crossfilter from it. Otherwise assume it is a + * crossfilter instance. + * 2. Create a dimension on the crossfilter keyed by `id_accessor` + * 3. Create a group from the dimension, reducing to the row when it's filtered in, or + * `null` when it's out. + * 4. Wrap the group in a fake group which filters out the nulls. + * + * The resulting fake group's `.all()` method returns an array of the currently filtered-in + * `{key, value}` pairs where the key is `id_accessor(row)` and the value is the row. + * @method make + * @memberof dc_graph.flat_group + * @param {Array} source - the data array for crossfilter, or a crossfilter + * @param {Function} id_accessor - accessor function taking a row object and returning its + * unique identifier + * @return {Object} `{crossfilter, dimension, group}` + **/ + make: function(source, id_accessor) { + var cf; + if(Array.isArray(source)) + cf = crossfilter(source); + else cf = source; + return dim_group(cf, id_accessor); + }, + /** + * Create a flat dimension and group from an existing crossfilter. + * + * @method another + * @memberof dc_graph.flat_group + * @deprecated use .make() instead + * @param {Object} ndx - crossfilter instance + * @param {Function} id_accessor - accessor function taking a row object and returning its + * unique identifier + * @return {Object} `{crossfilter, dimension, group}` + **/ + another: deprecate_function('use .make() instead', function(cf, id_accessor) { + return this.make(cf, id_accessor); + }) + }; +})(); + + + +var convert_tree_helper = function(data, attrs, options, parent, level, inherit) { + level = level || 0; + if(attrs.length > (options.valuesByAttr ? 1 : 0)) { + var attr = attrs.shift(); + var nodes = [], edges = []; + var children = data.map(function(v) { + var key = v[options.nestKey]; + var childKey = options.nestKeysUnique ? key : uuid(); + if(childKey) { + var node; + if(options.ancestorKeys) { + inherit = inherit || {}; + if(attr) + inherit[attr] = key; + node = Object.assign({}, inherit); + } else node = {}; + node[options.nodeKey] = childKey; + if(options.label && options.labelFun) + node[options.label] = options.labelFun(key, attr, v); + if(options.level) + node[options.level] = level+1; + nodes.push(node); + if(parent) { + var edge = {}; + edge[options.edgeSource] = parent; + edge[options.edgeTarget] = childKey; + edges.push(edge); + } + } + var children = options.valuesByAttr ? v[attrs[0]] : v.values; + var recurse = convert_tree_helper(children, attrs.slice(0), options, + childKey, level+1, Object.assign({}, inherit)); + return recurse; + }); + return {nodes: Array.prototype.concat.apply(nodes, children.map(dc.pluck('nodes'))), + edges: Array.prototype.concat.apply(edges, children.map(dc.pluck('edges')))}; + } + else return {nodes: data.map(function(v) { + v = Object.assign({}, v); + if(options.level) + v[options.level] = level+1; + return v; + }), edges: data.map(function(v) { + var edge = {}; + edge[options.edgeSource] = parent; + edge[options.edgeTarget] = v[options.nodeKey]; + return edge; + })}; +}; + +dc_graph.convert_tree = function(data, attrs, options) { + options = Object.assign({ + nodeKey: 'key', + edgeKey: 'key', + edgeSource: 'sourcename', + edgeTarget: 'targetname', + nestKey: 'key' + }, options); + if(Array.isArray(data)) + return convert_tree_helper(data, attrs, options, options.root, 0, options.inherit); + else { + attrs = [''].concat(attrs); + return convert_tree_helper([data], attrs, options, options.root, 0, options.inherit); + } +}; + +dc_graph.convert_nest = function(nest, attrs, nodeKeyAttr, edgeSourceAttr, edgeTargetAttr, parent, inherit) { + return dc_graph.convert_tree(nest, attrs, { + nodeKey: nodeKeyAttr, + edgeSource: edgeSourceAttr, + edgeTarget: edgeTargetAttr, + root: parent, + inherit: inherit, + ancestorKeys: true, + label: 'name', + labelFun: function(key, attr, v) { return attr + ':' + key; }, + level: '_level' + }); +}; + +dc_graph.convert_adjacency_list = function(nodes, namesIn, namesOut) { + // adjacenciesAttr, edgeKeyAttr, edgeSourceAttr, edgeTargetAttr, parent, inherit) { + var edges = Array.prototype.concat.apply([], nodes.map(function(n) { + return n[namesIn.adjacencies].map(function(adj) { + var e = {}; + if(namesOut.edgeKey) + e[namesOut.edgeKey] = uuid(); + e[namesOut.edgeSource] = n[namesIn.nodeKey]; + e[namesOut.edgeTarget] = namesIn.targetKey ? adj[namesIn.targetKey] : adj; + if(namesOut.adjacency) + e[namesOut.adjacency] = adj; + return e; + }); + })); + return { + nodes: nodes, + edges: edges + }; +}; + + +// collapse edges between same source and target +dc_graph.deparallelize = function(group, sourceTag, targetTag, options) { + options = options || {}; + var both = options.both || false, + reduce = options.reduce || null; + return { + all: function() { + var ST = {}; + group.all().forEach(function(kv) { + var source = kv.value[sourceTag], + target = kv.value[targetTag]; + var dir = both ? true : source < target; + var min = dir ? source : target, max = dir ? target : source; + ST[min] = ST[min] || {}; + var entry; + if(ST[min][max]) { + entry = ST[min][max]; + if(reduce) + entry.original = reduce(entry.original, kv); + } else ST[min][max] = entry = {in: 0, out: 0, original: Object.assign({}, kv)}; + if(dir) + ++entry.in; + else + ++entry.out; + }); + var ret = []; + Object.keys(ST).forEach(function(source) { + Object.keys(ST[source]).forEach(function(target) { + var entry = ST[source][target]; + entry[sourceTag] = source; + entry[targetTag] = target; + ret.push({key: entry.original.key, value: entry}); + }); + }); + return ret; + } + }; +}; + +dc_graph.path_reader = function(pathsgroup) { + var highlight_paths_group = dc_graph.register_highlight_paths_group(pathsgroup || 'highlight-paths-group'); + var _intervals, _intervalTree, _time; + + function register_path_objs(path, nop, eop) { + reader.elementList.eval(path).forEach(function(element) { + var key, paths; + switch(reader.elementType.eval(element)) { + case 'node': + key = reader.nodeKey.eval(element); + paths = nop[key] = nop[key] || []; + break; + case 'edge': + key = reader.edgeSource.eval(element) + '-' + reader.edgeTarget.eval(element); + paths = eop[key] = eop[key] || []; + break; + } + paths.push(path); + }); + } + + var reader = { + pathList: property(identity, false), + timeRange: property(null, false), + pathStrength: property(null, false), + elementList: property(identity, false), + elementType: property(null, false), + nodeKey: property(null, false), + edgeSource: property(null, false), + edgeTarget: property(null, false), + clear: function() { + highlight_paths_group.paths_changed({}, {}, []); + }, + data: function(data) { + var nop = {}, eop = {}, allpaths = [], has_ranges; + reader.pathList.eval(data).forEach(function(path) { + if((path._range = reader.timeRange.eval(path))) { // ugh modifying user data + if(has_ranges===false) + throw new Error("can't have a mix of ranged and non-ranged paths"); + has_ranges = true; + } else { + if(has_ranges===true) + throw new Error("can't have a mix of ranged and non-ranged paths"); + has_ranges = false; + register_path_objs(path, nop, eop); + } + allpaths.push(path); + }); + if(has_ranges) { + _intervals = allpaths.map(function(path) { + var interval = [path._range[0].getTime(), path._range[1].getTime()]; + interval.path = path; + return interval; + }); + // currently must include lysenko-interval-tree separately + _intervalTree = lysenkoIntervalTree(_intervals); + if(_time) + this.setTime(_time); + } else { + _intervals = null; + _intervalTree = null; + highlight_paths_group.paths_changed(nop, eop, allpaths); + } + }, + getIntervals: function() { + return _intervals; + }, + setTime: function(t) { + if(t && _intervalTree) { + var paths = [], nop = {}, eop = {}; + _intervalTree.queryPoint(t.getTime(), function(interval) { + paths.push(interval.path); + register_path_objs(interval.path, nop, eop); + }); + highlight_paths_group.paths_changed(nop, eop, paths); + } + _time = t; + } + }; + + return reader; +}; + + +dc_graph.path_selector = function(parent, reader, pathsgroup, chartgroup) { + var highlight_paths_group = dc_graph.register_highlight_paths_group(pathsgroup || 'highlight-paths-group'); + var root = d3.select(parent).append('svg'); + var paths_ = []; + var hovered = null, selected = null; + + // unfortunately these functions are copied from dc_graph.highlight_paths + function contains_path(paths) { + return function(path) { + return paths ? paths.indexOf(path)>=0 : false; + }; + } + + function doesnt_contain_path(paths) { + var cp = contains_path(paths); + return function(path) { + return !cp(path); + }; + } + + function toggle_paths(pathsA, pathsB) { + if(!pathsA) + return pathsB; + else if(!pathsB) + return pathsA; + if(pathsB.every(contains_path(pathsA))) + return pathsA.filter(doesnt_contain_path(pathsB)); + else return pathsA.concat(pathsB.filter(doesnt_contain_path(pathsA))); + } + + // this should use the whole cascading architecture + // and allow customization rather than hardcoding everything + // in fact, you can't even reliably overlap attributes without that (so we don't) + + function draw_paths(diagram, paths) { + if(paths.length === 0) return; + var xpadding = 30; + var space = 30; + var radius = 8; + // set the height of SVG accordingly + root.attr('height', 20*(paths.length+1)) + .attr('width', xpadding+(space+2*radius)*(paths.length/2+1)+20); + + root.selectAll('.path-selector').remove(); + + var pathlist = root.selectAll('g.path-selector').data(paths); + pathlist.enter() + .append('g') + .attr('class', 'path-selector') + .attr("transform", function(path, i) { return "translate(0, " + i*20 + ")"; }) + .each(function(path_data, i) { + var nodes = path_data.element_list.filter(function(d) { return d.element_type === 'node'; }); + // line + var line = d3.select(this).append('line'); + line.attr('x1', xpadding+space) + .attr('y1', radius+1) + .attr('x2', xpadding+space*nodes.length) + .attr('y2', radius+1) + .attr('opacity', 0.4) + .attr('stroke-width', 5) + .attr('stroke', '#bdbdbd'); + + // dots + var path = d3.select(this).selectAll('circle').data(nodes); + path.enter() + .append('circle') + .attr('cx', function(d, i) { return xpadding+space*(i+1); }) + .attr('cy', radius+1) + .attr('r', radius) + .attr('opacity', 0.4) + .attr('fill', function(d) { + // TODO path_selector shouldn't know the data structure of orignal node objects + var regeneratedNode = {key:d.property_map.ecomp_uid, value:d.property_map}; + return diagram.nodeStroke()(regeneratedNode); + }); + + // label + var text = d3.select(this).append('text'); + text.text('Path '+i) + .attr('class', 'path_label') + .attr('x', 0) + .attr('y', radius*1.7) + .on('mouseover.path-selector', function() { + highlight_paths_group.hover_changed([path_data]); + }) + .on('mouseout.path-selector', function() { + highlight_paths_group.hover_changed(null); + }) + .on('click.path-selector', function() { + highlight_paths_group.select_changed(toggle_paths(selected, [path_data])); + }); + }); + pathlist.exit().transition(1000).attr('opacity', 0).remove(); + } + + function draw_hovered() { + var is_hovered = contains_path(hovered); + root.selectAll('g.path-selector') + .each(function(d, i) { + var textColor = is_hovered(d) ? '#e41a1c' : 'black'; + var lineColor = is_hovered(d) ? 'black' : '#bdbdbd'; + var opacity = is_hovered(d) ? '1' : '0.4'; + d3.select(this).select('.path_label').attr('fill', textColor); + d3.select(this).selectAll('line') + .attr('stroke', lineColor) + .attr('opacity', opacity); + d3.select(this).selectAll('circle').attr('opacity', opacity); + }); + } + + function draw_selected() { + var is_selected = contains_path(selected); + root.selectAll('g.path-selector') + .each(function(d, i) { + var textWeight = is_selected(d) ? 'bold' : 'normal'; + var lineColor = is_selected(d) ? 'black' : '#bdbdbd'; + var opacity = is_selected(d) ? '1' : '0.4'; + d3.select(this).select('.path_label') + .attr('font-weight', textWeight); + d3.select(this).selectAll('line') + .attr('stroke', lineColor) + .attr('opacity', opacity); + d3.select(this).selectAll('circle').attr('opacity', opacity); + }); + } + + highlight_paths_group + .on('paths_changed.path-selector', function(nop, eop, paths) { + hovered = selected = null; + paths_ = paths; + selector.redraw(); + }) + .on('hover_changed.path-selector', function(hpaths) { + hovered = hpaths; + draw_hovered(); + }) + .on('select_changed.path-selector', function(spaths) { + selected = spaths; + draw_selected(); + }); + var selector = { + default_text: property('Nothing here'), + zero_text: property('No paths'), + error_text: property(null), + queried: property(false), + redraw: function() { + draw_paths(diagram, paths_); + draw_hovered(); + draw_selected(); + }, + render: function() { + this.redraw(); + return this; + } + }; + dc.registerChart(selector, chartgroup); + return selector; +}; + +dc_graph.node_name = function(i) { + // a-z, A-Z, aa-Zz, then quit + if(i<26) + return String.fromCharCode(97+i); + else if(i<52) + return String.fromCharCode(65+i-26); + else if(i<52*52) + return dc_graph.node_name(Math.floor(i/52)) + dc_graph.node_name(i%52); + else throw new Error("no, that's too large"); +}; +dc_graph.node_object = function(i, attrs) { + attrs = attrs || {}; + return _.extend({ + id: i, + name: dc_graph.node_name(i) + }, attrs); +}; + +dc_graph.edge_object = function(namef, i, j, attrs) { + attrs = attrs || {}; + return _.extend({ + source: i, + target: j, + sourcename: namef(i), + targetname: namef(j) + }, attrs); +}; + +dc_graph.generate = function(type, args, env, callback) { + var nodes, edges, i, j; + var nodePrefix = env.nodePrefix || ''; + var namef = function(i) { + return nodes[i].name; + }; + var N = args[0]; + var linkLength = env.linkLength || 30; + switch(type) { + case 'clique': + case 'cliquestf': + nodes = new Array(N); + edges = []; + for(i = 0; i<N; ++i) { + nodes[i] = dc_graph.node_object(i, {circle: "A", name: nodePrefix+dc_graph.node_name(i)}); + for(j=0; j<i; ++j) + edges.push(dc_graph.edge_object(namef, i, j, {notLayout: true, undirected: true})); + } + if(type==='cliquestf') + for(i = 0; i<N; ++i) { + nodes[i+N] = dc_graph.node_object(i+N); + nodes[i+2*N] = dc_graph.node_object(i+2*N); + edges.push(dc_graph.edge_object(namef, i, i+N, {undirected: true})); + edges.push(dc_graph.edge_object(namef, i, i+2*N, {undirected: true})); + } + break; + case 'wheel': + nodes = new Array(N); + for(i = 0; i < N; ++i) + nodes[i] = dc_graph.node_object(i, {name: nodePrefix+dc_graph.node_name(i)}); + edges = dc_graph.wheel_edges(namef, _.range(N), N*linkLength/2); + var rimLength = edges[0].distance; + for(i = 0; i < args[1]; ++i) + for(j = 0; j < N; ++j) { + var a = j, b = (j+1)%N, t; + if(i%2 === 1) { + t = a; + a = b; + b = t; + } + edges.push(dc_graph.edge_object(namef, a, b, {distance: rimLength, par: i+2})); + } + break; + default: + throw new Error("unknown generation type "+type); + } + var graph = {nodes: nodes, links: edges}; + callback(null, graph); +}; + +dc_graph.wheel_edges = function(namef, nindices, R) { + var N = nindices.length; + var edges = []; + var strutSkip = Math.floor(N/2), + rimLength = 2 * R * Math.sin(Math.PI / N), + strutLength = 2 * R * Math.sin(strutSkip * Math.PI / N); + for(var i = 0; i < N; ++i) + edges.push(dc_graph.edge_object(namef, nindices[i], nindices[(i+1)%N], {distance: rimLength})); + for(i = 0; i < N/2; ++i) { + edges.push(dc_graph.edge_object(namef, nindices[i], nindices[(i+strutSkip)%N], {distance: strutLength})); + if(N%2 && i != Math.floor(N/2)) + edges.push(dc_graph.edge_object(namef, nindices[i], nindices[(i+N-strutSkip)%N], {distance: strutLength})); + } + return edges; +}; + +dc_graph.random_graph = function(options) { + options = Object.assign({ + ncolors: 5, + ndashes: 4, + nodeKey: 'key', + edgeKey: 'key', + sourceKey: 'sourcename', + targetKey: 'targetname', + colorTag: 'color', + dashTag: 'dash', + nodeKeyGen: function(i) { return 'n' + i; }, + edgeKeyGen: function(i) { return 'e' + i; }, + newComponentProb: 0.1, + newNodeProb: 0.9, + removeEdgeProb: 0.75, + log: false + }, options); + if(isNaN(options.newNodeProb)) + options.newNodeProb = 0.9; + if(options.newNodProb <= 0) + options.newNodeProb = 0.1; + var _nodes = [], _edges = []; + function new_node() { + var n = {}; + n[options.nodeKey] = options.nodeKeyGen(_nodes.length); + n[options.colorTag] = Math.floor(Math.random()*options.ncolors); + _nodes.push(n); + return n; + } + function random_node() { + return _nodes[Math.floor(Math.random()*_nodes.length)]; + } + return { + nodes: function() { + return _nodes; + }, + edges: function() { + return _edges; + }, + generate: function(N) { + while(N-- > 0) { + var choice = Math.random(); + var n1, n2; + if(!_nodes.length || choice < options.newComponentProb) + n1 = new_node(); + else + n1 = random_node(); + if(choice < options.newNodeProb) + n2 = new_node(); + else + n2 = random_node(); + if(n1 && n2) { + var edge = {}; + edge[options.edgeKey] = options.edgeKeyGen(_edges.length); + edge[options.sourceKey] = n1[options.nodeKey]; + edge[options.targetKey] = n2[options.nodeKey]; + edge[options.dashTag] = Math.floor(Math.random()*options.ndashes); + if(options.log) + console.log(n1[options.nodeKey] + ' -> ' + n2[options.nodeKey]); + _edges.push(edge); + } + } + }, + remove: function(N) { + while(N-- > 0) { + var choice = Math.random(); + if(choice < options.removeEdgeProb) + _edges.splice(Math.floor(Math.random()*_edges.length), 1); + else { + var n = _nodes[Math.floor(Math.random()*_nodes.length)]; + var eis = []; + _edges.forEach(function(e, ei) { + if(e[options.sourceKey] === n[options.nodeKey] || + e[options.targetKey] === n[options.nodeKey]) + eis.push(ei); + }); + eis.reverse().forEach(function(ei) { + _edges.splice(ei, 1); + }); + } + } + } + }; +}; + +dc_graph.supergraph = function(data, options) { + if(!dc_graph.supergraph.pattern) { + var mg = metagraph; + var graph_and_subgraph = { + nodes: { + graph: mg.graph_pattern(options), + sg: mg.subgraph_pattern(options), + subgraph: mg.graph_pattern(options) + }, + edges: { + to_sg: { + source: 'graph', + target: 'sg', + input: 'parent' + }, + from_sg: { + source: 'subgraph', + target: 'sg', + input: 'child' + } + } + }; + dc_graph.supergraph.pattern = mg.compose(mg.graph_detect(graph_and_subgraph)); + } + return dc_graph.supergraph.pattern.node('graph.Graph').value().create(data); +}; + +var dont_use_key = deprecation_warning('dc_graph.line_breaks now takes a string - d.key behavior is deprecated and will be removed in a later version'); + +dc_graph.line_breaks = function(charexp, max_line_length) { + var regexp = new RegExp(charexp, 'g'); + return function(s) { + if(typeof s === 'object') { // backward compatibility + dont_use_key(); + s = s.key; + } + var result; + var line = '', lines = [], part, i = 0; + do { + result = regexp.exec(s); + if(result) + part = s.slice(i, regexp.lastIndex); + else + part = s.slice(i); + if(line.length + part.length > max_line_length && line.length > 0) { + lines.push(line); + line = ''; + } + line += part; + i = regexp.lastIndex; + } + while(result !== null); + lines.push(line); + return lines; + }; +}; + +dc_graph.build_type_graph = function(nodes, edges, nkey, ntype, esource, etarget) { + var nmap = {}, tnodes = {}, tedges = {}; + nodes.forEach(function(n) { + nmap[nkey(n)] = n; + var t = ntype(n); + if(!tnodes[t]) + tnodes[t] = {type: t}; + }); + edges.forEach(function(e) { + var source = esource(e), target = etarget(e), sn, tn; + if(!(sn = nmap[source])) + throw new Error('source key ' + source + ' not found!'); + if(!(tn = nmap[target])) + throw new Error('target key ' + target + ' not found!'); + var etype = ntype(sn) + '/' + ntype(tn); + if(!tedges[etype]) + tedges[etype] = { + type: etype, + source: ntype(sn), + target: ntype(tn) + }; + }); + return { + nodes: Object.keys(tnodes).map(function(k) { return tnodes[k]; }), + edges: Object.keys(tedges).map(function(k) { return tedges[k]; }) + }; +} + +dc_graph.d3 = d3; +dc_graph.crossfilter = crossfilter; +dc_graph.dc = dc; + +return dc_graph; +} + if (typeof define === 'function' && define.amd) { + define(["d3", "crossfilter", "dc"], _dc_graph); + } else if (typeof module == "object" && module.exports) { + var _d3 = require('d3'); + var _crossfilter = require('crossfilter'); + if (typeof _crossfilter !== "function") { + _crossfilter = _crossfilter.crossfilter; + } + var _dc = require('dc'); + module.exports = _dc_graph(_d3, _crossfilter, _dc); + } else { + this.dc_graph = _dc_graph(d3, crossfilter, dc); + } +} +)(); + +//# sourceMappingURL=dc.graph.js.map \ No newline at end of file diff --git a/src/legacy/design-studio/js/dc.js b/src/legacy/design-studio/js/dc.js new file mode 100644 index 0000000..81ae4c8 --- /dev/null +++ b/src/legacy/design-studio/js/dc.js @@ -0,0 +1,10777 @@ +/*! + * dc 2.0.5 + * http://dc-js.github.io/dc.js/ + * Copyright 2012-2016 Nick Zhu & the dc.js Developers + * https://github.com/dc-js/dc.js/blob/master/AUTHORS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +(function() { function _dc(d3, crossfilter) { +'use strict'; + +/** + * The entire dc.js library is scoped under the **dc** name space. It does not introduce + * anything else into the global name space. + * + * Most `dc` functions are designed to allow function chaining, meaning they return the current chart + * instance whenever it is appropriate. The getter forms of functions do not participate in function + * chaining because they return values that are not the chart, although some, + * such as {@link dc.baseMixin#svg .svg} and {@link dc.coordinateGridMixin#xAxis .xAxis}, + * return values that are themselves chainable d3 objects. + * @namespace dc + * @version 2.0.5 + * @example + * // Example chaining + * chart.width(300) + * .height(300) + * .filter('sunday'); + */ +/*jshint -W079*/ +var dc = { + version: '2.0.5', + constants: { + CHART_CLASS: 'dc-chart', + DEBUG_GROUP_CLASS: 'debug', + STACK_CLASS: 'stack', + DESELECTED_CLASS: 'deselected', + SELECTED_CLASS: 'selected', + NODE_INDEX_NAME: '__index__', + GROUP_INDEX_NAME: '__group_index__', + DEFAULT_CHART_GROUP: '__default_chart_group__', + EVENT_DELAY: 40, + NEGLIGIBLE_NUMBER: 1e-10 + }, + _renderlet: null +}; +/*jshint +W079*/ + +/** + * The dc.chartRegistry object maintains sets of all instantiated dc.js charts under named groups + * and the default group. + * + * A chart group often corresponds to a crossfilter instance. It specifies + * the set of charts which should be updated when a filter changes on one of the charts or when the + * global functions {@link dc.filterAll dc.filterAll}, {@link dc.refocusAll dc.refocusAll}, + * {@link dc.renderAll dc.renderAll}, {@link dc.redrawAll dc.redrawAll}, or chart functions + * {@link dc.baseMixin#renderGroup baseMixin.renderGroup}, + * {@link dc.baseMixin#redrawGroup baseMixin.redrawGroup} are called. + * + * @namespace chartRegistry + * @memberof dc + * @type {{has, register, deregister, clear, list}} + */ +dc.chartRegistry = (function () { + // chartGroup:string => charts:array + var _chartMap = {}; + + function initializeChartGroup (group) { + if (!group) { + group = dc.constants.DEFAULT_CHART_GROUP; + } + + if (!_chartMap[group]) { + _chartMap[group] = []; + } + + return group; + } + + return { + /** + * Determine if a given chart instance resides in any group in the registry. + * @method has + * @memberof dc.chartRegistry + * @param {Object} chart dc.js chart instance + * @returns {Boolean} + */ + has: function (chart) { + for (var e in _chartMap) { + if (_chartMap[e].indexOf(chart) >= 0) { + return true; + } + } + return false; + }, + + /** + * Add given chart instance to the given group, creating the group if necessary. + * If no group is provided, the default group `dc.constants.DEFAULT_CHART_GROUP` will be used. + * @method register + * @memberof dc.chartRegistry + * @param {Object} chart dc.js chart instance + * @param {String} [group] Group name + */ + register: function (chart, group) { + group = initializeChartGroup(group); + _chartMap[group].push(chart); + }, + + /** + * Remove given chart instance from the given group, creating the group if necessary. + * If no group is provided, the default group `dc.constants.DEFAULT_CHART_GROUP` will be used. + * @method deregister + * @memberof dc.chartRegistry + * @param {Object} chart dc.js chart instance + * @param {String} [group] Group name + */ + deregister: function (chart, group) { + group = initializeChartGroup(group); + for (var i = 0; i < _chartMap[group].length; i++) { + if (_chartMap[group][i].anchorName() === chart.anchorName()) { + _chartMap[group].splice(i, 1); + break; + } + } + }, + + /** + * Clear given group if one is provided, otherwise clears all groups. + * @method clear + * @memberof dc.chartRegistry + * @param {String} group Group name + */ + clear: function (group) { + if (group) { + delete _chartMap[group]; + } else { + _chartMap = {}; + } + }, + + /** + * Get an array of each chart instance in the given group. + * If no group is provided, the charts in the default group are returned. + * @method list + * @memberof dc.chartRegistry + * @param {String} [group] Group name + * @returns {Array<Object>} + */ + list: function (group) { + group = initializeChartGroup(group); + return _chartMap[group]; + } + }; +})(); + +/** + * Add given chart instance to the given group, creating the group if necessary. + * If no group is provided, the default group `dc.constants.DEFAULT_CHART_GROUP` will be used. + * @memberof dc + * @method registerChart + * @param {Object} chart dc.js chart instance + * @param {String} [group] Group name + */ +dc.registerChart = function (chart, group) { + dc.chartRegistry.register(chart, group); +}; + +/** + * Remove given chart instance from the given group, creating the group if necessary. + * If no group is provided, the default group `dc.constants.DEFAULT_CHART_GROUP` will be used. + * @memberof dc + * @method deregisterChart + * @param {Object} chart dc.js chart instance + * @param {String} [group] Group name + */ +dc.deregisterChart = function (chart, group) { + dc.chartRegistry.deregister(chart, group); +}; + +/** + * Determine if a given chart instance resides in any group in the registry. + * @memberof dc + * @method hasChart + * @param {Object} chart dc.js chart instance + * @returns {Boolean} + */ +dc.hasChart = function (chart) { + return dc.chartRegistry.has(chart); +}; + +/** + * Clear given group if one is provided, otherwise clears all groups. + * @memberof dc + * @method deregisterAllCharts + * @param {String} group Group name + */ +dc.deregisterAllCharts = function (group) { + dc.chartRegistry.clear(group); +}; + +/** + * Clear all filters on all charts within the given chart group. If the chart group is not given then + * only charts that belong to the default chart group will be reset. + * @memberof dc + * @method filterAll + * @param {String} [group] + */ +dc.filterAll = function (group) { + var charts = dc.chartRegistry.list(group); + for (var i = 0; i < charts.length; ++i) { + charts[i].filterAll(); + } +}; + +/** + * Reset zoom level / focus on all charts that belong to the given chart group. If the chart group is + * not given then only charts that belong to the default chart group will be reset. + * @memberof dc + * @method refocusAll + * @param {String} [group] + */ +dc.refocusAll = function (group) { + var charts = dc.chartRegistry.list(group); + for (var i = 0; i < charts.length; ++i) { + if (charts[i].focus) { + charts[i].focus(); + } + } +}; + +/** + * Re-render all charts belong to the given chart group. If the chart group is not given then only + * charts that belong to the default chart group will be re-rendered. + * @memberof dc + * @method renderAll + * @param {String} [group] + */ +dc.renderAll = function (group) { + var charts = dc.chartRegistry.list(group); + for (var i = 0; i < charts.length; ++i) { + charts[i].render(); + } + + if (dc._renderlet !== null) { + dc._renderlet(group); + } +}; + +/** + * Redraw all charts belong to the given chart group. If the chart group is not given then only charts + * that belong to the default chart group will be re-drawn. Redraw is different from re-render since + * when redrawing dc tries to update the graphic incrementally, using transitions, instead of starting + * from scratch. + * @memberof dc + * @method redrawAll + * @param {String} [group] + */ +dc.redrawAll = function (group) { + var charts = dc.chartRegistry.list(group); + for (var i = 0; i < charts.length; ++i) { + charts[i].redraw(); + } + + if (dc._renderlet !== null) { + dc._renderlet(group); + } +}; + +/** + * If this boolean is set truthy, all transitions will be disabled, and changes to the charts will happen + * immediately. + * @memberof dc + * @member disableTransitions + * @type {Boolean} + * @default false + */ +dc.disableTransitions = false; + +/** + * Start a transition on a selection if transitions are globally enabled + * ({@link dc.disableTransitions} is false) and the duration is greater than zero; otherwise return + * the selection. Since most operations are the same on a d3 selection and a d3 transition, this + * allows a common code path for both cases. + * @memberof dc + * @method transition + * @param {d3.selection} selection - the selection to be transitioned + * @param {Number|Function} [duration=250] - the duration of the transition in milliseconds, a + * function returning the duration, or 0 for no transition + * @param {Number|Function} [delay] - the delay of the transition in milliseconds, or a function + * returning the delay, or 0 for no delay + * @param {String} [name] - the name of the transition (if concurrent transitions on the same + * elements are needed) + * @returns {d3.transition|d3.selection} + */ +dc.transition = function (selection, duration, delay, name) { + if (dc.disableTransitions || duration <= 0) { + return selection; + } + + var s = selection.transition(name); + + if (duration >= 0 || duration !== undefined) { + s = s.duration(duration); + } + if (delay >= 0 || delay !== undefined) { + s = s.delay(delay); + } + + return s; +}; + +/* somewhat silly, but to avoid duplicating logic */ +dc.optionalTransition = function (enable, duration, delay, name) { + if (enable) { + return function (selection) { + return dc.transition(selection, duration, delay, name); + }; + } else { + return function (selection) { + return selection; + }; + } +}; + +// See http://stackoverflow.com/a/20773846 +dc.afterTransition = function (transition, callback) { + if (transition.empty() || !transition.duration) { + callback.call(transition); + } else { + var n = 0; + transition + .each(function () { ++n; }) + .each('end', function () { + if (!--n) { + callback.call(transition); + } + }); + } +}; + +/** + * @namespace units + * @memberof dc + * @type {{}} + */ +dc.units = {}; + +/** + * The default value for {@link dc.coordinateGridMixin#xUnits .xUnits} for the + * {@link dc.coordinateGridMixin Coordinate Grid Chart} and should + * be used when the x values are a sequence of integers. + * It is a function that counts the number of integers in the range supplied in its start and end parameters. + * @method integers + * @memberof dc.units + * @see {@link dc.coordinateGridMixin#xUnits coordinateGridMixin.xUnits} + * @example + * chart.xUnits(dc.units.integers) // already the default + * @param {Number} start + * @param {Number} end + * @returns {Number} + */ +dc.units.integers = function (start, end) { + return Math.abs(end - start); +}; + +/** + * This argument can be passed to the {@link dc.coordinateGridMixin#xUnits .xUnits} function of the to + * specify ordinal units for the x axis. Usually this parameter is used in combination with passing + * {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Ordinal-Scales.md d3.scale.ordinal} to + * {@link dc.coordinateGridMixin#x .x}. + * It just returns the domain passed to it, which for ordinal charts is an array of all values. + * @method ordinal + * @memberof dc.units + * @see {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Ordinal-Scales.md d3.scale.ordinal} + * @see {@link dc.coordinateGridMixin#xUnits coordinateGridMixin.xUnits} + * @see {@link dc.coordinateGridMixin#x coordinateGridMixin.x} + * @example + * chart.xUnits(dc.units.ordinal) + * .x(d3.scale.ordinal()) + * @param {*} start + * @param {*} end + * @param {Array<String>} domain + * @returns {Array<String>} + */ +dc.units.ordinal = function (start, end, domain) { + return domain; +}; + +/** + * @namespace fp + * @memberof dc.units + * @type {{}} + */ +dc.units.fp = {}; +/** + * This function generates an argument for the {@link dc.coordinateGridMixin Coordinate Grid Chart} + * {@link dc.coordinateGridMixin#xUnits .xUnits} function specifying that the x values are floating-point + * numbers with the given precision. + * The returned function determines how many values at the given precision will fit into the range + * supplied in its start and end parameters. + * @method precision + * @memberof dc.units.fp + * @see {@link dc.coordinateGridMixin#xUnits coordinateGridMixin.xUnits} + * @example + * // specify values (and ticks) every 0.1 units + * chart.xUnits(dc.units.fp.precision(0.1) + * // there are 500 units between 0.5 and 1 if the precision is 0.001 + * var thousandths = dc.units.fp.precision(0.001); + * thousandths(0.5, 1.0) // returns 500 + * @param {Number} precision + * @returns {Function} start-end unit function + */ +dc.units.fp.precision = function (precision) { + var _f = function (s, e) { + var d = Math.abs((e - s) / _f.resolution); + if (dc.utils.isNegligible(d - Math.floor(d))) { + return Math.floor(d); + } else { + return Math.ceil(d); + } + }; + _f.resolution = precision; + return _f; +}; + +dc.round = {}; +dc.round.floor = function (n) { + return Math.floor(n); +}; +dc.round.ceil = function (n) { + return Math.ceil(n); +}; +dc.round.round = function (n) { + return Math.round(n); +}; + +dc.override = function (obj, functionName, newFunction) { + var existingFunction = obj[functionName]; + obj['_' + functionName] = existingFunction; + obj[functionName] = newFunction; +}; + +dc.renderlet = function (_) { + if (!arguments.length) { + return dc._renderlet; + } + dc._renderlet = _; + return dc; +}; + +dc.instanceOfChart = function (o) { + return o instanceof Object && o.__dcFlag__ && true; +}; + +dc.errors = {}; + +dc.errors.Exception = function (msg) { + var _msg = msg || 'Unexpected internal error'; + + this.message = _msg; + + this.toString = function () { + return _msg; + }; + this.stack = (new Error()).stack; +}; +dc.errors.Exception.prototype = Object.create(Error.prototype); +dc.errors.Exception.prototype.constructor = dc.errors.Exception; + +dc.errors.InvalidStateException = function () { + dc.errors.Exception.apply(this, arguments); +}; + +dc.errors.InvalidStateException.prototype = Object.create(dc.errors.Exception.prototype); +dc.errors.InvalidStateException.prototype.constructor = dc.errors.InvalidStateException; + +dc.errors.BadArgumentException = function () { + dc.errors.Exception.apply(this, arguments); +}; + +dc.errors.BadArgumentException.prototype = Object.create(dc.errors.Exception.prototype); +dc.errors.BadArgumentException.prototype.constructor = dc.errors.BadArgumentException; + +/** + * The default date format for dc.js + * @name dateFormat + * @memberof dc + * @type {Function} + * @default d3.time.format('%m/%d/%Y') + */ +dc.dateFormat = d3.time.format('%m/%d/%Y'); + +/** + * @namespace printers + * @memberof dc + * @type {{}} + */ +dc.printers = {}; + +/** + * Converts a list of filters into a readable string. + * @method filters + * @memberof dc.printers + * @param {Array<dc.filters>} filters + * @returns {String} + */ +dc.printers.filters = function (filters) { + var s = ''; + + for (var i = 0; i < filters.length; ++i) { + if (i > 0) { + s += ', '; + } + s += dc.printers.filter(filters[i]); + } + + return s; +}; + +/** + * Converts a filter into a readable string. + * @method filter + * @memberof dc.printers + * @param {dc.filters|any|Array<any>} filter + * @returns {String} + */ +dc.printers.filter = function (filter) { + var s = ''; + + if (typeof filter !== 'undefined' && filter !== null) { + if (filter instanceof Array) { + if (filter.length >= 2) { + s = '[' + dc.utils.printSingleValue(filter[0]) + ' -> ' + dc.utils.printSingleValue(filter[1]) + ']'; + } else if (filter.length >= 1) { + s = dc.utils.printSingleValue(filter[0]); + } + } else { + s = dc.utils.printSingleValue(filter); + } + } + + return s; +}; + +/** + * Returns a function that given a string property name, can be used to pluck the property off an object. A function + * can be passed as the second argument to also alter the data being returned. + * + * This can be a useful shorthand method to create accessor functions. + * @method pluck + * @memberof dc + * @example + * var xPluck = dc.pluck('x'); + * var objA = {x: 1}; + * xPluck(objA) // 1 + * @example + * var xPosition = dc.pluck('x', function (x, i) { + * // `this` is the original datum, + * // `x` is the x property of the datum, + * // `i` is the position in the array + * return this.radius + x; + * }); + * dc.selectAll('.circle').data(...).x(xPosition); + * @param {String} n + * @param {Function} [f] + * @returns {Function} + */ +dc.pluck = function (n, f) { + if (!f) { + return function (d) { return d[n]; }; + } + return function (d, i) { return f.call(d, d[n], i); }; +}; + +/** + * @namespace utils + * @memberof dc + * @type {{}} + */ +dc.utils = {}; + +/** + * Print a single value filter. + * @method printSingleValue + * @memberof dc.utils + * @param {any} filter + * @returns {String} + */ +dc.utils.printSingleValue = function (filter) { + var s = '' + filter; + + if (filter instanceof Date) { + s = dc.dateFormat(filter); + } else if (typeof(filter) === 'string') { + s = filter; + } else if (dc.utils.isFloat(filter)) { + s = dc.utils.printSingleValue.fformat(filter); + } else if (dc.utils.isInteger(filter)) { + s = Math.round(filter); + } + + return s; +}; +dc.utils.printSingleValue.fformat = d3.format('.2f'); + +/** + * Arbitrary add one value to another. + * @method add + * @memberof dc.utils + * @todo + * These assume than any string r is a percentage (whether or not it includes %). + * They also generate strange results if l is a string. + * @param {String|Date|Number} l the value to modify + * @param {Number} r the amount by which to modify the value + * @param {String} [t] if `l` is a `Date`, the + * [interval](https://github.com/d3/d3-3.x-api-reference/blob/master/Time-Intervals.md#interval) in + * the `d3.time` namespace + * @returns {String|Date|Number} + */ +dc.utils.add = function (l, r, t) { + if (typeof r === 'string') { + r = r.replace('%', ''); + } + + if (l instanceof Date) { + if (typeof r === 'string') { + r = +r; + } + if (t === 'millis') { + return new Date(l.getTime() + r); + } + t = t || 'day'; + return d3.time[t].offset(l, r); + } else if (typeof r === 'string') { + var percentage = (+r / 100); + return l > 0 ? l * (1 + percentage) : l * (1 - percentage); + } else { + return l + r; + } +}; + +/** + * Arbitrary subtract one value from another. + * @method subtract + * @memberof dc.utils + * @todo + * These assume than any string r is a percentage (whether or not it includes %). + * They also generate strange results if l is a string. + * @param {String|Date|Number} l the value to modify + * @param {Number} r the amount by which to modify the value + * @param {String} [t] if `l` is a `Date`, the + * [interval](https://github.com/d3/d3-3.x-api-reference/blob/master/Time-Intervals.md#interval) in + * the `d3.time` namespace + * @returns {String|Date|Number} + */ +dc.utils.subtract = function (l, r, t) { + if (typeof r === 'string') { + r = r.replace('%', ''); + } + + if (l instanceof Date) { + if (typeof r === 'string') { + r = +r; + } + if (t === 'millis') { + return new Date(l.getTime() - r); + } + t = t || 'day'; + return d3.time[t].offset(l, -r); + } else if (typeof r === 'string') { + var percentage = (+r / 100); + return l < 0 ? l * (1 + percentage) : l * (1 - percentage); + } else { + return l - r; + } +}; + +/** + * Is the value a number? + * @method isNumber + * @memberof dc.utils + * @param {any} n + * @returns {Boolean} + */ +dc.utils.isNumber = function (n) { + return n === +n; +}; + +/** + * Is the value a float? + * @method isFloat + * @memberof dc.utils + * @param {any} n + * @returns {Boolean} + */ +dc.utils.isFloat = function (n) { + return n === +n && n !== (n | 0); +}; + +/** + * Is the value an integer? + * @method isInteger + * @memberof dc.utils + * @param {any} n + * @returns {Boolean} + */ +dc.utils.isInteger = function (n) { + return n === +n && n === (n | 0); +}; + +/** + * Is the value very close to zero? + * @method isNegligible + * @memberof dc.utils + * @param {any} n + * @returns {Boolean} + */ +dc.utils.isNegligible = function (n) { + return !dc.utils.isNumber(n) || (n < dc.constants.NEGLIGIBLE_NUMBER && n > -dc.constants.NEGLIGIBLE_NUMBER); +}; + +/** + * Ensure the value is no greater or less than the min/max values. If it is return the boundary value. + * @method clamp + * @memberof dc.utils + * @param {any} val + * @param {any} min + * @param {any} max + * @returns {any} + */ +dc.utils.clamp = function (val, min, max) { + return val < min ? min : (val > max ? max : val); +}; + +/** + * Using a simple static counter, provide a unique integer id. + * @method uniqueId + * @memberof dc.utils + * @returns {Number} + */ +var _idCounter = 0; +dc.utils.uniqueId = function () { + return ++_idCounter; +}; + +/** + * Convert a name to an ID. + * @method nameToId + * @memberof dc.utils + * @param {String} name + * @returns {String} + */ +dc.utils.nameToId = function (name) { + return name.toLowerCase().replace(/[\s]/g, '_').replace(/[\.']/g, ''); +}; + +/** + * Append or select an item on a parent element. + * @method appendOrSelect + * @memberof dc.utils + * @param {d3.selection} parent + * @param {String} selector + * @param {String} tag + * @returns {d3.selection} + */ +dc.utils.appendOrSelect = function (parent, selector, tag) { + tag = tag || selector; + var element = parent.select(selector); + if (element.empty()) { + element = parent.append(tag); + } + return element; +}; + +/** + * Return the number if the value is a number; else 0. + * @method safeNumber + * @memberof dc.utils + * @param {Number|any} n + * @returns {Number} + */ +dc.utils.safeNumber = function (n) { return dc.utils.isNumber(+n) ? +n : 0;}; + +dc.logger = {}; + +dc.logger.enableDebugLog = false; + +dc.logger.warn = function (msg) { + if (console) { + if (console.warn) { + console.warn(msg); + } else if (console.log) { + console.log(msg); + } + } + + return dc.logger; +}; + +dc.logger.debug = function (msg) { + if (dc.logger.enableDebugLog && console) { + if (console.debug) { + console.debug(msg); + } else if (console.log) { + console.log(msg); + } + } + + return dc.logger; +}; + +dc.logger.deprecate = function (fn, msg) { + // Allow logging of deprecation + var warned = false; + function deprecated () { + if (!warned) { + dc.logger.warn(msg); + warned = true; + } + return fn.apply(this, arguments); + } + return deprecated; +}; + +dc.events = { + current: null +}; + +/** + * This function triggers a throttled event function with a specified delay (in milli-seconds). Events + * that are triggered repetitively due to user interaction such brush dragging might flood the library + * and invoke more renders than can be executed in time. Using this function to wrap your event + * function allows the library to smooth out the rendering by throttling events and only responding to + * the most recent event. + * @name events.trigger + * @memberof dc + * @example + * chart.on('renderlet', function(chart) { + * // smooth the rendering through event throttling + * dc.events.trigger(function(){ + * // focus some other chart to the range selected by user on this chart + * someOtherChart.focus(chart.filter()); + * }); + * }) + * @param {Function} closure + * @param {Number} [delay] + */ +dc.events.trigger = function (closure, delay) { + if (!delay) { + closure(); + return; + } + + dc.events.current = closure; + + setTimeout(function () { + if (closure === dc.events.current) { + closure(); + } + }, delay); +}; + +/** + * The dc.js filters are functions which are passed into crossfilter to chose which records will be + * accumulated to produce values for the charts. In the crossfilter model, any filters applied on one + * dimension will affect all the other dimensions but not that one. dc always applies a filter + * function to the dimension; the function combines multiple filters and if any of them accept a + * record, it is filtered in. + * + * These filter constructors are used as appropriate by the various charts to implement brushing. We + * mention below which chart uses which filter. In some cases, many instances of a filter will be added. + * + * Each of the dc.js filters is an object with the following properties: + * * `isFiltered` - a function that returns true if a value is within the filter + * * `filterType` - a string identifying the filter, here the name of the constructor + * + * Currently these filter objects are also arrays, but this is not a requirement. Custom filters + * can be used as long as they have the properties above. + * @namespace filters + * @memberof dc + * @type {{}} + */ +dc.filters = {}; + +/** + * RangedFilter is a filter which accepts keys between `low` and `high`. It is used to implement X + * axis brushing for the {@link dc.coordinateGridMixin coordinate grid charts}. + * + * Its `filterType` is 'RangedFilter' + * @name RangedFilter + * @memberof dc.filters + * @param {Number} low + * @param {Number} high + * @returns {Array<Number>} + * @constructor + */ +dc.filters.RangedFilter = function (low, high) { + var range = new Array(low, high); + range.isFiltered = function (value) { + return value >= this[0] && value < this[1]; + }; + range.filterType = 'RangedFilter'; + + return range; +}; + +/** + * TwoDimensionalFilter is a filter which accepts a single two-dimensional value. It is used by the + * {@link dc.heatMap heat map chart} to include particular cells as they are clicked. (Rows and columns are + * filtered by filtering all the cells in the row or column.) + * + * Its `filterType` is 'TwoDimensionalFilter' + * @name TwoDimensionalFilter + * @memberof dc.filters + * @param {Array<Number>} filter + * @returns {Array<Number>} + * @constructor + */ +dc.filters.TwoDimensionalFilter = function (filter) { + if (filter === null) { return null; } + + var f = filter; + f.isFiltered = function (value) { + return value.length && value.length === f.length && + value[0] === f[0] && value[1] === f[1]; + }; + f.filterType = 'TwoDimensionalFilter'; + + return f; +}; + +/** + * The RangedTwoDimensionalFilter allows filtering all values which fit within a rectangular + * region. It is used by the {@link dc.scatterPlot scatter plot} to implement rectangular brushing. + * + * It takes two two-dimensional points in the form `[[x1,y1],[x2,y2]]`, and normalizes them so that + * `x1 <= x2` and `y1 <= y2`. It then returns a filter which accepts any points which are in the + * rectangular range including the lower values but excluding the higher values. + * + * If an array of two values are given to the RangedTwoDimensionalFilter, it interprets the values as + * two x coordinates `x1` and `x2` and returns a filter which accepts any points for which `x1 <= x < + * x2`. + * + * Its `filterType` is 'RangedTwoDimensionalFilter' + * @name RangedTwoDimensionalFilter + * @memberof dc.filters + * @param {Array<Array<Number>>} filter + * @returns {Array<Array<Number>>} + * @constructor + */ +dc.filters.RangedTwoDimensionalFilter = function (filter) { + if (filter === null) { return null; } + + var f = filter; + var fromBottomLeft; + + if (f[0] instanceof Array) { + fromBottomLeft = [ + [Math.min(filter[0][0], filter[1][0]), Math.min(filter[0][1], filter[1][1])], + [Math.max(filter[0][0], filter[1][0]), Math.max(filter[0][1], filter[1][1])] + ]; + } else { + fromBottomLeft = [[filter[0], -Infinity], [filter[1], Infinity]]; + } + + f.isFiltered = function (value) { + var x, y; + + if (value instanceof Array) { + x = value[0]; + y = value[1]; + } else { + x = value; + y = fromBottomLeft[0][1]; + } + + return x >= fromBottomLeft[0][0] && x < fromBottomLeft[1][0] && + y >= fromBottomLeft[0][1] && y < fromBottomLeft[1][1]; + }; + f.filterType = 'RangedTwoDimensionalFilter'; + + return f; +}; + +/** + * `dc.baseMixin` is an abstract functional object representing a basic `dc` chart object + * for all chart and widget implementations. Methods from the {@link #dc.baseMixin dc.baseMixin} are inherited + * and available on all chart implementations in the `dc` library. + * @name baseMixin + * @memberof dc + * @mixin + * @param {Object} _chart + * @returns {dc.baseMixin} + */ +dc.baseMixin = function (_chart) { + _chart.__dcFlag__ = dc.utils.uniqueId(); + + var _dimension; + var _group; + + var _anchor; + var _root; + var _svg; + var _isChild; + + var _minWidth = 200; + var _defaultWidthCalc = function (element) { + var width = element && element.getBoundingClientRect && element.getBoundingClientRect().width; + return (width && width > _minWidth) ? width : _minWidth; + }; + var _widthCalc = _defaultWidthCalc; + + var _minHeight = 200; + var _defaultHeightCalc = function (element) { + var height = element && element.getBoundingClientRect && element.getBoundingClientRect().height; + return (height && height > _minHeight) ? height : _minHeight; + }; + var _heightCalc = _defaultHeightCalc; + var _width, _height; + + var _keyAccessor = dc.pluck('key'); + var _valueAccessor = dc.pluck('value'); + var _label = dc.pluck('key'); + + var _ordering = dc.pluck('key'); + var _orderSort; + + var _renderLabel = false; + + var _title = function (d) { + return _chart.keyAccessor()(d) + ': ' + _chart.valueAccessor()(d); + }; + var _renderTitle = true; + var _controlsUseVisibility = false; + + var _transitionDuration = 750; + + var _transitionDelay = 0; + + var _filterPrinter = dc.printers.filters; + + var _mandatoryAttributes = ['dimension', 'group']; + + var _chartGroup = dc.constants.DEFAULT_CHART_GROUP; + + var _listeners = d3.dispatch( + 'preRender', + 'postRender', + 'preRedraw', + 'postRedraw', + 'filtered', + 'zoomed', + 'renderlet', + 'pretransition'); + + var _legend; + var _commitHandler; + + var _filters = []; + var _filterHandler = function (dimension, filters) { + if (filters.length === 0) { + dimension.filter(null); + } else if (filters.length === 1 && !filters[0].isFiltered) { + // single value and not a function-based filter + dimension.filterExact(filters[0]); + } else if (filters.length === 1 && filters[0].filterType === 'RangedFilter') { + // single range-based filter + dimension.filterRange(filters[0]); + } else { + dimension.filterFunction(function (d) { + for (var i = 0; i < filters.length; i++) { + var filter = filters[i]; + if (filter.isFiltered && filter.isFiltered(d)) { + return true; + } else if (filter <= d && filter >= d) { + return true; + } + } + return false; + }); + } + return filters; + }; + + var _data = function (group) { + return group.all(); + }; + + /** + * Set or get the height attribute of a chart. The height is applied to the SVGElement generated by + * the chart when rendered (or re-rendered). If a value is given, then it will be used to calculate + * the new height and the chart returned for method chaining. The value can either be a numeric, a + * function, or falsy. If no value is specified then the value of the current height attribute will + * be returned. + * + * By default, without an explicit height being given, the chart will select the width of its + * anchor element. If that isn't possible it defaults to 200 (provided by the + * {@link dc.baseMixin#minHeight minHeight} property). Setting the value falsy will return + * the chart to the default behavior. + * @method height + * @memberof dc.baseMixin + * @instance + * @see {@link dc.baseMixin#minHeight minHeight} + * @example + * // Default height + * chart.height(function (element) { + * var height = element && element.getBoundingClientRect && element.getBoundingClientRect().height; + * return (height && height > chart.minHeight()) ? height : chart.minHeight(); + * }); + * + * chart.height(250); // Set the chart's height to 250px; + * chart.height(function(anchor) { return doSomethingWith(anchor); }); // set the chart's height with a function + * chart.height(null); // reset the height to the default auto calculation + * @param {Number|Function} [height] + * @returns {Number|dc.baseMixin} + */ + _chart.height = function (height) { + if (!arguments.length) { + if (!dc.utils.isNumber(_height)) { + // only calculate once + _height = _heightCalc(_root.node()); + } + return _height; + } + _heightCalc = d3.functor(height || _defaultHeightCalc); + _height = undefined; + return _chart; + }; + + /** + * Set or get the width attribute of a chart. + * @method width + * @memberof dc.baseMixin + * @instance + * @see {@link dc.baseMixin#height height} + * @see {@link dc.baseMixin#minWidth minWidth} + * @example + * // Default width + * chart.width(function (element) { + * var width = element && element.getBoundingClientRect && element.getBoundingClientRect().width; + * return (width && width > chart.minWidth()) ? width : chart.minWidth(); + * }); + * @param {Number|Function} [width] + * @returns {Number|dc.baseMixin} + */ + _chart.width = function (width) { + if (!arguments.length) { + if (!dc.utils.isNumber(_width)) { + // only calculate once + _width = _widthCalc(_root.node()); + } + return _width; + } + _widthCalc = d3.functor(width || _defaultWidthCalc); + _width = undefined; + return _chart; + }; + + /** + * Set or get the minimum width attribute of a chart. This only has effect when used with the default + * {@link dc.baseMixin#width width} function. + * @method minWidth + * @memberof dc.baseMixin + * @instance + * @see {@link dc.baseMixin#width width} + * @param {Number} [minWidth=200] + * @returns {Number|dc.baseMixin} + */ + _chart.minWidth = function (minWidth) { + if (!arguments.length) { + return _minWidth; + } + _minWidth = minWidth; + return _chart; + }; + + /** + * Set or get the minimum height attribute of a chart. This only has effect when used with the default + * {@link dc.baseMixin#height height} function. + * @method minHeight + * @memberof dc.baseMixin + * @instance + * @see {@link dc.baseMixin#height height} + * @param {Number} [minHeight=200] + * @returns {Number|dc.baseMixin} + */ + _chart.minHeight = function (minHeight) { + if (!arguments.length) { + return _minHeight; + } + _minHeight = minHeight; + return _chart; + }; + + /** + * **mandatory** + * + * Set or get the dimension attribute of a chart. In `dc`, a dimension can be any valid + * {@link https://github.com/crossfilter/crossfilter/wiki/API-Reference#dimension crossfilter dimension} + * + * If a value is given, then it will be used as the new dimension. If no value is specified then + * the current dimension will be returned. + * @method dimension + * @memberof dc.baseMixin + * @instance + * @see {@link https://github.com/crossfilter/crossfilter/wiki/API-Reference#dimension crossfilter.dimension} + * @example + * var index = crossfilter([]); + * var dimension = index.dimension(dc.pluck('key')); + * chart.dimension(dimension); + * @param {crossfilter.dimension} [dimension] + * @returns {crossfilter.dimension|dc.baseMixin} + */ + _chart.dimension = function (dimension) { + if (!arguments.length) { + return _dimension; + } + _dimension = dimension; + _chart.expireCache(); + return _chart; + }; + + /** + * Set the data callback or retrieve the chart's data set. The data callback is passed the chart's + * group and by default will return + * {@link https://github.com/crossfilter/crossfilter/wiki/API-Reference#group_all group.all}. + * This behavior may be modified to, for instance, return only the top 5 groups. + * @method data + * @memberof dc.baseMixin + * @instance + * @example + * // Default data function + * chart.data(function (group) { return group.all(); }); + * + * chart.data(function (group) { return group.top(5); }); + * @param {Function} [callback] + * @returns {*|dc.baseMixin} + */ + _chart.data = function (callback) { + if (!arguments.length) { + return _data.call(_chart, _group); + } + _data = d3.functor(callback); + _chart.expireCache(); + return _chart; + }; + + /** + * **mandatory** + * + * Set or get the group attribute of a chart. In `dc` a group is a + * {@link https://github.com/crossfilter/crossfilter/wiki/API-Reference#group-map-reduce crossfilter group}. + * Usually the group should be created from the particular dimension associated with the same chart. If a value is + * given, then it will be used as the new group. + * + * If no value specified then the current group will be returned. + * If `name` is specified then it will be used to generate legend label. + * @method group + * @memberof dc.baseMixin + * @instance + * @see {@link https://github.com/crossfilter/crossfilter/wiki/API-Reference#group-map-reduce crossfilter.group} + * @example + * var index = crossfilter([]); + * var dimension = index.dimension(dc.pluck('key')); + * chart.dimension(dimension); + * chart.group(dimension.group(crossfilter.reduceSum())); + * @param {crossfilter.group} [group] + * @param {String} [name] + * @returns {crossfilter.group|dc.baseMixin} + */ + _chart.group = function (group, name) { + if (!arguments.length) { + return _group; + } + _group = group; + _chart._groupName = name; + _chart.expireCache(); + return _chart; + }; + + /** + * Get or set an accessor to order ordinal dimensions. The chart uses + * {@link https://github.com/crossfilter/crossfilter/wiki/API-Reference#quicksort_by crossfilter.quicksort.by} + * to sort elements; this accessor returns the value to order on. + * @method ordering + * @memberof dc.baseMixin + * @instance + * @see {@link https://github.com/crossfilter/crossfilter/wiki/API-Reference#quicksort_by crossfilter.quicksort.by} + * @example + * // Default ordering accessor + * _chart.ordering(dc.pluck('key')); + * @param {Function} [orderFunction] + * @returns {Function|dc.baseMixin} + */ + _chart.ordering = function (orderFunction) { + if (!arguments.length) { + return _ordering; + } + _ordering = orderFunction; + _orderSort = crossfilter.quicksort.by(_ordering); + _chart.expireCache(); + return _chart; + }; + + _chart._computeOrderedGroups = function (data) { + var dataCopy = data.slice(0); + + if (dataCopy.length <= 1) { + return dataCopy; + } + + if (!_orderSort) { + _orderSort = crossfilter.quicksort.by(_ordering); + } + + return _orderSort(dataCopy, 0, dataCopy.length); + }; + + /** + * Clear all filters associated with this chart. The same effect can be achieved by calling + * {@link dc.baseMixin#filter chart.filter(null)}. + * @method filterAll + * @memberof dc.baseMixin + * @instance + * @returns {dc.baseMixin} + */ + _chart.filterAll = function () { + return _chart.filter(null); + }; + + /** + * Execute d3 single selection in the chart's scope using the given selector and return the d3 + * selection. + * + * This function is **not chainable** since it does not return a chart instance; however the d3 + * selection result can be chained to d3 function calls. + * @method select + * @memberof dc.baseMixin + * @instance + * @see {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Selections.md#d3_select d3.select} + * @example + * // Has the same effect as d3.select('#chart-id').select(selector) + * chart.select(selector) + * @returns {d3.selection} + */ + _chart.select = function (s) { + return _root.select(s); + }; + + /** + * Execute in scope d3 selectAll using the given selector and return d3 selection result. + * + * This function is **not chainable** since it does not return a chart instance; however the d3 + * selection result can be chained to d3 function calls. + * @method selectAll + * @memberof dc.baseMixin + * @instance + * @see {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Selections.md#d3_selectAll d3.selectAll} + * @example + * // Has the same effect as d3.select('#chart-id').selectAll(selector) + * chart.selectAll(selector) + * @returns {d3.selection} + */ + _chart.selectAll = function (s) { + return _root ? _root.selectAll(s) : null; + }; + + /** + * Set the root SVGElement to either be an existing chart's root; or any valid [d3 single + * selector](https://github.com/d3/d3-3.x-api-reference/blob/master/Selections.md#selecting-elements) specifying a dom + * block element such as a div; or a dom element or d3 selection. Optionally registers the chart + * within the chartGroup. This class is called internally on chart initialization, but be called + * again to relocate the chart. However, it will orphan any previously created SVGElements. + * @method anchor + * @memberof dc.baseMixin + * @instance + * @param {anchorChart|anchorSelector|anchorNode} [parent] + * @param {String} [chartGroup] + * @returns {String|node|d3.selection|dc.baseMixin} + */ + _chart.anchor = function (parent, chartGroup) { + if (!arguments.length) { + return _anchor; + } + if (dc.instanceOfChart(parent)) { + _anchor = parent.anchor(); + _root = parent.root(); + _isChild = true; + } else if (parent) { + if (parent.select && parent.classed) { // detect d3 selection + _anchor = parent.node(); + } else { + _anchor = parent; + } + _root = d3.select(_anchor); + _root.classed(dc.constants.CHART_CLASS, true); + dc.registerChart(_chart, chartGroup); + _isChild = false; + } else { + throw new dc.errors.BadArgumentException('parent must be defined'); + } + _chartGroup = chartGroup; + return _chart; + }; + + /** + * Returns the DOM id for the chart's anchored location. + * @method anchorName + * @memberof dc.baseMixin + * @instance + * @returns {String} + */ + _chart.anchorName = function () { + var a = _chart.anchor(); + if (a && a.id) { + return a.id; + } + if (a && a.replace) { + return a.replace('#', ''); + } + return 'dc-chart' + _chart.chartID(); + }; + + /** + * Returns the root element where a chart resides. Usually it will be the parent div element where + * the SVGElement was created. You can also pass in a new root element however this is usually handled by + * dc internally. Resetting the root element on a chart outside of dc internals may have + * unexpected consequences. + * @method root + * @memberof dc.baseMixin + * @instance + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement HTMLElement} + * @param {HTMLElement} [rootElement] + * @returns {HTMLElement|dc.baseMixin} + */ + _chart.root = function (rootElement) { + if (!arguments.length) { + return _root; + } + _root = rootElement; + return _chart; + }; + + /** + * Returns the top SVGElement for this specific chart. You can also pass in a new SVGElement, + * however this is usually handled by dc internally. Resetting the SVGElement on a chart outside + * of dc internals may have unexpected consequences. + * @method svg + * @memberof dc.baseMixin + * @instance + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/SVGElement SVGElement} + * @param {SVGElement|d3.selection} [svgElement] + * @returns {SVGElement|d3.selection|dc.baseMixin} + */ + _chart.svg = function (svgElement) { + if (!arguments.length) { + return _svg; + } + _svg = svgElement; + return _chart; + }; + + /** + * Remove the chart's SVGElements from the dom and recreate the container SVGElement. + * @method resetSvg + * @memberof dc.baseMixin + * @instance + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/SVGElement SVGElement} + * @returns {SVGElement} + */ + _chart.resetSvg = function () { + _chart.select('svg').remove(); + return generateSvg(); + }; + + function sizeSvg () { + if (_svg) { + _svg + .attr('width', _chart.width()) + .attr('height', _chart.height()); + } + } + + function generateSvg () { + _svg = _chart.root().append('svg'); + sizeSvg(); + return _svg; + } + + /** + * Set or get the filter printer function. The filter printer function is used to generate human + * friendly text for filter value(s) associated with the chart instance. The text will get shown + * in the `.filter element; see {@link dc.baseMixin#turnOnControls turnOnControls}. + * + * By default dc charts use a default filter printer {@link dc.printers.filters dc.printers.filters} + * that provides simple printing support for both single value and ranged filters. + * @method filterPrinter + * @memberof dc.baseMixin + * @instance + * @example + * // for a chart with an ordinal brush, print the filters in upper case + * chart.filterPrinter(function(filters) { + * return filters.map(function(f) { return f.toUpperCase(); }).join(', '); + * }); + * // for a chart with a range brush, print the filter as start and extent + * chart.filterPrinter(function(filters) { + * return 'start ' + dc.utils.printSingleValue(filters[0][0]) + + * ' extent ' + dc.utils.printSingleValue(filters[0][1] - filters[0][0]); + * }); + * @param {Function} [filterPrinterFunction=dc.printers.filters] + * @returns {Function|dc.baseMixin} + */ + _chart.filterPrinter = function (filterPrinterFunction) { + if (!arguments.length) { + return _filterPrinter; + } + _filterPrinter = filterPrinterFunction; + return _chart; + }; + + /** + * If set, use the `visibility` attribute instead of the `display` attribute for showing/hiding + * chart reset and filter controls, for less disruption to the layout. + * @method controlsUseVisibility + * @memberof dc.baseMixin + * @instance + * @param {Boolean} [controlsUseVisibility=false] + * @returns {Boolean|dc.baseMixin} + **/ + _chart.controlsUseVisibility = function (_) { + if (!arguments.length) { + return _controlsUseVisibility; + } + _controlsUseVisibility = _; + return _chart; + }; + + /** + * Turn on optional control elements within the root element. dc currently supports the + * following html control elements. + * * root.selectAll('.reset') - elements are turned on if the chart has an active filter. This type + * of control element is usually used to store a reset link to allow user to reset filter on a + * certain chart. This element will be turned off automatically if the filter is cleared. + * * root.selectAll('.filter') elements are turned on if the chart has an active filter. The text + * content of this element is then replaced with the current filter value using the filter printer + * function. This type of element will be turned off automatically if the filter is cleared. + * @method turnOnControls + * @memberof dc.baseMixin + * @instance + * @returns {dc.baseMixin} + */ + _chart.turnOnControls = function () { + if (_root) { + var attribute = _chart.controlsUseVisibility() ? 'visibility' : 'display'; + _chart.selectAll('.reset').style(attribute, null); + _chart.selectAll('.filter').text(_filterPrinter(_chart.filters())).style(attribute, null); + } + return _chart; + }; + + /** + * Turn off optional control elements within the root element. + * @method turnOffControls + * @memberof dc.baseMixin + * @see {@link dc.baseMixin#turnOnControls turnOnControls} + * @instance + * @returns {dc.baseMixin} + */ + _chart.turnOffControls = function () { + if (_root) { + var attribute = _chart.controlsUseVisibility() ? 'visibility' : 'display'; + var value = _chart.controlsUseVisibility() ? 'hidden' : 'none'; + _chart.selectAll('.reset').style(attribute, value); + _chart.selectAll('.filter').style(attribute, value).text(_chart.filter()); + } + return _chart; + }; + + /** + * Set or get the animation transition duration (in milliseconds) for this chart instance. + * @method transitionDuration + * @memberof dc.baseMixin + * @instance + * @param {Number} [duration=750] + * @returns {Number|dc.baseMixin} + */ + _chart.transitionDuration = function (duration) { + if (!arguments.length) { + return _transitionDuration; + } + _transitionDuration = duration; + return _chart; + }; + + /** + * Set or get the animation transition delay (in milliseconds) for this chart instance. + * @method transitionDelay + * @memberof dc.baseMixin + * @instance + * @param {Number} [delay=0] + * @returns {Number|dc.baseMixin} + */ + _chart.transitionDelay = function (delay) { + if (!arguments.length) { + return _transitionDelay; + } + _transitionDelay = delay; + return _chart; + }; + + _chart._mandatoryAttributes = function (_) { + if (!arguments.length) { + return _mandatoryAttributes; + } + _mandatoryAttributes = _; + return _chart; + }; + + function checkForMandatoryAttributes (a) { + if (!_chart[a] || !_chart[a]()) { + throw new dc.errors.InvalidStateException('Mandatory attribute chart.' + a + + ' is missing on chart[#' + _chart.anchorName() + ']'); + } + } + + /** + * Invoking this method will force the chart to re-render everything from scratch. Generally it + * should only be used to render the chart for the first time on the page or if you want to make + * sure everything is redrawn from scratch instead of relying on the default incremental redrawing + * behaviour. + * @method render + * @memberof dc.baseMixin + * @instance + * @returns {dc.baseMixin} + */ + _chart.render = function () { + _height = _width = undefined; // force recalculate + _listeners.preRender(_chart); + + if (_mandatoryAttributes) { + _mandatoryAttributes.forEach(checkForMandatoryAttributes); + } + + var result = _chart._doRender(); + + if (_legend) { + _legend.render(); + } + + _chart._activateRenderlets('postRender'); + + return result; + }; + + _chart._activateRenderlets = function (event) { + _listeners.pretransition(_chart); + if (_chart.transitionDuration() > 0 && _svg) { + _svg.transition().duration(_chart.transitionDuration()).delay(_chart.transitionDelay()) + .each('end', function () { + _listeners.renderlet(_chart); + if (event) { + _listeners[event](_chart); + } + }); + } else { + _listeners.renderlet(_chart); + if (event) { + _listeners[event](_chart); + } + } + }; + + /** + * Calling redraw will cause the chart to re-render data changes incrementally. If there is no + * change in the underlying data dimension then calling this method will have no effect on the + * chart. Most chart interaction in dc will automatically trigger this method through internal + * events (in particular {@link dc.redrawAll dc.redrawAll}); therefore, you only need to + * manually invoke this function if data is manipulated outside of dc's control (for example if + * data is loaded in the background using + * {@link https://github.com/crossfilter/crossfilter/wiki/API-Reference#crossfilter_add crossfilter.add}). + * @method redraw + * @memberof dc.baseMixin + * @instance + * @returns {dc.baseMixin} + */ + _chart.redraw = function () { + sizeSvg(); + _listeners.preRedraw(_chart); + + var result = _chart._doRedraw(); + + if (_legend) { + _legend.render(); + } + + _chart._activateRenderlets('postRedraw'); + + return result; + }; + + /** + * Gets/sets the commit handler. If the chart has a commit handler, the handler will be called when + * the chart's filters have changed, in order to send the filter data asynchronously to a server. + * + * Unlike other functions in dc.js, the commit handler is asynchronous. It takes two arguments: + * a flag indicating whether this is a render (true) or a redraw (false), and a callback to be + * triggered once the commit is filtered. The callback has the standard node.js continuation signature + * with error first and result second. + * @method commitHandler + * @memberof dc.baseMixin + * @instance + * @returns {dc.baseMixin} + */ + _chart.commitHandler = function (commitHandler) { + if (!arguments.length) { + return _commitHandler; + } + _commitHandler = commitHandler; + return _chart; + }; + + /** + * Redraws all charts in the same group as this chart, typically in reaction to a filter + * change. If the chart has a {@link dc.baseMixin.commitFilter commitHandler}, it will + * be executed and waited for. + * @method redrawGroup + * @memberof dc.baseMixin + * @instance + * @returns {dc.baseMixin} + */ + _chart.redrawGroup = function () { + if (_commitHandler) { + _commitHandler(false, function (error, result) { + if (error) { + console.log(error); + } else { + dc.redrawAll(_chart.chartGroup()); + } + }); + } else { + dc.redrawAll(_chart.chartGroup()); + } + return _chart; + }; + + /** + * Renders all charts in the same group as this chart. If the chart has a + * {@link dc.baseMixin.commitFilter commitHandler}, it will be executed and waited for + * @method renderGroup + * @memberof dc.baseMixin + * @instance + * @returns {dc.baseMixin} + */ + _chart.renderGroup = function () { + if (_commitHandler) { + _commitHandler(false, function (error, result) { + if (error) { + console.log(error); + } else { + dc.renderAll(_chart.chartGroup()); + } + }); + } else { + dc.renderAll(_chart.chartGroup()); + } + return _chart; + }; + + _chart._invokeFilteredListener = function (f) { + if (f !== undefined) { + _listeners.filtered(_chart, f); + } + }; + + _chart._invokeZoomedListener = function () { + _listeners.zoomed(_chart); + }; + + var _hasFilterHandler = function (filters, filter) { + if (filter === null || typeof(filter) === 'undefined') { + return filters.length > 0; + } + return filters.some(function (f) { + return filter <= f && filter >= f; + }); + }; + + /** + * Set or get the has filter handler. The has filter handler is a function that checks to see if + * the chart's current filters include a specific filter. Using a custom has filter handler allows + * you to change the way filters are checked for and replaced. + * @method hasFilterHandler + * @memberof dc.baseMixin + * @instance + * @example + * // default has filter handler + * chart.hasFilterHandler(function (filters, filter) { + * if (filter === null || typeof(filter) === 'undefined') { + * return filters.length > 0; + * } + * return filters.some(function (f) { + * return filter <= f && filter >= f; + * }); + * }); + * + * // custom filter handler (no-op) + * chart.hasFilterHandler(function(filters, filter) { + * return false; + * }); + * @param {Function} [hasFilterHandler] + * @returns {Function|dc.baseMixin} + */ + _chart.hasFilterHandler = function (hasFilterHandler) { + if (!arguments.length) { + return _hasFilterHandler; + } + _hasFilterHandler = hasFilterHandler; + return _chart; + }; + + /** + * Check whether any active filter or a specific filter is associated with particular chart instance. + * This function is **not chainable**. + * @method hasFilter + * @memberof dc.baseMixin + * @instance + * @see {@link dc.baseMixin#hasFilterHandler hasFilterHandler} + * @param {*} [filter] + * @returns {Boolean} + */ + _chart.hasFilter = function (filter) { + return _hasFilterHandler(_filters, filter); + }; + + var _removeFilterHandler = function (filters, filter) { + for (var i = 0; i < filters.length; i++) { + if (filters[i] <= filter && filters[i] >= filter) { + filters.splice(i, 1); + break; + } + } + return filters; + }; + + /** + * Set or get the remove filter handler. The remove filter handler is a function that removes a + * filter from the chart's current filters. Using a custom remove filter handler allows you to + * change how filters are removed or perform additional work when removing a filter, e.g. when + * using a filter server other than crossfilter. + * + * Any changes should modify the `filters` array argument and return that array. + * @method removeFilterHandler + * @memberof dc.baseMixin + * @instance + * @example + * // default remove filter handler + * chart.removeFilterHandler(function (filters, filter) { + * for (var i = 0; i < filters.length; i++) { + * if (filters[i] <= filter && filters[i] >= filter) { + * filters.splice(i, 1); + * break; + * } + * } + * return filters; + * }); + * + * // custom filter handler (no-op) + * chart.removeFilterHandler(function(filters, filter) { + * return filters; + * }); + * @param {Function} [removeFilterHandler] + * @returns {Function|dc.baseMixin} + */ + _chart.removeFilterHandler = function (removeFilterHandler) { + if (!arguments.length) { + return _removeFilterHandler; + } + _removeFilterHandler = removeFilterHandler; + return _chart; + }; + + var _addFilterHandler = function (filters, filter) { + filters.push(filter); + return filters; + }; + + /** + * Set or get the add filter handler. The add filter handler is a function that adds a filter to + * the chart's filter list. Using a custom add filter handler allows you to change the way filters + * are added or perform additional work when adding a filter, e.g. when using a filter server other + * than crossfilter. + * + * Any changes should modify the `filters` array argument and return that array. + * @method addFilterHandler + * @memberof dc.baseMixin + * @instance + * @example + * // default add filter handler + * chart.addFilterHandler(function (filters, filter) { + * filters.push(filter); + * return filters; + * }); + * + * // custom filter handler (no-op) + * chart.addFilterHandler(function(filters, filter) { + * return filters; + * }); + * @param {Function} [addFilterHandler] + * @returns {Function|dc.baseMixin} + */ + _chart.addFilterHandler = function (addFilterHandler) { + if (!arguments.length) { + return _addFilterHandler; + } + _addFilterHandler = addFilterHandler; + return _chart; + }; + + var _resetFilterHandler = function (filters) { + return []; + }; + + /** + * Set or get the reset filter handler. The reset filter handler is a function that resets the + * chart's filter list by returning a new list. Using a custom reset filter handler allows you to + * change the way filters are reset, or perform additional work when resetting the filters, + * e.g. when using a filter server other than crossfilter. + * + * This function should return an array. + * @method resetFilterHandler + * @memberof dc.baseMixin + * @instance + * @example + * // default remove filter handler + * function (filters) { + * return []; + * } + * + * // custom filter handler (no-op) + * chart.resetFilterHandler(function(filters) { + * return filters; + * }); + * @param {Function} [resetFilterHandler] + * @returns {dc.baseMixin} + */ + _chart.resetFilterHandler = function (resetFilterHandler) { + if (!arguments.length) { + return _resetFilterHandler; + } + _resetFilterHandler = resetFilterHandler; + return _chart; + }; + + function applyFilters () { + if (_chart.dimension() && _chart.dimension().filter) { + var fs = _filterHandler(_chart.dimension(), _filters); + _filters = fs ? fs : _filters; + } + } + + /** + * Replace the chart filter. This is equivalent to calling `chart.filter(null).filter(filter)` + * but more efficient because the filter is only applied once. + * + * @method replaceFilter + * @memberof dc.baseMixin + * @instance + * @param {*} [filter] + * @returns {dc.baseMixin} + **/ + _chart.replaceFilter = function (filter) { + _filters = _resetFilterHandler(_filters); + _chart.filter(filter); + return _chart; + }; + + /** + * Filter the chart by the given parameter, or return the current filter if no input parameter + * is given. + * + * The filter parameter can take one of these forms: + * * A single value: the value will be toggled (added if it is not present in the current + * filters, removed if it is present) + * * An array containing a single array of values (`[[value,value,value]]`): each value is + * toggled + * * When appropriate for the chart, a {@link dc.filters dc filter object} such as + * * {@link dc.filters.RangedFilter `dc.filters.RangedFilter`} for the + * {@link dc.coordinateGridMixin dc.coordinateGridMixin} charts + * * {@link dc.filters.TwoDimensionalFilter `dc.filters.TwoDimensionalFilter`} for the + * {@link dc.heatMap heat map} + * * {@link dc.filters.RangedTwoDimensionalFilter `dc.filters.RangedTwoDimensionalFilter`} + * for the {@link dc.scatterPlot scatter plot} + * * `null`: the filter will be reset using the + * {@link dc.baseMixin#resetFilterHandler resetFilterHandler} + * + * Note that this is always a toggle (even when it doesn't make sense for the filter type). If + * you wish to replace the current filter, either call `chart.filter(null)` first - or it's more + * efficient to call {@link dc.baseMixin#replaceFilter `chart.replaceFilter(filter)`} instead. + * + * Each toggle is executed by checking if the value is already present using the + * {@link dc.baseMixin#hasFilterHandler hasFilterHandler}; if it is not present, it is added + * using the {@link dc.baseMixin#addFilterHandler addFilterHandler}; if it is already present, + * it is removed using the {@link dc.baseMixin#removeFilterHandler removeFilterHandler}. + * + * Once the filters array has been updated, the filters are applied to the + * crossfilter dimension, using the {@link dc.baseMixin#filterHandler filterHandler}. + * + * Once you have set the filters, call {@link dc.baseMixin#redrawGroup `chart.redrawGroup()`} + * (or {@link dc.redrawAll `dc.redrawAll()`}) to redraw the chart's group. + * @method filter + * @memberof dc.baseMixin + * @instance + * @see {@link dc.baseMixin#addFilterHandler addFilterHandler} + * @see {@link dc.baseMixin#removeFilterHandler removeFilterHandler} + * @see {@link dc.baseMixin#resetFilterHandler resetFilterHandler} + * @see {@link dc.baseMixin#filterHandler filterHandler} + * @example + * // filter by a single string + * chart.filter('Sunday'); + * // filter by a single age + * chart.filter(18); + * // filter by a set of states + * chart.filter([['MA', 'TX', 'ND', 'WA']]); + * // filter by range -- note the use of dc.filters.RangedFilter, which is different + * // from the syntax for filtering a crossfilter dimension directly, dimension.filter([15,20]) + * chart.filter(dc.filters.RangedFilter(15,20)); + * @param {*} [filter] + * @returns {dc.baseMixin} + */ + _chart.filter = function (filter) { + if (!arguments.length) { + return _filters.length > 0 ? _filters[0] : null; + } + if (filter instanceof Array && filter[0] instanceof Array && !filter.isFiltered) { + filter[0].forEach(function (d) { + if (_chart.hasFilter(d)) { + _removeFilterHandler(_filters, d); + } else { + _addFilterHandler(_filters, d); + } + }); + } else if (filter === null) { + _filters = _resetFilterHandler(_filters); + } else { + if (_chart.hasFilter(filter)) { + _removeFilterHandler(_filters, filter); + } else { + _addFilterHandler(_filters, filter); + } + } + applyFilters(); + _chart._invokeFilteredListener(filter); + + if (_root !== null && _chart.hasFilter()) { + _chart.turnOnControls(); + } else { + _chart.turnOffControls(); + } + + return _chart; + }; + + /** + * Returns all current filters. This method does not perform defensive cloning of the internal + * filter array before returning, therefore any modification of the returned array will effect the + * chart's internal filter storage. + * @method filters + * @memberof dc.baseMixin + * @instance + * @returns {Array<*>} + */ + _chart.filters = function () { + return _filters; + }; + + _chart.highlightSelected = function (e) { + d3.select(e).classed(dc.constants.SELECTED_CLASS, true); + d3.select(e).classed(dc.constants.DESELECTED_CLASS, false); + }; + + _chart.fadeDeselected = function (e) { + d3.select(e).classed(dc.constants.SELECTED_CLASS, false); + d3.select(e).classed(dc.constants.DESELECTED_CLASS, true); + }; + + _chart.resetHighlight = function (e) { + d3.select(e).classed(dc.constants.SELECTED_CLASS, false); + d3.select(e).classed(dc.constants.DESELECTED_CLASS, false); + }; + + /** + * This function is passed to d3 as the onClick handler for each chart. The default behavior is to + * filter on the clicked datum (passed to the callback) and redraw the chart group. + * @method onClick + * @memberof dc.baseMixin + * @instance + * @param {*} datum + */ + _chart.onClick = function (datum) { + var filter = _chart.keyAccessor()(datum); + dc.events.trigger(function () { + _chart.filter(filter); + _chart.redrawGroup(); + }); + }; + + /** + * Set or get the filter handler. The filter handler is a function that performs the filter action + * on a specific dimension. Using a custom filter handler allows you to perform additional logic + * before or after filtering. + * @method filterHandler + * @memberof dc.baseMixin + * @instance + * @see {@link https://github.com/crossfilter/crossfilter/wiki/API-Reference#dimension_filter crossfilter.dimension.filter} + * @example + * // the default filter handler handles all possible cases for the charts in dc.js + * // you can replace it with something more specialized for your own chart + * chart.filterHandler(function (dimension, filters) { + * if (filters.length === 0) { + * // the empty case (no filtering) + * dimension.filter(null); + * } else if (filters.length === 1 && !filters[0].isFiltered) { + * // single value and not a function-based filter + * dimension.filterExact(filters[0]); + * } else if (filters.length === 1 && filters[0].filterType === 'RangedFilter') { + * // single range-based filter + * dimension.filterRange(filters[0]); + * } else { + * // an array of values, or an array of filter objects + * dimension.filterFunction(function (d) { + * for (var i = 0; i < filters.length; i++) { + * var filter = filters[i]; + * if (filter.isFiltered && filter.isFiltered(d)) { + * return true; + * } else if (filter <= d && filter >= d) { + * return true; + * } + * } + * return false; + * }); + * } + * return filters; + * }); + * + * // custom filter handler + * chart.filterHandler(function(dimension, filter){ + * var newFilter = filter + 10; + * dimension.filter(newFilter); + * return newFilter; // set the actual filter value to the new value + * }); + * @param {Function} [filterHandler] + * @returns {Function|dc.baseMixin} + */ + _chart.filterHandler = function (filterHandler) { + if (!arguments.length) { + return _filterHandler; + } + _filterHandler = filterHandler; + return _chart; + }; + + // abstract function stub + _chart._doRender = function () { + // do nothing in base, should be overridden by sub-function + return _chart; + }; + + _chart._doRedraw = function () { + // do nothing in base, should be overridden by sub-function + return _chart; + }; + + _chart.legendables = function () { + // do nothing in base, should be overridden by sub-function + return []; + }; + + _chart.legendHighlight = function () { + // do nothing in base, should be overridden by sub-function + }; + + _chart.legendReset = function () { + // do nothing in base, should be overridden by sub-function + }; + + _chart.legendToggle = function () { + // do nothing in base, should be overriden by sub-function + }; + + _chart.isLegendableHidden = function () { + // do nothing in base, should be overridden by sub-function + return false; + }; + + /** + * Set or get the key accessor function. The key accessor function is used to retrieve the key + * value from the crossfilter group. Key values are used differently in different charts, for + * example keys correspond to slices in a pie chart and x axis positions in a grid coordinate chart. + * @method keyAccessor + * @memberof dc.baseMixin + * @instance + * @example + * // default key accessor + * chart.keyAccessor(function(d) { return d.key; }); + * // custom key accessor for a multi-value crossfilter reduction + * chart.keyAccessor(function(p) { return p.value.absGain; }); + * @param {Function} [keyAccessor] + * @returns {Function|dc.baseMixin} + */ + _chart.keyAccessor = function (keyAccessor) { + if (!arguments.length) { + return _keyAccessor; + } + _keyAccessor = keyAccessor; + return _chart; + }; + + /** + * Set or get the value accessor function. The value accessor function is used to retrieve the + * value from the crossfilter group. Group values are used differently in different charts, for + * example values correspond to slice sizes in a pie chart and y axis positions in a grid + * coordinate chart. + * @method valueAccessor + * @memberof dc.baseMixin + * @instance + * @example + * // default value accessor + * chart.valueAccessor(function(d) { return d.value; }); + * // custom value accessor for a multi-value crossfilter reduction + * chart.valueAccessor(function(p) { return p.value.percentageGain; }); + * @param {Function} [valueAccessor] + * @returns {Function|dc.baseMixin} + */ + _chart.valueAccessor = function (valueAccessor) { + if (!arguments.length) { + return _valueAccessor; + } + _valueAccessor = valueAccessor; + return _chart; + }; + + /** + * Set or get the label function. The chart class will use this function to render labels for each + * child element in the chart, e.g. slices in a pie chart or bubbles in a bubble chart. Not every + * chart supports the label function, for example line chart does not use this function + * at all. By default, enables labels; pass false for the second parameter if this is not desired. + * @method label + * @memberof dc.baseMixin + * @instance + * @example + * // default label function just return the key + * chart.label(function(d) { return d.key; }); + * // label function has access to the standard d3 data binding and can get quite complicated + * chart.label(function(d) { return d.data.key + '(' + Math.floor(d.data.value / all.value() * 100) + '%)'; }); + * @param {Function} [labelFunction] + * @param {Boolean} [enableLabels=true] + * @returns {Function|dc.baseMixin} + */ + _chart.label = function (labelFunction, enableLabels) { + if (!arguments.length) { + return _label; + } + _label = labelFunction; + if ((enableLabels === undefined) || enableLabels) { + _renderLabel = true; + } + return _chart; + }; + + /** + * Turn on/off label rendering + * @method renderLabel + * @memberof dc.baseMixin + * @instance + * @param {Boolean} [renderLabel=false] + * @returns {Boolean|dc.baseMixin} + */ + _chart.renderLabel = function (renderLabel) { + if (!arguments.length) { + return _renderLabel; + } + _renderLabel = renderLabel; + return _chart; + }; + + /** + * Set or get the title function. The chart class will use this function to render the SVGElement title + * (usually interpreted by browser as tooltips) for each child element in the chart, e.g. a slice + * in a pie chart or a bubble in a bubble chart. Almost every chart supports the title function; + * however in grid coordinate charts you need to turn off the brush in order to see titles, because + * otherwise the brush layer will block tooltip triggering. + * @method title + * @memberof dc.baseMixin + * @instance + * @example + * // default title function shows "key: value" + * chart.title(function(d) { return d.key + ': ' + d.value; }); + * // title function has access to the standard d3 data binding and can get quite complicated + * chart.title(function(p) { + * return p.key.getFullYear() + * + '\n' + * + 'Index Gain: ' + numberFormat(p.value.absGain) + '\n' + * + 'Index Gain in Percentage: ' + numberFormat(p.value.percentageGain) + '%\n' + * + 'Fluctuation / Index Ratio: ' + numberFormat(p.value.fluctuationPercentage) + '%'; + * }); + * @param {Function} [titleFunction] + * @returns {Function|dc.baseMixin} + */ + _chart.title = function (titleFunction) { + if (!arguments.length) { + return _title; + } + _title = titleFunction; + return _chart; + }; + + /** + * Turn on/off title rendering, or return the state of the render title flag if no arguments are + * given. + * @method renderTitle + * @memberof dc.baseMixin + * @instance + * @param {Boolean} [renderTitle=true] + * @returns {Boolean|dc.baseMixin} + */ + _chart.renderTitle = function (renderTitle) { + if (!arguments.length) { + return _renderTitle; + } + _renderTitle = renderTitle; + return _chart; + }; + + /** + * A renderlet is similar to an event listener on rendering event. Multiple renderlets can be added + * to an individual chart. Each time a chart is rerendered or redrawn the renderlets are invoked + * right after the chart finishes its transitions, giving you a way to modify the SVGElements. + * Renderlet functions take the chart instance as the only input parameter and you can + * use the dc API or use raw d3 to achieve pretty much any effect. + * + * Use {@link dc.baseMixin#on on} with a 'renderlet' prefix. + * Generates a random key for the renderlet, which makes it hard to remove. + * @method renderlet + * @memberof dc.baseMixin + * @instance + * @deprecated + * @example + * // do this instead of .renderlet(function(chart) { ... }) + * chart.on("renderlet", function(chart){ + * // mix of dc API and d3 manipulation + * chart.select('g.y').style('display', 'none'); + * // its a closure so you can also access other chart variable available in the closure scope + * moveChart.filter(chart.filter()); + * }); + * @param {Function} renderletFunction + * @returns {dc.baseMixin} + */ + _chart.renderlet = dc.logger.deprecate(function (renderletFunction) { + _chart.on('renderlet.' + dc.utils.uniqueId(), renderletFunction); + return _chart; + }, 'chart.renderlet has been deprecated. Please use chart.on("renderlet.<renderletKey>", renderletFunction)'); + + /** + * Get or set the chart group to which this chart belongs. Chart groups are rendered or redrawn + * together since it is expected they share the same underlying crossfilter data set. + * @method chartGroup + * @memberof dc.baseMixin + * @instance + * @param {String} [chartGroup] + * @returns {String|dc.baseMixin} + */ + _chart.chartGroup = function (chartGroup) { + if (!arguments.length) { + return _chartGroup; + } + if (!_isChild) { + dc.deregisterChart(_chart, _chartGroup); + } + _chartGroup = chartGroup; + if (!_isChild) { + dc.registerChart(_chart, _chartGroup); + } + return _chart; + }; + + /** + * Expire the internal chart cache. dc charts cache some data internally on a per chart basis to + * speed up rendering and avoid unnecessary calculation; however it might be useful to clear the + * cache if you have changed state which will affect rendering. For example, if you invoke + * {@link https://github.com/crossfilter/crossfilter/wiki/API-Reference#crossfilter_add crossfilter.add} + * function or reset group or dimension after rendering, it is a good idea to + * clear the cache to make sure charts are rendered properly. + * @method expireCache + * @memberof dc.baseMixin + * @instance + * @returns {dc.baseMixin} + */ + _chart.expireCache = function () { + // do nothing in base, should be overridden by sub-function + return _chart; + }; + + /** + * Attach a dc.legend widget to this chart. The legend widget will automatically draw legend labels + * based on the color setting and names associated with each group. + * @method legend + * @memberof dc.baseMixin + * @instance + * @example + * chart.legend(dc.legend().x(400).y(10).itemHeight(13).gap(5)) + * @param {dc.legend} [legend] + * @returns {dc.legend|dc.baseMixin} + */ + _chart.legend = function (legend) { + if (!arguments.length) { + return _legend; + } + _legend = legend; + _legend.parent(_chart); + return _chart; + }; + + /** + * Returns the internal numeric ID of the chart. + * @method chartID + * @memberof dc.baseMixin + * @instance + * @returns {String} + */ + _chart.chartID = function () { + return _chart.__dcFlag__; + }; + + /** + * Set chart options using a configuration object. Each key in the object will cause the method of + * the same name to be called with the value to set that attribute for the chart. + * @method options + * @memberof dc.baseMixin + * @instance + * @example + * chart.options({dimension: myDimension, group: myGroup}); + * @param {{}} opts + * @returns {dc.baseMixin} + */ + _chart.options = function (opts) { + var applyOptions = [ + 'anchor', + 'group', + 'xAxisLabel', + 'yAxisLabel', + 'stack', + 'title', + 'point', + 'getColor', + 'overlayGeoJson' + ]; + + for (var o in opts) { + if (typeof(_chart[o]) === 'function') { + if (opts[o] instanceof Array && applyOptions.indexOf(o) !== -1) { + _chart[o].apply(_chart, opts[o]); + } else { + _chart[o].call(_chart, opts[o]); + } + } else { + dc.logger.debug('Not a valid option setter name: ' + o); + } + } + return _chart; + }; + + /** + * All dc chart instance supports the following listeners. + * Supports the following events: + * * `renderlet` - This listener function will be invoked after transitions after redraw and render. Replaces the + * deprecated {@link dc.baseMixin#renderlet renderlet} method. + * * `pretransition` - Like `.on('renderlet', ...)` but the event is fired before transitions start. + * * `preRender` - This listener function will be invoked before chart rendering. + * * `postRender` - This listener function will be invoked after chart finish rendering including + * all renderlets' logic. + * * `preRedraw` - This listener function will be invoked before chart redrawing. + * * `postRedraw` - This listener function will be invoked after chart finish redrawing + * including all renderlets' logic. + * * `filtered` - This listener function will be invoked after a filter is applied, added or removed. + * * `zoomed` - This listener function will be invoked after a zoom is triggered. + * @method on + * @memberof dc.baseMixin + * @instance + * @see {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Internals.md#dispatch_on d3.dispatch.on} + * @example + * .on('renderlet', function(chart, filter){...}) + * .on('pretransition', function(chart, filter){...}) + * .on('preRender', function(chart){...}) + * .on('postRender', function(chart){...}) + * .on('preRedraw', function(chart){...}) + * .on('postRedraw', function(chart){...}) + * .on('filtered', function(chart, filter){...}) + * .on('zoomed', function(chart, filter){...}) + * @param {String} event + * @param {Function} listener + * @returns {dc.baseMixin} + */ + _chart.on = function (event, listener) { + _listeners.on(event, listener); + return _chart; + }; + + return _chart; +}; + +/** + * Margin is a mixin that provides margin utility functions for both the Row Chart and Coordinate Grid + * Charts. + * @name marginMixin + * @memberof dc + * @mixin + * @param {Object} _chart + * @returns {dc.marginMixin} + */ +dc.marginMixin = function (_chart) { + var _margin = {top: 10, right: 50, bottom: 30, left: 30}; + + /** + * Get or set the margins for a particular coordinate grid chart instance. The margins is stored as + * an associative Javascript array. + * @method margins + * @memberof dc.marginMixin + * @instance + * @example + * var leftMargin = chart.margins().left; // 30 by default + * chart.margins().left = 50; + * leftMargin = chart.margins().left; // now 50 + * @param {{top: Number, right: Number, left: Number, bottom: Number}} [margins={top: 10, right: 50, bottom: 30, left: 30}] + * @returns {{top: Number, right: Number, left: Number, bottom: Number}|dc.marginMixin} + */ + _chart.margins = function (margins) { + if (!arguments.length) { + return _margin; + } + _margin = margins; + return _chart; + }; + + _chart.effectiveWidth = function () { + return _chart.width() - _chart.margins().left - _chart.margins().right; + }; + + _chart.effectiveHeight = function () { + return _chart.height() - _chart.margins().top - _chart.margins().bottom; + }; + + return _chart; +}; + +/** + * The Color Mixin is an abstract chart functional class providing universal coloring support + * as a mix-in for any concrete chart implementation. + * @name colorMixin + * @memberof dc + * @mixin + * @param {Object} _chart + * @returns {dc.colorMixin} + */ +dc.colorMixin = function (_chart) { + var _colors = d3.scale.category20c(); + var _defaultAccessor = true; + + var _colorAccessor = function (d) { return _chart.keyAccessor()(d); }; + + /** + * Retrieve current color scale or set a new color scale. This methods accepts any function that + * operates like a d3 scale. + * @method colors + * @memberof dc.colorMixin + * @instance + * @see {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Scales.md d3.scale} + * @example + * // alternate categorical scale + * chart.colors(d3.scale.category20b()); + * // ordinal scale + * chart.colors(d3.scale.ordinal().range(['red','green','blue'])); + * // convenience method, the same as above + * chart.ordinalColors(['red','green','blue']); + * // set a linear scale + * chart.linearColors(["#4575b4", "#ffffbf", "#a50026"]); + * @param {d3.scale} [colorScale=d3.scale.category20c()] + * @returns {d3.scale|dc.colorMixin} + */ + _chart.colors = function (colorScale) { + if (!arguments.length) { + return _colors; + } + if (colorScale instanceof Array) { + _colors = d3.scale.quantize().range(colorScale); // deprecated legacy support, note: this fails for ordinal domains + } else { + _colors = d3.functor(colorScale); + } + return _chart; + }; + + /** + * Convenience method to set the color scale to + * {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Ordinal-Scales.md#ordinal d3.scale.ordinal} with + * range `r`. + * @method ordinalColors + * @memberof dc.colorMixin + * @instance + * @param {Array<String>} r + * @returns {dc.colorMixin} + */ + _chart.ordinalColors = function (r) { + return _chart.colors(d3.scale.ordinal().range(r)); + }; + + /** + * Convenience method to set the color scale to an Hcl interpolated linear scale with range `r`. + * @method linearColors + * @memberof dc.colorMixin + * @instance + * @param {Array<Number>} r + * @returns {dc.colorMixin} + */ + _chart.linearColors = function (r) { + return _chart.colors(d3.scale.linear() + .range(r) + .interpolate(d3.interpolateHcl)); + }; + + /** + * Set or the get color accessor function. This function will be used to map a data point in a + * crossfilter group to a color value on the color scale. The default function uses the key + * accessor. + * @method colorAccessor + * @memberof dc.colorMixin + * @instance + * @example + * // default index based color accessor + * .colorAccessor(function (d, i){return i;}) + * // color accessor for a multi-value crossfilter reduction + * .colorAccessor(function (d){return d.value.absGain;}) + * @param {Function} [colorAccessor] + * @returns {Function|dc.colorMixin} + */ + _chart.colorAccessor = function (colorAccessor) { + if (!arguments.length) { + return _colorAccessor; + } + _colorAccessor = colorAccessor; + _defaultAccessor = false; + return _chart; + }; + + // what is this? + _chart.defaultColorAccessor = function () { + return _defaultAccessor; + }; + + /** + * Set or get the current domain for the color mapping function. The domain must be supplied as an + * array. + * + * Note: previously this method accepted a callback function. Instead you may use a custom scale + * set by {@link dc.colorMixin#colors .colors}. + * @method colorDomain + * @memberof dc.colorMixin + * @instance + * @param {Array<String>} [domain] + * @returns {Array<String>|dc.colorMixin} + */ + _chart.colorDomain = function (domain) { + if (!arguments.length) { + return _colors.domain(); + } + _colors.domain(domain); + return _chart; + }; + + /** + * Set the domain by determining the min and max values as retrieved by + * {@link dc.colorMixin#colorAccessor .colorAccessor} over the chart's dataset. + * @method calculateColorDomain + * @memberof dc.colorMixin + * @instance + * @returns {dc.colorMixin} + */ + _chart.calculateColorDomain = function () { + var newDomain = [d3.min(_chart.data(), _chart.colorAccessor()), + d3.max(_chart.data(), _chart.colorAccessor())]; + _colors.domain(newDomain); + return _chart; + }; + + /** + * Get the color for the datum d and counter i. This is used internally by charts to retrieve a color. + * @method getColor + * @memberof dc.colorMixin + * @instance + * @param {*} d + * @param {Number} [i] + * @returns {String} + */ + _chart.getColor = function (d, i) { + return _colors(_colorAccessor.call(this, d, i)); + }; + + /** + * **Deprecated.** Get/set the color calculator. This actually replaces the + * {@link dc.colorMixin#getColor getColor} method! + * + * This is not recommended, since using a {@link dc.colorMixin#colorAccessor colorAccessor} and + * color scale ({@link dc.colorMixin#colors .colors}) is more powerful and idiomatic d3. + * @method colorCalculator + * @memberof dc.colorMixin + * @instance + * @param {*} [colorCalculator] + * @returns {Function|dc.colorMixin} + */ + _chart.colorCalculator = dc.logger.deprecate(function (colorCalculator) { + if (!arguments.length) { + return _chart.getColor; + } + _chart.getColor = colorCalculator; + return _chart; + }, 'colorMixin.colorCalculator has been deprecated. Please colorMixin.colors and colorMixin.colorAccessor instead'); + + return _chart; +}; + +/** + * Coordinate Grid is an abstract base chart designed to support a number of coordinate grid based + * concrete chart types, e.g. bar chart, line chart, and bubble chart. + * @name coordinateGridMixin + * @memberof dc + * @mixin + * @mixes dc.colorMixin + * @mixes dc.marginMixin + * @mixes dc.baseMixin + * @param {Object} _chart + * @returns {dc.coordinateGridMixin} + */ +dc.coordinateGridMixin = function (_chart) { + var GRID_LINE_CLASS = 'grid-line'; + var HORIZONTAL_CLASS = 'horizontal'; + var VERTICAL_CLASS = 'vertical'; + var Y_AXIS_LABEL_CLASS = 'y-axis-label'; + var X_AXIS_LABEL_CLASS = 'x-axis-label'; + var DEFAULT_AXIS_LABEL_PADDING = 12; + + _chart = dc.colorMixin(dc.marginMixin(dc.baseMixin(_chart))); + + _chart.colors(d3.scale.category10()); + _chart._mandatoryAttributes().push('x'); + var _parent; + var _g; + var _chartBodyG; + + var _x; + var _xOriginalDomain; + var _xAxis = d3.svg.axis().orient('bottom'); + var _xUnits = dc.units.integers; + var _xAxisPadding = 0; + var _xAxisPaddingUnit = 'day'; + var _xElasticity = false; + var _xAxisLabel; + var _xAxisLabelPadding = 0; + var _lastXDomain; + + var _y; + var _yAxis = d3.svg.axis().orient('left'); + var _yAxisPadding = 0; + var _yElasticity = false; + var _yAxisLabel; + var _yAxisLabelPadding = 0; + + var _brush = d3.svg.brush(); + var _brushOn = true; + var _round; + + var _renderHorizontalGridLine = false; + var _renderVerticalGridLine = false; + + var _refocused = false, _resizing = false; + var _unitCount; + + var _zoomScale = [1, Infinity]; + var _zoomOutRestrict = true; + + var _zoom = d3.behavior.zoom().on('zoom', zoomHandler); + var _nullZoom = d3.behavior.zoom().on('zoom', null); + var _hasBeenMouseZoomable = false; + + var _rangeChart; + var _focusChart; + + var _mouseZoomable = false; + var _clipPadding = 0; + + var _outerRangeBandPadding = 0.5; + var _rangeBandPadding = 0; + + var _useRightYAxis = false; + + /** + * When changing the domain of the x or y scale, it is necessary to tell the chart to recalculate + * and redraw the axes. (`.rescale()` is called automatically when the x or y scale is replaced + * with {@link dc.coordinateGridMixin+x .x()} or {@link dc.coordinateGridMixin#y .y()}, and has + * no effect on elastic scales.) + * @method rescale + * @memberof dc.coordinateGridMixin + * @instance + * @returns {dc.coordinateGridMixin} + */ + _chart.rescale = function () { + _unitCount = undefined; + _resizing = true; + return _chart; + }; + + _chart.resizing = function () { + return _resizing; + }; + + /** + * Get or set the range selection chart associated with this instance. Setting the range selection + * chart using this function will automatically update its selection brush when the current chart + * zooms in. In return the given range chart will also automatically attach this chart as its focus + * chart hence zoom in when range brush updates. + * + * Usually the range and focus charts will share a dimension. The range chart will set the zoom + * boundaries for the focus chart, so its dimension values must be compatible with the domain of + * the focus chart. + * + * See the [Nasdaq 100 Index](http://dc-js.github.com/dc.js/) example for this effect in action. + * @method rangeChart + * @memberof dc.coordinateGridMixin + * @instance + * @param {dc.coordinateGridMixin} [rangeChart] + * @returns {dc.coordinateGridMixin} + */ + _chart.rangeChart = function (rangeChart) { + if (!arguments.length) { + return _rangeChart; + } + _rangeChart = rangeChart; + _rangeChart.focusChart(_chart); + return _chart; + }; + + /** + * Get or set the scale extent for mouse zooms. + * @method zoomScale + * @memberof dc.coordinateGridMixin + * @instance + * @param {Array<Number|Date>} [extent=[1, Infinity]] + * @returns {Array<Number|Date>|dc.coordinateGridMixin} + */ + _chart.zoomScale = function (extent) { + if (!arguments.length) { + return _zoomScale; + } + _zoomScale = extent; + return _chart; + }; + + /** + * Get or set the zoom restriction for the chart. If true limits the zoom to origional domain of the chart. + * @method zoomOutRestrict + * @memberof dc.coordinateGridMixin + * @instance + * @param {Boolean} [zoomOutRestrict=true] + * @returns {Boolean|dc.coordinateGridMixin} + */ + _chart.zoomOutRestrict = function (zoomOutRestrict) { + if (!arguments.length) { + return _zoomOutRestrict; + } + _zoomScale[0] = zoomOutRestrict ? 1 : 0; + _zoomOutRestrict = zoomOutRestrict; + return _chart; + }; + + _chart._generateG = function (parent) { + if (parent === undefined) { + _parent = _chart.svg(); + } else { + _parent = parent; + } + + var href = window.location.href.split('#')[0]; + + _g = _parent.append('g'); + + _chartBodyG = _g.append('g').attr('class', 'chart-body') + .attr('transform', 'translate(' + _chart.margins().left + ', ' + _chart.margins().top + ')') + .attr('clip-path', 'url(' + href + '#' + getClipPathId() + ')'); + + return _g; + }; + + /** + * Get or set the root g element. This method is usually used to retrieve the g element in order to + * overlay custom svg drawing programatically. **Caution**: The root g element is usually generated + * by dc.js internals, and resetting it might produce unpredictable result. + * @method g + * @memberof dc.coordinateGridMixin + * @instance + * @param {SVGElement} [gElement] + * @returns {SVGElement|dc.coordinateGridMixin} + */ + _chart.g = function (gElement) { + if (!arguments.length) { + return _g; + } + _g = gElement; + return _chart; + }; + + /** + * Set or get mouse zoom capability flag (default: false). When turned on the chart will be + * zoomable using the mouse wheel. If the range selector chart is attached zooming will also update + * the range selection brush on the associated range selector chart. + * @method mouseZoomable + * @memberof dc.coordinateGridMixin + * @instance + * @param {Boolean} [mouseZoomable=false] + * @returns {Boolean|dc.coordinateGridMixin} + */ + _chart.mouseZoomable = function (mouseZoomable) { + if (!arguments.length) { + return _mouseZoomable; + } + _mouseZoomable = mouseZoomable; + return _chart; + }; + + /** + * Retrieve the svg group for the chart body. + * @method chartBodyG + * @memberof dc.coordinateGridMixin + * @instance + * @param {SVGElement} [chartBodyG] + * @returns {SVGElement} + */ + _chart.chartBodyG = function (chartBodyG) { + if (!arguments.length) { + return _chartBodyG; + } + _chartBodyG = chartBodyG; + return _chart; + }; + + /** + * **mandatory** + * + * Get or set the x scale. The x scale can be any d3 + * {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Quantitative-Scales.md quantitive scale} or + * {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Ordinal-Scales.md ordinal scale}. + * @method x + * @memberof dc.coordinateGridMixin + * @instance + * @see {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Scales.md d3.scale} + * @example + * // set x to a linear scale + * chart.x(d3.scale.linear().domain([-2500, 2500])) + * // set x to a time scale to generate histogram + * chart.x(d3.time.scale().domain([new Date(1985, 0, 1), new Date(2012, 11, 31)])) + * @param {d3.scale} [xScale] + * @returns {d3.scale|dc.coordinateGridMixin} + */ + _chart.x = function (xScale) { + if (!arguments.length) { + return _x; + } + _x = xScale; + _xOriginalDomain = _x.domain(); + _chart.rescale(); + return _chart; + }; + + _chart.xOriginalDomain = function () { + return _xOriginalDomain; + }; + + /** + * Set or get the xUnits function. The coordinate grid chart uses the xUnits function to calculate + * the number of data projections on x axis such as the number of bars for a bar chart or the + * number of dots for a line chart. This function is expected to return a Javascript array of all + * data points on x axis, or the number of points on the axis. [d3 time range functions + * d3.time.days, d3.time.months, and + * d3.time.years](https://github.com/d3/d3-3.x-api-reference/blob/master/Time-Intervals.md#aliases) are all valid xUnits + * function. dc.js also provides a few units function, see the {@link dc.units Units Namespace} for + * a list of built-in units functions. + * @method xUnits + * @memberof dc.coordinateGridMixin + * @instance + * @todo Add docs for utilities + * @example + * // set x units to count days + * chart.xUnits(d3.time.days); + * // set x units to count months + * chart.xUnits(d3.time.months); + * + * // A custom xUnits function can be used as long as it follows the following interface: + * // units in integer + * function(start, end, xDomain) { + * // simply calculates how many integers in the domain + * return Math.abs(end - start); + * }; + * + * // fixed units + * function(start, end, xDomain) { + * // be aware using fixed units will disable the focus/zoom ability on the chart + * return 1000; + * @param {Function} [xUnits=dc.units.integers] + * @returns {Function|dc.coordinateGridMixin} + */ + _chart.xUnits = function (xUnits) { + if (!arguments.length) { + return _xUnits; + } + _xUnits = xUnits; + return _chart; + }; + + /** + * Set or get the x axis used by a particular coordinate grid chart instance. This function is most + * useful when x axis customization is required. The x axis in dc.js is an instance of a + * {@link https://github.com/d3/d3-3.x-api-reference/blob/master/SVG-Axes.md#axis d3 axis object}; + * therefore it supports any valid d3 axis manipulation. + * + * **Caution**: The x axis is usually generated internally by dc; resetting it may cause + * unexpected results. Note also that when used as a getter, this function is not chainable: + * it returns the axis, not the chart, + * {@link https://github.com/dc-js/dc.js/wiki/FAQ#why-does-everything-break-after-a-call-to-xaxis-or-yaxis + * so attempting to call chart functions after calling `.xAxis()` will fail}. + * @method xAxis + * @memberof dc.coordinateGridMixin + * @instance + * @see {@link https://github.com/d3/d3-3.x-api-reference/blob/master/SVG-Axes.md#axis d3.svg.axis} + * @example + * // customize x axis tick format + * chart.xAxis().tickFormat(function(v) {return v + '%';}); + * // customize x axis tick values + * chart.xAxis().tickValues([0, 100, 200, 300]); + * @param {d3.svg.axis} [xAxis=d3.svg.axis().orient('bottom')] + * @returns {d3.svg.axis|dc.coordinateGridMixin} + */ + _chart.xAxis = function (xAxis) { + if (!arguments.length) { + return _xAxis; + } + _xAxis = xAxis; + return _chart; + }; + + /** + * Turn on/off elastic x axis behavior. If x axis elasticity is turned on, then the grid chart will + * attempt to recalculate the x axis range whenever a redraw event is triggered. + * @method elasticX + * @memberof dc.coordinateGridMixin + * @instance + * @param {Boolean} [elasticX=false] + * @returns {Boolean|dc.coordinateGridMixin} + */ + _chart.elasticX = function (elasticX) { + if (!arguments.length) { + return _xElasticity; + } + _xElasticity = elasticX; + return _chart; + }; + + /** + * Set or get x axis padding for the elastic x axis. The padding will be added to both end of the x + * axis if elasticX is turned on; otherwise it is ignored. + * + * Padding can be an integer or percentage in string (e.g. '10%'). Padding can be applied to + * number or date x axes. When padding a date axis, an integer represents number of units being padded + * and a percentage string will be treated the same as an integer. The unit will be determined by the + * xAxisPaddingUnit variable. + * @method xAxisPadding + * @memberof dc.coordinateGridMixin + * @instance + * @param {Number|String} [padding=0] + * @returns {Number|String|dc.coordinateGridMixin} + */ + _chart.xAxisPadding = function (padding) { + if (!arguments.length) { + return _xAxisPadding; + } + _xAxisPadding = padding; + return _chart; + }; + + /** + * Set or get x axis padding unit for the elastic x axis. The padding unit will determine which unit to + * use when applying xAxis padding if elasticX is turned on and if x-axis uses a time dimension; + * otherwise it is ignored. + * + * Padding unit is a string that will be used when the padding is calculated. Available parameters are + * the available d3 time intervals; see + * {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Time-Intervals.md#interval d3.time.interval}. + * @method xAxisPaddingUnit + * @memberof dc.coordinateGridMixin + * @instance + * @param {String} [unit='days'] + * @returns {String|dc.coordinateGridMixin} + */ + _chart.xAxisPaddingUnit = function (unit) { + if (!arguments.length) { + return _xAxisPaddingUnit; + } + _xAxisPaddingUnit = unit; + return _chart; + }; + + /** + * Returns the number of units displayed on the x axis using the unit measure configured by + * {@link dc.coordinateGridMixin#xUnits xUnits}. + * @method xUnitCount + * @memberof dc.coordinateGridMixin + * @instance + * @returns {Number} + */ + _chart.xUnitCount = function () { + if (_unitCount === undefined) { + var units = _chart.xUnits()(_chart.x().domain()[0], _chart.x().domain()[1], _chart.x().domain()); + + if (units instanceof Array) { + _unitCount = units.length; + } else { + _unitCount = units; + } + } + + return _unitCount; + }; + + /** + * Gets or sets whether the chart should be drawn with a right axis instead of a left axis. When + * used with a chart in a composite chart, allows both left and right Y axes to be shown on a + * chart. + * @method useRightYAxis + * @memberof dc.coordinateGridMixin + * @instance + * @param {Boolean} [useRightYAxis=false] + * @returns {Boolean|dc.coordinateGridMixin} + */ + _chart.useRightYAxis = function (useRightYAxis) { + if (!arguments.length) { + return _useRightYAxis; + } + _useRightYAxis = useRightYAxis; + return _chart; + }; + + /** + * Returns true if the chart is using ordinal xUnits ({@link dc.units.ordinal dc.units.ordinal}, or false + * otherwise. Most charts behave differently with ordinal data and use the result of this method to + * trigger the appropriate logic. + * @method isOrdinal + * @memberof dc.coordinateGridMixin + * @instance + * @returns {Boolean} + */ + _chart.isOrdinal = function () { + return _chart.xUnits() === dc.units.ordinal; + }; + + _chart._useOuterPadding = function () { + return true; + }; + + _chart._ordinalXDomain = function () { + var groups = _chart._computeOrderedGroups(_chart.data()); + return groups.map(_chart.keyAccessor()); + }; + + function compareDomains (d1, d2) { + return !d1 || !d2 || d1.length !== d2.length || + d1.some(function (elem, i) { return (elem && d2[i]) ? elem.toString() !== d2[i].toString() : elem === d2[i]; }); + } + + function prepareXAxis (g, render) { + if (!_chart.isOrdinal()) { + if (_chart.elasticX()) { + _x.domain([_chart.xAxisMin(), _chart.xAxisMax()]); + } + } else { // _chart.isOrdinal() + if (_chart.elasticX() || _x.domain().length === 0) { + _x.domain(_chart._ordinalXDomain()); + } + } + + // has the domain changed? + var xdom = _x.domain(); + if (render || compareDomains(_lastXDomain, xdom)) { + _chart.rescale(); + } + _lastXDomain = xdom; + + // please can't we always use rangeBands for bar charts? + if (_chart.isOrdinal()) { + _x.rangeBands([0, _chart.xAxisLength()], _rangeBandPadding, + _chart._useOuterPadding() ? _outerRangeBandPadding : 0); + } else { + _x.range([0, _chart.xAxisLength()]); + } + + _xAxis = _xAxis.scale(_chart.x()); + + renderVerticalGridLines(g); + } + + _chart.renderXAxis = function (g) { + var axisXG = g.select('g.x'); + + if (axisXG.empty()) { + axisXG = g.append('g') + .attr('class', 'axis x') + .attr('transform', 'translate(' + _chart.margins().left + ',' + _chart._xAxisY() + ')'); + } + + var axisXLab = g.select('text.' + X_AXIS_LABEL_CLASS); + if (axisXLab.empty() && _chart.xAxisLabel()) { + axisXLab = g.append('text') + .attr('class', X_AXIS_LABEL_CLASS) + .attr('transform', 'translate(' + (_chart.margins().left + _chart.xAxisLength() / 2) + ',' + + (_chart.height() - _xAxisLabelPadding) + ')') + .attr('text-anchor', 'middle'); + } + if (_chart.xAxisLabel() && axisXLab.text() !== _chart.xAxisLabel()) { + axisXLab.text(_chart.xAxisLabel()); + } + + dc.transition(axisXG, _chart.transitionDuration(), _chart.transitionDelay()) + .attr('transform', 'translate(' + _chart.margins().left + ',' + _chart._xAxisY() + ')') + .call(_xAxis); + dc.transition(axisXLab, _chart.transitionDuration(), _chart.transitionDelay()) + .attr('transform', 'translate(' + (_chart.margins().left + _chart.xAxisLength() / 2) + ',' + + (_chart.height() - _xAxisLabelPadding) + ')'); + }; + + function renderVerticalGridLines (g) { + var gridLineG = g.select('g.' + VERTICAL_CLASS); + + if (_renderVerticalGridLine) { + if (gridLineG.empty()) { + gridLineG = g.insert('g', ':first-child') + .attr('class', GRID_LINE_CLASS + ' ' + VERTICAL_CLASS) + .attr('transform', 'translate(' + _chart.margins().left + ',' + _chart.margins().top + ')'); + } + + var ticks = _xAxis.tickValues() ? _xAxis.tickValues() : + (typeof _x.ticks === 'function' ? _x.ticks(_xAxis.ticks()[0]) : _x.domain()); + + var lines = gridLineG.selectAll('line') + .data(ticks); + + // enter + var linesGEnter = lines.enter() + .append('line') + .attr('x1', function (d) { + return _x(d); + }) + .attr('y1', _chart._xAxisY() - _chart.margins().top) + .attr('x2', function (d) { + return _x(d); + }) + .attr('y2', 0) + .attr('opacity', 0); + dc.transition(linesGEnter, _chart.transitionDuration(), _chart.transitionDelay()) + .attr('opacity', 1); + + // update + dc.transition(lines, _chart.transitionDuration(), _chart.transitionDelay()) + .attr('x1', function (d) { + return _x(d); + }) + .attr('y1', _chart._xAxisY() - _chart.margins().top) + .attr('x2', function (d) { + return _x(d); + }) + .attr('y2', 0); + + // exit + lines.exit().remove(); + } else { + gridLineG.selectAll('line').remove(); + } + } + + _chart._xAxisY = function () { + return (_chart.height() - _chart.margins().bottom); + }; + + _chart.xAxisLength = function () { + return _chart.effectiveWidth(); + }; + + /** + * Set or get the x axis label. If setting the label, you may optionally include additional padding to + * the margin to make room for the label. By default the padded is set to 12 to accomodate the text height. + * @method xAxisLabel + * @memberof dc.coordinateGridMixin + * @instance + * @param {String} [labelText] + * @param {Number} [padding=12] + * @returns {String} + */ + _chart.xAxisLabel = function (labelText, padding) { + if (!arguments.length) { + return _xAxisLabel; + } + _xAxisLabel = labelText; + _chart.margins().bottom -= _xAxisLabelPadding; + _xAxisLabelPadding = (padding === undefined) ? DEFAULT_AXIS_LABEL_PADDING : padding; + _chart.margins().bottom += _xAxisLabelPadding; + return _chart; + }; + + _chart._prepareYAxis = function (g) { + if (_y === undefined || _chart.elasticY()) { + if (_y === undefined) { + _y = d3.scale.linear(); + } + var min = _chart.yAxisMin() || 0, + max = _chart.yAxisMax() || 0; + _y.domain([min, max]).rangeRound([_chart.yAxisHeight(), 0]); + } + + _y.range([_chart.yAxisHeight(), 0]); + _yAxis = _yAxis.scale(_y); + + if (_useRightYAxis) { + _yAxis.orient('right'); + } + + _chart._renderHorizontalGridLinesForAxis(g, _y, _yAxis); + }; + + _chart.renderYAxisLabel = function (axisClass, text, rotation, labelXPosition) { + labelXPosition = labelXPosition || _yAxisLabelPadding; + + var axisYLab = _chart.g().select('text.' + Y_AXIS_LABEL_CLASS + '.' + axisClass + '-label'); + var labelYPosition = (_chart.margins().top + _chart.yAxisHeight() / 2); + if (axisYLab.empty() && text) { + axisYLab = _chart.g().append('text') + .attr('transform', 'translate(' + labelXPosition + ',' + labelYPosition + '),rotate(' + rotation + ')') + .attr('class', Y_AXIS_LABEL_CLASS + ' ' + axisClass + '-label') + .attr('text-anchor', 'middle') + .text(text); + } + if (text && axisYLab.text() !== text) { + axisYLab.text(text); + } + dc.transition(axisYLab, _chart.transitionDuration(), _chart.transitionDelay()) + .attr('transform', 'translate(' + labelXPosition + ',' + labelYPosition + '),rotate(' + rotation + ')'); + }; + + _chart.renderYAxisAt = function (axisClass, axis, position) { + var axisYG = _chart.g().select('g.' + axisClass); + if (axisYG.empty()) { + axisYG = _chart.g().append('g') + .attr('class', 'axis ' + axisClass) + .attr('transform', 'translate(' + position + ',' + _chart.margins().top + ')'); + } + + dc.transition(axisYG, _chart.transitionDuration(), _chart.transitionDelay()) + .attr('transform', 'translate(' + position + ',' + _chart.margins().top + ')') + .call(axis); + }; + + _chart.renderYAxis = function () { + var axisPosition = _useRightYAxis ? (_chart.width() - _chart.margins().right) : _chart._yAxisX(); + _chart.renderYAxisAt('y', _yAxis, axisPosition); + var labelPosition = _useRightYAxis ? (_chart.width() - _yAxisLabelPadding) : _yAxisLabelPadding; + var rotation = _useRightYAxis ? 90 : -90; + _chart.renderYAxisLabel('y', _chart.yAxisLabel(), rotation, labelPosition); + }; + + _chart._renderHorizontalGridLinesForAxis = function (g, scale, axis) { + var gridLineG = g.select('g.' + HORIZONTAL_CLASS); + + if (_renderHorizontalGridLine) { + var ticks = axis.tickValues() ? axis.tickValues() : scale.ticks(axis.ticks()[0]); + + if (gridLineG.empty()) { + gridLineG = g.insert('g', ':first-child') + .attr('class', GRID_LINE_CLASS + ' ' + HORIZONTAL_CLASS) + .attr('transform', 'translate(' + _chart.margins().left + ',' + _chart.margins().top + ')'); + } + + var lines = gridLineG.selectAll('line') + .data(ticks); + + // enter + var linesGEnter = lines.enter() + .append('line') + .attr('x1', 1) + .attr('y1', function (d) { + return scale(d); + }) + .attr('x2', _chart.xAxisLength()) + .attr('y2', function (d) { + return scale(d); + }) + .attr('opacity', 0); + dc.transition(linesGEnter, _chart.transitionDuration(), _chart.transitionDelay()) + .attr('opacity', 1); + + // update + dc.transition(lines, _chart.transitionDuration(), _chart.transitionDelay()) + .attr('x1', 1) + .attr('y1', function (d) { + return scale(d); + }) + .attr('x2', _chart.xAxisLength()) + .attr('y2', function (d) { + return scale(d); + }); + + // exit + lines.exit().remove(); + } else { + gridLineG.selectAll('line').remove(); + } + }; + + _chart._yAxisX = function () { + return _chart.useRightYAxis() ? _chart.width() - _chart.margins().right : _chart.margins().left; + }; + + /** + * Set or get the y axis label. If setting the label, you may optionally include additional padding + * to the margin to make room for the label. By default the padding is set to 12 to accommodate the + * text height. + * @method yAxisLabel + * @memberof dc.coordinateGridMixin + * @instance + * @param {String} [labelText] + * @param {Number} [padding=12] + * @returns {String|dc.coordinateGridMixin} + */ + _chart.yAxisLabel = function (labelText, padding) { + if (!arguments.length) { + return _yAxisLabel; + } + _yAxisLabel = labelText; + _chart.margins().left -= _yAxisLabelPadding; + _yAxisLabelPadding = (padding === undefined) ? DEFAULT_AXIS_LABEL_PADDING : padding; + _chart.margins().left += _yAxisLabelPadding; + return _chart; + }; + + /** + * Get or set the y scale. The y scale is typically automatically determined by the chart implementation. + * @method y + * @memberof dc.coordinateGridMixin + * @instance + * @see {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Scales.md d3.scale} + * @param {d3.scale} [yScale] + * @returns {d3.scale|dc.coordinateGridMixin} + */ + _chart.y = function (yScale) { + if (!arguments.length) { + return _y; + } + _y = yScale; + _chart.rescale(); + return _chart; + }; + + /** + * Set or get the y axis used by the coordinate grid chart instance. This function is most useful + * when y axis customization is required. The y axis in dc.js is simply an instance of a [d3 axis + * object](https://github.com/d3/d3-3.x-api-reference/blob/master/SVG-Axes.md#axis); therefore it supports any + * valid d3 axis manipulation. + * + * **Caution**: The y axis is usually generated internally by dc; resetting it may cause + * unexpected results. Note also that when used as a getter, this function is not chainable: it + * returns the axis, not the chart, + * {@link https://github.com/dc-js/dc.js/wiki/FAQ#why-does-everything-break-after-a-call-to-xaxis-or-yaxis + * so attempting to call chart functions after calling `.yAxis()` will fail}. + * @method yAxis + * @memberof dc.coordinateGridMixin + * @instance + * @see {@link https://github.com/d3/d3-3.x-api-reference/blob/master/SVG-Axes.md#axis d3.svg.axis} + * @example + * // customize y axis tick format + * chart.yAxis().tickFormat(function(v) {return v + '%';}); + * // customize y axis tick values + * chart.yAxis().tickValues([0, 100, 200, 300]); + * @param {d3.svg.axis} [yAxis=d3.svg.axis().orient('left')] + * @returns {d3.svg.axis|dc.coordinateGridMixin} + */ + _chart.yAxis = function (yAxis) { + if (!arguments.length) { + return _yAxis; + } + _yAxis = yAxis; + return _chart; + }; + + /** + * Turn on/off elastic y axis behavior. If y axis elasticity is turned on, then the grid chart will + * attempt to recalculate the y axis range whenever a redraw event is triggered. + * @method elasticY + * @memberof dc.coordinateGridMixin + * @instance + * @param {Boolean} [elasticY=false] + * @returns {Boolean|dc.coordinateGridMixin} + */ + _chart.elasticY = function (elasticY) { + if (!arguments.length) { + return _yElasticity; + } + _yElasticity = elasticY; + return _chart; + }; + + /** + * Turn on/off horizontal grid lines. + * @method renderHorizontalGridLines + * @memberof dc.coordinateGridMixin + * @instance + * @param {Boolean} [renderHorizontalGridLines=false] + * @returns {Boolean|dc.coordinateGridMixin} + */ + _chart.renderHorizontalGridLines = function (renderHorizontalGridLines) { + if (!arguments.length) { + return _renderHorizontalGridLine; + } + _renderHorizontalGridLine = renderHorizontalGridLines; + return _chart; + }; + + /** + * Turn on/off vertical grid lines. + * @method renderVerticalGridLines + * @memberof dc.coordinateGridMixin + * @instance + * @param {Boolean} [renderVerticalGridLines=false] + * @returns {Boolean|dc.coordinateGridMixin} + */ + _chart.renderVerticalGridLines = function (renderVerticalGridLines) { + if (!arguments.length) { + return _renderVerticalGridLine; + } + _renderVerticalGridLine = renderVerticalGridLines; + return _chart; + }; + + /** + * Calculates the minimum x value to display in the chart. Includes xAxisPadding if set. + * @method xAxisMin + * @memberof dc.coordinateGridMixin + * @instance + * @returns {*} + */ + _chart.xAxisMin = function () { + var min = d3.min(_chart.data(), function (e) { + return _chart.keyAccessor()(e); + }); + return dc.utils.subtract(min, _xAxisPadding, _xAxisPaddingUnit); + }; + + /** + * Calculates the maximum x value to display in the chart. Includes xAxisPadding if set. + * @method xAxisMax + * @memberof dc.coordinateGridMixin + * @instance + * @returns {*} + */ + _chart.xAxisMax = function () { + var max = d3.max(_chart.data(), function (e) { + return _chart.keyAccessor()(e); + }); + return dc.utils.add(max, _xAxisPadding, _xAxisPaddingUnit); + }; + + /** + * Calculates the minimum y value to display in the chart. Includes yAxisPadding if set. + * @method yAxisMin + * @memberof dc.coordinateGridMixin + * @instance + * @returns {*} + */ + _chart.yAxisMin = function () { + var min = d3.min(_chart.data(), function (e) { + return _chart.valueAccessor()(e); + }); + return dc.utils.subtract(min, _yAxisPadding); + }; + + /** + * Calculates the maximum y value to display in the chart. Includes yAxisPadding if set. + * @method yAxisMax + * @memberof dc.coordinateGridMixin + * @instance + * @returns {*} + */ + _chart.yAxisMax = function () { + var max = d3.max(_chart.data(), function (e) { + return _chart.valueAccessor()(e); + }); + return dc.utils.add(max, _yAxisPadding); + }; + + /** + * Set or get y axis padding for the elastic y axis. The padding will be added to the top and + * bottom of the y axis if elasticY is turned on; otherwise it is ignored. + * + * Padding can be an integer or percentage in string (e.g. '10%'). Padding can be applied to + * number or date axes. When padding a date axis, an integer represents number of days being padded + * and a percentage string will be treated the same as an integer. + * @method yAxisPadding + * @memberof dc.coordinateGridMixin + * @instance + * @param {Number|String} [padding=0] + * @returns {Number|dc.coordinateGridMixin} + */ + _chart.yAxisPadding = function (padding) { + if (!arguments.length) { + return _yAxisPadding; + } + _yAxisPadding = padding; + return _chart; + }; + + _chart.yAxisHeight = function () { + return _chart.effectiveHeight(); + }; + + /** + * Set or get the rounding function used to quantize the selection when brushing is enabled. + * @method round + * @memberof dc.coordinateGridMixin + * @instance + * @example + * // set x unit round to by month, this will make sure range selection brush will + * // select whole months + * chart.round(d3.time.month.round); + * @param {Function} [round] + * @returns {Function|dc.coordinateGridMixin} + */ + _chart.round = function (round) { + if (!arguments.length) { + return _round; + } + _round = round; + return _chart; + }; + + _chart._rangeBandPadding = function (_) { + if (!arguments.length) { + return _rangeBandPadding; + } + _rangeBandPadding = _; + return _chart; + }; + + _chart._outerRangeBandPadding = function (_) { + if (!arguments.length) { + return _outerRangeBandPadding; + } + _outerRangeBandPadding = _; + return _chart; + }; + + dc.override(_chart, 'filter', function (_) { + if (!arguments.length) { + return _chart._filter(); + } + + _chart._filter(_); + + if (_) { + _chart.brush().extent(_); + } else { + _chart.brush().clear(); + } + + return _chart; + }); + + _chart.brush = function (_) { + if (!arguments.length) { + return _brush; + } + _brush = _; + return _chart; + }; + + function brushHeight () { + return _chart._xAxisY() - _chart.margins().top; + } + + _chart.renderBrush = function (g) { + if (_brushOn) { + _brush.on('brush', _chart._brushing); + _brush.on('brushstart', _chart._disableMouseZoom); + _brush.on('brushend', configureMouseZoom); + + var gBrush = g.append('g') + .attr('class', 'brush') + .attr('transform', 'translate(' + _chart.margins().left + ',' + _chart.margins().top + ')') + .call(_brush.x(_chart.x())); + _chart.setBrushY(gBrush, false); + _chart.setHandlePaths(gBrush); + + if (_chart.hasFilter()) { + _chart.redrawBrush(g, false); + } + } + }; + + _chart.setHandlePaths = function (gBrush) { + gBrush.selectAll('.resize').append('path').attr('d', _chart.resizeHandlePath); + }; + + _chart.setBrushY = function (gBrush) { + gBrush.selectAll('rect') + .attr('height', brushHeight()); + gBrush.selectAll('.resize path') + .attr('d', _chart.resizeHandlePath); + }; + + _chart.extendBrush = function () { + var extent = _brush.extent(); + if (_chart.round()) { + extent[0] = extent.map(_chart.round())[0]; + extent[1] = extent.map(_chart.round())[1]; + + _g.select('.brush') + .call(_brush.extent(extent)); + } + return extent; + }; + + _chart.brushIsEmpty = function (extent) { + return _brush.empty() || !extent || extent[1] <= extent[0]; + }; + + _chart._brushing = function () { + var extent = _chart.extendBrush(); + + _chart.redrawBrush(_g, false); + + if (_chart.brushIsEmpty(extent)) { + dc.events.trigger(function () { + _chart.filter(null); + _chart.redrawGroup(); + }, dc.constants.EVENT_DELAY); + } else { + var rangedFilter = dc.filters.RangedFilter(extent[0], extent[1]); + + dc.events.trigger(function () { + _chart.replaceFilter(rangedFilter); + _chart.redrawGroup(); + }, dc.constants.EVENT_DELAY); + } + }; + + _chart.redrawBrush = function (g, doTransition) { + if (_brushOn) { + if (_chart.filter() && _chart.brush().empty()) { + _chart.brush().extent(_chart.filter()); + } + + var gBrush = dc.optionalTransition(doTransition, _chart.transitionDuration(), _chart.transitionDelay())(g.select('g.brush')); + _chart.setBrushY(gBrush); + gBrush.call(_chart.brush() + .x(_chart.x()) + .extent(_chart.brush().extent())); + } + + _chart.fadeDeselectedArea(); + }; + + _chart.fadeDeselectedArea = function () { + // do nothing, sub-chart should override this function + }; + + // borrowed from Crossfilter example + _chart.resizeHandlePath = function (d) { + var e = +(d === 'e'), x = e ? 1 : -1, y = brushHeight() / 3; + return 'M' + (0.5 * x) + ',' + y + + 'A6,6 0 0 ' + e + ' ' + (6.5 * x) + ',' + (y + 6) + + 'V' + (2 * y - 6) + + 'A6,6 0 0 ' + e + ' ' + (0.5 * x) + ',' + (2 * y) + + 'Z' + + 'M' + (2.5 * x) + ',' + (y + 8) + + 'V' + (2 * y - 8) + + 'M' + (4.5 * x) + ',' + (y + 8) + + 'V' + (2 * y - 8); + }; + + function getClipPathId () { + return _chart.anchorName().replace(/[ .#=\[\]"]/g, '-') + '-clip'; + } + + /** + * Get or set the padding in pixels for the clip path. Once set padding will be applied evenly to + * the top, left, right, and bottom when the clip path is generated. If set to zero, the clip area + * will be exactly the chart body area minus the margins. + * @method clipPadding + * @memberof dc.coordinateGridMixin + * @instance + * @param {Number} [padding=5] + * @returns {Number|dc.coordinateGridMixin} + */ + _chart.clipPadding = function (padding) { + if (!arguments.length) { + return _clipPadding; + } + _clipPadding = padding; + return _chart; + }; + + function generateClipPath () { + var defs = dc.utils.appendOrSelect(_parent, 'defs'); + // cannot select <clippath> elements; bug in WebKit, must select by id + // https://groups.google.com/forum/#!topic/d3-js/6EpAzQ2gU9I + var id = getClipPathId(); + var chartBodyClip = dc.utils.appendOrSelect(defs, '#' + id, 'clipPath').attr('id', id); + + var padding = _clipPadding * 2; + + dc.utils.appendOrSelect(chartBodyClip, 'rect') + .attr('width', _chart.xAxisLength() + padding) + .attr('height', _chart.yAxisHeight() + padding) + .attr('transform', 'translate(-' + _clipPadding + ', -' + _clipPadding + ')'); + } + + _chart._preprocessData = function () {}; + + _chart._doRender = function () { + _chart.resetSvg(); + + _chart._preprocessData(); + + _chart._generateG(); + generateClipPath(); + + drawChart(true); + + configureMouseZoom(); + + return _chart; + }; + + _chart._doRedraw = function () { + _chart._preprocessData(); + + drawChart(false); + generateClipPath(); + + return _chart; + }; + + function drawChart (render) { + if (_chart.isOrdinal()) { + _brushOn = false; + } + + prepareXAxis(_chart.g(), render); + _chart._prepareYAxis(_chart.g()); + + _chart.plotData(); + + if (_chart.elasticX() || _resizing || render) { + _chart.renderXAxis(_chart.g()); + } + + if (_chart.elasticY() || _resizing || render) { + _chart.renderYAxis(_chart.g()); + } + + if (render) { + _chart.renderBrush(_chart.g(), false); + } else { + _chart.redrawBrush(_chart.g(), _resizing); + } + _chart.fadeDeselectedArea(); + _resizing = false; + } + + function configureMouseZoom () { + if (_mouseZoomable) { + _chart._enableMouseZoom(); + } else if (_hasBeenMouseZoomable) { + _chart._disableMouseZoom(); + } + } + + _chart._enableMouseZoom = function () { + _hasBeenMouseZoomable = true; + _zoom.x(_chart.x()) + .scaleExtent(_zoomScale) + .size([_chart.width(), _chart.height()]) + .duration(_chart.transitionDuration()); + _chart.root().call(_zoom); + }; + + _chart._disableMouseZoom = function () { + _chart.root().call(_nullZoom); + }; + + function zoomHandler () { + _refocused = true; + if (_zoomOutRestrict) { + var constraint = _xOriginalDomain; + if (_rangeChart) { + constraint = intersectExtents(constraint, _rangeChart.x().domain()); + } + var constrained = constrainExtent(_chart.x().domain(), constraint); + if (constrained) { + _chart.x().domain(constrained); + } + } + + var domain = _chart.x().domain(); + var domFilter = dc.filters.RangedFilter(domain[0], domain[1]); + + _chart.replaceFilter(domFilter); + _chart.rescale(); + _chart.redraw(); + + if (_rangeChart && !rangesEqual(_chart.filter(), _rangeChart.filter())) { + dc.events.trigger(function () { + _rangeChart.replaceFilter(domFilter); + _rangeChart.redraw(); + }); + } + + _chart._invokeZoomedListener(); + + dc.events.trigger(function () { + _chart.redrawGroup(); + }, dc.constants.EVENT_DELAY); + + _refocused = !rangesEqual(domain, _xOriginalDomain); + } + + function intersectExtents (ext1, ext2) { + if (ext1[0] > ext2[1] || ext1[1] < ext2[0]) { + console.warn('could not intersect extents'); + } + return [Math.max(ext1[0], ext2[0]), Math.min(ext1[1], ext2[1])]; + } + + function constrainExtent (extent, constraint) { + var size = extent[1] - extent[0]; + if (extent[0] < constraint[0]) { + return [constraint[0], Math.min(constraint[1], dc.utils.add(constraint[0], size, 'millis'))]; + } else if (extent[1] > constraint[1]) { + return [Math.max(constraint[0], dc.utils.subtract(constraint[1], size, 'millis')), constraint[1]]; + } else { + return null; + } + } + + /** + * Zoom this chart to focus on the given range. The given range should be an array containing only + * 2 elements (`[start, end]`) defining a range in the x domain. If the range is not given or set + * to null, then the zoom will be reset. _For focus to work elasticX has to be turned off; + * otherwise focus will be ignored. + * @method focus + * @memberof dc.coordinateGridMixin + * @instance + * @example + * chart.on('renderlet', function(chart) { + * // smooth the rendering through event throttling + * dc.events.trigger(function(){ + * // focus some other chart to the range selected by user on this chart + * someOtherChart.focus(chart.filter()); + * }); + * }) + * @param {Array<Number>} [range] + */ + _chart.focus = function (range) { + if (hasRangeSelected(range)) { + _chart.x().domain(range); + } else { + _chart.x().domain(_xOriginalDomain); + } + + _zoom.x(_chart.x()); + zoomHandler(); + }; + + _chart.refocused = function () { + return _refocused; + }; + + _chart.focusChart = function (c) { + if (!arguments.length) { + return _focusChart; + } + _focusChart = c; + _chart.on('filtered', function (chart) { + if (!chart.filter()) { + dc.events.trigger(function () { + _focusChart.x().domain(_focusChart.xOriginalDomain()); + }); + } else if (!rangesEqual(chart.filter(), _focusChart.filter())) { + dc.events.trigger(function () { + _focusChart.focus(chart.filter()); + }); + } + }); + return _chart; + }; + + function rangesEqual (range1, range2) { + if (!range1 && !range2) { + return true; + } else if (!range1 || !range2) { + return false; + } else if (range1.length === 0 && range2.length === 0) { + return true; + } else if (range1[0].valueOf() === range2[0].valueOf() && + range1[1].valueOf() === range2[1].valueOf()) { + return true; + } + return false; + } + + /** + * Turn on/off the brush-based range filter. When brushing is on then user can drag the mouse + * across a chart with a quantitative scale to perform range filtering based on the extent of the + * brush, or click on the bars of an ordinal bar chart or slices of a pie chart to filter and + * un-filter them. However turning on the brush filter will disable other interactive elements on + * the chart such as highlighting, tool tips, and reference lines. Zooming will still be possible + * if enabled, but only via scrolling (panning will be disabled.) + * @method brushOn + * @memberof dc.coordinateGridMixin + * @instance + * @param {Boolean} [brushOn=true] + * @returns {Boolean|dc.coordinateGridMixin} + */ + _chart.brushOn = function (brushOn) { + if (!arguments.length) { + return _brushOn; + } + _brushOn = brushOn; + return _chart; + }; + + function hasRangeSelected (range) { + return range instanceof Array && range.length > 1; + } + + return _chart; +}; + +/** + * Stack Mixin is an mixin that provides cross-chart support of stackability using d3.layout.stack. + * @name stackMixin + * @memberof dc + * @mixin + * @param {Object} _chart + * @returns {dc.stackMixin} + */ +dc.stackMixin = function (_chart) { + + function prepareValues (layer, layerIdx) { + var valAccessor = layer.accessor || _chart.valueAccessor(); + layer.name = String(layer.name || layerIdx); + layer.values = layer.group.all().map(function (d, i) { + return { + x: _chart.keyAccessor()(d, i), + y: layer.hidden ? null : valAccessor(d, i), + data: d, + layer: layer.name, + hidden: layer.hidden + }; + }); + + layer.values = layer.values.filter(domainFilter()); + return layer.values; + } + + var _stackLayout = d3.layout.stack() + .values(prepareValues); + + var _stack = []; + var _titles = {}; + + var _hidableStacks = false; + var _evadeDomainFilter = false; + + function domainFilter () { + if (!_chart.x() || _evadeDomainFilter) { + return d3.functor(true); + } + var xDomain = _chart.x().domain(); + if (_chart.isOrdinal()) { + // TODO #416 + //var domainSet = d3.set(xDomain); + return function () { + return true; //domainSet.has(p.x); + }; + } + if (_chart.elasticX()) { + return function () { return true; }; + } + return function (p) { + //return true; + return p.x >= xDomain[0] && p.x <= xDomain[xDomain.length - 1]; + }; + } + + /** + * Stack a new crossfilter group onto this chart with an optional custom value accessor. All stacks + * in the same chart will share the same key accessor and therefore the same set of keys. + * + * For example, in a stacked bar chart, the bars of each stack will be positioned using the same set + * of keys on the x axis, while stacked vertically. If name is specified then it will be used to + * generate the legend label. + * @method stack + * @memberof dc.stackMixin + * @instance + * @see {@link https://github.com/crossfilter/crossfilter/wiki/API-Reference#group-map-reduce crossfilter.group} + * @example + * // stack group using default accessor + * chart.stack(valueSumGroup) + * // stack group using custom accessor + * .stack(avgByDayGroup, function(d){return d.value.avgByDay;}); + * @param {crossfilter.group} group + * @param {String} [name] + * @param {Function} [accessor] + * @returns {Array<{group: crossfilter.group, name: String, accessor: Function}>|dc.stackMixin} + */ + _chart.stack = function (group, name, accessor) { + if (!arguments.length) { + return _stack; + } + + if (arguments.length <= 2) { + accessor = name; + } + + var layer = {group: group}; + if (typeof name === 'string') { + layer.name = name; + } + if (typeof accessor === 'function') { + layer.accessor = accessor; + } + _stack.push(layer); + + return _chart; + }; + + dc.override(_chart, 'group', function (g, n, f) { + if (!arguments.length) { + return _chart._group(); + } + _stack = []; + _titles = {}; + _chart.stack(g, n); + if (f) { + _chart.valueAccessor(f); + } + return _chart._group(g, n); + }); + + /** + * Allow named stacks to be hidden or shown by clicking on legend items. + * This does not affect the behavior of hideStack or showStack. + * @method hidableStacks + * @memberof dc.stackMixin + * @instance + * @param {Boolean} [hidableStacks=false] + * @returns {Boolean|dc.stackMixin} + */ + _chart.hidableStacks = function (hidableStacks) { + if (!arguments.length) { + return _hidableStacks; + } + _hidableStacks = hidableStacks; + return _chart; + }; + + function findLayerByName (n) { + var i = _stack.map(dc.pluck('name')).indexOf(n); + return _stack[i]; + } + + /** + * Hide all stacks on the chart with the given name. + * The chart must be re-rendered for this change to appear. + * @method hideStack + * @memberof dc.stackMixin + * @instance + * @param {String} stackName + * @returns {dc.stackMixin} + */ + _chart.hideStack = function (stackName) { + var layer = findLayerByName(stackName); + if (layer) { + layer.hidden = true; + } + return _chart; + }; + + /** + * Show all stacks on the chart with the given name. + * The chart must be re-rendered for this change to appear. + * @method showStack + * @memberof dc.stackMixin + * @instance + * @param {String} stackName + * @returns {dc.stackMixin} + */ + _chart.showStack = function (stackName) { + var layer = findLayerByName(stackName); + if (layer) { + layer.hidden = false; + } + return _chart; + }; + + _chart.getValueAccessorByIndex = function (index) { + return _stack[index].accessor || _chart.valueAccessor(); + }; + + _chart.yAxisMin = function () { + var min = d3.min(flattenStack(), function (p) { + return (p.y < 0) ? (p.y + p.y0) : p.y0; + }); + + return dc.utils.subtract(min, _chart.yAxisPadding()); + + }; + + _chart.yAxisMax = function () { + var max = d3.max(flattenStack(), function (p) { + return (p.y > 0) ? (p.y + p.y0) : p.y0; + }); + + return dc.utils.add(max, _chart.yAxisPadding()); + }; + + function flattenStack () { + var valueses = _chart.data().map(function (layer) { return layer.values; }); + return Array.prototype.concat.apply([], valueses); + } + + _chart.xAxisMin = function () { + var min = d3.min(flattenStack(), dc.pluck('x')); + return dc.utils.subtract(min, _chart.xAxisPadding(), _chart.xAxisPaddingUnit()); + }; + + _chart.xAxisMax = function () { + var max = d3.max(flattenStack(), dc.pluck('x')); + return dc.utils.add(max, _chart.xAxisPadding(), _chart.xAxisPaddingUnit()); + }; + + /** + * Set or get the title function. Chart class will use this function to render svg title (usually interpreted by + * browser as tooltips) for each child element in the chart, i.e. a slice in a pie chart or a bubble in a bubble chart. + * Almost every chart supports title function however in grid coordinate chart you need to turn off brush in order to + * use title otherwise the brush layer will block tooltip trigger. + * + * If the first argument is a stack name, the title function will get or set the title for that stack. If stackName + * is not provided, the first stack is implied. + * @method title + * @memberof dc.stackMixin + * @instance + * @example + * // set a title function on 'first stack' + * chart.title('first stack', function(d) { return d.key + ': ' + d.value; }); + * // get a title function from 'second stack' + * var secondTitleFunction = chart.title('second stack'); + * @param {String} [stackName] + * @param {Function} [titleAccessor] + * @returns {String|dc.stackMixin} + */ + dc.override(_chart, 'title', function (stackName, titleAccessor) { + if (!stackName) { + return _chart._title(); + } + + if (typeof stackName === 'function') { + return _chart._title(stackName); + } + if (stackName === _chart._groupName && typeof titleAccessor === 'function') { + return _chart._title(titleAccessor); + } + + if (typeof titleAccessor !== 'function') { + return _titles[stackName] || _chart._title(); + } + + _titles[stackName] = titleAccessor; + + return _chart; + }); + + /** + * Gets or sets the stack layout algorithm, which computes a baseline for each stack and + * propagates it to the next. + * @method stackLayout + * @memberof dc.stackMixin + * @instance + * @see {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Stack-Layout.md d3.layout.stack} + * @param {Function} [stack=d3.layout.stack] + * @returns {Function|dc.stackMixin} + */ + _chart.stackLayout = function (stack) { + if (!arguments.length) { + return _stackLayout; + } + _stackLayout = stack; + if (_stackLayout.values() === d3.layout.stack().values()) { + _stackLayout.values(prepareValues); + } + return _chart; + }; + + /** + * Since dc.js 2.0, there has been {@link https://github.com/dc-js/dc.js/issues/949 an issue} + * where points are filtered to the current domain. While this is a useful optimization, it is + * incorrectly implemented: the next point outside the domain is required in order to draw lines + * that are clipped to the bounds, as well as bars that are partly clipped. + * + * A fix will be included in dc.js 2.1.x, but a workaround is needed for dc.js 2.0 and until + * that fix is published, so set this flag to skip any filtering of points. + * + * Once the bug is fixed, this flag will have no effect, and it will be deprecated. + * @method evadeDomainFilter + * @memberof dc.stackMixin + * @instance + * @param {Boolean} [evadeDomainFilter=false] + * @returns {Boolean|dc.stackMixin} + */ + _chart.evadeDomainFilter = function (evadeDomainFilter) { + if (!arguments.length) { + return _evadeDomainFilter; + } + _evadeDomainFilter = evadeDomainFilter; + return _chart; + }; + + function visability (l) { + return !l.hidden; + } + + _chart.data(function () { + var layers = _stack.filter(visability); + return layers.length ? _chart.stackLayout()(layers) : []; + }); + + _chart._ordinalXDomain = function () { + var flat = flattenStack().map(dc.pluck('data')); + var ordered = _chart._computeOrderedGroups(flat); + return ordered.map(_chart.keyAccessor()); + }; + + _chart.colorAccessor(function (d) { + var layer = this.layer || this.name || d.name || d.layer; + return layer; + }); + + _chart.legendables = function () { + return _stack.map(function (layer, i) { + return { + chart: _chart, + name: layer.name, + hidden: layer.hidden || false, + color: _chart.getColor.call(layer, layer.values, i) + }; + }); + }; + + _chart.isLegendableHidden = function (d) { + var layer = findLayerByName(d.name); + return layer ? layer.hidden : false; + }; + + _chart.legendToggle = function (d) { + if (_hidableStacks) { + if (_chart.isLegendableHidden(d)) { + _chart.showStack(d.name); + } else { + _chart.hideStack(d.name); + } + //_chart.redraw(); + _chart.renderGroup(); + } + }; + + return _chart; +}; + +/** + * Cap is a mixin that groups small data elements below a _cap_ into an *others* grouping for both the + * Row and Pie Charts. + * + * The top ordered elements in the group up to the cap amount will be kept in the chart, and the rest + * will be replaced with an *others* element, with value equal to the sum of the replaced values. The + * keys of the elements below the cap limit are recorded in order to filter by those keys when the + * others* element is clicked. + * @name capMixin + * @memberof dc + * @mixin + * @param {Object} _chart + * @returns {dc.capMixin} + */ +dc.capMixin = function (_chart) { + + var _cap = Infinity; + + var _othersLabel = 'Others'; + + var _othersGrouper = function (topRows) { + var topRowsSum = d3.sum(topRows, _chart.valueAccessor()), + allRows = _chart.group().all(), + allRowsSum = d3.sum(allRows, _chart.valueAccessor()), + topKeys = topRows.map(_chart.keyAccessor()), + allKeys = allRows.map(_chart.keyAccessor()), + topSet = d3.set(topKeys), + others = allKeys.filter(function (d) {return !topSet.has(d);}); + if (allRowsSum > topRowsSum) { + return topRows.concat([{ + 'others': others, + 'key': _chart.othersLabel(), + 'value': allRowsSum - topRowsSum + }]); + } + return topRows; + }; + + _chart.cappedKeyAccessor = function (d, i) { + if (d.others) { + return d.key; + } + return _chart.keyAccessor()(d, i); + }; + + _chart.cappedValueAccessor = function (d, i) { + if (d.others) { + return d.value; + } + return _chart.valueAccessor()(d, i); + }; + + _chart.data(function (group) { + if (_cap === Infinity) { + return _chart._computeOrderedGroups(group.all()); + } else { + var topRows = group.top(_cap); // ordered by crossfilter group order (default value) + topRows = _chart._computeOrderedGroups(topRows); // re-order using ordering (default key) + if (_othersGrouper) { + return _othersGrouper(topRows); + } + return topRows; + } + }); + + /** + * Get or set the count of elements to that will be included in the cap. If there is an + * {@link dc.capMixin#othersGrouper othersGrouper}, any further elements will be combined in an + * extra element with its name determined by {@link dc.capMixin#othersLabel othersLabel}. + * + * Up through dc.js 2.0.*, capping uses + * {@link https://github.com/crossfilter/crossfilter/wiki/API-Reference#group_top group.top(N)}, + * which selects the largest items according to + * {@link https://github.com/crossfilter/crossfilter/wiki/API-Reference#group_order group.order()}. + * The chart then sorts the items according to {@link dc.baseMixin#ordering baseMixin.ordering()}. + * So the two values essentially have to agree, but if the former is incorrect (it's easy to + * forget about `group.order()`), the latter will mask the problem. This also makes + * {@link https://github.com/dc-js/dc.js/wiki/FAQ#fake-groups fake groups} difficult to + * implement. + * + * In dc.js 2.1 and forward, only + * {@link https://github.com/crossfilter/crossfilter/wiki/API-Reference#group_all group.all()} + * and `baseMixin.ordering()` are used. + * @method cap + * @memberof dc.capMixin + * @instance + * @param {Number} [count=Infinity] + * @returns {Number|dc.capMixin} + */ + _chart.cap = function (count) { + if (!arguments.length) { + return _cap; + } + _cap = count; + return _chart; + }; + + /** + * Get or set the label for *Others* slice when slices cap is specified. + * @method othersLabel + * @memberof dc.capMixin + * @instance + * @param {String} [label="Others"] + * @returns {String|dc.capMixin} + */ + _chart.othersLabel = function (label) { + if (!arguments.length) { + return _othersLabel; + } + _othersLabel = label; + return _chart; + }; + + /** + * Get or set the grouper function that will perform the insertion of data for the *Others* slice + * if the slices cap is specified. If set to a falsy value, no others will be added. By default the + * grouper function computes the sum of all values below the cap. + * @method othersGrouper + * @memberof dc.capMixin + * @instance + * @example + * // Do not show others + * chart.othersGrouper(null); + * // Default others grouper + * chart.othersGrouper(function (topRows) { + * var topRowsSum = d3.sum(topRows, _chart.valueAccessor()), + * allRows = _chart.group().all(), + * allRowsSum = d3.sum(allRows, _chart.valueAccessor()), + * topKeys = topRows.map(_chart.keyAccessor()), + * allKeys = allRows.map(_chart.keyAccessor()), + * topSet = d3.set(topKeys), + * others = allKeys.filter(function (d) {return !topSet.has(d);}); + * if (allRowsSum > topRowsSum) { + * return topRows.concat([{ + * 'others': others, + * 'key': _chart.othersLabel(), + * 'value': allRowsSum - topRowsSum + * }]); + * } + * return topRows; + * }); + * // Custom others grouper + * chart.othersGrouper(function (data) { + * // compute the value for others, presumably the sum of all values below the cap + * var othersSum = yourComputeOthersValueLogic(data) + * + * // the keys are needed to properly filter when the others element is clicked + * var othersKeys = yourComputeOthersKeysArrayLogic(data); + * + * // add the others row to the dataset + * data.push({'key': 'Others', 'value': othersSum, 'others': othersKeys }); + * + * return data; + * }); + * @param {Function} [grouperFunction] + * @returns {Function|dc.capMixin} + */ + _chart.othersGrouper = function (grouperFunction) { + if (!arguments.length) { + return _othersGrouper; + } + _othersGrouper = grouperFunction; + return _chart; + }; + + dc.override(_chart, 'onClick', function (d) { + if (d.others) { + _chart.filter([d.others]); + } + _chart._onClick(d); + }); + + return _chart; +}; + +/** + * This Mixin provides reusable functionalities for any chart that needs to visualize data using bubbles. + * @name bubbleMixin + * @memberof dc + * @mixin + * @mixes dc.colorMixin + * @param {Object} _chart + * @returns {dc.bubbleMixin} + */ +dc.bubbleMixin = function (_chart) { + var _maxBubbleRelativeSize = 0.3; + var _minRadiusWithLabel = 10; + + _chart.BUBBLE_NODE_CLASS = 'node'; + _chart.BUBBLE_CLASS = 'bubble'; + _chart.MIN_RADIUS = 10; + + _chart = dc.colorMixin(_chart); + + _chart.renderLabel(true); + + _chart.data(function (group) { + return group.top(Infinity); + }); + + var _r = d3.scale.linear().domain([0, 100]); + + var _rValueAccessor = function (d) { + return d.r; + }; + + /** + * Get or set the bubble radius scale. By default the bubble chart uses + * {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Quantitative-Scales.md#linear d3.scale.linear().domain([0, 100])} + * as its radius scale. + * @method r + * @memberof dc.bubbleMixin + * @instance + * @see {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Scales.md d3.scale} + * @param {d3.scale} [bubbleRadiusScale=d3.scale.linear().domain([0, 100])] + * @returns {d3.scale|dc.bubbleMixin} + */ + _chart.r = function (bubbleRadiusScale) { + if (!arguments.length) { + return _r; + } + _r = bubbleRadiusScale; + return _chart; + }; + + /** + * Get or set the radius value accessor function. If set, the radius value accessor function will + * be used to retrieve a data value for each bubble. The data retrieved then will be mapped using + * the r scale to the actual bubble radius. This allows you to encode a data dimension using bubble + * size. + * @method radiusValueAccessor + * @memberof dc.bubbleMixin + * @instance + * @param {Function} [radiusValueAccessor] + * @returns {Function|dc.bubbleMixin} + */ + _chart.radiusValueAccessor = function (radiusValueAccessor) { + if (!arguments.length) { + return _rValueAccessor; + } + _rValueAccessor = radiusValueAccessor; + return _chart; + }; + + _chart.rMin = function () { + var min = d3.min(_chart.data(), function (e) { + return _chart.radiusValueAccessor()(e); + }); + return min; + }; + + _chart.rMax = function () { + var max = d3.max(_chart.data(), function (e) { + return _chart.radiusValueAccessor()(e); + }); + return max; + }; + + _chart.bubbleR = function (d) { + var value = _chart.radiusValueAccessor()(d); + var r = _chart.r()(value); + if (isNaN(r) || value <= 0) { + r = 0; + } + return r; + }; + + var labelFunction = function (d) { + return _chart.label()(d); + }; + + var shouldLabel = function (d) { + return (_chart.bubbleR(d) > _minRadiusWithLabel); + }; + + var labelOpacity = function (d) { + return shouldLabel(d) ? 1 : 0; + }; + + var labelPointerEvent = function (d) { + return shouldLabel(d) ? 'all' : 'none'; + }; + + _chart._doRenderLabel = function (bubbleGEnter) { + if (_chart.renderLabel()) { + var label = bubbleGEnter.select('text'); + + if (label.empty()) { + label = bubbleGEnter.append('text') + .attr('text-anchor', 'middle') + .attr('dy', '.3em') + .on('click', _chart.onClick); + } + + label + .attr('opacity', 0) + .attr('pointer-events', labelPointerEvent) + .text(labelFunction); + dc.transition(label, _chart.transitionDuration(), _chart.transitionDelay()) + .attr('opacity', labelOpacity); + } + }; + + _chart.doUpdateLabels = function (bubbleGEnter) { + if (_chart.renderLabel()) { + var labels = bubbleGEnter.select('text') + .attr('pointer-events', labelPointerEvent) + .text(labelFunction); + dc.transition(labels, _chart.transitionDuration(), _chart.transitionDelay()) + .attr('opacity', labelOpacity); + } + }; + + var titleFunction = function (d) { + return _chart.title()(d); + }; + + _chart._doRenderTitles = function (g) { + if (_chart.renderTitle()) { + var title = g.select('title'); + + if (title.empty()) { + g.append('title').text(titleFunction); + } + } + }; + + _chart.doUpdateTitles = function (g) { + if (_chart.renderTitle()) { + g.select('title').text(titleFunction); + } + }; + + /** + * Get or set the minimum radius. This will be used to initialize the radius scale's range. + * @method minRadius + * @memberof dc.bubbleMixin + * @instance + * @param {Number} [radius=10] + * @returns {Number|dc.bubbleMixin} + */ + _chart.minRadius = function (radius) { + if (!arguments.length) { + return _chart.MIN_RADIUS; + } + _chart.MIN_RADIUS = radius; + return _chart; + }; + + /** + * Get or set the minimum radius for label rendering. If a bubble's radius is less than this value + * then no label will be rendered. + * @method minRadiusWithLabel + * @memberof dc.bubbleMixin + * @instance + * @param {Number} [radius=10] + * @returns {Number|dc.bubbleMixin} + */ + + _chart.minRadiusWithLabel = function (radius) { + if (!arguments.length) { + return _minRadiusWithLabel; + } + _minRadiusWithLabel = radius; + return _chart; + }; + + /** + * Get or set the maximum relative size of a bubble to the length of x axis. This value is useful + * when the difference in radius between bubbles is too great. + * @method maxBubbleRelativeSize + * @memberof dc.bubbleMixin + * @instance + * @param {Number} [relativeSize=0.3] + * @returns {Number|dc.bubbleMixin} + */ + _chart.maxBubbleRelativeSize = function (relativeSize) { + if (!arguments.length) { + return _maxBubbleRelativeSize; + } + _maxBubbleRelativeSize = relativeSize; + return _chart; + }; + + _chart.fadeDeselectedArea = function () { + if (_chart.hasFilter()) { + _chart.selectAll('g.' + _chart.BUBBLE_NODE_CLASS).each(function (d) { + if (_chart.isSelectedNode(d)) { + _chart.highlightSelected(this); + } else { + _chart.fadeDeselected(this); + } + }); + } else { + _chart.selectAll('g.' + _chart.BUBBLE_NODE_CLASS).each(function () { + _chart.resetHighlight(this); + }); + } + }; + + _chart.isSelectedNode = function (d) { + return _chart.hasFilter(d.key); + }; + + _chart.onClick = function (d) { + var filter = d.key; + dc.events.trigger(function () { + _chart.filter(filter); + _chart.redrawGroup(); + }); + }; + + return _chart; +}; + +/** + * The pie chart implementation is usually used to visualize a small categorical distribution. The pie + * chart uses keyAccessor to determine the slices, and valueAccessor to calculate the size of each + * slice relative to the sum of all values. Slices are ordered by {@link dc.baseMixin#ordering ordering} + * which defaults to sorting by key. + * + * Examples: + * - {@link http://dc-js.github.com/dc.js/ Nasdaq 100 Index} + * @class pieChart + * @memberof dc + * @mixes dc.capMixin + * @mixes dc.colorMixin + * @mixes dc.baseMixin + * @example + * // create a pie chart under #chart-container1 element using the default global chart group + * var chart1 = dc.pieChart('#chart-container1'); + * // create a pie chart under #chart-container2 element using chart group A + * var chart2 = dc.pieChart('#chart-container2', 'chartGroupA'); + * @param {String|node|d3.selection} parent - Any valid + * {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Selections.md#selecting-elements d3 single selector} specifying + * a dom block element such as a div; or a dom element or d3 selection. + * @param {String} [chartGroup] - The name of the chart group this chart instance should be placed in. + * Interaction with a chart will only trigger events and redraws within the chart's group. + * @returns {dc.pieChart} + */ +dc.pieChart = function (parent, chartGroup) { + var DEFAULT_MIN_ANGLE_FOR_LABEL = 0.5; + + var _sliceCssClass = 'pie-slice'; + var _labelCssClass = 'pie-label'; + var _sliceGroupCssClass = 'pie-slice-group'; + var _labelGroupCssClass = 'pie-label-group'; + var _emptyCssClass = 'empty-chart'; + var _emptyTitle = 'empty'; + + var _radius, + _givenRadius, // specified radius, if any + _innerRadius = 0, + _externalRadiusPadding = 0; + + var _g; + var _cx; + var _cy; + var _minAngleForLabel = DEFAULT_MIN_ANGLE_FOR_LABEL; + var _externalLabelRadius; + var _drawPaths = false; + var _chart = dc.capMixin(dc.colorMixin(dc.baseMixin({}))); + + _chart.colorAccessor(_chart.cappedKeyAccessor); + + _chart.title(function (d) { + return _chart.cappedKeyAccessor(d) + ': ' + _chart.cappedValueAccessor(d); + }); + + /** + * Get or set the maximum number of slices the pie chart will generate. The top slices are determined by + * value from high to low. Other slices exeeding the cap will be rolled up into one single *Others* slice. + * @method slicesCap + * @memberof dc.pieChart + * @instance + * @param {Number} [cap] + * @returns {Number|dc.pieChart} + */ + _chart.slicesCap = _chart.cap; + + _chart.label(_chart.cappedKeyAccessor); + _chart.renderLabel(true); + + _chart.transitionDuration(350); + _chart.transitionDelay(0); + + _chart._doRender = function () { + _chart.resetSvg(); + + _g = _chart.svg() + .append('g') + .attr('transform', 'translate(' + _chart.cx() + ',' + _chart.cy() + ')'); + + _g.append('g').attr('class', _sliceGroupCssClass); + _g.append('g').attr('class', _labelGroupCssClass); + + drawChart(); + + return _chart; + }; + + function drawChart () { + // set radius from chart size if none given, or if given radius is too large + var maxRadius = d3.min([_chart.width(), _chart.height()]) / 2; + _radius = _givenRadius && _givenRadius < maxRadius ? _givenRadius : maxRadius; + + var arc = buildArcs(); + + var pie = pieLayout(); + var pieData; + // if we have data... + if (d3.sum(_chart.data(), _chart.valueAccessor())) { + pieData = pie(_chart.data()); + _g.classed(_emptyCssClass, false); + } else { + // otherwise we'd be getting NaNs, so override + // note: abuse others for its ignoring the value accessor + pieData = pie([{key: _emptyTitle, value: 1, others: [_emptyTitle]}]); + _g.classed(_emptyCssClass, true); + } + + if (_g) { + var slices = _g.select('g.' + _sliceGroupCssClass) + .selectAll('g.' + _sliceCssClass) + .data(pieData); + + var labels = _g.select('g.' + _labelGroupCssClass) + .selectAll('text.' + _labelCssClass) + .data(pieData); + + createElements(slices, labels, arc, pieData); + + updateElements(pieData, arc); + + removeElements(slices, labels); + + highlightFilter(); + + dc.transition(_g, _chart.transitionDuration(), _chart.transitionDelay()) + .attr('transform', 'translate(' + _chart.cx() + ',' + _chart.cy() + ')'); + } + } + + function createElements (slices, labels, arc, pieData) { + var slicesEnter = createSliceNodes(slices); + + createSlicePath(slicesEnter, arc); + + createTitles(slicesEnter); + + createLabels(labels, pieData, arc); + } + + function createSliceNodes (slices) { + var slicesEnter = slices + .enter() + .append('g') + .attr('class', function (d, i) { + return _sliceCssClass + ' _' + i; + }); + return slicesEnter; + } + + function createSlicePath (slicesEnter, arc) { + var slicePath = slicesEnter.append('path') + .attr('fill', fill) + .on('click', onClick) + .attr('d', function (d, i) { + return safeArc(d, i, arc); + }); + + var transition = dc.transition(slicePath, _chart.transitionDuration(), _chart.transitionDelay()); + if (transition.attrTween) { + transition.attrTween('d', tweenPie); + } + } + + function createTitles (slicesEnter) { + if (_chart.renderTitle()) { + slicesEnter.append('title').text(function (d) { + return _chart.title()(d.data); + }); + } + } + + _chart._applyLabelText = function (labels) { + labels + .text(function (d) { + var data = d.data; + if ((sliceHasNoData(data) || sliceTooSmall(d)) && !isSelectedSlice(d)) { + return ''; + } + return _chart.label()(d.data); + }); + }; + + function positionLabels (labels, arc) { + _chart._applyLabelText(labels); + dc.transition(labels, _chart.transitionDuration(), _chart.transitionDelay()) + .attr('transform', function (d) { + return labelPosition(d, arc); + }) + .attr('text-anchor', 'middle'); + } + + function highlightSlice (i, whether) { + _chart.select('g.pie-slice._' + i) + .classed('highlight', whether); + } + + function createLabels (labels, pieData, arc) { + if (_chart.renderLabel()) { + var labelsEnter = labels + .enter() + .append('text') + .attr('class', function (d, i) { + var classes = _sliceCssClass + ' ' + _labelCssClass + ' _' + i; + if (_externalLabelRadius) { + classes += ' external'; + } + return classes; + }) + .on('click', onClick) + .on('mouseover', function (d, i) { + highlightSlice(i, true); + }) + .on('mouseout', function (d, i) { + highlightSlice(i, false); + }); + positionLabels(labelsEnter, arc); + if (_externalLabelRadius && _drawPaths) { + updateLabelPaths(pieData, arc); + } + } + } + + function updateLabelPaths (pieData, arc) { + var polyline = _g.selectAll('polyline.' + _sliceCssClass) + .data(pieData); + + polyline + .enter() + .append('polyline') + .attr('class', function (d, i) { + return 'pie-path _' + i + ' ' + _sliceCssClass; + }) + .on('click', onClick) + .on('mouseover', function (d, i) { + highlightSlice(i, true); + }) + .on('mouseout', function (d, i) { + highlightSlice(i, false); + }); + + polyline.exit().remove(); + var arc2 = d3.svg.arc() + .outerRadius(_radius - _externalRadiusPadding + _externalLabelRadius) + .innerRadius(_radius - _externalRadiusPadding); + var transition = dc.transition(polyline, _chart.transitionDuration(), _chart.transitionDelay()); + // this is one rare case where d3.selection differs from d3.transition + if (transition.attrTween) { + transition + .attrTween('points', function (d) { + var current = this._current || d; + current = {startAngle: current.startAngle, endAngle: current.endAngle}; + var interpolate = d3.interpolate(current, d); + this._current = interpolate(0); + return function (t) { + var d2 = interpolate(t); + return [arc.centroid(d2), arc2.centroid(d2)]; + }; + }); + } else { + transition.attr('points', function (d) { + return [arc.centroid(d), arc2.centroid(d)]; + }); + } + transition.style('visibility', function (d) { + return d.endAngle - d.startAngle < 0.0001 ? 'hidden' : 'visible'; + }); + + } + + function updateElements (pieData, arc) { + updateSlicePaths(pieData, arc); + updateLabels(pieData, arc); + updateTitles(pieData); + } + + function updateSlicePaths (pieData, arc) { + var slicePaths = _g.selectAll('g.' + _sliceCssClass) + .data(pieData) + .select('path') + .attr('d', function (d, i) { + return safeArc(d, i, arc); + }); + var transition = dc.transition(slicePaths, _chart.transitionDuration(), _chart.transitionDelay()); + if (transition.attrTween) { + transition.attrTween('d', tweenPie); + } + transition.attr('fill', fill); + } + + function updateLabels (pieData, arc) { + if (_chart.renderLabel()) { + var labels = _g.selectAll('text.' + _labelCssClass) + .data(pieData); + positionLabels(labels, arc); + if (_externalLabelRadius && _drawPaths) { + updateLabelPaths(pieData, arc); + } + } + } + + function updateTitles (pieData) { + if (_chart.renderTitle()) { + _g.selectAll('g.' + _sliceCssClass) + .data(pieData) + .select('title') + .text(function (d) { + return _chart.title()(d.data); + }); + } + } + + function removeElements (slices, labels) { + slices.exit().remove(); + labels.exit().remove(); + } + + function highlightFilter () { + if (_chart.hasFilter()) { + _chart.selectAll('g.' + _sliceCssClass).each(function (d) { + if (isSelectedSlice(d)) { + _chart.highlightSelected(this); + } else { + _chart.fadeDeselected(this); + } + }); + } else { + _chart.selectAll('g.' + _sliceCssClass).each(function () { + _chart.resetHighlight(this); + }); + } + } + + /** + * Get or set the external radius padding of the pie chart. This will force the radius of the + * pie chart to become smaller or larger depending on the value. + * @method externalRadiusPadding + * @memberof dc.pieChart + * @instance + * @param {Number} [externalRadiusPadding=0] + * @returns {Number|dc.pieChart} + */ + _chart.externalRadiusPadding = function (externalRadiusPadding) { + if (!arguments.length) { + return _externalRadiusPadding; + } + _externalRadiusPadding = externalRadiusPadding; + return _chart; + }; + + /** + * Get or set the inner radius of the pie chart. If the inner radius is greater than 0px then the + * pie chart will be rendered as a doughnut chart. + * @method innerRadius + * @memberof dc.pieChart + * @instance + * @param {Number} [innerRadius=0] + * @returns {Number|dc.pieChart} + */ + _chart.innerRadius = function (innerRadius) { + if (!arguments.length) { + return _innerRadius; + } + _innerRadius = innerRadius; + return _chart; + }; + + /** + * Get or set the outer radius. If the radius is not set, it will be half of the minimum of the + * chart width and height. + * @method radius + * @memberof dc.pieChart + * @instance + * @param {Number} [radius] + * @returns {Number|dc.pieChart} + */ + _chart.radius = function (radius) { + if (!arguments.length) { + return _givenRadius; + } + _givenRadius = radius; + return _chart; + }; + + /** + * Get or set center x coordinate position. Default is center of svg. + * @method cx + * @memberof dc.pieChart + * @instance + * @param {Number} [cx] + * @returns {Number|dc.pieChart} + */ + _chart.cx = function (cx) { + if (!arguments.length) { + return (_cx || _chart.width() / 2); + } + _cx = cx; + return _chart; + }; + + /** + * Get or set center y coordinate position. Default is center of svg. + * @method cy + * @memberof dc.pieChart + * @instance + * @param {Number} [cy] + * @returns {Number|dc.pieChart} + */ + _chart.cy = function (cy) { + if (!arguments.length) { + return (_cy || _chart.height() / 2); + } + _cy = cy; + return _chart; + }; + + function buildArcs () { + return d3.svg.arc() + .outerRadius(_radius - _externalRadiusPadding) + .innerRadius(_innerRadius); + } + + function isSelectedSlice (d) { + return _chart.hasFilter(_chart.cappedKeyAccessor(d.data)); + } + + _chart._doRedraw = function () { + drawChart(); + return _chart; + }; + + /** + * Get or set the minimal slice angle for label rendering. Any slice with a smaller angle will not + * display a slice label. + * @method minAngleForLabel + * @memberof dc.pieChart + * @instance + * @param {Number} [minAngleForLabel=0.5] + * @returns {Number|dc.pieChart} + */ + _chart.minAngleForLabel = function (minAngleForLabel) { + if (!arguments.length) { + return _minAngleForLabel; + } + _minAngleForLabel = minAngleForLabel; + return _chart; + }; + + function pieLayout () { + return d3.layout.pie().sort(null).value(_chart.cappedValueAccessor); + } + + function sliceTooSmall (d) { + var angle = (d.endAngle - d.startAngle); + return isNaN(angle) || angle < _minAngleForLabel; + } + + function sliceHasNoData (d) { + return _chart.cappedValueAccessor(d) === 0; + } + + function tweenPie (b) { + b.innerRadius = _innerRadius; + var current = this._current; + if (isOffCanvas(current)) { + current = {startAngle: 0, endAngle: 0}; + } else { + // only interpolate startAngle & endAngle, not the whole data object + current = {startAngle: current.startAngle, endAngle: current.endAngle}; + } + var i = d3.interpolate(current, b); + this._current = i(0); + return function (t) { + return safeArc(i(t), 0, buildArcs()); + }; + } + + function isOffCanvas (current) { + return !current || isNaN(current.startAngle) || isNaN(current.endAngle); + } + + function fill (d, i) { + return _chart.getColor(d.data, i); + } + + function onClick (d, i) { + if (_g.attr('class') !== _emptyCssClass) { + _chart.onClick(d.data, i); + } + } + + function safeArc (d, i, arc) { + var path = arc(d, i); + if (path.indexOf('NaN') >= 0) { + path = 'M0,0'; + } + return path; + } + + /** + * Title to use for the only slice when there is no data. + * @method emptyTitle + * @memberof dc.pieChart + * @instance + * @param {String} [title] + * @returns {String|dc.pieChart} + */ + _chart.emptyTitle = function (title) { + if (arguments.length === 0) { + return _emptyTitle; + } + _emptyTitle = title; + return _chart; + }; + + /** + * Position slice labels offset from the outer edge of the chart. + * + * The argument specifies the extra radius to be added for slice labels. + * @method externalLabels + * @memberof dc.pieChart + * @instance + * @param {Number} [externalLabelRadius] + * @returns {Number|dc.pieChart} + */ + _chart.externalLabels = function (externalLabelRadius) { + if (arguments.length === 0) { + return _externalLabelRadius; + } else if (externalLabelRadius) { + _externalLabelRadius = externalLabelRadius; + } else { + _externalLabelRadius = undefined; + } + + return _chart; + }; + + /** + * Get or set whether to draw lines from pie slices to their labels. + * + * @method drawPaths + * @memberof dc.pieChart + * @instance + * @param {Boolean} [drawPaths] + * @returns {Boolean|dc.pieChart} + */ + _chart.drawPaths = function (drawPaths) { + if (arguments.length === 0) { + return _drawPaths; + } + _drawPaths = drawPaths; + return _chart; + }; + + function labelPosition (d, arc) { + var centroid; + if (_externalLabelRadius) { + centroid = d3.svg.arc() + .outerRadius(_radius - _externalRadiusPadding + _externalLabelRadius) + .innerRadius(_radius - _externalRadiusPadding + _externalLabelRadius) + .centroid(d); + } else { + centroid = arc.centroid(d); + } + if (isNaN(centroid[0]) || isNaN(centroid[1])) { + return 'translate(0,0)'; + } else { + return 'translate(' + centroid + ')'; + } + } + + _chart.legendables = function () { + return _chart.data().map(function (d, i) { + var legendable = {name: d.key, data: d.value, others: d.others, chart: _chart}; + legendable.color = _chart.getColor(d, i); + return legendable; + }); + }; + + _chart.legendHighlight = function (d) { + highlightSliceFromLegendable(d, true); + }; + + _chart.legendReset = function (d) { + highlightSliceFromLegendable(d, false); + }; + + _chart.legendToggle = function (d) { + _chart.onClick({key: d.name, others: d.others}); + }; + + function highlightSliceFromLegendable (legendable, highlighted) { + _chart.selectAll('g.pie-slice').each(function (d) { + if (legendable.name === d.data.key) { + d3.select(this).classed('highlight', highlighted); + } + }); + } + + return _chart.anchor(parent, chartGroup); +}; + +/** + * Concrete bar chart/histogram implementation. + * + * Examples: + * - {@link http://dc-js.github.com/dc.js/ Nasdaq 100 Index} + * - {@link http://dc-js.github.com/dc.js/crime/index.html Canadian City Crime Stats} + * @class barChart + * @memberof dc + * @mixes dc.stackMixin + * @mixes dc.coordinateGridMixin + * @example + * // create a bar chart under #chart-container1 element using the default global chart group + * var chart1 = dc.barChart('#chart-container1'); + * // create a bar chart under #chart-container2 element using chart group A + * var chart2 = dc.barChart('#chart-container2', 'chartGroupA'); + * // create a sub-chart under a composite parent chart + * var chart3 = dc.barChart(compositeChart); + * @param {String|node|d3.selection|dc.compositeChart} parent - Any valid + * {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Selections.md#selecting-elements d3 single selector} + * specifying a dom block element such as a div; or a dom element or d3 selection. If the bar + * chart is a sub-chart in a {@link dc.compositeChart Composite Chart} then pass in the parent + * composite chart instance instead. + * @param {String} [chartGroup] - The name of the chart group this chart instance should be placed in. + * Interaction with a chart will only trigger events and redraws within the chart's group. + * @returns {dc.barChart} + */ +dc.barChart = function (parent, chartGroup) { + var MIN_BAR_WIDTH = 1; + var DEFAULT_GAP_BETWEEN_BARS = 2; + var LABEL_PADDING = 3; + + var _chart = dc.stackMixin(dc.coordinateGridMixin({})); + + var _gap = DEFAULT_GAP_BETWEEN_BARS; + var _centerBar = false; + var _alwaysUseRounding = false; + + var _barWidth; + + dc.override(_chart, 'rescale', function () { + _chart._rescale(); + _barWidth = undefined; + return _chart; + }); + + dc.override(_chart, 'render', function () { + if (_chart.round() && _centerBar && !_alwaysUseRounding) { + dc.logger.warn('By default, brush rounding is disabled if bars are centered. ' + + 'See dc.js bar chart API documentation for details.'); + } + + return _chart._render(); + }); + + _chart.label(function (d) { + return dc.utils.printSingleValue(d.y0 + d.y); + }, false); + + _chart.plotData = function () { + var layers = _chart.chartBodyG().selectAll('g.stack') + .data(_chart.data()); + + calculateBarWidth(); + + layers + .enter() + .append('g') + .attr('class', function (d, i) { + return 'stack ' + '_' + i; + }); + + var last = layers.size() - 1; + layers.each(function (d, i) { + var layer = d3.select(this); + + renderBars(layer, i, d); + + if (_chart.renderLabel() && last === i) { + renderLabels(layer, i, d); + } + }); + }; + + function barHeight (d) { + return dc.utils.safeNumber(Math.abs(_chart.y()(d.y + d.y0) - _chart.y()(d.y0))); + } + + function renderLabels (layer, layerIndex, d) { + var labels = layer.selectAll('text.barLabel') + .data(d.values, dc.pluck('x')); + + labels.enter() + .append('text') + .attr('class', 'barLabel') + .attr('text-anchor', 'middle'); + + if (_chart.isOrdinal()) { + labels.on('click', _chart.onClick); + labels.attr('cursor', 'pointer'); + } + + dc.transition(labels, _chart.transitionDuration(), _chart.transitionDelay()) + .attr('x', function (d) { + var x = _chart.x()(d.x); + if (!_centerBar) { + x += _barWidth / 2; + } + return dc.utils.safeNumber(x); + }) + .attr('y', function (d) { + var y = _chart.y()(d.y + d.y0); + + if (d.y < 0) { + y -= barHeight(d); + } + + return dc.utils.safeNumber(y - LABEL_PADDING); + }) + .text(function (d) { + return _chart.label()(d); + }); + + dc.transition(labels.exit(), _chart.transitionDuration(), _chart.transitionDelay()) + .attr('height', 0) + .remove(); + } + + function renderBars (layer, layerIndex, d) { + var bars = layer.selectAll('rect.bar') + .data(d.values, dc.pluck('x')); + + var enter = bars.enter() + .append('rect') + .attr('class', 'bar') + .attr('fill', dc.pluck('data', _chart.getColor)) + .attr('y', _chart.yAxisHeight()) + .attr('height', 0); + + if (_chart.renderTitle()) { + enter.append('title').text(dc.pluck('data', _chart.title(d.name))); + } + + if (_chart.isOrdinal()) { + bars.on('click', _chart.onClick); + } + + dc.transition(bars, _chart.transitionDuration(), _chart.transitionDelay()) + .attr('x', function (d) { + var x = _chart.x()(d.x); + if (_centerBar) { + x -= _barWidth / 2; + } + if (_chart.isOrdinal() && _gap !== undefined) { + x += _gap / 2; + } + return dc.utils.safeNumber(x); + }) + .attr('y', function (d) { + var y = _chart.y()(d.y + d.y0); + + if (d.y < 0) { + y -= barHeight(d); + } + + return dc.utils.safeNumber(y); + }) + .attr('width', _barWidth) + .attr('height', function (d) { + return barHeight(d); + }) + .attr('fill', dc.pluck('data', _chart.getColor)) + .select('title').text(dc.pluck('data', _chart.title(d.name))); + + dc.transition(bars.exit(), _chart.transitionDuration(), _chart.transitionDelay()) + .attr('x', function (d) { return _chart.x()(d.x); }) + .attr('width', _barWidth * 0.9) + .remove(); + } + + function calculateBarWidth () { + if (_barWidth === undefined) { + var numberOfBars = _chart.xUnitCount(); + + // please can't we always use rangeBands for bar charts? + if (_chart.isOrdinal() && _gap === undefined) { + _barWidth = Math.floor(_chart.x().rangeBand()); + } else if (_gap) { + _barWidth = Math.floor((_chart.xAxisLength() - (numberOfBars - 1) * _gap) / numberOfBars); + } else { + _barWidth = Math.floor(_chart.xAxisLength() / (1 + _chart.barPadding()) / numberOfBars); + } + + if (_barWidth === Infinity || isNaN(_barWidth) || _barWidth < MIN_BAR_WIDTH) { + _barWidth = MIN_BAR_WIDTH; + } + } + } + + _chart.fadeDeselectedArea = function () { + var bars = _chart.chartBodyG().selectAll('rect.bar'); + var extent = _chart.brush().extent(); + + if (_chart.isOrdinal()) { + if (_chart.hasFilter()) { + bars.classed(dc.constants.SELECTED_CLASS, function (d) { + return _chart.hasFilter(d.x); + }); + bars.classed(dc.constants.DESELECTED_CLASS, function (d) { + return !_chart.hasFilter(d.x); + }); + } else { + bars.classed(dc.constants.SELECTED_CLASS, false); + bars.classed(dc.constants.DESELECTED_CLASS, false); + } + } else { + if (!_chart.brushIsEmpty(extent)) { + var start = extent[0]; + var end = extent[1]; + + bars.classed(dc.constants.DESELECTED_CLASS, function (d) { + return d.x < start || d.x >= end; + }); + } else { + bars.classed(dc.constants.DESELECTED_CLASS, false); + } + } + }; + + /** + * Whether the bar chart will render each bar centered around the data position on the x-axis. + * @method centerBar + * @memberof dc.barChart + * @instance + * @param {Boolean} [centerBar=false] + * @returns {Boolean|dc.barChart} + */ + _chart.centerBar = function (centerBar) { + if (!arguments.length) { + return _centerBar; + } + _centerBar = centerBar; + return _chart; + }; + + dc.override(_chart, 'onClick', function (d) { + _chart._onClick(d.data); + }); + + /** + * Get or set the spacing between bars as a fraction of bar size. Valid values are between 0-1. + * Setting this value will also remove any previously set {@link dc.barChart#gap gap}. See the + * {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Ordinal-Scales.md#ordinal_rangeBands d3 docs} + * for a visual description of how the padding is applied. + * @method barPadding + * @memberof dc.barChart + * @instance + * @param {Number} [barPadding=0] + * @returns {Number|dc.barChart} + */ + _chart.barPadding = function (barPadding) { + if (!arguments.length) { + return _chart._rangeBandPadding(); + } + _chart._rangeBandPadding(barPadding); + _gap = undefined; + return _chart; + }; + + _chart._useOuterPadding = function () { + return _gap === undefined; + }; + + /** + * Get or set the outer padding on an ordinal bar chart. This setting has no effect on non-ordinal charts. + * Will pad the width by `padding * barWidth` on each side of the chart. + * @method outerPadding + * @memberof dc.barChart + * @instance + * @param {Number} [padding=0.5] + * @returns {Number|dc.barChart} + */ + _chart.outerPadding = _chart._outerRangeBandPadding; + + /** + * Manually set fixed gap (in px) between bars instead of relying on the default auto-generated + * gap. By default the bar chart implementation will calculate and set the gap automatically + * based on the number of data points and the length of the x axis. + * @method gap + * @memberof dc.barChart + * @instance + * @param {Number} [gap=2] + * @returns {Number|dc.barChart} + */ + _chart.gap = function (gap) { + if (!arguments.length) { + return _gap; + } + _gap = gap; + return _chart; + }; + + _chart.extendBrush = function () { + var extent = _chart.brush().extent(); + if (_chart.round() && (!_centerBar || _alwaysUseRounding)) { + extent[0] = extent.map(_chart.round())[0]; + extent[1] = extent.map(_chart.round())[1]; + + _chart.chartBodyG().select('.brush') + .call(_chart.brush().extent(extent)); + } + + return extent; + }; + + /** + * Set or get whether rounding is enabled when bars are centered. If false, using + * rounding with centered bars will result in a warning and rounding will be ignored. This flag + * has no effect if bars are not {@link dc.barChart#centerBar centered}. + * When using standard d3.js rounding methods, the brush often doesn't align correctly with + * centered bars since the bars are offset. The rounding function must add an offset to + * compensate, such as in the following example. + * @method alwaysUseRounding + * @memberof dc.barChart + * @instance + * @example + * chart.round(function(n) { return Math.floor(n) + 0.5; }); + * @param {Boolean} [alwaysUseRounding=false] + * @returns {Boolean|dc.barChart} + */ + _chart.alwaysUseRounding = function (alwaysUseRounding) { + if (!arguments.length) { + return _alwaysUseRounding; + } + _alwaysUseRounding = alwaysUseRounding; + return _chart; + }; + + function colorFilter (color, inv) { + return function () { + var item = d3.select(this); + var match = item.attr('fill') === color; + return inv ? !match : match; + }; + } + + _chart.legendHighlight = function (d) { + if (!_chart.isLegendableHidden(d)) { + _chart.g().selectAll('rect.bar') + .classed('highlight', colorFilter(d.color)) + .classed('fadeout', colorFilter(d.color, true)); + } + }; + + _chart.legendReset = function () { + _chart.g().selectAll('rect.bar') + .classed('highlight', false) + .classed('fadeout', false); + }; + + dc.override(_chart, 'xAxisMax', function () { + var max = this._xAxisMax(); + if ('resolution' in _chart.xUnits()) { + var res = _chart.xUnits().resolution; + max += res; + } + return max; + }); + + return _chart.anchor(parent, chartGroup); +}; + +/** + * Concrete line/area chart implementation. + * + * Examples: + * - {@link http://dc-js.github.com/dc.js/ Nasdaq 100 Index} + * - {@link http://dc-js.github.com/dc.js/crime/index.html Canadian City Crime Stats} + * @class lineChart + * @memberof dc + * @mixes dc.stackMixin + * @mixes dc.coordinateGridMixin + * @example + * // create a line chart under #chart-container1 element using the default global chart group + * var chart1 = dc.lineChart('#chart-container1'); + * // create a line chart under #chart-container2 element using chart group A + * var chart2 = dc.lineChart('#chart-container2', 'chartGroupA'); + * // create a sub-chart under a composite parent chart + * var chart3 = dc.lineChart(compositeChart); + * @param {String|node|d3.selection|dc.compositeChart} parent - Any valid + * {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Selections.md#selecting-elements d3 single selector} + * specifying a dom block element such as a div; or a dom element or d3 selection. If the line + * chart is a sub-chart in a {@link dc.compositeChart Composite Chart} then pass in the parent + * composite chart instance instead. + * @param {String} [chartGroup] - The name of the chart group this chart instance should be placed in. + * Interaction with a chart will only trigger events and redraws within the chart's group. + * @returns {dc.lineChart} + */ +dc.lineChart = function (parent, chartGroup) { + var DEFAULT_DOT_RADIUS = 5; + var TOOLTIP_G_CLASS = 'dc-tooltip'; + var DOT_CIRCLE_CLASS = 'dot'; + var Y_AXIS_REF_LINE_CLASS = 'yRef'; + var X_AXIS_REF_LINE_CLASS = 'xRef'; + var DEFAULT_DOT_OPACITY = 1e-6; + var LABEL_PADDING = 3; + + var _chart = dc.stackMixin(dc.coordinateGridMixin({})); + var _renderArea = false; + var _dotRadius = DEFAULT_DOT_RADIUS; + var _dataPointRadius = null; + var _dataPointFillOpacity = DEFAULT_DOT_OPACITY; + var _dataPointStrokeOpacity = DEFAULT_DOT_OPACITY; + var _interpolate = 'linear'; + var _tension = 0.7; + var _defined; + var _dashStyle; + var _xyTipsOn = true; + + _chart.transitionDuration(500); + _chart.transitionDelay(0); + _chart._rangeBandPadding(1); + + _chart.plotData = function () { + var chartBody = _chart.chartBodyG(); + var layersList = chartBody.select('g.stack-list'); + + if (layersList.empty()) { + layersList = chartBody.append('g').attr('class', 'stack-list'); + } + + var layers = layersList.selectAll('g.stack').data(_chart.data()); + + var layersEnter = layers + .enter() + .append('g') + .attr('class', function (d, i) { + return 'stack ' + '_' + i; + }); + + drawLine(layersEnter, layers); + + drawArea(layersEnter, layers); + + drawDots(chartBody, layers); + + if (_chart.renderLabel()) { + drawLabels(layers); + } + }; + + /** + * Gets or sets the interpolator to use for lines drawn, by string name, allowing e.g. step + * functions, splines, and cubic interpolation. This is passed to + * {@link https://github.com/d3/d3-3.x-api-reference/blob/master/SVG-Shapes.md#line_interpolate d3.svg.line.interpolate} and + * {@link https://github.com/d3/d3-3.x-api-reference/blob/master/SVG-Shapes.md#area_interpolate d3.svg.area.interpolate}, + * where you can find a complete list of valid arguments. + * @method interpolate + * @memberof dc.lineChart + * @instance + * @see {@link https://github.com/d3/d3-3.x-api-reference/blob/master/SVG-Shapes.md#line_interpolate d3.svg.line.interpolate} + * @see {@link https://github.com/d3/d3-3.x-api-reference/blob/master/SVG-Shapes.md#area_interpolate d3.svg.area.interpolate} + * @param {String} [interpolate='linear'] + * @returns {String|dc.lineChart} + */ + _chart.interpolate = function (interpolate) { + if (!arguments.length) { + return _interpolate; + } + _interpolate = interpolate; + return _chart; + }; + + /** + * Gets or sets the tension to use for lines drawn, in the range 0 to 1. + * This parameter further customizes the interpolation behavior. It is passed to + * {@link https://github.com/d3/d3-3.x-api-reference/blob/master/SVG-Shapes.md#line_tension d3.svg.line.tension} and + * {@link https://github.com/d3/d3-3.x-api-reference/blob/master/SVG-Shapes.md#area_tension d3.svg.area.tension}. + * @method tension + * @memberof dc.lineChart + * @instance + * @see {@link https://github.com/d3/d3-3.x-api-reference/blob/master/SVG-Shapes.md#line_interpolate d3.svg.line.interpolate} + * @see {@link https://github.com/d3/d3-3.x-api-reference/blob/master/SVG-Shapes.md#area_interpolate d3.svg.area.interpolate} + * @param {Number} [tension=0.7] + * @returns {Number|dc.lineChart} + */ + _chart.tension = function (tension) { + if (!arguments.length) { + return _tension; + } + _tension = tension; + return _chart; + }; + + /** + * Gets or sets a function that will determine discontinuities in the line which should be + * skipped: the path will be broken into separate subpaths if some points are undefined. + * This function is passed to + * {@link https://github.com/d3/d3-3.x-api-reference/blob/master/SVG-Shapes.md#line_defined d3.svg.line.defined} + * + * Note: crossfilter will sometimes coerce nulls to 0, so you may need to carefully write + * custom reduce functions to get this to work, depending on your data. See + * {@link https://github.com/dc-js/dc.js/issues/615#issuecomment-49089248 this GitHub comment} + * for more details and an example. + * @method defined + * @memberof dc.lineChart + * @instance + * @see {@link https://github.com/d3/d3-3.x-api-reference/blob/master/SVG-Shapes.md#line_defined d3.svg.line.defined} + * @param {Function} [defined] + * @returns {Function|dc.lineChart} + */ + _chart.defined = function (defined) { + if (!arguments.length) { + return _defined; + } + _defined = defined; + return _chart; + }; + + /** + * Set the line's d3 dashstyle. This value becomes the 'stroke-dasharray' of line. Defaults to empty + * array (solid line). + * @method dashStyle + * @memberof dc.lineChart + * @instance + * @see {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-dasharray stroke-dasharray} + * @example + * // create a Dash Dot Dot Dot + * chart.dashStyle([3,1,1,1]); + * @param {Array<Number>} [dashStyle=[]] + * @returns {Array<Number>|dc.lineChart} + */ + _chart.dashStyle = function (dashStyle) { + if (!arguments.length) { + return _dashStyle; + } + _dashStyle = dashStyle; + return _chart; + }; + + /** + * Get or set render area flag. If the flag is set to true then the chart will render the area + * beneath each line and the line chart effectively becomes an area chart. + * @method renderArea + * @memberof dc.lineChart + * @instance + * @param {Boolean} [renderArea=false] + * @returns {Boolean|dc.lineChart} + */ + _chart.renderArea = function (renderArea) { + if (!arguments.length) { + return _renderArea; + } + _renderArea = renderArea; + return _chart; + }; + + function colors (d, i) { + return _chart.getColor.call(d, d.values, i); + } + + function drawLine (layersEnter, layers) { + var line = d3.svg.line() + .x(function (d) { + return _chart.x()(d.x); + }) + .y(function (d) { + return _chart.y()(d.y + d.y0); + }) + .interpolate(_interpolate) + .tension(_tension); + if (_defined) { + line.defined(_defined); + } + + var path = layersEnter.append('path') + .attr('class', 'line') + .attr('stroke', colors); + if (_dashStyle) { + path.attr('stroke-dasharray', _dashStyle); + } + + dc.transition(layers.select('path.line'), _chart.transitionDuration(), _chart.transitionDelay()) + //.ease('linear') + .attr('stroke', colors) + .attr('d', function (d) { + return safeD(line(d.values)); + }); + } + + function drawArea (layersEnter, layers) { + if (_renderArea) { + var area = d3.svg.area() + .x(function (d) { + return _chart.x()(d.x); + }) + .y(function (d) { + return _chart.y()(d.y + d.y0); + }) + .y0(function (d) { + return _chart.y()(d.y0); + }) + .interpolate(_interpolate) + .tension(_tension); + if (_defined) { + area.defined(_defined); + } + + layersEnter.append('path') + .attr('class', 'area') + .attr('fill', colors) + .attr('d', function (d) { + return safeD(area(d.values)); + }); + + dc.transition(layers.select('path.area'), _chart.transitionDuration(), _chart.transitionDelay()) + //.ease('linear') + .attr('fill', colors) + .attr('d', function (d) { + return safeD(area(d.values)); + }); + } + } + + function safeD (d) { + return (!d || d.indexOf('NaN') >= 0) ? 'M0,0' : d; + } + + function drawDots (chartBody, layers) { + if (_chart.xyTipsOn() === 'always' || (!_chart.brushOn() && _chart.xyTipsOn())) { + var tooltipListClass = TOOLTIP_G_CLASS + '-list'; + var tooltips = chartBody.select('g.' + tooltipListClass); + + if (tooltips.empty()) { + tooltips = chartBody.append('g').attr('class', tooltipListClass); + } + + layers.each(function (d, layerIndex) { + var points = d.values; + if (_defined) { + points = points.filter(_defined); + } + + var g = tooltips.select('g.' + TOOLTIP_G_CLASS + '._' + layerIndex); + if (g.empty()) { + g = tooltips.append('g').attr('class', TOOLTIP_G_CLASS + ' _' + layerIndex); + } + + createRefLines(g); + + var dots = g.selectAll('circle.' + DOT_CIRCLE_CLASS) + .data(points, dc.pluck('x')); + + dots.enter() + .append('circle') + .attr('class', DOT_CIRCLE_CLASS) + .attr('r', getDotRadius()) + .style('fill-opacity', _dataPointFillOpacity) + .style('stroke-opacity', _dataPointStrokeOpacity) + .attr('fill', _chart.getColor) + .on('mousemove', function () { + var dot = d3.select(this); + showDot(dot); + showRefLines(dot, g); + }) + .on('mouseout', function () { + var dot = d3.select(this); + hideDot(dot); + hideRefLines(g); + }); + + dots.call(renderTitle, d); + + dc.transition(dots, _chart.transitionDuration()) + .attr('cx', function (d) { + return dc.utils.safeNumber(_chart.x()(d.x)); + }) + .attr('cy', function (d) { + return dc.utils.safeNumber(_chart.y()(d.y + d.y0)); + }) + .attr('fill', _chart.getColor); + + dots.exit().remove(); + }); + } + } + + _chart.label(function (d) { + return dc.utils.printSingleValue(d.y0 + d.y); + }, false); + + function drawLabels (layers) { + layers.each(function (d, layerIndex) { + var layer = d3.select(this); + var labels = layer.selectAll('text.lineLabel') + .data(d.values, dc.pluck('x')); + + labels.enter() + .append('text') + .attr('class', 'lineLabel') + .attr('text-anchor', 'middle'); + + dc.transition(labels, _chart.transitionDuration()) + .attr('x', function (d) { + return dc.utils.safeNumber(_chart.x()(d.x)); + }) + .attr('y', function (d) { + var y = _chart.y()(d.y + d.y0) - LABEL_PADDING; + return dc.utils.safeNumber(y); + }) + .text(function (d) { + return _chart.label()(d); + }); + + dc.transition(labels.exit(), _chart.transitionDuration()) + .attr('height', 0) + .remove(); + }); + } + + function createRefLines (g) { + var yRefLine = g.select('path.' + Y_AXIS_REF_LINE_CLASS).empty() ? + g.append('path').attr('class', Y_AXIS_REF_LINE_CLASS) : g.select('path.' + Y_AXIS_REF_LINE_CLASS); + yRefLine.style('display', 'none').attr('stroke-dasharray', '5,5'); + + var xRefLine = g.select('path.' + X_AXIS_REF_LINE_CLASS).empty() ? + g.append('path').attr('class', X_AXIS_REF_LINE_CLASS) : g.select('path.' + X_AXIS_REF_LINE_CLASS); + xRefLine.style('display', 'none').attr('stroke-dasharray', '5,5'); + } + + function showDot (dot) { + dot.style('fill-opacity', 0.8); + dot.style('stroke-opacity', 0.8); + dot.attr('r', _dotRadius); + return dot; + } + + function showRefLines (dot, g) { + var x = dot.attr('cx'); + var y = dot.attr('cy'); + var yAxisX = (_chart._yAxisX() - _chart.margins().left); + var yAxisRefPathD = 'M' + yAxisX + ' ' + y + 'L' + (x) + ' ' + (y); + var xAxisRefPathD = 'M' + x + ' ' + _chart.yAxisHeight() + 'L' + x + ' ' + y; + g.select('path.' + Y_AXIS_REF_LINE_CLASS).style('display', '').attr('d', yAxisRefPathD); + g.select('path.' + X_AXIS_REF_LINE_CLASS).style('display', '').attr('d', xAxisRefPathD); + } + + function getDotRadius () { + return _dataPointRadius || _dotRadius; + } + + function hideDot (dot) { + dot.style('fill-opacity', _dataPointFillOpacity) + .style('stroke-opacity', _dataPointStrokeOpacity) + .attr('r', getDotRadius()); + } + + function hideRefLines (g) { + g.select('path.' + Y_AXIS_REF_LINE_CLASS).style('display', 'none'); + g.select('path.' + X_AXIS_REF_LINE_CLASS).style('display', 'none'); + } + + function renderTitle (dot, d) { + if (_chart.renderTitle()) { + dot.select('title').remove(); + dot.append('title').text(dc.pluck('data', _chart.title(d.name))); + } + } + + /** + * Turn on/off the mouseover behavior of an individual data point which renders a circle and x/y axis + * dashed lines back to each respective axis. This is ignored if the chart + * {@link dc.coordinateGridMixin#brushOn brush} is on + * @method xyTipsOn + * @memberof dc.lineChart + * @instance + * @param {Boolean} [xyTipsOn=false] + * @returns {Boolean|dc.lineChart} + */ + _chart.xyTipsOn = function (xyTipsOn) { + if (!arguments.length) { + return _xyTipsOn; + } + _xyTipsOn = xyTipsOn; + return _chart; + }; + + /** + * Get or set the radius (in px) for dots displayed on the data points. + * @method dotRadius + * @memberof dc.lineChart + * @instance + * @param {Number} [dotRadius=5] + * @returns {Number|dc.lineChart} + */ + _chart.dotRadius = function (dotRadius) { + if (!arguments.length) { + return _dotRadius; + } + _dotRadius = dotRadius; + return _chart; + }; + + /** + * Always show individual dots for each datapoint. + * + * If `options` is falsy, it disables data point rendering. If no `options` are provided, the + * current `options` values are instead returned. + * @method renderDataPoints + * @memberof dc.lineChart + * @instance + * @example + * chart.renderDataPoints({radius: 2, fillOpacity: 0.8, strokeOpacity: 0.8}) + * @param {{fillOpacity: Number, strokeOpacity: Number, radius: Number}} [options={fillOpacity: 0.8, strokeOpacity: 0.8, radius: 2}] + * @returns {{fillOpacity: Number, strokeOpacity: Number, radius: Number}|dc.lineChart} + */ + _chart.renderDataPoints = function (options) { + if (!arguments.length) { + return { + fillOpacity: _dataPointFillOpacity, + strokeOpacity: _dataPointStrokeOpacity, + radius: _dataPointRadius + }; + } else if (!options) { + _dataPointFillOpacity = DEFAULT_DOT_OPACITY; + _dataPointStrokeOpacity = DEFAULT_DOT_OPACITY; + _dataPointRadius = null; + } else { + _dataPointFillOpacity = options.fillOpacity || 0.8; + _dataPointStrokeOpacity = options.strokeOpacity || 0.8; + _dataPointRadius = options.radius || 2; + } + return _chart; + }; + + function colorFilter (color, dashstyle, inv) { + return function () { + var item = d3.select(this); + var match = (item.attr('stroke') === color && + item.attr('stroke-dasharray') === ((dashstyle instanceof Array) ? + dashstyle.join(',') : null)) || item.attr('fill') === color; + return inv ? !match : match; + }; + } + + _chart.legendHighlight = function (d) { + if (!_chart.isLegendableHidden(d)) { + _chart.g().selectAll('path.line, path.area') + .classed('highlight', colorFilter(d.color, d.dashstyle)) + .classed('fadeout', colorFilter(d.color, d.dashstyle, true)); + } + }; + + _chart.legendReset = function () { + _chart.g().selectAll('path.line, path.area') + .classed('highlight', false) + .classed('fadeout', false); + }; + + dc.override(_chart, 'legendables', function () { + var legendables = _chart._legendables(); + if (!_dashStyle) { + return legendables; + } + return legendables.map(function (l) { + l.dashstyle = _dashStyle; + return l; + }); + }); + + return _chart.anchor(parent, chartGroup); +}; + +/** + * The data count widget is a simple widget designed to display the number of records selected by the + * current filters out of the total number of records in the data set. Once created the data count widget + * will automatically update the text content of child elements with the following classes: + * + * * `.total-count` - total number of records + * * `.filter-count` - number of records matched by the current filters + * + * Note: this widget works best for the specific case of showing the number of records out of a + * total. If you want a more general-purpose numeric display, please use the + * {@link dc.numberDisplay} widget instead. + * + * Examples: + * - {@link http://dc-js.github.com/dc.js/ Nasdaq 100 Index} + * @class dataCount + * @memberof dc + * @mixes dc.baseMixin + * @example + * var ndx = crossfilter(data); + * var all = ndx.groupAll(); + * + * dc.dataCount('.dc-data-count') + * .dimension(ndx) + * .group(all); + * @param {String|node|d3.selection} parent - Any valid + * {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Selections.md#selecting-elements d3 single selector} specifying + * a dom block element such as a div; or a dom element or d3 selection. + * @param {String} [chartGroup] - The name of the chart group this chart instance should be placed in. + * Interaction with a chart will only trigger events and redraws within the chart's group. + * @returns {dc.dataCount} + */ +dc.dataCount = function (parent, chartGroup) { + var _formatNumber = d3.format(',d'); + var _chart = dc.baseMixin({}); + var _html = {some: '', all: ''}; + + /** + * Gets or sets an optional object specifying HTML templates to use depending how many items are + * selected. The text `%total-count` will replaced with the total number of records, and the text + * `%filter-count` will be replaced with the number of selected records. + * - all: HTML template to use if all items are selected + * - some: HTML template to use if not all items are selected + * @method html + * @memberof dc.dataCount + * @instance + * @example + * counter.html({ + * some: '%filter-count out of %total-count records selected', + * all: 'All records selected. Click on charts to apply filters' + * }) + * @param {{some:String, all: String}} [options] + * @returns {{some:String, all: String}|dc.dataCount} + */ + _chart.html = function (options) { + if (!arguments.length) { + return _html; + } + if (options.all) { + _html.all = options.all; + } + if (options.some) { + _html.some = options.some; + } + return _chart; + }; + + /** + * Gets or sets an optional function to format the filter count and total count. + * @method formatNumber + * @memberof dc.dataCount + * @instance + * @see {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md d3.format} + * @example + * counter.formatNumber(d3.format('.2g')) + * @param {Function} [formatter=d3.format('.2g')] + * @returns {Function|dc.dataCount} + */ + _chart.formatNumber = function (formatter) { + if (!arguments.length) { + return _formatNumber; + } + _formatNumber = formatter; + return _chart; + }; + + _chart._doRender = function () { + var tot = _chart.dimension().size(), + val = _chart.group().value(); + var all = _formatNumber(tot); + var selected = _formatNumber(val); + + if ((tot === val) && (_html.all !== '')) { + _chart.root().html(_html.all.replace('%total-count', all).replace('%filter-count', selected)); + } else if (_html.some !== '') { + _chart.root().html(_html.some.replace('%total-count', all).replace('%filter-count', selected)); + } else { + _chart.selectAll('.total-count').text(all); + _chart.selectAll('.filter-count').text(selected); + } + return _chart; + }; + + _chart._doRedraw = function () { + return _chart._doRender(); + }; + + return _chart.anchor(parent, chartGroup); +}; + +/** + * The data table is a simple widget designed to list crossfilter focused data set (rows being + * filtered) in a good old tabular fashion. + * + * Note: Unlike other charts, the data table (and data grid chart) use the {@link dc.dataTable#group group} attribute as a + * keying function for {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Arrays.md#nest nesting} the data + * together in groups. Do not pass in a crossfilter group as this will not work. + * + * Another interesting feature of the data table is that you can pass a crossfilter group to the `dimension`, as + * long as you specify the {@link dc.dataTable#order order} as `d3.descending`, since the data + * table will use `dimension.top()` to fetch the data in that case, and the method is equally + * supported on the crossfilter group as the crossfilter dimension. + * + * Examples: + * - {@link http://dc-js.github.com/dc.js/ Nasdaq 100 Index} + * - {@link http://dc-js.github.io/dc.js/examples/table-on-aggregated-data.html dataTable on a crossfilter group} + * ({@link https://github.com/dc-js/dc.js/blob/develop/web/examples/table-on-aggregated-data.html source}) + * @class dataTable + * @memberof dc + * @mixes dc.baseMixin + * @param {String|node|d3.selection} parent - Any valid + * {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Selections.md#selecting-elements d3 single selector} specifying + * a dom block element such as a div; or a dom element or d3 selection. + * @param {String} [chartGroup] - The name of the chart group this chart instance should be placed in. + * Interaction with a chart will only trigger events and redraws within the chart's group. + * @returns {dc.dataTable} + */ +dc.dataTable = function (parent, chartGroup) { + var LABEL_CSS_CLASS = 'dc-table-label'; + var ROW_CSS_CLASS = 'dc-table-row'; + var COLUMN_CSS_CLASS = 'dc-table-column'; + var GROUP_CSS_CLASS = 'dc-table-group'; + var HEAD_CSS_CLASS = 'dc-table-head'; + + var _chart = dc.baseMixin({}); + + var _size = 25; + var _columns = []; + var _sortBy = function (d) { + return d; + }; + var _order = d3.ascending; + var _beginSlice = 0; + var _endSlice; + var _showGroups = true; + + _chart._doRender = function () { + _chart.selectAll('tbody').remove(); + + renderRows(renderGroups()); + + return _chart; + }; + + _chart._doColumnValueFormat = function (v, d) { + return ((typeof v === 'function') ? + v(d) : // v as function + ((typeof v === 'string') ? + d[v] : // v is field name string + v.format(d) // v is Object, use fn (element 2) + ) + ); + }; + + _chart._doColumnHeaderFormat = function (d) { + // if 'function', convert to string representation + // show a string capitalized + // if an object then display its label string as-is. + return (typeof d === 'function') ? + _chart._doColumnHeaderFnToString(d) : + ((typeof d === 'string') ? + _chart._doColumnHeaderCapitalize(d) : String(d.label)); + }; + + _chart._doColumnHeaderCapitalize = function (s) { + // capitalize + return s.charAt(0).toUpperCase() + s.slice(1); + }; + + _chart._doColumnHeaderFnToString = function (f) { + // columnString(f) { + var s = String(f); + var i1 = s.indexOf('return '); + if (i1 >= 0) { + var i2 = s.lastIndexOf(';'); + if (i2 >= 0) { + s = s.substring(i1 + 7, i2); + var i3 = s.indexOf('numberFormat'); + if (i3 >= 0) { + s = s.replace('numberFormat', ''); + } + } + } + return s; + }; + + function renderGroups () { + // The 'original' example uses all 'functions'. + // If all 'functions' are used, then don't remove/add a header, and leave + // the html alone. This preserves the functionality of earlier releases. + // A 2nd option is a string representing a field in the data. + // A third option is to supply an Object such as an array of 'information', and + // supply your own _doColumnHeaderFormat and _doColumnValueFormat functions to + // create what you need. + var bAllFunctions = true; + _columns.forEach(function (f) { + bAllFunctions = bAllFunctions & (typeof f === 'function'); + }); + + if (!bAllFunctions) { + // ensure one thead + var thead = _chart.selectAll('thead').data([0]); + thead.enter().append('thead'); + thead.exit().remove(); + + // with one tr + var headrow = thead.selectAll('tr').data([0]); + headrow.enter().append('tr'); + headrow.exit().remove(); + + // with a th for each column + var headcols = headrow.selectAll('th') + .data(_columns); + headcols.enter().append('th'); + headcols.exit().remove(); + + headcols + .attr('class', HEAD_CSS_CLASS) + .html(function (d) { + return (_chart._doColumnHeaderFormat(d)); + + }); + } + + var groups = _chart.root().selectAll('tbody') + .data(nestEntries(), function (d) { + return _chart.keyAccessor()(d); + }); + + var rowGroup = groups + .enter() + .append('tbody'); + + if (_showGroups === true) { + rowGroup + .append('tr') + .attr('class', GROUP_CSS_CLASS) + .append('td') + .attr('class', LABEL_CSS_CLASS) + .attr('colspan', _columns.length) + .html(function (d) { + return _chart.keyAccessor()(d); + }); + } + + groups.exit().remove(); + + return rowGroup; + } + + function nestEntries () { + var entries; + if (_order === d3.ascending) { + entries = _chart.dimension().bottom(_size); + } else { + entries = _chart.dimension().top(_size); + } + + return d3.nest() + .key(_chart.group()) + .sortKeys(_order) + .entries(entries.sort(function (a, b) { + return _order(_sortBy(a), _sortBy(b)); + }).slice(_beginSlice, _endSlice)); + } + + function renderRows (groups) { + var rows = groups.order() + .selectAll('tr.' + ROW_CSS_CLASS) + .data(function (d) { + return d.values; + }); + + var rowEnter = rows.enter() + .append('tr') + .attr('class', ROW_CSS_CLASS); + + _columns.forEach(function (v, i) { + rowEnter.append('td') + .attr('class', COLUMN_CSS_CLASS + ' _' + i) + .html(function (d) { + return _chart._doColumnValueFormat(v, d); + }); + }); + + rows.exit().remove(); + + return rows; + } + + _chart._doRedraw = function () { + return _chart._doRender(); + }; + + /** + * Get or set the group function for the data table. The group function takes a data row and + * returns the key to specify to {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Arrays.md#d3_nest d3.nest} + * to split rows into groups. + * + * Do not pass in a crossfilter group as this will not work. + * @method group + * @memberof dc.dataTable + * @instance + * @example + * // group rows by the value of their field + * chart + * .group(function(d) { return d.field; }) + * @param {Function} groupFunction Function taking a row of data and returning the nest key. + * @returns {Function|dc.dataTable} + */ + + /** + * Get or set the table size which determines the number of rows displayed by the widget. + * @method size + * @memberof dc.dataTable + * @instance + * @param {Number} [size=25] + * @returns {Number|dc.dataTable} + */ + _chart.size = function (size) { + if (!arguments.length) { + return _size; + } + _size = size; + return _chart; + }; + + /** + * Get or set the index of the beginning slice which determines which entries get displayed + * by the widget. Useful when implementing pagination. + * + * Note: the sortBy function will determine how the rows are ordered for pagination purposes. + + * See the {@link http://dc-js.github.io/dc.js/examples/table-pagination.html table pagination example} + * to see how to implement the pagination user interface using `beginSlice` and `endSlice`. + * @method beginSlice + * @memberof dc.dataTable + * @instance + * @param {Number} [beginSlice=0] + * @returns {Number|dc.dataTable} + */ + _chart.beginSlice = function (beginSlice) { + if (!arguments.length) { + return _beginSlice; + } + _beginSlice = beginSlice; + return _chart; + }; + + /** + * Get or set the index of the end slice which determines which entries get displayed by the + * widget. Useful when implementing pagination. See {@link dc.dataTable#beginSlice `beginSlice`} for more information. + * @method endSlice + * @memberof dc.dataTable + * @instance + * @param {Number|undefined} [endSlice=undefined] + * @returns {Number|dc.dataTable} + */ + _chart.endSlice = function (endSlice) { + if (!arguments.length) { + return _endSlice; + } + _endSlice = endSlice; + return _chart; + }; + + /** + * Get or set column functions. The data table widget supports several methods of specifying the + * columns to display. + * + * The original method uses an array of functions to generate dynamic columns. Column functions + * are simple javascript functions with only one input argument `d` which represents a row in + * the data set. The return value of these functions will be used to generate the content for + * each cell. However, this method requires the HTML for the table to have a fixed set of column + * headers. + * + * <pre><code>chart.columns([ + * function(d) { return d.date; }, + * function(d) { return d.open; }, + * function(d) { return d.close; }, + * function(d) { return numberFormat(d.close - d.open); }, + * function(d) { return d.volume; } + * ]); + * </code></pre> + * + * In the second method, you can list the columns to read from the data without specifying it as + * a function, except where necessary (ie, computed columns). Note the data element name is + * capitalized when displayed in the table header. You can also mix in functions as necessary, + * using the third `{label, format}` form, as shown below. + * + * <pre><code>chart.columns([ + * "date", // d["date"], ie, a field accessor; capitalized automatically + * "open", // ... + * "close", // ... + * { + * label: "Change", + * format: function (d) { + * return numberFormat(d.close - d.open); + * } + * }, + * "volume" // d["volume"], ie, a field accessor; capitalized automatically + * ]); + * </code></pre> + * + * In the third example, we specify all fields using the `{label, format}` method: + * <pre><code>chart.columns([ + * { + * label: "Date", + * format: function (d) { return d.date; } + * }, + * { + * label: "Open", + * format: function (d) { return numberFormat(d.open); } + * }, + * { + * label: "Close", + * format: function (d) { return numberFormat(d.close); } + * }, + * { + * label: "Change", + * format: function (d) { return numberFormat(d.close - d.open); } + * }, + * { + * label: "Volume", + * format: function (d) { return d.volume; } + * } + * ]); + * </code></pre> + * + * You may wish to override the dataTable functions `_doColumnHeaderCapitalize` and + * `_doColumnHeaderFnToString`, which are used internally to translate the column information or + * function into a displayed header. The first one is used on the "string" column specifier; the + * second is used to transform a stringified function into something displayable. For the Stock + * example, the function for Change becomes the table header **d.close - d.open**. + * + * Finally, you can even specify a completely different form of column definition. To do this, + * override `_chart._doColumnHeaderFormat` and `_chart._doColumnValueFormat` Be aware that + * fields without numberFormat specification will be displayed just as they are stored in the + * data, unformatted. + * @method columns + * @memberof dc.dataTable + * @instance + * @param {Array<Function>} [columns=[]] + * @returns {Array<Function>}|dc.dataTable} + */ + _chart.columns = function (columns) { + if (!arguments.length) { + return _columns; + } + _columns = columns; + return _chart; + }; + + /** + * Get or set sort-by function. This function works as a value accessor at row level and returns a + * particular field to be sorted by. + * @method sortBy + * @memberof dc.dataTable + * @instance + * @example + * chart.sortBy(function(d) { + * return d.date; + * }); + * @param {Function} [sortBy=identity function] + * @returns {Function|dc.dataTable} + */ + _chart.sortBy = function (sortBy) { + if (!arguments.length) { + return _sortBy; + } + _sortBy = sortBy; + return _chart; + }; + + /** + * Get or set sort order. If the order is `d3.ascending`, the data table will use + * `dimension().bottom()` to fetch the data; otherwise it will use `dimension().top()` + * @method order + * @memberof dc.dataTable + * @instance + * @see {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Arrays.md#d3_ascending d3.ascending} + * @see {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Arrays.md#d3_descending d3.descending} + * @example + * chart.order(d3.descending); + * @param {Function} [order=d3.ascending] + * @returns {Function|dc.dataTable} + */ + _chart.order = function (order) { + if (!arguments.length) { + return _order; + } + _order = order; + return _chart; + }; + + /** + * Get or set if group rows will be shown. The dataTable {@link dc.dataTable#group group} + * function must be specified even if groups are not shown. + * @method showGroups + * @memberof dc.dataTable + * @instance + * @example + * chart + * .group([value], [name]) + * .showGroups(true|false); + * @param {Boolean} [showGroups=true] + * @returns {Boolean|dc.dataTable} + */ + _chart.showGroups = function (showGroups) { + if (!arguments.length) { + return _showGroups; + } + _showGroups = showGroups; + return _chart; + }; + + return _chart.anchor(parent, chartGroup); +}; + +/** + * Data grid is a simple widget designed to list the filtered records, providing + * a simple way to define how the items are displayed. + * + * Note: Unlike other charts, the data grid chart (and data table) use the {@link dc.dataGrid#group group} attribute as a keying function + * for {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Arrays.md#nest nesting} the data together in groups. + * Do not pass in a crossfilter group as this will not work. + * + * Examples: + * - {@link http://europarl.me/dc.js/web/ep/index.html List of members of the european parliament} + * @class dataGrid + * @memberof dc + * @mixes dc.baseMixin + * @param {String|node|d3.selection} parent - Any valid + * {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Selections.md#selecting-elements d3 single selector} specifying + * a dom block element such as a div; or a dom element or d3 selection. + * @param {String} [chartGroup] - The name of the chart group this chart instance should be placed in. + * Interaction with a chart will only trigger events and redraws within the chart's group. + * @returns {dc.dataGrid} + */ +dc.dataGrid = function (parent, chartGroup) { + var LABEL_CSS_CLASS = 'dc-grid-label'; + var ITEM_CSS_CLASS = 'dc-grid-item'; + var GROUP_CSS_CLASS = 'dc-grid-group'; + var GRID_CSS_CLASS = 'dc-grid-top'; + + var _chart = dc.baseMixin({}); + + var _size = 999; // shouldn't be needed, but you might + var _html = function (d) { return 'you need to provide an html() handling param: ' + JSON.stringify(d); }; + var _sortBy = function (d) { + return d; + }; + var _order = d3.ascending; + var _beginSlice = 0, _endSlice; + + var _htmlGroup = function (d) { + return '<div class=\'' + GROUP_CSS_CLASS + '\'><h1 class=\'' + LABEL_CSS_CLASS + '\'>' + + _chart.keyAccessor()(d) + '</h1></div>'; + }; + + _chart._doRender = function () { + _chart.selectAll('div.' + GRID_CSS_CLASS).remove(); + + renderItems(renderGroups()); + + return _chart; + }; + + function renderGroups () { + var groups = _chart.root().selectAll('div.' + GRID_CSS_CLASS) + .data(nestEntries(), function (d) { + return _chart.keyAccessor()(d); + }); + + var itemGroup = groups + .enter() + .append('div') + .attr('class', GRID_CSS_CLASS); + + if (_htmlGroup) { + itemGroup + .html(function (d) { + return _htmlGroup(d); + }); + } + + groups.exit().remove(); + return itemGroup; + } + + function nestEntries () { + var entries = _chart.dimension().top(_size); + + return d3.nest() + .key(_chart.group()) + .sortKeys(_order) + .entries(entries.sort(function (a, b) { + return _order(_sortBy(a), _sortBy(b)); + }).slice(_beginSlice, _endSlice)); + } + + function renderItems (groups) { + var items = groups.order() + .selectAll('div.' + ITEM_CSS_CLASS) + .data(function (d) { + return d.values; + }); + + items.enter() + .append('div') + .attr('class', ITEM_CSS_CLASS) + .html(function (d) { + return _html(d); + }); + + items.exit().remove(); + + return items; + } + + _chart._doRedraw = function () { + return _chart._doRender(); + }; + + /** + * Get or set the group function for the data grid. The group function takes a data row and + * returns the key to specify to {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Arrays.md#d3_nest d3.nest} + * to split rows into groups. + * + * Do not pass in a crossfilter group as this will not work. + * @method group + * @memberof dc.dataGrid + * @instance + * @example + * // group rows by the value of their field + * chart + * .group(function(d) { return d.field; }) + * @param {Function} groupFunction Function taking a row of data and returning the nest key. + * @returns {Function|dc.dataTable} + */ + + /** + * Get or set the index of the beginning slice which determines which entries get displayed by the widget. + * Useful when implementing pagination. + * @method beginSlice + * @memberof dc.dataGrid + * @instance + * @param {Number} [beginSlice=0] + * @returns {Number|dc.dataGrid} + */ + _chart.beginSlice = function (beginSlice) { + if (!arguments.length) { + return _beginSlice; + } + _beginSlice = beginSlice; + return _chart; + }; + + /** + * Get or set the index of the end slice which determines which entries get displayed by the widget. + * Useful when implementing pagination. + * @method endSlice + * @memberof dc.dataGrid + * @instance + * @param {Number} [endSlice] + * @returns {Number|dc.dataGrid} + */ + _chart.endSlice = function (endSlice) { + if (!arguments.length) { + return _endSlice; + } + _endSlice = endSlice; + return _chart; + }; + + /** + * Get or set the grid size which determines the number of items displayed by the widget. + * @method size + * @memberof dc.dataGrid + * @instance + * @param {Number} [size=999] + * @returns {Number|dc.dataGrid} + */ + _chart.size = function (size) { + if (!arguments.length) { + return _size; + } + _size = size; + return _chart; + }; + + /** + * Get or set the function that formats an item. The data grid widget uses a + * function to generate dynamic html. Use your favourite templating engine or + * generate the string directly. + * @method html + * @memberof dc.dataGrid + * @instance + * @example + * chart.html(function (d) { return '<div class='item '+data.exampleCategory+''>'+data.exampleString+'</div>';}); + * @param {Function} [html] + * @returns {Function|dc.dataGrid} + */ + _chart.html = function (html) { + if (!arguments.length) { + return _html; + } + _html = html; + return _chart; + }; + + /** + * Get or set the function that formats a group label. + * @method htmlGroup + * @memberof dc.dataGrid + * @instance + * @example + * chart.htmlGroup (function (d) { return '<h2>'.d.key . 'with ' . d.values.length .' items</h2>'}); + * @param {Function} [htmlGroup] + * @returns {Function|dc.dataGrid} + */ + _chart.htmlGroup = function (htmlGroup) { + if (!arguments.length) { + return _htmlGroup; + } + _htmlGroup = htmlGroup; + return _chart; + }; + + /** + * Get or set sort-by function. This function works as a value accessor at the item + * level and returns a particular field to be sorted. + * @method sortBy + * @memberof dc.dataGrid + * @instance + * @example + * chart.sortBy(function(d) { + * return d.date; + * }); + * @param {Function} [sortByFunction] + * @returns {Function|dc.dataGrid} + */ + _chart.sortBy = function (sortByFunction) { + if (!arguments.length) { + return _sortBy; + } + _sortBy = sortByFunction; + return _chart; + }; + + /** + * Get or set sort the order function. + * @method order + * @memberof dc.dataGrid + * @instance + * @see {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Arrays.md#d3_ascending d3.ascending} + * @see {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Arrays.md#d3_descending d3.descending} + * @example + * chart.order(d3.descending); + * @param {Function} [order=d3.ascending] + * @returns {Function|dc.dataGrid} + */ + _chart.order = function (order) { + if (!arguments.length) { + return _order; + } + _order = order; + return _chart; + }; + + return _chart.anchor(parent, chartGroup); +}; + +/** + * A concrete implementation of a general purpose bubble chart that allows data visualization using the + * following dimensions: + * - x axis position + * - y axis position + * - bubble radius + * - color + * + * Examples: + * - {@link http://dc-js.github.com/dc.js/ Nasdaq 100 Index} + * - {@link http://dc-js.github.com/dc.js/vc/index.html US Venture Capital Landscape 2011} + * @class bubbleChart + * @memberof dc + * @mixes dc.bubbleMixin + * @mixes dc.coordinateGridMixin + * @example + * // create a bubble chart under #chart-container1 element using the default global chart group + * var bubbleChart1 = dc.bubbleChart('#chart-container1'); + * // create a bubble chart under #chart-container2 element using chart group A + * var bubbleChart2 = dc.bubbleChart('#chart-container2', 'chartGroupA'); + * @param {String|node|d3.selection} parent - Any valid + * {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Selections.md#selecting-elements d3 single selector} specifying + * a dom block element such as a div; or a dom element or d3 selection. + * @param {String} [chartGroup] - The name of the chart group this chart instance should be placed in. + * Interaction with a chart will only trigger events and redraws within the chart's group. + * @returns {dc.bubbleChart} + */ +dc.bubbleChart = function (parent, chartGroup) { + var _chart = dc.bubbleMixin(dc.coordinateGridMixin({})); + + var _elasticRadius = false; + var _sortBubbleSize = false; + + _chart.transitionDuration(750); + + _chart.transitionDelay(0); + + var bubbleLocator = function (d) { + return 'translate(' + (bubbleX(d)) + ',' + (bubbleY(d)) + ')'; + }; + + /** + * Turn on or off the elastic bubble radius feature, or return the value of the flag. If this + * feature is turned on, then bubble radii will be automatically rescaled to fit the chart better. + * @method elasticRadius + * @memberof dc.bubbleChart + * @instance + * @param {Boolean} [elasticRadius=false] + * @returns {Boolean|dc.bubbleChart} + */ + _chart.elasticRadius = function (elasticRadius) { + if (!arguments.length) { + return _elasticRadius; + } + _elasticRadius = elasticRadius; + return _chart; + }; + + /** + * Turn on or off the bubble sorting feature, or return the value of the flag. If enabled, + * bubbles will be sorted by their radius, with smaller bubbles in front. + * @method sortBubbleSize + * @memberof dc.bubbleChart + * @instance + * @param {Boolean} [sortBubbleSize=false] + * @returns {Boolean|dc.bubbleChart} + */ + _chart.sortBubbleSize = function (sortBubbleSize) { + if (!arguments.length) { + return _sortBubbleSize; + } + _sortBubbleSize = sortBubbleSize; + return _chart; + }; + + _chart.plotData = function () { + if (_elasticRadius) { + _chart.r().domain([_chart.rMin(), _chart.rMax()]); + } + + _chart.r().range([_chart.MIN_RADIUS, _chart.xAxisLength() * _chart.maxBubbleRelativeSize()]); + + var data = _chart.data(); + if (_sortBubbleSize) { + // sort descending so smaller bubbles are on top + var radiusAccessor = _chart.radiusValueAccessor(); + data.sort(function (a, b) { return d3.descending(radiusAccessor(a), radiusAccessor(b)); }); + } + var bubbleG = _chart.chartBodyG().selectAll('g.' + _chart.BUBBLE_NODE_CLASS) + .data(data, function (d) { return d.key; }); + if (_sortBubbleSize) { + // Call order here to update dom order based on sort + bubbleG.order(); + } + + renderNodes(bubbleG); + + updateNodes(bubbleG); + + removeNodes(bubbleG); + + _chart.fadeDeselectedArea(); + }; + + function renderNodes (bubbleG) { + var bubbleGEnter = bubbleG.enter().append('g'); + + bubbleGEnter + .attr('class', _chart.BUBBLE_NODE_CLASS) + .attr('transform', bubbleLocator) + .append('circle').attr('class', function (d, i) { + return _chart.BUBBLE_CLASS + ' _' + i; + }) + .on('click', _chart.onClick) + .attr('fill', _chart.getColor) + .attr('r', 0); + dc.transition(bubbleG, _chart.transitionDuration(), _chart.transitionDelay()) + .select('circle.' + _chart.BUBBLE_CLASS) + .attr('r', function (d) { + return _chart.bubbleR(d); + }) + .attr('opacity', function (d) { + return (_chart.bubbleR(d) > 0) ? 1 : 0; + }); + + _chart._doRenderLabel(bubbleGEnter); + + _chart._doRenderTitles(bubbleGEnter); + } + + function updateNodes (bubbleG) { + dc.transition(bubbleG, _chart.transitionDuration(), _chart.transitionDelay()) + .attr('transform', bubbleLocator) + .select('circle.' + _chart.BUBBLE_CLASS) + .attr('fill', _chart.getColor) + .attr('r', function (d) { + return _chart.bubbleR(d); + }) + .attr('opacity', function (d) { + return (_chart.bubbleR(d) > 0) ? 1 : 0; + }); + + _chart.doUpdateLabels(bubbleG); + _chart.doUpdateTitles(bubbleG); + } + + function removeNodes (bubbleG) { + bubbleG.exit().remove(); + } + + function bubbleX (d) { + var x = _chart.x()(_chart.keyAccessor()(d)); + if (isNaN(x)) { + x = 0; + } + return x; + } + + function bubbleY (d) { + var y = _chart.y()(_chart.valueAccessor()(d)); + if (isNaN(y)) { + y = 0; + } + return y; + } + + _chart.renderBrush = function () { + // override default x axis brush from parent chart + }; + + _chart.redrawBrush = function () { + // override default x axis brush from parent chart + _chart.fadeDeselectedArea(); + }; + + return _chart.anchor(parent, chartGroup); +}; + +/** + * Composite charts are a special kind of chart that render multiple charts on the same Coordinate + * Grid. You can overlay (compose) different bar/line/area charts in a single composite chart to + * achieve some quite flexible charting effects. + * @class compositeChart + * @memberof dc + * @mixes dc.coordinateGridMixin + * @example + * // create a composite chart under #chart-container1 element using the default global chart group + * var compositeChart1 = dc.compositeChart('#chart-container1'); + * // create a composite chart under #chart-container2 element using chart group A + * var compositeChart2 = dc.compositeChart('#chart-container2', 'chartGroupA'); + * @param {String|node|d3.selection} parent - Any valid + * {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Selections.md#selecting-elements d3 single selector} specifying + * a dom block element such as a div; or a dom element or d3 selection. + * @param {String} [chartGroup] - The name of the chart group this chart instance should be placed in. + * Interaction with a chart will only trigger events and redraws within the chart's group. + * @returns {dc.compositeChart} + */ +dc.compositeChart = function (parent, chartGroup) { + + var SUB_CHART_CLASS = 'sub'; + var DEFAULT_RIGHT_Y_AXIS_LABEL_PADDING = 12; + + var _chart = dc.coordinateGridMixin({}); + var _children = []; + + var _childOptions = {}; + + var _shareColors = false, + _shareTitle = true, + _alignYAxes = false; + + var _rightYAxis = d3.svg.axis(), + _rightYAxisLabel = 0, + _rightYAxisLabelPadding = DEFAULT_RIGHT_Y_AXIS_LABEL_PADDING, + _rightY, + _rightAxisGridLines = false; + + _chart._mandatoryAttributes([]); + _chart.transitionDuration(500); + _chart.transitionDelay(0); + + dc.override(_chart, '_generateG', function () { + var g = this.__generateG(); + + for (var i = 0; i < _children.length; ++i) { + var child = _children[i]; + + generateChildG(child, i); + + if (!child.dimension()) { + child.dimension(_chart.dimension()); + } + if (!child.group()) { + child.group(_chart.group()); + } + + child.chartGroup(_chart.chartGroup()); + child.svg(_chart.svg()); + child.xUnits(_chart.xUnits()); + child.transitionDuration(_chart.transitionDuration(), _chart.transitionDelay()); + child.brushOn(_chart.brushOn()); + child.renderTitle(_chart.renderTitle()); + child.elasticX(_chart.elasticX()); + } + + return g; + }); + + _chart._brushing = function () { + var extent = _chart.extendBrush(); + var brushIsEmpty = _chart.brushIsEmpty(extent); + + for (var i = 0; i < _children.length; ++i) { + _children[i].replaceFilter(brushIsEmpty ? null : extent); + } + }; + + _chart._prepareYAxis = function () { + var left = (leftYAxisChildren().length !== 0); + var right = (rightYAxisChildren().length !== 0); + var ranges = calculateYAxisRanges(left, right); + + if (left) { prepareLeftYAxis(ranges); } + if (right) { prepareRightYAxis(ranges); } + + if (leftYAxisChildren().length > 0 && !_rightAxisGridLines) { + _chart._renderHorizontalGridLinesForAxis(_chart.g(), _chart.y(), _chart.yAxis()); + } else if (rightYAxisChildren().length > 0) { + _chart._renderHorizontalGridLinesForAxis(_chart.g(), _rightY, _rightYAxis); + } + }; + + _chart.renderYAxis = function () { + if (leftYAxisChildren().length !== 0) { + _chart.renderYAxisAt('y', _chart.yAxis(), _chart.margins().left); + _chart.renderYAxisLabel('y', _chart.yAxisLabel(), -90); + } + + if (rightYAxisChildren().length !== 0) { + _chart.renderYAxisAt('yr', _chart.rightYAxis(), _chart.width() - _chart.margins().right); + _chart.renderYAxisLabel('yr', _chart.rightYAxisLabel(), 90, _chart.width() - _rightYAxisLabelPadding); + } + }; + + function calculateYAxisRanges (left, right) { + var lyAxisMin, lyAxisMax, ryAxisMin, ryAxisMax; + var ranges; + + if (left) { + lyAxisMin = yAxisMin(); + lyAxisMax = yAxisMax(); + } + + if (right) { + ryAxisMin = rightYAxisMin(); + ryAxisMax = rightYAxisMax(); + } + + if (_chart.alignYAxes() && left && right) { + ranges = alignYAxisRanges(lyAxisMin, lyAxisMax, ryAxisMin, ryAxisMax); + } + + return ranges || { + lyAxisMin: lyAxisMin, + lyAxisMax: lyAxisMax, + ryAxisMin: ryAxisMin, + ryAxisMax: ryAxisMax + }; + } + + function alignYAxisRanges (lyAxisMin, lyAxisMax, ryAxisMin, ryAxisMax) { + // since the two series will share a zero, each Y is just a multiple + // of the other. and the ratio should be the ratio of the ranges of the + // input data, so that they come out the same height. so we just min/max + + // note: both ranges already include zero due to the stack mixin (#667) + // if #667 changes, we can reconsider whether we want data height or + // height from zero to be equal. and it will be possible for the axes + // to be aligned but not visible. + var extentRatio = (ryAxisMax - ryAxisMin) / (lyAxisMax - lyAxisMin); + + return { + lyAxisMin: Math.min(lyAxisMin, ryAxisMin / extentRatio), + lyAxisMax: Math.max(lyAxisMax, ryAxisMax / extentRatio), + ryAxisMin: Math.min(ryAxisMin, lyAxisMin * extentRatio), + ryAxisMax: Math.max(ryAxisMax, lyAxisMax * extentRatio) + }; + } + + function prepareRightYAxis (ranges) { + var needDomain = _chart.rightY() === undefined || _chart.elasticY(), + needRange = needDomain || _chart.resizing(); + if (_chart.rightY() === undefined) { + _chart.rightY(d3.scale.linear()); + } + if (needDomain) { + _chart.rightY().domain([ranges.ryAxisMin, ranges.ryAxisMax]); + } + if (needRange) { + _chart.rightY().rangeRound([_chart.yAxisHeight(), 0]); + } + + _chart.rightY().range([_chart.yAxisHeight(), 0]); + _chart.rightYAxis(_chart.rightYAxis().scale(_chart.rightY())); + + _chart.rightYAxis().orient('right'); + } + + function prepareLeftYAxis (ranges) { + var needDomain = _chart.y() === undefined || _chart.elasticY(), + needRange = needDomain || _chart.resizing(); + if (_chart.y() === undefined) { + _chart.y(d3.scale.linear()); + } + if (needDomain) { + _chart.y().domain([ranges.lyAxisMin, ranges.lyAxisMax]); + } + if (needRange) { + _chart.y().rangeRound([_chart.yAxisHeight(), 0]); + } + + _chart.y().range([_chart.yAxisHeight(), 0]); + _chart.yAxis(_chart.yAxis().scale(_chart.y())); + + _chart.yAxis().orient('left'); + } + + function generateChildG (child, i) { + child._generateG(_chart.g()); + child.g().attr('class', SUB_CHART_CLASS + ' _' + i); + } + + _chart.plotData = function () { + for (var i = 0; i < _children.length; ++i) { + var child = _children[i]; + + if (!child.g()) { + generateChildG(child, i); + } + + if (_shareColors) { + child.colors(_chart.colors()); + } + + child.x(_chart.x()); + + child.xAxis(_chart.xAxis()); + + if (child.useRightYAxis()) { + child.y(_chart.rightY()); + child.yAxis(_chart.rightYAxis()); + } else { + child.y(_chart.y()); + child.yAxis(_chart.yAxis()); + } + + child.plotData(); + + child._activateRenderlets(); + } + }; + + /** + * Get or set whether to draw gridlines from the right y axis. Drawing from the left y axis is the + * default behavior. This option is only respected when subcharts with both left and right y-axes + * are present. + * @method useRightAxisGridLines + * @memberof dc.compositeChart + * @instance + * @param {Boolean} [useRightAxisGridLines=false] + * @returns {Boolean|dc.compositeChart} + */ + _chart.useRightAxisGridLines = function (useRightAxisGridLines) { + if (!arguments) { + return _rightAxisGridLines; + } + + _rightAxisGridLines = useRightAxisGridLines; + return _chart; + }; + + /** + * Get or set chart-specific options for all child charts. This is equivalent to calling + * {@link dc.baseMixin#options .options} on each child chart. + * @method childOptions + * @memberof dc.compositeChart + * @instance + * @param {Object} [childOptions] + * @returns {Object|dc.compositeChart} + */ + _chart.childOptions = function (childOptions) { + if (!arguments.length) { + return _childOptions; + } + _childOptions = childOptions; + _children.forEach(function (child) { + child.options(_childOptions); + }); + return _chart; + }; + + _chart.fadeDeselectedArea = function () { + for (var i = 0; i < _children.length; ++i) { + var child = _children[i]; + child.brush(_chart.brush()); + child.fadeDeselectedArea(); + } + }; + + /** + * Set or get the right y axis label. + * @method rightYAxisLabel + * @memberof dc.compositeChart + * @instance + * @param {String} [rightYAxisLabel] + * @param {Number} [padding] + * @returns {String|dc.compositeChart} + */ + _chart.rightYAxisLabel = function (rightYAxisLabel, padding) { + if (!arguments.length) { + return _rightYAxisLabel; + } + _rightYAxisLabel = rightYAxisLabel; + _chart.margins().right -= _rightYAxisLabelPadding; + _rightYAxisLabelPadding = (padding === undefined) ? DEFAULT_RIGHT_Y_AXIS_LABEL_PADDING : padding; + _chart.margins().right += _rightYAxisLabelPadding; + return _chart; + }; + + /** + * Combine the given charts into one single composite coordinate grid chart. + * @method compose + * @memberof dc.compositeChart + * @instance + * @example + * moveChart.compose([ + * // when creating sub-chart you need to pass in the parent chart + * dc.lineChart(moveChart) + * .group(indexAvgByMonthGroup) // if group is missing then parent's group will be used + * .valueAccessor(function (d){return d.value.avg;}) + * // most of the normal functions will continue to work in a composed chart + * .renderArea(true) + * .stack(monthlyMoveGroup, function (d){return d.value;}) + * .title(function (d){ + * var value = d.value.avg?d.value.avg:d.value; + * if(isNaN(value)) value = 0; + * return dateFormat(d.key) + '\n' + numberFormat(value); + * }), + * dc.barChart(moveChart) + * .group(volumeByMonthGroup) + * .centerBar(true) + * ]); + * @param {Array<Chart>} [subChartArray] + * @returns {dc.compositeChart} + */ + _chart.compose = function (subChartArray) { + _children = subChartArray; + _children.forEach(function (child) { + child.height(_chart.height()); + child.width(_chart.width()); + child.margins(_chart.margins()); + + if (_shareTitle) { + child.title(_chart.title()); + } + + child.options(_childOptions); + }); + return _chart; + }; + + /** + * Returns the child charts which are composed into the composite chart. + * @method children + * @memberof dc.compositeChart + * @instance + * @returns {Array<dc.baseMixin>} + */ + _chart.children = function () { + return _children; + }; + + /** + * Get or set color sharing for the chart. If set, the {@link dc.colorMixin#colors .colors()} value from this chart + * will be shared with composed children. Additionally if the child chart implements + * Stackable and has not set a custom .colorAccessor, then it will generate a color + * specific to its order in the composition. + * @method shareColors + * @memberof dc.compositeChart + * @instance + * @param {Boolean} [shareColors=false] + * @returns {Boolean|dc.compositeChart} + */ + _chart.shareColors = function (shareColors) { + if (!arguments.length) { + return _shareColors; + } + _shareColors = shareColors; + return _chart; + }; + + /** + * Get or set title sharing for the chart. If set, the {@link dc.baseMixin#title .title()} value from + * this chart will be shared with composed children. + * @method shareTitle + * @memberof dc.compositeChart + * @instance + * @param {Boolean} [shareTitle=true] + * @returns {Boolean|dc.compositeChart} + */ + _chart.shareTitle = function (shareTitle) { + if (!arguments.length) { + return _shareTitle; + } + _shareTitle = shareTitle; + return _chart; + }; + + /** + * Get or set the y scale for the right axis. The right y scale is typically automatically + * generated by the chart implementation. + * @method rightY + * @memberof dc.compositeChart + * @instance + * @see {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Scales.md d3.scale} + * @param {d3.scale} [yScale] + * @returns {d3.scale|dc.compositeChart} + */ + _chart.rightY = function (yScale) { + if (!arguments.length) { + return _rightY; + } + _rightY = yScale; + _chart.rescale(); + return _chart; + }; + + /** + * Get or set alignment between left and right y axes. A line connecting '0' on both y axis + * will be parallel to x axis. This only has effect when {@link #dc.coordinateGridMixin+elasticY elasticY} is true. + * @method alignYAxes + * @memberof dc.compositeChart + * @instance + * @param {Boolean} [alignYAxes=false] + * @returns {Chart} + */ + _chart.alignYAxes = function (alignYAxes) { + if (!arguments.length) { + return _alignYAxes; + } + _alignYAxes = alignYAxes; + _chart.rescale(); + return _chart; + }; + + function leftYAxisChildren () { + return _children.filter(function (child) { + return !child.useRightYAxis(); + }); + } + + function rightYAxisChildren () { + return _children.filter(function (child) { + return child.useRightYAxis(); + }); + } + + function getYAxisMin (charts) { + return charts.map(function (c) { + return c.yAxisMin(); + }); + } + + delete _chart.yAxisMin; + function yAxisMin () { + return d3.min(getYAxisMin(leftYAxisChildren())); + } + + function rightYAxisMin () { + return d3.min(getYAxisMin(rightYAxisChildren())); + } + + function getYAxisMax (charts) { + return charts.map(function (c) { + return c.yAxisMax(); + }); + } + + delete _chart.yAxisMax; + function yAxisMax () { + return dc.utils.add(d3.max(getYAxisMax(leftYAxisChildren())), _chart.yAxisPadding()); + } + + function rightYAxisMax () { + return dc.utils.add(d3.max(getYAxisMax(rightYAxisChildren())), _chart.yAxisPadding()); + } + + function getAllXAxisMinFromChildCharts () { + return _children.map(function (c) { + return c.xAxisMin(); + }); + } + + dc.override(_chart, 'xAxisMin', function () { + return dc.utils.subtract(d3.min(getAllXAxisMinFromChildCharts()), _chart.xAxisPadding()); + }); + + function getAllXAxisMaxFromChildCharts () { + return _children.map(function (c) { + return c.xAxisMax(); + }); + } + + dc.override(_chart, 'xAxisMax', function () { + return dc.utils.add(d3.max(getAllXAxisMaxFromChildCharts()), _chart.xAxisPadding()); + }); + + _chart.legendables = function () { + return _children.reduce(function (items, child) { + if (_shareColors) { + child.colors(_chart.colors()); + } + items.push.apply(items, child.legendables()); + return items; + }, []); + }; + + _chart.legendHighlight = function (d) { + for (var j = 0; j < _children.length; ++j) { + var child = _children[j]; + child.legendHighlight(d); + } + }; + + _chart.legendReset = function (d) { + for (var j = 0; j < _children.length; ++j) { + var child = _children[j]; + child.legendReset(d); + } + }; + + _chart.legendToggle = function () { + console.log('composite should not be getting legendToggle itself'); + }; + + /** + * Set or get the right y axis used by the composite chart. This function is most useful when y + * axis customization is required. The y axis in dc.js is an instance of a [d3 axis + * object](https://github.com/d3/d3-3.x-api-reference/blob/master/SVG-Axes.md#axis) therefore it supports any valid + * d3 axis manipulation. + * + * **Caution**: The y axis is usually generated internally by dc; resetting it may cause + * unexpected results. + * @method rightYAxis + * @memberof dc.compositeChart + * @instance + * @see {@link https://github.com/d3/d3-3.x-api-reference/blob/master/SVG-Axes.md#axis d3.svg.axis} + * @example + * // customize y axis tick format + * chart.rightYAxis().tickFormat(function (v) {return v + '%';}); + * // customize y axis tick values + * chart.rightYAxis().tickValues([0, 100, 200, 300]); + * @param {d3.svg.axis} [rightYAxis] + * @returns {d3.svg.axis|dc.compositeChart} + */ + _chart.rightYAxis = function (rightYAxis) { + if (!arguments.length) { + return _rightYAxis; + } + _rightYAxis = rightYAxis; + return _chart; + }; + + return _chart.anchor(parent, chartGroup); +}; + +/** + * A series chart is a chart that shows multiple series of data overlaid on one chart, where the + * series is specified in the data. It is a specialization of Composite Chart and inherits all + * composite features other than recomposing the chart. + * + * Examples: + * - {@link http://dc-js.github.io/dc.js/examples/series.html Series Chart} + * @class seriesChart + * @memberof dc + * @mixes dc.compositeChart + * @example + * // create a series chart under #chart-container1 element using the default global chart group + * var seriesChart1 = dc.seriesChart("#chart-container1"); + * // create a series chart under #chart-container2 element using chart group A + * var seriesChart2 = dc.seriesChart("#chart-container2", "chartGroupA"); + * @param {String|node|d3.selection} parent - Any valid + * {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Selections.md#selecting-elements d3 single selector} specifying + * a dom block element such as a div; or a dom element or d3 selection. + * @param {String} [chartGroup] - The name of the chart group this chart instance should be placed in. + * Interaction with a chart will only trigger events and redraws within the chart's group. + * @returns {dc.seriesChart} + */ +dc.seriesChart = function (parent, chartGroup) { + var _chart = dc.compositeChart(parent, chartGroup); + + function keySort (a, b) { + return d3.ascending(_chart.keyAccessor()(a), _chart.keyAccessor()(b)); + } + + var _charts = {}; + var _chartFunction = dc.lineChart; + var _seriesAccessor; + var _seriesSort = d3.ascending; + var _valueSort = keySort; + + _chart._mandatoryAttributes().push('seriesAccessor', 'chart'); + _chart.shareColors(true); + + _chart._preprocessData = function () { + var keep = []; + var childrenChanged; + var nester = d3.nest().key(_seriesAccessor); + if (_seriesSort) { + nester.sortKeys(_seriesSort); + } + if (_valueSort) { + nester.sortValues(_valueSort); + } + var nesting = nester.entries(_chart.data()); + var children = + nesting.map(function (sub, i) { + var subChart = _charts[sub.key] || _chartFunction.call(_chart, _chart, chartGroup, sub.key, i); + if (!_charts[sub.key]) { + childrenChanged = true; + } + _charts[sub.key] = subChart; + keep.push(sub.key); + return subChart + .dimension(_chart.dimension()) + .group({all: d3.functor(sub.values)}, sub.key) + .keyAccessor(_chart.keyAccessor()) + .valueAccessor(_chart.valueAccessor()) + .brushOn(_chart.brushOn()); + }); + // this works around the fact compositeChart doesn't really + // have a removal interface + Object.keys(_charts) + .filter(function (c) {return keep.indexOf(c) === -1;}) + .forEach(function (c) { + clearChart(c); + childrenChanged = true; + }); + _chart._compose(children); + if (childrenChanged && _chart.legend()) { + _chart.legend().render(); + } + }; + + function clearChart (c) { + if (_charts[c].g()) { + _charts[c].g().remove(); + } + delete _charts[c]; + } + + function resetChildren () { + Object.keys(_charts).map(clearChart); + _charts = {}; + } + + /** + * Get or set the chart function, which generates the child charts. + * @method chart + * @memberof dc.seriesChart + * @instance + * @example + * // put interpolation on the line charts used for the series + * chart.chart(function(c) { return dc.lineChart(c).interpolate('basis'); }) + * // do a scatter series chart + * chart.chart(dc.scatterPlot) + * @param {Function} [chartFunction=dc.lineChart] + * @returns {Function|dc.seriesChart} + */ + _chart.chart = function (chartFunction) { + if (!arguments.length) { + return _chartFunction; + } + _chartFunction = chartFunction; + resetChildren(); + return _chart; + }; + + /** + * **mandatory** + * + * Get or set accessor function for the displayed series. Given a datum, this function + * should return the series that datum belongs to. + * @method seriesAccessor + * @memberof dc.seriesChart + * @instance + * @example + * // simple series accessor + * chart.seriesAccessor(function(d) { return "Expt: " + d.key[0]; }) + * @param {Function} [accessor] + * @returns {Function|dc.seriesChart} + */ + _chart.seriesAccessor = function (accessor) { + if (!arguments.length) { + return _seriesAccessor; + } + _seriesAccessor = accessor; + resetChildren(); + return _chart; + }; + + /** + * Get or set a function to sort the list of series by, given series values. + * @method seriesSort + * @memberof dc.seriesChart + * @instance + * @see {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Arrays.md#d3_ascending d3.ascending} + * @see {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Arrays.md#d3_descending d3.descending} + * @example + * chart.seriesSort(d3.descending); + * @param {Function} [sortFunction=d3.ascending] + * @returns {Function|dc.seriesChart} + */ + _chart.seriesSort = function (sortFunction) { + if (!arguments.length) { + return _seriesSort; + } + _seriesSort = sortFunction; + resetChildren(); + return _chart; + }; + + /** + * Get or set a function to sort each series values by. By default this is the key accessor which, + * for example, will ensure a lineChart series connects its points in increasing key/x order, + * rather than haphazardly. + * @method valueSort + * @memberof dc.seriesChart + * @instance + * @see {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Arrays.md#d3_ascending d3.ascending} + * @see {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Arrays.md#d3_descending d3.descending} + * @example + * // Default value sort + * _chart.valueSort(function keySort (a, b) { + * return d3.ascending(_chart.keyAccessor()(a), _chart.keyAccessor()(b)); + * }); + * @param {Function} [sortFunction] + * @returns {Function|dc.seriesChart} + */ + _chart.valueSort = function (sortFunction) { + if (!arguments.length) { + return _valueSort; + } + _valueSort = sortFunction; + resetChildren(); + return _chart; + }; + + // make compose private + _chart._compose = _chart.compose; + delete _chart.compose; + + return _chart; +}; + +/** + * The geo choropleth chart is designed as an easy way to create a crossfilter driven choropleth map + * from GeoJson data. This chart implementation was inspired by + * {@link http://bl.ocks.org/4060606 the great d3 choropleth example}. + * + * Examples: + * - {@link http://dc-js.github.com/dc.js/vc/index.html US Venture Capital Landscape 2011} + * @class geoChoroplethChart + * @memberof dc + * @mixes dc.colorMixin + * @mixes dc.baseMixin + * @example + * // create a choropleth chart under '#us-chart' element using the default global chart group + * var chart1 = dc.geoChoroplethChart('#us-chart'); + * // create a choropleth chart under '#us-chart2' element using chart group A + * var chart2 = dc.compositeChart('#us-chart2', 'chartGroupA'); + * @param {String|node|d3.selection} parent - Any valid + * {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Selections.md#selecting-elements d3 single selector} specifying + * a dom block element such as a div; or a dom element or d3 selection. + * @param {String} [chartGroup] - The name of the chart group this chart instance should be placed in. + * Interaction with a chart will only trigger events and redraws within the chart's group. + * @returns {dc.geoChoroplethChart} + */ +dc.geoChoroplethChart = function (parent, chartGroup) { + var _chart = dc.colorMixin(dc.baseMixin({})); + + _chart.colorAccessor(function (d) { + return d || 0; + }); + + var _geoPath = d3.geo.path(); + var _projectionFlag; + + var _geoJsons = []; + + _chart._doRender = function () { + _chart.resetSvg(); + for (var layerIndex = 0; layerIndex < _geoJsons.length; ++layerIndex) { + var states = _chart.svg().append('g') + .attr('class', 'layer' + layerIndex); + + var regionG = states.selectAll('g.' + geoJson(layerIndex).name) + .data(geoJson(layerIndex).data) + .enter() + .append('g') + .attr('class', geoJson(layerIndex).name); + + regionG + .append('path') + .attr('fill', 'white') + .attr('d', _geoPath); + + regionG.append('title'); + + plotData(layerIndex); + } + _projectionFlag = false; + }; + + function plotData (layerIndex) { + var data = generateLayeredData(); + + if (isDataLayer(layerIndex)) { + var regionG = renderRegionG(layerIndex); + + renderPaths(regionG, layerIndex, data); + + renderTitle(regionG, layerIndex, data); + } + } + + function generateLayeredData () { + var data = {}; + var groupAll = _chart.data(); + for (var i = 0; i < groupAll.length; ++i) { + data[_chart.keyAccessor()(groupAll[i])] = _chart.valueAccessor()(groupAll[i]); + } + return data; + } + + function isDataLayer (layerIndex) { + return geoJson(layerIndex).keyAccessor; + } + + function renderRegionG (layerIndex) { + var regionG = _chart.svg() + .selectAll(layerSelector(layerIndex)) + .classed('selected', function (d) { + return isSelected(layerIndex, d); + }) + .classed('deselected', function (d) { + return isDeselected(layerIndex, d); + }) + .attr('class', function (d) { + var layerNameClass = geoJson(layerIndex).name; + var regionClass = dc.utils.nameToId(geoJson(layerIndex).keyAccessor(d)); + var baseClasses = layerNameClass + ' ' + regionClass; + if (isSelected(layerIndex, d)) { + baseClasses += ' selected'; + } + if (isDeselected(layerIndex, d)) { + baseClasses += ' deselected'; + } + return baseClasses; + }); + return regionG; + } + + function layerSelector (layerIndex) { + return 'g.layer' + layerIndex + ' g.' + geoJson(layerIndex).name; + } + + function isSelected (layerIndex, d) { + return _chart.hasFilter() && _chart.hasFilter(getKey(layerIndex, d)); + } + + function isDeselected (layerIndex, d) { + return _chart.hasFilter() && !_chart.hasFilter(getKey(layerIndex, d)); + } + + function getKey (layerIndex, d) { + return geoJson(layerIndex).keyAccessor(d); + } + + function geoJson (index) { + return _geoJsons[index]; + } + + function renderPaths (regionG, layerIndex, data) { + var paths = regionG + .select('path') + .attr('fill', function () { + var currentFill = d3.select(this).attr('fill'); + if (currentFill) { + return currentFill; + } + return 'none'; + }) + .on('click', function (d) { + return _chart.onClick(d, layerIndex); + }); + + dc.transition(paths, _chart.transitionDuration(), _chart.transitionDelay()).attr('fill', function (d, i) { + return _chart.getColor(data[geoJson(layerIndex).keyAccessor(d)], i); + }); + } + + _chart.onClick = function (d, layerIndex) { + var selectedRegion = geoJson(layerIndex).keyAccessor(d); + dc.events.trigger(function () { + _chart.filter(selectedRegion); + _chart.redrawGroup(); + }); + }; + + function renderTitle (regionG, layerIndex, data) { + if (_chart.renderTitle()) { + regionG.selectAll('title').text(function (d) { + var key = getKey(layerIndex, d); + var value = data[key]; + return _chart.title()({key: key, value: value}); + }); + } + } + + _chart._doRedraw = function () { + for (var layerIndex = 0; layerIndex < _geoJsons.length; ++layerIndex) { + plotData(layerIndex); + if (_projectionFlag) { + _chart.svg().selectAll('g.' + geoJson(layerIndex).name + ' path').attr('d', _geoPath); + } + } + _projectionFlag = false; + }; + + /** + * **mandatory** + * + * Use this function to insert a new GeoJson map layer. This function can be invoked multiple times + * if you have multiple GeoJson data layers to render on top of each other. If you overlay multiple + * layers with the same name the new overlay will override the existing one. + * @method overlayGeoJson + * @memberof dc.geoChoroplethChart + * @instance + * @see {@link http://geojson.org/ GeoJSON} + * @see {@link https://github.com/topojson/topojson/wiki TopoJSON} + * @see {@link https://github.com/topojson/topojson-1.x-api-reference/blob/master/API-Reference.md#wiki-feature topojson.feature} + * @example + * // insert a layer for rendering US states + * chart.overlayGeoJson(statesJson.features, 'state', function(d) { + * return d.properties.name; + * }); + * @param {geoJson} json - a geojson feed + * @param {String} name - name of the layer + * @param {Function} keyAccessor - accessor function used to extract 'key' from the GeoJson data. The key extracted by + * this function should match the keys returned by the crossfilter groups. + * @returns {dc.geoChoroplethChart} + */ + _chart.overlayGeoJson = function (json, name, keyAccessor) { + for (var i = 0; i < _geoJsons.length; ++i) { + if (_geoJsons[i].name === name) { + _geoJsons[i].data = json; + _geoJsons[i].keyAccessor = keyAccessor; + return _chart; + } + } + _geoJsons.push({name: name, data: json, keyAccessor: keyAccessor}); + return _chart; + }; + + /** + * Set custom geo projection function. See the available + * {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Geo-Projections.md d3 geo projection functions}. + * @method projection + * @memberof dc.geoChoroplethChart + * @instance + * @see {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Geo-Projections.md d3.geo.projection} + * @see {@link https://github.com/d3/d3-geo-projection Extended d3.geo.projection} + * @param {d3.projection} [projection=d3.geo.albersUsa()] + * @returns {dc.geoChoroplethChart} + */ + _chart.projection = function (projection) { + _geoPath.projection(projection); + _projectionFlag = true; + return _chart; + }; + + /** + * Returns all GeoJson layers currently registered with this chart. The returned array is a + * reference to this chart's internal data structure, so any modification to this array will also + * modify this chart's internal registration. + * @method geoJsons + * @memberof dc.geoChoroplethChart + * @instance + * @returns {Array<{name:String, data: Object, accessor: Function}>} + */ + _chart.geoJsons = function () { + return _geoJsons; + }; + + /** + * Returns the {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Geo-Paths.md#path d3.geo.path} object used to + * render the projection and features. Can be useful for figuring out the bounding box of the + * feature set and thus a way to calculate scale and translation for the projection. + * @method geoPath + * @memberof dc.geoChoroplethChart + * @instance + * @see {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Geo-Paths.md#path d3.geo.path} + * @returns {d3.geo.path} + */ + _chart.geoPath = function () { + return _geoPath; + }; + + /** + * Remove a GeoJson layer from this chart by name + * @method removeGeoJson + * @memberof dc.geoChoroplethChart + * @instance + * @param {String} name + * @returns {dc.geoChoroplethChart} + */ + _chart.removeGeoJson = function (name) { + var geoJsons = []; + + for (var i = 0; i < _geoJsons.length; ++i) { + var layer = _geoJsons[i]; + if (layer.name !== name) { + geoJsons.push(layer); + } + } + + _geoJsons = geoJsons; + + return _chart; + }; + + return _chart.anchor(parent, chartGroup); +}; + +/** + * The bubble overlay chart is quite different from the typical bubble chart. With the bubble overlay + * chart you can arbitrarily place bubbles on an existing svg or bitmap image, thus changing the + * typical x and y positioning while retaining the capability to visualize data using bubble radius + * and coloring. + * + * Examples: + * - {@link http://dc-js.github.com/dc.js/crime/index.html Canadian City Crime Stats} + * @class bubbleOverlay + * @memberof dc + * @mixes dc.bubbleMixin + * @mixes dc.baseMixin + * @example + * // create a bubble overlay chart on top of the '#chart-container1 svg' element using the default global chart group + * var bubbleChart1 = dc.bubbleOverlayChart('#chart-container1').svg(d3.select('#chart-container1 svg')); + * // create a bubble overlay chart on top of the '#chart-container2 svg' element using chart group A + * var bubbleChart2 = dc.compositeChart('#chart-container2', 'chartGroupA').svg(d3.select('#chart-container2 svg')); + * @param {String|node|d3.selection} parent - Any valid + * {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Selections.md#selecting-elements d3 single selector} specifying + * a dom block element such as a div; or a dom element or d3 selection. + * @param {String} [chartGroup] - The name of the chart group this chart instance should be placed in. + * Interaction with a chart will only trigger events and redraws within the chart's group. + * @returns {dc.bubbleOverlay} + */ +dc.bubbleOverlay = function (parent, chartGroup) { + var BUBBLE_OVERLAY_CLASS = 'bubble-overlay'; + var BUBBLE_NODE_CLASS = 'node'; + var BUBBLE_CLASS = 'bubble'; + + /** + * **mandatory** + * + * Set the underlying svg image element. Unlike other dc charts this chart will not generate a svg + * element; therefore the bubble overlay chart will not work if this function is not invoked. If the + * underlying image is a bitmap, then an empty svg will need to be created on top of the image. + * @method svg + * @memberof dc.bubbleOverlay + * @instance + * @example + * // set up underlying svg element + * chart.svg(d3.select('#chart svg')); + * @param {SVGElement|d3.selection} [imageElement] + * @returns {dc.bubbleOverlay} + */ + var _chart = dc.bubbleMixin(dc.baseMixin({})); + var _g; + var _points = []; + + _chart.transitionDuration(750); + + _chart.transitionDelay(0); + + _chart.radiusValueAccessor(function (d) { + return d.value; + }); + + /** + * **mandatory** + * + * Set up a data point on the overlay. The name of a data point should match a specific 'key' among + * data groups generated using keyAccessor. If a match is found (point name <-> data group key) + * then a bubble will be generated at the position specified by the function. x and y + * value specified here are relative to the underlying svg. + * @method point + * @memberof dc.bubbleOverlay + * @instance + * @param {String} name + * @param {Number} x + * @param {Number} y + * @returns {dc.bubbleOverlay} + */ + _chart.point = function (name, x, y) { + _points.push({name: name, x: x, y: y}); + return _chart; + }; + + _chart._doRender = function () { + _g = initOverlayG(); + + _chart.r().range([_chart.MIN_RADIUS, _chart.width() * _chart.maxBubbleRelativeSize()]); + + initializeBubbles(); + + _chart.fadeDeselectedArea(); + + return _chart; + }; + + function initOverlayG () { + _g = _chart.select('g.' + BUBBLE_OVERLAY_CLASS); + if (_g.empty()) { + _g = _chart.svg().append('g').attr('class', BUBBLE_OVERLAY_CLASS); + } + return _g; + } + + function initializeBubbles () { + var data = mapData(); + + _points.forEach(function (point) { + var nodeG = getNodeG(point, data); + + var circle = nodeG.select('circle.' + BUBBLE_CLASS); + + if (circle.empty()) { + circle = nodeG.append('circle') + .attr('class', BUBBLE_CLASS) + .attr('r', 0) + .attr('fill', _chart.getColor) + .on('click', _chart.onClick); + } + + dc.transition(circle, _chart.transitionDuration(), _chart.transitionDelay()) + .attr('r', function (d) { + return _chart.bubbleR(d); + }); + + _chart._doRenderLabel(nodeG); + + _chart._doRenderTitles(nodeG); + }); + } + + function mapData () { + var data = {}; + _chart.data().forEach(function (datum) { + data[_chart.keyAccessor()(datum)] = datum; + }); + return data; + } + + function getNodeG (point, data) { + var bubbleNodeClass = BUBBLE_NODE_CLASS + ' ' + dc.utils.nameToId(point.name); + + var nodeG = _g.select('g.' + dc.utils.nameToId(point.name)); + + if (nodeG.empty()) { + nodeG = _g.append('g') + .attr('class', bubbleNodeClass) + .attr('transform', 'translate(' + point.x + ',' + point.y + ')'); + } + + nodeG.datum(data[point.name]); + + return nodeG; + } + + _chart._doRedraw = function () { + updateBubbles(); + + _chart.fadeDeselectedArea(); + + return _chart; + }; + + function updateBubbles () { + var data = mapData(); + + _points.forEach(function (point) { + var nodeG = getNodeG(point, data); + + var circle = nodeG.select('circle.' + BUBBLE_CLASS); + + dc.transition(circle, _chart.transitionDuration(), _chart.transitionDelay()) + .attr('r', function (d) { + return _chart.bubbleR(d); + }) + .attr('fill', _chart.getColor); + + _chart.doUpdateLabels(nodeG); + + _chart.doUpdateTitles(nodeG); + }); + } + + _chart.debug = function (flag) { + if (flag) { + var debugG = _chart.select('g.' + dc.constants.DEBUG_GROUP_CLASS); + + if (debugG.empty()) { + debugG = _chart.svg() + .append('g') + .attr('class', dc.constants.DEBUG_GROUP_CLASS); + } + + var debugText = debugG.append('text') + .attr('x', 10) + .attr('y', 20); + + debugG + .append('rect') + .attr('width', _chart.width()) + .attr('height', _chart.height()) + .on('mousemove', function () { + var position = d3.mouse(debugG.node()); + var msg = position[0] + ', ' + position[1]; + debugText.text(msg); + }); + } else { + _chart.selectAll('.debug').remove(); + } + + return _chart; + }; + + _chart.anchor(parent, chartGroup); + + return _chart; +}; + +/** + * Concrete row chart implementation. + * + * Examples: + * - {@link http://dc-js.github.com/dc.js/ Nasdaq 100 Index} + * @class rowChart + * @memberof dc + * @mixes dc.capMixin + * @mixes dc.marginMixin + * @mixes dc.colorMixin + * @mixes dc.baseMixin + * @example + * // create a row chart under #chart-container1 element using the default global chart group + * var chart1 = dc.rowChart('#chart-container1'); + * // create a row chart under #chart-container2 element using chart group A + * var chart2 = dc.rowChart('#chart-container2', 'chartGroupA'); + * @param {String|node|d3.selection} parent - Any valid + * {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Selections.md#selecting-elements d3 single selector} specifying + * a dom block element such as a div; or a dom element or d3 selection. + * @param {String} [chartGroup] - The name of the chart group this chart instance should be placed in. + * Interaction with a chart will only trigger events and redraws within the chart's group. + * @returns {dc.rowChart} + */ +dc.rowChart = function (parent, chartGroup) { + + var _g; + + var _labelOffsetX = 10; + var _labelOffsetY = 15; + var _hasLabelOffsetY = false; + var _dyOffset = '0.35em'; // this helps center labels https://github.com/d3/d3-3.x-api-reference/blob/master/SVG-Shapes.md#svg_text + var _titleLabelOffsetX = 2; + + var _gap = 5; + + var _fixedBarHeight = false; + var _rowCssClass = 'row'; + var _titleRowCssClass = 'titlerow'; + var _renderTitleLabel = false; + + var _chart = dc.capMixin(dc.marginMixin(dc.colorMixin(dc.baseMixin({})))); + + var _x; + + var _elasticX; + + var _xAxis = d3.svg.axis().orient('bottom'); + + var _rowData; + + _chart.rowsCap = _chart.cap; + + function calculateAxisScale () { + if (!_x || _elasticX) { + var extent = d3.extent(_rowData, _chart.cappedValueAccessor); + if (extent[0] > 0) { + extent[0] = 0; + } + if (extent[1] < 0) { + extent[1] = 0; + } + _x = d3.scale.linear().domain(extent) + .range([0, _chart.effectiveWidth()]); + } + _xAxis.scale(_x); + } + + function drawAxis () { + var axisG = _g.select('g.axis'); + + calculateAxisScale(); + + if (axisG.empty()) { + axisG = _g.append('g').attr('class', 'axis'); + } + axisG.attr('transform', 'translate(0, ' + _chart.effectiveHeight() + ')'); + + dc.transition(axisG, _chart.transitionDuration(), _chart.transitionDelay()) + .call(_xAxis); + } + + _chart._doRender = function () { + _chart.resetSvg(); + + _g = _chart.svg() + .append('g') + .attr('transform', 'translate(' + _chart.margins().left + ',' + _chart.margins().top + ')'); + + drawChart(); + + return _chart; + }; + + _chart.title(function (d) { + return _chart.cappedKeyAccessor(d) + ': ' + _chart.cappedValueAccessor(d); + }); + + _chart.label(_chart.cappedKeyAccessor); + + /** + * Gets or sets the x scale. The x scale can be any d3 + * {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Quantitative-Scales.md quantitive scale}. + * @method x + * @memberof dc.rowChart + * @instance + * @see {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Quantitative-Scales.md quantitive scale} + * @param {d3.scale} [scale] + * @returns {d3.scale|dc.rowChart} + */ + _chart.x = function (scale) { + if (!arguments.length) { + return _x; + } + _x = scale; + return _chart; + }; + + function drawGridLines () { + _g.selectAll('g.tick') + .select('line.grid-line') + .remove(); + + _g.selectAll('g.tick') + .append('line') + .attr('class', 'grid-line') + .attr('x1', 0) + .attr('y1', 0) + .attr('x2', 0) + .attr('y2', function () { + return -_chart.effectiveHeight(); + }); + } + + function drawChart () { + _rowData = _chart.data(); + + drawAxis(); + drawGridLines(); + + var rows = _g.selectAll('g.' + _rowCssClass) + .data(_rowData); + + createElements(rows); + removeElements(rows); + updateElements(rows); + } + + function createElements (rows) { + var rowEnter = rows.enter() + .append('g') + .attr('class', function (d, i) { + return _rowCssClass + ' _' + i; + }); + + rowEnter.append('rect').attr('width', 0); + + createLabels(rowEnter); + } + + function removeElements (rows) { + rows.exit().remove(); + } + + function rootValue () { + var root = _x(0); + return (root === -Infinity || root !== root) ? _x(1) : root; + } + + function updateElements (rows) { + var n = _rowData.length; + + var height; + if (!_fixedBarHeight) { + height = (_chart.effectiveHeight() - (n + 1) * _gap) / n; + } else { + height = _fixedBarHeight; + } + + // vertically align label in center unless they override the value via property setter + if (!_hasLabelOffsetY) { + _labelOffsetY = height / 2; + } + + var rect = rows.attr('transform', function (d, i) { + return 'translate(0,' + ((i + 1) * _gap + i * height) + ')'; + }).select('rect') + .attr('height', height) + .attr('fill', _chart.getColor) + .on('click', onClick) + .classed('deselected', function (d) { + return (_chart.hasFilter()) ? !isSelectedRow(d) : false; + }) + .classed('selected', function (d) { + return (_chart.hasFilter()) ? isSelectedRow(d) : false; + }); + + dc.transition(rect, _chart.transitionDuration(), _chart.transitionDelay()) + .attr('width', function (d) { + return Math.abs(rootValue() - _x(_chart.valueAccessor()(d))); + }) + .attr('transform', translateX); + + createTitles(rows); + updateLabels(rows); + } + + function createTitles (rows) { + if (_chart.renderTitle()) { + rows.select('title').remove(); + rows.append('title').text(_chart.title()); + } + } + + function createLabels (rowEnter) { + if (_chart.renderLabel()) { + rowEnter.append('text') + .on('click', onClick); + } + if (_chart.renderTitleLabel()) { + rowEnter.append('text') + .attr('class', _titleRowCssClass) + .on('click', onClick); + } + } + + function updateLabels (rows) { + if (_chart.renderLabel()) { + var lab = rows.select('text') + .attr('x', _labelOffsetX) + .attr('y', _labelOffsetY) + .attr('dy', _dyOffset) + .on('click', onClick) + .attr('class', function (d, i) { + return _rowCssClass + ' _' + i; + }) + .text(function (d) { + return _chart.label()(d); + }); + dc.transition(lab, _chart.transitionDuration(), _chart.transitionDelay()) + .attr('transform', translateX); + } + if (_chart.renderTitleLabel()) { + var titlelab = rows.select('.' + _titleRowCssClass) + .attr('x', _chart.effectiveWidth() - _titleLabelOffsetX) + .attr('y', _labelOffsetY) + .attr('dy', _dyOffset) + .attr('text-anchor', 'end') + .on('click', onClick) + .attr('class', function (d, i) { + return _titleRowCssClass + ' _' + i ; + }) + .text(function (d) { + return _chart.title()(d); + }); + dc.transition(titlelab, _chart.transitionDuration(), _chart.transitionDelay()) + .attr('transform', translateX); + } + } + + /** + * Turn on/off Title label rendering (values) using SVG style of text-anchor 'end'. + * @method renderTitleLabel + * @memberof dc.rowChart + * @instance + * @param {Boolean} [renderTitleLabel=false] + * @returns {Boolean|dc.rowChart} + */ + _chart.renderTitleLabel = function (renderTitleLabel) { + if (!arguments.length) { + return _renderTitleLabel; + } + _renderTitleLabel = renderTitleLabel; + return _chart; + }; + + function onClick (d) { + _chart.onClick(d); + } + + function translateX (d) { + var x = _x(_chart.cappedValueAccessor(d)), + x0 = rootValue(), + s = x > x0 ? x0 : x; + return 'translate(' + s + ',0)'; + } + + _chart._doRedraw = function () { + drawChart(); + return _chart; + }; + + /** + * Get the x axis for the row chart instance. Note: not settable for row charts. + * See the {@link https://github.com/d3/d3-3.x-api-reference/blob/master/SVG-Axes.md#axis d3 axis object} + * documention for more information. + * @method xAxis + * @memberof dc.rowChart + * @instance + * @see {@link https://github.com/d3/d3-3.x-api-reference/blob/master/SVG-Axes.md#axis d3.svg.axis} + * @example + * // customize x axis tick format + * chart.xAxis().tickFormat(function (v) {return v + '%';}); + * // customize x axis tick values + * chart.xAxis().tickValues([0, 100, 200, 300]); + * @returns {d3.svg.axis} + */ + _chart.xAxis = function () { + return _xAxis; + }; + + /** + * Get or set the fixed bar height. Default is [false] which will auto-scale bars. + * For example, if you want to fix the height for a specific number of bars (useful in TopN charts) + * you could fix height as follows (where count = total number of bars in your TopN and gap is + * your vertical gap space). + * @method fixedBarHeight + * @memberof dc.rowChart + * @instance + * @example + * chart.fixedBarHeight( chartheight - (count + 1) * gap / count); + * @param {Boolean|Number} [fixedBarHeight=false] + * @returns {Boolean|Number|dc.rowChart} + */ + _chart.fixedBarHeight = function (fixedBarHeight) { + if (!arguments.length) { + return _fixedBarHeight; + } + _fixedBarHeight = fixedBarHeight; + return _chart; + }; + + /** + * Get or set the vertical gap space between rows on a particular row chart instance. + * @method gap + * @memberof dc.rowChart + * @instance + * @param {Number} [gap=5] + * @returns {Number|dc.rowChart} + */ + _chart.gap = function (gap) { + if (!arguments.length) { + return _gap; + } + _gap = gap; + return _chart; + }; + + /** + * Get or set the elasticity on x axis. If this attribute is set to true, then the x axis will rescle to auto-fit the + * data range when filtered. + * @method elasticX + * @memberof dc.rowChart + * @instance + * @param {Boolean} [elasticX] + * @returns {Boolean|dc.rowChart} + */ + _chart.elasticX = function (elasticX) { + if (!arguments.length) { + return _elasticX; + } + _elasticX = elasticX; + return _chart; + }; + + /** + * Get or set the x offset (horizontal space to the top left corner of a row) for labels on a particular row chart. + * @method labelOffsetX + * @memberof dc.rowChart + * @instance + * @param {Number} [labelOffsetX=10] + * @returns {Number|dc.rowChart} + */ + _chart.labelOffsetX = function (labelOffsetX) { + if (!arguments.length) { + return _labelOffsetX; + } + _labelOffsetX = labelOffsetX; + return _chart; + }; + + /** + * Get or set the y offset (vertical space to the top left corner of a row) for labels on a particular row chart. + * @method labelOffsetY + * @memberof dc.rowChart + * @instance + * @param {Number} [labelOffsety=15] + * @returns {Number|dc.rowChart} + */ + _chart.labelOffsetY = function (labelOffsety) { + if (!arguments.length) { + return _labelOffsetY; + } + _labelOffsetY = labelOffsety; + _hasLabelOffsetY = true; + return _chart; + }; + + /** + * Get of set the x offset (horizontal space between right edge of row and right edge or text. + * @method titleLabelOffsetX + * @memberof dc.rowChart + * @instance + * @param {Number} [titleLabelOffsetX=2] + * @returns {Number|dc.rowChart} + */ + _chart.titleLabelOffsetX = function (titleLabelOffsetX) { + if (!arguments.length) { + return _titleLabelOffsetX; + } + _titleLabelOffsetX = titleLabelOffsetX; + return _chart; + }; + + function isSelectedRow (d) { + return _chart.hasFilter(_chart.cappedKeyAccessor(d)); + } + + return _chart.anchor(parent, chartGroup); +}; + +/** + * Legend is a attachable widget that can be added to other dc charts to render horizontal legend + * labels. + * + * Examples: + * - {@link http://dc-js.github.com/dc.js/ Nasdaq 100 Index} + * - {@link http://dc-js.github.com/dc.js/crime/index.html Canadian City Crime Stats} + * @class legend + * @memberof dc + * @example + * chart.legend(dc.legend().x(400).y(10).itemHeight(13).gap(5)) + * @returns {dc.legend} + */ +dc.legend = function () { + var LABEL_GAP = 2; + + var _legend = {}, + _parent, + _x = 0, + _y = 0, + _itemHeight = 12, + _gap = 5, + _horizontal = false, + _legendWidth = 560, + _itemWidth = 70, + _autoItemWidth = false, + _legendText = dc.pluck('name'), + _maxItems; + + var _g; + + _legend.parent = function (p) { + if (!arguments.length) { + return _parent; + } + _parent = p; + return _legend; + }; + + _legend.render = function () { + _parent.svg().select('g.dc-legend').remove(); + _g = _parent.svg().append('g') + .attr('class', 'dc-legend') + .attr('transform', 'translate(' + _x + ',' + _y + ')'); + var legendables = _parent.legendables(); + + if (_maxItems !== undefined) { + legendables = legendables.slice(0, _maxItems); + } + + var itemEnter = _g.selectAll('g.dc-legend-item') + .data(legendables) + .enter() + .append('g') + .attr('class', 'dc-legend-item') + .on('mouseover', function (d) { + _parent.legendHighlight(d); + }) + .on('mouseout', function (d) { + _parent.legendReset(d); + }) + .on('click', function (d) { + d.chart.legendToggle(d); + }); + + _g.selectAll('g.dc-legend-item') + .classed('fadeout', function (d) { + return d.chart.isLegendableHidden(d); + }); + + if (legendables.some(dc.pluck('dashstyle'))) { + itemEnter + .append('line') + .attr('x1', 0) + .attr('y1', _itemHeight / 2) + .attr('x2', _itemHeight) + .attr('y2', _itemHeight / 2) + .attr('stroke-width', 2) + .attr('stroke-dasharray', dc.pluck('dashstyle')) + .attr('stroke', dc.pluck('color')); + } else { + itemEnter + .append('rect') + .attr('width', _itemHeight) + .attr('height', _itemHeight) + .attr('fill', function (d) {return d ? d.color : 'blue';}); + } + + itemEnter.append('text') + .text(_legendText) + .attr('x', _itemHeight + LABEL_GAP) + .attr('y', function () { + return _itemHeight / 2 + (this.clientHeight ? this.clientHeight : 13) / 2 - 2; + }); + + var _cumulativeLegendTextWidth = 0; + var row = 0; + itemEnter.attr('transform', function (d, i) { + if (_horizontal) { + var itemWidth = _autoItemWidth === true ? this.getBBox().width + _gap : _itemWidth; + if ((_cumulativeLegendTextWidth + itemWidth) > _legendWidth && _cumulativeLegendTextWidth > 0) { + ++row; + _cumulativeLegendTextWidth = 0; + } + var translateBy = 'translate(' + _cumulativeLegendTextWidth + ',' + row * legendItemHeight() + ')'; + _cumulativeLegendTextWidth += itemWidth; + return translateBy; + } else { + return 'translate(0,' + i * legendItemHeight() + ')'; + } + }); + }; + + function legendItemHeight () { + return _gap + _itemHeight; + } + + /** + * Set or get x coordinate for legend widget. + * @method x + * @memberof dc.legend + * @instance + * @param {Number} [x=0] + * @returns {Number|dc.legend} + */ + _legend.x = function (x) { + if (!arguments.length) { + return _x; + } + _x = x; + return _legend; + }; + + /** + * Set or get y coordinate for legend widget. + * @method y + * @memberof dc.legend + * @instance + * @param {Number} [y=0] + * @returns {Number|dc.legend} + */ + _legend.y = function (y) { + if (!arguments.length) { + return _y; + } + _y = y; + return _legend; + }; + + /** + * Set or get gap between legend items. + * @method gap + * @memberof dc.legend + * @instance + * @param {Number} [gap=5] + * @returns {Number|dc.legend} + */ + _legend.gap = function (gap) { + if (!arguments.length) { + return _gap; + } + _gap = gap; + return _legend; + }; + + /** + * Set or get legend item height. + * @method itemHeight + * @memberof dc.legend + * @instance + * @param {Number} [itemHeight=12] + * @returns {Number|dc.legend} + */ + _legend.itemHeight = function (itemHeight) { + if (!arguments.length) { + return _itemHeight; + } + _itemHeight = itemHeight; + return _legend; + }; + + /** + * Position legend horizontally instead of vertically. + * @method horizontal + * @memberof dc.legend + * @instance + * @param {Boolean} [horizontal=false] + * @returns {Boolean|dc.legend} + */ + _legend.horizontal = function (horizontal) { + if (!arguments.length) { + return _horizontal; + } + _horizontal = horizontal; + return _legend; + }; + + /** + * Maximum width for horizontal legend. + * @method legendWidth + * @memberof dc.legend + * @instance + * @param {Number} [legendWidth=500] + * @returns {Number|dc.legend} + */ + _legend.legendWidth = function (legendWidth) { + if (!arguments.length) { + return _legendWidth; + } + _legendWidth = legendWidth; + return _legend; + }; + + /** + * Legend item width for horizontal legend. + * @method itemWidth + * @memberof dc.legend + * @instance + * @param {Number} [itemWidth=70] + * @returns {Number|dc.legend} + */ + _legend.itemWidth = function (itemWidth) { + if (!arguments.length) { + return _itemWidth; + } + _itemWidth = itemWidth; + return _legend; + }; + + /** + * Turn automatic width for legend items on or off. If true, {@link dc.legend#itemWidth itemWidth} is ignored. + * This setting takes into account the {@link dc.legend#gap gap}. + * @method autoItemWidth + * @memberof dc.legend + * @instance + * @param {Boolean} [autoItemWidth=false] + * @returns {Boolean|dc.legend} + */ + _legend.autoItemWidth = function (autoItemWidth) { + if (!arguments.length) { + return _autoItemWidth; + } + _autoItemWidth = autoItemWidth; + return _legend; + }; + + /** + * Set or get the legend text function. The legend widget uses this function to render the legend + * text for each item. If no function is specified the legend widget will display the names + * associated with each group. + * @method legendText + * @memberof dc.legend + * @instance + * @param {Function} [legendText] + * @returns {Function|dc.legend} + * @example + * // default legendText + * legend.legendText(dc.pluck('name')) + * + * // create numbered legend items + * chart.legend(dc.legend().legendText(function(d, i) { return i + '. ' + d.name; })) + * + * // create legend displaying group counts + * chart.legend(dc.legend().legendText(function(d) { return d.name + ': ' d.data; })) + **/ + _legend.legendText = function (legendText) { + if (!arguments.length) { + return _legendText; + } + _legendText = legendText; + return _legend; + }; + + /** + * Maximum number of legend items to display + * @method maxItems + * @memberof dc.legend + * @instance + * @param {Number} [maxItems] + * @return {dc.legend} + */ + _legend.maxItems = function (maxItems) { + if (!arguments.length) { + return _maxItems; + } + _maxItems = dc.utils.isNumber(maxItems) ? maxItems : undefined; + return _legend; + }; + + return _legend; +}; + +/** + * A scatter plot chart + * + * Examples: + * - {@link http://dc-js.github.io/dc.js/examples/scatter.html Scatter Chart} + * - {@link http://dc-js.github.io/dc.js/examples/multi-scatter.html Multi-Scatter Chart} + * @class scatterPlot + * @memberof dc + * @mixes dc.coordinateGridMixin + * @example + * // create a scatter plot under #chart-container1 element using the default global chart group + * var chart1 = dc.scatterPlot('#chart-container1'); + * // create a scatter plot under #chart-container2 element using chart group A + * var chart2 = dc.scatterPlot('#chart-container2', 'chartGroupA'); + * // create a sub-chart under a composite parent chart + * var chart3 = dc.scatterPlot(compositeChart); + * @param {String|node|d3.selection} parent - Any valid + * {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Selections.md#selecting-elements d3 single selector} specifying + * a dom block element such as a div; or a dom element or d3 selection. + * @param {String} [chartGroup] - The name of the chart group this chart instance should be placed in. + * Interaction with a chart will only trigger events and redraws within the chart's group. + * @returns {dc.scatterPlot} + */ +dc.scatterPlot = function (parent, chartGroup) { + var _chart = dc.coordinateGridMixin({}); + var _symbol = d3.svg.symbol(); + + var _existenceAccessor = function (d) { return d.value; }; + + var originalKeyAccessor = _chart.keyAccessor(); + _chart.keyAccessor(function (d) { return originalKeyAccessor(d)[0]; }); + _chart.valueAccessor(function (d) { return originalKeyAccessor(d)[1]; }); + _chart.colorAccessor(function () { return _chart._groupName; }); + + _chart.title(function (d) { + // this basically just counteracts the setting of its own key/value accessors + // see https://github.com/dc-js/dc.js/issues/702 + return _chart.keyAccessor()(d) + ',' + _chart.valueAccessor()(d) + ': ' + + _chart.existenceAccessor()(d); + }); + + var _locator = function (d) { + return 'translate(' + _chart.x()(_chart.keyAccessor()(d)) + ',' + + _chart.y()(_chart.valueAccessor()(d)) + ')'; + }; + + var _highlightedSize = 7; + var _symbolSize = 5; + var _excludedSize = 3; + var _excludedColor = null; + var _excludedOpacity = 1.0; + var _emptySize = 0; + var _emptyOpacity = 0; + var _nonemptyOpacity = 1; + var _emptyColor = null; + var _filtered = []; + + function elementSize (d, i) { + if (!_existenceAccessor(d)) { + return Math.pow(_emptySize, 2); + } else if (_filtered[i]) { + return Math.pow(_symbolSize, 2); + } else { + return Math.pow(_excludedSize, 2); + } + } + _symbol.size(elementSize); + + dc.override(_chart, '_filter', function (filter) { + if (!arguments.length) { + return _chart.__filter(); + } + + return _chart.__filter(dc.filters.RangedTwoDimensionalFilter(filter)); + }); + + _chart.plotData = function () { + var symbols = _chart.chartBodyG().selectAll('path.symbol') + .data(_chart.data()); + + symbols + .enter() + .append('path') + .attr('class', 'symbol') + .attr('opacity', 0) + .attr('fill', _chart.getColor) + .attr('transform', _locator); + + symbols.call(renderTitles, _chart.data()); + + symbols.each(function (d, i) { + _filtered[i] = !_chart.filter() || _chart.filter().isFiltered([d.key[0], d.key[1]]); + }); + + dc.transition(symbols, _chart.transitionDuration(), _chart.transitionDelay()) + .attr('opacity', function (d, i) { + if (!_existenceAccessor(d)) { + return _emptyOpacity; + } else if (_filtered[i]) { + return _nonemptyOpacity; + } else { + return _chart.excludedOpacity(); + } + }) + .attr('fill', function (d, i) { + if (_emptyColor && !_existenceAccessor(d)) { + return _emptyColor; + } else if (_chart.excludedColor() && !_filtered[i]) { + return _chart.excludedColor(); + } else { + return _chart.getColor(d); + } + }) + .attr('transform', _locator) + .attr('d', _symbol); + + dc.transition(symbols.exit(), _chart.transitionDuration(), _chart.transitionDelay()) + .attr('opacity', 0).remove(); + }; + + function renderTitles (symbol, d) { + if (_chart.renderTitle()) { + symbol.selectAll('title').remove(); + symbol.append('title').text(function (d) { + return _chart.title()(d); + }); + } + } + + /** + * Get or set the existence accessor. If a point exists, it is drawn with + * {@link dc.scatterPlot#symbolSize symbolSize} radius and + * opacity 1; if it does not exist, it is drawn with + * {@link dc.scatterPlot#emptySize emptySize} radius and opacity 0. By default, + * the existence accessor checks if the reduced value is truthy. + * @method existenceAccessor + * @memberof dc.scatterPlot + * @instance + * @see {@link dc.scatterPlot#symbolSize symbolSize} + * @see {@link dc.scatterPlot#emptySize emptySize} + * @example + * // default accessor + * chart.existenceAccessor(function (d) { return d.value; }); + * @param {Function} [accessor] + * @returns {Function|dc.scatterPlot} + */ + _chart.existenceAccessor = function (accessor) { + if (!arguments.length) { + return _existenceAccessor; + } + _existenceAccessor = accessor; + return this; + }; + + /** + * Get or set the symbol type used for each point. By default the symbol is a circle. + * Type can be a constant or an accessor. + * @method symbol + * @memberof dc.scatterPlot + * @instance + * @see {@link https://github.com/d3/d3-3.x-api-reference/blob/master/SVG-Shapes.md#symbol_type d3.svg.symbol.type} + * @example + * // Circle type + * chart.symbol('circle'); + * // Square type + * chart.symbol('square'); + * @param {String|Function} [type='circle'] + * @returns {String|Function|dc.scatterPlot} + */ + _chart.symbol = function (type) { + if (!arguments.length) { + return _symbol.type(); + } + _symbol.type(type); + return _chart; + }; + + /** + * Get or set the symbol generator. By default `dc.scatterPlot` will use + * {@link https://github.com/d3/d3-3.x-api-reference/blob/master/SVG-Shapes.md#symbol d3.svg.symbol()} + * to generate symbols. `dc.scatterPlot` will set the + * {@link https://github.com/d3/d3-3.x-api-reference/blob/master/SVG-Shapes.md#symbol_size size accessor} + * on the symbol generator. + * @method customSymbol + * @memberof dc.scatterPlot + * @instance + * @see {@link https://github.com/d3/d3-3.x-api-reference/blob/master/SVG-Shapes.md#symbol d3.svg.symbol} + * @see {@link https://stackoverflow.com/questions/25332120/create-additional-d3-js-symbols Create additional D3.js symbols} + * @param {String|Function} [customSymbol=d3.svg.symbol()] + * @returns {String|Function|dc.scatterPlot} + */ + _chart.customSymbol = function (customSymbol) { + if (!arguments.length) { + return _symbol; + } + _symbol = customSymbol; + _symbol.size(elementSize); + return _chart; + }; + + /** + * Set or get radius for symbols. + * @method symbolSize + * @memberof dc.scatterPlot + * @instance + * @see {@link https://github.com/d3/d3-3.x-api-reference/blob/master/SVG-Shapes.md#symbol_size d3.svg.symbol.size} + * @param {Number} [symbolSize=3] + * @returns {Number|dc.scatterPlot} + */ + _chart.symbolSize = function (symbolSize) { + if (!arguments.length) { + return _symbolSize; + } + _symbolSize = symbolSize; + return _chart; + }; + + /** + * Set or get radius for highlighted symbols. + * @method highlightedSize + * @memberof dc.scatterPlot + * @instance + * @see {@link https://github.com/d3/d3-3.x-api-reference/blob/master/SVG-Shapes.md#symbol_size d3.svg.symbol.size} + * @param {Number} [highlightedSize=5] + * @returns {Number|dc.scatterPlot} + */ + _chart.highlightedSize = function (highlightedSize) { + if (!arguments.length) { + return _highlightedSize; + } + _highlightedSize = highlightedSize; + return _chart; + }; + + /** + * Set or get size for symbols excluded from this chart's filter. If null, no + * special size is applied for symbols based on their filter status. + * @method excludedSize + * @memberof dc.scatterPlot + * @instance + * @see {@link https://github.com/d3/d3-3.x-api-reference/blob/master/SVG-Shapes.md#symbol_size d3.svg.symbol.size} + * @param {Number} [excludedSize=null] + * @returns {Number|dc.scatterPlot} + */ + _chart.excludedSize = function (excludedSize) { + if (!arguments.length) { + return _excludedSize; + } + _excludedSize = excludedSize; + return _chart; + }; + + /** + * Set or get color for symbols excluded from this chart's filter. If null, no + * special color is applied for symbols based on their filter status. + * @method excludedColor + * @memberof dc.scatterPlot + * @instance + * @param {Number} [excludedColor=null] + * @returns {Number|dc.scatterPlot} + */ + _chart.excludedColor = function (excludedColor) { + if (!arguments.length) { + return _excludedColor; + } + _excludedColor = excludedColor; + return _chart; + }; + + /** + * Set or get opacity for symbols excluded from this chart's filter. + * @method excludedOpacity + * @memberof dc.scatterPlot + * @instance + * @param {Number} [excludedOpacity=1.0] + * @returns {Number|dc.scatterPlot} + */ + _chart.excludedOpacity = function (excludedOpacity) { + if (!arguments.length) { + return _excludedOpacity; + } + _excludedOpacity = excludedOpacity; + return _chart; + }; + + /** + * Set or get radius for symbols when the group is empty. + * @method emptySize + * @memberof dc.scatterPlot + * @instance + * @see {@link https://github.com/d3/d3-3.x-api-reference/blob/master/SVG-Shapes.md#symbol_size d3.svg.symbol.size} + * @param {Number} [emptySize=0] + * @returns {Number|dc.scatterPlot} + */ + _chart.hiddenSize = _chart.emptySize = function (emptySize) { + if (!arguments.length) { + return _emptySize; + } + _emptySize = emptySize; + return _chart; + }; + + /** + * Set or get color for symbols when the group is empty. If null, just use the + * {@link dc.colorMixin#colors colorMixin.colors} color scale zero value. + * @name emptyColor + * @memberof dc.scatterPlot + * @instance + * @param {String} [emptyColor=null] + * @return {String} + * @return {dc.scatterPlot}/ + */ + _chart.emptyColor = function (emptyColor) { + if (!arguments.length) { + return _emptyColor; + } + _emptyColor = emptyColor; + return _chart; + }; + + /** + * Set or get opacity for symbols when the group is empty. + * @name emptyOpacity + * @memberof dc.scatterPlot + * @instance + * @param {Number} [emptyOpacity=0] + * @return {Number} + * @return {dc.scatterPlot} + */ + _chart.emptyOpacity = function (emptyOpacity) { + if (!arguments.length) { + return _emptyOpacity; + } + _emptyOpacity = emptyOpacity; + return _chart; + }; + + /** + * Set or get opacity for symbols when the group is not empty. + * @name nonemptyOpacity + * @memberof dc.scatterPlot + * @instance + * @param {Number} [nonemptyOpacity=1] + * @return {Number} + * @return {dc.scatterPlot} + */ + _chart.nonemptyOpacity = function (nonemptyOpacity) { + if (!arguments.length) { + return _emptyOpacity; + } + _nonemptyOpacity = nonemptyOpacity; + return _chart; + }; + + _chart.legendables = function () { + return [{chart: _chart, name: _chart._groupName, color: _chart.getColor()}]; + }; + + _chart.legendHighlight = function (d) { + resizeSymbolsWhere(function (symbol) { + return symbol.attr('fill') === d.color; + }, _highlightedSize); + _chart.chartBodyG().selectAll('.chart-body path.symbol').filter(function () { + return d3.select(this).attr('fill') !== d.color; + }).classed('fadeout', true); + }; + + _chart.legendReset = function (d) { + resizeSymbolsWhere(function (symbol) { + return symbol.attr('fill') === d.color; + }, _symbolSize); + _chart.chartBodyG().selectAll('.chart-body path.symbol').filter(function () { + return d3.select(this).attr('fill') !== d.color; + }).classed('fadeout', false); + }; + + function resizeSymbolsWhere (condition, size) { + var symbols = _chart.chartBodyG().selectAll('.chart-body path.symbol').filter(function () { + return condition(d3.select(this)); + }); + var oldSize = _symbol.size(); + _symbol.size(Math.pow(size, 2)); + dc.transition(symbols, _chart.transitionDuration(), _chart.transitionDelay()).attr('d', _symbol); + _symbol.size(oldSize); + } + + _chart.setHandlePaths = function () { + // no handle paths for poly-brushes + }; + + _chart.extendBrush = function () { + var extent = _chart.brush().extent(); + if (_chart.round()) { + extent[0] = extent[0].map(_chart.round()); + extent[1] = extent[1].map(_chart.round()); + + _chart.g().select('.brush') + .call(_chart.brush().extent(extent)); + } + return extent; + }; + + _chart.brushIsEmpty = function (extent) { + return _chart.brush().empty() || !extent || extent[0][0] >= extent[1][0] || extent[0][1] >= extent[1][1]; + }; + + _chart._brushing = function () { + var extent = _chart.extendBrush(); + + _chart.redrawBrush(_chart.g()); + + if (_chart.brushIsEmpty(extent)) { + dc.events.trigger(function () { + _chart.filter(null); + _chart.redrawGroup(); + }); + + } else { + var ranged2DFilter = dc.filters.RangedTwoDimensionalFilter(extent); + dc.events.trigger(function () { + _chart.filter(null); + _chart.filter(ranged2DFilter); + _chart.redrawGroup(); + }, dc.constants.EVENT_DELAY); + + } + }; + + _chart.setBrushY = function (gBrush) { + gBrush.call(_chart.brush().y(_chart.y())); + }; + + return _chart.anchor(parent, chartGroup); +}; + +/** + * A display of a single numeric value. + * Unlike other charts, you do not need to set a dimension. Instead a group object must be provided and + * a valueAccessor that returns a single value. + * @class numberDisplay + * @memberof dc + * @mixes dc.baseMixin + * @example + * // create a number display under #chart-container1 element using the default global chart group + * var display1 = dc.numberDisplay('#chart-container1'); + * @param {String|node|d3.selection} parent - Any valid + * {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Selections.md#selecting-elements d3 single selector} specifying + * a dom block element such as a div; or a dom element or d3 selection. + * @param {String} [chartGroup] - The name of the chart group this chart instance should be placed in. + * Interaction with a chart will only trigger events and redraws within the chart's group. + * @returns {dc.numberDisplay} + */ +dc.numberDisplay = function (parent, chartGroup) { + var SPAN_CLASS = 'number-display'; + var _formatNumber = d3.format('.2s'); + var _chart = dc.baseMixin({}); + var _html = {one: '', some: '', none: ''}; + var _lastValue; + + // dimension not required + _chart._mandatoryAttributes(['group']); + + /** + * Gets or sets an optional object specifying HTML templates to use depending on the number + * displayed. The text `%number` will be replaced with the current value. + * - one: HTML template to use if the number is 1 + * - zero: HTML template to use if the number is 0 + * - some: HTML template to use otherwise + * @method html + * @memberof dc.numberDisplay + * @instance + * @example + * numberWidget.html({ + * one:'%number record', + * some:'%number records', + * none:'no records'}) + * @param {{one:String, some:String, none:String}} [html={one: '', some: '', none: ''}] + * @returns {{one:String, some:String, none:String}|dc.numberDisplay} + */ + _chart.html = function (html) { + if (!arguments.length) { + return _html; + } + if (html.none) { + _html.none = html.none;//if none available + } else if (html.one) { + _html.none = html.one;//if none not available use one + } else if (html.some) { + _html.none = html.some;//if none and one not available use some + } + if (html.one) { + _html.one = html.one;//if one available + } else if (html.some) { + _html.one = html.some;//if one not available use some + } + if (html.some) { + _html.some = html.some;//if some available + } else if (html.one) { + _html.some = html.one;//if some not available use one + } + return _chart; + }; + + /** + * Calculate and return the underlying value of the display. + * @method value + * @memberof dc.numberDisplay + * @instance + * @returns {Number} + */ + _chart.value = function () { + return _chart.data(); + }; + + _chart.data(function (group) { + var valObj = group.value ? group.value() : group.top(1)[0]; + return _chart.valueAccessor()(valObj); + }); + + _chart.transitionDuration(250); // good default + _chart.transitionDelay(0); + + _chart._doRender = function () { + var newValue = _chart.value(), + span = _chart.selectAll('.' + SPAN_CLASS); + + if (span.empty()) { + span = span.data([0]) + .enter() + .append('span') + .attr('class', SPAN_CLASS); + } + + span.transition() + .duration(_chart.transitionDuration()) + .delay(_chart.transitionDelay()) + .ease('quad-out-in') + .tween('text', function () { + // [XA] don't try and interpolate from Infinity, else this breaks. + var interpStart = isFinite(_lastValue) ? _lastValue : 0; + var interp = d3.interpolateNumber(interpStart || 0, newValue); + _lastValue = newValue; + return function (t) { + var html = null, num = _chart.formatNumber()(interp(t)); + if (newValue === 0 && (_html.none !== '')) { + html = _html.none; + } else if (newValue === 1 && (_html.one !== '')) { + html = _html.one; + } else if (_html.some !== '') { + html = _html.some; + } + this.innerHTML = html ? html.replace('%number', num) : num; + }; + }); + }; + + _chart._doRedraw = function () { + return _chart._doRender(); + }; + + /** + * Get or set a function to format the value for the display. + * @method formatNumber + * @memberof dc.numberDisplay + * @instance + * @see {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md d3.format} + * @param {Function} [formatter=d3.format('.2s')] + * @returns {Function|dc.numberDisplay} + */ + _chart.formatNumber = function (formatter) { + if (!arguments.length) { + return _formatNumber; + } + _formatNumber = formatter; + return _chart; + }; + + return _chart.anchor(parent, chartGroup); +}; + +/** + * A heat map is matrix that represents the values of two dimensions of data using colors. + * @class heatMap + * @memberof dc + * @mixes dc.colorMixin + * @mixes dc.marginMixin + * @mixes dc.baseMixin + * @example + * // create a heat map under #chart-container1 element using the default global chart group + * var heatMap1 = dc.heatMap('#chart-container1'); + * // create a heat map under #chart-container2 element using chart group A + * var heatMap2 = dc.heatMap('#chart-container2', 'chartGroupA'); + * @param {String|node|d3.selection} parent - Any valid + * {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Selections.md#selecting-elements d3 single selector} specifying + * a dom block element such as a div; or a dom element or d3 selection. + * @param {String} [chartGroup] - The name of the chart group this chart instance should be placed in. + * Interaction with a chart will only trigger events and redraws within the chart's group. + * @returns {dc.heatMap} + */ +dc.heatMap = function (parent, chartGroup) { + + var DEFAULT_BORDER_RADIUS = 6.75; + + var _chartBody; + + var _cols; + var _rows; + var _xBorderRadius = DEFAULT_BORDER_RADIUS; + var _yBorderRadius = DEFAULT_BORDER_RADIUS; + + var _chart = dc.colorMixin(dc.marginMixin(dc.baseMixin({}))); + _chart._mandatoryAttributes(['group']); + _chart.title(_chart.colorAccessor()); + + var _colsLabel = function (d) { + return d; + }; + var _rowsLabel = function (d) { + return d; + }; + + /** + * Set or get the column label function. The chart class uses this function to render + * column labels on the X axis. It is passed the column name. + * @method colsLabel + * @memberof dc.heatMap + * @instance + * @example + * // the default label function just returns the name + * chart.colsLabel(function(d) { return d; }); + * @param {Function} [labelFunction=function(d) { return d; }] + * @returns {Function|dc.heatMap} + */ + _chart.colsLabel = function (labelFunction) { + if (!arguments.length) { + return _colsLabel; + } + _colsLabel = labelFunction; + return _chart; + }; + + /** + * Set or get the row label function. The chart class uses this function to render + * row labels on the Y axis. It is passed the row name. + * @method rowsLabel + * @memberof dc.heatMap + * @instance + * @example + * // the default label function just returns the name + * chart.rowsLabel(function(d) { return d; }); + * @param {Function} [labelFunction=function(d) { return d; }] + * @returns {Function|dc.heatMap} + */ + _chart.rowsLabel = function (labelFunction) { + if (!arguments.length) { + return _rowsLabel; + } + _rowsLabel = labelFunction; + return _chart; + }; + + var _xAxisOnClick = function (d) { filterAxis(0, d); }; + var _yAxisOnClick = function (d) { filterAxis(1, d); }; + var _boxOnClick = function (d) { + var filter = d.key; + dc.events.trigger(function () { + _chart.filter(filter); + _chart.redrawGroup(); + }); + }; + + function filterAxis (axis, value) { + var cellsOnAxis = _chart.selectAll('.box-group').filter(function (d) { + return d.key[axis] === value; + }); + var unfilteredCellsOnAxis = cellsOnAxis.filter(function (d) { + return !_chart.hasFilter(d.key); + }); + dc.events.trigger(function () { + var selection = unfilteredCellsOnAxis.empty() ? cellsOnAxis : unfilteredCellsOnAxis; + var filters = selection.data().map(function (kv) { + return dc.filters.TwoDimensionalFilter(kv.key); + }); + _chart._filter([filters]); + _chart.redrawGroup(); + }); + } + + dc.override(_chart, 'filter', function (filter) { + if (!arguments.length) { + return _chart._filter(); + } + + return _chart._filter(dc.filters.TwoDimensionalFilter(filter)); + }); + + function uniq (d, i, a) { + return !i || a[i - 1] !== d; + } + + /** + * Gets or sets the values used to create the rows of the heatmap, as an array. By default, all + * the values will be fetched from the data using the value accessor, and they will be sorted in + * ascending order. + * @method rows + * @memberof dc.heatMap + * @instance + * @param {Array<String|Number>} [rows] + * @returns {Array<String|Number>|dc.heatMap} + */ + _chart.rows = function (rows) { + if (arguments.length) { + _rows = rows; + return _chart; + } + if (_rows) { + return _rows; + } + var rowValues = _chart.data().map(_chart.valueAccessor()); + rowValues.sort(d3.ascending); + return d3.scale.ordinal().domain(rowValues.filter(uniq)); + }; + + /** + * Gets or sets the keys used to create the columns of the heatmap, as an array. By default, all + * the values will be fetched from the data using the key accessor, and they will be sorted in + * ascending order. + * @method cols + * @memberof dc.heatMap + * @instance + * @param {Array<String|Number>} [cols] + * @returns {Array<String|Number>|dc.heatMap} + */ + _chart.cols = function (cols) { + if (arguments.length) { + _cols = cols; + return _chart; + } + if (_cols) { + return _cols; + } + var colValues = _chart.data().map(_chart.keyAccessor()); + colValues.sort(d3.ascending); + return d3.scale.ordinal().domain(colValues.filter(uniq)); + }; + + _chart._doRender = function () { + _chart.resetSvg(); + + _chartBody = _chart.svg() + .append('g') + .attr('class', 'heatmap') + .attr('transform', 'translate(' + _chart.margins().left + ',' + _chart.margins().top + ')'); + + return _chart._doRedraw(); + }; + + _chart._doRedraw = function () { + var rows = _chart.rows(), + cols = _chart.cols(), + rowCount = rows.domain().length, + colCount = cols.domain().length, + boxWidth = Math.floor(_chart.effectiveWidth() / colCount), + boxHeight = Math.floor(_chart.effectiveHeight() / rowCount); + + cols.rangeRoundBands([0, _chart.effectiveWidth()]); + rows.rangeRoundBands([_chart.effectiveHeight(), 0]); + + var boxes = _chartBody.selectAll('g.box-group').data(_chart.data(), function (d, i) { + return _chart.keyAccessor()(d, i) + '\0' + _chart.valueAccessor()(d, i); + }); + var gEnter = boxes.enter().append('g') + .attr('class', 'box-group'); + + gEnter.append('rect') + .attr('class', 'heat-box') + .attr('fill', 'white') + .on('click', _chart.boxOnClick()); + + if (_chart.renderTitle()) { + gEnter.append('title'); + boxes.select('title').text(_chart.title()); + } + + dc.transition(boxes.select('rect'), _chart.transitionDuration(), _chart.transitionDelay()) + .attr('x', function (d, i) { return cols(_chart.keyAccessor()(d, i)); }) + .attr('y', function (d, i) { return rows(_chart.valueAccessor()(d, i)); }) + .attr('rx', _xBorderRadius) + .attr('ry', _yBorderRadius) + .attr('fill', _chart.getColor) + .attr('width', boxWidth) + .attr('height', boxHeight); + + boxes.exit().remove(); + + var gCols = _chartBody.select('g.cols'); + if (gCols.empty()) { + gCols = _chartBody.append('g').attr('class', 'cols axis'); + } + var gColsText = gCols.selectAll('text').data(cols.domain()); + gColsText.enter().append('text') + .attr('x', function (d) { return cols(d) + boxWidth / 2; }) + .style('text-anchor', 'middle') + .attr('y', _chart.effectiveHeight()) + .attr('dy', 12) + .on('click', _chart.xAxisOnClick()) + .text(_chart.colsLabel()); + dc.transition(gColsText, _chart.transitionDuration(), _chart.transitionDelay()) + .text(_chart.colsLabel()) + .attr('x', function (d) { return cols(d) + boxWidth / 2; }) + .attr('y', _chart.effectiveHeight()); + gColsText.exit().remove(); + var gRows = _chartBody.select('g.rows'); + if (gRows.empty()) { + gRows = _chartBody.append('g').attr('class', 'rows axis'); + } + var gRowsText = gRows.selectAll('text').data(rows.domain()); + gRowsText.enter().append('text') + .attr('dy', 6) + .style('text-anchor', 'end') + .attr('x', 0) + .attr('dx', -2) + .on('click', _chart.yAxisOnClick()) + .text(_chart.rowsLabel()); + dc.transition(gRowsText, _chart.transitionDuration(), _chart.transitionDelay()) + .text(_chart.rowsLabel()) + .attr('y', function (d) { return rows(d) + boxHeight / 2; }); + gRowsText.exit().remove(); + + if (_chart.hasFilter()) { + _chart.selectAll('g.box-group').each(function (d) { + if (_chart.isSelectedNode(d)) { + _chart.highlightSelected(this); + } else { + _chart.fadeDeselected(this); + } + }); + } else { + _chart.selectAll('g.box-group').each(function () { + _chart.resetHighlight(this); + }); + } + return _chart; + }; + + /** + * Gets or sets the handler that fires when an individual cell is clicked in the heatmap. + * By default, filtering of the cell will be toggled. + * @method boxOnClick + * @memberof dc.heatMap + * @instance + * @example + * // default box on click handler + * chart.boxOnClick(function (d) { + * var filter = d.key; + * dc.events.trigger(function () { + * _chart.filter(filter); + * _chart.redrawGroup(); + * }); + * }); + * @param {Function} [handler] + * @returns {Function|dc.heatMap} + */ + _chart.boxOnClick = function (handler) { + if (!arguments.length) { + return _boxOnClick; + } + _boxOnClick = handler; + return _chart; + }; + + /** + * Gets or sets the handler that fires when a column tick is clicked in the x axis. + * By default, if any cells in the column are unselected, the whole column will be selected, + * otherwise the whole column will be unselected. + * @method xAxisOnClick + * @memberof dc.heatMap + * @instance + * @param {Function} [handler] + * @returns {Function|dc.heatMap} + */ + _chart.xAxisOnClick = function (handler) { + if (!arguments.length) { + return _xAxisOnClick; + } + _xAxisOnClick = handler; + return _chart; + }; + + /** + * Gets or sets the handler that fires when a row tick is clicked in the y axis. + * By default, if any cells in the row are unselected, the whole row will be selected, + * otherwise the whole row will be unselected. + * @method yAxisOnClick + * @memberof dc.heatMap + * @instance + * @param {Function} [handler] + * @returns {Function|dc.heatMap} + */ + _chart.yAxisOnClick = function (handler) { + if (!arguments.length) { + return _yAxisOnClick; + } + _yAxisOnClick = handler; + return _chart; + }; + + /** + * Gets or sets the X border radius. Set to 0 to get full rectangles. + * @method xBorderRadius + * @memberof dc.heatMap + * @instance + * @param {Number} [xBorderRadius=6.75] + * @returns {Number|dc.heatMap} + */ + _chart.xBorderRadius = function (xBorderRadius) { + if (!arguments.length) { + return _xBorderRadius; + } + _xBorderRadius = xBorderRadius; + return _chart; + }; + + /** + * Gets or sets the Y border radius. Set to 0 to get full rectangles. + * @method yBorderRadius + * @memberof dc.heatMap + * @instance + * @param {Number} [yBorderRadius=6.75] + * @returns {Number|dc.heatMap} + */ + _chart.yBorderRadius = function (yBorderRadius) { + if (!arguments.length) { + return _yBorderRadius; + } + _yBorderRadius = yBorderRadius; + return _chart; + }; + + _chart.isSelectedNode = function (d) { + return _chart.hasFilter(d.key); + }; + + return _chart.anchor(parent, chartGroup); +}; + +// https://github.com/d3/d3-plugins/blob/master/box/box.js +(function () { + + // Inspired by http://informationandvisualization.de/blog/box-plot + d3.box = function () { + var width = 1, + height = 1, + duration = 0, + delay = 0, + domain = null, + value = Number, + whiskers = boxWhiskers, + quartiles = boxQuartiles, + tickFormat = null; + + // For each small multiple… + function box (g) { + g.each(function (d, i) { + d = d.map(value).sort(d3.ascending); + var g = d3.select(this), + n = d.length, + min = d[0], + max = d[n - 1]; + + // Compute quartiles. Must return exactly 3 elements. + var quartileData = d.quartiles = quartiles(d); + + // Compute whiskers. Must return exactly 2 elements, or null. + var whiskerIndices = whiskers && whiskers.call(this, d, i), + whiskerData = whiskerIndices && whiskerIndices.map(function (i) { return d[i]; }); + + // Compute outliers. If no whiskers are specified, all data are 'outliers'. + // We compute the outliers as indices, so that we can join across transitions! + var outlierIndices = whiskerIndices ? + d3.range(0, whiskerIndices[0]).concat(d3.range(whiskerIndices[1] + 1, n)) : d3.range(n); + + // Compute the new x-scale. + var x1 = d3.scale.linear() + .domain(domain && domain.call(this, d, i) || [min, max]) + .range([height, 0]); + + // Retrieve the old x-scale, if this is an update. + var x0 = this.__chart__ || d3.scale.linear() + .domain([0, Infinity]) + .range(x1.range()); + + // Stash the new scale. + this.__chart__ = x1; + + // Note: the box, median, and box tick elements are fixed in number, + // so we only have to handle enter and update. In contrast, the outliers + // and other elements are variable, so we need to exit them! Variable + // elements also fade in and out. + + // Update center line: the vertical line spanning the whiskers. + var center = g.selectAll('line.center') + .data(whiskerData ? [whiskerData] : []); + + center.enter().insert('line', 'rect') + .attr('class', 'center') + .attr('x1', width / 2) + .attr('y1', function (d) { return x0(d[0]); }) + .attr('x2', width / 2) + .attr('y2', function (d) { return x0(d[1]); }) + .style('opacity', 1e-6) + .transition() + .duration(duration) + .delay(delay) + .style('opacity', 1) + .attr('y1', function (d) { return x1(d[0]); }) + .attr('y2', function (d) { return x1(d[1]); }); + + center.transition() + .duration(duration) + .delay(delay) + .style('opacity', 1) + .attr('x1', width / 2) + .attr('x2', width / 2) + .attr('y1', function (d) { return x1(d[0]); }) + .attr('y2', function (d) { return x1(d[1]); }); + + center.exit().transition() + .duration(duration) + .delay(delay) + .style('opacity', 1e-6) + .attr('y1', function (d) { return x1(d[0]); }) + .attr('y2', function (d) { return x1(d[1]); }) + .remove(); + + // Update innerquartile box. + var box = g.selectAll('rect.box') + .data([quartileData]); + + box.enter().append('rect') + .attr('class', 'box') + .attr('x', 0) + .attr('y', function (d) { return x0(d[2]); }) + .attr('width', width) + .attr('height', function (d) { return x0(d[0]) - x0(d[2]); }) + .transition() + .duration(duration) + .delay(delay) + .attr('y', function (d) { return x1(d[2]); }) + .attr('height', function (d) { return x1(d[0]) - x1(d[2]); }); + + box.transition() + .duration(duration) + .delay(delay) + .attr('width', width) + .attr('y', function (d) { return x1(d[2]); }) + .attr('height', function (d) { return x1(d[0]) - x1(d[2]); }); + + // Update median line. + var medianLine = g.selectAll('line.median') + .data([quartileData[1]]); + + medianLine.enter().append('line') + .attr('class', 'median') + .attr('x1', 0) + .attr('y1', x0) + .attr('x2', width) + .attr('y2', x0) + .transition() + .duration(duration) + .delay(delay) + .attr('y1', x1) + .attr('y2', x1); + + medianLine.transition() + .duration(duration) + .delay(delay) + .attr('x1', 0) + .attr('x2', width) + .attr('y1', x1) + .attr('y2', x1); + + // Update whiskers. + var whisker = g.selectAll('line.whisker') + .data(whiskerData || []); + + whisker.enter().insert('line', 'circle, text') + .attr('class', 'whisker') + .attr('x1', 0) + .attr('y1', x0) + .attr('x2', width) + .attr('y2', x0) + .style('opacity', 1e-6) + .transition() + .duration(duration) + .delay(delay) + .attr('y1', x1) + .attr('y2', x1) + .style('opacity', 1); + + whisker.transition() + .duration(duration) + .delay(delay) + .attr('x1', 0) + .attr('x2', width) + .attr('y1', x1) + .attr('y2', x1) + .style('opacity', 1); + + whisker.exit().transition() + .duration(duration) + .delay(delay) + .attr('y1', x1) + .attr('y2', x1) + .style('opacity', 1e-6) + .remove(); + + // Update outliers. + var outlier = g.selectAll('circle.outlier') + .data(outlierIndices, Number); + + outlier.enter().insert('circle', 'text') + .attr('class', 'outlier') + .attr('r', 5) + .attr('cx', width / 2) + .attr('cy', function (i) { return x0(d[i]); }) + .style('opacity', 1e-6) + .transition() + .duration(duration) + .delay(delay) + .attr('cy', function (i) { return x1(d[i]); }) + .style('opacity', 1); + + outlier.transition() + .duration(duration) + .delay(delay) + .attr('cx', width / 2) + .attr('cy', function (i) { return x1(d[i]); }) + .style('opacity', 1); + + outlier.exit().transition() + .duration(duration) + .delay(delay) + .attr('cy', function (i) { return x1(d[i]); }) + .style('opacity', 1e-6) + .remove(); + + // Compute the tick format. + var format = tickFormat || x1.tickFormat(8); + + // Update box ticks. + var boxTick = g.selectAll('text.box') + .data(quartileData); + + boxTick.enter().append('text') + .attr('class', 'box') + .attr('dy', '.3em') + .attr('dx', function (d, i) { return i & 1 ? 6 : -6; }) + .attr('x', function (d, i) { return i & 1 ? width : 0; }) + .attr('y', x0) + .attr('text-anchor', function (d, i) { return i & 1 ? 'start' : 'end'; }) + .text(format) + .transition() + .duration(duration) + .delay(delay) + .attr('y', x1); + + boxTick.transition() + .duration(duration) + .delay(delay) + .text(format) + .attr('x', function (d, i) { return i & 1 ? width : 0; }) + .attr('y', x1); + + // Update whisker ticks. These are handled separately from the box + // ticks because they may or may not exist, and we want don't want + // to join box ticks pre-transition with whisker ticks post-. + var whiskerTick = g.selectAll('text.whisker') + .data(whiskerData || []); + + whiskerTick.enter().append('text') + .attr('class', 'whisker') + .attr('dy', '.3em') + .attr('dx', 6) + .attr('x', width) + .attr('y', x0) + .text(format) + .style('opacity', 1e-6) + .transition() + .duration(duration) + .delay(delay) + .attr('y', x1) + .style('opacity', 1); + + whiskerTick.transition() + .duration(duration) + .delay(delay) + .text(format) + .attr('x', width) + .attr('y', x1) + .style('opacity', 1); + + whiskerTick.exit().transition() + .duration(duration) + .delay(delay) + .attr('y', x1) + .style('opacity', 1e-6) + .remove(); + }); + d3.timer.flush(); + } + + box.width = function (x) { + if (!arguments.length) { + return width; + } + width = x; + return box; + }; + + box.height = function (x) { + if (!arguments.length) { + return height; + } + height = x; + return box; + }; + + box.tickFormat = function (x) { + if (!arguments.length) { + return tickFormat; + } + tickFormat = x; + return box; + }; + + box.duration = function (x) { + if (!arguments.length) { + return duration; + } + duration = x; + return box; + }; + + box.domain = function (x) { + if (!arguments.length) { + return domain; + } + domain = x === null ? x : d3.functor(x); + return box; + }; + + box.value = function (x) { + if (!arguments.length) { + return value; + } + value = x; + return box; + }; + + box.whiskers = function (x) { + if (!arguments.length) { + return whiskers; + } + whiskers = x; + return box; + }; + + box.quartiles = function (x) { + if (!arguments.length) { + return quartiles; + } + quartiles = x; + return box; + }; + + return box; + }; + + function boxWhiskers (d) { + return [0, d.length - 1]; + } + + function boxQuartiles (d) { + return [ + d3.quantile(d, 0.25), + d3.quantile(d, 0.5), + d3.quantile(d, 0.75) + ]; + } + +})(); + + +/** + * A box plot is a chart that depicts numerical data via their quartile ranges. + * + * Examples: + * - {@link http://dc-js.github.io/dc.js/examples/box-plot-time.html Box plot time example} + * - {@link http://dc-js.github.io/dc.js/examples/box-plot.html Box plot example} + * @class boxPlot + * @memberof dc + * @mixes dc.coordinateGridMixin + * @example + * // create a box plot under #chart-container1 element using the default global chart group + * var boxPlot1 = dc.boxPlot('#chart-container1'); + * // create a box plot under #chart-container2 element using chart group A + * var boxPlot2 = dc.boxPlot('#chart-container2', 'chartGroupA'); + * @param {String|node|d3.selection} parent - Any valid + * {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Selections.md#selecting-elements d3 single selector} specifying + * a dom block element such as a div; or a dom element or d3 selection. + * @param {String} [chartGroup] - The name of the chart group this chart instance should be placed in. + * Interaction with a chart will only trigger events and redraws within the chart's group. + * @returns {dc.boxPlot} + */ +dc.boxPlot = function (parent, chartGroup) { + var _chart = dc.coordinateGridMixin({}); + + // Returns a function to compute the interquartile range. + function DEFAULT_WHISKERS_IQR (k) { + return function (d) { + var q1 = d.quartiles[0], + q3 = d.quartiles[2], + iqr = (q3 - q1) * k, + i = -1, + j = d.length; + do { ++i; } while (d[i] < q1 - iqr); + do { --j; } while (d[j] > q3 + iqr); + return [i, j]; + }; + } + + var _whiskerIqrFactor = 1.5; + var _whiskersIqr = DEFAULT_WHISKERS_IQR; + var _whiskers = _whiskersIqr(_whiskerIqrFactor); + + var _box = d3.box(); + var _tickFormat = null; + + var _boxWidth = function (innerChartWidth, xUnits) { + if (_chart.isOrdinal()) { + return _chart.x().rangeBand(); + } else { + return innerChartWidth / (1 + _chart.boxPadding()) / xUnits; + } + }; + + // default padding to handle min/max whisker text + _chart.yAxisPadding(12); + + // default to ordinal + _chart.x(d3.scale.ordinal()); + _chart.xUnits(dc.units.ordinal); + + // valueAccessor should return an array of values that can be coerced into numbers + // or if data is overloaded for a static array of arrays, it should be `Number`. + // Empty arrays are not included. + _chart.data(function (group) { + return group.all().map(function (d) { + d.map = function (accessor) { return accessor.call(d, d); }; + return d; + }).filter(function (d) { + var values = _chart.valueAccessor()(d); + return values.length !== 0; + }); + }); + + /** + * Get or set the spacing between boxes as a fraction of box size. Valid values are within 0-1. + * See the {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Ordinal-Scales.md#ordinal_rangeBands d3 docs} + * for a visual description of how the padding is applied. + * @method boxPadding + * @memberof dc.boxPlot + * @instance + * @see {@link https://github.com/d3/d3-3.x-api-reference/blob/master/Ordinal-Scales.md#ordinal_rangeBands d3.scale.ordinal.rangeBands} + * @param {Number} [padding=0.8] + * @returns {Number|dc.boxPlot} + */ + _chart.boxPadding = _chart._rangeBandPadding; + _chart.boxPadding(0.8); + + /** + * Get or set the outer padding on an ordinal box chart. This setting has no effect on non-ordinal charts + * or on charts with a custom {@link dc.boxPlot#boxWidth .boxWidth}. Will pad the width by + * `padding * barWidth` on each side of the chart. + * @method outerPadding + * @memberof dc.boxPlot + * @instance + * @param {Number} [padding=0.5] + * @returns {Number|dc.boxPlot} + */ + _chart.outerPadding = _chart._outerRangeBandPadding; + _chart.outerPadding(0.5); + + /** + * Get or set the numerical width of the boxplot box. The width may also be a function taking as + * parameters the chart width excluding the right and left margins, as well as the number of x + * units. + * @example + * // Using numerical parameter + * chart.boxWidth(10); + * // Using function + * chart.boxWidth((innerChartWidth, xUnits) { ... }); + * @method boxWidth + * @memberof dc.boxPlot + * @instance + * @param {Number|Function} [boxWidth=0.5] + * @returns {Number|Function|dc.boxPlot} + */ + _chart.boxWidth = function (boxWidth) { + if (!arguments.length) { + return _boxWidth; + } + _boxWidth = d3.functor(boxWidth); + return _chart; + }; + + var boxTransform = function (d, i) { + var xOffset = _chart.x()(_chart.keyAccessor()(d, i)); + return 'translate(' + xOffset + ', 0)'; + }; + + _chart._preprocessData = function () { + if (_chart.elasticX()) { + _chart.x().domain([]); + } + }; + + _chart.plotData = function () { + var _calculatedBoxWidth = _boxWidth(_chart.effectiveWidth(), _chart.xUnitCount()); + + _box.whiskers(_whiskers) + .width(_calculatedBoxWidth) + .height(_chart.effectiveHeight()) + .value(_chart.valueAccessor()) + .domain(_chart.y().domain()) + .duration(_chart.transitionDuration()) + .tickFormat(_tickFormat); + + var boxesG = _chart.chartBodyG().selectAll('g.box').data(_chart.data(), _chart.keyAccessor()); + + renderBoxes(boxesG); + updateBoxes(boxesG); + removeBoxes(boxesG); + + _chart.fadeDeselectedArea(); + }; + + function renderBoxes (boxesG) { + var boxesGEnter = boxesG.enter().append('g'); + + boxesGEnter + .attr('class', 'box') + .attr('transform', boxTransform) + .call(_box) + .on('click', function (d) { + _chart.filter(_chart.keyAccessor()(d)); + _chart.redrawGroup(); + }); + } + + function updateBoxes (boxesG) { + dc.transition(boxesG, _chart.transitionDuration(), _chart.transitionDelay()) + .attr('transform', boxTransform) + .call(_box) + .each(function () { + d3.select(this).select('rect.box').attr('fill', _chart.getColor); + }); + } + + function removeBoxes (boxesG) { + boxesG.exit().remove().call(_box); + } + + _chart.fadeDeselectedArea = function () { + if (_chart.hasFilter()) { + if (_chart.isOrdinal()) { + _chart.g().selectAll('g.box').each(function (d) { + if (_chart.isSelectedNode(d)) { + _chart.highlightSelected(this); + } else { + _chart.fadeDeselected(this); + } + }); + } else { + var extent = _chart.brush().extent(); + var start = extent[0]; + var end = extent[1]; + var keyAccessor = _chart.keyAccessor(); + _chart.g().selectAll('g.box').each(function (d) { + var key = keyAccessor(d); + if (key < start || key >= end) { + _chart.fadeDeselected(this); + } else { + _chart.highlightSelected(this); + } + }); + } + } else { + _chart.g().selectAll('g.box').each(function () { + _chart.resetHighlight(this); + }); + } + }; + + _chart.isSelectedNode = function (d) { + return _chart.hasFilter(_chart.keyAccessor()(d)); + }; + + _chart.yAxisMin = function () { + var min = d3.min(_chart.data(), function (e) { + return d3.min(_chart.valueAccessor()(e)); + }); + return dc.utils.subtract(min, _chart.yAxisPadding()); + }; + + _chart.yAxisMax = function () { + var max = d3.max(_chart.data(), function (e) { + return d3.max(_chart.valueAccessor()(e)); + }); + return dc.utils.add(max, _chart.yAxisPadding()); + }; + + /** + * Set the numerical format of the boxplot median, whiskers and quartile labels. Defaults to + * integer formatting. + * @example + * // format ticks to 2 decimal places + * chart.tickFormat(d3.format('.2f')); + * @method tickFormat + * @memberof dc.boxPlot + * @instance + * @param {Function} [tickFormat] + * @returns {Number|Function|dc.boxPlot} + */ + _chart.tickFormat = function (tickFormat) { + if (!arguments.length) { + return _tickFormat; + } + _tickFormat = tickFormat; + return _chart; + }; + + return _chart.anchor(parent, chartGroup); +}; + +// Renamed functions + +dc.abstractBubbleChart = dc.bubbleMixin; +dc.baseChart = dc.baseMixin; +dc.capped = dc.capMixin; +dc.colorChart = dc.colorMixin; +dc.coordinateGridChart = dc.coordinateGridMixin; +dc.marginable = dc.marginMixin; +dc.stackableChart = dc.stackMixin; + +// Expose d3 and crossfilter, so that clients in browserify +// case can obtain them if they need them. +dc.d3 = d3; +dc.crossfilter = crossfilter; + +return dc;} + if(typeof define === "function" && define.amd) { + define(["d3", "crossfilter2"], _dc); + } else if(typeof module === "object" && module.exports) { + var _d3 = require('d3'); + var _crossfilter = require('crossfilter2'); + // When using npm + browserify, 'crossfilter' is a function, + // since package.json specifies index.js as main function, and it + // does special handling. When using bower + browserify, + // there's no main in bower.json (in fact, there's no bower.json), + // so we need to fix it. + if (typeof _crossfilter !== "function") { + _crossfilter = _crossfilter.crossfilter; + } + module.exports = _dc(_d3, _crossfilter); + } else { + this.dc = _dc(d3, crossfilter); + } +} +)(); + +//# sourceMappingURL=dc.js.map \ No newline at end of file diff --git a/src/legacy/design-studio/js/jquery-ui.js b/src/legacy/design-studio/js/jquery-ui.js new file mode 100644 index 0000000..0213552 --- /dev/null +++ b/src/legacy/design-studio/js/jquery-ui.js @@ -0,0 +1,18706 @@ +/*! jQuery UI - v1.12.1 - 2016-09-14 +* http://jqueryui.com +* Includes: widget.js, position.js, data.js, disable-selection.js, effect.js, effects/effect-blind.js, effects/effect-bounce.js, effects/effect-clip.js, effects/effect-drop.js, effects/effect-explode.js, effects/effect-fade.js, effects/effect-fold.js, effects/effect-highlight.js, effects/effect-puff.js, effects/effect-pulsate.js, effects/effect-scale.js, effects/effect-shake.js, effects/effect-size.js, effects/effect-slide.js, effects/effect-transfer.js, focusable.js, form-reset-mixin.js, jquery-1-7.js, keycode.js, labels.js, scroll-parent.js, tabbable.js, unique-id.js, widgets/accordion.js, widgets/autocomplete.js, widgets/button.js, widgets/checkboxradio.js, widgets/controlgroup.js, widgets/datepicker.js, widgets/dialog.js, widgets/draggable.js, widgets/droppable.js, widgets/menu.js, widgets/mouse.js, widgets/progressbar.js, widgets/resizable.js, widgets/selectable.js, widgets/selectmenu.js, widgets/slider.js, widgets/sortable.js, widgets/spinner.js, widgets/tabs.js, widgets/tooltip.js +* Copyright jQuery Foundation and other contributors; Licensed MIT */ + +(function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define([ "jquery" ], factory ); + } else { + + // Browser globals + factory( jQuery ); + } +}(function( $ ) { + +$.ui = $.ui || {}; + +var version = $.ui.version = "1.12.1"; + + +/*! + * jQuery UI Widget 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: Widget +//>>group: Core +//>>description: Provides a factory for creating stateful widgets with a common API. +//>>docs: http://api.jqueryui.com/jQuery.widget/ +//>>demos: http://jqueryui.com/widget/ + + + +var widgetUuid = 0; +var widgetSlice = Array.prototype.slice; + +$.cleanData = ( function( orig ) { + return function( elems ) { + var events, elem, i; + for ( i = 0; ( elem = elems[ i ] ) != null; i++ ) { + try { + + // Only trigger remove when necessary to save time + events = $._data( elem, "events" ); + if ( events && events.remove ) { + $( elem ).triggerHandler( "remove" ); + } + + // Http://bugs.jquery.com/ticket/8235 + } catch ( e ) {} + } + orig( elems ); + }; +} )( $.cleanData ); + +$.widget = function( name, base, prototype ) { + var existingConstructor, constructor, basePrototype; + + // ProxiedPrototype allows the provided prototype to remain unmodified + // so that it can be used as a mixin for multiple widgets (#8876) + var proxiedPrototype = {}; + + var namespace = name.split( "." )[ 0 ]; + name = name.split( "." )[ 1 ]; + var fullName = namespace + "-" + name; + + if ( !prototype ) { + prototype = base; + base = $.Widget; + } + + if ( $.isArray( prototype ) ) { + prototype = $.extend.apply( null, [ {} ].concat( prototype ) ); + } + + // Create selector for plugin + $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) { + return !!$.data( elem, fullName ); + }; + + $[ namespace ] = $[ namespace ] || {}; + existingConstructor = $[ namespace ][ name ]; + constructor = $[ namespace ][ name ] = function( options, element ) { + + // Allow instantiation without "new" keyword + if ( !this._createWidget ) { + return new constructor( options, element ); + } + + // Allow instantiation without initializing for simple inheritance + // must use "new" keyword (the code above always passes args) + if ( arguments.length ) { + this._createWidget( options, element ); + } + }; + + // Extend with the existing constructor to carry over any static properties + $.extend( constructor, existingConstructor, { + version: prototype.version, + + // Copy the object used to create the prototype in case we need to + // redefine the widget later + _proto: $.extend( {}, prototype ), + + // Track widgets that inherit from this widget in case this widget is + // redefined after a widget inherits from it + _childConstructors: [] + } ); + + basePrototype = new base(); + + // We need to make the options hash a property directly on the new instance + // otherwise we'll modify the options hash on the prototype that we're + // inheriting from + basePrototype.options = $.widget.extend( {}, basePrototype.options ); + $.each( prototype, function( prop, value ) { + if ( !$.isFunction( value ) ) { + proxiedPrototype[ prop ] = value; + return; + } + proxiedPrototype[ prop ] = ( function() { + function _super() { + return base.prototype[ prop ].apply( this, arguments ); + } + + function _superApply( args ) { + return base.prototype[ prop ].apply( this, args ); + } + + return function() { + var __super = this._super; + var __superApply = this._superApply; + var returnValue; + + this._super = _super; + this._superApply = _superApply; + + returnValue = value.apply( this, arguments ); + + this._super = __super; + this._superApply = __superApply; + + return returnValue; + }; + } )(); + } ); + constructor.prototype = $.widget.extend( basePrototype, { + + // TODO: remove support for widgetEventPrefix + // always use the name + a colon as the prefix, e.g., draggable:start + // don't prefix for widgets that aren't DOM-based + widgetEventPrefix: existingConstructor ? ( basePrototype.widgetEventPrefix || name ) : name + }, proxiedPrototype, { + constructor: constructor, + namespace: namespace, + widgetName: name, + widgetFullName: fullName + } ); + + // If this widget is being redefined then we need to find all widgets that + // are inheriting from it and redefine all of them so that they inherit from + // the new version of this widget. We're essentially trying to replace one + // level in the prototype chain. + if ( existingConstructor ) { + $.each( existingConstructor._childConstructors, function( i, child ) { + var childPrototype = child.prototype; + + // Redefine the child widget using the same prototype that was + // originally used, but inherit from the new version of the base + $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, + child._proto ); + } ); + + // Remove the list of existing child constructors from the old constructor + // so the old child constructors can be garbage collected + delete existingConstructor._childConstructors; + } else { + base._childConstructors.push( constructor ); + } + + $.widget.bridge( name, constructor ); + + return constructor; +}; + +$.widget.extend = function( target ) { + var input = widgetSlice.call( arguments, 1 ); + var inputIndex = 0; + var inputLength = input.length; + var key; + var value; + + for ( ; inputIndex < inputLength; inputIndex++ ) { + for ( key in input[ inputIndex ] ) { + value = input[ inputIndex ][ key ]; + if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) { + + // Clone objects + if ( $.isPlainObject( value ) ) { + target[ key ] = $.isPlainObject( target[ key ] ) ? + $.widget.extend( {}, target[ key ], value ) : + + // Don't extend strings, arrays, etc. with objects + $.widget.extend( {}, value ); + + // Copy everything else by reference + } else { + target[ key ] = value; + } + } + } + } + return target; +}; + +$.widget.bridge = function( name, object ) { + var fullName = object.prototype.widgetFullName || name; + $.fn[ name ] = function( options ) { + var isMethodCall = typeof options === "string"; + var args = widgetSlice.call( arguments, 1 ); + var returnValue = this; + + if ( isMethodCall ) { + + // If this is an empty collection, we need to have the instance method + // return undefined instead of the jQuery instance + if ( !this.length && options === "instance" ) { + returnValue = undefined; + } else { + this.each( function() { + var methodValue; + var instance = $.data( this, fullName ); + + if ( options === "instance" ) { + returnValue = instance; + return false; + } + + if ( !instance ) { + return $.error( "cannot call methods on " + name + + " prior to initialization; " + + "attempted to call method '" + options + "'" ); + } + + if ( !$.isFunction( instance[ options ] ) || options.charAt( 0 ) === "_" ) { + return $.error( "no such method '" + options + "' for " + name + + " widget instance" ); + } + + methodValue = instance[ options ].apply( instance, args ); + + if ( methodValue !== instance && methodValue !== undefined ) { + returnValue = methodValue && methodValue.jquery ? + returnValue.pushStack( methodValue.get() ) : + methodValue; + return false; + } + } ); + } + } else { + + // Allow multiple hashes to be passed on init + if ( args.length ) { + options = $.widget.extend.apply( null, [ options ].concat( args ) ); + } + + this.each( function() { + var instance = $.data( this, fullName ); + if ( instance ) { + instance.option( options || {} ); + if ( instance._init ) { + instance._init(); + } + } else { + $.data( this, fullName, new object( options, this ) ); + } + } ); + } + + return returnValue; + }; +}; + +$.Widget = function( /* options, element */ ) {}; +$.Widget._childConstructors = []; + +$.Widget.prototype = { + widgetName: "widget", + widgetEventPrefix: "", + defaultElement: "<div>", + + options: { + classes: {}, + disabled: false, + + // Callbacks + create: null + }, + + _createWidget: function( options, element ) { + element = $( element || this.defaultElement || this )[ 0 ]; + this.element = $( element ); + this.uuid = widgetUuid++; + this.eventNamespace = "." + this.widgetName + this.uuid; + + this.bindings = $(); + this.hoverable = $(); + this.focusable = $(); + this.classesElementLookup = {}; + + if ( element !== this ) { + $.data( element, this.widgetFullName, this ); + this._on( true, this.element, { + remove: function( event ) { + if ( event.target === element ) { + this.destroy(); + } + } + } ); + this.document = $( element.style ? + + // Element within the document + element.ownerDocument : + + // Element is window or document + element.document || element ); + this.window = $( this.document[ 0 ].defaultView || this.document[ 0 ].parentWindow ); + } + + this.options = $.widget.extend( {}, + this.options, + this._getCreateOptions(), + options ); + + this._create(); + + if ( this.options.disabled ) { + this._setOptionDisabled( this.options.disabled ); + } + + this._trigger( "create", null, this._getCreateEventData() ); + this._init(); + }, + + _getCreateOptions: function() { + return {}; + }, + + _getCreateEventData: $.noop, + + _create: $.noop, + + _init: $.noop, + + destroy: function() { + var that = this; + + this._destroy(); + $.each( this.classesElementLookup, function( key, value ) { + that._removeClass( value, key ); + } ); + + // We can probably remove the unbind calls in 2.0 + // all event bindings should go through this._on() + this.element + .off( this.eventNamespace ) + .removeData( this.widgetFullName ); + this.widget() + .off( this.eventNamespace ) + .removeAttr( "aria-disabled" ); + + // Clean up events and states + this.bindings.off( this.eventNamespace ); + }, + + _destroy: $.noop, + + widget: function() { + return this.element; + }, + + option: function( key, value ) { + var options = key; + var parts; + var curOption; + var i; + + if ( arguments.length === 0 ) { + + // Don't return a reference to the internal hash + return $.widget.extend( {}, this.options ); + } + + if ( typeof key === "string" ) { + + // Handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } } + options = {}; + parts = key.split( "." ); + key = parts.shift(); + if ( parts.length ) { + curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] ); + for ( i = 0; i < parts.length - 1; i++ ) { + curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {}; + curOption = curOption[ parts[ i ] ]; + } + key = parts.pop(); + if ( arguments.length === 1 ) { + return curOption[ key ] === undefined ? null : curOption[ key ]; + } + curOption[ key ] = value; + } else { + if ( arguments.length === 1 ) { + return this.options[ key ] === undefined ? null : this.options[ key ]; + } + options[ key ] = value; + } + } + + this._setOptions( options ); + + return this; + }, + + _setOptions: function( options ) { + var key; + + for ( key in options ) { + this._setOption( key, options[ key ] ); + } + + return this; + }, + + _setOption: function( key, value ) { + if ( key === "classes" ) { + this._setOptionClasses( value ); + } + + this.options[ key ] = value; + + if ( key === "disabled" ) { + this._setOptionDisabled( value ); + } + + return this; + }, + + _setOptionClasses: function( value ) { + var classKey, elements, currentElements; + + for ( classKey in value ) { + currentElements = this.classesElementLookup[ classKey ]; + if ( value[ classKey ] === this.options.classes[ classKey ] || + !currentElements || + !currentElements.length ) { + continue; + } + + // We are doing this to create a new jQuery object because the _removeClass() call + // on the next line is going to destroy the reference to the current elements being + // tracked. We need to save a copy of this collection so that we can add the new classes + // below. + elements = $( currentElements.get() ); + this._removeClass( currentElements, classKey ); + + // We don't use _addClass() here, because that uses this.options.classes + // for generating the string of classes. We want to use the value passed in from + // _setOption(), this is the new value of the classes option which was passed to + // _setOption(). We pass this value directly to _classes(). + elements.addClass( this._classes( { + element: elements, + keys: classKey, + classes: value, + add: true + } ) ); + } + }, + + _setOptionDisabled: function( value ) { + this._toggleClass( this.widget(), this.widgetFullName + "-disabled", null, !!value ); + + // If the widget is becoming disabled, then nothing is interactive + if ( value ) { + this._removeClass( this.hoverable, null, "ui-state-hover" ); + this._removeClass( this.focusable, null, "ui-state-focus" ); + } + }, + + enable: function() { + return this._setOptions( { disabled: false } ); + }, + + disable: function() { + return this._setOptions( { disabled: true } ); + }, + + _classes: function( options ) { + var full = []; + var that = this; + + options = $.extend( { + element: this.element, + classes: this.options.classes || {} + }, options ); + + function processClassString( classes, checkOption ) { + var current, i; + for ( i = 0; i < classes.length; i++ ) { + current = that.classesElementLookup[ classes[ i ] ] || $(); + if ( options.add ) { + current = $( $.unique( current.get().concat( options.element.get() ) ) ); + } else { + current = $( current.not( options.element ).get() ); + } + that.classesElementLookup[ classes[ i ] ] = current; + full.push( classes[ i ] ); + if ( checkOption && options.classes[ classes[ i ] ] ) { + full.push( options.classes[ classes[ i ] ] ); + } + } + } + + this._on( options.element, { + "remove": "_untrackClassesElement" + } ); + + if ( options.keys ) { + processClassString( options.keys.match( /\S+/g ) || [], true ); + } + if ( options.extra ) { + processClassString( options.extra.match( /\S+/g ) || [] ); + } + + return full.join( " " ); + }, + + _untrackClassesElement: function( event ) { + var that = this; + $.each( that.classesElementLookup, function( key, value ) { + if ( $.inArray( event.target, value ) !== -1 ) { + that.classesElementLookup[ key ] = $( value.not( event.target ).get() ); + } + } ); + }, + + _removeClass: function( element, keys, extra ) { + return this._toggleClass( element, keys, extra, false ); + }, + + _addClass: function( element, keys, extra ) { + return this._toggleClass( element, keys, extra, true ); + }, + + _toggleClass: function( element, keys, extra, add ) { + add = ( typeof add === "boolean" ) ? add : extra; + var shift = ( typeof element === "string" || element === null ), + options = { + extra: shift ? keys : extra, + keys: shift ? element : keys, + element: shift ? this.element : element, + add: add + }; + options.element.toggleClass( this._classes( options ), add ); + return this; + }, + + _on: function( suppressDisabledCheck, element, handlers ) { + var delegateElement; + var instance = this; + + // No suppressDisabledCheck flag, shuffle arguments + if ( typeof suppressDisabledCheck !== "boolean" ) { + handlers = element; + element = suppressDisabledCheck; + suppressDisabledCheck = false; + } + + // No element argument, shuffle and use this.element + if ( !handlers ) { + handlers = element; + element = this.element; + delegateElement = this.widget(); + } else { + element = delegateElement = $( element ); + this.bindings = this.bindings.add( element ); + } + + $.each( handlers, function( event, handler ) { + function handlerProxy() { + + // Allow widgets to customize the disabled handling + // - disabled as an array instead of boolean + // - disabled class as method for disabling individual parts + if ( !suppressDisabledCheck && + ( instance.options.disabled === true || + $( this ).hasClass( "ui-state-disabled" ) ) ) { + return; + } + return ( typeof handler === "string" ? instance[ handler ] : handler ) + .apply( instance, arguments ); + } + + // Copy the guid so direct unbinding works + if ( typeof handler !== "string" ) { + handlerProxy.guid = handler.guid = + handler.guid || handlerProxy.guid || $.guid++; + } + + var match = event.match( /^([\w:-]*)\s*(.*)$/ ); + var eventName = match[ 1 ] + instance.eventNamespace; + var selector = match[ 2 ]; + + if ( selector ) { + delegateElement.on( eventName, selector, handlerProxy ); + } else { + element.on( eventName, handlerProxy ); + } + } ); + }, + + _off: function( element, eventName ) { + eventName = ( eventName || "" ).split( " " ).join( this.eventNamespace + " " ) + + this.eventNamespace; + element.off( eventName ).off( eventName ); + + // Clear the stack to avoid memory leaks (#10056) + this.bindings = $( this.bindings.not( element ).get() ); + this.focusable = $( this.focusable.not( element ).get() ); + this.hoverable = $( this.hoverable.not( element ).get() ); + }, + + _delay: function( handler, delay ) { + function handlerProxy() { + return ( typeof handler === "string" ? instance[ handler ] : handler ) + .apply( instance, arguments ); + } + var instance = this; + return setTimeout( handlerProxy, delay || 0 ); + }, + + _hoverable: function( element ) { + this.hoverable = this.hoverable.add( element ); + this._on( element, { + mouseenter: function( event ) { + this._addClass( $( event.currentTarget ), null, "ui-state-hover" ); + }, + mouseleave: function( event ) { + this._removeClass( $( event.currentTarget ), null, "ui-state-hover" ); + } + } ); + }, + + _focusable: function( element ) { + this.focusable = this.focusable.add( element ); + this._on( element, { + focusin: function( event ) { + this._addClass( $( event.currentTarget ), null, "ui-state-focus" ); + }, + focusout: function( event ) { + this._removeClass( $( event.currentTarget ), null, "ui-state-focus" ); + } + } ); + }, + + _trigger: function( type, event, data ) { + var prop, orig; + var callback = this.options[ type ]; + + data = data || {}; + event = $.Event( event ); + event.type = ( type === this.widgetEventPrefix ? + type : + this.widgetEventPrefix + type ).toLowerCase(); + + // The original event may come from any element + // so we need to reset the target on the new event + event.target = this.element[ 0 ]; + + // Copy original event properties over to the new event + orig = event.originalEvent; + if ( orig ) { + for ( prop in orig ) { + if ( !( prop in event ) ) { + event[ prop ] = orig[ prop ]; + } + } + } + + this.element.trigger( event, data ); + return !( $.isFunction( callback ) && + callback.apply( this.element[ 0 ], [ event ].concat( data ) ) === false || + event.isDefaultPrevented() ); + } +}; + +$.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) { + $.Widget.prototype[ "_" + method ] = function( element, options, callback ) { + if ( typeof options === "string" ) { + options = { effect: options }; + } + + var hasOptions; + var effectName = !options ? + method : + options === true || typeof options === "number" ? + defaultEffect : + options.effect || defaultEffect; + + options = options || {}; + if ( typeof options === "number" ) { + options = { duration: options }; + } + + hasOptions = !$.isEmptyObject( options ); + options.complete = callback; + + if ( options.delay ) { + element.delay( options.delay ); + } + + if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) { + element[ method ]( options ); + } else if ( effectName !== method && element[ effectName ] ) { + element[ effectName ]( options.duration, options.easing, callback ); + } else { + element.queue( function( next ) { + $( this )[ method ](); + if ( callback ) { + callback.call( element[ 0 ] ); + } + next(); + } ); + } + }; +} ); + +var widget = $.widget; + + +/*! + * jQuery UI Position 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/position/ + */ + +//>>label: Position +//>>group: Core +//>>description: Positions elements relative to other elements. +//>>docs: http://api.jqueryui.com/position/ +//>>demos: http://jqueryui.com/position/ + + +( function() { +var cachedScrollbarWidth, + max = Math.max, + abs = Math.abs, + rhorizontal = /left|center|right/, + rvertical = /top|center|bottom/, + roffset = /[\+\-]\d+(\.[\d]+)?%?/, + rposition = /^\w+/, + rpercent = /%$/, + _position = $.fn.position; + +function getOffsets( offsets, width, height ) { + return [ + parseFloat( offsets[ 0 ] ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ), + parseFloat( offsets[ 1 ] ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 ) + ]; +} + +function parseCss( element, property ) { + return parseInt( $.css( element, property ), 10 ) || 0; +} + +function getDimensions( elem ) { + var raw = elem[ 0 ]; + if ( raw.nodeType === 9 ) { + return { + width: elem.width(), + height: elem.height(), + offset: { top: 0, left: 0 } + }; + } + if ( $.isWindow( raw ) ) { + return { + width: elem.width(), + height: elem.height(), + offset: { top: elem.scrollTop(), left: elem.scrollLeft() } + }; + } + if ( raw.preventDefault ) { + return { + width: 0, + height: 0, + offset: { top: raw.pageY, left: raw.pageX } + }; + } + return { + width: elem.outerWidth(), + height: elem.outerHeight(), + offset: elem.offset() + }; +} + +$.position = { + scrollbarWidth: function() { + if ( cachedScrollbarWidth !== undefined ) { + return cachedScrollbarWidth; + } + var w1, w2, + div = $( "<div " + + "style='display:block;position:absolute;width:50px;height:50px;overflow:hidden;'>" + + "<div style='height:100px;width:auto;'></div></div>" ), + innerDiv = div.children()[ 0 ]; + + $( "body" ).append( div ); + w1 = innerDiv.offsetWidth; + div.css( "overflow", "scroll" ); + + w2 = innerDiv.offsetWidth; + + if ( w1 === w2 ) { + w2 = div[ 0 ].clientWidth; + } + + div.remove(); + + return ( cachedScrollbarWidth = w1 - w2 ); + }, + getScrollInfo: function( within ) { + var overflowX = within.isWindow || within.isDocument ? "" : + within.element.css( "overflow-x" ), + overflowY = within.isWindow || within.isDocument ? "" : + within.element.css( "overflow-y" ), + hasOverflowX = overflowX === "scroll" || + ( overflowX === "auto" && within.width < within.element[ 0 ].scrollWidth ), + hasOverflowY = overflowY === "scroll" || + ( overflowY === "auto" && within.height < within.element[ 0 ].scrollHeight ); + return { + width: hasOverflowY ? $.position.scrollbarWidth() : 0, + height: hasOverflowX ? $.position.scrollbarWidth() : 0 + }; + }, + getWithinInfo: function( element ) { + var withinElement = $( element || window ), + isWindow = $.isWindow( withinElement[ 0 ] ), + isDocument = !!withinElement[ 0 ] && withinElement[ 0 ].nodeType === 9, + hasOffset = !isWindow && !isDocument; + return { + element: withinElement, + isWindow: isWindow, + isDocument: isDocument, + offset: hasOffset ? $( element ).offset() : { left: 0, top: 0 }, + scrollLeft: withinElement.scrollLeft(), + scrollTop: withinElement.scrollTop(), + width: withinElement.outerWidth(), + height: withinElement.outerHeight() + }; + } +}; + +$.fn.position = function( options ) { + if ( !options || !options.of ) { + return _position.apply( this, arguments ); + } + + // Make a copy, we don't want to modify arguments + options = $.extend( {}, options ); + + var atOffset, targetWidth, targetHeight, targetOffset, basePosition, dimensions, + target = $( options.of ), + within = $.position.getWithinInfo( options.within ), + scrollInfo = $.position.getScrollInfo( within ), + collision = ( options.collision || "flip" ).split( " " ), + offsets = {}; + + dimensions = getDimensions( target ); + if ( target[ 0 ].preventDefault ) { + + // Force left top to allow flipping + options.at = "left top"; + } + targetWidth = dimensions.width; + targetHeight = dimensions.height; + targetOffset = dimensions.offset; + + // Clone to reuse original targetOffset later + basePosition = $.extend( {}, targetOffset ); + + // Force my and at to have valid horizontal and vertical positions + // if a value is missing or invalid, it will be converted to center + $.each( [ "my", "at" ], function() { + var pos = ( options[ this ] || "" ).split( " " ), + horizontalOffset, + verticalOffset; + + if ( pos.length === 1 ) { + pos = rhorizontal.test( pos[ 0 ] ) ? + pos.concat( [ "center" ] ) : + rvertical.test( pos[ 0 ] ) ? + [ "center" ].concat( pos ) : + [ "center", "center" ]; + } + pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : "center"; + pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : "center"; + + // Calculate offsets + horizontalOffset = roffset.exec( pos[ 0 ] ); + verticalOffset = roffset.exec( pos[ 1 ] ); + offsets[ this ] = [ + horizontalOffset ? horizontalOffset[ 0 ] : 0, + verticalOffset ? verticalOffset[ 0 ] : 0 + ]; + + // Reduce to just the positions without the offsets + options[ this ] = [ + rposition.exec( pos[ 0 ] )[ 0 ], + rposition.exec( pos[ 1 ] )[ 0 ] + ]; + } ); + + // Normalize collision option + if ( collision.length === 1 ) { + collision[ 1 ] = collision[ 0 ]; + } + + if ( options.at[ 0 ] === "right" ) { + basePosition.left += targetWidth; + } else if ( options.at[ 0 ] === "center" ) { + basePosition.left += targetWidth / 2; + } + + if ( options.at[ 1 ] === "bottom" ) { + basePosition.top += targetHeight; + } else if ( options.at[ 1 ] === "center" ) { + basePosition.top += targetHeight / 2; + } + + atOffset = getOffsets( offsets.at, targetWidth, targetHeight ); + basePosition.left += atOffset[ 0 ]; + basePosition.top += atOffset[ 1 ]; + + return this.each( function() { + var collisionPosition, using, + elem = $( this ), + elemWidth = elem.outerWidth(), + elemHeight = elem.outerHeight(), + marginLeft = parseCss( this, "marginLeft" ), + marginTop = parseCss( this, "marginTop" ), + collisionWidth = elemWidth + marginLeft + parseCss( this, "marginRight" ) + + scrollInfo.width, + collisionHeight = elemHeight + marginTop + parseCss( this, "marginBottom" ) + + scrollInfo.height, + position = $.extend( {}, basePosition ), + myOffset = getOffsets( offsets.my, elem.outerWidth(), elem.outerHeight() ); + + if ( options.my[ 0 ] === "right" ) { + position.left -= elemWidth; + } else if ( options.my[ 0 ] === "center" ) { + position.left -= elemWidth / 2; + } + + if ( options.my[ 1 ] === "bottom" ) { + position.top -= elemHeight; + } else if ( options.my[ 1 ] === "center" ) { + position.top -= elemHeight / 2; + } + + position.left += myOffset[ 0 ]; + position.top += myOffset[ 1 ]; + + collisionPosition = { + marginLeft: marginLeft, + marginTop: marginTop + }; + + $.each( [ "left", "top" ], function( i, dir ) { + if ( $.ui.position[ collision[ i ] ] ) { + $.ui.position[ collision[ i ] ][ dir ]( position, { + targetWidth: targetWidth, + targetHeight: targetHeight, + elemWidth: elemWidth, + elemHeight: elemHeight, + collisionPosition: collisionPosition, + collisionWidth: collisionWidth, + collisionHeight: collisionHeight, + offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ], + my: options.my, + at: options.at, + within: within, + elem: elem + } ); + } + } ); + + if ( options.using ) { + + // Adds feedback as second argument to using callback, if present + using = function( props ) { + var left = targetOffset.left - position.left, + right = left + targetWidth - elemWidth, + top = targetOffset.top - position.top, + bottom = top + targetHeight - elemHeight, + feedback = { + target: { + element: target, + left: targetOffset.left, + top: targetOffset.top, + width: targetWidth, + height: targetHeight + }, + element: { + element: elem, + left: position.left, + top: position.top, + width: elemWidth, + height: elemHeight + }, + horizontal: right < 0 ? "left" : left > 0 ? "right" : "center", + vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle" + }; + if ( targetWidth < elemWidth && abs( left + right ) < targetWidth ) { + feedback.horizontal = "center"; + } + if ( targetHeight < elemHeight && abs( top + bottom ) < targetHeight ) { + feedback.vertical = "middle"; + } + if ( max( abs( left ), abs( right ) ) > max( abs( top ), abs( bottom ) ) ) { + feedback.important = "horizontal"; + } else { + feedback.important = "vertical"; + } + options.using.call( this, props, feedback ); + }; + } + + elem.offset( $.extend( position, { using: using } ) ); + } ); +}; + +$.ui.position = { + fit: { + left: function( position, data ) { + var within = data.within, + withinOffset = within.isWindow ? within.scrollLeft : within.offset.left, + outerWidth = within.width, + collisionPosLeft = position.left - data.collisionPosition.marginLeft, + overLeft = withinOffset - collisionPosLeft, + overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset, + newOverRight; + + // Element is wider than within + if ( data.collisionWidth > outerWidth ) { + + // Element is initially over the left side of within + if ( overLeft > 0 && overRight <= 0 ) { + newOverRight = position.left + overLeft + data.collisionWidth - outerWidth - + withinOffset; + position.left += overLeft - newOverRight; + + // Element is initially over right side of within + } else if ( overRight > 0 && overLeft <= 0 ) { + position.left = withinOffset; + + // Element is initially over both left and right sides of within + } else { + if ( overLeft > overRight ) { + position.left = withinOffset + outerWidth - data.collisionWidth; + } else { + position.left = withinOffset; + } + } + + // Too far left -> align with left edge + } else if ( overLeft > 0 ) { + position.left += overLeft; + + // Too far right -> align with right edge + } else if ( overRight > 0 ) { + position.left -= overRight; + + // Adjust based on position and margin + } else { + position.left = max( position.left - collisionPosLeft, position.left ); + } + }, + top: function( position, data ) { + var within = data.within, + withinOffset = within.isWindow ? within.scrollTop : within.offset.top, + outerHeight = data.within.height, + collisionPosTop = position.top - data.collisionPosition.marginTop, + overTop = withinOffset - collisionPosTop, + overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset, + newOverBottom; + + // Element is taller than within + if ( data.collisionHeight > outerHeight ) { + + // Element is initially over the top of within + if ( overTop > 0 && overBottom <= 0 ) { + newOverBottom = position.top + overTop + data.collisionHeight - outerHeight - + withinOffset; + position.top += overTop - newOverBottom; + + // Element is initially over bottom of within + } else if ( overBottom > 0 && overTop <= 0 ) { + position.top = withinOffset; + + // Element is initially over both top and bottom of within + } else { + if ( overTop > overBottom ) { + position.top = withinOffset + outerHeight - data.collisionHeight; + } else { + position.top = withinOffset; + } + } + + // Too far up -> align with top + } else if ( overTop > 0 ) { + position.top += overTop; + + // Too far down -> align with bottom edge + } else if ( overBottom > 0 ) { + position.top -= overBottom; + + // Adjust based on position and margin + } else { + position.top = max( position.top - collisionPosTop, position.top ); + } + } + }, + flip: { + left: function( position, data ) { + var within = data.within, + withinOffset = within.offset.left + within.scrollLeft, + outerWidth = within.width, + offsetLeft = within.isWindow ? within.scrollLeft : within.offset.left, + collisionPosLeft = position.left - data.collisionPosition.marginLeft, + overLeft = collisionPosLeft - offsetLeft, + overRight = collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft, + myOffset = data.my[ 0 ] === "left" ? + -data.elemWidth : + data.my[ 0 ] === "right" ? + data.elemWidth : + 0, + atOffset = data.at[ 0 ] === "left" ? + data.targetWidth : + data.at[ 0 ] === "right" ? + -data.targetWidth : + 0, + offset = -2 * data.offset[ 0 ], + newOverRight, + newOverLeft; + + if ( overLeft < 0 ) { + newOverRight = position.left + myOffset + atOffset + offset + data.collisionWidth - + outerWidth - withinOffset; + if ( newOverRight < 0 || newOverRight < abs( overLeft ) ) { + position.left += myOffset + atOffset + offset; + } + } else if ( overRight > 0 ) { + newOverLeft = position.left - data.collisionPosition.marginLeft + myOffset + + atOffset + offset - offsetLeft; + if ( newOverLeft > 0 || abs( newOverLeft ) < overRight ) { + position.left += myOffset + atOffset + offset; + } + } + }, + top: function( position, data ) { + var within = data.within, + withinOffset = within.offset.top + within.scrollTop, + outerHeight = within.height, + offsetTop = within.isWindow ? within.scrollTop : within.offset.top, + collisionPosTop = position.top - data.collisionPosition.marginTop, + overTop = collisionPosTop - offsetTop, + overBottom = collisionPosTop + data.collisionHeight - outerHeight - offsetTop, + top = data.my[ 1 ] === "top", + myOffset = top ? + -data.elemHeight : + data.my[ 1 ] === "bottom" ? + data.elemHeight : + 0, + atOffset = data.at[ 1 ] === "top" ? + data.targetHeight : + data.at[ 1 ] === "bottom" ? + -data.targetHeight : + 0, + offset = -2 * data.offset[ 1 ], + newOverTop, + newOverBottom; + if ( overTop < 0 ) { + newOverBottom = position.top + myOffset + atOffset + offset + data.collisionHeight - + outerHeight - withinOffset; + if ( newOverBottom < 0 || newOverBottom < abs( overTop ) ) { + position.top += myOffset + atOffset + offset; + } + } else if ( overBottom > 0 ) { + newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset + + offset - offsetTop; + if ( newOverTop > 0 || abs( newOverTop ) < overBottom ) { + position.top += myOffset + atOffset + offset; + } + } + } + }, + flipfit: { + left: function() { + $.ui.position.flip.left.apply( this, arguments ); + $.ui.position.fit.left.apply( this, arguments ); + }, + top: function() { + $.ui.position.flip.top.apply( this, arguments ); + $.ui.position.fit.top.apply( this, arguments ); + } + } +}; + +} )(); + +var position = $.ui.position; + + +/*! + * jQuery UI :data 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: :data Selector +//>>group: Core +//>>description: Selects elements which have data stored under the specified key. +//>>docs: http://api.jqueryui.com/data-selector/ + + +var data = $.extend( $.expr[ ":" ], { + data: $.expr.createPseudo ? + $.expr.createPseudo( function( dataName ) { + return function( elem ) { + return !!$.data( elem, dataName ); + }; + } ) : + + // Support: jQuery <1.8 + function( elem, i, match ) { + return !!$.data( elem, match[ 3 ] ); + } +} ); + +/*! + * jQuery UI Disable Selection 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: disableSelection +//>>group: Core +//>>description: Disable selection of text content within the set of matched elements. +//>>docs: http://api.jqueryui.com/disableSelection/ + +// This file is deprecated + + +var disableSelection = $.fn.extend( { + disableSelection: ( function() { + var eventType = "onselectstart" in document.createElement( "div" ) ? + "selectstart" : + "mousedown"; + + return function() { + return this.on( eventType + ".ui-disableSelection", function( event ) { + event.preventDefault(); + } ); + }; + } )(), + + enableSelection: function() { + return this.off( ".ui-disableSelection" ); + } +} ); + + +/*! + * jQuery UI Effects 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: Effects Core +//>>group: Effects +// jscs:disable maximumLineLength +//>>description: Extends the internal jQuery effects. Includes morphing and easing. Required by all other effects. +// jscs:enable maximumLineLength +//>>docs: http://api.jqueryui.com/category/effects-core/ +//>>demos: http://jqueryui.com/effect/ + + + +var dataSpace = "ui-effects-", + dataSpaceStyle = "ui-effects-style", + dataSpaceAnimated = "ui-effects-animated", + + // Create a local jQuery because jQuery Color relies on it and the + // global may not exist with AMD and a custom build (#10199) + jQuery = $; + +$.effects = { + effect: {} +}; + +/*! + * jQuery Color Animations v2.1.2 + * https://github.com/jquery/jquery-color + * + * Copyright 2014 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * Date: Wed Jan 16 08:47:09 2013 -0600 + */ +( function( jQuery, undefined ) { + + var stepHooks = "backgroundColor borderBottomColor borderLeftColor borderRightColor " + + "borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor", + + // Plusequals test for += 100 -= 100 + rplusequals = /^([\-+])=\s*(\d+\.?\d*)/, + + // A set of RE's that can match strings and generate color tuples. + stringParsers = [ { + re: /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/, + parse: function( execResult ) { + return [ + execResult[ 1 ], + execResult[ 2 ], + execResult[ 3 ], + execResult[ 4 ] + ]; + } + }, { + re: /rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/, + parse: function( execResult ) { + return [ + execResult[ 1 ] * 2.55, + execResult[ 2 ] * 2.55, + execResult[ 3 ] * 2.55, + execResult[ 4 ] + ]; + } + }, { + + // This regex ignores A-F because it's compared against an already lowercased string + re: /#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/, + parse: function( execResult ) { + return [ + parseInt( execResult[ 1 ], 16 ), + parseInt( execResult[ 2 ], 16 ), + parseInt( execResult[ 3 ], 16 ) + ]; + } + }, { + + // This regex ignores A-F because it's compared against an already lowercased string + re: /#([a-f0-9])([a-f0-9])([a-f0-9])/, + parse: function( execResult ) { + return [ + parseInt( execResult[ 1 ] + execResult[ 1 ], 16 ), + parseInt( execResult[ 2 ] + execResult[ 2 ], 16 ), + parseInt( execResult[ 3 ] + execResult[ 3 ], 16 ) + ]; + } + }, { + re: /hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/, + space: "hsla", + parse: function( execResult ) { + return [ + execResult[ 1 ], + execResult[ 2 ] / 100, + execResult[ 3 ] / 100, + execResult[ 4 ] + ]; + } + } ], + + // JQuery.Color( ) + color = jQuery.Color = function( color, green, blue, alpha ) { + return new jQuery.Color.fn.parse( color, green, blue, alpha ); + }, + spaces = { + rgba: { + props: { + red: { + idx: 0, + type: "byte" + }, + green: { + idx: 1, + type: "byte" + }, + blue: { + idx: 2, + type: "byte" + } + } + }, + + hsla: { + props: { + hue: { + idx: 0, + type: "degrees" + }, + saturation: { + idx: 1, + type: "percent" + }, + lightness: { + idx: 2, + type: "percent" + } + } + } + }, + propTypes = { + "byte": { + floor: true, + max: 255 + }, + "percent": { + max: 1 + }, + "degrees": { + mod: 360, + floor: true + } + }, + support = color.support = {}, + + // Element for support tests + supportElem = jQuery( "<p>" )[ 0 ], + + // Colors = jQuery.Color.names + colors, + + // Local aliases of functions called often + each = jQuery.each; + +// Determine rgba support immediately +supportElem.style.cssText = "background-color:rgba(1,1,1,.5)"; +support.rgba = supportElem.style.backgroundColor.indexOf( "rgba" ) > -1; + +// Define cache name and alpha properties +// for rgba and hsla spaces +each( spaces, function( spaceName, space ) { + space.cache = "_" + spaceName; + space.props.alpha = { + idx: 3, + type: "percent", + def: 1 + }; +} ); + +function clamp( value, prop, allowEmpty ) { + var type = propTypes[ prop.type ] || {}; + + if ( value == null ) { + return ( allowEmpty || !prop.def ) ? null : prop.def; + } + + // ~~ is an short way of doing floor for positive numbers + value = type.floor ? ~~value : parseFloat( value ); + + // IE will pass in empty strings as value for alpha, + // which will hit this case + if ( isNaN( value ) ) { + return prop.def; + } + + if ( type.mod ) { + + // We add mod before modding to make sure that negatives values + // get converted properly: -10 -> 350 + return ( value + type.mod ) % type.mod; + } + + // For now all property types without mod have min and max + return 0 > value ? 0 : type.max < value ? type.max : value; +} + +function stringParse( string ) { + var inst = color(), + rgba = inst._rgba = []; + + string = string.toLowerCase(); + + each( stringParsers, function( i, parser ) { + var parsed, + match = parser.re.exec( string ), + values = match && parser.parse( match ), + spaceName = parser.space || "rgba"; + + if ( values ) { + parsed = inst[ spaceName ]( values ); + + // If this was an rgba parse the assignment might happen twice + // oh well.... + inst[ spaces[ spaceName ].cache ] = parsed[ spaces[ spaceName ].cache ]; + rgba = inst._rgba = parsed._rgba; + + // Exit each( stringParsers ) here because we matched + return false; + } + } ); + + // Found a stringParser that handled it + if ( rgba.length ) { + + // If this came from a parsed string, force "transparent" when alpha is 0 + // chrome, (and maybe others) return "transparent" as rgba(0,0,0,0) + if ( rgba.join() === "0,0,0,0" ) { + jQuery.extend( rgba, colors.transparent ); + } + return inst; + } + + // Named colors + return colors[ string ]; +} + +color.fn = jQuery.extend( color.prototype, { + parse: function( red, green, blue, alpha ) { + if ( red === undefined ) { + this._rgba = [ null, null, null, null ]; + return this; + } + if ( red.jquery || red.nodeType ) { + red = jQuery( red ).css( green ); + green = undefined; + } + + var inst = this, + type = jQuery.type( red ), + rgba = this._rgba = []; + + // More than 1 argument specified - assume ( red, green, blue, alpha ) + if ( green !== undefined ) { + red = [ red, green, blue, alpha ]; + type = "array"; + } + + if ( type === "string" ) { + return this.parse( stringParse( red ) || colors._default ); + } + + if ( type === "array" ) { + each( spaces.rgba.props, function( key, prop ) { + rgba[ prop.idx ] = clamp( red[ prop.idx ], prop ); + } ); + return this; + } + + if ( type === "object" ) { + if ( red instanceof color ) { + each( spaces, function( spaceName, space ) { + if ( red[ space.cache ] ) { + inst[ space.cache ] = red[ space.cache ].slice(); + } + } ); + } else { + each( spaces, function( spaceName, space ) { + var cache = space.cache; + each( space.props, function( key, prop ) { + + // If the cache doesn't exist, and we know how to convert + if ( !inst[ cache ] && space.to ) { + + // If the value was null, we don't need to copy it + // if the key was alpha, we don't need to copy it either + if ( key === "alpha" || red[ key ] == null ) { + return; + } + inst[ cache ] = space.to( inst._rgba ); + } + + // This is the only case where we allow nulls for ALL properties. + // call clamp with alwaysAllowEmpty + inst[ cache ][ prop.idx ] = clamp( red[ key ], prop, true ); + } ); + + // Everything defined but alpha? + if ( inst[ cache ] && + jQuery.inArray( null, inst[ cache ].slice( 0, 3 ) ) < 0 ) { + + // Use the default of 1 + inst[ cache ][ 3 ] = 1; + if ( space.from ) { + inst._rgba = space.from( inst[ cache ] ); + } + } + } ); + } + return this; + } + }, + is: function( compare ) { + var is = color( compare ), + same = true, + inst = this; + + each( spaces, function( _, space ) { + var localCache, + isCache = is[ space.cache ]; + if ( isCache ) { + localCache = inst[ space.cache ] || space.to && space.to( inst._rgba ) || []; + each( space.props, function( _, prop ) { + if ( isCache[ prop.idx ] != null ) { + same = ( isCache[ prop.idx ] === localCache[ prop.idx ] ); + return same; + } + } ); + } + return same; + } ); + return same; + }, + _space: function() { + var used = [], + inst = this; + each( spaces, function( spaceName, space ) { + if ( inst[ space.cache ] ) { + used.push( spaceName ); + } + } ); + return used.pop(); + }, + transition: function( other, distance ) { + var end = color( other ), + spaceName = end._space(), + space = spaces[ spaceName ], + startColor = this.alpha() === 0 ? color( "transparent" ) : this, + start = startColor[ space.cache ] || space.to( startColor._rgba ), + result = start.slice(); + + end = end[ space.cache ]; + each( space.props, function( key, prop ) { + var index = prop.idx, + startValue = start[ index ], + endValue = end[ index ], + type = propTypes[ prop.type ] || {}; + + // If null, don't override start value + if ( endValue === null ) { + return; + } + + // If null - use end + if ( startValue === null ) { + result[ index ] = endValue; + } else { + if ( type.mod ) { + if ( endValue - startValue > type.mod / 2 ) { + startValue += type.mod; + } else if ( startValue - endValue > type.mod / 2 ) { + startValue -= type.mod; + } + } + result[ index ] = clamp( ( endValue - startValue ) * distance + startValue, prop ); + } + } ); + return this[ spaceName ]( result ); + }, + blend: function( opaque ) { + + // If we are already opaque - return ourself + if ( this._rgba[ 3 ] === 1 ) { + return this; + } + + var rgb = this._rgba.slice(), + a = rgb.pop(), + blend = color( opaque )._rgba; + + return color( jQuery.map( rgb, function( v, i ) { + return ( 1 - a ) * blend[ i ] + a * v; + } ) ); + }, + toRgbaString: function() { + var prefix = "rgba(", + rgba = jQuery.map( this._rgba, function( v, i ) { + return v == null ? ( i > 2 ? 1 : 0 ) : v; + } ); + + if ( rgba[ 3 ] === 1 ) { + rgba.pop(); + prefix = "rgb("; + } + + return prefix + rgba.join() + ")"; + }, + toHslaString: function() { + var prefix = "hsla(", + hsla = jQuery.map( this.hsla(), function( v, i ) { + if ( v == null ) { + v = i > 2 ? 1 : 0; + } + + // Catch 1 and 2 + if ( i && i < 3 ) { + v = Math.round( v * 100 ) + "%"; + } + return v; + } ); + + if ( hsla[ 3 ] === 1 ) { + hsla.pop(); + prefix = "hsl("; + } + return prefix + hsla.join() + ")"; + }, + toHexString: function( includeAlpha ) { + var rgba = this._rgba.slice(), + alpha = rgba.pop(); + + if ( includeAlpha ) { + rgba.push( ~~( alpha * 255 ) ); + } + + return "#" + jQuery.map( rgba, function( v ) { + + // Default to 0 when nulls exist + v = ( v || 0 ).toString( 16 ); + return v.length === 1 ? "0" + v : v; + } ).join( "" ); + }, + toString: function() { + return this._rgba[ 3 ] === 0 ? "transparent" : this.toRgbaString(); + } +} ); +color.fn.parse.prototype = color.fn; + +// Hsla conversions adapted from: +// https://code.google.com/p/maashaack/source/browse/packages/graphics/trunk/src/graphics/colors/HUE2RGB.as?r=5021 + +function hue2rgb( p, q, h ) { + h = ( h + 1 ) % 1; + if ( h * 6 < 1 ) { + return p + ( q - p ) * h * 6; + } + if ( h * 2 < 1 ) { + return q; + } + if ( h * 3 < 2 ) { + return p + ( q - p ) * ( ( 2 / 3 ) - h ) * 6; + } + return p; +} + +spaces.hsla.to = function( rgba ) { + if ( rgba[ 0 ] == null || rgba[ 1 ] == null || rgba[ 2 ] == null ) { + return [ null, null, null, rgba[ 3 ] ]; + } + var r = rgba[ 0 ] / 255, + g = rgba[ 1 ] / 255, + b = rgba[ 2 ] / 255, + a = rgba[ 3 ], + max = Math.max( r, g, b ), + min = Math.min( r, g, b ), + diff = max - min, + add = max + min, + l = add * 0.5, + h, s; + + if ( min === max ) { + h = 0; + } else if ( r === max ) { + h = ( 60 * ( g - b ) / diff ) + 360; + } else if ( g === max ) { + h = ( 60 * ( b - r ) / diff ) + 120; + } else { + h = ( 60 * ( r - g ) / diff ) + 240; + } + + // Chroma (diff) == 0 means greyscale which, by definition, saturation = 0% + // otherwise, saturation is based on the ratio of chroma (diff) to lightness (add) + if ( diff === 0 ) { + s = 0; + } else if ( l <= 0.5 ) { + s = diff / add; + } else { + s = diff / ( 2 - add ); + } + return [ Math.round( h ) % 360, s, l, a == null ? 1 : a ]; +}; + +spaces.hsla.from = function( hsla ) { + if ( hsla[ 0 ] == null || hsla[ 1 ] == null || hsla[ 2 ] == null ) { + return [ null, null, null, hsla[ 3 ] ]; + } + var h = hsla[ 0 ] / 360, + s = hsla[ 1 ], + l = hsla[ 2 ], + a = hsla[ 3 ], + q = l <= 0.5 ? l * ( 1 + s ) : l + s - l * s, + p = 2 * l - q; + + return [ + Math.round( hue2rgb( p, q, h + ( 1 / 3 ) ) * 255 ), + Math.round( hue2rgb( p, q, h ) * 255 ), + Math.round( hue2rgb( p, q, h - ( 1 / 3 ) ) * 255 ), + a + ]; +}; + +each( spaces, function( spaceName, space ) { + var props = space.props, + cache = space.cache, + to = space.to, + from = space.from; + + // Makes rgba() and hsla() + color.fn[ spaceName ] = function( value ) { + + // Generate a cache for this space if it doesn't exist + if ( to && !this[ cache ] ) { + this[ cache ] = to( this._rgba ); + } + if ( value === undefined ) { + return this[ cache ].slice(); + } + + var ret, + type = jQuery.type( value ), + arr = ( type === "array" || type === "object" ) ? value : arguments, + local = this[ cache ].slice(); + + each( props, function( key, prop ) { + var val = arr[ type === "object" ? key : prop.idx ]; + if ( val == null ) { + val = local[ prop.idx ]; + } + local[ prop.idx ] = clamp( val, prop ); + } ); + + if ( from ) { + ret = color( from( local ) ); + ret[ cache ] = local; + return ret; + } else { + return color( local ); + } + }; + + // Makes red() green() blue() alpha() hue() saturation() lightness() + each( props, function( key, prop ) { + + // Alpha is included in more than one space + if ( color.fn[ key ] ) { + return; + } + color.fn[ key ] = function( value ) { + var vtype = jQuery.type( value ), + fn = ( key === "alpha" ? ( this._hsla ? "hsla" : "rgba" ) : spaceName ), + local = this[ fn ](), + cur = local[ prop.idx ], + match; + + if ( vtype === "undefined" ) { + return cur; + } + + if ( vtype === "function" ) { + value = value.call( this, cur ); + vtype = jQuery.type( value ); + } + if ( value == null && prop.empty ) { + return this; + } + if ( vtype === "string" ) { + match = rplusequals.exec( value ); + if ( match ) { + value = cur + parseFloat( match[ 2 ] ) * ( match[ 1 ] === "+" ? 1 : -1 ); + } + } + local[ prop.idx ] = value; + return this[ fn ]( local ); + }; + } ); +} ); + +// Add cssHook and .fx.step function for each named hook. +// accept a space separated string of properties +color.hook = function( hook ) { + var hooks = hook.split( " " ); + each( hooks, function( i, hook ) { + jQuery.cssHooks[ hook ] = { + set: function( elem, value ) { + var parsed, curElem, + backgroundColor = ""; + + if ( value !== "transparent" && ( jQuery.type( value ) !== "string" || + ( parsed = stringParse( value ) ) ) ) { + value = color( parsed || value ); + if ( !support.rgba && value._rgba[ 3 ] !== 1 ) { + curElem = hook === "backgroundColor" ? elem.parentNode : elem; + while ( + ( backgroundColor === "" || backgroundColor === "transparent" ) && + curElem && curElem.style + ) { + try { + backgroundColor = jQuery.css( curElem, "backgroundColor" ); + curElem = curElem.parentNode; + } catch ( e ) { + } + } + + value = value.blend( backgroundColor && backgroundColor !== "transparent" ? + backgroundColor : + "_default" ); + } + + value = value.toRgbaString(); + } + try { + elem.style[ hook ] = value; + } catch ( e ) { + + // Wrapped to prevent IE from throwing errors on "invalid" values like + // 'auto' or 'inherit' + } + } + }; + jQuery.fx.step[ hook ] = function( fx ) { + if ( !fx.colorInit ) { + fx.start = color( fx.elem, hook ); + fx.end = color( fx.end ); + fx.colorInit = true; + } + jQuery.cssHooks[ hook ].set( fx.elem, fx.start.transition( fx.end, fx.pos ) ); + }; + } ); + +}; + +color.hook( stepHooks ); + +jQuery.cssHooks.borderColor = { + expand: function( value ) { + var expanded = {}; + + each( [ "Top", "Right", "Bottom", "Left" ], function( i, part ) { + expanded[ "border" + part + "Color" ] = value; + } ); + return expanded; + } +}; + +// Basic color names only. +// Usage of any of the other color names requires adding yourself or including +// jquery.color.svg-names.js. +colors = jQuery.Color.names = { + + // 4.1. Basic color keywords + aqua: "#00ffff", + black: "#000000", + blue: "#0000ff", + fuchsia: "#ff00ff", + gray: "#808080", + green: "#008000", + lime: "#00ff00", + maroon: "#800000", + navy: "#000080", + olive: "#808000", + purple: "#800080", + red: "#ff0000", + silver: "#c0c0c0", + teal: "#008080", + white: "#ffffff", + yellow: "#ffff00", + + // 4.2.3. "transparent" color keyword + transparent: [ null, null, null, 0 ], + + _default: "#ffffff" +}; + +} )( jQuery ); + +/******************************************************************************/ +/****************************** CLASS ANIMATIONS ******************************/ +/******************************************************************************/ +( function() { + +var classAnimationActions = [ "add", "remove", "toggle" ], + shorthandStyles = { + border: 1, + borderBottom: 1, + borderColor: 1, + borderLeft: 1, + borderRight: 1, + borderTop: 1, + borderWidth: 1, + margin: 1, + padding: 1 + }; + +$.each( + [ "borderLeftStyle", "borderRightStyle", "borderBottomStyle", "borderTopStyle" ], + function( _, prop ) { + $.fx.step[ prop ] = function( fx ) { + if ( fx.end !== "none" && !fx.setAttr || fx.pos === 1 && !fx.setAttr ) { + jQuery.style( fx.elem, prop, fx.end ); + fx.setAttr = true; + } + }; + } +); + +function getElementStyles( elem ) { + var key, len, + style = elem.ownerDocument.defaultView ? + elem.ownerDocument.defaultView.getComputedStyle( elem, null ) : + elem.currentStyle, + styles = {}; + + if ( style && style.length && style[ 0 ] && style[ style[ 0 ] ] ) { + len = style.length; + while ( len-- ) { + key = style[ len ]; + if ( typeof style[ key ] === "string" ) { + styles[ $.camelCase( key ) ] = style[ key ]; + } + } + + // Support: Opera, IE <9 + } else { + for ( key in style ) { + if ( typeof style[ key ] === "string" ) { + styles[ key ] = style[ key ]; + } + } + } + + return styles; +} + +function styleDifference( oldStyle, newStyle ) { + var diff = {}, + name, value; + + for ( name in newStyle ) { + value = newStyle[ name ]; + if ( oldStyle[ name ] !== value ) { + if ( !shorthandStyles[ name ] ) { + if ( $.fx.step[ name ] || !isNaN( parseFloat( value ) ) ) { + diff[ name ] = value; + } + } + } + } + + return diff; +} + +// Support: jQuery <1.8 +if ( !$.fn.addBack ) { + $.fn.addBack = function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter( selector ) + ); + }; +} + +$.effects.animateClass = function( value, duration, easing, callback ) { + var o = $.speed( duration, easing, callback ); + + return this.queue( function() { + var animated = $( this ), + baseClass = animated.attr( "class" ) || "", + applyClassChange, + allAnimations = o.children ? animated.find( "*" ).addBack() : animated; + + // Map the animated objects to store the original styles. + allAnimations = allAnimations.map( function() { + var el = $( this ); + return { + el: el, + start: getElementStyles( this ) + }; + } ); + + // Apply class change + applyClassChange = function() { + $.each( classAnimationActions, function( i, action ) { + if ( value[ action ] ) { + animated[ action + "Class" ]( value[ action ] ); + } + } ); + }; + applyClassChange(); + + // Map all animated objects again - calculate new styles and diff + allAnimations = allAnimations.map( function() { + this.end = getElementStyles( this.el[ 0 ] ); + this.diff = styleDifference( this.start, this.end ); + return this; + } ); + + // Apply original class + animated.attr( "class", baseClass ); + + // Map all animated objects again - this time collecting a promise + allAnimations = allAnimations.map( function() { + var styleInfo = this, + dfd = $.Deferred(), + opts = $.extend( {}, o, { + queue: false, + complete: function() { + dfd.resolve( styleInfo ); + } + } ); + + this.el.animate( this.diff, opts ); + return dfd.promise(); + } ); + + // Once all animations have completed: + $.when.apply( $, allAnimations.get() ).done( function() { + + // Set the final class + applyClassChange(); + + // For each animated element, + // clear all css properties that were animated + $.each( arguments, function() { + var el = this.el; + $.each( this.diff, function( key ) { + el.css( key, "" ); + } ); + } ); + + // This is guarnteed to be there if you use jQuery.speed() + // it also handles dequeuing the next anim... + o.complete.call( animated[ 0 ] ); + } ); + } ); +}; + +$.fn.extend( { + addClass: ( function( orig ) { + return function( classNames, speed, easing, callback ) { + return speed ? + $.effects.animateClass.call( this, + { add: classNames }, speed, easing, callback ) : + orig.apply( this, arguments ); + }; + } )( $.fn.addClass ), + + removeClass: ( function( orig ) { + return function( classNames, speed, easing, callback ) { + return arguments.length > 1 ? + $.effects.animateClass.call( this, + { remove: classNames }, speed, easing, callback ) : + orig.apply( this, arguments ); + }; + } )( $.fn.removeClass ), + + toggleClass: ( function( orig ) { + return function( classNames, force, speed, easing, callback ) { + if ( typeof force === "boolean" || force === undefined ) { + if ( !speed ) { + + // Without speed parameter + return orig.apply( this, arguments ); + } else { + return $.effects.animateClass.call( this, + ( force ? { add: classNames } : { remove: classNames } ), + speed, easing, callback ); + } + } else { + + // Without force parameter + return $.effects.animateClass.call( this, + { toggle: classNames }, force, speed, easing ); + } + }; + } )( $.fn.toggleClass ), + + switchClass: function( remove, add, speed, easing, callback ) { + return $.effects.animateClass.call( this, { + add: add, + remove: remove + }, speed, easing, callback ); + } +} ); + +} )(); + +/******************************************************************************/ +/*********************************** EFFECTS **********************************/ +/******************************************************************************/ + +( function() { + +if ( $.expr && $.expr.filters && $.expr.filters.animated ) { + $.expr.filters.animated = ( function( orig ) { + return function( elem ) { + return !!$( elem ).data( dataSpaceAnimated ) || orig( elem ); + }; + } )( $.expr.filters.animated ); +} + +if ( $.uiBackCompat !== false ) { + $.extend( $.effects, { + + // Saves a set of properties in a data storage + save: function( element, set ) { + var i = 0, length = set.length; + for ( ; i < length; i++ ) { + if ( set[ i ] !== null ) { + element.data( dataSpace + set[ i ], element[ 0 ].style[ set[ i ] ] ); + } + } + }, + + // Restores a set of previously saved properties from a data storage + restore: function( element, set ) { + var val, i = 0, length = set.length; + for ( ; i < length; i++ ) { + if ( set[ i ] !== null ) { + val = element.data( dataSpace + set[ i ] ); + element.css( set[ i ], val ); + } + } + }, + + setMode: function( el, mode ) { + if ( mode === "toggle" ) { + mode = el.is( ":hidden" ) ? "show" : "hide"; + } + return mode; + }, + + // Wraps the element around a wrapper that copies position properties + createWrapper: function( element ) { + + // If the element is already wrapped, return it + if ( element.parent().is( ".ui-effects-wrapper" ) ) { + return element.parent(); + } + + // Wrap the element + var props = { + width: element.outerWidth( true ), + height: element.outerHeight( true ), + "float": element.css( "float" ) + }, + wrapper = $( "<div></div>" ) + .addClass( "ui-effects-wrapper" ) + .css( { + fontSize: "100%", + background: "transparent", + border: "none", + margin: 0, + padding: 0 + } ), + + // Store the size in case width/height are defined in % - Fixes #5245 + size = { + width: element.width(), + height: element.height() + }, + active = document.activeElement; + + // Support: Firefox + // Firefox incorrectly exposes anonymous content + // https://bugzilla.mozilla.org/show_bug.cgi?id=561664 + try { + active.id; + } catch ( e ) { + active = document.body; + } + + element.wrap( wrapper ); + + // Fixes #7595 - Elements lose focus when wrapped. + if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) { + $( active ).trigger( "focus" ); + } + + // Hotfix for jQuery 1.4 since some change in wrap() seems to actually + // lose the reference to the wrapped element + wrapper = element.parent(); + + // Transfer positioning properties to the wrapper + if ( element.css( "position" ) === "static" ) { + wrapper.css( { position: "relative" } ); + element.css( { position: "relative" } ); + } else { + $.extend( props, { + position: element.css( "position" ), + zIndex: element.css( "z-index" ) + } ); + $.each( [ "top", "left", "bottom", "right" ], function( i, pos ) { + props[ pos ] = element.css( pos ); + if ( isNaN( parseInt( props[ pos ], 10 ) ) ) { + props[ pos ] = "auto"; + } + } ); + element.css( { + position: "relative", + top: 0, + left: 0, + right: "auto", + bottom: "auto" + } ); + } + element.css( size ); + + return wrapper.css( props ).show(); + }, + + removeWrapper: function( element ) { + var active = document.activeElement; + + if ( element.parent().is( ".ui-effects-wrapper" ) ) { + element.parent().replaceWith( element ); + + // Fixes #7595 - Elements lose focus when wrapped. + if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) { + $( active ).trigger( "focus" ); + } + } + + return element; + } + } ); +} + +$.extend( $.effects, { + version: "1.12.1", + + define: function( name, mode, effect ) { + if ( !effect ) { + effect = mode; + mode = "effect"; + } + + $.effects.effect[ name ] = effect; + $.effects.effect[ name ].mode = mode; + + return effect; + }, + + scaledDimensions: function( element, percent, direction ) { + if ( percent === 0 ) { + return { + height: 0, + width: 0, + outerHeight: 0, + outerWidth: 0 + }; + } + + var x = direction !== "horizontal" ? ( ( percent || 100 ) / 100 ) : 1, + y = direction !== "vertical" ? ( ( percent || 100 ) / 100 ) : 1; + + return { + height: element.height() * y, + width: element.width() * x, + outerHeight: element.outerHeight() * y, + outerWidth: element.outerWidth() * x + }; + + }, + + clipToBox: function( animation ) { + return { + width: animation.clip.right - animation.clip.left, + height: animation.clip.bottom - animation.clip.top, + left: animation.clip.left, + top: animation.clip.top + }; + }, + + // Injects recently queued functions to be first in line (after "inprogress") + unshift: function( element, queueLength, count ) { + var queue = element.queue(); + + if ( queueLength > 1 ) { + queue.splice.apply( queue, + [ 1, 0 ].concat( queue.splice( queueLength, count ) ) ); + } + element.dequeue(); + }, + + saveStyle: function( element ) { + element.data( dataSpaceStyle, element[ 0 ].style.cssText ); + }, + + restoreStyle: function( element ) { + element[ 0 ].style.cssText = element.data( dataSpaceStyle ) || ""; + element.removeData( dataSpaceStyle ); + }, + + mode: function( element, mode ) { + var hidden = element.is( ":hidden" ); + + if ( mode === "toggle" ) { + mode = hidden ? "show" : "hide"; + } + if ( hidden ? mode === "hide" : mode === "show" ) { + mode = "none"; + } + return mode; + }, + + // Translates a [top,left] array into a baseline value + getBaseline: function( origin, original ) { + var y, x; + + switch ( origin[ 0 ] ) { + case "top": + y = 0; + break; + case "middle": + y = 0.5; + break; + case "bottom": + y = 1; + break; + default: + y = origin[ 0 ] / original.height; + } + + switch ( origin[ 1 ] ) { + case "left": + x = 0; + break; + case "center": + x = 0.5; + break; + case "right": + x = 1; + break; + default: + x = origin[ 1 ] / original.width; + } + + return { + x: x, + y: y + }; + }, + + // Creates a placeholder element so that the original element can be made absolute + createPlaceholder: function( element ) { + var placeholder, + cssPosition = element.css( "position" ), + position = element.position(); + + // Lock in margins first to account for form elements, which + // will change margin if you explicitly set height + // see: http://jsfiddle.net/JZSMt/3/ https://bugs.webkit.org/show_bug.cgi?id=107380 + // Support: Safari + element.css( { + marginTop: element.css( "marginTop" ), + marginBottom: element.css( "marginBottom" ), + marginLeft: element.css( "marginLeft" ), + marginRight: element.css( "marginRight" ) + } ) + .outerWidth( element.outerWidth() ) + .outerHeight( element.outerHeight() ); + + if ( /^(static|relative)/.test( cssPosition ) ) { + cssPosition = "absolute"; + + placeholder = $( "<" + element[ 0 ].nodeName + ">" ).insertAfter( element ).css( { + + // Convert inline to inline block to account for inline elements + // that turn to inline block based on content (like img) + display: /^(inline|ruby)/.test( element.css( "display" ) ) ? + "inline-block" : + "block", + visibility: "hidden", + + // Margins need to be set to account for margin collapse + marginTop: element.css( "marginTop" ), + marginBottom: element.css( "marginBottom" ), + marginLeft: element.css( "marginLeft" ), + marginRight: element.css( "marginRight" ), + "float": element.css( "float" ) + } ) + .outerWidth( element.outerWidth() ) + .outerHeight( element.outerHeight() ) + .addClass( "ui-effects-placeholder" ); + + element.data( dataSpace + "placeholder", placeholder ); + } + + element.css( { + position: cssPosition, + left: position.left, + top: position.top + } ); + + return placeholder; + }, + + removePlaceholder: function( element ) { + var dataKey = dataSpace + "placeholder", + placeholder = element.data( dataKey ); + + if ( placeholder ) { + placeholder.remove(); + element.removeData( dataKey ); + } + }, + + // Removes a placeholder if it exists and restores + // properties that were modified during placeholder creation + cleanUp: function( element ) { + $.effects.restoreStyle( element ); + $.effects.removePlaceholder( element ); + }, + + setTransition: function( element, list, factor, value ) { + value = value || {}; + $.each( list, function( i, x ) { + var unit = element.cssUnit( x ); + if ( unit[ 0 ] > 0 ) { + value[ x ] = unit[ 0 ] * factor + unit[ 1 ]; + } + } ); + return value; + } +} ); + +// Return an effect options object for the given parameters: +function _normalizeArguments( effect, options, speed, callback ) { + + // Allow passing all options as the first parameter + if ( $.isPlainObject( effect ) ) { + options = effect; + effect = effect.effect; + } + + // Convert to an object + effect = { effect: effect }; + + // Catch (effect, null, ...) + if ( options == null ) { + options = {}; + } + + // Catch (effect, callback) + if ( $.isFunction( options ) ) { + callback = options; + speed = null; + options = {}; + } + + // Catch (effect, speed, ?) + if ( typeof options === "number" || $.fx.speeds[ options ] ) { + callback = speed; + speed = options; + options = {}; + } + + // Catch (effect, options, callback) + if ( $.isFunction( speed ) ) { + callback = speed; + speed = null; + } + + // Add options to effect + if ( options ) { + $.extend( effect, options ); + } + + speed = speed || options.duration; + effect.duration = $.fx.off ? 0 : + typeof speed === "number" ? speed : + speed in $.fx.speeds ? $.fx.speeds[ speed ] : + $.fx.speeds._default; + + effect.complete = callback || options.complete; + + return effect; +} + +function standardAnimationOption( option ) { + + // Valid standard speeds (nothing, number, named speed) + if ( !option || typeof option === "number" || $.fx.speeds[ option ] ) { + return true; + } + + // Invalid strings - treat as "normal" speed + if ( typeof option === "string" && !$.effects.effect[ option ] ) { + return true; + } + + // Complete callback + if ( $.isFunction( option ) ) { + return true; + } + + // Options hash (but not naming an effect) + if ( typeof option === "object" && !option.effect ) { + return true; + } + + // Didn't match any standard API + return false; +} + +$.fn.extend( { + effect: function( /* effect, options, speed, callback */ ) { + var args = _normalizeArguments.apply( this, arguments ), + effectMethod = $.effects.effect[ args.effect ], + defaultMode = effectMethod.mode, + queue = args.queue, + queueName = queue || "fx", + complete = args.complete, + mode = args.mode, + modes = [], + prefilter = function( next ) { + var el = $( this ), + normalizedMode = $.effects.mode( el, mode ) || defaultMode; + + // Sentinel for duck-punching the :animated psuedo-selector + el.data( dataSpaceAnimated, true ); + + // Save effect mode for later use, + // we can't just call $.effects.mode again later, + // as the .show() below destroys the initial state + modes.push( normalizedMode ); + + // See $.uiBackCompat inside of run() for removal of defaultMode in 1.13 + if ( defaultMode && ( normalizedMode === "show" || + ( normalizedMode === defaultMode && normalizedMode === "hide" ) ) ) { + el.show(); + } + + if ( !defaultMode || normalizedMode !== "none" ) { + $.effects.saveStyle( el ); + } + + if ( $.isFunction( next ) ) { + next(); + } + }; + + if ( $.fx.off || !effectMethod ) { + + // Delegate to the original method (e.g., .show()) if possible + if ( mode ) { + return this[ mode ]( args.duration, complete ); + } else { + return this.each( function() { + if ( complete ) { + complete.call( this ); + } + } ); + } + } + + function run( next ) { + var elem = $( this ); + + function cleanup() { + elem.removeData( dataSpaceAnimated ); + + $.effects.cleanUp( elem ); + + if ( args.mode === "hide" ) { + elem.hide(); + } + + done(); + } + + function done() { + if ( $.isFunction( complete ) ) { + complete.call( elem[ 0 ] ); + } + + if ( $.isFunction( next ) ) { + next(); + } + } + + // Override mode option on a per element basis, + // as toggle can be either show or hide depending on element state + args.mode = modes.shift(); + + if ( $.uiBackCompat !== false && !defaultMode ) { + if ( elem.is( ":hidden" ) ? mode === "hide" : mode === "show" ) { + + // Call the core method to track "olddisplay" properly + elem[ mode ](); + done(); + } else { + effectMethod.call( elem[ 0 ], args, done ); + } + } else { + if ( args.mode === "none" ) { + + // Call the core method to track "olddisplay" properly + elem[ mode ](); + done(); + } else { + effectMethod.call( elem[ 0 ], args, cleanup ); + } + } + } + + // Run prefilter on all elements first to ensure that + // any showing or hiding happens before placeholder creation, + // which ensures that any layout changes are correctly captured. + return queue === false ? + this.each( prefilter ).each( run ) : + this.queue( queueName, prefilter ).queue( queueName, run ); + }, + + show: ( function( orig ) { + return function( option ) { + if ( standardAnimationOption( option ) ) { + return orig.apply( this, arguments ); + } else { + var args = _normalizeArguments.apply( this, arguments ); + args.mode = "show"; + return this.effect.call( this, args ); + } + }; + } )( $.fn.show ), + + hide: ( function( orig ) { + return function( option ) { + if ( standardAnimationOption( option ) ) { + return orig.apply( this, arguments ); + } else { + var args = _normalizeArguments.apply( this, arguments ); + args.mode = "hide"; + return this.effect.call( this, args ); + } + }; + } )( $.fn.hide ), + + toggle: ( function( orig ) { + return function( option ) { + if ( standardAnimationOption( option ) || typeof option === "boolean" ) { + return orig.apply( this, arguments ); + } else { + var args = _normalizeArguments.apply( this, arguments ); + args.mode = "toggle"; + return this.effect.call( this, args ); + } + }; + } )( $.fn.toggle ), + + cssUnit: function( key ) { + var style = this.css( key ), + val = []; + + $.each( [ "em", "px", "%", "pt" ], function( i, unit ) { + if ( style.indexOf( unit ) > 0 ) { + val = [ parseFloat( style ), unit ]; + } + } ); + return val; + }, + + cssClip: function( clipObj ) { + if ( clipObj ) { + return this.css( "clip", "rect(" + clipObj.top + "px " + clipObj.right + "px " + + clipObj.bottom + "px " + clipObj.left + "px)" ); + } + return parseClip( this.css( "clip" ), this ); + }, + + transfer: function( options, done ) { + var element = $( this ), + target = $( options.to ), + targetFixed = target.css( "position" ) === "fixed", + body = $( "body" ), + fixTop = targetFixed ? body.scrollTop() : 0, + fixLeft = targetFixed ? body.scrollLeft() : 0, + endPosition = target.offset(), + animation = { + top: endPosition.top - fixTop, + left: endPosition.left - fixLeft, + height: target.innerHeight(), + width: target.innerWidth() + }, + startPosition = element.offset(), + transfer = $( "<div class='ui-effects-transfer'></div>" ) + .appendTo( "body" ) + .addClass( options.className ) + .css( { + top: startPosition.top - fixTop, + left: startPosition.left - fixLeft, + height: element.innerHeight(), + width: element.innerWidth(), + position: targetFixed ? "fixed" : "absolute" + } ) + .animate( animation, options.duration, options.easing, function() { + transfer.remove(); + if ( $.isFunction( done ) ) { + done(); + } + } ); + } +} ); + +function parseClip( str, element ) { + var outerWidth = element.outerWidth(), + outerHeight = element.outerHeight(), + clipRegex = /^rect\((-?\d*\.?\d*px|-?\d+%|auto),?\s*(-?\d*\.?\d*px|-?\d+%|auto),?\s*(-?\d*\.?\d*px|-?\d+%|auto),?\s*(-?\d*\.?\d*px|-?\d+%|auto)\)$/, + values = clipRegex.exec( str ) || [ "", 0, outerWidth, outerHeight, 0 ]; + + return { + top: parseFloat( values[ 1 ] ) || 0, + right: values[ 2 ] === "auto" ? outerWidth : parseFloat( values[ 2 ] ), + bottom: values[ 3 ] === "auto" ? outerHeight : parseFloat( values[ 3 ] ), + left: parseFloat( values[ 4 ] ) || 0 + }; +} + +$.fx.step.clip = function( fx ) { + if ( !fx.clipInit ) { + fx.start = $( fx.elem ).cssClip(); + if ( typeof fx.end === "string" ) { + fx.end = parseClip( fx.end, fx.elem ); + } + fx.clipInit = true; + } + + $( fx.elem ).cssClip( { + top: fx.pos * ( fx.end.top - fx.start.top ) + fx.start.top, + right: fx.pos * ( fx.end.right - fx.start.right ) + fx.start.right, + bottom: fx.pos * ( fx.end.bottom - fx.start.bottom ) + fx.start.bottom, + left: fx.pos * ( fx.end.left - fx.start.left ) + fx.start.left + } ); +}; + +} )(); + +/******************************************************************************/ +/*********************************** EASING ***********************************/ +/******************************************************************************/ + +( function() { + +// Based on easing equations from Robert Penner (http://www.robertpenner.com/easing) + +var baseEasings = {}; + +$.each( [ "Quad", "Cubic", "Quart", "Quint", "Expo" ], function( i, name ) { + baseEasings[ name ] = function( p ) { + return Math.pow( p, i + 2 ); + }; +} ); + +$.extend( baseEasings, { + Sine: function( p ) { + return 1 - Math.cos( p * Math.PI / 2 ); + }, + Circ: function( p ) { + return 1 - Math.sqrt( 1 - p * p ); + }, + Elastic: function( p ) { + return p === 0 || p === 1 ? p : + -Math.pow( 2, 8 * ( p - 1 ) ) * Math.sin( ( ( p - 1 ) * 80 - 7.5 ) * Math.PI / 15 ); + }, + Back: function( p ) { + return p * p * ( 3 * p - 2 ); + }, + Bounce: function( p ) { + var pow2, + bounce = 4; + + while ( p < ( ( pow2 = Math.pow( 2, --bounce ) ) - 1 ) / 11 ) {} + return 1 / Math.pow( 4, 3 - bounce ) - 7.5625 * Math.pow( ( pow2 * 3 - 2 ) / 22 - p, 2 ); + } +} ); + +$.each( baseEasings, function( name, easeIn ) { + $.easing[ "easeIn" + name ] = easeIn; + $.easing[ "easeOut" + name ] = function( p ) { + return 1 - easeIn( 1 - p ); + }; + $.easing[ "easeInOut" + name ] = function( p ) { + return p < 0.5 ? + easeIn( p * 2 ) / 2 : + 1 - easeIn( p * -2 + 2 ) / 2; + }; +} ); + +} )(); + +var effect = $.effects; + + +/*! + * jQuery UI Effects Blind 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: Blind Effect +//>>group: Effects +//>>description: Blinds the element. +//>>docs: http://api.jqueryui.com/blind-effect/ +//>>demos: http://jqueryui.com/effect/ + + + +var effectsEffectBlind = $.effects.define( "blind", "hide", function( options, done ) { + var map = { + up: [ "bottom", "top" ], + vertical: [ "bottom", "top" ], + down: [ "top", "bottom" ], + left: [ "right", "left" ], + horizontal: [ "right", "left" ], + right: [ "left", "right" ] + }, + element = $( this ), + direction = options.direction || "up", + start = element.cssClip(), + animate = { clip: $.extend( {}, start ) }, + placeholder = $.effects.createPlaceholder( element ); + + animate.clip[ map[ direction ][ 0 ] ] = animate.clip[ map[ direction ][ 1 ] ]; + + if ( options.mode === "show" ) { + element.cssClip( animate.clip ); + if ( placeholder ) { + placeholder.css( $.effects.clipToBox( animate ) ); + } + + animate.clip = start; + } + + if ( placeholder ) { + placeholder.animate( $.effects.clipToBox( animate ), options.duration, options.easing ); + } + + element.animate( animate, { + queue: false, + duration: options.duration, + easing: options.easing, + complete: done + } ); +} ); + + +/*! + * jQuery UI Effects Bounce 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: Bounce Effect +//>>group: Effects +//>>description: Bounces an element horizontally or vertically n times. +//>>docs: http://api.jqueryui.com/bounce-effect/ +//>>demos: http://jqueryui.com/effect/ + + + +var effectsEffectBounce = $.effects.define( "bounce", function( options, done ) { + var upAnim, downAnim, refValue, + element = $( this ), + + // Defaults: + mode = options.mode, + hide = mode === "hide", + show = mode === "show", + direction = options.direction || "up", + distance = options.distance, + times = options.times || 5, + + // Number of internal animations + anims = times * 2 + ( show || hide ? 1 : 0 ), + speed = options.duration / anims, + easing = options.easing, + + // Utility: + ref = ( direction === "up" || direction === "down" ) ? "top" : "left", + motion = ( direction === "up" || direction === "left" ), + i = 0, + + queuelen = element.queue().length; + + $.effects.createPlaceholder( element ); + + refValue = element.css( ref ); + + // Default distance for the BIGGEST bounce is the outer Distance / 3 + if ( !distance ) { + distance = element[ ref === "top" ? "outerHeight" : "outerWidth" ]() / 3; + } + + if ( show ) { + downAnim = { opacity: 1 }; + downAnim[ ref ] = refValue; + + // If we are showing, force opacity 0 and set the initial position + // then do the "first" animation + element + .css( "opacity", 0 ) + .css( ref, motion ? -distance * 2 : distance * 2 ) + .animate( downAnim, speed, easing ); + } + + // Start at the smallest distance if we are hiding + if ( hide ) { + distance = distance / Math.pow( 2, times - 1 ); + } + + downAnim = {}; + downAnim[ ref ] = refValue; + + // Bounces up/down/left/right then back to 0 -- times * 2 animations happen here + for ( ; i < times; i++ ) { + upAnim = {}; + upAnim[ ref ] = ( motion ? "-=" : "+=" ) + distance; + + element + .animate( upAnim, speed, easing ) + .animate( downAnim, speed, easing ); + + distance = hide ? distance * 2 : distance / 2; + } + + // Last Bounce when Hiding + if ( hide ) { + upAnim = { opacity: 0 }; + upAnim[ ref ] = ( motion ? "-=" : "+=" ) + distance; + + element.animate( upAnim, speed, easing ); + } + + element.queue( done ); + + $.effects.unshift( element, queuelen, anims + 1 ); +} ); + + +/*! + * jQuery UI Effects Clip 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: Clip Effect +//>>group: Effects +//>>description: Clips the element on and off like an old TV. +//>>docs: http://api.jqueryui.com/clip-effect/ +//>>demos: http://jqueryui.com/effect/ + + + +var effectsEffectClip = $.effects.define( "clip", "hide", function( options, done ) { + var start, + animate = {}, + element = $( this ), + direction = options.direction || "vertical", + both = direction === "both", + horizontal = both || direction === "horizontal", + vertical = both || direction === "vertical"; + + start = element.cssClip(); + animate.clip = { + top: vertical ? ( start.bottom - start.top ) / 2 : start.top, + right: horizontal ? ( start.right - start.left ) / 2 : start.right, + bottom: vertical ? ( start.bottom - start.top ) / 2 : start.bottom, + left: horizontal ? ( start.right - start.left ) / 2 : start.left + }; + + $.effects.createPlaceholder( element ); + + if ( options.mode === "show" ) { + element.cssClip( animate.clip ); + animate.clip = start; + } + + element.animate( animate, { + queue: false, + duration: options.duration, + easing: options.easing, + complete: done + } ); + +} ); + + +/*! + * jQuery UI Effects Drop 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: Drop Effect +//>>group: Effects +//>>description: Moves an element in one direction and hides it at the same time. +//>>docs: http://api.jqueryui.com/drop-effect/ +//>>demos: http://jqueryui.com/effect/ + + + +var effectsEffectDrop = $.effects.define( "drop", "hide", function( options, done ) { + + var distance, + element = $( this ), + mode = options.mode, + show = mode === "show", + direction = options.direction || "left", + ref = ( direction === "up" || direction === "down" ) ? "top" : "left", + motion = ( direction === "up" || direction === "left" ) ? "-=" : "+=", + oppositeMotion = ( motion === "+=" ) ? "-=" : "+=", + animation = { + opacity: 0 + }; + + $.effects.createPlaceholder( element ); + + distance = options.distance || + element[ ref === "top" ? "outerHeight" : "outerWidth" ]( true ) / 2; + + animation[ ref ] = motion + distance; + + if ( show ) { + element.css( animation ); + + animation[ ref ] = oppositeMotion + distance; + animation.opacity = 1; + } + + // Animate + element.animate( animation, { + queue: false, + duration: options.duration, + easing: options.easing, + complete: done + } ); +} ); + + +/*! + * jQuery UI Effects Explode 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: Explode Effect +//>>group: Effects +// jscs:disable maximumLineLength +//>>description: Explodes an element in all directions into n pieces. Implodes an element to its original wholeness. +// jscs:enable maximumLineLength +//>>docs: http://api.jqueryui.com/explode-effect/ +//>>demos: http://jqueryui.com/effect/ + + + +var effectsEffectExplode = $.effects.define( "explode", "hide", function( options, done ) { + + var i, j, left, top, mx, my, + rows = options.pieces ? Math.round( Math.sqrt( options.pieces ) ) : 3, + cells = rows, + element = $( this ), + mode = options.mode, + show = mode === "show", + + // Show and then visibility:hidden the element before calculating offset + offset = element.show().css( "visibility", "hidden" ).offset(), + + // Width and height of a piece + width = Math.ceil( element.outerWidth() / cells ), + height = Math.ceil( element.outerHeight() / rows ), + pieces = []; + + // Children animate complete: + function childComplete() { + pieces.push( this ); + if ( pieces.length === rows * cells ) { + animComplete(); + } + } + + // Clone the element for each row and cell. + for ( i = 0; i < rows; i++ ) { // ===> + top = offset.top + i * height; + my = i - ( rows - 1 ) / 2; + + for ( j = 0; j < cells; j++ ) { // ||| + left = offset.left + j * width; + mx = j - ( cells - 1 ) / 2; + + // Create a clone of the now hidden main element that will be absolute positioned + // within a wrapper div off the -left and -top equal to size of our pieces + element + .clone() + .appendTo( "body" ) + .wrap( "<div></div>" ) + .css( { + position: "absolute", + visibility: "visible", + left: -j * width, + top: -i * height + } ) + + // Select the wrapper - make it overflow: hidden and absolute positioned based on + // where the original was located +left and +top equal to the size of pieces + .parent() + .addClass( "ui-effects-explode" ) + .css( { + position: "absolute", + overflow: "hidden", + width: width, + height: height, + left: left + ( show ? mx * width : 0 ), + top: top + ( show ? my * height : 0 ), + opacity: show ? 0 : 1 + } ) + .animate( { + left: left + ( show ? 0 : mx * width ), + top: top + ( show ? 0 : my * height ), + opacity: show ? 1 : 0 + }, options.duration || 500, options.easing, childComplete ); + } + } + + function animComplete() { + element.css( { + visibility: "visible" + } ); + $( pieces ).remove(); + done(); + } +} ); + + +/*! + * jQuery UI Effects Fade 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: Fade Effect +//>>group: Effects +//>>description: Fades the element. +//>>docs: http://api.jqueryui.com/fade-effect/ +//>>demos: http://jqueryui.com/effect/ + + + +var effectsEffectFade = $.effects.define( "fade", "toggle", function( options, done ) { + var show = options.mode === "show"; + + $( this ) + .css( "opacity", show ? 0 : 1 ) + .animate( { + opacity: show ? 1 : 0 + }, { + queue: false, + duration: options.duration, + easing: options.easing, + complete: done + } ); +} ); + + +/*! + * jQuery UI Effects Fold 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: Fold Effect +//>>group: Effects +//>>description: Folds an element first horizontally and then vertically. +//>>docs: http://api.jqueryui.com/fold-effect/ +//>>demos: http://jqueryui.com/effect/ + + + +var effectsEffectFold = $.effects.define( "fold", "hide", function( options, done ) { + + // Create element + var element = $( this ), + mode = options.mode, + show = mode === "show", + hide = mode === "hide", + size = options.size || 15, + percent = /([0-9]+)%/.exec( size ), + horizFirst = !!options.horizFirst, + ref = horizFirst ? [ "right", "bottom" ] : [ "bottom", "right" ], + duration = options.duration / 2, + + placeholder = $.effects.createPlaceholder( element ), + + start = element.cssClip(), + animation1 = { clip: $.extend( {}, start ) }, + animation2 = { clip: $.extend( {}, start ) }, + + distance = [ start[ ref[ 0 ] ], start[ ref[ 1 ] ] ], + + queuelen = element.queue().length; + + if ( percent ) { + size = parseInt( percent[ 1 ], 10 ) / 100 * distance[ hide ? 0 : 1 ]; + } + animation1.clip[ ref[ 0 ] ] = size; + animation2.clip[ ref[ 0 ] ] = size; + animation2.clip[ ref[ 1 ] ] = 0; + + if ( show ) { + element.cssClip( animation2.clip ); + if ( placeholder ) { + placeholder.css( $.effects.clipToBox( animation2 ) ); + } + + animation2.clip = start; + } + + // Animate + element + .queue( function( next ) { + if ( placeholder ) { + placeholder + .animate( $.effects.clipToBox( animation1 ), duration, options.easing ) + .animate( $.effects.clipToBox( animation2 ), duration, options.easing ); + } + + next(); + } ) + .animate( animation1, duration, options.easing ) + .animate( animation2, duration, options.easing ) + .queue( done ); + + $.effects.unshift( element, queuelen, 4 ); +} ); + + +/*! + * jQuery UI Effects Highlight 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: Highlight Effect +//>>group: Effects +//>>description: Highlights the background of an element in a defined color for a custom duration. +//>>docs: http://api.jqueryui.com/highlight-effect/ +//>>demos: http://jqueryui.com/effect/ + + + +var effectsEffectHighlight = $.effects.define( "highlight", "show", function( options, done ) { + var element = $( this ), + animation = { + backgroundColor: element.css( "backgroundColor" ) + }; + + if ( options.mode === "hide" ) { + animation.opacity = 0; + } + + $.effects.saveStyle( element ); + + element + .css( { + backgroundImage: "none", + backgroundColor: options.color || "#ffff99" + } ) + .animate( animation, { + queue: false, + duration: options.duration, + easing: options.easing, + complete: done + } ); +} ); + + +/*! + * jQuery UI Effects Size 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: Size Effect +//>>group: Effects +//>>description: Resize an element to a specified width and height. +//>>docs: http://api.jqueryui.com/size-effect/ +//>>demos: http://jqueryui.com/effect/ + + + +var effectsEffectSize = $.effects.define( "size", function( options, done ) { + + // Create element + var baseline, factor, temp, + element = $( this ), + + // Copy for children + cProps = [ "fontSize" ], + vProps = [ "borderTopWidth", "borderBottomWidth", "paddingTop", "paddingBottom" ], + hProps = [ "borderLeftWidth", "borderRightWidth", "paddingLeft", "paddingRight" ], + + // Set options + mode = options.mode, + restore = mode !== "effect", + scale = options.scale || "both", + origin = options.origin || [ "middle", "center" ], + position = element.css( "position" ), + pos = element.position(), + original = $.effects.scaledDimensions( element ), + from = options.from || original, + to = options.to || $.effects.scaledDimensions( element, 0 ); + + $.effects.createPlaceholder( element ); + + if ( mode === "show" ) { + temp = from; + from = to; + to = temp; + } + + // Set scaling factor + factor = { + from: { + y: from.height / original.height, + x: from.width / original.width + }, + to: { + y: to.height / original.height, + x: to.width / original.width + } + }; + + // Scale the css box + if ( scale === "box" || scale === "both" ) { + + // Vertical props scaling + if ( factor.from.y !== factor.to.y ) { + from = $.effects.setTransition( element, vProps, factor.from.y, from ); + to = $.effects.setTransition( element, vProps, factor.to.y, to ); + } + + // Horizontal props scaling + if ( factor.from.x !== factor.to.x ) { + from = $.effects.setTransition( element, hProps, factor.from.x, from ); + to = $.effects.setTransition( element, hProps, factor.to.x, to ); + } + } + + // Scale the content + if ( scale === "content" || scale === "both" ) { + + // Vertical props scaling + if ( factor.from.y !== factor.to.y ) { + from = $.effects.setTransition( element, cProps, factor.from.y, from ); + to = $.effects.setTransition( element, cProps, factor.to.y, to ); + } + } + + // Adjust the position properties based on the provided origin points + if ( origin ) { + baseline = $.effects.getBaseline( origin, original ); + from.top = ( original.outerHeight - from.outerHeight ) * baseline.y + pos.top; + from.left = ( original.outerWidth - from.outerWidth ) * baseline.x + pos.left; + to.top = ( original.outerHeight - to.outerHeight ) * baseline.y + pos.top; + to.left = ( original.outerWidth - to.outerWidth ) * baseline.x + pos.left; + } + element.css( from ); + + // Animate the children if desired + if ( scale === "content" || scale === "both" ) { + + vProps = vProps.concat( [ "marginTop", "marginBottom" ] ).concat( cProps ); + hProps = hProps.concat( [ "marginLeft", "marginRight" ] ); + + // Only animate children with width attributes specified + // TODO: is this right? should we include anything with css width specified as well + element.find( "*[width]" ).each( function() { + var child = $( this ), + childOriginal = $.effects.scaledDimensions( child ), + childFrom = { + height: childOriginal.height * factor.from.y, + width: childOriginal.width * factor.from.x, + outerHeight: childOriginal.outerHeight * factor.from.y, + outerWidth: childOriginal.outerWidth * factor.from.x + }, + childTo = { + height: childOriginal.height * factor.to.y, + width: childOriginal.width * factor.to.x, + outerHeight: childOriginal.height * factor.to.y, + outerWidth: childOriginal.width * factor.to.x + }; + + // Vertical props scaling + if ( factor.from.y !== factor.to.y ) { + childFrom = $.effects.setTransition( child, vProps, factor.from.y, childFrom ); + childTo = $.effects.setTransition( child, vProps, factor.to.y, childTo ); + } + + // Horizontal props scaling + if ( factor.from.x !== factor.to.x ) { + childFrom = $.effects.setTransition( child, hProps, factor.from.x, childFrom ); + childTo = $.effects.setTransition( child, hProps, factor.to.x, childTo ); + } + + if ( restore ) { + $.effects.saveStyle( child ); + } + + // Animate children + child.css( childFrom ); + child.animate( childTo, options.duration, options.easing, function() { + + // Restore children + if ( restore ) { + $.effects.restoreStyle( child ); + } + } ); + } ); + } + + // Animate + element.animate( to, { + queue: false, + duration: options.duration, + easing: options.easing, + complete: function() { + + var offset = element.offset(); + + if ( to.opacity === 0 ) { + element.css( "opacity", from.opacity ); + } + + if ( !restore ) { + element + .css( "position", position === "static" ? "relative" : position ) + .offset( offset ); + + // Need to save style here so that automatic style restoration + // doesn't restore to the original styles from before the animation. + $.effects.saveStyle( element ); + } + + done(); + } + } ); + +} ); + + +/*! + * jQuery UI Effects Scale 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: Scale Effect +//>>group: Effects +//>>description: Grows or shrinks an element and its content. +//>>docs: http://api.jqueryui.com/scale-effect/ +//>>demos: http://jqueryui.com/effect/ + + + +var effectsEffectScale = $.effects.define( "scale", function( options, done ) { + + // Create element + var el = $( this ), + mode = options.mode, + percent = parseInt( options.percent, 10 ) || + ( parseInt( options.percent, 10 ) === 0 ? 0 : ( mode !== "effect" ? 0 : 100 ) ), + + newOptions = $.extend( true, { + from: $.effects.scaledDimensions( el ), + to: $.effects.scaledDimensions( el, percent, options.direction || "both" ), + origin: options.origin || [ "middle", "center" ] + }, options ); + + // Fade option to support puff + if ( options.fade ) { + newOptions.from.opacity = 1; + newOptions.to.opacity = 0; + } + + $.effects.effect.size.call( this, newOptions, done ); +} ); + + +/*! + * jQuery UI Effects Puff 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: Puff Effect +//>>group: Effects +//>>description: Creates a puff effect by scaling the element up and hiding it at the same time. +//>>docs: http://api.jqueryui.com/puff-effect/ +//>>demos: http://jqueryui.com/effect/ + + + +var effectsEffectPuff = $.effects.define( "puff", "hide", function( options, done ) { + var newOptions = $.extend( true, {}, options, { + fade: true, + percent: parseInt( options.percent, 10 ) || 150 + } ); + + $.effects.effect.scale.call( this, newOptions, done ); +} ); + + +/*! + * jQuery UI Effects Pulsate 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: Pulsate Effect +//>>group: Effects +//>>description: Pulsates an element n times by changing the opacity to zero and back. +//>>docs: http://api.jqueryui.com/pulsate-effect/ +//>>demos: http://jqueryui.com/effect/ + + + +var effectsEffectPulsate = $.effects.define( "pulsate", "show", function( options, done ) { + var element = $( this ), + mode = options.mode, + show = mode === "show", + hide = mode === "hide", + showhide = show || hide, + + // Showing or hiding leaves off the "last" animation + anims = ( ( options.times || 5 ) * 2 ) + ( showhide ? 1 : 0 ), + duration = options.duration / anims, + animateTo = 0, + i = 1, + queuelen = element.queue().length; + + if ( show || !element.is( ":visible" ) ) { + element.css( "opacity", 0 ).show(); + animateTo = 1; + } + + // Anims - 1 opacity "toggles" + for ( ; i < anims; i++ ) { + element.animate( { opacity: animateTo }, duration, options.easing ); + animateTo = 1 - animateTo; + } + + element.animate( { opacity: animateTo }, duration, options.easing ); + + element.queue( done ); + + $.effects.unshift( element, queuelen, anims + 1 ); +} ); + + +/*! + * jQuery UI Effects Shake 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: Shake Effect +//>>group: Effects +//>>description: Shakes an element horizontally or vertically n times. +//>>docs: http://api.jqueryui.com/shake-effect/ +//>>demos: http://jqueryui.com/effect/ + + + +var effectsEffectShake = $.effects.define( "shake", function( options, done ) { + + var i = 1, + element = $( this ), + direction = options.direction || "left", + distance = options.distance || 20, + times = options.times || 3, + anims = times * 2 + 1, + speed = Math.round( options.duration / anims ), + ref = ( direction === "up" || direction === "down" ) ? "top" : "left", + positiveMotion = ( direction === "up" || direction === "left" ), + animation = {}, + animation1 = {}, + animation2 = {}, + + queuelen = element.queue().length; + + $.effects.createPlaceholder( element ); + + // Animation + animation[ ref ] = ( positiveMotion ? "-=" : "+=" ) + distance; + animation1[ ref ] = ( positiveMotion ? "+=" : "-=" ) + distance * 2; + animation2[ ref ] = ( positiveMotion ? "-=" : "+=" ) + distance * 2; + + // Animate + element.animate( animation, speed, options.easing ); + + // Shakes + for ( ; i < times; i++ ) { + element + .animate( animation1, speed, options.easing ) + .animate( animation2, speed, options.easing ); + } + + element + .animate( animation1, speed, options.easing ) + .animate( animation, speed / 2, options.easing ) + .queue( done ); + + $.effects.unshift( element, queuelen, anims + 1 ); +} ); + + +/*! + * jQuery UI Effects Slide 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: Slide Effect +//>>group: Effects +//>>description: Slides an element in and out of the viewport. +//>>docs: http://api.jqueryui.com/slide-effect/ +//>>demos: http://jqueryui.com/effect/ + + + +var effectsEffectSlide = $.effects.define( "slide", "show", function( options, done ) { + var startClip, startRef, + element = $( this ), + map = { + up: [ "bottom", "top" ], + down: [ "top", "bottom" ], + left: [ "right", "left" ], + right: [ "left", "right" ] + }, + mode = options.mode, + direction = options.direction || "left", + ref = ( direction === "up" || direction === "down" ) ? "top" : "left", + positiveMotion = ( direction === "up" || direction === "left" ), + distance = options.distance || + element[ ref === "top" ? "outerHeight" : "outerWidth" ]( true ), + animation = {}; + + $.effects.createPlaceholder( element ); + + startClip = element.cssClip(); + startRef = element.position()[ ref ]; + + // Define hide animation + animation[ ref ] = ( positiveMotion ? -1 : 1 ) * distance + startRef; + animation.clip = element.cssClip(); + animation.clip[ map[ direction ][ 1 ] ] = animation.clip[ map[ direction ][ 0 ] ]; + + // Reverse the animation if we're showing + if ( mode === "show" ) { + element.cssClip( animation.clip ); + element.css( ref, animation[ ref ] ); + animation.clip = startClip; + animation[ ref ] = startRef; + } + + // Actually animate + element.animate( animation, { + queue: false, + duration: options.duration, + easing: options.easing, + complete: done + } ); +} ); + + +/*! + * jQuery UI Effects Transfer 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: Transfer Effect +//>>group: Effects +//>>description: Displays a transfer effect from one element to another. +//>>docs: http://api.jqueryui.com/transfer-effect/ +//>>demos: http://jqueryui.com/effect/ + + + +var effect; +if ( $.uiBackCompat !== false ) { + effect = $.effects.define( "transfer", function( options, done ) { + $( this ).transfer( options, done ); + } ); +} +var effectsEffectTransfer = effect; + + +/*! + * jQuery UI Focusable 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: :focusable Selector +//>>group: Core +//>>description: Selects elements which can be focused. +//>>docs: http://api.jqueryui.com/focusable-selector/ + + + +// Selectors +$.ui.focusable = function( element, hasTabindex ) { + var map, mapName, img, focusableIfVisible, fieldset, + nodeName = element.nodeName.toLowerCase(); + + if ( "area" === nodeName ) { + map = element.parentNode; + mapName = map.name; + if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) { + return false; + } + img = $( "img[usemap='#" + mapName + "']" ); + return img.length > 0 && img.is( ":visible" ); + } + + if ( /^(input|select|textarea|button|object)$/.test( nodeName ) ) { + focusableIfVisible = !element.disabled; + + if ( focusableIfVisible ) { + + // Form controls within a disabled fieldset are disabled. + // However, controls within the fieldset's legend do not get disabled. + // Since controls generally aren't placed inside legends, we skip + // this portion of the check. + fieldset = $( element ).closest( "fieldset" )[ 0 ]; + if ( fieldset ) { + focusableIfVisible = !fieldset.disabled; + } + } + } else if ( "a" === nodeName ) { + focusableIfVisible = element.href || hasTabindex; + } else { + focusableIfVisible = hasTabindex; + } + + return focusableIfVisible && $( element ).is( ":visible" ) && visible( $( element ) ); +}; + +// Support: IE 8 only +// IE 8 doesn't resolve inherit to visible/hidden for computed values +function visible( element ) { + var visibility = element.css( "visibility" ); + while ( visibility === "inherit" ) { + element = element.parent(); + visibility = element.css( "visibility" ); + } + return visibility !== "hidden"; +} + +$.extend( $.expr[ ":" ], { + focusable: function( element ) { + return $.ui.focusable( element, $.attr( element, "tabindex" ) != null ); + } +} ); + +var focusable = $.ui.focusable; + + + + +// Support: IE8 Only +// IE8 does not support the form attribute and when it is supplied. It overwrites the form prop +// with a string, so we need to find the proper form. +var form = $.fn.form = function() { + return typeof this[ 0 ].form === "string" ? this.closest( "form" ) : $( this[ 0 ].form ); +}; + + +/*! + * jQuery UI Form Reset Mixin 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: Form Reset Mixin +//>>group: Core +//>>description: Refresh input widgets when their form is reset +//>>docs: http://api.jqueryui.com/form-reset-mixin/ + + + +var formResetMixin = $.ui.formResetMixin = { + _formResetHandler: function() { + var form = $( this ); + + // Wait for the form reset to actually happen before refreshing + setTimeout( function() { + var instances = form.data( "ui-form-reset-instances" ); + $.each( instances, function() { + this.refresh(); + } ); + } ); + }, + + _bindFormResetHandler: function() { + this.form = this.element.form(); + if ( !this.form.length ) { + return; + } + + var instances = this.form.data( "ui-form-reset-instances" ) || []; + if ( !instances.length ) { + + // We don't use _on() here because we use a single event handler per form + this.form.on( "reset.ui-form-reset", this._formResetHandler ); + } + instances.push( this ); + this.form.data( "ui-form-reset-instances", instances ); + }, + + _unbindFormResetHandler: function() { + if ( !this.form.length ) { + return; + } + + var instances = this.form.data( "ui-form-reset-instances" ); + instances.splice( $.inArray( this, instances ), 1 ); + if ( instances.length ) { + this.form.data( "ui-form-reset-instances", instances ); + } else { + this.form + .removeData( "ui-form-reset-instances" ) + .off( "reset.ui-form-reset" ); + } + } +}; + + +/*! + * jQuery UI Support for jQuery core 1.7.x 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + */ + +//>>label: jQuery 1.7 Support +//>>group: Core +//>>description: Support version 1.7.x of jQuery core + + + +// Support: jQuery 1.7 only +// Not a great way to check versions, but since we only support 1.7+ and only +// need to detect <1.8, this is a simple check that should suffice. Checking +// for "1.7." would be a bit safer, but the version string is 1.7, not 1.7.0 +// and we'll never reach 1.70.0 (if we do, we certainly won't be supporting +// 1.7 anymore). See #11197 for why we're not using feature detection. +if ( $.fn.jquery.substring( 0, 3 ) === "1.7" ) { + + // Setters for .innerWidth(), .innerHeight(), .outerWidth(), .outerHeight() + // Unlike jQuery Core 1.8+, these only support numeric values to set the + // dimensions in pixels + $.each( [ "Width", "Height" ], function( i, name ) { + var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ], + type = name.toLowerCase(), + orig = { + innerWidth: $.fn.innerWidth, + innerHeight: $.fn.innerHeight, + outerWidth: $.fn.outerWidth, + outerHeight: $.fn.outerHeight + }; + + function reduce( elem, size, border, margin ) { + $.each( side, function() { + size -= parseFloat( $.css( elem, "padding" + this ) ) || 0; + if ( border ) { + size -= parseFloat( $.css( elem, "border" + this + "Width" ) ) || 0; + } + if ( margin ) { + size -= parseFloat( $.css( elem, "margin" + this ) ) || 0; + } + } ); + return size; + } + + $.fn[ "inner" + name ] = function( size ) { + if ( size === undefined ) { + return orig[ "inner" + name ].call( this ); + } + + return this.each( function() { + $( this ).css( type, reduce( this, size ) + "px" ); + } ); + }; + + $.fn[ "outer" + name ] = function( size, margin ) { + if ( typeof size !== "number" ) { + return orig[ "outer" + name ].call( this, size ); + } + + return this.each( function() { + $( this ).css( type, reduce( this, size, true, margin ) + "px" ); + } ); + }; + } ); + + $.fn.addBack = function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter( selector ) + ); + }; +} + +; +/*! + * jQuery UI Keycode 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: Keycode +//>>group: Core +//>>description: Provide keycodes as keynames +//>>docs: http://api.jqueryui.com/jQuery.ui.keyCode/ + + +var keycode = $.ui.keyCode = { + BACKSPACE: 8, + COMMA: 188, + DELETE: 46, + DOWN: 40, + END: 35, + ENTER: 13, + ESCAPE: 27, + HOME: 36, + LEFT: 37, + PAGE_DOWN: 34, + PAGE_UP: 33, + PERIOD: 190, + RIGHT: 39, + SPACE: 32, + TAB: 9, + UP: 38 +}; + + + + +// Internal use only +var escapeSelector = $.ui.escapeSelector = ( function() { + var selectorEscape = /([!"#$%&'()*+,./:;<=>?@[\]^`{|}~])/g; + return function( selector ) { + return selector.replace( selectorEscape, "\\$1" ); + }; +} )(); + + +/*! + * jQuery UI Labels 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: labels +//>>group: Core +//>>description: Find all the labels associated with a given input +//>>docs: http://api.jqueryui.com/labels/ + + + +var labels = $.fn.labels = function() { + var ancestor, selector, id, labels, ancestors; + + // Check control.labels first + if ( this[ 0 ].labels && this[ 0 ].labels.length ) { + return this.pushStack( this[ 0 ].labels ); + } + + // Support: IE <= 11, FF <= 37, Android <= 2.3 only + // Above browsers do not support control.labels. Everything below is to support them + // as well as document fragments. control.labels does not work on document fragments + labels = this.eq( 0 ).parents( "label" ); + + // Look for the label based on the id + id = this.attr( "id" ); + if ( id ) { + + // We don't search against the document in case the element + // is disconnected from the DOM + ancestor = this.eq( 0 ).parents().last(); + + // Get a full set of top level ancestors + ancestors = ancestor.add( ancestor.length ? ancestor.siblings() : this.siblings() ); + + // Create a selector for the label based on the id + selector = "label[for='" + $.ui.escapeSelector( id ) + "']"; + + labels = labels.add( ancestors.find( selector ).addBack( selector ) ); + + } + + // Return whatever we have found for labels + return this.pushStack( labels ); +}; + + +/*! + * jQuery UI Scroll Parent 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: scrollParent +//>>group: Core +//>>description: Get the closest ancestor element that is scrollable. +//>>docs: http://api.jqueryui.com/scrollParent/ + + + +var scrollParent = $.fn.scrollParent = function( includeHidden ) { + var position = this.css( "position" ), + excludeStaticParent = position === "absolute", + overflowRegex = includeHidden ? /(auto|scroll|hidden)/ : /(auto|scroll)/, + scrollParent = this.parents().filter( function() { + var parent = $( this ); + if ( excludeStaticParent && parent.css( "position" ) === "static" ) { + return false; + } + return overflowRegex.test( parent.css( "overflow" ) + parent.css( "overflow-y" ) + + parent.css( "overflow-x" ) ); + } ).eq( 0 ); + + return position === "fixed" || !scrollParent.length ? + $( this[ 0 ].ownerDocument || document ) : + scrollParent; +}; + + +/*! + * jQuery UI Tabbable 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: :tabbable Selector +//>>group: Core +//>>description: Selects elements which can be tabbed to. +//>>docs: http://api.jqueryui.com/tabbable-selector/ + + + +var tabbable = $.extend( $.expr[ ":" ], { + tabbable: function( element ) { + var tabIndex = $.attr( element, "tabindex" ), + hasTabindex = tabIndex != null; + return ( !hasTabindex || tabIndex >= 0 ) && $.ui.focusable( element, hasTabindex ); + } +} ); + + +/*! + * jQuery UI Unique ID 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: uniqueId +//>>group: Core +//>>description: Functions to generate and remove uniqueId's +//>>docs: http://api.jqueryui.com/uniqueId/ + + + +var uniqueId = $.fn.extend( { + uniqueId: ( function() { + var uuid = 0; + + return function() { + return this.each( function() { + if ( !this.id ) { + this.id = "ui-id-" + ( ++uuid ); + } + } ); + }; + } )(), + + removeUniqueId: function() { + return this.each( function() { + if ( /^ui-id-\d+$/.test( this.id ) ) { + $( this ).removeAttr( "id" ); + } + } ); + } +} ); + + +/*! + * jQuery UI Accordion 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: Accordion +//>>group: Widgets +// jscs:disable maximumLineLength +//>>description: Displays collapsible content panels for presenting information in a limited amount of space. +// jscs:enable maximumLineLength +//>>docs: http://api.jqueryui.com/accordion/ +//>>demos: http://jqueryui.com/accordion/ +//>>css.structure: ../../themes/base/core.css +//>>css.structure: ../../themes/base/accordion.css +//>>css.theme: ../../themes/base/theme.css + + + +var widgetsAccordion = $.widget( "ui.accordion", { + version: "1.12.1", + options: { + active: 0, + animate: {}, + classes: { + "ui-accordion-header": "ui-corner-top", + "ui-accordion-header-collapsed": "ui-corner-all", + "ui-accordion-content": "ui-corner-bottom" + }, + collapsible: false, + event: "click", + header: "> li > :first-child, > :not(li):even", + heightStyle: "auto", + icons: { + activeHeader: "ui-icon-triangle-1-s", + header: "ui-icon-triangle-1-e" + }, + + // Callbacks + activate: null, + beforeActivate: null + }, + + hideProps: { + borderTopWidth: "hide", + borderBottomWidth: "hide", + paddingTop: "hide", + paddingBottom: "hide", + height: "hide" + }, + + showProps: { + borderTopWidth: "show", + borderBottomWidth: "show", + paddingTop: "show", + paddingBottom: "show", + height: "show" + }, + + _create: function() { + var options = this.options; + + this.prevShow = this.prevHide = $(); + this._addClass( "ui-accordion", "ui-widget ui-helper-reset" ); + this.element.attr( "role", "tablist" ); + + // Don't allow collapsible: false and active: false / null + if ( !options.collapsible && ( options.active === false || options.active == null ) ) { + options.active = 0; + } + + this._processPanels(); + + // handle negative values + if ( options.active < 0 ) { + options.active += this.headers.length; + } + this._refresh(); + }, + + _getCreateEventData: function() { + return { + header: this.active, + panel: !this.active.length ? $() : this.active.next() + }; + }, + + _createIcons: function() { + var icon, children, + icons = this.options.icons; + + if ( icons ) { + icon = $( "<span>" ); + this._addClass( icon, "ui-accordion-header-icon", "ui-icon " + icons.header ); + icon.prependTo( this.headers ); + children = this.active.children( ".ui-accordion-header-icon" ); + this._removeClass( children, icons.header ) + ._addClass( children, null, icons.activeHeader ) + ._addClass( this.headers, "ui-accordion-icons" ); + } + }, + + _destroyIcons: function() { + this._removeClass( this.headers, "ui-accordion-icons" ); + this.headers.children( ".ui-accordion-header-icon" ).remove(); + }, + + _destroy: function() { + var contents; + + // Clean up main element + this.element.removeAttr( "role" ); + + // Clean up headers + this.headers + .removeAttr( "role aria-expanded aria-selected aria-controls tabIndex" ) + .removeUniqueId(); + + this._destroyIcons(); + + // Clean up content panels + contents = this.headers.next() + .css( "display", "" ) + .removeAttr( "role aria-hidden aria-labelledby" ) + .removeUniqueId(); + + if ( this.options.heightStyle !== "content" ) { + contents.css( "height", "" ); + } + }, + + _setOption: function( key, value ) { + if ( key === "active" ) { + + // _activate() will handle invalid values and update this.options + this._activate( value ); + return; + } + + if ( key === "event" ) { + if ( this.options.event ) { + this._off( this.headers, this.options.event ); + } + this._setupEvents( value ); + } + + this._super( key, value ); + + // Setting collapsible: false while collapsed; open first panel + if ( key === "collapsible" && !value && this.options.active === false ) { + this._activate( 0 ); + } + + if ( key === "icons" ) { + this._destroyIcons(); + if ( value ) { + this._createIcons(); + } + } + }, + + _setOptionDisabled: function( value ) { + this._super( value ); + + this.element.attr( "aria-disabled", value ); + + // Support: IE8 Only + // #5332 / #6059 - opacity doesn't cascade to positioned elements in IE + // so we need to add the disabled class to the headers and panels + this._toggleClass( null, "ui-state-disabled", !!value ); + this._toggleClass( this.headers.add( this.headers.next() ), null, "ui-state-disabled", + !!value ); + }, + + _keydown: function( event ) { + if ( event.altKey || event.ctrlKey ) { + return; + } + + var keyCode = $.ui.keyCode, + length = this.headers.length, + currentIndex = this.headers.index( event.target ), + toFocus = false; + + switch ( event.keyCode ) { + case keyCode.RIGHT: + case keyCode.DOWN: + toFocus = this.headers[ ( currentIndex + 1 ) % length ]; + break; + case keyCode.LEFT: + case keyCode.UP: + toFocus = this.headers[ ( currentIndex - 1 + length ) % length ]; + break; + case keyCode.SPACE: + case keyCode.ENTER: + this._eventHandler( event ); + break; + case keyCode.HOME: + toFocus = this.headers[ 0 ]; + break; + case keyCode.END: + toFocus = this.headers[ length - 1 ]; + break; + } + + if ( toFocus ) { + $( event.target ).attr( "tabIndex", -1 ); + $( toFocus ).attr( "tabIndex", 0 ); + $( toFocus ).trigger( "focus" ); + event.preventDefault(); + } + }, + + _panelKeyDown: function( event ) { + if ( event.keyCode === $.ui.keyCode.UP && event.ctrlKey ) { + $( event.currentTarget ).prev().trigger( "focus" ); + } + }, + + refresh: function() { + var options = this.options; + this._processPanels(); + + // Was collapsed or no panel + if ( ( options.active === false && options.collapsible === true ) || + !this.headers.length ) { + options.active = false; + this.active = $(); + + // active false only when collapsible is true + } else if ( options.active === false ) { + this._activate( 0 ); + + // was active, but active panel is gone + } else if ( this.active.length && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) { + + // all remaining panel are disabled + if ( this.headers.length === this.headers.find( ".ui-state-disabled" ).length ) { + options.active = false; + this.active = $(); + + // activate previous panel + } else { + this._activate( Math.max( 0, options.active - 1 ) ); + } + + // was active, active panel still exists + } else { + + // make sure active index is correct + options.active = this.headers.index( this.active ); + } + + this._destroyIcons(); + + this._refresh(); + }, + + _processPanels: function() { + var prevHeaders = this.headers, + prevPanels = this.panels; + + this.headers = this.element.find( this.options.header ); + this._addClass( this.headers, "ui-accordion-header ui-accordion-header-collapsed", + "ui-state-default" ); + + this.panels = this.headers.next().filter( ":not(.ui-accordion-content-active)" ).hide(); + this._addClass( this.panels, "ui-accordion-content", "ui-helper-reset ui-widget-content" ); + + // Avoid memory leaks (#10056) + if ( prevPanels ) { + this._off( prevHeaders.not( this.headers ) ); + this._off( prevPanels.not( this.panels ) ); + } + }, + + _refresh: function() { + var maxHeight, + options = this.options, + heightStyle = options.heightStyle, + parent = this.element.parent(); + + this.active = this._findActive( options.active ); + this._addClass( this.active, "ui-accordion-header-active", "ui-state-active" ) + ._removeClass( this.active, "ui-accordion-header-collapsed" ); + this._addClass( this.active.next(), "ui-accordion-content-active" ); + this.active.next().show(); + + this.headers + .attr( "role", "tab" ) + .each( function() { + var header = $( this ), + headerId = header.uniqueId().attr( "id" ), + panel = header.next(), + panelId = panel.uniqueId().attr( "id" ); + header.attr( "aria-controls", panelId ); + panel.attr( "aria-labelledby", headerId ); + } ) + .next() + .attr( "role", "tabpanel" ); + + this.headers + .not( this.active ) + .attr( { + "aria-selected": "false", + "aria-expanded": "false", + tabIndex: -1 + } ) + .next() + .attr( { + "aria-hidden": "true" + } ) + .hide(); + + // Make sure at least one header is in the tab order + if ( !this.active.length ) { + this.headers.eq( 0 ).attr( "tabIndex", 0 ); + } else { + this.active.attr( { + "aria-selected": "true", + "aria-expanded": "true", + tabIndex: 0 + } ) + .next() + .attr( { + "aria-hidden": "false" + } ); + } + + this._createIcons(); + + this._setupEvents( options.event ); + + if ( heightStyle === "fill" ) { + maxHeight = parent.height(); + this.element.siblings( ":visible" ).each( function() { + var elem = $( this ), + position = elem.css( "position" ); + + if ( position === "absolute" || position === "fixed" ) { + return; + } + maxHeight -= elem.outerHeight( true ); + } ); + + this.headers.each( function() { + maxHeight -= $( this ).outerHeight( true ); + } ); + + this.headers.next() + .each( function() { + $( this ).height( Math.max( 0, maxHeight - + $( this ).innerHeight() + $( this ).height() ) ); + } ) + .css( "overflow", "auto" ); + } else if ( heightStyle === "auto" ) { + maxHeight = 0; + this.headers.next() + .each( function() { + var isVisible = $( this ).is( ":visible" ); + if ( !isVisible ) { + $( this ).show(); + } + maxHeight = Math.max( maxHeight, $( this ).css( "height", "" ).height() ); + if ( !isVisible ) { + $( this ).hide(); + } + } ) + .height( maxHeight ); + } + }, + + _activate: function( index ) { + var active = this._findActive( index )[ 0 ]; + + // Trying to activate the already active panel + if ( active === this.active[ 0 ] ) { + return; + } + + // Trying to collapse, simulate a click on the currently active header + active = active || this.active[ 0 ]; + + this._eventHandler( { + target: active, + currentTarget: active, + preventDefault: $.noop + } ); + }, + + _findActive: function( selector ) { + return typeof selector === "number" ? this.headers.eq( selector ) : $(); + }, + + _setupEvents: function( event ) { + var events = { + keydown: "_keydown" + }; + if ( event ) { + $.each( event.split( " " ), function( index, eventName ) { + events[ eventName ] = "_eventHandler"; + } ); + } + + this._off( this.headers.add( this.headers.next() ) ); + this._on( this.headers, events ); + this._on( this.headers.next(), { keydown: "_panelKeyDown" } ); + this._hoverable( this.headers ); + this._focusable( this.headers ); + }, + + _eventHandler: function( event ) { + var activeChildren, clickedChildren, + options = this.options, + active = this.active, + clicked = $( event.currentTarget ), + clickedIsActive = clicked[ 0 ] === active[ 0 ], + collapsing = clickedIsActive && options.collapsible, + toShow = collapsing ? $() : clicked.next(), + toHide = active.next(), + eventData = { + oldHeader: active, + oldPanel: toHide, + newHeader: collapsing ? $() : clicked, + newPanel: toShow + }; + + event.preventDefault(); + + if ( + + // click on active header, but not collapsible + ( clickedIsActive && !options.collapsible ) || + + // allow canceling activation + ( this._trigger( "beforeActivate", event, eventData ) === false ) ) { + return; + } + + options.active = collapsing ? false : this.headers.index( clicked ); + + // When the call to ._toggle() comes after the class changes + // it causes a very odd bug in IE 8 (see #6720) + this.active = clickedIsActive ? $() : clicked; + this._toggle( eventData ); + + // Switch classes + // corner classes on the previously active header stay after the animation + this._removeClass( active, "ui-accordion-header-active", "ui-state-active" ); + if ( options.icons ) { + activeChildren = active.children( ".ui-accordion-header-icon" ); + this._removeClass( activeChildren, null, options.icons.activeHeader ) + ._addClass( activeChildren, null, options.icons.header ); + } + + if ( !clickedIsActive ) { + this._removeClass( clicked, "ui-accordion-header-collapsed" ) + ._addClass( clicked, "ui-accordion-header-active", "ui-state-active" ); + if ( options.icons ) { + clickedChildren = clicked.children( ".ui-accordion-header-icon" ); + this._removeClass( clickedChildren, null, options.icons.header ) + ._addClass( clickedChildren, null, options.icons.activeHeader ); + } + + this._addClass( clicked.next(), "ui-accordion-content-active" ); + } + }, + + _toggle: function( data ) { + var toShow = data.newPanel, + toHide = this.prevShow.length ? this.prevShow : data.oldPanel; + + // Handle activating a panel during the animation for another activation + this.prevShow.add( this.prevHide ).stop( true, true ); + this.prevShow = toShow; + this.prevHide = toHide; + + if ( this.options.animate ) { + this._animate( toShow, toHide, data ); + } else { + toHide.hide(); + toShow.show(); + this._toggleComplete( data ); + } + + toHide.attr( { + "aria-hidden": "true" + } ); + toHide.prev().attr( { + "aria-selected": "false", + "aria-expanded": "false" + } ); + + // if we're switching panels, remove the old header from the tab order + // if we're opening from collapsed state, remove the previous header from the tab order + // if we're collapsing, then keep the collapsing header in the tab order + if ( toShow.length && toHide.length ) { + toHide.prev().attr( { + "tabIndex": -1, + "aria-expanded": "false" + } ); + } else if ( toShow.length ) { + this.headers.filter( function() { + return parseInt( $( this ).attr( "tabIndex" ), 10 ) === 0; + } ) + .attr( "tabIndex", -1 ); + } + + toShow + .attr( "aria-hidden", "false" ) + .prev() + .attr( { + "aria-selected": "true", + "aria-expanded": "true", + tabIndex: 0 + } ); + }, + + _animate: function( toShow, toHide, data ) { + var total, easing, duration, + that = this, + adjust = 0, + boxSizing = toShow.css( "box-sizing" ), + down = toShow.length && + ( !toHide.length || ( toShow.index() < toHide.index() ) ), + animate = this.options.animate || {}, + options = down && animate.down || animate, + complete = function() { + that._toggleComplete( data ); + }; + + if ( typeof options === "number" ) { + duration = options; + } + if ( typeof options === "string" ) { + easing = options; + } + + // fall back from options to animation in case of partial down settings + easing = easing || options.easing || animate.easing; + duration = duration || options.duration || animate.duration; + + if ( !toHide.length ) { + return toShow.animate( this.showProps, duration, easing, complete ); + } + if ( !toShow.length ) { + return toHide.animate( this.hideProps, duration, easing, complete ); + } + + total = toShow.show().outerHeight(); + toHide.animate( this.hideProps, { + duration: duration, + easing: easing, + step: function( now, fx ) { + fx.now = Math.round( now ); + } + } ); + toShow + .hide() + .animate( this.showProps, { + duration: duration, + easing: easing, + complete: complete, + step: function( now, fx ) { + fx.now = Math.round( now ); + if ( fx.prop !== "height" ) { + if ( boxSizing === "content-box" ) { + adjust += fx.now; + } + } else if ( that.options.heightStyle !== "content" ) { + fx.now = Math.round( total - toHide.outerHeight() - adjust ); + adjust = 0; + } + } + } ); + }, + + _toggleComplete: function( data ) { + var toHide = data.oldPanel, + prev = toHide.prev(); + + this._removeClass( toHide, "ui-accordion-content-active" ); + this._removeClass( prev, "ui-accordion-header-active" ) + ._addClass( prev, "ui-accordion-header-collapsed" ); + + // Work around for rendering bug in IE (#5421) + if ( toHide.length ) { + toHide.parent()[ 0 ].className = toHide.parent()[ 0 ].className; + } + this._trigger( "activate", null, data ); + } +} ); + + + +var safeActiveElement = $.ui.safeActiveElement = function( document ) { + var activeElement; + + // Support: IE 9 only + // IE9 throws an "Unspecified error" accessing document.activeElement from an <iframe> + try { + activeElement = document.activeElement; + } catch ( error ) { + activeElement = document.body; + } + + // Support: IE 9 - 11 only + // IE may return null instead of an element + // Interestingly, this only seems to occur when NOT in an iframe + if ( !activeElement ) { + activeElement = document.body; + } + + // Support: IE 11 only + // IE11 returns a seemingly empty object in some cases when accessing + // document.activeElement from an <iframe> + if ( !activeElement.nodeName ) { + activeElement = document.body; + } + + return activeElement; +}; + + +/*! + * jQuery UI Menu 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: Menu +//>>group: Widgets +//>>description: Creates nestable menus. +//>>docs: http://api.jqueryui.com/menu/ +//>>demos: http://jqueryui.com/menu/ +//>>css.structure: ../../themes/base/core.css +//>>css.structure: ../../themes/base/menu.css +//>>css.theme: ../../themes/base/theme.css + + + +var widgetsMenu = $.widget( "ui.menu", { + version: "1.12.1", + defaultElement: "<ul>", + delay: 300, + options: { + icons: { + submenu: "ui-icon-caret-1-e" + }, + items: "> *", + menus: "ul", + position: { + my: "left top", + at: "right top" + }, + role: "menu", + + // Callbacks + blur: null, + focus: null, + select: null + }, + + _create: function() { + this.activeMenu = this.element; + + // Flag used to prevent firing of the click handler + // as the event bubbles up through nested menus + this.mouseHandled = false; + this.element + .uniqueId() + .attr( { + role: this.options.role, + tabIndex: 0 + } ); + + this._addClass( "ui-menu", "ui-widget ui-widget-content" ); + this._on( { + + // Prevent focus from sticking to links inside menu after clicking + // them (focus should always stay on UL during navigation). + "mousedown .ui-menu-item": function( event ) { + event.preventDefault(); + }, + "click .ui-menu-item": function( event ) { + var target = $( event.target ); + var active = $( $.ui.safeActiveElement( this.document[ 0 ] ) ); + if ( !this.mouseHandled && target.not( ".ui-state-disabled" ).length ) { + this.select( event ); + + // Only set the mouseHandled flag if the event will bubble, see #9469. + if ( !event.isPropagationStopped() ) { + this.mouseHandled = true; + } + + // Open submenu on click + if ( target.has( ".ui-menu" ).length ) { + this.expand( event ); + } else if ( !this.element.is( ":focus" ) && + active.closest( ".ui-menu" ).length ) { + + // Redirect focus to the menu + this.element.trigger( "focus", [ true ] ); + + // If the active item is on the top level, let it stay active. + // Otherwise, blur the active item since it is no longer visible. + if ( this.active && this.active.parents( ".ui-menu" ).length === 1 ) { + clearTimeout( this.timer ); + } + } + } + }, + "mouseenter .ui-menu-item": function( event ) { + + // Ignore mouse events while typeahead is active, see #10458. + // Prevents focusing the wrong item when typeahead causes a scroll while the mouse + // is over an item in the menu + if ( this.previousFilter ) { + return; + } + + var actualTarget = $( event.target ).closest( ".ui-menu-item" ), + target = $( event.currentTarget ); + + // Ignore bubbled events on parent items, see #11641 + if ( actualTarget[ 0 ] !== target[ 0 ] ) { + return; + } + + // Remove ui-state-active class from siblings of the newly focused menu item + // to avoid a jump caused by adjacent elements both having a class with a border + this._removeClass( target.siblings().children( ".ui-state-active" ), + null, "ui-state-active" ); + this.focus( event, target ); + }, + mouseleave: "collapseAll", + "mouseleave .ui-menu": "collapseAll", + focus: function( event, keepActiveItem ) { + + // If there's already an active item, keep it active + // If not, activate the first item + var item = this.active || this.element.find( this.options.items ).eq( 0 ); + + if ( !keepActiveItem ) { + this.focus( event, item ); + } + }, + blur: function( event ) { + this._delay( function() { + var notContained = !$.contains( + this.element[ 0 ], + $.ui.safeActiveElement( this.document[ 0 ] ) + ); + if ( notContained ) { + this.collapseAll( event ); + } + } ); + }, + keydown: "_keydown" + } ); + + this.refresh(); + + // Clicks outside of a menu collapse any open menus + this._on( this.document, { + click: function( event ) { + if ( this._closeOnDocumentClick( event ) ) { + this.collapseAll( event ); + } + + // Reset the mouseHandled flag + this.mouseHandled = false; + } + } ); + }, + + _destroy: function() { + var items = this.element.find( ".ui-menu-item" ) + .removeAttr( "role aria-disabled" ), + submenus = items.children( ".ui-menu-item-wrapper" ) + .removeUniqueId() + .removeAttr( "tabIndex role aria-haspopup" ); + + // Destroy (sub)menus + this.element + .removeAttr( "aria-activedescendant" ) + .find( ".ui-menu" ).addBack() + .removeAttr( "role aria-labelledby aria-expanded aria-hidden aria-disabled " + + "tabIndex" ) + .removeUniqueId() + .show(); + + submenus.children().each( function() { + var elem = $( this ); + if ( elem.data( "ui-menu-submenu-caret" ) ) { + elem.remove(); + } + } ); + }, + + _keydown: function( event ) { + var match, prev, character, skip, + preventDefault = true; + + switch ( event.keyCode ) { + case $.ui.keyCode.PAGE_UP: + this.previousPage( event ); + break; + case $.ui.keyCode.PAGE_DOWN: + this.nextPage( event ); + break; + case $.ui.keyCode.HOME: + this._move( "first", "first", event ); + break; + case $.ui.keyCode.END: + this._move( "last", "last", event ); + break; + case $.ui.keyCode.UP: + this.previous( event ); + break; + case $.ui.keyCode.DOWN: + this.next( event ); + break; + case $.ui.keyCode.LEFT: + this.collapse( event ); + break; + case $.ui.keyCode.RIGHT: + if ( this.active && !this.active.is( ".ui-state-disabled" ) ) { + this.expand( event ); + } + break; + case $.ui.keyCode.ENTER: + case $.ui.keyCode.SPACE: + this._activate( event ); + break; + case $.ui.keyCode.ESCAPE: + this.collapse( event ); + break; + default: + preventDefault = false; + prev = this.previousFilter || ""; + skip = false; + + // Support number pad values + character = event.keyCode >= 96 && event.keyCode <= 105 ? + ( event.keyCode - 96 ).toString() : String.fromCharCode( event.keyCode ); + + clearTimeout( this.filterTimer ); + + if ( character === prev ) { + skip = true; + } else { + character = prev + character; + } + + match = this._filterMenuItems( character ); + match = skip && match.index( this.active.next() ) !== -1 ? + this.active.nextAll( ".ui-menu-item" ) : + match; + + // If no matches on the current filter, reset to the last character pressed + // to move down the menu to the first item that starts with that character + if ( !match.length ) { + character = String.fromCharCode( event.keyCode ); + match = this._filterMenuItems( character ); + } + + if ( match.length ) { + this.focus( event, match ); + this.previousFilter = character; + this.filterTimer = this._delay( function() { + delete this.previousFilter; + }, 1000 ); + } else { + delete this.previousFilter; + } + } + + if ( preventDefault ) { + event.preventDefault(); + } + }, + + _activate: function( event ) { + if ( this.active && !this.active.is( ".ui-state-disabled" ) ) { + if ( this.active.children( "[aria-haspopup='true']" ).length ) { + this.expand( event ); + } else { + this.select( event ); + } + } + }, + + refresh: function() { + var menus, items, newSubmenus, newItems, newWrappers, + that = this, + icon = this.options.icons.submenu, + submenus = this.element.find( this.options.menus ); + + this._toggleClass( "ui-menu-icons", null, !!this.element.find( ".ui-icon" ).length ); + + // Initialize nested menus + newSubmenus = submenus.filter( ":not(.ui-menu)" ) + .hide() + .attr( { + role: this.options.role, + "aria-hidden": "true", + "aria-expanded": "false" + } ) + .each( function() { + var menu = $( this ), + item = menu.prev(), + submenuCaret = $( "<span>" ).data( "ui-menu-submenu-caret", true ); + + that._addClass( submenuCaret, "ui-menu-icon", "ui-icon " + icon ); + item + .attr( "aria-haspopup", "true" ) + .prepend( submenuCaret ); + menu.attr( "aria-labelledby", item.attr( "id" ) ); + } ); + + this._addClass( newSubmenus, "ui-menu", "ui-widget ui-widget-content ui-front" ); + + menus = submenus.add( this.element ); + items = menus.find( this.options.items ); + + // Initialize menu-items containing spaces and/or dashes only as dividers + items.not( ".ui-menu-item" ).each( function() { + var item = $( this ); + if ( that._isDivider( item ) ) { + that._addClass( item, "ui-menu-divider", "ui-widget-content" ); + } + } ); + + // Don't refresh list items that are already adapted + newItems = items.not( ".ui-menu-item, .ui-menu-divider" ); + newWrappers = newItems.children() + .not( ".ui-menu" ) + .uniqueId() + .attr( { + tabIndex: -1, + role: this._itemRole() + } ); + this._addClass( newItems, "ui-menu-item" ) + ._addClass( newWrappers, "ui-menu-item-wrapper" ); + + // Add aria-disabled attribute to any disabled menu item + items.filter( ".ui-state-disabled" ).attr( "aria-disabled", "true" ); + + // If the active item has been removed, blur the menu + if ( this.active && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) { + this.blur(); + } + }, + + _itemRole: function() { + return { + menu: "menuitem", + listbox: "option" + }[ this.options.role ]; + }, + + _setOption: function( key, value ) { + if ( key === "icons" ) { + var icons = this.element.find( ".ui-menu-icon" ); + this._removeClass( icons, null, this.options.icons.submenu ) + ._addClass( icons, null, value.submenu ); + } + this._super( key, value ); + }, + + _setOptionDisabled: function( value ) { + this._super( value ); + + this.element.attr( "aria-disabled", String( value ) ); + this._toggleClass( null, "ui-state-disabled", !!value ); + }, + + focus: function( event, item ) { + var nested, focused, activeParent; + this.blur( event, event && event.type === "focus" ); + + this._scrollIntoView( item ); + + this.active = item.first(); + + focused = this.active.children( ".ui-menu-item-wrapper" ); + this._addClass( focused, null, "ui-state-active" ); + + // Only update aria-activedescendant if there's a role + // otherwise we assume focus is managed elsewhere + if ( this.options.role ) { + this.element.attr( "aria-activedescendant", focused.attr( "id" ) ); + } + + // Highlight active parent menu item, if any + activeParent = this.active + .parent() + .closest( ".ui-menu-item" ) + .children( ".ui-menu-item-wrapper" ); + this._addClass( activeParent, null, "ui-state-active" ); + + if ( event && event.type === "keydown" ) { + this._close(); + } else { + this.timer = this._delay( function() { + this._close(); + }, this.delay ); + } + + nested = item.children( ".ui-menu" ); + if ( nested.length && event && ( /^mouse/.test( event.type ) ) ) { + this._startOpening( nested ); + } + this.activeMenu = item.parent(); + + this._trigger( "focus", event, { item: item } ); + }, + + _scrollIntoView: function( item ) { + var borderTop, paddingTop, offset, scroll, elementHeight, itemHeight; + if ( this._hasScroll() ) { + borderTop = parseFloat( $.css( this.activeMenu[ 0 ], "borderTopWidth" ) ) || 0; + paddingTop = parseFloat( $.css( this.activeMenu[ 0 ], "paddingTop" ) ) || 0; + offset = item.offset().top - this.activeMenu.offset().top - borderTop - paddingTop; + scroll = this.activeMenu.scrollTop(); + elementHeight = this.activeMenu.height(); + itemHeight = item.outerHeight(); + + if ( offset < 0 ) { + this.activeMenu.scrollTop( scroll + offset ); + } else if ( offset + itemHeight > elementHeight ) { + this.activeMenu.scrollTop( scroll + offset - elementHeight + itemHeight ); + } + } + }, + + blur: function( event, fromFocus ) { + if ( !fromFocus ) { + clearTimeout( this.timer ); + } + + if ( !this.active ) { + return; + } + + this._removeClass( this.active.children( ".ui-menu-item-wrapper" ), + null, "ui-state-active" ); + + this._trigger( "blur", event, { item: this.active } ); + this.active = null; + }, + + _startOpening: function( submenu ) { + clearTimeout( this.timer ); + + // Don't open if already open fixes a Firefox bug that caused a .5 pixel + // shift in the submenu position when mousing over the caret icon + if ( submenu.attr( "aria-hidden" ) !== "true" ) { + return; + } + + this.timer = this._delay( function() { + this._close(); + this._open( submenu ); + }, this.delay ); + }, + + _open: function( submenu ) { + var position = $.extend( { + of: this.active + }, this.options.position ); + + clearTimeout( this.timer ); + this.element.find( ".ui-menu" ).not( submenu.parents( ".ui-menu" ) ) + .hide() + .attr( "aria-hidden", "true" ); + + submenu + .show() + .removeAttr( "aria-hidden" ) + .attr( "aria-expanded", "true" ) + .position( position ); + }, + + collapseAll: function( event, all ) { + clearTimeout( this.timer ); + this.timer = this._delay( function() { + + // If we were passed an event, look for the submenu that contains the event + var currentMenu = all ? this.element : + $( event && event.target ).closest( this.element.find( ".ui-menu" ) ); + + // If we found no valid submenu ancestor, use the main menu to close all + // sub menus anyway + if ( !currentMenu.length ) { + currentMenu = this.element; + } + + this._close( currentMenu ); + + this.blur( event ); + + // Work around active item staying active after menu is blurred + this._removeClass( currentMenu.find( ".ui-state-active" ), null, "ui-state-active" ); + + this.activeMenu = currentMenu; + }, this.delay ); + }, + + // With no arguments, closes the currently active menu - if nothing is active + // it closes all menus. If passed an argument, it will search for menus BELOW + _close: function( startMenu ) { + if ( !startMenu ) { + startMenu = this.active ? this.active.parent() : this.element; + } + + startMenu.find( ".ui-menu" ) + .hide() + .attr( "aria-hidden", "true" ) + .attr( "aria-expanded", "false" ); + }, + + _closeOnDocumentClick: function( event ) { + return !$( event.target ).closest( ".ui-menu" ).length; + }, + + _isDivider: function( item ) { + + // Match hyphen, em dash, en dash + return !/[^\-\u2014\u2013\s]/.test( item.text() ); + }, + + collapse: function( event ) { + var newItem = this.active && + this.active.parent().closest( ".ui-menu-item", this.element ); + if ( newItem && newItem.length ) { + this._close(); + this.focus( event, newItem ); + } + }, + + expand: function( event ) { + var newItem = this.active && + this.active + .children( ".ui-menu " ) + .find( this.options.items ) + .first(); + + if ( newItem && newItem.length ) { + this._open( newItem.parent() ); + + // Delay so Firefox will not hide activedescendant change in expanding submenu from AT + this._delay( function() { + this.focus( event, newItem ); + } ); + } + }, + + next: function( event ) { + this._move( "next", "first", event ); + }, + + previous: function( event ) { + this._move( "prev", "last", event ); + }, + + isFirstItem: function() { + return this.active && !this.active.prevAll( ".ui-menu-item" ).length; + }, + + isLastItem: function() { + return this.active && !this.active.nextAll( ".ui-menu-item" ).length; + }, + + _move: function( direction, filter, event ) { + var next; + if ( this.active ) { + if ( direction === "first" || direction === "last" ) { + next = this.active + [ direction === "first" ? "prevAll" : "nextAll" ]( ".ui-menu-item" ) + .eq( -1 ); + } else { + next = this.active + [ direction + "All" ]( ".ui-menu-item" ) + .eq( 0 ); + } + } + if ( !next || !next.length || !this.active ) { + next = this.activeMenu.find( this.options.items )[ filter ](); + } + + this.focus( event, next ); + }, + + nextPage: function( event ) { + var item, base, height; + + if ( !this.active ) { + this.next( event ); + return; + } + if ( this.isLastItem() ) { + return; + } + if ( this._hasScroll() ) { + base = this.active.offset().top; + height = this.element.height(); + this.active.nextAll( ".ui-menu-item" ).each( function() { + item = $( this ); + return item.offset().top - base - height < 0; + } ); + + this.focus( event, item ); + } else { + this.focus( event, this.activeMenu.find( this.options.items ) + [ !this.active ? "first" : "last" ]() ); + } + }, + + previousPage: function( event ) { + var item, base, height; + if ( !this.active ) { + this.next( event ); + return; + } + if ( this.isFirstItem() ) { + return; + } + if ( this._hasScroll() ) { + base = this.active.offset().top; + height = this.element.height(); + this.active.prevAll( ".ui-menu-item" ).each( function() { + item = $( this ); + return item.offset().top - base + height > 0; + } ); + + this.focus( event, item ); + } else { + this.focus( event, this.activeMenu.find( this.options.items ).first() ); + } + }, + + _hasScroll: function() { + return this.element.outerHeight() < this.element.prop( "scrollHeight" ); + }, + + select: function( event ) { + + // TODO: It should never be possible to not have an active item at this + // point, but the tests don't trigger mouseenter before click. + this.active = this.active || $( event.target ).closest( ".ui-menu-item" ); + var ui = { item: this.active }; + if ( !this.active.has( ".ui-menu" ).length ) { + this.collapseAll( event, true ); + } + this._trigger( "select", event, ui ); + }, + + _filterMenuItems: function( character ) { + var escapedCharacter = character.replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" ), + regex = new RegExp( "^" + escapedCharacter, "i" ); + + return this.activeMenu + .find( this.options.items ) + + // Only match on items, not dividers or other content (#10571) + .filter( ".ui-menu-item" ) + .filter( function() { + return regex.test( + $.trim( $( this ).children( ".ui-menu-item-wrapper" ).text() ) ); + } ); + } +} ); + + +/*! + * jQuery UI Autocomplete 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: Autocomplete +//>>group: Widgets +//>>description: Lists suggested words as the user is typing. +//>>docs: http://api.jqueryui.com/autocomplete/ +//>>demos: http://jqueryui.com/autocomplete/ +//>>css.structure: ../../themes/base/core.css +//>>css.structure: ../../themes/base/autocomplete.css +//>>css.theme: ../../themes/base/theme.css + + + +$.widget( "ui.autocomplete", { + version: "1.12.1", + defaultElement: "<input>", + options: { + appendTo: null, + autoFocus: false, + delay: 300, + minLength: 1, + position: { + my: "left top", + at: "left bottom", + collision: "none" + }, + source: null, + + // Callbacks + change: null, + close: null, + focus: null, + open: null, + response: null, + search: null, + select: null + }, + + requestIndex: 0, + pending: 0, + + _create: function() { + + // Some browsers only repeat keydown events, not keypress events, + // so we use the suppressKeyPress flag to determine if we've already + // handled the keydown event. #7269 + // Unfortunately the code for & in keypress is the same as the up arrow, + // so we use the suppressKeyPressRepeat flag to avoid handling keypress + // events when we know the keydown event was used to modify the + // search term. #7799 + var suppressKeyPress, suppressKeyPressRepeat, suppressInput, + nodeName = this.element[ 0 ].nodeName.toLowerCase(), + isTextarea = nodeName === "textarea", + isInput = nodeName === "input"; + + // Textareas are always multi-line + // Inputs are always single-line, even if inside a contentEditable element + // IE also treats inputs as contentEditable + // All other element types are determined by whether or not they're contentEditable + this.isMultiLine = isTextarea || !isInput && this._isContentEditable( this.element ); + + this.valueMethod = this.element[ isTextarea || isInput ? "val" : "text" ]; + this.isNewMenu = true; + + this._addClass( "ui-autocomplete-input" ); + this.element.attr( "autocomplete", "off" ); + + this._on( this.element, { + keydown: function( event ) { + if ( this.element.prop( "readOnly" ) ) { + suppressKeyPress = true; + suppressInput = true; + suppressKeyPressRepeat = true; + return; + } + + suppressKeyPress = false; + suppressInput = false; + suppressKeyPressRepeat = false; + var keyCode = $.ui.keyCode; + switch ( event.keyCode ) { + case keyCode.PAGE_UP: + suppressKeyPress = true; + this._move( "previousPage", event ); + break; + case keyCode.PAGE_DOWN: + suppressKeyPress = true; + this._move( "nextPage", event ); + break; + case keyCode.UP: + suppressKeyPress = true; + this._keyEvent( "previous", event ); + break; + case keyCode.DOWN: + suppressKeyPress = true; + this._keyEvent( "next", event ); + break; + case keyCode.ENTER: + + // when menu is open and has focus + if ( this.menu.active ) { + + // #6055 - Opera still allows the keypress to occur + // which causes forms to submit + suppressKeyPress = true; + event.preventDefault(); + this.menu.select( event ); + } + break; + case keyCode.TAB: + if ( this.menu.active ) { + this.menu.select( event ); + } + break; + case keyCode.ESCAPE: + if ( this.menu.element.is( ":visible" ) ) { + if ( !this.isMultiLine ) { + this._value( this.term ); + } + this.close( event ); + + // Different browsers have different default behavior for escape + // Single press can mean undo or clear + // Double press in IE means clear the whole form + event.preventDefault(); + } + break; + default: + suppressKeyPressRepeat = true; + + // search timeout should be triggered before the input value is changed + this._searchTimeout( event ); + break; + } + }, + keypress: function( event ) { + if ( suppressKeyPress ) { + suppressKeyPress = false; + if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) { + event.preventDefault(); + } + return; + } + if ( suppressKeyPressRepeat ) { + return; + } + + // Replicate some key handlers to allow them to repeat in Firefox and Opera + var keyCode = $.ui.keyCode; + switch ( event.keyCode ) { + case keyCode.PAGE_UP: + this._move( "previousPage", event ); + break; + case keyCode.PAGE_DOWN: + this._move( "nextPage", event ); + break; + case keyCode.UP: + this._keyEvent( "previous", event ); + break; + case keyCode.DOWN: + this._keyEvent( "next", event ); + break; + } + }, + input: function( event ) { + if ( suppressInput ) { + suppressInput = false; + event.preventDefault(); + return; + } + this._searchTimeout( event ); + }, + focus: function() { + this.selectedItem = null; + this.previous = this._value(); + }, + blur: function( event ) { + if ( this.cancelBlur ) { + delete this.cancelBlur; + return; + } + + clearTimeout( this.searching ); + this.close( event ); + this._change( event ); + } + } ); + + this._initSource(); + this.menu = $( "<ul>" ) + .appendTo( this._appendTo() ) + .menu( { + + // disable ARIA support, the live region takes care of that + role: null + } ) + .hide() + .menu( "instance" ); + + this._addClass( this.menu.element, "ui-autocomplete", "ui-front" ); + this._on( this.menu.element, { + mousedown: function( event ) { + + // prevent moving focus out of the text field + event.preventDefault(); + + // IE doesn't prevent moving focus even with event.preventDefault() + // so we set a flag to know when we should ignore the blur event + this.cancelBlur = true; + this._delay( function() { + delete this.cancelBlur; + + // Support: IE 8 only + // Right clicking a menu item or selecting text from the menu items will + // result in focus moving out of the input. However, we've already received + // and ignored the blur event because of the cancelBlur flag set above. So + // we restore focus to ensure that the menu closes properly based on the user's + // next actions. + if ( this.element[ 0 ] !== $.ui.safeActiveElement( this.document[ 0 ] ) ) { + this.element.trigger( "focus" ); + } + } ); + }, + menufocus: function( event, ui ) { + var label, item; + + // support: Firefox + // Prevent accidental activation of menu items in Firefox (#7024 #9118) + if ( this.isNewMenu ) { + this.isNewMenu = false; + if ( event.originalEvent && /^mouse/.test( event.originalEvent.type ) ) { + this.menu.blur(); + + this.document.one( "mousemove", function() { + $( event.target ).trigger( event.originalEvent ); + } ); + + return; + } + } + + item = ui.item.data( "ui-autocomplete-item" ); + if ( false !== this._trigger( "focus", event, { item: item } ) ) { + + // use value to match what will end up in the input, if it was a key event + if ( event.originalEvent && /^key/.test( event.originalEvent.type ) ) { + this._value( item.value ); + } + } + + // Announce the value in the liveRegion + label = ui.item.attr( "aria-label" ) || item.value; + if ( label && $.trim( label ).length ) { + this.liveRegion.children().hide(); + $( "<div>" ).text( label ).appendTo( this.liveRegion ); + } + }, + menuselect: function( event, ui ) { + var item = ui.item.data( "ui-autocomplete-item" ), + previous = this.previous; + + // Only trigger when focus was lost (click on menu) + if ( this.element[ 0 ] !== $.ui.safeActiveElement( this.document[ 0 ] ) ) { + this.element.trigger( "focus" ); + this.previous = previous; + + // #6109 - IE triggers two focus events and the second + // is asynchronous, so we need to reset the previous + // term synchronously and asynchronously :-( + this._delay( function() { + this.previous = previous; + this.selectedItem = item; + } ); + } + + if ( false !== this._trigger( "select", event, { item: item } ) ) { + this._value( item.value ); + } + + // reset the term after the select event + // this allows custom select handling to work properly + this.term = this._value(); + + this.close( event ); + this.selectedItem = item; + } + } ); + + this.liveRegion = $( "<div>", { + role: "status", + "aria-live": "assertive", + "aria-relevant": "additions" + } ) + .appendTo( this.document[ 0 ].body ); + + this._addClass( this.liveRegion, null, "ui-helper-hidden-accessible" ); + + // Turning off autocomplete prevents the browser from remembering the + // value when navigating through history, so we re-enable autocomplete + // if the page is unloaded before the widget is destroyed. #7790 + this._on( this.window, { + beforeunload: function() { + this.element.removeAttr( "autocomplete" ); + } + } ); + }, + + _destroy: function() { + clearTimeout( this.searching ); + this.element.removeAttr( "autocomplete" ); + this.menu.element.remove(); + this.liveRegion.remove(); + }, + + _setOption: function( key, value ) { + this._super( key, value ); + if ( key === "source" ) { + this._initSource(); + } + if ( key === "appendTo" ) { + this.menu.element.appendTo( this._appendTo() ); + } + if ( key === "disabled" && value && this.xhr ) { + this.xhr.abort(); + } + }, + + _isEventTargetInWidget: function( event ) { + var menuElement = this.menu.element[ 0 ]; + + return event.target === this.element[ 0 ] || + event.target === menuElement || + $.contains( menuElement, event.target ); + }, + + _closeOnClickOutside: function( event ) { + if ( !this._isEventTargetInWidget( event ) ) { + this.close(); + } + }, + + _appendTo: function() { + var element = this.options.appendTo; + + if ( element ) { + element = element.jquery || element.nodeType ? + $( element ) : + this.document.find( element ).eq( 0 ); + } + + if ( !element || !element[ 0 ] ) { + element = this.element.closest( ".ui-front, dialog" ); + } + + if ( !element.length ) { + element = this.document[ 0 ].body; + } + + return element; + }, + + _initSource: function() { + var array, url, + that = this; + if ( $.isArray( this.options.source ) ) { + array = this.options.source; + this.source = function( request, response ) { + response( $.ui.autocomplete.filter( array, request.term ) ); + }; + } else if ( typeof this.options.source === "string" ) { + url = this.options.source; + this.source = function( request, response ) { + if ( that.xhr ) { + that.xhr.abort(); + } + that.xhr = $.ajax( { + url: url, + data: request, + dataType: "json", + success: function( data ) { + response( data ); + }, + error: function() { + response( [] ); + } + } ); + }; + } else { + this.source = this.options.source; + } + }, + + _searchTimeout: function( event ) { + clearTimeout( this.searching ); + this.searching = this._delay( function() { + + // Search if the value has changed, or if the user retypes the same value (see #7434) + var equalValues = this.term === this._value(), + menuVisible = this.menu.element.is( ":visible" ), + modifierKey = event.altKey || event.ctrlKey || event.metaKey || event.shiftKey; + + if ( !equalValues || ( equalValues && !menuVisible && !modifierKey ) ) { + this.selectedItem = null; + this.search( null, event ); + } + }, this.options.delay ); + }, + + search: function( value, event ) { + value = value != null ? value : this._value(); + + // Always save the actual value, not the one passed as an argument + this.term = this._value(); + + if ( value.length < this.options.minLength ) { + return this.close( event ); + } + + if ( this._trigger( "search", event ) === false ) { + return; + } + + return this._search( value ); + }, + + _search: function( value ) { + this.pending++; + this._addClass( "ui-autocomplete-loading" ); + this.cancelSearch = false; + + this.source( { term: value }, this._response() ); + }, + + _response: function() { + var index = ++this.requestIndex; + + return $.proxy( function( content ) { + if ( index === this.requestIndex ) { + this.__response( content ); + } + + this.pending--; + if ( !this.pending ) { + this._removeClass( "ui-autocomplete-loading" ); + } + }, this ); + }, + + __response: function( content ) { + if ( content ) { + content = this._normalize( content ); + } + this._trigger( "response", null, { content: content } ); + if ( !this.options.disabled && content && content.length && !this.cancelSearch ) { + this._suggest( content ); + this._trigger( "open" ); + } else { + + // use ._close() instead of .close() so we don't cancel future searches + this._close(); + } + }, + + close: function( event ) { + this.cancelSearch = true; + this._close( event ); + }, + + _close: function( event ) { + + // Remove the handler that closes the menu on outside clicks + this._off( this.document, "mousedown" ); + + if ( this.menu.element.is( ":visible" ) ) { + this.menu.element.hide(); + this.menu.blur(); + this.isNewMenu = true; + this._trigger( "close", event ); + } + }, + + _change: function( event ) { + if ( this.previous !== this._value() ) { + this._trigger( "change", event, { item: this.selectedItem } ); + } + }, + + _normalize: function( items ) { + + // assume all items have the right format when the first item is complete + if ( items.length && items[ 0 ].label && items[ 0 ].value ) { + return items; + } + return $.map( items, function( item ) { + if ( typeof item === "string" ) { + return { + label: item, + value: item + }; + } + return $.extend( {}, item, { + label: item.label || item.value, + value: item.value || item.label + } ); + } ); + }, + + _suggest: function( items ) { + var ul = this.menu.element.empty(); + this._renderMenu( ul, items ); + this.isNewMenu = true; + this.menu.refresh(); + + // Size and position menu + ul.show(); + this._resizeMenu(); + ul.position( $.extend( { + of: this.element + }, this.options.position ) ); + + if ( this.options.autoFocus ) { + this.menu.next(); + } + + // Listen for interactions outside of the widget (#6642) + this._on( this.document, { + mousedown: "_closeOnClickOutside" + } ); + }, + + _resizeMenu: function() { + var ul = this.menu.element; + ul.outerWidth( Math.max( + + // Firefox wraps long text (possibly a rounding bug) + // so we add 1px to avoid the wrapping (#7513) + ul.width( "" ).outerWidth() + 1, + this.element.outerWidth() + ) ); + }, + + _renderMenu: function( ul, items ) { + var that = this; + $.each( items, function( index, item ) { + that._renderItemData( ul, item ); + } ); + }, + + _renderItemData: function( ul, item ) { + return this._renderItem( ul, item ).data( "ui-autocomplete-item", item ); + }, + + _renderItem: function( ul, item ) { + return $( "<li>" ) + .append( $( "<div>" ).text( item.label ) ) + .appendTo( ul ); + }, + + _move: function( direction, event ) { + if ( !this.menu.element.is( ":visible" ) ) { + this.search( null, event ); + return; + } + if ( this.menu.isFirstItem() && /^previous/.test( direction ) || + this.menu.isLastItem() && /^next/.test( direction ) ) { + + if ( !this.isMultiLine ) { + this._value( this.term ); + } + + this.menu.blur(); + return; + } + this.menu[ direction ]( event ); + }, + + widget: function() { + return this.menu.element; + }, + + _value: function() { + return this.valueMethod.apply( this.element, arguments ); + }, + + _keyEvent: function( keyEvent, event ) { + if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) { + this._move( keyEvent, event ); + + // Prevents moving cursor to beginning/end of the text field in some browsers + event.preventDefault(); + } + }, + + // Support: Chrome <=50 + // We should be able to just use this.element.prop( "isContentEditable" ) + // but hidden elements always report false in Chrome. + // https://code.google.com/p/chromium/issues/detail?id=313082 + _isContentEditable: function( element ) { + if ( !element.length ) { + return false; + } + + var editable = element.prop( "contentEditable" ); + + if ( editable === "inherit" ) { + return this._isContentEditable( element.parent() ); + } + + return editable === "true"; + } +} ); + +$.extend( $.ui.autocomplete, { + escapeRegex: function( value ) { + return value.replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" ); + }, + filter: function( array, term ) { + var matcher = new RegExp( $.ui.autocomplete.escapeRegex( term ), "i" ); + return $.grep( array, function( value ) { + return matcher.test( value.label || value.value || value ); + } ); + } +} ); + +// Live region extension, adding a `messages` option +// NOTE: This is an experimental API. We are still investigating +// a full solution for string manipulation and internationalization. +$.widget( "ui.autocomplete", $.ui.autocomplete, { + options: { + messages: { + noResults: "No search results.", + results: function( amount ) { + return amount + ( amount > 1 ? " results are" : " result is" ) + + " available, use up and down arrow keys to navigate."; + } + } + }, + + __response: function( content ) { + var message; + this._superApply( arguments ); + if ( this.options.disabled || this.cancelSearch ) { + return; + } + if ( content && content.length ) { + message = this.options.messages.results( content.length ); + } else { + message = this.options.messages.noResults; + } + this.liveRegion.children().hide(); + $( "<div>" ).text( message ).appendTo( this.liveRegion ); + } +} ); + +var widgetsAutocomplete = $.ui.autocomplete; + + +/*! + * jQuery UI Controlgroup 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: Controlgroup +//>>group: Widgets +//>>description: Visually groups form control widgets +//>>docs: http://api.jqueryui.com/controlgroup/ +//>>demos: http://jqueryui.com/controlgroup/ +//>>css.structure: ../../themes/base/core.css +//>>css.structure: ../../themes/base/controlgroup.css +//>>css.theme: ../../themes/base/theme.css + + +var controlgroupCornerRegex = /ui-corner-([a-z]){2,6}/g; + +var widgetsControlgroup = $.widget( "ui.controlgroup", { + version: "1.12.1", + defaultElement: "<div>", + options: { + direction: "horizontal", + disabled: null, + onlyVisible: true, + items: { + "button": "input[type=button], input[type=submit], input[type=reset], button, a", + "controlgroupLabel": ".ui-controlgroup-label", + "checkboxradio": "input[type='checkbox'], input[type='radio']", + "selectmenu": "select", + "spinner": ".ui-spinner-input" + } + }, + + _create: function() { + this._enhance(); + }, + + // To support the enhanced option in jQuery Mobile, we isolate DOM manipulation + _enhance: function() { + this.element.attr( "role", "toolbar" ); + this.refresh(); + }, + + _destroy: function() { + this._callChildMethod( "destroy" ); + this.childWidgets.removeData( "ui-controlgroup-data" ); + this.element.removeAttr( "role" ); + if ( this.options.items.controlgroupLabel ) { + this.element + .find( this.options.items.controlgroupLabel ) + .find( ".ui-controlgroup-label-contents" ) + .contents().unwrap(); + } + }, + + _initWidgets: function() { + var that = this, + childWidgets = []; + + // First we iterate over each of the items options + $.each( this.options.items, function( widget, selector ) { + var labels; + var options = {}; + + // Make sure the widget has a selector set + if ( !selector ) { + return; + } + + if ( widget === "controlgroupLabel" ) { + labels = that.element.find( selector ); + labels.each( function() { + var element = $( this ); + + if ( element.children( ".ui-controlgroup-label-contents" ).length ) { + return; + } + element.contents() + .wrapAll( "<span class='ui-controlgroup-label-contents'></span>" ); + } ); + that._addClass( labels, null, "ui-widget ui-widget-content ui-state-default" ); + childWidgets = childWidgets.concat( labels.get() ); + return; + } + + // Make sure the widget actually exists + if ( !$.fn[ widget ] ) { + return; + } + + // We assume everything is in the middle to start because we can't determine + // first / last elements until all enhancments are done. + if ( that[ "_" + widget + "Options" ] ) { + options = that[ "_" + widget + "Options" ]( "middle" ); + } else { + options = { classes: {} }; + } + + // Find instances of this widget inside controlgroup and init them + that.element + .find( selector ) + .each( function() { + var element = $( this ); + var instance = element[ widget ]( "instance" ); + + // We need to clone the default options for this type of widget to avoid + // polluting the variable options which has a wider scope than a single widget. + var instanceOptions = $.widget.extend( {}, options ); + + // If the button is the child of a spinner ignore it + // TODO: Find a more generic solution + if ( widget === "button" && element.parent( ".ui-spinner" ).length ) { + return; + } + + // Create the widget if it doesn't exist + if ( !instance ) { + instance = element[ widget ]()[ widget ]( "instance" ); + } + if ( instance ) { + instanceOptions.classes = + that._resolveClassesValues( instanceOptions.classes, instance ); + } + element[ widget ]( instanceOptions ); + + // Store an instance of the controlgroup to be able to reference + // from the outermost element for changing options and refresh + var widgetElement = element[ widget ]( "widget" ); + $.data( widgetElement[ 0 ], "ui-controlgroup-data", + instance ? instance : element[ widget ]( "instance" ) ); + + childWidgets.push( widgetElement[ 0 ] ); + } ); + } ); + + this.childWidgets = $( $.unique( childWidgets ) ); + this._addClass( this.childWidgets, "ui-controlgroup-item" ); + }, + + _callChildMethod: function( method ) { + this.childWidgets.each( function() { + var element = $( this ), + data = element.data( "ui-controlgroup-data" ); + if ( data && data[ method ] ) { + data[ method ](); + } + } ); + }, + + _updateCornerClass: function( element, position ) { + var remove = "ui-corner-top ui-corner-bottom ui-corner-left ui-corner-right ui-corner-all"; + var add = this._buildSimpleOptions( position, "label" ).classes.label; + + this._removeClass( element, null, remove ); + this._addClass( element, null, add ); + }, + + _buildSimpleOptions: function( position, key ) { + var direction = this.options.direction === "vertical"; + var result = { + classes: {} + }; + result.classes[ key ] = { + "middle": "", + "first": "ui-corner-" + ( direction ? "top" : "left" ), + "last": "ui-corner-" + ( direction ? "bottom" : "right" ), + "only": "ui-corner-all" + }[ position ]; + + return result; + }, + + _spinnerOptions: function( position ) { + var options = this._buildSimpleOptions( position, "ui-spinner" ); + + options.classes[ "ui-spinner-up" ] = ""; + options.classes[ "ui-spinner-down" ] = ""; + + return options; + }, + + _buttonOptions: function( position ) { + return this._buildSimpleOptions( position, "ui-button" ); + }, + + _checkboxradioOptions: function( position ) { + return this._buildSimpleOptions( position, "ui-checkboxradio-label" ); + }, + + _selectmenuOptions: function( position ) { + var direction = this.options.direction === "vertical"; + return { + width: direction ? "auto" : false, + classes: { + middle: { + "ui-selectmenu-button-open": "", + "ui-selectmenu-button-closed": "" + }, + first: { + "ui-selectmenu-button-open": "ui-corner-" + ( direction ? "top" : "tl" ), + "ui-selectmenu-button-closed": "ui-corner-" + ( direction ? "top" : "left" ) + }, + last: { + "ui-selectmenu-button-open": direction ? "" : "ui-corner-tr", + "ui-selectmenu-button-closed": "ui-corner-" + ( direction ? "bottom" : "right" ) + }, + only: { + "ui-selectmenu-button-open": "ui-corner-top", + "ui-selectmenu-button-closed": "ui-corner-all" + } + + }[ position ] + }; + }, + + _resolveClassesValues: function( classes, instance ) { + var result = {}; + $.each( classes, function( key ) { + var current = instance.options.classes[ key ] || ""; + current = $.trim( current.replace( controlgroupCornerRegex, "" ) ); + result[ key ] = ( current + " " + classes[ key ] ).replace( /\s+/g, " " ); + } ); + return result; + }, + + _setOption: function( key, value ) { + if ( key === "direction" ) { + this._removeClass( "ui-controlgroup-" + this.options.direction ); + } + + this._super( key, value ); + if ( key === "disabled" ) { + this._callChildMethod( value ? "disable" : "enable" ); + return; + } + + this.refresh(); + }, + + refresh: function() { + var children, + that = this; + + this._addClass( "ui-controlgroup ui-controlgroup-" + this.options.direction ); + + if ( this.options.direction === "horizontal" ) { + this._addClass( null, "ui-helper-clearfix" ); + } + this._initWidgets(); + + children = this.childWidgets; + + // We filter here because we need to track all childWidgets not just the visible ones + if ( this.options.onlyVisible ) { + children = children.filter( ":visible" ); + } + + if ( children.length ) { + + // We do this last because we need to make sure all enhancment is done + // before determining first and last + $.each( [ "first", "last" ], function( index, value ) { + var instance = children[ value ]().data( "ui-controlgroup-data" ); + + if ( instance && that[ "_" + instance.widgetName + "Options" ] ) { + var options = that[ "_" + instance.widgetName + "Options" ]( + children.length === 1 ? "only" : value + ); + options.classes = that._resolveClassesValues( options.classes, instance ); + instance.element[ instance.widgetName ]( options ); + } else { + that._updateCornerClass( children[ value ](), value ); + } + } ); + + // Finally call the refresh method on each of the child widgets. + this._callChildMethod( "refresh" ); + } + } +} ); + +/*! + * jQuery UI Checkboxradio 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: Checkboxradio +//>>group: Widgets +//>>description: Enhances a form with multiple themeable checkboxes or radio buttons. +//>>docs: http://api.jqueryui.com/checkboxradio/ +//>>demos: http://jqueryui.com/checkboxradio/ +//>>css.structure: ../../themes/base/core.css +//>>css.structure: ../../themes/base/button.css +//>>css.structure: ../../themes/base/checkboxradio.css +//>>css.theme: ../../themes/base/theme.css + + + +$.widget( "ui.checkboxradio", [ $.ui.formResetMixin, { + version: "1.12.1", + options: { + disabled: null, + label: null, + icon: true, + classes: { + "ui-checkboxradio-label": "ui-corner-all", + "ui-checkboxradio-icon": "ui-corner-all" + } + }, + + _getCreateOptions: function() { + var disabled, labels; + var that = this; + var options = this._super() || {}; + + // We read the type here, because it makes more sense to throw a element type error first, + // rather then the error for lack of a label. Often if its the wrong type, it + // won't have a label (e.g. calling on a div, btn, etc) + this._readType(); + + labels = this.element.labels(); + + // If there are multiple labels, use the last one + this.label = $( labels[ labels.length - 1 ] ); + if ( !this.label.length ) { + $.error( "No label found for checkboxradio widget" ); + } + + this.originalLabel = ""; + + // We need to get the label text but this may also need to make sure it does not contain the + // input itself. + this.label.contents().not( this.element[ 0 ] ).each( function() { + + // The label contents could be text, html, or a mix. We concat each element to get a + // string representation of the label, without the input as part of it. + that.originalLabel += this.nodeType === 3 ? $( this ).text() : this.outerHTML; + } ); + + // Set the label option if we found label text + if ( this.originalLabel ) { + options.label = this.originalLabel; + } + + disabled = this.element[ 0 ].disabled; + if ( disabled != null ) { + options.disabled = disabled; + } + return options; + }, + + _create: function() { + var checked = this.element[ 0 ].checked; + + this._bindFormResetHandler(); + + if ( this.options.disabled == null ) { + this.options.disabled = this.element[ 0 ].disabled; + } + + this._setOption( "disabled", this.options.disabled ); + this._addClass( "ui-checkboxradio", "ui-helper-hidden-accessible" ); + this._addClass( this.label, "ui-checkboxradio-label", "ui-button ui-widget" ); + + if ( this.type === "radio" ) { + this._addClass( this.label, "ui-checkboxradio-radio-label" ); + } + + if ( this.options.label && this.options.label !== this.originalLabel ) { + this._updateLabel(); + } else if ( this.originalLabel ) { + this.options.label = this.originalLabel; + } + + this._enhance(); + + if ( checked ) { + this._addClass( this.label, "ui-checkboxradio-checked", "ui-state-active" ); + if ( this.icon ) { + this._addClass( this.icon, null, "ui-state-hover" ); + } + } + + this._on( { + change: "_toggleClasses", + focus: function() { + this._addClass( this.label, null, "ui-state-focus ui-visual-focus" ); + }, + blur: function() { + this._removeClass( this.label, null, "ui-state-focus ui-visual-focus" ); + } + } ); + }, + + _readType: function() { + var nodeName = this.element[ 0 ].nodeName.toLowerCase(); + this.type = this.element[ 0 ].type; + if ( nodeName !== "input" || !/radio|checkbox/.test( this.type ) ) { + $.error( "Can't create checkboxradio on element.nodeName=" + nodeName + + " and element.type=" + this.type ); + } + }, + + // Support jQuery Mobile enhanced option + _enhance: function() { + this._updateIcon( this.element[ 0 ].checked ); + }, + + widget: function() { + return this.label; + }, + + _getRadioGroup: function() { + var group; + var name = this.element[ 0 ].name; + var nameSelector = "input[name='" + $.ui.escapeSelector( name ) + "']"; + + if ( !name ) { + return $( [] ); + } + + if ( this.form.length ) { + group = $( this.form[ 0 ].elements ).filter( nameSelector ); + } else { + + // Not inside a form, check all inputs that also are not inside a form + group = $( nameSelector ).filter( function() { + return $( this ).form().length === 0; + } ); + } + + return group.not( this.element ); + }, + + _toggleClasses: function() { + var checked = this.element[ 0 ].checked; + this._toggleClass( this.label, "ui-checkboxradio-checked", "ui-state-active", checked ); + + if ( this.options.icon && this.type === "checkbox" ) { + this._toggleClass( this.icon, null, "ui-icon-check ui-state-checked", checked ) + ._toggleClass( this.icon, null, "ui-icon-blank", !checked ); + } + + if ( this.type === "radio" ) { + this._getRadioGroup() + .each( function() { + var instance = $( this ).checkboxradio( "instance" ); + + if ( instance ) { + instance._removeClass( instance.label, + "ui-checkboxradio-checked", "ui-state-active" ); + } + } ); + } + }, + + _destroy: function() { + this._unbindFormResetHandler(); + + if ( this.icon ) { + this.icon.remove(); + this.iconSpace.remove(); + } + }, + + _setOption: function( key, value ) { + + // We don't allow the value to be set to nothing + if ( key === "label" && !value ) { + return; + } + + this._super( key, value ); + + if ( key === "disabled" ) { + this._toggleClass( this.label, null, "ui-state-disabled", value ); + this.element[ 0 ].disabled = value; + + // Don't refresh when setting disabled + return; + } + this.refresh(); + }, + + _updateIcon: function( checked ) { + var toAdd = "ui-icon ui-icon-background "; + + if ( this.options.icon ) { + if ( !this.icon ) { + this.icon = $( "<span>" ); + this.iconSpace = $( "<span> </span>" ); + this._addClass( this.iconSpace, "ui-checkboxradio-icon-space" ); + } + + if ( this.type === "checkbox" ) { + toAdd += checked ? "ui-icon-check ui-state-checked" : "ui-icon-blank"; + this._removeClass( this.icon, null, checked ? "ui-icon-blank" : "ui-icon-check" ); + } else { + toAdd += "ui-icon-blank"; + } + this._addClass( this.icon, "ui-checkboxradio-icon", toAdd ); + if ( !checked ) { + this._removeClass( this.icon, null, "ui-icon-check ui-state-checked" ); + } + this.icon.prependTo( this.label ).after( this.iconSpace ); + } else if ( this.icon !== undefined ) { + this.icon.remove(); + this.iconSpace.remove(); + delete this.icon; + } + }, + + _updateLabel: function() { + + // Remove the contents of the label ( minus the icon, icon space, and input ) + var contents = this.label.contents().not( this.element[ 0 ] ); + if ( this.icon ) { + contents = contents.not( this.icon[ 0 ] ); + } + if ( this.iconSpace ) { + contents = contents.not( this.iconSpace[ 0 ] ); + } + contents.remove(); + + this.label.append( this.options.label ); + }, + + refresh: function() { + var checked = this.element[ 0 ].checked, + isDisabled = this.element[ 0 ].disabled; + + this._updateIcon( checked ); + this._toggleClass( this.label, "ui-checkboxradio-checked", "ui-state-active", checked ); + if ( this.options.label !== null ) { + this._updateLabel(); + } + + if ( isDisabled !== this.options.disabled ) { + this._setOptions( { "disabled": isDisabled } ); + } + } + +} ] ); + +var widgetsCheckboxradio = $.ui.checkboxradio; + + +/*! + * jQuery UI Button 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: Button +//>>group: Widgets +//>>description: Enhances a form with themeable buttons. +//>>docs: http://api.jqueryui.com/button/ +//>>demos: http://jqueryui.com/button/ +//>>css.structure: ../../themes/base/core.css +//>>css.structure: ../../themes/base/button.css +//>>css.theme: ../../themes/base/theme.css + + + +$.widget( "ui.button", { + version: "1.12.1", + defaultElement: "<button>", + options: { + classes: { + "ui-button": "ui-corner-all" + }, + disabled: null, + icon: null, + iconPosition: "beginning", + label: null, + showLabel: true + }, + + _getCreateOptions: function() { + var disabled, + + // This is to support cases like in jQuery Mobile where the base widget does have + // an implementation of _getCreateOptions + options = this._super() || {}; + + this.isInput = this.element.is( "input" ); + + disabled = this.element[ 0 ].disabled; + if ( disabled != null ) { + options.disabled = disabled; + } + + this.originalLabel = this.isInput ? this.element.val() : this.element.html(); + if ( this.originalLabel ) { + options.label = this.originalLabel; + } + + return options; + }, + + _create: function() { + if ( !this.option.showLabel & !this.options.icon ) { + this.options.showLabel = true; + } + + // We have to check the option again here even though we did in _getCreateOptions, + // because null may have been passed on init which would override what was set in + // _getCreateOptions + if ( this.options.disabled == null ) { + this.options.disabled = this.element[ 0 ].disabled || false; + } + + this.hasTitle = !!this.element.attr( "title" ); + + // Check to see if the label needs to be set or if its already correct + if ( this.options.label && this.options.label !== this.originalLabel ) { + if ( this.isInput ) { + this.element.val( this.options.label ); + } else { + this.element.html( this.options.label ); + } + } + this._addClass( "ui-button", "ui-widget" ); + this._setOption( "disabled", this.options.disabled ); + this._enhance(); + + if ( this.element.is( "a" ) ) { + this._on( { + "keyup": function( event ) { + if ( event.keyCode === $.ui.keyCode.SPACE ) { + event.preventDefault(); + + // Support: PhantomJS <= 1.9, IE 8 Only + // If a native click is available use it so we actually cause navigation + // otherwise just trigger a click event + if ( this.element[ 0 ].click ) { + this.element[ 0 ].click(); + } else { + this.element.trigger( "click" ); + } + } + } + } ); + } + }, + + _enhance: function() { + if ( !this.element.is( "button" ) ) { + this.element.attr( "role", "button" ); + } + + if ( this.options.icon ) { + this._updateIcon( "icon", this.options.icon ); + this._updateTooltip(); + } + }, + + _updateTooltip: function() { + this.title = this.element.attr( "title" ); + + if ( !this.options.showLabel && !this.title ) { + this.element.attr( "title", this.options.label ); + } + }, + + _updateIcon: function( option, value ) { + var icon = option !== "iconPosition", + position = icon ? this.options.iconPosition : value, + displayBlock = position === "top" || position === "bottom"; + + // Create icon + if ( !this.icon ) { + this.icon = $( "<span>" ); + + this._addClass( this.icon, "ui-button-icon", "ui-icon" ); + + if ( !this.options.showLabel ) { + this._addClass( "ui-button-icon-only" ); + } + } else if ( icon ) { + + // If we are updating the icon remove the old icon class + this._removeClass( this.icon, null, this.options.icon ); + } + + // If we are updating the icon add the new icon class + if ( icon ) { + this._addClass( this.icon, null, value ); + } + + this._attachIcon( position ); + + // If the icon is on top or bottom we need to add the ui-widget-icon-block class and remove + // the iconSpace if there is one. + if ( displayBlock ) { + this._addClass( this.icon, null, "ui-widget-icon-block" ); + if ( this.iconSpace ) { + this.iconSpace.remove(); + } + } else { + + // Position is beginning or end so remove the ui-widget-icon-block class and add the + // space if it does not exist + if ( !this.iconSpace ) { + this.iconSpace = $( "<span> </span>" ); + this._addClass( this.iconSpace, "ui-button-icon-space" ); + } + this._removeClass( this.icon, null, "ui-wiget-icon-block" ); + this._attachIconSpace( position ); + } + }, + + _destroy: function() { + this.element.removeAttr( "role" ); + + if ( this.icon ) { + this.icon.remove(); + } + if ( this.iconSpace ) { + this.iconSpace.remove(); + } + if ( !this.hasTitle ) { + this.element.removeAttr( "title" ); + } + }, + + _attachIconSpace: function( iconPosition ) { + this.icon[ /^(?:end|bottom)/.test( iconPosition ) ? "before" : "after" ]( this.iconSpace ); + }, + + _attachIcon: function( iconPosition ) { + this.element[ /^(?:end|bottom)/.test( iconPosition ) ? "append" : "prepend" ]( this.icon ); + }, + + _setOptions: function( options ) { + var newShowLabel = options.showLabel === undefined ? + this.options.showLabel : + options.showLabel, + newIcon = options.icon === undefined ? this.options.icon : options.icon; + + if ( !newShowLabel && !newIcon ) { + options.showLabel = true; + } + this._super( options ); + }, + + _setOption: function( key, value ) { + if ( key === "icon" ) { + if ( value ) { + this._updateIcon( key, value ); + } else if ( this.icon ) { + this.icon.remove(); + if ( this.iconSpace ) { + this.iconSpace.remove(); + } + } + } + + if ( key === "iconPosition" ) { + this._updateIcon( key, value ); + } + + // Make sure we can't end up with a button that has neither text nor icon + if ( key === "showLabel" ) { + this._toggleClass( "ui-button-icon-only", null, !value ); + this._updateTooltip(); + } + + if ( key === "label" ) { + if ( this.isInput ) { + this.element.val( value ); + } else { + + // If there is an icon, append it, else nothing then append the value + // this avoids removal of the icon when setting label text + this.element.html( value ); + if ( this.icon ) { + this._attachIcon( this.options.iconPosition ); + this._attachIconSpace( this.options.iconPosition ); + } + } + } + + this._super( key, value ); + + if ( key === "disabled" ) { + this._toggleClass( null, "ui-state-disabled", value ); + this.element[ 0 ].disabled = value; + if ( value ) { + this.element.blur(); + } + } + }, + + refresh: function() { + + // Make sure to only check disabled if its an element that supports this otherwise + // check for the disabled class to determine state + var isDisabled = this.element.is( "input, button" ) ? + this.element[ 0 ].disabled : this.element.hasClass( "ui-button-disabled" ); + + if ( isDisabled !== this.options.disabled ) { + this._setOptions( { disabled: isDisabled } ); + } + + this._updateTooltip(); + } +} ); + +// DEPRECATED +if ( $.uiBackCompat !== false ) { + + // Text and Icons options + $.widget( "ui.button", $.ui.button, { + options: { + text: true, + icons: { + primary: null, + secondary: null + } + }, + + _create: function() { + if ( this.options.showLabel && !this.options.text ) { + this.options.showLabel = this.options.text; + } + if ( !this.options.showLabel && this.options.text ) { + this.options.text = this.options.showLabel; + } + if ( !this.options.icon && ( this.options.icons.primary || + this.options.icons.secondary ) ) { + if ( this.options.icons.primary ) { + this.options.icon = this.options.icons.primary; + } else { + this.options.icon = this.options.icons.secondary; + this.options.iconPosition = "end"; + } + } else if ( this.options.icon ) { + this.options.icons.primary = this.options.icon; + } + this._super(); + }, + + _setOption: function( key, value ) { + if ( key === "text" ) { + this._super( "showLabel", value ); + return; + } + if ( key === "showLabel" ) { + this.options.text = value; + } + if ( key === "icon" ) { + this.options.icons.primary = value; + } + if ( key === "icons" ) { + if ( value.primary ) { + this._super( "icon", value.primary ); + this._super( "iconPosition", "beginning" ); + } else if ( value.secondary ) { + this._super( "icon", value.secondary ); + this._super( "iconPosition", "end" ); + } + } + this._superApply( arguments ); + } + } ); + + $.fn.button = ( function( orig ) { + return function() { + if ( !this.length || ( this.length && this[ 0 ].tagName !== "INPUT" ) || + ( this.length && this[ 0 ].tagName === "INPUT" && ( + this.attr( "type" ) !== "checkbox" && this.attr( "type" ) !== "radio" + ) ) ) { + return orig.apply( this, arguments ); + } + if ( !$.ui.checkboxradio ) { + $.error( "Checkboxradio widget missing" ); + } + if ( arguments.length === 0 ) { + return this.checkboxradio( { + "icon": false + } ); + } + return this.checkboxradio.apply( this, arguments ); + }; + } )( $.fn.button ); + + $.fn.buttonset = function() { + if ( !$.ui.controlgroup ) { + $.error( "Controlgroup widget missing" ); + } + if ( arguments[ 0 ] === "option" && arguments[ 1 ] === "items" && arguments[ 2 ] ) { + return this.controlgroup.apply( this, + [ arguments[ 0 ], "items.button", arguments[ 2 ] ] ); + } + if ( arguments[ 0 ] === "option" && arguments[ 1 ] === "items" ) { + return this.controlgroup.apply( this, [ arguments[ 0 ], "items.button" ] ); + } + if ( typeof arguments[ 0 ] === "object" && arguments[ 0 ].items ) { + arguments[ 0 ].items = { + button: arguments[ 0 ].items + }; + } + return this.controlgroup.apply( this, arguments ); + }; +} + +var widgetsButton = $.ui.button; + + +// jscs:disable maximumLineLength +/* jscs:disable requireCamelCaseOrUpperCaseIdentifiers */ +/*! + * jQuery UI Datepicker 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: Datepicker +//>>group: Widgets +//>>description: Displays a calendar from an input or inline for selecting dates. +//>>docs: http://api.jqueryui.com/datepicker/ +//>>demos: http://jqueryui.com/datepicker/ +//>>css.structure: ../../themes/base/core.css +//>>css.structure: ../../themes/base/datepicker.css +//>>css.theme: ../../themes/base/theme.css + + + +$.extend( $.ui, { datepicker: { version: "1.12.1" } } ); + +var datepicker_instActive; + +function datepicker_getZindex( elem ) { + var position, value; + while ( elem.length && elem[ 0 ] !== document ) { + + // Ignore z-index if position is set to a value where z-index is ignored by the browser + // This makes behavior of this function consistent across browsers + // WebKit always returns auto if the element is positioned + position = elem.css( "position" ); + if ( position === "absolute" || position === "relative" || position === "fixed" ) { + + // IE returns 0 when zIndex is not specified + // other browsers return a string + // we ignore the case of nested elements with an explicit value of 0 + // <div style="z-index: -10;"><div style="z-index: 0;"></div></div> + value = parseInt( elem.css( "zIndex" ), 10 ); + if ( !isNaN( value ) && value !== 0 ) { + return value; + } + } + elem = elem.parent(); + } + + return 0; +} +/* Date picker manager. + Use the singleton instance of this class, $.datepicker, to interact with the date picker. + Settings for (groups of) date pickers are maintained in an instance object, + allowing multiple different settings on the same page. */ + +function Datepicker() { + this._curInst = null; // The current instance in use + this._keyEvent = false; // If the last event was a key event + this._disabledInputs = []; // List of date picker inputs that have been disabled + this._datepickerShowing = false; // True if the popup picker is showing , false if not + this._inDialog = false; // True if showing within a "dialog", false if not + this._mainDivId = "ui-datepicker-div"; // The ID of the main datepicker division + this._inlineClass = "ui-datepicker-inline"; // The name of the inline marker class + this._appendClass = "ui-datepicker-append"; // The name of the append marker class + this._triggerClass = "ui-datepicker-trigger"; // The name of the trigger marker class + this._dialogClass = "ui-datepicker-dialog"; // The name of the dialog marker class + this._disableClass = "ui-datepicker-disabled"; // The name of the disabled covering marker class + this._unselectableClass = "ui-datepicker-unselectable"; // The name of the unselectable cell marker class + this._currentClass = "ui-datepicker-current-day"; // The name of the current day marker class + this._dayOverClass = "ui-datepicker-days-cell-over"; // The name of the day hover marker class + this.regional = []; // Available regional settings, indexed by language code + this.regional[ "" ] = { // Default regional settings + closeText: "Done", // Display text for close link + prevText: "Prev", // Display text for previous month link + nextText: "Next", // Display text for next month link + currentText: "Today", // Display text for current month link + monthNames: [ "January","February","March","April","May","June", + "July","August","September","October","November","December" ], // Names of months for drop-down and formatting + monthNamesShort: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ], // For formatting + dayNames: [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ], // For formatting + dayNamesShort: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ], // For formatting + dayNamesMin: [ "Su","Mo","Tu","We","Th","Fr","Sa" ], // Column headings for days starting at Sunday + weekHeader: "Wk", // Column header for week of the year + dateFormat: "mm/dd/yy", // See format options on parseDate + firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ... + isRTL: false, // True if right-to-left language, false if left-to-right + showMonthAfterYear: false, // True if the year select precedes month, false for month then year + yearSuffix: "" // Additional text to append to the year in the month headers + }; + this._defaults = { // Global defaults for all the date picker instances + showOn: "focus", // "focus" for popup on focus, + // "button" for trigger button, or "both" for either + showAnim: "fadeIn", // Name of jQuery animation for popup + showOptions: {}, // Options for enhanced animations + defaultDate: null, // Used when field is blank: actual date, + // +/-number for offset from today, null for today + appendText: "", // Display text following the input box, e.g. showing the format + buttonText: "...", // Text for trigger button + buttonImage: "", // URL for trigger button image + buttonImageOnly: false, // True if the image appears alone, false if it appears on a button + hideIfNoPrevNext: false, // True to hide next/previous month links + // if not applicable, false to just disable them + navigationAsDateFormat: false, // True if date formatting applied to prev/today/next links + gotoCurrent: false, // True if today link goes back to current selection instead + changeMonth: false, // True if month can be selected directly, false if only prev/next + changeYear: false, // True if year can be selected directly, false if only prev/next + yearRange: "c-10:c+10", // Range of years to display in drop-down, + // either relative to today's year (-nn:+nn), relative to currently displayed year + // (c-nn:c+nn), absolute (nnnn:nnnn), or a combination of the above (nnnn:-n) + showOtherMonths: false, // True to show dates in other months, false to leave blank + selectOtherMonths: false, // True to allow selection of dates in other months, false for unselectable + showWeek: false, // True to show week of the year, false to not show it + calculateWeek: this.iso8601Week, // How to calculate the week of the year, + // takes a Date and returns the number of the week for it + shortYearCutoff: "+10", // Short year values < this are in the current century, + // > this are in the previous century, + // string value starting with "+" for current year + value + minDate: null, // The earliest selectable date, or null for no limit + maxDate: null, // The latest selectable date, or null for no limit + duration: "fast", // Duration of display/closure + beforeShowDay: null, // Function that takes a date and returns an array with + // [0] = true if selectable, false if not, [1] = custom CSS class name(s) or "", + // [2] = cell title (optional), e.g. $.datepicker.noWeekends + beforeShow: null, // Function that takes an input field and + // returns a set of custom settings for the date picker + onSelect: null, // Define a callback function when a date is selected + onChangeMonthYear: null, // Define a callback function when the month or year is changed + onClose: null, // Define a callback function when the datepicker is closed + numberOfMonths: 1, // Number of months to show at a time + showCurrentAtPos: 0, // The position in multipe months at which to show the current month (starting at 0) + stepMonths: 1, // Number of months to step back/forward + stepBigMonths: 12, // Number of months to step back/forward for the big links + altField: "", // Selector for an alternate field to store selected dates into + altFormat: "", // The date format to use for the alternate field + constrainInput: true, // The input is constrained by the current date format + showButtonPanel: false, // True to show button panel, false to not show it + autoSize: false, // True to size the input for the date format, false to leave as is + disabled: false // The initial disabled state + }; + $.extend( this._defaults, this.regional[ "" ] ); + this.regional.en = $.extend( true, {}, this.regional[ "" ] ); + this.regional[ "en-US" ] = $.extend( true, {}, this.regional.en ); + this.dpDiv = datepicker_bindHover( $( "<div id='" + this._mainDivId + "' class='ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>" ) ); +} + +$.extend( Datepicker.prototype, { + /* Class name added to elements to indicate already configured with a date picker. */ + markerClassName: "hasDatepicker", + + //Keep track of the maximum number of rows displayed (see #7043) + maxRows: 4, + + // TODO rename to "widget" when switching to widget factory + _widgetDatepicker: function() { + return this.dpDiv; + }, + + /* Override the default settings for all instances of the date picker. + * @param settings object - the new settings to use as defaults (anonymous object) + * @return the manager object + */ + setDefaults: function( settings ) { + datepicker_extendRemove( this._defaults, settings || {} ); + return this; + }, + + /* Attach the date picker to a jQuery selection. + * @param target element - the target input field or division or span + * @param settings object - the new settings to use for this date picker instance (anonymous) + */ + _attachDatepicker: function( target, settings ) { + var nodeName, inline, inst; + nodeName = target.nodeName.toLowerCase(); + inline = ( nodeName === "div" || nodeName === "span" ); + if ( !target.id ) { + this.uuid += 1; + target.id = "dp" + this.uuid; + } + inst = this._newInst( $( target ), inline ); + inst.settings = $.extend( {}, settings || {} ); + if ( nodeName === "input" ) { + this._connectDatepicker( target, inst ); + } else if ( inline ) { + this._inlineDatepicker( target, inst ); + } + }, + + /* Create a new instance object. */ + _newInst: function( target, inline ) { + var id = target[ 0 ].id.replace( /([^A-Za-z0-9_\-])/g, "\\\\$1" ); // escape jQuery meta chars + return { id: id, input: target, // associated target + selectedDay: 0, selectedMonth: 0, selectedYear: 0, // current selection + drawMonth: 0, drawYear: 0, // month being drawn + inline: inline, // is datepicker inline or not + dpDiv: ( !inline ? this.dpDiv : // presentation div + datepicker_bindHover( $( "<div class='" + this._inlineClass + " ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>" ) ) ) }; + }, + + /* Attach the date picker to an input field. */ + _connectDatepicker: function( target, inst ) { + var input = $( target ); + inst.append = $( [] ); + inst.trigger = $( [] ); + if ( input.hasClass( this.markerClassName ) ) { + return; + } + this._attachments( input, inst ); + input.addClass( this.markerClassName ).on( "keydown", this._doKeyDown ). + on( "keypress", this._doKeyPress ).on( "keyup", this._doKeyUp ); + this._autoSize( inst ); + $.data( target, "datepicker", inst ); + + //If disabled option is true, disable the datepicker once it has been attached to the input (see ticket #5665) + if ( inst.settings.disabled ) { + this._disableDatepicker( target ); + } + }, + + /* Make attachments based on settings. */ + _attachments: function( input, inst ) { + var showOn, buttonText, buttonImage, + appendText = this._get( inst, "appendText" ), + isRTL = this._get( inst, "isRTL" ); + + if ( inst.append ) { + inst.append.remove(); + } + if ( appendText ) { + inst.append = $( "<span class='" + this._appendClass + "'>" + appendText + "</span>" ); + input[ isRTL ? "before" : "after" ]( inst.append ); + } + + input.off( "focus", this._showDatepicker ); + + if ( inst.trigger ) { + inst.trigger.remove(); + } + + showOn = this._get( inst, "showOn" ); + if ( showOn === "focus" || showOn === "both" ) { // pop-up date picker when in the marked field + input.on( "focus", this._showDatepicker ); + } + if ( showOn === "button" || showOn === "both" ) { // pop-up date picker when button clicked + buttonText = this._get( inst, "buttonText" ); + buttonImage = this._get( inst, "buttonImage" ); + inst.trigger = $( this._get( inst, "buttonImageOnly" ) ? + $( "<img/>" ).addClass( this._triggerClass ). + attr( { src: buttonImage, alt: buttonText, title: buttonText } ) : + $( "<button type='button'></button>" ).addClass( this._triggerClass ). + html( !buttonImage ? buttonText : $( "<img/>" ).attr( + { src:buttonImage, alt:buttonText, title:buttonText } ) ) ); + input[ isRTL ? "before" : "after" ]( inst.trigger ); + inst.trigger.on( "click", function() { + if ( $.datepicker._datepickerShowing && $.datepicker._lastInput === input[ 0 ] ) { + $.datepicker._hideDatepicker(); + } else if ( $.datepicker._datepickerShowing && $.datepicker._lastInput !== input[ 0 ] ) { + $.datepicker._hideDatepicker(); + $.datepicker._showDatepicker( input[ 0 ] ); + } else { + $.datepicker._showDatepicker( input[ 0 ] ); + } + return false; + } ); + } + }, + + /* Apply the maximum length for the date format. */ + _autoSize: function( inst ) { + if ( this._get( inst, "autoSize" ) && !inst.inline ) { + var findMax, max, maxI, i, + date = new Date( 2009, 12 - 1, 20 ), // Ensure double digits + dateFormat = this._get( inst, "dateFormat" ); + + if ( dateFormat.match( /[DM]/ ) ) { + findMax = function( names ) { + max = 0; + maxI = 0; + for ( i = 0; i < names.length; i++ ) { + if ( names[ i ].length > max ) { + max = names[ i ].length; + maxI = i; + } + } + return maxI; + }; + date.setMonth( findMax( this._get( inst, ( dateFormat.match( /MM/ ) ? + "monthNames" : "monthNamesShort" ) ) ) ); + date.setDate( findMax( this._get( inst, ( dateFormat.match( /DD/ ) ? + "dayNames" : "dayNamesShort" ) ) ) + 20 - date.getDay() ); + } + inst.input.attr( "size", this._formatDate( inst, date ).length ); + } + }, + + /* Attach an inline date picker to a div. */ + _inlineDatepicker: function( target, inst ) { + var divSpan = $( target ); + if ( divSpan.hasClass( this.markerClassName ) ) { + return; + } + divSpan.addClass( this.markerClassName ).append( inst.dpDiv ); + $.data( target, "datepicker", inst ); + this._setDate( inst, this._getDefaultDate( inst ), true ); + this._updateDatepicker( inst ); + this._updateAlternate( inst ); + + //If disabled option is true, disable the datepicker before showing it (see ticket #5665) + if ( inst.settings.disabled ) { + this._disableDatepicker( target ); + } + + // Set display:block in place of inst.dpDiv.show() which won't work on disconnected elements + // http://bugs.jqueryui.com/ticket/7552 - A Datepicker created on a detached div has zero height + inst.dpDiv.css( "display", "block" ); + }, + + /* Pop-up the date picker in a "dialog" box. + * @param input element - ignored + * @param date string or Date - the initial date to display + * @param onSelect function - the function to call when a date is selected + * @param settings object - update the dialog date picker instance's settings (anonymous object) + * @param pos int[2] - coordinates for the dialog's position within the screen or + * event - with x/y coordinates or + * leave empty for default (screen centre) + * @return the manager object + */ + _dialogDatepicker: function( input, date, onSelect, settings, pos ) { + var id, browserWidth, browserHeight, scrollX, scrollY, + inst = this._dialogInst; // internal instance + + if ( !inst ) { + this.uuid += 1; + id = "dp" + this.uuid; + this._dialogInput = $( "<input type='text' id='" + id + + "' style='position: absolute; top: -100px; width: 0px;'/>" ); + this._dialogInput.on( "keydown", this._doKeyDown ); + $( "body" ).append( this._dialogInput ); + inst = this._dialogInst = this._newInst( this._dialogInput, false ); + inst.settings = {}; + $.data( this._dialogInput[ 0 ], "datepicker", inst ); + } + datepicker_extendRemove( inst.settings, settings || {} ); + date = ( date && date.constructor === Date ? this._formatDate( inst, date ) : date ); + this._dialogInput.val( date ); + + this._pos = ( pos ? ( pos.length ? pos : [ pos.pageX, pos.pageY ] ) : null ); + if ( !this._pos ) { + browserWidth = document.documentElement.clientWidth; + browserHeight = document.documentElement.clientHeight; + scrollX = document.documentElement.scrollLeft || document.body.scrollLeft; + scrollY = document.documentElement.scrollTop || document.body.scrollTop; + this._pos = // should use actual width/height below + [ ( browserWidth / 2 ) - 100 + scrollX, ( browserHeight / 2 ) - 150 + scrollY ]; + } + + // Move input on screen for focus, but hidden behind dialog + this._dialogInput.css( "left", ( this._pos[ 0 ] + 20 ) + "px" ).css( "top", this._pos[ 1 ] + "px" ); + inst.settings.onSelect = onSelect; + this._inDialog = true; + this.dpDiv.addClass( this._dialogClass ); + this._showDatepicker( this._dialogInput[ 0 ] ); + if ( $.blockUI ) { + $.blockUI( this.dpDiv ); + } + $.data( this._dialogInput[ 0 ], "datepicker", inst ); + return this; + }, + + /* Detach a datepicker from its control. + * @param target element - the target input field or division or span + */ + _destroyDatepicker: function( target ) { + var nodeName, + $target = $( target ), + inst = $.data( target, "datepicker" ); + + if ( !$target.hasClass( this.markerClassName ) ) { + return; + } + + nodeName = target.nodeName.toLowerCase(); + $.removeData( target, "datepicker" ); + if ( nodeName === "input" ) { + inst.append.remove(); + inst.trigger.remove(); + $target.removeClass( this.markerClassName ). + off( "focus", this._showDatepicker ). + off( "keydown", this._doKeyDown ). + off( "keypress", this._doKeyPress ). + off( "keyup", this._doKeyUp ); + } else if ( nodeName === "div" || nodeName === "span" ) { + $target.removeClass( this.markerClassName ).empty(); + } + + if ( datepicker_instActive === inst ) { + datepicker_instActive = null; + } + }, + + /* Enable the date picker to a jQuery selection. + * @param target element - the target input field or division or span + */ + _enableDatepicker: function( target ) { + var nodeName, inline, + $target = $( target ), + inst = $.data( target, "datepicker" ); + + if ( !$target.hasClass( this.markerClassName ) ) { + return; + } + + nodeName = target.nodeName.toLowerCase(); + if ( nodeName === "input" ) { + target.disabled = false; + inst.trigger.filter( "button" ). + each( function() { this.disabled = false; } ).end(). + filter( "img" ).css( { opacity: "1.0", cursor: "" } ); + } else if ( nodeName === "div" || nodeName === "span" ) { + inline = $target.children( "." + this._inlineClass ); + inline.children().removeClass( "ui-state-disabled" ); + inline.find( "select.ui-datepicker-month, select.ui-datepicker-year" ). + prop( "disabled", false ); + } + this._disabledInputs = $.map( this._disabledInputs, + function( value ) { return ( value === target ? null : value ); } ); // delete entry + }, + + /* Disable the date picker to a jQuery selection. + * @param target element - the target input field or division or span + */ + _disableDatepicker: function( target ) { + var nodeName, inline, + $target = $( target ), + inst = $.data( target, "datepicker" ); + + if ( !$target.hasClass( this.markerClassName ) ) { + return; + } + + nodeName = target.nodeName.toLowerCase(); + if ( nodeName === "input" ) { + target.disabled = true; + inst.trigger.filter( "button" ). + each( function() { this.disabled = true; } ).end(). + filter( "img" ).css( { opacity: "0.5", cursor: "default" } ); + } else if ( nodeName === "div" || nodeName === "span" ) { + inline = $target.children( "." + this._inlineClass ); + inline.children().addClass( "ui-state-disabled" ); + inline.find( "select.ui-datepicker-month, select.ui-datepicker-year" ). + prop( "disabled", true ); + } + this._disabledInputs = $.map( this._disabledInputs, + function( value ) { return ( value === target ? null : value ); } ); // delete entry + this._disabledInputs[ this._disabledInputs.length ] = target; + }, + + /* Is the first field in a jQuery collection disabled as a datepicker? + * @param target element - the target input field or division or span + * @return boolean - true if disabled, false if enabled + */ + _isDisabledDatepicker: function( target ) { + if ( !target ) { + return false; + } + for ( var i = 0; i < this._disabledInputs.length; i++ ) { + if ( this._disabledInputs[ i ] === target ) { + return true; + } + } + return false; + }, + + /* Retrieve the instance data for the target control. + * @param target element - the target input field or division or span + * @return object - the associated instance data + * @throws error if a jQuery problem getting data + */ + _getInst: function( target ) { + try { + return $.data( target, "datepicker" ); + } + catch ( err ) { + throw "Missing instance data for this datepicker"; + } + }, + + /* Update or retrieve the settings for a date picker attached to an input field or division. + * @param target element - the target input field or division or span + * @param name object - the new settings to update or + * string - the name of the setting to change or retrieve, + * when retrieving also "all" for all instance settings or + * "defaults" for all global defaults + * @param value any - the new value for the setting + * (omit if above is an object or to retrieve a value) + */ + _optionDatepicker: function( target, name, value ) { + var settings, date, minDate, maxDate, + inst = this._getInst( target ); + + if ( arguments.length === 2 && typeof name === "string" ) { + return ( name === "defaults" ? $.extend( {}, $.datepicker._defaults ) : + ( inst ? ( name === "all" ? $.extend( {}, inst.settings ) : + this._get( inst, name ) ) : null ) ); + } + + settings = name || {}; + if ( typeof name === "string" ) { + settings = {}; + settings[ name ] = value; + } + + if ( inst ) { + if ( this._curInst === inst ) { + this._hideDatepicker(); + } + + date = this._getDateDatepicker( target, true ); + minDate = this._getMinMaxDate( inst, "min" ); + maxDate = this._getMinMaxDate( inst, "max" ); + datepicker_extendRemove( inst.settings, settings ); + + // reformat the old minDate/maxDate values if dateFormat changes and a new minDate/maxDate isn't provided + if ( minDate !== null && settings.dateFormat !== undefined && settings.minDate === undefined ) { + inst.settings.minDate = this._formatDate( inst, minDate ); + } + if ( maxDate !== null && settings.dateFormat !== undefined && settings.maxDate === undefined ) { + inst.settings.maxDate = this._formatDate( inst, maxDate ); + } + if ( "disabled" in settings ) { + if ( settings.disabled ) { + this._disableDatepicker( target ); + } else { + this._enableDatepicker( target ); + } + } + this._attachments( $( target ), inst ); + this._autoSize( inst ); + this._setDate( inst, date ); + this._updateAlternate( inst ); + this._updateDatepicker( inst ); + } + }, + + // Change method deprecated + _changeDatepicker: function( target, name, value ) { + this._optionDatepicker( target, name, value ); + }, + + /* Redraw the date picker attached to an input field or division. + * @param target element - the target input field or division or span + */ + _refreshDatepicker: function( target ) { + var inst = this._getInst( target ); + if ( inst ) { + this._updateDatepicker( inst ); + } + }, + + /* Set the dates for a jQuery selection. + * @param target element - the target input field or division or span + * @param date Date - the new date + */ + _setDateDatepicker: function( target, date ) { + var inst = this._getInst( target ); + if ( inst ) { + this._setDate( inst, date ); + this._updateDatepicker( inst ); + this._updateAlternate( inst ); + } + }, + + /* Get the date(s) for the first entry in a jQuery selection. + * @param target element - the target input field or division or span + * @param noDefault boolean - true if no default date is to be used + * @return Date - the current date + */ + _getDateDatepicker: function( target, noDefault ) { + var inst = this._getInst( target ); + if ( inst && !inst.inline ) { + this._setDateFromField( inst, noDefault ); + } + return ( inst ? this._getDate( inst ) : null ); + }, + + /* Handle keystrokes. */ + _doKeyDown: function( event ) { + var onSelect, dateStr, sel, + inst = $.datepicker._getInst( event.target ), + handled = true, + isRTL = inst.dpDiv.is( ".ui-datepicker-rtl" ); + + inst._keyEvent = true; + if ( $.datepicker._datepickerShowing ) { + switch ( event.keyCode ) { + case 9: $.datepicker._hideDatepicker(); + handled = false; + break; // hide on tab out + case 13: sel = $( "td." + $.datepicker._dayOverClass + ":not(." + + $.datepicker._currentClass + ")", inst.dpDiv ); + if ( sel[ 0 ] ) { + $.datepicker._selectDay( event.target, inst.selectedMonth, inst.selectedYear, sel[ 0 ] ); + } + + onSelect = $.datepicker._get( inst, "onSelect" ); + if ( onSelect ) { + dateStr = $.datepicker._formatDate( inst ); + + // Trigger custom callback + onSelect.apply( ( inst.input ? inst.input[ 0 ] : null ), [ dateStr, inst ] ); + } else { + $.datepicker._hideDatepicker(); + } + + return false; // don't submit the form + case 27: $.datepicker._hideDatepicker(); + break; // hide on escape + case 33: $.datepicker._adjustDate( event.target, ( event.ctrlKey ? + -$.datepicker._get( inst, "stepBigMonths" ) : + -$.datepicker._get( inst, "stepMonths" ) ), "M" ); + break; // previous month/year on page up/+ ctrl + case 34: $.datepicker._adjustDate( event.target, ( event.ctrlKey ? + +$.datepicker._get( inst, "stepBigMonths" ) : + +$.datepicker._get( inst, "stepMonths" ) ), "M" ); + break; // next month/year on page down/+ ctrl + case 35: if ( event.ctrlKey || event.metaKey ) { + $.datepicker._clearDate( event.target ); + } + handled = event.ctrlKey || event.metaKey; + break; // clear on ctrl or command +end + case 36: if ( event.ctrlKey || event.metaKey ) { + $.datepicker._gotoToday( event.target ); + } + handled = event.ctrlKey || event.metaKey; + break; // current on ctrl or command +home + case 37: if ( event.ctrlKey || event.metaKey ) { + $.datepicker._adjustDate( event.target, ( isRTL ? +1 : -1 ), "D" ); + } + handled = event.ctrlKey || event.metaKey; + + // -1 day on ctrl or command +left + if ( event.originalEvent.altKey ) { + $.datepicker._adjustDate( event.target, ( event.ctrlKey ? + -$.datepicker._get( inst, "stepBigMonths" ) : + -$.datepicker._get( inst, "stepMonths" ) ), "M" ); + } + + // next month/year on alt +left on Mac + break; + case 38: if ( event.ctrlKey || event.metaKey ) { + $.datepicker._adjustDate( event.target, -7, "D" ); + } + handled = event.ctrlKey || event.metaKey; + break; // -1 week on ctrl or command +up + case 39: if ( event.ctrlKey || event.metaKey ) { + $.datepicker._adjustDate( event.target, ( isRTL ? -1 : +1 ), "D" ); + } + handled = event.ctrlKey || event.metaKey; + + // +1 day on ctrl or command +right + if ( event.originalEvent.altKey ) { + $.datepicker._adjustDate( event.target, ( event.ctrlKey ? + +$.datepicker._get( inst, "stepBigMonths" ) : + +$.datepicker._get( inst, "stepMonths" ) ), "M" ); + } + + // next month/year on alt +right + break; + case 40: if ( event.ctrlKey || event.metaKey ) { + $.datepicker._adjustDate( event.target, +7, "D" ); + } + handled = event.ctrlKey || event.metaKey; + break; // +1 week on ctrl or command +down + default: handled = false; + } + } else if ( event.keyCode === 36 && event.ctrlKey ) { // display the date picker on ctrl+home + $.datepicker._showDatepicker( this ); + } else { + handled = false; + } + + if ( handled ) { + event.preventDefault(); + event.stopPropagation(); + } + }, + + /* Filter entered characters - based on date format. */ + _doKeyPress: function( event ) { + var chars, chr, + inst = $.datepicker._getInst( event.target ); + + if ( $.datepicker._get( inst, "constrainInput" ) ) { + chars = $.datepicker._possibleChars( $.datepicker._get( inst, "dateFormat" ) ); + chr = String.fromCharCode( event.charCode == null ? event.keyCode : event.charCode ); + return event.ctrlKey || event.metaKey || ( chr < " " || !chars || chars.indexOf( chr ) > -1 ); + } + }, + + /* Synchronise manual entry and field/alternate field. */ + _doKeyUp: function( event ) { + var date, + inst = $.datepicker._getInst( event.target ); + + if ( inst.input.val() !== inst.lastVal ) { + try { + date = $.datepicker.parseDate( $.datepicker._get( inst, "dateFormat" ), + ( inst.input ? inst.input.val() : null ), + $.datepicker._getFormatConfig( inst ) ); + + if ( date ) { // only if valid + $.datepicker._setDateFromField( inst ); + $.datepicker._updateAlternate( inst ); + $.datepicker._updateDatepicker( inst ); + } + } + catch ( err ) { + } + } + return true; + }, + + /* Pop-up the date picker for a given input field. + * If false returned from beforeShow event handler do not show. + * @param input element - the input field attached to the date picker or + * event - if triggered by focus + */ + _showDatepicker: function( input ) { + input = input.target || input; + if ( input.nodeName.toLowerCase() !== "input" ) { // find from button/image trigger + input = $( "input", input.parentNode )[ 0 ]; + } + + if ( $.datepicker._isDisabledDatepicker( input ) || $.datepicker._lastInput === input ) { // already here + return; + } + + var inst, beforeShow, beforeShowSettings, isFixed, + offset, showAnim, duration; + + inst = $.datepicker._getInst( input ); + if ( $.datepicker._curInst && $.datepicker._curInst !== inst ) { + $.datepicker._curInst.dpDiv.stop( true, true ); + if ( inst && $.datepicker._datepickerShowing ) { + $.datepicker._hideDatepicker( $.datepicker._curInst.input[ 0 ] ); + } + } + + beforeShow = $.datepicker._get( inst, "beforeShow" ); + beforeShowSettings = beforeShow ? beforeShow.apply( input, [ input, inst ] ) : {}; + if ( beforeShowSettings === false ) { + return; + } + datepicker_extendRemove( inst.settings, beforeShowSettings ); + + inst.lastVal = null; + $.datepicker._lastInput = input; + $.datepicker._setDateFromField( inst ); + + if ( $.datepicker._inDialog ) { // hide cursor + input.value = ""; + } + if ( !$.datepicker._pos ) { // position below input + $.datepicker._pos = $.datepicker._findPos( input ); + $.datepicker._pos[ 1 ] += input.offsetHeight; // add the height + } + + isFixed = false; + $( input ).parents().each( function() { + isFixed |= $( this ).css( "position" ) === "fixed"; + return !isFixed; + } ); + + offset = { left: $.datepicker._pos[ 0 ], top: $.datepicker._pos[ 1 ] }; + $.datepicker._pos = null; + + //to avoid flashes on Firefox + inst.dpDiv.empty(); + + // determine sizing offscreen + inst.dpDiv.css( { position: "absolute", display: "block", top: "-1000px" } ); + $.datepicker._updateDatepicker( inst ); + + // fix width for dynamic number of date pickers + // and adjust position before showing + offset = $.datepicker._checkOffset( inst, offset, isFixed ); + inst.dpDiv.css( { position: ( $.datepicker._inDialog && $.blockUI ? + "static" : ( isFixed ? "fixed" : "absolute" ) ), display: "none", + left: offset.left + "px", top: offset.top + "px" } ); + + if ( !inst.inline ) { + showAnim = $.datepicker._get( inst, "showAnim" ); + duration = $.datepicker._get( inst, "duration" ); + inst.dpDiv.css( "z-index", datepicker_getZindex( $( input ) ) + 1 ); + $.datepicker._datepickerShowing = true; + + if ( $.effects && $.effects.effect[ showAnim ] ) { + inst.dpDiv.show( showAnim, $.datepicker._get( inst, "showOptions" ), duration ); + } else { + inst.dpDiv[ showAnim || "show" ]( showAnim ? duration : null ); + } + + if ( $.datepicker._shouldFocusInput( inst ) ) { + inst.input.trigger( "focus" ); + } + + $.datepicker._curInst = inst; + } + }, + + /* Generate the date picker content. */ + _updateDatepicker: function( inst ) { + this.maxRows = 4; //Reset the max number of rows being displayed (see #7043) + datepicker_instActive = inst; // for delegate hover events + inst.dpDiv.empty().append( this._generateHTML( inst ) ); + this._attachHandlers( inst ); + + var origyearshtml, + numMonths = this._getNumberOfMonths( inst ), + cols = numMonths[ 1 ], + width = 17, + activeCell = inst.dpDiv.find( "." + this._dayOverClass + " a" ); + + if ( activeCell.length > 0 ) { + datepicker_handleMouseover.apply( activeCell.get( 0 ) ); + } + + inst.dpDiv.removeClass( "ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4" ).width( "" ); + if ( cols > 1 ) { + inst.dpDiv.addClass( "ui-datepicker-multi-" + cols ).css( "width", ( width * cols ) + "em" ); + } + inst.dpDiv[ ( numMonths[ 0 ] !== 1 || numMonths[ 1 ] !== 1 ? "add" : "remove" ) + + "Class" ]( "ui-datepicker-multi" ); + inst.dpDiv[ ( this._get( inst, "isRTL" ) ? "add" : "remove" ) + + "Class" ]( "ui-datepicker-rtl" ); + + if ( inst === $.datepicker._curInst && $.datepicker._datepickerShowing && $.datepicker._shouldFocusInput( inst ) ) { + inst.input.trigger( "focus" ); + } + + // Deffered render of the years select (to avoid flashes on Firefox) + if ( inst.yearshtml ) { + origyearshtml = inst.yearshtml; + setTimeout( function() { + + //assure that inst.yearshtml didn't change. + if ( origyearshtml === inst.yearshtml && inst.yearshtml ) { + inst.dpDiv.find( "select.ui-datepicker-year:first" ).replaceWith( inst.yearshtml ); + } + origyearshtml = inst.yearshtml = null; + }, 0 ); + } + }, + + // #6694 - don't focus the input if it's already focused + // this breaks the change event in IE + // Support: IE and jQuery <1.9 + _shouldFocusInput: function( inst ) { + return inst.input && inst.input.is( ":visible" ) && !inst.input.is( ":disabled" ) && !inst.input.is( ":focus" ); + }, + + /* Check positioning to remain on screen. */ + _checkOffset: function( inst, offset, isFixed ) { + var dpWidth = inst.dpDiv.outerWidth(), + dpHeight = inst.dpDiv.outerHeight(), + inputWidth = inst.input ? inst.input.outerWidth() : 0, + inputHeight = inst.input ? inst.input.outerHeight() : 0, + viewWidth = document.documentElement.clientWidth + ( isFixed ? 0 : $( document ).scrollLeft() ), + viewHeight = document.documentElement.clientHeight + ( isFixed ? 0 : $( document ).scrollTop() ); + + offset.left -= ( this._get( inst, "isRTL" ) ? ( dpWidth - inputWidth ) : 0 ); + offset.left -= ( isFixed && offset.left === inst.input.offset().left ) ? $( document ).scrollLeft() : 0; + offset.top -= ( isFixed && offset.top === ( inst.input.offset().top + inputHeight ) ) ? $( document ).scrollTop() : 0; + + // Now check if datepicker is showing outside window viewport - move to a better place if so. + offset.left -= Math.min( offset.left, ( offset.left + dpWidth > viewWidth && viewWidth > dpWidth ) ? + Math.abs( offset.left + dpWidth - viewWidth ) : 0 ); + offset.top -= Math.min( offset.top, ( offset.top + dpHeight > viewHeight && viewHeight > dpHeight ) ? + Math.abs( dpHeight + inputHeight ) : 0 ); + + return offset; + }, + + /* Find an object's position on the screen. */ + _findPos: function( obj ) { + var position, + inst = this._getInst( obj ), + isRTL = this._get( inst, "isRTL" ); + + while ( obj && ( obj.type === "hidden" || obj.nodeType !== 1 || $.expr.filters.hidden( obj ) ) ) { + obj = obj[ isRTL ? "previousSibling" : "nextSibling" ]; + } + + position = $( obj ).offset(); + return [ position.left, position.top ]; + }, + + /* Hide the date picker from view. + * @param input element - the input field attached to the date picker + */ + _hideDatepicker: function( input ) { + var showAnim, duration, postProcess, onClose, + inst = this._curInst; + + if ( !inst || ( input && inst !== $.data( input, "datepicker" ) ) ) { + return; + } + + if ( this._datepickerShowing ) { + showAnim = this._get( inst, "showAnim" ); + duration = this._get( inst, "duration" ); + postProcess = function() { + $.datepicker._tidyDialog( inst ); + }; + + // DEPRECATED: after BC for 1.8.x $.effects[ showAnim ] is not needed + if ( $.effects && ( $.effects.effect[ showAnim ] || $.effects[ showAnim ] ) ) { + inst.dpDiv.hide( showAnim, $.datepicker._get( inst, "showOptions" ), duration, postProcess ); + } else { + inst.dpDiv[ ( showAnim === "slideDown" ? "slideUp" : + ( showAnim === "fadeIn" ? "fadeOut" : "hide" ) ) ]( ( showAnim ? duration : null ), postProcess ); + } + + if ( !showAnim ) { + postProcess(); + } + this._datepickerShowing = false; + + onClose = this._get( inst, "onClose" ); + if ( onClose ) { + onClose.apply( ( inst.input ? inst.input[ 0 ] : null ), [ ( inst.input ? inst.input.val() : "" ), inst ] ); + } + + this._lastInput = null; + if ( this._inDialog ) { + this._dialogInput.css( { position: "absolute", left: "0", top: "-100px" } ); + if ( $.blockUI ) { + $.unblockUI(); + $( "body" ).append( this.dpDiv ); + } + } + this._inDialog = false; + } + }, + + /* Tidy up after a dialog display. */ + _tidyDialog: function( inst ) { + inst.dpDiv.removeClass( this._dialogClass ).off( ".ui-datepicker-calendar" ); + }, + + /* Close date picker if clicked elsewhere. */ + _checkExternalClick: function( event ) { + if ( !$.datepicker._curInst ) { + return; + } + + var $target = $( event.target ), + inst = $.datepicker._getInst( $target[ 0 ] ); + + if ( ( ( $target[ 0 ].id !== $.datepicker._mainDivId && + $target.parents( "#" + $.datepicker._mainDivId ).length === 0 && + !$target.hasClass( $.datepicker.markerClassName ) && + !$target.closest( "." + $.datepicker._triggerClass ).length && + $.datepicker._datepickerShowing && !( $.datepicker._inDialog && $.blockUI ) ) ) || + ( $target.hasClass( $.datepicker.markerClassName ) && $.datepicker._curInst !== inst ) ) { + $.datepicker._hideDatepicker(); + } + }, + + /* Adjust one of the date sub-fields. */ + _adjustDate: function( id, offset, period ) { + var target = $( id ), + inst = this._getInst( target[ 0 ] ); + + if ( this._isDisabledDatepicker( target[ 0 ] ) ) { + return; + } + this._adjustInstDate( inst, offset + + ( period === "M" ? this._get( inst, "showCurrentAtPos" ) : 0 ), // undo positioning + period ); + this._updateDatepicker( inst ); + }, + + /* Action for current link. */ + _gotoToday: function( id ) { + var date, + target = $( id ), + inst = this._getInst( target[ 0 ] ); + + if ( this._get( inst, "gotoCurrent" ) && inst.currentDay ) { + inst.selectedDay = inst.currentDay; + inst.drawMonth = inst.selectedMonth = inst.currentMonth; + inst.drawYear = inst.selectedYear = inst.currentYear; + } else { + date = new Date(); + inst.selectedDay = date.getDate(); + inst.drawMonth = inst.selectedMonth = date.getMonth(); + inst.drawYear = inst.selectedYear = date.getFullYear(); + } + this._notifyChange( inst ); + this._adjustDate( target ); + }, + + /* Action for selecting a new month/year. */ + _selectMonthYear: function( id, select, period ) { + var target = $( id ), + inst = this._getInst( target[ 0 ] ); + + inst[ "selected" + ( period === "M" ? "Month" : "Year" ) ] = + inst[ "draw" + ( period === "M" ? "Month" : "Year" ) ] = + parseInt( select.options[ select.selectedIndex ].value, 10 ); + + this._notifyChange( inst ); + this._adjustDate( target ); + }, + + /* Action for selecting a day. */ + _selectDay: function( id, month, year, td ) { + var inst, + target = $( id ); + + if ( $( td ).hasClass( this._unselectableClass ) || this._isDisabledDatepicker( target[ 0 ] ) ) { + return; + } + + inst = this._getInst( target[ 0 ] ); + inst.selectedDay = inst.currentDay = $( "a", td ).html(); + inst.selectedMonth = inst.currentMonth = month; + inst.selectedYear = inst.currentYear = year; + this._selectDate( id, this._formatDate( inst, + inst.currentDay, inst.currentMonth, inst.currentYear ) ); + }, + + /* Erase the input field and hide the date picker. */ + _clearDate: function( id ) { + var target = $( id ); + this._selectDate( target, "" ); + }, + + /* Update the input field with the selected date. */ + _selectDate: function( id, dateStr ) { + var onSelect, + target = $( id ), + inst = this._getInst( target[ 0 ] ); + + dateStr = ( dateStr != null ? dateStr : this._formatDate( inst ) ); + if ( inst.input ) { + inst.input.val( dateStr ); + } + this._updateAlternate( inst ); + + onSelect = this._get( inst, "onSelect" ); + if ( onSelect ) { + onSelect.apply( ( inst.input ? inst.input[ 0 ] : null ), [ dateStr, inst ] ); // trigger custom callback + } else if ( inst.input ) { + inst.input.trigger( "change" ); // fire the change event + } + + if ( inst.inline ) { + this._updateDatepicker( inst ); + } else { + this._hideDatepicker(); + this._lastInput = inst.input[ 0 ]; + if ( typeof( inst.input[ 0 ] ) !== "object" ) { + inst.input.trigger( "focus" ); // restore focus + } + this._lastInput = null; + } + }, + + /* Update any alternate field to synchronise with the main field. */ + _updateAlternate: function( inst ) { + var altFormat, date, dateStr, + altField = this._get( inst, "altField" ); + + if ( altField ) { // update alternate field too + altFormat = this._get( inst, "altFormat" ) || this._get( inst, "dateFormat" ); + date = this._getDate( inst ); + dateStr = this.formatDate( altFormat, date, this._getFormatConfig( inst ) ); + $( altField ).val( dateStr ); + } + }, + + /* Set as beforeShowDay function to prevent selection of weekends. + * @param date Date - the date to customise + * @return [boolean, string] - is this date selectable?, what is its CSS class? + */ + noWeekends: function( date ) { + var day = date.getDay(); + return [ ( day > 0 && day < 6 ), "" ]; + }, + + /* Set as calculateWeek to determine the week of the year based on the ISO 8601 definition. + * @param date Date - the date to get the week for + * @return number - the number of the week within the year that contains this date + */ + iso8601Week: function( date ) { + var time, + checkDate = new Date( date.getTime() ); + + // Find Thursday of this week starting on Monday + checkDate.setDate( checkDate.getDate() + 4 - ( checkDate.getDay() || 7 ) ); + + time = checkDate.getTime(); + checkDate.setMonth( 0 ); // Compare with Jan 1 + checkDate.setDate( 1 ); + return Math.floor( Math.round( ( time - checkDate ) / 86400000 ) / 7 ) + 1; + }, + + /* Parse a string value into a date object. + * See formatDate below for the possible formats. + * + * @param format string - the expected format of the date + * @param value string - the date in the above format + * @param settings Object - attributes include: + * shortYearCutoff number - the cutoff year for determining the century (optional) + * dayNamesShort string[7] - abbreviated names of the days from Sunday (optional) + * dayNames string[7] - names of the days from Sunday (optional) + * monthNamesShort string[12] - abbreviated names of the months (optional) + * monthNames string[12] - names of the months (optional) + * @return Date - the extracted date value or null if value is blank + */ + parseDate: function( format, value, settings ) { + if ( format == null || value == null ) { + throw "Invalid arguments"; + } + + value = ( typeof value === "object" ? value.toString() : value + "" ); + if ( value === "" ) { + return null; + } + + var iFormat, dim, extra, + iValue = 0, + shortYearCutoffTemp = ( settings ? settings.shortYearCutoff : null ) || this._defaults.shortYearCutoff, + shortYearCutoff = ( typeof shortYearCutoffTemp !== "string" ? shortYearCutoffTemp : + new Date().getFullYear() % 100 + parseInt( shortYearCutoffTemp, 10 ) ), + dayNamesShort = ( settings ? settings.dayNamesShort : null ) || this._defaults.dayNamesShort, + dayNames = ( settings ? settings.dayNames : null ) || this._defaults.dayNames, + monthNamesShort = ( settings ? settings.monthNamesShort : null ) || this._defaults.monthNamesShort, + monthNames = ( settings ? settings.monthNames : null ) || this._defaults.monthNames, + year = -1, + month = -1, + day = -1, + doy = -1, + literal = false, + date, + + // Check whether a format character is doubled + lookAhead = function( match ) { + var matches = ( iFormat + 1 < format.length && format.charAt( iFormat + 1 ) === match ); + if ( matches ) { + iFormat++; + } + return matches; + }, + + // Extract a number from the string value + getNumber = function( match ) { + var isDoubled = lookAhead( match ), + size = ( match === "@" ? 14 : ( match === "!" ? 20 : + ( match === "y" && isDoubled ? 4 : ( match === "o" ? 3 : 2 ) ) ) ), + minSize = ( match === "y" ? size : 1 ), + digits = new RegExp( "^\\d{" + minSize + "," + size + "}" ), + num = value.substring( iValue ).match( digits ); + if ( !num ) { + throw "Missing number at position " + iValue; + } + iValue += num[ 0 ].length; + return parseInt( num[ 0 ], 10 ); + }, + + // Extract a name from the string value and convert to an index + getName = function( match, shortNames, longNames ) { + var index = -1, + names = $.map( lookAhead( match ) ? longNames : shortNames, function( v, k ) { + return [ [ k, v ] ]; + } ).sort( function( a, b ) { + return -( a[ 1 ].length - b[ 1 ].length ); + } ); + + $.each( names, function( i, pair ) { + var name = pair[ 1 ]; + if ( value.substr( iValue, name.length ).toLowerCase() === name.toLowerCase() ) { + index = pair[ 0 ]; + iValue += name.length; + return false; + } + } ); + if ( index !== -1 ) { + return index + 1; + } else { + throw "Unknown name at position " + iValue; + } + }, + + // Confirm that a literal character matches the string value + checkLiteral = function() { + if ( value.charAt( iValue ) !== format.charAt( iFormat ) ) { + throw "Unexpected literal at position " + iValue; + } + iValue++; + }; + + for ( iFormat = 0; iFormat < format.length; iFormat++ ) { + if ( literal ) { + if ( format.charAt( iFormat ) === "'" && !lookAhead( "'" ) ) { + literal = false; + } else { + checkLiteral(); + } + } else { + switch ( format.charAt( iFormat ) ) { + case "d": + day = getNumber( "d" ); + break; + case "D": + getName( "D", dayNamesShort, dayNames ); + break; + case "o": + doy = getNumber( "o" ); + break; + case "m": + month = getNumber( "m" ); + break; + case "M": + month = getName( "M", monthNamesShort, monthNames ); + break; + case "y": + year = getNumber( "y" ); + break; + case "@": + date = new Date( getNumber( "@" ) ); + year = date.getFullYear(); + month = date.getMonth() + 1; + day = date.getDate(); + break; + case "!": + date = new Date( ( getNumber( "!" ) - this._ticksTo1970 ) / 10000 ); + year = date.getFullYear(); + month = date.getMonth() + 1; + day = date.getDate(); + break; + case "'": + if ( lookAhead( "'" ) ) { + checkLiteral(); + } else { + literal = true; + } + break; + default: + checkLiteral(); + } + } + } + + if ( iValue < value.length ) { + extra = value.substr( iValue ); + if ( !/^\s+/.test( extra ) ) { + throw "Extra/unparsed characters found in date: " + extra; + } + } + + if ( year === -1 ) { + year = new Date().getFullYear(); + } else if ( year < 100 ) { + year += new Date().getFullYear() - new Date().getFullYear() % 100 + + ( year <= shortYearCutoff ? 0 : -100 ); + } + + if ( doy > -1 ) { + month = 1; + day = doy; + do { + dim = this._getDaysInMonth( year, month - 1 ); + if ( day <= dim ) { + break; + } + month++; + day -= dim; + } while ( true ); + } + + date = this._daylightSavingAdjust( new Date( year, month - 1, day ) ); + if ( date.getFullYear() !== year || date.getMonth() + 1 !== month || date.getDate() !== day ) { + throw "Invalid date"; // E.g. 31/02/00 + } + return date; + }, + + /* Standard date formats. */ + ATOM: "yy-mm-dd", // RFC 3339 (ISO 8601) + COOKIE: "D, dd M yy", + ISO_8601: "yy-mm-dd", + RFC_822: "D, d M y", + RFC_850: "DD, dd-M-y", + RFC_1036: "D, d M y", + RFC_1123: "D, d M yy", + RFC_2822: "D, d M yy", + RSS: "D, d M y", // RFC 822 + TICKS: "!", + TIMESTAMP: "@", + W3C: "yy-mm-dd", // ISO 8601 + + _ticksTo1970: ( ( ( 1970 - 1 ) * 365 + Math.floor( 1970 / 4 ) - Math.floor( 1970 / 100 ) + + Math.floor( 1970 / 400 ) ) * 24 * 60 * 60 * 10000000 ), + + /* Format a date object into a string value. + * The format can be combinations of the following: + * d - day of month (no leading zero) + * dd - day of month (two digit) + * o - day of year (no leading zeros) + * oo - day of year (three digit) + * D - day name short + * DD - day name long + * m - month of year (no leading zero) + * mm - month of year (two digit) + * M - month name short + * MM - month name long + * y - year (two digit) + * yy - year (four digit) + * @ - Unix timestamp (ms since 01/01/1970) + * ! - Windows ticks (100ns since 01/01/0001) + * "..." - literal text + * '' - single quote + * + * @param format string - the desired format of the date + * @param date Date - the date value to format + * @param settings Object - attributes include: + * dayNamesShort string[7] - abbreviated names of the days from Sunday (optional) + * dayNames string[7] - names of the days from Sunday (optional) + * monthNamesShort string[12] - abbreviated names of the months (optional) + * monthNames string[12] - names of the months (optional) + * @return string - the date in the above format + */ + formatDate: function( format, date, settings ) { + if ( !date ) { + return ""; + } + + var iFormat, + dayNamesShort = ( settings ? settings.dayNamesShort : null ) || this._defaults.dayNamesShort, + dayNames = ( settings ? settings.dayNames : null ) || this._defaults.dayNames, + monthNamesShort = ( settings ? settings.monthNamesShort : null ) || this._defaults.monthNamesShort, + monthNames = ( settings ? settings.monthNames : null ) || this._defaults.monthNames, + + // Check whether a format character is doubled + lookAhead = function( match ) { + var matches = ( iFormat + 1 < format.length && format.charAt( iFormat + 1 ) === match ); + if ( matches ) { + iFormat++; + } + return matches; + }, + + // Format a number, with leading zero if necessary + formatNumber = function( match, value, len ) { + var num = "" + value; + if ( lookAhead( match ) ) { + while ( num.length < len ) { + num = "0" + num; + } + } + return num; + }, + + // Format a name, short or long as requested + formatName = function( match, value, shortNames, longNames ) { + return ( lookAhead( match ) ? longNames[ value ] : shortNames[ value ] ); + }, + output = "", + literal = false; + + if ( date ) { + for ( iFormat = 0; iFormat < format.length; iFormat++ ) { + if ( literal ) { + if ( format.charAt( iFormat ) === "'" && !lookAhead( "'" ) ) { + literal = false; + } else { + output += format.charAt( iFormat ); + } + } else { + switch ( format.charAt( iFormat ) ) { + case "d": + output += formatNumber( "d", date.getDate(), 2 ); + break; + case "D": + output += formatName( "D", date.getDay(), dayNamesShort, dayNames ); + break; + case "o": + output += formatNumber( "o", + Math.round( ( new Date( date.getFullYear(), date.getMonth(), date.getDate() ).getTime() - new Date( date.getFullYear(), 0, 0 ).getTime() ) / 86400000 ), 3 ); + break; + case "m": + output += formatNumber( "m", date.getMonth() + 1, 2 ); + break; + case "M": + output += formatName( "M", date.getMonth(), monthNamesShort, monthNames ); + break; + case "y": + output += ( lookAhead( "y" ) ? date.getFullYear() : + ( date.getFullYear() % 100 < 10 ? "0" : "" ) + date.getFullYear() % 100 ); + break; + case "@": + output += date.getTime(); + break; + case "!": + output += date.getTime() * 10000 + this._ticksTo1970; + break; + case "'": + if ( lookAhead( "'" ) ) { + output += "'"; + } else { + literal = true; + } + break; + default: + output += format.charAt( iFormat ); + } + } + } + } + return output; + }, + + /* Extract all possible characters from the date format. */ + _possibleChars: function( format ) { + var iFormat, + chars = "", + literal = false, + + // Check whether a format character is doubled + lookAhead = function( match ) { + var matches = ( iFormat + 1 < format.length && format.charAt( iFormat + 1 ) === match ); + if ( matches ) { + iFormat++; + } + return matches; + }; + + for ( iFormat = 0; iFormat < format.length; iFormat++ ) { + if ( literal ) { + if ( format.charAt( iFormat ) === "'" && !lookAhead( "'" ) ) { + literal = false; + } else { + chars += format.charAt( iFormat ); + } + } else { + switch ( format.charAt( iFormat ) ) { + case "d": case "m": case "y": case "@": + chars += "0123456789"; + break; + case "D": case "M": + return null; // Accept anything + case "'": + if ( lookAhead( "'" ) ) { + chars += "'"; + } else { + literal = true; + } + break; + default: + chars += format.charAt( iFormat ); + } + } + } + return chars; + }, + + /* Get a setting value, defaulting if necessary. */ + _get: function( inst, name ) { + return inst.settings[ name ] !== undefined ? + inst.settings[ name ] : this._defaults[ name ]; + }, + + /* Parse existing date and initialise date picker. */ + _setDateFromField: function( inst, noDefault ) { + if ( inst.input.val() === inst.lastVal ) { + return; + } + + var dateFormat = this._get( inst, "dateFormat" ), + dates = inst.lastVal = inst.input ? inst.input.val() : null, + defaultDate = this._getDefaultDate( inst ), + date = defaultDate, + settings = this._getFormatConfig( inst ); + + try { + date = this.parseDate( dateFormat, dates, settings ) || defaultDate; + } catch ( event ) { + dates = ( noDefault ? "" : dates ); + } + inst.selectedDay = date.getDate(); + inst.drawMonth = inst.selectedMonth = date.getMonth(); + inst.drawYear = inst.selectedYear = date.getFullYear(); + inst.currentDay = ( dates ? date.getDate() : 0 ); + inst.currentMonth = ( dates ? date.getMonth() : 0 ); + inst.currentYear = ( dates ? date.getFullYear() : 0 ); + this._adjustInstDate( inst ); + }, + + /* Retrieve the default date shown on opening. */ + _getDefaultDate: function( inst ) { + return this._restrictMinMax( inst, + this._determineDate( inst, this._get( inst, "defaultDate" ), new Date() ) ); + }, + + /* A date may be specified as an exact value or a relative one. */ + _determineDate: function( inst, date, defaultDate ) { + var offsetNumeric = function( offset ) { + var date = new Date(); + date.setDate( date.getDate() + offset ); + return date; + }, + offsetString = function( offset ) { + try { + return $.datepicker.parseDate( $.datepicker._get( inst, "dateFormat" ), + offset, $.datepicker._getFormatConfig( inst ) ); + } + catch ( e ) { + + // Ignore + } + + var date = ( offset.toLowerCase().match( /^c/ ) ? + $.datepicker._getDate( inst ) : null ) || new Date(), + year = date.getFullYear(), + month = date.getMonth(), + day = date.getDate(), + pattern = /([+\-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g, + matches = pattern.exec( offset ); + + while ( matches ) { + switch ( matches[ 2 ] || "d" ) { + case "d" : case "D" : + day += parseInt( matches[ 1 ], 10 ); break; + case "w" : case "W" : + day += parseInt( matches[ 1 ], 10 ) * 7; break; + case "m" : case "M" : + month += parseInt( matches[ 1 ], 10 ); + day = Math.min( day, $.datepicker._getDaysInMonth( year, month ) ); + break; + case "y": case "Y" : + year += parseInt( matches[ 1 ], 10 ); + day = Math.min( day, $.datepicker._getDaysInMonth( year, month ) ); + break; + } + matches = pattern.exec( offset ); + } + return new Date( year, month, day ); + }, + newDate = ( date == null || date === "" ? defaultDate : ( typeof date === "string" ? offsetString( date ) : + ( typeof date === "number" ? ( isNaN( date ) ? defaultDate : offsetNumeric( date ) ) : new Date( date.getTime() ) ) ) ); + + newDate = ( newDate && newDate.toString() === "Invalid Date" ? defaultDate : newDate ); + if ( newDate ) { + newDate.setHours( 0 ); + newDate.setMinutes( 0 ); + newDate.setSeconds( 0 ); + newDate.setMilliseconds( 0 ); + } + return this._daylightSavingAdjust( newDate ); + }, + + /* Handle switch to/from daylight saving. + * Hours may be non-zero on daylight saving cut-over: + * > 12 when midnight changeover, but then cannot generate + * midnight datetime, so jump to 1AM, otherwise reset. + * @param date (Date) the date to check + * @return (Date) the corrected date + */ + _daylightSavingAdjust: function( date ) { + if ( !date ) { + return null; + } + date.setHours( date.getHours() > 12 ? date.getHours() + 2 : 0 ); + return date; + }, + + /* Set the date(s) directly. */ + _setDate: function( inst, date, noChange ) { + var clear = !date, + origMonth = inst.selectedMonth, + origYear = inst.selectedYear, + newDate = this._restrictMinMax( inst, this._determineDate( inst, date, new Date() ) ); + + inst.selectedDay = inst.currentDay = newDate.getDate(); + inst.drawMonth = inst.selectedMonth = inst.currentMonth = newDate.getMonth(); + inst.drawYear = inst.selectedYear = inst.currentYear = newDate.getFullYear(); + if ( ( origMonth !== inst.selectedMonth || origYear !== inst.selectedYear ) && !noChange ) { + this._notifyChange( inst ); + } + this._adjustInstDate( inst ); + if ( inst.input ) { + inst.input.val( clear ? "" : this._formatDate( inst ) ); + } + }, + + /* Retrieve the date(s) directly. */ + _getDate: function( inst ) { + var startDate = ( !inst.currentYear || ( inst.input && inst.input.val() === "" ) ? null : + this._daylightSavingAdjust( new Date( + inst.currentYear, inst.currentMonth, inst.currentDay ) ) ); + return startDate; + }, + + /* Attach the onxxx handlers. These are declared statically so + * they work with static code transformers like Caja. + */ + _attachHandlers: function( inst ) { + var stepMonths = this._get( inst, "stepMonths" ), + id = "#" + inst.id.replace( /\\\\/g, "\\" ); + inst.dpDiv.find( "[data-handler]" ).map( function() { + var handler = { + prev: function() { + $.datepicker._adjustDate( id, -stepMonths, "M" ); + }, + next: function() { + $.datepicker._adjustDate( id, +stepMonths, "M" ); + }, + hide: function() { + $.datepicker._hideDatepicker(); + }, + today: function() { + $.datepicker._gotoToday( id ); + }, + selectDay: function() { + $.datepicker._selectDay( id, +this.getAttribute( "data-month" ), +this.getAttribute( "data-year" ), this ); + return false; + }, + selectMonth: function() { + $.datepicker._selectMonthYear( id, this, "M" ); + return false; + }, + selectYear: function() { + $.datepicker._selectMonthYear( id, this, "Y" ); + return false; + } + }; + $( this ).on( this.getAttribute( "data-event" ), handler[ this.getAttribute( "data-handler" ) ] ); + } ); + }, + + /* Generate the HTML for the current state of the date picker. */ + _generateHTML: function( inst ) { + var maxDraw, prevText, prev, nextText, next, currentText, gotoDate, + controls, buttonPanel, firstDay, showWeek, dayNames, dayNamesMin, + monthNames, monthNamesShort, beforeShowDay, showOtherMonths, + selectOtherMonths, defaultDate, html, dow, row, group, col, selectedDate, + cornerClass, calender, thead, day, daysInMonth, leadDays, curRows, numRows, + printDate, dRow, tbody, daySettings, otherMonth, unselectable, + tempDate = new Date(), + today = this._daylightSavingAdjust( + new Date( tempDate.getFullYear(), tempDate.getMonth(), tempDate.getDate() ) ), // clear time + isRTL = this._get( inst, "isRTL" ), + showButtonPanel = this._get( inst, "showButtonPanel" ), + hideIfNoPrevNext = this._get( inst, "hideIfNoPrevNext" ), + navigationAsDateFormat = this._get( inst, "navigationAsDateFormat" ), + numMonths = this._getNumberOfMonths( inst ), + showCurrentAtPos = this._get( inst, "showCurrentAtPos" ), + stepMonths = this._get( inst, "stepMonths" ), + isMultiMonth = ( numMonths[ 0 ] !== 1 || numMonths[ 1 ] !== 1 ), + currentDate = this._daylightSavingAdjust( ( !inst.currentDay ? new Date( 9999, 9, 9 ) : + new Date( inst.currentYear, inst.currentMonth, inst.currentDay ) ) ), + minDate = this._getMinMaxDate( inst, "min" ), + maxDate = this._getMinMaxDate( inst, "max" ), + drawMonth = inst.drawMonth - showCurrentAtPos, + drawYear = inst.drawYear; + + if ( drawMonth < 0 ) { + drawMonth += 12; + drawYear--; + } + if ( maxDate ) { + maxDraw = this._daylightSavingAdjust( new Date( maxDate.getFullYear(), + maxDate.getMonth() - ( numMonths[ 0 ] * numMonths[ 1 ] ) + 1, maxDate.getDate() ) ); + maxDraw = ( minDate && maxDraw < minDate ? minDate : maxDraw ); + while ( this._daylightSavingAdjust( new Date( drawYear, drawMonth, 1 ) ) > maxDraw ) { + drawMonth--; + if ( drawMonth < 0 ) { + drawMonth = 11; + drawYear--; + } + } + } + inst.drawMonth = drawMonth; + inst.drawYear = drawYear; + + prevText = this._get( inst, "prevText" ); + prevText = ( !navigationAsDateFormat ? prevText : this.formatDate( prevText, + this._daylightSavingAdjust( new Date( drawYear, drawMonth - stepMonths, 1 ) ), + this._getFormatConfig( inst ) ) ); + + prev = ( this._canAdjustMonth( inst, -1, drawYear, drawMonth ) ? + "<a class='ui-datepicker-prev ui-corner-all' data-handler='prev' data-event='click'" + + " title='" + prevText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "e" : "w" ) + "'>" + prevText + "</span></a>" : + ( hideIfNoPrevNext ? "" : "<a class='ui-datepicker-prev ui-corner-all ui-state-disabled' title='" + prevText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "e" : "w" ) + "'>" + prevText + "</span></a>" ) ); + + nextText = this._get( inst, "nextText" ); + nextText = ( !navigationAsDateFormat ? nextText : this.formatDate( nextText, + this._daylightSavingAdjust( new Date( drawYear, drawMonth + stepMonths, 1 ) ), + this._getFormatConfig( inst ) ) ); + + next = ( this._canAdjustMonth( inst, +1, drawYear, drawMonth ) ? + "<a class='ui-datepicker-next ui-corner-all' data-handler='next' data-event='click'" + + " title='" + nextText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "w" : "e" ) + "'>" + nextText + "</span></a>" : + ( hideIfNoPrevNext ? "" : "<a class='ui-datepicker-next ui-corner-all ui-state-disabled' title='" + nextText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "w" : "e" ) + "'>" + nextText + "</span></a>" ) ); + + currentText = this._get( inst, "currentText" ); + gotoDate = ( this._get( inst, "gotoCurrent" ) && inst.currentDay ? currentDate : today ); + currentText = ( !navigationAsDateFormat ? currentText : + this.formatDate( currentText, gotoDate, this._getFormatConfig( inst ) ) ); + + controls = ( !inst.inline ? "<button type='button' class='ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all' data-handler='hide' data-event='click'>" + + this._get( inst, "closeText" ) + "</button>" : "" ); + + buttonPanel = ( showButtonPanel ) ? "<div class='ui-datepicker-buttonpane ui-widget-content'>" + ( isRTL ? controls : "" ) + + ( this._isInRange( inst, gotoDate ) ? "<button type='button' class='ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all' data-handler='today' data-event='click'" + + ">" + currentText + "</button>" : "" ) + ( isRTL ? "" : controls ) + "</div>" : ""; + + firstDay = parseInt( this._get( inst, "firstDay" ), 10 ); + firstDay = ( isNaN( firstDay ) ? 0 : firstDay ); + + showWeek = this._get( inst, "showWeek" ); + dayNames = this._get( inst, "dayNames" ); + dayNamesMin = this._get( inst, "dayNamesMin" ); + monthNames = this._get( inst, "monthNames" ); + monthNamesShort = this._get( inst, "monthNamesShort" ); + beforeShowDay = this._get( inst, "beforeShowDay" ); + showOtherMonths = this._get( inst, "showOtherMonths" ); + selectOtherMonths = this._get( inst, "selectOtherMonths" ); + defaultDate = this._getDefaultDate( inst ); + html = ""; + + for ( row = 0; row < numMonths[ 0 ]; row++ ) { + group = ""; + this.maxRows = 4; + for ( col = 0; col < numMonths[ 1 ]; col++ ) { + selectedDate = this._daylightSavingAdjust( new Date( drawYear, drawMonth, inst.selectedDay ) ); + cornerClass = " ui-corner-all"; + calender = ""; + if ( isMultiMonth ) { + calender += "<div class='ui-datepicker-group"; + if ( numMonths[ 1 ] > 1 ) { + switch ( col ) { + case 0: calender += " ui-datepicker-group-first"; + cornerClass = " ui-corner-" + ( isRTL ? "right" : "left" ); break; + case numMonths[ 1 ] - 1: calender += " ui-datepicker-group-last"; + cornerClass = " ui-corner-" + ( isRTL ? "left" : "right" ); break; + default: calender += " ui-datepicker-group-middle"; cornerClass = ""; break; + } + } + calender += "'>"; + } + calender += "<div class='ui-datepicker-header ui-widget-header ui-helper-clearfix" + cornerClass + "'>" + + ( /all|left/.test( cornerClass ) && row === 0 ? ( isRTL ? next : prev ) : "" ) + + ( /all|right/.test( cornerClass ) && row === 0 ? ( isRTL ? prev : next ) : "" ) + + this._generateMonthYearHeader( inst, drawMonth, drawYear, minDate, maxDate, + row > 0 || col > 0, monthNames, monthNamesShort ) + // draw month headers + "</div><table class='ui-datepicker-calendar'><thead>" + + "<tr>"; + thead = ( showWeek ? "<th class='ui-datepicker-week-col'>" + this._get( inst, "weekHeader" ) + "</th>" : "" ); + for ( dow = 0; dow < 7; dow++ ) { // days of the week + day = ( dow + firstDay ) % 7; + thead += "<th scope='col'" + ( ( dow + firstDay + 6 ) % 7 >= 5 ? " class='ui-datepicker-week-end'" : "" ) + ">" + + "<span title='" + dayNames[ day ] + "'>" + dayNamesMin[ day ] + "</span></th>"; + } + calender += thead + "</tr></thead><tbody>"; + daysInMonth = this._getDaysInMonth( drawYear, drawMonth ); + if ( drawYear === inst.selectedYear && drawMonth === inst.selectedMonth ) { + inst.selectedDay = Math.min( inst.selectedDay, daysInMonth ); + } + leadDays = ( this._getFirstDayOfMonth( drawYear, drawMonth ) - firstDay + 7 ) % 7; + curRows = Math.ceil( ( leadDays + daysInMonth ) / 7 ); // calculate the number of rows to generate + numRows = ( isMultiMonth ? this.maxRows > curRows ? this.maxRows : curRows : curRows ); //If multiple months, use the higher number of rows (see #7043) + this.maxRows = numRows; + printDate = this._daylightSavingAdjust( new Date( drawYear, drawMonth, 1 - leadDays ) ); + for ( dRow = 0; dRow < numRows; dRow++ ) { // create date picker rows + calender += "<tr>"; + tbody = ( !showWeek ? "" : "<td class='ui-datepicker-week-col'>" + + this._get( inst, "calculateWeek" )( printDate ) + "</td>" ); + for ( dow = 0; dow < 7; dow++ ) { // create date picker days + daySettings = ( beforeShowDay ? + beforeShowDay.apply( ( inst.input ? inst.input[ 0 ] : null ), [ printDate ] ) : [ true, "" ] ); + otherMonth = ( printDate.getMonth() !== drawMonth ); + unselectable = ( otherMonth && !selectOtherMonths ) || !daySettings[ 0 ] || + ( minDate && printDate < minDate ) || ( maxDate && printDate > maxDate ); + tbody += "<td class='" + + ( ( dow + firstDay + 6 ) % 7 >= 5 ? " ui-datepicker-week-end" : "" ) + // highlight weekends + ( otherMonth ? " ui-datepicker-other-month" : "" ) + // highlight days from other months + ( ( printDate.getTime() === selectedDate.getTime() && drawMonth === inst.selectedMonth && inst._keyEvent ) || // user pressed key + ( defaultDate.getTime() === printDate.getTime() && defaultDate.getTime() === selectedDate.getTime() ) ? + + // or defaultDate is current printedDate and defaultDate is selectedDate + " " + this._dayOverClass : "" ) + // highlight selected day + ( unselectable ? " " + this._unselectableClass + " ui-state-disabled" : "" ) + // highlight unselectable days + ( otherMonth && !showOtherMonths ? "" : " " + daySettings[ 1 ] + // highlight custom dates + ( printDate.getTime() === currentDate.getTime() ? " " + this._currentClass : "" ) + // highlight selected day + ( printDate.getTime() === today.getTime() ? " ui-datepicker-today" : "" ) ) + "'" + // highlight today (if different) + ( ( !otherMonth || showOtherMonths ) && daySettings[ 2 ] ? " title='" + daySettings[ 2 ].replace( /'/g, "'" ) + "'" : "" ) + // cell title + ( unselectable ? "" : " data-handler='selectDay' data-event='click' data-month='" + printDate.getMonth() + "' data-year='" + printDate.getFullYear() + "'" ) + ">" + // actions + ( otherMonth && !showOtherMonths ? " " : // display for other months + ( unselectable ? "<span class='ui-state-default'>" + printDate.getDate() + "</span>" : "<a class='ui-state-default" + + ( printDate.getTime() === today.getTime() ? " ui-state-highlight" : "" ) + + ( printDate.getTime() === currentDate.getTime() ? " ui-state-active" : "" ) + // highlight selected day + ( otherMonth ? " ui-priority-secondary" : "" ) + // distinguish dates from other months + "' href='#'>" + printDate.getDate() + "</a>" ) ) + "</td>"; // display selectable date + printDate.setDate( printDate.getDate() + 1 ); + printDate = this._daylightSavingAdjust( printDate ); + } + calender += tbody + "</tr>"; + } + drawMonth++; + if ( drawMonth > 11 ) { + drawMonth = 0; + drawYear++; + } + calender += "</tbody></table>" + ( isMultiMonth ? "</div>" + + ( ( numMonths[ 0 ] > 0 && col === numMonths[ 1 ] - 1 ) ? "<div class='ui-datepicker-row-break'></div>" : "" ) : "" ); + group += calender; + } + html += group; + } + html += buttonPanel; + inst._keyEvent = false; + return html; + }, + + /* Generate the month and year header. */ + _generateMonthYearHeader: function( inst, drawMonth, drawYear, minDate, maxDate, + secondary, monthNames, monthNamesShort ) { + + var inMinYear, inMaxYear, month, years, thisYear, determineYear, year, endYear, + changeMonth = this._get( inst, "changeMonth" ), + changeYear = this._get( inst, "changeYear" ), + showMonthAfterYear = this._get( inst, "showMonthAfterYear" ), + html = "<div class='ui-datepicker-title'>", + monthHtml = ""; + + // Month selection + if ( secondary || !changeMonth ) { + monthHtml += "<span class='ui-datepicker-month'>" + monthNames[ drawMonth ] + "</span>"; + } else { + inMinYear = ( minDate && minDate.getFullYear() === drawYear ); + inMaxYear = ( maxDate && maxDate.getFullYear() === drawYear ); + monthHtml += "<select class='ui-datepicker-month' data-handler='selectMonth' data-event='change'>"; + for ( month = 0; month < 12; month++ ) { + if ( ( !inMinYear || month >= minDate.getMonth() ) && ( !inMaxYear || month <= maxDate.getMonth() ) ) { + monthHtml += "<option value='" + month + "'" + + ( month === drawMonth ? " selected='selected'" : "" ) + + ">" + monthNamesShort[ month ] + "</option>"; + } + } + monthHtml += "</select>"; + } + + if ( !showMonthAfterYear ) { + html += monthHtml + ( secondary || !( changeMonth && changeYear ) ? " " : "" ); + } + + // Year selection + if ( !inst.yearshtml ) { + inst.yearshtml = ""; + if ( secondary || !changeYear ) { + html += "<span class='ui-datepicker-year'>" + drawYear + "</span>"; + } else { + + // determine range of years to display + years = this._get( inst, "yearRange" ).split( ":" ); + thisYear = new Date().getFullYear(); + determineYear = function( value ) { + var year = ( value.match( /c[+\-].*/ ) ? drawYear + parseInt( value.substring( 1 ), 10 ) : + ( value.match( /[+\-].*/ ) ? thisYear + parseInt( value, 10 ) : + parseInt( value, 10 ) ) ); + return ( isNaN( year ) ? thisYear : year ); + }; + year = determineYear( years[ 0 ] ); + endYear = Math.max( year, determineYear( years[ 1 ] || "" ) ); + year = ( minDate ? Math.max( year, minDate.getFullYear() ) : year ); + endYear = ( maxDate ? Math.min( endYear, maxDate.getFullYear() ) : endYear ); + inst.yearshtml += "<select class='ui-datepicker-year' data-handler='selectYear' data-event='change'>"; + for ( ; year <= endYear; year++ ) { + inst.yearshtml += "<option value='" + year + "'" + + ( year === drawYear ? " selected='selected'" : "" ) + + ">" + year + "</option>"; + } + inst.yearshtml += "</select>"; + + html += inst.yearshtml; + inst.yearshtml = null; + } + } + + html += this._get( inst, "yearSuffix" ); + if ( showMonthAfterYear ) { + html += ( secondary || !( changeMonth && changeYear ) ? " " : "" ) + monthHtml; + } + html += "</div>"; // Close datepicker_header + return html; + }, + + /* Adjust one of the date sub-fields. */ + _adjustInstDate: function( inst, offset, period ) { + var year = inst.selectedYear + ( period === "Y" ? offset : 0 ), + month = inst.selectedMonth + ( period === "M" ? offset : 0 ), + day = Math.min( inst.selectedDay, this._getDaysInMonth( year, month ) ) + ( period === "D" ? offset : 0 ), + date = this._restrictMinMax( inst, this._daylightSavingAdjust( new Date( year, month, day ) ) ); + + inst.selectedDay = date.getDate(); + inst.drawMonth = inst.selectedMonth = date.getMonth(); + inst.drawYear = inst.selectedYear = date.getFullYear(); + if ( period === "M" || period === "Y" ) { + this._notifyChange( inst ); + } + }, + + /* Ensure a date is within any min/max bounds. */ + _restrictMinMax: function( inst, date ) { + var minDate = this._getMinMaxDate( inst, "min" ), + maxDate = this._getMinMaxDate( inst, "max" ), + newDate = ( minDate && date < minDate ? minDate : date ); + return ( maxDate && newDate > maxDate ? maxDate : newDate ); + }, + + /* Notify change of month/year. */ + _notifyChange: function( inst ) { + var onChange = this._get( inst, "onChangeMonthYear" ); + if ( onChange ) { + onChange.apply( ( inst.input ? inst.input[ 0 ] : null ), + [ inst.selectedYear, inst.selectedMonth + 1, inst ] ); + } + }, + + /* Determine the number of months to show. */ + _getNumberOfMonths: function( inst ) { + var numMonths = this._get( inst, "numberOfMonths" ); + return ( numMonths == null ? [ 1, 1 ] : ( typeof numMonths === "number" ? [ 1, numMonths ] : numMonths ) ); + }, + + /* Determine the current maximum date - ensure no time components are set. */ + _getMinMaxDate: function( inst, minMax ) { + return this._determineDate( inst, this._get( inst, minMax + "Date" ), null ); + }, + + /* Find the number of days in a given month. */ + _getDaysInMonth: function( year, month ) { + return 32 - this._daylightSavingAdjust( new Date( year, month, 32 ) ).getDate(); + }, + + /* Find the day of the week of the first of a month. */ + _getFirstDayOfMonth: function( year, month ) { + return new Date( year, month, 1 ).getDay(); + }, + + /* Determines if we should allow a "next/prev" month display change. */ + _canAdjustMonth: function( inst, offset, curYear, curMonth ) { + var numMonths = this._getNumberOfMonths( inst ), + date = this._daylightSavingAdjust( new Date( curYear, + curMonth + ( offset < 0 ? offset : numMonths[ 0 ] * numMonths[ 1 ] ), 1 ) ); + + if ( offset < 0 ) { + date.setDate( this._getDaysInMonth( date.getFullYear(), date.getMonth() ) ); + } + return this._isInRange( inst, date ); + }, + + /* Is the given date in the accepted range? */ + _isInRange: function( inst, date ) { + var yearSplit, currentYear, + minDate = this._getMinMaxDate( inst, "min" ), + maxDate = this._getMinMaxDate( inst, "max" ), + minYear = null, + maxYear = null, + years = this._get( inst, "yearRange" ); + if ( years ) { + yearSplit = years.split( ":" ); + currentYear = new Date().getFullYear(); + minYear = parseInt( yearSplit[ 0 ], 10 ); + maxYear = parseInt( yearSplit[ 1 ], 10 ); + if ( yearSplit[ 0 ].match( /[+\-].*/ ) ) { + minYear += currentYear; + } + if ( yearSplit[ 1 ].match( /[+\-].*/ ) ) { + maxYear += currentYear; + } + } + + return ( ( !minDate || date.getTime() >= minDate.getTime() ) && + ( !maxDate || date.getTime() <= maxDate.getTime() ) && + ( !minYear || date.getFullYear() >= minYear ) && + ( !maxYear || date.getFullYear() <= maxYear ) ); + }, + + /* Provide the configuration settings for formatting/parsing. */ + _getFormatConfig: function( inst ) { + var shortYearCutoff = this._get( inst, "shortYearCutoff" ); + shortYearCutoff = ( typeof shortYearCutoff !== "string" ? shortYearCutoff : + new Date().getFullYear() % 100 + parseInt( shortYearCutoff, 10 ) ); + return { shortYearCutoff: shortYearCutoff, + dayNamesShort: this._get( inst, "dayNamesShort" ), dayNames: this._get( inst, "dayNames" ), + monthNamesShort: this._get( inst, "monthNamesShort" ), monthNames: this._get( inst, "monthNames" ) }; + }, + + /* Format the given date for display. */ + _formatDate: function( inst, day, month, year ) { + if ( !day ) { + inst.currentDay = inst.selectedDay; + inst.currentMonth = inst.selectedMonth; + inst.currentYear = inst.selectedYear; + } + var date = ( day ? ( typeof day === "object" ? day : + this._daylightSavingAdjust( new Date( year, month, day ) ) ) : + this._daylightSavingAdjust( new Date( inst.currentYear, inst.currentMonth, inst.currentDay ) ) ); + return this.formatDate( this._get( inst, "dateFormat" ), date, this._getFormatConfig( inst ) ); + } +} ); + +/* + * Bind hover events for datepicker elements. + * Done via delegate so the binding only occurs once in the lifetime of the parent div. + * Global datepicker_instActive, set by _updateDatepicker allows the handlers to find their way back to the active picker. + */ +function datepicker_bindHover( dpDiv ) { + var selector = "button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a"; + return dpDiv.on( "mouseout", selector, function() { + $( this ).removeClass( "ui-state-hover" ); + if ( this.className.indexOf( "ui-datepicker-prev" ) !== -1 ) { + $( this ).removeClass( "ui-datepicker-prev-hover" ); + } + if ( this.className.indexOf( "ui-datepicker-next" ) !== -1 ) { + $( this ).removeClass( "ui-datepicker-next-hover" ); + } + } ) + .on( "mouseover", selector, datepicker_handleMouseover ); +} + +function datepicker_handleMouseover() { + if ( !$.datepicker._isDisabledDatepicker( datepicker_instActive.inline ? datepicker_instActive.dpDiv.parent()[ 0 ] : datepicker_instActive.input[ 0 ] ) ) { + $( this ).parents( ".ui-datepicker-calendar" ).find( "a" ).removeClass( "ui-state-hover" ); + $( this ).addClass( "ui-state-hover" ); + if ( this.className.indexOf( "ui-datepicker-prev" ) !== -1 ) { + $( this ).addClass( "ui-datepicker-prev-hover" ); + } + if ( this.className.indexOf( "ui-datepicker-next" ) !== -1 ) { + $( this ).addClass( "ui-datepicker-next-hover" ); + } + } +} + +/* jQuery extend now ignores nulls! */ +function datepicker_extendRemove( target, props ) { + $.extend( target, props ); + for ( var name in props ) { + if ( props[ name ] == null ) { + target[ name ] = props[ name ]; + } + } + return target; +} + +/* Invoke the datepicker functionality. + @param options string - a command, optionally followed by additional parameters or + Object - settings for attaching new datepicker functionality + @return jQuery object */ +$.fn.datepicker = function( options ) { + + /* Verify an empty collection wasn't passed - Fixes #6976 */ + if ( !this.length ) { + return this; + } + + /* Initialise the date picker. */ + if ( !$.datepicker.initialized ) { + $( document ).on( "mousedown", $.datepicker._checkExternalClick ); + $.datepicker.initialized = true; + } + + /* Append datepicker main container to body if not exist. */ + if ( $( "#" + $.datepicker._mainDivId ).length === 0 ) { + $( "body" ).append( $.datepicker.dpDiv ); + } + + var otherArgs = Array.prototype.slice.call( arguments, 1 ); + if ( typeof options === "string" && ( options === "isDisabled" || options === "getDate" || options === "widget" ) ) { + return $.datepicker[ "_" + options + "Datepicker" ]. + apply( $.datepicker, [ this[ 0 ] ].concat( otherArgs ) ); + } + if ( options === "option" && arguments.length === 2 && typeof arguments[ 1 ] === "string" ) { + return $.datepicker[ "_" + options + "Datepicker" ]. + apply( $.datepicker, [ this[ 0 ] ].concat( otherArgs ) ); + } + return this.each( function() { + typeof options === "string" ? + $.datepicker[ "_" + options + "Datepicker" ]. + apply( $.datepicker, [ this ].concat( otherArgs ) ) : + $.datepicker._attachDatepicker( this, options ); + } ); +}; + +$.datepicker = new Datepicker(); // singleton instance +$.datepicker.initialized = false; +$.datepicker.uuid = new Date().getTime(); +$.datepicker.version = "1.12.1"; + +var widgetsDatepicker = $.datepicker; + + + + +// This file is deprecated +var ie = $.ui.ie = !!/msie [\w.]+/.exec( navigator.userAgent.toLowerCase() ); + +/*! + * jQuery UI Mouse 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: Mouse +//>>group: Widgets +//>>description: Abstracts mouse-based interactions to assist in creating certain widgets. +//>>docs: http://api.jqueryui.com/mouse/ + + + +var mouseHandled = false; +$( document ).on( "mouseup", function() { + mouseHandled = false; +} ); + +var widgetsMouse = $.widget( "ui.mouse", { + version: "1.12.1", + options: { + cancel: "input, textarea, button, select, option", + distance: 1, + delay: 0 + }, + _mouseInit: function() { + var that = this; + + this.element + .on( "mousedown." + this.widgetName, function( event ) { + return that._mouseDown( event ); + } ) + .on( "click." + this.widgetName, function( event ) { + if ( true === $.data( event.target, that.widgetName + ".preventClickEvent" ) ) { + $.removeData( event.target, that.widgetName + ".preventClickEvent" ); + event.stopImmediatePropagation(); + return false; + } + } ); + + this.started = false; + }, + + // TODO: make sure destroying one instance of mouse doesn't mess with + // other instances of mouse + _mouseDestroy: function() { + this.element.off( "." + this.widgetName ); + if ( this._mouseMoveDelegate ) { + this.document + .off( "mousemove." + this.widgetName, this._mouseMoveDelegate ) + .off( "mouseup." + this.widgetName, this._mouseUpDelegate ); + } + }, + + _mouseDown: function( event ) { + + // don't let more than one widget handle mouseStart + if ( mouseHandled ) { + return; + } + + this._mouseMoved = false; + + // We may have missed mouseup (out of window) + ( this._mouseStarted && this._mouseUp( event ) ); + + this._mouseDownEvent = event; + + var that = this, + btnIsLeft = ( event.which === 1 ), + + // event.target.nodeName works around a bug in IE 8 with + // disabled inputs (#7620) + elIsCancel = ( typeof this.options.cancel === "string" && event.target.nodeName ? + $( event.target ).closest( this.options.cancel ).length : false ); + if ( !btnIsLeft || elIsCancel || !this._mouseCapture( event ) ) { + return true; + } + + this.mouseDelayMet = !this.options.delay; + if ( !this.mouseDelayMet ) { + this._mouseDelayTimer = setTimeout( function() { + that.mouseDelayMet = true; + }, this.options.delay ); + } + + if ( this._mouseDistanceMet( event ) && this._mouseDelayMet( event ) ) { + this._mouseStarted = ( this._mouseStart( event ) !== false ); + if ( !this._mouseStarted ) { + event.preventDefault(); + return true; + } + } + + // Click event may never have fired (Gecko & Opera) + if ( true === $.data( event.target, this.widgetName + ".preventClickEvent" ) ) { + $.removeData( event.target, this.widgetName + ".preventClickEvent" ); + } + + // These delegates are required to keep context + this._mouseMoveDelegate = function( event ) { + return that._mouseMove( event ); + }; + this._mouseUpDelegate = function( event ) { + return that._mouseUp( event ); + }; + + this.document + .on( "mousemove." + this.widgetName, this._mouseMoveDelegate ) + .on( "mouseup." + this.widgetName, this._mouseUpDelegate ); + + event.preventDefault(); + + mouseHandled = true; + return true; + }, + + _mouseMove: function( event ) { + + // Only check for mouseups outside the document if you've moved inside the document + // at least once. This prevents the firing of mouseup in the case of IE<9, which will + // fire a mousemove event if content is placed under the cursor. See #7778 + // Support: IE <9 + if ( this._mouseMoved ) { + + // IE mouseup check - mouseup happened when mouse was out of window + if ( $.ui.ie && ( !document.documentMode || document.documentMode < 9 ) && + !event.button ) { + return this._mouseUp( event ); + + // Iframe mouseup check - mouseup occurred in another document + } else if ( !event.which ) { + + // Support: Safari <=8 - 9 + // Safari sets which to 0 if you press any of the following keys + // during a drag (#14461) + if ( event.originalEvent.altKey || event.originalEvent.ctrlKey || + event.originalEvent.metaKey || event.originalEvent.shiftKey ) { + this.ignoreMissingWhich = true; + } else if ( !this.ignoreMissingWhich ) { + return this._mouseUp( event ); + } + } + } + + if ( event.which || event.button ) { + this._mouseMoved = true; + } + + if ( this._mouseStarted ) { + this._mouseDrag( event ); + return event.preventDefault(); + } + + if ( this._mouseDistanceMet( event ) && this._mouseDelayMet( event ) ) { + this._mouseStarted = + ( this._mouseStart( this._mouseDownEvent, event ) !== false ); + ( this._mouseStarted ? this._mouseDrag( event ) : this._mouseUp( event ) ); + } + + return !this._mouseStarted; + }, + + _mouseUp: function( event ) { + this.document + .off( "mousemove." + this.widgetName, this._mouseMoveDelegate ) + .off( "mouseup." + this.widgetName, this._mouseUpDelegate ); + + if ( this._mouseStarted ) { + this._mouseStarted = false; + + if ( event.target === this._mouseDownEvent.target ) { + $.data( event.target, this.widgetName + ".preventClickEvent", true ); + } + + this._mouseStop( event ); + } + + if ( this._mouseDelayTimer ) { + clearTimeout( this._mouseDelayTimer ); + delete this._mouseDelayTimer; + } + + this.ignoreMissingWhich = false; + mouseHandled = false; + event.preventDefault(); + }, + + _mouseDistanceMet: function( event ) { + return ( Math.max( + Math.abs( this._mouseDownEvent.pageX - event.pageX ), + Math.abs( this._mouseDownEvent.pageY - event.pageY ) + ) >= this.options.distance + ); + }, + + _mouseDelayMet: function( /* event */ ) { + return this.mouseDelayMet; + }, + + // These are placeholder methods, to be overriden by extending plugin + _mouseStart: function( /* event */ ) {}, + _mouseDrag: function( /* event */ ) {}, + _mouseStop: function( /* event */ ) {}, + _mouseCapture: function( /* event */ ) { return true; } +} ); + + + + +// $.ui.plugin is deprecated. Use $.widget() extensions instead. +var plugin = $.ui.plugin = { + add: function( module, option, set ) { + var i, + proto = $.ui[ module ].prototype; + for ( i in set ) { + proto.plugins[ i ] = proto.plugins[ i ] || []; + proto.plugins[ i ].push( [ option, set[ i ] ] ); + } + }, + call: function( instance, name, args, allowDisconnected ) { + var i, + set = instance.plugins[ name ]; + + if ( !set ) { + return; + } + + if ( !allowDisconnected && ( !instance.element[ 0 ].parentNode || + instance.element[ 0 ].parentNode.nodeType === 11 ) ) { + return; + } + + for ( i = 0; i < set.length; i++ ) { + if ( instance.options[ set[ i ][ 0 ] ] ) { + set[ i ][ 1 ].apply( instance.element, args ); + } + } + } +}; + + + +var safeBlur = $.ui.safeBlur = function( element ) { + + // Support: IE9 - 10 only + // If the <body> is blurred, IE will switch windows, see #9420 + if ( element && element.nodeName.toLowerCase() !== "body" ) { + $( element ).trigger( "blur" ); + } +}; + + +/*! + * jQuery UI Draggable 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: Draggable +//>>group: Interactions +//>>description: Enables dragging functionality for any element. +//>>docs: http://api.jqueryui.com/draggable/ +//>>demos: http://jqueryui.com/draggable/ +//>>css.structure: ../../themes/base/draggable.css + + + +$.widget( "ui.draggable", $.ui.mouse, { + version: "1.12.1", + widgetEventPrefix: "drag", + options: { + addClasses: true, + appendTo: "parent", + axis: false, + connectToSortable: false, + containment: false, + cursor: "auto", + cursorAt: false, + grid: false, + handle: false, + helper: "original", + iframeFix: false, + opacity: false, + refreshPositions: false, + revert: false, + revertDuration: 500, + scope: "default", + scroll: true, + scrollSensitivity: 20, + scrollSpeed: 20, + snap: false, + snapMode: "both", + snapTolerance: 20, + stack: false, + zIndex: false, + + // Callbacks + drag: null, + start: null, + stop: null + }, + _create: function() { + + if ( this.options.helper === "original" ) { + this._setPositionRelative(); + } + if ( this.options.addClasses ) { + this._addClass( "ui-draggable" ); + } + this._setHandleClassName(); + + this._mouseInit(); + }, + + _setOption: function( key, value ) { + this._super( key, value ); + if ( key === "handle" ) { + this._removeHandleClassName(); + this._setHandleClassName(); + } + }, + + _destroy: function() { + if ( ( this.helper || this.element ).is( ".ui-draggable-dragging" ) ) { + this.destroyOnClear = true; + return; + } + this._removeHandleClassName(); + this._mouseDestroy(); + }, + + _mouseCapture: function( event ) { + var o = this.options; + + // Among others, prevent a drag on a resizable-handle + if ( this.helper || o.disabled || + $( event.target ).closest( ".ui-resizable-handle" ).length > 0 ) { + return false; + } + + //Quit if we're not on a valid handle + this.handle = this._getHandle( event ); + if ( !this.handle ) { + return false; + } + + this._blurActiveElement( event ); + + this._blockFrames( o.iframeFix === true ? "iframe" : o.iframeFix ); + + return true; + + }, + + _blockFrames: function( selector ) { + this.iframeBlocks = this.document.find( selector ).map( function() { + var iframe = $( this ); + + return $( "<div>" ) + .css( "position", "absolute" ) + .appendTo( iframe.parent() ) + .outerWidth( iframe.outerWidth() ) + .outerHeight( iframe.outerHeight() ) + .offset( iframe.offset() )[ 0 ]; + } ); + }, + + _unblockFrames: function() { + if ( this.iframeBlocks ) { + this.iframeBlocks.remove(); + delete this.iframeBlocks; + } + }, + + _blurActiveElement: function( event ) { + var activeElement = $.ui.safeActiveElement( this.document[ 0 ] ), + target = $( event.target ); + + // Don't blur if the event occurred on an element that is within + // the currently focused element + // See #10527, #12472 + if ( target.closest( activeElement ).length ) { + return; + } + + // Blur any element that currently has focus, see #4261 + $.ui.safeBlur( activeElement ); + }, + + _mouseStart: function( event ) { + + var o = this.options; + + //Create and append the visible helper + this.helper = this._createHelper( event ); + + this._addClass( this.helper, "ui-draggable-dragging" ); + + //Cache the helper size + this._cacheHelperProportions(); + + //If ddmanager is used for droppables, set the global draggable + if ( $.ui.ddmanager ) { + $.ui.ddmanager.current = this; + } + + /* + * - Position generation - + * This block generates everything position related - it's the core of draggables. + */ + + //Cache the margins of the original element + this._cacheMargins(); + + //Store the helper's css position + this.cssPosition = this.helper.css( "position" ); + this.scrollParent = this.helper.scrollParent( true ); + this.offsetParent = this.helper.offsetParent(); + this.hasFixedAncestor = this.helper.parents().filter( function() { + return $( this ).css( "position" ) === "fixed"; + } ).length > 0; + + //The element's absolute position on the page minus margins + this.positionAbs = this.element.offset(); + this._refreshOffsets( event ); + + //Generate the original position + this.originalPosition = this.position = this._generatePosition( event, false ); + this.originalPageX = event.pageX; + this.originalPageY = event.pageY; + + //Adjust the mouse offset relative to the helper if "cursorAt" is supplied + ( o.cursorAt && this._adjustOffsetFromHelper( o.cursorAt ) ); + + //Set a containment if given in the options + this._setContainment(); + + //Trigger event + callbacks + if ( this._trigger( "start", event ) === false ) { + this._clear(); + return false; + } + + //Recache the helper size + this._cacheHelperProportions(); + + //Prepare the droppable offsets + if ( $.ui.ddmanager && !o.dropBehaviour ) { + $.ui.ddmanager.prepareOffsets( this, event ); + } + + // Execute the drag once - this causes the helper not to be visible before getting its + // correct position + this._mouseDrag( event, true ); + + // If the ddmanager is used for droppables, inform the manager that dragging has started + // (see #5003) + if ( $.ui.ddmanager ) { + $.ui.ddmanager.dragStart( this, event ); + } + + return true; + }, + + _refreshOffsets: function( event ) { + this.offset = { + top: this.positionAbs.top - this.margins.top, + left: this.positionAbs.left - this.margins.left, + scroll: false, + parent: this._getParentOffset(), + relative: this._getRelativeOffset() + }; + + this.offset.click = { + left: event.pageX - this.offset.left, + top: event.pageY - this.offset.top + }; + }, + + _mouseDrag: function( event, noPropagation ) { + + // reset any necessary cached properties (see #5009) + if ( this.hasFixedAncestor ) { + this.offset.parent = this._getParentOffset(); + } + + //Compute the helpers position + this.position = this._generatePosition( event, true ); + this.positionAbs = this._convertPositionTo( "absolute" ); + + //Call plugins and callbacks and use the resulting position if something is returned + if ( !noPropagation ) { + var ui = this._uiHash(); + if ( this._trigger( "drag", event, ui ) === false ) { + this._mouseUp( new $.Event( "mouseup", event ) ); + return false; + } + this.position = ui.position; + } + + this.helper[ 0 ].style.left = this.position.left + "px"; + this.helper[ 0 ].style.top = this.position.top + "px"; + + if ( $.ui.ddmanager ) { + $.ui.ddmanager.drag( this, event ); + } + + return false; + }, + + _mouseStop: function( event ) { + + //If we are using droppables, inform the manager about the drop + var that = this, + dropped = false; + if ( $.ui.ddmanager && !this.options.dropBehaviour ) { + dropped = $.ui.ddmanager.drop( this, event ); + } + + //if a drop comes from outside (a sortable) + if ( this.dropped ) { + dropped = this.dropped; + this.dropped = false; + } + + if ( ( this.options.revert === "invalid" && !dropped ) || + ( this.options.revert === "valid" && dropped ) || + this.options.revert === true || ( $.isFunction( this.options.revert ) && + this.options.revert.call( this.element, dropped ) ) + ) { + $( this.helper ).animate( + this.originalPosition, + parseInt( this.options.revertDuration, 10 ), + function() { + if ( that._trigger( "stop", event ) !== false ) { + that._clear(); + } + } + ); + } else { + if ( this._trigger( "stop", event ) !== false ) { + this._clear(); + } + } + + return false; + }, + + _mouseUp: function( event ) { + this._unblockFrames(); + + // If the ddmanager is used for droppables, inform the manager that dragging has stopped + // (see #5003) + if ( $.ui.ddmanager ) { + $.ui.ddmanager.dragStop( this, event ); + } + + // Only need to focus if the event occurred on the draggable itself, see #10527 + if ( this.handleElement.is( event.target ) ) { + + // The interaction is over; whether or not the click resulted in a drag, + // focus the element + this.element.trigger( "focus" ); + } + + return $.ui.mouse.prototype._mouseUp.call( this, event ); + }, + + cancel: function() { + + if ( this.helper.is( ".ui-draggable-dragging" ) ) { + this._mouseUp( new $.Event( "mouseup", { target: this.element[ 0 ] } ) ); + } else { + this._clear(); + } + + return this; + + }, + + _getHandle: function( event ) { + return this.options.handle ? + !!$( event.target ).closest( this.element.find( this.options.handle ) ).length : + true; + }, + + _setHandleClassName: function() { + this.handleElement = this.options.handle ? + this.element.find( this.options.handle ) : this.element; + this._addClass( this.handleElement, "ui-draggable-handle" ); + }, + + _removeHandleClassName: function() { + this._removeClass( this.handleElement, "ui-draggable-handle" ); + }, + + _createHelper: function( event ) { + + var o = this.options, + helperIsFunction = $.isFunction( o.helper ), + helper = helperIsFunction ? + $( o.helper.apply( this.element[ 0 ], [ event ] ) ) : + ( o.helper === "clone" ? + this.element.clone().removeAttr( "id" ) : + this.element ); + + if ( !helper.parents( "body" ).length ) { + helper.appendTo( ( o.appendTo === "parent" ? + this.element[ 0 ].parentNode : + o.appendTo ) ); + } + + // Http://bugs.jqueryui.com/ticket/9446 + // a helper function can return the original element + // which wouldn't have been set to relative in _create + if ( helperIsFunction && helper[ 0 ] === this.element[ 0 ] ) { + this._setPositionRelative(); + } + + if ( helper[ 0 ] !== this.element[ 0 ] && + !( /(fixed|absolute)/ ).test( helper.css( "position" ) ) ) { + helper.css( "position", "absolute" ); + } + + return helper; + + }, + + _setPositionRelative: function() { + if ( !( /^(?:r|a|f)/ ).test( this.element.css( "position" ) ) ) { + this.element[ 0 ].style.position = "relative"; + } + }, + + _adjustOffsetFromHelper: function( obj ) { + if ( typeof obj === "string" ) { + obj = obj.split( " " ); + } + if ( $.isArray( obj ) ) { + obj = { left: +obj[ 0 ], top: +obj[ 1 ] || 0 }; + } + if ( "left" in obj ) { + this.offset.click.left = obj.left + this.margins.left; + } + if ( "right" in obj ) { + this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left; + } + if ( "top" in obj ) { + this.offset.click.top = obj.top + this.margins.top; + } + if ( "bottom" in obj ) { + this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top; + } + }, + + _isRootNode: function( element ) { + return ( /(html|body)/i ).test( element.tagName ) || element === this.document[ 0 ]; + }, + + _getParentOffset: function() { + + //Get the offsetParent and cache its position + var po = this.offsetParent.offset(), + document = this.document[ 0 ]; + + // This is a special case where we need to modify a offset calculated on start, since the + // following happened: + // 1. The position of the helper is absolute, so it's position is calculated based on the + // next positioned parent + // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't + // the document, which means that the scroll is included in the initial calculation of the + // offset of the parent, and never recalculated upon drag + if ( this.cssPosition === "absolute" && this.scrollParent[ 0 ] !== document && + $.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) { + po.left += this.scrollParent.scrollLeft(); + po.top += this.scrollParent.scrollTop(); + } + + if ( this._isRootNode( this.offsetParent[ 0 ] ) ) { + po = { top: 0, left: 0 }; + } + + return { + top: po.top + ( parseInt( this.offsetParent.css( "borderTopWidth" ), 10 ) || 0 ), + left: po.left + ( parseInt( this.offsetParent.css( "borderLeftWidth" ), 10 ) || 0 ) + }; + + }, + + _getRelativeOffset: function() { + if ( this.cssPosition !== "relative" ) { + return { top: 0, left: 0 }; + } + + var p = this.element.position(), + scrollIsRootNode = this._isRootNode( this.scrollParent[ 0 ] ); + + return { + top: p.top - ( parseInt( this.helper.css( "top" ), 10 ) || 0 ) + + ( !scrollIsRootNode ? this.scrollParent.scrollTop() : 0 ), + left: p.left - ( parseInt( this.helper.css( "left" ), 10 ) || 0 ) + + ( !scrollIsRootNode ? this.scrollParent.scrollLeft() : 0 ) + }; + + }, + + _cacheMargins: function() { + this.margins = { + left: ( parseInt( this.element.css( "marginLeft" ), 10 ) || 0 ), + top: ( parseInt( this.element.css( "marginTop" ), 10 ) || 0 ), + right: ( parseInt( this.element.css( "marginRight" ), 10 ) || 0 ), + bottom: ( parseInt( this.element.css( "marginBottom" ), 10 ) || 0 ) + }; + }, + + _cacheHelperProportions: function() { + this.helperProportions = { + width: this.helper.outerWidth(), + height: this.helper.outerHeight() + }; + }, + + _setContainment: function() { + + var isUserScrollable, c, ce, + o = this.options, + document = this.document[ 0 ]; + + this.relativeContainer = null; + + if ( !o.containment ) { + this.containment = null; + return; + } + + if ( o.containment === "window" ) { + this.containment = [ + $( window ).scrollLeft() - this.offset.relative.left - this.offset.parent.left, + $( window ).scrollTop() - this.offset.relative.top - this.offset.parent.top, + $( window ).scrollLeft() + $( window ).width() - + this.helperProportions.width - this.margins.left, + $( window ).scrollTop() + + ( $( window ).height() || document.body.parentNode.scrollHeight ) - + this.helperProportions.height - this.margins.top + ]; + return; + } + + if ( o.containment === "document" ) { + this.containment = [ + 0, + 0, + $( document ).width() - this.helperProportions.width - this.margins.left, + ( $( document ).height() || document.body.parentNode.scrollHeight ) - + this.helperProportions.height - this.margins.top + ]; + return; + } + + if ( o.containment.constructor === Array ) { + this.containment = o.containment; + return; + } + + if ( o.containment === "parent" ) { + o.containment = this.helper[ 0 ].parentNode; + } + + c = $( o.containment ); + ce = c[ 0 ]; + + if ( !ce ) { + return; + } + + isUserScrollable = /(scroll|auto)/.test( c.css( "overflow" ) ); + + this.containment = [ + ( parseInt( c.css( "borderLeftWidth" ), 10 ) || 0 ) + + ( parseInt( c.css( "paddingLeft" ), 10 ) || 0 ), + ( parseInt( c.css( "borderTopWidth" ), 10 ) || 0 ) + + ( parseInt( c.css( "paddingTop" ), 10 ) || 0 ), + ( isUserScrollable ? Math.max( ce.scrollWidth, ce.offsetWidth ) : ce.offsetWidth ) - + ( parseInt( c.css( "borderRightWidth" ), 10 ) || 0 ) - + ( parseInt( c.css( "paddingRight" ), 10 ) || 0 ) - + this.helperProportions.width - + this.margins.left - + this.margins.right, + ( isUserScrollable ? Math.max( ce.scrollHeight, ce.offsetHeight ) : ce.offsetHeight ) - + ( parseInt( c.css( "borderBottomWidth" ), 10 ) || 0 ) - + ( parseInt( c.css( "paddingBottom" ), 10 ) || 0 ) - + this.helperProportions.height - + this.margins.top - + this.margins.bottom + ]; + this.relativeContainer = c; + }, + + _convertPositionTo: function( d, pos ) { + + if ( !pos ) { + pos = this.position; + } + + var mod = d === "absolute" ? 1 : -1, + scrollIsRootNode = this._isRootNode( this.scrollParent[ 0 ] ); + + return { + top: ( + + // The absolute mouse position + pos.top + + + // Only for relative positioned nodes: Relative offset from element to offset parent + this.offset.relative.top * mod + + + // The offsetParent's offset without borders (offset + border) + this.offset.parent.top * mod - + ( ( this.cssPosition === "fixed" ? + -this.offset.scroll.top : + ( scrollIsRootNode ? 0 : this.offset.scroll.top ) ) * mod ) + ), + left: ( + + // The absolute mouse position + pos.left + + + // Only for relative positioned nodes: Relative offset from element to offset parent + this.offset.relative.left * mod + + + // The offsetParent's offset without borders (offset + border) + this.offset.parent.left * mod - + ( ( this.cssPosition === "fixed" ? + -this.offset.scroll.left : + ( scrollIsRootNode ? 0 : this.offset.scroll.left ) ) * mod ) + ) + }; + + }, + + _generatePosition: function( event, constrainPosition ) { + + var containment, co, top, left, + o = this.options, + scrollIsRootNode = this._isRootNode( this.scrollParent[ 0 ] ), + pageX = event.pageX, + pageY = event.pageY; + + // Cache the scroll + if ( !scrollIsRootNode || !this.offset.scroll ) { + this.offset.scroll = { + top: this.scrollParent.scrollTop(), + left: this.scrollParent.scrollLeft() + }; + } + + /* + * - Position constraining - + * Constrain the position to a mix of grid, containment. + */ + + // If we are not dragging yet, we won't check for options + if ( constrainPosition ) { + if ( this.containment ) { + if ( this.relativeContainer ) { + co = this.relativeContainer.offset(); + containment = [ + this.containment[ 0 ] + co.left, + this.containment[ 1 ] + co.top, + this.containment[ 2 ] + co.left, + this.containment[ 3 ] + co.top + ]; + } else { + containment = this.containment; + } + + if ( event.pageX - this.offset.click.left < containment[ 0 ] ) { + pageX = containment[ 0 ] + this.offset.click.left; + } + if ( event.pageY - this.offset.click.top < containment[ 1 ] ) { + pageY = containment[ 1 ] + this.offset.click.top; + } + if ( event.pageX - this.offset.click.left > containment[ 2 ] ) { + pageX = containment[ 2 ] + this.offset.click.left; + } + if ( event.pageY - this.offset.click.top > containment[ 3 ] ) { + pageY = containment[ 3 ] + this.offset.click.top; + } + } + + if ( o.grid ) { + + //Check for grid elements set to 0 to prevent divide by 0 error causing invalid + // argument errors in IE (see ticket #6950) + top = o.grid[ 1 ] ? this.originalPageY + Math.round( ( pageY - + this.originalPageY ) / o.grid[ 1 ] ) * o.grid[ 1 ] : this.originalPageY; + pageY = containment ? ( ( top - this.offset.click.top >= containment[ 1 ] || + top - this.offset.click.top > containment[ 3 ] ) ? + top : + ( ( top - this.offset.click.top >= containment[ 1 ] ) ? + top - o.grid[ 1 ] : top + o.grid[ 1 ] ) ) : top; + + left = o.grid[ 0 ] ? this.originalPageX + + Math.round( ( pageX - this.originalPageX ) / o.grid[ 0 ] ) * o.grid[ 0 ] : + this.originalPageX; + pageX = containment ? ( ( left - this.offset.click.left >= containment[ 0 ] || + left - this.offset.click.left > containment[ 2 ] ) ? + left : + ( ( left - this.offset.click.left >= containment[ 0 ] ) ? + left - o.grid[ 0 ] : left + o.grid[ 0 ] ) ) : left; + } + + if ( o.axis === "y" ) { + pageX = this.originalPageX; + } + + if ( o.axis === "x" ) { + pageY = this.originalPageY; + } + } + + return { + top: ( + + // The absolute mouse position + pageY - + + // Click offset (relative to the element) + this.offset.click.top - + + // Only for relative positioned nodes: Relative offset from element to offset parent + this.offset.relative.top - + + // The offsetParent's offset without borders (offset + border) + this.offset.parent.top + + ( this.cssPosition === "fixed" ? + -this.offset.scroll.top : + ( scrollIsRootNode ? 0 : this.offset.scroll.top ) ) + ), + left: ( + + // The absolute mouse position + pageX - + + // Click offset (relative to the element) + this.offset.click.left - + + // Only for relative positioned nodes: Relative offset from element to offset parent + this.offset.relative.left - + + // The offsetParent's offset without borders (offset + border) + this.offset.parent.left + + ( this.cssPosition === "fixed" ? + -this.offset.scroll.left : + ( scrollIsRootNode ? 0 : this.offset.scroll.left ) ) + ) + }; + + }, + + _clear: function() { + this._removeClass( this.helper, "ui-draggable-dragging" ); + if ( this.helper[ 0 ] !== this.element[ 0 ] && !this.cancelHelperRemoval ) { + this.helper.remove(); + } + this.helper = null; + this.cancelHelperRemoval = false; + if ( this.destroyOnClear ) { + this.destroy(); + } + }, + + // From now on bulk stuff - mainly helpers + + _trigger: function( type, event, ui ) { + ui = ui || this._uiHash(); + $.ui.plugin.call( this, type, [ event, ui, this ], true ); + + // Absolute position and offset (see #6884 ) have to be recalculated after plugins + if ( /^(drag|start|stop)/.test( type ) ) { + this.positionAbs = this._convertPositionTo( "absolute" ); + ui.offset = this.positionAbs; + } + return $.Widget.prototype._trigger.call( this, type, event, ui ); + }, + + plugins: {}, + + _uiHash: function() { + return { + helper: this.helper, + position: this.position, + originalPosition: this.originalPosition, + offset: this.positionAbs + }; + } + +} ); + +$.ui.plugin.add( "draggable", "connectToSortable", { + start: function( event, ui, draggable ) { + var uiSortable = $.extend( {}, ui, { + item: draggable.element + } ); + + draggable.sortables = []; + $( draggable.options.connectToSortable ).each( function() { + var sortable = $( this ).sortable( "instance" ); + + if ( sortable && !sortable.options.disabled ) { + draggable.sortables.push( sortable ); + + // RefreshPositions is called at drag start to refresh the containerCache + // which is used in drag. This ensures it's initialized and synchronized + // with any changes that might have happened on the page since initialization. + sortable.refreshPositions(); + sortable._trigger( "activate", event, uiSortable ); + } + } ); + }, + stop: function( event, ui, draggable ) { + var uiSortable = $.extend( {}, ui, { + item: draggable.element + } ); + + draggable.cancelHelperRemoval = false; + + $.each( draggable.sortables, function() { + var sortable = this; + + if ( sortable.isOver ) { + sortable.isOver = 0; + + // Allow this sortable to handle removing the helper + draggable.cancelHelperRemoval = true; + sortable.cancelHelperRemoval = false; + + // Use _storedCSS To restore properties in the sortable, + // as this also handles revert (#9675) since the draggable + // may have modified them in unexpected ways (#8809) + sortable._storedCSS = { + position: sortable.placeholder.css( "position" ), + top: sortable.placeholder.css( "top" ), + left: sortable.placeholder.css( "left" ) + }; + + sortable._mouseStop( event ); + + // Once drag has ended, the sortable should return to using + // its original helper, not the shared helper from draggable + sortable.options.helper = sortable.options._helper; + } else { + + // Prevent this Sortable from removing the helper. + // However, don't set the draggable to remove the helper + // either as another connected Sortable may yet handle the removal. + sortable.cancelHelperRemoval = true; + + sortable._trigger( "deactivate", event, uiSortable ); + } + } ); + }, + drag: function( event, ui, draggable ) { + $.each( draggable.sortables, function() { + var innermostIntersecting = false, + sortable = this; + + // Copy over variables that sortable's _intersectsWith uses + sortable.positionAbs = draggable.positionAbs; + sortable.helperProportions = draggable.helperProportions; + sortable.offset.click = draggable.offset.click; + + if ( sortable._intersectsWith( sortable.containerCache ) ) { + innermostIntersecting = true; + + $.each( draggable.sortables, function() { + + // Copy over variables that sortable's _intersectsWith uses + this.positionAbs = draggable.positionAbs; + this.helperProportions = draggable.helperProportions; + this.offset.click = draggable.offset.click; + + if ( this !== sortable && + this._intersectsWith( this.containerCache ) && + $.contains( sortable.element[ 0 ], this.element[ 0 ] ) ) { + innermostIntersecting = false; + } + + return innermostIntersecting; + } ); + } + + if ( innermostIntersecting ) { + + // If it intersects, we use a little isOver variable and set it once, + // so that the move-in stuff gets fired only once. + if ( !sortable.isOver ) { + sortable.isOver = 1; + + // Store draggable's parent in case we need to reappend to it later. + draggable._parent = ui.helper.parent(); + + sortable.currentItem = ui.helper + .appendTo( sortable.element ) + .data( "ui-sortable-item", true ); + + // Store helper option to later restore it + sortable.options._helper = sortable.options.helper; + + sortable.options.helper = function() { + return ui.helper[ 0 ]; + }; + + // Fire the start events of the sortable with our passed browser event, + // and our own helper (so it doesn't create a new one) + event.target = sortable.currentItem[ 0 ]; + sortable._mouseCapture( event, true ); + sortable._mouseStart( event, true, true ); + + // Because the browser event is way off the new appended portlet, + // modify necessary variables to reflect the changes + sortable.offset.click.top = draggable.offset.click.top; + sortable.offset.click.left = draggable.offset.click.left; + sortable.offset.parent.left -= draggable.offset.parent.left - + sortable.offset.parent.left; + sortable.offset.parent.top -= draggable.offset.parent.top - + sortable.offset.parent.top; + + draggable._trigger( "toSortable", event ); + + // Inform draggable that the helper is in a valid drop zone, + // used solely in the revert option to handle "valid/invalid". + draggable.dropped = sortable.element; + + // Need to refreshPositions of all sortables in the case that + // adding to one sortable changes the location of the other sortables (#9675) + $.each( draggable.sortables, function() { + this.refreshPositions(); + } ); + + // Hack so receive/update callbacks work (mostly) + draggable.currentItem = draggable.element; + sortable.fromOutside = draggable; + } + + if ( sortable.currentItem ) { + sortable._mouseDrag( event ); + + // Copy the sortable's position because the draggable's can potentially reflect + // a relative position, while sortable is always absolute, which the dragged + // element has now become. (#8809) + ui.position = sortable.position; + } + } else { + + // If it doesn't intersect with the sortable, and it intersected before, + // we fake the drag stop of the sortable, but make sure it doesn't remove + // the helper by using cancelHelperRemoval. + if ( sortable.isOver ) { + + sortable.isOver = 0; + sortable.cancelHelperRemoval = true; + + // Calling sortable's mouseStop would trigger a revert, + // so revert must be temporarily false until after mouseStop is called. + sortable.options._revert = sortable.options.revert; + sortable.options.revert = false; + + sortable._trigger( "out", event, sortable._uiHash( sortable ) ); + sortable._mouseStop( event, true ); + + // Restore sortable behaviors that were modfied + // when the draggable entered the sortable area (#9481) + sortable.options.revert = sortable.options._revert; + sortable.options.helper = sortable.options._helper; + + if ( sortable.placeholder ) { + sortable.placeholder.remove(); + } + + // Restore and recalculate the draggable's offset considering the sortable + // may have modified them in unexpected ways. (#8809, #10669) + ui.helper.appendTo( draggable._parent ); + draggable._refreshOffsets( event ); + ui.position = draggable._generatePosition( event, true ); + + draggable._trigger( "fromSortable", event ); + + // Inform draggable that the helper is no longer in a valid drop zone + draggable.dropped = false; + + // Need to refreshPositions of all sortables just in case removing + // from one sortable changes the location of other sortables (#9675) + $.each( draggable.sortables, function() { + this.refreshPositions(); + } ); + } + } + } ); + } +} ); + +$.ui.plugin.add( "draggable", "cursor", { + start: function( event, ui, instance ) { + var t = $( "body" ), + o = instance.options; + + if ( t.css( "cursor" ) ) { + o._cursor = t.css( "cursor" ); + } + t.css( "cursor", o.cursor ); + }, + stop: function( event, ui, instance ) { + var o = instance.options; + if ( o._cursor ) { + $( "body" ).css( "cursor", o._cursor ); + } + } +} ); + +$.ui.plugin.add( "draggable", "opacity", { + start: function( event, ui, instance ) { + var t = $( ui.helper ), + o = instance.options; + if ( t.css( "opacity" ) ) { + o._opacity = t.css( "opacity" ); + } + t.css( "opacity", o.opacity ); + }, + stop: function( event, ui, instance ) { + var o = instance.options; + if ( o._opacity ) { + $( ui.helper ).css( "opacity", o._opacity ); + } + } +} ); + +$.ui.plugin.add( "draggable", "scroll", { + start: function( event, ui, i ) { + if ( !i.scrollParentNotHidden ) { + i.scrollParentNotHidden = i.helper.scrollParent( false ); + } + + if ( i.scrollParentNotHidden[ 0 ] !== i.document[ 0 ] && + i.scrollParentNotHidden[ 0 ].tagName !== "HTML" ) { + i.overflowOffset = i.scrollParentNotHidden.offset(); + } + }, + drag: function( event, ui, i ) { + + var o = i.options, + scrolled = false, + scrollParent = i.scrollParentNotHidden[ 0 ], + document = i.document[ 0 ]; + + if ( scrollParent !== document && scrollParent.tagName !== "HTML" ) { + if ( !o.axis || o.axis !== "x" ) { + if ( ( i.overflowOffset.top + scrollParent.offsetHeight ) - event.pageY < + o.scrollSensitivity ) { + scrollParent.scrollTop = scrolled = scrollParent.scrollTop + o.scrollSpeed; + } else if ( event.pageY - i.overflowOffset.top < o.scrollSensitivity ) { + scrollParent.scrollTop = scrolled = scrollParent.scrollTop - o.scrollSpeed; + } + } + + if ( !o.axis || o.axis !== "y" ) { + if ( ( i.overflowOffset.left + scrollParent.offsetWidth ) - event.pageX < + o.scrollSensitivity ) { + scrollParent.scrollLeft = scrolled = scrollParent.scrollLeft + o.scrollSpeed; + } else if ( event.pageX - i.overflowOffset.left < o.scrollSensitivity ) { + scrollParent.scrollLeft = scrolled = scrollParent.scrollLeft - o.scrollSpeed; + } + } + + } else { + + if ( !o.axis || o.axis !== "x" ) { + if ( event.pageY - $( document ).scrollTop() < o.scrollSensitivity ) { + scrolled = $( document ).scrollTop( $( document ).scrollTop() - o.scrollSpeed ); + } else if ( $( window ).height() - ( event.pageY - $( document ).scrollTop() ) < + o.scrollSensitivity ) { + scrolled = $( document ).scrollTop( $( document ).scrollTop() + o.scrollSpeed ); + } + } + + if ( !o.axis || o.axis !== "y" ) { + if ( event.pageX - $( document ).scrollLeft() < o.scrollSensitivity ) { + scrolled = $( document ).scrollLeft( + $( document ).scrollLeft() - o.scrollSpeed + ); + } else if ( $( window ).width() - ( event.pageX - $( document ).scrollLeft() ) < + o.scrollSensitivity ) { + scrolled = $( document ).scrollLeft( + $( document ).scrollLeft() + o.scrollSpeed + ); + } + } + + } + + if ( scrolled !== false && $.ui.ddmanager && !o.dropBehaviour ) { + $.ui.ddmanager.prepareOffsets( i, event ); + } + + } +} ); + +$.ui.plugin.add( "draggable", "snap", { + start: function( event, ui, i ) { + + var o = i.options; + + i.snapElements = []; + + $( o.snap.constructor !== String ? ( o.snap.items || ":data(ui-draggable)" ) : o.snap ) + .each( function() { + var $t = $( this ), + $o = $t.offset(); + if ( this !== i.element[ 0 ] ) { + i.snapElements.push( { + item: this, + width: $t.outerWidth(), height: $t.outerHeight(), + top: $o.top, left: $o.left + } ); + } + } ); + + }, + drag: function( event, ui, inst ) { + + var ts, bs, ls, rs, l, r, t, b, i, first, + o = inst.options, + d = o.snapTolerance, + x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width, + y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height; + + for ( i = inst.snapElements.length - 1; i >= 0; i-- ) { + + l = inst.snapElements[ i ].left - inst.margins.left; + r = l + inst.snapElements[ i ].width; + t = inst.snapElements[ i ].top - inst.margins.top; + b = t + inst.snapElements[ i ].height; + + if ( x2 < l - d || x1 > r + d || y2 < t - d || y1 > b + d || + !$.contains( inst.snapElements[ i ].item.ownerDocument, + inst.snapElements[ i ].item ) ) { + if ( inst.snapElements[ i ].snapping ) { + ( inst.options.snap.release && + inst.options.snap.release.call( + inst.element, + event, + $.extend( inst._uiHash(), { snapItem: inst.snapElements[ i ].item } ) + ) ); + } + inst.snapElements[ i ].snapping = false; + continue; + } + + if ( o.snapMode !== "inner" ) { + ts = Math.abs( t - y2 ) <= d; + bs = Math.abs( b - y1 ) <= d; + ls = Math.abs( l - x2 ) <= d; + rs = Math.abs( r - x1 ) <= d; + if ( ts ) { + ui.position.top = inst._convertPositionTo( "relative", { + top: t - inst.helperProportions.height, + left: 0 + } ).top; + } + if ( bs ) { + ui.position.top = inst._convertPositionTo( "relative", { + top: b, + left: 0 + } ).top; + } + if ( ls ) { + ui.position.left = inst._convertPositionTo( "relative", { + top: 0, + left: l - inst.helperProportions.width + } ).left; + } + if ( rs ) { + ui.position.left = inst._convertPositionTo( "relative", { + top: 0, + left: r + } ).left; + } + } + + first = ( ts || bs || ls || rs ); + + if ( o.snapMode !== "outer" ) { + ts = Math.abs( t - y1 ) <= d; + bs = Math.abs( b - y2 ) <= d; + ls = Math.abs( l - x1 ) <= d; + rs = Math.abs( r - x2 ) <= d; + if ( ts ) { + ui.position.top = inst._convertPositionTo( "relative", { + top: t, + left: 0 + } ).top; + } + if ( bs ) { + ui.position.top = inst._convertPositionTo( "relative", { + top: b - inst.helperProportions.height, + left: 0 + } ).top; + } + if ( ls ) { + ui.position.left = inst._convertPositionTo( "relative", { + top: 0, + left: l + } ).left; + } + if ( rs ) { + ui.position.left = inst._convertPositionTo( "relative", { + top: 0, + left: r - inst.helperProportions.width + } ).left; + } + } + + if ( !inst.snapElements[ i ].snapping && ( ts || bs || ls || rs || first ) ) { + ( inst.options.snap.snap && + inst.options.snap.snap.call( + inst.element, + event, + $.extend( inst._uiHash(), { + snapItem: inst.snapElements[ i ].item + } ) ) ); + } + inst.snapElements[ i ].snapping = ( ts || bs || ls || rs || first ); + + } + + } +} ); + +$.ui.plugin.add( "draggable", "stack", { + start: function( event, ui, instance ) { + var min, + o = instance.options, + group = $.makeArray( $( o.stack ) ).sort( function( a, b ) { + return ( parseInt( $( a ).css( "zIndex" ), 10 ) || 0 ) - + ( parseInt( $( b ).css( "zIndex" ), 10 ) || 0 ); + } ); + + if ( !group.length ) { return; } + + min = parseInt( $( group[ 0 ] ).css( "zIndex" ), 10 ) || 0; + $( group ).each( function( i ) { + $( this ).css( "zIndex", min + i ); + } ); + this.css( "zIndex", ( min + group.length ) ); + } +} ); + +$.ui.plugin.add( "draggable", "zIndex", { + start: function( event, ui, instance ) { + var t = $( ui.helper ), + o = instance.options; + + if ( t.css( "zIndex" ) ) { + o._zIndex = t.css( "zIndex" ); + } + t.css( "zIndex", o.zIndex ); + }, + stop: function( event, ui, instance ) { + var o = instance.options; + + if ( o._zIndex ) { + $( ui.helper ).css( "zIndex", o._zIndex ); + } + } +} ); + +var widgetsDraggable = $.ui.draggable; + + +/*! + * jQuery UI Resizable 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: Resizable +//>>group: Interactions +//>>description: Enables resize functionality for any element. +//>>docs: http://api.jqueryui.com/resizable/ +//>>demos: http://jqueryui.com/resizable/ +//>>css.structure: ../../themes/base/core.css +//>>css.structure: ../../themes/base/resizable.css +//>>css.theme: ../../themes/base/theme.css + + + +$.widget( "ui.resizable", $.ui.mouse, { + version: "1.12.1", + widgetEventPrefix: "resize", + options: { + alsoResize: false, + animate: false, + animateDuration: "slow", + animateEasing: "swing", + aspectRatio: false, + autoHide: false, + classes: { + "ui-resizable-se": "ui-icon ui-icon-gripsmall-diagonal-se" + }, + containment: false, + ghost: false, + grid: false, + handles: "e,s,se", + helper: false, + maxHeight: null, + maxWidth: null, + minHeight: 10, + minWidth: 10, + + // See #7960 + zIndex: 90, + + // Callbacks + resize: null, + start: null, + stop: null + }, + + _num: function( value ) { + return parseFloat( value ) || 0; + }, + + _isNumber: function( value ) { + return !isNaN( parseFloat( value ) ); + }, + + _hasScroll: function( el, a ) { + + if ( $( el ).css( "overflow" ) === "hidden" ) { + return false; + } + + var scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop", + has = false; + + if ( el[ scroll ] > 0 ) { + return true; + } + + // TODO: determine which cases actually cause this to happen + // if the element doesn't have the scroll set, see if it's possible to + // set the scroll + el[ scroll ] = 1; + has = ( el[ scroll ] > 0 ); + el[ scroll ] = 0; + return has; + }, + + _create: function() { + + var margins, + o = this.options, + that = this; + this._addClass( "ui-resizable" ); + + $.extend( this, { + _aspectRatio: !!( o.aspectRatio ), + aspectRatio: o.aspectRatio, + originalElement: this.element, + _proportionallyResizeElements: [], + _helper: o.helper || o.ghost || o.animate ? o.helper || "ui-resizable-helper" : null + } ); + + // Wrap the element if it cannot hold child nodes + if ( this.element[ 0 ].nodeName.match( /^(canvas|textarea|input|select|button|img)$/i ) ) { + + this.element.wrap( + $( "<div class='ui-wrapper' style='overflow: hidden;'></div>" ).css( { + position: this.element.css( "position" ), + width: this.element.outerWidth(), + height: this.element.outerHeight(), + top: this.element.css( "top" ), + left: this.element.css( "left" ) + } ) + ); + + this.element = this.element.parent().data( + "ui-resizable", this.element.resizable( "instance" ) + ); + + this.elementIsWrapper = true; + + margins = { + marginTop: this.originalElement.css( "marginTop" ), + marginRight: this.originalElement.css( "marginRight" ), + marginBottom: this.originalElement.css( "marginBottom" ), + marginLeft: this.originalElement.css( "marginLeft" ) + }; + + this.element.css( margins ); + this.originalElement.css( "margin", 0 ); + + // support: Safari + // Prevent Safari textarea resize + this.originalResizeStyle = this.originalElement.css( "resize" ); + this.originalElement.css( "resize", "none" ); + + this._proportionallyResizeElements.push( this.originalElement.css( { + position: "static", + zoom: 1, + display: "block" + } ) ); + + // Support: IE9 + // avoid IE jump (hard set the margin) + this.originalElement.css( margins ); + + this._proportionallyResize(); + } + + this._setupHandles(); + + if ( o.autoHide ) { + $( this.element ) + .on( "mouseenter", function() { + if ( o.disabled ) { + return; + } + that._removeClass( "ui-resizable-autohide" ); + that._handles.show(); + } ) + .on( "mouseleave", function() { + if ( o.disabled ) { + return; + } + if ( !that.resizing ) { + that._addClass( "ui-resizable-autohide" ); + that._handles.hide(); + } + } ); + } + + this._mouseInit(); + }, + + _destroy: function() { + + this._mouseDestroy(); + + var wrapper, + _destroy = function( exp ) { + $( exp ) + .removeData( "resizable" ) + .removeData( "ui-resizable" ) + .off( ".resizable" ) + .find( ".ui-resizable-handle" ) + .remove(); + }; + + // TODO: Unwrap at same DOM position + if ( this.elementIsWrapper ) { + _destroy( this.element ); + wrapper = this.element; + this.originalElement.css( { + position: wrapper.css( "position" ), + width: wrapper.outerWidth(), + height: wrapper.outerHeight(), + top: wrapper.css( "top" ), + left: wrapper.css( "left" ) + } ).insertAfter( wrapper ); + wrapper.remove(); + } + + this.originalElement.css( "resize", this.originalResizeStyle ); + _destroy( this.originalElement ); + + return this; + }, + + _setOption: function( key, value ) { + this._super( key, value ); + + switch ( key ) { + case "handles": + this._removeHandles(); + this._setupHandles(); + break; + default: + break; + } + }, + + _setupHandles: function() { + var o = this.options, handle, i, n, hname, axis, that = this; + this.handles = o.handles || + ( !$( ".ui-resizable-handle", this.element ).length ? + "e,s,se" : { + n: ".ui-resizable-n", + e: ".ui-resizable-e", + s: ".ui-resizable-s", + w: ".ui-resizable-w", + se: ".ui-resizable-se", + sw: ".ui-resizable-sw", + ne: ".ui-resizable-ne", + nw: ".ui-resizable-nw" + } ); + + this._handles = $(); + if ( this.handles.constructor === String ) { + + if ( this.handles === "all" ) { + this.handles = "n,e,s,w,se,sw,ne,nw"; + } + + n = this.handles.split( "," ); + this.handles = {}; + + for ( i = 0; i < n.length; i++ ) { + + handle = $.trim( n[ i ] ); + hname = "ui-resizable-" + handle; + axis = $( "<div>" ); + this._addClass( axis, "ui-resizable-handle " + hname ); + + axis.css( { zIndex: o.zIndex } ); + + this.handles[ handle ] = ".ui-resizable-" + handle; + this.element.append( axis ); + } + + } + + this._renderAxis = function( target ) { + + var i, axis, padPos, padWrapper; + + target = target || this.element; + + for ( i in this.handles ) { + + if ( this.handles[ i ].constructor === String ) { + this.handles[ i ] = this.element.children( this.handles[ i ] ).first().show(); + } else if ( this.handles[ i ].jquery || this.handles[ i ].nodeType ) { + this.handles[ i ] = $( this.handles[ i ] ); + this._on( this.handles[ i ], { "mousedown": that._mouseDown } ); + } + + if ( this.elementIsWrapper && + this.originalElement[ 0 ] + .nodeName + .match( /^(textarea|input|select|button)$/i ) ) { + axis = $( this.handles[ i ], this.element ); + + padWrapper = /sw|ne|nw|se|n|s/.test( i ) ? + axis.outerHeight() : + axis.outerWidth(); + + padPos = [ "padding", + /ne|nw|n/.test( i ) ? "Top" : + /se|sw|s/.test( i ) ? "Bottom" : + /^e$/.test( i ) ? "Right" : "Left" ].join( "" ); + + target.css( padPos, padWrapper ); + + this._proportionallyResize(); + } + + this._handles = this._handles.add( this.handles[ i ] ); + } + }; + + // TODO: make renderAxis a prototype function + this._renderAxis( this.element ); + + this._handles = this._handles.add( this.element.find( ".ui-resizable-handle" ) ); + this._handles.disableSelection(); + + this._handles.on( "mouseover", function() { + if ( !that.resizing ) { + if ( this.className ) { + axis = this.className.match( /ui-resizable-(se|sw|ne|nw|n|e|s|w)/i ); + } + that.axis = axis && axis[ 1 ] ? axis[ 1 ] : "se"; + } + } ); + + if ( o.autoHide ) { + this._handles.hide(); + this._addClass( "ui-resizable-autohide" ); + } + }, + + _removeHandles: function() { + this._handles.remove(); + }, + + _mouseCapture: function( event ) { + var i, handle, + capture = false; + + for ( i in this.handles ) { + handle = $( this.handles[ i ] )[ 0 ]; + if ( handle === event.target || $.contains( handle, event.target ) ) { + capture = true; + } + } + + return !this.options.disabled && capture; + }, + + _mouseStart: function( event ) { + + var curleft, curtop, cursor, + o = this.options, + el = this.element; + + this.resizing = true; + + this._renderProxy(); + + curleft = this._num( this.helper.css( "left" ) ); + curtop = this._num( this.helper.css( "top" ) ); + + if ( o.containment ) { + curleft += $( o.containment ).scrollLeft() || 0; + curtop += $( o.containment ).scrollTop() || 0; + } + + this.offset = this.helper.offset(); + this.position = { left: curleft, top: curtop }; + + this.size = this._helper ? { + width: this.helper.width(), + height: this.helper.height() + } : { + width: el.width(), + height: el.height() + }; + + this.originalSize = this._helper ? { + width: el.outerWidth(), + height: el.outerHeight() + } : { + width: el.width(), + height: el.height() + }; + + this.sizeDiff = { + width: el.outerWidth() - el.width(), + height: el.outerHeight() - el.height() + }; + + this.originalPosition = { left: curleft, top: curtop }; + this.originalMousePosition = { left: event.pageX, top: event.pageY }; + + this.aspectRatio = ( typeof o.aspectRatio === "number" ) ? + o.aspectRatio : + ( ( this.originalSize.width / this.originalSize.height ) || 1 ); + + cursor = $( ".ui-resizable-" + this.axis ).css( "cursor" ); + $( "body" ).css( "cursor", cursor === "auto" ? this.axis + "-resize" : cursor ); + + this._addClass( "ui-resizable-resizing" ); + this._propagate( "start", event ); + return true; + }, + + _mouseDrag: function( event ) { + + var data, props, + smp = this.originalMousePosition, + a = this.axis, + dx = ( event.pageX - smp.left ) || 0, + dy = ( event.pageY - smp.top ) || 0, + trigger = this._change[ a ]; + + this._updatePrevProperties(); + + if ( !trigger ) { + return false; + } + + data = trigger.apply( this, [ event, dx, dy ] ); + + this._updateVirtualBoundaries( event.shiftKey ); + if ( this._aspectRatio || event.shiftKey ) { + data = this._updateRatio( data, event ); + } + + data = this._respectSize( data, event ); + + this._updateCache( data ); + + this._propagate( "resize", event ); + + props = this._applyChanges(); + + if ( !this._helper && this._proportionallyResizeElements.length ) { + this._proportionallyResize(); + } + + if ( !$.isEmptyObject( props ) ) { + this._updatePrevProperties(); + this._trigger( "resize", event, this.ui() ); + this._applyChanges(); + } + + return false; + }, + + _mouseStop: function( event ) { + + this.resizing = false; + var pr, ista, soffseth, soffsetw, s, left, top, + o = this.options, that = this; + + if ( this._helper ) { + + pr = this._proportionallyResizeElements; + ista = pr.length && ( /textarea/i ).test( pr[ 0 ].nodeName ); + soffseth = ista && this._hasScroll( pr[ 0 ], "left" ) ? 0 : that.sizeDiff.height; + soffsetw = ista ? 0 : that.sizeDiff.width; + + s = { + width: ( that.helper.width() - soffsetw ), + height: ( that.helper.height() - soffseth ) + }; + left = ( parseFloat( that.element.css( "left" ) ) + + ( that.position.left - that.originalPosition.left ) ) || null; + top = ( parseFloat( that.element.css( "top" ) ) + + ( that.position.top - that.originalPosition.top ) ) || null; + + if ( !o.animate ) { + this.element.css( $.extend( s, { top: top, left: left } ) ); + } + + that.helper.height( that.size.height ); + that.helper.width( that.size.width ); + + if ( this._helper && !o.animate ) { + this._proportionallyResize(); + } + } + + $( "body" ).css( "cursor", "auto" ); + + this._removeClass( "ui-resizable-resizing" ); + + this._propagate( "stop", event ); + + if ( this._helper ) { + this.helper.remove(); + } + + return false; + + }, + + _updatePrevProperties: function() { + this.prevPosition = { + top: this.position.top, + left: this.position.left + }; + this.prevSize = { + width: this.size.width, + height: this.size.height + }; + }, + + _applyChanges: function() { + var props = {}; + + if ( this.position.top !== this.prevPosition.top ) { + props.top = this.position.top + "px"; + } + if ( this.position.left !== this.prevPosition.left ) { + props.left = this.position.left + "px"; + } + if ( this.size.width !== this.prevSize.width ) { + props.width = this.size.width + "px"; + } + if ( this.size.height !== this.prevSize.height ) { + props.height = this.size.height + "px"; + } + + this.helper.css( props ); + + return props; + }, + + _updateVirtualBoundaries: function( forceAspectRatio ) { + var pMinWidth, pMaxWidth, pMinHeight, pMaxHeight, b, + o = this.options; + + b = { + minWidth: this._isNumber( o.minWidth ) ? o.minWidth : 0, + maxWidth: this._isNumber( o.maxWidth ) ? o.maxWidth : Infinity, + minHeight: this._isNumber( o.minHeight ) ? o.minHeight : 0, + maxHeight: this._isNumber( o.maxHeight ) ? o.maxHeight : Infinity + }; + + if ( this._aspectRatio || forceAspectRatio ) { + pMinWidth = b.minHeight * this.aspectRatio; + pMinHeight = b.minWidth / this.aspectRatio; + pMaxWidth = b.maxHeight * this.aspectRatio; + pMaxHeight = b.maxWidth / this.aspectRatio; + + if ( pMinWidth > b.minWidth ) { + b.minWidth = pMinWidth; + } + if ( pMinHeight > b.minHeight ) { + b.minHeight = pMinHeight; + } + if ( pMaxWidth < b.maxWidth ) { + b.maxWidth = pMaxWidth; + } + if ( pMaxHeight < b.maxHeight ) { + b.maxHeight = pMaxHeight; + } + } + this._vBoundaries = b; + }, + + _updateCache: function( data ) { + this.offset = this.helper.offset(); + if ( this._isNumber( data.left ) ) { + this.position.left = data.left; + } + if ( this._isNumber( data.top ) ) { + this.position.top = data.top; + } + if ( this._isNumber( data.height ) ) { + this.size.height = data.height; + } + if ( this._isNumber( data.width ) ) { + this.size.width = data.width; + } + }, + + _updateRatio: function( data ) { + + var cpos = this.position, + csize = this.size, + a = this.axis; + + if ( this._isNumber( data.height ) ) { + data.width = ( data.height * this.aspectRatio ); + } else if ( this._isNumber( data.width ) ) { + data.height = ( data.width / this.aspectRatio ); + } + + if ( a === "sw" ) { + data.left = cpos.left + ( csize.width - data.width ); + data.top = null; + } + if ( a === "nw" ) { + data.top = cpos.top + ( csize.height - data.height ); + data.left = cpos.left + ( csize.width - data.width ); + } + + return data; + }, + + _respectSize: function( data ) { + + var o = this._vBoundaries, + a = this.axis, + ismaxw = this._isNumber( data.width ) && o.maxWidth && ( o.maxWidth < data.width ), + ismaxh = this._isNumber( data.height ) && o.maxHeight && ( o.maxHeight < data.height ), + isminw = this._isNumber( data.width ) && o.minWidth && ( o.minWidth > data.width ), + isminh = this._isNumber( data.height ) && o.minHeight && ( o.minHeight > data.height ), + dw = this.originalPosition.left + this.originalSize.width, + dh = this.originalPosition.top + this.originalSize.height, + cw = /sw|nw|w/.test( a ), ch = /nw|ne|n/.test( a ); + if ( isminw ) { + data.width = o.minWidth; + } + if ( isminh ) { + data.height = o.minHeight; + } + if ( ismaxw ) { + data.width = o.maxWidth; + } + if ( ismaxh ) { + data.height = o.maxHeight; + } + + if ( isminw && cw ) { + data.left = dw - o.minWidth; + } + if ( ismaxw && cw ) { + data.left = dw - o.maxWidth; + } + if ( isminh && ch ) { + data.top = dh - o.minHeight; + } + if ( ismaxh && ch ) { + data.top = dh - o.maxHeight; + } + + // Fixing jump error on top/left - bug #2330 + if ( !data.width && !data.height && !data.left && data.top ) { + data.top = null; + } else if ( !data.width && !data.height && !data.top && data.left ) { + data.left = null; + } + + return data; + }, + + _getPaddingPlusBorderDimensions: function( element ) { + var i = 0, + widths = [], + borders = [ + element.css( "borderTopWidth" ), + element.css( "borderRightWidth" ), + element.css( "borderBottomWidth" ), + element.css( "borderLeftWidth" ) + ], + paddings = [ + element.css( "paddingTop" ), + element.css( "paddingRight" ), + element.css( "paddingBottom" ), + element.css( "paddingLeft" ) + ]; + + for ( ; i < 4; i++ ) { + widths[ i ] = ( parseFloat( borders[ i ] ) || 0 ); + widths[ i ] += ( parseFloat( paddings[ i ] ) || 0 ); + } + + return { + height: widths[ 0 ] + widths[ 2 ], + width: widths[ 1 ] + widths[ 3 ] + }; + }, + + _proportionallyResize: function() { + + if ( !this._proportionallyResizeElements.length ) { + return; + } + + var prel, + i = 0, + element = this.helper || this.element; + + for ( ; i < this._proportionallyResizeElements.length; i++ ) { + + prel = this._proportionallyResizeElements[ i ]; + + // TODO: Seems like a bug to cache this.outerDimensions + // considering that we are in a loop. + if ( !this.outerDimensions ) { + this.outerDimensions = this._getPaddingPlusBorderDimensions( prel ); + } + + prel.css( { + height: ( element.height() - this.outerDimensions.height ) || 0, + width: ( element.width() - this.outerDimensions.width ) || 0 + } ); + + } + + }, + + _renderProxy: function() { + + var el = this.element, o = this.options; + this.elementOffset = el.offset(); + + if ( this._helper ) { + + this.helper = this.helper || $( "<div style='overflow:hidden;'></div>" ); + + this._addClass( this.helper, this._helper ); + this.helper.css( { + width: this.element.outerWidth(), + height: this.element.outerHeight(), + position: "absolute", + left: this.elementOffset.left + "px", + top: this.elementOffset.top + "px", + zIndex: ++o.zIndex //TODO: Don't modify option + } ); + + this.helper + .appendTo( "body" ) + .disableSelection(); + + } else { + this.helper = this.element; + } + + }, + + _change: { + e: function( event, dx ) { + return { width: this.originalSize.width + dx }; + }, + w: function( event, dx ) { + var cs = this.originalSize, sp = this.originalPosition; + return { left: sp.left + dx, width: cs.width - dx }; + }, + n: function( event, dx, dy ) { + var cs = this.originalSize, sp = this.originalPosition; + return { top: sp.top + dy, height: cs.height - dy }; + }, + s: function( event, dx, dy ) { + return { height: this.originalSize.height + dy }; + }, + se: function( event, dx, dy ) { + return $.extend( this._change.s.apply( this, arguments ), + this._change.e.apply( this, [ event, dx, dy ] ) ); + }, + sw: function( event, dx, dy ) { + return $.extend( this._change.s.apply( this, arguments ), + this._change.w.apply( this, [ event, dx, dy ] ) ); + }, + ne: function( event, dx, dy ) { + return $.extend( this._change.n.apply( this, arguments ), + this._change.e.apply( this, [ event, dx, dy ] ) ); + }, + nw: function( event, dx, dy ) { + return $.extend( this._change.n.apply( this, arguments ), + this._change.w.apply( this, [ event, dx, dy ] ) ); + } + }, + + _propagate: function( n, event ) { + $.ui.plugin.call( this, n, [ event, this.ui() ] ); + ( n !== "resize" && this._trigger( n, event, this.ui() ) ); + }, + + plugins: {}, + + ui: function() { + return { + originalElement: this.originalElement, + element: this.element, + helper: this.helper, + position: this.position, + size: this.size, + originalSize: this.originalSize, + originalPosition: this.originalPosition + }; + } + +} ); + +/* + * Resizable Extensions + */ + +$.ui.plugin.add( "resizable", "animate", { + + stop: function( event ) { + var that = $( this ).resizable( "instance" ), + o = that.options, + pr = that._proportionallyResizeElements, + ista = pr.length && ( /textarea/i ).test( pr[ 0 ].nodeName ), + soffseth = ista && that._hasScroll( pr[ 0 ], "left" ) ? 0 : that.sizeDiff.height, + soffsetw = ista ? 0 : that.sizeDiff.width, + style = { + width: ( that.size.width - soffsetw ), + height: ( that.size.height - soffseth ) + }, + left = ( parseFloat( that.element.css( "left" ) ) + + ( that.position.left - that.originalPosition.left ) ) || null, + top = ( parseFloat( that.element.css( "top" ) ) + + ( that.position.top - that.originalPosition.top ) ) || null; + + that.element.animate( + $.extend( style, top && left ? { top: top, left: left } : {} ), { + duration: o.animateDuration, + easing: o.animateEasing, + step: function() { + + var data = { + width: parseFloat( that.element.css( "width" ) ), + height: parseFloat( that.element.css( "height" ) ), + top: parseFloat( that.element.css( "top" ) ), + left: parseFloat( that.element.css( "left" ) ) + }; + + if ( pr && pr.length ) { + $( pr[ 0 ] ).css( { width: data.width, height: data.height } ); + } + + // Propagating resize, and updating values for each animation step + that._updateCache( data ); + that._propagate( "resize", event ); + + } + } + ); + } + +} ); + +$.ui.plugin.add( "resizable", "containment", { + + start: function() { + var element, p, co, ch, cw, width, height, + that = $( this ).resizable( "instance" ), + o = that.options, + el = that.element, + oc = o.containment, + ce = ( oc instanceof $ ) ? + oc.get( 0 ) : + ( /parent/.test( oc ) ) ? el.parent().get( 0 ) : oc; + + if ( !ce ) { + return; + } + + that.containerElement = $( ce ); + + if ( /document/.test( oc ) || oc === document ) { + that.containerOffset = { + left: 0, + top: 0 + }; + that.containerPosition = { + left: 0, + top: 0 + }; + + that.parentData = { + element: $( document ), + left: 0, + top: 0, + width: $( document ).width(), + height: $( document ).height() || document.body.parentNode.scrollHeight + }; + } else { + element = $( ce ); + p = []; + $( [ "Top", "Right", "Left", "Bottom" ] ).each( function( i, name ) { + p[ i ] = that._num( element.css( "padding" + name ) ); + } ); + + that.containerOffset = element.offset(); + that.containerPosition = element.position(); + that.containerSize = { + height: ( element.innerHeight() - p[ 3 ] ), + width: ( element.innerWidth() - p[ 1 ] ) + }; + + co = that.containerOffset; + ch = that.containerSize.height; + cw = that.containerSize.width; + width = ( that._hasScroll ( ce, "left" ) ? ce.scrollWidth : cw ); + height = ( that._hasScroll ( ce ) ? ce.scrollHeight : ch ) ; + + that.parentData = { + element: ce, + left: co.left, + top: co.top, + width: width, + height: height + }; + } + }, + + resize: function( event ) { + var woset, hoset, isParent, isOffsetRelative, + that = $( this ).resizable( "instance" ), + o = that.options, + co = that.containerOffset, + cp = that.position, + pRatio = that._aspectRatio || event.shiftKey, + cop = { + top: 0, + left: 0 + }, + ce = that.containerElement, + continueResize = true; + + if ( ce[ 0 ] !== document && ( /static/ ).test( ce.css( "position" ) ) ) { + cop = co; + } + + if ( cp.left < ( that._helper ? co.left : 0 ) ) { + that.size.width = that.size.width + + ( that._helper ? + ( that.position.left - co.left ) : + ( that.position.left - cop.left ) ); + + if ( pRatio ) { + that.size.height = that.size.width / that.aspectRatio; + continueResize = false; + } + that.position.left = o.helper ? co.left : 0; + } + + if ( cp.top < ( that._helper ? co.top : 0 ) ) { + that.size.height = that.size.height + + ( that._helper ? + ( that.position.top - co.top ) : + that.position.top ); + + if ( pRatio ) { + that.size.width = that.size.height * that.aspectRatio; + continueResize = false; + } + that.position.top = that._helper ? co.top : 0; + } + + isParent = that.containerElement.get( 0 ) === that.element.parent().get( 0 ); + isOffsetRelative = /relative|absolute/.test( that.containerElement.css( "position" ) ); + + if ( isParent && isOffsetRelative ) { + that.offset.left = that.parentData.left + that.position.left; + that.offset.top = that.parentData.top + that.position.top; + } else { + that.offset.left = that.element.offset().left; + that.offset.top = that.element.offset().top; + } + + woset = Math.abs( that.sizeDiff.width + + ( that._helper ? + that.offset.left - cop.left : + ( that.offset.left - co.left ) ) ); + + hoset = Math.abs( that.sizeDiff.height + + ( that._helper ? + that.offset.top - cop.top : + ( that.offset.top - co.top ) ) ); + + if ( woset + that.size.width >= that.parentData.width ) { + that.size.width = that.parentData.width - woset; + if ( pRatio ) { + that.size.height = that.size.width / that.aspectRatio; + continueResize = false; + } + } + + if ( hoset + that.size.height >= that.parentData.height ) { + that.size.height = that.parentData.height - hoset; + if ( pRatio ) { + that.size.width = that.size.height * that.aspectRatio; + continueResize = false; + } + } + + if ( !continueResize ) { + that.position.left = that.prevPosition.left; + that.position.top = that.prevPosition.top; + that.size.width = that.prevSize.width; + that.size.height = that.prevSize.height; + } + }, + + stop: function() { + var that = $( this ).resizable( "instance" ), + o = that.options, + co = that.containerOffset, + cop = that.containerPosition, + ce = that.containerElement, + helper = $( that.helper ), + ho = helper.offset(), + w = helper.outerWidth() - that.sizeDiff.width, + h = helper.outerHeight() - that.sizeDiff.height; + + if ( that._helper && !o.animate && ( /relative/ ).test( ce.css( "position" ) ) ) { + $( this ).css( { + left: ho.left - cop.left - co.left, + width: w, + height: h + } ); + } + + if ( that._helper && !o.animate && ( /static/ ).test( ce.css( "position" ) ) ) { + $( this ).css( { + left: ho.left - cop.left - co.left, + width: w, + height: h + } ); + } + } +} ); + +$.ui.plugin.add( "resizable", "alsoResize", { + + start: function() { + var that = $( this ).resizable( "instance" ), + o = that.options; + + $( o.alsoResize ).each( function() { + var el = $( this ); + el.data( "ui-resizable-alsoresize", { + width: parseFloat( el.width() ), height: parseFloat( el.height() ), + left: parseFloat( el.css( "left" ) ), top: parseFloat( el.css( "top" ) ) + } ); + } ); + }, + + resize: function( event, ui ) { + var that = $( this ).resizable( "instance" ), + o = that.options, + os = that.originalSize, + op = that.originalPosition, + delta = { + height: ( that.size.height - os.height ) || 0, + width: ( that.size.width - os.width ) || 0, + top: ( that.position.top - op.top ) || 0, + left: ( that.position.left - op.left ) || 0 + }; + + $( o.alsoResize ).each( function() { + var el = $( this ), start = $( this ).data( "ui-resizable-alsoresize" ), style = {}, + css = el.parents( ui.originalElement[ 0 ] ).length ? + [ "width", "height" ] : + [ "width", "height", "top", "left" ]; + + $.each( css, function( i, prop ) { + var sum = ( start[ prop ] || 0 ) + ( delta[ prop ] || 0 ); + if ( sum && sum >= 0 ) { + style[ prop ] = sum || null; + } + } ); + + el.css( style ); + } ); + }, + + stop: function() { + $( this ).removeData( "ui-resizable-alsoresize" ); + } +} ); + +$.ui.plugin.add( "resizable", "ghost", { + + start: function() { + + var that = $( this ).resizable( "instance" ), cs = that.size; + + that.ghost = that.originalElement.clone(); + that.ghost.css( { + opacity: 0.25, + display: "block", + position: "relative", + height: cs.height, + width: cs.width, + margin: 0, + left: 0, + top: 0 + } ); + + that._addClass( that.ghost, "ui-resizable-ghost" ); + + // DEPRECATED + // TODO: remove after 1.12 + if ( $.uiBackCompat !== false && typeof that.options.ghost === "string" ) { + + // Ghost option + that.ghost.addClass( this.options.ghost ); + } + + that.ghost.appendTo( that.helper ); + + }, + + resize: function() { + var that = $( this ).resizable( "instance" ); + if ( that.ghost ) { + that.ghost.css( { + position: "relative", + height: that.size.height, + width: that.size.width + } ); + } + }, + + stop: function() { + var that = $( this ).resizable( "instance" ); + if ( that.ghost && that.helper ) { + that.helper.get( 0 ).removeChild( that.ghost.get( 0 ) ); + } + } + +} ); + +$.ui.plugin.add( "resizable", "grid", { + + resize: function() { + var outerDimensions, + that = $( this ).resizable( "instance" ), + o = that.options, + cs = that.size, + os = that.originalSize, + op = that.originalPosition, + a = that.axis, + grid = typeof o.grid === "number" ? [ o.grid, o.grid ] : o.grid, + gridX = ( grid[ 0 ] || 1 ), + gridY = ( grid[ 1 ] || 1 ), + ox = Math.round( ( cs.width - os.width ) / gridX ) * gridX, + oy = Math.round( ( cs.height - os.height ) / gridY ) * gridY, + newWidth = os.width + ox, + newHeight = os.height + oy, + isMaxWidth = o.maxWidth && ( o.maxWidth < newWidth ), + isMaxHeight = o.maxHeight && ( o.maxHeight < newHeight ), + isMinWidth = o.minWidth && ( o.minWidth > newWidth ), + isMinHeight = o.minHeight && ( o.minHeight > newHeight ); + + o.grid = grid; + + if ( isMinWidth ) { + newWidth += gridX; + } + if ( isMinHeight ) { + newHeight += gridY; + } + if ( isMaxWidth ) { + newWidth -= gridX; + } + if ( isMaxHeight ) { + newHeight -= gridY; + } + + if ( /^(se|s|e)$/.test( a ) ) { + that.size.width = newWidth; + that.size.height = newHeight; + } else if ( /^(ne)$/.test( a ) ) { + that.size.width = newWidth; + that.size.height = newHeight; + that.position.top = op.top - oy; + } else if ( /^(sw)$/.test( a ) ) { + that.size.width = newWidth; + that.size.height = newHeight; + that.position.left = op.left - ox; + } else { + if ( newHeight - gridY <= 0 || newWidth - gridX <= 0 ) { + outerDimensions = that._getPaddingPlusBorderDimensions( this ); + } + + if ( newHeight - gridY > 0 ) { + that.size.height = newHeight; + that.position.top = op.top - oy; + } else { + newHeight = gridY - outerDimensions.height; + that.size.height = newHeight; + that.position.top = op.top + os.height - newHeight; + } + if ( newWidth - gridX > 0 ) { + that.size.width = newWidth; + that.position.left = op.left - ox; + } else { + newWidth = gridX - outerDimensions.width; + that.size.width = newWidth; + that.position.left = op.left + os.width - newWidth; + } + } + } + +} ); + +var widgetsResizable = $.ui.resizable; + + +/*! + * jQuery UI Dialog 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: Dialog +//>>group: Widgets +//>>description: Displays customizable dialog windows. +//>>docs: http://api.jqueryui.com/dialog/ +//>>demos: http://jqueryui.com/dialog/ +//>>css.structure: ../../themes/base/core.css +//>>css.structure: ../../themes/base/dialog.css +//>>css.theme: ../../themes/base/theme.css + + + +$.widget( "ui.dialog", { + version: "1.12.1", + options: { + appendTo: "body", + autoOpen: true, + buttons: [], + classes: { + "ui-dialog": "ui-corner-all", + "ui-dialog-titlebar": "ui-corner-all" + }, + closeOnEscape: true, + closeText: "Close", + draggable: true, + hide: null, + height: "auto", + maxHeight: null, + maxWidth: null, + minHeight: 150, + minWidth: 150, + modal: false, + position: { + my: "center", + at: "center", + of: window, + collision: "fit", + + // Ensure the titlebar is always visible + using: function( pos ) { + var topOffset = $( this ).css( pos ).offset().top; + if ( topOffset < 0 ) { + $( this ).css( "top", pos.top - topOffset ); + } + } + }, + resizable: true, + show: null, + title: null, + width: 300, + + // Callbacks + beforeClose: null, + close: null, + drag: null, + dragStart: null, + dragStop: null, + focus: null, + open: null, + resize: null, + resizeStart: null, + resizeStop: null + }, + + sizeRelatedOptions: { + buttons: true, + height: true, + maxHeight: true, + maxWidth: true, + minHeight: true, + minWidth: true, + width: true + }, + + resizableRelatedOptions: { + maxHeight: true, + maxWidth: true, + minHeight: true, + minWidth: true + }, + + _create: function() { + this.originalCss = { + display: this.element[ 0 ].style.display, + width: this.element[ 0 ].style.width, + minHeight: this.element[ 0 ].style.minHeight, + maxHeight: this.element[ 0 ].style.maxHeight, + height: this.element[ 0 ].style.height + }; + this.originalPosition = { + parent: this.element.parent(), + index: this.element.parent().children().index( this.element ) + }; + this.originalTitle = this.element.attr( "title" ); + if ( this.options.title == null && this.originalTitle != null ) { + this.options.title = this.originalTitle; + } + + // Dialogs can't be disabled + if ( this.options.disabled ) { + this.options.disabled = false; + } + + this._createWrapper(); + + this.element + .show() + .removeAttr( "title" ) + .appendTo( this.uiDialog ); + + this._addClass( "ui-dialog-content", "ui-widget-content" ); + + this._createTitlebar(); + this._createButtonPane(); + + if ( this.options.draggable && $.fn.draggable ) { + this._makeDraggable(); + } + if ( this.options.resizable && $.fn.resizable ) { + this._makeResizable(); + } + + this._isOpen = false; + + this._trackFocus(); + }, + + _init: function() { + if ( this.options.autoOpen ) { + this.open(); + } + }, + + _appendTo: function() { + var element = this.options.appendTo; + if ( element && ( element.jquery || element.nodeType ) ) { + return $( element ); + } + return this.document.find( element || "body" ).eq( 0 ); + }, + + _destroy: function() { + var next, + originalPosition = this.originalPosition; + + this._untrackInstance(); + this._destroyOverlay(); + + this.element + .removeUniqueId() + .css( this.originalCss ) + + // Without detaching first, the following becomes really slow + .detach(); + + this.uiDialog.remove(); + + if ( this.originalTitle ) { + this.element.attr( "title", this.originalTitle ); + } + + next = originalPosition.parent.children().eq( originalPosition.index ); + + // Don't try to place the dialog next to itself (#8613) + if ( next.length && next[ 0 ] !== this.element[ 0 ] ) { + next.before( this.element ); + } else { + originalPosition.parent.append( this.element ); + } + }, + + widget: function() { + return this.uiDialog; + }, + + disable: $.noop, + enable: $.noop, + + close: function( event ) { + var that = this; + + if ( !this._isOpen || this._trigger( "beforeClose", event ) === false ) { + return; + } + + this._isOpen = false; + this._focusedElement = null; + this._destroyOverlay(); + this._untrackInstance(); + + if ( !this.opener.filter( ":focusable" ).trigger( "focus" ).length ) { + + // Hiding a focused element doesn't trigger blur in WebKit + // so in case we have nothing to focus on, explicitly blur the active element + // https://bugs.webkit.org/show_bug.cgi?id=47182 + $.ui.safeBlur( $.ui.safeActiveElement( this.document[ 0 ] ) ); + } + + this._hide( this.uiDialog, this.options.hide, function() { + that._trigger( "close", event ); + } ); + }, + + isOpen: function() { + return this._isOpen; + }, + + moveToTop: function() { + this._moveToTop(); + }, + + _moveToTop: function( event, silent ) { + var moved = false, + zIndices = this.uiDialog.siblings( ".ui-front:visible" ).map( function() { + return +$( this ).css( "z-index" ); + } ).get(), + zIndexMax = Math.max.apply( null, zIndices ); + + if ( zIndexMax >= +this.uiDialog.css( "z-index" ) ) { + this.uiDialog.css( "z-index", zIndexMax + 1 ); + moved = true; + } + + if ( moved && !silent ) { + this._trigger( "focus", event ); + } + return moved; + }, + + open: function() { + var that = this; + if ( this._isOpen ) { + if ( this._moveToTop() ) { + this._focusTabbable(); + } + return; + } + + this._isOpen = true; + this.opener = $( $.ui.safeActiveElement( this.document[ 0 ] ) ); + + this._size(); + this._position(); + this._createOverlay(); + this._moveToTop( null, true ); + + // Ensure the overlay is moved to the top with the dialog, but only when + // opening. The overlay shouldn't move after the dialog is open so that + // modeless dialogs opened after the modal dialog stack properly. + if ( this.overlay ) { + this.overlay.css( "z-index", this.uiDialog.css( "z-index" ) - 1 ); + } + + this._show( this.uiDialog, this.options.show, function() { + that._focusTabbable(); + that._trigger( "focus" ); + } ); + + // Track the dialog immediately upon openening in case a focus event + // somehow occurs outside of the dialog before an element inside the + // dialog is focused (#10152) + this._makeFocusTarget(); + + this._trigger( "open" ); + }, + + _focusTabbable: function() { + + // Set focus to the first match: + // 1. An element that was focused previously + // 2. First element inside the dialog matching [autofocus] + // 3. Tabbable element inside the content element + // 4. Tabbable element inside the buttonpane + // 5. The close button + // 6. The dialog itself + var hasFocus = this._focusedElement; + if ( !hasFocus ) { + hasFocus = this.element.find( "[autofocus]" ); + } + if ( !hasFocus.length ) { + hasFocus = this.element.find( ":tabbable" ); + } + if ( !hasFocus.length ) { + hasFocus = this.uiDialogButtonPane.find( ":tabbable" ); + } + if ( !hasFocus.length ) { + hasFocus = this.uiDialogTitlebarClose.filter( ":tabbable" ); + } + if ( !hasFocus.length ) { + hasFocus = this.uiDialog; + } + hasFocus.eq( 0 ).trigger( "focus" ); + }, + + _keepFocus: function( event ) { + function checkFocus() { + var activeElement = $.ui.safeActiveElement( this.document[ 0 ] ), + isActive = this.uiDialog[ 0 ] === activeElement || + $.contains( this.uiDialog[ 0 ], activeElement ); + if ( !isActive ) { + this._focusTabbable(); + } + } + event.preventDefault(); + checkFocus.call( this ); + + // support: IE + // IE <= 8 doesn't prevent moving focus even with event.preventDefault() + // so we check again later + this._delay( checkFocus ); + }, + + _createWrapper: function() { + this.uiDialog = $( "<div>" ) + .hide() + .attr( { + + // Setting tabIndex makes the div focusable + tabIndex: -1, + role: "dialog" + } ) + .appendTo( this._appendTo() ); + + this._addClass( this.uiDialog, "ui-dialog", "ui-widget ui-widget-content ui-front" ); + this._on( this.uiDialog, { + keydown: function( event ) { + if ( this.options.closeOnEscape && !event.isDefaultPrevented() && event.keyCode && + event.keyCode === $.ui.keyCode.ESCAPE ) { + event.preventDefault(); + this.close( event ); + return; + } + + // Prevent tabbing out of dialogs + if ( event.keyCode !== $.ui.keyCode.TAB || event.isDefaultPrevented() ) { + return; + } + var tabbables = this.uiDialog.find( ":tabbable" ), + first = tabbables.filter( ":first" ), + last = tabbables.filter( ":last" ); + + if ( ( event.target === last[ 0 ] || event.target === this.uiDialog[ 0 ] ) && + !event.shiftKey ) { + this._delay( function() { + first.trigger( "focus" ); + } ); + event.preventDefault(); + } else if ( ( event.target === first[ 0 ] || + event.target === this.uiDialog[ 0 ] ) && event.shiftKey ) { + this._delay( function() { + last.trigger( "focus" ); + } ); + event.preventDefault(); + } + }, + mousedown: function( event ) { + if ( this._moveToTop( event ) ) { + this._focusTabbable(); + } + } + } ); + + // We assume that any existing aria-describedby attribute means + // that the dialog content is marked up properly + // otherwise we brute force the content as the description + if ( !this.element.find( "[aria-describedby]" ).length ) { + this.uiDialog.attr( { + "aria-describedby": this.element.uniqueId().attr( "id" ) + } ); + } + }, + + _createTitlebar: function() { + var uiDialogTitle; + + this.uiDialogTitlebar = $( "<div>" ); + this._addClass( this.uiDialogTitlebar, + "ui-dialog-titlebar", "ui-widget-header ui-helper-clearfix" ); + this._on( this.uiDialogTitlebar, { + mousedown: function( event ) { + + // Don't prevent click on close button (#8838) + // Focusing a dialog that is partially scrolled out of view + // causes the browser to scroll it into view, preventing the click event + if ( !$( event.target ).closest( ".ui-dialog-titlebar-close" ) ) { + + // Dialog isn't getting focus when dragging (#8063) + this.uiDialog.trigger( "focus" ); + } + } + } ); + + // Support: IE + // Use type="button" to prevent enter keypresses in textboxes from closing the + // dialog in IE (#9312) + this.uiDialogTitlebarClose = $( "<button type='button'></button>" ) + .button( { + label: $( "<a>" ).text( this.options.closeText ).html(), + icon: "ui-icon-closethick", + showLabel: false + } ) + .appendTo( this.uiDialogTitlebar ); + + this._addClass( this.uiDialogTitlebarClose, "ui-dialog-titlebar-close" ); + this._on( this.uiDialogTitlebarClose, { + click: function( event ) { + event.preventDefault(); + this.close( event ); + } + } ); + + uiDialogTitle = $( "<span>" ).uniqueId().prependTo( this.uiDialogTitlebar ); + this._addClass( uiDialogTitle, "ui-dialog-title" ); + this._title( uiDialogTitle ); + + this.uiDialogTitlebar.prependTo( this.uiDialog ); + + this.uiDialog.attr( { + "aria-labelledby": uiDialogTitle.attr( "id" ) + } ); + }, + + _title: function( title ) { + if ( this.options.title ) { + title.text( this.options.title ); + } else { + title.html( " " ); + } + }, + + _createButtonPane: function() { + this.uiDialogButtonPane = $( "<div>" ); + this._addClass( this.uiDialogButtonPane, "ui-dialog-buttonpane", + "ui-widget-content ui-helper-clearfix" ); + + this.uiButtonSet = $( "<div>" ) + .appendTo( this.uiDialogButtonPane ); + this._addClass( this.uiButtonSet, "ui-dialog-buttonset" ); + + this._createButtons(); + }, + + _createButtons: function() { + var that = this, + buttons = this.options.buttons; + + // If we already have a button pane, remove it + this.uiDialogButtonPane.remove(); + this.uiButtonSet.empty(); + + if ( $.isEmptyObject( buttons ) || ( $.isArray( buttons ) && !buttons.length ) ) { + this._removeClass( this.uiDialog, "ui-dialog-buttons" ); + return; + } + + $.each( buttons, function( name, props ) { + var click, buttonOptions; + props = $.isFunction( props ) ? + { click: props, text: name } : + props; + + // Default to a non-submitting button + props = $.extend( { type: "button" }, props ); + + // Change the context for the click callback to be the main element + click = props.click; + buttonOptions = { + icon: props.icon, + iconPosition: props.iconPosition, + showLabel: props.showLabel, + + // Deprecated options + icons: props.icons, + text: props.text + }; + + delete props.click; + delete props.icon; + delete props.iconPosition; + delete props.showLabel; + + // Deprecated options + delete props.icons; + if ( typeof props.text === "boolean" ) { + delete props.text; + } + + $( "<button></button>", props ) + .button( buttonOptions ) + .appendTo( that.uiButtonSet ) + .on( "click", function() { + click.apply( that.element[ 0 ], arguments ); + } ); + } ); + this._addClass( this.uiDialog, "ui-dialog-buttons" ); + this.uiDialogButtonPane.appendTo( this.uiDialog ); + }, + + _makeDraggable: function() { + var that = this, + options = this.options; + + function filteredUi( ui ) { + return { + position: ui.position, + offset: ui.offset + }; + } + + this.uiDialog.draggable( { + cancel: ".ui-dialog-content, .ui-dialog-titlebar-close", + handle: ".ui-dialog-titlebar", + containment: "document", + start: function( event, ui ) { + that._addClass( $( this ), "ui-dialog-dragging" ); + that._blockFrames(); + that._trigger( "dragStart", event, filteredUi( ui ) ); + }, + drag: function( event, ui ) { + that._trigger( "drag", event, filteredUi( ui ) ); + }, + stop: function( event, ui ) { + var left = ui.offset.left - that.document.scrollLeft(), + top = ui.offset.top - that.document.scrollTop(); + + options.position = { + my: "left top", + at: "left" + ( left >= 0 ? "+" : "" ) + left + " " + + "top" + ( top >= 0 ? "+" : "" ) + top, + of: that.window + }; + that._removeClass( $( this ), "ui-dialog-dragging" ); + that._unblockFrames(); + that._trigger( "dragStop", event, filteredUi( ui ) ); + } + } ); + }, + + _makeResizable: function() { + var that = this, + options = this.options, + handles = options.resizable, + + // .ui-resizable has position: relative defined in the stylesheet + // but dialogs have to use absolute or fixed positioning + position = this.uiDialog.css( "position" ), + resizeHandles = typeof handles === "string" ? + handles : + "n,e,s,w,se,sw,ne,nw"; + + function filteredUi( ui ) { + return { + originalPosition: ui.originalPosition, + originalSize: ui.originalSize, + position: ui.position, + size: ui.size + }; + } + + this.uiDialog.resizable( { + cancel: ".ui-dialog-content", + containment: "document", + alsoResize: this.element, + maxWidth: options.maxWidth, + maxHeight: options.maxHeight, + minWidth: options.minWidth, + minHeight: this._minHeight(), + handles: resizeHandles, + start: function( event, ui ) { + that._addClass( $( this ), "ui-dialog-resizing" ); + that._blockFrames(); + that._trigger( "resizeStart", event, filteredUi( ui ) ); + }, + resize: function( event, ui ) { + that._trigger( "resize", event, filteredUi( ui ) ); + }, + stop: function( event, ui ) { + var offset = that.uiDialog.offset(), + left = offset.left - that.document.scrollLeft(), + top = offset.top - that.document.scrollTop(); + + options.height = that.uiDialog.height(); + options.width = that.uiDialog.width(); + options.position = { + my: "left top", + at: "left" + ( left >= 0 ? "+" : "" ) + left + " " + + "top" + ( top >= 0 ? "+" : "" ) + top, + of: that.window + }; + that._removeClass( $( this ), "ui-dialog-resizing" ); + that._unblockFrames(); + that._trigger( "resizeStop", event, filteredUi( ui ) ); + } + } ) + .css( "position", position ); + }, + + _trackFocus: function() { + this._on( this.widget(), { + focusin: function( event ) { + this._makeFocusTarget(); + this._focusedElement = $( event.target ); + } + } ); + }, + + _makeFocusTarget: function() { + this._untrackInstance(); + this._trackingInstances().unshift( this ); + }, + + _untrackInstance: function() { + var instances = this._trackingInstances(), + exists = $.inArray( this, instances ); + if ( exists !== -1 ) { + instances.splice( exists, 1 ); + } + }, + + _trackingInstances: function() { + var instances = this.document.data( "ui-dialog-instances" ); + if ( !instances ) { + instances = []; + this.document.data( "ui-dialog-instances", instances ); + } + return instances; + }, + + _minHeight: function() { + var options = this.options; + + return options.height === "auto" ? + options.minHeight : + Math.min( options.minHeight, options.height ); + }, + + _position: function() { + + // Need to show the dialog to get the actual offset in the position plugin + var isVisible = this.uiDialog.is( ":visible" ); + if ( !isVisible ) { + this.uiDialog.show(); + } + this.uiDialog.position( this.options.position ); + if ( !isVisible ) { + this.uiDialog.hide(); + } + }, + + _setOptions: function( options ) { + var that = this, + resize = false, + resizableOptions = {}; + + $.each( options, function( key, value ) { + that._setOption( key, value ); + + if ( key in that.sizeRelatedOptions ) { + resize = true; + } + if ( key in that.resizableRelatedOptions ) { + resizableOptions[ key ] = value; + } + } ); + + if ( resize ) { + this._size(); + this._position(); + } + if ( this.uiDialog.is( ":data(ui-resizable)" ) ) { + this.uiDialog.resizable( "option", resizableOptions ); + } + }, + + _setOption: function( key, value ) { + var isDraggable, isResizable, + uiDialog = this.uiDialog; + + if ( key === "disabled" ) { + return; + } + + this._super( key, value ); + + if ( key === "appendTo" ) { + this.uiDialog.appendTo( this._appendTo() ); + } + + if ( key === "buttons" ) { + this._createButtons(); + } + + if ( key === "closeText" ) { + this.uiDialogTitlebarClose.button( { + + // Ensure that we always pass a string + label: $( "<a>" ).text( "" + this.options.closeText ).html() + } ); + } + + if ( key === "draggable" ) { + isDraggable = uiDialog.is( ":data(ui-draggable)" ); + if ( isDraggable && !value ) { + uiDialog.draggable( "destroy" ); + } + + if ( !isDraggable && value ) { + this._makeDraggable(); + } + } + + if ( key === "position" ) { + this._position(); + } + + if ( key === "resizable" ) { + + // currently resizable, becoming non-resizable + isResizable = uiDialog.is( ":data(ui-resizable)" ); + if ( isResizable && !value ) { + uiDialog.resizable( "destroy" ); + } + + // Currently resizable, changing handles + if ( isResizable && typeof value === "string" ) { + uiDialog.resizable( "option", "handles", value ); + } + + // Currently non-resizable, becoming resizable + if ( !isResizable && value !== false ) { + this._makeResizable(); + } + } + + if ( key === "title" ) { + this._title( this.uiDialogTitlebar.find( ".ui-dialog-title" ) ); + } + }, + + _size: function() { + + // If the user has resized the dialog, the .ui-dialog and .ui-dialog-content + // divs will both have width and height set, so we need to reset them + var nonContentHeight, minContentHeight, maxContentHeight, + options = this.options; + + // Reset content sizing + this.element.show().css( { + width: "auto", + minHeight: 0, + maxHeight: "none", + height: 0 + } ); + + if ( options.minWidth > options.width ) { + options.width = options.minWidth; + } + + // Reset wrapper sizing + // determine the height of all the non-content elements + nonContentHeight = this.uiDialog.css( { + height: "auto", + width: options.width + } ) + .outerHeight(); + minContentHeight = Math.max( 0, options.minHeight - nonContentHeight ); + maxContentHeight = typeof options.maxHeight === "number" ? + Math.max( 0, options.maxHeight - nonContentHeight ) : + "none"; + + if ( options.height === "auto" ) { + this.element.css( { + minHeight: minContentHeight, + maxHeight: maxContentHeight, + height: "auto" + } ); + } else { + this.element.height( Math.max( 0, options.height - nonContentHeight ) ); + } + + if ( this.uiDialog.is( ":data(ui-resizable)" ) ) { + this.uiDialog.resizable( "option", "minHeight", this._minHeight() ); + } + }, + + _blockFrames: function() { + this.iframeBlocks = this.document.find( "iframe" ).map( function() { + var iframe = $( this ); + + return $( "<div>" ) + .css( { + position: "absolute", + width: iframe.outerWidth(), + height: iframe.outerHeight() + } ) + .appendTo( iframe.parent() ) + .offset( iframe.offset() )[ 0 ]; + } ); + }, + + _unblockFrames: function() { + if ( this.iframeBlocks ) { + this.iframeBlocks.remove(); + delete this.iframeBlocks; + } + }, + + _allowInteraction: function( event ) { + if ( $( event.target ).closest( ".ui-dialog" ).length ) { + return true; + } + + // TODO: Remove hack when datepicker implements + // the .ui-front logic (#8989) + return !!$( event.target ).closest( ".ui-datepicker" ).length; + }, + + _createOverlay: function() { + if ( !this.options.modal ) { + return; + } + + // We use a delay in case the overlay is created from an + // event that we're going to be cancelling (#2804) + var isOpening = true; + this._delay( function() { + isOpening = false; + } ); + + if ( !this.document.data( "ui-dialog-overlays" ) ) { + + // Prevent use of anchors and inputs + // Using _on() for an event handler shared across many instances is + // safe because the dialogs stack and must be closed in reverse order + this._on( this.document, { + focusin: function( event ) { + if ( isOpening ) { + return; + } + + if ( !this._allowInteraction( event ) ) { + event.preventDefault(); + this._trackingInstances()[ 0 ]._focusTabbable(); + } + } + } ); + } + + this.overlay = $( "<div>" ) + .appendTo( this._appendTo() ); + + this._addClass( this.overlay, null, "ui-widget-overlay ui-front" ); + this._on( this.overlay, { + mousedown: "_keepFocus" + } ); + this.document.data( "ui-dialog-overlays", + ( this.document.data( "ui-dialog-overlays" ) || 0 ) + 1 ); + }, + + _destroyOverlay: function() { + if ( !this.options.modal ) { + return; + } + + if ( this.overlay ) { + var overlays = this.document.data( "ui-dialog-overlays" ) - 1; + + if ( !overlays ) { + this._off( this.document, "focusin" ); + this.document.removeData( "ui-dialog-overlays" ); + } else { + this.document.data( "ui-dialog-overlays", overlays ); + } + + this.overlay.remove(); + this.overlay = null; + } + } +} ); + +// DEPRECATED +// TODO: switch return back to widget declaration at top of file when this is removed +if ( $.uiBackCompat !== false ) { + + // Backcompat for dialogClass option + $.widget( "ui.dialog", $.ui.dialog, { + options: { + dialogClass: "" + }, + _createWrapper: function() { + this._super(); + this.uiDialog.addClass( this.options.dialogClass ); + }, + _setOption: function( key, value ) { + if ( key === "dialogClass" ) { + this.uiDialog + .removeClass( this.options.dialogClass ) + .addClass( value ); + } + this._superApply( arguments ); + } + } ); +} + +var widgetsDialog = $.ui.dialog; + + +/*! + * jQuery UI Droppable 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: Droppable +//>>group: Interactions +//>>description: Enables drop targets for draggable elements. +//>>docs: http://api.jqueryui.com/droppable/ +//>>demos: http://jqueryui.com/droppable/ + + + +$.widget( "ui.droppable", { + version: "1.12.1", + widgetEventPrefix: "drop", + options: { + accept: "*", + addClasses: true, + greedy: false, + scope: "default", + tolerance: "intersect", + + // Callbacks + activate: null, + deactivate: null, + drop: null, + out: null, + over: null + }, + _create: function() { + + var proportions, + o = this.options, + accept = o.accept; + + this.isover = false; + this.isout = true; + + this.accept = $.isFunction( accept ) ? accept : function( d ) { + return d.is( accept ); + }; + + this.proportions = function( /* valueToWrite */ ) { + if ( arguments.length ) { + + // Store the droppable's proportions + proportions = arguments[ 0 ]; + } else { + + // Retrieve or derive the droppable's proportions + return proportions ? + proportions : + proportions = { + width: this.element[ 0 ].offsetWidth, + height: this.element[ 0 ].offsetHeight + }; + } + }; + + this._addToManager( o.scope ); + + o.addClasses && this._addClass( "ui-droppable" ); + + }, + + _addToManager: function( scope ) { + + // Add the reference and positions to the manager + $.ui.ddmanager.droppables[ scope ] = $.ui.ddmanager.droppables[ scope ] || []; + $.ui.ddmanager.droppables[ scope ].push( this ); + }, + + _splice: function( drop ) { + var i = 0; + for ( ; i < drop.length; i++ ) { + if ( drop[ i ] === this ) { + drop.splice( i, 1 ); + } + } + }, + + _destroy: function() { + var drop = $.ui.ddmanager.droppables[ this.options.scope ]; + + this._splice( drop ); + }, + + _setOption: function( key, value ) { + + if ( key === "accept" ) { + this.accept = $.isFunction( value ) ? value : function( d ) { + return d.is( value ); + }; + } else if ( key === "scope" ) { + var drop = $.ui.ddmanager.droppables[ this.options.scope ]; + + this._splice( drop ); + this._addToManager( value ); + } + + this._super( key, value ); + }, + + _activate: function( event ) { + var draggable = $.ui.ddmanager.current; + + this._addActiveClass(); + if ( draggable ) { + this._trigger( "activate", event, this.ui( draggable ) ); + } + }, + + _deactivate: function( event ) { + var draggable = $.ui.ddmanager.current; + + this._removeActiveClass(); + if ( draggable ) { + this._trigger( "deactivate", event, this.ui( draggable ) ); + } + }, + + _over: function( event ) { + + var draggable = $.ui.ddmanager.current; + + // Bail if draggable and droppable are same element + if ( !draggable || ( draggable.currentItem || + draggable.element )[ 0 ] === this.element[ 0 ] ) { + return; + } + + if ( this.accept.call( this.element[ 0 ], ( draggable.currentItem || + draggable.element ) ) ) { + this._addHoverClass(); + this._trigger( "over", event, this.ui( draggable ) ); + } + + }, + + _out: function( event ) { + + var draggable = $.ui.ddmanager.current; + + // Bail if draggable and droppable are same element + if ( !draggable || ( draggable.currentItem || + draggable.element )[ 0 ] === this.element[ 0 ] ) { + return; + } + + if ( this.accept.call( this.element[ 0 ], ( draggable.currentItem || + draggable.element ) ) ) { + this._removeHoverClass(); + this._trigger( "out", event, this.ui( draggable ) ); + } + + }, + + _drop: function( event, custom ) { + + var draggable = custom || $.ui.ddmanager.current, + childrenIntersection = false; + + // Bail if draggable and droppable are same element + if ( !draggable || ( draggable.currentItem || + draggable.element )[ 0 ] === this.element[ 0 ] ) { + return false; + } + + this.element + .find( ":data(ui-droppable)" ) + .not( ".ui-draggable-dragging" ) + .each( function() { + var inst = $( this ).droppable( "instance" ); + if ( + inst.options.greedy && + !inst.options.disabled && + inst.options.scope === draggable.options.scope && + inst.accept.call( + inst.element[ 0 ], ( draggable.currentItem || draggable.element ) + ) && + intersect( + draggable, + $.extend( inst, { offset: inst.element.offset() } ), + inst.options.tolerance, event + ) + ) { + childrenIntersection = true; + return false; } + } ); + if ( childrenIntersection ) { + return false; + } + + if ( this.accept.call( this.element[ 0 ], + ( draggable.currentItem || draggable.element ) ) ) { + this._removeActiveClass(); + this._removeHoverClass(); + + this._trigger( "drop", event, this.ui( draggable ) ); + return this.element; + } + + return false; + + }, + + ui: function( c ) { + return { + draggable: ( c.currentItem || c.element ), + helper: c.helper, + position: c.position, + offset: c.positionAbs + }; + }, + + // Extension points just to make backcompat sane and avoid duplicating logic + // TODO: Remove in 1.13 along with call to it below + _addHoverClass: function() { + this._addClass( "ui-droppable-hover" ); + }, + + _removeHoverClass: function() { + this._removeClass( "ui-droppable-hover" ); + }, + + _addActiveClass: function() { + this._addClass( "ui-droppable-active" ); + }, + + _removeActiveClass: function() { + this._removeClass( "ui-droppable-active" ); + } +} ); + +var intersect = $.ui.intersect = ( function() { + function isOverAxis( x, reference, size ) { + return ( x >= reference ) && ( x < ( reference + size ) ); + } + + return function( draggable, droppable, toleranceMode, event ) { + + if ( !droppable.offset ) { + return false; + } + + var x1 = ( draggable.positionAbs || + draggable.position.absolute ).left + draggable.margins.left, + y1 = ( draggable.positionAbs || + draggable.position.absolute ).top + draggable.margins.top, + x2 = x1 + draggable.helperProportions.width, + y2 = y1 + draggable.helperProportions.height, + l = droppable.offset.left, + t = droppable.offset.top, + r = l + droppable.proportions().width, + b = t + droppable.proportions().height; + + switch ( toleranceMode ) { + case "fit": + return ( l <= x1 && x2 <= r && t <= y1 && y2 <= b ); + case "intersect": + return ( l < x1 + ( draggable.helperProportions.width / 2 ) && // Right Half + x2 - ( draggable.helperProportions.width / 2 ) < r && // Left Half + t < y1 + ( draggable.helperProportions.height / 2 ) && // Bottom Half + y2 - ( draggable.helperProportions.height / 2 ) < b ); // Top Half + case "pointer": + return isOverAxis( event.pageY, t, droppable.proportions().height ) && + isOverAxis( event.pageX, l, droppable.proportions().width ); + case "touch": + return ( + ( y1 >= t && y1 <= b ) || // Top edge touching + ( y2 >= t && y2 <= b ) || // Bottom edge touching + ( y1 < t && y2 > b ) // Surrounded vertically + ) && ( + ( x1 >= l && x1 <= r ) || // Left edge touching + ( x2 >= l && x2 <= r ) || // Right edge touching + ( x1 < l && x2 > r ) // Surrounded horizontally + ); + default: + return false; + } + }; +} )(); + +/* + This manager tracks offsets of draggables and droppables +*/ +$.ui.ddmanager = { + current: null, + droppables: { "default": [] }, + prepareOffsets: function( t, event ) { + + var i, j, + m = $.ui.ddmanager.droppables[ t.options.scope ] || [], + type = event ? event.type : null, // workaround for #2317 + list = ( t.currentItem || t.element ).find( ":data(ui-droppable)" ).addBack(); + + droppablesLoop: for ( i = 0; i < m.length; i++ ) { + + // No disabled and non-accepted + if ( m[ i ].options.disabled || ( t && !m[ i ].accept.call( m[ i ].element[ 0 ], + ( t.currentItem || t.element ) ) ) ) { + continue; + } + + // Filter out elements in the current dragged item + for ( j = 0; j < list.length; j++ ) { + if ( list[ j ] === m[ i ].element[ 0 ] ) { + m[ i ].proportions().height = 0; + continue droppablesLoop; + } + } + + m[ i ].visible = m[ i ].element.css( "display" ) !== "none"; + if ( !m[ i ].visible ) { + continue; + } + + // Activate the droppable if used directly from draggables + if ( type === "mousedown" ) { + m[ i ]._activate.call( m[ i ], event ); + } + + m[ i ].offset = m[ i ].element.offset(); + m[ i ].proportions( { + width: m[ i ].element[ 0 ].offsetWidth, + height: m[ i ].element[ 0 ].offsetHeight + } ); + + } + + }, + drop: function( draggable, event ) { + + var dropped = false; + + // Create a copy of the droppables in case the list changes during the drop (#9116) + $.each( ( $.ui.ddmanager.droppables[ draggable.options.scope ] || [] ).slice(), function() { + + if ( !this.options ) { + return; + } + if ( !this.options.disabled && this.visible && + intersect( draggable, this, this.options.tolerance, event ) ) { + dropped = this._drop.call( this, event ) || dropped; + } + + if ( !this.options.disabled && this.visible && this.accept.call( this.element[ 0 ], + ( draggable.currentItem || draggable.element ) ) ) { + this.isout = true; + this.isover = false; + this._deactivate.call( this, event ); + } + + } ); + return dropped; + + }, + dragStart: function( draggable, event ) { + + // Listen for scrolling so that if the dragging causes scrolling the position of the + // droppables can be recalculated (see #5003) + draggable.element.parentsUntil( "body" ).on( "scroll.droppable", function() { + if ( !draggable.options.refreshPositions ) { + $.ui.ddmanager.prepareOffsets( draggable, event ); + } + } ); + }, + drag: function( draggable, event ) { + + // If you have a highly dynamic page, you might try this option. It renders positions + // every time you move the mouse. + if ( draggable.options.refreshPositions ) { + $.ui.ddmanager.prepareOffsets( draggable, event ); + } + + // Run through all droppables and check their positions based on specific tolerance options + $.each( $.ui.ddmanager.droppables[ draggable.options.scope ] || [], function() { + + if ( this.options.disabled || this.greedyChild || !this.visible ) { + return; + } + + var parentInstance, scope, parent, + intersects = intersect( draggable, this, this.options.tolerance, event ), + c = !intersects && this.isover ? + "isout" : + ( intersects && !this.isover ? "isover" : null ); + if ( !c ) { + return; + } + + if ( this.options.greedy ) { + + // find droppable parents with same scope + scope = this.options.scope; + parent = this.element.parents( ":data(ui-droppable)" ).filter( function() { + return $( this ).droppable( "instance" ).options.scope === scope; + } ); + + if ( parent.length ) { + parentInstance = $( parent[ 0 ] ).droppable( "instance" ); + parentInstance.greedyChild = ( c === "isover" ); + } + } + + // We just moved into a greedy child + if ( parentInstance && c === "isover" ) { + parentInstance.isover = false; + parentInstance.isout = true; + parentInstance._out.call( parentInstance, event ); + } + + this[ c ] = true; + this[ c === "isout" ? "isover" : "isout" ] = false; + this[ c === "isover" ? "_over" : "_out" ].call( this, event ); + + // We just moved out of a greedy child + if ( parentInstance && c === "isout" ) { + parentInstance.isout = false; + parentInstance.isover = true; + parentInstance._over.call( parentInstance, event ); + } + } ); + + }, + dragStop: function( draggable, event ) { + draggable.element.parentsUntil( "body" ).off( "scroll.droppable" ); + + // Call prepareOffsets one final time since IE does not fire return scroll events when + // overflow was caused by drag (see #5003) + if ( !draggable.options.refreshPositions ) { + $.ui.ddmanager.prepareOffsets( draggable, event ); + } + } +}; + +// DEPRECATED +// TODO: switch return back to widget declaration at top of file when this is removed +if ( $.uiBackCompat !== false ) { + + // Backcompat for activeClass and hoverClass options + $.widget( "ui.droppable", $.ui.droppable, { + options: { + hoverClass: false, + activeClass: false + }, + _addActiveClass: function() { + this._super(); + if ( this.options.activeClass ) { + this.element.addClass( this.options.activeClass ); + } + }, + _removeActiveClass: function() { + this._super(); + if ( this.options.activeClass ) { + this.element.removeClass( this.options.activeClass ); + } + }, + _addHoverClass: function() { + this._super(); + if ( this.options.hoverClass ) { + this.element.addClass( this.options.hoverClass ); + } + }, + _removeHoverClass: function() { + this._super(); + if ( this.options.hoverClass ) { + this.element.removeClass( this.options.hoverClass ); + } + } + } ); +} + +var widgetsDroppable = $.ui.droppable; + + +/*! + * jQuery UI Progressbar 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: Progressbar +//>>group: Widgets +// jscs:disable maximumLineLength +//>>description: Displays a status indicator for loading state, standard percentage, and other progress indicators. +// jscs:enable maximumLineLength +//>>docs: http://api.jqueryui.com/progressbar/ +//>>demos: http://jqueryui.com/progressbar/ +//>>css.structure: ../../themes/base/core.css +//>>css.structure: ../../themes/base/progressbar.css +//>>css.theme: ../../themes/base/theme.css + + + +var widgetsProgressbar = $.widget( "ui.progressbar", { + version: "1.12.1", + options: { + classes: { + "ui-progressbar": "ui-corner-all", + "ui-progressbar-value": "ui-corner-left", + "ui-progressbar-complete": "ui-corner-right" + }, + max: 100, + value: 0, + + change: null, + complete: null + }, + + min: 0, + + _create: function() { + + // Constrain initial value + this.oldValue = this.options.value = this._constrainedValue(); + + this.element.attr( { + + // Only set static values; aria-valuenow and aria-valuemax are + // set inside _refreshValue() + role: "progressbar", + "aria-valuemin": this.min + } ); + this._addClass( "ui-progressbar", "ui-widget ui-widget-content" ); + + this.valueDiv = $( "<div>" ).appendTo( this.element ); + this._addClass( this.valueDiv, "ui-progressbar-value", "ui-widget-header" ); + this._refreshValue(); + }, + + _destroy: function() { + this.element.removeAttr( "role aria-valuemin aria-valuemax aria-valuenow" ); + + this.valueDiv.remove(); + }, + + value: function( newValue ) { + if ( newValue === undefined ) { + return this.options.value; + } + + this.options.value = this._constrainedValue( newValue ); + this._refreshValue(); + }, + + _constrainedValue: function( newValue ) { + if ( newValue === undefined ) { + newValue = this.options.value; + } + + this.indeterminate = newValue === false; + + // Sanitize value + if ( typeof newValue !== "number" ) { + newValue = 0; + } + + return this.indeterminate ? false : + Math.min( this.options.max, Math.max( this.min, newValue ) ); + }, + + _setOptions: function( options ) { + + // Ensure "value" option is set after other values (like max) + var value = options.value; + delete options.value; + + this._super( options ); + + this.options.value = this._constrainedValue( value ); + this._refreshValue(); + }, + + _setOption: function( key, value ) { + if ( key === "max" ) { + + // Don't allow a max less than min + value = Math.max( this.min, value ); + } + this._super( key, value ); + }, + + _setOptionDisabled: function( value ) { + this._super( value ); + + this.element.attr( "aria-disabled", value ); + this._toggleClass( null, "ui-state-disabled", !!value ); + }, + + _percentage: function() { + return this.indeterminate ? + 100 : + 100 * ( this.options.value - this.min ) / ( this.options.max - this.min ); + }, + + _refreshValue: function() { + var value = this.options.value, + percentage = this._percentage(); + + this.valueDiv + .toggle( this.indeterminate || value > this.min ) + .width( percentage.toFixed( 0 ) + "%" ); + + this + ._toggleClass( this.valueDiv, "ui-progressbar-complete", null, + value === this.options.max ) + ._toggleClass( "ui-progressbar-indeterminate", null, this.indeterminate ); + + if ( this.indeterminate ) { + this.element.removeAttr( "aria-valuenow" ); + if ( !this.overlayDiv ) { + this.overlayDiv = $( "<div>" ).appendTo( this.valueDiv ); + this._addClass( this.overlayDiv, "ui-progressbar-overlay" ); + } + } else { + this.element.attr( { + "aria-valuemax": this.options.max, + "aria-valuenow": value + } ); + if ( this.overlayDiv ) { + this.overlayDiv.remove(); + this.overlayDiv = null; + } + } + + if ( this.oldValue !== value ) { + this.oldValue = value; + this._trigger( "change" ); + } + if ( value === this.options.max ) { + this._trigger( "complete" ); + } + } +} ); + + +/*! + * jQuery UI Selectable 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: Selectable +//>>group: Interactions +//>>description: Allows groups of elements to be selected with the mouse. +//>>docs: http://api.jqueryui.com/selectable/ +//>>demos: http://jqueryui.com/selectable/ +//>>css.structure: ../../themes/base/selectable.css + + + +var widgetsSelectable = $.widget( "ui.selectable", $.ui.mouse, { + version: "1.12.1", + options: { + appendTo: "body", + autoRefresh: true, + distance: 0, + filter: "*", + tolerance: "touch", + + // Callbacks + selected: null, + selecting: null, + start: null, + stop: null, + unselected: null, + unselecting: null + }, + _create: function() { + var that = this; + + this._addClass( "ui-selectable" ); + + this.dragged = false; + + // Cache selectee children based on filter + this.refresh = function() { + that.elementPos = $( that.element[ 0 ] ).offset(); + that.selectees = $( that.options.filter, that.element[ 0 ] ); + that._addClass( that.selectees, "ui-selectee" ); + that.selectees.each( function() { + var $this = $( this ), + selecteeOffset = $this.offset(), + pos = { + left: selecteeOffset.left - that.elementPos.left, + top: selecteeOffset.top - that.elementPos.top + }; + $.data( this, "selectable-item", { + element: this, + $element: $this, + left: pos.left, + top: pos.top, + right: pos.left + $this.outerWidth(), + bottom: pos.top + $this.outerHeight(), + startselected: false, + selected: $this.hasClass( "ui-selected" ), + selecting: $this.hasClass( "ui-selecting" ), + unselecting: $this.hasClass( "ui-unselecting" ) + } ); + } ); + }; + this.refresh(); + + this._mouseInit(); + + this.helper = $( "<div>" ); + this._addClass( this.helper, "ui-selectable-helper" ); + }, + + _destroy: function() { + this.selectees.removeData( "selectable-item" ); + this._mouseDestroy(); + }, + + _mouseStart: function( event ) { + var that = this, + options = this.options; + + this.opos = [ event.pageX, event.pageY ]; + this.elementPos = $( this.element[ 0 ] ).offset(); + + if ( this.options.disabled ) { + return; + } + + this.selectees = $( options.filter, this.element[ 0 ] ); + + this._trigger( "start", event ); + + $( options.appendTo ).append( this.helper ); + + // position helper (lasso) + this.helper.css( { + "left": event.pageX, + "top": event.pageY, + "width": 0, + "height": 0 + } ); + + if ( options.autoRefresh ) { + this.refresh(); + } + + this.selectees.filter( ".ui-selected" ).each( function() { + var selectee = $.data( this, "selectable-item" ); + selectee.startselected = true; + if ( !event.metaKey && !event.ctrlKey ) { + that._removeClass( selectee.$element, "ui-selected" ); + selectee.selected = false; + that._addClass( selectee.$element, "ui-unselecting" ); + selectee.unselecting = true; + + // selectable UNSELECTING callback + that._trigger( "unselecting", event, { + unselecting: selectee.element + } ); + } + } ); + + $( event.target ).parents().addBack().each( function() { + var doSelect, + selectee = $.data( this, "selectable-item" ); + if ( selectee ) { + doSelect = ( !event.metaKey && !event.ctrlKey ) || + !selectee.$element.hasClass( "ui-selected" ); + that._removeClass( selectee.$element, doSelect ? "ui-unselecting" : "ui-selected" ) + ._addClass( selectee.$element, doSelect ? "ui-selecting" : "ui-unselecting" ); + selectee.unselecting = !doSelect; + selectee.selecting = doSelect; + selectee.selected = doSelect; + + // selectable (UN)SELECTING callback + if ( doSelect ) { + that._trigger( "selecting", event, { + selecting: selectee.element + } ); + } else { + that._trigger( "unselecting", event, { + unselecting: selectee.element + } ); + } + return false; + } + } ); + + }, + + _mouseDrag: function( event ) { + + this.dragged = true; + + if ( this.options.disabled ) { + return; + } + + var tmp, + that = this, + options = this.options, + x1 = this.opos[ 0 ], + y1 = this.opos[ 1 ], + x2 = event.pageX, + y2 = event.pageY; + + if ( x1 > x2 ) { tmp = x2; x2 = x1; x1 = tmp; } + if ( y1 > y2 ) { tmp = y2; y2 = y1; y1 = tmp; } + this.helper.css( { left: x1, top: y1, width: x2 - x1, height: y2 - y1 } ); + + this.selectees.each( function() { + var selectee = $.data( this, "selectable-item" ), + hit = false, + offset = {}; + + //prevent helper from being selected if appendTo: selectable + if ( !selectee || selectee.element === that.element[ 0 ] ) { + return; + } + + offset.left = selectee.left + that.elementPos.left; + offset.right = selectee.right + that.elementPos.left; + offset.top = selectee.top + that.elementPos.top; + offset.bottom = selectee.bottom + that.elementPos.top; + + if ( options.tolerance === "touch" ) { + hit = ( !( offset.left > x2 || offset.right < x1 || offset.top > y2 || + offset.bottom < y1 ) ); + } else if ( options.tolerance === "fit" ) { + hit = ( offset.left > x1 && offset.right < x2 && offset.top > y1 && + offset.bottom < y2 ); + } + + if ( hit ) { + + // SELECT + if ( selectee.selected ) { + that._removeClass( selectee.$element, "ui-selected" ); + selectee.selected = false; + } + if ( selectee.unselecting ) { + that._removeClass( selectee.$element, "ui-unselecting" ); + selectee.unselecting = false; + } + if ( !selectee.selecting ) { + that._addClass( selectee.$element, "ui-selecting" ); + selectee.selecting = true; + + // selectable SELECTING callback + that._trigger( "selecting", event, { + selecting: selectee.element + } ); + } + } else { + + // UNSELECT + if ( selectee.selecting ) { + if ( ( event.metaKey || event.ctrlKey ) && selectee.startselected ) { + that._removeClass( selectee.$element, "ui-selecting" ); + selectee.selecting = false; + that._addClass( selectee.$element, "ui-selected" ); + selectee.selected = true; + } else { + that._removeClass( selectee.$element, "ui-selecting" ); + selectee.selecting = false; + if ( selectee.startselected ) { + that._addClass( selectee.$element, "ui-unselecting" ); + selectee.unselecting = true; + } + + // selectable UNSELECTING callback + that._trigger( "unselecting", event, { + unselecting: selectee.element + } ); + } + } + if ( selectee.selected ) { + if ( !event.metaKey && !event.ctrlKey && !selectee.startselected ) { + that._removeClass( selectee.$element, "ui-selected" ); + selectee.selected = false; + + that._addClass( selectee.$element, "ui-unselecting" ); + selectee.unselecting = true; + + // selectable UNSELECTING callback + that._trigger( "unselecting", event, { + unselecting: selectee.element + } ); + } + } + } + } ); + + return false; + }, + + _mouseStop: function( event ) { + var that = this; + + this.dragged = false; + + $( ".ui-unselecting", this.element[ 0 ] ).each( function() { + var selectee = $.data( this, "selectable-item" ); + that._removeClass( selectee.$element, "ui-unselecting" ); + selectee.unselecting = false; + selectee.startselected = false; + that._trigger( "unselected", event, { + unselected: selectee.element + } ); + } ); + $( ".ui-selecting", this.element[ 0 ] ).each( function() { + var selectee = $.data( this, "selectable-item" ); + that._removeClass( selectee.$element, "ui-selecting" ) + ._addClass( selectee.$element, "ui-selected" ); + selectee.selecting = false; + selectee.selected = true; + selectee.startselected = true; + that._trigger( "selected", event, { + selected: selectee.element + } ); + } ); + this._trigger( "stop", event ); + + this.helper.remove(); + + return false; + } + +} ); + + +/*! + * jQuery UI Selectmenu 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: Selectmenu +//>>group: Widgets +// jscs:disable maximumLineLength +//>>description: Duplicates and extends the functionality of a native HTML select element, allowing it to be customizable in behavior and appearance far beyond the limitations of a native select. +// jscs:enable maximumLineLength +//>>docs: http://api.jqueryui.com/selectmenu/ +//>>demos: http://jqueryui.com/selectmenu/ +//>>css.structure: ../../themes/base/core.css +//>>css.structure: ../../themes/base/selectmenu.css, ../../themes/base/button.css +//>>css.theme: ../../themes/base/theme.css + + + +var widgetsSelectmenu = $.widget( "ui.selectmenu", [ $.ui.formResetMixin, { + version: "1.12.1", + defaultElement: "<select>", + options: { + appendTo: null, + classes: { + "ui-selectmenu-button-open": "ui-corner-top", + "ui-selectmenu-button-closed": "ui-corner-all" + }, + disabled: null, + icons: { + button: "ui-icon-triangle-1-s" + }, + position: { + my: "left top", + at: "left bottom", + collision: "none" + }, + width: false, + + // Callbacks + change: null, + close: null, + focus: null, + open: null, + select: null + }, + + _create: function() { + var selectmenuId = this.element.uniqueId().attr( "id" ); + this.ids = { + element: selectmenuId, + button: selectmenuId + "-button", + menu: selectmenuId + "-menu" + }; + + this._drawButton(); + this._drawMenu(); + this._bindFormResetHandler(); + + this._rendered = false; + this.menuItems = $(); + }, + + _drawButton: function() { + var icon, + that = this, + item = this._parseOption( + this.element.find( "option:selected" ), + this.element[ 0 ].selectedIndex + ); + + // Associate existing label with the new button + this.labels = this.element.labels().attr( "for", this.ids.button ); + this._on( this.labels, { + click: function( event ) { + this.button.focus(); + event.preventDefault(); + } + } ); + + // Hide original select element + this.element.hide(); + + // Create button + this.button = $( "<span>", { + tabindex: this.options.disabled ? -1 : 0, + id: this.ids.button, + role: "combobox", + "aria-expanded": "false", + "aria-autocomplete": "list", + "aria-owns": this.ids.menu, + "aria-haspopup": "true", + title: this.element.attr( "title" ) + } ) + .insertAfter( this.element ); + + this._addClass( this.button, "ui-selectmenu-button ui-selectmenu-button-closed", + "ui-button ui-widget" ); + + icon = $( "<span>" ).appendTo( this.button ); + this._addClass( icon, "ui-selectmenu-icon", "ui-icon " + this.options.icons.button ); + this.buttonItem = this._renderButtonItem( item ) + .appendTo( this.button ); + + if ( this.options.width !== false ) { + this._resizeButton(); + } + + this._on( this.button, this._buttonEvents ); + this.button.one( "focusin", function() { + + // Delay rendering the menu items until the button receives focus. + // The menu may have already been rendered via a programmatic open. + if ( !that._rendered ) { + that._refreshMenu(); + } + } ); + }, + + _drawMenu: function() { + var that = this; + + // Create menu + this.menu = $( "<ul>", { + "aria-hidden": "true", + "aria-labelledby": this.ids.button, + id: this.ids.menu + } ); + + // Wrap menu + this.menuWrap = $( "<div>" ).append( this.menu ); + this._addClass( this.menuWrap, "ui-selectmenu-menu", "ui-front" ); + this.menuWrap.appendTo( this._appendTo() ); + + // Initialize menu widget + this.menuInstance = this.menu + .menu( { + classes: { + "ui-menu": "ui-corner-bottom" + }, + role: "listbox", + select: function( event, ui ) { + event.preventDefault(); + + // Support: IE8 + // If the item was selected via a click, the text selection + // will be destroyed in IE + that._setSelection(); + + that._select( ui.item.data( "ui-selectmenu-item" ), event ); + }, + focus: function( event, ui ) { + var item = ui.item.data( "ui-selectmenu-item" ); + + // Prevent inital focus from firing and check if its a newly focused item + if ( that.focusIndex != null && item.index !== that.focusIndex ) { + that._trigger( "focus", event, { item: item } ); + if ( !that.isOpen ) { + that._select( item, event ); + } + } + that.focusIndex = item.index; + + that.button.attr( "aria-activedescendant", + that.menuItems.eq( item.index ).attr( "id" ) ); + } + } ) + .menu( "instance" ); + + // Don't close the menu on mouseleave + this.menuInstance._off( this.menu, "mouseleave" ); + + // Cancel the menu's collapseAll on document click + this.menuInstance._closeOnDocumentClick = function() { + return false; + }; + + // Selects often contain empty items, but never contain dividers + this.menuInstance._isDivider = function() { + return false; + }; + }, + + refresh: function() { + this._refreshMenu(); + this.buttonItem.replaceWith( + this.buttonItem = this._renderButtonItem( + + // Fall back to an empty object in case there are no options + this._getSelectedItem().data( "ui-selectmenu-item" ) || {} + ) + ); + if ( this.options.width === null ) { + this._resizeButton(); + } + }, + + _refreshMenu: function() { + var item, + options = this.element.find( "option" ); + + this.menu.empty(); + + this._parseOptions( options ); + this._renderMenu( this.menu, this.items ); + + this.menuInstance.refresh(); + this.menuItems = this.menu.find( "li" ) + .not( ".ui-selectmenu-optgroup" ) + .find( ".ui-menu-item-wrapper" ); + + this._rendered = true; + + if ( !options.length ) { + return; + } + + item = this._getSelectedItem(); + + // Update the menu to have the correct item focused + this.menuInstance.focus( null, item ); + this._setAria( item.data( "ui-selectmenu-item" ) ); + + // Set disabled state + this._setOption( "disabled", this.element.prop( "disabled" ) ); + }, + + open: function( event ) { + if ( this.options.disabled ) { + return; + } + + // If this is the first time the menu is being opened, render the items + if ( !this._rendered ) { + this._refreshMenu(); + } else { + + // Menu clears focus on close, reset focus to selected item + this._removeClass( this.menu.find( ".ui-state-active" ), null, "ui-state-active" ); + this.menuInstance.focus( null, this._getSelectedItem() ); + } + + // If there are no options, don't open the menu + if ( !this.menuItems.length ) { + return; + } + + this.isOpen = true; + this._toggleAttr(); + this._resizeMenu(); + this._position(); + + this._on( this.document, this._documentClick ); + + this._trigger( "open", event ); + }, + + _position: function() { + this.menuWrap.position( $.extend( { of: this.button }, this.options.position ) ); + }, + + close: function( event ) { + if ( !this.isOpen ) { + return; + } + + this.isOpen = false; + this._toggleAttr(); + + this.range = null; + this._off( this.document ); + + this._trigger( "close", event ); + }, + + widget: function() { + return this.button; + }, + + menuWidget: function() { + return this.menu; + }, + + _renderButtonItem: function( item ) { + var buttonItem = $( "<span>" ); + + this._setText( buttonItem, item.label ); + this._addClass( buttonItem, "ui-selectmenu-text" ); + + return buttonItem; + }, + + _renderMenu: function( ul, items ) { + var that = this, + currentOptgroup = ""; + + $.each( items, function( index, item ) { + var li; + + if ( item.optgroup !== currentOptgroup ) { + li = $( "<li>", { + text: item.optgroup + } ); + that._addClass( li, "ui-selectmenu-optgroup", "ui-menu-divider" + + ( item.element.parent( "optgroup" ).prop( "disabled" ) ? + " ui-state-disabled" : + "" ) ); + + li.appendTo( ul ); + + currentOptgroup = item.optgroup; + } + + that._renderItemData( ul, item ); + } ); + }, + + _renderItemData: function( ul, item ) { + return this._renderItem( ul, item ).data( "ui-selectmenu-item", item ); + }, + + _renderItem: function( ul, item ) { + var li = $( "<li>" ), + wrapper = $( "<div>", { + title: item.element.attr( "title" ) + } ); + + if ( item.disabled ) { + this._addClass( li, null, "ui-state-disabled" ); + } + this._setText( wrapper, item.label ); + + return li.append( wrapper ).appendTo( ul ); + }, + + _setText: function( element, value ) { + if ( value ) { + element.text( value ); + } else { + element.html( " " ); + } + }, + + _move: function( direction, event ) { + var item, next, + filter = ".ui-menu-item"; + + if ( this.isOpen ) { + item = this.menuItems.eq( this.focusIndex ).parent( "li" ); + } else { + item = this.menuItems.eq( this.element[ 0 ].selectedIndex ).parent( "li" ); + filter += ":not(.ui-state-disabled)"; + } + + if ( direction === "first" || direction === "last" ) { + next = item[ direction === "first" ? "prevAll" : "nextAll" ]( filter ).eq( -1 ); + } else { + next = item[ direction + "All" ]( filter ).eq( 0 ); + } + + if ( next.length ) { + this.menuInstance.focus( event, next ); + } + }, + + _getSelectedItem: function() { + return this.menuItems.eq( this.element[ 0 ].selectedIndex ).parent( "li" ); + }, + + _toggle: function( event ) { + this[ this.isOpen ? "close" : "open" ]( event ); + }, + + _setSelection: function() { + var selection; + + if ( !this.range ) { + return; + } + + if ( window.getSelection ) { + selection = window.getSelection(); + selection.removeAllRanges(); + selection.addRange( this.range ); + + // Support: IE8 + } else { + this.range.select(); + } + + // Support: IE + // Setting the text selection kills the button focus in IE, but + // restoring the focus doesn't kill the selection. + this.button.focus(); + }, + + _documentClick: { + mousedown: function( event ) { + if ( !this.isOpen ) { + return; + } + + if ( !$( event.target ).closest( ".ui-selectmenu-menu, #" + + $.ui.escapeSelector( this.ids.button ) ).length ) { + this.close( event ); + } + } + }, + + _buttonEvents: { + + // Prevent text selection from being reset when interacting with the selectmenu (#10144) + mousedown: function() { + var selection; + + if ( window.getSelection ) { + selection = window.getSelection(); + if ( selection.rangeCount ) { + this.range = selection.getRangeAt( 0 ); + } + + // Support: IE8 + } else { + this.range = document.selection.createRange(); + } + }, + + click: function( event ) { + this._setSelection(); + this._toggle( event ); + }, + + keydown: function( event ) { + var preventDefault = true; + switch ( event.keyCode ) { + case $.ui.keyCode.TAB: + case $.ui.keyCode.ESCAPE: + this.close( event ); + preventDefault = false; + break; + case $.ui.keyCode.ENTER: + if ( this.isOpen ) { + this._selectFocusedItem( event ); + } + break; + case $.ui.keyCode.UP: + if ( event.altKey ) { + this._toggle( event ); + } else { + this._move( "prev", event ); + } + break; + case $.ui.keyCode.DOWN: + if ( event.altKey ) { + this._toggle( event ); + } else { + this._move( "next", event ); + } + break; + case $.ui.keyCode.SPACE: + if ( this.isOpen ) { + this._selectFocusedItem( event ); + } else { + this._toggle( event ); + } + break; + case $.ui.keyCode.LEFT: + this._move( "prev", event ); + break; + case $.ui.keyCode.RIGHT: + this._move( "next", event ); + break; + case $.ui.keyCode.HOME: + case $.ui.keyCode.PAGE_UP: + this._move( "first", event ); + break; + case $.ui.keyCode.END: + case $.ui.keyCode.PAGE_DOWN: + this._move( "last", event ); + break; + default: + this.menu.trigger( event ); + preventDefault = false; + } + + if ( preventDefault ) { + event.preventDefault(); + } + } + }, + + _selectFocusedItem: function( event ) { + var item = this.menuItems.eq( this.focusIndex ).parent( "li" ); + if ( !item.hasClass( "ui-state-disabled" ) ) { + this._select( item.data( "ui-selectmenu-item" ), event ); + } + }, + + _select: function( item, event ) { + var oldIndex = this.element[ 0 ].selectedIndex; + + // Change native select element + this.element[ 0 ].selectedIndex = item.index; + this.buttonItem.replaceWith( this.buttonItem = this._renderButtonItem( item ) ); + this._setAria( item ); + this._trigger( "select", event, { item: item } ); + + if ( item.index !== oldIndex ) { + this._trigger( "change", event, { item: item } ); + } + + this.close( event ); + }, + + _setAria: function( item ) { + var id = this.menuItems.eq( item.index ).attr( "id" ); + + this.button.attr( { + "aria-labelledby": id, + "aria-activedescendant": id + } ); + this.menu.attr( "aria-activedescendant", id ); + }, + + _setOption: function( key, value ) { + if ( key === "icons" ) { + var icon = this.button.find( "span.ui-icon" ); + this._removeClass( icon, null, this.options.icons.button ) + ._addClass( icon, null, value.button ); + } + + this._super( key, value ); + + if ( key === "appendTo" ) { + this.menuWrap.appendTo( this._appendTo() ); + } + + if ( key === "width" ) { + this._resizeButton(); + } + }, + + _setOptionDisabled: function( value ) { + this._super( value ); + + this.menuInstance.option( "disabled", value ); + this.button.attr( "aria-disabled", value ); + this._toggleClass( this.button, null, "ui-state-disabled", value ); + + this.element.prop( "disabled", value ); + if ( value ) { + this.button.attr( "tabindex", -1 ); + this.close(); + } else { + this.button.attr( "tabindex", 0 ); + } + }, + + _appendTo: function() { + var element = this.options.appendTo; + + if ( element ) { + element = element.jquery || element.nodeType ? + $( element ) : + this.document.find( element ).eq( 0 ); + } + + if ( !element || !element[ 0 ] ) { + element = this.element.closest( ".ui-front, dialog" ); + } + + if ( !element.length ) { + element = this.document[ 0 ].body; + } + + return element; + }, + + _toggleAttr: function() { + this.button.attr( "aria-expanded", this.isOpen ); + + // We can't use two _toggleClass() calls here, because we need to make sure + // we always remove classes first and add them second, otherwise if both classes have the + // same theme class, it will be removed after we add it. + this._removeClass( this.button, "ui-selectmenu-button-" + + ( this.isOpen ? "closed" : "open" ) ) + ._addClass( this.button, "ui-selectmenu-button-" + + ( this.isOpen ? "open" : "closed" ) ) + ._toggleClass( this.menuWrap, "ui-selectmenu-open", null, this.isOpen ); + + this.menu.attr( "aria-hidden", !this.isOpen ); + }, + + _resizeButton: function() { + var width = this.options.width; + + // For `width: false`, just remove inline style and stop + if ( width === false ) { + this.button.css( "width", "" ); + return; + } + + // For `width: null`, match the width of the original element + if ( width === null ) { + width = this.element.show().outerWidth(); + this.element.hide(); + } + + this.button.outerWidth( width ); + }, + + _resizeMenu: function() { + this.menu.outerWidth( Math.max( + this.button.outerWidth(), + + // Support: IE10 + // IE10 wraps long text (possibly a rounding bug) + // so we add 1px to avoid the wrapping + this.menu.width( "" ).outerWidth() + 1 + ) ); + }, + + _getCreateOptions: function() { + var options = this._super(); + + options.disabled = this.element.prop( "disabled" ); + + return options; + }, + + _parseOptions: function( options ) { + var that = this, + data = []; + options.each( function( index, item ) { + data.push( that._parseOption( $( item ), index ) ); + } ); + this.items = data; + }, + + _parseOption: function( option, index ) { + var optgroup = option.parent( "optgroup" ); + + return { + element: option, + index: index, + value: option.val(), + label: option.text(), + optgroup: optgroup.attr( "label" ) || "", + disabled: optgroup.prop( "disabled" ) || option.prop( "disabled" ) + }; + }, + + _destroy: function() { + this._unbindFormResetHandler(); + this.menuWrap.remove(); + this.button.remove(); + this.element.show(); + this.element.removeUniqueId(); + this.labels.attr( "for", this.ids.element ); + } +} ] ); + + +/*! + * jQuery UI Slider 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: Slider +//>>group: Widgets +//>>description: Displays a flexible slider with ranges and accessibility via keyboard. +//>>docs: http://api.jqueryui.com/slider/ +//>>demos: http://jqueryui.com/slider/ +//>>css.structure: ../../themes/base/core.css +//>>css.structure: ../../themes/base/slider.css +//>>css.theme: ../../themes/base/theme.css + + + +var widgetsSlider = $.widget( "ui.slider", $.ui.mouse, { + version: "1.12.1", + widgetEventPrefix: "slide", + + options: { + animate: false, + classes: { + "ui-slider": "ui-corner-all", + "ui-slider-handle": "ui-corner-all", + + // Note: ui-widget-header isn't the most fittingly semantic framework class for this + // element, but worked best visually with a variety of themes + "ui-slider-range": "ui-corner-all ui-widget-header" + }, + distance: 0, + max: 100, + min: 0, + orientation: "horizontal", + range: false, + step: 1, + value: 0, + values: null, + + // Callbacks + change: null, + slide: null, + start: null, + stop: null + }, + + // Number of pages in a slider + // (how many times can you page up/down to go through the whole range) + numPages: 5, + + _create: function() { + this._keySliding = false; + this._mouseSliding = false; + this._animateOff = true; + this._handleIndex = null; + this._detectOrientation(); + this._mouseInit(); + this._calculateNewMax(); + + this._addClass( "ui-slider ui-slider-" + this.orientation, + "ui-widget ui-widget-content" ); + + this._refresh(); + + this._animateOff = false; + }, + + _refresh: function() { + this._createRange(); + this._createHandles(); + this._setupEvents(); + this._refreshValue(); + }, + + _createHandles: function() { + var i, handleCount, + options = this.options, + existingHandles = this.element.find( ".ui-slider-handle" ), + handle = "<span tabindex='0'></span>", + handles = []; + + handleCount = ( options.values && options.values.length ) || 1; + + if ( existingHandles.length > handleCount ) { + existingHandles.slice( handleCount ).remove(); + existingHandles = existingHandles.slice( 0, handleCount ); + } + + for ( i = existingHandles.length; i < handleCount; i++ ) { + handles.push( handle ); + } + + this.handles = existingHandles.add( $( handles.join( "" ) ).appendTo( this.element ) ); + + this._addClass( this.handles, "ui-slider-handle", "ui-state-default" ); + + this.handle = this.handles.eq( 0 ); + + this.handles.each( function( i ) { + $( this ) + .data( "ui-slider-handle-index", i ) + .attr( "tabIndex", 0 ); + } ); + }, + + _createRange: function() { + var options = this.options; + + if ( options.range ) { + if ( options.range === true ) { + if ( !options.values ) { + options.values = [ this._valueMin(), this._valueMin() ]; + } else if ( options.values.length && options.values.length !== 2 ) { + options.values = [ options.values[ 0 ], options.values[ 0 ] ]; + } else if ( $.isArray( options.values ) ) { + options.values = options.values.slice( 0 ); + } + } + + if ( !this.range || !this.range.length ) { + this.range = $( "<div>" ) + .appendTo( this.element ); + + this._addClass( this.range, "ui-slider-range" ); + } else { + this._removeClass( this.range, "ui-slider-range-min ui-slider-range-max" ); + + // Handle range switching from true to min/max + this.range.css( { + "left": "", + "bottom": "" + } ); + } + if ( options.range === "min" || options.range === "max" ) { + this._addClass( this.range, "ui-slider-range-" + options.range ); + } + } else { + if ( this.range ) { + this.range.remove(); + } + this.range = null; + } + }, + + _setupEvents: function() { + this._off( this.handles ); + this._on( this.handles, this._handleEvents ); + this._hoverable( this.handles ); + this._focusable( this.handles ); + }, + + _destroy: function() { + this.handles.remove(); + if ( this.range ) { + this.range.remove(); + } + + this._mouseDestroy(); + }, + + _mouseCapture: function( event ) { + var position, normValue, distance, closestHandle, index, allowed, offset, mouseOverHandle, + that = this, + o = this.options; + + if ( o.disabled ) { + return false; + } + + this.elementSize = { + width: this.element.outerWidth(), + height: this.element.outerHeight() + }; + this.elementOffset = this.element.offset(); + + position = { x: event.pageX, y: event.pageY }; + normValue = this._normValueFromMouse( position ); + distance = this._valueMax() - this._valueMin() + 1; + this.handles.each( function( i ) { + var thisDistance = Math.abs( normValue - that.values( i ) ); + if ( ( distance > thisDistance ) || + ( distance === thisDistance && + ( i === that._lastChangedValue || that.values( i ) === o.min ) ) ) { + distance = thisDistance; + closestHandle = $( this ); + index = i; + } + } ); + + allowed = this._start( event, index ); + if ( allowed === false ) { + return false; + } + this._mouseSliding = true; + + this._handleIndex = index; + + this._addClass( closestHandle, null, "ui-state-active" ); + closestHandle.trigger( "focus" ); + + offset = closestHandle.offset(); + mouseOverHandle = !$( event.target ).parents().addBack().is( ".ui-slider-handle" ); + this._clickOffset = mouseOverHandle ? { left: 0, top: 0 } : { + left: event.pageX - offset.left - ( closestHandle.width() / 2 ), + top: event.pageY - offset.top - + ( closestHandle.height() / 2 ) - + ( parseInt( closestHandle.css( "borderTopWidth" ), 10 ) || 0 ) - + ( parseInt( closestHandle.css( "borderBottomWidth" ), 10 ) || 0 ) + + ( parseInt( closestHandle.css( "marginTop" ), 10 ) || 0 ) + }; + + if ( !this.handles.hasClass( "ui-state-hover" ) ) { + this._slide( event, index, normValue ); + } + this._animateOff = true; + return true; + }, + + _mouseStart: function() { + return true; + }, + + _mouseDrag: function( event ) { + var position = { x: event.pageX, y: event.pageY }, + normValue = this._normValueFromMouse( position ); + + this._slide( event, this._handleIndex, normValue ); + + return false; + }, + + _mouseStop: function( event ) { + this._removeClass( this.handles, null, "ui-state-active" ); + this._mouseSliding = false; + + this._stop( event, this._handleIndex ); + this._change( event, this._handleIndex ); + + this._handleIndex = null; + this._clickOffset = null; + this._animateOff = false; + + return false; + }, + + _detectOrientation: function() { + this.orientation = ( this.options.orientation === "vertical" ) ? "vertical" : "horizontal"; + }, + + _normValueFromMouse: function( position ) { + var pixelTotal, + pixelMouse, + percentMouse, + valueTotal, + valueMouse; + + if ( this.orientation === "horizontal" ) { + pixelTotal = this.elementSize.width; + pixelMouse = position.x - this.elementOffset.left - + ( this._clickOffset ? this._clickOffset.left : 0 ); + } else { + pixelTotal = this.elementSize.height; + pixelMouse = position.y - this.elementOffset.top - + ( this._clickOffset ? this._clickOffset.top : 0 ); + } + + percentMouse = ( pixelMouse / pixelTotal ); + if ( percentMouse > 1 ) { + percentMouse = 1; + } + if ( percentMouse < 0 ) { + percentMouse = 0; + } + if ( this.orientation === "vertical" ) { + percentMouse = 1 - percentMouse; + } + + valueTotal = this._valueMax() - this._valueMin(); + valueMouse = this._valueMin() + percentMouse * valueTotal; + + return this._trimAlignValue( valueMouse ); + }, + + _uiHash: function( index, value, values ) { + var uiHash = { + handle: this.handles[ index ], + handleIndex: index, + value: value !== undefined ? value : this.value() + }; + + if ( this._hasMultipleValues() ) { + uiHash.value = value !== undefined ? value : this.values( index ); + uiHash.values = values || this.values(); + } + + return uiHash; + }, + + _hasMultipleValues: function() { + return this.options.values && this.options.values.length; + }, + + _start: function( event, index ) { + return this._trigger( "start", event, this._uiHash( index ) ); + }, + + _slide: function( event, index, newVal ) { + var allowed, otherVal, + currentValue = this.value(), + newValues = this.values(); + + if ( this._hasMultipleValues() ) { + otherVal = this.values( index ? 0 : 1 ); + currentValue = this.values( index ); + + if ( this.options.values.length === 2 && this.options.range === true ) { + newVal = index === 0 ? Math.min( otherVal, newVal ) : Math.max( otherVal, newVal ); + } + + newValues[ index ] = newVal; + } + + if ( newVal === currentValue ) { + return; + } + + allowed = this._trigger( "slide", event, this._uiHash( index, newVal, newValues ) ); + + // A slide can be canceled by returning false from the slide callback + if ( allowed === false ) { + return; + } + + if ( this._hasMultipleValues() ) { + this.values( index, newVal ); + } else { + this.value( newVal ); + } + }, + + _stop: function( event, index ) { + this._trigger( "stop", event, this._uiHash( index ) ); + }, + + _change: function( event, index ) { + if ( !this._keySliding && !this._mouseSliding ) { + + //store the last changed value index for reference when handles overlap + this._lastChangedValue = index; + this._trigger( "change", event, this._uiHash( index ) ); + } + }, + + value: function( newValue ) { + if ( arguments.length ) { + this.options.value = this._trimAlignValue( newValue ); + this._refreshValue(); + this._change( null, 0 ); + return; + } + + return this._value(); + }, + + values: function( index, newValue ) { + var vals, + newValues, + i; + + if ( arguments.length > 1 ) { + this.options.values[ index ] = this._trimAlignValue( newValue ); + this._refreshValue(); + this._change( null, index ); + return; + } + + if ( arguments.length ) { + if ( $.isArray( arguments[ 0 ] ) ) { + vals = this.options.values; + newValues = arguments[ 0 ]; + for ( i = 0; i < vals.length; i += 1 ) { + vals[ i ] = this._trimAlignValue( newValues[ i ] ); + this._change( null, i ); + } + this._refreshValue(); + } else { + if ( this._hasMultipleValues() ) { + return this._values( index ); + } else { + return this.value(); + } + } + } else { + return this._values(); + } + }, + + _setOption: function( key, value ) { + var i, + valsLength = 0; + + if ( key === "range" && this.options.range === true ) { + if ( value === "min" ) { + this.options.value = this._values( 0 ); + this.options.values = null; + } else if ( value === "max" ) { + this.options.value = this._values( this.options.values.length - 1 ); + this.options.values = null; + } + } + + if ( $.isArray( this.options.values ) ) { + valsLength = this.options.values.length; + } + + this._super( key, value ); + + switch ( key ) { + case "orientation": + this._detectOrientation(); + this._removeClass( "ui-slider-horizontal ui-slider-vertical" ) + ._addClass( "ui-slider-" + this.orientation ); + this._refreshValue(); + if ( this.options.range ) { + this._refreshRange( value ); + } + + // Reset positioning from previous orientation + this.handles.css( value === "horizontal" ? "bottom" : "left", "" ); + break; + case "value": + this._animateOff = true; + this._refreshValue(); + this._change( null, 0 ); + this._animateOff = false; + break; + case "values": + this._animateOff = true; + this._refreshValue(); + + // Start from the last handle to prevent unreachable handles (#9046) + for ( i = valsLength - 1; i >= 0; i-- ) { + this._change( null, i ); + } + this._animateOff = false; + break; + case "step": + case "min": + case "max": + this._animateOff = true; + this._calculateNewMax(); + this._refreshValue(); + this._animateOff = false; + break; + case "range": + this._animateOff = true; + this._refresh(); + this._animateOff = false; + break; + } + }, + + _setOptionDisabled: function( value ) { + this._super( value ); + + this._toggleClass( null, "ui-state-disabled", !!value ); + }, + + //internal value getter + // _value() returns value trimmed by min and max, aligned by step + _value: function() { + var val = this.options.value; + val = this._trimAlignValue( val ); + + return val; + }, + + //internal values getter + // _values() returns array of values trimmed by min and max, aligned by step + // _values( index ) returns single value trimmed by min and max, aligned by step + _values: function( index ) { + var val, + vals, + i; + + if ( arguments.length ) { + val = this.options.values[ index ]; + val = this._trimAlignValue( val ); + + return val; + } else if ( this._hasMultipleValues() ) { + + // .slice() creates a copy of the array + // this copy gets trimmed by min and max and then returned + vals = this.options.values.slice(); + for ( i = 0; i < vals.length; i += 1 ) { + vals[ i ] = this._trimAlignValue( vals[ i ] ); + } + + return vals; + } else { + return []; + } + }, + + // Returns the step-aligned value that val is closest to, between (inclusive) min and max + _trimAlignValue: function( val ) { + if ( val <= this._valueMin() ) { + return this._valueMin(); + } + if ( val >= this._valueMax() ) { + return this._valueMax(); + } + var step = ( this.options.step > 0 ) ? this.options.step : 1, + valModStep = ( val - this._valueMin() ) % step, + alignValue = val - valModStep; + + if ( Math.abs( valModStep ) * 2 >= step ) { + alignValue += ( valModStep > 0 ) ? step : ( -step ); + } + + // Since JavaScript has problems with large floats, round + // the final value to 5 digits after the decimal point (see #4124) + return parseFloat( alignValue.toFixed( 5 ) ); + }, + + _calculateNewMax: function() { + var max = this.options.max, + min = this._valueMin(), + step = this.options.step, + aboveMin = Math.round( ( max - min ) / step ) * step; + max = aboveMin + min; + if ( max > this.options.max ) { + + //If max is not divisible by step, rounding off may increase its value + max -= step; + } + this.max = parseFloat( max.toFixed( this._precision() ) ); + }, + + _precision: function() { + var precision = this._precisionOf( this.options.step ); + if ( this.options.min !== null ) { + precision = Math.max( precision, this._precisionOf( this.options.min ) ); + } + return precision; + }, + + _precisionOf: function( num ) { + var str = num.toString(), + decimal = str.indexOf( "." ); + return decimal === -1 ? 0 : str.length - decimal - 1; + }, + + _valueMin: function() { + return this.options.min; + }, + + _valueMax: function() { + return this.max; + }, + + _refreshRange: function( orientation ) { + if ( orientation === "vertical" ) { + this.range.css( { "width": "", "left": "" } ); + } + if ( orientation === "horizontal" ) { + this.range.css( { "height": "", "bottom": "" } ); + } + }, + + _refreshValue: function() { + var lastValPercent, valPercent, value, valueMin, valueMax, + oRange = this.options.range, + o = this.options, + that = this, + animate = ( !this._animateOff ) ? o.animate : false, + _set = {}; + + if ( this._hasMultipleValues() ) { + this.handles.each( function( i ) { + valPercent = ( that.values( i ) - that._valueMin() ) / ( that._valueMax() - + that._valueMin() ) * 100; + _set[ that.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%"; + $( this ).stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate ); + if ( that.options.range === true ) { + if ( that.orientation === "horizontal" ) { + if ( i === 0 ) { + that.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { + left: valPercent + "%" + }, o.animate ); + } + if ( i === 1 ) { + that.range[ animate ? "animate" : "css" ]( { + width: ( valPercent - lastValPercent ) + "%" + }, { + queue: false, + duration: o.animate + } ); + } + } else { + if ( i === 0 ) { + that.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { + bottom: ( valPercent ) + "%" + }, o.animate ); + } + if ( i === 1 ) { + that.range[ animate ? "animate" : "css" ]( { + height: ( valPercent - lastValPercent ) + "%" + }, { + queue: false, + duration: o.animate + } ); + } + } + } + lastValPercent = valPercent; + } ); + } else { + value = this.value(); + valueMin = this._valueMin(); + valueMax = this._valueMax(); + valPercent = ( valueMax !== valueMin ) ? + ( value - valueMin ) / ( valueMax - valueMin ) * 100 : + 0; + _set[ this.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%"; + this.handle.stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate ); + + if ( oRange === "min" && this.orientation === "horizontal" ) { + this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { + width: valPercent + "%" + }, o.animate ); + } + if ( oRange === "max" && this.orientation === "horizontal" ) { + this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { + width: ( 100 - valPercent ) + "%" + }, o.animate ); + } + if ( oRange === "min" && this.orientation === "vertical" ) { + this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { + height: valPercent + "%" + }, o.animate ); + } + if ( oRange === "max" && this.orientation === "vertical" ) { + this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { + height: ( 100 - valPercent ) + "%" + }, o.animate ); + } + } + }, + + _handleEvents: { + keydown: function( event ) { + var allowed, curVal, newVal, step, + index = $( event.target ).data( "ui-slider-handle-index" ); + + switch ( event.keyCode ) { + case $.ui.keyCode.HOME: + case $.ui.keyCode.END: + case $.ui.keyCode.PAGE_UP: + case $.ui.keyCode.PAGE_DOWN: + case $.ui.keyCode.UP: + case $.ui.keyCode.RIGHT: + case $.ui.keyCode.DOWN: + case $.ui.keyCode.LEFT: + event.preventDefault(); + if ( !this._keySliding ) { + this._keySliding = true; + this._addClass( $( event.target ), null, "ui-state-active" ); + allowed = this._start( event, index ); + if ( allowed === false ) { + return; + } + } + break; + } + + step = this.options.step; + if ( this._hasMultipleValues() ) { + curVal = newVal = this.values( index ); + } else { + curVal = newVal = this.value(); + } + + switch ( event.keyCode ) { + case $.ui.keyCode.HOME: + newVal = this._valueMin(); + break; + case $.ui.keyCode.END: + newVal = this._valueMax(); + break; + case $.ui.keyCode.PAGE_UP: + newVal = this._trimAlignValue( + curVal + ( ( this._valueMax() - this._valueMin() ) / this.numPages ) + ); + break; + case $.ui.keyCode.PAGE_DOWN: + newVal = this._trimAlignValue( + curVal - ( ( this._valueMax() - this._valueMin() ) / this.numPages ) ); + break; + case $.ui.keyCode.UP: + case $.ui.keyCode.RIGHT: + if ( curVal === this._valueMax() ) { + return; + } + newVal = this._trimAlignValue( curVal + step ); + break; + case $.ui.keyCode.DOWN: + case $.ui.keyCode.LEFT: + if ( curVal === this._valueMin() ) { + return; + } + newVal = this._trimAlignValue( curVal - step ); + break; + } + + this._slide( event, index, newVal ); + }, + keyup: function( event ) { + var index = $( event.target ).data( "ui-slider-handle-index" ); + + if ( this._keySliding ) { + this._keySliding = false; + this._stop( event, index ); + this._change( event, index ); + this._removeClass( $( event.target ), null, "ui-state-active" ); + } + } + } +} ); + + +/*! + * jQuery UI Sortable 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: Sortable +//>>group: Interactions +//>>description: Enables items in a list to be sorted using the mouse. +//>>docs: http://api.jqueryui.com/sortable/ +//>>demos: http://jqueryui.com/sortable/ +//>>css.structure: ../../themes/base/sortable.css + + + +var widgetsSortable = $.widget( "ui.sortable", $.ui.mouse, { + version: "1.12.1", + widgetEventPrefix: "sort", + ready: false, + options: { + appendTo: "parent", + axis: false, + connectWith: false, + containment: false, + cursor: "auto", + cursorAt: false, + dropOnEmpty: true, + forcePlaceholderSize: false, + forceHelperSize: false, + grid: false, + handle: false, + helper: "original", + items: "> *", + opacity: false, + placeholder: false, + revert: false, + scroll: true, + scrollSensitivity: 20, + scrollSpeed: 20, + scope: "default", + tolerance: "intersect", + zIndex: 1000, + + // Callbacks + activate: null, + beforeStop: null, + change: null, + deactivate: null, + out: null, + over: null, + receive: null, + remove: null, + sort: null, + start: null, + stop: null, + update: null + }, + + _isOverAxis: function( x, reference, size ) { + return ( x >= reference ) && ( x < ( reference + size ) ); + }, + + _isFloating: function( item ) { + return ( /left|right/ ).test( item.css( "float" ) ) || + ( /inline|table-cell/ ).test( item.css( "display" ) ); + }, + + _create: function() { + this.containerCache = {}; + this._addClass( "ui-sortable" ); + + //Get the items + this.refresh(); + + //Let's determine the parent's offset + this.offset = this.element.offset(); + + //Initialize mouse events for interaction + this._mouseInit(); + + this._setHandleClassName(); + + //We're ready to go + this.ready = true; + + }, + + _setOption: function( key, value ) { + this._super( key, value ); + + if ( key === "handle" ) { + this._setHandleClassName(); + } + }, + + _setHandleClassName: function() { + var that = this; + this._removeClass( this.element.find( ".ui-sortable-handle" ), "ui-sortable-handle" ); + $.each( this.items, function() { + that._addClass( + this.instance.options.handle ? + this.item.find( this.instance.options.handle ) : + this.item, + "ui-sortable-handle" + ); + } ); + }, + + _destroy: function() { + this._mouseDestroy(); + + for ( var i = this.items.length - 1; i >= 0; i-- ) { + this.items[ i ].item.removeData( this.widgetName + "-item" ); + } + + return this; + }, + + _mouseCapture: function( event, overrideHandle ) { + var currentItem = null, + validHandle = false, + that = this; + + if ( this.reverting ) { + return false; + } + + if ( this.options.disabled || this.options.type === "static" ) { + return false; + } + + //We have to refresh the items data once first + this._refreshItems( event ); + + //Find out if the clicked node (or one of its parents) is a actual item in this.items + $( event.target ).parents().each( function() { + if ( $.data( this, that.widgetName + "-item" ) === that ) { + currentItem = $( this ); + return false; + } + } ); + if ( $.data( event.target, that.widgetName + "-item" ) === that ) { + currentItem = $( event.target ); + } + + if ( !currentItem ) { + return false; + } + if ( this.options.handle && !overrideHandle ) { + $( this.options.handle, currentItem ).find( "*" ).addBack().each( function() { + if ( this === event.target ) { + validHandle = true; + } + } ); + if ( !validHandle ) { + return false; + } + } + + this.currentItem = currentItem; + this._removeCurrentsFromItems(); + return true; + + }, + + _mouseStart: function( event, overrideHandle, noActivation ) { + + var i, body, + o = this.options; + + this.currentContainer = this; + + //We only need to call refreshPositions, because the refreshItems call has been moved to + // mouseCapture + this.refreshPositions(); + + //Create and append the visible helper + this.helper = this._createHelper( event ); + + //Cache the helper size + this._cacheHelperProportions(); + + /* + * - Position generation - + * This block generates everything position related - it's the core of draggables. + */ + + //Cache the margins of the original element + this._cacheMargins(); + + //Get the next scrolling parent + this.scrollParent = this.helper.scrollParent(); + + //The element's absolute position on the page minus margins + this.offset = this.currentItem.offset(); + this.offset = { + top: this.offset.top - this.margins.top, + left: this.offset.left - this.margins.left + }; + + $.extend( this.offset, { + click: { //Where the click happened, relative to the element + left: event.pageX - this.offset.left, + top: event.pageY - this.offset.top + }, + parent: this._getParentOffset(), + + // This is a relative to absolute position minus the actual position calculation - + // only used for relative positioned helper + relative: this._getRelativeOffset() + } ); + + // Only after we got the offset, we can change the helper's position to absolute + // TODO: Still need to figure out a way to make relative sorting possible + this.helper.css( "position", "absolute" ); + this.cssPosition = this.helper.css( "position" ); + + //Generate the original position + this.originalPosition = this._generatePosition( event ); + this.originalPageX = event.pageX; + this.originalPageY = event.pageY; + + //Adjust the mouse offset relative to the helper if "cursorAt" is supplied + ( o.cursorAt && this._adjustOffsetFromHelper( o.cursorAt ) ); + + //Cache the former DOM position + this.domPosition = { + prev: this.currentItem.prev()[ 0 ], + parent: this.currentItem.parent()[ 0 ] + }; + + // If the helper is not the original, hide the original so it's not playing any role during + // the drag, won't cause anything bad this way + if ( this.helper[ 0 ] !== this.currentItem[ 0 ] ) { + this.currentItem.hide(); + } + + //Create the placeholder + this._createPlaceholder(); + + //Set a containment if given in the options + if ( o.containment ) { + this._setContainment(); + } + + if ( o.cursor && o.cursor !== "auto" ) { // cursor option + body = this.document.find( "body" ); + + // Support: IE + this.storedCursor = body.css( "cursor" ); + body.css( "cursor", o.cursor ); + + this.storedStylesheet = + $( "<style>*{ cursor: " + o.cursor + " !important; }</style>" ).appendTo( body ); + } + + if ( o.opacity ) { // opacity option + if ( this.helper.css( "opacity" ) ) { + this._storedOpacity = this.helper.css( "opacity" ); + } + this.helper.css( "opacity", o.opacity ); + } + + if ( o.zIndex ) { // zIndex option + if ( this.helper.css( "zIndex" ) ) { + this._storedZIndex = this.helper.css( "zIndex" ); + } + this.helper.css( "zIndex", o.zIndex ); + } + + //Prepare scrolling + if ( this.scrollParent[ 0 ] !== this.document[ 0 ] && + this.scrollParent[ 0 ].tagName !== "HTML" ) { + this.overflowOffset = this.scrollParent.offset(); + } + + //Call callbacks + this._trigger( "start", event, this._uiHash() ); + + //Recache the helper size + if ( !this._preserveHelperProportions ) { + this._cacheHelperProportions(); + } + + //Post "activate" events to possible containers + if ( !noActivation ) { + for ( i = this.containers.length - 1; i >= 0; i-- ) { + this.containers[ i ]._trigger( "activate", event, this._uiHash( this ) ); + } + } + + //Prepare possible droppables + if ( $.ui.ddmanager ) { + $.ui.ddmanager.current = this; + } + + if ( $.ui.ddmanager && !o.dropBehaviour ) { + $.ui.ddmanager.prepareOffsets( this, event ); + } + + this.dragging = true; + + this._addClass( this.helper, "ui-sortable-helper" ); + + // Execute the drag once - this causes the helper not to be visiblebefore getting its + // correct position + this._mouseDrag( event ); + return true; + + }, + + _mouseDrag: function( event ) { + var i, item, itemElement, intersection, + o = this.options, + scrolled = false; + + //Compute the helpers position + this.position = this._generatePosition( event ); + this.positionAbs = this._convertPositionTo( "absolute" ); + + if ( !this.lastPositionAbs ) { + this.lastPositionAbs = this.positionAbs; + } + + //Do scrolling + if ( this.options.scroll ) { + if ( this.scrollParent[ 0 ] !== this.document[ 0 ] && + this.scrollParent[ 0 ].tagName !== "HTML" ) { + + if ( ( this.overflowOffset.top + this.scrollParent[ 0 ].offsetHeight ) - + event.pageY < o.scrollSensitivity ) { + this.scrollParent[ 0 ].scrollTop = + scrolled = this.scrollParent[ 0 ].scrollTop + o.scrollSpeed; + } else if ( event.pageY - this.overflowOffset.top < o.scrollSensitivity ) { + this.scrollParent[ 0 ].scrollTop = + scrolled = this.scrollParent[ 0 ].scrollTop - o.scrollSpeed; + } + + if ( ( this.overflowOffset.left + this.scrollParent[ 0 ].offsetWidth ) - + event.pageX < o.scrollSensitivity ) { + this.scrollParent[ 0 ].scrollLeft = scrolled = + this.scrollParent[ 0 ].scrollLeft + o.scrollSpeed; + } else if ( event.pageX - this.overflowOffset.left < o.scrollSensitivity ) { + this.scrollParent[ 0 ].scrollLeft = scrolled = + this.scrollParent[ 0 ].scrollLeft - o.scrollSpeed; + } + + } else { + + if ( event.pageY - this.document.scrollTop() < o.scrollSensitivity ) { + scrolled = this.document.scrollTop( this.document.scrollTop() - o.scrollSpeed ); + } else if ( this.window.height() - ( event.pageY - this.document.scrollTop() ) < + o.scrollSensitivity ) { + scrolled = this.document.scrollTop( this.document.scrollTop() + o.scrollSpeed ); + } + + if ( event.pageX - this.document.scrollLeft() < o.scrollSensitivity ) { + scrolled = this.document.scrollLeft( + this.document.scrollLeft() - o.scrollSpeed + ); + } else if ( this.window.width() - ( event.pageX - this.document.scrollLeft() ) < + o.scrollSensitivity ) { + scrolled = this.document.scrollLeft( + this.document.scrollLeft() + o.scrollSpeed + ); + } + + } + + if ( scrolled !== false && $.ui.ddmanager && !o.dropBehaviour ) { + $.ui.ddmanager.prepareOffsets( this, event ); + } + } + + //Regenerate the absolute position used for position checks + this.positionAbs = this._convertPositionTo( "absolute" ); + + //Set the helper position + if ( !this.options.axis || this.options.axis !== "y" ) { + this.helper[ 0 ].style.left = this.position.left + "px"; + } + if ( !this.options.axis || this.options.axis !== "x" ) { + this.helper[ 0 ].style.top = this.position.top + "px"; + } + + //Rearrange + for ( i = this.items.length - 1; i >= 0; i-- ) { + + //Cache variables and intersection, continue if no intersection + item = this.items[ i ]; + itemElement = item.item[ 0 ]; + intersection = this._intersectsWithPointer( item ); + if ( !intersection ) { + continue; + } + + // Only put the placeholder inside the current Container, skip all + // items from other containers. This works because when moving + // an item from one container to another the + // currentContainer is switched before the placeholder is moved. + // + // Without this, moving items in "sub-sortables" can cause + // the placeholder to jitter between the outer and inner container. + if ( item.instance !== this.currentContainer ) { + continue; + } + + // Cannot intersect with itself + // no useless actions that have been done before + // no action if the item moved is the parent of the item checked + if ( itemElement !== this.currentItem[ 0 ] && + this.placeholder[ intersection === 1 ? "next" : "prev" ]()[ 0 ] !== itemElement && + !$.contains( this.placeholder[ 0 ], itemElement ) && + ( this.options.type === "semi-dynamic" ? + !$.contains( this.element[ 0 ], itemElement ) : + true + ) + ) { + + this.direction = intersection === 1 ? "down" : "up"; + + if ( this.options.tolerance === "pointer" || this._intersectsWithSides( item ) ) { + this._rearrange( event, item ); + } else { + break; + } + + this._trigger( "change", event, this._uiHash() ); + break; + } + } + + //Post events to containers + this._contactContainers( event ); + + //Interconnect with droppables + if ( $.ui.ddmanager ) { + $.ui.ddmanager.drag( this, event ); + } + + //Call callbacks + this._trigger( "sort", event, this._uiHash() ); + + this.lastPositionAbs = this.positionAbs; + return false; + + }, + + _mouseStop: function( event, noPropagation ) { + + if ( !event ) { + return; + } + + //If we are using droppables, inform the manager about the drop + if ( $.ui.ddmanager && !this.options.dropBehaviour ) { + $.ui.ddmanager.drop( this, event ); + } + + if ( this.options.revert ) { + var that = this, + cur = this.placeholder.offset(), + axis = this.options.axis, + animation = {}; + + if ( !axis || axis === "x" ) { + animation.left = cur.left - this.offset.parent.left - this.margins.left + + ( this.offsetParent[ 0 ] === this.document[ 0 ].body ? + 0 : + this.offsetParent[ 0 ].scrollLeft + ); + } + if ( !axis || axis === "y" ) { + animation.top = cur.top - this.offset.parent.top - this.margins.top + + ( this.offsetParent[ 0 ] === this.document[ 0 ].body ? + 0 : + this.offsetParent[ 0 ].scrollTop + ); + } + this.reverting = true; + $( this.helper ).animate( + animation, + parseInt( this.options.revert, 10 ) || 500, + function() { + that._clear( event ); + } + ); + } else { + this._clear( event, noPropagation ); + } + + return false; + + }, + + cancel: function() { + + if ( this.dragging ) { + + this._mouseUp( new $.Event( "mouseup", { target: null } ) ); + + if ( this.options.helper === "original" ) { + this.currentItem.css( this._storedCSS ); + this._removeClass( this.currentItem, "ui-sortable-helper" ); + } else { + this.currentItem.show(); + } + + //Post deactivating events to containers + for ( var i = this.containers.length - 1; i >= 0; i-- ) { + this.containers[ i ]._trigger( "deactivate", null, this._uiHash( this ) ); + if ( this.containers[ i ].containerCache.over ) { + this.containers[ i ]._trigger( "out", null, this._uiHash( this ) ); + this.containers[ i ].containerCache.over = 0; + } + } + + } + + if ( this.placeholder ) { + + //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, + // it unbinds ALL events from the original node! + if ( this.placeholder[ 0 ].parentNode ) { + this.placeholder[ 0 ].parentNode.removeChild( this.placeholder[ 0 ] ); + } + if ( this.options.helper !== "original" && this.helper && + this.helper[ 0 ].parentNode ) { + this.helper.remove(); + } + + $.extend( this, { + helper: null, + dragging: false, + reverting: false, + _noFinalSort: null + } ); + + if ( this.domPosition.prev ) { + $( this.domPosition.prev ).after( this.currentItem ); + } else { + $( this.domPosition.parent ).prepend( this.currentItem ); + } + } + + return this; + + }, + + serialize: function( o ) { + + var items = this._getItemsAsjQuery( o && o.connected ), + str = []; + o = o || {}; + + $( items ).each( function() { + var res = ( $( o.item || this ).attr( o.attribute || "id" ) || "" ) + .match( o.expression || ( /(.+)[\-=_](.+)/ ) ); + if ( res ) { + str.push( + ( o.key || res[ 1 ] + "[]" ) + + "=" + ( o.key && o.expression ? res[ 1 ] : res[ 2 ] ) ); + } + } ); + + if ( !str.length && o.key ) { + str.push( o.key + "=" ); + } + + return str.join( "&" ); + + }, + + toArray: function( o ) { + + var items = this._getItemsAsjQuery( o && o.connected ), + ret = []; + + o = o || {}; + + items.each( function() { + ret.push( $( o.item || this ).attr( o.attribute || "id" ) || "" ); + } ); + return ret; + + }, + + /* Be careful with the following core functions */ + _intersectsWith: function( item ) { + + var x1 = this.positionAbs.left, + x2 = x1 + this.helperProportions.width, + y1 = this.positionAbs.top, + y2 = y1 + this.helperProportions.height, + l = item.left, + r = l + item.width, + t = item.top, + b = t + item.height, + dyClick = this.offset.click.top, + dxClick = this.offset.click.left, + isOverElementHeight = ( this.options.axis === "x" ) || ( ( y1 + dyClick ) > t && + ( y1 + dyClick ) < b ), + isOverElementWidth = ( this.options.axis === "y" ) || ( ( x1 + dxClick ) > l && + ( x1 + dxClick ) < r ), + isOverElement = isOverElementHeight && isOverElementWidth; + + if ( this.options.tolerance === "pointer" || + this.options.forcePointerForContainers || + ( this.options.tolerance !== "pointer" && + this.helperProportions[ this.floating ? "width" : "height" ] > + item[ this.floating ? "width" : "height" ] ) + ) { + return isOverElement; + } else { + + return ( l < x1 + ( this.helperProportions.width / 2 ) && // Right Half + x2 - ( this.helperProportions.width / 2 ) < r && // Left Half + t < y1 + ( this.helperProportions.height / 2 ) && // Bottom Half + y2 - ( this.helperProportions.height / 2 ) < b ); // Top Half + + } + }, + + _intersectsWithPointer: function( item ) { + var verticalDirection, horizontalDirection, + isOverElementHeight = ( this.options.axis === "x" ) || + this._isOverAxis( + this.positionAbs.top + this.offset.click.top, item.top, item.height ), + isOverElementWidth = ( this.options.axis === "y" ) || + this._isOverAxis( + this.positionAbs.left + this.offset.click.left, item.left, item.width ), + isOverElement = isOverElementHeight && isOverElementWidth; + + if ( !isOverElement ) { + return false; + } + + verticalDirection = this._getDragVerticalDirection(); + horizontalDirection = this._getDragHorizontalDirection(); + + return this.floating ? + ( ( horizontalDirection === "right" || verticalDirection === "down" ) ? 2 : 1 ) + : ( verticalDirection && ( verticalDirection === "down" ? 2 : 1 ) ); + + }, + + _intersectsWithSides: function( item ) { + + var isOverBottomHalf = this._isOverAxis( this.positionAbs.top + + this.offset.click.top, item.top + ( item.height / 2 ), item.height ), + isOverRightHalf = this._isOverAxis( this.positionAbs.left + + this.offset.click.left, item.left + ( item.width / 2 ), item.width ), + verticalDirection = this._getDragVerticalDirection(), + horizontalDirection = this._getDragHorizontalDirection(); + + if ( this.floating && horizontalDirection ) { + return ( ( horizontalDirection === "right" && isOverRightHalf ) || + ( horizontalDirection === "left" && !isOverRightHalf ) ); + } else { + return verticalDirection && ( ( verticalDirection === "down" && isOverBottomHalf ) || + ( verticalDirection === "up" && !isOverBottomHalf ) ); + } + + }, + + _getDragVerticalDirection: function() { + var delta = this.positionAbs.top - this.lastPositionAbs.top; + return delta !== 0 && ( delta > 0 ? "down" : "up" ); + }, + + _getDragHorizontalDirection: function() { + var delta = this.positionAbs.left - this.lastPositionAbs.left; + return delta !== 0 && ( delta > 0 ? "right" : "left" ); + }, + + refresh: function( event ) { + this._refreshItems( event ); + this._setHandleClassName(); + this.refreshPositions(); + return this; + }, + + _connectWith: function() { + var options = this.options; + return options.connectWith.constructor === String ? + [ options.connectWith ] : + options.connectWith; + }, + + _getItemsAsjQuery: function( connected ) { + + var i, j, cur, inst, + items = [], + queries = [], + connectWith = this._connectWith(); + + if ( connectWith && connected ) { + for ( i = connectWith.length - 1; i >= 0; i-- ) { + cur = $( connectWith[ i ], this.document[ 0 ] ); + for ( j = cur.length - 1; j >= 0; j-- ) { + inst = $.data( cur[ j ], this.widgetFullName ); + if ( inst && inst !== this && !inst.options.disabled ) { + queries.push( [ $.isFunction( inst.options.items ) ? + inst.options.items.call( inst.element ) : + $( inst.options.items, inst.element ) + .not( ".ui-sortable-helper" ) + .not( ".ui-sortable-placeholder" ), inst ] ); + } + } + } + } + + queries.push( [ $.isFunction( this.options.items ) ? + this.options.items + .call( this.element, null, { options: this.options, item: this.currentItem } ) : + $( this.options.items, this.element ) + .not( ".ui-sortable-helper" ) + .not( ".ui-sortable-placeholder" ), this ] ); + + function addItems() { + items.push( this ); + } + for ( i = queries.length - 1; i >= 0; i-- ) { + queries[ i ][ 0 ].each( addItems ); + } + + return $( items ); + + }, + + _removeCurrentsFromItems: function() { + + var list = this.currentItem.find( ":data(" + this.widgetName + "-item)" ); + + this.items = $.grep( this.items, function( item ) { + for ( var j = 0; j < list.length; j++ ) { + if ( list[ j ] === item.item[ 0 ] ) { + return false; + } + } + return true; + } ); + + }, + + _refreshItems: function( event ) { + + this.items = []; + this.containers = [ this ]; + + var i, j, cur, inst, targetData, _queries, item, queriesLength, + items = this.items, + queries = [ [ $.isFunction( this.options.items ) ? + this.options.items.call( this.element[ 0 ], event, { item: this.currentItem } ) : + $( this.options.items, this.element ), this ] ], + connectWith = this._connectWith(); + + //Shouldn't be run the first time through due to massive slow-down + if ( connectWith && this.ready ) { + for ( i = connectWith.length - 1; i >= 0; i-- ) { + cur = $( connectWith[ i ], this.document[ 0 ] ); + for ( j = cur.length - 1; j >= 0; j-- ) { + inst = $.data( cur[ j ], this.widgetFullName ); + if ( inst && inst !== this && !inst.options.disabled ) { + queries.push( [ $.isFunction( inst.options.items ) ? + inst.options.items + .call( inst.element[ 0 ], event, { item: this.currentItem } ) : + $( inst.options.items, inst.element ), inst ] ); + this.containers.push( inst ); + } + } + } + } + + for ( i = queries.length - 1; i >= 0; i-- ) { + targetData = queries[ i ][ 1 ]; + _queries = queries[ i ][ 0 ]; + + for ( j = 0, queriesLength = _queries.length; j < queriesLength; j++ ) { + item = $( _queries[ j ] ); + + // Data for target checking (mouse manager) + item.data( this.widgetName + "-item", targetData ); + + items.push( { + item: item, + instance: targetData, + width: 0, height: 0, + left: 0, top: 0 + } ); + } + } + + }, + + refreshPositions: function( fast ) { + + // Determine whether items are being displayed horizontally + this.floating = this.items.length ? + this.options.axis === "x" || this._isFloating( this.items[ 0 ].item ) : + false; + + //This has to be redone because due to the item being moved out/into the offsetParent, + // the offsetParent's position will change + if ( this.offsetParent && this.helper ) { + this.offset.parent = this._getParentOffset(); + } + + var i, item, t, p; + + for ( i = this.items.length - 1; i >= 0; i-- ) { + item = this.items[ i ]; + + //We ignore calculating positions of all connected containers when we're not over them + if ( item.instance !== this.currentContainer && this.currentContainer && + item.item[ 0 ] !== this.currentItem[ 0 ] ) { + continue; + } + + t = this.options.toleranceElement ? + $( this.options.toleranceElement, item.item ) : + item.item; + + if ( !fast ) { + item.width = t.outerWidth(); + item.height = t.outerHeight(); + } + + p = t.offset(); + item.left = p.left; + item.top = p.top; + } + + if ( this.options.custom && this.options.custom.refreshContainers ) { + this.options.custom.refreshContainers.call( this ); + } else { + for ( i = this.containers.length - 1; i >= 0; i-- ) { + p = this.containers[ i ].element.offset(); + this.containers[ i ].containerCache.left = p.left; + this.containers[ i ].containerCache.top = p.top; + this.containers[ i ].containerCache.width = + this.containers[ i ].element.outerWidth(); + this.containers[ i ].containerCache.height = + this.containers[ i ].element.outerHeight(); + } + } + + return this; + }, + + _createPlaceholder: function( that ) { + that = that || this; + var className, + o = that.options; + + if ( !o.placeholder || o.placeholder.constructor === String ) { + className = o.placeholder; + o.placeholder = { + element: function() { + + var nodeName = that.currentItem[ 0 ].nodeName.toLowerCase(), + element = $( "<" + nodeName + ">", that.document[ 0 ] ); + + that._addClass( element, "ui-sortable-placeholder", + className || that.currentItem[ 0 ].className ) + ._removeClass( element, "ui-sortable-helper" ); + + if ( nodeName === "tbody" ) { + that._createTrPlaceholder( + that.currentItem.find( "tr" ).eq( 0 ), + $( "<tr>", that.document[ 0 ] ).appendTo( element ) + ); + } else if ( nodeName === "tr" ) { + that._createTrPlaceholder( that.currentItem, element ); + } else if ( nodeName === "img" ) { + element.attr( "src", that.currentItem.attr( "src" ) ); + } + + if ( !className ) { + element.css( "visibility", "hidden" ); + } + + return element; + }, + update: function( container, p ) { + + // 1. If a className is set as 'placeholder option, we don't force sizes - + // the class is responsible for that + // 2. The option 'forcePlaceholderSize can be enabled to force it even if a + // class name is specified + if ( className && !o.forcePlaceholderSize ) { + return; + } + + //If the element doesn't have a actual height by itself (without styles coming + // from a stylesheet), it receives the inline height from the dragged item + if ( !p.height() ) { + p.height( + that.currentItem.innerHeight() - + parseInt( that.currentItem.css( "paddingTop" ) || 0, 10 ) - + parseInt( that.currentItem.css( "paddingBottom" ) || 0, 10 ) ); + } + if ( !p.width() ) { + p.width( + that.currentItem.innerWidth() - + parseInt( that.currentItem.css( "paddingLeft" ) || 0, 10 ) - + parseInt( that.currentItem.css( "paddingRight" ) || 0, 10 ) ); + } + } + }; + } + + //Create the placeholder + that.placeholder = $( o.placeholder.element.call( that.element, that.currentItem ) ); + + //Append it after the actual current item + that.currentItem.after( that.placeholder ); + + //Update the size of the placeholder (TODO: Logic to fuzzy, see line 316/317) + o.placeholder.update( that, that.placeholder ); + + }, + + _createTrPlaceholder: function( sourceTr, targetTr ) { + var that = this; + + sourceTr.children().each( function() { + $( "<td> </td>", that.document[ 0 ] ) + .attr( "colspan", $( this ).attr( "colspan" ) || 1 ) + .appendTo( targetTr ); + } ); + }, + + _contactContainers: function( event ) { + var i, j, dist, itemWithLeastDistance, posProperty, sizeProperty, cur, nearBottom, + floating, axis, + innermostContainer = null, + innermostIndex = null; + + // Get innermost container that intersects with item + for ( i = this.containers.length - 1; i >= 0; i-- ) { + + // Never consider a container that's located within the item itself + if ( $.contains( this.currentItem[ 0 ], this.containers[ i ].element[ 0 ] ) ) { + continue; + } + + if ( this._intersectsWith( this.containers[ i ].containerCache ) ) { + + // If we've already found a container and it's more "inner" than this, then continue + if ( innermostContainer && + $.contains( + this.containers[ i ].element[ 0 ], + innermostContainer.element[ 0 ] ) ) { + continue; + } + + innermostContainer = this.containers[ i ]; + innermostIndex = i; + + } else { + + // container doesn't intersect. trigger "out" event if necessary + if ( this.containers[ i ].containerCache.over ) { + this.containers[ i ]._trigger( "out", event, this._uiHash( this ) ); + this.containers[ i ].containerCache.over = 0; + } + } + + } + + // If no intersecting containers found, return + if ( !innermostContainer ) { + return; + } + + // Move the item into the container if it's not there already + if ( this.containers.length === 1 ) { + if ( !this.containers[ innermostIndex ].containerCache.over ) { + this.containers[ innermostIndex ]._trigger( "over", event, this._uiHash( this ) ); + this.containers[ innermostIndex ].containerCache.over = 1; + } + } else { + + // When entering a new container, we will find the item with the least distance and + // append our item near it + dist = 10000; + itemWithLeastDistance = null; + floating = innermostContainer.floating || this._isFloating( this.currentItem ); + posProperty = floating ? "left" : "top"; + sizeProperty = floating ? "width" : "height"; + axis = floating ? "pageX" : "pageY"; + + for ( j = this.items.length - 1; j >= 0; j-- ) { + if ( !$.contains( + this.containers[ innermostIndex ].element[ 0 ], this.items[ j ].item[ 0 ] ) + ) { + continue; + } + if ( this.items[ j ].item[ 0 ] === this.currentItem[ 0 ] ) { + continue; + } + + cur = this.items[ j ].item.offset()[ posProperty ]; + nearBottom = false; + if ( event[ axis ] - cur > this.items[ j ][ sizeProperty ] / 2 ) { + nearBottom = true; + } + + if ( Math.abs( event[ axis ] - cur ) < dist ) { + dist = Math.abs( event[ axis ] - cur ); + itemWithLeastDistance = this.items[ j ]; + this.direction = nearBottom ? "up" : "down"; + } + } + + //Check if dropOnEmpty is enabled + if ( !itemWithLeastDistance && !this.options.dropOnEmpty ) { + return; + } + + if ( this.currentContainer === this.containers[ innermostIndex ] ) { + if ( !this.currentContainer.containerCache.over ) { + this.containers[ innermostIndex ]._trigger( "over", event, this._uiHash() ); + this.currentContainer.containerCache.over = 1; + } + return; + } + + itemWithLeastDistance ? + this._rearrange( event, itemWithLeastDistance, null, true ) : + this._rearrange( event, null, this.containers[ innermostIndex ].element, true ); + this._trigger( "change", event, this._uiHash() ); + this.containers[ innermostIndex ]._trigger( "change", event, this._uiHash( this ) ); + this.currentContainer = this.containers[ innermostIndex ]; + + //Update the placeholder + this.options.placeholder.update( this.currentContainer, this.placeholder ); + + this.containers[ innermostIndex ]._trigger( "over", event, this._uiHash( this ) ); + this.containers[ innermostIndex ].containerCache.over = 1; + } + + }, + + _createHelper: function( event ) { + + var o = this.options, + helper = $.isFunction( o.helper ) ? + $( o.helper.apply( this.element[ 0 ], [ event, this.currentItem ] ) ) : + ( o.helper === "clone" ? this.currentItem.clone() : this.currentItem ); + + //Add the helper to the DOM if that didn't happen already + if ( !helper.parents( "body" ).length ) { + $( o.appendTo !== "parent" ? + o.appendTo : + this.currentItem[ 0 ].parentNode )[ 0 ].appendChild( helper[ 0 ] ); + } + + if ( helper[ 0 ] === this.currentItem[ 0 ] ) { + this._storedCSS = { + width: this.currentItem[ 0 ].style.width, + height: this.currentItem[ 0 ].style.height, + position: this.currentItem.css( "position" ), + top: this.currentItem.css( "top" ), + left: this.currentItem.css( "left" ) + }; + } + + if ( !helper[ 0 ].style.width || o.forceHelperSize ) { + helper.width( this.currentItem.width() ); + } + if ( !helper[ 0 ].style.height || o.forceHelperSize ) { + helper.height( this.currentItem.height() ); + } + + return helper; + + }, + + _adjustOffsetFromHelper: function( obj ) { + if ( typeof obj === "string" ) { + obj = obj.split( " " ); + } + if ( $.isArray( obj ) ) { + obj = { left: +obj[ 0 ], top: +obj[ 1 ] || 0 }; + } + if ( "left" in obj ) { + this.offset.click.left = obj.left + this.margins.left; + } + if ( "right" in obj ) { + this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left; + } + if ( "top" in obj ) { + this.offset.click.top = obj.top + this.margins.top; + } + if ( "bottom" in obj ) { + this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top; + } + }, + + _getParentOffset: function() { + + //Get the offsetParent and cache its position + this.offsetParent = this.helper.offsetParent(); + var po = this.offsetParent.offset(); + + // This is a special case where we need to modify a offset calculated on start, since the + // following happened: + // 1. The position of the helper is absolute, so it's position is calculated based on the + // next positioned parent + // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't + // the document, which means that the scroll is included in the initial calculation of the + // offset of the parent, and never recalculated upon drag + if ( this.cssPosition === "absolute" && this.scrollParent[ 0 ] !== this.document[ 0 ] && + $.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) { + po.left += this.scrollParent.scrollLeft(); + po.top += this.scrollParent.scrollTop(); + } + + // This needs to be actually done for all browsers, since pageX/pageY includes this + // information with an ugly IE fix + if ( this.offsetParent[ 0 ] === this.document[ 0 ].body || + ( this.offsetParent[ 0 ].tagName && + this.offsetParent[ 0 ].tagName.toLowerCase() === "html" && $.ui.ie ) ) { + po = { top: 0, left: 0 }; + } + + return { + top: po.top + ( parseInt( this.offsetParent.css( "borderTopWidth" ), 10 ) || 0 ), + left: po.left + ( parseInt( this.offsetParent.css( "borderLeftWidth" ), 10 ) || 0 ) + }; + + }, + + _getRelativeOffset: function() { + + if ( this.cssPosition === "relative" ) { + var p = this.currentItem.position(); + return { + top: p.top - ( parseInt( this.helper.css( "top" ), 10 ) || 0 ) + + this.scrollParent.scrollTop(), + left: p.left - ( parseInt( this.helper.css( "left" ), 10 ) || 0 ) + + this.scrollParent.scrollLeft() + }; + } else { + return { top: 0, left: 0 }; + } + + }, + + _cacheMargins: function() { + this.margins = { + left: ( parseInt( this.currentItem.css( "marginLeft" ), 10 ) || 0 ), + top: ( parseInt( this.currentItem.css( "marginTop" ), 10 ) || 0 ) + }; + }, + + _cacheHelperProportions: function() { + this.helperProportions = { + width: this.helper.outerWidth(), + height: this.helper.outerHeight() + }; + }, + + _setContainment: function() { + + var ce, co, over, + o = this.options; + if ( o.containment === "parent" ) { + o.containment = this.helper[ 0 ].parentNode; + } + if ( o.containment === "document" || o.containment === "window" ) { + this.containment = [ + 0 - this.offset.relative.left - this.offset.parent.left, + 0 - this.offset.relative.top - this.offset.parent.top, + o.containment === "document" ? + this.document.width() : + this.window.width() - this.helperProportions.width - this.margins.left, + ( o.containment === "document" ? + ( this.document.height() || document.body.parentNode.scrollHeight ) : + this.window.height() || this.document[ 0 ].body.parentNode.scrollHeight + ) - this.helperProportions.height - this.margins.top + ]; + } + + if ( !( /^(document|window|parent)$/ ).test( o.containment ) ) { + ce = $( o.containment )[ 0 ]; + co = $( o.containment ).offset(); + over = ( $( ce ).css( "overflow" ) !== "hidden" ); + + this.containment = [ + co.left + ( parseInt( $( ce ).css( "borderLeftWidth" ), 10 ) || 0 ) + + ( parseInt( $( ce ).css( "paddingLeft" ), 10 ) || 0 ) - this.margins.left, + co.top + ( parseInt( $( ce ).css( "borderTopWidth" ), 10 ) || 0 ) + + ( parseInt( $( ce ).css( "paddingTop" ), 10 ) || 0 ) - this.margins.top, + co.left + ( over ? Math.max( ce.scrollWidth, ce.offsetWidth ) : ce.offsetWidth ) - + ( parseInt( $( ce ).css( "borderLeftWidth" ), 10 ) || 0 ) - + ( parseInt( $( ce ).css( "paddingRight" ), 10 ) || 0 ) - + this.helperProportions.width - this.margins.left, + co.top + ( over ? Math.max( ce.scrollHeight, ce.offsetHeight ) : ce.offsetHeight ) - + ( parseInt( $( ce ).css( "borderTopWidth" ), 10 ) || 0 ) - + ( parseInt( $( ce ).css( "paddingBottom" ), 10 ) || 0 ) - + this.helperProportions.height - this.margins.top + ]; + } + + }, + + _convertPositionTo: function( d, pos ) { + + if ( !pos ) { + pos = this.position; + } + var mod = d === "absolute" ? 1 : -1, + scroll = this.cssPosition === "absolute" && + !( this.scrollParent[ 0 ] !== this.document[ 0 ] && + $.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) ? + this.offsetParent : + this.scrollParent, + scrollIsRootNode = ( /(html|body)/i ).test( scroll[ 0 ].tagName ); + + return { + top: ( + + // The absolute mouse position + pos.top + + + // Only for relative positioned nodes: Relative offset from element to offset parent + this.offset.relative.top * mod + + + // The offsetParent's offset without borders (offset + border) + this.offset.parent.top * mod - + ( ( this.cssPosition === "fixed" ? + -this.scrollParent.scrollTop() : + ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod ) + ), + left: ( + + // The absolute mouse position + pos.left + + + // Only for relative positioned nodes: Relative offset from element to offset parent + this.offset.relative.left * mod + + + // The offsetParent's offset without borders (offset + border) + this.offset.parent.left * mod - + ( ( this.cssPosition === "fixed" ? + -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : + scroll.scrollLeft() ) * mod ) + ) + }; + + }, + + _generatePosition: function( event ) { + + var top, left, + o = this.options, + pageX = event.pageX, + pageY = event.pageY, + scroll = this.cssPosition === "absolute" && + !( this.scrollParent[ 0 ] !== this.document[ 0 ] && + $.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) ? + this.offsetParent : + this.scrollParent, + scrollIsRootNode = ( /(html|body)/i ).test( scroll[ 0 ].tagName ); + + // This is another very weird special case that only happens for relative elements: + // 1. If the css position is relative + // 2. and the scroll parent is the document or similar to the offset parent + // we have to refresh the relative offset during the scroll so there are no jumps + if ( this.cssPosition === "relative" && !( this.scrollParent[ 0 ] !== this.document[ 0 ] && + this.scrollParent[ 0 ] !== this.offsetParent[ 0 ] ) ) { + this.offset.relative = this._getRelativeOffset(); + } + + /* + * - Position constraining - + * Constrain the position to a mix of grid, containment. + */ + + if ( this.originalPosition ) { //If we are not dragging yet, we won't check for options + + if ( this.containment ) { + if ( event.pageX - this.offset.click.left < this.containment[ 0 ] ) { + pageX = this.containment[ 0 ] + this.offset.click.left; + } + if ( event.pageY - this.offset.click.top < this.containment[ 1 ] ) { + pageY = this.containment[ 1 ] + this.offset.click.top; + } + if ( event.pageX - this.offset.click.left > this.containment[ 2 ] ) { + pageX = this.containment[ 2 ] + this.offset.click.left; + } + if ( event.pageY - this.offset.click.top > this.containment[ 3 ] ) { + pageY = this.containment[ 3 ] + this.offset.click.top; + } + } + + if ( o.grid ) { + top = this.originalPageY + Math.round( ( pageY - this.originalPageY ) / + o.grid[ 1 ] ) * o.grid[ 1 ]; + pageY = this.containment ? + ( ( top - this.offset.click.top >= this.containment[ 1 ] && + top - this.offset.click.top <= this.containment[ 3 ] ) ? + top : + ( ( top - this.offset.click.top >= this.containment[ 1 ] ) ? + top - o.grid[ 1 ] : top + o.grid[ 1 ] ) ) : + top; + + left = this.originalPageX + Math.round( ( pageX - this.originalPageX ) / + o.grid[ 0 ] ) * o.grid[ 0 ]; + pageX = this.containment ? + ( ( left - this.offset.click.left >= this.containment[ 0 ] && + left - this.offset.click.left <= this.containment[ 2 ] ) ? + left : + ( ( left - this.offset.click.left >= this.containment[ 0 ] ) ? + left - o.grid[ 0 ] : left + o.grid[ 0 ] ) ) : + left; + } + + } + + return { + top: ( + + // The absolute mouse position + pageY - + + // Click offset (relative to the element) + this.offset.click.top - + + // Only for relative positioned nodes: Relative offset from element to offset parent + this.offset.relative.top - + + // The offsetParent's offset without borders (offset + border) + this.offset.parent.top + + ( ( this.cssPosition === "fixed" ? + -this.scrollParent.scrollTop() : + ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) ) + ), + left: ( + + // The absolute mouse position + pageX - + + // Click offset (relative to the element) + this.offset.click.left - + + // Only for relative positioned nodes: Relative offset from element to offset parent + this.offset.relative.left - + + // The offsetParent's offset without borders (offset + border) + this.offset.parent.left + + ( ( this.cssPosition === "fixed" ? + -this.scrollParent.scrollLeft() : + scrollIsRootNode ? 0 : scroll.scrollLeft() ) ) + ) + }; + + }, + + _rearrange: function( event, i, a, hardRefresh ) { + + a ? a[ 0 ].appendChild( this.placeholder[ 0 ] ) : + i.item[ 0 ].parentNode.insertBefore( this.placeholder[ 0 ], + ( this.direction === "down" ? i.item[ 0 ] : i.item[ 0 ].nextSibling ) ); + + //Various things done here to improve the performance: + // 1. we create a setTimeout, that calls refreshPositions + // 2. on the instance, we have a counter variable, that get's higher after every append + // 3. on the local scope, we copy the counter variable, and check in the timeout, + // if it's still the same + // 4. this lets only the last addition to the timeout stack through + this.counter = this.counter ? ++this.counter : 1; + var counter = this.counter; + + this._delay( function() { + if ( counter === this.counter ) { + + //Precompute after each DOM insertion, NOT on mousemove + this.refreshPositions( !hardRefresh ); + } + } ); + + }, + + _clear: function( event, noPropagation ) { + + this.reverting = false; + + // We delay all events that have to be triggered to after the point where the placeholder + // has been removed and everything else normalized again + var i, + delayedTriggers = []; + + // We first have to update the dom position of the actual currentItem + // Note: don't do it if the current item is already removed (by a user), or it gets + // reappended (see #4088) + if ( !this._noFinalSort && this.currentItem.parent().length ) { + this.placeholder.before( this.currentItem ); + } + this._noFinalSort = null; + + if ( this.helper[ 0 ] === this.currentItem[ 0 ] ) { + for ( i in this._storedCSS ) { + if ( this._storedCSS[ i ] === "auto" || this._storedCSS[ i ] === "static" ) { + this._storedCSS[ i ] = ""; + } + } + this.currentItem.css( this._storedCSS ); + this._removeClass( this.currentItem, "ui-sortable-helper" ); + } else { + this.currentItem.show(); + } + + if ( this.fromOutside && !noPropagation ) { + delayedTriggers.push( function( event ) { + this._trigger( "receive", event, this._uiHash( this.fromOutside ) ); + } ); + } + if ( ( this.fromOutside || + this.domPosition.prev !== + this.currentItem.prev().not( ".ui-sortable-helper" )[ 0 ] || + this.domPosition.parent !== this.currentItem.parent()[ 0 ] ) && !noPropagation ) { + + // Trigger update callback if the DOM position has changed + delayedTriggers.push( function( event ) { + this._trigger( "update", event, this._uiHash() ); + } ); + } + + // Check if the items Container has Changed and trigger appropriate + // events. + if ( this !== this.currentContainer ) { + if ( !noPropagation ) { + delayedTriggers.push( function( event ) { + this._trigger( "remove", event, this._uiHash() ); + } ); + delayedTriggers.push( ( function( c ) { + return function( event ) { + c._trigger( "receive", event, this._uiHash( this ) ); + }; + } ).call( this, this.currentContainer ) ); + delayedTriggers.push( ( function( c ) { + return function( event ) { + c._trigger( "update", event, this._uiHash( this ) ); + }; + } ).call( this, this.currentContainer ) ); + } + } + + //Post events to containers + function delayEvent( type, instance, container ) { + return function( event ) { + container._trigger( type, event, instance._uiHash( instance ) ); + }; + } + for ( i = this.containers.length - 1; i >= 0; i-- ) { + if ( !noPropagation ) { + delayedTriggers.push( delayEvent( "deactivate", this, this.containers[ i ] ) ); + } + if ( this.containers[ i ].containerCache.over ) { + delayedTriggers.push( delayEvent( "out", this, this.containers[ i ] ) ); + this.containers[ i ].containerCache.over = 0; + } + } + + //Do what was originally in plugins + if ( this.storedCursor ) { + this.document.find( "body" ).css( "cursor", this.storedCursor ); + this.storedStylesheet.remove(); + } + if ( this._storedOpacity ) { + this.helper.css( "opacity", this._storedOpacity ); + } + if ( this._storedZIndex ) { + this.helper.css( "zIndex", this._storedZIndex === "auto" ? "" : this._storedZIndex ); + } + + this.dragging = false; + + if ( !noPropagation ) { + this._trigger( "beforeStop", event, this._uiHash() ); + } + + //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, + // it unbinds ALL events from the original node! + this.placeholder[ 0 ].parentNode.removeChild( this.placeholder[ 0 ] ); + + if ( !this.cancelHelperRemoval ) { + if ( this.helper[ 0 ] !== this.currentItem[ 0 ] ) { + this.helper.remove(); + } + this.helper = null; + } + + if ( !noPropagation ) { + for ( i = 0; i < delayedTriggers.length; i++ ) { + + // Trigger all delayed events + delayedTriggers[ i ].call( this, event ); + } + this._trigger( "stop", event, this._uiHash() ); + } + + this.fromOutside = false; + return !this.cancelHelperRemoval; + + }, + + _trigger: function() { + if ( $.Widget.prototype._trigger.apply( this, arguments ) === false ) { + this.cancel(); + } + }, + + _uiHash: function( _inst ) { + var inst = _inst || this; + return { + helper: inst.helper, + placeholder: inst.placeholder || $( [] ), + position: inst.position, + originalPosition: inst.originalPosition, + offset: inst.positionAbs, + item: inst.currentItem, + sender: _inst ? _inst.element : null + }; + } + +} ); + + +/*! + * jQuery UI Spinner 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: Spinner +//>>group: Widgets +//>>description: Displays buttons to easily input numbers via the keyboard or mouse. +//>>docs: http://api.jqueryui.com/spinner/ +//>>demos: http://jqueryui.com/spinner/ +//>>css.structure: ../../themes/base/core.css +//>>css.structure: ../../themes/base/spinner.css +//>>css.theme: ../../themes/base/theme.css + + + +function spinnerModifer( fn ) { + return function() { + var previous = this.element.val(); + fn.apply( this, arguments ); + this._refresh(); + if ( previous !== this.element.val() ) { + this._trigger( "change" ); + } + }; +} + +$.widget( "ui.spinner", { + version: "1.12.1", + defaultElement: "<input>", + widgetEventPrefix: "spin", + options: { + classes: { + "ui-spinner": "ui-corner-all", + "ui-spinner-down": "ui-corner-br", + "ui-spinner-up": "ui-corner-tr" + }, + culture: null, + icons: { + down: "ui-icon-triangle-1-s", + up: "ui-icon-triangle-1-n" + }, + incremental: true, + max: null, + min: null, + numberFormat: null, + page: 10, + step: 1, + + change: null, + spin: null, + start: null, + stop: null + }, + + _create: function() { + + // handle string values that need to be parsed + this._setOption( "max", this.options.max ); + this._setOption( "min", this.options.min ); + this._setOption( "step", this.options.step ); + + // Only format if there is a value, prevents the field from being marked + // as invalid in Firefox, see #9573. + if ( this.value() !== "" ) { + + // Format the value, but don't constrain. + this._value( this.element.val(), true ); + } + + this._draw(); + this._on( this._events ); + this._refresh(); + + // Turning off autocomplete prevents the browser from remembering the + // value when navigating through history, so we re-enable autocomplete + // if the page is unloaded before the widget is destroyed. #7790 + this._on( this.window, { + beforeunload: function() { + this.element.removeAttr( "autocomplete" ); + } + } ); + }, + + _getCreateOptions: function() { + var options = this._super(); + var element = this.element; + + $.each( [ "min", "max", "step" ], function( i, option ) { + var value = element.attr( option ); + if ( value != null && value.length ) { + options[ option ] = value; + } + } ); + + return options; + }, + + _events: { + keydown: function( event ) { + if ( this._start( event ) && this._keydown( event ) ) { + event.preventDefault(); + } + }, + keyup: "_stop", + focus: function() { + this.previous = this.element.val(); + }, + blur: function( event ) { + if ( this.cancelBlur ) { + delete this.cancelBlur; + return; + } + + this._stop(); + this._refresh(); + if ( this.previous !== this.element.val() ) { + this._trigger( "change", event ); + } + }, + mousewheel: function( event, delta ) { + if ( !delta ) { + return; + } + if ( !this.spinning && !this._start( event ) ) { + return false; + } + + this._spin( ( delta > 0 ? 1 : -1 ) * this.options.step, event ); + clearTimeout( this.mousewheelTimer ); + this.mousewheelTimer = this._delay( function() { + if ( this.spinning ) { + this._stop( event ); + } + }, 100 ); + event.preventDefault(); + }, + "mousedown .ui-spinner-button": function( event ) { + var previous; + + // We never want the buttons to have focus; whenever the user is + // interacting with the spinner, the focus should be on the input. + // If the input is focused then this.previous is properly set from + // when the input first received focus. If the input is not focused + // then we need to set this.previous based on the value before spinning. + previous = this.element[ 0 ] === $.ui.safeActiveElement( this.document[ 0 ] ) ? + this.previous : this.element.val(); + function checkFocus() { + var isActive = this.element[ 0 ] === $.ui.safeActiveElement( this.document[ 0 ] ); + if ( !isActive ) { + this.element.trigger( "focus" ); + this.previous = previous; + + // support: IE + // IE sets focus asynchronously, so we need to check if focus + // moved off of the input because the user clicked on the button. + this._delay( function() { + this.previous = previous; + } ); + } + } + + // Ensure focus is on (or stays on) the text field + event.preventDefault(); + checkFocus.call( this ); + + // Support: IE + // IE doesn't prevent moving focus even with event.preventDefault() + // so we set a flag to know when we should ignore the blur event + // and check (again) if focus moved off of the input. + this.cancelBlur = true; + this._delay( function() { + delete this.cancelBlur; + checkFocus.call( this ); + } ); + + if ( this._start( event ) === false ) { + return; + } + + this._repeat( null, $( event.currentTarget ) + .hasClass( "ui-spinner-up" ) ? 1 : -1, event ); + }, + "mouseup .ui-spinner-button": "_stop", + "mouseenter .ui-spinner-button": function( event ) { + + // button will add ui-state-active if mouse was down while mouseleave and kept down + if ( !$( event.currentTarget ).hasClass( "ui-state-active" ) ) { + return; + } + + if ( this._start( event ) === false ) { + return false; + } + this._repeat( null, $( event.currentTarget ) + .hasClass( "ui-spinner-up" ) ? 1 : -1, event ); + }, + + // TODO: do we really want to consider this a stop? + // shouldn't we just stop the repeater and wait until mouseup before + // we trigger the stop event? + "mouseleave .ui-spinner-button": "_stop" + }, + + // Support mobile enhanced option and make backcompat more sane + _enhance: function() { + this.uiSpinner = this.element + .attr( "autocomplete", "off" ) + .wrap( "<span>" ) + .parent() + + // Add buttons + .append( + "<a></a><a></a>" + ); + }, + + _draw: function() { + this._enhance(); + + this._addClass( this.uiSpinner, "ui-spinner", "ui-widget ui-widget-content" ); + this._addClass( "ui-spinner-input" ); + + this.element.attr( "role", "spinbutton" ); + + // Button bindings + this.buttons = this.uiSpinner.children( "a" ) + .attr( "tabIndex", -1 ) + .attr( "aria-hidden", true ) + .button( { + classes: { + "ui-button": "" + } + } ); + + // TODO: Right now button does not support classes this is already updated in button PR + this._removeClass( this.buttons, "ui-corner-all" ); + + this._addClass( this.buttons.first(), "ui-spinner-button ui-spinner-up" ); + this._addClass( this.buttons.last(), "ui-spinner-button ui-spinner-down" ); + this.buttons.first().button( { + "icon": this.options.icons.up, + "showLabel": false + } ); + this.buttons.last().button( { + "icon": this.options.icons.down, + "showLabel": false + } ); + + // IE 6 doesn't understand height: 50% for the buttons + // unless the wrapper has an explicit height + if ( this.buttons.height() > Math.ceil( this.uiSpinner.height() * 0.5 ) && + this.uiSpinner.height() > 0 ) { + this.uiSpinner.height( this.uiSpinner.height() ); + } + }, + + _keydown: function( event ) { + var options = this.options, + keyCode = $.ui.keyCode; + + switch ( event.keyCode ) { + case keyCode.UP: + this._repeat( null, 1, event ); + return true; + case keyCode.DOWN: + this._repeat( null, -1, event ); + return true; + case keyCode.PAGE_UP: + this._repeat( null, options.page, event ); + return true; + case keyCode.PAGE_DOWN: + this._repeat( null, -options.page, event ); + return true; + } + + return false; + }, + + _start: function( event ) { + if ( !this.spinning && this._trigger( "start", event ) === false ) { + return false; + } + + if ( !this.counter ) { + this.counter = 1; + } + this.spinning = true; + return true; + }, + + _repeat: function( i, steps, event ) { + i = i || 500; + + clearTimeout( this.timer ); + this.timer = this._delay( function() { + this._repeat( 40, steps, event ); + }, i ); + + this._spin( steps * this.options.step, event ); + }, + + _spin: function( step, event ) { + var value = this.value() || 0; + + if ( !this.counter ) { + this.counter = 1; + } + + value = this._adjustValue( value + step * this._increment( this.counter ) ); + + if ( !this.spinning || this._trigger( "spin", event, { value: value } ) !== false ) { + this._value( value ); + this.counter++; + } + }, + + _increment: function( i ) { + var incremental = this.options.incremental; + + if ( incremental ) { + return $.isFunction( incremental ) ? + incremental( i ) : + Math.floor( i * i * i / 50000 - i * i / 500 + 17 * i / 200 + 1 ); + } + + return 1; + }, + + _precision: function() { + var precision = this._precisionOf( this.options.step ); + if ( this.options.min !== null ) { + precision = Math.max( precision, this._precisionOf( this.options.min ) ); + } + return precision; + }, + + _precisionOf: function( num ) { + var str = num.toString(), + decimal = str.indexOf( "." ); + return decimal === -1 ? 0 : str.length - decimal - 1; + }, + + _adjustValue: function( value ) { + var base, aboveMin, + options = this.options; + + // Make sure we're at a valid step + // - find out where we are relative to the base (min or 0) + base = options.min !== null ? options.min : 0; + aboveMin = value - base; + + // - round to the nearest step + aboveMin = Math.round( aboveMin / options.step ) * options.step; + + // - rounding is based on 0, so adjust back to our base + value = base + aboveMin; + + // Fix precision from bad JS floating point math + value = parseFloat( value.toFixed( this._precision() ) ); + + // Clamp the value + if ( options.max !== null && value > options.max ) { + return options.max; + } + if ( options.min !== null && value < options.min ) { + return options.min; + } + + return value; + }, + + _stop: function( event ) { + if ( !this.spinning ) { + return; + } + + clearTimeout( this.timer ); + clearTimeout( this.mousewheelTimer ); + this.counter = 0; + this.spinning = false; + this._trigger( "stop", event ); + }, + + _setOption: function( key, value ) { + var prevValue, first, last; + + if ( key === "culture" || key === "numberFormat" ) { + prevValue = this._parse( this.element.val() ); + this.options[ key ] = value; + this.element.val( this._format( prevValue ) ); + return; + } + + if ( key === "max" || key === "min" || key === "step" ) { + if ( typeof value === "string" ) { + value = this._parse( value ); + } + } + if ( key === "icons" ) { + first = this.buttons.first().find( ".ui-icon" ); + this._removeClass( first, null, this.options.icons.up ); + this._addClass( first, null, value.up ); + last = this.buttons.last().find( ".ui-icon" ); + this._removeClass( last, null, this.options.icons.down ); + this._addClass( last, null, value.down ); + } + + this._super( key, value ); + }, + + _setOptionDisabled: function( value ) { + this._super( value ); + + this._toggleClass( this.uiSpinner, null, "ui-state-disabled", !!value ); + this.element.prop( "disabled", !!value ); + this.buttons.button( value ? "disable" : "enable" ); + }, + + _setOptions: spinnerModifer( function( options ) { + this._super( options ); + } ), + + _parse: function( val ) { + if ( typeof val === "string" && val !== "" ) { + val = window.Globalize && this.options.numberFormat ? + Globalize.parseFloat( val, 10, this.options.culture ) : +val; + } + return val === "" || isNaN( val ) ? null : val; + }, + + _format: function( value ) { + if ( value === "" ) { + return ""; + } + return window.Globalize && this.options.numberFormat ? + Globalize.format( value, this.options.numberFormat, this.options.culture ) : + value; + }, + + _refresh: function() { + this.element.attr( { + "aria-valuemin": this.options.min, + "aria-valuemax": this.options.max, + + // TODO: what should we do with values that can't be parsed? + "aria-valuenow": this._parse( this.element.val() ) + } ); + }, + + isValid: function() { + var value = this.value(); + + // Null is invalid + if ( value === null ) { + return false; + } + + // If value gets adjusted, it's invalid + return value === this._adjustValue( value ); + }, + + // Update the value without triggering change + _value: function( value, allowAny ) { + var parsed; + if ( value !== "" ) { + parsed = this._parse( value ); + if ( parsed !== null ) { + if ( !allowAny ) { + parsed = this._adjustValue( parsed ); + } + value = this._format( parsed ); + } + } + this.element.val( value ); + this._refresh(); + }, + + _destroy: function() { + this.element + .prop( "disabled", false ) + .removeAttr( "autocomplete role aria-valuemin aria-valuemax aria-valuenow" ); + + this.uiSpinner.replaceWith( this.element ); + }, + + stepUp: spinnerModifer( function( steps ) { + this._stepUp( steps ); + } ), + _stepUp: function( steps ) { + if ( this._start() ) { + this._spin( ( steps || 1 ) * this.options.step ); + this._stop(); + } + }, + + stepDown: spinnerModifer( function( steps ) { + this._stepDown( steps ); + } ), + _stepDown: function( steps ) { + if ( this._start() ) { + this._spin( ( steps || 1 ) * -this.options.step ); + this._stop(); + } + }, + + pageUp: spinnerModifer( function( pages ) { + this._stepUp( ( pages || 1 ) * this.options.page ); + } ), + + pageDown: spinnerModifer( function( pages ) { + this._stepDown( ( pages || 1 ) * this.options.page ); + } ), + + value: function( newVal ) { + if ( !arguments.length ) { + return this._parse( this.element.val() ); + } + spinnerModifer( this._value ).call( this, newVal ); + }, + + widget: function() { + return this.uiSpinner; + } +} ); + +// DEPRECATED +// TODO: switch return back to widget declaration at top of file when this is removed +if ( $.uiBackCompat !== false ) { + + // Backcompat for spinner html extension points + $.widget( "ui.spinner", $.ui.spinner, { + _enhance: function() { + this.uiSpinner = this.element + .attr( "autocomplete", "off" ) + .wrap( this._uiSpinnerHtml() ) + .parent() + + // Add buttons + .append( this._buttonHtml() ); + }, + _uiSpinnerHtml: function() { + return "<span>"; + }, + + _buttonHtml: function() { + return "<a></a><a></a>"; + } + } ); +} + +var widgetsSpinner = $.ui.spinner; + + +/*! + * jQuery UI Tabs 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: Tabs +//>>group: Widgets +//>>description: Transforms a set of container elements into a tab structure. +//>>docs: http://api.jqueryui.com/tabs/ +//>>demos: http://jqueryui.com/tabs/ +//>>css.structure: ../../themes/base/core.css +//>>css.structure: ../../themes/base/tabs.css +//>>css.theme: ../../themes/base/theme.css + + + +$.widget( "ui.tabs", { + version: "1.12.1", + delay: 300, + options: { + active: null, + classes: { + "ui-tabs": "ui-corner-all", + "ui-tabs-nav": "ui-corner-all", + "ui-tabs-panel": "ui-corner-bottom", + "ui-tabs-tab": "ui-corner-top" + }, + collapsible: false, + event: "click", + heightStyle: "content", + hide: null, + show: null, + + // Callbacks + activate: null, + beforeActivate: null, + beforeLoad: null, + load: null + }, + + _isLocal: ( function() { + var rhash = /#.*$/; + + return function( anchor ) { + var anchorUrl, locationUrl; + + anchorUrl = anchor.href.replace( rhash, "" ); + locationUrl = location.href.replace( rhash, "" ); + + // Decoding may throw an error if the URL isn't UTF-8 (#9518) + try { + anchorUrl = decodeURIComponent( anchorUrl ); + } catch ( error ) {} + try { + locationUrl = decodeURIComponent( locationUrl ); + } catch ( error ) {} + + return anchor.hash.length > 1 && anchorUrl === locationUrl; + }; + } )(), + + _create: function() { + var that = this, + options = this.options; + + this.running = false; + + this._addClass( "ui-tabs", "ui-widget ui-widget-content" ); + this._toggleClass( "ui-tabs-collapsible", null, options.collapsible ); + + this._processTabs(); + options.active = this._initialActive(); + + // Take disabling tabs via class attribute from HTML + // into account and update option properly. + if ( $.isArray( options.disabled ) ) { + options.disabled = $.unique( options.disabled.concat( + $.map( this.tabs.filter( ".ui-state-disabled" ), function( li ) { + return that.tabs.index( li ); + } ) + ) ).sort(); + } + + // Check for length avoids error when initializing empty list + if ( this.options.active !== false && this.anchors.length ) { + this.active = this._findActive( options.active ); + } else { + this.active = $(); + } + + this._refresh(); + + if ( this.active.length ) { + this.load( options.active ); + } + }, + + _initialActive: function() { + var active = this.options.active, + collapsible = this.options.collapsible, + locationHash = location.hash.substring( 1 ); + + if ( active === null ) { + + // check the fragment identifier in the URL + if ( locationHash ) { + this.tabs.each( function( i, tab ) { + if ( $( tab ).attr( "aria-controls" ) === locationHash ) { + active = i; + return false; + } + } ); + } + + // Check for a tab marked active via a class + if ( active === null ) { + active = this.tabs.index( this.tabs.filter( ".ui-tabs-active" ) ); + } + + // No active tab, set to false + if ( active === null || active === -1 ) { + active = this.tabs.length ? 0 : false; + } + } + + // Handle numbers: negative, out of range + if ( active !== false ) { + active = this.tabs.index( this.tabs.eq( active ) ); + if ( active === -1 ) { + active = collapsible ? false : 0; + } + } + + // Don't allow collapsible: false and active: false + if ( !collapsible && active === false && this.anchors.length ) { + active = 0; + } + + return active; + }, + + _getCreateEventData: function() { + return { + tab: this.active, + panel: !this.active.length ? $() : this._getPanelForTab( this.active ) + }; + }, + + _tabKeydown: function( event ) { + var focusedTab = $( $.ui.safeActiveElement( this.document[ 0 ] ) ).closest( "li" ), + selectedIndex = this.tabs.index( focusedTab ), + goingForward = true; + + if ( this._handlePageNav( event ) ) { + return; + } + + switch ( event.keyCode ) { + case $.ui.keyCode.RIGHT: + case $.ui.keyCode.DOWN: + selectedIndex++; + break; + case $.ui.keyCode.UP: + case $.ui.keyCode.LEFT: + goingForward = false; + selectedIndex--; + break; + case $.ui.keyCode.END: + selectedIndex = this.anchors.length - 1; + break; + case $.ui.keyCode.HOME: + selectedIndex = 0; + break; + case $.ui.keyCode.SPACE: + + // Activate only, no collapsing + event.preventDefault(); + clearTimeout( this.activating ); + this._activate( selectedIndex ); + return; + case $.ui.keyCode.ENTER: + + // Toggle (cancel delayed activation, allow collapsing) + event.preventDefault(); + clearTimeout( this.activating ); + + // Determine if we should collapse or activate + this._activate( selectedIndex === this.options.active ? false : selectedIndex ); + return; + default: + return; + } + + // Focus the appropriate tab, based on which key was pressed + event.preventDefault(); + clearTimeout( this.activating ); + selectedIndex = this._focusNextTab( selectedIndex, goingForward ); + + // Navigating with control/command key will prevent automatic activation + if ( !event.ctrlKey && !event.metaKey ) { + + // Update aria-selected immediately so that AT think the tab is already selected. + // Otherwise AT may confuse the user by stating that they need to activate the tab, + // but the tab will already be activated by the time the announcement finishes. + focusedTab.attr( "aria-selected", "false" ); + this.tabs.eq( selectedIndex ).attr( "aria-selected", "true" ); + + this.activating = this._delay( function() { + this.option( "active", selectedIndex ); + }, this.delay ); + } + }, + + _panelKeydown: function( event ) { + if ( this._handlePageNav( event ) ) { + return; + } + + // Ctrl+up moves focus to the current tab + if ( event.ctrlKey && event.keyCode === $.ui.keyCode.UP ) { + event.preventDefault(); + this.active.trigger( "focus" ); + } + }, + + // Alt+page up/down moves focus to the previous/next tab (and activates) + _handlePageNav: function( event ) { + if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_UP ) { + this._activate( this._focusNextTab( this.options.active - 1, false ) ); + return true; + } + if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_DOWN ) { + this._activate( this._focusNextTab( this.options.active + 1, true ) ); + return true; + } + }, + + _findNextTab: function( index, goingForward ) { + var lastTabIndex = this.tabs.length - 1; + + function constrain() { + if ( index > lastTabIndex ) { + index = 0; + } + if ( index < 0 ) { + index = lastTabIndex; + } + return index; + } + + while ( $.inArray( constrain(), this.options.disabled ) !== -1 ) { + index = goingForward ? index + 1 : index - 1; + } + + return index; + }, + + _focusNextTab: function( index, goingForward ) { + index = this._findNextTab( index, goingForward ); + this.tabs.eq( index ).trigger( "focus" ); + return index; + }, + + _setOption: function( key, value ) { + if ( key === "active" ) { + + // _activate() will handle invalid values and update this.options + this._activate( value ); + return; + } + + this._super( key, value ); + + if ( key === "collapsible" ) { + this._toggleClass( "ui-tabs-collapsible", null, value ); + + // Setting collapsible: false while collapsed; open first panel + if ( !value && this.options.active === false ) { + this._activate( 0 ); + } + } + + if ( key === "event" ) { + this._setupEvents( value ); + } + + if ( key === "heightStyle" ) { + this._setupHeightStyle( value ); + } + }, + + _sanitizeSelector: function( hash ) { + return hash ? hash.replace( /[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g, "\\$&" ) : ""; + }, + + refresh: function() { + var options = this.options, + lis = this.tablist.children( ":has(a[href])" ); + + // Get disabled tabs from class attribute from HTML + // this will get converted to a boolean if needed in _refresh() + options.disabled = $.map( lis.filter( ".ui-state-disabled" ), function( tab ) { + return lis.index( tab ); + } ); + + this._processTabs(); + + // Was collapsed or no tabs + if ( options.active === false || !this.anchors.length ) { + options.active = false; + this.active = $(); + + // was active, but active tab is gone + } else if ( this.active.length && !$.contains( this.tablist[ 0 ], this.active[ 0 ] ) ) { + + // all remaining tabs are disabled + if ( this.tabs.length === options.disabled.length ) { + options.active = false; + this.active = $(); + + // activate previous tab + } else { + this._activate( this._findNextTab( Math.max( 0, options.active - 1 ), false ) ); + } + + // was active, active tab still exists + } else { + + // make sure active index is correct + options.active = this.tabs.index( this.active ); + } + + this._refresh(); + }, + + _refresh: function() { + this._setOptionDisabled( this.options.disabled ); + this._setupEvents( this.options.event ); + this._setupHeightStyle( this.options.heightStyle ); + + this.tabs.not( this.active ).attr( { + "aria-selected": "false", + "aria-expanded": "false", + tabIndex: -1 + } ); + this.panels.not( this._getPanelForTab( this.active ) ) + .hide() + .attr( { + "aria-hidden": "true" + } ); + + // Make sure one tab is in the tab order + if ( !this.active.length ) { + this.tabs.eq( 0 ).attr( "tabIndex", 0 ); + } else { + this.active + .attr( { + "aria-selected": "true", + "aria-expanded": "true", + tabIndex: 0 + } ); + this._addClass( this.active, "ui-tabs-active", "ui-state-active" ); + this._getPanelForTab( this.active ) + .show() + .attr( { + "aria-hidden": "false" + } ); + } + }, + + _processTabs: function() { + var that = this, + prevTabs = this.tabs, + prevAnchors = this.anchors, + prevPanels = this.panels; + + this.tablist = this._getList().attr( "role", "tablist" ); + this._addClass( this.tablist, "ui-tabs-nav", + "ui-helper-reset ui-helper-clearfix ui-widget-header" ); + + // Prevent users from focusing disabled tabs via click + this.tablist + .on( "mousedown" + this.eventNamespace, "> li", function( event ) { + if ( $( this ).is( ".ui-state-disabled" ) ) { + event.preventDefault(); + } + } ) + + // Support: IE <9 + // Preventing the default action in mousedown doesn't prevent IE + // from focusing the element, so if the anchor gets focused, blur. + // We don't have to worry about focusing the previously focused + // element since clicking on a non-focusable element should focus + // the body anyway. + .on( "focus" + this.eventNamespace, ".ui-tabs-anchor", function() { + if ( $( this ).closest( "li" ).is( ".ui-state-disabled" ) ) { + this.blur(); + } + } ); + + this.tabs = this.tablist.find( "> li:has(a[href])" ) + .attr( { + role: "tab", + tabIndex: -1 + } ); + this._addClass( this.tabs, "ui-tabs-tab", "ui-state-default" ); + + this.anchors = this.tabs.map( function() { + return $( "a", this )[ 0 ]; + } ) + .attr( { + role: "presentation", + tabIndex: -1 + } ); + this._addClass( this.anchors, "ui-tabs-anchor" ); + + this.panels = $(); + + this.anchors.each( function( i, anchor ) { + var selector, panel, panelId, + anchorId = $( anchor ).uniqueId().attr( "id" ), + tab = $( anchor ).closest( "li" ), + originalAriaControls = tab.attr( "aria-controls" ); + + // Inline tab + if ( that._isLocal( anchor ) ) { + selector = anchor.hash; + panelId = selector.substring( 1 ); + panel = that.element.find( that._sanitizeSelector( selector ) ); + + // remote tab + } else { + + // If the tab doesn't already have aria-controls, + // generate an id by using a throw-away element + panelId = tab.attr( "aria-controls" ) || $( {} ).uniqueId()[ 0 ].id; + selector = "#" + panelId; + panel = that.element.find( selector ); + if ( !panel.length ) { + panel = that._createPanel( panelId ); + panel.insertAfter( that.panels[ i - 1 ] || that.tablist ); + } + panel.attr( "aria-live", "polite" ); + } + + if ( panel.length ) { + that.panels = that.panels.add( panel ); + } + if ( originalAriaControls ) { + tab.data( "ui-tabs-aria-controls", originalAriaControls ); + } + tab.attr( { + "aria-controls": panelId, + "aria-labelledby": anchorId + } ); + panel.attr( "aria-labelledby", anchorId ); + } ); + + this.panels.attr( "role", "tabpanel" ); + this._addClass( this.panels, "ui-tabs-panel", "ui-widget-content" ); + + // Avoid memory leaks (#10056) + if ( prevTabs ) { + this._off( prevTabs.not( this.tabs ) ); + this._off( prevAnchors.not( this.anchors ) ); + this._off( prevPanels.not( this.panels ) ); + } + }, + + // Allow overriding how to find the list for rare usage scenarios (#7715) + _getList: function() { + return this.tablist || this.element.find( "ol, ul" ).eq( 0 ); + }, + + _createPanel: function( id ) { + return $( "<div>" ) + .attr( "id", id ) + .data( "ui-tabs-destroy", true ); + }, + + _setOptionDisabled: function( disabled ) { + var currentItem, li, i; + + if ( $.isArray( disabled ) ) { + if ( !disabled.length ) { + disabled = false; + } else if ( disabled.length === this.anchors.length ) { + disabled = true; + } + } + + // Disable tabs + for ( i = 0; ( li = this.tabs[ i ] ); i++ ) { + currentItem = $( li ); + if ( disabled === true || $.inArray( i, disabled ) !== -1 ) { + currentItem.attr( "aria-disabled", "true" ); + this._addClass( currentItem, null, "ui-state-disabled" ); + } else { + currentItem.removeAttr( "aria-disabled" ); + this._removeClass( currentItem, null, "ui-state-disabled" ); + } + } + + this.options.disabled = disabled; + + this._toggleClass( this.widget(), this.widgetFullName + "-disabled", null, + disabled === true ); + }, + + _setupEvents: function( event ) { + var events = {}; + if ( event ) { + $.each( event.split( " " ), function( index, eventName ) { + events[ eventName ] = "_eventHandler"; + } ); + } + + this._off( this.anchors.add( this.tabs ).add( this.panels ) ); + + // Always prevent the default action, even when disabled + this._on( true, this.anchors, { + click: function( event ) { + event.preventDefault(); + } + } ); + this._on( this.anchors, events ); + this._on( this.tabs, { keydown: "_tabKeydown" } ); + this._on( this.panels, { keydown: "_panelKeydown" } ); + + this._focusable( this.tabs ); + this._hoverable( this.tabs ); + }, + + _setupHeightStyle: function( heightStyle ) { + var maxHeight, + parent = this.element.parent(); + + if ( heightStyle === "fill" ) { + maxHeight = parent.height(); + maxHeight -= this.element.outerHeight() - this.element.height(); + + this.element.siblings( ":visible" ).each( function() { + var elem = $( this ), + position = elem.css( "position" ); + + if ( position === "absolute" || position === "fixed" ) { + return; + } + maxHeight -= elem.outerHeight( true ); + } ); + + this.element.children().not( this.panels ).each( function() { + maxHeight -= $( this ).outerHeight( true ); + } ); + + this.panels.each( function() { + $( this ).height( Math.max( 0, maxHeight - + $( this ).innerHeight() + $( this ).height() ) ); + } ) + .css( "overflow", "auto" ); + } else if ( heightStyle === "auto" ) { + maxHeight = 0; + this.panels.each( function() { + maxHeight = Math.max( maxHeight, $( this ).height( "" ).height() ); + } ).height( maxHeight ); + } + }, + + _eventHandler: function( event ) { + var options = this.options, + active = this.active, + anchor = $( event.currentTarget ), + tab = anchor.closest( "li" ), + clickedIsActive = tab[ 0 ] === active[ 0 ], + collapsing = clickedIsActive && options.collapsible, + toShow = collapsing ? $() : this._getPanelForTab( tab ), + toHide = !active.length ? $() : this._getPanelForTab( active ), + eventData = { + oldTab: active, + oldPanel: toHide, + newTab: collapsing ? $() : tab, + newPanel: toShow + }; + + event.preventDefault(); + + if ( tab.hasClass( "ui-state-disabled" ) || + + // tab is already loading + tab.hasClass( "ui-tabs-loading" ) || + + // can't switch durning an animation + this.running || + + // click on active header, but not collapsible + ( clickedIsActive && !options.collapsible ) || + + // allow canceling activation + ( this._trigger( "beforeActivate", event, eventData ) === false ) ) { + return; + } + + options.active = collapsing ? false : this.tabs.index( tab ); + + this.active = clickedIsActive ? $() : tab; + if ( this.xhr ) { + this.xhr.abort(); + } + + if ( !toHide.length && !toShow.length ) { + $.error( "jQuery UI Tabs: Mismatching fragment identifier." ); + } + + if ( toShow.length ) { + this.load( this.tabs.index( tab ), event ); + } + this._toggle( event, eventData ); + }, + + // Handles show/hide for selecting tabs + _toggle: function( event, eventData ) { + var that = this, + toShow = eventData.newPanel, + toHide = eventData.oldPanel; + + this.running = true; + + function complete() { + that.running = false; + that._trigger( "activate", event, eventData ); + } + + function show() { + that._addClass( eventData.newTab.closest( "li" ), "ui-tabs-active", "ui-state-active" ); + + if ( toShow.length && that.options.show ) { + that._show( toShow, that.options.show, complete ); + } else { + toShow.show(); + complete(); + } + } + + // Start out by hiding, then showing, then completing + if ( toHide.length && this.options.hide ) { + this._hide( toHide, this.options.hide, function() { + that._removeClass( eventData.oldTab.closest( "li" ), + "ui-tabs-active", "ui-state-active" ); + show(); + } ); + } else { + this._removeClass( eventData.oldTab.closest( "li" ), + "ui-tabs-active", "ui-state-active" ); + toHide.hide(); + show(); + } + + toHide.attr( "aria-hidden", "true" ); + eventData.oldTab.attr( { + "aria-selected": "false", + "aria-expanded": "false" + } ); + + // If we're switching tabs, remove the old tab from the tab order. + // If we're opening from collapsed state, remove the previous tab from the tab order. + // If we're collapsing, then keep the collapsing tab in the tab order. + if ( toShow.length && toHide.length ) { + eventData.oldTab.attr( "tabIndex", -1 ); + } else if ( toShow.length ) { + this.tabs.filter( function() { + return $( this ).attr( "tabIndex" ) === 0; + } ) + .attr( "tabIndex", -1 ); + } + + toShow.attr( "aria-hidden", "false" ); + eventData.newTab.attr( { + "aria-selected": "true", + "aria-expanded": "true", + tabIndex: 0 + } ); + }, + + _activate: function( index ) { + var anchor, + active = this._findActive( index ); + + // Trying to activate the already active panel + if ( active[ 0 ] === this.active[ 0 ] ) { + return; + } + + // Trying to collapse, simulate a click on the current active header + if ( !active.length ) { + active = this.active; + } + + anchor = active.find( ".ui-tabs-anchor" )[ 0 ]; + this._eventHandler( { + target: anchor, + currentTarget: anchor, + preventDefault: $.noop + } ); + }, + + _findActive: function( index ) { + return index === false ? $() : this.tabs.eq( index ); + }, + + _getIndex: function( index ) { + + // meta-function to give users option to provide a href string instead of a numerical index. + if ( typeof index === "string" ) { + index = this.anchors.index( this.anchors.filter( "[href$='" + + $.ui.escapeSelector( index ) + "']" ) ); + } + + return index; + }, + + _destroy: function() { + if ( this.xhr ) { + this.xhr.abort(); + } + + this.tablist + .removeAttr( "role" ) + .off( this.eventNamespace ); + + this.anchors + .removeAttr( "role tabIndex" ) + .removeUniqueId(); + + this.tabs.add( this.panels ).each( function() { + if ( $.data( this, "ui-tabs-destroy" ) ) { + $( this ).remove(); + } else { + $( this ).removeAttr( "role tabIndex " + + "aria-live aria-busy aria-selected aria-labelledby aria-hidden aria-expanded" ); + } + } ); + + this.tabs.each( function() { + var li = $( this ), + prev = li.data( "ui-tabs-aria-controls" ); + if ( prev ) { + li + .attr( "aria-controls", prev ) + .removeData( "ui-tabs-aria-controls" ); + } else { + li.removeAttr( "aria-controls" ); + } + } ); + + this.panels.show(); + + if ( this.options.heightStyle !== "content" ) { + this.panels.css( "height", "" ); + } + }, + + enable: function( index ) { + var disabled = this.options.disabled; + if ( disabled === false ) { + return; + } + + if ( index === undefined ) { + disabled = false; + } else { + index = this._getIndex( index ); + if ( $.isArray( disabled ) ) { + disabled = $.map( disabled, function( num ) { + return num !== index ? num : null; + } ); + } else { + disabled = $.map( this.tabs, function( li, num ) { + return num !== index ? num : null; + } ); + } + } + this._setOptionDisabled( disabled ); + }, + + disable: function( index ) { + var disabled = this.options.disabled; + if ( disabled === true ) { + return; + } + + if ( index === undefined ) { + disabled = true; + } else { + index = this._getIndex( index ); + if ( $.inArray( index, disabled ) !== -1 ) { + return; + } + if ( $.isArray( disabled ) ) { + disabled = $.merge( [ index ], disabled ).sort(); + } else { + disabled = [ index ]; + } + } + this._setOptionDisabled( disabled ); + }, + + load: function( index, event ) { + index = this._getIndex( index ); + var that = this, + tab = this.tabs.eq( index ), + anchor = tab.find( ".ui-tabs-anchor" ), + panel = this._getPanelForTab( tab ), + eventData = { + tab: tab, + panel: panel + }, + complete = function( jqXHR, status ) { + if ( status === "abort" ) { + that.panels.stop( false, true ); + } + + that._removeClass( tab, "ui-tabs-loading" ); + panel.removeAttr( "aria-busy" ); + + if ( jqXHR === that.xhr ) { + delete that.xhr; + } + }; + + // Not remote + if ( this._isLocal( anchor[ 0 ] ) ) { + return; + } + + this.xhr = $.ajax( this._ajaxSettings( anchor, event, eventData ) ); + + // Support: jQuery <1.8 + // jQuery <1.8 returns false if the request is canceled in beforeSend, + // but as of 1.8, $.ajax() always returns a jqXHR object. + if ( this.xhr && this.xhr.statusText !== "canceled" ) { + this._addClass( tab, "ui-tabs-loading" ); + panel.attr( "aria-busy", "true" ); + + this.xhr + .done( function( response, status, jqXHR ) { + + // support: jQuery <1.8 + // http://bugs.jquery.com/ticket/11778 + setTimeout( function() { + panel.html( response ); + that._trigger( "load", event, eventData ); + + complete( jqXHR, status ); + }, 1 ); + } ) + .fail( function( jqXHR, status ) { + + // support: jQuery <1.8 + // http://bugs.jquery.com/ticket/11778 + setTimeout( function() { + complete( jqXHR, status ); + }, 1 ); + } ); + } + }, + + _ajaxSettings: function( anchor, event, eventData ) { + var that = this; + return { + + // Support: IE <11 only + // Strip any hash that exists to prevent errors with the Ajax request + url: anchor.attr( "href" ).replace( /#.*$/, "" ), + beforeSend: function( jqXHR, settings ) { + return that._trigger( "beforeLoad", event, + $.extend( { jqXHR: jqXHR, ajaxSettings: settings }, eventData ) ); + } + }; + }, + + _getPanelForTab: function( tab ) { + var id = $( tab ).attr( "aria-controls" ); + return this.element.find( this._sanitizeSelector( "#" + id ) ); + } +} ); + +// DEPRECATED +// TODO: Switch return back to widget declaration at top of file when this is removed +if ( $.uiBackCompat !== false ) { + + // Backcompat for ui-tab class (now ui-tabs-tab) + $.widget( "ui.tabs", $.ui.tabs, { + _processTabs: function() { + this._superApply( arguments ); + this._addClass( this.tabs, "ui-tab" ); + } + } ); +} + +var widgetsTabs = $.ui.tabs; + + +/*! + * jQuery UI Tooltip 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: Tooltip +//>>group: Widgets +//>>description: Shows additional information for any element on hover or focus. +//>>docs: http://api.jqueryui.com/tooltip/ +//>>demos: http://jqueryui.com/tooltip/ +//>>css.structure: ../../themes/base/core.css +//>>css.structure: ../../themes/base/tooltip.css +//>>css.theme: ../../themes/base/theme.css + + + +$.widget( "ui.tooltip", { + version: "1.12.1", + options: { + classes: { + "ui-tooltip": "ui-corner-all ui-widget-shadow" + }, + content: function() { + + // support: IE<9, Opera in jQuery <1.7 + // .text() can't accept undefined, so coerce to a string + var title = $( this ).attr( "title" ) || ""; + + // Escape title, since we're going from an attribute to raw HTML + return $( "<a>" ).text( title ).html(); + }, + hide: true, + + // Disabled elements have inconsistent behavior across browsers (#8661) + items: "[title]:not([disabled])", + position: { + my: "left top+15", + at: "left bottom", + collision: "flipfit flip" + }, + show: true, + track: false, + + // Callbacks + close: null, + open: null + }, + + _addDescribedBy: function( elem, id ) { + var describedby = ( elem.attr( "aria-describedby" ) || "" ).split( /\s+/ ); + describedby.push( id ); + elem + .data( "ui-tooltip-id", id ) + .attr( "aria-describedby", $.trim( describedby.join( " " ) ) ); + }, + + _removeDescribedBy: function( elem ) { + var id = elem.data( "ui-tooltip-id" ), + describedby = ( elem.attr( "aria-describedby" ) || "" ).split( /\s+/ ), + index = $.inArray( id, describedby ); + + if ( index !== -1 ) { + describedby.splice( index, 1 ); + } + + elem.removeData( "ui-tooltip-id" ); + describedby = $.trim( describedby.join( " " ) ); + if ( describedby ) { + elem.attr( "aria-describedby", describedby ); + } else { + elem.removeAttr( "aria-describedby" ); + } + }, + + _create: function() { + this._on( { + mouseover: "open", + focusin: "open" + } ); + + // IDs of generated tooltips, needed for destroy + this.tooltips = {}; + + // IDs of parent tooltips where we removed the title attribute + this.parents = {}; + + // Append the aria-live region so tooltips announce correctly + this.liveRegion = $( "<div>" ) + .attr( { + role: "log", + "aria-live": "assertive", + "aria-relevant": "additions" + } ) + .appendTo( this.document[ 0 ].body ); + this._addClass( this.liveRegion, null, "ui-helper-hidden-accessible" ); + + this.disabledTitles = $( [] ); + }, + + _setOption: function( key, value ) { + var that = this; + + this._super( key, value ); + + if ( key === "content" ) { + $.each( this.tooltips, function( id, tooltipData ) { + that._updateContent( tooltipData.element ); + } ); + } + }, + + _setOptionDisabled: function( value ) { + this[ value ? "_disable" : "_enable" ](); + }, + + _disable: function() { + var that = this; + + // Close open tooltips + $.each( this.tooltips, function( id, tooltipData ) { + var event = $.Event( "blur" ); + event.target = event.currentTarget = tooltipData.element[ 0 ]; + that.close( event, true ); + } ); + + // Remove title attributes to prevent native tooltips + this.disabledTitles = this.disabledTitles.add( + this.element.find( this.options.items ).addBack() + .filter( function() { + var element = $( this ); + if ( element.is( "[title]" ) ) { + return element + .data( "ui-tooltip-title", element.attr( "title" ) ) + .removeAttr( "title" ); + } + } ) + ); + }, + + _enable: function() { + + // restore title attributes + this.disabledTitles.each( function() { + var element = $( this ); + if ( element.data( "ui-tooltip-title" ) ) { + element.attr( "title", element.data( "ui-tooltip-title" ) ); + } + } ); + this.disabledTitles = $( [] ); + }, + + open: function( event ) { + var that = this, + target = $( event ? event.target : this.element ) + + // we need closest here due to mouseover bubbling, + // but always pointing at the same event target + .closest( this.options.items ); + + // No element to show a tooltip for or the tooltip is already open + if ( !target.length || target.data( "ui-tooltip-id" ) ) { + return; + } + + if ( target.attr( "title" ) ) { + target.data( "ui-tooltip-title", target.attr( "title" ) ); + } + + target.data( "ui-tooltip-open", true ); + + // Kill parent tooltips, custom or native, for hover + if ( event && event.type === "mouseover" ) { + target.parents().each( function() { + var parent = $( this ), + blurEvent; + if ( parent.data( "ui-tooltip-open" ) ) { + blurEvent = $.Event( "blur" ); + blurEvent.target = blurEvent.currentTarget = this; + that.close( blurEvent, true ); + } + if ( parent.attr( "title" ) ) { + parent.uniqueId(); + that.parents[ this.id ] = { + element: this, + title: parent.attr( "title" ) + }; + parent.attr( "title", "" ); + } + } ); + } + + this._registerCloseHandlers( event, target ); + this._updateContent( target, event ); + }, + + _updateContent: function( target, event ) { + var content, + contentOption = this.options.content, + that = this, + eventType = event ? event.type : null; + + if ( typeof contentOption === "string" || contentOption.nodeType || + contentOption.jquery ) { + return this._open( event, target, contentOption ); + } + + content = contentOption.call( target[ 0 ], function( response ) { + + // IE may instantly serve a cached response for ajax requests + // delay this call to _open so the other call to _open runs first + that._delay( function() { + + // Ignore async response if tooltip was closed already + if ( !target.data( "ui-tooltip-open" ) ) { + return; + } + + // JQuery creates a special event for focusin when it doesn't + // exist natively. To improve performance, the native event + // object is reused and the type is changed. Therefore, we can't + // rely on the type being correct after the event finished + // bubbling, so we set it back to the previous value. (#8740) + if ( event ) { + event.type = eventType; + } + this._open( event, target, response ); + } ); + } ); + if ( content ) { + this._open( event, target, content ); + } + }, + + _open: function( event, target, content ) { + var tooltipData, tooltip, delayedShow, a11yContent, + positionOption = $.extend( {}, this.options.position ); + + if ( !content ) { + return; + } + + // Content can be updated multiple times. If the tooltip already + // exists, then just update the content and bail. + tooltipData = this._find( target ); + if ( tooltipData ) { + tooltipData.tooltip.find( ".ui-tooltip-content" ).html( content ); + return; + } + + // If we have a title, clear it to prevent the native tooltip + // we have to check first to avoid defining a title if none exists + // (we don't want to cause an element to start matching [title]) + // + // We use removeAttr only for key events, to allow IE to export the correct + // accessible attributes. For mouse events, set to empty string to avoid + // native tooltip showing up (happens only when removing inside mouseover). + if ( target.is( "[title]" ) ) { + if ( event && event.type === "mouseover" ) { + target.attr( "title", "" ); + } else { + target.removeAttr( "title" ); + } + } + + tooltipData = this._tooltip( target ); + tooltip = tooltipData.tooltip; + this._addDescribedBy( target, tooltip.attr( "id" ) ); + tooltip.find( ".ui-tooltip-content" ).html( content ); + + // Support: Voiceover on OS X, JAWS on IE <= 9 + // JAWS announces deletions even when aria-relevant="additions" + // Voiceover will sometimes re-read the entire log region's contents from the beginning + this.liveRegion.children().hide(); + a11yContent = $( "<div>" ).html( tooltip.find( ".ui-tooltip-content" ).html() ); + a11yContent.removeAttr( "name" ).find( "[name]" ).removeAttr( "name" ); + a11yContent.removeAttr( "id" ).find( "[id]" ).removeAttr( "id" ); + a11yContent.appendTo( this.liveRegion ); + + function position( event ) { + positionOption.of = event; + if ( tooltip.is( ":hidden" ) ) { + return; + } + tooltip.position( positionOption ); + } + if ( this.options.track && event && /^mouse/.test( event.type ) ) { + this._on( this.document, { + mousemove: position + } ); + + // trigger once to override element-relative positioning + position( event ); + } else { + tooltip.position( $.extend( { + of: target + }, this.options.position ) ); + } + + tooltip.hide(); + + this._show( tooltip, this.options.show ); + + // Handle tracking tooltips that are shown with a delay (#8644). As soon + // as the tooltip is visible, position the tooltip using the most recent + // event. + // Adds the check to add the timers only when both delay and track options are set (#14682) + if ( this.options.track && this.options.show && this.options.show.delay ) { + delayedShow = this.delayedShow = setInterval( function() { + if ( tooltip.is( ":visible" ) ) { + position( positionOption.of ); + clearInterval( delayedShow ); + } + }, $.fx.interval ); + } + + this._trigger( "open", event, { tooltip: tooltip } ); + }, + + _registerCloseHandlers: function( event, target ) { + var events = { + keyup: function( event ) { + if ( event.keyCode === $.ui.keyCode.ESCAPE ) { + var fakeEvent = $.Event( event ); + fakeEvent.currentTarget = target[ 0 ]; + this.close( fakeEvent, true ); + } + } + }; + + // Only bind remove handler for delegated targets. Non-delegated + // tooltips will handle this in destroy. + if ( target[ 0 ] !== this.element[ 0 ] ) { + events.remove = function() { + this._removeTooltip( this._find( target ).tooltip ); + }; + } + + if ( !event || event.type === "mouseover" ) { + events.mouseleave = "close"; + } + if ( !event || event.type === "focusin" ) { + events.focusout = "close"; + } + this._on( true, target, events ); + }, + + close: function( event ) { + var tooltip, + that = this, + target = $( event ? event.currentTarget : this.element ), + tooltipData = this._find( target ); + + // The tooltip may already be closed + if ( !tooltipData ) { + + // We set ui-tooltip-open immediately upon open (in open()), but only set the + // additional data once there's actually content to show (in _open()). So even if the + // tooltip doesn't have full data, we always remove ui-tooltip-open in case we're in + // the period between open() and _open(). + target.removeData( "ui-tooltip-open" ); + return; + } + + tooltip = tooltipData.tooltip; + + // Disabling closes the tooltip, so we need to track when we're closing + // to avoid an infinite loop in case the tooltip becomes disabled on close + if ( tooltipData.closing ) { + return; + } + + // Clear the interval for delayed tracking tooltips + clearInterval( this.delayedShow ); + + // Only set title if we had one before (see comment in _open()) + // If the title attribute has changed since open(), don't restore + if ( target.data( "ui-tooltip-title" ) && !target.attr( "title" ) ) { + target.attr( "title", target.data( "ui-tooltip-title" ) ); + } + + this._removeDescribedBy( target ); + + tooltipData.hiding = true; + tooltip.stop( true ); + this._hide( tooltip, this.options.hide, function() { + that._removeTooltip( $( this ) ); + } ); + + target.removeData( "ui-tooltip-open" ); + this._off( target, "mouseleave focusout keyup" ); + + // Remove 'remove' binding only on delegated targets + if ( target[ 0 ] !== this.element[ 0 ] ) { + this._off( target, "remove" ); + } + this._off( this.document, "mousemove" ); + + if ( event && event.type === "mouseleave" ) { + $.each( this.parents, function( id, parent ) { + $( parent.element ).attr( "title", parent.title ); + delete that.parents[ id ]; + } ); + } + + tooltipData.closing = true; + this._trigger( "close", event, { tooltip: tooltip } ); + if ( !tooltipData.hiding ) { + tooltipData.closing = false; + } + }, + + _tooltip: function( element ) { + var tooltip = $( "<div>" ).attr( "role", "tooltip" ), + content = $( "<div>" ).appendTo( tooltip ), + id = tooltip.uniqueId().attr( "id" ); + + this._addClass( content, "ui-tooltip-content" ); + this._addClass( tooltip, "ui-tooltip", "ui-widget ui-widget-content" ); + + tooltip.appendTo( this._appendTo( element ) ); + + return this.tooltips[ id ] = { + element: element, + tooltip: tooltip + }; + }, + + _find: function( target ) { + var id = target.data( "ui-tooltip-id" ); + return id ? this.tooltips[ id ] : null; + }, + + _removeTooltip: function( tooltip ) { + tooltip.remove(); + delete this.tooltips[ tooltip.attr( "id" ) ]; + }, + + _appendTo: function( target ) { + var element = target.closest( ".ui-front, dialog" ); + + if ( !element.length ) { + element = this.document[ 0 ].body; + } + + return element; + }, + + _destroy: function() { + var that = this; + + // Close open tooltips + $.each( this.tooltips, function( id, tooltipData ) { + + // Delegate to close method to handle common cleanup + var event = $.Event( "blur" ), + element = tooltipData.element; + event.target = event.currentTarget = element[ 0 ]; + that.close( event, true ); + + // Remove immediately; destroying an open tooltip doesn't use the + // hide animation + $( "#" + id ).remove(); + + // Restore the title + if ( element.data( "ui-tooltip-title" ) ) { + + // If the title attribute has changed since open(), don't restore + if ( !element.attr( "title" ) ) { + element.attr( "title", element.data( "ui-tooltip-title" ) ); + } + element.removeData( "ui-tooltip-title" ); + } + } ); + this.liveRegion.remove(); + } +} ); + +// DEPRECATED +// TODO: Switch return back to widget declaration at top of file when this is removed +if ( $.uiBackCompat !== false ) { + + // Backcompat for tooltipClass option + $.widget( "ui.tooltip", $.ui.tooltip, { + options: { + tooltipClass: null + }, + _tooltip: function() { + var tooltipData = this._superApply( arguments ); + if ( this.options.tooltipClass ) { + tooltipData.tooltip.addClass( this.options.tooltipClass ); + } + return tooltipData; + } + } ); +} + +var widgetsTooltip = $.ui.tooltip; + + + + +})); \ No newline at end of file diff --git a/src/legacy/design-studio/js/jquery.dataTables.js b/src/legacy/design-studio/js/jquery.dataTables.js new file mode 100644 index 0000000..ddf1539 --- /dev/null +++ b/src/legacy/design-studio/js/jquery.dataTables.js @@ -0,0 +1,15307 @@ +/*! DataTables 1.10.13 + * ©2008-2016 SpryMedia Ltd - datatables.net/license + */ + +/** + * @summary DataTables + * @description Paginate, search and order HTML tables + * @version 1.10.13 + * @file jquery.dataTables.js + * @author SpryMedia Ltd + * @contact www.datatables.net + * @copyright Copyright 2008-2016 SpryMedia Ltd. + * + * This source file is free software, available under the following license: + * MIT license - http://datatables.net/license + * + * This source file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details. + * + * For details please refer to: http://www.datatables.net + */ + +/*jslint evil: true, undef: true, browser: true */ +/*globals $,require,jQuery,define,_selector_run,_selector_opts,_selector_first,_selector_row_indexes,_ext,_Api,_api_register,_api_registerPlural,_re_new_lines,_re_html,_re_formatted_numeric,_re_escape_regex,_empty,_intVal,_numToDecimal,_isNumber,_isHtml,_htmlNumeric,_pluck,_pluck_order,_range,_stripHtml,_unique,_fnBuildAjax,_fnAjaxUpdate,_fnAjaxParameters,_fnAjaxUpdateDraw,_fnAjaxDataSrc,_fnAddColumn,_fnColumnOptions,_fnAdjustColumnSizing,_fnVisibleToColumnIndex,_fnColumnIndexToVisible,_fnVisbleColumns,_fnGetColumns,_fnColumnTypes,_fnApplyColumnDefs,_fnHungarianMap,_fnCamelToHungarian,_fnLanguageCompat,_fnBrowserDetect,_fnAddData,_fnAddTr,_fnNodeToDataIndex,_fnNodeToColumnIndex,_fnGetCellData,_fnSetCellData,_fnSplitObjNotation,_fnGetObjectDataFn,_fnSetObjectDataFn,_fnGetDataMaster,_fnClearTable,_fnDeleteIndex,_fnInvalidate,_fnGetRowElements,_fnCreateTr,_fnBuildHead,_fnDrawHead,_fnDraw,_fnReDraw,_fnAddOptionsHtml,_fnDetectHeader,_fnGetUniqueThs,_fnFeatureHtmlFilter,_fnFilterComplete,_fnFilterCustom,_fnFilterColumn,_fnFilter,_fnFilterCreateSearch,_fnEscapeRegex,_fnFilterData,_fnFeatureHtmlInfo,_fnUpdateInfo,_fnInfoMacros,_fnInitialise,_fnInitComplete,_fnLengthChange,_fnFeatureHtmlLength,_fnFeatureHtmlPaginate,_fnPageChange,_fnFeatureHtmlProcessing,_fnProcessingDisplay,_fnFeatureHtmlTable,_fnScrollDraw,_fnApplyToChildren,_fnCalculateColumnWidths,_fnThrottle,_fnConvertToWidth,_fnGetWidestNode,_fnGetMaxLenString,_fnStringToCss,_fnSortFlatten,_fnSort,_fnSortAria,_fnSortListener,_fnSortAttachListener,_fnSortingClasses,_fnSortData,_fnSaveState,_fnLoadState,_fnSettingsFromNode,_fnLog,_fnMap,_fnBindAction,_fnCallbackReg,_fnCallbackFire,_fnLengthOverflow,_fnRenderer,_fnDataSource,_fnRowAttributes*/ + +(function( factory ) { + "use strict"; + + if ( typeof define === 'function' && define.amd ) { + // AMD + define( ['jquery'], function ( $ ) { + return factory( $, window, document ); + } ); + } + else if ( typeof exports === 'object' ) { + // CommonJS + module.exports = function (root, $) { + if ( ! root ) { + // CommonJS environments without a window global must pass a + // root. This will give an error otherwise + root = window; + } + + if ( ! $ ) { + $ = typeof window !== 'undefined' ? // jQuery's factory checks for a global window + require('jquery') : + require('jquery')( root ); + } + + return factory( $, root, root.document ); + }; + } + else { + // Browser + factory( jQuery, window, document ); + } +} +(function( $, window, document, undefined ) { + "use strict"; + + /** + * DataTables is a plug-in for the jQuery Javascript library. It is a highly + * flexible tool, based upon the foundations of progressive enhancement, + * which will add advanced interaction controls to any HTML table. For a + * full list of features please refer to + * [DataTables.net](href="http://datatables.net). + * + * Note that the `DataTable` object is not a global variable but is aliased + * to `jQuery.fn.DataTable` and `jQuery.fn.dataTable` through which it may + * be accessed. + * + * @class + * @param {object} [init={}] Configuration object for DataTables. Options + * are defined by {@link DataTable.defaults} + * @requires jQuery 1.7+ + * + * @example + * // Basic initialisation + * $(document).ready( function { + * $('#example').dataTable(); + * } ); + * + * @example + * // Initialisation with configuration options - in this case, disable + * // pagination and sorting. + * $(document).ready( function { + * $('#example').dataTable( { + * "paginate": false, + * "sort": false + * } ); + * } ); + */ + var DataTable = function ( options ) + { + /** + * Perform a jQuery selector action on the table's TR elements (from the tbody) and + * return the resulting jQuery object. + * @param {string|node|jQuery} sSelector jQuery selector or node collection to act on + * @param {object} [oOpts] Optional parameters for modifying the rows to be included + * @param {string} [oOpts.filter=none] Select TR elements that meet the current filter + * criterion ("applied") or all TR elements (i.e. no filter). + * @param {string} [oOpts.order=current] Order of the TR elements in the processed array. + * Can be either 'current', whereby the current sorting of the table is used, or + * 'original' whereby the original order the data was read into the table is used. + * @param {string} [oOpts.page=all] Limit the selection to the currently displayed page + * ("current") or not ("all"). If 'current' is given, then order is assumed to be + * 'current' and filter is 'applied', regardless of what they might be given as. + * @returns {object} jQuery object, filtered by the given selector. + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * + * // Highlight every second row + * oTable.$('tr:odd').css('backgroundColor', 'blue'); + * } ); + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * + * // Filter to rows with 'Webkit' in them, add a background colour and then + * // remove the filter, thus highlighting the 'Webkit' rows only. + * oTable.fnFilter('Webkit'); + * oTable.$('tr', {"search": "applied"}).css('backgroundColor', 'blue'); + * oTable.fnFilter(''); + * } ); + */ + this.$ = function ( sSelector, oOpts ) + { + return this.api(true).$( sSelector, oOpts ); + }; + + + /** + * Almost identical to $ in operation, but in this case returns the data for the matched + * rows - as such, the jQuery selector used should match TR row nodes or TD/TH cell nodes + * rather than any descendants, so the data can be obtained for the row/cell. If matching + * rows are found, the data returned is the original data array/object that was used to + * create the row (or a generated array if from a DOM source). + * + * This method is often useful in-combination with $ where both functions are given the + * same parameters and the array indexes will match identically. + * @param {string|node|jQuery} sSelector jQuery selector or node collection to act on + * @param {object} [oOpts] Optional parameters for modifying the rows to be included + * @param {string} [oOpts.filter=none] Select elements that meet the current filter + * criterion ("applied") or all elements (i.e. no filter). + * @param {string} [oOpts.order=current] Order of the data in the processed array. + * Can be either 'current', whereby the current sorting of the table is used, or + * 'original' whereby the original order the data was read into the table is used. + * @param {string} [oOpts.page=all] Limit the selection to the currently displayed page + * ("current") or not ("all"). If 'current' is given, then order is assumed to be + * 'current' and filter is 'applied', regardless of what they might be given as. + * @returns {array} Data for the matched elements. If any elements, as a result of the + * selector, were not TR, TD or TH elements in the DataTable, they will have a null + * entry in the array. + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * + * // Get the data from the first row in the table + * var data = oTable._('tr:first'); + * + * // Do something useful with the data + * alert( "First cell is: "+data[0] ); + * } ); + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * + * // Filter to 'Webkit' and get all data for + * oTable.fnFilter('Webkit'); + * var data = oTable._('tr', {"search": "applied"}); + * + * // Do something with the data + * alert( data.length+" rows matched the search" ); + * } ); + */ + this._ = function ( sSelector, oOpts ) + { + return this.api(true).rows( sSelector, oOpts ).data(); + }; + + + /** + * Create a DataTables Api instance, with the currently selected tables for + * the Api's context. + * @param {boolean} [traditional=false] Set the API instance's context to be + * only the table referred to by the `DataTable.ext.iApiIndex` option, as was + * used in the API presented by DataTables 1.9- (i.e. the traditional mode), + * or if all tables captured in the jQuery object should be used. + * @return {DataTables.Api} + */ + this.api = function ( traditional ) + { + return traditional ? + new _Api( + _fnSettingsFromNode( this[ _ext.iApiIndex ] ) + ) : + new _Api( this ); + }; + + + /** + * Add a single new row or multiple rows of data to the table. Please note + * that this is suitable for client-side processing only - if you are using + * server-side processing (i.e. "bServerSide": true), then to add data, you + * must add it to the data source, i.e. the server-side, through an Ajax call. + * @param {array|object} data The data to be added to the table. This can be: + * <ul> + * <li>1D array of data - add a single row with the data provided</li> + * <li>2D array of arrays - add multiple rows in a single call</li> + * <li>object - data object when using <i>mData</i></li> + * <li>array of objects - multiple data objects when using <i>mData</i></li> + * </ul> + * @param {bool} [redraw=true] redraw the table or not + * @returns {array} An array of integers, representing the list of indexes in + * <i>aoData</i> ({@link DataTable.models.oSettings}) that have been added to + * the table. + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * // Global var for counter + * var giCount = 2; + * + * $(document).ready(function() { + * $('#example').dataTable(); + * } ); + * + * function fnClickAddRow() { + * $('#example').dataTable().fnAddData( [ + * giCount+".1", + * giCount+".2", + * giCount+".3", + * giCount+".4" ] + * ); + * + * giCount++; + * } + */ + this.fnAddData = function( data, redraw ) + { + var api = this.api( true ); + + /* Check if we want to add multiple rows or not */ + var rows = $.isArray(data) && ( $.isArray(data[0]) || $.isPlainObject(data[0]) ) ? + api.rows.add( data ) : + api.row.add( data ); + + if ( redraw === undefined || redraw ) { + api.draw(); + } + + return rows.flatten().toArray(); + }; + + + /** + * This function will make DataTables recalculate the column sizes, based on the data + * contained in the table and the sizes applied to the columns (in the DOM, CSS or + * through the sWidth parameter). This can be useful when the width of the table's + * parent element changes (for example a window resize). + * @param {boolean} [bRedraw=true] Redraw the table or not, you will typically want to + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable( { + * "sScrollY": "200px", + * "bPaginate": false + * } ); + * + * $(window).on('resize', function () { + * oTable.fnAdjustColumnSizing(); + * } ); + * } ); + */ + this.fnAdjustColumnSizing = function ( bRedraw ) + { + var api = this.api( true ).columns.adjust(); + var settings = api.settings()[0]; + var scroll = settings.oScroll; + + if ( bRedraw === undefined || bRedraw ) { + api.draw( false ); + } + else if ( scroll.sX !== "" || scroll.sY !== "" ) { + /* If not redrawing, but scrolling, we want to apply the new column sizes anyway */ + _fnScrollDraw( settings ); + } + }; + + + /** + * Quickly and simply clear a table + * @param {bool} [bRedraw=true] redraw the table or not + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * + * // Immediately 'nuke' the current rows (perhaps waiting for an Ajax callback...) + * oTable.fnClearTable(); + * } ); + */ + this.fnClearTable = function( bRedraw ) + { + var api = this.api( true ).clear(); + + if ( bRedraw === undefined || bRedraw ) { + api.draw(); + } + }; + + + /** + * The exact opposite of 'opening' a row, this function will close any rows which + * are currently 'open'. + * @param {node} nTr the table row to 'close' + * @returns {int} 0 on success, or 1 if failed (can't find the row) + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable; + * + * // 'open' an information row when a row is clicked on + * $('#example tbody tr').click( function () { + * if ( oTable.fnIsOpen(this) ) { + * oTable.fnClose( this ); + * } else { + * oTable.fnOpen( this, "Temporary row opened", "info_row" ); + * } + * } ); + * + * oTable = $('#example').dataTable(); + * } ); + */ + this.fnClose = function( nTr ) + { + this.api( true ).row( nTr ).child.hide(); + }; + + + /** + * Remove a row for the table + * @param {mixed} target The index of the row from aoData to be deleted, or + * the TR element you want to delete + * @param {function|null} [callBack] Callback function + * @param {bool} [redraw=true] Redraw the table or not + * @returns {array} The row that was deleted + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * + * // Immediately remove the first row + * oTable.fnDeleteRow( 0 ); + * } ); + */ + this.fnDeleteRow = function( target, callback, redraw ) + { + var api = this.api( true ); + var rows = api.rows( target ); + var settings = rows.settings()[0]; + var data = settings.aoData[ rows[0][0] ]; + + rows.remove(); + + if ( callback ) { + callback.call( this, settings, data ); + } + + if ( redraw === undefined || redraw ) { + api.draw(); + } + + return data; + }; + + + /** + * Restore the table to it's original state in the DOM by removing all of DataTables + * enhancements, alterations to the DOM structure of the table and event listeners. + * @param {boolean} [remove=false] Completely remove the table from the DOM + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * // This example is fairly pointless in reality, but shows how fnDestroy can be used + * var oTable = $('#example').dataTable(); + * oTable.fnDestroy(); + * } ); + */ + this.fnDestroy = function ( remove ) + { + this.api( true ).destroy( remove ); + }; + + + /** + * Redraw the table + * @param {bool} [complete=true] Re-filter and resort (if enabled) the table before the draw. + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * + * // Re-draw the table - you wouldn't want to do it here, but it's an example :-) + * oTable.fnDraw(); + * } ); + */ + this.fnDraw = function( complete ) + { + // Note that this isn't an exact match to the old call to _fnDraw - it takes + // into account the new data, but can hold position. + this.api( true ).draw( complete ); + }; + + + /** + * Filter the input based on data + * @param {string} sInput String to filter the table on + * @param {int|null} [iColumn] Column to limit filtering to + * @param {bool} [bRegex=false] Treat as regular expression or not + * @param {bool} [bSmart=true] Perform smart filtering or not + * @param {bool} [bShowGlobal=true] Show the input global filter in it's input box(es) + * @param {bool} [bCaseInsensitive=true] Do case-insensitive matching (true) or not (false) + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * + * // Sometime later - filter... + * oTable.fnFilter( 'test string' ); + * } ); + */ + this.fnFilter = function( sInput, iColumn, bRegex, bSmart, bShowGlobal, bCaseInsensitive ) + { + var api = this.api( true ); + + if ( iColumn === null || iColumn === undefined ) { + api.search( sInput, bRegex, bSmart, bCaseInsensitive ); + } + else { + api.column( iColumn ).search( sInput, bRegex, bSmart, bCaseInsensitive ); + } + + api.draw(); + }; + + + /** + * Get the data for the whole table, an individual row or an individual cell based on the + * provided parameters. + * @param {int|node} [src] A TR row node, TD/TH cell node or an integer. If given as + * a TR node then the data source for the whole row will be returned. If given as a + * TD/TH cell node then iCol will be automatically calculated and the data for the + * cell returned. If given as an integer, then this is treated as the aoData internal + * data index for the row (see fnGetPosition) and the data for that row used. + * @param {int} [col] Optional column index that you want the data of. + * @returns {array|object|string} If mRow is undefined, then the data for all rows is + * returned. If mRow is defined, just data for that row, and is iCol is + * defined, only data for the designated cell is returned. + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * // Row data + * $(document).ready(function() { + * oTable = $('#example').dataTable(); + * + * oTable.$('tr').click( function () { + * var data = oTable.fnGetData( this ); + * // ... do something with the array / object of data for the row + * } ); + * } ); + * + * @example + * // Individual cell data + * $(document).ready(function() { + * oTable = $('#example').dataTable(); + * + * oTable.$('td').click( function () { + * var sData = oTable.fnGetData( this ); + * alert( 'The cell clicked on had the value of '+sData ); + * } ); + * } ); + */ + this.fnGetData = function( src, col ) + { + var api = this.api( true ); + + if ( src !== undefined ) { + var type = src.nodeName ? src.nodeName.toLowerCase() : ''; + + return col !== undefined || type == 'td' || type == 'th' ? + api.cell( src, col ).data() : + api.row( src ).data() || null; + } + + return api.data().toArray(); + }; + + + /** + * Get an array of the TR nodes that are used in the table's body. Note that you will + * typically want to use the '$' API method in preference to this as it is more + * flexible. + * @param {int} [iRow] Optional row index for the TR element you want + * @returns {array|node} If iRow is undefined, returns an array of all TR elements + * in the table's body, or iRow is defined, just the TR element requested. + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * + * // Get the nodes from the table + * var nNodes = oTable.fnGetNodes( ); + * } ); + */ + this.fnGetNodes = function( iRow ) + { + var api = this.api( true ); + + return iRow !== undefined ? + api.row( iRow ).node() : + api.rows().nodes().flatten().toArray(); + }; + + + /** + * Get the array indexes of a particular cell from it's DOM element + * and column index including hidden columns + * @param {node} node this can either be a TR, TD or TH in the table's body + * @returns {int} If nNode is given as a TR, then a single index is returned, or + * if given as a cell, an array of [row index, column index (visible), + * column index (all)] is given. + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * $('#example tbody td').click( function () { + * // Get the position of the current data from the node + * var aPos = oTable.fnGetPosition( this ); + * + * // Get the data array for this row + * var aData = oTable.fnGetData( aPos[0] ); + * + * // Update the data array and return the value + * aData[ aPos[1] ] = 'clicked'; + * this.innerHTML = 'clicked'; + * } ); + * + * // Init DataTables + * oTable = $('#example').dataTable(); + * } ); + */ + this.fnGetPosition = function( node ) + { + var api = this.api( true ); + var nodeName = node.nodeName.toUpperCase(); + + if ( nodeName == 'TR' ) { + return api.row( node ).index(); + } + else if ( nodeName == 'TD' || nodeName == 'TH' ) { + var cell = api.cell( node ).index(); + + return [ + cell.row, + cell.columnVisible, + cell.column + ]; + } + return null; + }; + + + /** + * Check to see if a row is 'open' or not. + * @param {node} nTr the table row to check + * @returns {boolean} true if the row is currently open, false otherwise + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable; + * + * // 'open' an information row when a row is clicked on + * $('#example tbody tr').click( function () { + * if ( oTable.fnIsOpen(this) ) { + * oTable.fnClose( this ); + * } else { + * oTable.fnOpen( this, "Temporary row opened", "info_row" ); + * } + * } ); + * + * oTable = $('#example').dataTable(); + * } ); + */ + this.fnIsOpen = function( nTr ) + { + return this.api( true ).row( nTr ).child.isShown(); + }; + + + /** + * This function will place a new row directly after a row which is currently + * on display on the page, with the HTML contents that is passed into the + * function. This can be used, for example, to ask for confirmation that a + * particular record should be deleted. + * @param {node} nTr The table row to 'open' + * @param {string|node|jQuery} mHtml The HTML to put into the row + * @param {string} sClass Class to give the new TD cell + * @returns {node} The row opened. Note that if the table row passed in as the + * first parameter, is not found in the table, this method will silently + * return. + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable; + * + * // 'open' an information row when a row is clicked on + * $('#example tbody tr').click( function () { + * if ( oTable.fnIsOpen(this) ) { + * oTable.fnClose( this ); + * } else { + * oTable.fnOpen( this, "Temporary row opened", "info_row" ); + * } + * } ); + * + * oTable = $('#example').dataTable(); + * } ); + */ + this.fnOpen = function( nTr, mHtml, sClass ) + { + return this.api( true ) + .row( nTr ) + .child( mHtml, sClass ) + .show() + .child()[0]; + }; + + + /** + * Change the pagination - provides the internal logic for pagination in a simple API + * function. With this function you can have a DataTables table go to the next, + * previous, first or last pages. + * @param {string|int} mAction Paging action to take: "first", "previous", "next" or "last" + * or page number to jump to (integer), note that page 0 is the first page. + * @param {bool} [bRedraw=true] Redraw the table or not + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * oTable.fnPageChange( 'next' ); + * } ); + */ + this.fnPageChange = function ( mAction, bRedraw ) + { + var api = this.api( true ).page( mAction ); + + if ( bRedraw === undefined || bRedraw ) { + api.draw(false); + } + }; + + + /** + * Show a particular column + * @param {int} iCol The column whose display should be changed + * @param {bool} bShow Show (true) or hide (false) the column + * @param {bool} [bRedraw=true] Redraw the table or not + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * + * // Hide the second column after initialisation + * oTable.fnSetColumnVis( 1, false ); + * } ); + */ + this.fnSetColumnVis = function ( iCol, bShow, bRedraw ) + { + var api = this.api( true ).column( iCol ).visible( bShow ); + + if ( bRedraw === undefined || bRedraw ) { + api.columns.adjust().draw(); + } + }; + + + /** + * Get the settings for a particular table for external manipulation + * @returns {object} DataTables settings object. See + * {@link DataTable.models.oSettings} + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * var oSettings = oTable.fnSettings(); + * + * // Show an example parameter from the settings + * alert( oSettings._iDisplayStart ); + * } ); + */ + this.fnSettings = function() + { + return _fnSettingsFromNode( this[_ext.iApiIndex] ); + }; + + + /** + * Sort the table by a particular column + * @param {int} iCol the data index to sort on. Note that this will not match the + * 'display index' if you have hidden data entries + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * + * // Sort immediately with columns 0 and 1 + * oTable.fnSort( [ [0,'asc'], [1,'asc'] ] ); + * } ); + */ + this.fnSort = function( aaSort ) + { + this.api( true ).order( aaSort ).draw(); + }; + + + /** + * Attach a sort listener to an element for a given column + * @param {node} nNode the element to attach the sort listener to + * @param {int} iColumn the column that a click on this node will sort on + * @param {function} [fnCallback] callback function when sort is run + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * + * // Sort on column 1, when 'sorter' is clicked on + * oTable.fnSortListener( document.getElementById('sorter'), 1 ); + * } ); + */ + this.fnSortListener = function( nNode, iColumn, fnCallback ) + { + this.api( true ).order.listener( nNode, iColumn, fnCallback ); + }; + + + /** + * Update a table cell or row - this method will accept either a single value to + * update the cell with, an array of values with one element for each column or + * an object in the same format as the original data source. The function is + * self-referencing in order to make the multi column updates easier. + * @param {object|array|string} mData Data to update the cell/row with + * @param {node|int} mRow TR element you want to update or the aoData index + * @param {int} [iColumn] The column to update, give as null or undefined to + * update a whole row. + * @param {bool} [bRedraw=true] Redraw the table or not + * @param {bool} [bAction=true] Perform pre-draw actions or not + * @returns {int} 0 on success, 1 on error + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * oTable.fnUpdate( 'Example update', 0, 0 ); // Single cell + * oTable.fnUpdate( ['a', 'b', 'c', 'd', 'e'], $('tbody tr')[0] ); // Row + * } ); + */ + this.fnUpdate = function( mData, mRow, iColumn, bRedraw, bAction ) + { + var api = this.api( true ); + + if ( iColumn === undefined || iColumn === null ) { + api.row( mRow ).data( mData ); + } + else { + api.cell( mRow, iColumn ).data( mData ); + } + + if ( bAction === undefined || bAction ) { + api.columns.adjust(); + } + + if ( bRedraw === undefined || bRedraw ) { + api.draw(); + } + return 0; + }; + + + /** + * Provide a common method for plug-ins to check the version of DataTables being used, in order + * to ensure compatibility. + * @param {string} sVersion Version string to check for, in the format "X.Y.Z". Note that the + * formats "X" and "X.Y" are also acceptable. + * @returns {boolean} true if this version of DataTables is greater or equal to the required + * version, or false if this version of DataTales is not suitable + * @method + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * alert( oTable.fnVersionCheck( '1.9.0' ) ); + * } ); + */ + this.fnVersionCheck = _ext.fnVersionCheck; + + + var _that = this; + var emptyInit = options === undefined; + var len = this.length; + + if ( emptyInit ) { + options = {}; + } + + this.oApi = this.internal = _ext.internal; + + // Extend with old style plug-in API methods + for ( var fn in DataTable.ext.internal ) { + if ( fn ) { + this[fn] = _fnExternApiFunc(fn); + } + } + + this.each(function() { + // For each initialisation we want to give it a clean initialisation + // object that can be bashed around + var o = {}; + var oInit = len > 1 ? // optimisation for single table case + _fnExtend( o, options, true ) : + options; + + /*global oInit,_that,emptyInit*/ + var i=0, iLen, j, jLen, k, kLen; + var sId = this.getAttribute( 'id' ); + var bInitHandedOff = false; + var defaults = DataTable.defaults; + var $this = $(this); + + + /* Sanity check */ + if ( this.nodeName.toLowerCase() != 'table' ) + { + _fnLog( null, 0, 'Non-table node initialisation ('+this.nodeName+')', 2 ); + return; + } + + /* Backwards compatibility for the defaults */ + _fnCompatOpts( defaults ); + _fnCompatCols( defaults.column ); + + /* Convert the camel-case defaults to Hungarian */ + _fnCamelToHungarian( defaults, defaults, true ); + _fnCamelToHungarian( defaults.column, defaults.column, true ); + + /* Setting up the initialisation object */ + _fnCamelToHungarian( defaults, $.extend( oInit, $this.data() ) ); + + + + /* Check to see if we are re-initialising a table */ + var allSettings = DataTable.settings; + for ( i=0, iLen=allSettings.length ; i<iLen ; i++ ) + { + var s = allSettings[i]; + + /* Base check on table node */ + if ( s.nTable == this || s.nTHead.parentNode == this || (s.nTFoot && s.nTFoot.parentNode == this) ) + { + var bRetrieve = oInit.bRetrieve !== undefined ? oInit.bRetrieve : defaults.bRetrieve; + var bDestroy = oInit.bDestroy !== undefined ? oInit.bDestroy : defaults.bDestroy; + + if ( emptyInit || bRetrieve ) + { + return s.oInstance; + } + else if ( bDestroy ) + { + s.oInstance.fnDestroy(); + break; + } + else + { + _fnLog( s, 0, 'Cannot reinitialise DataTable', 3 ); + return; + } + } + + /* If the element we are initialising has the same ID as a table which was previously + * initialised, but the table nodes don't match (from before) then we destroy the old + * instance by simply deleting it. This is under the assumption that the table has been + * destroyed by other methods. Anyone using non-id selectors will need to do this manually + */ + if ( s.sTableId == this.id ) + { + allSettings.splice( i, 1 ); + break; + } + } + + /* Ensure the table has an ID - required for accessibility */ + if ( sId === null || sId === "" ) + { + sId = "DataTables_Table_"+(DataTable.ext._unique++); + this.id = sId; + } + + /* Create the settings object for this table and set some of the default parameters */ + var oSettings = $.extend( true, {}, DataTable.models.oSettings, { + "sDestroyWidth": $this[0].style.width, + "sInstance": sId, + "sTableId": sId + } ); + oSettings.nTable = this; + oSettings.oApi = _that.internal; + oSettings.oInit = oInit; + + allSettings.push( oSettings ); + + // Need to add the instance after the instance after the settings object has been added + // to the settings array, so we can self reference the table instance if more than one + oSettings.oInstance = (_that.length===1) ? _that : $this.dataTable(); + + // Backwards compatibility, before we apply all the defaults + _fnCompatOpts( oInit ); + + if ( oInit.oLanguage ) + { + _fnLanguageCompat( oInit.oLanguage ); + } + + // If the length menu is given, but the init display length is not, use the length menu + if ( oInit.aLengthMenu && ! oInit.iDisplayLength ) + { + oInit.iDisplayLength = $.isArray( oInit.aLengthMenu[0] ) ? + oInit.aLengthMenu[0][0] : oInit.aLengthMenu[0]; + } + + // Apply the defaults and init options to make a single init object will all + // options defined from defaults and instance options. + oInit = _fnExtend( $.extend( true, {}, defaults ), oInit ); + + + // Map the initialisation options onto the settings object + _fnMap( oSettings.oFeatures, oInit, [ + "bPaginate", + "bLengthChange", + "bFilter", + "bSort", + "bSortMulti", + "bInfo", + "bProcessing", + "bAutoWidth", + "bSortClasses", + "bServerSide", + "bDeferRender" + ] ); + _fnMap( oSettings, oInit, [ + "asStripeClasses", + "ajax", + "fnServerData", + "fnFormatNumber", + "sServerMethod", + "aaSorting", + "aaSortingFixed", + "aLengthMenu", + "sPaginationType", + "sAjaxSource", + "sAjaxDataProp", + "iStateDuration", + "sDom", + "bSortCellsTop", + "iTabIndex", + "fnStateLoadCallback", + "fnStateSaveCallback", + "renderer", + "searchDelay", + "rowId", + [ "iCookieDuration", "iStateDuration" ], // backwards compat + [ "oSearch", "oPreviousSearch" ], + [ "aoSearchCols", "aoPreSearchCols" ], + [ "iDisplayLength", "_iDisplayLength" ], + [ "bJQueryUI", "bJUI" ] + ] ); + _fnMap( oSettings.oScroll, oInit, [ + [ "sScrollX", "sX" ], + [ "sScrollXInner", "sXInner" ], + [ "sScrollY", "sY" ], + [ "bScrollCollapse", "bCollapse" ] + ] ); + _fnMap( oSettings.oLanguage, oInit, "fnInfoCallback" ); + + /* Callback functions which are array driven */ + _fnCallbackReg( oSettings, 'aoDrawCallback', oInit.fnDrawCallback, 'user' ); + _fnCallbackReg( oSettings, 'aoServerParams', oInit.fnServerParams, 'user' ); + _fnCallbackReg( oSettings, 'aoStateSaveParams', oInit.fnStateSaveParams, 'user' ); + _fnCallbackReg( oSettings, 'aoStateLoadParams', oInit.fnStateLoadParams, 'user' ); + _fnCallbackReg( oSettings, 'aoStateLoaded', oInit.fnStateLoaded, 'user' ); + _fnCallbackReg( oSettings, 'aoRowCallback', oInit.fnRowCallback, 'user' ); + _fnCallbackReg( oSettings, 'aoRowCreatedCallback', oInit.fnCreatedRow, 'user' ); + _fnCallbackReg( oSettings, 'aoHeaderCallback', oInit.fnHeaderCallback, 'user' ); + _fnCallbackReg( oSettings, 'aoFooterCallback', oInit.fnFooterCallback, 'user' ); + _fnCallbackReg( oSettings, 'aoInitComplete', oInit.fnInitComplete, 'user' ); + _fnCallbackReg( oSettings, 'aoPreDrawCallback', oInit.fnPreDrawCallback, 'user' ); + + oSettings.rowIdFn = _fnGetObjectDataFn( oInit.rowId ); + + /* Browser support detection */ + _fnBrowserDetect( oSettings ); + + var oClasses = oSettings.oClasses; + + // @todo Remove in 1.11 + if ( oInit.bJQueryUI ) + { + /* Use the JUI classes object for display. You could clone the oStdClasses object if + * you want to have multiple tables with multiple independent classes + */ + $.extend( oClasses, DataTable.ext.oJUIClasses, oInit.oClasses ); + + if ( oInit.sDom === defaults.sDom && defaults.sDom === "lfrtip" ) + { + /* Set the DOM to use a layout suitable for jQuery UI's theming */ + oSettings.sDom = '<"H"lfr>t<"F"ip>'; + } + + if ( ! oSettings.renderer ) { + oSettings.renderer = 'jqueryui'; + } + else if ( $.isPlainObject( oSettings.renderer ) && ! oSettings.renderer.header ) { + oSettings.renderer.header = 'jqueryui'; + } + } + else + { + $.extend( oClasses, DataTable.ext.classes, oInit.oClasses ); + } + $this.addClass( oClasses.sTable ); + + + if ( oSettings.iInitDisplayStart === undefined ) + { + /* Display start point, taking into account the save saving */ + oSettings.iInitDisplayStart = oInit.iDisplayStart; + oSettings._iDisplayStart = oInit.iDisplayStart; + } + + if ( oInit.iDeferLoading !== null ) + { + oSettings.bDeferLoading = true; + var tmp = $.isArray( oInit.iDeferLoading ); + oSettings._iRecordsDisplay = tmp ? oInit.iDeferLoading[0] : oInit.iDeferLoading; + oSettings._iRecordsTotal = tmp ? oInit.iDeferLoading[1] : oInit.iDeferLoading; + } + + /* Language definitions */ + var oLanguage = oSettings.oLanguage; + $.extend( true, oLanguage, oInit.oLanguage ); + + if ( oLanguage.sUrl ) + { + /* Get the language definitions from a file - because this Ajax call makes the language + * get async to the remainder of this function we use bInitHandedOff to indicate that + * _fnInitialise will be fired by the returned Ajax handler, rather than the constructor + */ + $.ajax( { + dataType: 'json', + url: oLanguage.sUrl, + success: function ( json ) { + _fnLanguageCompat( json ); + _fnCamelToHungarian( defaults.oLanguage, json ); + $.extend( true, oLanguage, json ); + _fnInitialise( oSettings ); + }, + error: function () { + // Error occurred loading language file, continue on as best we can + _fnInitialise( oSettings ); + } + } ); + bInitHandedOff = true; + } + + /* + * Stripes + */ + if ( oInit.asStripeClasses === null ) + { + oSettings.asStripeClasses =[ + oClasses.sStripeOdd, + oClasses.sStripeEven + ]; + } + + /* Remove row stripe classes if they are already on the table row */ + var stripeClasses = oSettings.asStripeClasses; + var rowOne = $this.children('tbody').find('tr').eq(0); + if ( $.inArray( true, $.map( stripeClasses, function(el, i) { + return rowOne.hasClass(el); + } ) ) !== -1 ) { + $('tbody tr', this).removeClass( stripeClasses.join(' ') ); + oSettings.asDestroyStripes = stripeClasses.slice(); + } + + /* + * Columns + * See if we should load columns automatically or use defined ones + */ + var anThs = []; + var aoColumnsInit; + var nThead = this.getElementsByTagName('thead'); + if ( nThead.length !== 0 ) + { + _fnDetectHeader( oSettings.aoHeader, nThead[0] ); + anThs = _fnGetUniqueThs( oSettings ); + } + + /* If not given a column array, generate one with nulls */ + if ( oInit.aoColumns === null ) + { + aoColumnsInit = []; + for ( i=0, iLen=anThs.length ; i<iLen ; i++ ) + { + aoColumnsInit.push( null ); + } + } + else + { + aoColumnsInit = oInit.aoColumns; + } + + /* Add the columns */ + for ( i=0, iLen=aoColumnsInit.length ; i<iLen ; i++ ) + { + _fnAddColumn( oSettings, anThs ? anThs[i] : null ); + } + + /* Apply the column definitions */ + _fnApplyColumnDefs( oSettings, oInit.aoColumnDefs, aoColumnsInit, function (iCol, oDef) { + _fnColumnOptions( oSettings, iCol, oDef ); + } ); + + /* HTML5 attribute detection - build an mData object automatically if the + * attributes are found + */ + if ( rowOne.length ) { + var a = function ( cell, name ) { + return cell.getAttribute( 'data-'+name ) !== null ? name : null; + }; + + $( rowOne[0] ).children('th, td').each( function (i, cell) { + var col = oSettings.aoColumns[i]; + + if ( col.mData === i ) { + var sort = a( cell, 'sort' ) || a( cell, 'order' ); + var filter = a( cell, 'filter' ) || a( cell, 'search' ); + + if ( sort !== null || filter !== null ) { + col.mData = { + _: i+'.display', + sort: sort !== null ? i+'.@data-'+sort : undefined, + type: sort !== null ? i+'.@data-'+sort : undefined, + filter: filter !== null ? i+'.@data-'+filter : undefined + }; + + _fnColumnOptions( oSettings, i ); + } + } + } ); + } + + var features = oSettings.oFeatures; + var loadedInit = function () { + /* + * Sorting + * @todo For modularisation (1.11) this needs to do into a sort start up handler + */ + + // If aaSorting is not defined, then we use the first indicator in asSorting + // in case that has been altered, so the default sort reflects that option + if ( oInit.aaSorting === undefined ) { + var sorting = oSettings.aaSorting; + for ( i=0, iLen=sorting.length ; i<iLen ; i++ ) { + sorting[i][1] = oSettings.aoColumns[ i ].asSorting[0]; + } + } + + /* Do a first pass on the sorting classes (allows any size changes to be taken into + * account, and also will apply sorting disabled classes if disabled + */ + _fnSortingClasses( oSettings ); + + if ( features.bSort ) { + _fnCallbackReg( oSettings, 'aoDrawCallback', function () { + if ( oSettings.bSorted ) { + var aSort = _fnSortFlatten( oSettings ); + var sortedColumns = {}; + + $.each( aSort, function (i, val) { + sortedColumns[ val.src ] = val.dir; + } ); + + _fnCallbackFire( oSettings, null, 'order', [oSettings, aSort, sortedColumns] ); + _fnSortAria( oSettings ); + } + } ); + } + + _fnCallbackReg( oSettings, 'aoDrawCallback', function () { + if ( oSettings.bSorted || _fnDataSource( oSettings ) === 'ssp' || features.bDeferRender ) { + _fnSortingClasses( oSettings ); + } + }, 'sc' ); + + + /* + * Final init + * Cache the header, body and footer as required, creating them if needed + */ + + // Work around for Webkit bug 83867 - store the caption-side before removing from doc + var captions = $this.children('caption').each( function () { + this._captionSide = $(this).css('caption-side'); + } ); + + var thead = $this.children('thead'); + if ( thead.length === 0 ) { + thead = $('<thead/>').appendTo($this); + } + oSettings.nTHead = thead[0]; + + var tbody = $this.children('tbody'); + if ( tbody.length === 0 ) { + tbody = $('<tbody/>').appendTo($this); + } + oSettings.nTBody = tbody[0]; + + var tfoot = $this.children('tfoot'); + if ( tfoot.length === 0 && captions.length > 0 && (oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "") ) { + // If we are a scrolling table, and no footer has been given, then we need to create + // a tfoot element for the caption element to be appended to + tfoot = $('<tfoot/>').appendTo($this); + } + + if ( tfoot.length === 0 || tfoot.children().length === 0 ) { + $this.addClass( oClasses.sNoFooter ); + } + else if ( tfoot.length > 0 ) { + oSettings.nTFoot = tfoot[0]; + _fnDetectHeader( oSettings.aoFooter, oSettings.nTFoot ); + } + + /* Check if there is data passing into the constructor */ + if ( oInit.aaData ) { + for ( i=0 ; i<oInit.aaData.length ; i++ ) { + _fnAddData( oSettings, oInit.aaData[ i ] ); + } + } + else if ( oSettings.bDeferLoading || _fnDataSource( oSettings ) == 'dom' ) { + /* Grab the data from the page - only do this when deferred loading or no Ajax + * source since there is no point in reading the DOM data if we are then going + * to replace it with Ajax data + */ + _fnAddTr( oSettings, $(oSettings.nTBody).children('tr') ); + } + + /* Copy the data index array */ + oSettings.aiDisplay = oSettings.aiDisplayMaster.slice(); + + /* Initialisation complete - table can be drawn */ + oSettings.bInitialised = true; + + /* Check if we need to initialise the table (it might not have been handed off to the + * language processor) + */ + if ( bInitHandedOff === false ) { + _fnInitialise( oSettings ); + } + }; + + /* Must be done after everything which can be overridden by the state saving! */ + if ( oInit.bStateSave ) + { + features.bStateSave = true; + _fnCallbackReg( oSettings, 'aoDrawCallback', _fnSaveState, 'state_save' ); + _fnLoadState( oSettings, oInit, loadedInit ); + } + else { + loadedInit(); + } + + } ); + _that = null; + return this; + }; + + + /* + * It is useful to have variables which are scoped locally so only the + * DataTables functions can access them and they don't leak into global space. + * At the same time these functions are often useful over multiple files in the + * core and API, so we list, or at least document, all variables which are used + * by DataTables as private variables here. This also ensures that there is no + * clashing of variable names and that they can easily referenced for reuse. + */ + + + // Defined else where + // _selector_run + // _selector_opts + // _selector_first + // _selector_row_indexes + + var _ext; // DataTable.ext + var _Api; // DataTable.Api + var _api_register; // DataTable.Api.register + var _api_registerPlural; // DataTable.Api.registerPlural + + var _re_dic = {}; + var _re_new_lines = /[\r\n]/g; + var _re_html = /<.*?>/g; + + // This is not strict ISO8601 - Date.parse() is quite lax, although + // implementations differ between browsers. + var _re_date = /^\d{2,4}[\.\/\-]\d{1,2}[\.\/\-]\d{1,2}([T ]{1}\d{1,2}[:\.]\d{2}([\.:]\d{2})?)?$/; + + // Escape regular expression special characters + var _re_escape_regex = new RegExp( '(\\' + [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^', '-' ].join('|\\') + ')', 'g' ); + + // http://en.wikipedia.org/wiki/Foreign_exchange_market + // - \u20BD - Russian ruble. + // - \u20a9 - South Korean Won + // - \u20BA - Turkish Lira + // - \u20B9 - Indian Rupee + // - R - Brazil (R$) and South Africa + // - fr - Swiss Franc + // - kr - Swedish krona, Norwegian krone and Danish krone + // - \u2009 is thin space and \u202F is narrow no-break space, both used in many + // standards as thousands separators. + var _re_formatted_numeric = /[',$£€¥%\u2009\u202F\u20BD\u20a9\u20BArfk]/gi; + + + var _empty = function ( d ) { + return !d || d === true || d === '-' ? true : false; + }; + + + var _intVal = function ( s ) { + var integer = parseInt( s, 10 ); + return !isNaN(integer) && isFinite(s) ? integer : null; + }; + + // Convert from a formatted number with characters other than `.` as the + // decimal place, to a Javascript number + var _numToDecimal = function ( num, decimalPoint ) { + // Cache created regular expressions for speed as this function is called often + if ( ! _re_dic[ decimalPoint ] ) { + _re_dic[ decimalPoint ] = new RegExp( _fnEscapeRegex( decimalPoint ), 'g' ); + } + return typeof num === 'string' && decimalPoint !== '.' ? + num.replace( /\./g, '' ).replace( _re_dic[ decimalPoint ], '.' ) : + num; + }; + + + var _isNumber = function ( d, decimalPoint, formatted ) { + var strType = typeof d === 'string'; + + // If empty return immediately so there must be a number if it is a + // formatted string (this stops the string "k", or "kr", etc being detected + // as a formatted number for currency + if ( _empty( d ) ) { + return true; + } + + if ( decimalPoint && strType ) { + d = _numToDecimal( d, decimalPoint ); + } + + if ( formatted && strType ) { + d = d.replace( _re_formatted_numeric, '' ); + } + + return !isNaN( parseFloat(d) ) && isFinite( d ); + }; + + + // A string without HTML in it can be considered to be HTML still + var _isHtml = function ( d ) { + return _empty( d ) || typeof d === 'string'; + }; + + + var _htmlNumeric = function ( d, decimalPoint, formatted ) { + if ( _empty( d ) ) { + return true; + } + + var html = _isHtml( d ); + return ! html ? + null : + _isNumber( _stripHtml( d ), decimalPoint, formatted ) ? + true : + null; + }; + + + var _pluck = function ( a, prop, prop2 ) { + var out = []; + var i=0, ien=a.length; + + // Could have the test in the loop for slightly smaller code, but speed + // is essential here + if ( prop2 !== undefined ) { + for ( ; i<ien ; i++ ) { + if ( a[i] && a[i][ prop ] ) { + out.push( a[i][ prop ][ prop2 ] ); + } + } + } + else { + for ( ; i<ien ; i++ ) { + if ( a[i] ) { + out.push( a[i][ prop ] ); + } + } + } + + return out; + }; + + + // Basically the same as _pluck, but rather than looping over `a` we use `order` + // as the indexes to pick from `a` + var _pluck_order = function ( a, order, prop, prop2 ) + { + var out = []; + var i=0, ien=order.length; + + // Could have the test in the loop for slightly smaller code, but speed + // is essential here + if ( prop2 !== undefined ) { + for ( ; i<ien ; i++ ) { + if ( a[ order[i] ][ prop ] ) { + out.push( a[ order[i] ][ prop ][ prop2 ] ); + } + } + } + else { + for ( ; i<ien ; i++ ) { + out.push( a[ order[i] ][ prop ] ); + } + } + + return out; + }; + + + var _range = function ( len, start ) + { + var out = []; + var end; + + if ( start === undefined ) { + start = 0; + end = len; + } + else { + end = start; + start = len; + } + + for ( var i=start ; i<end ; i++ ) { + out.push( i ); + } + + return out; + }; + + + var _removeEmpty = function ( a ) + { + var out = []; + + for ( var i=0, ien=a.length ; i<ien ; i++ ) { + if ( a[i] ) { // careful - will remove all falsy values! + out.push( a[i] ); + } + } + + return out; + }; + + + var _stripHtml = function ( d ) { + return d.replace( _re_html, '' ); + }; + + + /** + * Find the unique elements in a source array. + * + * @param {array} src Source array + * @return {array} Array of unique items + * @ignore + */ + var _unique = function ( src ) + { + // A faster unique method is to use object keys to identify used values, + // but this doesn't work with arrays or objects, which we must also + // consider. See jsperf.com/compare-array-unique-versions/4 for more + // information. + var + out = [], + val, + i, ien=src.length, + j, k=0; + + again: for ( i=0 ; i<ien ; i++ ) { + val = src[i]; + + for ( j=0 ; j<k ; j++ ) { + if ( out[j] === val ) { + continue again; + } + } + + out.push( val ); + k++; + } + + return out; + }; + + + /** + * DataTables utility methods + * + * This namespace provides helper methods that DataTables uses internally to + * create a DataTable, but which are not exclusively used only for DataTables. + * These methods can be used by extension authors to save the duplication of + * code. + * + * @namespace + */ + DataTable.util = { + /** + * Throttle the calls to a function. Arguments and context are maintained + * for the throttled function. + * + * @param {function} fn Function to be called + * @param {integer} freq Call frequency in mS + * @return {function} Wrapped function + */ + throttle: function ( fn, freq ) { + var + frequency = freq !== undefined ? freq : 200, + last, + timer; + + return function () { + var + that = this, + now = +new Date(), + args = arguments; + + if ( last && now < last + frequency ) { + clearTimeout( timer ); + + timer = setTimeout( function () { + last = undefined; + fn.apply( that, args ); + }, frequency ); + } + else { + last = now; + fn.apply( that, args ); + } + }; + }, + + + /** + * Escape a string such that it can be used in a regular expression + * + * @param {string} val string to escape + * @returns {string} escaped string + */ + escapeRegex: function ( val ) { + return val.replace( _re_escape_regex, '\\$1' ); + } + }; + + + + /** + * Create a mapping object that allows camel case parameters to be looked up + * for their Hungarian counterparts. The mapping is stored in a private + * parameter called `_hungarianMap` which can be accessed on the source object. + * @param {object} o + * @memberof DataTable#oApi + */ + function _fnHungarianMap ( o ) + { + var + hungarian = 'a aa ai ao as b fn i m o s ', + match, + newKey, + map = {}; + + $.each( o, function (key, val) { + match = key.match(/^([^A-Z]+?)([A-Z])/); + + if ( match && hungarian.indexOf(match[1]+' ') !== -1 ) + { + newKey = key.replace( match[0], match[2].toLowerCase() ); + map[ newKey ] = key; + + if ( match[1] === 'o' ) + { + _fnHungarianMap( o[key] ); + } + } + } ); + + o._hungarianMap = map; + } + + + /** + * Convert from camel case parameters to Hungarian, based on a Hungarian map + * created by _fnHungarianMap. + * @param {object} src The model object which holds all parameters that can be + * mapped. + * @param {object} user The object to convert from camel case to Hungarian. + * @param {boolean} force When set to `true`, properties which already have a + * Hungarian value in the `user` object will be overwritten. Otherwise they + * won't be. + * @memberof DataTable#oApi + */ + function _fnCamelToHungarian ( src, user, force ) + { + if ( ! src._hungarianMap ) { + _fnHungarianMap( src ); + } + + var hungarianKey; + + $.each( user, function (key, val) { + hungarianKey = src._hungarianMap[ key ]; + + if ( hungarianKey !== undefined && (force || user[hungarianKey] === undefined) ) + { + // For objects, we need to buzz down into the object to copy parameters + if ( hungarianKey.charAt(0) === 'o' ) + { + // Copy the camelCase options over to the hungarian + if ( ! user[ hungarianKey ] ) { + user[ hungarianKey ] = {}; + } + $.extend( true, user[hungarianKey], user[key] ); + + _fnCamelToHungarian( src[hungarianKey], user[hungarianKey], force ); + } + else { + user[hungarianKey] = user[ key ]; + } + } + } ); + } + + + /** + * Language compatibility - when certain options are given, and others aren't, we + * need to duplicate the values over, in order to provide backwards compatibility + * with older language files. + * @param {object} oSettings dataTables settings object + * @memberof DataTable#oApi + */ + function _fnLanguageCompat( lang ) + { + var defaults = DataTable.defaults.oLanguage; + var zeroRecords = lang.sZeroRecords; + + /* Backwards compatibility - if there is no sEmptyTable given, then use the same as + * sZeroRecords - assuming that is given. + */ + if ( ! lang.sEmptyTable && zeroRecords && + defaults.sEmptyTable === "No Results Found" ) + { + _fnMap( lang, lang, 'sZeroRecords', 'sEmptyTable' ); + } + + /* Likewise with loading records */ + if ( ! lang.sLoadingRecords && zeroRecords && + defaults.sLoadingRecords === "Loading..." ) + { + _fnMap( lang, lang, 'sZeroRecords', 'sLoadingRecords' ); + } + + // Old parameter name of the thousands separator mapped onto the new + if ( lang.sInfoThousands ) { + lang.sThousands = lang.sInfoThousands; + } + + var decimal = lang.sDecimal; + if ( decimal ) { + _addNumericSort( decimal ); + } + } + + + /** + * Map one parameter onto another + * @param {object} o Object to map + * @param {*} knew The new parameter name + * @param {*} old The old parameter name + */ + var _fnCompatMap = function ( o, knew, old ) { + if ( o[ knew ] !== undefined ) { + o[ old ] = o[ knew ]; + } + }; + + + /** + * Provide backwards compatibility for the main DT options. Note that the new + * options are mapped onto the old parameters, so this is an external interface + * change only. + * @param {object} init Object to map + */ + function _fnCompatOpts ( init ) + { + _fnCompatMap( init, 'ordering', 'bSort' ); + _fnCompatMap( init, 'orderMulti', 'bSortMulti' ); + _fnCompatMap( init, 'orderClasses', 'bSortClasses' ); + _fnCompatMap( init, 'orderCellsTop', 'bSortCellsTop' ); + _fnCompatMap( init, 'order', 'aaSorting' ); + _fnCompatMap( init, 'orderFixed', 'aaSortingFixed' ); + _fnCompatMap( init, 'paging', 'bPaginate' ); + _fnCompatMap( init, 'pagingType', 'sPaginationType' ); + _fnCompatMap( init, 'pageLength', 'iDisplayLength' ); + _fnCompatMap( init, 'searching', 'bFilter' ); + + // Boolean initialisation of x-scrolling + if ( typeof init.sScrollX === 'boolean' ) { + init.sScrollX = init.sScrollX ? '100%' : ''; + } + if ( typeof init.scrollX === 'boolean' ) { + init.scrollX = init.scrollX ? '100%' : ''; + } + + // Column search objects are in an array, so it needs to be converted + // element by element + var searchCols = init.aoSearchCols; + + if ( searchCols ) { + for ( var i=0, ien=searchCols.length ; i<ien ; i++ ) { + if ( searchCols[i] ) { + _fnCamelToHungarian( DataTable.models.oSearch, searchCols[i] ); + } + } + } + } + + + /** + * Provide backwards compatibility for column options. Note that the new options + * are mapped onto the old parameters, so this is an external interface change + * only. + * @param {object} init Object to map + */ + function _fnCompatCols ( init ) + { + _fnCompatMap( init, 'orderable', 'bSortable' ); + _fnCompatMap( init, 'orderData', 'aDataSort' ); + _fnCompatMap( init, 'orderSequence', 'asSorting' ); + _fnCompatMap( init, 'orderDataType', 'sortDataType' ); + + // orderData can be given as an integer + var dataSort = init.aDataSort; + if ( dataSort && ! $.isArray( dataSort ) ) { + init.aDataSort = [ dataSort ]; + } + } + + + /** + * Browser feature detection for capabilities, quirks + * @param {object} settings dataTables settings object + * @memberof DataTable#oApi + */ + function _fnBrowserDetect( settings ) + { + // We don't need to do this every time DataTables is constructed, the values + // calculated are specific to the browser and OS configuration which we + // don't expect to change between initialisations + if ( ! DataTable.__browser ) { + var browser = {}; + DataTable.__browser = browser; + + // Scrolling feature / quirks detection + var n = $('<div/>') + .css( { + position: 'fixed', + top: 0, + left: $(window).scrollLeft()*-1, // allow for scrolling + height: 1, + width: 1, + overflow: 'hidden' + } ) + .append( + $('<div/>') + .css( { + position: 'absolute', + top: 1, + left: 1, + width: 100, + overflow: 'scroll' + } ) + .append( + $('<div/>') + .css( { + width: '100%', + height: 10 + } ) + ) + ) + .appendTo( 'body' ); + + var outer = n.children(); + var inner = outer.children(); + + // Numbers below, in order, are: + // inner.offsetWidth, inner.clientWidth, outer.offsetWidth, outer.clientWidth + // + // IE6 XP: 100 100 100 83 + // IE7 Vista: 100 100 100 83 + // IE 8+ Windows: 83 83 100 83 + // Evergreen Windows: 83 83 100 83 + // Evergreen Mac with scrollbars: 85 85 100 85 + // Evergreen Mac without scrollbars: 100 100 100 100 + + // Get scrollbar width + browser.barWidth = outer[0].offsetWidth - outer[0].clientWidth; + + // IE6/7 will oversize a width 100% element inside a scrolling element, to + // include the width of the scrollbar, while other browsers ensure the inner + // element is contained without forcing scrolling + browser.bScrollOversize = inner[0].offsetWidth === 100 && outer[0].clientWidth !== 100; + + // In rtl text layout, some browsers (most, but not all) will place the + // scrollbar on the left, rather than the right. + browser.bScrollbarLeft = Math.round( inner.offset().left ) !== 1; + + // IE8- don't provide height and width for getBoundingClientRect + browser.bBounding = n[0].getBoundingClientRect().width ? true : false; + + n.remove(); + } + + $.extend( settings.oBrowser, DataTable.__browser ); + settings.oScroll.iBarWidth = DataTable.__browser.barWidth; + } + + + /** + * Array.prototype reduce[Right] method, used for browsers which don't support + * JS 1.6. Done this way to reduce code size, since we iterate either way + * @param {object} settings dataTables settings object + * @memberof DataTable#oApi + */ + function _fnReduce ( that, fn, init, start, end, inc ) + { + var + i = start, + value, + isSet = false; + + if ( init !== undefined ) { + value = init; + isSet = true; + } + + while ( i !== end ) { + if ( ! that.hasOwnProperty(i) ) { + continue; + } + + value = isSet ? + fn( value, that[i], i, that ) : + that[i]; + + isSet = true; + i += inc; + } + + return value; + } + + /** + * Add a column to the list used for the table with default values + * @param {object} oSettings dataTables settings object + * @param {node} nTh The th element for this column + * @memberof DataTable#oApi + */ + function _fnAddColumn( oSettings, nTh ) + { + // Add column to aoColumns array + var oDefaults = DataTable.defaults.column; + var iCol = oSettings.aoColumns.length; + var oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, { + "nTh": nTh ? nTh : document.createElement('th'), + "sTitle": oDefaults.sTitle ? oDefaults.sTitle : nTh ? nTh.innerHTML : '', + "aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol], + "mData": oDefaults.mData ? oDefaults.mData : iCol, + idx: iCol + } ); + oSettings.aoColumns.push( oCol ); + + // Add search object for column specific search. Note that the `searchCols[ iCol ]` + // passed into extend can be undefined. This allows the user to give a default + // with only some of the parameters defined, and also not give a default + var searchCols = oSettings.aoPreSearchCols; + searchCols[ iCol ] = $.extend( {}, DataTable.models.oSearch, searchCols[ iCol ] ); + + // Use the default column options function to initialise classes etc + _fnColumnOptions( oSettings, iCol, $(nTh).data() ); + } + + + /** + * Apply options for a column + * @param {object} oSettings dataTables settings object + * @param {int} iCol column index to consider + * @param {object} oOptions object with sType, bVisible and bSearchable etc + * @memberof DataTable#oApi + */ + function _fnColumnOptions( oSettings, iCol, oOptions ) + { + var oCol = oSettings.aoColumns[ iCol ]; + var oClasses = oSettings.oClasses; + var th = $(oCol.nTh); + + // Try to get width information from the DOM. We can't get it from CSS + // as we'd need to parse the CSS stylesheet. `width` option can override + if ( ! oCol.sWidthOrig ) { + // Width attribute + oCol.sWidthOrig = th.attr('width') || null; + + // Style attribute + var t = (th.attr('style') || '').match(/width:\s*(\d+[pxem%]+)/); + if ( t ) { + oCol.sWidthOrig = t[1]; + } + } + + /* User specified column options */ + if ( oOptions !== undefined && oOptions !== null ) + { + // Backwards compatibility + _fnCompatCols( oOptions ); + + // Map camel case parameters to their Hungarian counterparts + _fnCamelToHungarian( DataTable.defaults.column, oOptions ); + + /* Backwards compatibility for mDataProp */ + if ( oOptions.mDataProp !== undefined && !oOptions.mData ) + { + oOptions.mData = oOptions.mDataProp; + } + + if ( oOptions.sType ) + { + oCol._sManualType = oOptions.sType; + } + + // `class` is a reserved word in Javascript, so we need to provide + // the ability to use a valid name for the camel case input + if ( oOptions.className && ! oOptions.sClass ) + { + oOptions.sClass = oOptions.className; + } + + $.extend( oCol, oOptions ); + _fnMap( oCol, oOptions, "sWidth", "sWidthOrig" ); + + /* iDataSort to be applied (backwards compatibility), but aDataSort will take + * priority if defined + */ + if ( oOptions.iDataSort !== undefined ) + { + oCol.aDataSort = [ oOptions.iDataSort ]; + } + _fnMap( oCol, oOptions, "aDataSort" ); + } + + /* Cache the data get and set functions for speed */ + var mDataSrc = oCol.mData; + var mData = _fnGetObjectDataFn( mDataSrc ); + var mRender = oCol.mRender ? _fnGetObjectDataFn( oCol.mRender ) : null; + + var attrTest = function( src ) { + return typeof src === 'string' && src.indexOf('@') !== -1; + }; + oCol._bAttrSrc = $.isPlainObject( mDataSrc ) && ( + attrTest(mDataSrc.sort) || attrTest(mDataSrc.type) || attrTest(mDataSrc.filter) + ); + oCol._setter = null; + + oCol.fnGetData = function (rowData, type, meta) { + var innerData = mData( rowData, type, undefined, meta ); + + return mRender && type ? + mRender( innerData, type, rowData, meta ) : + innerData; + }; + oCol.fnSetData = function ( rowData, val, meta ) { + return _fnSetObjectDataFn( mDataSrc )( rowData, val, meta ); + }; + + // Indicate if DataTables should read DOM data as an object or array + // Used in _fnGetRowElements + if ( typeof mDataSrc !== 'number' ) { + oSettings._rowReadObject = true; + } + + /* Feature sorting overrides column specific when off */ + if ( !oSettings.oFeatures.bSort ) + { + oCol.bSortable = false; + th.addClass( oClasses.sSortableNone ); // Have to add class here as order event isn't called + } + + /* Check that the class assignment is correct for sorting */ + var bAsc = $.inArray('asc', oCol.asSorting) !== -1; + var bDesc = $.inArray('desc', oCol.asSorting) !== -1; + if ( !oCol.bSortable || (!bAsc && !bDesc) ) + { + oCol.sSortingClass = oClasses.sSortableNone; + oCol.sSortingClassJUI = ""; + } + else if ( bAsc && !bDesc ) + { + oCol.sSortingClass = oClasses.sSortableAsc; + oCol.sSortingClassJUI = oClasses.sSortJUIAscAllowed; + } + else if ( !bAsc && bDesc ) + { + oCol.sSortingClass = oClasses.sSortableDesc; + oCol.sSortingClassJUI = oClasses.sSortJUIDescAllowed; + } + else + { + oCol.sSortingClass = oClasses.sSortable; + oCol.sSortingClassJUI = oClasses.sSortJUI; + } + } + + + /** + * Adjust the table column widths for new data. Note: you would probably want to + * do a redraw after calling this function! + * @param {object} settings dataTables settings object + * @memberof DataTable#oApi + */ + function _fnAdjustColumnSizing ( settings ) + { + /* Not interested in doing column width calculation if auto-width is disabled */ + if ( settings.oFeatures.bAutoWidth !== false ) + { + var columns = settings.aoColumns; + + _fnCalculateColumnWidths( settings ); + for ( var i=0 , iLen=columns.length ; i<iLen ; i++ ) + { + columns[i].nTh.style.width = columns[i].sWidth; + } + } + + var scroll = settings.oScroll; + if ( scroll.sY !== '' || scroll.sX !== '') + { + _fnScrollDraw( settings ); + } + + _fnCallbackFire( settings, null, 'column-sizing', [settings] ); + } + + + /** + * Covert the index of a visible column to the index in the data array (take account + * of hidden columns) + * @param {object} oSettings dataTables settings object + * @param {int} iMatch Visible column index to lookup + * @returns {int} i the data index + * @memberof DataTable#oApi + */ + function _fnVisibleToColumnIndex( oSettings, iMatch ) + { + var aiVis = _fnGetColumns( oSettings, 'bVisible' ); + + return typeof aiVis[iMatch] === 'number' ? + aiVis[iMatch] : + null; + } + + + /** + * Covert the index of an index in the data array and convert it to the visible + * column index (take account of hidden columns) + * @param {int} iMatch Column index to lookup + * @param {object} oSettings dataTables settings object + * @returns {int} i the data index + * @memberof DataTable#oApi + */ + function _fnColumnIndexToVisible( oSettings, iMatch ) + { + var aiVis = _fnGetColumns( oSettings, 'bVisible' ); + var iPos = $.inArray( iMatch, aiVis ); + + return iPos !== -1 ? iPos : null; + } + + + /** + * Get the number of visible columns + * @param {object} oSettings dataTables settings object + * @returns {int} i the number of visible columns + * @memberof DataTable#oApi + */ + function _fnVisbleColumns( oSettings ) + { + var vis = 0; + + // No reduce in IE8, use a loop for now + $.each( oSettings.aoColumns, function ( i, col ) { + if ( col.bVisible && $(col.nTh).css('display') !== 'none' ) { + vis++; + } + } ); + + return vis; + } + + + /** + * Get an array of column indexes that match a given property + * @param {object} oSettings dataTables settings object + * @param {string} sParam Parameter in aoColumns to look for - typically + * bVisible or bSearchable + * @returns {array} Array of indexes with matched properties + * @memberof DataTable#oApi + */ + function _fnGetColumns( oSettings, sParam ) + { + var a = []; + + $.map( oSettings.aoColumns, function(val, i) { + if ( val[sParam] ) { + a.push( i ); + } + } ); + + return a; + } + + + /** + * Calculate the 'type' of a column + * @param {object} settings dataTables settings object + * @memberof DataTable#oApi + */ + function _fnColumnTypes ( settings ) + { + var columns = settings.aoColumns; + var data = settings.aoData; + var types = DataTable.ext.type.detect; + var i, ien, j, jen, k, ken; + var col, cell, detectedType, cache; + + // For each column, spin over the + for ( i=0, ien=columns.length ; i<ien ; i++ ) { + col = columns[i]; + cache = []; + + if ( ! col.sType && col._sManualType ) { + col.sType = col._sManualType; + } + else if ( ! col.sType ) { + for ( j=0, jen=types.length ; j<jen ; j++ ) { + for ( k=0, ken=data.length ; k<ken ; k++ ) { + // Use a cache array so we only need to get the type data + // from the formatter once (when using multiple detectors) + if ( cache[k] === undefined ) { + cache[k] = _fnGetCellData( settings, k, i, 'type' ); + } + + detectedType = types[j]( cache[k], settings ); + + // If null, then this type can't apply to this column, so + // rather than testing all cells, break out. There is an + // exception for the last type which is `html`. We need to + // scan all rows since it is possible to mix string and HTML + // types + if ( ! detectedType && j !== types.length-1 ) { + break; + } + + // Only a single match is needed for html type since it is + // bottom of the pile and very similar to string + if ( detectedType === 'html' ) { + break; + } + } + + // Type is valid for all data points in the column - use this + // type + if ( detectedType ) { + col.sType = detectedType; + break; + } + } + + // Fall back - if no type was detected, always use string + if ( ! col.sType ) { + col.sType = 'string'; + } + } + } + } + + + /** + * Take the column definitions and static columns arrays and calculate how + * they relate to column indexes. The callback function will then apply the + * definition found for a column to a suitable configuration object. + * @param {object} oSettings dataTables settings object + * @param {array} aoColDefs The aoColumnDefs array that is to be applied + * @param {array} aoCols The aoColumns array that defines columns individually + * @param {function} fn Callback function - takes two parameters, the calculated + * column index and the definition for that column. + * @memberof DataTable#oApi + */ + function _fnApplyColumnDefs( oSettings, aoColDefs, aoCols, fn ) + { + var i, iLen, j, jLen, k, kLen, def; + var columns = oSettings.aoColumns; + + // Column definitions with aTargets + if ( aoColDefs ) + { + /* Loop over the definitions array - loop in reverse so first instance has priority */ + for ( i=aoColDefs.length-1 ; i>=0 ; i-- ) + { + def = aoColDefs[i]; + + /* Each definition can target multiple columns, as it is an array */ + var aTargets = def.targets !== undefined ? + def.targets : + def.aTargets; + + if ( ! $.isArray( aTargets ) ) + { + aTargets = [ aTargets ]; + } + + for ( j=0, jLen=aTargets.length ; j<jLen ; j++ ) + { + if ( typeof aTargets[j] === 'number' && aTargets[j] >= 0 ) + { + /* Add columns that we don't yet know about */ + while( columns.length <= aTargets[j] ) + { + _fnAddColumn( oSettings ); + } + + /* Integer, basic index */ + fn( aTargets[j], def ); + } + else if ( typeof aTargets[j] === 'number' && aTargets[j] < 0 ) + { + /* Negative integer, right to left column counting */ + fn( columns.length+aTargets[j], def ); + } + else if ( typeof aTargets[j] === 'string' ) + { + /* Class name matching on TH element */ + for ( k=0, kLen=columns.length ; k<kLen ; k++ ) + { + if ( aTargets[j] == "_all" || + $(columns[k].nTh).hasClass( aTargets[j] ) ) + { + fn( k, def ); + } + } + } + } + } + } + + // Statically defined columns array + if ( aoCols ) + { + for ( i=0, iLen=aoCols.length ; i<iLen ; i++ ) + { + fn( i, aoCols[i] ); + } + } + } + + /** + * Add a data array to the table, creating DOM node etc. This is the parallel to + * _fnGatherData, but for adding rows from a Javascript source, rather than a + * DOM source. + * @param {object} oSettings dataTables settings object + * @param {array} aData data array to be added + * @param {node} [nTr] TR element to add to the table - optional. If not given, + * DataTables will create a row automatically + * @param {array} [anTds] Array of TD|TH elements for the row - must be given + * if nTr is. + * @returns {int} >=0 if successful (index of new aoData entry), -1 if failed + * @memberof DataTable#oApi + */ + function _fnAddData ( oSettings, aDataIn, nTr, anTds ) + { + /* Create the object for storing information about this new row */ + var iRow = oSettings.aoData.length; + var oData = $.extend( true, {}, DataTable.models.oRow, { + src: nTr ? 'dom' : 'data', + idx: iRow + } ); + + oData._aData = aDataIn; + oSettings.aoData.push( oData ); + + /* Create the cells */ + var nTd, sThisType; + var columns = oSettings.aoColumns; + + // Invalidate the column types as the new data needs to be revalidated + for ( var i=0, iLen=columns.length ; i<iLen ; i++ ) + { + columns[i].sType = null; + } + + /* Add to the display array */ + oSettings.aiDisplayMaster.push( iRow ); + + var id = oSettings.rowIdFn( aDataIn ); + if ( id !== undefined ) { + oSettings.aIds[ id ] = oData; + } + + /* Create the DOM information, or register it if already present */ + if ( nTr || ! oSettings.oFeatures.bDeferRender ) + { + _fnCreateTr( oSettings, iRow, nTr, anTds ); + } + + return iRow; + } + + + /** + * Add one or more TR elements to the table. Generally we'd expect to + * use this for reading data from a DOM sourced table, but it could be + * used for an TR element. Note that if a TR is given, it is used (i.e. + * it is not cloned). + * @param {object} settings dataTables settings object + * @param {array|node|jQuery} trs The TR element(s) to add to the table + * @returns {array} Array of indexes for the added rows + * @memberof DataTable#oApi + */ + function _fnAddTr( settings, trs ) + { + var row; + + // Allow an individual node to be passed in + if ( ! (trs instanceof $) ) { + trs = $(trs); + } + + return trs.map( function (i, el) { + row = _fnGetRowElements( settings, el ); + return _fnAddData( settings, row.data, el, row.cells ); + } ); + } + + + /** + * Take a TR element and convert it to an index in aoData + * @param {object} oSettings dataTables settings object + * @param {node} n the TR element to find + * @returns {int} index if the node is found, null if not + * @memberof DataTable#oApi + */ + function _fnNodeToDataIndex( oSettings, n ) + { + return (n._DT_RowIndex!==undefined) ? n._DT_RowIndex : null; + } + + + /** + * Take a TD element and convert it into a column data index (not the visible index) + * @param {object} oSettings dataTables settings object + * @param {int} iRow The row number the TD/TH can be found in + * @param {node} n The TD/TH element to find + * @returns {int} index if the node is found, -1 if not + * @memberof DataTable#oApi + */ + function _fnNodeToColumnIndex( oSettings, iRow, n ) + { + return $.inArray( n, oSettings.aoData[ iRow ].anCells ); + } + + + /** + * Get the data for a given cell from the internal cache, taking into account data mapping + * @param {object} settings dataTables settings object + * @param {int} rowIdx aoData row id + * @param {int} colIdx Column index + * @param {string} type data get type ('display', 'type' 'filter' 'sort') + * @returns {*} Cell data + * @memberof DataTable#oApi + */ + function _fnGetCellData( settings, rowIdx, colIdx, type ) + { + var draw = settings.iDraw; + var col = settings.aoColumns[colIdx]; + var rowData = settings.aoData[rowIdx]._aData; + var defaultContent = col.sDefaultContent; + var cellData = col.fnGetData( rowData, type, { + settings: settings, + row: rowIdx, + col: colIdx + } ); + + if ( cellData === undefined ) { + if ( settings.iDrawError != draw && defaultContent === null ) { + _fnLog( settings, 0, "Requested unknown parameter "+ + (typeof col.mData=='function' ? '{function}' : "'"+col.mData+"'")+ + " for row "+rowIdx+", column "+colIdx, 4 ); + settings.iDrawError = draw; + } + return defaultContent; + } + + // When the data source is null and a specific data type is requested (i.e. + // not the original data), we can use default column data + if ( (cellData === rowData || cellData === null) && defaultContent !== null && type !== undefined ) { + cellData = defaultContent; + } + else if ( typeof cellData === 'function' ) { + // If the data source is a function, then we run it and use the return, + // executing in the scope of the data object (for instances) + return cellData.call( rowData ); + } + + if ( cellData === null && type == 'display' ) { + return ''; + } + return cellData; + } + + + /** + * Set the value for a specific cell, into the internal data cache + * @param {object} settings dataTables settings object + * @param {int} rowIdx aoData row id + * @param {int} colIdx Column index + * @param {*} val Value to set + * @memberof DataTable#oApi + */ + function _fnSetCellData( settings, rowIdx, colIdx, val ) + { + var col = settings.aoColumns[colIdx]; + var rowData = settings.aoData[rowIdx]._aData; + + col.fnSetData( rowData, val, { + settings: settings, + row: rowIdx, + col: colIdx + } ); + } + + + // Private variable that is used to match action syntax in the data property object + var __reArray = /\[.*?\]$/; + var __reFn = /\(\)$/; + + /** + * Split string on periods, taking into account escaped periods + * @param {string} str String to split + * @return {array} Split string + */ + function _fnSplitObjNotation( str ) + { + return $.map( str.match(/(\\.|[^\.])+/g) || [''], function ( s ) { + return s.replace(/\\\./g, '.'); + } ); + } + + + /** + * Return a function that can be used to get data from a source object, taking + * into account the ability to use nested objects as a source + * @param {string|int|function} mSource The data source for the object + * @returns {function} Data get function + * @memberof DataTable#oApi + */ + function _fnGetObjectDataFn( mSource ) + { + if ( $.isPlainObject( mSource ) ) + { + /* Build an object of get functions, and wrap them in a single call */ + var o = {}; + $.each( mSource, function (key, val) { + if ( val ) { + o[key] = _fnGetObjectDataFn( val ); + } + } ); + + return function (data, type, row, meta) { + var t = o[type] || o._; + return t !== undefined ? + t(data, type, row, meta) : + data; + }; + } + else if ( mSource === null ) + { + /* Give an empty string for rendering / sorting etc */ + return function (data) { // type, row and meta also passed, but not used + return data; + }; + } + else if ( typeof mSource === 'function' ) + { + return function (data, type, row, meta) { + return mSource( data, type, row, meta ); + }; + } + else if ( typeof mSource === 'string' && (mSource.indexOf('.') !== -1 || + mSource.indexOf('[') !== -1 || mSource.indexOf('(') !== -1) ) + { + /* If there is a . in the source string then the data source is in a + * nested object so we loop over the data for each level to get the next + * level down. On each loop we test for undefined, and if found immediately + * return. This allows entire objects to be missing and sDefaultContent to + * be used if defined, rather than throwing an error + */ + var fetchData = function (data, type, src) { + var arrayNotation, funcNotation, out, innerSrc; + + if ( src !== "" ) + { + var a = _fnSplitObjNotation( src ); + + for ( var i=0, iLen=a.length ; i<iLen ; i++ ) + { + // Check if we are dealing with special notation + arrayNotation = a[i].match(__reArray); + funcNotation = a[i].match(__reFn); + + if ( arrayNotation ) + { + // Array notation + a[i] = a[i].replace(__reArray, ''); + + // Condition allows simply [] to be passed in + if ( a[i] !== "" ) { + data = data[ a[i] ]; + } + out = []; + + // Get the remainder of the nested object to get + a.splice( 0, i+1 ); + innerSrc = a.join('.'); + + // Traverse each entry in the array getting the properties requested + if ( $.isArray( data ) ) { + for ( var j=0, jLen=data.length ; j<jLen ; j++ ) { + out.push( fetchData( data[j], type, innerSrc ) ); + } + } + + // If a string is given in between the array notation indicators, that + // is used to join the strings together, otherwise an array is returned + var join = arrayNotation[0].substring(1, arrayNotation[0].length-1); + data = (join==="") ? out : out.join(join); + + // The inner call to fetchData has already traversed through the remainder + // of the source requested, so we exit from the loop + break; + } + else if ( funcNotation ) + { + // Function call + a[i] = a[i].replace(__reFn, ''); + data = data[ a[i] ](); + continue; + } + + if ( data === null || data[ a[i] ] === undefined ) + { + return undefined; + } + data = data[ a[i] ]; + } + } + + return data; + }; + + return function (data, type) { // row and meta also passed, but not used + return fetchData( data, type, mSource ); + }; + } + else + { + /* Array or flat object mapping */ + return function (data, type) { // row and meta also passed, but not used + return data[mSource]; + }; + } + } + + + /** + * Return a function that can be used to set data from a source object, taking + * into account the ability to use nested objects as a source + * @param {string|int|function} mSource The data source for the object + * @returns {function} Data set function + * @memberof DataTable#oApi + */ + function _fnSetObjectDataFn( mSource ) + { + if ( $.isPlainObject( mSource ) ) + { + /* Unlike get, only the underscore (global) option is used for for + * setting data since we don't know the type here. This is why an object + * option is not documented for `mData` (which is read/write), but it is + * for `mRender` which is read only. + */ + return _fnSetObjectDataFn( mSource._ ); + } + else if ( mSource === null ) + { + /* Nothing to do when the data source is null */ + return function () {}; + } + else if ( typeof mSource === 'function' ) + { + return function (data, val, meta) { + mSource( data, 'set', val, meta ); + }; + } + else if ( typeof mSource === 'string' && (mSource.indexOf('.') !== -1 || + mSource.indexOf('[') !== -1 || mSource.indexOf('(') !== -1) ) + { + /* Like the get, we need to get data from a nested object */ + var setData = function (data, val, src) { + var a = _fnSplitObjNotation( src ), b; + var aLast = a[a.length-1]; + var arrayNotation, funcNotation, o, innerSrc; + + for ( var i=0, iLen=a.length-1 ; i<iLen ; i++ ) + { + // Check if we are dealing with an array notation request + arrayNotation = a[i].match(__reArray); + funcNotation = a[i].match(__reFn); + + if ( arrayNotation ) + { + a[i] = a[i].replace(__reArray, ''); + data[ a[i] ] = []; + + // Get the remainder of the nested object to set so we can recurse + b = a.slice(); + b.splice( 0, i+1 ); + innerSrc = b.join('.'); + + // Traverse each entry in the array setting the properties requested + if ( $.isArray( val ) ) + { + for ( var j=0, jLen=val.length ; j<jLen ; j++ ) + { + o = {}; + setData( o, val[j], innerSrc ); + data[ a[i] ].push( o ); + } + } + else + { + // We've been asked to save data to an array, but it + // isn't array data to be saved. Best that can be done + // is to just save the value. + data[ a[i] ] = val; + } + + // The inner call to setData has already traversed through the remainder + // of the source and has set the data, thus we can exit here + return; + } + else if ( funcNotation ) + { + // Function call + a[i] = a[i].replace(__reFn, ''); + data = data[ a[i] ]( val ); + } + + // If the nested object doesn't currently exist - since we are + // trying to set the value - create it + if ( data[ a[i] ] === null || data[ a[i] ] === undefined ) + { + data[ a[i] ] = {}; + } + data = data[ a[i] ]; + } + + // Last item in the input - i.e, the actual set + if ( aLast.match(__reFn ) ) + { + // Function call + data = data[ aLast.replace(__reFn, '') ]( val ); + } + else + { + // If array notation is used, we just want to strip it and use the property name + // and assign the value. If it isn't used, then we get the result we want anyway + data[ aLast.replace(__reArray, '') ] = val; + } + }; + + return function (data, val) { // meta is also passed in, but not used + return setData( data, val, mSource ); + }; + } + else + { + /* Array or flat object mapping */ + return function (data, val) { // meta is also passed in, but not used + data[mSource] = val; + }; + } + } + + + /** + * Return an array with the full table data + * @param {object} oSettings dataTables settings object + * @returns array {array} aData Master data array + * @memberof DataTable#oApi + */ + function _fnGetDataMaster ( settings ) + { + return _pluck( settings.aoData, '_aData' ); + } + + + /** + * Nuke the table + * @param {object} oSettings dataTables settings object + * @memberof DataTable#oApi + */ + function _fnClearTable( settings ) + { + settings.aoData.length = 0; + settings.aiDisplayMaster.length = 0; + settings.aiDisplay.length = 0; + settings.aIds = {}; + } + + + /** + * Take an array of integers (index array) and remove a target integer (value - not + * the key!) + * @param {array} a Index array to target + * @param {int} iTarget value to find + * @memberof DataTable#oApi + */ + function _fnDeleteIndex( a, iTarget, splice ) + { + var iTargetIndex = -1; + + for ( var i=0, iLen=a.length ; i<iLen ; i++ ) + { + if ( a[i] == iTarget ) + { + iTargetIndex = i; + } + else if ( a[i] > iTarget ) + { + a[i]--; + } + } + + if ( iTargetIndex != -1 && splice === undefined ) + { + a.splice( iTargetIndex, 1 ); + } + } + + + /** + * Mark cached data as invalid such that a re-read of the data will occur when + * the cached data is next requested. Also update from the data source object. + * + * @param {object} settings DataTables settings object + * @param {int} rowIdx Row index to invalidate + * @param {string} [src] Source to invalidate from: undefined, 'auto', 'dom' + * or 'data' + * @param {int} [colIdx] Column index to invalidate. If undefined the whole + * row will be invalidated + * @memberof DataTable#oApi + * + * @todo For the modularisation of v1.11 this will need to become a callback, so + * the sort and filter methods can subscribe to it. That will required + * initialisation options for sorting, which is why it is not already baked in + */ + function _fnInvalidate( settings, rowIdx, src, colIdx ) + { + var row = settings.aoData[ rowIdx ]; + var i, ien; + var cellWrite = function ( cell, col ) { + // This is very frustrating, but in IE if you just write directly + // to innerHTML, and elements that are overwritten are GC'ed, + // even if there is a reference to them elsewhere + while ( cell.childNodes.length ) { + cell.removeChild( cell.firstChild ); + } + + cell.innerHTML = _fnGetCellData( settings, rowIdx, col, 'display' ); + }; + + // Are we reading last data from DOM or the data object? + if ( src === 'dom' || ((! src || src === 'auto') && row.src === 'dom') ) { + // Read the data from the DOM + row._aData = _fnGetRowElements( + settings, row, colIdx, colIdx === undefined ? undefined : row._aData + ) + .data; + } + else { + // Reading from data object, update the DOM + var cells = row.anCells; + + if ( cells ) { + if ( colIdx !== undefined ) { + cellWrite( cells[colIdx], colIdx ); + } + else { + for ( i=0, ien=cells.length ; i<ien ; i++ ) { + cellWrite( cells[i], i ); + } + } + } + } + + // For both row and cell invalidation, the cached data for sorting and + // filtering is nulled out + row._aSortData = null; + row._aFilterData = null; + + // Invalidate the type for a specific column (if given) or all columns since + // the data might have changed + var cols = settings.aoColumns; + if ( colIdx !== undefined ) { + cols[ colIdx ].sType = null; + } + else { + for ( i=0, ien=cols.length ; i<ien ; i++ ) { + cols[i].sType = null; + } + + // Update DataTables special `DT_*` attributes for the row + _fnRowAttributes( settings, row ); + } + } + + + /** + * Build a data source object from an HTML row, reading the contents of the + * cells that are in the row. + * + * @param {object} settings DataTables settings object + * @param {node|object} TR element from which to read data or existing row + * object from which to re-read the data from the cells + * @param {int} [colIdx] Optional column index + * @param {array|object} [d] Data source object. If `colIdx` is given then this + * parameter should also be given and will be used to write the data into. + * Only the column in question will be written + * @returns {object} Object with two parameters: `data` the data read, in + * document order, and `cells` and array of nodes (they can be useful to the + * caller, so rather than needing a second traversal to get them, just return + * them from here). + * @memberof DataTable#oApi + */ + function _fnGetRowElements( settings, row, colIdx, d ) + { + var + tds = [], + td = row.firstChild, + name, col, o, i=0, contents, + columns = settings.aoColumns, + objectRead = settings._rowReadObject; + + // Allow the data object to be passed in, or construct + d = d !== undefined ? + d : + objectRead ? + {} : + []; + + var attr = function ( str, td ) { + if ( typeof str === 'string' ) { + var idx = str.indexOf('@'); + + if ( idx !== -1 ) { + var attr = str.substring( idx+1 ); + var setter = _fnSetObjectDataFn( str ); + setter( d, td.getAttribute( attr ) ); + } + } + }; + + // Read data from a cell and store into the data object + var cellProcess = function ( cell ) { + if ( colIdx === undefined || colIdx === i ) { + col = columns[i]; + contents = $.trim(cell.innerHTML); + + if ( col && col._bAttrSrc ) { + var setter = _fnSetObjectDataFn( col.mData._ ); + setter( d, contents ); + + attr( col.mData.sort, cell ); + attr( col.mData.type, cell ); + attr( col.mData.filter, cell ); + } + else { + // Depending on the `data` option for the columns the data can + // be read to either an object or an array. + if ( objectRead ) { + if ( ! col._setter ) { + // Cache the setter function + col._setter = _fnSetObjectDataFn( col.mData ); + } + col._setter( d, contents ); + } + else { + d[i] = contents; + } + } + } + + i++; + }; + + if ( td ) { + // `tr` element was passed in + while ( td ) { + name = td.nodeName.toUpperCase(); + + if ( name == "TD" || name == "TH" ) { + cellProcess( td ); + tds.push( td ); + } + + td = td.nextSibling; + } + } + else { + // Existing row object passed in + tds = row.anCells; + + for ( var j=0, jen=tds.length ; j<jen ; j++ ) { + cellProcess( tds[j] ); + } + } + + // Read the ID from the DOM if present + var rowNode = row.firstChild ? row : row.nTr; + + if ( rowNode ) { + var id = rowNode.getAttribute( 'id' ); + + if ( id ) { + _fnSetObjectDataFn( settings.rowId )( d, id ); + } + } + + return { + data: d, + cells: tds + }; + } + /** + * Create a new TR element (and it's TD children) for a row + * @param {object} oSettings dataTables settings object + * @param {int} iRow Row to consider + * @param {node} [nTrIn] TR element to add to the table - optional. If not given, + * DataTables will create a row automatically + * @param {array} [anTds] Array of TD|TH elements for the row - must be given + * if nTr is. + * @memberof DataTable#oApi + */ + function _fnCreateTr ( oSettings, iRow, nTrIn, anTds ) + { + var + row = oSettings.aoData[iRow], + rowData = row._aData, + cells = [], + nTr, nTd, oCol, + i, iLen; + + if ( row.nTr === null ) + { + nTr = nTrIn || document.createElement('tr'); + + row.nTr = nTr; + row.anCells = cells; + + /* Use a private property on the node to allow reserve mapping from the node + * to the aoData array for fast look up + */ + nTr._DT_RowIndex = iRow; + + /* Special parameters can be given by the data source to be used on the row */ + _fnRowAttributes( oSettings, row ); + + /* Process each column */ + for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ ) + { + oCol = oSettings.aoColumns[i]; + + nTd = nTrIn ? anTds[i] : document.createElement( oCol.sCellType ); + nTd._DT_CellIndex = { + row: iRow, + column: i + }; + + cells.push( nTd ); + + // Need to create the HTML if new, or if a rendering function is defined + if ( (!nTrIn || oCol.mRender || oCol.mData !== i) && + (!$.isPlainObject(oCol.mData) || oCol.mData._ !== i+'.display') + ) { + nTd.innerHTML = _fnGetCellData( oSettings, iRow, i, 'display' ); + } + + /* Add user defined class */ + if ( oCol.sClass ) + { + nTd.className += ' '+oCol.sClass; + } + + // Visibility - add or remove as required + if ( oCol.bVisible && ! nTrIn ) + { + nTr.appendChild( nTd ); + } + else if ( ! oCol.bVisible && nTrIn ) + { + nTd.parentNode.removeChild( nTd ); + } + + if ( oCol.fnCreatedCell ) + { + oCol.fnCreatedCell.call( oSettings.oInstance, + nTd, _fnGetCellData( oSettings, iRow, i ), rowData, iRow, i + ); + } + } + + _fnCallbackFire( oSettings, 'aoRowCreatedCallback', null, [nTr, rowData, iRow] ); + } + + // Remove once webkit bug 131819 and Chromium bug 365619 have been resolved + // and deployed + row.nTr.setAttribute( 'role', 'row' ); + } + + + /** + * Add attributes to a row based on the special `DT_*` parameters in a data + * source object. + * @param {object} settings DataTables settings object + * @param {object} DataTables row object for the row to be modified + * @memberof DataTable#oApi + */ + function _fnRowAttributes( settings, row ) + { + var tr = row.nTr; + var data = row._aData; + + if ( tr ) { + var id = settings.rowIdFn( data ); + + if ( id ) { + tr.id = id; + } + + if ( data.DT_RowClass ) { + // Remove any classes added by DT_RowClass before + var a = data.DT_RowClass.split(' '); + row.__rowc = row.__rowc ? + _unique( row.__rowc.concat( a ) ) : + a; + + $(tr) + .removeClass( row.__rowc.join(' ') ) + .addClass( data.DT_RowClass ); + } + + if ( data.DT_RowAttr ) { + $(tr).attr( data.DT_RowAttr ); + } + + if ( data.DT_RowData ) { + $(tr).data( data.DT_RowData ); + } + } + } + + + /** + * Create the HTML header for the table + * @param {object} oSettings dataTables settings object + * @memberof DataTable#oApi + */ + function _fnBuildHead( oSettings ) + { + var i, ien, cell, row, column; + var thead = oSettings.nTHead; + var tfoot = oSettings.nTFoot; + var createHeader = $('th, td', thead).length === 0; + var classes = oSettings.oClasses; + var columns = oSettings.aoColumns; + + if ( createHeader ) { + row = $('<tr/>').appendTo( thead ); + } + + for ( i=0, ien=columns.length ; i<ien ; i++ ) { + column = columns[i]; + cell = $( column.nTh ).addClass( column.sClass ); + + if ( createHeader ) { + cell.appendTo( row ); + } + + // 1.11 move into sorting + if ( oSettings.oFeatures.bSort ) { + cell.addClass( column.sSortingClass ); + + if ( column.bSortable !== false ) { + cell + .attr( 'tabindex', oSettings.iTabIndex ) + .attr( 'aria-controls', oSettings.sTableId ); + + _fnSortAttachListener( oSettings, column.nTh, i ); + } + } + + if ( column.sTitle != cell[0].innerHTML ) { + cell.html( column.sTitle ); + } + + _fnRenderer( oSettings, 'header' )( + oSettings, cell, column, classes + ); + } + + if ( createHeader ) { + _fnDetectHeader( oSettings.aoHeader, thead ); + } + + /* ARIA role for the rows */ + $(thead).find('>tr').attr('role', 'row'); + + /* Deal with the footer - add classes if required */ + $(thead).find('>tr>th, >tr>td').addClass( classes.sHeaderTH ); + $(tfoot).find('>tr>th, >tr>td').addClass( classes.sFooterTH ); + + // Cache the footer cells. Note that we only take the cells from the first + // row in the footer. If there is more than one row the user wants to + // interact with, they need to use the table().foot() method. Note also this + // allows cells to be used for multiple columns using colspan + if ( tfoot !== null ) { + var cells = oSettings.aoFooter[0]; + + for ( i=0, ien=cells.length ; i<ien ; i++ ) { + column = columns[i]; + column.nTf = cells[i].cell; + + if ( column.sClass ) { + $(column.nTf).addClass( column.sClass ); + } + } + } + } + + + /** + * Draw the header (or footer) element based on the column visibility states. The + * methodology here is to use the layout array from _fnDetectHeader, modified for + * the instantaneous column visibility, to construct the new layout. The grid is + * traversed over cell at a time in a rows x columns grid fashion, although each + * cell insert can cover multiple elements in the grid - which is tracks using the + * aApplied array. Cell inserts in the grid will only occur where there isn't + * already a cell in that position. + * @param {object} oSettings dataTables settings object + * @param array {objects} aoSource Layout array from _fnDetectHeader + * @param {boolean} [bIncludeHidden=false] If true then include the hidden columns in the calc, + * @memberof DataTable#oApi + */ + function _fnDrawHead( oSettings, aoSource, bIncludeHidden ) + { + var i, iLen, j, jLen, k, kLen, n, nLocalTr; + var aoLocal = []; + var aApplied = []; + var iColumns = oSettings.aoColumns.length; + var iRowspan, iColspan; + + if ( ! aoSource ) + { + return; + } + + if ( bIncludeHidden === undefined ) + { + bIncludeHidden = false; + } + + /* Make a copy of the master layout array, but without the visible columns in it */ + for ( i=0, iLen=aoSource.length ; i<iLen ; i++ ) + { + aoLocal[i] = aoSource[i].slice(); + aoLocal[i].nTr = aoSource[i].nTr; + + /* Remove any columns which are currently hidden */ + for ( j=iColumns-1 ; j>=0 ; j-- ) + { + if ( !oSettings.aoColumns[j].bVisible && !bIncludeHidden ) + { + aoLocal[i].splice( j, 1 ); + } + } + + /* Prep the applied array - it needs an element for each row */ + aApplied.push( [] ); + } + + for ( i=0, iLen=aoLocal.length ; i<iLen ; i++ ) + { + nLocalTr = aoLocal[i].nTr; + + /* All cells are going to be replaced, so empty out the row */ + if ( nLocalTr ) + { + while( (n = nLocalTr.firstChild) ) + { + nLocalTr.removeChild( n ); + } + } + + for ( j=0, jLen=aoLocal[i].length ; j<jLen ; j++ ) + { + iRowspan = 1; + iColspan = 1; + + /* Check to see if there is already a cell (row/colspan) covering our target + * insert point. If there is, then there is nothing to do. + */ + if ( aApplied[i][j] === undefined ) + { + nLocalTr.appendChild( aoLocal[i][j].cell ); + aApplied[i][j] = 1; + + /* Expand the cell to cover as many rows as needed */ + while ( aoLocal[i+iRowspan] !== undefined && + aoLocal[i][j].cell == aoLocal[i+iRowspan][j].cell ) + { + aApplied[i+iRowspan][j] = 1; + iRowspan++; + } + + /* Expand the cell to cover as many columns as needed */ + while ( aoLocal[i][j+iColspan] !== undefined && + aoLocal[i][j].cell == aoLocal[i][j+iColspan].cell ) + { + /* Must update the applied array over the rows for the columns */ + for ( k=0 ; k<iRowspan ; k++ ) + { + aApplied[i+k][j+iColspan] = 1; + } + iColspan++; + } + + /* Do the actual expansion in the DOM */ + $(aoLocal[i][j].cell) + .attr('rowspan', iRowspan) + .attr('colspan', iColspan); + } + } + } + } + + + /** + * Insert the required TR nodes into the table for display + * @param {object} oSettings dataTables settings object + * @memberof DataTable#oApi + */ + function _fnDraw( oSettings ) + { + /* Provide a pre-callback function which can be used to cancel the draw is false is returned */ + var aPreDraw = _fnCallbackFire( oSettings, 'aoPreDrawCallback', 'preDraw', [oSettings] ); + if ( $.inArray( false, aPreDraw ) !== -1 ) + { + _fnProcessingDisplay( oSettings, false ); + return; + } + + var i, iLen, n; + var anRows = []; + var iRowCount = 0; + var asStripeClasses = oSettings.asStripeClasses; + var iStripes = asStripeClasses.length; + var iOpenRows = oSettings.aoOpenRows.length; + var oLang = oSettings.oLanguage; + var iInitDisplayStart = oSettings.iInitDisplayStart; + var bServerSide = _fnDataSource( oSettings ) == 'ssp'; + var aiDisplay = oSettings.aiDisplay; + + oSettings.bDrawing = true; + + /* Check and see if we have an initial draw position from state saving */ + if ( iInitDisplayStart !== undefined && iInitDisplayStart !== -1 ) + { + oSettings._iDisplayStart = bServerSide ? + iInitDisplayStart : + iInitDisplayStart >= oSettings.fnRecordsDisplay() ? + 0 : + iInitDisplayStart; + + oSettings.iInitDisplayStart = -1; + } + + var iDisplayStart = oSettings._iDisplayStart; + var iDisplayEnd = oSettings.fnDisplayEnd(); + + /* Server-side processing draw intercept */ + if ( oSettings.bDeferLoading ) + { + oSettings.bDeferLoading = false; + oSettings.iDraw++; + _fnProcessingDisplay( oSettings, false ); + } + else if ( !bServerSide ) + { + oSettings.iDraw++; + } + else if ( !oSettings.bDestroying && !_fnAjaxUpdate( oSettings ) ) + { + return; + } + + if ( aiDisplay.length !== 0 ) + { + var iStart = bServerSide ? 0 : iDisplayStart; + var iEnd = bServerSide ? oSettings.aoData.length : iDisplayEnd; + + for ( var j=iStart ; j<iEnd ; j++ ) + { + var iDataIndex = aiDisplay[j]; + var aoData = oSettings.aoData[ iDataIndex ]; + if ( aoData.nTr === null ) + { + _fnCreateTr( oSettings, iDataIndex ); + } + + var nRow = aoData.nTr; + + /* Remove the old striping classes and then add the new one */ + if ( iStripes !== 0 ) + { + var sStripe = asStripeClasses[ iRowCount % iStripes ]; + if ( aoData._sRowStripe != sStripe ) + { + $(nRow).removeClass( aoData._sRowStripe ).addClass( sStripe ); + aoData._sRowStripe = sStripe; + } + } + + // Row callback functions - might want to manipulate the row + // iRowCount and j are not currently documented. Are they at all + // useful? + _fnCallbackFire( oSettings, 'aoRowCallback', null, + [nRow, aoData._aData, iRowCount, j] ); + + anRows.push( nRow ); + iRowCount++; + } + } + else + { + /* Table is empty - create a row with an empty message in it */ + var sZero = oLang.sZeroRecords; + if ( oSettings.iDraw == 1 && _fnDataSource( oSettings ) == 'ajax' ) + { + sZero = oLang.sLoadingRecords; + } + else if ( oLang.sEmptyTable && oSettings.fnRecordsTotal() === 0 ) + { + sZero = oLang.sEmptyTable; + } + + anRows[ 0 ] = $( '<tr/>', { 'class': iStripes ? asStripeClasses[0] : '' } ) + .append( $('<td />', { + 'valign': 'top', + 'colSpan': _fnVisbleColumns( oSettings ), + 'class': oSettings.oClasses.sRowEmpty + } ).html( sZero ) )[0]; + } + + /* Header and footer callbacks */ + _fnCallbackFire( oSettings, 'aoHeaderCallback', 'header', [ $(oSettings.nTHead).children('tr')[0], + _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] ); + + _fnCallbackFire( oSettings, 'aoFooterCallback', 'footer', [ $(oSettings.nTFoot).children('tr')[0], + _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] ); + + var body = $(oSettings.nTBody); + + body.children().detach(); + body.append( $(anRows) ); + + /* Call all required callback functions for the end of a draw */ + _fnCallbackFire( oSettings, 'aoDrawCallback', 'draw', [oSettings] ); + + /* Draw is complete, sorting and filtering must be as well */ + oSettings.bSorted = false; + oSettings.bFiltered = false; + oSettings.bDrawing = false; + } + + + /** + * Redraw the table - taking account of the various features which are enabled + * @param {object} oSettings dataTables settings object + * @param {boolean} [holdPosition] Keep the current paging position. By default + * the paging is reset to the first page + * @memberof DataTable#oApi + */ + function _fnReDraw( settings, holdPosition ) + { + var + features = settings.oFeatures, + sort = features.bSort, + filter = features.bFilter; + + if ( sort ) { + _fnSort( settings ); + } + + if ( filter ) { + _fnFilterComplete( settings, settings.oPreviousSearch ); + } + else { + // No filtering, so we want to just use the display master + settings.aiDisplay = settings.aiDisplayMaster.slice(); + } + + if ( holdPosition !== true ) { + settings._iDisplayStart = 0; + } + + // Let any modules know about the draw hold position state (used by + // scrolling internally) + settings._drawHold = holdPosition; + + _fnDraw( settings ); + + settings._drawHold = false; + } + + + /** + * Add the options to the page HTML for the table + * @param {object} oSettings dataTables settings object + * @memberof DataTable#oApi + */ + function _fnAddOptionsHtml ( oSettings ) + { + var classes = oSettings.oClasses; + var table = $(oSettings.nTable); + var holding = $('<div/>').insertBefore( table ); // Holding element for speed + var features = oSettings.oFeatures; + + // All DataTables are wrapped in a div + var insert = $('<div/>', { + id: oSettings.sTableId+'_wrapper', + 'class': classes.sWrapper + (oSettings.nTFoot ? '' : ' '+classes.sNoFooter) + } ); + + oSettings.nHolding = holding[0]; + oSettings.nTableWrapper = insert[0]; + oSettings.nTableReinsertBefore = oSettings.nTable.nextSibling; + + /* Loop over the user set positioning and place the elements as needed */ + var aDom = oSettings.sDom.split(''); + var featureNode, cOption, nNewNode, cNext, sAttr, j; + for ( var i=0 ; i<aDom.length ; i++ ) + { + featureNode = null; + cOption = aDom[i]; + + if ( cOption == '<' ) + { + /* New container div */ + nNewNode = $('<div/>')[0]; + + /* Check to see if we should append an id and/or a class name to the container */ + cNext = aDom[i+1]; + if ( cNext == "'" || cNext == '"' ) + { + sAttr = ""; + j = 2; + while ( aDom[i+j] != cNext ) + { + sAttr += aDom[i+j]; + j++; + } + + /* Replace jQuery UI constants @todo depreciated */ + if ( sAttr == "H" ) + { + sAttr = classes.sJUIHeader; + } + else if ( sAttr == "F" ) + { + sAttr = classes.sJUIFooter; + } + + /* The attribute can be in the format of "#id.class", "#id" or "class" This logic + * breaks the string into parts and applies them as needed + */ + if ( sAttr.indexOf('.') != -1 ) + { + var aSplit = sAttr.split('.'); + nNewNode.id = aSplit[0].substr(1, aSplit[0].length-1); + nNewNode.className = aSplit[1]; + } + else if ( sAttr.charAt(0) == "#" ) + { + nNewNode.id = sAttr.substr(1, sAttr.length-1); + } + else + { + nNewNode.className = sAttr; + } + + i += j; /* Move along the position array */ + } + + insert.append( nNewNode ); + insert = $(nNewNode); + } + else if ( cOption == '>' ) + { + /* End container div */ + insert = insert.parent(); + } + // @todo Move options into their own plugins? + else if ( cOption == 'l' && features.bPaginate && features.bLengthChange ) + { + /* Length */ + featureNode = _fnFeatureHtmlLength( oSettings ); + } + else if ( cOption == 'f' && features.bFilter ) + { + /* Filter */ + featureNode = _fnFeatureHtmlFilter( oSettings ); + } + else if ( cOption == 'r' && features.bProcessing ) + { + /* pRocessing */ + featureNode = _fnFeatureHtmlProcessing( oSettings ); + } + else if ( cOption == 't' ) + { + /* Table */ + featureNode = _fnFeatureHtmlTable( oSettings ); + } + else if ( cOption == 'i' && features.bInfo ) + { + /* Info */ + featureNode = _fnFeatureHtmlInfo( oSettings ); + } + else if ( cOption == 'p' && features.bPaginate ) + { + /* Pagination */ + featureNode = _fnFeatureHtmlPaginate( oSettings ); + } + else if ( DataTable.ext.feature.length !== 0 ) + { + /* Plug-in features */ + var aoFeatures = DataTable.ext.feature; + for ( var k=0, kLen=aoFeatures.length ; k<kLen ; k++ ) + { + if ( cOption == aoFeatures[k].cFeature ) + { + featureNode = aoFeatures[k].fnInit( oSettings ); + break; + } + } + } + + /* Add to the 2D features array */ + if ( featureNode ) + { + var aanFeatures = oSettings.aanFeatures; + + if ( ! aanFeatures[cOption] ) + { + aanFeatures[cOption] = []; + } + + aanFeatures[cOption].push( featureNode ); + insert.append( featureNode ); + } + } + + /* Built our DOM structure - replace the holding div with what we want */ + holding.replaceWith( insert ); + oSettings.nHolding = null; + } + + + /** + * Use the DOM source to create up an array of header cells. The idea here is to + * create a layout grid (array) of rows x columns, which contains a reference + * to the cell that that point in the grid (regardless of col/rowspan), such that + * any column / row could be removed and the new grid constructed + * @param array {object} aLayout Array to store the calculated layout in + * @param {node} nThead The header/footer element for the table + * @memberof DataTable#oApi + */ + function _fnDetectHeader ( aLayout, nThead ) + { + var nTrs = $(nThead).children('tr'); + var nTr, nCell; + var i, k, l, iLen, jLen, iColShifted, iColumn, iColspan, iRowspan; + var bUnique; + var fnShiftCol = function ( a, i, j ) { + var k = a[i]; + while ( k[j] ) { + j++; + } + return j; + }; + + aLayout.splice( 0, aLayout.length ); + + /* We know how many rows there are in the layout - so prep it */ + for ( i=0, iLen=nTrs.length ; i<iLen ; i++ ) + { + aLayout.push( [] ); + } + + /* Calculate a layout array */ + for ( i=0, iLen=nTrs.length ; i<iLen ; i++ ) + { + nTr = nTrs[i]; + iColumn = 0; + + /* For every cell in the row... */ + nCell = nTr.firstChild; + while ( nCell ) { + if ( nCell.nodeName.toUpperCase() == "TD" || + nCell.nodeName.toUpperCase() == "TH" ) + { + /* Get the col and rowspan attributes from the DOM and sanitise them */ + iColspan = nCell.getAttribute('colspan') * 1; + iRowspan = nCell.getAttribute('rowspan') * 1; + iColspan = (!iColspan || iColspan===0 || iColspan===1) ? 1 : iColspan; + iRowspan = (!iRowspan || iRowspan===0 || iRowspan===1) ? 1 : iRowspan; + + /* There might be colspan cells already in this row, so shift our target + * accordingly + */ + iColShifted = fnShiftCol( aLayout, i, iColumn ); + + /* Cache calculation for unique columns */ + bUnique = iColspan === 1 ? true : false; + + /* If there is col / rowspan, copy the information into the layout grid */ + for ( l=0 ; l<iColspan ; l++ ) + { + for ( k=0 ; k<iRowspan ; k++ ) + { + aLayout[i+k][iColShifted+l] = { + "cell": nCell, + "unique": bUnique + }; + aLayout[i+k].nTr = nTr; + } + } + } + nCell = nCell.nextSibling; + } + } + } + + + /** + * Get an array of unique th elements, one for each column + * @param {object} oSettings dataTables settings object + * @param {node} nHeader automatically detect the layout from this node - optional + * @param {array} aLayout thead/tfoot layout from _fnDetectHeader - optional + * @returns array {node} aReturn list of unique th's + * @memberof DataTable#oApi + */ + function _fnGetUniqueThs ( oSettings, nHeader, aLayout ) + { + var aReturn = []; + if ( !aLayout ) + { + aLayout = oSettings.aoHeader; + if ( nHeader ) + { + aLayout = []; + _fnDetectHeader( aLayout, nHeader ); + } + } + + for ( var i=0, iLen=aLayout.length ; i<iLen ; i++ ) + { + for ( var j=0, jLen=aLayout[i].length ; j<jLen ; j++ ) + { + if ( aLayout[i][j].unique && + (!aReturn[j] || !oSettings.bSortCellsTop) ) + { + aReturn[j] = aLayout[i][j].cell; + } + } + } + + return aReturn; + } + + /** + * Create an Ajax call based on the table's settings, taking into account that + * parameters can have multiple forms, and backwards compatibility. + * + * @param {object} oSettings dataTables settings object + * @param {array} data Data to send to the server, required by + * DataTables - may be augmented by developer callbacks + * @param {function} fn Callback function to run when data is obtained + */ + function _fnBuildAjax( oSettings, data, fn ) + { + // Compatibility with 1.9-, allow fnServerData and event to manipulate + _fnCallbackFire( oSettings, 'aoServerParams', 'serverParams', [data] ); + + // Convert to object based for 1.10+ if using the old array scheme which can + // come from server-side processing or serverParams + if ( data && $.isArray(data) ) { + var tmp = {}; + var rbracket = /(.*?)\[\]$/; + + $.each( data, function (key, val) { + var match = val.name.match(rbracket); + + if ( match ) { + // Support for arrays + var name = match[0]; + + if ( ! tmp[ name ] ) { + tmp[ name ] = []; + } + tmp[ name ].push( val.value ); + } + else { + tmp[val.name] = val.value; + } + } ); + data = tmp; + } + + var ajaxData; + var ajax = oSettings.ajax; + var instance = oSettings.oInstance; + var callback = function ( json ) { + _fnCallbackFire( oSettings, null, 'xhr', [oSettings, json, oSettings.jqXHR] ); + fn( json ); + }; + + if ( $.isPlainObject( ajax ) && ajax.data ) + { + ajaxData = ajax.data; + + var newData = $.isFunction( ajaxData ) ? + ajaxData( data, oSettings ) : // fn can manipulate data or return + ajaxData; // an object object or array to merge + + // If the function returned something, use that alone + data = $.isFunction( ajaxData ) && newData ? + newData : + $.extend( true, data, newData ); + + // Remove the data property as we've resolved it already and don't want + // jQuery to do it again (it is restored at the end of the function) + delete ajax.data; + } + + var baseAjax = { + "data": data, + "success": function (json) { + var error = json.error || json.sError; + if ( error ) { + _fnLog( oSettings, 0, error ); + } + + oSettings.json = json; + callback( json ); + }, + "dataType": "json", + "cache": false, + "type": oSettings.sServerMethod, + "error": function (xhr, error, thrown) { + var ret = _fnCallbackFire( oSettings, null, 'xhr', [oSettings, null, oSettings.jqXHR] ); + + if ( $.inArray( true, ret ) === -1 ) { + if ( error == "parsererror" ) { + _fnLog( oSettings, 0, 'Invalid JSON response', 1 ); + } + else if ( xhr.readyState === 4 ) { + _fnLog( oSettings, 0, 'Ajax error', 7 ); + } + } + + _fnProcessingDisplay( oSettings, false ); + } + }; + + // Store the data submitted for the API + oSettings.oAjaxData = data; + + // Allow plug-ins and external processes to modify the data + _fnCallbackFire( oSettings, null, 'preXhr', [oSettings, data] ); + + if ( oSettings.fnServerData ) + { + // DataTables 1.9- compatibility + oSettings.fnServerData.call( instance, + oSettings.sAjaxSource, + $.map( data, function (val, key) { // Need to convert back to 1.9 trad format + return { name: key, value: val }; + } ), + callback, + oSettings + ); + } + else if ( oSettings.sAjaxSource || typeof ajax === 'string' ) + { + // DataTables 1.9- compatibility + oSettings.jqXHR = $.ajax( $.extend( baseAjax, { + url: ajax || oSettings.sAjaxSource + } ) ); + } + else if ( $.isFunction( ajax ) ) + { + // Is a function - let the caller define what needs to be done + oSettings.jqXHR = ajax.call( instance, data, callback, oSettings ); + } + else + { + // Object to extend the base settings + oSettings.jqXHR = $.ajax( $.extend( baseAjax, ajax ) ); + + // Restore for next time around + ajax.data = ajaxData; + } + } + + + /** + * Update the table using an Ajax call + * @param {object} settings dataTables settings object + * @returns {boolean} Block the table drawing or not + * @memberof DataTable#oApi + */ + function _fnAjaxUpdate( settings ) + { + if ( settings.bAjaxDataGet ) { + settings.iDraw++; + _fnProcessingDisplay( settings, true ); + + _fnBuildAjax( + settings, + _fnAjaxParameters( settings ), + function(json) { + _fnAjaxUpdateDraw( settings, json ); + } + ); + + return false; + } + return true; + } + + + /** + * Build up the parameters in an object needed for a server-side processing + * request. Note that this is basically done twice, is different ways - a modern + * method which is used by default in DataTables 1.10 which uses objects and + * arrays, or the 1.9- method with is name / value pairs. 1.9 method is used if + * the sAjaxSource option is used in the initialisation, or the legacyAjax + * option is set. + * @param {object} oSettings dataTables settings object + * @returns {bool} block the table drawing or not + * @memberof DataTable#oApi + */ + function _fnAjaxParameters( settings ) + { + var + columns = settings.aoColumns, + columnCount = columns.length, + features = settings.oFeatures, + preSearch = settings.oPreviousSearch, + preColSearch = settings.aoPreSearchCols, + i, data = [], dataProp, column, columnSearch, + sort = _fnSortFlatten( settings ), + displayStart = settings._iDisplayStart, + displayLength = features.bPaginate !== false ? + settings._iDisplayLength : + -1; + + var param = function ( name, value ) { + data.push( { 'name': name, 'value': value } ); + }; + + // DataTables 1.9- compatible method + param( 'sEcho', settings.iDraw ); + param( 'iColumns', columnCount ); + param( 'sColumns', _pluck( columns, 'sName' ).join(',') ); + param( 'iDisplayStart', displayStart ); + param( 'iDisplayLength', displayLength ); + + // DataTables 1.10+ method + var d = { + draw: settings.iDraw, + columns: [], + order: [], + start: displayStart, + length: displayLength, + search: { + value: preSearch.sSearch, + regex: preSearch.bRegex + } + }; + + for ( i=0 ; i<columnCount ; i++ ) { + column = columns[i]; + columnSearch = preColSearch[i]; + dataProp = typeof column.mData=="function" ? 'function' : column.mData ; + + d.columns.push( { + data: dataProp, + name: column.sName, + searchable: column.bSearchable, + orderable: column.bSortable, + search: { + value: columnSearch.sSearch, + regex: columnSearch.bRegex + } + } ); + + param( "mDataProp_"+i, dataProp ); + + if ( features.bFilter ) { + param( 'sSearch_'+i, columnSearch.sSearch ); + param( 'bRegex_'+i, columnSearch.bRegex ); + param( 'bSearchable_'+i, column.bSearchable ); + } + + if ( features.bSort ) { + param( 'bSortable_'+i, column.bSortable ); + } + } + + if ( features.bFilter ) { + param( 'sSearch', preSearch.sSearch ); + param( 'bRegex', preSearch.bRegex ); + } + + if ( features.bSort ) { + $.each( sort, function ( i, val ) { + d.order.push( { column: val.col, dir: val.dir } ); + + param( 'iSortCol_'+i, val.col ); + param( 'sSortDir_'+i, val.dir ); + } ); + + param( 'iSortingCols', sort.length ); + } + + // If the legacy.ajax parameter is null, then we automatically decide which + // form to use, based on sAjaxSource + var legacy = DataTable.ext.legacy.ajax; + if ( legacy === null ) { + return settings.sAjaxSource ? data : d; + } + + // Otherwise, if legacy has been specified then we use that to decide on the + // form + return legacy ? data : d; + } + + + /** + * Data the data from the server (nuking the old) and redraw the table + * @param {object} oSettings dataTables settings object + * @param {object} json json data return from the server. + * @param {string} json.sEcho Tracking flag for DataTables to match requests + * @param {int} json.iTotalRecords Number of records in the data set, not accounting for filtering + * @param {int} json.iTotalDisplayRecords Number of records in the data set, accounting for filtering + * @param {array} json.aaData The data to display on this page + * @param {string} [json.sColumns] Column ordering (sName, comma separated) + * @memberof DataTable#oApi + */ + function _fnAjaxUpdateDraw ( settings, json ) + { + // v1.10 uses camelCase variables, while 1.9 uses Hungarian notation. + // Support both + var compat = function ( old, modern ) { + return json[old] !== undefined ? json[old] : json[modern]; + }; + + var data = _fnAjaxDataSrc( settings, json ); + var draw = compat( 'sEcho', 'draw' ); + var recordsTotal = compat( 'iTotalRecords', 'recordsTotal' ); + var recordsFiltered = compat( 'iTotalDisplayRecords', 'recordsFiltered' ); + + if ( draw ) { + // Protect against out of sequence returns + if ( draw*1 < settings.iDraw ) { + return; + } + settings.iDraw = draw * 1; + } + + _fnClearTable( settings ); + settings._iRecordsTotal = parseInt(recordsTotal, 10); + settings._iRecordsDisplay = parseInt(recordsFiltered, 10); + + for ( var i=0, ien=data.length ; i<ien ; i++ ) { + _fnAddData( settings, data[i] ); + } + settings.aiDisplay = settings.aiDisplayMaster.slice(); + + settings.bAjaxDataGet = false; + _fnDraw( settings ); + + if ( ! settings._bInitComplete ) { + _fnInitComplete( settings, json ); + } + + settings.bAjaxDataGet = true; + _fnProcessingDisplay( settings, false ); + } + + + /** + * Get the data from the JSON data source to use for drawing a table. Using + * `_fnGetObjectDataFn` allows the data to be sourced from a property of the + * source object, or from a processing function. + * @param {object} oSettings dataTables settings object + * @param {object} json Data source object / array from the server + * @return {array} Array of data to use + */ + function _fnAjaxDataSrc ( oSettings, json ) + { + var dataSrc = $.isPlainObject( oSettings.ajax ) && oSettings.ajax.dataSrc !== undefined ? + oSettings.ajax.dataSrc : + oSettings.sAjaxDataProp; // Compatibility with 1.9-. + + // Compatibility with 1.9-. In order to read from aaData, check if the + // default has been changed, if not, check for aaData + if ( dataSrc === 'data' ) { + return json.aaData || json[dataSrc]; + } + + return dataSrc !== "" ? + _fnGetObjectDataFn( dataSrc )( json ) : + json; + } + + /** + * Generate the node required for filtering text + * @returns {node} Filter control element + * @param {object} oSettings dataTables settings object + * @memberof DataTable#oApi + */ + function _fnFeatureHtmlFilter ( settings ) + { + var classes = settings.oClasses; + var tableId = settings.sTableId; + var language = settings.oLanguage; + var previousSearch = settings.oPreviousSearch; + var features = settings.aanFeatures; + var input = '<input type="search" class="'+classes.sFilterInput+'"/>'; + + var str = language.sSearch; + str = str.match(/_INPUT_/) ? + str.replace('_INPUT_', input) : + str+input; + + var filter = $('<div/>', { + 'id': ! features.f ? tableId+'_filter' : null, + 'class': classes.sFilter + } ) + .append( $('<label/>' ).append( str ) ); + + var searchFn = function() { + /* Update all other filter input elements for the new display */ + var n = features.f; + var val = !this.value ? "" : this.value; // mental IE8 fix :-( + + /* Now do the filter */ + if ( val != previousSearch.sSearch ) { + _fnFilterComplete( settings, { + "sSearch": val, + "bRegex": previousSearch.bRegex, + "bSmart": previousSearch.bSmart , + "bCaseInsensitive": previousSearch.bCaseInsensitive + } ); + + // Need to redraw, without resorting + settings._iDisplayStart = 0; + _fnDraw( settings ); + } + }; + + var searchDelay = settings.searchDelay !== null ? + settings.searchDelay : + _fnDataSource( settings ) === 'ssp' ? + 400 : + 0; + + var jqFilter = $('input', filter) + .val( previousSearch.sSearch ) + .attr( 'placeholder', language.sSearchPlaceholder ) + .on( + 'keyup.DT search.DT input.DT paste.DT cut.DT', + searchDelay ? + _fnThrottle( searchFn, searchDelay ) : + searchFn + ) + .on( 'keypress.DT', function(e) { + /* Prevent form submission */ + if ( e.keyCode == 13 ) { + return false; + } + } ) + .attr('aria-controls', tableId); + + // Update the input elements whenever the table is filtered + $(settings.nTable).on( 'search.dt.DT', function ( ev, s ) { + if ( settings === s ) { + // IE9 throws an 'unknown error' if document.activeElement is used + // inside an iframe or frame... + try { + if ( jqFilter[0] !== document.activeElement ) { + jqFilter.val( previousSearch.sSearch ); + } + } + catch ( e ) {} + } + } ); + + return filter[0]; + } + + + /** + * Filter the table using both the global filter and column based filtering + * @param {object} oSettings dataTables settings object + * @param {object} oSearch search information + * @param {int} [iForce] force a research of the master array (1) or not (undefined or 0) + * @memberof DataTable#oApi + */ + function _fnFilterComplete ( oSettings, oInput, iForce ) + { + var oPrevSearch = oSettings.oPreviousSearch; + var aoPrevSearch = oSettings.aoPreSearchCols; + var fnSaveFilter = function ( oFilter ) { + /* Save the filtering values */ + oPrevSearch.sSearch = oFilter.sSearch; + oPrevSearch.bRegex = oFilter.bRegex; + oPrevSearch.bSmart = oFilter.bSmart; + oPrevSearch.bCaseInsensitive = oFilter.bCaseInsensitive; + }; + var fnRegex = function ( o ) { + // Backwards compatibility with the bEscapeRegex option + return o.bEscapeRegex !== undefined ? !o.bEscapeRegex : o.bRegex; + }; + + // Resolve any column types that are unknown due to addition or invalidation + // @todo As per sort - can this be moved into an event handler? + _fnColumnTypes( oSettings ); + + /* In server-side processing all filtering is done by the server, so no point hanging around here */ + if ( _fnDataSource( oSettings ) != 'ssp' ) + { + /* Global filter */ + _fnFilter( oSettings, oInput.sSearch, iForce, fnRegex(oInput), oInput.bSmart, oInput.bCaseInsensitive ); + fnSaveFilter( oInput ); + + /* Now do the individual column filter */ + for ( var i=0 ; i<aoPrevSearch.length ; i++ ) + { + _fnFilterColumn( oSettings, aoPrevSearch[i].sSearch, i, fnRegex(aoPrevSearch[i]), + aoPrevSearch[i].bSmart, aoPrevSearch[i].bCaseInsensitive ); + } + + /* Custom filtering */ + _fnFilterCustom( oSettings ); + } + else + { + fnSaveFilter( oInput ); + } + + /* Tell the draw function we have been filtering */ + oSettings.bFiltered = true; + _fnCallbackFire( oSettings, null, 'search', [oSettings] ); + } + + + /** + * Apply custom filtering functions + * @param {object} oSettings dataTables settings object + * @memberof DataTable#oApi + */ + function _fnFilterCustom( settings ) + { + var filters = DataTable.ext.search; + var displayRows = settings.aiDisplay; + var row, rowIdx; + + for ( var i=0, ien=filters.length ; i<ien ; i++ ) { + var rows = []; + + // Loop over each row and see if it should be included + for ( var j=0, jen=displayRows.length ; j<jen ; j++ ) { + rowIdx = displayRows[ j ]; + row = settings.aoData[ rowIdx ]; + + if ( filters[i]( settings, row._aFilterData, rowIdx, row._aData, j ) ) { + rows.push( rowIdx ); + } + } + + // So the array reference doesn't break set the results into the + // existing array + displayRows.length = 0; + $.merge( displayRows, rows ); + } + } + + + /** + * Filter the table on a per-column basis + * @param {object} oSettings dataTables settings object + * @param {string} sInput string to filter on + * @param {int} iColumn column to filter + * @param {bool} bRegex treat search string as a regular expression or not + * @param {bool} bSmart use smart filtering or not + * @param {bool} bCaseInsensitive Do case insenstive matching or not + * @memberof DataTable#oApi + */ + function _fnFilterColumn ( settings, searchStr, colIdx, regex, smart, caseInsensitive ) + { + if ( searchStr === '' ) { + return; + } + + var data; + var out = []; + var display = settings.aiDisplay; + var rpSearch = _fnFilterCreateSearch( searchStr, regex, smart, caseInsensitive ); + + for ( var i=0 ; i<display.length ; i++ ) { + data = settings.aoData[ display[i] ]._aFilterData[ colIdx ]; + + if ( rpSearch.test( data ) ) { + out.push( display[i] ); + } + } + + settings.aiDisplay = out; + } + + + /** + * Filter the data table based on user input and draw the table + * @param {object} settings dataTables settings object + * @param {string} input string to filter on + * @param {int} force optional - force a research of the master array (1) or not (undefined or 0) + * @param {bool} regex treat as a regular expression or not + * @param {bool} smart perform smart filtering or not + * @param {bool} caseInsensitive Do case insenstive matching or not + * @memberof DataTable#oApi + */ + function _fnFilter( settings, input, force, regex, smart, caseInsensitive ) + { + var rpSearch = _fnFilterCreateSearch( input, regex, smart, caseInsensitive ); + var prevSearch = settings.oPreviousSearch.sSearch; + var displayMaster = settings.aiDisplayMaster; + var display, invalidated, i; + var filtered = []; + + // Need to take account of custom filtering functions - always filter + if ( DataTable.ext.search.length !== 0 ) { + force = true; + } + + // Check if any of the rows were invalidated + invalidated = _fnFilterData( settings ); + + // If the input is blank - we just want the full data set + if ( input.length <= 0 ) { + settings.aiDisplay = displayMaster.slice(); + } + else { + // New search - start from the master array + if ( invalidated || + force || + prevSearch.length > input.length || + input.indexOf(prevSearch) !== 0 || + settings.bSorted // On resort, the display master needs to be + // re-filtered since indexes will have changed + ) { + settings.aiDisplay = displayMaster.slice(); + } + + // Search the display array + display = settings.aiDisplay; + + for ( i=0 ; i<display.length ; i++ ) { + if ( rpSearch.test( settings.aoData[ display[i] ]._sFilterRow ) ) { + filtered.push( display[i] ); + } + } + + settings.aiDisplay = filtered; + } + } + + + /** + * Build a regular expression object suitable for searching a table + * @param {string} sSearch string to search for + * @param {bool} bRegex treat as a regular expression or not + * @param {bool} bSmart perform smart filtering or not + * @param {bool} bCaseInsensitive Do case insensitive matching or not + * @returns {RegExp} constructed object + * @memberof DataTable#oApi + */ + function _fnFilterCreateSearch( search, regex, smart, caseInsensitive ) + { + search = regex ? + search : + _fnEscapeRegex( search ); + + if ( smart ) { + /* For smart filtering we want to allow the search to work regardless of + * word order. We also want double quoted text to be preserved, so word + * order is important - a la google. So this is what we want to + * generate: + * + * ^(?=.*?\bone\b)(?=.*?\btwo three\b)(?=.*?\bfour\b).*$ + */ + var a = $.map( search.match( /"[^"]+"|[^ ]+/g ) || [''], function ( word ) { + if ( word.charAt(0) === '"' ) { + var m = word.match( /^"(.*)"$/ ); + word = m ? m[1] : word; + } + + return word.replace('"', ''); + } ); + + search = '^(?=.*?'+a.join( ')(?=.*?' )+').*$'; + } + + return new RegExp( search, caseInsensitive ? 'i' : '' ); + } + + + /** + * Escape a string such that it can be used in a regular expression + * @param {string} sVal string to escape + * @returns {string} escaped string + * @memberof DataTable#oApi + */ + var _fnEscapeRegex = DataTable.util.escapeRegex; + + var __filter_div = $('<div>')[0]; + var __filter_div_textContent = __filter_div.textContent !== undefined; + + // Update the filtering data for each row if needed (by invalidation or first run) + function _fnFilterData ( settings ) + { + var columns = settings.aoColumns; + var column; + var i, j, ien, jen, filterData, cellData, row; + var fomatters = DataTable.ext.type.search; + var wasInvalidated = false; + + for ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) { + row = settings.aoData[i]; + + if ( ! row._aFilterData ) { + filterData = []; + + for ( j=0, jen=columns.length ; j<jen ; j++ ) { + column = columns[j]; + + if ( column.bSearchable ) { + cellData = _fnGetCellData( settings, i, j, 'filter' ); + + if ( fomatters[ column.sType ] ) { + cellData = fomatters[ column.sType ]( cellData ); + } + + // Search in DataTables 1.10 is string based. In 1.11 this + // should be altered to also allow strict type checking. + if ( cellData === null ) { + cellData = ''; + } + + if ( typeof cellData !== 'string' && cellData.toString ) { + cellData = cellData.toString(); + } + } + else { + cellData = ''; + } + + // If it looks like there is an HTML entity in the string, + // attempt to decode it so sorting works as expected. Note that + // we could use a single line of jQuery to do this, but the DOM + // method used here is much faster http://jsperf.com/html-decode + if ( cellData.indexOf && cellData.indexOf('&') !== -1 ) { + __filter_div.innerHTML = cellData; + cellData = __filter_div_textContent ? + __filter_div.textContent : + __filter_div.innerText; + } + + if ( cellData.replace ) { + cellData = cellData.replace(/[\r\n]/g, ''); + } + + filterData.push( cellData ); + } + + row._aFilterData = filterData; + row._sFilterRow = filterData.join(' '); + wasInvalidated = true; + } + } + + return wasInvalidated; + } + + + /** + * Convert from the internal Hungarian notation to camelCase for external + * interaction + * @param {object} obj Object to convert + * @returns {object} Inverted object + * @memberof DataTable#oApi + */ + function _fnSearchToCamel ( obj ) + { + return { + search: obj.sSearch, + smart: obj.bSmart, + regex: obj.bRegex, + caseInsensitive: obj.bCaseInsensitive + }; + } + + + + /** + * Convert from camelCase notation to the internal Hungarian. We could use the + * Hungarian convert function here, but this is cleaner + * @param {object} obj Object to convert + * @returns {object} Inverted object + * @memberof DataTable#oApi + */ + function _fnSearchToHung ( obj ) + { + return { + sSearch: obj.search, + bSmart: obj.smart, + bRegex: obj.regex, + bCaseInsensitive: obj.caseInsensitive + }; + } + + /** + * Generate the node required for the info display + * @param {object} oSettings dataTables settings object + * @returns {node} Information element + * @memberof DataTable#oApi + */ + function _fnFeatureHtmlInfo ( settings ) + { + var + tid = settings.sTableId, + nodes = settings.aanFeatures.i, + n = $('<div/>', { + 'class': settings.oClasses.sInfo, + 'id': ! nodes ? tid+'_info' : null + } ); + + if ( ! nodes ) { + // Update display on each draw + settings.aoDrawCallback.push( { + "fn": _fnUpdateInfo, + "sName": "information" + } ); + + n + .attr( 'role', 'status' ) + .attr( 'aria-live', 'polite' ); + + // Table is described by our info div + $(settings.nTable).attr( 'aria-describedby', tid+'_info' ); + } + + return n[0]; + } + + + /** + * Update the information elements in the display + * @param {object} settings dataTables settings object + * @memberof DataTable#oApi + */ + function _fnUpdateInfo ( settings ) + { + /* Show information about the table */ + var nodes = settings.aanFeatures.i; + if ( nodes.length === 0 ) { + return; + } + + var + lang = settings.oLanguage, + start = settings._iDisplayStart+1, + end = settings.fnDisplayEnd(), + max = settings.fnRecordsTotal(), + total = settings.fnRecordsDisplay(), + out = total ? + lang.sInfo : + lang.sInfoEmpty; + + if ( total !== max ) { + /* Record set after filtering */ + out += ' ' + lang.sInfoFiltered; + } + + // Convert the macros + out += lang.sInfoPostFix; + out = _fnInfoMacros( settings, out ); + + var callback = lang.fnInfoCallback; + if ( callback !== null ) { + out = callback.call( settings.oInstance, + settings, start, end, max, total, out + ); + } + + $(nodes).html( out ); + } + + + function _fnInfoMacros ( settings, str ) + { + // When infinite scrolling, we are always starting at 1. _iDisplayStart is used only + // internally + var + formatter = settings.fnFormatNumber, + start = settings._iDisplayStart+1, + len = settings._iDisplayLength, + vis = settings.fnRecordsDisplay(), + all = len === -1; + + return str. + replace(/_START_/g, formatter.call( settings, start ) ). + replace(/_END_/g, formatter.call( settings, settings.fnDisplayEnd() ) ). + replace(/_MAX_/g, formatter.call( settings, settings.fnRecordsTotal() ) ). + replace(/_TOTAL_/g, formatter.call( settings, vis ) ). + replace(/_PAGE_/g, formatter.call( settings, all ? 1 : Math.ceil( start / len ) ) ). + replace(/_PAGES_/g, formatter.call( settings, all ? 1 : Math.ceil( vis / len ) ) ); + } + + + + /** + * Draw the table for the first time, adding all required features + * @param {object} settings dataTables settings object + * @memberof DataTable#oApi + */ + function _fnInitialise ( settings ) + { + var i, iLen, iAjaxStart=settings.iInitDisplayStart; + var columns = settings.aoColumns, column; + var features = settings.oFeatures; + var deferLoading = settings.bDeferLoading; // value modified by the draw + + /* Ensure that the table data is fully initialised */ + if ( ! settings.bInitialised ) { + setTimeout( function(){ _fnInitialise( settings ); }, 200 ); + return; + } + + /* Show the display HTML options */ + _fnAddOptionsHtml( settings ); + + /* Build and draw the header / footer for the table */ + _fnBuildHead( settings ); + _fnDrawHead( settings, settings.aoHeader ); + _fnDrawHead( settings, settings.aoFooter ); + + /* Okay to show that something is going on now */ + _fnProcessingDisplay( settings, true ); + + /* Calculate sizes for columns */ + if ( features.bAutoWidth ) { + _fnCalculateColumnWidths( settings ); + } + + for ( i=0, iLen=columns.length ; i<iLen ; i++ ) { + column = columns[i]; + + if ( column.sWidth ) { + column.nTh.style.width = _fnStringToCss( column.sWidth ); + } + } + + _fnCallbackFire( settings, null, 'preInit', [settings] ); + + // If there is default sorting required - let's do it. The sort function + // will do the drawing for us. Otherwise we draw the table regardless of the + // Ajax source - this allows the table to look initialised for Ajax sourcing + // data (show 'loading' message possibly) + _fnReDraw( settings ); + + // Server-side processing init complete is done by _fnAjaxUpdateDraw + var dataSrc = _fnDataSource( settings ); + if ( dataSrc != 'ssp' || deferLoading ) { + // if there is an ajax source load the data + if ( dataSrc == 'ajax' ) { + _fnBuildAjax( settings, [], function(json) { + var aData = _fnAjaxDataSrc( settings, json ); + + // Got the data - add it to the table + for ( i=0 ; i<aData.length ; i++ ) { + _fnAddData( settings, aData[i] ); + } + + // Reset the init display for cookie saving. We've already done + // a filter, and therefore cleared it before. So we need to make + // it appear 'fresh' + settings.iInitDisplayStart = iAjaxStart; + + _fnReDraw( settings ); + + _fnProcessingDisplay( settings, false ); + _fnInitComplete( settings, json ); + }, settings ); + } + else { + _fnProcessingDisplay( settings, false ); + _fnInitComplete( settings ); + } + } + } + + + /** + * Draw the table for the first time, adding all required features + * @param {object} oSettings dataTables settings object + * @param {object} [json] JSON from the server that completed the table, if using Ajax source + * with client-side processing (optional) + * @memberof DataTable#oApi + */ + function _fnInitComplete ( settings, json ) + { + settings._bInitComplete = true; + + // When data was added after the initialisation (data or Ajax) we need to + // calculate the column sizing + if ( json || settings.oInit.aaData ) { + _fnAdjustColumnSizing( settings ); + } + + _fnCallbackFire( settings, null, 'plugin-init', [settings, json] ); + _fnCallbackFire( settings, 'aoInitComplete', 'init', [settings, json] ); + } + + + function _fnLengthChange ( settings, val ) + { + var len = parseInt( val, 10 ); + settings._iDisplayLength = len; + + _fnLengthOverflow( settings ); + + // Fire length change event + _fnCallbackFire( settings, null, 'length', [settings, len] ); + } + + + /** + * Generate the node required for user display length changing + * @param {object} settings dataTables settings object + * @returns {node} Display length feature node + * @memberof DataTable#oApi + */ + function _fnFeatureHtmlLength ( settings ) + { + var + classes = settings.oClasses, + tableId = settings.sTableId, + menu = settings.aLengthMenu, + d2 = $.isArray( menu[0] ), + lengths = d2 ? menu[0] : menu, + language = d2 ? menu[1] : menu; + + var select = $('<select/>', { + 'name': tableId+'_length', + 'aria-controls': tableId, + 'class': classes.sLengthSelect + } ); + + for ( var i=0, ien=lengths.length ; i<ien ; i++ ) { + select[0][ i ] = new Option( language[i], lengths[i] ); + } + + var div = $('<div><label/></div>').addClass( classes.sLength ); + if ( ! settings.aanFeatures.l ) { + div[0].id = tableId+'_length'; + } + + div.children().append( + settings.oLanguage.sLengthMenu.replace( '_MENU_', select[0].outerHTML ) + ); + + // Can't use `select` variable as user might provide their own and the + // reference is broken by the use of outerHTML + $('select', div) + .val( settings._iDisplayLength ) + .on( 'change.DT', function(e) { + _fnLengthChange( settings, $(this).val() ); + _fnDraw( settings ); + } ); + + // Update node value whenever anything changes the table's length + $(settings.nTable).on( 'length.dt.DT', function (e, s, len) { + if ( settings === s ) { + $('select', div).val( len ); + } + } ); + + return div[0]; + } + + + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Note that most of the paging logic is done in + * DataTable.ext.pager + */ + + /** + * Generate the node required for default pagination + * @param {object} oSettings dataTables settings object + * @returns {node} Pagination feature node + * @memberof DataTable#oApi + */ + function _fnFeatureHtmlPaginate ( settings ) + { + var + type = settings.sPaginationType, + plugin = DataTable.ext.pager[ type ], + modern = typeof plugin === 'function', + redraw = function( settings ) { + _fnDraw( settings ); + }, + node = $('<div/>').addClass( settings.oClasses.sPaging + type )[0], + features = settings.aanFeatures; + + if ( ! modern ) { + plugin.fnInit( settings, node, redraw ); + } + + /* Add a draw callback for the pagination on first instance, to update the paging display */ + if ( ! features.p ) + { + node.id = settings.sTableId+'_paginate'; + + settings.aoDrawCallback.push( { + "fn": function( settings ) { + if ( modern ) { + var + start = settings._iDisplayStart, + len = settings._iDisplayLength, + visRecords = settings.fnRecordsDisplay(), + all = len === -1, + page = all ? 0 : Math.ceil( start / len ), + pages = all ? 1 : Math.ceil( visRecords / len ), + buttons = plugin(page, pages), + i, ien; + + for ( i=0, ien=features.p.length ; i<ien ; i++ ) { + _fnRenderer( settings, 'pageButton' )( + settings, features.p[i], i, buttons, page, pages + ); + } + } + else { + plugin.fnUpdate( settings, redraw ); + } + }, + "sName": "pagination" + } ); + } + + return node; + } + + + /** + * Alter the display settings to change the page + * @param {object} settings DataTables settings object + * @param {string|int} action Paging action to take: "first", "previous", + * "next" or "last" or page number to jump to (integer) + * @param [bool] redraw Automatically draw the update or not + * @returns {bool} true page has changed, false - no change + * @memberof DataTable#oApi + */ + function _fnPageChange ( settings, action, redraw ) + { + var + start = settings._iDisplayStart, + len = settings._iDisplayLength, + records = settings.fnRecordsDisplay(); + + if ( records === 0 || len === -1 ) + { + start = 0; + } + else if ( typeof action === "number" ) + { + start = action * len; + + if ( start > records ) + { + start = 0; + } + } + else if ( action == "first" ) + { + start = 0; + } + else if ( action == "previous" ) + { + start = len >= 0 ? + start - len : + 0; + + if ( start < 0 ) + { + start = 0; + } + } + else if ( action == "next" ) + { + if ( start + len < records ) + { + start += len; + } + } + else if ( action == "last" ) + { + start = Math.floor( (records-1) / len) * len; + } + else + { + _fnLog( settings, 0, "Unknown paging action: "+action, 5 ); + } + + var changed = settings._iDisplayStart !== start; + settings._iDisplayStart = start; + + if ( changed ) { + _fnCallbackFire( settings, null, 'page', [settings] ); + + if ( redraw ) { + _fnDraw( settings ); + } + } + + return changed; + } + + + + /** + * Generate the node required for the processing node + * @param {object} settings dataTables settings object + * @returns {node} Processing element + * @memberof DataTable#oApi + */ + function _fnFeatureHtmlProcessing ( settings ) + { + return $('<div/>', { + 'id': ! settings.aanFeatures.r ? settings.sTableId+'_processing' : null, + 'class': settings.oClasses.sProcessing + } ) + .html( settings.oLanguage.sProcessing ) + .insertBefore( settings.nTable )[0]; + } + + + /** + * Display or hide the processing indicator + * @param {object} settings dataTables settings object + * @param {bool} show Show the processing indicator (true) or not (false) + * @memberof DataTable#oApi + */ + function _fnProcessingDisplay ( settings, show ) + { + if ( settings.oFeatures.bProcessing ) { + $(settings.aanFeatures.r).css( 'display', show ? 'block' : 'none' ); + } + + _fnCallbackFire( settings, null, 'processing', [settings, show] ); + } + + /** + * Add any control elements for the table - specifically scrolling + * @param {object} settings dataTables settings object + * @returns {node} Node to add to the DOM + * @memberof DataTable#oApi + */ + function _fnFeatureHtmlTable ( settings ) + { + var table = $(settings.nTable); + + // Add the ARIA grid role to the table + table.attr( 'role', 'grid' ); + + // Scrolling from here on in + var scroll = settings.oScroll; + + if ( scroll.sX === '' && scroll.sY === '' ) { + return settings.nTable; + } + + var scrollX = scroll.sX; + var scrollY = scroll.sY; + var classes = settings.oClasses; + var caption = table.children('caption'); + var captionSide = caption.length ? caption[0]._captionSide : null; + var headerClone = $( table[0].cloneNode(false) ); + var footerClone = $( table[0].cloneNode(false) ); + var footer = table.children('tfoot'); + var _div = '<div/>'; + var size = function ( s ) { + return !s ? null : _fnStringToCss( s ); + }; + + if ( ! footer.length ) { + footer = null; + } + + /* + * The HTML structure that we want to generate in this function is: + * div - scroller + * div - scroll head + * div - scroll head inner + * table - scroll head table + * thead - thead + * div - scroll body + * table - table (master table) + * thead - thead clone for sizing + * tbody - tbody + * div - scroll foot + * div - scroll foot inner + * table - scroll foot table + * tfoot - tfoot + */ + var scroller = $( _div, { 'class': classes.sScrollWrapper } ) + .append( + $(_div, { 'class': classes.sScrollHead } ) + .css( { + overflow: 'hidden', + position: 'relative', + border: 0, + width: scrollX ? size(scrollX) : '100%' + } ) + .append( + $(_div, { 'class': classes.sScrollHeadInner } ) + .css( { + 'box-sizing': 'content-box', + width: scroll.sXInner || '100%' + } ) + .append( + headerClone + .removeAttr('id') + .css( 'margin-left', 0 ) + .append( captionSide === 'top' ? caption : null ) + .append( + table.children('thead') + ) + ) + ) + ) + .append( + $(_div, { 'class': classes.sScrollBody } ) + .css( { + position: 'relative', + overflow: 'auto', + width: size( scrollX ) + } ) + .append( table ) + ); + + if ( footer ) { + scroller.append( + $(_div, { 'class': classes.sScrollFoot } ) + .css( { + overflow: 'hidden', + border: 0, + width: scrollX ? size(scrollX) : '100%' + } ) + .append( + $(_div, { 'class': classes.sScrollFootInner } ) + .append( + footerClone + .removeAttr('id') + .css( 'margin-left', 0 ) + .append( captionSide === 'bottom' ? caption : null ) + .append( + table.children('tfoot') + ) + ) + ) + ); + } + + var children = scroller.children(); + var scrollHead = children[0]; + var scrollBody = children[1]; + var scrollFoot = footer ? children[2] : null; + + // When the body is scrolled, then we also want to scroll the headers + if ( scrollX ) { + $(scrollBody).on( 'scroll.DT', function (e) { + var scrollLeft = this.scrollLeft; + + scrollHead.scrollLeft = scrollLeft; + + if ( footer ) { + scrollFoot.scrollLeft = scrollLeft; + } + } ); + } + + $(scrollBody).css( + scrollY && scroll.bCollapse ? 'max-height' : 'height', + scrollY + ); + + settings.nScrollHead = scrollHead; + settings.nScrollBody = scrollBody; + settings.nScrollFoot = scrollFoot; + + // On redraw - align columns + settings.aoDrawCallback.push( { + "fn": _fnScrollDraw, + "sName": "scrolling" + } ); + + return scroller[0]; + } + + + + /** + * Update the header, footer and body tables for resizing - i.e. column + * alignment. + * + * Welcome to the most horrible function DataTables. The process that this + * function follows is basically: + * 1. Re-create the table inside the scrolling div + * 2. Take live measurements from the DOM + * 3. Apply the measurements to align the columns + * 4. Clean up + * + * @param {object} settings dataTables settings object + * @memberof DataTable#oApi + */ + function _fnScrollDraw ( settings ) + { + // Given that this is such a monster function, a lot of variables are use + // to try and keep the minimised size as small as possible + var + scroll = settings.oScroll, + scrollX = scroll.sX, + scrollXInner = scroll.sXInner, + scrollY = scroll.sY, + barWidth = scroll.iBarWidth, + divHeader = $(settings.nScrollHead), + divHeaderStyle = divHeader[0].style, + divHeaderInner = divHeader.children('div'), + divHeaderInnerStyle = divHeaderInner[0].style, + divHeaderTable = divHeaderInner.children('table'), + divBodyEl = settings.nScrollBody, + divBody = $(divBodyEl), + divBodyStyle = divBodyEl.style, + divFooter = $(settings.nScrollFoot), + divFooterInner = divFooter.children('div'), + divFooterTable = divFooterInner.children('table'), + header = $(settings.nTHead), + table = $(settings.nTable), + tableEl = table[0], + tableStyle = tableEl.style, + footer = settings.nTFoot ? $(settings.nTFoot) : null, + browser = settings.oBrowser, + ie67 = browser.bScrollOversize, + dtHeaderCells = _pluck( settings.aoColumns, 'nTh' ), + headerTrgEls, footerTrgEls, + headerSrcEls, footerSrcEls, + headerCopy, footerCopy, + headerWidths=[], footerWidths=[], + headerContent=[], footerContent=[], + idx, correction, sanityWidth, + zeroOut = function(nSizer) { + var style = nSizer.style; + style.paddingTop = "0"; + style.paddingBottom = "0"; + style.borderTopWidth = "0"; + style.borderBottomWidth = "0"; + style.height = 0; + }; + + // If the scrollbar visibility has changed from the last draw, we need to + // adjust the column sizes as the table width will have changed to account + // for the scrollbar + var scrollBarVis = divBodyEl.scrollHeight > divBodyEl.clientHeight; + + if ( settings.scrollBarVis !== scrollBarVis && settings.scrollBarVis !== undefined ) { + settings.scrollBarVis = scrollBarVis; + _fnAdjustColumnSizing( settings ); + return; // adjust column sizing will call this function again + } + else { + settings.scrollBarVis = scrollBarVis; + } + + /* + * 1. Re-create the table inside the scrolling div + */ + + // Remove the old minimised thead and tfoot elements in the inner table + table.children('thead, tfoot').remove(); + + if ( footer ) { + footerCopy = footer.clone().prependTo( table ); + footerTrgEls = footer.find('tr'); // the original tfoot is in its own table and must be sized + footerSrcEls = footerCopy.find('tr'); + } + + // Clone the current header and footer elements and then place it into the inner table + headerCopy = header.clone().prependTo( table ); + headerTrgEls = header.find('tr'); // original header is in its own table + headerSrcEls = headerCopy.find('tr'); + headerCopy.find('th, td').removeAttr('tabindex'); + + + /* + * 2. Take live measurements from the DOM - do not alter the DOM itself! + */ + + // Remove old sizing and apply the calculated column widths + // Get the unique column headers in the newly created (cloned) header. We want to apply the + // calculated sizes to this header + if ( ! scrollX ) + { + divBodyStyle.width = '100%'; + divHeader[0].style.width = '100%'; + } + + $.each( _fnGetUniqueThs( settings, headerCopy ), function ( i, el ) { + idx = _fnVisibleToColumnIndex( settings, i ); + el.style.width = settings.aoColumns[idx].sWidth; + } ); + + if ( footer ) { + _fnApplyToChildren( function(n) { + n.style.width = ""; + }, footerSrcEls ); + } + + // Size the table as a whole + sanityWidth = table.outerWidth(); + if ( scrollX === "" ) { + // No x scrolling + tableStyle.width = "100%"; + + // IE7 will make the width of the table when 100% include the scrollbar + // - which is shouldn't. When there is a scrollbar we need to take this + // into account. + if ( ie67 && (table.find('tbody').height() > divBodyEl.offsetHeight || + divBody.css('overflow-y') == "scroll") + ) { + tableStyle.width = _fnStringToCss( table.outerWidth() - barWidth); + } + + // Recalculate the sanity width + sanityWidth = table.outerWidth(); + } + else if ( scrollXInner !== "" ) { + // legacy x scroll inner has been given - use it + tableStyle.width = _fnStringToCss(scrollXInner); + + // Recalculate the sanity width + sanityWidth = table.outerWidth(); + } + + // Hidden header should have zero height, so remove padding and borders. Then + // set the width based on the real headers + + // Apply all styles in one pass + _fnApplyToChildren( zeroOut, headerSrcEls ); + + // Read all widths in next pass + _fnApplyToChildren( function(nSizer) { + headerContent.push( nSizer.innerHTML ); + headerWidths.push( _fnStringToCss( $(nSizer).css('width') ) ); + }, headerSrcEls ); + + // Apply all widths in final pass + _fnApplyToChildren( function(nToSize, i) { + // Only apply widths to the DataTables detected header cells - this + // prevents complex headers from having contradictory sizes applied + if ( $.inArray( nToSize, dtHeaderCells ) !== -1 ) { + nToSize.style.width = headerWidths[i]; + } + }, headerTrgEls ); + + $(headerSrcEls).height(0); + + /* Same again with the footer if we have one */ + if ( footer ) + { + _fnApplyToChildren( zeroOut, footerSrcEls ); + + _fnApplyToChildren( function(nSizer) { + footerContent.push( nSizer.innerHTML ); + footerWidths.push( _fnStringToCss( $(nSizer).css('width') ) ); + }, footerSrcEls ); + + _fnApplyToChildren( function(nToSize, i) { + nToSize.style.width = footerWidths[i]; + }, footerTrgEls ); + + $(footerSrcEls).height(0); + } + + + /* + * 3. Apply the measurements + */ + + // "Hide" the header and footer that we used for the sizing. We need to keep + // the content of the cell so that the width applied to the header and body + // both match, but we want to hide it completely. We want to also fix their + // width to what they currently are + _fnApplyToChildren( function(nSizer, i) { + nSizer.innerHTML = '<div class="dataTables_sizing" style="height:0;overflow:hidden;">'+headerContent[i]+'</div>'; + nSizer.style.width = headerWidths[i]; + }, headerSrcEls ); + + if ( footer ) + { + _fnApplyToChildren( function(nSizer, i) { + nSizer.innerHTML = '<div class="dataTables_sizing" style="height:0;overflow:hidden;">'+footerContent[i]+'</div>'; + nSizer.style.width = footerWidths[i]; + }, footerSrcEls ); + } + + // Sanity check that the table is of a sensible width. If not then we are going to get + // misalignment - try to prevent this by not allowing the table to shrink below its min width + if ( table.outerWidth() < sanityWidth ) + { + // The min width depends upon if we have a vertical scrollbar visible or not */ + correction = ((divBodyEl.scrollHeight > divBodyEl.offsetHeight || + divBody.css('overflow-y') == "scroll")) ? + sanityWidth+barWidth : + sanityWidth; + + // IE6/7 are a law unto themselves... + if ( ie67 && (divBodyEl.scrollHeight > + divBodyEl.offsetHeight || divBody.css('overflow-y') == "scroll") + ) { + tableStyle.width = _fnStringToCss( correction-barWidth ); + } + + // And give the user a warning that we've stopped the table getting too small + if ( scrollX === "" || scrollXInner !== "" ) { + _fnLog( settings, 1, 'Possible column misalignment', 6 ); + } + } + else + { + correction = '100%'; + } + + // Apply to the container elements + divBodyStyle.width = _fnStringToCss( correction ); + divHeaderStyle.width = _fnStringToCss( correction ); + + if ( footer ) { + settings.nScrollFoot.style.width = _fnStringToCss( correction ); + } + + + /* + * 4. Clean up + */ + if ( ! scrollY ) { + /* IE7< puts a vertical scrollbar in place (when it shouldn't be) due to subtracting + * the scrollbar height from the visible display, rather than adding it on. We need to + * set the height in order to sort this. Don't want to do it in any other browsers. + */ + if ( ie67 ) { + divBodyStyle.height = _fnStringToCss( tableEl.offsetHeight+barWidth ); + } + } + + /* Finally set the width's of the header and footer tables */ + var iOuterWidth = table.outerWidth(); + divHeaderTable[0].style.width = _fnStringToCss( iOuterWidth ); + divHeaderInnerStyle.width = _fnStringToCss( iOuterWidth ); + + // Figure out if there are scrollbar present - if so then we need a the header and footer to + // provide a bit more space to allow "overflow" scrolling (i.e. past the scrollbar) + var bScrolling = table.height() > divBodyEl.clientHeight || divBody.css('overflow-y') == "scroll"; + var padding = 'padding' + (browser.bScrollbarLeft ? 'Left' : 'Right' ); + divHeaderInnerStyle[ padding ] = bScrolling ? barWidth+"px" : "0px"; + + if ( footer ) { + divFooterTable[0].style.width = _fnStringToCss( iOuterWidth ); + divFooterInner[0].style.width = _fnStringToCss( iOuterWidth ); + divFooterInner[0].style[padding] = bScrolling ? barWidth+"px" : "0px"; + } + + // Correct DOM ordering for colgroup - comes before the thead + table.children('colgroup').insertBefore( table.children('thead') ); + + /* Adjust the position of the header in case we loose the y-scrollbar */ + divBody.scroll(); + + // If sorting or filtering has occurred, jump the scrolling back to the top + // only if we aren't holding the position + if ( (settings.bSorted || settings.bFiltered) && ! settings._drawHold ) { + divBodyEl.scrollTop = 0; + } + } + + + + /** + * Apply a given function to the display child nodes of an element array (typically + * TD children of TR rows + * @param {function} fn Method to apply to the objects + * @param array {nodes} an1 List of elements to look through for display children + * @param array {nodes} an2 Another list (identical structure to the first) - optional + * @memberof DataTable#oApi + */ + function _fnApplyToChildren( fn, an1, an2 ) + { + var index=0, i=0, iLen=an1.length; + var nNode1, nNode2; + + while ( i < iLen ) { + nNode1 = an1[i].firstChild; + nNode2 = an2 ? an2[i].firstChild : null; + + while ( nNode1 ) { + if ( nNode1.nodeType === 1 ) { + if ( an2 ) { + fn( nNode1, nNode2, index ); + } + else { + fn( nNode1, index ); + } + + index++; + } + + nNode1 = nNode1.nextSibling; + nNode2 = an2 ? nNode2.nextSibling : null; + } + + i++; + } + } + + + + var __re_html_remove = /<.*?>/g; + + + /** + * Calculate the width of columns for the table + * @param {object} oSettings dataTables settings object + * @memberof DataTable#oApi + */ + function _fnCalculateColumnWidths ( oSettings ) + { + var + table = oSettings.nTable, + columns = oSettings.aoColumns, + scroll = oSettings.oScroll, + scrollY = scroll.sY, + scrollX = scroll.sX, + scrollXInner = scroll.sXInner, + columnCount = columns.length, + visibleColumns = _fnGetColumns( oSettings, 'bVisible' ), + headerCells = $('th', oSettings.nTHead), + tableWidthAttr = table.getAttribute('width'), // from DOM element + tableContainer = table.parentNode, + userInputs = false, + i, column, columnIdx, width, outerWidth, + browser = oSettings.oBrowser, + ie67 = browser.bScrollOversize; + + var styleWidth = table.style.width; + if ( styleWidth && styleWidth.indexOf('%') !== -1 ) { + tableWidthAttr = styleWidth; + } + + /* Convert any user input sizes into pixel sizes */ + for ( i=0 ; i<visibleColumns.length ; i++ ) { + column = columns[ visibleColumns[i] ]; + + if ( column.sWidth !== null ) { + column.sWidth = _fnConvertToWidth( column.sWidthOrig, tableContainer ); + + userInputs = true; + } + } + + /* If the number of columns in the DOM equals the number that we have to + * process in DataTables, then we can use the offsets that are created by + * the web- browser. No custom sizes can be set in order for this to happen, + * nor scrolling used + */ + if ( ie67 || ! userInputs && ! scrollX && ! scrollY && + columnCount == _fnVisbleColumns( oSettings ) && + columnCount == headerCells.length + ) { + for ( i=0 ; i<columnCount ; i++ ) { + var colIdx = _fnVisibleToColumnIndex( oSettings, i ); + + if ( colIdx !== null ) { + columns[ colIdx ].sWidth = _fnStringToCss( headerCells.eq(i).width() ); + } + } + } + else + { + // Otherwise construct a single row, worst case, table with the widest + // node in the data, assign any user defined widths, then insert it into + // the DOM and allow the browser to do all the hard work of calculating + // table widths + var tmpTable = $(table).clone() // don't use cloneNode - IE8 will remove events on the main table + .css( 'visibility', 'hidden' ) + .removeAttr( 'id' ); + + // Clean up the table body + tmpTable.find('tbody tr').remove(); + var tr = $('<tr/>').appendTo( tmpTable.find('tbody') ); + + // Clone the table header and footer - we can't use the header / footer + // from the cloned table, since if scrolling is active, the table's + // real header and footer are contained in different table tags + tmpTable.find('thead, tfoot').remove(); + tmpTable + .append( $(oSettings.nTHead).clone() ) + .append( $(oSettings.nTFoot).clone() ); + + // Remove any assigned widths from the footer (from scrolling) + tmpTable.find('tfoot th, tfoot td').css('width', ''); + + // Apply custom sizing to the cloned header + headerCells = _fnGetUniqueThs( oSettings, tmpTable.find('thead')[0] ); + + for ( i=0 ; i<visibleColumns.length ; i++ ) { + column = columns[ visibleColumns[i] ]; + + headerCells[i].style.width = column.sWidthOrig !== null && column.sWidthOrig !== '' ? + _fnStringToCss( column.sWidthOrig ) : + ''; + + // For scrollX we need to force the column width otherwise the + // browser will collapse it. If this width is smaller than the + // width the column requires, then it will have no effect + if ( column.sWidthOrig && scrollX ) { + $( headerCells[i] ).append( $('<div/>').css( { + width: column.sWidthOrig, + margin: 0, + padding: 0, + border: 0, + height: 1 + } ) ); + } + } + + // Find the widest cell for each column and put it into the table + if ( oSettings.aoData.length ) { + for ( i=0 ; i<visibleColumns.length ; i++ ) { + columnIdx = visibleColumns[i]; + column = columns[ columnIdx ]; + + $( _fnGetWidestNode( oSettings, columnIdx ) ) + .clone( false ) + .append( column.sContentPadding ) + .appendTo( tr ); + } + } + + // Tidy the temporary table - remove name attributes so there aren't + // duplicated in the dom (radio elements for example) + $('[name]', tmpTable).removeAttr('name'); + + // Table has been built, attach to the document so we can work with it. + // A holding element is used, positioned at the top of the container + // with minimal height, so it has no effect on if the container scrolls + // or not. Otherwise it might trigger scrolling when it actually isn't + // needed + var holder = $('<div/>').css( scrollX || scrollY ? + { + position: 'absolute', + top: 0, + left: 0, + height: 1, + right: 0, + overflow: 'hidden' + } : + {} + ) + .append( tmpTable ) + .appendTo( tableContainer ); + + // When scrolling (X or Y) we want to set the width of the table as + // appropriate. However, when not scrolling leave the table width as it + // is. This results in slightly different, but I think correct behaviour + if ( scrollX && scrollXInner ) { + tmpTable.width( scrollXInner ); + } + else if ( scrollX ) { + tmpTable.css( 'width', 'auto' ); + tmpTable.removeAttr('width'); + + // If there is no width attribute or style, then allow the table to + // collapse + if ( tmpTable.width() < tableContainer.clientWidth && tableWidthAttr ) { + tmpTable.width( tableContainer.clientWidth ); + } + } + else if ( scrollY ) { + tmpTable.width( tableContainer.clientWidth ); + } + else if ( tableWidthAttr ) { + tmpTable.width( tableWidthAttr ); + } + + // Get the width of each column in the constructed table - we need to + // know the inner width (so it can be assigned to the other table's + // cells) and the outer width so we can calculate the full width of the + // table. This is safe since DataTables requires a unique cell for each + // column, but if ever a header can span multiple columns, this will + // need to be modified. + var total = 0; + for ( i=0 ; i<visibleColumns.length ; i++ ) { + var cell = $(headerCells[i]); + var border = cell.outerWidth() - cell.width(); + + // Use getBounding... where possible (not IE8-) because it can give + // sub-pixel accuracy, which we then want to round up! + var bounding = browser.bBounding ? + Math.ceil( headerCells[i].getBoundingClientRect().width ) : + cell.outerWidth(); + + // Total is tracked to remove any sub-pixel errors as the outerWidth + // of the table might not equal the total given here (IE!). + total += bounding; + + // Width for each column to use + columns[ visibleColumns[i] ].sWidth = _fnStringToCss( bounding - border ); + } + + table.style.width = _fnStringToCss( total ); + + // Finished with the table - ditch it + holder.remove(); + } + + // If there is a width attr, we want to attach an event listener which + // allows the table sizing to automatically adjust when the window is + // resized. Use the width attr rather than CSS, since we can't know if the + // CSS is a relative value or absolute - DOM read is always px. + if ( tableWidthAttr ) { + table.style.width = _fnStringToCss( tableWidthAttr ); + } + + if ( (tableWidthAttr || scrollX) && ! oSettings._reszEvt ) { + var bindResize = function () { + $(window).on('resize.DT-'+oSettings.sInstance, _fnThrottle( function () { + _fnAdjustColumnSizing( oSettings ); + } ) ); + }; + + // IE6/7 will crash if we bind a resize event handler on page load. + // To be removed in 1.11 which drops IE6/7 support + if ( ie67 ) { + setTimeout( bindResize, 1000 ); + } + else { + bindResize(); + } + + oSettings._reszEvt = true; + } + } + + + /** + * Throttle the calls to a function. Arguments and context are maintained for + * the throttled function + * @param {function} fn Function to be called + * @param {int} [freq=200] call frequency in mS + * @returns {function} wrapped function + * @memberof DataTable#oApi + */ + var _fnThrottle = DataTable.util.throttle; + + + /** + * Convert a CSS unit width to pixels (e.g. 2em) + * @param {string} width width to be converted + * @param {node} parent parent to get the with for (required for relative widths) - optional + * @returns {int} width in pixels + * @memberof DataTable#oApi + */ + function _fnConvertToWidth ( width, parent ) + { + if ( ! width ) { + return 0; + } + + var n = $('<div/>') + .css( 'width', _fnStringToCss( width ) ) + .appendTo( parent || document.body ); + + var val = n[0].offsetWidth; + n.remove(); + + return val; + } + + + /** + * Get the widest node + * @param {object} settings dataTables settings object + * @param {int} colIdx column of interest + * @returns {node} widest table node + * @memberof DataTable#oApi + */ + function _fnGetWidestNode( settings, colIdx ) + { + var idx = _fnGetMaxLenString( settings, colIdx ); + if ( idx < 0 ) { + return null; + } + + var data = settings.aoData[ idx ]; + return ! data.nTr ? // Might not have been created when deferred rendering + $('<td/>').html( _fnGetCellData( settings, idx, colIdx, 'display' ) )[0] : + data.anCells[ colIdx ]; + } + + + /** + * Get the maximum strlen for each data column + * @param {object} settings dataTables settings object + * @param {int} colIdx column of interest + * @returns {string} max string length for each column + * @memberof DataTable#oApi + */ + function _fnGetMaxLenString( settings, colIdx ) + { + var s, max=-1, maxIdx = -1; + + for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) { + s = _fnGetCellData( settings, i, colIdx, 'display' )+''; + s = s.replace( __re_html_remove, '' ); + s = s.replace( / /g, ' ' ); + + if ( s.length > max ) { + max = s.length; + maxIdx = i; + } + } + + return maxIdx; + } + + + /** + * Append a CSS unit (only if required) to a string + * @param {string} value to css-ify + * @returns {string} value with css unit + * @memberof DataTable#oApi + */ + function _fnStringToCss( s ) + { + if ( s === null ) { + return '0px'; + } + + if ( typeof s == 'number' ) { + return s < 0 ? + '0px' : + s+'px'; + } + + // Check it has a unit character already + return s.match(/\d$/) ? + s+'px' : + s; + } + + + + function _fnSortFlatten ( settings ) + { + var + i, iLen, k, kLen, + aSort = [], + aiOrig = [], + aoColumns = settings.aoColumns, + aDataSort, iCol, sType, srcCol, + fixed = settings.aaSortingFixed, + fixedObj = $.isPlainObject( fixed ), + nestedSort = [], + add = function ( a ) { + if ( a.length && ! $.isArray( a[0] ) ) { + // 1D array + nestedSort.push( a ); + } + else { + // 2D array + $.merge( nestedSort, a ); + } + }; + + // Build the sort array, with pre-fix and post-fix options if they have been + // specified + if ( $.isArray( fixed ) ) { + add( fixed ); + } + + if ( fixedObj && fixed.pre ) { + add( fixed.pre ); + } + + add( settings.aaSorting ); + + if (fixedObj && fixed.post ) { + add( fixed.post ); + } + + for ( i=0 ; i<nestedSort.length ; i++ ) + { + srcCol = nestedSort[i][0]; + aDataSort = aoColumns[ srcCol ].aDataSort; + + for ( k=0, kLen=aDataSort.length ; k<kLen ; k++ ) + { + iCol = aDataSort[k]; + sType = aoColumns[ iCol ].sType || 'string'; + + if ( nestedSort[i]._idx === undefined ) { + nestedSort[i]._idx = $.inArray( nestedSort[i][1], aoColumns[iCol].asSorting ); + } + + aSort.push( { + src: srcCol, + col: iCol, + dir: nestedSort[i][1], + index: nestedSort[i]._idx, + type: sType, + formatter: DataTable.ext.type.order[ sType+"-pre" ] + } ); + } + } + + return aSort; + } + + /** + * Change the order of the table + * @param {object} oSettings dataTables settings object + * @memberof DataTable#oApi + * @todo This really needs split up! + */ + function _fnSort ( oSettings ) + { + var + i, ien, iLen, j, jLen, k, kLen, + sDataType, nTh, + aiOrig = [], + oExtSort = DataTable.ext.type.order, + aoData = oSettings.aoData, + aoColumns = oSettings.aoColumns, + aDataSort, data, iCol, sType, oSort, + formatters = 0, + sortCol, + displayMaster = oSettings.aiDisplayMaster, + aSort; + + // Resolve any column types that are unknown due to addition or invalidation + // @todo Can this be moved into a 'data-ready' handler which is called when + // data is going to be used in the table? + _fnColumnTypes( oSettings ); + + aSort = _fnSortFlatten( oSettings ); + + for ( i=0, ien=aSort.length ; i<ien ; i++ ) { + sortCol = aSort[i]; + + // Track if we can use the fast sort algorithm + if ( sortCol.formatter ) { + formatters++; + } + + // Load the data needed for the sort, for each cell + _fnSortData( oSettings, sortCol.col ); + } + + /* No sorting required if server-side or no sorting array */ + if ( _fnDataSource( oSettings ) != 'ssp' && aSort.length !== 0 ) + { + // Create a value - key array of the current row positions such that we can use their + // current position during the sort, if values match, in order to perform stable sorting + for ( i=0, iLen=displayMaster.length ; i<iLen ; i++ ) { + aiOrig[ displayMaster[i] ] = i; + } + + /* Do the sort - here we want multi-column sorting based on a given data source (column) + * and sorting function (from oSort) in a certain direction. It's reasonably complex to + * follow on it's own, but this is what we want (example two column sorting): + * fnLocalSorting = function(a,b){ + * var iTest; + * iTest = oSort['string-asc']('data11', 'data12'); + * if (iTest !== 0) + * return iTest; + * iTest = oSort['numeric-desc']('data21', 'data22'); + * if (iTest !== 0) + * return iTest; + * return oSort['numeric-asc']( aiOrig[a], aiOrig[b] ); + * } + * Basically we have a test for each sorting column, if the data in that column is equal, + * test the next column. If all columns match, then we use a numeric sort on the row + * positions in the original data array to provide a stable sort. + * + * Note - I know it seems excessive to have two sorting methods, but the first is around + * 15% faster, so the second is only maintained for backwards compatibility with sorting + * methods which do not have a pre-sort formatting function. + */ + if ( formatters === aSort.length ) { + // All sort types have formatting functions + displayMaster.sort( function ( a, b ) { + var + x, y, k, test, sort, + len=aSort.length, + dataA = aoData[a]._aSortData, + dataB = aoData[b]._aSortData; + + for ( k=0 ; k<len ; k++ ) { + sort = aSort[k]; + + x = dataA[ sort.col ]; + y = dataB[ sort.col ]; + + test = x<y ? -1 : x>y ? 1 : 0; + if ( test !== 0 ) { + return sort.dir === 'asc' ? test : -test; + } + } + + x = aiOrig[a]; + y = aiOrig[b]; + return x<y ? -1 : x>y ? 1 : 0; + } ); + } + else { + // Depreciated - remove in 1.11 (providing a plug-in option) + // Not all sort types have formatting methods, so we have to call their sorting + // methods. + displayMaster.sort( function ( a, b ) { + var + x, y, k, l, test, sort, fn, + len=aSort.length, + dataA = aoData[a]._aSortData, + dataB = aoData[b]._aSortData; + + for ( k=0 ; k<len ; k++ ) { + sort = aSort[k]; + + x = dataA[ sort.col ]; + y = dataB[ sort.col ]; + + fn = oExtSort[ sort.type+"-"+sort.dir ] || oExtSort[ "string-"+sort.dir ]; + test = fn( x, y ); + if ( test !== 0 ) { + return test; + } + } + + x = aiOrig[a]; + y = aiOrig[b]; + return x<y ? -1 : x>y ? 1 : 0; + } ); + } + } + + /* Tell the draw function that we have sorted the data */ + oSettings.bSorted = true; + } + + + function _fnSortAria ( settings ) + { + var label; + var nextSort; + var columns = settings.aoColumns; + var aSort = _fnSortFlatten( settings ); + var oAria = settings.oLanguage.oAria; + + // ARIA attributes - need to loop all columns, to update all (removing old + // attributes as needed) + for ( var i=0, iLen=columns.length ; i<iLen ; i++ ) + { + var col = columns[i]; + var asSorting = col.asSorting; + var sTitle = col.sTitle.replace( /<.*?>/g, "" ); + var th = col.nTh; + + // IE7 is throwing an error when setting these properties with jQuery's + // attr() and removeAttr() methods... + th.removeAttribute('aria-sort'); + + /* In ARIA only the first sorting column can be marked as sorting - no multi-sort option */ + if ( col.bSortable ) { + if ( aSort.length > 0 && aSort[0].col == i ) { + th.setAttribute('aria-sort', aSort[0].dir=="asc" ? "ascending" : "descending" ); + nextSort = asSorting[ aSort[0].index+1 ] || asSorting[0]; + } + else { + nextSort = asSorting[0]; + } + + label = sTitle + ( nextSort === "asc" ? + oAria.sSortAscending : + oAria.sSortDescending + ); + } + else { + label = sTitle; + } + + th.setAttribute('aria-label', label); + } + } + + + /** + * Function to run on user sort request + * @param {object} settings dataTables settings object + * @param {node} attachTo node to attach the handler to + * @param {int} colIdx column sorting index + * @param {boolean} [append=false] Append the requested sort to the existing + * sort if true (i.e. multi-column sort) + * @param {function} [callback] callback function + * @memberof DataTable#oApi + */ + function _fnSortListener ( settings, colIdx, append, callback ) + { + var col = settings.aoColumns[ colIdx ]; + var sorting = settings.aaSorting; + var asSorting = col.asSorting; + var nextSortIdx; + var next = function ( a, overflow ) { + var idx = a._idx; + if ( idx === undefined ) { + idx = $.inArray( a[1], asSorting ); + } + + return idx+1 < asSorting.length ? + idx+1 : + overflow ? + null : + 0; + }; + + // Convert to 2D array if needed + if ( typeof sorting[0] === 'number' ) { + sorting = settings.aaSorting = [ sorting ]; + } + + // If appending the sort then we are multi-column sorting + if ( append && settings.oFeatures.bSortMulti ) { + // Are we already doing some kind of sort on this column? + var sortIdx = $.inArray( colIdx, _pluck(sorting, '0') ); + + if ( sortIdx !== -1 ) { + // Yes, modify the sort + nextSortIdx = next( sorting[sortIdx], true ); + + if ( nextSortIdx === null && sorting.length === 1 ) { + nextSortIdx = 0; // can't remove sorting completely + } + + if ( nextSortIdx === null ) { + sorting.splice( sortIdx, 1 ); + } + else { + sorting[sortIdx][1] = asSorting[ nextSortIdx ]; + sorting[sortIdx]._idx = nextSortIdx; + } + } + else { + // No sort on this column yet + sorting.push( [ colIdx, asSorting[0], 0 ] ); + sorting[sorting.length-1]._idx = 0; + } + } + else if ( sorting.length && sorting[0][0] == colIdx ) { + // Single column - already sorting on this column, modify the sort + nextSortIdx = next( sorting[0] ); + + sorting.length = 1; + sorting[0][1] = asSorting[ nextSortIdx ]; + sorting[0]._idx = nextSortIdx; + } + else { + // Single column - sort only on this column + sorting.length = 0; + sorting.push( [ colIdx, asSorting[0] ] ); + sorting[0]._idx = 0; + } + + // Run the sort by calling a full redraw + _fnReDraw( settings ); + + // callback used for async user interaction + if ( typeof callback == 'function' ) { + callback( settings ); + } + } + + + /** + * Attach a sort handler (click) to a node + * @param {object} settings dataTables settings object + * @param {node} attachTo node to attach the handler to + * @param {int} colIdx column sorting index + * @param {function} [callback] callback function + * @memberof DataTable#oApi + */ + function _fnSortAttachListener ( settings, attachTo, colIdx, callback ) + { + var col = settings.aoColumns[ colIdx ]; + + _fnBindAction( attachTo, {}, function (e) { + /* If the column is not sortable - don't to anything */ + if ( col.bSortable === false ) { + return; + } + + // If processing is enabled use a timeout to allow the processing + // display to be shown - otherwise to it synchronously + if ( settings.oFeatures.bProcessing ) { + _fnProcessingDisplay( settings, true ); + + setTimeout( function() { + _fnSortListener( settings, colIdx, e.shiftKey, callback ); + + // In server-side processing, the draw callback will remove the + // processing display + if ( _fnDataSource( settings ) !== 'ssp' ) { + _fnProcessingDisplay( settings, false ); + } + }, 0 ); + } + else { + _fnSortListener( settings, colIdx, e.shiftKey, callback ); + } + } ); + } + + + /** + * Set the sorting classes on table's body, Note: it is safe to call this function + * when bSort and bSortClasses are false + * @param {object} oSettings dataTables settings object + * @memberof DataTable#oApi + */ + function _fnSortingClasses( settings ) + { + var oldSort = settings.aLastSort; + var sortClass = settings.oClasses.sSortColumn; + var sort = _fnSortFlatten( settings ); + var features = settings.oFeatures; + var i, ien, colIdx; + + if ( features.bSort && features.bSortClasses ) { + // Remove old sorting classes + for ( i=0, ien=oldSort.length ; i<ien ; i++ ) { + colIdx = oldSort[i].src; + + // Remove column sorting + $( _pluck( settings.aoData, 'anCells', colIdx ) ) + .removeClass( sortClass + (i<2 ? i+1 : 3) ); + } + + // Add new column sorting + for ( i=0, ien=sort.length ; i<ien ; i++ ) { + colIdx = sort[i].src; + + $( _pluck( settings.aoData, 'anCells', colIdx ) ) + .addClass( sortClass + (i<2 ? i+1 : 3) ); + } + } + + settings.aLastSort = sort; + } + + + // Get the data to sort a column, be it from cache, fresh (populating the + // cache), or from a sort formatter + function _fnSortData( settings, idx ) + { + // Custom sorting function - provided by the sort data type + var column = settings.aoColumns[ idx ]; + var customSort = DataTable.ext.order[ column.sSortDataType ]; + var customData; + + if ( customSort ) { + customData = customSort.call( settings.oInstance, settings, idx, + _fnColumnIndexToVisible( settings, idx ) + ); + } + + // Use / populate cache + var row, cellData; + var formatter = DataTable.ext.type.order[ column.sType+"-pre" ]; + + for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) { + row = settings.aoData[i]; + + if ( ! row._aSortData ) { + row._aSortData = []; + } + + if ( ! row._aSortData[idx] || customSort ) { + cellData = customSort ? + customData[i] : // If there was a custom sort function, use data from there + _fnGetCellData( settings, i, idx, 'sort' ); + + row._aSortData[ idx ] = formatter ? + formatter( cellData ) : + cellData; + } + } + } + + + + /** + * Save the state of a table + * @param {object} oSettings dataTables settings object + * @memberof DataTable#oApi + */ + function _fnSaveState ( settings ) + { + if ( !settings.oFeatures.bStateSave || settings.bDestroying ) + { + return; + } + + /* Store the interesting variables */ + var state = { + time: +new Date(), + start: settings._iDisplayStart, + length: settings._iDisplayLength, + order: $.extend( true, [], settings.aaSorting ), + search: _fnSearchToCamel( settings.oPreviousSearch ), + columns: $.map( settings.aoColumns, function ( col, i ) { + return { + visible: col.bVisible, + search: _fnSearchToCamel( settings.aoPreSearchCols[i] ) + }; + } ) + }; + + _fnCallbackFire( settings, "aoStateSaveParams", 'stateSaveParams', [settings, state] ); + + settings.oSavedState = state; + settings.fnStateSaveCallback.call( settings.oInstance, settings, state ); + } + + + /** + * Attempt to load a saved table state + * @param {object} oSettings dataTables settings object + * @param {object} oInit DataTables init object so we can override settings + * @param {function} callback Callback to execute when the state has been loaded + * @memberof DataTable#oApi + */ + function _fnLoadState ( settings, oInit, callback ) + { + var i, ien; + var columns = settings.aoColumns; + var loaded = function ( s ) { + if ( ! s || ! s.time ) { + callback(); + return; + } + + // Allow custom and plug-in manipulation functions to alter the saved data set and + // cancelling of loading by returning false + var abStateLoad = _fnCallbackFire( settings, 'aoStateLoadParams', 'stateLoadParams', [settings, state] ); + if ( $.inArray( false, abStateLoad ) !== -1 ) { + callback(); + return; + } + + // Reject old data + var duration = settings.iStateDuration; + if ( duration > 0 && s.time < +new Date() - (duration*1000) ) { + callback(); + return; + } + + // Number of columns have changed - all bets are off, no restore of settings + if ( s.columns && columns.length !== s.columns.length ) { + callback(); + return; + } + + // Store the saved state so it might be accessed at any time + settings.oLoadedState = $.extend( true, {}, state ); + + // Restore key features - todo - for 1.11 this needs to be done by + // subscribed events + if ( s.start !== undefined ) { + settings._iDisplayStart = s.start; + settings.iInitDisplayStart = s.start; + } + if ( s.length !== undefined ) { + settings._iDisplayLength = s.length; + } + + // Order + if ( s.order !== undefined ) { + settings.aaSorting = []; + $.each( s.order, function ( i, col ) { + settings.aaSorting.push( col[0] >= columns.length ? + [ 0, col[1] ] : + col + ); + } ); + } + + // Search + if ( s.search !== undefined ) { + $.extend( settings.oPreviousSearch, _fnSearchToHung( s.search ) ); + } + + // Columns + // + if ( s.columns ) { + for ( i=0, ien=s.columns.length ; i<ien ; i++ ) { + var col = s.columns[i]; + + // Visibility + if ( col.visible !== undefined ) { + columns[i].bVisible = col.visible; + } + + // Search + if ( col.search !== undefined ) { + $.extend( settings.aoPreSearchCols[i], _fnSearchToHung( col.search ) ); + } + } + } + + _fnCallbackFire( settings, 'aoStateLoaded', 'stateLoaded', [settings, state] ); + callback(); + } + + if ( ! settings.oFeatures.bStateSave ) { + callback(); + return; + } + + var state = settings.fnStateLoadCallback.call( settings.oInstance, settings, loaded ); + + if ( state !== undefined ) { + loaded( state ); + } + // otherwise, wait for the loaded callback to be executed + } + + + /** + * Return the settings object for a particular table + * @param {node} table table we are using as a dataTable + * @returns {object} Settings object - or null if not found + * @memberof DataTable#oApi + */ + function _fnSettingsFromNode ( table ) + { + var settings = DataTable.settings; + var idx = $.inArray( table, _pluck( settings, 'nTable' ) ); + + return idx !== -1 ? + settings[ idx ] : + null; + } + + + /** + * Log an error message + * @param {object} settings dataTables settings object + * @param {int} level log error messages, or display them to the user + * @param {string} msg error message + * @param {int} tn Technical note id to get more information about the error. + * @memberof DataTable#oApi + */ + function _fnLog( settings, level, msg, tn ) + { + msg = 'DataTables warning: '+ + (settings ? 'table id='+settings.sTableId+' - ' : '')+msg; + + if ( tn ) { + msg += '. For more information about this error, please see '+ + 'http://datatables.net/tn/'+tn; + } + + if ( ! level ) { + // Backwards compatibility pre 1.10 + var ext = DataTable.ext; + var type = ext.sErrMode || ext.errMode; + + if ( settings ) { + _fnCallbackFire( settings, null, 'error', [ settings, tn, msg ] ); + } + + if ( type == 'alert' ) { + alert( msg ); + } + else if ( type == 'throw' ) { + throw new Error(msg); + } + else if ( typeof type == 'function' ) { + type( settings, tn, msg ); + } + } + else if ( window.console && console.log ) { + console.log( msg ); + } + } + + + /** + * See if a property is defined on one object, if so assign it to the other object + * @param {object} ret target object + * @param {object} src source object + * @param {string} name property + * @param {string} [mappedName] name to map too - optional, name used if not given + * @memberof DataTable#oApi + */ + function _fnMap( ret, src, name, mappedName ) + { + if ( $.isArray( name ) ) { + $.each( name, function (i, val) { + if ( $.isArray( val ) ) { + _fnMap( ret, src, val[0], val[1] ); + } + else { + _fnMap( ret, src, val ); + } + } ); + + return; + } + + if ( mappedName === undefined ) { + mappedName = name; + } + + if ( src[name] !== undefined ) { + ret[mappedName] = src[name]; + } + } + + + /** + * Extend objects - very similar to jQuery.extend, but deep copy objects, and + * shallow copy arrays. The reason we need to do this, is that we don't want to + * deep copy array init values (such as aaSorting) since the dev wouldn't be + * able to override them, but we do want to deep copy arrays. + * @param {object} out Object to extend + * @param {object} extender Object from which the properties will be applied to + * out + * @param {boolean} breakRefs If true, then arrays will be sliced to take an + * independent copy with the exception of the `data` or `aaData` parameters + * if they are present. This is so you can pass in a collection to + * DataTables and have that used as your data source without breaking the + * references + * @returns {object} out Reference, just for convenience - out === the return. + * @memberof DataTable#oApi + * @todo This doesn't take account of arrays inside the deep copied objects. + */ + function _fnExtend( out, extender, breakRefs ) + { + var val; + + for ( var prop in extender ) { + if ( extender.hasOwnProperty(prop) ) { + val = extender[prop]; + + if ( $.isPlainObject( val ) ) { + if ( ! $.isPlainObject( out[prop] ) ) { + out[prop] = {}; + } + $.extend( true, out[prop], val ); + } + else if ( breakRefs && prop !== 'data' && prop !== 'aaData' && $.isArray(val) ) { + out[prop] = val.slice(); + } + else { + out[prop] = val; + } + } + } + + return out; + } + + + /** + * Bind an event handers to allow a click or return key to activate the callback. + * This is good for accessibility since a return on the keyboard will have the + * same effect as a click, if the element has focus. + * @param {element} n Element to bind the action to + * @param {object} oData Data object to pass to the triggered function + * @param {function} fn Callback function for when the event is triggered + * @memberof DataTable#oApi + */ + function _fnBindAction( n, oData, fn ) + { + $(n) + .on( 'click.DT', oData, function (e) { + n.blur(); // Remove focus outline for mouse users + fn(e); + } ) + .on( 'keypress.DT', oData, function (e){ + if ( e.which === 13 ) { + e.preventDefault(); + fn(e); + } + } ) + .on( 'selectstart.DT', function () { + /* Take the brutal approach to cancelling text selection */ + return false; + } ); + } + + + /** + * Register a callback function. Easily allows a callback function to be added to + * an array store of callback functions that can then all be called together. + * @param {object} oSettings dataTables settings object + * @param {string} sStore Name of the array storage for the callbacks in oSettings + * @param {function} fn Function to be called back + * @param {string} sName Identifying name for the callback (i.e. a label) + * @memberof DataTable#oApi + */ + function _fnCallbackReg( oSettings, sStore, fn, sName ) + { + if ( fn ) + { + oSettings[sStore].push( { + "fn": fn, + "sName": sName + } ); + } + } + + + /** + * Fire callback functions and trigger events. Note that the loop over the + * callback array store is done backwards! Further note that you do not want to + * fire off triggers in time sensitive applications (for example cell creation) + * as its slow. + * @param {object} settings dataTables settings object + * @param {string} callbackArr Name of the array storage for the callbacks in + * oSettings + * @param {string} eventName Name of the jQuery custom event to trigger. If + * null no trigger is fired + * @param {array} args Array of arguments to pass to the callback function / + * trigger + * @memberof DataTable#oApi + */ + function _fnCallbackFire( settings, callbackArr, eventName, args ) + { + var ret = []; + + if ( callbackArr ) { + ret = $.map( settings[callbackArr].slice().reverse(), function (val, i) { + return val.fn.apply( settings.oInstance, args ); + } ); + } + + if ( eventName !== null ) { + var e = $.Event( eventName+'.dt' ); + + $(settings.nTable).trigger( e, args ); + + ret.push( e.result ); + } + + return ret; + } + + + function _fnLengthOverflow ( settings ) + { + var + start = settings._iDisplayStart, + end = settings.fnDisplayEnd(), + len = settings._iDisplayLength; + + /* If we have space to show extra rows (backing up from the end point - then do so */ + if ( start >= end ) + { + start = end - len; + } + + // Keep the start record on the current page + start -= (start % len); + + if ( len === -1 || start < 0 ) + { + start = 0; + } + + settings._iDisplayStart = start; + } + + + function _fnRenderer( settings, type ) + { + var renderer = settings.renderer; + var host = DataTable.ext.renderer[type]; + + if ( $.isPlainObject( renderer ) && renderer[type] ) { + // Specific renderer for this type. If available use it, otherwise use + // the default. + return host[renderer[type]] || host._; + } + else if ( typeof renderer === 'string' ) { + // Common renderer - if there is one available for this type use it, + // otherwise use the default + return host[renderer] || host._; + } + + // Use the default + return host._; + } + + + /** + * Detect the data source being used for the table. Used to simplify the code + * a little (ajax) and to make it compress a little smaller. + * + * @param {object} settings dataTables settings object + * @returns {string} Data source + * @memberof DataTable#oApi + */ + function _fnDataSource ( settings ) + { + if ( settings.oFeatures.bServerSide ) { + return 'ssp'; + } + else if ( settings.ajax || settings.sAjaxSource ) { + return 'ajax'; + } + return 'dom'; + } + + + + + /** + * Computed structure of the DataTables API, defined by the options passed to + * `DataTable.Api.register()` when building the API. + * + * The structure is built in order to speed creation and extension of the Api + * objects since the extensions are effectively pre-parsed. + * + * The array is an array of objects with the following structure, where this + * base array represents the Api prototype base: + * + * [ + * { + * name: 'data' -- string - Property name + * val: function () {}, -- function - Api method (or undefined if just an object + * methodExt: [ ... ], -- array - Array of Api object definitions to extend the method result + * propExt: [ ... ] -- array - Array of Api object definitions to extend the property + * }, + * { + * name: 'row' + * val: {}, + * methodExt: [ ... ], + * propExt: [ + * { + * name: 'data' + * val: function () {}, + * methodExt: [ ... ], + * propExt: [ ... ] + * }, + * ... + * ] + * } + * ] + * + * @type {Array} + * @ignore + */ + var __apiStruct = []; + + + /** + * `Array.prototype` reference. + * + * @type object + * @ignore + */ + var __arrayProto = Array.prototype; + + + /** + * Abstraction for `context` parameter of the `Api` constructor to allow it to + * take several different forms for ease of use. + * + * Each of the input parameter types will be converted to a DataTables settings + * object where possible. + * + * @param {string|node|jQuery|object} mixed DataTable identifier. Can be one + * of: + * + * * `string` - jQuery selector. Any DataTables' matching the given selector + * with be found and used. + * * `node` - `TABLE` node which has already been formed into a DataTable. + * * `jQuery` - A jQuery object of `TABLE` nodes. + * * `object` - DataTables settings object + * * `DataTables.Api` - API instance + * @return {array|null} Matching DataTables settings objects. `null` or + * `undefined` is returned if no matching DataTable is found. + * @ignore + */ + var _toSettings = function ( mixed ) + { + var idx, jq; + var settings = DataTable.settings; + var tables = $.map( settings, function (el, i) { + return el.nTable; + } ); + + if ( ! mixed ) { + return []; + } + else if ( mixed.nTable && mixed.oApi ) { + // DataTables settings object + return [ mixed ]; + } + else if ( mixed.nodeName && mixed.nodeName.toLowerCase() === 'table' ) { + // Table node + idx = $.inArray( mixed, tables ); + return idx !== -1 ? [ settings[idx] ] : null; + } + else if ( mixed && typeof mixed.settings === 'function' ) { + return mixed.settings().toArray(); + } + else if ( typeof mixed === 'string' ) { + // jQuery selector + jq = $(mixed); + } + else if ( mixed instanceof $ ) { + // jQuery object (also DataTables instance) + jq = mixed; + } + + if ( jq ) { + return jq.map( function(i) { + idx = $.inArray( this, tables ); + return idx !== -1 ? settings[idx] : null; + } ).toArray(); + } + }; + + + /** + * DataTables API class - used to control and interface with one or more + * DataTables enhanced tables. + * + * The API class is heavily based on jQuery, presenting a chainable interface + * that you can use to interact with tables. Each instance of the API class has + * a "context" - i.e. the tables that it will operate on. This could be a single + * table, all tables on a page or a sub-set thereof. + * + * Additionally the API is designed to allow you to easily work with the data in + * the tables, retrieving and manipulating it as required. This is done by + * presenting the API class as an array like interface. The contents of the + * array depend upon the actions requested by each method (for example + * `rows().nodes()` will return an array of nodes, while `rows().data()` will + * return an array of objects or arrays depending upon your table's + * configuration). The API object has a number of array like methods (`push`, + * `pop`, `reverse` etc) as well as additional helper methods (`each`, `pluck`, + * `unique` etc) to assist your working with the data held in a table. + * + * Most methods (those which return an Api instance) are chainable, which means + * the return from a method call also has all of the methods available that the + * top level object had. For example, these two calls are equivalent: + * + * // Not chained + * api.row.add( {...} ); + * api.draw(); + * + * // Chained + * api.row.add( {...} ).draw(); + * + * @class DataTable.Api + * @param {array|object|string|jQuery} context DataTable identifier. This is + * used to define which DataTables enhanced tables this API will operate on. + * Can be one of: + * + * * `string` - jQuery selector. Any DataTables' matching the given selector + * with be found and used. + * * `node` - `TABLE` node which has already been formed into a DataTable. + * * `jQuery` - A jQuery object of `TABLE` nodes. + * * `object` - DataTables settings object + * @param {array} [data] Data to initialise the Api instance with. + * + * @example + * // Direct initialisation during DataTables construction + * var api = $('#example').DataTable(); + * + * @example + * // Initialisation using a DataTables jQuery object + * var api = $('#example').dataTable().api(); + * + * @example + * // Initialisation as a constructor + * var api = new $.fn.DataTable.Api( 'table.dataTable' ); + */ + _Api = function ( context, data ) + { + if ( ! (this instanceof _Api) ) { + return new _Api( context, data ); + } + + var settings = []; + var ctxSettings = function ( o ) { + var a = _toSettings( o ); + if ( a ) { + settings = settings.concat( a ); + } + }; + + if ( $.isArray( context ) ) { + for ( var i=0, ien=context.length ; i<ien ; i++ ) { + ctxSettings( context[i] ); + } + } + else { + ctxSettings( context ); + } + + // Remove duplicates + this.context = _unique( settings ); + + // Initial data + if ( data ) { + $.merge( this, data ); + } + + // selector + this.selector = { + rows: null, + cols: null, + opts: null + }; + + _Api.extend( this, this, __apiStruct ); + }; + + DataTable.Api = _Api; + + // Don't destroy the existing prototype, just extend it. Required for jQuery 2's + // isPlainObject. + $.extend( _Api.prototype, { + any: function () + { + return this.count() !== 0; + }, + + + concat: __arrayProto.concat, + + + context: [], // array of table settings objects + + + count: function () + { + return this.flatten().length; + }, + + + each: function ( fn ) + { + for ( var i=0, ien=this.length ; i<ien; i++ ) { + fn.call( this, this[i], i, this ); + } + + return this; + }, + + + eq: function ( idx ) + { + var ctx = this.context; + + return ctx.length > idx ? + new _Api( ctx[idx], this[idx] ) : + null; + }, + + + filter: function ( fn ) + { + var a = []; + + if ( __arrayProto.filter ) { + a = __arrayProto.filter.call( this, fn, this ); + } + else { + // Compatibility for browsers without EMCA-252-5 (JS 1.6) + for ( var i=0, ien=this.length ; i<ien ; i++ ) { + if ( fn.call( this, this[i], i, this ) ) { + a.push( this[i] ); + } + } + } + + return new _Api( this.context, a ); + }, + + + flatten: function () + { + var a = []; + return new _Api( this.context, a.concat.apply( a, this.toArray() ) ); + }, + + + join: __arrayProto.join, + + + indexOf: __arrayProto.indexOf || function (obj, start) + { + for ( var i=(start || 0), ien=this.length ; i<ien ; i++ ) { + if ( this[i] === obj ) { + return i; + } + } + return -1; + }, + + iterator: function ( flatten, type, fn, alwaysNew ) { + var + a = [], ret, + i, ien, j, jen, + context = this.context, + rows, items, item, + selector = this.selector; + + // Argument shifting + if ( typeof flatten === 'string' ) { + alwaysNew = fn; + fn = type; + type = flatten; + flatten = false; + } + + for ( i=0, ien=context.length ; i<ien ; i++ ) { + var apiInst = new _Api( context[i] ); + + if ( type === 'table' ) { + ret = fn.call( apiInst, context[i], i ); + + if ( ret !== undefined ) { + a.push( ret ); + } + } + else if ( type === 'columns' || type === 'rows' ) { + // this has same length as context - one entry for each table + ret = fn.call( apiInst, context[i], this[i], i ); + + if ( ret !== undefined ) { + a.push( ret ); + } + } + else if ( type === 'column' || type === 'column-rows' || type === 'row' || type === 'cell' ) { + // columns and rows share the same structure. + // 'this' is an array of column indexes for each context + items = this[i]; + + if ( type === 'column-rows' ) { + rows = _selector_row_indexes( context[i], selector.opts ); + } + + for ( j=0, jen=items.length ; j<jen ; j++ ) { + item = items[j]; + + if ( type === 'cell' ) { + ret = fn.call( apiInst, context[i], item.row, item.column, i, j ); + } + else { + ret = fn.call( apiInst, context[i], item, i, j, rows ); + } + + if ( ret !== undefined ) { + a.push( ret ); + } + } + } + } + + if ( a.length || alwaysNew ) { + var api = new _Api( context, flatten ? a.concat.apply( [], a ) : a ); + var apiSelector = api.selector; + apiSelector.rows = selector.rows; + apiSelector.cols = selector.cols; + apiSelector.opts = selector.opts; + return api; + } + return this; + }, + + + lastIndexOf: __arrayProto.lastIndexOf || function (obj, start) + { + // Bit cheeky... + return this.indexOf.apply( this.toArray.reverse(), arguments ); + }, + + + length: 0, + + + map: function ( fn ) + { + var a = []; + + if ( __arrayProto.map ) { + a = __arrayProto.map.call( this, fn, this ); + } + else { + // Compatibility for browsers without EMCA-252-5 (JS 1.6) + for ( var i=0, ien=this.length ; i<ien ; i++ ) { + a.push( fn.call( this, this[i], i ) ); + } + } + + return new _Api( this.context, a ); + }, + + + pluck: function ( prop ) + { + return this.map( function ( el ) { + return el[ prop ]; + } ); + }, + + pop: __arrayProto.pop, + + + push: __arrayProto.push, + + + // Does not return an API instance + reduce: __arrayProto.reduce || function ( fn, init ) + { + return _fnReduce( this, fn, init, 0, this.length, 1 ); + }, + + + reduceRight: __arrayProto.reduceRight || function ( fn, init ) + { + return _fnReduce( this, fn, init, this.length-1, -1, -1 ); + }, + + + reverse: __arrayProto.reverse, + + + // Object with rows, columns and opts + selector: null, + + + shift: __arrayProto.shift, + + + sort: __arrayProto.sort, // ? name - order? + + + splice: __arrayProto.splice, + + + toArray: function () + { + return __arrayProto.slice.call( this ); + }, + + + to$: function () + { + return $( this ); + }, + + + toJQuery: function () + { + return $( this ); + }, + + + unique: function () + { + return new _Api( this.context, _unique(this) ); + }, + + + unshift: __arrayProto.unshift + } ); + + + _Api.extend = function ( scope, obj, ext ) + { + // Only extend API instances and static properties of the API + if ( ! ext.length || ! obj || ( ! (obj instanceof _Api) && ! obj.__dt_wrapper ) ) { + return; + } + + var + i, ien, + j, jen, + struct, inner, + methodScoping = function ( scope, fn, struc ) { + return function () { + var ret = fn.apply( scope, arguments ); + + // Method extension + _Api.extend( ret, ret, struc.methodExt ); + return ret; + }; + }; + + for ( i=0, ien=ext.length ; i<ien ; i++ ) { + struct = ext[i]; + + // Value + obj[ struct.name ] = typeof struct.val === 'function' ? + methodScoping( scope, struct.val, struct ) : + $.isPlainObject( struct.val ) ? + {} : + struct.val; + + obj[ struct.name ].__dt_wrapper = true; + + // Property extension + _Api.extend( scope, obj[ struct.name ], struct.propExt ); + } + }; + + + // @todo - Is there need for an augment function? + // _Api.augment = function ( inst, name ) + // { + // // Find src object in the structure from the name + // var parts = name.split('.'); + + // _Api.extend( inst, obj ); + // }; + + + // [ + // { + // name: 'data' -- string - Property name + // val: function () {}, -- function - Api method (or undefined if just an object + // methodExt: [ ... ], -- array - Array of Api object definitions to extend the method result + // propExt: [ ... ] -- array - Array of Api object definitions to extend the property + // }, + // { + // name: 'row' + // val: {}, + // methodExt: [ ... ], + // propExt: [ + // { + // name: 'data' + // val: function () {}, + // methodExt: [ ... ], + // propExt: [ ... ] + // }, + // ... + // ] + // } + // ] + + _Api.register = _api_register = function ( name, val ) + { + if ( $.isArray( name ) ) { + for ( var j=0, jen=name.length ; j<jen ; j++ ) { + _Api.register( name[j], val ); + } + return; + } + + var + i, ien, + heir = name.split('.'), + struct = __apiStruct, + key, method; + + var find = function ( src, name ) { + for ( var i=0, ien=src.length ; i<ien ; i++ ) { + if ( src[i].name === name ) { + return src[i]; + } + } + return null; + }; + + for ( i=0, ien=heir.length ; i<ien ; i++ ) { + method = heir[i].indexOf('()') !== -1; + key = method ? + heir[i].replace('()', '') : + heir[i]; + + var src = find( struct, key ); + if ( ! src ) { + src = { + name: key, + val: {}, + methodExt: [], + propExt: [] + }; + struct.push( src ); + } + + if ( i === ien-1 ) { + src.val = val; + } + else { + struct = method ? + src.methodExt : + src.propExt; + } + } + }; + + + _Api.registerPlural = _api_registerPlural = function ( pluralName, singularName, val ) { + _Api.register( pluralName, val ); + + _Api.register( singularName, function () { + var ret = val.apply( this, arguments ); + + if ( ret === this ) { + // Returned item is the API instance that was passed in, return it + return this; + } + else if ( ret instanceof _Api ) { + // New API instance returned, want the value from the first item + // in the returned array for the singular result. + return ret.length ? + $.isArray( ret[0] ) ? + new _Api( ret.context, ret[0] ) : // Array results are 'enhanced' + ret[0] : + undefined; + } + + // Non-API return - just fire it back + return ret; + } ); + }; + + + /** + * Selector for HTML tables. Apply the given selector to the give array of + * DataTables settings objects. + * + * @param {string|integer} [selector] jQuery selector string or integer + * @param {array} Array of DataTables settings objects to be filtered + * @return {array} + * @ignore + */ + var __table_selector = function ( selector, a ) + { + // Integer is used to pick out a table by index + if ( typeof selector === 'number' ) { + return [ a[ selector ] ]; + } + + // Perform a jQuery selector on the table nodes + var nodes = $.map( a, function (el, i) { + return el.nTable; + } ); + + return $(nodes) + .filter( selector ) + .map( function (i) { + // Need to translate back from the table node to the settings + var idx = $.inArray( this, nodes ); + return a[ idx ]; + } ) + .toArray(); + }; + + + + /** + * Context selector for the API's context (i.e. the tables the API instance + * refers to. + * + * @name DataTable.Api#tables + * @param {string|integer} [selector] Selector to pick which tables the iterator + * should operate on. If not given, all tables in the current context are + * used. This can be given as a jQuery selector (for example `':gt(0)'`) to + * select multiple tables or as an integer to select a single table. + * @returns {DataTable.Api} Returns a new API instance if a selector is given. + */ + _api_register( 'tables()', function ( selector ) { + // A new instance is created if there was a selector specified + return selector ? + new _Api( __table_selector( selector, this.context ) ) : + this; + } ); + + + _api_register( 'table()', function ( selector ) { + var tables = this.tables( selector ); + var ctx = tables.context; + + // Truncate to the first matched table + return ctx.length ? + new _Api( ctx[0] ) : + tables; + } ); + + + _api_registerPlural( 'tables().nodes()', 'table().node()' , function () { + return this.iterator( 'table', function ( ctx ) { + return ctx.nTable; + }, 1 ); + } ); + + + _api_registerPlural( 'tables().body()', 'table().body()' , function () { + return this.iterator( 'table', function ( ctx ) { + return ctx.nTBody; + }, 1 ); + } ); + + + _api_registerPlural( 'tables().header()', 'table().header()' , function () { + return this.iterator( 'table', function ( ctx ) { + return ctx.nTHead; + }, 1 ); + } ); + + + _api_registerPlural( 'tables().footer()', 'table().footer()' , function () { + return this.iterator( 'table', function ( ctx ) { + return ctx.nTFoot; + }, 1 ); + } ); + + + _api_registerPlural( 'tables().containers()', 'table().container()' , function () { + return this.iterator( 'table', function ( ctx ) { + return ctx.nTableWrapper; + }, 1 ); + } ); + + + + /** + * Redraw the tables in the current context. + */ + _api_register( 'draw()', function ( paging ) { + return this.iterator( 'table', function ( settings ) { + if ( paging === 'page' ) { + _fnDraw( settings ); + } + else { + if ( typeof paging === 'string' ) { + paging = paging === 'full-hold' ? + false : + true; + } + + _fnReDraw( settings, paging===false ); + } + } ); + } ); + + + + /** + * Get the current page index. + * + * @return {integer} Current page index (zero based) + *//** + * Set the current page. + * + * Note that if you attempt to show a page which does not exist, DataTables will + * not throw an error, but rather reset the paging. + * + * @param {integer|string} action The paging action to take. This can be one of: + * * `integer` - The page index to jump to + * * `string` - An action to take: + * * `first` - Jump to first page. + * * `next` - Jump to the next page + * * `previous` - Jump to previous page + * * `last` - Jump to the last page. + * @returns {DataTables.Api} this + */ + _api_register( 'page()', function ( action ) { + if ( action === undefined ) { + return this.page.info().page; // not an expensive call + } + + // else, have an action to take on all tables + return this.iterator( 'table', function ( settings ) { + _fnPageChange( settings, action ); + } ); + } ); + + + /** + * Paging information for the first table in the current context. + * + * If you require paging information for another table, use the `table()` method + * with a suitable selector. + * + * @return {object} Object with the following properties set: + * * `page` - Current page index (zero based - i.e. the first page is `0`) + * * `pages` - Total number of pages + * * `start` - Display index for the first record shown on the current page + * * `end` - Display index for the last record shown on the current page + * * `length` - Display length (number of records). Note that generally `start + * + length = end`, but this is not always true, for example if there are + * only 2 records to show on the final page, with a length of 10. + * * `recordsTotal` - Full data set length + * * `recordsDisplay` - Data set length once the current filtering criterion + * are applied. + */ + _api_register( 'page.info()', function ( action ) { + if ( this.context.length === 0 ) { + return undefined; + } + + var + settings = this.context[0], + start = settings._iDisplayStart, + len = settings.oFeatures.bPaginate ? settings._iDisplayLength : -1, + visRecords = settings.fnRecordsDisplay(), + all = len === -1; + + return { + "page": all ? 0 : Math.floor( start / len ), + "pages": all ? 1 : Math.ceil( visRecords / len ), + "start": start, + "end": settings.fnDisplayEnd(), + "length": len, + "recordsTotal": settings.fnRecordsTotal(), + "recordsDisplay": visRecords, + "serverSide": _fnDataSource( settings ) === 'ssp' + }; + } ); + + + /** + * Get the current page length. + * + * @return {integer} Current page length. Note `-1` indicates that all records + * are to be shown. + *//** + * Set the current page length. + * + * @param {integer} Page length to set. Use `-1` to show all records. + * @returns {DataTables.Api} this + */ + _api_register( 'page.len()', function ( len ) { + // Note that we can't call this function 'length()' because `length` + // is a Javascript property of functions which defines how many arguments + // the function expects. + if ( len === undefined ) { + return this.context.length !== 0 ? + this.context[0]._iDisplayLength : + undefined; + } + + // else, set the page length + return this.iterator( 'table', function ( settings ) { + _fnLengthChange( settings, len ); + } ); + } ); + + + + var __reload = function ( settings, holdPosition, callback ) { + // Use the draw event to trigger a callback + if ( callback ) { + var api = new _Api( settings ); + + api.one( 'draw', function () { + callback( api.ajax.json() ); + } ); + } + + if ( _fnDataSource( settings ) == 'ssp' ) { + _fnReDraw( settings, holdPosition ); + } + else { + _fnProcessingDisplay( settings, true ); + + // Cancel an existing request + var xhr = settings.jqXHR; + if ( xhr && xhr.readyState !== 4 ) { + xhr.abort(); + } + + // Trigger xhr + _fnBuildAjax( settings, [], function( json ) { + _fnClearTable( settings ); + + var data = _fnAjaxDataSrc( settings, json ); + for ( var i=0, ien=data.length ; i<ien ; i++ ) { + _fnAddData( settings, data[i] ); + } + + _fnReDraw( settings, holdPosition ); + _fnProcessingDisplay( settings, false ); + } ); + } + }; + + + /** + * Get the JSON response from the last Ajax request that DataTables made to the + * server. Note that this returns the JSON from the first table in the current + * context. + * + * @return {object} JSON received from the server. + */ + _api_register( 'ajax.json()', function () { + var ctx = this.context; + + if ( ctx.length > 0 ) { + return ctx[0].json; + } + + // else return undefined; + } ); + + + /** + * Get the data submitted in the last Ajax request + */ + _api_register( 'ajax.params()', function () { + var ctx = this.context; + + if ( ctx.length > 0 ) { + return ctx[0].oAjaxData; + } + + // else return undefined; + } ); + + + /** + * Reload tables from the Ajax data source. Note that this function will + * automatically re-draw the table when the remote data has been loaded. + * + * @param {boolean} [reset=true] Reset (default) or hold the current paging + * position. A full re-sort and re-filter is performed when this method is + * called, which is why the pagination reset is the default action. + * @returns {DataTables.Api} this + */ + _api_register( 'ajax.reload()', function ( callback, resetPaging ) { + return this.iterator( 'table', function (settings) { + __reload( settings, resetPaging===false, callback ); + } ); + } ); + + + /** + * Get the current Ajax URL. Note that this returns the URL from the first + * table in the current context. + * + * @return {string} Current Ajax source URL + *//** + * Set the Ajax URL. Note that this will set the URL for all tables in the + * current context. + * + * @param {string} url URL to set. + * @returns {DataTables.Api} this + */ + _api_register( 'ajax.url()', function ( url ) { + var ctx = this.context; + + if ( url === undefined ) { + // get + if ( ctx.length === 0 ) { + return undefined; + } + ctx = ctx[0]; + + return ctx.ajax ? + $.isPlainObject( ctx.ajax ) ? + ctx.ajax.url : + ctx.ajax : + ctx.sAjaxSource; + } + + // set + return this.iterator( 'table', function ( settings ) { + if ( $.isPlainObject( settings.ajax ) ) { + settings.ajax.url = url; + } + else { + settings.ajax = url; + } + // No need to consider sAjaxSource here since DataTables gives priority + // to `ajax` over `sAjaxSource`. So setting `ajax` here, renders any + // value of `sAjaxSource` redundant. + } ); + } ); + + + /** + * Load data from the newly set Ajax URL. Note that this method is only + * available when `ajax.url()` is used to set a URL. Additionally, this method + * has the same effect as calling `ajax.reload()` but is provided for + * convenience when setting a new URL. Like `ajax.reload()` it will + * automatically redraw the table once the remote data has been loaded. + * + * @returns {DataTables.Api} this + */ + _api_register( 'ajax.url().load()', function ( callback, resetPaging ) { + // Same as a reload, but makes sense to present it for easy access after a + // url change + return this.iterator( 'table', function ( ctx ) { + __reload( ctx, resetPaging===false, callback ); + } ); + } ); + + + + + var _selector_run = function ( type, selector, selectFn, settings, opts ) + { + var + out = [], res, + a, i, ien, j, jen, + selectorType = typeof selector; + + // Can't just check for isArray here, as an API or jQuery instance might be + // given with their array like look + if ( ! selector || selectorType === 'string' || selectorType === 'function' || selector.length === undefined ) { + selector = [ selector ]; + } + + for ( i=0, ien=selector.length ; i<ien ; i++ ) { + // Only split on simple strings - complex expressions will be jQuery selectors + a = selector[i] && selector[i].split && ! selector[i].match(/[\[\(:]/) ? + selector[i].split(',') : + [ selector[i] ]; + + for ( j=0, jen=a.length ; j<jen ; j++ ) { + res = selectFn( typeof a[j] === 'string' ? $.trim(a[j]) : a[j] ); + + if ( res && res.length ) { + out = out.concat( res ); + } + } + } + + // selector extensions + var ext = _ext.selector[ type ]; + if ( ext.length ) { + for ( i=0, ien=ext.length ; i<ien ; i++ ) { + out = ext[i]( settings, opts, out ); + } + } + + return _unique( out ); + }; + + + var _selector_opts = function ( opts ) + { + if ( ! opts ) { + opts = {}; + } + + // Backwards compatibility for 1.9- which used the terminology filter rather + // than search + if ( opts.filter && opts.search === undefined ) { + opts.search = opts.filter; + } + + return $.extend( { + search: 'none', + order: 'current', + page: 'all' + }, opts ); + }; + + + var _selector_first = function ( inst ) + { + // Reduce the API instance to the first item found + for ( var i=0, ien=inst.length ; i<ien ; i++ ) { + if ( inst[i].length > 0 ) { + // Assign the first element to the first item in the instance + // and truncate the instance and context + inst[0] = inst[i]; + inst[0].length = 1; + inst.length = 1; + inst.context = [ inst.context[i] ]; + + return inst; + } + } + + // Not found - return an empty instance + inst.length = 0; + return inst; + }; + + + var _selector_row_indexes = function ( settings, opts ) + { + var + i, ien, tmp, a=[], + displayFiltered = settings.aiDisplay, + displayMaster = settings.aiDisplayMaster; + + var + search = opts.search, // none, applied, removed + order = opts.order, // applied, current, index (original - compatibility with 1.9) + page = opts.page; // all, current + + if ( _fnDataSource( settings ) == 'ssp' ) { + // In server-side processing mode, most options are irrelevant since + // rows not shown don't exist and the index order is the applied order + // Removed is a special case - for consistency just return an empty + // array + return search === 'removed' ? + [] : + _range( 0, displayMaster.length ); + } + else if ( page == 'current' ) { + // Current page implies that order=current and fitler=applied, since it is + // fairly senseless otherwise, regardless of what order and search actually + // are + for ( i=settings._iDisplayStart, ien=settings.fnDisplayEnd() ; i<ien ; i++ ) { + a.push( displayFiltered[i] ); + } + } + else if ( order == 'current' || order == 'applied' ) { + a = search == 'none' ? + displayMaster.slice() : // no search + search == 'applied' ? + displayFiltered.slice() : // applied search + $.map( displayMaster, function (el, i) { // removed search + return $.inArray( el, displayFiltered ) === -1 ? el : null; + } ); + } + else if ( order == 'index' || order == 'original' ) { + for ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) { + if ( search == 'none' ) { + a.push( i ); + } + else { // applied | removed + tmp = $.inArray( i, displayFiltered ); + + if ((tmp === -1 && search == 'removed') || + (tmp >= 0 && search == 'applied') ) + { + a.push( i ); + } + } + } + } + + return a; + }; + + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Rows + * + * {} - no selector - use all available rows + * {integer} - row aoData index + * {node} - TR node + * {string} - jQuery selector to apply to the TR elements + * {array} - jQuery array of nodes, or simply an array of TR nodes + * + */ + + + var __row_selector = function ( settings, selector, opts ) + { + var rows; + var run = function ( sel ) { + var selInt = _intVal( sel ); + var i, ien; + + // Short cut - selector is a number and no options provided (default is + // all records, so no need to check if the index is in there, since it + // must be - dev error if the index doesn't exist). + if ( selInt !== null && ! opts ) { + return [ selInt ]; + } + + if ( ! rows ) { + rows = _selector_row_indexes( settings, opts ); + } + + if ( selInt !== null && $.inArray( selInt, rows ) !== -1 ) { + // Selector - integer + return [ selInt ]; + } + else if ( sel === null || sel === undefined || sel === '' ) { + // Selector - none + return rows; + } + + // Selector - function + if ( typeof sel === 'function' ) { + return $.map( rows, function (idx) { + var row = settings.aoData[ idx ]; + return sel( idx, row._aData, row.nTr ) ? idx : null; + } ); + } + + // Get nodes in the order from the `rows` array with null values removed + var nodes = _removeEmpty( + _pluck_order( settings.aoData, rows, 'nTr' ) + ); + + // Selector - node + if ( sel.nodeName ) { + if ( sel._DT_RowIndex !== undefined ) { + return [ sel._DT_RowIndex ]; // Property added by DT for fast lookup + } + else if ( sel._DT_CellIndex ) { + return [ sel._DT_CellIndex.row ]; + } + else { + var host = $(sel).closest('*[data-dt-row]'); + return host.length ? + [ host.data('dt-row') ] : + []; + } + } + + // ID selector. Want to always be able to select rows by id, regardless + // of if the tr element has been created or not, so can't rely upon + // jQuery here - hence a custom implementation. This does not match + // Sizzle's fast selector or HTML4 - in HTML5 the ID can be anything, + // but to select it using a CSS selector engine (like Sizzle or + // querySelect) it would need to need to be escaped for some characters. + // DataTables simplifies this for row selectors since you can select + // only a row. A # indicates an id any anything that follows is the id - + // unescaped. + if ( typeof sel === 'string' && sel.charAt(0) === '#' ) { + // get row index from id + var rowObj = settings.aIds[ sel.replace( /^#/, '' ) ]; + if ( rowObj !== undefined ) { + return [ rowObj.idx ]; + } + + // need to fall through to jQuery in case there is DOM id that + // matches + } + + // Selector - jQuery selector string, array of nodes or jQuery object/ + // As jQuery's .filter() allows jQuery objects to be passed in filter, + // it also allows arrays, so this will cope with all three options + return $(nodes) + .filter( sel ) + .map( function () { + return this._DT_RowIndex; + } ) + .toArray(); + }; + + return _selector_run( 'row', selector, run, settings, opts ); + }; + + + _api_register( 'rows()', function ( selector, opts ) { + // argument shifting + if ( selector === undefined ) { + selector = ''; + } + else if ( $.isPlainObject( selector ) ) { + opts = selector; + selector = ''; + } + + opts = _selector_opts( opts ); + + var inst = this.iterator( 'table', function ( settings ) { + return __row_selector( settings, selector, opts ); + }, 1 ); + + // Want argument shifting here and in __row_selector? + inst.selector.rows = selector; + inst.selector.opts = opts; + + return inst; + } ); + + _api_register( 'rows().nodes()', function () { + return this.iterator( 'row', function ( settings, row ) { + return settings.aoData[ row ].nTr || undefined; + }, 1 ); + } ); + + _api_register( 'rows().data()', function () { + return this.iterator( true, 'rows', function ( settings, rows ) { + return _pluck_order( settings.aoData, rows, '_aData' ); + }, 1 ); + } ); + + _api_registerPlural( 'rows().cache()', 'row().cache()', function ( type ) { + return this.iterator( 'row', function ( settings, row ) { + var r = settings.aoData[ row ]; + return type === 'search' ? r._aFilterData : r._aSortData; + }, 1 ); + } ); + + _api_registerPlural( 'rows().invalidate()', 'row().invalidate()', function ( src ) { + return this.iterator( 'row', function ( settings, row ) { + _fnInvalidate( settings, row, src ); + } ); + } ); + + _api_registerPlural( 'rows().indexes()', 'row().index()', function () { + return this.iterator( 'row', function ( settings, row ) { + return row; + }, 1 ); + } ); + + _api_registerPlural( 'rows().ids()', 'row().id()', function ( hash ) { + var a = []; + var context = this.context; + + // `iterator` will drop undefined values, but in this case we want them + for ( var i=0, ien=context.length ; i<ien ; i++ ) { + for ( var j=0, jen=this[i].length ; j<jen ; j++ ) { + var id = context[i].rowIdFn( context[i].aoData[ this[i][j] ]._aData ); + a.push( (hash === true ? '#' : '' )+ id ); + } + } + + return new _Api( context, a ); + } ); + + _api_registerPlural( 'rows().remove()', 'row().remove()', function () { + var that = this; + + this.iterator( 'row', function ( settings, row, thatIdx ) { + var data = settings.aoData; + var rowData = data[ row ]; + var i, ien, j, jen; + var loopRow, loopCells; + + data.splice( row, 1 ); + + // Update the cached indexes + for ( i=0, ien=data.length ; i<ien ; i++ ) { + loopRow = data[i]; + loopCells = loopRow.anCells; + + // Rows + if ( loopRow.nTr !== null ) { + loopRow.nTr._DT_RowIndex = i; + } + + // Cells + if ( loopCells !== null ) { + for ( j=0, jen=loopCells.length ; j<jen ; j++ ) { + loopCells[j]._DT_CellIndex.row = i; + } + } + } + + // Delete from the display arrays + _fnDeleteIndex( settings.aiDisplayMaster, row ); + _fnDeleteIndex( settings.aiDisplay, row ); + _fnDeleteIndex( that[ thatIdx ], row, false ); // maintain local indexes + + // Check for an 'overflow' they case for displaying the table + _fnLengthOverflow( settings ); + + // Remove the row's ID reference if there is one + var id = settings.rowIdFn( rowData._aData ); + if ( id !== undefined ) { + delete settings.aIds[ id ]; + } + } ); + + this.iterator( 'table', function ( settings ) { + for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) { + settings.aoData[i].idx = i; + } + } ); + + return this; + } ); + + + _api_register( 'rows.add()', function ( rows ) { + var newRows = this.iterator( 'table', function ( settings ) { + var row, i, ien; + var out = []; + + for ( i=0, ien=rows.length ; i<ien ; i++ ) { + row = rows[i]; + + if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) { + out.push( _fnAddTr( settings, row )[0] ); + } + else { + out.push( _fnAddData( settings, row ) ); + } + } + + return out; + }, 1 ); + + // Return an Api.rows() extended instance, so rows().nodes() etc can be used + var modRows = this.rows( -1 ); + modRows.pop(); + $.merge( modRows, newRows ); + + return modRows; + } ); + + + + + + /** + * + */ + _api_register( 'row()', function ( selector, opts ) { + return _selector_first( this.rows( selector, opts ) ); + } ); + + + _api_register( 'row().data()', function ( data ) { + var ctx = this.context; + + if ( data === undefined ) { + // Get + return ctx.length && this.length ? + ctx[0].aoData[ this[0] ]._aData : + undefined; + } + + // Set + ctx[0].aoData[ this[0] ]._aData = data; + + // Automatically invalidate + _fnInvalidate( ctx[0], this[0], 'data' ); + + return this; + } ); + + + _api_register( 'row().node()', function () { + var ctx = this.context; + + return ctx.length && this.length ? + ctx[0].aoData[ this[0] ].nTr || null : + null; + } ); + + + _api_register( 'row.add()', function ( row ) { + // Allow a jQuery object to be passed in - only a single row is added from + // it though - the first element in the set + if ( row instanceof $ && row.length ) { + row = row[0]; + } + + var rows = this.iterator( 'table', function ( settings ) { + if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) { + return _fnAddTr( settings, row )[0]; + } + return _fnAddData( settings, row ); + } ); + + // Return an Api.rows() extended instance, with the newly added row selected + return this.row( rows[0] ); + } ); + + + + var __details_add = function ( ctx, row, data, klass ) + { + // Convert to array of TR elements + var rows = []; + var addRow = function ( r, k ) { + // Recursion to allow for arrays of jQuery objects + if ( $.isArray( r ) || r instanceof $ ) { + for ( var i=0, ien=r.length ; i<ien ; i++ ) { + addRow( r[i], k ); + } + return; + } + + // If we get a TR element, then just add it directly - up to the dev + // to add the correct number of columns etc + if ( r.nodeName && r.nodeName.toLowerCase() === 'tr' ) { + rows.push( r ); + } + else { + // Otherwise create a row with a wrapper + var created = $('<tr><td/></tr>').addClass( k ); + $('td', created) + .addClass( k ) + .html( r ) + [0].colSpan = _fnVisbleColumns( ctx ); + + rows.push( created[0] ); + } + }; + + addRow( data, klass ); + + if ( row._details ) { + row._details.detach(); + } + + row._details = $(rows); + + // If the children were already shown, that state should be retained + if ( row._detailsShow ) { + row._details.insertAfter( row.nTr ); + } + }; + + + var __details_remove = function ( api, idx ) + { + var ctx = api.context; + + if ( ctx.length ) { + var row = ctx[0].aoData[ idx !== undefined ? idx : api[0] ]; + + if ( row && row._details ) { + row._details.remove(); + + row._detailsShow = undefined; + row._details = undefined; + } + } + }; + + + var __details_display = function ( api, show ) { + var ctx = api.context; + + if ( ctx.length && api.length ) { + var row = ctx[0].aoData[ api[0] ]; + + if ( row._details ) { + row._detailsShow = show; + + if ( show ) { + row._details.insertAfter( row.nTr ); + } + else { + row._details.detach(); + } + + __details_events( ctx[0] ); + } + } + }; + + + var __details_events = function ( settings ) + { + var api = new _Api( settings ); + var namespace = '.dt.DT_details'; + var drawEvent = 'draw'+namespace; + var colvisEvent = 'column-visibility'+namespace; + var destroyEvent = 'destroy'+namespace; + var data = settings.aoData; + + api.off( drawEvent +' '+ colvisEvent +' '+ destroyEvent ); + + if ( _pluck( data, '_details' ).length > 0 ) { + // On each draw, insert the required elements into the document + api.on( drawEvent, function ( e, ctx ) { + if ( settings !== ctx ) { + return; + } + + api.rows( {page:'current'} ).eq(0).each( function (idx) { + // Internal data grab + var row = data[ idx ]; + + if ( row._detailsShow ) { + row._details.insertAfter( row.nTr ); + } + } ); + } ); + + // Column visibility change - update the colspan + api.on( colvisEvent, function ( e, ctx, idx, vis ) { + if ( settings !== ctx ) { + return; + } + + // Update the colspan for the details rows (note, only if it already has + // a colspan) + var row, visible = _fnVisbleColumns( ctx ); + + for ( var i=0, ien=data.length ; i<ien ; i++ ) { + row = data[i]; + + if ( row._details ) { + row._details.children('td[colspan]').attr('colspan', visible ); + } + } + } ); + + // Table destroyed - nuke any child rows + api.on( destroyEvent, function ( e, ctx ) { + if ( settings !== ctx ) { + return; + } + + for ( var i=0, ien=data.length ; i<ien ; i++ ) { + if ( data[i]._details ) { + __details_remove( api, i ); + } + } + } ); + } + }; + + // Strings for the method names to help minification + var _emp = ''; + var _child_obj = _emp+'row().child'; + var _child_mth = _child_obj+'()'; + + // data can be: + // tr + // string + // jQuery or array of any of the above + _api_register( _child_mth, function ( data, klass ) { + var ctx = this.context; + + if ( data === undefined ) { + // get + return ctx.length && this.length ? + ctx[0].aoData[ this[0] ]._details : + undefined; + } + else if ( data === true ) { + // show + this.child.show(); + } + else if ( data === false ) { + // remove + __details_remove( this ); + } + else if ( ctx.length && this.length ) { + // set + __details_add( ctx[0], ctx[0].aoData[ this[0] ], data, klass ); + } + + return this; + } ); + + + _api_register( [ + _child_obj+'.show()', + _child_mth+'.show()' // only when `child()` was called with parameters (without + ], function ( show ) { // it returns an object and this method is not executed) + __details_display( this, true ); + return this; + } ); + + + _api_register( [ + _child_obj+'.hide()', + _child_mth+'.hide()' // only when `child()` was called with parameters (without + ], function () { // it returns an object and this method is not executed) + __details_display( this, false ); + return this; + } ); + + + _api_register( [ + _child_obj+'.remove()', + _child_mth+'.remove()' // only when `child()` was called with parameters (without + ], function () { // it returns an object and this method is not executed) + __details_remove( this ); + return this; + } ); + + + _api_register( _child_obj+'.isShown()', function () { + var ctx = this.context; + + if ( ctx.length && this.length ) { + // _detailsShown as false or undefined will fall through to return false + return ctx[0].aoData[ this[0] ]._detailsShow || false; + } + return false; + } ); + + + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Columns + * + * {integer} - column index (>=0 count from left, <0 count from right) + * "{integer}:visIdx" - visible column index (i.e. translate to column index) (>=0 count from left, <0 count from right) + * "{integer}:visible" - alias for {integer}:visIdx (>=0 count from left, <0 count from right) + * "{string}:name" - column name + * "{string}" - jQuery selector on column header nodes + * + */ + + // can be an array of these items, comma separated list, or an array of comma + // separated lists + + var __re_column_selector = /^([^:]+):(name|visIdx|visible)$/; + + + // r1 and r2 are redundant - but it means that the parameters match for the + // iterator callback in columns().data() + var __columnData = function ( settings, column, r1, r2, rows ) { + var a = []; + for ( var row=0, ien=rows.length ; row<ien ; row++ ) { + a.push( _fnGetCellData( settings, rows[row], column ) ); + } + return a; + }; + + + var __column_selector = function ( settings, selector, opts ) + { + var + columns = settings.aoColumns, + names = _pluck( columns, 'sName' ), + nodes = _pluck( columns, 'nTh' ); + + var run = function ( s ) { + var selInt = _intVal( s ); + + // Selector - all + if ( s === '' ) { + return _range( columns.length ); + } + + // Selector - index + if ( selInt !== null ) { + return [ selInt >= 0 ? + selInt : // Count from left + columns.length + selInt // Count from right (+ because its a negative value) + ]; + } + + // Selector = function + if ( typeof s === 'function' ) { + var rows = _selector_row_indexes( settings, opts ); + + return $.map( columns, function (col, idx) { + return s( + idx, + __columnData( settings, idx, 0, 0, rows ), + nodes[ idx ] + ) ? idx : null; + } ); + } + + // jQuery or string selector + var match = typeof s === 'string' ? + s.match( __re_column_selector ) : + ''; + + if ( match ) { + switch( match[2] ) { + case 'visIdx': + case 'visible': + var idx = parseInt( match[1], 10 ); + // Visible index given, convert to column index + if ( idx < 0 ) { + // Counting from the right + var visColumns = $.map( columns, function (col,i) { + return col.bVisible ? i : null; + } ); + return [ visColumns[ visColumns.length + idx ] ]; + } + // Counting from the left + return [ _fnVisibleToColumnIndex( settings, idx ) ]; + + case 'name': + // match by name. `names` is column index complete and in order + return $.map( names, function (name, i) { + return name === match[1] ? i : null; + } ); + + default: + return []; + } + } + + // Cell in the table body + if ( s.nodeName && s._DT_CellIndex ) { + return [ s._DT_CellIndex.column ]; + } + + // jQuery selector on the TH elements for the columns + var jqResult = $( nodes ) + .filter( s ) + .map( function () { + return $.inArray( this, nodes ); // `nodes` is column index complete and in order + } ) + .toArray(); + + if ( jqResult.length || ! s.nodeName ) { + return jqResult; + } + + // Otherwise a node which might have a `dt-column` data attribute, or be + // a child or such an element + var host = $(s).closest('*[data-dt-column]'); + return host.length ? + [ host.data('dt-column') ] : + []; + }; + + return _selector_run( 'column', selector, run, settings, opts ); + }; + + + var __setColumnVis = function ( settings, column, vis ) { + var + cols = settings.aoColumns, + col = cols[ column ], + data = settings.aoData, + row, cells, i, ien, tr; + + // Get + if ( vis === undefined ) { + return col.bVisible; + } + + // Set + // No change + if ( col.bVisible === vis ) { + return; + } + + if ( vis ) { + // Insert column + // Need to decide if we should use appendChild or insertBefore + var insertBefore = $.inArray( true, _pluck(cols, 'bVisible'), column+1 ); + + for ( i=0, ien=data.length ; i<ien ; i++ ) { + tr = data[i].nTr; + cells = data[i].anCells; + + if ( tr ) { + // insertBefore can act like appendChild if 2nd arg is null + tr.insertBefore( cells[ column ], cells[ insertBefore ] || null ); + } + } + } + else { + // Remove column + $( _pluck( settings.aoData, 'anCells', column ) ).detach(); + } + + // Common actions + col.bVisible = vis; + _fnDrawHead( settings, settings.aoHeader ); + _fnDrawHead( settings, settings.aoFooter ); + + _fnSaveState( settings ); + }; + + + _api_register( 'columns()', function ( selector, opts ) { + // argument shifting + if ( selector === undefined ) { + selector = ''; + } + else if ( $.isPlainObject( selector ) ) { + opts = selector; + selector = ''; + } + + opts = _selector_opts( opts ); + + var inst = this.iterator( 'table', function ( settings ) { + return __column_selector( settings, selector, opts ); + }, 1 ); + + // Want argument shifting here and in _row_selector? + inst.selector.cols = selector; + inst.selector.opts = opts; + + return inst; + } ); + + _api_registerPlural( 'columns().header()', 'column().header()', function ( selector, opts ) { + return this.iterator( 'column', function ( settings, column ) { + return settings.aoColumns[column].nTh; + }, 1 ); + } ); + + _api_registerPlural( 'columns().footer()', 'column().footer()', function ( selector, opts ) { + return this.iterator( 'column', function ( settings, column ) { + return settings.aoColumns[column].nTf; + }, 1 ); + } ); + + _api_registerPlural( 'columns().data()', 'column().data()', function () { + return this.iterator( 'column-rows', __columnData, 1 ); + } ); + + _api_registerPlural( 'columns().dataSrc()', 'column().dataSrc()', function () { + return this.iterator( 'column', function ( settings, column ) { + return settings.aoColumns[column].mData; + }, 1 ); + } ); + + _api_registerPlural( 'columns().cache()', 'column().cache()', function ( type ) { + return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) { + return _pluck_order( settings.aoData, rows, + type === 'search' ? '_aFilterData' : '_aSortData', column + ); + }, 1 ); + } ); + + _api_registerPlural( 'columns().nodes()', 'column().nodes()', function () { + return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) { + return _pluck_order( settings.aoData, rows, 'anCells', column ) ; + }, 1 ); + } ); + + _api_registerPlural( 'columns().visible()', 'column().visible()', function ( vis, calc ) { + var ret = this.iterator( 'column', function ( settings, column ) { + if ( vis === undefined ) { + return settings.aoColumns[ column ].bVisible; + } // else + __setColumnVis( settings, column, vis ); + } ); + + // Group the column visibility changes + if ( vis !== undefined ) { + // Second loop once the first is done for events + this.iterator( 'column', function ( settings, column ) { + _fnCallbackFire( settings, null, 'column-visibility', [settings, column, vis, calc] ); + } ); + + if ( calc === undefined || calc ) { + this.columns.adjust(); + } + } + + return ret; + } ); + + _api_registerPlural( 'columns().indexes()', 'column().index()', function ( type ) { + return this.iterator( 'column', function ( settings, column ) { + return type === 'visible' ? + _fnColumnIndexToVisible( settings, column ) : + column; + }, 1 ); + } ); + + _api_register( 'columns.adjust()', function () { + return this.iterator( 'table', function ( settings ) { + _fnAdjustColumnSizing( settings ); + }, 1 ); + } ); + + _api_register( 'column.index()', function ( type, idx ) { + if ( this.context.length !== 0 ) { + var ctx = this.context[0]; + + if ( type === 'fromVisible' || type === 'toData' ) { + return _fnVisibleToColumnIndex( ctx, idx ); + } + else if ( type === 'fromData' || type === 'toVisible' ) { + return _fnColumnIndexToVisible( ctx, idx ); + } + } + } ); + + _api_register( 'column()', function ( selector, opts ) { + return _selector_first( this.columns( selector, opts ) ); + } ); + + + + var __cell_selector = function ( settings, selector, opts ) + { + var data = settings.aoData; + var rows = _selector_row_indexes( settings, opts ); + var cells = _removeEmpty( _pluck_order( data, rows, 'anCells' ) ); + var allCells = $( [].concat.apply([], cells) ); + var row; + var columns = settings.aoColumns.length; + var a, i, ien, j, o, host; + + var run = function ( s ) { + var fnSelector = typeof s === 'function'; + + if ( s === null || s === undefined || fnSelector ) { + // All cells and function selectors + a = []; + + for ( i=0, ien=rows.length ; i<ien ; i++ ) { + row = rows[i]; + + for ( j=0 ; j<columns ; j++ ) { + o = { + row: row, + column: j + }; + + if ( fnSelector ) { + // Selector - function + host = data[ row ]; + + if ( s( o, _fnGetCellData(settings, row, j), host.anCells ? host.anCells[j] : null ) ) { + a.push( o ); + } + } + else { + // Selector - all + a.push( o ); + } + } + } + + return a; + } + + // Selector - index + if ( $.isPlainObject( s ) ) { + return [s]; + } + + // Selector - jQuery filtered cells + var jqResult = allCells + .filter( s ) + .map( function (i, el) { + return { // use a new object, in case someone changes the values + row: el._DT_CellIndex.row, + column: el._DT_CellIndex.column + }; + } ) + .toArray(); + + if ( jqResult.length || ! s.nodeName ) { + return jqResult; + } + + // Otherwise the selector is a node, and there is one last option - the + // element might be a child of an element which has dt-row and dt-column + // data attributes + host = $(s).closest('*[data-dt-row]'); + return host.length ? + [ { + row: host.data('dt-row'), + column: host.data('dt-column') + } ] : + []; + }; + + return _selector_run( 'cell', selector, run, settings, opts ); + }; + + + + + _api_register( 'cells()', function ( rowSelector, columnSelector, opts ) { + // Argument shifting + if ( $.isPlainObject( rowSelector ) ) { + // Indexes + if ( rowSelector.row === undefined ) { + // Selector options in first parameter + opts = rowSelector; + rowSelector = null; + } + else { + // Cell index objects in first parameter + opts = columnSelector; + columnSelector = null; + } + } + if ( $.isPlainObject( columnSelector ) ) { + opts = columnSelector; + columnSelector = null; + } + + // Cell selector + if ( columnSelector === null || columnSelector === undefined ) { + return this.iterator( 'table', function ( settings ) { + return __cell_selector( settings, rowSelector, _selector_opts( opts ) ); + } ); + } + + // Row + column selector + var columns = this.columns( columnSelector, opts ); + var rows = this.rows( rowSelector, opts ); + var a, i, ien, j, jen; + + var cells = this.iterator( 'table', function ( settings, idx ) { + a = []; + + for ( i=0, ien=rows[idx].length ; i<ien ; i++ ) { + for ( j=0, jen=columns[idx].length ; j<jen ; j++ ) { + a.push( { + row: rows[idx][i], + column: columns[idx][j] + } ); + } + } + + return a; + }, 1 ); + + $.extend( cells.selector, { + cols: columnSelector, + rows: rowSelector, + opts: opts + } ); + + return cells; + } ); + + + _api_registerPlural( 'cells().nodes()', 'cell().node()', function () { + return this.iterator( 'cell', function ( settings, row, column ) { + var data = settings.aoData[ row ]; + + return data && data.anCells ? + data.anCells[ column ] : + undefined; + }, 1 ); + } ); + + + _api_register( 'cells().data()', function () { + return this.iterator( 'cell', function ( settings, row, column ) { + return _fnGetCellData( settings, row, column ); + }, 1 ); + } ); + + + _api_registerPlural( 'cells().cache()', 'cell().cache()', function ( type ) { + type = type === 'search' ? '_aFilterData' : '_aSortData'; + + return this.iterator( 'cell', function ( settings, row, column ) { + return settings.aoData[ row ][ type ][ column ]; + }, 1 ); + } ); + + + _api_registerPlural( 'cells().render()', 'cell().render()', function ( type ) { + return this.iterator( 'cell', function ( settings, row, column ) { + return _fnGetCellData( settings, row, column, type ); + }, 1 ); + } ); + + + _api_registerPlural( 'cells().indexes()', 'cell().index()', function () { + return this.iterator( 'cell', function ( settings, row, column ) { + return { + row: row, + column: column, + columnVisible: _fnColumnIndexToVisible( settings, column ) + }; + }, 1 ); + } ); + + + _api_registerPlural( 'cells().invalidate()', 'cell().invalidate()', function ( src ) { + return this.iterator( 'cell', function ( settings, row, column ) { + _fnInvalidate( settings, row, src, column ); + } ); + } ); + + + + _api_register( 'cell()', function ( rowSelector, columnSelector, opts ) { + return _selector_first( this.cells( rowSelector, columnSelector, opts ) ); + } ); + + + _api_register( 'cell().data()', function ( data ) { + var ctx = this.context; + var cell = this[0]; + + if ( data === undefined ) { + // Get + return ctx.length && cell.length ? + _fnGetCellData( ctx[0], cell[0].row, cell[0].column ) : + undefined; + } + + // Set + _fnSetCellData( ctx[0], cell[0].row, cell[0].column, data ); + _fnInvalidate( ctx[0], cell[0].row, 'data', cell[0].column ); + + return this; + } ); + + + + /** + * Get current ordering (sorting) that has been applied to the table. + * + * @returns {array} 2D array containing the sorting information for the first + * table in the current context. Each element in the parent array represents + * a column being sorted upon (i.e. multi-sorting with two columns would have + * 2 inner arrays). The inner arrays may have 2 or 3 elements. The first is + * the column index that the sorting condition applies to, the second is the + * direction of the sort (`desc` or `asc`) and, optionally, the third is the + * index of the sorting order from the `column.sorting` initialisation array. + *//** + * Set the ordering for the table. + * + * @param {integer} order Column index to sort upon. + * @param {string} direction Direction of the sort to be applied (`asc` or `desc`) + * @returns {DataTables.Api} this + *//** + * Set the ordering for the table. + * + * @param {array} order 1D array of sorting information to be applied. + * @param {array} [...] Optional additional sorting conditions + * @returns {DataTables.Api} this + *//** + * Set the ordering for the table. + * + * @param {array} order 2D array of sorting information to be applied. + * @returns {DataTables.Api} this + */ + _api_register( 'order()', function ( order, dir ) { + var ctx = this.context; + + if ( order === undefined ) { + // get + return ctx.length !== 0 ? + ctx[0].aaSorting : + undefined; + } + + // set + if ( typeof order === 'number' ) { + // Simple column / direction passed in + order = [ [ order, dir ] ]; + } + else if ( order.length && ! $.isArray( order[0] ) ) { + // Arguments passed in (list of 1D arrays) + order = Array.prototype.slice.call( arguments ); + } + // otherwise a 2D array was passed in + + return this.iterator( 'table', function ( settings ) { + settings.aaSorting = order.slice(); + } ); + } ); + + + /** + * Attach a sort listener to an element for a given column + * + * @param {node|jQuery|string} node Identifier for the element(s) to attach the + * listener to. This can take the form of a single DOM node, a jQuery + * collection of nodes or a jQuery selector which will identify the node(s). + * @param {integer} column the column that a click on this node will sort on + * @param {function} [callback] callback function when sort is run + * @returns {DataTables.Api} this + */ + _api_register( 'order.listener()', function ( node, column, callback ) { + return this.iterator( 'table', function ( settings ) { + _fnSortAttachListener( settings, node, column, callback ); + } ); + } ); + + + _api_register( 'order.fixed()', function ( set ) { + if ( ! set ) { + var ctx = this.context; + var fixed = ctx.length ? + ctx[0].aaSortingFixed : + undefined; + + return $.isArray( fixed ) ? + { pre: fixed } : + fixed; + } + + return this.iterator( 'table', function ( settings ) { + settings.aaSortingFixed = $.extend( true, {}, set ); + } ); + } ); + + + // Order by the selected column(s) + _api_register( [ + 'columns().order()', + 'column().order()' + ], function ( dir ) { + var that = this; + + return this.iterator( 'table', function ( settings, i ) { + var sort = []; + + $.each( that[i], function (j, col) { + sort.push( [ col, dir ] ); + } ); + + settings.aaSorting = sort; + } ); + } ); + + + + _api_register( 'search()', function ( input, regex, smart, caseInsen ) { + var ctx = this.context; + + if ( input === undefined ) { + // get + return ctx.length !== 0 ? + ctx[0].oPreviousSearch.sSearch : + undefined; + } + + // set + return this.iterator( 'table', function ( settings ) { + if ( ! settings.oFeatures.bFilter ) { + return; + } + + _fnFilterComplete( settings, $.extend( {}, settings.oPreviousSearch, { + "sSearch": input+"", + "bRegex": regex === null ? false : regex, + "bSmart": smart === null ? true : smart, + "bCaseInsensitive": caseInsen === null ? true : caseInsen + } ), 1 ); + } ); + } ); + + + _api_registerPlural( + 'columns().search()', + 'column().search()', + function ( input, regex, smart, caseInsen ) { + return this.iterator( 'column', function ( settings, column ) { + var preSearch = settings.aoPreSearchCols; + + if ( input === undefined ) { + // get + return preSearch[ column ].sSearch; + } + + // set + if ( ! settings.oFeatures.bFilter ) { + return; + } + + $.extend( preSearch[ column ], { + "sSearch": input+"", + "bRegex": regex === null ? false : regex, + "bSmart": smart === null ? true : smart, + "bCaseInsensitive": caseInsen === null ? true : caseInsen + } ); + + _fnFilterComplete( settings, settings.oPreviousSearch, 1 ); + } ); + } + ); + + /* + * State API methods + */ + + _api_register( 'state()', function () { + return this.context.length ? + this.context[0].oSavedState : + null; + } ); + + + _api_register( 'state.clear()', function () { + return this.iterator( 'table', function ( settings ) { + // Save an empty object + settings.fnStateSaveCallback.call( settings.oInstance, settings, {} ); + } ); + } ); + + + _api_register( 'state.loaded()', function () { + return this.context.length ? + this.context[0].oLoadedState : + null; + } ); + + + _api_register( 'state.save()', function () { + return this.iterator( 'table', function ( settings ) { + _fnSaveState( settings ); + } ); + } ); + + + + /** + * Provide a common method for plug-ins to check the version of DataTables being + * used, in order to ensure compatibility. + * + * @param {string} version Version string to check for, in the format "X.Y.Z". + * Note that the formats "X" and "X.Y" are also acceptable. + * @returns {boolean} true if this version of DataTables is greater or equal to + * the required version, or false if this version of DataTales is not + * suitable + * @static + * @dtopt API-Static + * + * @example + * alert( $.fn.dataTable.versionCheck( '1.9.0' ) ); + */ + DataTable.versionCheck = DataTable.fnVersionCheck = function( version ) + { + var aThis = DataTable.version.split('.'); + var aThat = version.split('.'); + var iThis, iThat; + + for ( var i=0, iLen=aThat.length ; i<iLen ; i++ ) { + iThis = parseInt( aThis[i], 10 ) || 0; + iThat = parseInt( aThat[i], 10 ) || 0; + + // Parts are the same, keep comparing + if (iThis === iThat) { + continue; + } + + // Parts are different, return immediately + return iThis > iThat; + } + + return true; + }; + + + /** + * Check if a `<table>` node is a DataTable table already or not. + * + * @param {node|jquery|string} table Table node, jQuery object or jQuery + * selector for the table to test. Note that if more than more than one + * table is passed on, only the first will be checked + * @returns {boolean} true the table given is a DataTable, or false otherwise + * @static + * @dtopt API-Static + * + * @example + * if ( ! $.fn.DataTable.isDataTable( '#example' ) ) { + * $('#example').dataTable(); + * } + */ + DataTable.isDataTable = DataTable.fnIsDataTable = function ( table ) + { + var t = $(table).get(0); + var is = false; + + if ( table instanceof DataTable.Api ) { + return true; + } + + $.each( DataTable.settings, function (i, o) { + var head = o.nScrollHead ? $('table', o.nScrollHead)[0] : null; + var foot = o.nScrollFoot ? $('table', o.nScrollFoot)[0] : null; + + if ( o.nTable === t || head === t || foot === t ) { + is = true; + } + } ); + + return is; + }; + + + /** + * Get all DataTable tables that have been initialised - optionally you can + * select to get only currently visible tables. + * + * @param {boolean} [visible=false] Flag to indicate if you want all (default) + * or visible tables only. + * @returns {array} Array of `table` nodes (not DataTable instances) which are + * DataTables + * @static + * @dtopt API-Static + * + * @example + * $.each( $.fn.dataTable.tables(true), function () { + * $(table).DataTable().columns.adjust(); + * } ); + */ + DataTable.tables = DataTable.fnTables = function ( visible ) + { + var api = false; + + if ( $.isPlainObject( visible ) ) { + api = visible.api; + visible = visible.visible; + } + + var a = $.map( DataTable.settings, function (o) { + if ( !visible || (visible && $(o.nTable).is(':visible')) ) { + return o.nTable; + } + } ); + + return api ? + new _Api( a ) : + a; + }; + + + /** + * Convert from camel case parameters to Hungarian notation. This is made public + * for the extensions to provide the same ability as DataTables core to accept + * either the 1.9 style Hungarian notation, or the 1.10+ style camelCase + * parameters. + * + * @param {object} src The model object which holds all parameters that can be + * mapped. + * @param {object} user The object to convert from camel case to Hungarian. + * @param {boolean} force When set to `true`, properties which already have a + * Hungarian value in the `user` object will be overwritten. Otherwise they + * won't be. + */ + DataTable.camelToHungarian = _fnCamelToHungarian; + + + + /** + * + */ + _api_register( '$()', function ( selector, opts ) { + var + rows = this.rows( opts ).nodes(), // Get all rows + jqRows = $(rows); + + return $( [].concat( + jqRows.filter( selector ).toArray(), + jqRows.find( selector ).toArray() + ) ); + } ); + + + // jQuery functions to operate on the tables + $.each( [ 'on', 'one', 'off' ], function (i, key) { + _api_register( key+'()', function ( /* event, handler */ ) { + var args = Array.prototype.slice.call(arguments); + + // Add the `dt` namespace automatically if it isn't already present + args[0] = $.map( args[0].split( /\s/ ), function ( e ) { + return ! e.match(/\.dt\b/) ? + e+'.dt' : + e; + } ).join( ' ' ); + + var inst = $( this.tables().nodes() ); + inst[key].apply( inst, args ); + return this; + } ); + } ); + + + _api_register( 'clear()', function () { + return this.iterator( 'table', function ( settings ) { + _fnClearTable( settings ); + } ); + } ); + + + _api_register( 'settings()', function () { + return new _Api( this.context, this.context ); + } ); + + + _api_register( 'init()', function () { + var ctx = this.context; + return ctx.length ? ctx[0].oInit : null; + } ); + + + _api_register( 'data()', function () { + return this.iterator( 'table', function ( settings ) { + return _pluck( settings.aoData, '_aData' ); + } ).flatten(); + } ); + + + _api_register( 'destroy()', function ( remove ) { + remove = remove || false; + + return this.iterator( 'table', function ( settings ) { + var orig = settings.nTableWrapper.parentNode; + var classes = settings.oClasses; + var table = settings.nTable; + var tbody = settings.nTBody; + var thead = settings.nTHead; + var tfoot = settings.nTFoot; + var jqTable = $(table); + var jqTbody = $(tbody); + var jqWrapper = $(settings.nTableWrapper); + var rows = $.map( settings.aoData, function (r) { return r.nTr; } ); + var i, ien; + + // Flag to note that the table is currently being destroyed - no action + // should be taken + settings.bDestroying = true; + + // Fire off the destroy callbacks for plug-ins etc + _fnCallbackFire( settings, "aoDestroyCallback", "destroy", [settings] ); + + // If not being removed from the document, make all columns visible + if ( ! remove ) { + new _Api( settings ).columns().visible( true ); + } + + // Blitz all `DT` namespaced events (these are internal events, the + // lowercase, `dt` events are user subscribed and they are responsible + // for removing them + jqWrapper.off('.DT').find(':not(tbody *)').off('.DT'); + $(window).off('.DT-'+settings.sInstance); + + // When scrolling we had to break the table up - restore it + if ( table != thead.parentNode ) { + jqTable.children('thead').detach(); + jqTable.append( thead ); + } + + if ( tfoot && table != tfoot.parentNode ) { + jqTable.children('tfoot').detach(); + jqTable.append( tfoot ); + } + + settings.aaSorting = []; + settings.aaSortingFixed = []; + _fnSortingClasses( settings ); + + $( rows ).removeClass( settings.asStripeClasses.join(' ') ); + + $('th, td', thead).removeClass( classes.sSortable+' '+ + classes.sSortableAsc+' '+classes.sSortableDesc+' '+classes.sSortableNone + ); + + if ( settings.bJUI ) { + $('th span.'+classes.sSortIcon+ ', td span.'+classes.sSortIcon, thead).detach(); + $('th, td', thead).each( function () { + var wrapper = $('div.'+classes.sSortJUIWrapper, this); + $(this).append( wrapper.contents() ); + wrapper.detach(); + } ); + } + + // Add the TR elements back into the table in their original order + jqTbody.children().detach(); + jqTbody.append( rows ); + + // Remove the DataTables generated nodes, events and classes + var removedMethod = remove ? 'remove' : 'detach'; + jqTable[ removedMethod ](); + jqWrapper[ removedMethod ](); + + // If we need to reattach the table to the document + if ( ! remove && orig ) { + // insertBefore acts like appendChild if !arg[1] + orig.insertBefore( table, settings.nTableReinsertBefore ); + + // Restore the width of the original table - was read from the style property, + // so we can restore directly to that + jqTable + .css( 'width', settings.sDestroyWidth ) + .removeClass( classes.sTable ); + + // If the were originally stripe classes - then we add them back here. + // Note this is not fool proof (for example if not all rows had stripe + // classes - but it's a good effort without getting carried away + ien = settings.asDestroyStripes.length; + + if ( ien ) { + jqTbody.children().each( function (i) { + $(this).addClass( settings.asDestroyStripes[i % ien] ); + } ); + } + } + + /* Remove the settings object from the settings array */ + var idx = $.inArray( settings, DataTable.settings ); + if ( idx !== -1 ) { + DataTable.settings.splice( idx, 1 ); + } + } ); + } ); + + + // Add the `every()` method for rows, columns and cells in a compact form + $.each( [ 'column', 'row', 'cell' ], function ( i, type ) { + _api_register( type+'s().every()', function ( fn ) { + var opts = this.selector.opts; + var api = this; + + return this.iterator( type, function ( settings, arg1, arg2, arg3, arg4 ) { + // Rows and columns: + // arg1 - index + // arg2 - table counter + // arg3 - loop counter + // arg4 - undefined + // Cells: + // arg1 - row index + // arg2 - column index + // arg3 - table counter + // arg4 - loop counter + fn.call( + api[ type ]( + arg1, + type==='cell' ? arg2 : opts, + type==='cell' ? opts : undefined + ), + arg1, arg2, arg3, arg4 + ); + } ); + } ); + } ); + + + // i18n method for extensions to be able to use the language object from the + // DataTable + _api_register( 'i18n()', function ( token, def, plural ) { + var ctx = this.context[0]; + var resolved = _fnGetObjectDataFn( token )( ctx.oLanguage ); + + if ( resolved === undefined ) { + resolved = def; + } + + if ( plural !== undefined && $.isPlainObject( resolved ) ) { + resolved = resolved[ plural ] !== undefined ? + resolved[ plural ] : + resolved._; + } + + return resolved.replace( '%d', plural ); // nb: plural might be undefined, + } ); + + /** + * Version string for plug-ins to check compatibility. Allowed format is + * `a.b.c-d` where: a:int, b:int, c:int, d:string(dev|beta|alpha). `d` is used + * only for non-release builds. See http://semver.org/ for more information. + * @member + * @type string + * @default Version number + */ + DataTable.version = "1.10.13"; + + /** + * Private data store, containing all of the settings objects that are + * created for the tables on a given page. + * + * Note that the `DataTable.settings` object is aliased to + * `jQuery.fn.dataTableExt` through which it may be accessed and + * manipulated, or `jQuery.fn.dataTable.settings`. + * @member + * @type array + * @default [] + * @private + */ + DataTable.settings = []; + + /** + * Object models container, for the various models that DataTables has + * available to it. These models define the objects that are used to hold + * the active state and configuration of the table. + * @namespace + */ + DataTable.models = {}; + + + + /** + * Template object for the way in which DataTables holds information about + * search information for the global filter and individual column filters. + * @namespace + */ + DataTable.models.oSearch = { + /** + * Flag to indicate if the filtering should be case insensitive or not + * @type boolean + * @default true + */ + "bCaseInsensitive": true, + + /** + * Applied search term + * @type string + * @default <i>Empty string</i> + */ + "sSearch": "", + + /** + * Flag to indicate if the search term should be interpreted as a + * regular expression (true) or not (false) and therefore and special + * regex characters escaped. + * @type boolean + * @default false + */ + "bRegex": false, + + /** + * Flag to indicate if DataTables is to use its smart filtering or not. + * @type boolean + * @default true + */ + "bSmart": true + }; + + + + + /** + * Template object for the way in which DataTables holds information about + * each individual row. This is the object format used for the settings + * aoData array. + * @namespace + */ + DataTable.models.oRow = { + /** + * TR element for the row + * @type node + * @default null + */ + "nTr": null, + + /** + * Array of TD elements for each row. This is null until the row has been + * created. + * @type array nodes + * @default [] + */ + "anCells": null, + + /** + * Data object from the original data source for the row. This is either + * an array if using the traditional form of DataTables, or an object if + * using mData options. The exact type will depend on the passed in + * data from the data source, or will be an array if using DOM a data + * source. + * @type array|object + * @default [] + */ + "_aData": [], + + /** + * Sorting data cache - this array is ostensibly the same length as the + * number of columns (although each index is generated only as it is + * needed), and holds the data that is used for sorting each column in the + * row. We do this cache generation at the start of the sort in order that + * the formatting of the sort data need be done only once for each cell + * per sort. This array should not be read from or written to by anything + * other than the master sorting methods. + * @type array + * @default null + * @private + */ + "_aSortData": null, + + /** + * Per cell filtering data cache. As per the sort data cache, used to + * increase the performance of the filtering in DataTables + * @type array + * @default null + * @private + */ + "_aFilterData": null, + + /** + * Filtering data cache. This is the same as the cell filtering cache, but + * in this case a string rather than an array. This is easily computed with + * a join on `_aFilterData`, but is provided as a cache so the join isn't + * needed on every search (memory traded for performance) + * @type array + * @default null + * @private + */ + "_sFilterRow": null, + + /** + * Cache of the class name that DataTables has applied to the row, so we + * can quickly look at this variable rather than needing to do a DOM check + * on className for the nTr property. + * @type string + * @default <i>Empty string</i> + * @private + */ + "_sRowStripe": "", + + /** + * Denote if the original data source was from the DOM, or the data source + * object. This is used for invalidating data, so DataTables can + * automatically read data from the original source, unless uninstructed + * otherwise. + * @type string + * @default null + * @private + */ + "src": null, + + /** + * Index in the aoData array. This saves an indexOf lookup when we have the + * object, but want to know the index + * @type integer + * @default -1 + * @private + */ + "idx": -1 + }; + + + /** + * Template object for the column information object in DataTables. This object + * is held in the settings aoColumns array and contains all the information that + * DataTables needs about each individual column. + * + * Note that this object is related to {@link DataTable.defaults.column} + * but this one is the internal data store for DataTables's cache of columns. + * It should NOT be manipulated outside of DataTables. Any configuration should + * be done through the initialisation options. + * @namespace + */ + DataTable.models.oColumn = { + /** + * Column index. This could be worked out on-the-fly with $.inArray, but it + * is faster to just hold it as a variable + * @type integer + * @default null + */ + "idx": null, + + /** + * A list of the columns that sorting should occur on when this column + * is sorted. That this property is an array allows multi-column sorting + * to be defined for a column (for example first name / last name columns + * would benefit from this). The values are integers pointing to the + * columns to be sorted on (typically it will be a single integer pointing + * at itself, but that doesn't need to be the case). + * @type array + */ + "aDataSort": null, + + /** + * Define the sorting directions that are applied to the column, in sequence + * as the column is repeatedly sorted upon - i.e. the first value is used + * as the sorting direction when the column if first sorted (clicked on). + * Sort it again (click again) and it will move on to the next index. + * Repeat until loop. + * @type array + */ + "asSorting": null, + + /** + * Flag to indicate if the column is searchable, and thus should be included + * in the filtering or not. + * @type boolean + */ + "bSearchable": null, + + /** + * Flag to indicate if the column is sortable or not. + * @type boolean + */ + "bSortable": null, + + /** + * Flag to indicate if the column is currently visible in the table or not + * @type boolean + */ + "bVisible": null, + + /** + * Store for manual type assignment using the `column.type` option. This + * is held in store so we can manipulate the column's `sType` property. + * @type string + * @default null + * @private + */ + "_sManualType": null, + + /** + * Flag to indicate if HTML5 data attributes should be used as the data + * source for filtering or sorting. True is either are. + * @type boolean + * @default false + * @private + */ + "_bAttrSrc": false, + + /** + * Developer definable function that is called whenever a cell is created (Ajax source, + * etc) or processed for input (DOM source). This can be used as a compliment to mRender + * allowing you to modify the DOM element (add background colour for example) when the + * element is available. + * @type function + * @param {element} nTd The TD node that has been created + * @param {*} sData The Data for the cell + * @param {array|object} oData The data for the whole row + * @param {int} iRow The row index for the aoData data store + * @default null + */ + "fnCreatedCell": null, + + /** + * Function to get data from a cell in a column. You should <b>never</b> + * access data directly through _aData internally in DataTables - always use + * the method attached to this property. It allows mData to function as + * required. This function is automatically assigned by the column + * initialisation method + * @type function + * @param {array|object} oData The data array/object for the array + * (i.e. aoData[]._aData) + * @param {string} sSpecific The specific data type you want to get - + * 'display', 'type' 'filter' 'sort' + * @returns {*} The data for the cell from the given row's data + * @default null + */ + "fnGetData": null, + + /** + * Function to set data for a cell in the column. You should <b>never</b> + * set the data directly to _aData internally in DataTables - always use + * this method. It allows mData to function as required. This function + * is automatically assigned by the column initialisation method + * @type function + * @param {array|object} oData The data array/object for the array + * (i.e. aoData[]._aData) + * @param {*} sValue Value to set + * @default null + */ + "fnSetData": null, + + /** + * Property to read the value for the cells in the column from the data + * source array / object. If null, then the default content is used, if a + * function is given then the return from the function is used. + * @type function|int|string|null + * @default null + */ + "mData": null, + + /** + * Partner property to mData which is used (only when defined) to get + * the data - i.e. it is basically the same as mData, but without the + * 'set' option, and also the data fed to it is the result from mData. + * This is the rendering method to match the data method of mData. + * @type function|int|string|null + * @default null + */ + "mRender": null, + + /** + * Unique header TH/TD element for this column - this is what the sorting + * listener is attached to (if sorting is enabled.) + * @type node + * @default null + */ + "nTh": null, + + /** + * Unique footer TH/TD element for this column (if there is one). Not used + * in DataTables as such, but can be used for plug-ins to reference the + * footer for each column. + * @type node + * @default null + */ + "nTf": null, + + /** + * The class to apply to all TD elements in the table's TBODY for the column + * @type string + * @default null + */ + "sClass": null, + + /** + * When DataTables calculates the column widths to assign to each column, + * it finds the longest string in each column and then constructs a + * temporary table and reads the widths from that. The problem with this + * is that "mmm" is much wider then "iiii", but the latter is a longer + * string - thus the calculation can go wrong (doing it properly and putting + * it into an DOM object and measuring that is horribly(!) slow). Thus as + * a "work around" we provide this option. It will append its value to the + * text that is found to be the longest string for the column - i.e. padding. + * @type string + */ + "sContentPadding": null, + + /** + * Allows a default value to be given for a column's data, and will be used + * whenever a null data source is encountered (this can be because mData + * is set to null, or because the data source itself is null). + * @type string + * @default null + */ + "sDefaultContent": null, + + /** + * Name for the column, allowing reference to the column by name as well as + * by index (needs a lookup to work by name). + * @type string + */ + "sName": null, + + /** + * Custom sorting data type - defines which of the available plug-ins in + * afnSortData the custom sorting will use - if any is defined. + * @type string + * @default std + */ + "sSortDataType": 'std', + + /** + * Class to be applied to the header element when sorting on this column + * @type string + * @default null + */ + "sSortingClass": null, + + /** + * Class to be applied to the header element when sorting on this column - + * when jQuery UI theming is used. + * @type string + * @default null + */ + "sSortingClassJUI": null, + + /** + * Title of the column - what is seen in the TH element (nTh). + * @type string + */ + "sTitle": null, + + /** + * Column sorting and filtering type + * @type string + * @default null + */ + "sType": null, + + /** + * Width of the column + * @type string + * @default null + */ + "sWidth": null, + + /** + * Width of the column when it was first "encountered" + * @type string + * @default null + */ + "sWidthOrig": null + }; + + + /* + * Developer note: The properties of the object below are given in Hungarian + * notation, that was used as the interface for DataTables prior to v1.10, however + * from v1.10 onwards the primary interface is camel case. In order to avoid + * breaking backwards compatibility utterly with this change, the Hungarian + * version is still, internally the primary interface, but is is not documented + * - hence the @name tags in each doc comment. This allows a Javascript function + * to create a map from Hungarian notation to camel case (going the other direction + * would require each property to be listed, which would at around 3K to the size + * of DataTables, while this method is about a 0.5K hit. + * + * Ultimately this does pave the way for Hungarian notation to be dropped + * completely, but that is a massive amount of work and will break current + * installs (therefore is on-hold until v2). + */ + + /** + * Initialisation options that can be given to DataTables at initialisation + * time. + * @namespace + */ + DataTable.defaults = { + /** + * An array of data to use for the table, passed in at initialisation which + * will be used in preference to any data which is already in the DOM. This is + * particularly useful for constructing tables purely in Javascript, for + * example with a custom Ajax call. + * @type array + * @default null + * + * @dtopt Option + * @name DataTable.defaults.data + * + * @example + * // Using a 2D array data source + * $(document).ready( function () { + * $('#example').dataTable( { + * "data": [ + * ['Trident', 'Internet Explorer 4.0', 'Win 95+', 4, 'X'], + * ['Trident', 'Internet Explorer 5.0', 'Win 95+', 5, 'C'], + * ], + * "columns": [ + * { "title": "Engine" }, + * { "title": "Browser" }, + * { "title": "Platform" }, + * { "title": "Version" }, + * { "title": "Grade" } + * ] + * } ); + * } ); + * + * @example + * // Using an array of objects as a data source (`data`) + * $(document).ready( function () { + * $('#example').dataTable( { + * "data": [ + * { + * "engine": "Trident", + * "browser": "Internet Explorer 4.0", + * "platform": "Win 95+", + * "version": 4, + * "grade": "X" + * }, + * { + * "engine": "Trident", + * "browser": "Internet Explorer 5.0", + * "platform": "Win 95+", + * "version": 5, + * "grade": "C" + * } + * ], + * "columns": [ + * { "title": "Engine", "data": "engine" }, + * { "title": "Browser", "data": "browser" }, + * { "title": "Platform", "data": "platform" }, + * { "title": "Version", "data": "version" }, + * { "title": "Grade", "data": "grade" } + * ] + * } ); + * } ); + */ + "aaData": null, + + + /** + * If ordering is enabled, then DataTables will perform a first pass sort on + * initialisation. You can define which column(s) the sort is performed + * upon, and the sorting direction, with this variable. The `sorting` array + * should contain an array for each column to be sorted initially containing + * the column's index and a direction string ('asc' or 'desc'). + * @type array + * @default [[0,'asc']] + * + * @dtopt Option + * @name DataTable.defaults.order + * + * @example + * // Sort by 3rd column first, and then 4th column + * $(document).ready( function() { + * $('#example').dataTable( { + * "order": [[2,'asc'], [3,'desc']] + * } ); + * } ); + * + * // No initial sorting + * $(document).ready( function() { + * $('#example').dataTable( { + * "order": [] + * } ); + * } ); + */ + "aaSorting": [[0,'asc']], + + + /** + * This parameter is basically identical to the `sorting` parameter, but + * cannot be overridden by user interaction with the table. What this means + * is that you could have a column (visible or hidden) which the sorting + * will always be forced on first - any sorting after that (from the user) + * will then be performed as required. This can be useful for grouping rows + * together. + * @type array + * @default null + * + * @dtopt Option + * @name DataTable.defaults.orderFixed + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "orderFixed": [[0,'asc']] + * } ); + * } ) + */ + "aaSortingFixed": [], + + + /** + * DataTables can be instructed to load data to display in the table from a + * Ajax source. This option defines how that Ajax call is made and where to. + * + * The `ajax` property has three different modes of operation, depending on + * how it is defined. These are: + * + * * `string` - Set the URL from where the data should be loaded from. + * * `object` - Define properties for `jQuery.ajax`. + * * `function` - Custom data get function + * + * `string` + * -------- + * + * As a string, the `ajax` property simply defines the URL from which + * DataTables will load data. + * + * `object` + * -------- + * + * As an object, the parameters in the object are passed to + * [jQuery.ajax](http://api.jquery.com/jQuery.ajax/) allowing fine control + * of the Ajax request. DataTables has a number of default parameters which + * you can override using this option. Please refer to the jQuery + * documentation for a full description of the options available, although + * the following parameters provide additional options in DataTables or + * require special consideration: + * + * * `data` - As with jQuery, `data` can be provided as an object, but it + * can also be used as a function to manipulate the data DataTables sends + * to the server. The function takes a single parameter, an object of + * parameters with the values that DataTables has readied for sending. An + * object may be returned which will be merged into the DataTables + * defaults, or you can add the items to the object that was passed in and + * not return anything from the function. This supersedes `fnServerParams` + * from DataTables 1.9-. + * + * * `dataSrc` - By default DataTables will look for the property `data` (or + * `aaData` for compatibility with DataTables 1.9-) when obtaining data + * from an Ajax source or for server-side processing - this parameter + * allows that property to be changed. You can use Javascript dotted + * object notation to get a data source for multiple levels of nesting, or + * it my be used as a function. As a function it takes a single parameter, + * the JSON returned from the server, which can be manipulated as + * required, with the returned value being that used by DataTables as the + * data source for the table. This supersedes `sAjaxDataProp` from + * DataTables 1.9-. + * + * * `success` - Should not be overridden it is used internally in + * DataTables. To manipulate / transform the data returned by the server + * use `ajax.dataSrc`, or use `ajax` as a function (see below). + * + * `function` + * ---------- + * + * As a function, making the Ajax call is left up to yourself allowing + * complete control of the Ajax request. Indeed, if desired, a method other + * than Ajax could be used to obtain the required data, such as Web storage + * or an AIR database. + * + * The function is given four parameters and no return is required. The + * parameters are: + * + * 1. _object_ - Data to send to the server + * 2. _function_ - Callback function that must be executed when the required + * data has been obtained. That data should be passed into the callback + * as the only parameter + * 3. _object_ - DataTables settings object for the table + * + * Note that this supersedes `fnServerData` from DataTables 1.9-. + * + * @type string|object|function + * @default null + * + * @dtopt Option + * @name DataTable.defaults.ajax + * @since 1.10.0 + * + * @example + * // Get JSON data from a file via Ajax. + * // Note DataTables expects data in the form `{ data: [ ...data... ] }` by default). + * $('#example').dataTable( { + * "ajax": "data.json" + * } ); + * + * @example + * // Get JSON data from a file via Ajax, using `dataSrc` to change + * // `data` to `tableData` (i.e. `{ tableData: [ ...data... ] }`) + * $('#example').dataTable( { + * "ajax": { + * "url": "data.json", + * "dataSrc": "tableData" + * } + * } ); + * + * @example + * // Get JSON data from a file via Ajax, using `dataSrc` to read data + * // from a plain array rather than an array in an object + * $('#example').dataTable( { + * "ajax": { + * "url": "data.json", + * "dataSrc": "" + * } + * } ); + * + * @example + * // Manipulate the data returned from the server - add a link to data + * // (note this can, should, be done using `render` for the column - this + * // is just a simple example of how the data can be manipulated). + * $('#example').dataTable( { + * "ajax": { + * "url": "data.json", + * "dataSrc": function ( json ) { + * for ( var i=0, ien=json.length ; i<ien ; i++ ) { + * json[i][0] = '<a href="/message/'+json[i][0]+'>View message</a>'; + * } + * return json; + * } + * } + * } ); + * + * @example + * // Add data to the request + * $('#example').dataTable( { + * "ajax": { + * "url": "data.json", + * "data": function ( d ) { + * return { + * "extra_search": $('#extra').val() + * }; + * } + * } + * } ); + * + * @example + * // Send request as POST + * $('#example').dataTable( { + * "ajax": { + * "url": "data.json", + * "type": "POST" + * } + * } ); + * + * @example + * // Get the data from localStorage (could interface with a form for + * // adding, editing and removing rows). + * $('#example').dataTable( { + * "ajax": function (data, callback, settings) { + * callback( + * JSON.parse( localStorage.getItem('dataTablesData') ) + * ); + * } + * } ); + */ + "ajax": null, + + + /** + * This parameter allows you to readily specify the entries in the length drop + * down menu that DataTables shows when pagination is enabled. It can be + * either a 1D array of options which will be used for both the displayed + * option and the value, or a 2D array which will use the array in the first + * position as the value, and the array in the second position as the + * displayed options (useful for language strings such as 'All'). + * + * Note that the `pageLength` property will be automatically set to the + * first value given in this array, unless `pageLength` is also provided. + * @type array + * @default [ 10, 25, 50, 100 ] + * + * @dtopt Option + * @name DataTable.defaults.lengthMenu + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "lengthMenu": [[10, 25, 50, -1], [10, 25, 50, "All"]] + * } ); + * } ); + */ + "aLengthMenu": [ 10, 25, 50, 100 ], + + + /** + * The `columns` option in the initialisation parameter allows you to define + * details about the way individual columns behave. For a full list of + * column options that can be set, please see + * {@link DataTable.defaults.column}. Note that if you use `columns` to + * define your columns, you must have an entry in the array for every single + * column that you have in your table (these can be null if you don't which + * to specify any options). + * @member + * + * @name DataTable.defaults.column + */ + "aoColumns": null, + + /** + * Very similar to `columns`, `columnDefs` allows you to target a specific + * column, multiple columns, or all columns, using the `targets` property of + * each object in the array. This allows great flexibility when creating + * tables, as the `columnDefs` arrays can be of any length, targeting the + * columns you specifically want. `columnDefs` may use any of the column + * options available: {@link DataTable.defaults.column}, but it _must_ + * have `targets` defined in each object in the array. Values in the `targets` + * array may be: + * <ul> + * <li>a string - class name will be matched on the TH for the column</li> + * <li>0 or a positive integer - column index counting from the left</li> + * <li>a negative integer - column index counting from the right</li> + * <li>the string "_all" - all columns (i.e. assign a default)</li> + * </ul> + * @member + * + * @name DataTable.defaults.columnDefs + */ + "aoColumnDefs": null, + + + /** + * Basically the same as `search`, this parameter defines the individual column + * filtering state at initialisation time. The array must be of the same size + * as the number of columns, and each element be an object with the parameters + * `search` and `escapeRegex` (the latter is optional). 'null' is also + * accepted and the default will be used. + * @type array + * @default [] + * + * @dtopt Option + * @name DataTable.defaults.searchCols + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "searchCols": [ + * null, + * { "search": "My filter" }, + * null, + * { "search": "^[0-9]", "escapeRegex": false } + * ] + * } ); + * } ) + */ + "aoSearchCols": [], + + + /** + * An array of CSS classes that should be applied to displayed rows. This + * array may be of any length, and DataTables will apply each class + * sequentially, looping when required. + * @type array + * @default null <i>Will take the values determined by the `oClasses.stripe*` + * options</i> + * + * @dtopt Option + * @name DataTable.defaults.stripeClasses + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "stripeClasses": [ 'strip1', 'strip2', 'strip3' ] + * } ); + * } ) + */ + "asStripeClasses": null, + + + /** + * Enable or disable automatic column width calculation. This can be disabled + * as an optimisation (it takes some time to calculate the widths) if the + * tables widths are passed in using `columns`. + * @type boolean + * @default true + * + * @dtopt Features + * @name DataTable.defaults.autoWidth + * + * @example + * $(document).ready( function () { + * $('#example').dataTable( { + * "autoWidth": false + * } ); + * } ); + */ + "bAutoWidth": true, + + + /** + * Deferred rendering can provide DataTables with a huge speed boost when you + * are using an Ajax or JS data source for the table. This option, when set to + * true, will cause DataTables to defer the creation of the table elements for + * each row until they are needed for a draw - saving a significant amount of + * time. + * @type boolean + * @default false + * + * @dtopt Features + * @name DataTable.defaults.deferRender + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "ajax": "sources/arrays.txt", + * "deferRender": true + * } ); + * } ); + */ + "bDeferRender": false, + + + /** + * Replace a DataTable which matches the given selector and replace it with + * one which has the properties of the new initialisation object passed. If no + * table matches the selector, then the new DataTable will be constructed as + * per normal. + * @type boolean + * @default false + * + * @dtopt Options + * @name DataTable.defaults.destroy + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "srollY": "200px", + * "paginate": false + * } ); + * + * // Some time later.... + * $('#example').dataTable( { + * "filter": false, + * "destroy": true + * } ); + * } ); + */ + "bDestroy": false, + + + /** + * Enable or disable filtering of data. Filtering in DataTables is "smart" in + * that it allows the end user to input multiple words (space separated) and + * will match a row containing those words, even if not in the order that was + * specified (this allow matching across multiple columns). Note that if you + * wish to use filtering in DataTables this must remain 'true' - to remove the + * default filtering input box and retain filtering abilities, please use + * {@link DataTable.defaults.dom}. + * @type boolean + * @default true + * + * @dtopt Features + * @name DataTable.defaults.searching + * + * @example + * $(document).ready( function () { + * $('#example').dataTable( { + * "searching": false + * } ); + * } ); + */ + "bFilter": true, + + + /** + * Enable or disable the table information display. This shows information + * about the data that is currently visible on the page, including information + * about filtered data if that action is being performed. + * @type boolean + * @default true + * + * @dtopt Features + * @name DataTable.defaults.info + * + * @example + * $(document).ready( function () { + * $('#example').dataTable( { + * "info": false + * } ); + * } ); + */ + "bInfo": true, + + + /** + * Enable jQuery UI ThemeRoller support (required as ThemeRoller requires some + * slightly different and additional mark-up from what DataTables has + * traditionally used). + * @type boolean + * @default false + * + * @dtopt Features + * @name DataTable.defaults.jQueryUI + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "jQueryUI": true + * } ); + * } ); + */ + "bJQueryUI": false, + + + /** + * Allows the end user to select the size of a formatted page from a select + * menu (sizes are 10, 25, 50 and 100). Requires pagination (`paginate`). + * @type boolean + * @default true + * + * @dtopt Features + * @name DataTable.defaults.lengthChange + * + * @example + * $(document).ready( function () { + * $('#example').dataTable( { + * "lengthChange": false + * } ); + * } ); + */ + "bLengthChange": true, + + + /** + * Enable or disable pagination. + * @type boolean + * @default true + * + * @dtopt Features + * @name DataTable.defaults.paging + * + * @example + * $(document).ready( function () { + * $('#example').dataTable( { + * "paging": false + * } ); + * } ); + */ + "bPaginate": true, + + + /** + * Enable or disable the display of a 'processing' indicator when the table is + * being processed (e.g. a sort). This is particularly useful for tables with + * large amounts of data where it can take a noticeable amount of time to sort + * the entries. + * @type boolean + * @default false + * + * @dtopt Features + * @name DataTable.defaults.processing + * + * @example + * $(document).ready( function () { + * $('#example').dataTable( { + * "processing": true + * } ); + * } ); + */ + "bProcessing": false, + + + /** + * Retrieve the DataTables object for the given selector. Note that if the + * table has already been initialised, this parameter will cause DataTables + * to simply return the object that has already been set up - it will not take + * account of any changes you might have made to the initialisation object + * passed to DataTables (setting this parameter to true is an acknowledgement + * that you understand this). `destroy` can be used to reinitialise a table if + * you need. + * @type boolean + * @default false + * + * @dtopt Options + * @name DataTable.defaults.retrieve + * + * @example + * $(document).ready( function() { + * initTable(); + * tableActions(); + * } ); + * + * function initTable () + * { + * return $('#example').dataTable( { + * "scrollY": "200px", + * "paginate": false, + * "retrieve": true + * } ); + * } + * + * function tableActions () + * { + * var table = initTable(); + * // perform API operations with oTable + * } + */ + "bRetrieve": false, + + + /** + * When vertical (y) scrolling is enabled, DataTables will force the height of + * the table's viewport to the given height at all times (useful for layout). + * However, this can look odd when filtering data down to a small data set, + * and the footer is left "floating" further down. This parameter (when + * enabled) will cause DataTables to collapse the table's viewport down when + * the result set will fit within the given Y height. + * @type boolean + * @default false + * + * @dtopt Options + * @name DataTable.defaults.scrollCollapse + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "scrollY": "200", + * "scrollCollapse": true + * } ); + * } ); + */ + "bScrollCollapse": false, + + + /** + * Configure DataTables to use server-side processing. Note that the + * `ajax` parameter must also be given in order to give DataTables a + * source to obtain the required data for each draw. + * @type boolean + * @default false + * + * @dtopt Features + * @dtopt Server-side + * @name DataTable.defaults.serverSide + * + * @example + * $(document).ready( function () { + * $('#example').dataTable( { + * "serverSide": true, + * "ajax": "xhr.php" + * } ); + * } ); + */ + "bServerSide": false, + + + /** + * Enable or disable sorting of columns. Sorting of individual columns can be + * disabled by the `sortable` option for each column. + * @type boolean + * @default true + * + * @dtopt Features + * @name DataTable.defaults.ordering + * + * @example + * $(document).ready( function () { + * $('#example').dataTable( { + * "ordering": false + * } ); + * } ); + */ + "bSort": true, + + + /** + * Enable or display DataTables' ability to sort multiple columns at the + * same time (activated by shift-click by the user). + * @type boolean + * @default true + * + * @dtopt Options + * @name DataTable.defaults.orderMulti + * + * @example + * // Disable multiple column sorting ability + * $(document).ready( function () { + * $('#example').dataTable( { + * "orderMulti": false + * } ); + * } ); + */ + "bSortMulti": true, + + + /** + * Allows control over whether DataTables should use the top (true) unique + * cell that is found for a single column, or the bottom (false - default). + * This is useful when using complex headers. + * @type boolean + * @default false + * + * @dtopt Options + * @name DataTable.defaults.orderCellsTop + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "orderCellsTop": true + * } ); + * } ); + */ + "bSortCellsTop": false, + + + /** + * Enable or disable the addition of the classes `sorting\_1`, `sorting\_2` and + * `sorting\_3` to the columns which are currently being sorted on. This is + * presented as a feature switch as it can increase processing time (while + * classes are removed and added) so for large data sets you might want to + * turn this off. + * @type boolean + * @default true + * + * @dtopt Features + * @name DataTable.defaults.orderClasses + * + * @example + * $(document).ready( function () { + * $('#example').dataTable( { + * "orderClasses": false + * } ); + * } ); + */ + "bSortClasses": true, + + + /** + * Enable or disable state saving. When enabled HTML5 `localStorage` will be + * used to save table display information such as pagination information, + * display length, filtering and sorting. As such when the end user reloads + * the page the display display will match what thy had previously set up. + * + * Due to the use of `localStorage` the default state saving is not supported + * in IE6 or 7. If state saving is required in those browsers, use + * `stateSaveCallback` to provide a storage solution such as cookies. + * @type boolean + * @default false + * + * @dtopt Features + * @name DataTable.defaults.stateSave + * + * @example + * $(document).ready( function () { + * $('#example').dataTable( { + * "stateSave": true + * } ); + * } ); + */ + "bStateSave": false, + + + /** + * This function is called when a TR element is created (and all TD child + * elements have been inserted), or registered if using a DOM source, allowing + * manipulation of the TR element (adding classes etc). + * @type function + * @param {node} row "TR" element for the current row + * @param {array} data Raw data array for this row + * @param {int} dataIndex The index of this row in the internal aoData array + * + * @dtopt Callbacks + * @name DataTable.defaults.createdRow + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "createdRow": function( row, data, dataIndex ) { + * // Bold the grade for all 'A' grade browsers + * if ( data[4] == "A" ) + * { + * $('td:eq(4)', row).html( '<b>A</b>' ); + * } + * } + * } ); + * } ); + */ + "fnCreatedRow": null, + + + /** + * This function is called on every 'draw' event, and allows you to + * dynamically modify any aspect you want about the created DOM. + * @type function + * @param {object} settings DataTables settings object + * + * @dtopt Callbacks + * @name DataTable.defaults.drawCallback + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "drawCallback": function( settings ) { + * alert( 'DataTables has redrawn the table' ); + * } + * } ); + * } ); + */ + "fnDrawCallback": null, + + + /** + * Identical to fnHeaderCallback() but for the table footer this function + * allows you to modify the table footer on every 'draw' event. + * @type function + * @param {node} foot "TR" element for the footer + * @param {array} data Full table data (as derived from the original HTML) + * @param {int} start Index for the current display starting point in the + * display array + * @param {int} end Index for the current display ending point in the + * display array + * @param {array int} display Index array to translate the visual position + * to the full data array + * + * @dtopt Callbacks + * @name DataTable.defaults.footerCallback + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "footerCallback": function( tfoot, data, start, end, display ) { + * tfoot.getElementsByTagName('th')[0].innerHTML = "Starting index is "+start; + * } + * } ); + * } ) + */ + "fnFooterCallback": null, + + + /** + * When rendering large numbers in the information element for the table + * (i.e. "Showing 1 to 10 of 57 entries") DataTables will render large numbers + * to have a comma separator for the 'thousands' units (e.g. 1 million is + * rendered as "1,000,000") to help readability for the end user. This + * function will override the default method DataTables uses. + * @type function + * @member + * @param {int} toFormat number to be formatted + * @returns {string} formatted string for DataTables to show the number + * + * @dtopt Callbacks + * @name DataTable.defaults.formatNumber + * + * @example + * // Format a number using a single quote for the separator (note that + * // this can also be done with the language.thousands option) + * $(document).ready( function() { + * $('#example').dataTable( { + * "formatNumber": function ( toFormat ) { + * return toFormat.toString().replace( + * /\B(?=(\d{3})+(?!\d))/g, "'" + * ); + * }; + * } ); + * } ); + */ + "fnFormatNumber": function ( toFormat ) { + return toFormat.toString().replace( + /\B(?=(\d{3})+(?!\d))/g, + this.oLanguage.sThousands + ); + }, + + + /** + * This function is called on every 'draw' event, and allows you to + * dynamically modify the header row. This can be used to calculate and + * display useful information about the table. + * @type function + * @param {node} head "TR" element for the header + * @param {array} data Full table data (as derived from the original HTML) + * @param {int} start Index for the current display starting point in the + * display array + * @param {int} end Index for the current display ending point in the + * display array + * @param {array int} display Index array to translate the visual position + * to the full data array + * + * @dtopt Callbacks + * @name DataTable.defaults.headerCallback + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "fheaderCallback": function( head, data, start, end, display ) { + * head.getElementsByTagName('th')[0].innerHTML = "Displaying "+(end-start)+" records"; + * } + * } ); + * } ) + */ + "fnHeaderCallback": null, + + + /** + * The information element can be used to convey information about the current + * state of the table. Although the internationalisation options presented by + * DataTables are quite capable of dealing with most customisations, there may + * be times where you wish to customise the string further. This callback + * allows you to do exactly that. + * @type function + * @param {object} oSettings DataTables settings object + * @param {int} start Starting position in data for the draw + * @param {int} end End position in data for the draw + * @param {int} max Total number of rows in the table (regardless of + * filtering) + * @param {int} total Total number of rows in the data set, after filtering + * @param {string} pre The string that DataTables has formatted using it's + * own rules + * @returns {string} The string to be displayed in the information element. + * + * @dtopt Callbacks + * @name DataTable.defaults.infoCallback + * + * @example + * $('#example').dataTable( { + * "infoCallback": function( settings, start, end, max, total, pre ) { + * return start +" to "+ end; + * } + * } ); + */ + "fnInfoCallback": null, + + + /** + * Called when the table has been initialised. Normally DataTables will + * initialise sequentially and there will be no need for this function, + * however, this does not hold true when using external language information + * since that is obtained using an async XHR call. + * @type function + * @param {object} settings DataTables settings object + * @param {object} json The JSON object request from the server - only + * present if client-side Ajax sourced data is used + * + * @dtopt Callbacks + * @name DataTable.defaults.initComplete + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "initComplete": function(settings, json) { + * alert( 'DataTables has finished its initialisation.' ); + * } + * } ); + * } ) + */ + "fnInitComplete": null, + + + /** + * Called at the very start of each table draw and can be used to cancel the + * draw by returning false, any other return (including undefined) results in + * the full draw occurring). + * @type function + * @param {object} settings DataTables settings object + * @returns {boolean} False will cancel the draw, anything else (including no + * return) will allow it to complete. + * + * @dtopt Callbacks + * @name DataTable.defaults.preDrawCallback + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "preDrawCallback": function( settings ) { + * if ( $('#test').val() == 1 ) { + * return false; + * } + * } + * } ); + * } ); + */ + "fnPreDrawCallback": null, + + + /** + * This function allows you to 'post process' each row after it have been + * generated for each table draw, but before it is rendered on screen. This + * function might be used for setting the row class name etc. + * @type function + * @param {node} row "TR" element for the current row + * @param {array} data Raw data array for this row + * @param {int} displayIndex The display index for the current table draw + * @param {int} displayIndexFull The index of the data in the full list of + * rows (after filtering) + * + * @dtopt Callbacks + * @name DataTable.defaults.rowCallback + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "rowCallback": function( row, data, displayIndex, displayIndexFull ) { + * // Bold the grade for all 'A' grade browsers + * if ( data[4] == "A" ) { + * $('td:eq(4)', row).html( '<b>A</b>' ); + * } + * } + * } ); + * } ); + */ + "fnRowCallback": null, + + + /** + * __Deprecated__ The functionality provided by this parameter has now been + * superseded by that provided through `ajax`, which should be used instead. + * + * This parameter allows you to override the default function which obtains + * the data from the server so something more suitable for your application. + * For example you could use POST data, or pull information from a Gears or + * AIR database. + * @type function + * @member + * @param {string} source HTTP source to obtain the data from (`ajax`) + * @param {array} data A key/value pair object containing the data to send + * to the server + * @param {function} callback to be called on completion of the data get + * process that will draw the data on the page. + * @param {object} settings DataTables settings object + * + * @dtopt Callbacks + * @dtopt Server-side + * @name DataTable.defaults.serverData + * + * @deprecated 1.10. Please use `ajax` for this functionality now. + */ + "fnServerData": null, + + + /** + * __Deprecated__ The functionality provided by this parameter has now been + * superseded by that provided through `ajax`, which should be used instead. + * + * It is often useful to send extra data to the server when making an Ajax + * request - for example custom filtering information, and this callback + * function makes it trivial to send extra information to the server. The + * passed in parameter is the data set that has been constructed by + * DataTables, and you can add to this or modify it as you require. + * @type function + * @param {array} data Data array (array of objects which are name/value + * pairs) that has been constructed by DataTables and will be sent to the + * server. In the case of Ajax sourced data with server-side processing + * this will be an empty array, for server-side processing there will be a + * significant number of parameters! + * @returns {undefined} Ensure that you modify the data array passed in, + * as this is passed by reference. + * + * @dtopt Callbacks + * @dtopt Server-side + * @name DataTable.defaults.serverParams + * + * @deprecated 1.10. Please use `ajax` for this functionality now. + */ + "fnServerParams": null, + + + /** + * Load the table state. With this function you can define from where, and how, the + * state of a table is loaded. By default DataTables will load from `localStorage` + * but you might wish to use a server-side database or cookies. + * @type function + * @member + * @param {object} settings DataTables settings object + * @param {object} callback Callback that can be executed when done. It + * should be passed the loaded state object. + * @return {object} The DataTables state object to be loaded + * + * @dtopt Callbacks + * @name DataTable.defaults.stateLoadCallback + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "stateSave": true, + * "stateLoadCallback": function (settings, callback) { + * $.ajax( { + * "url": "/state_load", + * "dataType": "json", + * "success": function (json) { + * callback( json ); + * } + * } ); + * } + * } ); + * } ); + */ + "fnStateLoadCallback": function ( settings ) { + try { + return JSON.parse( + (settings.iStateDuration === -1 ? sessionStorage : localStorage).getItem( + 'DataTables_'+settings.sInstance+'_'+location.pathname + ) + ); + } catch (e) {} + }, + + + /** + * Callback which allows modification of the saved state prior to loading that state. + * This callback is called when the table is loading state from the stored data, but + * prior to the settings object being modified by the saved state. Note that for + * plug-in authors, you should use the `stateLoadParams` event to load parameters for + * a plug-in. + * @type function + * @param {object} settings DataTables settings object + * @param {object} data The state object that is to be loaded + * + * @dtopt Callbacks + * @name DataTable.defaults.stateLoadParams + * + * @example + * // Remove a saved filter, so filtering is never loaded + * $(document).ready( function() { + * $('#example').dataTable( { + * "stateSave": true, + * "stateLoadParams": function (settings, data) { + * data.oSearch.sSearch = ""; + * } + * } ); + * } ); + * + * @example + * // Disallow state loading by returning false + * $(document).ready( function() { + * $('#example').dataTable( { + * "stateSave": true, + * "stateLoadParams": function (settings, data) { + * return false; + * } + * } ); + * } ); + */ + "fnStateLoadParams": null, + + + /** + * Callback that is called when the state has been loaded from the state saving method + * and the DataTables settings object has been modified as a result of the loaded state. + * @type function + * @param {object} settings DataTables settings object + * @param {object} data The state object that was loaded + * + * @dtopt Callbacks + * @name DataTable.defaults.stateLoaded + * + * @example + * // Show an alert with the filtering value that was saved + * $(document).ready( function() { + * $('#example').dataTable( { + * "stateSave": true, + * "stateLoaded": function (settings, data) { + * alert( 'Saved filter was: '+data.oSearch.sSearch ); + * } + * } ); + * } ); + */ + "fnStateLoaded": null, + + + /** + * Save the table state. This function allows you to define where and how the state + * information for the table is stored By default DataTables will use `localStorage` + * but you might wish to use a server-side database or cookies. + * @type function + * @member + * @param {object} settings DataTables settings object + * @param {object} data The state object to be saved + * + * @dtopt Callbacks + * @name DataTable.defaults.stateSaveCallback + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "stateSave": true, + * "stateSaveCallback": function (settings, data) { + * // Send an Ajax request to the server with the state object + * $.ajax( { + * "url": "/state_save", + * "data": data, + * "dataType": "json", + * "method": "POST" + * "success": function () {} + * } ); + * } + * } ); + * } ); + */ + "fnStateSaveCallback": function ( settings, data ) { + try { + (settings.iStateDuration === -1 ? sessionStorage : localStorage).setItem( + 'DataTables_'+settings.sInstance+'_'+location.pathname, + JSON.stringify( data ) + ); + } catch (e) {} + }, + + + /** + * Callback which allows modification of the state to be saved. Called when the table + * has changed state a new state save is required. This method allows modification of + * the state saving object prior to actually doing the save, including addition or + * other state properties or modification. Note that for plug-in authors, you should + * use the `stateSaveParams` event to save parameters for a plug-in. + * @type function + * @param {object} settings DataTables settings object + * @param {object} data The state object to be saved + * + * @dtopt Callbacks + * @name DataTable.defaults.stateSaveParams + * + * @example + * // Remove a saved filter, so filtering is never saved + * $(document).ready( function() { + * $('#example').dataTable( { + * "stateSave": true, + * "stateSaveParams": function (settings, data) { + * data.oSearch.sSearch = ""; + * } + * } ); + * } ); + */ + "fnStateSaveParams": null, + + + /** + * Duration for which the saved state information is considered valid. After this period + * has elapsed the state will be returned to the default. + * Value is given in seconds. + * @type int + * @default 7200 <i>(2 hours)</i> + * + * @dtopt Options + * @name DataTable.defaults.stateDuration + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "stateDuration": 60*60*24; // 1 day + * } ); + * } ) + */ + "iStateDuration": 7200, + + + /** + * When enabled DataTables will not make a request to the server for the first + * page draw - rather it will use the data already on the page (no sorting etc + * will be applied to it), thus saving on an XHR at load time. `deferLoading` + * is used to indicate that deferred loading is required, but it is also used + * to tell DataTables how many records there are in the full table (allowing + * the information element and pagination to be displayed correctly). In the case + * where a filtering is applied to the table on initial load, this can be + * indicated by giving the parameter as an array, where the first element is + * the number of records available after filtering and the second element is the + * number of records without filtering (allowing the table information element + * to be shown correctly). + * @type int | array + * @default null + * + * @dtopt Options + * @name DataTable.defaults.deferLoading + * + * @example + * // 57 records available in the table, no filtering applied + * $(document).ready( function() { + * $('#example').dataTable( { + * "serverSide": true, + * "ajax": "scripts/server_processing.php", + * "deferLoading": 57 + * } ); + * } ); + * + * @example + * // 57 records after filtering, 100 without filtering (an initial filter applied) + * $(document).ready( function() { + * $('#example').dataTable( { + * "serverSide": true, + * "ajax": "scripts/server_processing.php", + * "deferLoading": [ 57, 100 ], + * "search": { + * "search": "my_filter" + * } + * } ); + * } ); + */ + "iDeferLoading": null, + + + /** + * Number of rows to display on a single page when using pagination. If + * feature enabled (`lengthChange`) then the end user will be able to override + * this to a custom setting using a pop-up menu. + * @type int + * @default 10 + * + * @dtopt Options + * @name DataTable.defaults.pageLength + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "pageLength": 50 + * } ); + * } ) + */ + "iDisplayLength": 10, + + + /** + * Define the starting point for data display when using DataTables with + * pagination. Note that this parameter is the number of records, rather than + * the page number, so if you have 10 records per page and want to start on + * the third page, it should be "20". + * @type int + * @default 0 + * + * @dtopt Options + * @name DataTable.defaults.displayStart + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "displayStart": 20 + * } ); + * } ) + */ + "iDisplayStart": 0, + + + /** + * By default DataTables allows keyboard navigation of the table (sorting, paging, + * and filtering) by adding a `tabindex` attribute to the required elements. This + * allows you to tab through the controls and press the enter key to activate them. + * The tabindex is default 0, meaning that the tab follows the flow of the document. + * You can overrule this using this parameter if you wish. Use a value of -1 to + * disable built-in keyboard navigation. + * @type int + * @default 0 + * + * @dtopt Options + * @name DataTable.defaults.tabIndex + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "tabIndex": 1 + * } ); + * } ); + */ + "iTabIndex": 0, + + + /** + * Classes that DataTables assigns to the various components and features + * that it adds to the HTML table. This allows classes to be configured + * during initialisation in addition to through the static + * {@link DataTable.ext.oStdClasses} object). + * @namespace + * @name DataTable.defaults.classes + */ + "oClasses": {}, + + + /** + * All strings that DataTables uses in the user interface that it creates + * are defined in this object, allowing you to modified them individually or + * completely replace them all as required. + * @namespace + * @name DataTable.defaults.language + */ + "oLanguage": { + /** + * Strings that are used for WAI-ARIA labels and controls only (these are not + * actually visible on the page, but will be read by screenreaders, and thus + * must be internationalised as well). + * @namespace + * @name DataTable.defaults.language.aria + */ + "oAria": { + /** + * ARIA label that is added to the table headers when the column may be + * sorted ascending by activing the column (click or return when focused). + * Note that the column header is prefixed to this string. + * @type string + * @default : activate to sort column ascending + * + * @dtopt Language + * @name DataTable.defaults.language.aria.sortAscending + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "language": { + * "aria": { + * "sortAscending": " - click/return to sort ascending" + * } + * } + * } ); + * } ); + */ + "sSortAscending": ": activate to sort column ascending", + + /** + * ARIA label that is added to the table headers when the column may be + * sorted descending by activing the column (click or return when focused). + * Note that the column header is prefixed to this string. + * @type string + * @default : activate to sort column ascending + * + * @dtopt Language + * @name DataTable.defaults.language.aria.sortDescending + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "language": { + * "aria": { + * "sortDescending": " - click/return to sort descending" + * } + * } + * } ); + * } ); + */ + "sSortDescending": ": activate to sort column descending" + }, + + /** + * Pagination string used by DataTables for the built-in pagination + * control types. + * @namespace + * @name DataTable.defaults.language.paginate + */ + "oPaginate": { + /** + * Text to use when using the 'full_numbers' type of pagination for the + * button to take the user to the first page. + * @type string + * @default First + * + * @dtopt Language + * @name DataTable.defaults.language.paginate.first + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "language": { + * "paginate": { + * "first": "First page" + * } + * } + * } ); + * } ); + */ + "sFirst": "First", + + + /** + * Text to use when using the 'full_numbers' type of pagination for the + * button to take the user to the last page. + * @type string + * @default Last + * + * @dtopt Language + * @name DataTable.defaults.language.paginate.last + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "language": { + * "paginate": { + * "last": "Last page" + * } + * } + * } ); + * } ); + */ + "sLast": "Last", + + + /** + * Text to use for the 'next' pagination button (to take the user to the + * next page). + * @type string + * @default Next + * + * @dtopt Language + * @name DataTable.defaults.language.paginate.next + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "language": { + * "paginate": { + * "next": "Next page" + * } + * } + * } ); + * } ); + */ + "sNext": "Next", + + + /** + * Text to use for the 'previous' pagination button (to take the user to + * the previous page). + * @type string + * @default Previous + * + * @dtopt Language + * @name DataTable.defaults.language.paginate.previous + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "language": { + * "paginate": { + * "previous": "Previous page" + * } + * } + * } ); + * } ); + */ + "sPrevious": "Previous" + }, + + /** + * This string is shown in preference to `zeroRecords` when the table is + * empty of data (regardless of filtering). Note that this is an optional + * parameter - if it is not given, the value of `zeroRecords` will be used + * instead (either the default or given value). + * @type string + * @default No data available in table + * + * @dtopt Language + * @name DataTable.defaults.language.emptyTable + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "language": { + * "emptyTable": "No data available in table" + * } + * } ); + * } ); + */ + "sEmptyTable": "No Results Found", + + + /** + * This string gives information to the end user about the information + * that is current on display on the page. The following tokens can be + * used in the string and will be dynamically replaced as the table + * display updates. This tokens can be placed anywhere in the string, or + * removed as needed by the language requires: + * + * * `\_START\_` - Display index of the first record on the current page + * * `\_END\_` - Display index of the last record on the current page + * * `\_TOTAL\_` - Number of records in the table after filtering + * * `\_MAX\_` - Number of records in the table without filtering + * * `\_PAGE\_` - Current page number + * * `\_PAGES\_` - Total number of pages of data in the table + * + * @type string + * @default Showing _START_ to _END_ of _TOTAL_ entries + * + * @dtopt Language + * @name DataTable.defaults.language.info + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "language": { + * "info": "Showing page _PAGE_ of _PAGES_" + * } + * } ); + * } ); + */ + "sInfo": "Showing _START_ to _END_ of _TOTAL_ entries", + + + /** + * Display information string for when the table is empty. Typically the + * format of this string should match `info`. + * @type string + * @default Showing 0 to 0 of 0 entries + * + * @dtopt Language + * @name DataTable.defaults.language.infoEmpty + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "language": { + * "infoEmpty": "No entries to show" + * } + * } ); + * } ); + */ + "sInfoEmpty": "Showing 0 to 0 of 0 entries", + + + /** + * When a user filters the information in a table, this string is appended + * to the information (`info`) to give an idea of how strong the filtering + * is. The variable _MAX_ is dynamically updated. + * @type string + * @default (filtered from _MAX_ total entries) + * + * @dtopt Language + * @name DataTable.defaults.language.infoFiltered + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "language": { + * "infoFiltered": " - filtering from _MAX_ records" + * } + * } ); + * } ); + */ + "sInfoFiltered": "(filtered from _MAX_ total entries)", + + + /** + * If can be useful to append extra information to the info string at times, + * and this variable does exactly that. This information will be appended to + * the `info` (`infoEmpty` and `infoFiltered` in whatever combination they are + * being used) at all times. + * @type string + * @default <i>Empty string</i> + * + * @dtopt Language + * @name DataTable.defaults.language.infoPostFix + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "language": { + * "infoPostFix": "All records shown are derived from real information." + * } + * } ); + * } ); + */ + "sInfoPostFix": "", + + + /** + * This decimal place operator is a little different from the other + * language options since DataTables doesn't output floating point + * numbers, so it won't ever use this for display of a number. Rather, + * what this parameter does is modify the sort methods of the table so + * that numbers which are in a format which has a character other than + * a period (`.`) as a decimal place will be sorted numerically. + * + * Note that numbers with different decimal places cannot be shown in + * the same table and still be sortable, the table must be consistent. + * However, multiple different tables on the page can use different + * decimal place characters. + * @type string + * @default + * + * @dtopt Language + * @name DataTable.defaults.language.decimal + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "language": { + * "decimal": "," + * "thousands": "." + * } + * } ); + * } ); + */ + "sDecimal": "", + + + /** + * DataTables has a build in number formatter (`formatNumber`) which is + * used to format large numbers that are used in the table information. + * By default a comma is used, but this can be trivially changed to any + * character you wish with this parameter. + * @type string + * @default , + * + * @dtopt Language + * @name DataTable.defaults.language.thousands + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "language": { + * "thousands": "'" + * } + * } ); + * } ); + */ + "sThousands": ",", + + + /** + * Detail the action that will be taken when the drop down menu for the + * pagination length option is changed. The '_MENU_' variable is replaced + * with a default select list of 10, 25, 50 and 100, and can be replaced + * with a custom select box if required. + * @type string + * @default Show _MENU_ entries + * + * @dtopt Language + * @name DataTable.defaults.language.lengthMenu + * + * @example + * // Language change only + * $(document).ready( function() { + * $('#example').dataTable( { + * "language": { + * "lengthMenu": "Display _MENU_ records" + * } + * } ); + * } ); + * + * @example + * // Language and options change + * $(document).ready( function() { + * $('#example').dataTable( { + * "language": { + * "lengthMenu": 'Display <select>'+ + * '<option value="10">10</option>'+ + * '<option value="20">20</option>'+ + * '<option value="30">30</option>'+ + * '<option value="40">40</option>'+ + * '<option value="50">50</option>'+ + * '<option value="-1">All</option>'+ + * '</select> records' + * } + * } ); + * } ); + */ + "sLengthMenu": "Show _MENU_ entries", + + + /** + * When using Ajax sourced data and during the first draw when DataTables is + * gathering the data, this message is shown in an empty row in the table to + * indicate to the end user the the data is being loaded. Note that this + * parameter is not used when loading data by server-side processing, just + * Ajax sourced data with client-side processing. + * @type string + * @default Loading... + * + * @dtopt Language + * @name DataTable.defaults.language.loadingRecords + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "language": { + * "loadingRecords": "Please wait - loading..." + * } + * } ); + * } ); + */ + "sLoadingRecords": "Loading...", + + + /** + * Text which is displayed when the table is processing a user action + * (usually a sort command or similar). + * @type string + * @default Processing... + * + * @dtopt Language + * @name DataTable.defaults.language.processing + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "language": { + * "processing": "DataTables is currently busy" + * } + * } ); + * } ); + */ + "sProcessing": "Processing...", + + + /** + * Details the actions that will be taken when the user types into the + * filtering input text box. The variable "_INPUT_", if used in the string, + * is replaced with the HTML text box for the filtering input allowing + * control over where it appears in the string. If "_INPUT_" is not given + * then the input box is appended to the string automatically. + * @type string + * @default Search: + * + * @dtopt Language + * @name DataTable.defaults.language.search + * + * @example + * // Input text box will be appended at the end automatically + * $(document).ready( function() { + * $('#example').dataTable( { + * "language": { + * "search": "Filter records:" + * } + * } ); + * } ); + * + * @example + * // Specify where the filter should appear + * $(document).ready( function() { + * $('#example').dataTable( { + * "language": { + * "search": "Apply filter _INPUT_ to table" + * } + * } ); + * } ); + */ + "sSearch": "Search:", + + + /** + * Assign a `placeholder` attribute to the search `input` element + * @type string + * @default + * + * @dtopt Language + * @name DataTable.defaults.language.searchPlaceholder + */ + "sSearchPlaceholder": "", + + + /** + * All of the language information can be stored in a file on the + * server-side, which DataTables will look up if this parameter is passed. + * It must store the URL of the language file, which is in a JSON format, + * and the object has the same properties as the oLanguage object in the + * initialiser object (i.e. the above parameters). Please refer to one of + * the example language files to see how this works in action. + * @type string + * @default <i>Empty string - i.e. disabled</i> + * + * @dtopt Language + * @name DataTable.defaults.language.url + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "language": { + * "url": "http://www.sprymedia.co.uk/dataTables/lang.txt" + * } + * } ); + * } ); + */ + "sUrl": "", + + + /** + * Text shown inside the table records when the is no information to be + * displayed after filtering. `emptyTable` is shown when there is simply no + * information in the table at all (regardless of filtering). + * @type string + * @default No matching records found + * + * @dtopt Language + * @name DataTable.defaults.language.zeroRecords + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "language": { + * "zeroRecords": "No records to display" + * } + * } ); + * } ); + */ + "sZeroRecords": "No matching records found" + }, + + + /** + * This parameter allows you to have define the global filtering state at + * initialisation time. As an object the `search` parameter must be + * defined, but all other parameters are optional. When `regex` is true, + * the search string will be treated as a regular expression, when false + * (default) it will be treated as a straight string. When `smart` + * DataTables will use it's smart filtering methods (to word match at + * any point in the data), when false this will not be done. + * @namespace + * @extends DataTable.models.oSearch + * + * @dtopt Options + * @name DataTable.defaults.search + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "search": {"search": "Initial search"} + * } ); + * } ) + */ + "oSearch": $.extend( {}, DataTable.models.oSearch ), + + + /** + * __Deprecated__ The functionality provided by this parameter has now been + * superseded by that provided through `ajax`, which should be used instead. + * + * By default DataTables will look for the property `data` (or `aaData` for + * compatibility with DataTables 1.9-) when obtaining data from an Ajax + * source or for server-side processing - this parameter allows that + * property to be changed. You can use Javascript dotted object notation to + * get a data source for multiple levels of nesting. + * @type string + * @default data + * + * @dtopt Options + * @dtopt Server-side + * @name DataTable.defaults.ajaxDataProp + * + * @deprecated 1.10. Please use `ajax` for this functionality now. + */ + "sAjaxDataProp": "data", + + + /** + * __Deprecated__ The functionality provided by this parameter has now been + * superseded by that provided through `ajax`, which should be used instead. + * + * You can instruct DataTables to load data from an external + * source using this parameter (use aData if you want to pass data in you + * already have). Simply provide a url a JSON object can be obtained from. + * @type string + * @default null + * + * @dtopt Options + * @dtopt Server-side + * @name DataTable.defaults.ajaxSource + * + * @deprecated 1.10. Please use `ajax` for this functionality now. + */ + "sAjaxSource": null, + + + /** + * This initialisation variable allows you to specify exactly where in the + * DOM you want DataTables to inject the various controls it adds to the page + * (for example you might want the pagination controls at the top of the + * table). DIV elements (with or without a custom class) can also be added to + * aid styling. The follow syntax is used: + * <ul> + * <li>The following options are allowed: + * <ul> + * <li>'l' - Length changing</li> + * <li>'f' - Filtering input</li> + * <li>'t' - The table!</li> + * <li>'i' - Information</li> + * <li>'p' - Pagination</li> + * <li>'r' - pRocessing</li> + * </ul> + * </li> + * <li>The following constants are allowed: + * <ul> + * <li>'H' - jQueryUI theme "header" classes ('fg-toolbar ui-widget-header ui-corner-tl ui-corner-tr ui-helper-clearfix')</li> + * <li>'F' - jQueryUI theme "footer" classes ('fg-toolbar ui-widget-header ui-corner-bl ui-corner-br ui-helper-clearfix')</li> + * </ul> + * </li> + * <li>The following syntax is expected: + * <ul> + * <li>'<' and '>' - div elements</li> + * <li>'<"class" and '>' - div with a class</li> + * <li>'<"#id" and '>' - div with an ID</li> + * </ul> + * </li> + * <li>Examples: + * <ul> + * <li>'<"wrapper"flipt>'</li> + * <li>'<lf<t>ip>'</li> + * </ul> + * </li> + * </ul> + * @type string + * @default lfrtip <i>(when `jQueryUI` is false)</i> <b>or</b> + * <"H"lfr>t<"F"ip> <i>(when `jQueryUI` is true)</i> + * + * @dtopt Options + * @name DataTable.defaults.dom + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "dom": '<"top"i>rt<"bottom"flp><"clear">' + * } ); + * } ); + */ + "sDom": "lfrtip", + + + /** + * Search delay option. This will throttle full table searches that use the + * DataTables provided search input element (it does not effect calls to + * `dt-api search()`, providing a delay before the search is made. + * @type integer + * @default 0 + * + * @dtopt Options + * @name DataTable.defaults.searchDelay + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "searchDelay": 200 + * } ); + * } ) + */ + "searchDelay": null, + + + /** + * DataTables features six different built-in options for the buttons to + * display for pagination control: + * + * * `numbers` - Page number buttons only + * * `simple` - 'Previous' and 'Next' buttons only + * * 'simple_numbers` - 'Previous' and 'Next' buttons, plus page numbers + * * `full` - 'First', 'Previous', 'Next' and 'Last' buttons + * * `full_numbers` - 'First', 'Previous', 'Next' and 'Last' buttons, plus page numbers + * * `first_last_numbers` - 'First' and 'Last' buttons, plus page numbers + * + * Further methods can be added using {@link DataTable.ext.oPagination}. + * @type string + * @default simple_numbers + * + * @dtopt Options + * @name DataTable.defaults.pagingType + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "pagingType": "full_numbers" + * } ); + * } ) + */ + "sPaginationType": "simple_numbers", + + + /** + * Enable horizontal scrolling. When a table is too wide to fit into a + * certain layout, or you have a large number of columns in the table, you + * can enable x-scrolling to show the table in a viewport, which can be + * scrolled. This property can be `true` which will allow the table to + * scroll horizontally when needed, or any CSS unit, or a number (in which + * case it will be treated as a pixel measurement). Setting as simply `true` + * is recommended. + * @type boolean|string + * @default <i>blank string - i.e. disabled</i> + * + * @dtopt Features + * @name DataTable.defaults.scrollX + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "scrollX": true, + * "scrollCollapse": true + * } ); + * } ); + */ + "sScrollX": "", + + + /** + * This property can be used to force a DataTable to use more width than it + * might otherwise do when x-scrolling is enabled. For example if you have a + * table which requires to be well spaced, this parameter is useful for + * "over-sizing" the table, and thus forcing scrolling. This property can by + * any CSS unit, or a number (in which case it will be treated as a pixel + * measurement). + * @type string + * @default <i>blank string - i.e. disabled</i> + * + * @dtopt Options + * @name DataTable.defaults.scrollXInner + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "scrollX": "100%", + * "scrollXInner": "110%" + * } ); + * } ); + */ + "sScrollXInner": "", + + + /** + * Enable vertical scrolling. Vertical scrolling will constrain the DataTable + * to the given height, and enable scrolling for any data which overflows the + * current viewport. This can be used as an alternative to paging to display + * a lot of data in a small area (although paging and scrolling can both be + * enabled at the same time). This property can be any CSS unit, or a number + * (in which case it will be treated as a pixel measurement). + * @type string + * @default <i>blank string - i.e. disabled</i> + * + * @dtopt Features + * @name DataTable.defaults.scrollY + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "scrollY": "200px", + * "paginate": false + * } ); + * } ); + */ + "sScrollY": "", + + + /** + * __Deprecated__ The functionality provided by this parameter has now been + * superseded by that provided through `ajax`, which should be used instead. + * + * Set the HTTP method that is used to make the Ajax call for server-side + * processing or Ajax sourced data. + * @type string + * @default GET + * + * @dtopt Options + * @dtopt Server-side + * @name DataTable.defaults.serverMethod + * + * @deprecated 1.10. Please use `ajax` for this functionality now. + */ + "sServerMethod": "GET", + + + /** + * DataTables makes use of renderers when displaying HTML elements for + * a table. These renderers can be added or modified by plug-ins to + * generate suitable mark-up for a site. For example the Bootstrap + * integration plug-in for DataTables uses a paging button renderer to + * display pagination buttons in the mark-up required by Bootstrap. + * + * For further information about the renderers available see + * DataTable.ext.renderer + * @type string|object + * @default null + * + * @name DataTable.defaults.renderer + * + */ + "renderer": null, + + + /** + * Set the data property name that DataTables should use to get a row's id + * to set as the `id` property in the node. + * @type string + * @default DT_RowId + * + * @name DataTable.defaults.rowId + */ + "rowId": "DT_RowId" + }; + + _fnHungarianMap( DataTable.defaults ); + + + + /* + * Developer note - See note in model.defaults.js about the use of Hungarian + * notation and camel case. + */ + + /** + * Column options that can be given to DataTables at initialisation time. + * @namespace + */ + DataTable.defaults.column = { + /** + * Define which column(s) an order will occur on for this column. This + * allows a column's ordering to take multiple columns into account when + * doing a sort or use the data from a different column. For example first + * name / last name columns make sense to do a multi-column sort over the + * two columns. + * @type array|int + * @default null <i>Takes the value of the column index automatically</i> + * + * @name DataTable.defaults.column.orderData + * @dtopt Columns + * + * @example + * // Using `columnDefs` + * $(document).ready( function() { + * $('#example').dataTable( { + * "columnDefs": [ + * { "orderData": [ 0, 1 ], "targets": [ 0 ] }, + * { "orderData": [ 1, 0 ], "targets": [ 1 ] }, + * { "orderData": 2, "targets": [ 2 ] } + * ] + * } ); + * } ); + * + * @example + * // Using `columns` + * $(document).ready( function() { + * $('#example').dataTable( { + * "columns": [ + * { "orderData": [ 0, 1 ] }, + * { "orderData": [ 1, 0 ] }, + * { "orderData": 2 }, + * null, + * null + * ] + * } ); + * } ); + */ + "aDataSort": null, + "iDataSort": -1, + + + /** + * You can control the default ordering direction, and even alter the + * behaviour of the sort handler (i.e. only allow ascending ordering etc) + * using this parameter. + * @type array + * @default [ 'asc', 'desc' ] + * + * @name DataTable.defaults.column.orderSequence + * @dtopt Columns + * + * @example + * // Using `columnDefs` + * $(document).ready( function() { + * $('#example').dataTable( { + * "columnDefs": [ + * { "orderSequence": [ "asc" ], "targets": [ 1 ] }, + * { "orderSequence": [ "desc", "asc", "asc" ], "targets": [ 2 ] }, + * { "orderSequence": [ "desc" ], "targets": [ 3 ] } + * ] + * } ); + * } ); + * + * @example + * // Using `columns` + * $(document).ready( function() { + * $('#example').dataTable( { + * "columns": [ + * null, + * { "orderSequence": [ "asc" ] }, + * { "orderSequence": [ "desc", "asc", "asc" ] }, + * { "orderSequence": [ "desc" ] }, + * null + * ] + * } ); + * } ); + */ + "asSorting": [ 'asc', 'desc' ], + + + /** + * Enable or disable filtering on the data in this column. + * @type boolean + * @default true + * + * @name DataTable.defaults.column.searchable + * @dtopt Columns + * + * @example + * // Using `columnDefs` + * $(document).ready( function() { + * $('#example').dataTable( { + * "columnDefs": [ + * { "searchable": false, "targets": [ 0 ] } + * ] } ); + * } ); + * + * @example + * // Using `columns` + * $(document).ready( function() { + * $('#example').dataTable( { + * "columns": [ + * { "searchable": false }, + * null, + * null, + * null, + * null + * ] } ); + * } ); + */ + "bSearchable": true, + + + /** + * Enable or disable ordering on this column. + * @type boolean + * @default true + * + * @name DataTable.defaults.column.orderable + * @dtopt Columns + * + * @example + * // Using `columnDefs` + * $(document).ready( function() { + * $('#example').dataTable( { + * "columnDefs": [ + * { "orderable": false, "targets": [ 0 ] } + * ] } ); + * } ); + * + * @example + * // Using `columns` + * $(document).ready( function() { + * $('#example').dataTable( { + * "columns": [ + * { "orderable": false }, + * null, + * null, + * null, + * null + * ] } ); + * } ); + */ + "bSortable": true, + + + /** + * Enable or disable the display of this column. + * @type boolean + * @default true + * + * @name DataTable.defaults.column.visible + * @dtopt Columns + * + * @example + * // Using `columnDefs` + * $(document).ready( function() { + * $('#example').dataTable( { + * "columnDefs": [ + * { "visible": false, "targets": [ 0 ] } + * ] } ); + * } ); + * + * @example + * // Using `columns` + * $(document).ready( function() { + * $('#example').dataTable( { + * "columns": [ + * { "visible": false }, + * null, + * null, + * null, + * null + * ] } ); + * } ); + */ + "bVisible": true, + + + /** + * Developer definable function that is called whenever a cell is created (Ajax source, + * etc) or processed for input (DOM source). This can be used as a compliment to mRender + * allowing you to modify the DOM element (add background colour for example) when the + * element is available. + * @type function + * @param {element} td The TD node that has been created + * @param {*} cellData The Data for the cell + * @param {array|object} rowData The data for the whole row + * @param {int} row The row index for the aoData data store + * @param {int} col The column index for aoColumns + * + * @name DataTable.defaults.column.createdCell + * @dtopt Columns + * + * @example + * $(document).ready( function() { + * $('#example').dataTable( { + * "columnDefs": [ { + * "targets": [3], + * "createdCell": function (td, cellData, rowData, row, col) { + * if ( cellData == "1.7" ) { + * $(td).css('color', 'blue') + * } + * } + * } ] + * }); + * } ); + */ + "fnCreatedCell": null, + + + /** + * This parameter has been replaced by `data` in DataTables to ensure naming + * consistency. `dataProp` can still be used, as there is backwards + * compatibility in DataTables for this option, but it is strongly + * recommended that you use `data` in preference to `dataProp`. + * @name DataTable.defaults.column.dataProp + */ + + + /** + * This property can be used to read data from any data source property, + * including deeply nested objects / properties. `data` can be given in a + * number of different ways which effect its behaviour: + * + * * `integer` - treated as an array index for the data source. This is the + * default that DataTables uses (incrementally increased for each column). + * * `string` - read an object property from the data source. There are + * three 'special' options that can be used in the string to alter how + * DataTables reads the data from the source object: + * * `.` - Dotted Javascript notation. Just as you use a `.` in + * Javascript to read from nested objects, so to can the options + * specified in `data`. For example: `browser.version` or + * `browser.name`. If your object parameter name contains a period, use + * `\\` to escape it - i.e. `first\\.name`. + * * `[]` - Array notation. DataTables can automatically combine data + * from and array source, joining the data with the characters provided + * between the two brackets. For example: `name[, ]` would provide a + * comma-space separated list from the source array. If no characters + * are provided between the brackets, the original array source is + * returned. + * * `()` - Function notation. Adding `()` to the end of a parameter will + * execute a function of the name given. For example: `browser()` for a + * simple function on the data source, `browser.version()` for a + * function in a nested property or even `browser().version` to get an + * object property if the function called returns an object. Note that + * function notation is recommended for use in `render` rather than + * `data` as it is much simpler to use as a renderer. + * * `null` - use the original data source for the row rather than plucking + * data directly from it. This action has effects on two other + * initialisation options: + * * `defaultContent` - When null is given as the `data` option and + * `defaultContent` is specified for the column, the value defined by + * `defaultContent` will be used for the cell. + * * `render` - When null is used for the `data` option and the `render` + * option is specified for the column, the whole data source for the + * row is used for the renderer. + * * `function` - the function given will be executed whenever DataTables + * needs to set or get the data for a cell in the column. The function + * takes three parameters: + * * Parameters: + * * `{array|object}` The data source for the row + * * `{string}` The type call data requested - this will be 'set' when + * setting data or 'filter', 'display', 'type', 'sort' or undefined + * when gathering data. Note that when `undefined` is given for the + * type DataTables expects to get the raw data for the object back< + * * `{*}` Data to set when the second parameter is 'set'. + * * Return: + * * The return value from the function is not required when 'set' is + * the type of call, but otherwise the return is what will be used + * for the data requested. + * + * Note that `data` is a getter and setter option. If you just require + * formatting of data for output, you will likely want to use `render` which + * is simply a getter and thus simpler to use. + * + * Note that prior to DataTables 1.9.2 `data` was called `mDataProp`. The + * name change reflects the flexibility of this property and is consistent + * with the naming of mRender. If 'mDataProp' is given, then it will still + * be used by DataTables, as it automatically maps the old name to the new + * if required. + * + * @type string|int|function|null + * @default null <i>Use automatically calculated column index</i> + * + * @name DataTable.defaults.column.data + * @dtopt Columns + * + * @example + * // Read table data from objects + * // JSON structure for each row: + * // { + * // "engine": {value}, + * // "browser": {value}, + * // "platform": {value}, + * // "version": {value}, + * // "grade": {value} + * // } + * $(document).ready( function() { + * $('#example').dataTable( { + * "ajaxSource": "sources/objects.txt", + * "columns": [ + * { "data": "engine" }, + * { "data": "browser" }, + * { "data": "platform" }, + * { "data": "version" }, + * { "data": "grade" } + * ] + * } ); + * } ); + * + * @example + * // Read information from deeply nested objects + * // JSON structure for each row: + * // { + * // "engine": {value}, + * // "browser": {value}, + * // "platform": { + * // "inner": {value} + * // }, + * // "details": [ + * // {value}, {value} + * // ] + * // } + * $(document).ready( function() { + * $('#example').dataTable( { + * "ajaxSource": "sources/deep.txt", + * "columns": [ + * { "data": "engine" }, + * { "data": "browser" }, + * { "data": "platform.inner" }, + * { "data": "platform.details.0" }, + * { "data": "platform.details.1" } + * ] + * } ); + * } ); + * + * @example + * // Using `data` as a function to provide different information for + * // sorting, filtering and display. In this case, currency (price) + * $(document).ready( function() { + * $('#example').dataTable( { + * "columnDefs": [ { + * "targets": [ 0 ], + * "data": function ( source, type, val ) { + * if (type === 'set') { + * source.price = val; + * // Store the computed dislay and filter values for efficiency + * source.price_display = val=="" ? "" : "$"+numberFormat(val); + * source.price_filter = val=="" ? "" : "$"+numberFormat(val)+" "+val; + * return; + * } + * else if (type === 'display') { + * return source.price_display; + * } + * else if (type === 'filter') { + * return source.price_filter; + * } + * // 'sort', 'type' and undefined all just use the integer + * return source.price; + * } + * } ] + * } ); + * } ); + * + * @example + * // Using default content + * $(document).ready( function() { + * $('#example').dataTable( { + * "columnDefs": [ { + * "targets": [ 0 ], + * "data": null, + * "defaultContent": "Click to edit" + * } ] + * } ); + * } ); + * + * @example + * // Using array notation - outputting a list from an array + * $(document).ready( function() { + * $('#example').dataTable( { + * "columnDefs": [ { + * "targets": [ 0 ], + * "data": "name[, ]" + * } ] + * } ); + * } ); + * + */ + "mData": null, + + + /** + * This property is the rendering partner to `data` and it is suggested that + * when you want to manipulate data for display (including filtering, + * sorting etc) without altering the underlying data for the table, use this + * property. `render` can be considered to be the the read only companion to + * `data` which is read / write (then as such more complex). Like `data` + * this option can be given in a number of different ways to effect its + * behaviour: + * + * * `integer` - treated as an array index for the data source. This is the + * default that DataTables uses (incrementally increased for each column). + * * `string` - read an object property from the data source. There are + * three 'special' options that can be used in the string to alter how + * DataTables reads the data from the source object: + * * `.` - Dotted Javascript notation. Just as you use a `.` in + * Javascript to read from nested objects, so to can the options + * specified in `data`. For example: `browser.version` or + * `browser.name`. If your object parameter name contains a period, use + * `\\` to escape it - i.e. `first\\.name`. + * * `[]` - Array notation. DataTables can automatically combine data + * from and array source, joining the data with the characters provided + * between the two brackets. For example: `name[, ]` would provide a + * comma-space separated list from the source array. If no characters + * are provided between the brackets, the original array source is + * returned. + * * `()` - Function notation. Adding `()` to the end of a parameter will + * execute a function of the name given. For example: `browser()` for a + * simple function on the data source, `browser.version()` for a + * function in a nested property or even `browser().version` to get an + * object property if the function called returns an object. + * * `object` - use different data for the different data types requested by + * DataTables ('filter', 'display', 'type' or 'sort'). The property names + * of the object is the data type the property refers to and the value can + * defined using an integer, string or function using the same rules as + * `render` normally does. Note that an `_` option _must_ be specified. + * This is the default value to use if you haven't specified a value for + * the data type requested by DataTables. + * * `function` - the function given will be executed whenever DataTables + * needs to set or get the data for a cell in the column. The function + * takes three parameters: + * * Parameters: + * * {array|object} The data source for the row (based on `data`) + * * {string} The type call data requested - this will be 'filter', + * 'display', 'type' or 'sort'. + * * {array|object} The full data source for the row (not based on + * `data`) + * * Return: + * * The return value from the function is what will be used for the + * data requested. + * + * @type string|int|function|object|null + * @default null Use the data source value. + * + * @name DataTable.defaults.column.render + * @dtopt Columns + * + * @example + * // Create a comma separated list from an array of objects + * $(document).ready( function() { + * $('#example').dataTable( { + * "ajaxSource": "sources/deep.txt", + * "columns": [ + * { "data": "engine" }, + * { "data": "browser" }, + * { + * "data": "platform", + * "render": "[, ].name" + * } + * ] + * } ); + * } ); + * + * @example + * // Execute a function to obtain data + * $(document).ready( function() { + * $('#example').dataTable( { + * "columnDefs": [ { + * "targets": [ 0 ], + * "data": null, // Use the full data source object for the renderer's source + * "render": "browserName()" + * } ] + * } ); + * } ); + * + * @example + * // As an object, extracting different data for the different types + * // This would be used with a data source such as: + * // { "phone": 5552368, "phone_filter": "5552368 555-2368", "phone_display": "555-2368" } + * // Here the `phone` integer is used for sorting and type detection, while `phone_filter` + * // (which has both forms) is used for filtering for if a user inputs either format, while + * // the formatted phone number is the one that is shown in the table. + * $(document).ready( function() { + * $('#example').dataTable( { + * "columnDefs": [ { + * "targets": [ 0 ], + * "data": null, // Use the full data source object for the renderer's source + * "render": { + * "_": "phone", + * "filter": "phone_filter", + * "display": "phone_display" + * } + * } ] + * } ); + * } ); + * + * @example + * // Use as a function to create a link from the data source + * $(document).ready( function() { + * $('#example').dataTable( { + * "columnDefs": [ { + * "targets": [ 0 ], + * "data": "download_link", + * "render": function ( data, type, full ) { + * return '<a href="'+data+'">Download</a>'; + * } + * } ] + * } ); + * } ); + */ + "mRender": null, + + + /** + * Change the cell type created for the column - either TD cells or TH cells. This + * can be useful as TH cells have semantic meaning in the table body, allowing them + * to act as a header for a row (you may wish to add scope='row' to the TH elements). + * @type string + * @default td + * + * @name DataTable.defaults.column.cellType + * @dtopt Columns + * + * @example + * // Make the first column use TH cells + * $(document).ready( function() { + * $('#example').dataTable( { + * "columnDefs": [ { + * "targets": [ 0 ], + * "cellType": "th" + * } ] + * } ); + * } ); + */ + "sCellType": "td", + + + /** + * Class to give to each cell in this column. + * @type string + * @default <i>Empty string</i> + * + * @name DataTable.defaults.column.class + * @dtopt Columns + * + * @example + * // Using `columnDefs` + * $(document).ready( function() { + * $('#example').dataTable( { + * "columnDefs": [ + * { "class": "my_class", "targets": [ 0 ] } + * ] + * } ); + * } ); + * + * @example + * // Using `columns` + * $(document).ready( function() { + * $('#example').dataTable( { + * "columns": [ + * { "class": "my_class" }, + * null, + * null, + * null, + * null + * ] + * } ); + * } ); + */ + "sClass": "", + + /** + * When DataTables calculates the column widths to assign to each column, + * it finds the longest string in each column and then constructs a + * temporary table and reads the widths from that. The problem with this + * is that "mmm" is much wider then "iiii", but the latter is a longer + * string - thus the calculation can go wrong (doing it properly and putting + * it into an DOM object and measuring that is horribly(!) slow). Thus as + * a "work around" we provide this option. It will append its value to the + * text that is found to be the longest string for the column - i.e. padding. + * Generally you shouldn't need this! + * @type string + * @default <i>Empty string<i> + * + * @name DataTable.defaults.column.contentPadding + * @dtopt Columns + * + * @example + * // Using `columns` + * $(document).ready( function() { + * $('#example').dataTable( { + * "columns": [ + * null, + * null, + * null, + * { + * "contentPadding": "mmm" + * } + * ] + * } ); + * } ); + */ + "sContentPadding": "", + + + /** + * Allows a default value to be given for a column's data, and will be used + * whenever a null data source is encountered (this can be because `data` + * is set to null, or because the data source itself is null). + * @type string + * @default null + * + * @name DataTable.defaults.column.defaultContent + * @dtopt Columns + * + * @example + * // Using `columnDefs` + * $(document).ready( function() { + * $('#example').dataTable( { + * "columnDefs": [ + * { + * "data": null, + * "defaultContent": "Edit", + * "targets": [ -1 ] + * } + * ] + * } ); + * } ); + * + * @example + * // Using `columns` + * $(document).ready( function() { + * $('#example').dataTable( { + * "columns": [ + * null, + * null, + * null, + * { + * "data": null, + * "defaultContent": "Edit" + * } + * ] + * } ); + * } ); + */ + "sDefaultContent": null, + + + /** + * This parameter is only used in DataTables' server-side processing. It can + * be exceptionally useful to know what columns are being displayed on the + * client side, and to map these to database fields. When defined, the names + * also allow DataTables to reorder information from the server if it comes + * back in an unexpected order (i.e. if you switch your columns around on the + * client-side, your server-side code does not also need updating). + * @type string + * @default <i>Empty string</i> + * + * @name DataTable.defaults.column.name + * @dtopt Columns + * + * @example + * // Using `columnDefs` + * $(document).ready( function() { + * $('#example').dataTable( { + * "columnDefs": [ + * { "name": "engine", "targets": [ 0 ] }, + * { "name": "browser", "targets": [ 1 ] }, + * { "name": "platform", "targets": [ 2 ] }, + * { "name": "version", "targets": [ 3 ] }, + * { "name": "grade", "targets": [ 4 ] } + * ] + * } ); + * } ); + * + * @example + * // Using `columns` + * $(document).ready( function() { + * $('#example').dataTable( { + * "columns": [ + * { "name": "engine" }, + * { "name": "browser" }, + * { "name": "platform" }, + * { "name": "version" }, + * { "name": "grade" } + * ] + * } ); + * } ); + */ + "sName": "", + + + /** + * Defines a data source type for the ordering which can be used to read + * real-time information from the table (updating the internally cached + * version) prior to ordering. This allows ordering to occur on user + * editable elements such as form inputs. + * @type string + * @default std + * + * @name DataTable.defaults.column.orderDataType + * @dtopt Columns + * + * @example + * // Using `columnDefs` + * $(document).ready( function() { + * $('#example').dataTable( { + * "columnDefs": [ + * { "orderDataType": "dom-text", "targets": [ 2, 3 ] }, + * { "type": "numeric", "targets": [ 3 ] }, + * { "orderDataType": "dom-select", "targets": [ 4 ] }, + * { "orderDataType": "dom-checkbox", "targets": [ 5 ] } + * ] + * } ); + * } ); + * + * @example + * // Using `columns` + * $(document).ready( function() { + * $('#example').dataTable( { + * "columns": [ + * null, + * null, + * { "orderDataType": "dom-text" }, + * { "orderDataType": "dom-text", "type": "numeric" }, + * { "orderDataType": "dom-select" }, + * { "orderDataType": "dom-checkbox" } + * ] + * } ); + * } ); + */ + "sSortDataType": "std", + + + /** + * The title of this column. + * @type string + * @default null <i>Derived from the 'TH' value for this column in the + * original HTML table.</i> + * + * @name DataTable.defaults.column.title + * @dtopt Columns + * + * @example + * // Using `columnDefs` + * $(document).ready( function() { + * $('#example').dataTable( { + * "columnDefs": [ + * { "title": "My column title", "targets": [ 0 ] } + * ] + * } ); + * } ); + * + * @example + * // Using `columns` + * $(document).ready( function() { + * $('#example').dataTable( { + * "columns": [ + * { "title": "My column title" }, + * null, + * null, + * null, + * null + * ] + * } ); + * } ); + */ + "sTitle": null, + + + /** + * The type allows you to specify how the data for this column will be + * ordered. Four types (string, numeric, date and html (which will strip + * HTML tags before ordering)) are currently available. Note that only date + * formats understood by Javascript's Date() object will be accepted as type + * date. For example: "Mar 26, 2008 5:03 PM". May take the values: 'string', + * 'numeric', 'date' or 'html' (by default). Further types can be adding + * through plug-ins. + * @type string + * @default null <i>Auto-detected from raw data</i> + * + * @name DataTable.defaults.column.type + * @dtopt Columns + * + * @example + * // Using `columnDefs` + * $(document).ready( function() { + * $('#example').dataTable( { + * "columnDefs": [ + * { "type": "html", "targets": [ 0 ] } + * ] + * } ); + * } ); + * + * @example + * // Using `columns` + * $(document).ready( function() { + * $('#example').dataTable( { + * "columns": [ + * { "type": "html" }, + * null, + * null, + * null, + * null + * ] + * } ); + * } ); + */ + "sType": null, + + + /** + * Defining the width of the column, this parameter may take any CSS value + * (3em, 20px etc). DataTables applies 'smart' widths to columns which have not + * been given a specific width through this interface ensuring that the table + * remains readable. + * @type string + * @default null <i>Automatic</i> + * + * @name DataTable.defaults.column.width + * @dtopt Columns + * + * @example + * // Using `columnDefs` + * $(document).ready( function() { + * $('#example').dataTable( { + * "columnDefs": [ + * { "width": "20%", "targets": [ 0 ] } + * ] + * } ); + * } ); + * + * @example + * // Using `columns` + * $(document).ready( function() { + * $('#example').dataTable( { + * "columns": [ + * { "width": "20%" }, + * null, + * null, + * null, + * null + * ] + * } ); + * } ); + */ + "sWidth": null + }; + + _fnHungarianMap( DataTable.defaults.column ); + + + + /** + * DataTables settings object - this holds all the information needed for a + * given table, including configuration, data and current application of the + * table options. DataTables does not have a single instance for each DataTable + * with the settings attached to that instance, but rather instances of the + * DataTable "class" are created on-the-fly as needed (typically by a + * $().dataTable() call) and the settings object is then applied to that + * instance. + * + * Note that this object is related to {@link DataTable.defaults} but this + * one is the internal data store for DataTables's cache of columns. It should + * NOT be manipulated outside of DataTables. Any configuration should be done + * through the initialisation options. + * @namespace + * @todo Really should attach the settings object to individual instances so we + * don't need to create new instances on each $().dataTable() call (if the + * table already exists). It would also save passing oSettings around and + * into every single function. However, this is a very significant + * architecture change for DataTables and will almost certainly break + * backwards compatibility with older installations. This is something that + * will be done in 2.0. + */ + DataTable.models.oSettings = { + /** + * Primary features of DataTables and their enablement state. + * @namespace + */ + "oFeatures": { + + /** + * Flag to say if DataTables should automatically try to calculate the + * optimum table and columns widths (true) or not (false). + * Note that this parameter will be set by the initialisation routine. To + * set a default use {@link DataTable.defaults}. + * @type boolean + */ + "bAutoWidth": null, + + /** + * Delay the creation of TR and TD elements until they are actually + * needed by a driven page draw. This can give a significant speed + * increase for Ajax source and Javascript source data, but makes no + * difference at all fro DOM and server-side processing tables. + * Note that this parameter will be set by the initialisation routine. To + * set a default use {@link DataTable.defaults}. + * @type boolean + */ + "bDeferRender": null, + + /** + * Enable filtering on the table or not. Note that if this is disabled + * then there is no filtering at all on the table, including fnFilter. + * To just remove the filtering input use sDom and remove the 'f' option. + * Note that this parameter will be set by the initialisation routine. To + * set a default use {@link DataTable.defaults}. + * @type boolean + */ + "bFilter": null, + + /** + * Table information element (the 'Showing x of y records' div) enable + * flag. + * Note that this parameter will be set by the initialisation routine. To + * set a default use {@link DataTable.defaults}. + * @type boolean + */ + "bInfo": null, + + /** + * Present a user control allowing the end user to change the page size + * when pagination is enabled. + * Note that this parameter will be set by the initialisation routine. To + * set a default use {@link DataTable.defaults}. + * @type boolean + */ + "bLengthChange": null, + + /** + * Pagination enabled or not. Note that if this is disabled then length + * changing must also be disabled. + * Note that this parameter will be set by the initialisation routine. To + * set a default use {@link DataTable.defaults}. + * @type boolean + */ + "bPaginate": null, + + /** + * Processing indicator enable flag whenever DataTables is enacting a + * user request - typically an Ajax request for server-side processing. + * Note that this parameter will be set by the initialisation routine. To + * set a default use {@link DataTable.defaults}. + * @type boolean + */ + "bProcessing": null, + + /** + * Server-side processing enabled flag - when enabled DataTables will + * get all data from the server for every draw - there is no filtering, + * sorting or paging done on the client-side. + * Note that this parameter will be set by the initialisation routine. To + * set a default use {@link DataTable.defaults}. + * @type boolean + */ + "bServerSide": null, + + /** + * Sorting enablement flag. + * Note that this parameter will be set by the initialisation routine. To + * set a default use {@link DataTable.defaults}. + * @type boolean + */ + "bSort": null, + + /** + * Multi-column sorting + * Note that this parameter will be set by the initialisation routine. To + * set a default use {@link DataTable.defaults}. + * @type boolean + */ + "bSortMulti": null, + + /** + * Apply a class to the columns which are being sorted to provide a + * visual highlight or not. This can slow things down when enabled since + * there is a lot of DOM interaction. + * Note that this parameter will be set by the initialisation routine. To + * set a default use {@link DataTable.defaults}. + * @type boolean + */ + "bSortClasses": null, + + /** + * State saving enablement flag. + * Note that this parameter will be set by the initialisation routine. To + * set a default use {@link DataTable.defaults}. + * @type boolean + */ + "bStateSave": null + }, + + + /** + * Scrolling settings for a table. + * @namespace + */ + "oScroll": { + /** + * When the table is shorter in height than sScrollY, collapse the + * table container down to the height of the table (when true). + * Note that this parameter will be set by the initialisation routine. To + * set a default use {@link DataTable.defaults}. + * @type boolean + */ + "bCollapse": null, + + /** + * Width of the scrollbar for the web-browser's platform. Calculated + * during table initialisation. + * @type int + * @default 0 + */ + "iBarWidth": 0, + + /** + * Viewport width for horizontal scrolling. Horizontal scrolling is + * disabled if an empty string. + * Note that this parameter will be set by the initialisation routine. To + * set a default use {@link DataTable.defaults}. + * @type string + */ + "sX": null, + + /** + * Width to expand the table to when using x-scrolling. Typically you + * should not need to use this. + * Note that this parameter will be set by the initialisation routine. To + * set a default use {@link DataTable.defaults}. + * @type string + * @deprecated + */ + "sXInner": null, + + /** + * Viewport height for vertical scrolling. Vertical scrolling is disabled + * if an empty string. + * Note that this parameter will be set by the initialisation routine. To + * set a default use {@link DataTable.defaults}. + * @type string + */ + "sY": null + }, + + /** + * Language information for the table. + * @namespace + * @extends DataTable.defaults.oLanguage + */ + "oLanguage": { + /** + * Information callback function. See + * {@link DataTable.defaults.fnInfoCallback} + * @type function + * @default null + */ + "fnInfoCallback": null + }, + + /** + * Browser support parameters + * @namespace + */ + "oBrowser": { + /** + * Indicate if the browser incorrectly calculates width:100% inside a + * scrolling element (IE6/7) + * @type boolean + * @default false + */ + "bScrollOversize": false, + + /** + * Determine if the vertical scrollbar is on the right or left of the + * scrolling container - needed for rtl language layout, although not + * all browsers move the scrollbar (Safari). + * @type boolean + * @default false + */ + "bScrollbarLeft": false, + + /** + * Flag for if `getBoundingClientRect` is fully supported or not + * @type boolean + * @default false + */ + "bBounding": false, + + /** + * Browser scrollbar width + * @type integer + * @default 0 + */ + "barWidth": 0 + }, + + + "ajax": null, + + + /** + * Array referencing the nodes which are used for the features. The + * parameters of this object match what is allowed by sDom - i.e. + * <ul> + * <li>'l' - Length changing</li> + * <li>'f' - Filtering input</li> + * <li>'t' - The table!</li> + * <li>'i' - Information</li> + * <li>'p' - Pagination</li> + * <li>'r' - pRocessing</li> + * </ul> + * @type array + * @default [] + */ + "aanFeatures": [], + + /** + * Store data information - see {@link DataTable.models.oRow} for detailed + * information. + * @type array + * @default [] + */ + "aoData": [], + + /** + * Array of indexes which are in the current display (after filtering etc) + * @type array + * @default [] + */ + "aiDisplay": [], + + /** + * Array of indexes for display - no filtering + * @type array + * @default [] + */ + "aiDisplayMaster": [], + + /** + * Map of row ids to data indexes + * @type object + * @default {} + */ + "aIds": {}, + + /** + * Store information about each column that is in use + * @type array + * @default [] + */ + "aoColumns": [], + + /** + * Store information about the table's header + * @type array + * @default [] + */ + "aoHeader": [], + + /** + * Store information about the table's footer + * @type array + * @default [] + */ + "aoFooter": [], + + /** + * Store the applied global search information in case we want to force a + * research or compare the old search to a new one. + * Note that this parameter will be set by the initialisation routine. To + * set a default use {@link DataTable.defaults}. + * @namespace + * @extends DataTable.models.oSearch + */ + "oPreviousSearch": {}, + + /** + * Store the applied search for each column - see + * {@link DataTable.models.oSearch} for the format that is used for the + * filtering information for each column. + * @type array + * @default [] + */ + "aoPreSearchCols": [], + + /** + * Sorting that is applied to the table. Note that the inner arrays are + * used in the following manner: + * <ul> + * <li>Index 0 - column number</li> + * <li>Index 1 - current sorting direction</li> + * </ul> + * Note that this parameter will be set by the initialisation routine. To + * set a default use {@link DataTable.defaults}. + * @type array + * @todo These inner arrays should really be objects + */ + "aaSorting": null, + + /** + * Sorting that is always applied to the table (i.e. prefixed in front of + * aaSorting). + * Note that this parameter will be set by the initialisation routine. To + * set a default use {@link DataTable.defaults}. + * @type array + * @default [] + */ + "aaSortingFixed": [], + + /** + * Classes to use for the striping of a table. + * Note that this parameter will be set by the initialisation routine. To + * set a default use {@link DataTable.defaults}. + * @type array + * @default [] + */ + "asStripeClasses": null, + + /** + * If restoring a table - we should restore its striping classes as well + * @type array + * @default [] + */ + "asDestroyStripes": [], + + /** + * If restoring a table - we should restore its width + * @type int + * @default 0 + */ + "sDestroyWidth": 0, + + /** + * Callback functions array for every time a row is inserted (i.e. on a draw). + * @type array + * @default [] + */ + "aoRowCallback": [], + + /** + * Callback functions for the header on each draw. + * @type array + * @default [] + */ + "aoHeaderCallback": [], + + /** + * Callback function for the footer on each draw. + * @type array + * @default [] + */ + "aoFooterCallback": [], + + /** + * Array of callback functions for draw callback functions + * @type array + * @default [] + */ + "aoDrawCallback": [], + + /** + * Array of callback functions for row created function + * @type array + * @default [] + */ + "aoRowCreatedCallback": [], + + /** + * Callback functions for just before the table is redrawn. A return of + * false will be used to cancel the draw. + * @type array + * @default [] + */ + "aoPreDrawCallback": [], + + /** + * Callback functions for when the table has been initialised. + * @type array + * @default [] + */ + "aoInitComplete": [], + + + /** + * Callbacks for modifying the settings to be stored for state saving, prior to + * saving state. + * @type array + * @default [] + */ + "aoStateSaveParams": [], + + /** + * Callbacks for modifying the settings that have been stored for state saving + * prior to using the stored values to restore the state. + * @type array + * @default [] + */ + "aoStateLoadParams": [], + + /** + * Callbacks for operating on the settings object once the saved state has been + * loaded + * @type array + * @default [] + */ + "aoStateLoaded": [], + + /** + * Cache the table ID for quick access + * @type string + * @default <i>Empty string</i> + */ + "sTableId": "", + + /** + * The TABLE node for the main table + * @type node + * @default null + */ + "nTable": null, + + /** + * Permanent ref to the thead element + * @type node + * @default null + */ + "nTHead": null, + + /** + * Permanent ref to the tfoot element - if it exists + * @type node + * @default null + */ + "nTFoot": null, + + /** + * Permanent ref to the tbody element + * @type node + * @default null + */ + "nTBody": null, + + /** + * Cache the wrapper node (contains all DataTables controlled elements) + * @type node + * @default null + */ + "nTableWrapper": null, + + /** + * Indicate if when using server-side processing the loading of data + * should be deferred until the second draw. + * Note that this parameter will be set by the initialisation routine. To + * set a default use {@link DataTable.defaults}. + * @type boolean + * @default false + */ + "bDeferLoading": false, + + /** + * Indicate if all required information has been read in + * @type boolean + * @default false + */ + "bInitialised": false, + + /** + * Information about open rows. Each object in the array has the parameters + * 'nTr' and 'nParent' + * @type array + * @default [] + */ + "aoOpenRows": [], + + /** + * Dictate the positioning of DataTables' control elements - see + * {@link DataTable.model.oInit.sDom}. + * Note that this parameter will be set by the initialisation routine. To + * set a default use {@link DataTable.defaults}. + * @type string + * @default null + */ + "sDom": null, + + /** + * Search delay (in mS) + * @type integer + * @default null + */ + "searchDelay": null, + + /** + * Which type of pagination should be used. + * Note that this parameter will be set by the initialisation routine. To + * set a default use {@link DataTable.defaults}. + * @type string + * @default two_button + */ + "sPaginationType": "two_button", + + /** + * The state duration (for `stateSave`) in seconds. + * Note that this parameter will be set by the initialisation routine. To + * set a default use {@link DataTable.defaults}. + * @type int + * @default 0 + */ + "iStateDuration": 0, + + /** + * Array of callback functions for state saving. Each array element is an + * object with the following parameters: + * <ul> + * <li>function:fn - function to call. Takes two parameters, oSettings + * and the JSON string to save that has been thus far created. Returns + * a JSON string to be inserted into a json object + * (i.e. '"param": [ 0, 1, 2]')</li> + * <li>string:sName - name of callback</li> + * </ul> + * @type array + * @default [] + */ + "aoStateSave": [], + + /** + * Array of callback functions for state loading. Each array element is an + * object with the following parameters: + * <ul> + * <li>function:fn - function to call. Takes two parameters, oSettings + * and the object stored. May return false to cancel state loading</li> + * <li>string:sName - name of callback</li> + * </ul> + * @type array + * @default [] + */ + "aoStateLoad": [], + + /** + * State that was saved. Useful for back reference + * @type object + * @default null + */ + "oSavedState": null, + + /** + * State that was loaded. Useful for back reference + * @type object + * @default null + */ + "oLoadedState": null, + + /** + * Source url for AJAX data for the table. + * Note that this parameter will be set by the initialisation routine. To + * set a default use {@link DataTable.defaults}. + * @type string + * @default null + */ + "sAjaxSource": null, + + /** + * Property from a given object from which to read the table data from. This + * can be an empty string (when not server-side processing), in which case + * it is assumed an an array is given directly. + * Note that this parameter will be set by the initialisation routine. To + * set a default use {@link DataTable.defaults}. + * @type string + */ + "sAjaxDataProp": null, + + /** + * Note if draw should be blocked while getting data + * @type boolean + * @default true + */ + "bAjaxDataGet": true, + + /** + * The last jQuery XHR object that was used for server-side data gathering. + * This can be used for working with the XHR information in one of the + * callbacks + * @type object + * @default null + */ + "jqXHR": null, + + /** + * JSON returned from the server in the last Ajax request + * @type object + * @default undefined + */ + "json": undefined, + + /** + * Data submitted as part of the last Ajax request + * @type object + * @default undefined + */ + "oAjaxData": undefined, + + /** + * Function to get the server-side data. + * Note that this parameter will be set by the initialisation routine. To + * set a default use {@link DataTable.defaults}. + * @type function + */ + "fnServerData": null, + + /** + * Functions which are called prior to sending an Ajax request so extra + * parameters can easily be sent to the server + * @type array + * @default [] + */ + "aoServerParams": [], + + /** + * Send the XHR HTTP method - GET or POST (could be PUT or DELETE if + * required). + * Note that this parameter will be set by the initialisation routine. To + * set a default use {@link DataTable.defaults}. + * @type string + */ + "sServerMethod": null, + + /** + * Format numbers for display. + * Note that this parameter will be set by the initialisation routine. To + * set a default use {@link DataTable.defaults}. + * @type function + */ + "fnFormatNumber": null, + + /** + * List of options that can be used for the user selectable length menu. + * Note that this parameter will be set by the initialisation routine. To + * set a default use {@link DataTable.defaults}. + * @type array + * @default [] + */ + "aLengthMenu": null, + + /** + * Counter for the draws that the table does. Also used as a tracker for + * server-side processing + * @type int + * @default 0 + */ + "iDraw": 0, + + /** + * Indicate if a redraw is being done - useful for Ajax + * @type boolean + * @default false + */ + "bDrawing": false, + + /** + * Draw index (iDraw) of the last error when parsing the returned data + * @type int + * @default -1 + */ + "iDrawError": -1, + + /** + * Paging display length + * @type int + * @default 10 + */ + "_iDisplayLength": 10, + + /** + * Paging start point - aiDisplay index + * @type int + * @default 0 + */ + "_iDisplayStart": 0, + + /** + * Server-side processing - number of records in the result set + * (i.e. before filtering), Use fnRecordsTotal rather than + * this property to get the value of the number of records, regardless of + * the server-side processing setting. + * @type int + * @default 0 + * @private + */ + "_iRecordsTotal": 0, + + /** + * Server-side processing - number of records in the current display set + * (i.e. after filtering). Use fnRecordsDisplay rather than + * this property to get the value of the number of records, regardless of + * the server-side processing setting. + * @type boolean + * @default 0 + * @private + */ + "_iRecordsDisplay": 0, + + /** + * Flag to indicate if jQuery UI marking and classes should be used. + * Note that this parameter will be set by the initialisation routine. To + * set a default use {@link DataTable.defaults}. + * @type boolean + */ + "bJUI": null, + + /** + * The classes to use for the table + * @type object + * @default {} + */ + "oClasses": {}, + + /** + * Flag attached to the settings object so you can check in the draw + * callback if filtering has been done in the draw. Deprecated in favour of + * events. + * @type boolean + * @default false + * @deprecated + */ + "bFiltered": false, + + /** + * Flag attached to the settings object so you can check in the draw + * callback if sorting has been done in the draw. Deprecated in favour of + * events. + * @type boolean + * @default false + * @deprecated + */ + "bSorted": false, + + /** + * Indicate that if multiple rows are in the header and there is more than + * one unique cell per column, if the top one (true) or bottom one (false) + * should be used for sorting / title by DataTables. + * Note that this parameter will be set by the initialisation routine. To + * set a default use {@link DataTable.defaults}. + * @type boolean + */ + "bSortCellsTop": null, + + /** + * Initialisation object that is used for the table + * @type object + * @default null + */ + "oInit": null, + + /** + * Destroy callback functions - for plug-ins to attach themselves to the + * destroy so they can clean up markup and events. + * @type array + * @default [] + */ + "aoDestroyCallback": [], + + + /** + * Get the number of records in the current record set, before filtering + * @type function + */ + "fnRecordsTotal": function () + { + return _fnDataSource( this ) == 'ssp' ? + this._iRecordsTotal * 1 : + this.aiDisplayMaster.length; + }, + + /** + * Get the number of records in the current record set, after filtering + * @type function + */ + "fnRecordsDisplay": function () + { + return _fnDataSource( this ) == 'ssp' ? + this._iRecordsDisplay * 1 : + this.aiDisplay.length; + }, + + /** + * Get the display end point - aiDisplay index + * @type function + */ + "fnDisplayEnd": function () + { + var + len = this._iDisplayLength, + start = this._iDisplayStart, + calc = start + len, + records = this.aiDisplay.length, + features = this.oFeatures, + paginate = features.bPaginate; + + if ( features.bServerSide ) { + return paginate === false || len === -1 ? + start + records : + Math.min( start+len, this._iRecordsDisplay ); + } + else { + return ! paginate || calc>records || len===-1 ? + records : + calc; + } + }, + + /** + * The DataTables object for this table + * @type object + * @default null + */ + "oInstance": null, + + /** + * Unique identifier for each instance of the DataTables object. If there + * is an ID on the table node, then it takes that value, otherwise an + * incrementing internal counter is used. + * @type string + * @default null + */ + "sInstance": null, + + /** + * tabindex attribute value that is added to DataTables control elements, allowing + * keyboard navigation of the table and its controls. + */ + "iTabIndex": 0, + + /** + * DIV container for the footer scrolling table if scrolling + */ + "nScrollHead": null, + + /** + * DIV container for the footer scrolling table if scrolling + */ + "nScrollFoot": null, + + /** + * Last applied sort + * @type array + * @default [] + */ + "aLastSort": [], + + /** + * Stored plug-in instances + * @type object + * @default {} + */ + "oPlugins": {}, + + /** + * Function used to get a row's id from the row's data + * @type function + * @default null + */ + "rowIdFn": null, + + /** + * Data location where to store a row's id + * @type string + * @default null + */ + "rowId": null + }; + + /** + * Extension object for DataTables that is used to provide all extension + * options. + * + * Note that the `DataTable.ext` object is available through + * `jQuery.fn.dataTable.ext` where it may be accessed and manipulated. It is + * also aliased to `jQuery.fn.dataTableExt` for historic reasons. + * @namespace + * @extends DataTable.models.ext + */ + + + /** + * DataTables extensions + * + * This namespace acts as a collection area for plug-ins that can be used to + * extend DataTables capabilities. Indeed many of the build in methods + * use this method to provide their own capabilities (sorting methods for + * example). + * + * Note that this namespace is aliased to `jQuery.fn.dataTableExt` for legacy + * reasons + * + * @namespace + */ + DataTable.ext = _ext = { + /** + * Buttons. For use with the Buttons extension for DataTables. This is + * defined here so other extensions can define buttons regardless of load + * order. It is _not_ used by DataTables core. + * + * @type object + * @default {} + */ + buttons: {}, + + + /** + * Element class names + * + * @type object + * @default {} + */ + classes: {}, + + + /** + * DataTables build type (expanded by the download builder) + * + * @type string + */ + builder: "-source-", + + + /** + * Error reporting. + * + * How should DataTables report an error. Can take the value 'alert', + * 'throw', 'none' or a function. + * + * @type string|function + * @default alert + */ + errMode: "alert", + + + /** + * Feature plug-ins. + * + * This is an array of objects which describe the feature plug-ins that are + * available to DataTables. These feature plug-ins are then available for + * use through the `dom` initialisation option. + * + * Each feature plug-in is described by an object which must have the + * following properties: + * + * * `fnInit` - function that is used to initialise the plug-in, + * * `cFeature` - a character so the feature can be enabled by the `dom` + * instillation option. This is case sensitive. + * + * The `fnInit` function has the following input parameters: + * + * 1. `{object}` DataTables settings object: see + * {@link DataTable.models.oSettings} + * + * And the following return is expected: + * + * * {node|null} The element which contains your feature. Note that the + * return may also be void if your plug-in does not require to inject any + * DOM elements into DataTables control (`dom`) - for example this might + * be useful when developing a plug-in which allows table control via + * keyboard entry + * + * @type array + * + * @example + * $.fn.dataTable.ext.features.push( { + * "fnInit": function( oSettings ) { + * return new TableTools( { "oDTSettings": oSettings } ); + * }, + * "cFeature": "T" + * } ); + */ + feature: [], + + + /** + * Row searching. + * + * This method of searching is complimentary to the default type based + * searching, and a lot more comprehensive as it allows you complete control + * over the searching logic. Each element in this array is a function + * (parameters described below) that is called for every row in the table, + * and your logic decides if it should be included in the searching data set + * or not. + * + * Searching functions have the following input parameters: + * + * 1. `{object}` DataTables settings object: see + * {@link DataTable.models.oSettings} + * 2. `{array|object}` Data for the row to be processed (same as the + * original format that was passed in as the data source, or an array + * from a DOM data source + * 3. `{int}` Row index ({@link DataTable.models.oSettings.aoData}), which + * can be useful to retrieve the `TR` element if you need DOM interaction. + * + * And the following return is expected: + * + * * {boolean} Include the row in the searched result set (true) or not + * (false) + * + * Note that as with the main search ability in DataTables, technically this + * is "filtering", since it is subtractive. However, for consistency in + * naming we call it searching here. + * + * @type array + * @default [] + * + * @example + * // The following example shows custom search being applied to the + * // fourth column (i.e. the data[3] index) based on two input values + * // from the end-user, matching the data in a certain range. + * $.fn.dataTable.ext.search.push( + * function( settings, data, dataIndex ) { + * var min = document.getElementById('min').value * 1; + * var max = document.getElementById('max').value * 1; + * var version = data[3] == "-" ? 0 : data[3]*1; + * + * if ( min == "" && max == "" ) { + * return true; + * } + * else if ( min == "" && version < max ) { + * return true; + * } + * else if ( min < version && "" == max ) { + * return true; + * } + * else if ( min < version && version < max ) { + * return true; + * } + * return false; + * } + * ); + */ + search: [], + + + /** + * Selector extensions + * + * The `selector` option can be used to extend the options available for the + * selector modifier options (`selector-modifier` object data type) that + * each of the three built in selector types offer (row, column and cell + + * their plural counterparts). For example the Select extension uses this + * mechanism to provide an option to select only rows, columns and cells + * that have been marked as selected by the end user (`{selected: true}`), + * which can be used in conjunction with the existing built in selector + * options. + * + * Each property is an array to which functions can be pushed. The functions + * take three attributes: + * + * * Settings object for the host table + * * Options object (`selector-modifier` object type) + * * Array of selected item indexes + * + * The return is an array of the resulting item indexes after the custom + * selector has been applied. + * + * @type object + */ + selector: { + cell: [], + column: [], + row: [] + }, + + + /** + * Internal functions, exposed for used in plug-ins. + * + * Please note that you should not need to use the internal methods for + * anything other than a plug-in (and even then, try to avoid if possible). + * The internal function may change between releases. + * + * @type object + * @default {} + */ + internal: {}, + + + /** + * Legacy configuration options. Enable and disable legacy options that + * are available in DataTables. + * + * @type object + */ + legacy: { + /** + * Enable / disable DataTables 1.9 compatible server-side processing + * requests + * + * @type boolean + * @default null + */ + ajax: null + }, + + + /** + * Pagination plug-in methods. + * + * Each entry in this object is a function and defines which buttons should + * be shown by the pagination rendering method that is used for the table: + * {@link DataTable.ext.renderer.pageButton}. The renderer addresses how the + * buttons are displayed in the document, while the functions here tell it + * what buttons to display. This is done by returning an array of button + * descriptions (what each button will do). + * + * Pagination types (the four built in options and any additional plug-in + * options defined here) can be used through the `paginationType` + * initialisation parameter. + * + * The functions defined take two parameters: + * + * 1. `{int} page` The current page index + * 2. `{int} pages` The number of pages in the table + * + * Each function is expected to return an array where each element of the + * array can be one of: + * + * * `first` - Jump to first page when activated + * * `last` - Jump to last page when activated + * * `previous` - Show previous page when activated + * * `next` - Show next page when activated + * * `{int}` - Show page of the index given + * * `{array}` - A nested array containing the above elements to add a + * containing 'DIV' element (might be useful for styling). + * + * Note that DataTables v1.9- used this object slightly differently whereby + * an object with two functions would be defined for each plug-in. That + * ability is still supported by DataTables 1.10+ to provide backwards + * compatibility, but this option of use is now decremented and no longer + * documented in DataTables 1.10+. + * + * @type object + * @default {} + * + * @example + * // Show previous, next and current page buttons only + * $.fn.dataTableExt.oPagination.current = function ( page, pages ) { + * return [ 'previous', page, 'next' ]; + * }; + */ + pager: {}, + + + renderer: { + pageButton: {}, + header: {} + }, + + + /** + * Ordering plug-ins - custom data source + * + * The extension options for ordering of data available here is complimentary + * to the default type based ordering that DataTables typically uses. It + * allows much greater control over the the data that is being used to + * order a column, but is necessarily therefore more complex. + * + * This type of ordering is useful if you want to do ordering based on data + * live from the DOM (for example the contents of an 'input' element) rather + * than just the static string that DataTables knows of. + * + * The way these plug-ins work is that you create an array of the values you + * wish to be ordering for the column in question and then return that + * array. The data in the array much be in the index order of the rows in + * the table (not the currently ordering order!). Which order data gathering + * function is run here depends on the `dt-init columns.orderDataType` + * parameter that is used for the column (if any). + * + * The functions defined take two parameters: + * + * 1. `{object}` DataTables settings object: see + * {@link DataTable.models.oSettings} + * 2. `{int}` Target column index + * + * Each function is expected to return an array: + * + * * `{array}` Data for the column to be ordering upon + * + * @type array + * + * @example + * // Ordering using `input` node values + * $.fn.dataTable.ext.order['dom-text'] = function ( settings, col ) + * { + * return this.api().column( col, {order:'index'} ).nodes().map( function ( td, i ) { + * return $('input', td).val(); + * } ); + * } + */ + order: {}, + + + /** + * Type based plug-ins. + * + * Each column in DataTables has a type assigned to it, either by automatic + * detection or by direct assignment using the `type` option for the column. + * The type of a column will effect how it is ordering and search (plug-ins + * can also make use of the column type if required). + * + * @namespace + */ + type: { + /** + * Type detection functions. + * + * The functions defined in this object are used to automatically detect + * a column's type, making initialisation of DataTables super easy, even + * when complex data is in the table. + * + * The functions defined take two parameters: + * + * 1. `{*}` Data from the column cell to be analysed + * 2. `{settings}` DataTables settings object. This can be used to + * perform context specific type detection - for example detection + * based on language settings such as using a comma for a decimal + * place. Generally speaking the options from the settings will not + * be required + * + * Each function is expected to return: + * + * * `{string|null}` Data type detected, or null if unknown (and thus + * pass it on to the other type detection functions. + * + * @type array + * + * @example + * // Currency type detection plug-in: + * $.fn.dataTable.ext.type.detect.push( + * function ( data, settings ) { + * // Check the numeric part + * if ( ! $.isNumeric( data.substring(1) ) ) { + * return null; + * } + * + * // Check prefixed by currency + * if ( data.charAt(0) == '$' || data.charAt(0) == '£' ) { + * return 'currency'; + * } + * return null; + * } + * ); + */ + detect: [], + + + /** + * Type based search formatting. + * + * The type based searching functions can be used to pre-format the + * data to be search on. For example, it can be used to strip HTML + * tags or to de-format telephone numbers for numeric only searching. + * + * Note that is a search is not defined for a column of a given type, + * no search formatting will be performed. + * + * Pre-processing of searching data plug-ins - When you assign the sType + * for a column (or have it automatically detected for you by DataTables + * or a type detection plug-in), you will typically be using this for + * custom sorting, but it can also be used to provide custom searching + * by allowing you to pre-processing the data and returning the data in + * the format that should be searched upon. This is done by adding + * functions this object with a parameter name which matches the sType + * for that target column. This is the corollary of <i>afnSortData</i> + * for searching data. + * + * The functions defined take a single parameter: + * + * 1. `{*}` Data from the column cell to be prepared for searching + * + * Each function is expected to return: + * + * * `{string|null}` Formatted string that will be used for the searching. + * + * @type object + * @default {} + * + * @example + * $.fn.dataTable.ext.type.search['title-numeric'] = function ( d ) { + * return d.replace(/\n/g," ").replace( /<.*?>/g, "" ); + * } + */ + search: {}, + + + /** + * Type based ordering. + * + * The column type tells DataTables what ordering to apply to the table + * when a column is sorted upon. The order for each type that is defined, + * is defined by the functions available in this object. + * + * Each ordering option can be described by three properties added to + * this object: + * + * * `{type}-pre` - Pre-formatting function + * * `{type}-asc` - Ascending order function + * * `{type}-desc` - Descending order function + * + * All three can be used together, only `{type}-pre` or only + * `{type}-asc` and `{type}-desc` together. It is generally recommended + * that only `{type}-pre` is used, as this provides the optimal + * implementation in terms of speed, although the others are provided + * for compatibility with existing Javascript sort functions. + * + * `{type}-pre`: Functions defined take a single parameter: + * + * 1. `{*}` Data from the column cell to be prepared for ordering + * + * And return: + * + * * `{*}` Data to be sorted upon + * + * `{type}-asc` and `{type}-desc`: Functions are typical Javascript sort + * functions, taking two parameters: + * + * 1. `{*}` Data to compare to the second parameter + * 2. `{*}` Data to compare to the first parameter + * + * And returning: + * + * * `{*}` Ordering match: <0 if first parameter should be sorted lower + * than the second parameter, ===0 if the two parameters are equal and + * >0 if the first parameter should be sorted height than the second + * parameter. + * + * @type object + * @default {} + * + * @example + * // Numeric ordering of formatted numbers with a pre-formatter + * $.extend( $.fn.dataTable.ext.type.order, { + * "string-pre": function(x) { + * a = (a === "-" || a === "") ? 0 : a.replace( /[^\d\-\.]/g, "" ); + * return parseFloat( a ); + * } + * } ); + * + * @example + * // Case-sensitive string ordering, with no pre-formatting method + * $.extend( $.fn.dataTable.ext.order, { + * "string-case-asc": function(x,y) { + * return ((x < y) ? -1 : ((x > y) ? 1 : 0)); + * }, + * "string-case-desc": function(x,y) { + * return ((x < y) ? 1 : ((x > y) ? -1 : 0)); + * } + * } ); + */ + order: {} + }, + + /** + * Unique DataTables instance counter + * + * @type int + * @private + */ + _unique: 0, + + + // + // Depreciated + // The following properties are retained for backwards compatiblity only. + // The should not be used in new projects and will be removed in a future + // version + // + + /** + * Version check function. + * @type function + * @depreciated Since 1.10 + */ + fnVersionCheck: DataTable.fnVersionCheck, + + + /** + * Index for what 'this' index API functions should use + * @type int + * @deprecated Since v1.10 + */ + iApiIndex: 0, + + + /** + * jQuery UI class container + * @type object + * @deprecated Since v1.10 + */ + oJUIClasses: {}, + + + /** + * Software version + * @type string + * @deprecated Since v1.10 + */ + sVersion: DataTable.version + }; + + + // + // Backwards compatibility. Alias to pre 1.10 Hungarian notation counter parts + // + $.extend( _ext, { + afnFiltering: _ext.search, + aTypes: _ext.type.detect, + ofnSearch: _ext.type.search, + oSort: _ext.type.order, + afnSortData: _ext.order, + aoFeatures: _ext.feature, + oApi: _ext.internal, + oStdClasses: _ext.classes, + oPagination: _ext.pager + } ); + + + $.extend( DataTable.ext.classes, { + "sTable": "dataTable", + "sNoFooter": "no-footer", + + /* Paging buttons */ + "sPageButton": "paginate_button", + "sPageButtonActive": "current", + "sPageButtonDisabled": "disabled", + + /* Striping classes */ + "sStripeOdd": "odd", + "sStripeEven": "even", + + /* Empty row */ + "sRowEmpty": "dataTables_empty", + + /* Features */ + "sWrapper": "dataTables_wrapper", + "sFilter": "dataTables_filter", + "sInfo": "dataTables_info", + "sPaging": "dataTables_paginate paging_", /* Note that the type is postfixed */ + "sLength": "dataTables_length", + "sProcessing": "dataTables_processing", + + /* Sorting */ + "sSortAsc": "sorting_asc", + "sSortDesc": "sorting_desc", + "sSortable": "sorting", /* Sortable in both directions */ + "sSortableAsc": "sorting_asc_disabled", + "sSortableDesc": "sorting_desc_disabled", + "sSortableNone": "sorting_disabled", + "sSortColumn": "sorting_", /* Note that an int is postfixed for the sorting order */ + + /* Filtering */ + "sFilterInput": "", + + /* Page length */ + "sLengthSelect": "", + + /* Scrolling */ + "sScrollWrapper": "dataTables_scroll", + "sScrollHead": "dataTables_scrollHead", + "sScrollHeadInner": "dataTables_scrollHeadInner", + "sScrollBody": "dataTables_scrollBody", + "sScrollFoot": "dataTables_scrollFoot", + "sScrollFootInner": "dataTables_scrollFootInner", + + /* Misc */ + "sHeaderTH": "", + "sFooterTH": "", + + // Deprecated + "sSortJUIAsc": "", + "sSortJUIDesc": "", + "sSortJUI": "", + "sSortJUIAscAllowed": "", + "sSortJUIDescAllowed": "", + "sSortJUIWrapper": "", + "sSortIcon": "", + "sJUIHeader": "", + "sJUIFooter": "" + } ); + + + (function() { + + // Reused strings for better compression. Closure compiler appears to have a + // weird edge case where it is trying to expand strings rather than use the + // variable version. This results in about 200 bytes being added, for very + // little preference benefit since it this run on script load only. + var _empty = ''; + _empty = ''; + + var _stateDefault = _empty + 'ui-state-default'; + var _sortIcon = _empty + 'css_right ui-icon ui-icon-'; + var _headerFooter = _empty + 'fg-toolbar ui-toolbar ui-widget-header ui-helper-clearfix'; + + $.extend( DataTable.ext.oJUIClasses, DataTable.ext.classes, { + /* Full numbers paging buttons */ + "sPageButton": "fg-button ui-button "+_stateDefault, + "sPageButtonActive": "ui-state-disabled", + "sPageButtonDisabled": "ui-state-disabled", + + /* Features */ + "sPaging": "dataTables_paginate fg-buttonset ui-buttonset fg-buttonset-multi "+ + "ui-buttonset-multi paging_", /* Note that the type is postfixed */ + + /* Sorting */ + "sSortAsc": _stateDefault+" sorting_asc", + "sSortDesc": _stateDefault+" sorting_desc", + "sSortable": _stateDefault+" sorting", + "sSortableAsc": _stateDefault+" sorting_asc_disabled", + "sSortableDesc": _stateDefault+" sorting_desc_disabled", + "sSortableNone": _stateDefault+" sorting_disabled", + "sSortJUIAsc": _sortIcon+"triangle-1-n", + "sSortJUIDesc": _sortIcon+"triangle-1-s", + "sSortJUI": _sortIcon+"carat-2-n-s", + "sSortJUIAscAllowed": _sortIcon+"carat-1-n", + "sSortJUIDescAllowed": _sortIcon+"carat-1-s", + "sSortJUIWrapper": "DataTables_sort_wrapper", + "sSortIcon": "DataTables_sort_icon", + + /* Scrolling */ + "sScrollHead": "dataTables_scrollHead "+_stateDefault, + "sScrollFoot": "dataTables_scrollFoot "+_stateDefault, + + /* Misc */ + "sHeaderTH": _stateDefault, + "sFooterTH": _stateDefault, + "sJUIHeader": _headerFooter+" ui-corner-tl ui-corner-tr", + "sJUIFooter": _headerFooter+" ui-corner-bl ui-corner-br" + } ); + + }()); + + + + var extPagination = DataTable.ext.pager; + + function _numbers ( page, pages ) { + var + numbers = [], + buttons = extPagination.numbers_length, + half = Math.floor( buttons / 2 ), + i = 1; + + if ( pages <= buttons ) { + numbers = _range( 0, pages ); + } + else if ( page <= half ) { + numbers = _range( 0, buttons-2 ); + numbers.push( 'ellipsis' ); + numbers.push( pages-1 ); + } + else if ( page >= pages - 1 - half ) { + numbers = _range( pages-(buttons-2), pages ); + numbers.splice( 0, 0, 'ellipsis' ); // no unshift in ie6 + numbers.splice( 0, 0, 0 ); + } + else { + numbers = _range( page-half+2, page+half-1 ); + numbers.push( 'ellipsis' ); + numbers.push( pages-1 ); + numbers.splice( 0, 0, 'ellipsis' ); + numbers.splice( 0, 0, 0 ); + } + + numbers.DT_el = 'span'; + return numbers; + } + + + $.extend( extPagination, { + simple: function ( page, pages ) { + return [ 'previous', 'next' ]; + }, + + full: function ( page, pages ) { + return [ 'first', 'previous', 'next', 'last' ]; + }, + + numbers: function ( page, pages ) { + return [ _numbers(page, pages) ]; + }, + + simple_numbers: function ( page, pages ) { + return [ 'previous', _numbers(page, pages), 'next' ]; + }, + + full_numbers: function ( page, pages ) { + return [ 'first', 'previous', _numbers(page, pages), 'next', 'last' ]; + }, + + first_last_numbers: function (page, pages) { + return ['first', _numbers(page, pages), 'last']; + }, + + // For testing and plug-ins to use + _numbers: _numbers, + + // Number of number buttons (including ellipsis) to show. _Must be odd!_ + numbers_length: 7 + } ); + + + $.extend( true, DataTable.ext.renderer, { + pageButton: { + _: function ( settings, host, idx, buttons, page, pages ) { + var classes = settings.oClasses; + var lang = settings.oLanguage.oPaginate; + var aria = settings.oLanguage.oAria.paginate || {}; + var btnDisplay, btnClass, counter=0; + + var attach = function( container, buttons ) { + var i, ien, node, button; + var clickHandler = function ( e ) { + _fnPageChange( settings, e.data.action, true ); + }; + + for ( i=0, ien=buttons.length ; i<ien ; i++ ) { + button = buttons[i]; + + if ( $.isArray( button ) ) { + var inner = $( '<'+(button.DT_el || 'div')+'/>' ) + .appendTo( container ); + attach( inner, button ); + } + else { + btnDisplay = null; + btnClass = ''; + + switch ( button ) { + case 'ellipsis': + container.append('<span class="ellipsis">…</span>'); + break; + + case 'first': + btnDisplay = lang.sFirst; + btnClass = button + (page > 0 ? + '' : ' '+classes.sPageButtonDisabled); + break; + + case 'previous': + btnDisplay = lang.sPrevious; + btnClass = button + (page > 0 ? + '' : ' '+classes.sPageButtonDisabled); + break; + + case 'next': + btnDisplay = lang.sNext; + btnClass = button + (page < pages-1 ? + '' : ' '+classes.sPageButtonDisabled); + break; + + case 'last': + btnDisplay = lang.sLast; + btnClass = button + (page < pages-1 ? + '' : ' '+classes.sPageButtonDisabled); + break; + + default: + btnDisplay = button + 1; + btnClass = page === button ? + classes.sPageButtonActive : ''; + break; + } + + if ( btnDisplay !== null ) { + node = $('<a>', { + 'class': classes.sPageButton+' '+btnClass, + 'aria-controls': settings.sTableId, + 'aria-label': aria[ button ], + 'data-dt-idx': counter, + 'tabindex': settings.iTabIndex, + 'id': idx === 0 && typeof button === 'string' ? + settings.sTableId +'_'+ button : + null + } ) + .html( btnDisplay ) + .appendTo( container ); + + _fnBindAction( + node, {action: button}, clickHandler + ); + + counter++; + } + } + } + }; + + // IE9 throws an 'unknown error' if document.activeElement is used + // inside an iframe or frame. Try / catch the error. Not good for + // accessibility, but neither are frames. + var activeEl; + + try { + // Because this approach is destroying and recreating the paging + // elements, focus is lost on the select button which is bad for + // accessibility. So we want to restore focus once the draw has + // completed + activeEl = $(host).find(document.activeElement).data('dt-idx'); + } + catch (e) {} + + attach( $(host).empty(), buttons ); + + if ( activeEl !== undefined ) { + $(host).find( '[data-dt-idx='+activeEl+']' ).focus(); + } + } + } + } ); + + + + // Built in type detection. See model.ext.aTypes for information about + // what is required from this methods. + $.extend( DataTable.ext.type.detect, [ + // Plain numbers - first since V8 detects some plain numbers as dates + // e.g. Date.parse('55') (but not all, e.g. Date.parse('22')...). + function ( d, settings ) + { + var decimal = settings.oLanguage.sDecimal; + return _isNumber( d, decimal ) ? 'num'+decimal : null; + }, + + // Dates (only those recognised by the browser's Date.parse) + function ( d, settings ) + { + // V8 tries _very_ hard to make a string passed into `Date.parse()` + // valid, so we need to use a regex to restrict date formats. Use a + // plug-in for anything other than ISO8601 style strings + if ( d && !(d instanceof Date) && ! _re_date.test(d) ) { + return null; + } + var parsed = Date.parse(d); + return (parsed !== null && !isNaN(parsed)) || _empty(d) ? 'date' : null; + }, + + // Formatted numbers + function ( d, settings ) + { + var decimal = settings.oLanguage.sDecimal; + return _isNumber( d, decimal, true ) ? 'num-fmt'+decimal : null; + }, + + // HTML numeric + function ( d, settings ) + { + var decimal = settings.oLanguage.sDecimal; + return _htmlNumeric( d, decimal ) ? 'html-num'+decimal : null; + }, + + // HTML numeric, formatted + function ( d, settings ) + { + var decimal = settings.oLanguage.sDecimal; + return _htmlNumeric( d, decimal, true ) ? 'html-num-fmt'+decimal : null; + }, + + // HTML (this is strict checking - there must be html) + function ( d, settings ) + { + return _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1) ? + 'html' : null; + } + ] ); + + + + // Filter formatting functions. See model.ext.ofnSearch for information about + // what is required from these methods. + // + // Note that additional search methods are added for the html numbers and + // html formatted numbers by `_addNumericSort()` when we know what the decimal + // place is + + + $.extend( DataTable.ext.type.search, { + html: function ( data ) { + return _empty(data) ? + data : + typeof data === 'string' ? + data + .replace( _re_new_lines, " " ) + .replace( _re_html, "" ) : + ''; + }, + + string: function ( data ) { + return _empty(data) ? + data : + typeof data === 'string' ? + data.replace( _re_new_lines, " " ) : + data; + } + } ); + + + + var __numericReplace = function ( d, decimalPlace, re1, re2 ) { + if ( d !== 0 && (!d || d === '-') ) { + return -Infinity; + } + + // If a decimal place other than `.` is used, it needs to be given to the + // function so we can detect it and replace with a `.` which is the only + // decimal place Javascript recognises - it is not locale aware. + if ( decimalPlace ) { + d = _numToDecimal( d, decimalPlace ); + } + + if ( d.replace ) { + if ( re1 ) { + d = d.replace( re1, '' ); + } + + if ( re2 ) { + d = d.replace( re2, '' ); + } + } + + return d * 1; + }; + + + // Add the numeric 'deformatting' functions for sorting and search. This is done + // in a function to provide an easy ability for the language options to add + // additional methods if a non-period decimal place is used. + function _addNumericSort ( decimalPlace ) { + $.each( + { + // Plain numbers + "num": function ( d ) { + return __numericReplace( d, decimalPlace ); + }, + + // Formatted numbers + "num-fmt": function ( d ) { + return __numericReplace( d, decimalPlace, _re_formatted_numeric ); + }, + + // HTML numeric + "html-num": function ( d ) { + return __numericReplace( d, decimalPlace, _re_html ); + }, + + // HTML numeric, formatted + "html-num-fmt": function ( d ) { + return __numericReplace( d, decimalPlace, _re_html, _re_formatted_numeric ); + } + }, + function ( key, fn ) { + // Add the ordering method + _ext.type.order[ key+decimalPlace+'-pre' ] = fn; + + // For HTML types add a search formatter that will strip the HTML + if ( key.match(/^html\-/) ) { + _ext.type.search[ key+decimalPlace ] = _ext.type.search.html; + } + } + ); + } + + + // Default sort methods + $.extend( _ext.type.order, { + // Dates + "date-pre": function ( d ) { + return Date.parse( d ) || -Infinity; + }, + + // html + "html-pre": function ( a ) { + return _empty(a) ? + '' : + a.replace ? + a.replace( /<.*?>/g, "" ).toLowerCase() : + a+''; + }, + + // string + "string-pre": function ( a ) { + // This is a little complex, but faster than always calling toString, + // http://jsperf.com/tostring-v-check + return _empty(a) ? + '' : + typeof a === 'string' ? + a.toLowerCase() : + ! a.toString ? + '' : + a.toString(); + }, + + // string-asc and -desc are retained only for compatibility with the old + // sort methods + "string-asc": function ( x, y ) { + return ((x < y) ? -1 : ((x > y) ? 1 : 0)); + }, + + "string-desc": function ( x, y ) { + return ((x < y) ? 1 : ((x > y) ? -1 : 0)); + } + } ); + + + // Numeric sorting types - order doesn't matter here + _addNumericSort( '' ); + + + $.extend( true, DataTable.ext.renderer, { + header: { + _: function ( settings, cell, column, classes ) { + // No additional mark-up required + // Attach a sort listener to update on sort - note that using the + // `DT` namespace will allow the event to be removed automatically + // on destroy, while the `dt` namespaced event is the one we are + // listening for + $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) { + if ( settings !== ctx ) { // need to check this this is the host + return; // table, not a nested one + } + + var colIdx = column.idx; + + cell + .removeClass( + column.sSortingClass +' '+ + classes.sSortAsc +' '+ + classes.sSortDesc + ) + .addClass( columns[ colIdx ] == 'asc' ? + classes.sSortAsc : columns[ colIdx ] == 'desc' ? + classes.sSortDesc : + column.sSortingClass + ); + } ); + }, + + jqueryui: function ( settings, cell, column, classes ) { + $('<div/>') + .addClass( classes.sSortJUIWrapper ) + .append( cell.contents() ) + .append( $('<span/>') + .addClass( classes.sSortIcon+' '+column.sSortingClassJUI ) + ) + .appendTo( cell ); + + // Attach a sort listener to update on sort + $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) { + if ( settings !== ctx ) { + return; + } + + var colIdx = column.idx; + + cell + .removeClass( classes.sSortAsc +" "+classes.sSortDesc ) + .addClass( columns[ colIdx ] == 'asc' ? + classes.sSortAsc : columns[ colIdx ] == 'desc' ? + classes.sSortDesc : + column.sSortingClass + ); + + cell + .find( 'span.'+classes.sSortIcon ) + .removeClass( + classes.sSortJUIAsc +" "+ + classes.sSortJUIDesc +" "+ + classes.sSortJUI +" "+ + classes.sSortJUIAscAllowed +" "+ + classes.sSortJUIDescAllowed + ) + .addClass( columns[ colIdx ] == 'asc' ? + classes.sSortJUIAsc : columns[ colIdx ] == 'desc' ? + classes.sSortJUIDesc : + column.sSortingClassJUI + ); + } ); + } + } + } ); + + /* + * Public helper functions. These aren't used internally by DataTables, or + * called by any of the options passed into DataTables, but they can be used + * externally by developers working with DataTables. They are helper functions + * to make working with DataTables a little bit easier. + */ + + var __htmlEscapeEntities = function ( d ) { + return typeof d === 'string' ? + d.replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"') : + d; + }; + + /** + * Helpers for `columns.render`. + * + * The options defined here can be used with the `columns.render` initialisation + * option to provide a display renderer. The following functions are defined: + * + * * `number` - Will format numeric data (defined by `columns.data`) for + * display, retaining the original unformatted data for sorting and filtering. + * It takes 5 parameters: + * * `string` - Thousands grouping separator + * * `string` - Decimal point indicator + * * `integer` - Number of decimal points to show + * * `string` (optional) - Prefix. + * * `string` (optional) - Postfix (/suffix). + * * `text` - Escape HTML to help prevent XSS attacks. It has no optional + * parameters. + * + * @example + * // Column definition using the number renderer + * { + * data: "salary", + * render: $.fn.dataTable.render.number( '\'', '.', 0, '$' ) + * } + * + * @namespace + */ + DataTable.render = { + number: function ( thousands, decimal, precision, prefix, postfix ) { + return { + display: function ( d ) { + if ( typeof d !== 'number' && typeof d !== 'string' ) { + return d; + } + + var negative = d < 0 ? '-' : ''; + var flo = parseFloat( d ); + + // If NaN then there isn't much formatting that we can do - just + // return immediately, escaping any HTML (this was supposed to + // be a number after all) + if ( isNaN( flo ) ) { + return __htmlEscapeEntities( d ); + } + + flo = flo.toFixed( precision ); + d = Math.abs( flo ); + + var intPart = parseInt( d, 10 ); + var floatPart = precision ? + decimal+(d - intPart).toFixed( precision ).substring( 2 ): + ''; + + return negative + (prefix||'') + + intPart.toString().replace( + /\B(?=(\d{3})+(?!\d))/g, thousands + ) + + floatPart + + (postfix||''); + } + }; + }, + + text: function () { + return { + display: __htmlEscapeEntities + }; + } + }; + + + /* + * This is really a good bit rubbish this method of exposing the internal methods + * publicly... - To be fixed in 2.0 using methods on the prototype + */ + + + /** + * Create a wrapper function for exporting an internal functions to an external API. + * @param {string} fn API function name + * @returns {function} wrapped function + * @memberof DataTable#internal + */ + function _fnExternApiFunc (fn) + { + return function() { + var args = [_fnSettingsFromNode( this[DataTable.ext.iApiIndex] )].concat( + Array.prototype.slice.call(arguments) + ); + return DataTable.ext.internal[fn].apply( this, args ); + }; + } + + + /** + * Reference to internal functions for use by plug-in developers. Note that + * these methods are references to internal functions and are considered to be + * private. If you use these methods, be aware that they are liable to change + * between versions. + * @namespace + */ + $.extend( DataTable.ext.internal, { + _fnExternApiFunc: _fnExternApiFunc, + _fnBuildAjax: _fnBuildAjax, + _fnAjaxUpdate: _fnAjaxUpdate, + _fnAjaxParameters: _fnAjaxParameters, + _fnAjaxUpdateDraw: _fnAjaxUpdateDraw, + _fnAjaxDataSrc: _fnAjaxDataSrc, + _fnAddColumn: _fnAddColumn, + _fnColumnOptions: _fnColumnOptions, + _fnAdjustColumnSizing: _fnAdjustColumnSizing, + _fnVisibleToColumnIndex: _fnVisibleToColumnIndex, + _fnColumnIndexToVisible: _fnColumnIndexToVisible, + _fnVisbleColumns: _fnVisbleColumns, + _fnGetColumns: _fnGetColumns, + _fnColumnTypes: _fnColumnTypes, + _fnApplyColumnDefs: _fnApplyColumnDefs, + _fnHungarianMap: _fnHungarianMap, + _fnCamelToHungarian: _fnCamelToHungarian, + _fnLanguageCompat: _fnLanguageCompat, + _fnBrowserDetect: _fnBrowserDetect, + _fnAddData: _fnAddData, + _fnAddTr: _fnAddTr, + _fnNodeToDataIndex: _fnNodeToDataIndex, + _fnNodeToColumnIndex: _fnNodeToColumnIndex, + _fnGetCellData: _fnGetCellData, + _fnSetCellData: _fnSetCellData, + _fnSplitObjNotation: _fnSplitObjNotation, + _fnGetObjectDataFn: _fnGetObjectDataFn, + _fnSetObjectDataFn: _fnSetObjectDataFn, + _fnGetDataMaster: _fnGetDataMaster, + _fnClearTable: _fnClearTable, + _fnDeleteIndex: _fnDeleteIndex, + _fnInvalidate: _fnInvalidate, + _fnGetRowElements: _fnGetRowElements, + _fnCreateTr: _fnCreateTr, + _fnBuildHead: _fnBuildHead, + _fnDrawHead: _fnDrawHead, + _fnDraw: _fnDraw, + _fnReDraw: _fnReDraw, + _fnAddOptionsHtml: _fnAddOptionsHtml, + _fnDetectHeader: _fnDetectHeader, + _fnGetUniqueThs: _fnGetUniqueThs, + _fnFeatureHtmlFilter: _fnFeatureHtmlFilter, + _fnFilterComplete: _fnFilterComplete, + _fnFilterCustom: _fnFilterCustom, + _fnFilterColumn: _fnFilterColumn, + _fnFilter: _fnFilter, + _fnFilterCreateSearch: _fnFilterCreateSearch, + _fnEscapeRegex: _fnEscapeRegex, + _fnFilterData: _fnFilterData, + _fnFeatureHtmlInfo: _fnFeatureHtmlInfo, + _fnUpdateInfo: _fnUpdateInfo, + _fnInfoMacros: _fnInfoMacros, + _fnInitialise: _fnInitialise, + _fnInitComplete: _fnInitComplete, + _fnLengthChange: _fnLengthChange, + _fnFeatureHtmlLength: _fnFeatureHtmlLength, + _fnFeatureHtmlPaginate: _fnFeatureHtmlPaginate, + _fnPageChange: _fnPageChange, + _fnFeatureHtmlProcessing: _fnFeatureHtmlProcessing, + _fnProcessingDisplay: _fnProcessingDisplay, + _fnFeatureHtmlTable: _fnFeatureHtmlTable, + _fnScrollDraw: _fnScrollDraw, + _fnApplyToChildren: _fnApplyToChildren, + _fnCalculateColumnWidths: _fnCalculateColumnWidths, + _fnThrottle: _fnThrottle, + _fnConvertToWidth: _fnConvertToWidth, + _fnGetWidestNode: _fnGetWidestNode, + _fnGetMaxLenString: _fnGetMaxLenString, + _fnStringToCss: _fnStringToCss, + _fnSortFlatten: _fnSortFlatten, + _fnSort: _fnSort, + _fnSortAria: _fnSortAria, + _fnSortListener: _fnSortListener, + _fnSortAttachListener: _fnSortAttachListener, + _fnSortingClasses: _fnSortingClasses, + _fnSortData: _fnSortData, + _fnSaveState: _fnSaveState, + _fnLoadState: _fnLoadState, + _fnSettingsFromNode: _fnSettingsFromNode, + _fnLog: _fnLog, + _fnMap: _fnMap, + _fnBindAction: _fnBindAction, + _fnCallbackReg: _fnCallbackReg, + _fnCallbackFire: _fnCallbackFire, + _fnLengthOverflow: _fnLengthOverflow, + _fnRenderer: _fnRenderer, + _fnDataSource: _fnDataSource, + _fnRowAttributes: _fnRowAttributes, + _fnCalculateEnd: function () {} // Used by a lot of plug-ins, but redundant + // in 1.10, so this dead-end function is + // added to prevent errors + } ); + + + // jQuery access + $.fn.dataTable = DataTable; + + // Provide access to the host jQuery object (circular reference) + DataTable.$ = $; + + // Legacy aliases + $.fn.dataTableSettings = DataTable.settings; + $.fn.dataTableExt = DataTable.ext; + + // With a capital `D` we return a DataTables API instance rather than a + // jQuery object + $.fn.DataTable = function ( opts ) { + return $(this).dataTable( opts ).api(); + }; + + // All properties that are available to $.fn.dataTable should also be + // available on $.fn.DataTable + $.each( DataTable, function ( prop, val ) { + $.fn.DataTable[ prop ] = val; + } ); + + + // Information about events fired by DataTables - for documentation. + /** + * Draw event, fired whenever the table is redrawn on the page, at the same + * point as fnDrawCallback. This may be useful for binding events or + * performing calculations when the table is altered at all. + * @name DataTable#draw.dt + * @event + * @param {event} e jQuery event object + * @param {object} o DataTables settings object {@link DataTable.models.oSettings} + */ + + /** + * Search event, fired when the searching applied to the table (using the + * built-in global search, or column filters) is altered. + * @name DataTable#search.dt + * @event + * @param {event} e jQuery event object + * @param {object} o DataTables settings object {@link DataTable.models.oSettings} + */ + + /** + * Page change event, fired when the paging of the table is altered. + * @name DataTable#page.dt + * @event + * @param {event} e jQuery event object + * @param {object} o DataTables settings object {@link DataTable.models.oSettings} + */ + + /** + * Order event, fired when the ordering applied to the table is altered. + * @name DataTable#order.dt + * @event + * @param {event} e jQuery event object + * @param {object} o DataTables settings object {@link DataTable.models.oSettings} + */ + + /** + * DataTables initialisation complete event, fired when the table is fully + * drawn, including Ajax data loaded, if Ajax data is required. + * @name DataTable#init.dt + * @event + * @param {event} e jQuery event object + * @param {object} oSettings DataTables settings object + * @param {object} json The JSON object request from the server - only + * present if client-side Ajax sourced data is used</li></ol> + */ + + /** + * State save event, fired when the table has changed state a new state save + * is required. This event allows modification of the state saving object + * prior to actually doing the save, including addition or other state + * properties (for plug-ins) or modification of a DataTables core property. + * @name DataTable#stateSaveParams.dt + * @event + * @param {event} e jQuery event object + * @param {object} oSettings DataTables settings object + * @param {object} json The state information to be saved + */ + + /** + * State load event, fired when the table is loading state from the stored + * data, but prior to the settings object being modified by the saved state + * - allowing modification of the saved state is required or loading of + * state for a plug-in. + * @name DataTable#stateLoadParams.dt + * @event + * @param {event} e jQuery event object + * @param {object} oSettings DataTables settings object + * @param {object} json The saved state information + */ + + /** + * State loaded event, fired when state has been loaded from stored data and + * the settings object has been modified by the loaded data. + * @name DataTable#stateLoaded.dt + * @event + * @param {event} e jQuery event object + * @param {object} oSettings DataTables settings object + * @param {object} json The saved state information + */ + + /** + * Processing event, fired when DataTables is doing some kind of processing + * (be it, order, searcg or anything else). It can be used to indicate to + * the end user that there is something happening, or that something has + * finished. + * @name DataTable#processing.dt + * @event + * @param {event} e jQuery event object + * @param {object} oSettings DataTables settings object + * @param {boolean} bShow Flag for if DataTables is doing processing or not + */ + + /** + * Ajax (XHR) event, fired whenever an Ajax request is completed from a + * request to made to the server for new data. This event is called before + * DataTables processed the returned data, so it can also be used to pre- + * process the data returned from the server, if needed. + * + * Note that this trigger is called in `fnServerData`, if you override + * `fnServerData` and which to use this event, you need to trigger it in you + * success function. + * @name DataTable#xhr.dt + * @event + * @param {event} e jQuery event object + * @param {object} o DataTables settings object {@link DataTable.models.oSettings} + * @param {object} json JSON returned from the server + * + * @example + * // Use a custom property returned from the server in another DOM element + * $('#table').dataTable().on('xhr.dt', function (e, settings, json) { + * $('#status').html( json.status ); + * } ); + * + * @example + * // Pre-process the data returned from the server + * $('#table').dataTable().on('xhr.dt', function (e, settings, json) { + * for ( var i=0, ien=json.aaData.length ; i<ien ; i++ ) { + * json.aaData[i].sum = json.aaData[i].one + json.aaData[i].two; + * } + * // Note no return - manipulate the data directly in the JSON object. + * } ); + */ + + /** + * Destroy event, fired when the DataTable is destroyed by calling fnDestroy + * or passing the bDestroy:true parameter in the initialisation object. This + * can be used to remove bound events, added DOM nodes, etc. + * @name DataTable#destroy.dt + * @event + * @param {event} e jQuery event object + * @param {object} o DataTables settings object {@link DataTable.models.oSettings} + */ + + /** + * Page length change event, fired when number of records to show on each + * page (the length) is changed. + * @name DataTable#length.dt + * @event + * @param {event} e jQuery event object + * @param {object} o DataTables settings object {@link DataTable.models.oSettings} + * @param {integer} len New length + */ + + /** + * Column sizing has changed. + * @name DataTable#column-sizing.dt + * @event + * @param {event} e jQuery event object + * @param {object} o DataTables settings object {@link DataTable.models.oSettings} + */ + + /** + * Column visibility has changed. + * @name DataTable#column-visibility.dt + * @event + * @param {event} e jQuery event object + * @param {object} o DataTables settings object {@link DataTable.models.oSettings} + * @param {int} column Column index + * @param {bool} vis `false` if column now hidden, or `true` if visible + */ + + return $.fn.dataTable; +})); diff --git a/src/legacy/design-studio/js/jquery.js b/src/legacy/design-studio/js/jquery.js new file mode 100644 index 0000000..eed1777 --- /dev/null +++ b/src/legacy/design-studio/js/jquery.js @@ -0,0 +1,9210 @@ +/*! + * jQuery JavaScript Library v2.1.4 + * http://jquery.com/ + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * + * Copyright 2005, 2014 jQuery Foundation, Inc. and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2015-04-28T16:01Z + */ + +(function( global, factory ) { + + if ( typeof module === "object" && typeof module.exports === "object" ) { + // For CommonJS and CommonJS-like environments where a proper `window` + // is present, execute the factory and get jQuery. + // For environments that do not have a `window` with a `document` + // (such as Node.js), expose a factory as module.exports. + // This accentuates the need for the creation of a real `window`. + // e.g. var jQuery = require("jquery")(window); + // See ticket #14549 for more info. + module.exports = global.document ? + factory( global, true ) : + function( w ) { + if ( !w.document ) { + throw new Error( "jQuery requires a window with a document" ); + } + return factory( w ); + }; + } else { + factory( global ); + } + +// Pass this if window is not defined yet +}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) { + +// Support: Firefox 18+ +// Can't be in strict mode, several libs including ASP.NET trace +// the stack via arguments.caller.callee and Firefox dies if +// you try to trace through "use strict" call chains. (#13335) +// + +var arr = []; + +var slice = arr.slice; + +var concat = arr.concat; + +var push = arr.push; + +var indexOf = arr.indexOf; + +var class2type = {}; + +var toString = class2type.toString; + +var hasOwn = class2type.hasOwnProperty; + +var support = {}; + + + +var + // Use the correct document accordingly with window argument (sandbox) + document = window.document, + + version = "2.1.4", + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + // The jQuery object is actually just the init constructor 'enhanced' + // Need init if jQuery is called (just allow error to be thrown if not included) + return new jQuery.fn.init( selector, context ); + }, + + // Support: Android<4.1 + // Make sure we trim BOM and NBSP + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, + + // Matches dashed string for camelizing + rmsPrefix = /^-ms-/, + rdashAlpha = /-([\da-z])/gi, + + // Used by jQuery.camelCase as callback to replace() + fcamelCase = function( all, letter ) { + return letter.toUpperCase(); + }; + +jQuery.fn = jQuery.prototype = { + // The current version of jQuery being used + jquery: version, + + constructor: jQuery, + + // Start with an empty selector + selector: "", + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num != null ? + + // Return just the one element from the set + ( num < 0 ? this[ num + this.length ] : this[ num ] ) : + + // Return all the elements in a clean array + slice.call( this ); + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + ret.context = this.context; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return jQuery.each( this, callback, args ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map(this, function( elem, i ) { + return callback.call( elem, i, elem ); + })); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] ); + }, + + end: function() { + return this.prevObject || this.constructor(null); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: arr.sort, + splice: arr.splice +}; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[0] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + + // Skip the boolean and the target + target = arguments[ i ] || {}; + i++; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction(target) ) { + target = {}; + } + + // Extend jQuery itself if only one argument is passed + if ( i === length ) { + target = this; + i--; + } + + for ( ; i < length; i++ ) { + // Only deal with non-null/undefined values + if ( (options = arguments[ i ]) != null ) { + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { + if ( copyIsArray ) { + copyIsArray = false; + clone = src && jQuery.isArray(src) ? src : []; + + } else { + clone = src && jQuery.isPlainObject(src) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend({ + // Unique for each copy of jQuery on the page + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), + + // Assume jQuery is ready without the ready module + isReady: true, + + error: function( msg ) { + throw new Error( msg ); + }, + + noop: function() {}, + + isFunction: function( obj ) { + return jQuery.type(obj) === "function"; + }, + + isArray: Array.isArray, + + isWindow: function( obj ) { + return obj != null && obj === obj.window; + }, + + isNumeric: function( obj ) { + // parseFloat NaNs numeric-cast false positives (null|true|false|"") + // ...but misinterprets leading-number strings, particularly hex literals ("0x...") + // subtraction forces infinities to NaN + // adding 1 corrects loss of precision from parseFloat (#15100) + return !jQuery.isArray( obj ) && (obj - parseFloat( obj ) + 1) >= 0; + }, + + isPlainObject: function( obj ) { + // Not plain objects: + // - Any object or value whose internal [[Class]] property is not "[object Object]" + // - DOM nodes + // - window + if ( jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + return false; + } + + if ( obj.constructor && + !hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) { + return false; + } + + // If the function hasn't returned already, we're confident that + // |obj| is a plain object, created by {} or constructed with new Object + return true; + }, + + isEmptyObject: function( obj ) { + var name; + for ( name in obj ) { + return false; + } + return true; + }, + + type: function( obj ) { + if ( obj == null ) { + return obj + ""; + } + // Support: Android<4.0, iOS<6 (functionish RegExp) + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call(obj) ] || "object" : + typeof obj; + }, + + // Evaluates a script in a global context + globalEval: function( code ) { + var script, + indirect = eval; + + code = jQuery.trim( code ); + + if ( code ) { + // If the code includes a valid, prologue position + // strict mode pragma, execute code by injecting a + // script tag into the document. + if ( code.indexOf("use strict") === 1 ) { + script = document.createElement("script"); + script.text = code; + document.head.appendChild( script ).parentNode.removeChild( script ); + } else { + // Otherwise, avoid the DOM node creation, insertion + // and removal by using an indirect global eval + indirect( code ); + } + } + }, + + // Convert dashed to camelCase; used by the css and data modules + // Support: IE9-11+ + // Microsoft forgot to hump their vendor prefix (#9572) + camelCase: function( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + }, + + // args is for internal usage only + each: function( obj, callback, args ) { + var value, + i = 0, + length = obj.length, + isArray = isArraylike( obj ); + + if ( args ) { + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback.apply( obj[ i ], args ); + + if ( value === false ) { + break; + } + } + } else { + for ( i in obj ) { + value = callback.apply( obj[ i ], args ); + + if ( value === false ) { + break; + } + } + } + + // A special, fast, case for the most common use of each + } else { + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback.call( obj[ i ], i, obj[ i ] ); + + if ( value === false ) { + break; + } + } + } else { + for ( i in obj ) { + value = callback.call( obj[ i ], i, obj[ i ] ); + + if ( value === false ) { + break; + } + } + } + } + + return obj; + }, + + // Support: Android<4.1 + trim: function( text ) { + return text == null ? + "" : + ( text + "" ).replace( rtrim, "" ); + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArraylike( Object(arr) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + return arr == null ? -1 : indexOf.call( arr, elem, i ); + }, + + merge: function( first, second ) { + var len = +second.length, + j = 0, + i = first.length; + + for ( ; j < len; j++ ) { + first[ i++ ] = second[ j ]; + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, invert ) { + var callbackInverse, + matches = [], + i = 0, + length = elems.length, + callbackExpect = !invert; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + callbackInverse = !callback( elems[ i ], i ); + if ( callbackInverse !== callbackExpect ) { + matches.push( elems[ i ] ); + } + } + + return matches; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var value, + i = 0, + length = elems.length, + isArray = isArraylike( elems ), + ret = []; + + // Go through the array, translating each of the items to their new values + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + } + + // Flatten any nested arrays + return concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // Bind a function to a context, optionally partially applying any + // arguments. + proxy: function( fn, context ) { + var tmp, args, proxy; + + if ( typeof context === "string" ) { + tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !jQuery.isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + args = slice.call( arguments, 2 ); + proxy = function() { + return fn.apply( context || this, args.concat( slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || jQuery.guid++; + + return proxy; + }, + + now: Date.now, + + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support +}); + +// Populate the class2type map +jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +}); + +function isArraylike( obj ) { + + // Support: iOS 8.2 (not reproducible in simulator) + // `in` check used to prevent JIT error (gh-2145) + // hasOwn isn't used here due to false negatives + // regarding Nodelist length in IE + var length = "length" in obj && obj.length, + type = jQuery.type( obj ); + + if ( type === "function" || jQuery.isWindow( obj ) ) { + return false; + } + + if ( obj.nodeType === 1 && length ) { + return true; + } + + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; +} +var Sizzle = +/*! + * Sizzle CSS Selector Engine v2.2.0-pre + * http://sizzlejs.com/ + * + * Copyright 2008, 2014 jQuery Foundation, Inc. and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2014-12-16 + */ +(function( window ) { + +var i, + support, + Expr, + getText, + isXML, + tokenize, + compile, + select, + outermostContext, + sortInput, + hasDuplicate, + + // Local document vars + setDocument, + document, + docElem, + documentIsHTML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + + // Instance-specific data + expando = "sizzle" + 1 * new Date(), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + } + return 0; + }, + + // General-purpose constants + MAX_NEGATIVE = 1 << 31, + + // Instance methods + hasOwn = ({}).hasOwnProperty, + arr = [], + pop = arr.pop, + push_native = arr.push, + push = arr.push, + slice = arr.slice, + // Use a stripped-down indexOf as it's faster than native + // http://jsperf.com/thor-indexof-vs-for/5 + indexOf = function( list, elem ) { + var i = 0, + len = list.length; + for ( ; i < len; i++ ) { + if ( list[i] === elem ) { + return i; + } + } + return -1; + }, + + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", + + // Regular expressions + + // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + // http://www.w3.org/TR/css3-syntax/#characters + characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", + + // Loosely modeled on CSS identifier characters + // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors + // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + identifier = characterEncoding.replace( "w", "w#" ), + + // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + characterEncoding + ")(?:" + whitespace + + // Operator (capture 2) + "*([*^$|!~]?=)" + whitespace + + // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + + "*\\]", + + pseudos = ":(" + characterEncoding + ")(?:\\((" + + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: + // 1. quoted (capture 3; capture 4 or capture 5) + "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + + // 2. simple (capture 6) + "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + + // 3. anything else (capture 2) + ".*" + + ")\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rwhitespace = new RegExp( whitespace + "+", "g" ), + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), + + rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ), + + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + characterEncoding + ")" ), + "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), + "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rnative = /^[^{]+\{\s*\[native \w/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rsibling = /[+~]/, + rescape = /'|\\/g, + + // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), + funescape = function( _, escaped, escapedWhitespace ) { + var high = "0x" + escaped - 0x10000; + // NaN means non-codepoint + // Support: Firefox<24 + // Workaround erroneous numeric interpretation of +"0x" + return high !== high || escapedWhitespace ? + escaped : + high < 0 ? + // BMP codepoint + String.fromCharCode( high + 0x10000 ) : + // Supplemental Plane codepoint (surrogate pair) + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }, + + // Used for iframes + // See setDocument() + // Removing the function wrapper causes a "Permission Denied" + // error in IE + unloadHandler = function() { + setDocument(); + }; + +// Optimize for push.apply( _, NodeList ) +try { + push.apply( + (arr = slice.call( preferredDoc.childNodes )), + preferredDoc.childNodes + ); + // Support: Android<4.0 + // Detect silently failing push.apply + arr[ preferredDoc.childNodes.length ].nodeType; +} catch ( e ) { + push = { apply: arr.length ? + + // Leverage slice if possible + function( target, els ) { + push_native.apply( target, slice.call(els) ); + } : + + // Support: IE<9 + // Otherwise append directly + function( target, els ) { + var j = target.length, + i = 0; + // Can't trust NodeList.length + while ( (target[j++] = els[i++]) ) {} + target.length = j - 1; + } + }; +} + +function Sizzle( selector, context, results, seed ) { + var match, elem, m, nodeType, + // QSA vars + i, groups, old, nid, newContext, newSelector; + + if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { + setDocument( context ); + } + + context = context || document; + results = results || []; + nodeType = context.nodeType; + + if ( typeof selector !== "string" || !selector || + nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { + + return results; + } + + if ( !seed && documentIsHTML ) { + + // Try to shortcut find operations when possible (e.g., not under DocumentFragment) + if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) { + // Speed-up: Sizzle("#ID") + if ( (m = match[1]) ) { + if ( nodeType === 9 ) { + elem = context.getElementById( m ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document (jQuery #6963) + if ( elem && elem.parentNode ) { + // Handle the case where IE, Opera, and Webkit return items + // by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + } else { + // Context is not a document + if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && + contains( context, elem ) && elem.id === m ) { + results.push( elem ); + return results; + } + } + + // Speed-up: Sizzle("TAG") + } else if ( match[2] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Speed-up: Sizzle(".CLASS") + } else if ( (m = match[3]) && support.getElementsByClassName ) { + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // QSA path + if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { + nid = old = expando; + newContext = context; + newSelector = nodeType !== 1 && selector; + + // qSA works strangely on Element-rooted queries + // We can work around this by specifying an extra ID on the root + // and working up from there (Thanks to Andrew Dupont for the technique) + // IE 8 doesn't work on object elements + if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { + groups = tokenize( selector ); + + if ( (old = context.getAttribute("id")) ) { + nid = old.replace( rescape, "\\$&" ); + } else { + context.setAttribute( "id", nid ); + } + nid = "[id='" + nid + "'] "; + + i = groups.length; + while ( i-- ) { + groups[i] = nid + toSelector( groups[i] ); + } + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context; + newSelector = groups.join(","); + } + + if ( newSelector ) { + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch(qsaError) { + } finally { + if ( !old ) { + context.removeAttribute("id"); + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} + +/** + * Create key-value caches of limited size + * @returns {Function(string, Object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + var keys = []; + + function cache( key, value ) { + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key + " " ) > Expr.cacheLength ) { + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return (cache[ key + " " ] = value); + } + return cache; +} + +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} + +/** + * Support testing using an element + * @param {Function} fn Passed the created div and expects a boolean result + */ +function assert( fn ) { + var div = document.createElement("div"); + + try { + return !!fn( div ); + } catch (e) { + return false; + } finally { + // Remove from its parent by default + if ( div.parentNode ) { + div.parentNode.removeChild( div ); + } + // release memory in IE + div = null; + } +} + +/** + * Adds the same handler for all of the specified attrs + * @param {String} attrs Pipe-separated list of attributes + * @param {Function} handler The method that will be applied + */ +function addHandle( attrs, handler ) { + var arr = attrs.split("|"), + i = attrs.length; + + while ( i-- ) { + Expr.attrHandle[ arr[i] ] = handler; + } +} + +/** + * Checks document order of two siblings + * @param {Element} a + * @param {Element} b + * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b + */ +function siblingCheck( a, b ) { + var cur = b && a, + diff = cur && a.nodeType === 1 && b.nodeType === 1 && + ( ~b.sourceIndex || MAX_NEGATIVE ) - + ( ~a.sourceIndex || MAX_NEGATIVE ); + + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } + + // Check if b follows a + if ( cur ) { + while ( (cur = cur.nextSibling) ) { + if ( cur === b ) { + return -1; + } + } + } + + return a ? 1 : -1; +} + +/** + * Returns a function to use in pseudos for input types + * @param {String} type + */ +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for buttons + * @param {String} type + */ +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for positionals + * @param {Function} fn + */ +function createPositionalPseudo( fn ) { + return markFunction(function( argument ) { + argument = +argument; + return markFunction(function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ (j = matchIndexes[i]) ] ) { + seed[j] = !(matches[j] = seed[j]); + } + } + }); + }); +} + +/** + * Checks a node for validity as a Sizzle context + * @param {Element|Object=} context + * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value + */ +function testContext( context ) { + return context && typeof context.getElementsByTagName !== "undefined" && context; +} + +// Expose support vars for convenience +support = Sizzle.support = {}; + +/** + * Detects XML nodes + * @param {Element|Object} elem An element or a document + * @returns {Boolean} True iff elem is a non-HTML XML node + */ +isXML = Sizzle.isXML = function( elem ) { + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = elem && (elem.ownerDocument || elem).documentElement; + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +/** + * Sets document-related variables once based on the current document + * @param {Element|Object} [doc] An element or document object to use to set the document + * @returns {Object} Returns the current document + */ +setDocument = Sizzle.setDocument = function( node ) { + var hasCompare, parent, + doc = node ? node.ownerDocument || node : preferredDoc; + + // If no document and documentElement is available, return + if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + // Set our document + document = doc; + docElem = doc.documentElement; + parent = doc.defaultView; + + // Support: IE>8 + // If iframe document is assigned to "document" variable and if iframe has been reloaded, + // IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936 + // IE6-8 do not support the defaultView property so parent will be undefined + if ( parent && parent !== parent.top ) { + // IE11 does not have attachEvent, so all must suffer + if ( parent.addEventListener ) { + parent.addEventListener( "unload", unloadHandler, false ); + } else if ( parent.attachEvent ) { + parent.attachEvent( "onunload", unloadHandler ); + } + } + + /* Support tests + ---------------------------------------------------------------------- */ + documentIsHTML = !isXML( doc ); + + /* Attributes + ---------------------------------------------------------------------- */ + + // Support: IE<8 + // Verify that getAttribute really returns attributes and not properties + // (excepting IE8 booleans) + support.attributes = assert(function( div ) { + div.className = "i"; + return !div.getAttribute("className"); + }); + + /* getElement(s)By* + ---------------------------------------------------------------------- */ + + // Check if getElementsByTagName("*") returns only elements + support.getElementsByTagName = assert(function( div ) { + div.appendChild( doc.createComment("") ); + return !div.getElementsByTagName("*").length; + }); + + // Support: IE<9 + support.getElementsByClassName = rnative.test( doc.getElementsByClassName ); + + // Support: IE<10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert(function( div ) { + docElem.appendChild( div ).id = expando; + return !doc.getElementsByName || !doc.getElementsByName( expando ).length; + }); + + // ID find and filter + if ( support.getById ) { + Expr.find["ID"] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var m = context.getElementById( id ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + return m && m.parentNode ? [ m ] : []; + } + }; + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute("id") === attrId; + }; + }; + } else { + // Support: IE6/7 + // getElementById is not reliable as a find shortcut + delete Expr.find["ID"]; + + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); + return node && node.value === attrId; + }; + }; + } + + // Tag + Expr.find["TAG"] = support.getElementsByTagName ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( tag ); + + // DocumentFragment nodes don't have gEBTN + } else if ( support.qsa ) { + return context.querySelectorAll( tag ); + } + } : + + function( tag, context ) { + var elem, + tmp = [], + i = 0, + // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too + results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + while ( (elem = results[i++]) ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }; + + // Class + Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { + if ( documentIsHTML ) { + return context.getElementsByClassName( className ); + } + }; + + /* QSA/matchesSelector + ---------------------------------------------------------------------- */ + + // QSA and matchesSelector support + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + rbuggyMatches = []; + + // qSa(:focus) reports false when true (Chrome 21) + // We allow this because of a bug in IE8/9 that throws an error + // whenever `document.activeElement` is accessed on an iframe + // So, we allow :focus to pass through QSA all the time to avoid the IE error + // See http://bugs.jquery.com/ticket/13378 + rbuggyQSA = []; + + if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) { + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert(function( div ) { + // Select is set to empty string on purpose + // This is to test IE's treatment of not explicitly + // setting a boolean content attribute, + // since its presence should be enough + // http://bugs.jquery.com/ticket/12359 + docElem.appendChild( div ).innerHTML = "<a id='" + expando + "'></a>" + + "<select id='" + expando + "-\f]' msallowcapture=''>" + + "<option selected=''></option></select>"; + + // Support: IE8, Opera 11-12.16 + // Nothing should be selected when empty strings follow ^= or $= or *= + // The test attribute must be unknown in Opera but "safe" for WinRT + // http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section + if ( div.querySelectorAll("[msallowcapture^='']").length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } + + // Support: IE8 + // Boolean attributes and "value" are not treated correctly + if ( !div.querySelectorAll("[selected]").length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } + + // Support: Chrome<29, Android<4.2+, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.7+ + if ( !div.querySelectorAll( "[id~=" + expando + "-]" ).length ) { + rbuggyQSA.push("~="); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here and will not see later tests + if ( !div.querySelectorAll(":checked").length ) { + rbuggyQSA.push(":checked"); + } + + // Support: Safari 8+, iOS 8+ + // https://bugs.webkit.org/show_bug.cgi?id=136851 + // In-page `selector#id sibing-combinator selector` fails + if ( !div.querySelectorAll( "a#" + expando + "+*" ).length ) { + rbuggyQSA.push(".#.+[+~]"); + } + }); + + assert(function( div ) { + // Support: Windows 8 Native Apps + // The type and name attributes are restricted during .innerHTML assignment + var input = doc.createElement("input"); + input.setAttribute( "type", "hidden" ); + div.appendChild( input ).setAttribute( "name", "D" ); + + // Support: IE8 + // Enforce case-sensitivity of name attribute + if ( div.querySelectorAll("[name=d]").length ) { + rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here and will not see later tests + if ( !div.querySelectorAll(":enabled").length ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Opera 10-11 does not throw on post-comma invalid pseudos + div.querySelectorAll("*,:x"); + rbuggyQSA.push(",.*:"); + }); + } + + if ( (support.matchesSelector = rnative.test( (matches = docElem.matches || + docElem.webkitMatchesSelector || + docElem.mozMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector) )) ) { + + assert(function( div ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call( div, "div" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( div, "[s!='']:x" ); + rbuggyMatches.push( "!=", pseudos ); + }); + } + + rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); + rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); + + /* Contains + ---------------------------------------------------------------------- */ + hasCompare = rnative.test( docElem.compareDocumentPosition ); + + // Element contains another + // Purposefully does not implement inclusive descendent + // As in, an element does not contain itself + contains = hasCompare || rnative.test( docElem.contains ) ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + )); + } : + function( a, b ) { + if ( b ) { + while ( (b = b.parentNode) ) { + if ( b === a ) { + return true; + } + } + } + return false; + }; + + /* Sorting + ---------------------------------------------------------------------- */ + + // Document order sorting + sortOrder = hasCompare ? + function( a, b ) { + + // Flag for duplicate removal + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + // Sort on method existence if only one input has compareDocumentPosition + var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; + if ( compare ) { + return compare; + } + + // Calculate position if both inputs belong to the same document + compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ? + a.compareDocumentPosition( b ) : + + // Otherwise we know they are disconnected + 1; + + // Disconnected nodes + if ( compare & 1 || + (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { + + // Choose the first element that is related to our preferred document + if ( a === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) { + return -1; + } + if ( b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) { + return 1; + } + + // Maintain original order + return sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + } + + return compare & 4 ? -1 : 1; + } : + function( a, b ) { + // Exit early if the nodes are identical + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + var cur, + i = 0, + aup = a.parentNode, + bup = b.parentNode, + ap = [ a ], + bp = [ b ]; + + // Parentless nodes are either documents or disconnected + if ( !aup || !bup ) { + return a === doc ? -1 : + b === doc ? 1 : + aup ? -1 : + bup ? 1 : + sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + + // If the nodes are siblings, we can do a quick check + } else if ( aup === bup ) { + return siblingCheck( a, b ); + } + + // Otherwise we need full lists of their ancestors for comparison + cur = a; + while ( (cur = cur.parentNode) ) { + ap.unshift( cur ); + } + cur = b; + while ( (cur = cur.parentNode) ) { + bp.unshift( cur ); + } + + // Walk down the tree looking for a discrepancy + while ( ap[i] === bp[i] ) { + i++; + } + + return i ? + // Do a sibling check if the nodes have a common ancestor + siblingCheck( ap[i], bp[i] ) : + + // Otherwise nodes in our document sort first + ap[i] === preferredDoc ? -1 : + bp[i] === preferredDoc ? 1 : + 0; + }; + + return doc; +}; + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + // Make sure that attribute selectors are quoted + expr = expr.replace( rattributeQuotes, "='$1']" ); + + if ( support.matchesSelector && documentIsHTML && + ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && + ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { + + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || support.disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch (e) {} + } + + return Sizzle( expr, document, null, [ elem ] ).length > 0; +}; + +Sizzle.contains = function( context, elem ) { + // Set document vars if needed + if ( ( context.ownerDocument || context ) !== document ) { + setDocument( context ); + } + return contains( context, elem ); +}; + +Sizzle.attr = function( elem, name ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + var fn = Expr.attrHandle[ name.toLowerCase() ], + // Don't get fooled by Object.prototype properties (jQuery #13807) + val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? + fn( elem, name, !documentIsHTML ) : + undefined; + + return val !== undefined ? + val : + support.attributes || !documentIsHTML ? + elem.getAttribute( name ) : + (val = elem.getAttributeNode(name)) && val.specified ? + val.value : + null; +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +/** + * Document sorting and removing duplicates + * @param {ArrayLike} results + */ +Sizzle.uniqueSort = function( results ) { + var elem, + duplicates = [], + j = 0, + i = 0; + + // Unless we *know* we can detect duplicates, assume their presence + hasDuplicate = !support.detectDuplicates; + sortInput = !support.sortStable && results.slice( 0 ); + results.sort( sortOrder ); + + if ( hasDuplicate ) { + while ( (elem = results[i++]) ) { + if ( elem === results[ i ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + // Clear input after sorting to release objects + // See https://github.com/jquery/sizzle/pull/225 + sortInput = null; + + return results; +}; + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( !nodeType ) { + // If no nodeType, this is expected to be an array + while ( (node = elem[i++]) ) { + // Do not traverse comment nodes + ret += getText( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + // Use textContent for elements + // innerText usage removed for consistency of new lines (jQuery #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + // Do not include comment or processing instruction nodes + + return ret; +}; + +Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + attrHandle: {}, + + find: {}, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[1] = match[1].replace( runescape, funescape ); + + // Move the given value to match[3] whether quoted or unquoted + match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape ); + + if ( match[2] === "~=" ) { + match[3] = " " + match[3] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 what (child|of-type) + 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 4 xn-component of xn+y argument ([+-]?\d*n|) + 5 sign of xn-component + 6 x of xn-component + 7 sign of y-component + 8 y of y-component + */ + match[1] = match[1].toLowerCase(); + + if ( match[1].slice( 0, 3 ) === "nth" ) { + // nth-* requires argument + if ( !match[3] ) { + Sizzle.error( match[0] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); + match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); + + // other types prohibit arguments + } else if ( match[3] ) { + Sizzle.error( match[0] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var excess, + unquoted = !match[6] && match[2]; + + if ( matchExpr["CHILD"].test( match[0] ) ) { + return null; + } + + // Accept quoted arguments as-is + if ( match[3] ) { + match[2] = match[4] || match[5] || ""; + + // Strip excess characters from unquoted arguments + } else if ( unquoted && rpseudo.test( unquoted ) && + // Get excess from tokenize (recursively) + (excess = tokenize( unquoted, true )) && + // advance to the next closing parenthesis + (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { + + // excess is a negative index + match[0] = match[0].slice( 0, excess ); + match[2] = unquoted.slice( 0, excess ); + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + + "TAG": function( nodeNameSelector ) { + var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); + return nodeNameSelector === "*" ? + function() { return true; } : + function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ className + " " ]; + + return pattern || + (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && + classCache( className, function( elem ) { + return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "" ); + }); + }, + + "ATTR": function( name, operator, check ) { + return function( elem ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.slice( -check.length ) === check : + operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : + false; + }; + }, + + "CHILD": function( type, what, argument, first, last ) { + var simple = type.slice( 0, 3 ) !== "nth", + forward = type.slice( -4 ) !== "last", + ofType = what === "of-type"; + + return first === 1 && last === 0 ? + + // Shortcut for :nth-*(n) + function( elem ) { + return !!elem.parentNode; + } : + + function( elem, context, xml ) { + var cache, outerCache, node, diff, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType; + + if ( parent ) { + + // :(first|last|only)-(child|of-type) + if ( simple ) { + while ( dir ) { + node = elem; + while ( (node = node[ dir ]) ) { + if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) { + return false; + } + } + // Reverse direction for :only-* (if we haven't yet done so) + start = dir = type === "only" && !start && "nextSibling"; + } + return true; + } + + start = [ forward ? parent.firstChild : parent.lastChild ]; + + // non-xml :nth-child(...) stores cache data on `parent` + if ( forward && useCache ) { + // Seek `elem` from a previously-cached index + outerCache = parent[ expando ] || (parent[ expando ] = {}); + cache = outerCache[ type ] || []; + nodeIndex = cache[0] === dirruns && cache[1]; + diff = cache[0] === dirruns && cache[2]; + node = nodeIndex && parent.childNodes[ nodeIndex ]; + + while ( (node = ++nodeIndex && node && node[ dir ] || + + // Fallback to seeking `elem` from the start + (diff = nodeIndex = 0) || start.pop()) ) { + + // When found, cache indexes on `parent` and break + if ( node.nodeType === 1 && ++diff && node === elem ) { + outerCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + // Use previously-cached element index if available + } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) { + diff = cache[1]; + + // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...) + } else { + // Use the same loop as above to seek `elem` from the start + while ( (node = ++nodeIndex && node && node[ dir ] || + (diff = nodeIndex = 0) || start.pop()) ) { + + if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) { + // Cache the index of each encountered element + if ( useCache ) { + (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ]; + } + + if ( node === elem ) { + break; + } + } + } + } + + // Incorporate the offset, then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction(function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf( seed, matched[i] ); + seed[ idx ] = !( matches[ idx ] = matched[i] ); + } + }) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + // Potentially complex pseudos + "not": markFunction(function( selector ) { + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction(function( seed, matches, context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( (elem = unmatched[i]) ) { + seed[i] = !(matches[i] = elem); + } + } + }) : + function( elem, context, xml ) { + input[0] = elem; + matcher( input, null, xml, results ); + // Don't keep the element (issue #299) + input[0] = null; + return !results.pop(); + }; + }), + + "has": markFunction(function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + }), + + "contains": markFunction(function( text ) { + text = text.replace( runescape, funescape ); + return function( elem ) { + return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; + }; + }), + + // "Whether an element is represented by a :lang() selector + // is based solely on the element's language value + // being equal to the identifier C, + // or beginning with the identifier C immediately followed by "-". + // The matching of C against the element's language value is performed case-insensitively. + // The identifier C does not have to be a valid language name." + // http://www.w3.org/TR/selectors/#lang-pseudo + "lang": markFunction( function( lang ) { + // lang value must be a valid identifier + if ( !ridentifier.test(lang || "") ) { + Sizzle.error( "unsupported lang: " + lang ); + } + lang = lang.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + var elemLang; + do { + if ( (elemLang = documentIsHTML ? + elem.lang : + elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) { + + elemLang = elemLang.toLowerCase(); + return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; + } + } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); + return false; + }; + }), + + // Miscellaneous + "target": function( elem ) { + var hash = window.location && window.location.hash; + return hash && hash.slice( 1 ) === elem.id; + }, + + "root": function( elem ) { + return elem === docElem; + }, + + "focus": function( elem ) { + return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); + }, + + // Boolean properties + "enabled": function( elem ) { + return elem.disabled === false; + }, + + "disabled": function( elem ) { + return elem.disabled === true; + }, + + "checked": function( elem ) { + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); + }, + + "selected": function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + // Contents + "empty": function( elem ) { + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), + // but not by others (comment: 8; processing instruction: 7; etc.) + // nodeType < 6 works because attributes (2) do not appear as children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + if ( elem.nodeType < 6 ) { + return false; + } + } + return true; + }, + + "parent": function( elem ) { + return !Expr.pseudos["empty"]( elem ); + }, + + // Element/input types + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "text": function( elem ) { + var attr; + return elem.nodeName.toLowerCase() === "input" && + elem.type === "text" && + + // Support: IE<8 + // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" + ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" ); + }, + + // Position-in-collection + "first": createPositionalPseudo(function() { + return [ 0 ]; + }), + + "last": createPositionalPseudo(function( matchIndexes, length ) { + return [ length - 1 ]; + }), + + "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + }), + + "even": createPositionalPseudo(function( matchIndexes, length ) { + var i = 0; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "odd": createPositionalPseudo(function( matchIndexes, length ) { + var i = 1; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }) + } +}; + +Expr.pseudos["nth"] = Expr.pseudos["eq"]; + +// Add button/input type pseudos +for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { + Expr.pseudos[ i ] = createInputPseudo( i ); +} +for ( i in { submit: true, reset: true } ) { + Expr.pseudos[ i ] = createButtonPseudo( i ); +} + +// Easy API for creating new setFilters +function setFilters() {} +setFilters.prototype = Expr.filters = Expr.pseudos; +Expr.setFilters = new setFilters(); + +tokenize = Sizzle.tokenize = function( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || (match = rcomma.exec( soFar )) ) { + if ( match ) { + // Don't consume trailing commas as valid + soFar = soFar.slice( match[0].length ) || soFar; + } + groups.push( (tokens = []) ); + } + + matched = false; + + // Combinators + if ( (match = rcombinators.exec( soFar )) ) { + matched = match.shift(); + tokens.push({ + value: matched, + // Cast descendant combinators to space + type: match[0].replace( rtrim, " " ) + }); + soFar = soFar.slice( matched.length ); + } + + // Filters + for ( type in Expr.filter ) { + if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || + (match = preFilters[ type ]( match ))) ) { + matched = match.shift(); + tokens.push({ + value: matched, + type: type, + matches: match + }); + soFar = soFar.slice( matched.length ); + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +}; + +function toSelector( tokens ) { + var i = 0, + len = tokens.length, + selector = ""; + for ( ; i < len; i++ ) { + selector += tokens[i].value; + } + return selector; +} + +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + checkNonElements = base && dir === "parentNode", + doneName = done++; + + return combinator.first ? + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + return matcher( elem, context, xml ); + } + } + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + var oldCache, outerCache, + newCache = [ dirruns, doneName ]; + + // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching + if ( xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + if ( matcher( elem, context, xml ) ) { + return true; + } + } + } + } else { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + outerCache = elem[ expando ] || (elem[ expando ] = {}); + if ( (oldCache = outerCache[ dir ]) && + oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { + + // Assign to newCache so results back-propagate to previous elements + return (newCache[ 2 ] = oldCache[ 2 ]); + } else { + // Reuse newcache so results back-propagate to previous elements + outerCache[ dir ] = newCache; + + // A match means we're done; a fail means we have to keep checking + if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) { + return true; + } + } + } + } + } + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[i]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[0]; +} + +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[i], results ); + } + return results; +} + +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( (elem = unmatched[i]) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction(function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( (elem = temp[i]) ) { + matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) ) { + // Restore matcherIn since elem is not yet a final match + temp.push( (matcherIn[i] = elem) ); + } + } + postFinder( null, (matcherOut = []), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) && + (temp = postFinder ? indexOf( seed, elem ) : preMap[i]) > -1 ) { + + seed[temp] = !(results[temp] = elem); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + }); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[0].type ], + implicitRelative = leadingRelative || Expr.relative[" "], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + (checkContext = context).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + // Avoid hanging onto element (issue #299) + checkContext = null; + return ret; + } ]; + + for ( ; i < len; i++ ) { + if ( (matcher = Expr.relative[ tokens[i].type ]) ) { + matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; + } else { + matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[j].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && toSelector( + // If the preceding token was a descendant combinator, insert an implicit any-element `*` + tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" }) + ).replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), + j < len && toSelector( tokens ) + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + var bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, outermost ) { + var elem, j, matcher, + matchedCount = 0, + i = "0", + unmatched = seed && [], + setMatched = [], + contextBackup = outermostContext, + // We must always have either seed elements or outermost context + elems = seed || byElement && Expr.find["TAG"]( "*", outermost ), + // Use integer dirruns iff this is the outermost matcher + dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1), + len = elems.length; + + if ( outermost ) { + outermostContext = context !== document && context; + } + + // Add elements passing elementMatchers directly to results + // Keep `i` a string if there are no elements so `matchedCount` will be "00" below + // Support: IE<9, Safari + // Tolerate NodeList properties (IE: "length"; Safari: <number>) matching elements by id + for ( ; i !== len && (elem = elems[i]) != null; i++ ) { + if ( byElement && elem ) { + j = 0; + while ( (matcher = elementMatchers[j++]) ) { + if ( matcher( elem, context, xml ) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + // They will have gone through all possible matchers + if ( (elem = !matcher && elem) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // Apply set filters to unmatched elements + matchedCount += i; + if ( bySet && i !== matchedCount ) { + j = 0; + while ( (matcher = setMatchers[j++]) ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !(unmatched[i] || setMatched[i]) ) { + setMatched[i] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ selector + " " ]; + + if ( !cached ) { + // Generate a function of recursive functions that can be used to check each element + if ( !match ) { + match = tokenize( selector ); + } + i = match.length; + while ( i-- ) { + cached = matcherFromTokens( match[i] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); + + // Save selector and tokenization + cached.selector = selector; + } + return cached; +}; + +/** + * A low-level selection function that works with Sizzle's compiled + * selector functions + * @param {String|Function} selector A selector or a pre-compiled + * selector function built with Sizzle.compile + * @param {Element} context + * @param {Array} [results] + * @param {Array} [seed] A set of elements to match against + */ +select = Sizzle.select = function( selector, context, results, seed ) { + var i, tokens, token, type, find, + compiled = typeof selector === "function" && selector, + match = !seed && tokenize( (selector = compiled.selector || selector) ); + + results = results || []; + + // Try to minimize operations if there is no seed and only one group + if ( match.length === 1 ) { + + // Take a shortcut and set the context if the root selector is an ID + tokens = match[0] = match[0].slice( 0 ); + if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && + support.getById && context.nodeType === 9 && documentIsHTML && + Expr.relative[ tokens[1].type ] ) { + + context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; + if ( !context ) { + return results; + + // Precompiled matchers will still verify ancestry, so step up a level + } else if ( compiled ) { + context = context.parentNode; + } + + selector = selector.slice( tokens.shift().value.length ); + } + + // Fetch a seed set for right-to-left matching + i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; + while ( i-- ) { + token = tokens[i]; + + // Abort if we hit a combinator + if ( Expr.relative[ (type = token.type) ] ) { + break; + } + if ( (find = Expr.find[ type ]) ) { + // Search, expanding context for leading sibling combinators + if ( (seed = find( + token.matches[0].replace( runescape, funescape ), + rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context + )) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && toSelector( tokens ); + if ( !selector ) { + push.apply( results, seed ); + return results; + } + + break; + } + } + } + } + + // Compile and execute a filtering function if one is not provided + // Provide `match` to avoid retokenization if we modified the selector above + ( compiled || compile( selector, match ) )( + seed, + context, + !documentIsHTML, + results, + rsibling.test( selector ) && testContext( context.parentNode ) || context + ); + return results; +}; + +// One-time assignments + +// Sort stability +support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; + +// Support: Chrome 14-35+ +// Always assume duplicates if they aren't passed to the comparison function +support.detectDuplicates = !!hasDuplicate; + +// Initialize against the default document +setDocument(); + +// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) +// Detached nodes confoundingly follow *each other* +support.sortDetached = assert(function( div1 ) { + // Should return 1, but returns 4 (following) + return div1.compareDocumentPosition( document.createElement("div") ) & 1; +}); + +// Support: IE<8 +// Prevent attribute/property "interpolation" +// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !assert(function( div ) { + div.innerHTML = "<a href='#'></a>"; + return div.firstChild.getAttribute("href") === "#" ; +}) ) { + addHandle( "type|href|height|width", function( elem, name, isXML ) { + if ( !isXML ) { + return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); + } + }); +} + +// Support: IE<9 +// Use defaultValue in place of getAttribute("value") +if ( !support.attributes || !assert(function( div ) { + div.innerHTML = "<input/>"; + div.firstChild.setAttribute( "value", "" ); + return div.firstChild.getAttribute( "value" ) === ""; +}) ) { + addHandle( "value", function( elem, name, isXML ) { + if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { + return elem.defaultValue; + } + }); +} + +// Support: IE<9 +// Use getAttributeNode to fetch booleans when getAttribute lies +if ( !assert(function( div ) { + return div.getAttribute("disabled") == null; +}) ) { + addHandle( booleans, function( elem, name, isXML ) { + var val; + if ( !isXML ) { + return elem[ name ] === true ? name.toLowerCase() : + (val = elem.getAttributeNode( name )) && val.specified ? + val.value : + null; + } + }); +} + +return Sizzle; + +})( window ); + + + +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; +jQuery.expr[":"] = jQuery.expr.pseudos; +jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; + + + +var rneedsContext = jQuery.expr.match.needsContext; + +var rsingleTag = (/^<(\w+)\s*\/?>(?:<\/\1>|)$/); + + + +var risSimple = /^.[^:#\[\.,]*$/; + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, not ) { + if ( jQuery.isFunction( qualifier ) ) { + return jQuery.grep( elements, function( elem, i ) { + /* jshint -W018 */ + return !!qualifier.call( elem, i, elem ) !== not; + }); + + } + + if ( qualifier.nodeType ) { + return jQuery.grep( elements, function( elem ) { + return ( elem === qualifier ) !== not; + }); + + } + + if ( typeof qualifier === "string" ) { + if ( risSimple.test( qualifier ) ) { + return jQuery.filter( qualifier, elements, not ); + } + + qualifier = jQuery.filter( qualifier, elements ); + } + + return jQuery.grep( elements, function( elem ) { + return ( indexOf.call( qualifier, elem ) >= 0 ) !== not; + }); +} + +jQuery.filter = function( expr, elems, not ) { + var elem = elems[ 0 ]; + + if ( not ) { + expr = ":not(" + expr + ")"; + } + + return elems.length === 1 && elem.nodeType === 1 ? + jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] : + jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { + return elem.nodeType === 1; + })); +}; + +jQuery.fn.extend({ + find: function( selector ) { + var i, + len = this.length, + ret = [], + self = this; + + if ( typeof selector !== "string" ) { + return this.pushStack( jQuery( selector ).filter(function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + }) ); + } + + for ( i = 0; i < len; i++ ) { + jQuery.find( selector, self[ i ], ret ); + } + + // Needed because $( selector, context ) becomes $( context ).find( selector ) + ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret ); + ret.selector = this.selector ? this.selector + " " + selector : selector; + return ret; + }, + filter: function( selector ) { + return this.pushStack( winnow(this, selector || [], false) ); + }, + not: function( selector ) { + return this.pushStack( winnow(this, selector || [], true) ); + }, + is: function( selector ) { + return !!winnow( + this, + + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + typeof selector === "string" && rneedsContext.test( selector ) ? + jQuery( selector ) : + selector || [], + false + ).length; + } +}); + + +// Initialize a jQuery object + + +// A central reference to the root jQuery(document) +var rootjQuery, + + // A simple way to check for HTML strings + // Prioritize #id over <tag> to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/, + + init = jQuery.fn.init = function( selector, context ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector[0] === "<" && selector[ selector.length - 1 ] === ">" && selector.length >= 3 ) { + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && (match[1] || !context) ) { + + // HANDLE: $(html) -> $(array) + if ( match[1] ) { + context = context instanceof jQuery ? context[0] : context; + + // Option to run scripts is true for back-compat + // Intentionally let the error be thrown if parseHTML is not present + jQuery.merge( this, jQuery.parseHTML( + match[1], + context && context.nodeType ? context.ownerDocument || context : document, + true + ) ); + + // HANDLE: $(html, props) + if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) { + for ( match in context ) { + // Properties of context are called as methods if possible + if ( jQuery.isFunction( this[ match ] ) ) { + this[ match ]( context[ match ] ); + + // ...and otherwise set as attributes + } else { + this.attr( match, context[ match ] ); + } + } + } + + return this; + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[2] ); + + // Support: Blackberry 4.6 + // gEBID returns nodes no longer in the document (#6963) + if ( elem && elem.parentNode ) { + // Inject the element directly into the jQuery object + this.length = 1; + this[0] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || rootjQuery ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(DOMElement) + } else if ( selector.nodeType ) { + this.context = this[0] = selector; + this.length = 1; + return this; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) { + return typeof rootjQuery.ready !== "undefined" ? + rootjQuery.ready( selector ) : + // Execute immediately if ready is not present + selector( jQuery ); + } + + if ( selector.selector !== undefined ) { + this.selector = selector.selector; + this.context = selector.context; + } + + return jQuery.makeArray( selector, this ); + }; + +// Give the init function the jQuery prototype for later instantiation +init.prototype = jQuery.fn; + +// Initialize central reference +rootjQuery = jQuery( document ); + + +var rparentsprev = /^(?:parents|prev(?:Until|All))/, + // Methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.extend({ + dir: function( elem, dir, until ) { + var matched = [], + truncate = until !== undefined; + + while ( (elem = elem[ dir ]) && elem.nodeType !== 9 ) { + if ( elem.nodeType === 1 ) { + if ( truncate && jQuery( elem ).is( until ) ) { + break; + } + matched.push( elem ); + } + } + return matched; + }, + + sibling: function( n, elem ) { + var matched = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + matched.push( n ); + } + } + + return matched; + } +}); + +jQuery.fn.extend({ + has: function( target ) { + var targets = jQuery( target, this ), + l = targets.length; + + return this.filter(function() { + var i = 0; + for ( ; i < l; i++ ) { + if ( jQuery.contains( this, targets[i] ) ) { + return true; + } + } + }); + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + matched = [], + pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ? + jQuery( selectors, context || this.context ) : + 0; + + for ( ; i < l; i++ ) { + for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) { + // Always skip document fragments + if ( cur.nodeType < 11 && (pos ? + pos.index(cur) > -1 : + + // Don't pass non-elements to Sizzle + cur.nodeType === 1 && + jQuery.find.matchesSelector(cur, selectors)) ) { + + matched.push( cur ); + break; + } + } + } + + return this.pushStack( matched.length > 1 ? jQuery.unique( matched ) : matched ); + }, + + // Determine the position of an element within the set + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; + } + + // Index in selector + if ( typeof elem === "string" ) { + return indexOf.call( jQuery( elem ), this[ 0 ] ); + } + + // Locate the position of the desired element + return indexOf.call( this, + + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[ 0 ] : elem + ); + }, + + add: function( selector, context ) { + return this.pushStack( + jQuery.unique( + jQuery.merge( this.get(), jQuery( selector, context ) ) + ) + ); + }, + + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter(selector) + ); + } +}); + +function sibling( cur, dir ) { + while ( (cur = cur[dir]) && cur.nodeType !== 1 ) {} + return cur; +} + +jQuery.each({ + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return jQuery.dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return jQuery.dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return jQuery.dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return jQuery.dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return jQuery.dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return jQuery.dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return jQuery.sibling( elem.firstChild ); + }, + contents: function( elem ) { + return elem.contentDocument || jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var matched = jQuery.map( this, fn, until ); + + if ( name.slice( -5 ) !== "Until" ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + matched = jQuery.filter( selector, matched ); + } + + if ( this.length > 1 ) { + // Remove duplicates + if ( !guaranteedUnique[ name ] ) { + jQuery.unique( matched ); + } + + // Reverse order for parents* and prev-derivatives + if ( rparentsprev.test( name ) ) { + matched.reverse(); + } + } + + return this.pushStack( matched ); + }; +}); +var rnotwhite = (/\S+/g); + + + +// String to Object options format cache +var optionsCache = {}; + +// Convert String-formatted options into Object-formatted ones and store in cache +function createOptions( options ) { + var object = optionsCache[ options ] = {}; + jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) { + object[ flag ] = true; + }); + return object; +} + +/* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( options ) { + + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + ( optionsCache[ options ] || createOptions( options ) ) : + jQuery.extend( {}, options ); + + var // Last fire value (for non-forgettable lists) + memory, + // Flag to know if list was already fired + fired, + // Flag to know if list is currently firing + firing, + // First callback to fire (used internally by add and fireWith) + firingStart, + // End of the loop when firing + firingLength, + // Index of currently firing callback (modified by remove if needed) + firingIndex, + // Actual callback list + list = [], + // Stack of fire calls for repeatable lists + stack = !options.once && [], + // Fire callbacks + fire = function( data ) { + memory = options.memory && data; + fired = true; + firingIndex = firingStart || 0; + firingStart = 0; + firingLength = list.length; + firing = true; + for ( ; list && firingIndex < firingLength; firingIndex++ ) { + if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { + memory = false; // To prevent further calls using add + break; + } + } + firing = false; + if ( list ) { + if ( stack ) { + if ( stack.length ) { + fire( stack.shift() ); + } + } else if ( memory ) { + list = []; + } else { + self.disable(); + } + } + }, + // Actual Callbacks object + self = { + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + // First, we save the current length + var start = list.length; + (function add( args ) { + jQuery.each( args, function( _, arg ) { + var type = jQuery.type( arg ); + if ( type === "function" ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && type !== "string" ) { + // Inspect recursively + add( arg ); + } + }); + })( arguments ); + // Do we need to add the callbacks to the + // current firing batch? + if ( firing ) { + firingLength = list.length; + // With memory, if we're not firing then + // we should call right away + } else if ( memory ) { + firingStart = start; + fire( memory ); + } + } + return this; + }, + // Remove a callback from the list + remove: function() { + if ( list ) { + jQuery.each( arguments, function( _, arg ) { + var index; + while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + // Handle firing indexes + if ( firing ) { + if ( index <= firingLength ) { + firingLength--; + } + if ( index <= firingIndex ) { + firingIndex--; + } + } + } + }); + } + return this; + }, + // Check if a given callback is in the list. + // If no argument is given, return whether or not list has callbacks attached. + has: function( fn ) { + return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length ); + }, + // Remove all callbacks from the list + empty: function() { + list = []; + firingLength = 0; + return this; + }, + // Have the list do nothing anymore + disable: function() { + list = stack = memory = undefined; + return this; + }, + // Is it disabled? + disabled: function() { + return !list; + }, + // Lock the list in its current state + lock: function() { + stack = undefined; + if ( !memory ) { + self.disable(); + } + return this; + }, + // Is it locked? + locked: function() { + return !stack; + }, + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( list && ( !fired || stack ) ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + if ( firing ) { + stack.push( args ); + } else { + fire( args ); + } + } + return this; + }, + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; + + +jQuery.extend({ + + Deferred: function( func ) { + var tuples = [ + // action, add listener, listener list, final state + [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], + [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], + [ "notify", "progress", jQuery.Callbacks("memory") ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + then: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + return jQuery.Deferred(function( newDefer ) { + jQuery.each( tuples, function( i, tuple ) { + var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; + // deferred[ done | fail | progress ] for forwarding actions to newDefer + deferred[ tuple[1] ](function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && jQuery.isFunction( returned.promise ) ) { + returned.promise() + .done( newDefer.resolve ) + .fail( newDefer.reject ) + .progress( newDefer.notify ); + } else { + newDefer[ tuple[ 0 ] + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments ); + } + }); + }); + fns = null; + }).promise(); + }, + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Keep pipe for back-compat + promise.pipe = promise.then; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 3 ]; + + // promise[ done | fail | progress ] = list.add + promise[ tuple[1] ] = list.add; + + // Handle state + if ( stateString ) { + list.add(function() { + // state = [ resolved | rejected ] + state = stateString; + + // [ reject_list | resolve_list ].disable; progress_list.lock + }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); + } + + // deferred[ resolve | reject | notify ] + deferred[ tuple[0] ] = function() { + deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments ); + return this; + }; + deferred[ tuple[0] + "With" ] = list.fireWith; + }); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( subordinate /* , ..., subordinateN */ ) { + var i = 0, + resolveValues = slice.call( arguments ), + length = resolveValues.length, + + // the count of uncompleted subordinates + remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, + + // the master Deferred. If resolveValues consist of only a single Deferred, just use that. + deferred = remaining === 1 ? subordinate : jQuery.Deferred(), + + // Update function for both resolve and progress values + updateFunc = function( i, contexts, values ) { + return function( value ) { + contexts[ i ] = this; + values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; + if ( values === progressValues ) { + deferred.notifyWith( contexts, values ); + } else if ( !( --remaining ) ) { + deferred.resolveWith( contexts, values ); + } + }; + }, + + progressValues, progressContexts, resolveContexts; + + // Add listeners to Deferred subordinates; treat others as resolved + if ( length > 1 ) { + progressValues = new Array( length ); + progressContexts = new Array( length ); + resolveContexts = new Array( length ); + for ( ; i < length; i++ ) { + if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { + resolveValues[ i ].promise() + .done( updateFunc( i, resolveContexts, resolveValues ) ) + .fail( deferred.reject ) + .progress( updateFunc( i, progressContexts, progressValues ) ); + } else { + --remaining; + } + } + } + + // If we're not waiting on anything, resolve the master + if ( !remaining ) { + deferred.resolveWith( resolveContexts, resolveValues ); + } + + return deferred.promise(); + } +}); + + +// The deferred used on DOM ready +var readyList; + +jQuery.fn.ready = function( fn ) { + // Add the callback + jQuery.ready.promise().done( fn ); + + return this; +}; + +jQuery.extend({ + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Hold (or release) the ready event + holdReady: function( hold ) { + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } + }, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + + // Trigger any bound ready events + if ( jQuery.fn.triggerHandler ) { + jQuery( document ).triggerHandler( "ready" ); + jQuery( document ).off( "ready" ); + } + } +}); + +/** + * The ready event handler and self cleanup method + */ +function completed() { + document.removeEventListener( "DOMContentLoaded", completed, false ); + window.removeEventListener( "load", completed, false ); + jQuery.ready(); +} + +jQuery.ready.promise = function( obj ) { + if ( !readyList ) { + + readyList = jQuery.Deferred(); + + // Catch cases where $(document).ready() is called after the browser event has already occurred. + // We once tried to use readyState "interactive" here, but it caused issues like the one + // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 + if ( document.readyState === "complete" ) { + // Handle it asynchronously to allow scripts the opportunity to delay ready + setTimeout( jQuery.ready ); + + } else { + + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed, false ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed, false ); + } + } + return readyList.promise( obj ); +}; + +// Kick off the DOM ready check even if the user does not +jQuery.ready.promise(); + + + + +// Multifunctional method to get and set values of a collection +// The value/s can optionally be executed if it's a function +var access = jQuery.access = function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + len = elems.length, + bulk = key == null; + + // Sets many values + if ( jQuery.type( key ) === "object" ) { + chainable = true; + for ( i in key ) { + jQuery.access( elems, fn, i, key[i], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !jQuery.isFunction( value ) ) { + raw = true; + } + + if ( bulk ) { + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; + + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } + + if ( fn ) { + for ( ; i < len; i++ ) { + fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) ); + } + } + } + + return chainable ? + elems : + + // Gets + bulk ? + fn.call( elems ) : + len ? fn( elems[0], key ) : emptyGet; +}; + + +/** + * Determines whether an object can have data + */ +jQuery.acceptData = function( owner ) { + // Accepts only: + // - Node + // - Node.ELEMENT_NODE + // - Node.DOCUMENT_NODE + // - Object + // - Any + /* jshint -W018 */ + return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); +}; + + +function Data() { + // Support: Android<4, + // Old WebKit does not have Object.preventExtensions/freeze method, + // return new empty object instead with no [[set]] accessor + Object.defineProperty( this.cache = {}, 0, { + get: function() { + return {}; + } + }); + + this.expando = jQuery.expando + Data.uid++; +} + +Data.uid = 1; +Data.accepts = jQuery.acceptData; + +Data.prototype = { + key: function( owner ) { + // We can accept data for non-element nodes in modern browsers, + // but we should not, see #8335. + // Always return the key for a frozen object. + if ( !Data.accepts( owner ) ) { + return 0; + } + + var descriptor = {}, + // Check if the owner object already has a cache key + unlock = owner[ this.expando ]; + + // If not, create one + if ( !unlock ) { + unlock = Data.uid++; + + // Secure it in a non-enumerable, non-writable property + try { + descriptor[ this.expando ] = { value: unlock }; + Object.defineProperties( owner, descriptor ); + + // Support: Android<4 + // Fallback to a less secure definition + } catch ( e ) { + descriptor[ this.expando ] = unlock; + jQuery.extend( owner, descriptor ); + } + } + + // Ensure the cache object + if ( !this.cache[ unlock ] ) { + this.cache[ unlock ] = {}; + } + + return unlock; + }, + set: function( owner, data, value ) { + var prop, + // There may be an unlock assigned to this node, + // if there is no entry for this "owner", create one inline + // and set the unlock as though an owner entry had always existed + unlock = this.key( owner ), + cache = this.cache[ unlock ]; + + // Handle: [ owner, key, value ] args + if ( typeof data === "string" ) { + cache[ data ] = value; + + // Handle: [ owner, { properties } ] args + } else { + // Fresh assignments by object are shallow copied + if ( jQuery.isEmptyObject( cache ) ) { + jQuery.extend( this.cache[ unlock ], data ); + // Otherwise, copy the properties one-by-one to the cache object + } else { + for ( prop in data ) { + cache[ prop ] = data[ prop ]; + } + } + } + return cache; + }, + get: function( owner, key ) { + // Either a valid cache is found, or will be created. + // New caches will be created and the unlock returned, + // allowing direct access to the newly created + // empty data object. A valid owner object must be provided. + var cache = this.cache[ this.key( owner ) ]; + + return key === undefined ? + cache : cache[ key ]; + }, + access: function( owner, key, value ) { + var stored; + // In cases where either: + // + // 1. No key was specified + // 2. A string key was specified, but no value provided + // + // Take the "read" path and allow the get method to determine + // which value to return, respectively either: + // + // 1. The entire cache object + // 2. The data stored at the key + // + if ( key === undefined || + ((key && typeof key === "string") && value === undefined) ) { + + stored = this.get( owner, key ); + + return stored !== undefined ? + stored : this.get( owner, jQuery.camelCase(key) ); + } + + // [*]When the key is not a string, or both a key and value + // are specified, set or extend (existing objects) with either: + // + // 1. An object of properties + // 2. A key and value + // + this.set( owner, key, value ); + + // Since the "set" path can have two possible entry points + // return the expected data based on which path was taken[*] + return value !== undefined ? value : key; + }, + remove: function( owner, key ) { + var i, name, camel, + unlock = this.key( owner ), + cache = this.cache[ unlock ]; + + if ( key === undefined ) { + this.cache[ unlock ] = {}; + + } else { + // Support array or space separated string of keys + if ( jQuery.isArray( key ) ) { + // If "name" is an array of keys... + // When data is initially created, via ("key", "val") signature, + // keys will be converted to camelCase. + // Since there is no way to tell _how_ a key was added, remove + // both plain key and camelCase key. #12786 + // This will only penalize the array argument path. + name = key.concat( key.map( jQuery.camelCase ) ); + } else { + camel = jQuery.camelCase( key ); + // Try the string as a key before any manipulation + if ( key in cache ) { + name = [ key, camel ]; + } else { + // If a key with the spaces exists, use it. + // Otherwise, create an array by matching non-whitespace + name = camel; + name = name in cache ? + [ name ] : ( name.match( rnotwhite ) || [] ); + } + } + + i = name.length; + while ( i-- ) { + delete cache[ name[ i ] ]; + } + } + }, + hasData: function( owner ) { + return !jQuery.isEmptyObject( + this.cache[ owner[ this.expando ] ] || {} + ); + }, + discard: function( owner ) { + if ( owner[ this.expando ] ) { + delete this.cache[ owner[ this.expando ] ]; + } + } +}; +var data_priv = new Data(); + +var data_user = new Data(); + + + +// Implementation Summary +// +// 1. Enforce API surface and semantic compatibility with 1.9.x branch +// 2. Improve the module's maintainability by reducing the storage +// paths to a single mechanism. +// 3. Use the same single mechanism to support "private" and "user" data. +// 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) +// 5. Avoid exposing implementation details on user objects (eg. expando properties) +// 6. Provide a clear path for implementation upgrade to WeakMap in 2014 + +var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, + rmultiDash = /([A-Z])/g; + +function dataAttr( elem, key, data ) { + var name; + + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = data === "true" ? true : + data === "false" ? false : + data === "null" ? null : + // Only convert to a number if it doesn't change the string + +data + "" === data ? +data : + rbrace.test( data ) ? jQuery.parseJSON( data ) : + data; + } catch( e ) {} + + // Make sure we set the data so it isn't changed later + data_user.set( elem, key, data ); + } else { + data = undefined; + } + } + return data; +} + +jQuery.extend({ + hasData: function( elem ) { + return data_user.hasData( elem ) || data_priv.hasData( elem ); + }, + + data: function( elem, name, data ) { + return data_user.access( elem, name, data ); + }, + + removeData: function( elem, name ) { + data_user.remove( elem, name ); + }, + + // TODO: Now that all calls to _data and _removeData have been replaced + // with direct calls to data_priv methods, these can be deprecated. + _data: function( elem, name, data ) { + return data_priv.access( elem, name, data ); + }, + + _removeData: function( elem, name ) { + data_priv.remove( elem, name ); + } +}); + +jQuery.fn.extend({ + data: function( key, value ) { + var i, name, data, + elem = this[ 0 ], + attrs = elem && elem.attributes; + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = data_user.get( elem ); + + if ( elem.nodeType === 1 && !data_priv.get( elem, "hasDataAttrs" ) ) { + i = attrs.length; + while ( i-- ) { + + // Support: IE11+ + // The attrs elements can be null (#14894) + if ( attrs[ i ] ) { + name = attrs[ i ].name; + if ( name.indexOf( "data-" ) === 0 ) { + name = jQuery.camelCase( name.slice(5) ); + dataAttr( elem, name, data[ name ] ); + } + } + } + data_priv.set( elem, "hasDataAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each(function() { + data_user.set( this, key ); + }); + } + + return access( this, function( value ) { + var data, + camelKey = jQuery.camelCase( key ); + + // The calling jQuery object (element matches) is not empty + // (and therefore has an element appears at this[ 0 ]) and the + // `value` parameter was not undefined. An empty jQuery object + // will result in `undefined` for elem = this[ 0 ] which will + // throw an exception if an attempt to read a data cache is made. + if ( elem && value === undefined ) { + // Attempt to get data from the cache + // with the key as-is + data = data_user.get( elem, key ); + if ( data !== undefined ) { + return data; + } + + // Attempt to get data from the cache + // with the key camelized + data = data_user.get( elem, camelKey ); + if ( data !== undefined ) { + return data; + } + + // Attempt to "discover" the data in + // HTML5 custom data-* attrs + data = dataAttr( elem, camelKey, undefined ); + if ( data !== undefined ) { + return data; + } + + // We tried really hard, but the data doesn't exist. + return; + } + + // Set the data... + this.each(function() { + // First, attempt to store a copy or reference of any + // data that might've been store with a camelCased key. + var data = data_user.get( this, camelKey ); + + // For HTML5 data-* attribute interop, we have to + // store property names with dashes in a camelCase form. + // This might not apply to all properties...* + data_user.set( this, camelKey, value ); + + // *... In the case of properties that might _actually_ + // have dashes, we need to also store a copy of that + // unchanged property. + if ( key.indexOf("-") !== -1 && data !== undefined ) { + data_user.set( this, key, value ); + } + }); + }, null, value, arguments.length > 1, null, true ); + }, + + removeData: function( key ) { + return this.each(function() { + data_user.remove( this, key ); + }); + } +}); + + +jQuery.extend({ + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = data_priv.get( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || jQuery.isArray( data ) ) { + queue = data_priv.access( elem, type, jQuery.makeArray(data) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // Clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // Not public - generate a queueHooks object, or return the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return data_priv.get( elem, key ) || data_priv.access( elem, key, { + empty: jQuery.Callbacks("once memory").add(function() { + data_priv.remove( elem, [ type + "queue", key ] ); + }) + }); + } +}); + +jQuery.fn.extend({ + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[0], type ); + } + + return data === undefined ? + this : + this.each(function() { + var queue = jQuery.queue( this, type, data ); + + // Ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[0] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + }); + }, + dequeue: function( type ) { + return this.each(function() { + jQuery.dequeue( this, type ); + }); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while ( i-- ) { + tmp = data_priv.get( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } +}); +var pnum = (/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/).source; + +var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; + +var isHidden = function( elem, el ) { + // isHidden might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; + return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem ); + }; + +var rcheckableType = (/^(?:checkbox|radio)$/i); + + + +(function() { + var fragment = document.createDocumentFragment(), + div = fragment.appendChild( document.createElement( "div" ) ), + input = document.createElement( "input" ); + + // Support: Safari<=5.1 + // Check state lost if the name is set (#11217) + // Support: Windows Web Apps (WWA) + // `name` and `type` must use .setAttribute for WWA (#14901) + input.setAttribute( "type", "radio" ); + input.setAttribute( "checked", "checked" ); + input.setAttribute( "name", "t" ); + + div.appendChild( input ); + + // Support: Safari<=5.1, Android<4.2 + // Older WebKit doesn't clone checked state correctly in fragments + support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Support: IE<=11+ + // Make sure textarea (and checkbox) defaultValue is properly cloned + div.innerHTML = "<textarea>x</textarea>"; + support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; +})(); +var strundefined = typeof undefined; + + + +support.focusinBubbles = "onfocusin" in window; + + +var + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|pointer|contextmenu)|click/, + rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)$/; + +function returnTrue() { + return true; +} + +function returnFalse() { + return false; +} + +function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } +} + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + global: {}, + + add: function( elem, types, handler, data, selector ) { + + var handleObjIn, eventHandle, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = data_priv.get( elem ); + + // Don't attach events to noData or text/comment nodes (but allow plain objects) + if ( !elemData ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + if ( !(events = elemData.events) ) { + events = elemData.events = {}; + } + if ( !(eventHandle = elemData.handle) ) { + eventHandle = elemData.handle = function( e ) { + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== strundefined && jQuery.event.triggered !== e.type ? + jQuery.event.dispatch.apply( elem, arguments ) : undefined; + }; + } + + // Handle multiple events separated by a space + types = ( types || "" ).match( rnotwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[t] ) || []; + type = origType = tmp[1]; + namespaces = ( tmp[2] || "" ).split( "." ).sort(); + + // There *must* be a type, no attaching namespace-only handlers + if ( !type ) { + continue; + } + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend({ + type: type, + origType: origType, + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join(".") + }, handleObjIn ); + + // Init the event handler queue if we're the first + if ( !(handlers = events[ type ]) ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener if the special events handler returns false + if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle, false ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + }, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + + var j, origCount, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = data_priv.hasData( elem ) && data_priv.get( elem ); + + if ( !elemData || !(events = elemData.events) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( rnotwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[t] ) || []; + type = origType = tmp[1]; + namespaces = ( tmp[2] || "" ).split( "." ).sort(); + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ); + + // Remove matching events + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !tmp || tmp.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { + handlers.splice( j, 1 ); + + if ( handleObj.selector ) { + handlers.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( origCount && !handlers.length ) { + if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + delete elemData.handle; + data_priv.remove( elem, "events" ); + } + }, + + trigger: function( event, data, elem, onlyHandlers ) { + + var i, cur, tmp, bubbleType, ontype, handle, special, + eventPath = [ elem || document ], + type = hasOwn.call( event, "type" ) ? event.type : event, + namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : []; + + cur = tmp = elem = elem || document; + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf(".") >= 0 ) { + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split("."); + type = namespaces.shift(); + namespaces.sort(); + } + ontype = type.indexOf(":") < 0 && "on" + type; + + // Caller can pass in a jQuery.Event object, Object, or just an event type string + event = event[ jQuery.expando ] ? + event : + new jQuery.Event( type, typeof event === "object" && event ); + + // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) + event.isTrigger = onlyHandlers ? 2 : 3; + event.namespace = namespaces.join("."); + event.namespace_re = event.namespace ? + new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) : + null; + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data == null ? + [ event ] : + jQuery.makeArray( data, [ event ] ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + if ( !rfocusMorph.test( bubbleType + type ) ) { + cur = cur.parentNode; + } + for ( ; cur; cur = cur.parentNode ) { + eventPath.push( cur ); + tmp = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( tmp === (elem.ownerDocument || document) ) { + eventPath.push( tmp.defaultView || tmp.parentWindow || window ); + } + } + + // Fire handlers on the event path + i = 0; + while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) { + + event.type = i > 1 ? + bubbleType : + special.bindType || type; + + // jQuery handler + handle = ( data_priv.get( cur, "events" ) || {} )[ event.type ] && data_priv.get( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + + // Native handler + handle = ontype && cur[ ontype ]; + if ( handle && handle.apply && jQuery.acceptData( cur ) ) { + event.result = handle.apply( cur, data ); + if ( event.result === false ) { + event.preventDefault(); + } + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) && + jQuery.acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name name as the event. + // Don't do default actions on window, that's where global variables be (#6170) + if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + tmp = elem[ ontype ]; + + if ( tmp ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + elem[ type ](); + jQuery.event.triggered = undefined; + + if ( tmp ) { + elem[ ontype ] = tmp; + } + } + } + } + + return event.result; + }, + + dispatch: function( event ) { + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( event ); + + var i, j, ret, matched, handleObj, + handlerQueue = [], + args = slice.call( arguments ), + handlers = ( data_priv.get( this, "events" ) || {} )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[0] = event; + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); + + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; + + j = 0; + while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) { + + // Triggered event must either 1) have no namespace, or 2) have namespace(s) + // a subset or equal to those in the bound event (both can have no namespace). + if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) { + + event.handleObj = handleObj; + event.data = handleObj.data; + + ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) + .apply( matched.elem, args ); + + if ( ret !== undefined ) { + if ( (event.result = ret) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + handlers: function( event, handlers ) { + var i, matches, sel, handleObj, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Find delegate handlers + // Black-hole SVG <use> instance trees (#13180) + // Avoid non-left-click bubbling in Firefox (#3861) + if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) { + + for ( ; cur !== this; cur = cur.parentNode || this ) { + + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.disabled !== true || event.type !== "click" ) { + matches = []; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matches[ sel ] === undefined ) { + matches[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) >= 0 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matches[ sel ] ) { + matches.push( handleObj ); + } + } + if ( matches.length ) { + handlerQueue.push({ elem: cur, handlers: matches }); + } + } + } + } + + // Add the remaining (directly-bound) handlers + if ( delegateCount < handlers.length ) { + handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) }); + } + + return handlerQueue; + }, + + // Includes some event props shared by KeyEvent and MouseEvent + props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), + + fixHooks: {}, + + keyHooks: { + props: "char charCode key keyCode".split(" "), + filter: function( event, original ) { + + // Add which for key events + if ( event.which == null ) { + event.which = original.charCode != null ? original.charCode : original.keyCode; + } + + return event; + } + }, + + mouseHooks: { + props: "button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement".split(" "), + filter: function( event, original ) { + var eventDoc, doc, body, + button = original.button; + + // Calculate pageX/Y if missing and clientX/Y available + if ( event.pageX == null && original.clientX != null ) { + eventDoc = event.target.ownerDocument || document; + doc = eventDoc.documentElement; + body = eventDoc.body; + + event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); + event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + // Note: button is not normalized, so don't use it + if ( !event.which && button !== undefined ) { + event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); + } + + return event; + } + }, + + fix: function( event ) { + if ( event[ jQuery.expando ] ) { + return event; + } + + // Create a writable copy of the event object and normalize some properties + var i, prop, copy, + type = event.type, + originalEvent = event, + fixHook = this.fixHooks[ type ]; + + if ( !fixHook ) { + this.fixHooks[ type ] = fixHook = + rmouseEvent.test( type ) ? this.mouseHooks : + rkeyEvent.test( type ) ? this.keyHooks : + {}; + } + copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; + + event = new jQuery.Event( originalEvent ); + + i = copy.length; + while ( i-- ) { + prop = copy[ i ]; + event[ prop ] = originalEvent[ prop ]; + } + + // Support: Cordova 2.5 (WebKit) (#13255) + // All events should have a target; Cordova deviceready doesn't + if ( !event.target ) { + event.target = document; + } + + // Support: Safari 6.0+, Chrome<28 + // Target should not be a text node (#504, #13143) + if ( event.target.nodeType === 3 ) { + event.target = event.target.parentNode; + } + + return fixHook.filter ? fixHook.filter( event, originalEvent ) : event; + }, + + special: { + load: { + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + focus: { + // Fire native event if possible so blur/focus sequence is correct + trigger: function() { + if ( this !== safeActiveElement() && this.focus ) { + this.focus(); + return false; + } + }, + delegateType: "focusin" + }, + blur: { + trigger: function() { + if ( this === safeActiveElement() && this.blur ) { + this.blur(); + return false; + } + }, + delegateType: "focusout" + }, + click: { + // For checkbox, fire native event so checked state will be right + trigger: function() { + if ( this.type === "checkbox" && this.click && jQuery.nodeName( this, "input" ) ) { + this.click(); + return false; + } + }, + + // For cross-browser consistency, don't fire native .click() on links + _default: function( event ) { + return jQuery.nodeName( event.target, "a" ); + } + }, + + beforeunload: { + postDispatch: function( event ) { + + // Support: Firefox 20+ + // Firefox doesn't alert if the returnValue field is not set. + if ( event.result !== undefined && event.originalEvent ) { + event.originalEvent.returnValue = event.result; + } + } + } + }, + + simulate: function( type, elem, event, bubble ) { + // Piggyback on a donor event to simulate a different one. + // Fake originalEvent to avoid donor's stopPropagation, but if the + // simulated event prevents default then we do the same on the donor. + var e = jQuery.extend( + new jQuery.Event(), + event, + { + type: type, + isSimulated: true, + originalEvent: {} + } + ); + if ( bubble ) { + jQuery.event.trigger( e, null, elem ); + } else { + jQuery.event.dispatch.call( elem, e ); + } + if ( e.isDefaultPrevented() ) { + event.preventDefault(); + } + } +}; + +jQuery.removeEvent = function( elem, type, handle ) { + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle, false ); + } +}; + +jQuery.Event = function( src, props ) { + // Allow instantiation without the 'new' keyword + if ( !(this instanceof jQuery.Event) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = src.defaultPrevented || + src.defaultPrevented === undefined && + // Support: Android<4.0 + src.returnValue === false ? + returnTrue : + returnFalse; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || jQuery.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + + preventDefault: function() { + var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + + if ( e && e.preventDefault ) { + e.preventDefault(); + } + }, + stopPropagation: function() { + var e = this.originalEvent; + + this.isPropagationStopped = returnTrue; + + if ( e && e.stopPropagation ) { + e.stopPropagation(); + } + }, + stopImmediatePropagation: function() { + var e = this.originalEvent; + + this.isImmediatePropagationStopped = returnTrue; + + if ( e && e.stopImmediatePropagation ) { + e.stopImmediatePropagation(); + } + + this.stopPropagation(); + } +}; + +// Create mouseenter/leave events using mouseover/out and event-time checks +// Support: Chrome 15+ +jQuery.each({ + mouseenter: "mouseover", + mouseleave: "mouseout", + pointerenter: "pointerover", + pointerleave: "pointerout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj; + + // For mousenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || (related !== target && !jQuery.contains( target, related )) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +}); + +// Support: Firefox, Chrome, Safari +// Create "bubbling" focus and blur events +if ( !support.focusinBubbles ) { + jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler on the document while someone wants focusin/focusout + var handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + var doc = this.ownerDocument || this, + attaches = data_priv.access( doc, fix ); + + if ( !attaches ) { + doc.addEventListener( orig, handler, true ); + } + data_priv.access( doc, fix, ( attaches || 0 ) + 1 ); + }, + teardown: function() { + var doc = this.ownerDocument || this, + attaches = data_priv.access( doc, fix ) - 1; + + if ( !attaches ) { + doc.removeEventListener( orig, handler, true ); + data_priv.remove( doc, fix ); + + } else { + data_priv.access( doc, fix, attaches ); + } + } + }; + }); +} + +jQuery.fn.extend({ + + on: function( types, selector, data, fn, /*INTERNAL*/ one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + this.on( type, selector, data, types[ type ], one ); + } + return this; + } + + if ( data == null && fn == null ) { + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return this; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return this.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + }); + }, + one: function( types, selector, data, fn ) { + return this.on( types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each(function() { + jQuery.event.remove( this, types, fn, selector ); + }); + }, + + trigger: function( type, data ) { + return this.each(function() { + jQuery.event.trigger( type, data, this ); + }); + }, + triggerHandler: function( type, data ) { + var elem = this[0]; + if ( elem ) { + return jQuery.event.trigger( type, data, elem, true ); + } + } +}); + + +var + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, + rtagName = /<([\w:]+)/, + rhtml = /<|&#?\w+;/, + rnoInnerhtml = /<(?:script|style|link)/i, + // checked="checked" or checked + rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, + rscriptType = /^$|\/(?:java|ecma)script/i, + rscriptTypeMasked = /^true\/(.*)/, + rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g, + + // We have to close these tags to support XHTML (#13200) + wrapMap = { + + // Support: IE9 + option: [ 1, "<select multiple='multiple'>", "</select>" ], + + thead: [ 1, "<table>", "</table>" ], + col: [ 2, "<table><colgroup>", "</colgroup></table>" ], + tr: [ 2, "<table><tbody>", "</tbody></table>" ], + td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ], + + _default: [ 0, "", "" ] + }; + +// Support: IE9 +wrapMap.optgroup = wrapMap.option; + +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +// Support: 1.x compatibility +// Manipulating tables requires a tbody +function manipulationTarget( elem, content ) { + return jQuery.nodeName( elem, "table" ) && + jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ? + + elem.getElementsByTagName("tbody")[0] || + elem.appendChild( elem.ownerDocument.createElement("tbody") ) : + elem; +} + +// Replace/restore the type attribute of script elements for safe DOM manipulation +function disableScript( elem ) { + elem.type = (elem.getAttribute("type") !== null) + "/" + elem.type; + return elem; +} +function restoreScript( elem ) { + var match = rscriptTypeMasked.exec( elem.type ); + + if ( match ) { + elem.type = match[ 1 ]; + } else { + elem.removeAttribute("type"); + } + + return elem; +} + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + data_priv.set( + elems[ i ], "globalEval", !refElements || data_priv.get( refElements[ i ], "globalEval" ) + ); + } +} + +function cloneCopyEvent( src, dest ) { + var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events; + + if ( dest.nodeType !== 1 ) { + return; + } + + // 1. Copy private data: events, handlers, etc. + if ( data_priv.hasData( src ) ) { + pdataOld = data_priv.access( src ); + pdataCur = data_priv.set( dest, pdataOld ); + events = pdataOld.events; + + if ( events ) { + delete pdataCur.handle; + pdataCur.events = {}; + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + } + + // 2. Copy user data + if ( data_user.hasData( src ) ) { + udataOld = data_user.access( src ); + udataCur = jQuery.extend( {}, udataOld ); + + data_user.set( dest, udataCur ); + } +} + +function getAll( context, tag ) { + var ret = context.getElementsByTagName ? context.getElementsByTagName( tag || "*" ) : + context.querySelectorAll ? context.querySelectorAll( tag || "*" ) : + []; + + return tag === undefined || tag && jQuery.nodeName( context, tag ) ? + jQuery.merge( [ context ], ret ) : + ret; +} + +// Fix IE bugs, see support tests +function fixInput( src, dest ) { + var nodeName = dest.nodeName.toLowerCase(); + + // Fails to persist the checked state of a cloned checkbox or radio button. + if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + dest.checked = src.checked; + + // Fails to return the selected option to the default selected state when cloning options + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } +} + +jQuery.extend({ + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var i, l, srcElements, destElements, + clone = elem.cloneNode( true ), + inPage = jQuery.contains( elem.ownerDocument, elem ); + + // Fix IE cloning issues + if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && + !jQuery.isXMLDoc( elem ) ) { + + // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + fixInput( srcElements[ i ], destElements[ i ] ); + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + if ( deepDataAndEvents ) { + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + cloneCopyEvent( srcElements[ i ], destElements[ i ] ); + } + } else { + cloneCopyEvent( elem, clone ); + } + } + + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); + } + + // Return the cloned set + return clone; + }, + + buildFragment: function( elems, context, scripts, selection ) { + var elem, tmp, tag, wrap, contains, j, + fragment = context.createDocumentFragment(), + nodes = [], + i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + elem = elems[ i ]; + + if ( elem || elem === 0 ) { + + // Add nodes directly + if ( jQuery.type( elem ) === "object" ) { + // Support: QtWebKit, PhantomJS + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); + + // Convert non-html into a text node + } else if ( !rhtml.test( elem ) ) { + nodes.push( context.createTextNode( elem ) ); + + // Convert html into DOM nodes + } else { + tmp = tmp || fragment.appendChild( context.createElement("div") ); + + // Deserialize a standard representation + tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + tmp.innerHTML = wrap[ 1 ] + elem.replace( rxhtmlTag, "<$1></$2>" ) + wrap[ 2 ]; + + // Descend through wrappers to the right content + j = wrap[ 0 ]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Support: QtWebKit, PhantomJS + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, tmp.childNodes ); + + // Remember the top-level container + tmp = fragment.firstChild; + + // Ensure the created nodes are orphaned (#12392) + tmp.textContent = ""; + } + } + } + + // Remove wrapper from fragment + fragment.textContent = ""; + + i = 0; + while ( (elem = nodes[ i++ ]) ) { + + // #4087 - If origin and destination elements are the same, and this is + // that element, do not do anything + if ( selection && jQuery.inArray( elem, selection ) !== -1 ) { + continue; + } + + contains = jQuery.contains( elem.ownerDocument, elem ); + + // Append to fragment + tmp = getAll( fragment.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( contains ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( (elem = tmp[ j++ ]) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + return fragment; + }, + + cleanData: function( elems ) { + var data, elem, type, key, + special = jQuery.event.special, + i = 0; + + for ( ; (elem = elems[ i ]) !== undefined; i++ ) { + if ( jQuery.acceptData( elem ) ) { + key = elem[ data_priv.expando ]; + + if ( key && (data = data_priv.cache[ key ]) ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + if ( data_priv.cache[ key ] ) { + // Discard any remaining `private` data + delete data_priv.cache[ key ]; + } + } + } + // Discard any remaining `user` data + delete data_user.cache[ elem[ data_user.expando ] ]; + } + } +}); + +jQuery.fn.extend({ + text: function( value ) { + return access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().each(function() { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + this.textContent = value; + } + }); + }, null, value, arguments.length ); + }, + + append: function() { + return this.domManip( arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.appendChild( elem ); + } + }); + }, + + prepend: function() { + return this.domManip( arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.insertBefore( elem, target.firstChild ); + } + }); + }, + + before: function() { + return this.domManip( arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + }); + }, + + after: function() { + return this.domManip( arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + }); + }, + + remove: function( selector, keepData /* Internal Use Only */ ) { + var elem, + elems = selector ? jQuery.filter( selector, this ) : this, + i = 0; + + for ( ; (elem = elems[i]) != null; i++ ) { + if ( !keepData && elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem ) ); + } + + if ( elem.parentNode ) { + if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) { + setGlobalEval( getAll( elem, "script" ) ); + } + elem.parentNode.removeChild( elem ); + } + } + + return this; + }, + + empty: function() { + var elem, + i = 0; + + for ( ; (elem = this[i]) != null; i++ ) { + if ( elem.nodeType === 1 ) { + + // Prevent memory leaks + jQuery.cleanData( getAll( elem, false ) ); + + // Remove any remaining nodes + elem.textContent = ""; + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map(function() { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + }); + }, + + html: function( value ) { + return access( this, function( value ) { + var elem = this[ 0 ] || {}, + i = 0, + l = this.length; + + if ( value === undefined && elem.nodeType === 1 ) { + return elem.innerHTML; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { + + value = value.replace( rxhtmlTag, "<$1></$2>" ); + + try { + for ( ; i < l; i++ ) { + elem = this[ i ] || {}; + + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch( e ) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function() { + var arg = arguments[ 0 ]; + + // Make the changes, replacing each context element with the new content + this.domManip( arguments, function( elem ) { + arg = this.parentNode; + + jQuery.cleanData( getAll( this ) ); + + if ( arg ) { + arg.replaceChild( elem, this ); + } + }); + + // Force removal if there was no new content (e.g., from empty arguments) + return arg && (arg.length || arg.nodeType) ? this : this.remove(); + }, + + detach: function( selector ) { + return this.remove( selector, true ); + }, + + domManip: function( args, callback ) { + + // Flatten any nested arrays + args = concat.apply( [], args ); + + var fragment, first, scripts, hasScripts, node, doc, + i = 0, + l = this.length, + set = this, + iNoClone = l - 1, + value = args[ 0 ], + isFunction = jQuery.isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( isFunction || + ( l > 1 && typeof value === "string" && + !support.checkClone && rchecked.test( value ) ) ) { + return this.each(function( index ) { + var self = set.eq( index ); + if ( isFunction ) { + args[ 0 ] = value.call( this, index, self.html() ); + } + self.domManip( args, callback ); + }); + } + + if ( l ) { + fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + if ( first ) { + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; + + // Use the original fragment for the last item instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + for ( ; i < l; i++ ) { + node = fragment; + + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + // Support: QtWebKit + // jQuery.merge because push.apply(_, arraylike) throws + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( this[ i ], node, i ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Reenable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !data_priv.access( node, "globalEval" ) && jQuery.contains( doc, node ) ) { + + if ( node.src ) { + // Optional AJAX dependency, but won't run scripts if not present + if ( jQuery._evalUrl ) { + jQuery._evalUrl( node.src ); + } + } else { + jQuery.globalEval( node.textContent.replace( rcleanScript, "" ) ); + } + } + } + } + } + } + + return this; + } +}); + +jQuery.each({ + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1, + i = 0; + + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone( true ); + jQuery( insert[ i ] )[ original ]( elems ); + + // Support: QtWebKit + // .get() because push.apply(_, arraylike) throws + push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +}); + + +var iframe, + elemdisplay = {}; + +/** + * Retrieve the actual display of a element + * @param {String} name nodeName of the element + * @param {Object} doc Document object + */ +// Called only from within defaultDisplay +function actualDisplay( name, doc ) { + var style, + elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ), + + // getDefaultComputedStyle might be reliably used only on attached element + display = window.getDefaultComputedStyle && ( style = window.getDefaultComputedStyle( elem[ 0 ] ) ) ? + + // Use of this method is a temporary fix (more like optimization) until something better comes along, + // since it was removed from specification and supported only in FF + style.display : jQuery.css( elem[ 0 ], "display" ); + + // We don't have any data stored on the element, + // so use "detach" method as fast way to get rid of the element + elem.detach(); + + return display; +} + +/** + * Try to determine the default display value of an element + * @param {String} nodeName + */ +function defaultDisplay( nodeName ) { + var doc = document, + display = elemdisplay[ nodeName ]; + + if ( !display ) { + display = actualDisplay( nodeName, doc ); + + // If the simple way fails, read from inside an iframe + if ( display === "none" || !display ) { + + // Use the already-created iframe if possible + iframe = (iframe || jQuery( "<iframe frameborder='0' width='0' height='0'/>" )).appendTo( doc.documentElement ); + + // Always write a new HTML skeleton so Webkit and Firefox don't choke on reuse + doc = iframe[ 0 ].contentDocument; + + // Support: IE + doc.write(); + doc.close(); + + display = actualDisplay( nodeName, doc ); + iframe.detach(); + } + + // Store the correct default display + elemdisplay[ nodeName ] = display; + } + + return display; +} +var rmargin = (/^margin/); + +var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" ); + +var getStyles = function( elem ) { + // Support: IE<=11+, Firefox<=30+ (#15098, #14150) + // IE throws on elements created in popups + // FF meanwhile throws on frame elements through "defaultView.getComputedStyle" + if ( elem.ownerDocument.defaultView.opener ) { + return elem.ownerDocument.defaultView.getComputedStyle( elem, null ); + } + + return window.getComputedStyle( elem, null ); + }; + + + +function curCSS( elem, name, computed ) { + var width, minWidth, maxWidth, ret, + style = elem.style; + + computed = computed || getStyles( elem ); + + // Support: IE9 + // getPropertyValue is only needed for .css('filter') (#12537) + if ( computed ) { + ret = computed.getPropertyValue( name ) || computed[ name ]; + } + + if ( computed ) { + + if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) { + ret = jQuery.style( elem, name ); + } + + // Support: iOS < 6 + // A tribute to the "awesome hack by Dean Edwards" + // iOS < 6 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels + // this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values + if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) { + + // Remember the original values + width = style.width; + minWidth = style.minWidth; + maxWidth = style.maxWidth; + + // Put in the new values to get a computed value out + style.minWidth = style.maxWidth = style.width = ret; + ret = computed.width; + + // Revert the changed values + style.width = width; + style.minWidth = minWidth; + style.maxWidth = maxWidth; + } + } + + return ret !== undefined ? + // Support: IE + // IE returns zIndex value as an integer. + ret + "" : + ret; +} + + +function addGetHookIf( conditionFn, hookFn ) { + // Define the hook, we'll check on the first run if it's really needed. + return { + get: function() { + if ( conditionFn() ) { + // Hook not needed (or it's not possible to use it due + // to missing dependency), remove it. + delete this.get; + return; + } + + // Hook needed; redefine it so that the support test is not executed again. + return (this.get = hookFn).apply( this, arguments ); + } + }; +} + + +(function() { + var pixelPositionVal, boxSizingReliableVal, + docElem = document.documentElement, + container = document.createElement( "div" ), + div = document.createElement( "div" ); + + if ( !div.style ) { + return; + } + + // Support: IE9-11+ + // Style of cloned element affects source element cloned (#8908) + div.style.backgroundClip = "content-box"; + div.cloneNode( true ).style.backgroundClip = ""; + support.clearCloneStyle = div.style.backgroundClip === "content-box"; + + container.style.cssText = "border:0;width:0;height:0;top:0;left:-9999px;margin-top:1px;" + + "position:absolute"; + container.appendChild( div ); + + // Executing both pixelPosition & boxSizingReliable tests require only one layout + // so they're executed at the same time to save the second computation. + function computePixelPositionAndBoxSizingReliable() { + div.style.cssText = + // Support: Firefox<29, Android 2.3 + // Vendor-prefix box-sizing + "-webkit-box-sizing:border-box;-moz-box-sizing:border-box;" + + "box-sizing:border-box;display:block;margin-top:1%;top:1%;" + + "border:1px;padding:1px;width:4px;position:absolute"; + div.innerHTML = ""; + docElem.appendChild( container ); + + var divStyle = window.getComputedStyle( div, null ); + pixelPositionVal = divStyle.top !== "1%"; + boxSizingReliableVal = divStyle.width === "4px"; + + docElem.removeChild( container ); + } + + // Support: node.js jsdom + // Don't assume that getComputedStyle is a property of the global object + if ( window.getComputedStyle ) { + jQuery.extend( support, { + pixelPosition: function() { + + // This test is executed only once but we still do memoizing + // since we can use the boxSizingReliable pre-computing. + // No need to check if the test was already performed, though. + computePixelPositionAndBoxSizingReliable(); + return pixelPositionVal; + }, + boxSizingReliable: function() { + if ( boxSizingReliableVal == null ) { + computePixelPositionAndBoxSizingReliable(); + } + return boxSizingReliableVal; + }, + reliableMarginRight: function() { + + // Support: Android 2.3 + // Check if div with explicit width and no margin-right incorrectly + // gets computed margin-right based on width of container. (#3333) + // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right + // This support function is only executed once so no memoizing is needed. + var ret, + marginDiv = div.appendChild( document.createElement( "div" ) ); + + // Reset CSS: box-sizing; display; margin; border; padding + marginDiv.style.cssText = div.style.cssText = + // Support: Firefox<29, Android 2.3 + // Vendor-prefix box-sizing + "-webkit-box-sizing:content-box;-moz-box-sizing:content-box;" + + "box-sizing:content-box;display:block;margin:0;border:0;padding:0"; + marginDiv.style.marginRight = marginDiv.style.width = "0"; + div.style.width = "1px"; + docElem.appendChild( container ); + + ret = !parseFloat( window.getComputedStyle( marginDiv, null ).marginRight ); + + docElem.removeChild( container ); + div.removeChild( marginDiv ); + + return ret; + } + }); + } +})(); + + +// A method for quickly swapping in/out CSS properties to get correct calculations. +jQuery.swap = function( elem, options, callback, args ) { + var ret, name, + old = {}; + + // Remember the old values, and insert the new ones + for ( name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + ret = callback.apply( elem, args || [] ); + + // Revert the old values + for ( name in options ) { + elem.style[ name ] = old[ name ]; + } + + return ret; +}; + + +var + // Swappable if display is none or starts with table except "table", "table-cell", or "table-caption" + // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display + rdisplayswap = /^(none|table(?!-c[ea]).+)/, + rnumsplit = new RegExp( "^(" + pnum + ")(.*)$", "i" ), + rrelNum = new RegExp( "^([+-])=(" + pnum + ")", "i" ), + + cssShow = { position: "absolute", visibility: "hidden", display: "block" }, + cssNormalTransform = { + letterSpacing: "0", + fontWeight: "400" + }, + + cssPrefixes = [ "Webkit", "O", "Moz", "ms" ]; + +// Return a css property mapped to a potentially vendor prefixed property +function vendorPropName( style, name ) { + + // Shortcut for names that are not vendor prefixed + if ( name in style ) { + return name; + } + + // Check for vendor prefixed names + var capName = name[0].toUpperCase() + name.slice(1), + origName = name, + i = cssPrefixes.length; + + while ( i-- ) { + name = cssPrefixes[ i ] + capName; + if ( name in style ) { + return name; + } + } + + return origName; +} + +function setPositiveNumber( elem, value, subtract ) { + var matches = rnumsplit.exec( value ); + return matches ? + // Guard against undefined "subtract", e.g., when used as in cssHooks + Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) : + value; +} + +function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) { + var i = extra === ( isBorderBox ? "border" : "content" ) ? + // If we already have the right measurement, avoid augmentation + 4 : + // Otherwise initialize for horizontal or vertical properties + name === "width" ? 1 : 0, + + val = 0; + + for ( ; i < 4; i += 2 ) { + // Both box models exclude margin, so add it if we want it + if ( extra === "margin" ) { + val += jQuery.css( elem, extra + cssExpand[ i ], true, styles ); + } + + if ( isBorderBox ) { + // border-box includes padding, so remove it if we want content + if ( extra === "content" ) { + val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + } + + // At this point, extra isn't border nor margin, so remove border + if ( extra !== "margin" ) { + val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } else { + // At this point, extra isn't content, so add padding + val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + + // At this point, extra isn't content nor padding, so add border + if ( extra !== "padding" ) { + val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } + } + + return val; +} + +function getWidthOrHeight( elem, name, extra ) { + + // Start with offset property, which is equivalent to the border-box value + var valueIsBorderBox = true, + val = name === "width" ? elem.offsetWidth : elem.offsetHeight, + styles = getStyles( elem ), + isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; + + // Some non-html elements return undefined for offsetWidth, so check for null/undefined + // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285 + // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668 + if ( val <= 0 || val == null ) { + // Fall back to computed then uncomputed css if necessary + val = curCSS( elem, name, styles ); + if ( val < 0 || val == null ) { + val = elem.style[ name ]; + } + + // Computed unit is not pixels. Stop here and return. + if ( rnumnonpx.test(val) ) { + return val; + } + + // Check for style in case a browser which returns unreliable values + // for getComputedStyle silently falls back to the reliable elem.style + valueIsBorderBox = isBorderBox && + ( support.boxSizingReliable() || val === elem.style[ name ] ); + + // Normalize "", auto, and prepare for extra + val = parseFloat( val ) || 0; + } + + // Use the active box-sizing model to add/subtract irrelevant styles + return ( val + + augmentWidthOrHeight( + elem, + name, + extra || ( isBorderBox ? "border" : "content" ), + valueIsBorderBox, + styles + ) + ) + "px"; +} + +function showHide( elements, show ) { + var display, elem, hidden, + values = [], + index = 0, + length = elements.length; + + for ( ; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + + values[ index ] = data_priv.get( elem, "olddisplay" ); + display = elem.style.display; + if ( show ) { + // Reset the inline display of this element to learn if it is + // being hidden by cascaded rules or not + if ( !values[ index ] && display === "none" ) { + elem.style.display = ""; + } + + // Set elements which have been overridden with display: none + // in a stylesheet to whatever the default browser style is + // for such an element + if ( elem.style.display === "" && isHidden( elem ) ) { + values[ index ] = data_priv.access( elem, "olddisplay", defaultDisplay(elem.nodeName) ); + } + } else { + hidden = isHidden( elem ); + + if ( display !== "none" || !hidden ) { + data_priv.set( elem, "olddisplay", hidden ? display : jQuery.css( elem, "display" ) ); + } + } + } + + // Set the display of most of the elements in a second loop + // to avoid the constant reflow + for ( index = 0; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + if ( !show || elem.style.display === "none" || elem.style.display === "" ) { + elem.style.display = show ? values[ index ] || "" : "none"; + } + } + + return elements; +} + +jQuery.extend({ + + // Add in style property hooks for overriding the default + // behavior of getting and setting a style property + cssHooks: { + opacity: { + get: function( elem, computed ) { + if ( computed ) { + + // We should always get a number back from opacity + var ret = curCSS( elem, "opacity" ); + return ret === "" ? "1" : ret; + } + } + } + }, + + // Don't automatically add "px" to these possibly-unitless properties + cssNumber: { + "columnCount": true, + "fillOpacity": true, + "flexGrow": true, + "flexShrink": true, + "fontWeight": true, + "lineHeight": true, + "opacity": true, + "order": true, + "orphans": true, + "widows": true, + "zIndex": true, + "zoom": true + }, + + // Add in properties whose names you wish to fix before + // setting or getting the value + cssProps: { + "float": "cssFloat" + }, + + // Get and set the style property on a DOM Node + style: function( elem, name, value, extra ) { + + // Don't set styles on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { + return; + } + + // Make sure that we're working with the right name + var ret, type, hooks, + origName = jQuery.camelCase( name ), + style = elem.style; + + name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) ); + + // Gets hook for the prefixed version, then unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // Check if we're setting a value + if ( value !== undefined ) { + type = typeof value; + + // Convert "+=" or "-=" to relative numbers (#7345) + if ( type === "string" && (ret = rrelNum.exec( value )) ) { + value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) ); + // Fixes bug #9237 + type = "number"; + } + + // Make sure that null and NaN values aren't set (#7116) + if ( value == null || value !== value ) { + return; + } + + // If a number, add 'px' to the (except for certain CSS properties) + if ( type === "number" && !jQuery.cssNumber[ origName ] ) { + value += "px"; + } + + // Support: IE9-11+ + // background-* props affect original clone's values + if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) { + style[ name ] = "inherit"; + } + + // If a hook was provided, use that value, otherwise just set the specified value + if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) { + style[ name ] = value; + } + + } else { + // If a hook was provided get the non-computed value from there + if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) { + return ret; + } + + // Otherwise just get the value from the style object + return style[ name ]; + } + }, + + css: function( elem, name, extra, styles ) { + var val, num, hooks, + origName = jQuery.camelCase( name ); + + // Make sure that we're working with the right name + name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) ); + + // Try prefixed name followed by the unprefixed name + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // If a hook was provided get the computed value from there + if ( hooks && "get" in hooks ) { + val = hooks.get( elem, true, extra ); + } + + // Otherwise, if a way to get the computed value exists, use that + if ( val === undefined ) { + val = curCSS( elem, name, styles ); + } + + // Convert "normal" to computed value + if ( val === "normal" && name in cssNormalTransform ) { + val = cssNormalTransform[ name ]; + } + + // Make numeric if forced or a qualifier was provided and val looks numeric + if ( extra === "" || extra ) { + num = parseFloat( val ); + return extra === true || jQuery.isNumeric( num ) ? num || 0 : val; + } + return val; + } +}); + +jQuery.each([ "height", "width" ], function( i, name ) { + jQuery.cssHooks[ name ] = { + get: function( elem, computed, extra ) { + if ( computed ) { + + // Certain elements can have dimension info if we invisibly show them + // but it must have a current display style that would benefit + return rdisplayswap.test( jQuery.css( elem, "display" ) ) && elem.offsetWidth === 0 ? + jQuery.swap( elem, cssShow, function() { + return getWidthOrHeight( elem, name, extra ); + }) : + getWidthOrHeight( elem, name, extra ); + } + }, + + set: function( elem, value, extra ) { + var styles = extra && getStyles( elem ); + return setPositiveNumber( elem, value, extra ? + augmentWidthOrHeight( + elem, + name, + extra, + jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + styles + ) : 0 + ); + } + }; +}); + +// Support: Android 2.3 +jQuery.cssHooks.marginRight = addGetHookIf( support.reliableMarginRight, + function( elem, computed ) { + if ( computed ) { + return jQuery.swap( elem, { "display": "inline-block" }, + curCSS, [ elem, "marginRight" ] ); + } + } +); + +// These hooks are used by animate to expand properties +jQuery.each({ + margin: "", + padding: "", + border: "Width" +}, function( prefix, suffix ) { + jQuery.cssHooks[ prefix + suffix ] = { + expand: function( value ) { + var i = 0, + expanded = {}, + + // Assumes a single number if not a string + parts = typeof value === "string" ? value.split(" ") : [ value ]; + + for ( ; i < 4; i++ ) { + expanded[ prefix + cssExpand[ i ] + suffix ] = + parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; + } + + return expanded; + } + }; + + if ( !rmargin.test( prefix ) ) { + jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber; + } +}); + +jQuery.fn.extend({ + css: function( name, value ) { + return access( this, function( elem, name, value ) { + var styles, len, + map = {}, + i = 0; + + if ( jQuery.isArray( name ) ) { + styles = getStyles( elem ); + len = name.length; + + for ( ; i < len; i++ ) { + map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); + } + + return map; + } + + return value !== undefined ? + jQuery.style( elem, name, value ) : + jQuery.css( elem, name ); + }, name, value, arguments.length > 1 ); + }, + show: function() { + return showHide( this, true ); + }, + hide: function() { + return showHide( this ); + }, + toggle: function( state ) { + if ( typeof state === "boolean" ) { + return state ? this.show() : this.hide(); + } + + return this.each(function() { + if ( isHidden( this ) ) { + jQuery( this ).show(); + } else { + jQuery( this ).hide(); + } + }); + } +}); + + +function Tween( elem, options, prop, end, easing ) { + return new Tween.prototype.init( elem, options, prop, end, easing ); +} +jQuery.Tween = Tween; + +Tween.prototype = { + constructor: Tween, + init: function( elem, options, prop, end, easing, unit ) { + this.elem = elem; + this.prop = prop; + this.easing = easing || "swing"; + this.options = options; + this.start = this.now = this.cur(); + this.end = end; + this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); + }, + cur: function() { + var hooks = Tween.propHooks[ this.prop ]; + + return hooks && hooks.get ? + hooks.get( this ) : + Tween.propHooks._default.get( this ); + }, + run: function( percent ) { + var eased, + hooks = Tween.propHooks[ this.prop ]; + + if ( this.options.duration ) { + this.pos = eased = jQuery.easing[ this.easing ]( + percent, this.options.duration * percent, 0, 1, this.options.duration + ); + } else { + this.pos = eased = percent; + } + this.now = ( this.end - this.start ) * eased + this.start; + + if ( this.options.step ) { + this.options.step.call( this.elem, this.now, this ); + } + + if ( hooks && hooks.set ) { + hooks.set( this ); + } else { + Tween.propHooks._default.set( this ); + } + return this; + } +}; + +Tween.prototype.init.prototype = Tween.prototype; + +Tween.propHooks = { + _default: { + get: function( tween ) { + var result; + + if ( tween.elem[ tween.prop ] != null && + (!tween.elem.style || tween.elem.style[ tween.prop ] == null) ) { + return tween.elem[ tween.prop ]; + } + + // Passing an empty string as a 3rd parameter to .css will automatically + // attempt a parseFloat and fallback to a string if the parse fails. + // Simple values such as "10px" are parsed to Float; + // complex values such as "rotate(1rad)" are returned as-is. + result = jQuery.css( tween.elem, tween.prop, "" ); + // Empty strings, null, undefined and "auto" are converted to 0. + return !result || result === "auto" ? 0 : result; + }, + set: function( tween ) { + // Use step hook for back compat. + // Use cssHook if its there. + // Use .style if available and use plain properties where available. + if ( jQuery.fx.step[ tween.prop ] ) { + jQuery.fx.step[ tween.prop ]( tween ); + } else if ( tween.elem.style && ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || jQuery.cssHooks[ tween.prop ] ) ) { + jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); + } else { + tween.elem[ tween.prop ] = tween.now; + } + } + } +}; + +// Support: IE9 +// Panic based approach to setting things on disconnected nodes +Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { + set: function( tween ) { + if ( tween.elem.nodeType && tween.elem.parentNode ) { + tween.elem[ tween.prop ] = tween.now; + } + } +}; + +jQuery.easing = { + linear: function( p ) { + return p; + }, + swing: function( p ) { + return 0.5 - Math.cos( p * Math.PI ) / 2; + } +}; + +jQuery.fx = Tween.prototype.init; + +// Back Compat <1.8 extension point +jQuery.fx.step = {}; + + + + +var + fxNow, timerId, + rfxtypes = /^(?:toggle|show|hide)$/, + rfxnum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ), + rrun = /queueHooks$/, + animationPrefilters = [ defaultPrefilter ], + tweeners = { + "*": [ function( prop, value ) { + var tween = this.createTween( prop, value ), + target = tween.cur(), + parts = rfxnum.exec( value ), + unit = parts && parts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), + + // Starting value computation is required for potential unit mismatches + start = ( jQuery.cssNumber[ prop ] || unit !== "px" && +target ) && + rfxnum.exec( jQuery.css( tween.elem, prop ) ), + scale = 1, + maxIterations = 20; + + if ( start && start[ 3 ] !== unit ) { + // Trust units reported by jQuery.css + unit = unit || start[ 3 ]; + + // Make sure we update the tween properties later on + parts = parts || []; + + // Iteratively approximate from a nonzero starting point + start = +target || 1; + + do { + // If previous iteration zeroed out, double until we get *something*. + // Use string for doubling so we don't accidentally see scale as unchanged below + scale = scale || ".5"; + + // Adjust and apply + start = start / scale; + jQuery.style( tween.elem, prop, start + unit ); + + // Update scale, tolerating zero or NaN from tween.cur(), + // break the loop if scale is unchanged or perfect, or if we've just had enough + } while ( scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations ); + } + + // Update tween properties + if ( parts ) { + start = tween.start = +start || +target || 0; + tween.unit = unit; + // If a +=/-= token was provided, we're doing a relative animation + tween.end = parts[ 1 ] ? + start + ( parts[ 1 ] + 1 ) * parts[ 2 ] : + +parts[ 2 ]; + } + + return tween; + } ] + }; + +// Animations created synchronously will run synchronously +function createFxNow() { + setTimeout(function() { + fxNow = undefined; + }); + return ( fxNow = jQuery.now() ); +} + +// Generate parameters to create a standard animation +function genFx( type, includeWidth ) { + var which, + i = 0, + attrs = { height: type }; + + // If we include width, step value is 1 to do all cssExpand values, + // otherwise step value is 2 to skip over Left and Right + includeWidth = includeWidth ? 1 : 0; + for ( ; i < 4 ; i += 2 - includeWidth ) { + which = cssExpand[ i ]; + attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; + } + + if ( includeWidth ) { + attrs.opacity = attrs.width = type; + } + + return attrs; +} + +function createTween( value, prop, animation ) { + var tween, + collection = ( tweeners[ prop ] || [] ).concat( tweeners[ "*" ] ), + index = 0, + length = collection.length; + for ( ; index < length; index++ ) { + if ( (tween = collection[ index ].call( animation, prop, value )) ) { + + // We're done with this property + return tween; + } + } +} + +function defaultPrefilter( elem, props, opts ) { + /* jshint validthis: true */ + var prop, value, toggle, tween, hooks, oldfire, display, checkDisplay, + anim = this, + orig = {}, + style = elem.style, + hidden = elem.nodeType && isHidden( elem ), + dataShow = data_priv.get( elem, "fxshow" ); + + // Handle queue: false promises + if ( !opts.queue ) { + hooks = jQuery._queueHooks( elem, "fx" ); + if ( hooks.unqueued == null ) { + hooks.unqueued = 0; + oldfire = hooks.empty.fire; + hooks.empty.fire = function() { + if ( !hooks.unqueued ) { + oldfire(); + } + }; + } + hooks.unqueued++; + + anim.always(function() { + // Ensure the complete handler is called before this completes + anim.always(function() { + hooks.unqueued--; + if ( !jQuery.queue( elem, "fx" ).length ) { + hooks.empty.fire(); + } + }); + }); + } + + // Height/width overflow pass + if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) { + // Make sure that nothing sneaks out + // Record all 3 overflow attributes because IE9-10 do not + // change the overflow attribute when overflowX and + // overflowY are set to the same value + opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; + + // Set display property to inline-block for height/width + // animations on inline elements that are having width/height animated + display = jQuery.css( elem, "display" ); + + // Test default display if display is currently "none" + checkDisplay = display === "none" ? + data_priv.get( elem, "olddisplay" ) || defaultDisplay( elem.nodeName ) : display; + + if ( checkDisplay === "inline" && jQuery.css( elem, "float" ) === "none" ) { + style.display = "inline-block"; + } + } + + if ( opts.overflow ) { + style.overflow = "hidden"; + anim.always(function() { + style.overflow = opts.overflow[ 0 ]; + style.overflowX = opts.overflow[ 1 ]; + style.overflowY = opts.overflow[ 2 ]; + }); + } + + // show/hide pass + for ( prop in props ) { + value = props[ prop ]; + if ( rfxtypes.exec( value ) ) { + delete props[ prop ]; + toggle = toggle || value === "toggle"; + if ( value === ( hidden ? "hide" : "show" ) ) { + + // If there is dataShow left over from a stopped hide or show and we are going to proceed with show, we should pretend to be hidden + if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) { + hidden = true; + } else { + continue; + } + } + orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop ); + + // Any non-fx value stops us from restoring the original display value + } else { + display = undefined; + } + } + + if ( !jQuery.isEmptyObject( orig ) ) { + if ( dataShow ) { + if ( "hidden" in dataShow ) { + hidden = dataShow.hidden; + } + } else { + dataShow = data_priv.access( elem, "fxshow", {} ); + } + + // Store state if its toggle - enables .stop().toggle() to "reverse" + if ( toggle ) { + dataShow.hidden = !hidden; + } + if ( hidden ) { + jQuery( elem ).show(); + } else { + anim.done(function() { + jQuery( elem ).hide(); + }); + } + anim.done(function() { + var prop; + + data_priv.remove( elem, "fxshow" ); + for ( prop in orig ) { + jQuery.style( elem, prop, orig[ prop ] ); + } + }); + for ( prop in orig ) { + tween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim ); + + if ( !( prop in dataShow ) ) { + dataShow[ prop ] = tween.start; + if ( hidden ) { + tween.end = tween.start; + tween.start = prop === "width" || prop === "height" ? 1 : 0; + } + } + } + + // If this is a noop like .hide().hide(), restore an overwritten display value + } else if ( (display === "none" ? defaultDisplay( elem.nodeName ) : display) === "inline" ) { + style.display = display; + } +} + +function propFilter( props, specialEasing ) { + var index, name, easing, value, hooks; + + // camelCase, specialEasing and expand cssHook pass + for ( index in props ) { + name = jQuery.camelCase( index ); + easing = specialEasing[ name ]; + value = props[ index ]; + if ( jQuery.isArray( value ) ) { + easing = value[ 1 ]; + value = props[ index ] = value[ 0 ]; + } + + if ( index !== name ) { + props[ name ] = value; + delete props[ index ]; + } + + hooks = jQuery.cssHooks[ name ]; + if ( hooks && "expand" in hooks ) { + value = hooks.expand( value ); + delete props[ name ]; + + // Not quite $.extend, this won't overwrite existing keys. + // Reusing 'index' because we have the correct "name" + for ( index in value ) { + if ( !( index in props ) ) { + props[ index ] = value[ index ]; + specialEasing[ index ] = easing; + } + } + } else { + specialEasing[ name ] = easing; + } + } +} + +function Animation( elem, properties, options ) { + var result, + stopped, + index = 0, + length = animationPrefilters.length, + deferred = jQuery.Deferred().always( function() { + // Don't match elem in the :animated selector + delete tick.elem; + }), + tick = function() { + if ( stopped ) { + return false; + } + var currentTime = fxNow || createFxNow(), + remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), + // Support: Android 2.3 + // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497) + temp = remaining / animation.duration || 0, + percent = 1 - temp, + index = 0, + length = animation.tweens.length; + + for ( ; index < length ; index++ ) { + animation.tweens[ index ].run( percent ); + } + + deferred.notifyWith( elem, [ animation, percent, remaining ]); + + if ( percent < 1 && length ) { + return remaining; + } else { + deferred.resolveWith( elem, [ animation ] ); + return false; + } + }, + animation = deferred.promise({ + elem: elem, + props: jQuery.extend( {}, properties ), + opts: jQuery.extend( true, { specialEasing: {} }, options ), + originalProperties: properties, + originalOptions: options, + startTime: fxNow || createFxNow(), + duration: options.duration, + tweens: [], + createTween: function( prop, end ) { + var tween = jQuery.Tween( elem, animation.opts, prop, end, + animation.opts.specialEasing[ prop ] || animation.opts.easing ); + animation.tweens.push( tween ); + return tween; + }, + stop: function( gotoEnd ) { + var index = 0, + // If we are going to the end, we want to run all the tweens + // otherwise we skip this part + length = gotoEnd ? animation.tweens.length : 0; + if ( stopped ) { + return this; + } + stopped = true; + for ( ; index < length ; index++ ) { + animation.tweens[ index ].run( 1 ); + } + + // Resolve when we played the last frame; otherwise, reject + if ( gotoEnd ) { + deferred.resolveWith( elem, [ animation, gotoEnd ] ); + } else { + deferred.rejectWith( elem, [ animation, gotoEnd ] ); + } + return this; + } + }), + props = animation.props; + + propFilter( props, animation.opts.specialEasing ); + + for ( ; index < length ; index++ ) { + result = animationPrefilters[ index ].call( animation, elem, props, animation.opts ); + if ( result ) { + return result; + } + } + + jQuery.map( props, createTween, animation ); + + if ( jQuery.isFunction( animation.opts.start ) ) { + animation.opts.start.call( elem, animation ); + } + + jQuery.fx.timer( + jQuery.extend( tick, { + elem: elem, + anim: animation, + queue: animation.opts.queue + }) + ); + + // attach callbacks from options + return animation.progress( animation.opts.progress ) + .done( animation.opts.done, animation.opts.complete ) + .fail( animation.opts.fail ) + .always( animation.opts.always ); +} + +jQuery.Animation = jQuery.extend( Animation, { + + tweener: function( props, callback ) { + if ( jQuery.isFunction( props ) ) { + callback = props; + props = [ "*" ]; + } else { + props = props.split(" "); + } + + var prop, + index = 0, + length = props.length; + + for ( ; index < length ; index++ ) { + prop = props[ index ]; + tweeners[ prop ] = tweeners[ prop ] || []; + tweeners[ prop ].unshift( callback ); + } + }, + + prefilter: function( callback, prepend ) { + if ( prepend ) { + animationPrefilters.unshift( callback ); + } else { + animationPrefilters.push( callback ); + } + } +}); + +jQuery.speed = function( speed, easing, fn ) { + var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { + complete: fn || !fn && easing || + jQuery.isFunction( speed ) && speed, + duration: speed, + easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing + }; + + opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration : + opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default; + + // Normalize opt.queue - true/undefined/null -> "fx" + if ( opt.queue == null || opt.queue === true ) { + opt.queue = "fx"; + } + + // Queueing + opt.old = opt.complete; + + opt.complete = function() { + if ( jQuery.isFunction( opt.old ) ) { + opt.old.call( this ); + } + + if ( opt.queue ) { + jQuery.dequeue( this, opt.queue ); + } + }; + + return opt; +}; + +jQuery.fn.extend({ + fadeTo: function( speed, to, easing, callback ) { + + // Show any hidden elements after setting opacity to 0 + return this.filter( isHidden ).css( "opacity", 0 ).show() + + // Animate to the value specified + .end().animate({ opacity: to }, speed, easing, callback ); + }, + animate: function( prop, speed, easing, callback ) { + var empty = jQuery.isEmptyObject( prop ), + optall = jQuery.speed( speed, easing, callback ), + doAnimation = function() { + // Operate on a copy of prop so per-property easing won't be lost + var anim = Animation( this, jQuery.extend( {}, prop ), optall ); + + // Empty animations, or finishing resolves immediately + if ( empty || data_priv.get( this, "finish" ) ) { + anim.stop( true ); + } + }; + doAnimation.finish = doAnimation; + + return empty || optall.queue === false ? + this.each( doAnimation ) : + this.queue( optall.queue, doAnimation ); + }, + stop: function( type, clearQueue, gotoEnd ) { + var stopQueue = function( hooks ) { + var stop = hooks.stop; + delete hooks.stop; + stop( gotoEnd ); + }; + + if ( typeof type !== "string" ) { + gotoEnd = clearQueue; + clearQueue = type; + type = undefined; + } + if ( clearQueue && type !== false ) { + this.queue( type || "fx", [] ); + } + + return this.each(function() { + var dequeue = true, + index = type != null && type + "queueHooks", + timers = jQuery.timers, + data = data_priv.get( this ); + + if ( index ) { + if ( data[ index ] && data[ index ].stop ) { + stopQueue( data[ index ] ); + } + } else { + for ( index in data ) { + if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) { + stopQueue( data[ index ] ); + } + } + } + + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) { + timers[ index ].anim.stop( gotoEnd ); + dequeue = false; + timers.splice( index, 1 ); + } + } + + // Start the next in the queue if the last step wasn't forced. + // Timers currently will call their complete callbacks, which + // will dequeue but only if they were gotoEnd. + if ( dequeue || !gotoEnd ) { + jQuery.dequeue( this, type ); + } + }); + }, + finish: function( type ) { + if ( type !== false ) { + type = type || "fx"; + } + return this.each(function() { + var index, + data = data_priv.get( this ), + queue = data[ type + "queue" ], + hooks = data[ type + "queueHooks" ], + timers = jQuery.timers, + length = queue ? queue.length : 0; + + // Enable finishing flag on private data + data.finish = true; + + // Empty the queue first + jQuery.queue( this, type, [] ); + + if ( hooks && hooks.stop ) { + hooks.stop.call( this, true ); + } + + // Look for any active animations, and finish them + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && timers[ index ].queue === type ) { + timers[ index ].anim.stop( true ); + timers.splice( index, 1 ); + } + } + + // Look for any animations in the old queue and finish them + for ( index = 0; index < length; index++ ) { + if ( queue[ index ] && queue[ index ].finish ) { + queue[ index ].finish.call( this ); + } + } + + // Turn off finishing flag + delete data.finish; + }); + } +}); + +jQuery.each([ "toggle", "show", "hide" ], function( i, name ) { + var cssFn = jQuery.fn[ name ]; + jQuery.fn[ name ] = function( speed, easing, callback ) { + return speed == null || typeof speed === "boolean" ? + cssFn.apply( this, arguments ) : + this.animate( genFx( name, true ), speed, easing, callback ); + }; +}); + +// Generate shortcuts for custom animations +jQuery.each({ + slideDown: genFx("show"), + slideUp: genFx("hide"), + slideToggle: genFx("toggle"), + fadeIn: { opacity: "show" }, + fadeOut: { opacity: "hide" }, + fadeToggle: { opacity: "toggle" } +}, function( name, props ) { + jQuery.fn[ name ] = function( speed, easing, callback ) { + return this.animate( props, speed, easing, callback ); + }; +}); + +jQuery.timers = []; +jQuery.fx.tick = function() { + var timer, + i = 0, + timers = jQuery.timers; + + fxNow = jQuery.now(); + + for ( ; i < timers.length; i++ ) { + timer = timers[ i ]; + // Checks the timer has not already been removed + if ( !timer() && timers[ i ] === timer ) { + timers.splice( i--, 1 ); + } + } + + if ( !timers.length ) { + jQuery.fx.stop(); + } + fxNow = undefined; +}; + +jQuery.fx.timer = function( timer ) { + jQuery.timers.push( timer ); + if ( timer() ) { + jQuery.fx.start(); + } else { + jQuery.timers.pop(); + } +}; + +jQuery.fx.interval = 13; + +jQuery.fx.start = function() { + if ( !timerId ) { + timerId = setInterval( jQuery.fx.tick, jQuery.fx.interval ); + } +}; + +jQuery.fx.stop = function() { + clearInterval( timerId ); + timerId = null; +}; + +jQuery.fx.speeds = { + slow: 600, + fast: 200, + // Default speed + _default: 400 +}; + + +// Based off of the plugin by Clint Helfers, with permission. +// http://blindsignals.com/index.php/2009/07/jquery-delay/ +jQuery.fn.delay = function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; + type = type || "fx"; + + return this.queue( type, function( next, hooks ) { + var timeout = setTimeout( next, time ); + hooks.stop = function() { + clearTimeout( timeout ); + }; + }); +}; + + +(function() { + var input = document.createElement( "input" ), + select = document.createElement( "select" ), + opt = select.appendChild( document.createElement( "option" ) ); + + input.type = "checkbox"; + + // Support: iOS<=5.1, Android<=4.2+ + // Default value for a checkbox should be "on" + support.checkOn = input.value !== ""; + + // Support: IE<=11+ + // Must access selectedIndex to make default options select + support.optSelected = opt.selected; + + // Support: Android<=2.3 + // Options inside disabled selects are incorrectly marked as disabled + select.disabled = true; + support.optDisabled = !opt.disabled; + + // Support: IE<=11+ + // An input loses its value after becoming a radio + input = document.createElement( "input" ); + input.value = "t"; + input.type = "radio"; + support.radioValue = input.value === "t"; +})(); + + +var nodeHook, boolHook, + attrHandle = jQuery.expr.attrHandle; + +jQuery.fn.extend({ + attr: function( name, value ) { + return access( this, jQuery.attr, name, value, arguments.length > 1 ); + }, + + removeAttr: function( name ) { + return this.each(function() { + jQuery.removeAttr( this, name ); + }); + } +}); + +jQuery.extend({ + attr: function( elem, name, value ) { + var hooks, ret, + nType = elem.nodeType; + + // don't get/set attributes on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === strundefined ) { + return jQuery.prop( elem, name, value ); + } + + // All attributes are lowercase + // Grab necessary hook if one is defined + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + name = name.toLowerCase(); + hooks = jQuery.attrHooks[ name ] || + ( jQuery.expr.match.bool.test( name ) ? boolHook : nodeHook ); + } + + if ( value !== undefined ) { + + if ( value === null ) { + jQuery.removeAttr( elem, name ); + + } else if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + elem.setAttribute( name, value + "" ); + return value; + } + + } else if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) { + return ret; + + } else { + ret = jQuery.find.attr( elem, name ); + + // Non-existent attributes return null, we normalize to undefined + return ret == null ? + undefined : + ret; + } + }, + + removeAttr: function( elem, value ) { + var name, propName, + i = 0, + attrNames = value && value.match( rnotwhite ); + + if ( attrNames && elem.nodeType === 1 ) { + while ( (name = attrNames[i++]) ) { + propName = jQuery.propFix[ name ] || name; + + // Boolean attributes get special treatment (#10870) + if ( jQuery.expr.match.bool.test( name ) ) { + // Set corresponding property to false + elem[ propName ] = false; + } + + elem.removeAttribute( name ); + } + } + }, + + attrHooks: { + type: { + set: function( elem, value ) { + if ( !support.radioValue && value === "radio" && + jQuery.nodeName( elem, "input" ) ) { + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + } + } +}); + +// Hooks for boolean attributes +boolHook = { + set: function( elem, value, name ) { + if ( value === false ) { + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else { + elem.setAttribute( name, name ); + } + return name; + } +}; +jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) { + var getter = attrHandle[ name ] || jQuery.find.attr; + + attrHandle[ name ] = function( elem, name, isXML ) { + var ret, handle; + if ( !isXML ) { + // Avoid an infinite loop by temporarily removing this function from the getter + handle = attrHandle[ name ]; + attrHandle[ name ] = ret; + ret = getter( elem, name, isXML ) != null ? + name.toLowerCase() : + null; + attrHandle[ name ] = handle; + } + return ret; + }; +}); + + + + +var rfocusable = /^(?:input|select|textarea|button)$/i; + +jQuery.fn.extend({ + prop: function( name, value ) { + return access( this, jQuery.prop, name, value, arguments.length > 1 ); + }, + + removeProp: function( name ) { + return this.each(function() { + delete this[ jQuery.propFix[ name ] || name ]; + }); + } +}); + +jQuery.extend({ + propFix: { + "for": "htmlFor", + "class": "className" + }, + + prop: function( elem, name, value ) { + var ret, hooks, notxml, + nType = elem.nodeType; + + // Don't get/set properties on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + if ( notxml ) { + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + return hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ? + ret : + ( elem[ name ] = value ); + + } else { + return hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ? + ret : + elem[ name ]; + } + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + return elem.hasAttribute( "tabindex" ) || rfocusable.test( elem.nodeName ) || elem.href ? + elem.tabIndex : + -1; + } + } + } +}); + +if ( !support.optSelected ) { + jQuery.propHooks.selected = { + get: function( elem ) { + var parent = elem.parentNode; + if ( parent && parent.parentNode ) { + parent.parentNode.selectedIndex; + } + return null; + } + }; +} + +jQuery.each([ + "tabIndex", + "readOnly", + "maxLength", + "cellSpacing", + "cellPadding", + "rowSpan", + "colSpan", + "useMap", + "frameBorder", + "contentEditable" +], function() { + jQuery.propFix[ this.toLowerCase() ] = this; +}); + + + + +var rclass = /[\t\r\n\f]/g; + +jQuery.fn.extend({ + addClass: function( value ) { + var classes, elem, cur, clazz, j, finalValue, + proceed = typeof value === "string" && value, + i = 0, + len = this.length; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).addClass( value.call( this, j, this.className ) ); + }); + } + + if ( proceed ) { + // The disjunction here is for better compressibility (see removeClass) + classes = ( value || "" ).match( rnotwhite ) || []; + + for ( ; i < len; i++ ) { + elem = this[ i ]; + cur = elem.nodeType === 1 && ( elem.className ? + ( " " + elem.className + " " ).replace( rclass, " " ) : + " " + ); + + if ( cur ) { + j = 0; + while ( (clazz = classes[j++]) ) { + if ( cur.indexOf( " " + clazz + " " ) < 0 ) { + cur += clazz + " "; + } + } + + // only assign if different to avoid unneeded rendering. + finalValue = jQuery.trim( cur ); + if ( elem.className !== finalValue ) { + elem.className = finalValue; + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + var classes, elem, cur, clazz, j, finalValue, + proceed = arguments.length === 0 || typeof value === "string" && value, + i = 0, + len = this.length; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).removeClass( value.call( this, j, this.className ) ); + }); + } + if ( proceed ) { + classes = ( value || "" ).match( rnotwhite ) || []; + + for ( ; i < len; i++ ) { + elem = this[ i ]; + // This expression is here for better compressibility (see addClass) + cur = elem.nodeType === 1 && ( elem.className ? + ( " " + elem.className + " " ).replace( rclass, " " ) : + "" + ); + + if ( cur ) { + j = 0; + while ( (clazz = classes[j++]) ) { + // Remove *all* instances + while ( cur.indexOf( " " + clazz + " " ) >= 0 ) { + cur = cur.replace( " " + clazz + " ", " " ); + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = value ? jQuery.trim( cur ) : ""; + if ( elem.className !== finalValue ) { + elem.className = finalValue; + } + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value; + + if ( typeof stateVal === "boolean" && type === "string" ) { + return stateVal ? this.addClass( value ) : this.removeClass( value ); + } + + if ( jQuery.isFunction( value ) ) { + return this.each(function( i ) { + jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal ); + }); + } + + return this.each(function() { + if ( type === "string" ) { + // Toggle individual class names + var className, + i = 0, + self = jQuery( this ), + classNames = value.match( rnotwhite ) || []; + + while ( (className = classNames[ i++ ]) ) { + // Check each className given, space separated list + if ( self.hasClass( className ) ) { + self.removeClass( className ); + } else { + self.addClass( className ); + } + } + + // Toggle whole class name + } else if ( type === strundefined || type === "boolean" ) { + if ( this.className ) { + // store className if set + data_priv.set( this, "__className__", this.className ); + } + + // If the element has a class name or if we're passed `false`, + // then remove the whole classname (if there was one, the above saved it). + // Otherwise bring back whatever was previously saved (if anything), + // falling back to the empty string if nothing was stored. + this.className = this.className || value === false ? "" : data_priv.get( this, "__className__" ) || ""; + } + }); + }, + + hasClass: function( selector ) { + var className = " " + selector + " ", + i = 0, + l = this.length; + for ( ; i < l; i++ ) { + if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) { + return true; + } + } + + return false; + } +}); + + + + +var rreturn = /\r/g; + +jQuery.fn.extend({ + val: function( value ) { + var hooks, ret, isFunction, + elem = this[0]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ]; + + if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) { + return ret; + } + + ret = elem.value; + + return typeof ret === "string" ? + // Handle most common string cases + ret.replace(rreturn, "") : + // Handle cases where value is null/undef or number + ret == null ? "" : ret; + } + + return; + } + + isFunction = jQuery.isFunction( value ); + + return this.each(function( i ) { + var val; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( isFunction ) { + val = value.call( this, i, jQuery( this ).val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + + } else if ( typeof val === "number" ) { + val += ""; + + } else if ( jQuery.isArray( val ) ) { + val = jQuery.map( val, function( value ) { + return value == null ? "" : value + ""; + }); + } + + hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + }); + } +}); + +jQuery.extend({ + valHooks: { + option: { + get: function( elem ) { + var val = jQuery.find.attr( elem, "value" ); + return val != null ? + val : + // Support: IE10-11+ + // option.text throws exceptions (#14686, #14858) + jQuery.trim( jQuery.text( elem ) ); + } + }, + select: { + get: function( elem ) { + var value, option, + options = elem.options, + index = elem.selectedIndex, + one = elem.type === "select-one" || index < 0, + values = one ? null : [], + max = one ? index + 1 : options.length, + i = index < 0 ? + max : + one ? index : 0; + + // Loop through all the selected options + for ( ; i < max; i++ ) { + option = options[ i ]; + + // IE6-9 doesn't update selected after form reset (#2551) + if ( ( option.selected || i === index ) && + // Don't return options that are disabled or in a disabled optgroup + ( support.optDisabled ? !option.disabled : option.getAttribute( "disabled" ) === null ) && + ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + return values; + }, + + set: function( elem, value ) { + var optionSet, option, + options = elem.options, + values = jQuery.makeArray( value ), + i = options.length; + + while ( i-- ) { + option = options[ i ]; + if ( (option.selected = jQuery.inArray( option.value, values ) >= 0) ) { + optionSet = true; + } + } + + // Force browsers to behave consistently when non-matching value is set + if ( !optionSet ) { + elem.selectedIndex = -1; + } + return values; + } + } + } +}); + +// Radios and checkboxes getter/setter +jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + set: function( elem, value ) { + if ( jQuery.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 ); + } + } + }; + if ( !support.checkOn ) { + jQuery.valHooks[ this ].get = function( elem ) { + return elem.getAttribute("value") === null ? "on" : elem.value; + }; + } +}); + + + + +// Return jQuery for attributes-only inclusion + + +jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " + + "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + + "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) { + + // Handle event binding + jQuery.fn[ name ] = function( data, fn ) { + return arguments.length > 0 ? + this.on( name, null, data, fn ) : + this.trigger( name ); + }; +}); + +jQuery.fn.extend({ + hover: function( fnOver, fnOut ) { + return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); + }, + + bind: function( types, data, fn ) { + return this.on( types, null, data, fn ); + }, + unbind: function( types, fn ) { + return this.off( types, null, fn ); + }, + + delegate: function( selector, types, data, fn ) { + return this.on( types, selector, data, fn ); + }, + undelegate: function( selector, types, fn ) { + // ( namespace ) or ( selector, types [, fn] ) + return arguments.length === 1 ? this.off( selector, "**" ) : this.off( types, selector || "**", fn ); + } +}); + + +var nonce = jQuery.now(); + +var rquery = (/\?/); + + + +// Support: Android 2.3 +// Workaround failure to string-cast null input +jQuery.parseJSON = function( data ) { + return JSON.parse( data + "" ); +}; + + +// Cross-browser xml parsing +jQuery.parseXML = function( data ) { + var xml, tmp; + if ( !data || typeof data !== "string" ) { + return null; + } + + // Support: IE9 + try { + tmp = new DOMParser(); + xml = tmp.parseFromString( data, "text/xml" ); + } catch ( e ) { + xml = undefined; + } + + if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) { + jQuery.error( "Invalid XML: " + data ); + } + return xml; +}; + + +var + rhash = /#.*$/, + rts = /([?&])_=[^&]*/, + rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg, + // #7653, #8125, #8152: local protocol detection + rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, + rnoContent = /^(?:GET|HEAD)$/, + rprotocol = /^\/\//, + rurl = /^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/, + + /* Prefilters + * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) + * 2) These are called: + * - BEFORE asking for a transport + * - AFTER param serialization (s.data is a string if s.processData is true) + * 3) key is the dataType + * 4) the catchall symbol "*" can be used + * 5) execution will start with transport dataType and THEN continue down to "*" if needed + */ + prefilters = {}, + + /* Transports bindings + * 1) key is the dataType + * 2) the catchall symbol "*" can be used + * 3) selection will start with transport dataType and THEN go to "*" if needed + */ + transports = {}, + + // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression + allTypes = "*/".concat( "*" ), + + // Document location + ajaxLocation = window.location.href, + + // Segment location into parts + ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || []; + +// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport +function addToPrefiltersOrTransports( structure ) { + + // dataTypeExpression is optional and defaults to "*" + return function( dataTypeExpression, func ) { + + if ( typeof dataTypeExpression !== "string" ) { + func = dataTypeExpression; + dataTypeExpression = "*"; + } + + var dataType, + i = 0, + dataTypes = dataTypeExpression.toLowerCase().match( rnotwhite ) || []; + + if ( jQuery.isFunction( func ) ) { + // For each dataType in the dataTypeExpression + while ( (dataType = dataTypes[i++]) ) { + // Prepend if requested + if ( dataType[0] === "+" ) { + dataType = dataType.slice( 1 ) || "*"; + (structure[ dataType ] = structure[ dataType ] || []).unshift( func ); + + // Otherwise append + } else { + (structure[ dataType ] = structure[ dataType ] || []).push( func ); + } + } + } + }; +} + +// Base inspection function for prefilters and transports +function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) { + + var inspected = {}, + seekingTransport = ( structure === transports ); + + function inspect( dataType ) { + var selected; + inspected[ dataType ] = true; + jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) { + var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR ); + if ( typeof dataTypeOrTransport === "string" && !seekingTransport && !inspected[ dataTypeOrTransport ] ) { + options.dataTypes.unshift( dataTypeOrTransport ); + inspect( dataTypeOrTransport ); + return false; + } else if ( seekingTransport ) { + return !( selected = dataTypeOrTransport ); + } + }); + return selected; + } + + return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" ); +} + +// A special extend for ajax options +// that takes "flat" options (not to be deep extended) +// Fixes #9887 +function ajaxExtend( target, src ) { + var key, deep, + flatOptions = jQuery.ajaxSettings.flatOptions || {}; + + for ( key in src ) { + if ( src[ key ] !== undefined ) { + ( flatOptions[ key ] ? target : ( deep || (deep = {}) ) )[ key ] = src[ key ]; + } + } + if ( deep ) { + jQuery.extend( true, target, deep ); + } + + return target; +} + +/* Handles responses to an ajax request: + * - finds the right dataType (mediates between content-type and expected dataType) + * - returns the corresponding response + */ +function ajaxHandleResponses( s, jqXHR, responses ) { + + var ct, type, finalDataType, firstDataType, + contents = s.contents, + dataTypes = s.dataTypes; + + // Remove auto dataType and get content-type in the process + while ( dataTypes[ 0 ] === "*" ) { + dataTypes.shift(); + if ( ct === undefined ) { + ct = s.mimeType || jqXHR.getResponseHeader("Content-Type"); + } + } + + // Check if we're dealing with a known content-type + if ( ct ) { + for ( type in contents ) { + if ( contents[ type ] && contents[ type ].test( ct ) ) { + dataTypes.unshift( type ); + break; + } + } + } + + // Check to see if we have a response for the expected dataType + if ( dataTypes[ 0 ] in responses ) { + finalDataType = dataTypes[ 0 ]; + } else { + // Try convertible dataTypes + for ( type in responses ) { + if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) { + finalDataType = type; + break; + } + if ( !firstDataType ) { + firstDataType = type; + } + } + // Or just use first one + finalDataType = finalDataType || firstDataType; + } + + // If we found a dataType + // We add the dataType to the list if needed + // and return the corresponding response + if ( finalDataType ) { + if ( finalDataType !== dataTypes[ 0 ] ) { + dataTypes.unshift( finalDataType ); + } + return responses[ finalDataType ]; + } +} + +/* Chain conversions given the request and the original response + * Also sets the responseXXX fields on the jqXHR instance + */ +function ajaxConvert( s, response, jqXHR, isSuccess ) { + var conv2, current, conv, tmp, prev, + converters = {}, + // Work with a copy of dataTypes in case we need to modify it for conversion + dataTypes = s.dataTypes.slice(); + + // Create converters map with lowercased keys + if ( dataTypes[ 1 ] ) { + for ( conv in s.converters ) { + converters[ conv.toLowerCase() ] = s.converters[ conv ]; + } + } + + current = dataTypes.shift(); + + // Convert to each sequential dataType + while ( current ) { + + if ( s.responseFields[ current ] ) { + jqXHR[ s.responseFields[ current ] ] = response; + } + + // Apply the dataFilter if provided + if ( !prev && isSuccess && s.dataFilter ) { + response = s.dataFilter( response, s.dataType ); + } + + prev = current; + current = dataTypes.shift(); + + if ( current ) { + + // There's only work to do if current dataType is non-auto + if ( current === "*" ) { + + current = prev; + + // Convert response if prev dataType is non-auto and differs from current + } else if ( prev !== "*" && prev !== current ) { + + // Seek a direct converter + conv = converters[ prev + " " + current ] || converters[ "* " + current ]; + + // If none found, seek a pair + if ( !conv ) { + for ( conv2 in converters ) { + + // If conv2 outputs current + tmp = conv2.split( " " ); + if ( tmp[ 1 ] === current ) { + + // If prev can be converted to accepted input + conv = converters[ prev + " " + tmp[ 0 ] ] || + converters[ "* " + tmp[ 0 ] ]; + if ( conv ) { + // Condense equivalence converters + if ( conv === true ) { + conv = converters[ conv2 ]; + + // Otherwise, insert the intermediate dataType + } else if ( converters[ conv2 ] !== true ) { + current = tmp[ 0 ]; + dataTypes.unshift( tmp[ 1 ] ); + } + break; + } + } + } + } + + // Apply converter (if not an equivalence) + if ( conv !== true ) { + + // Unless errors are allowed to bubble, catch and return them + if ( conv && s[ "throws" ] ) { + response = conv( response ); + } else { + try { + response = conv( response ); + } catch ( e ) { + return { state: "parsererror", error: conv ? e : "No conversion from " + prev + " to " + current }; + } + } + } + } + } + } + + return { state: "success", data: response }; +} + +jQuery.extend({ + + // Counter for holding the number of active queries + active: 0, + + // Last-Modified header cache for next request + lastModified: {}, + etag: {}, + + ajaxSettings: { + url: ajaxLocation, + type: "GET", + isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ), + global: true, + processData: true, + async: true, + contentType: "application/x-www-form-urlencoded; charset=UTF-8", + /* + timeout: 0, + data: null, + dataType: null, + username: null, + password: null, + cache: null, + throws: false, + traditional: false, + headers: {}, + */ + + accepts: { + "*": allTypes, + text: "text/plain", + html: "text/html", + xml: "application/xml, text/xml", + json: "application/json, text/javascript" + }, + + contents: { + xml: /xml/, + html: /html/, + json: /json/ + }, + + responseFields: { + xml: "responseXML", + text: "responseText", + json: "responseJSON" + }, + + // Data converters + // Keys separate source (or catchall "*") and destination types with a single space + converters: { + + // Convert anything to text + "* text": String, + + // Text to html (true = no transformation) + "text html": true, + + // Evaluate text as a json expression + "text json": jQuery.parseJSON, + + // Parse text as xml + "text xml": jQuery.parseXML + }, + + // For options that shouldn't be deep extended: + // you can add your own custom options here if + // and when you create one that shouldn't be + // deep extended (see ajaxExtend) + flatOptions: { + url: true, + context: true + } + }, + + // Creates a full fledged settings object into target + // with both ajaxSettings and settings fields. + // If target is omitted, writes into ajaxSettings. + ajaxSetup: function( target, settings ) { + return settings ? + + // Building a settings object + ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) : + + // Extending ajaxSettings + ajaxExtend( jQuery.ajaxSettings, target ); + }, + + ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), + ajaxTransport: addToPrefiltersOrTransports( transports ), + + // Main method + ajax: function( url, options ) { + + // If url is an object, simulate pre-1.5 signature + if ( typeof url === "object" ) { + options = url; + url = undefined; + } + + // Force options to be an object + options = options || {}; + + var transport, + // URL without anti-cache param + cacheURL, + // Response headers + responseHeadersString, + responseHeaders, + // timeout handle + timeoutTimer, + // Cross-domain detection vars + parts, + // To know if global events are to be dispatched + fireGlobals, + // Loop variable + i, + // Create the final options object + s = jQuery.ajaxSetup( {}, options ), + // Callbacks context + callbackContext = s.context || s, + // Context for global events is callbackContext if it is a DOM node or jQuery collection + globalEventContext = s.context && ( callbackContext.nodeType || callbackContext.jquery ) ? + jQuery( callbackContext ) : + jQuery.event, + // Deferreds + deferred = jQuery.Deferred(), + completeDeferred = jQuery.Callbacks("once memory"), + // Status-dependent callbacks + statusCode = s.statusCode || {}, + // Headers (they are sent all at once) + requestHeaders = {}, + requestHeadersNames = {}, + // The jqXHR state + state = 0, + // Default abort message + strAbort = "canceled", + // Fake xhr + jqXHR = { + readyState: 0, + + // Builds headers hashtable if needed + getResponseHeader: function( key ) { + var match; + if ( state === 2 ) { + if ( !responseHeaders ) { + responseHeaders = {}; + while ( (match = rheaders.exec( responseHeadersString )) ) { + responseHeaders[ match[1].toLowerCase() ] = match[ 2 ]; + } + } + match = responseHeaders[ key.toLowerCase() ]; + } + return match == null ? null : match; + }, + + // Raw string + getAllResponseHeaders: function() { + return state === 2 ? responseHeadersString : null; + }, + + // Caches the header + setRequestHeader: function( name, value ) { + var lname = name.toLowerCase(); + if ( !state ) { + name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name; + requestHeaders[ name ] = value; + } + return this; + }, + + // Overrides response content-type header + overrideMimeType: function( type ) { + if ( !state ) { + s.mimeType = type; + } + return this; + }, + + // Status-dependent callbacks + statusCode: function( map ) { + var code; + if ( map ) { + if ( state < 2 ) { + for ( code in map ) { + // Lazy-add the new callback in a way that preserves old ones + statusCode[ code ] = [ statusCode[ code ], map[ code ] ]; + } + } else { + // Execute the appropriate callbacks + jqXHR.always( map[ jqXHR.status ] ); + } + } + return this; + }, + + // Cancel the request + abort: function( statusText ) { + var finalText = statusText || strAbort; + if ( transport ) { + transport.abort( finalText ); + } + done( 0, finalText ); + return this; + } + }; + + // Attach deferreds + deferred.promise( jqXHR ).complete = completeDeferred.add; + jqXHR.success = jqXHR.done; + jqXHR.error = jqXHR.fail; + + // Remove hash character (#7531: and string promotion) + // Add protocol if not provided (prefilters might expect it) + // Handle falsy url in the settings object (#10093: consistency with old signature) + // We also use the url parameter if available + s.url = ( ( url || s.url || ajaxLocation ) + "" ).replace( rhash, "" ) + .replace( rprotocol, ajaxLocParts[ 1 ] + "//" ); + + // Alias method option to type as per ticket #12004 + s.type = options.method || options.type || s.method || s.type; + + // Extract dataTypes list + s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().match( rnotwhite ) || [ "" ]; + + // A cross-domain request is in order when we have a protocol:host:port mismatch + if ( s.crossDomain == null ) { + parts = rurl.exec( s.url.toLowerCase() ); + s.crossDomain = !!( parts && + ( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] || + ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? "80" : "443" ) ) !== + ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? "80" : "443" ) ) ) + ); + } + + // Convert data if not already a string + if ( s.data && s.processData && typeof s.data !== "string" ) { + s.data = jQuery.param( s.data, s.traditional ); + } + + // Apply prefilters + inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); + + // If request was aborted inside a prefilter, stop there + if ( state === 2 ) { + return jqXHR; + } + + // We can fire global events as of now if asked to + // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118) + fireGlobals = jQuery.event && s.global; + + // Watch for a new set of requests + if ( fireGlobals && jQuery.active++ === 0 ) { + jQuery.event.trigger("ajaxStart"); + } + + // Uppercase the type + s.type = s.type.toUpperCase(); + + // Determine if request has content + s.hasContent = !rnoContent.test( s.type ); + + // Save the URL in case we're toying with the If-Modified-Since + // and/or If-None-Match header later on + cacheURL = s.url; + + // More options handling for requests with no content + if ( !s.hasContent ) { + + // If data is available, append data to url + if ( s.data ) { + cacheURL = ( s.url += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data ); + // #9682: remove data so that it's not used in an eventual retry + delete s.data; + } + + // Add anti-cache in url if needed + if ( s.cache === false ) { + s.url = rts.test( cacheURL ) ? + + // If there is already a '_' parameter, set its value + cacheURL.replace( rts, "$1_=" + nonce++ ) : + + // Otherwise add one to the end + cacheURL + ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + nonce++; + } + } + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + if ( jQuery.lastModified[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] ); + } + if ( jQuery.etag[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] ); + } + } + + // Set the correct header, if data is being sent + if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { + jqXHR.setRequestHeader( "Content-Type", s.contentType ); + } + + // Set the Accepts header for the server, depending on the dataType + jqXHR.setRequestHeader( + "Accept", + s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ? + s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : + s.accepts[ "*" ] + ); + + // Check for headers option + for ( i in s.headers ) { + jqXHR.setRequestHeader( i, s.headers[ i ] ); + } + + // Allow custom headers/mimetypes and early abort + if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) { + // Abort if not done already and return + return jqXHR.abort(); + } + + // Aborting is no longer a cancellation + strAbort = "abort"; + + // Install callbacks on deferreds + for ( i in { success: 1, error: 1, complete: 1 } ) { + jqXHR[ i ]( s[ i ] ); + } + + // Get transport + transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); + + // If no transport, we auto-abort + if ( !transport ) { + done( -1, "No Transport" ); + } else { + jqXHR.readyState = 1; + + // Send global event + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); + } + // Timeout + if ( s.async && s.timeout > 0 ) { + timeoutTimer = setTimeout(function() { + jqXHR.abort("timeout"); + }, s.timeout ); + } + + try { + state = 1; + transport.send( requestHeaders, done ); + } catch ( e ) { + // Propagate exception as error if not done + if ( state < 2 ) { + done( -1, e ); + // Simply rethrow otherwise + } else { + throw e; + } + } + } + + // Callback for when everything is done + function done( status, nativeStatusText, responses, headers ) { + var isSuccess, success, error, response, modified, + statusText = nativeStatusText; + + // Called once + if ( state === 2 ) { + return; + } + + // State is "done" now + state = 2; + + // Clear timeout if it exists + if ( timeoutTimer ) { + clearTimeout( timeoutTimer ); + } + + // Dereference transport for early garbage collection + // (no matter how long the jqXHR object will be used) + transport = undefined; + + // Cache response headers + responseHeadersString = headers || ""; + + // Set readyState + jqXHR.readyState = status > 0 ? 4 : 0; + + // Determine if successful + isSuccess = status >= 200 && status < 300 || status === 304; + + // Get response data + if ( responses ) { + response = ajaxHandleResponses( s, jqXHR, responses ); + } + + // Convert no matter what (that way responseXXX fields are always set) + response = ajaxConvert( s, response, jqXHR, isSuccess ); + + // If successful, handle type chaining + if ( isSuccess ) { + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + modified = jqXHR.getResponseHeader("Last-Modified"); + if ( modified ) { + jQuery.lastModified[ cacheURL ] = modified; + } + modified = jqXHR.getResponseHeader("etag"); + if ( modified ) { + jQuery.etag[ cacheURL ] = modified; + } + } + + // if no content + if ( status === 204 || s.type === "HEAD" ) { + statusText = "nocontent"; + + // if not modified + } else if ( status === 304 ) { + statusText = "notmodified"; + + // If we have data, let's convert it + } else { + statusText = response.state; + success = response.data; + error = response.error; + isSuccess = !error; + } + } else { + // Extract error from statusText and normalize for non-aborts + error = statusText; + if ( status || !statusText ) { + statusText = "error"; + if ( status < 0 ) { + status = 0; + } + } + } + + // Set data for the fake xhr object + jqXHR.status = status; + jqXHR.statusText = ( nativeStatusText || statusText ) + ""; + + // Success/Error + if ( isSuccess ) { + deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); + } else { + deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); + } + + // Status-dependent callbacks + jqXHR.statusCode( statusCode ); + statusCode = undefined; + + if ( fireGlobals ) { + globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError", + [ jqXHR, s, isSuccess ? success : error ] ); + } + + // Complete + completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); + + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); + // Handle the global AJAX counter + if ( !( --jQuery.active ) ) { + jQuery.event.trigger("ajaxStop"); + } + } + } + + return jqXHR; + }, + + getJSON: function( url, data, callback ) { + return jQuery.get( url, data, callback, "json" ); + }, + + getScript: function( url, callback ) { + return jQuery.get( url, undefined, callback, "script" ); + } +}); + +jQuery.each( [ "get", "post" ], function( i, method ) { + jQuery[ method ] = function( url, data, callback, type ) { + // Shift arguments if data argument was omitted + if ( jQuery.isFunction( data ) ) { + type = type || callback; + callback = data; + data = undefined; + } + + return jQuery.ajax({ + url: url, + type: method, + dataType: type, + data: data, + success: callback + }); + }; +}); + + +jQuery._evalUrl = function( url ) { + return jQuery.ajax({ + url: url, + type: "GET", + dataType: "script", + async: false, + global: false, + "throws": true + }); +}; + + +jQuery.fn.extend({ + wrapAll: function( html ) { + var wrap; + + if ( jQuery.isFunction( html ) ) { + return this.each(function( i ) { + jQuery( this ).wrapAll( html.call(this, i) ); + }); + } + + if ( this[ 0 ] ) { + + // The elements to wrap the target around + wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true ); + + if ( this[ 0 ].parentNode ) { + wrap.insertBefore( this[ 0 ] ); + } + + wrap.map(function() { + var elem = this; + + while ( elem.firstElementChild ) { + elem = elem.firstElementChild; + } + + return elem; + }).append( this ); + } + + return this; + }, + + wrapInner: function( html ) { + if ( jQuery.isFunction( html ) ) { + return this.each(function( i ) { + jQuery( this ).wrapInner( html.call(this, i) ); + }); + } + + return this.each(function() { + var self = jQuery( this ), + contents = self.contents(); + + if ( contents.length ) { + contents.wrapAll( html ); + + } else { + self.append( html ); + } + }); + }, + + wrap: function( html ) { + var isFunction = jQuery.isFunction( html ); + + return this.each(function( i ) { + jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html ); + }); + }, + + unwrap: function() { + return this.parent().each(function() { + if ( !jQuery.nodeName( this, "body" ) ) { + jQuery( this ).replaceWith( this.childNodes ); + } + }).end(); + } +}); + + +jQuery.expr.filters.hidden = function( elem ) { + // Support: Opera <= 12.12 + // Opera reports offsetWidths and offsetHeights less than zero on some elements + return elem.offsetWidth <= 0 && elem.offsetHeight <= 0; +}; +jQuery.expr.filters.visible = function( elem ) { + return !jQuery.expr.filters.hidden( elem ); +}; + + + + +var r20 = /%20/g, + rbracket = /\[\]$/, + rCRLF = /\r?\n/g, + rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i, + rsubmittable = /^(?:input|select|textarea|keygen)/i; + +function buildParams( prefix, obj, traditional, add ) { + var name; + + if ( jQuery.isArray( obj ) ) { + // Serialize array item. + jQuery.each( obj, function( i, v ) { + if ( traditional || rbracket.test( prefix ) ) { + // Treat each array item as a scalar. + add( prefix, v ); + + } else { + // Item is non-scalar (array or object), encode its numeric index. + buildParams( prefix + "[" + ( typeof v === "object" ? i : "" ) + "]", v, traditional, add ); + } + }); + + } else if ( !traditional && jQuery.type( obj ) === "object" ) { + // Serialize object item. + for ( name in obj ) { + buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); + } + + } else { + // Serialize scalar item. + add( prefix, obj ); + } +} + +// Serialize an array of form elements or a set of +// key/values into a query string +jQuery.param = function( a, traditional ) { + var prefix, + s = [], + add = function( key, value ) { + // If value is a function, invoke it and return its value + value = jQuery.isFunction( value ) ? value() : ( value == null ? "" : value ); + s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value ); + }; + + // Set traditional to true for jQuery <= 1.3.2 behavior. + if ( traditional === undefined ) { + traditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional; + } + + // If an array was passed in, assume that it is an array of form elements. + if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { + // Serialize the form elements + jQuery.each( a, function() { + add( this.name, this.value ); + }); + + } else { + // If traditional, encode the "old" way (the way 1.3.2 or older + // did it), otherwise encode params recursively. + for ( prefix in a ) { + buildParams( prefix, a[ prefix ], traditional, add ); + } + } + + // Return the resulting serialization + return s.join( "&" ).replace( r20, "+" ); +}; + +jQuery.fn.extend({ + serialize: function() { + return jQuery.param( this.serializeArray() ); + }, + serializeArray: function() { + return this.map(function() { + // Can add propHook for "elements" to filter or add form elements + var elements = jQuery.prop( this, "elements" ); + return elements ? jQuery.makeArray( elements ) : this; + }) + .filter(function() { + var type = this.type; + + // Use .is( ":disabled" ) so that fieldset[disabled] works + return this.name && !jQuery( this ).is( ":disabled" ) && + rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) && + ( this.checked || !rcheckableType.test( type ) ); + }) + .map(function( i, elem ) { + var val = jQuery( this ).val(); + + return val == null ? + null : + jQuery.isArray( val ) ? + jQuery.map( val, function( val ) { + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + }) : + { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + }).get(); + } +}); + + +jQuery.ajaxSettings.xhr = function() { + try { + return new XMLHttpRequest(); + } catch( e ) {} +}; + +var xhrId = 0, + xhrCallbacks = {}, + xhrSuccessStatus = { + // file protocol always yields status code 0, assume 200 + 0: 200, + // Support: IE9 + // #1450: sometimes IE returns 1223 when it should be 204 + 1223: 204 + }, + xhrSupported = jQuery.ajaxSettings.xhr(); + +// Support: IE9 +// Open requests must be manually aborted on unload (#5280) +// See https://support.microsoft.com/kb/2856746 for more info +if ( window.attachEvent ) { + window.attachEvent( "onunload", function() { + for ( var key in xhrCallbacks ) { + xhrCallbacks[ key ](); + } + }); +} + +support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported ); +support.ajax = xhrSupported = !!xhrSupported; + +jQuery.ajaxTransport(function( options ) { + var callback; + + // Cross domain only allowed if supported through XMLHttpRequest + if ( support.cors || xhrSupported && !options.crossDomain ) { + return { + send: function( headers, complete ) { + var i, + xhr = options.xhr(), + id = ++xhrId; + + xhr.open( options.type, options.url, options.async, options.username, options.password ); + + // Apply custom fields if provided + if ( options.xhrFields ) { + for ( i in options.xhrFields ) { + xhr[ i ] = options.xhrFields[ i ]; + } + } + + // Override mime type if needed + if ( options.mimeType && xhr.overrideMimeType ) { + xhr.overrideMimeType( options.mimeType ); + } + + // X-Requested-With header + // For cross-domain requests, seeing as conditions for a preflight are + // akin to a jigsaw puzzle, we simply never set it to be sure. + // (it can always be set on a per-request basis or even using ajaxSetup) + // For same-domain requests, won't change header if already provided. + if ( !options.crossDomain && !headers["X-Requested-With"] ) { + headers["X-Requested-With"] = "XMLHttpRequest"; + } + + // Set headers + for ( i in headers ) { + xhr.setRequestHeader( i, headers[ i ] ); + } + + // Callback + callback = function( type ) { + return function() { + if ( callback ) { + delete xhrCallbacks[ id ]; + callback = xhr.onload = xhr.onerror = null; + + if ( type === "abort" ) { + xhr.abort(); + } else if ( type === "error" ) { + complete( + // file: protocol always yields status 0; see #8605, #14207 + xhr.status, + xhr.statusText + ); + } else { + complete( + xhrSuccessStatus[ xhr.status ] || xhr.status, + xhr.statusText, + // Support: IE9 + // Accessing binary-data responseText throws an exception + // (#11426) + typeof xhr.responseText === "string" ? { + text: xhr.responseText + } : undefined, + xhr.getAllResponseHeaders() + ); + } + } + }; + }; + + // Listen to events + xhr.onload = callback(); + xhr.onerror = callback("error"); + + // Create the abort callback + callback = xhrCallbacks[ id ] = callback("abort"); + + try { + // Do send the request (this may raise an exception) + xhr.send( options.hasContent && options.data || null ); + } catch ( e ) { + // #14683: Only rethrow if this hasn't been notified as an error yet + if ( callback ) { + throw e; + } + } + }, + + abort: function() { + if ( callback ) { + callback(); + } + } + }; + } +}); + + + + +// Install script dataType +jQuery.ajaxSetup({ + accepts: { + script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript" + }, + contents: { + script: /(?:java|ecma)script/ + }, + converters: { + "text script": function( text ) { + jQuery.globalEval( text ); + return text; + } + } +}); + +// Handle cache's special case and crossDomain +jQuery.ajaxPrefilter( "script", function( s ) { + if ( s.cache === undefined ) { + s.cache = false; + } + if ( s.crossDomain ) { + s.type = "GET"; + } +}); + +// Bind script tag hack transport +jQuery.ajaxTransport( "script", function( s ) { + // This transport only deals with cross domain requests + if ( s.crossDomain ) { + var script, callback; + return { + send: function( _, complete ) { + script = jQuery("<script>").prop({ + async: true, + charset: s.scriptCharset, + src: s.url + }).on( + "load error", + callback = function( evt ) { + script.remove(); + callback = null; + if ( evt ) { + complete( evt.type === "error" ? 404 : 200, evt.type ); + } + } + ); + document.head.appendChild( script[ 0 ] ); + }, + abort: function() { + if ( callback ) { + callback(); + } + } + }; + } +}); + + + + +var oldCallbacks = [], + rjsonp = /(=)\?(?=&|$)|\?\?/; + +// Default jsonp settings +jQuery.ajaxSetup({ + jsonp: "callback", + jsonpCallback: function() { + var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( nonce++ ) ); + this[ callback ] = true; + return callback; + } +}); + +// Detect, normalize options and install callbacks for jsonp requests +jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) { + + var callbackName, overwritten, responseContainer, + jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ? + "url" : + typeof s.data === "string" && !( s.contentType || "" ).indexOf("application/x-www-form-urlencoded") && rjsonp.test( s.data ) && "data" + ); + + // Handle iff the expected data type is "jsonp" or we have a parameter to set + if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) { + + // Get callback name, remembering preexisting value associated with it + callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ? + s.jsonpCallback() : + s.jsonpCallback; + + // Insert callback into url or form data + if ( jsonProp ) { + s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName ); + } else if ( s.jsonp !== false ) { + s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName; + } + + // Use data converter to retrieve json after script execution + s.converters["script json"] = function() { + if ( !responseContainer ) { + jQuery.error( callbackName + " was not called" ); + } + return responseContainer[ 0 ]; + }; + + // force json dataType + s.dataTypes[ 0 ] = "json"; + + // Install callback + overwritten = window[ callbackName ]; + window[ callbackName ] = function() { + responseContainer = arguments; + }; + + // Clean-up function (fires after converters) + jqXHR.always(function() { + // Restore preexisting value + window[ callbackName ] = overwritten; + + // Save back as free + if ( s[ callbackName ] ) { + // make sure that re-using the options doesn't screw things around + s.jsonpCallback = originalSettings.jsonpCallback; + + // save the callback name for future use + oldCallbacks.push( callbackName ); + } + + // Call if it was a function and we have a response + if ( responseContainer && jQuery.isFunction( overwritten ) ) { + overwritten( responseContainer[ 0 ] ); + } + + responseContainer = overwritten = undefined; + }); + + // Delegate to script + return "script"; + } +}); + + + + +// data: string of html +// context (optional): If specified, the fragment will be created in this context, defaults to document +// keepScripts (optional): If true, will include scripts passed in the html string +jQuery.parseHTML = function( data, context, keepScripts ) { + if ( !data || typeof data !== "string" ) { + return null; + } + if ( typeof context === "boolean" ) { + keepScripts = context; + context = false; + } + context = context || document; + + var parsed = rsingleTag.exec( data ), + scripts = !keepScripts && []; + + // Single tag + if ( parsed ) { + return [ context.createElement( parsed[1] ) ]; + } + + parsed = jQuery.buildFragment( [ data ], context, scripts ); + + if ( scripts && scripts.length ) { + jQuery( scripts ).remove(); + } + + return jQuery.merge( [], parsed.childNodes ); +}; + + +// Keep a copy of the old load method +var _load = jQuery.fn.load; + +/** + * Load a url into a page + */ +jQuery.fn.load = function( url, params, callback ) { + if ( typeof url !== "string" && _load ) { + return _load.apply( this, arguments ); + } + + var selector, type, response, + self = this, + off = url.indexOf(" "); + + if ( off >= 0 ) { + selector = jQuery.trim( url.slice( off ) ); + url = url.slice( 0, off ); + } + + // If it's a function + if ( jQuery.isFunction( params ) ) { + + // We assume that it's the callback + callback = params; + params = undefined; + + // Otherwise, build a param string + } else if ( params && typeof params === "object" ) { + type = "POST"; + } + + // If we have elements to modify, make the request + if ( self.length > 0 ) { + jQuery.ajax({ + url: url, + + // if "type" variable is undefined, then "GET" method will be used + type: type, + dataType: "html", + data: params + }).done(function( responseText ) { + + // Save response for use in complete callback + response = arguments; + + self.html( selector ? + + // If a selector was specified, locate the right elements in a dummy div + // Exclude scripts to avoid IE 'Permission Denied' errors + jQuery("<div>").append( jQuery.parseHTML( responseText ) ).find( selector ) : + + // Otherwise use the full result + responseText ); + + }).complete( callback && function( jqXHR, status ) { + self.each( callback, response || [ jqXHR.responseText, status, jqXHR ] ); + }); + } + + return this; +}; + + + + +// Attach a bunch of functions for handling common AJAX events +jQuery.each( [ "ajaxStart", "ajaxStop", "ajaxComplete", "ajaxError", "ajaxSuccess", "ajaxSend" ], function( i, type ) { + jQuery.fn[ type ] = function( fn ) { + return this.on( type, fn ); + }; +}); + + + + +jQuery.expr.filters.animated = function( elem ) { + return jQuery.grep(jQuery.timers, function( fn ) { + return elem === fn.elem; + }).length; +}; + + + + +var docElem = window.document.documentElement; + +/** + * Gets a window from an element + */ +function getWindow( elem ) { + return jQuery.isWindow( elem ) ? elem : elem.nodeType === 9 && elem.defaultView; +} + +jQuery.offset = { + setOffset: function( elem, options, i ) { + var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition, + position = jQuery.css( elem, "position" ), + curElem = jQuery( elem ), + props = {}; + + // Set position first, in-case top/left are set even on static elem + if ( position === "static" ) { + elem.style.position = "relative"; + } + + curOffset = curElem.offset(); + curCSSTop = jQuery.css( elem, "top" ); + curCSSLeft = jQuery.css( elem, "left" ); + calculatePosition = ( position === "absolute" || position === "fixed" ) && + ( curCSSTop + curCSSLeft ).indexOf("auto") > -1; + + // Need to be able to calculate position if either + // top or left is auto and position is either absolute or fixed + if ( calculatePosition ) { + curPosition = curElem.position(); + curTop = curPosition.top; + curLeft = curPosition.left; + + } else { + curTop = parseFloat( curCSSTop ) || 0; + curLeft = parseFloat( curCSSLeft ) || 0; + } + + if ( jQuery.isFunction( options ) ) { + options = options.call( elem, i, curOffset ); + } + + if ( options.top != null ) { + props.top = ( options.top - curOffset.top ) + curTop; + } + if ( options.left != null ) { + props.left = ( options.left - curOffset.left ) + curLeft; + } + + if ( "using" in options ) { + options.using.call( elem, props ); + + } else { + curElem.css( props ); + } + } +}; + +jQuery.fn.extend({ + offset: function( options ) { + if ( arguments.length ) { + return options === undefined ? + this : + this.each(function( i ) { + jQuery.offset.setOffset( this, options, i ); + }); + } + + var docElem, win, + elem = this[ 0 ], + box = { top: 0, left: 0 }, + doc = elem && elem.ownerDocument; + + if ( !doc ) { + return; + } + + docElem = doc.documentElement; + + // Make sure it's not a disconnected DOM node + if ( !jQuery.contains( docElem, elem ) ) { + return box; + } + + // Support: BlackBerry 5, iOS 3 (original iPhone) + // If we don't have gBCR, just use 0,0 rather than error + if ( typeof elem.getBoundingClientRect !== strundefined ) { + box = elem.getBoundingClientRect(); + } + win = getWindow( doc ); + return { + top: box.top + win.pageYOffset - docElem.clientTop, + left: box.left + win.pageXOffset - docElem.clientLeft + }; + }, + + position: function() { + if ( !this[ 0 ] ) { + return; + } + + var offsetParent, offset, + elem = this[ 0 ], + parentOffset = { top: 0, left: 0 }; + + // Fixed elements are offset from window (parentOffset = {top:0, left: 0}, because it is its only offset parent + if ( jQuery.css( elem, "position" ) === "fixed" ) { + // Assume getBoundingClientRect is there when computed position is fixed + offset = elem.getBoundingClientRect(); + + } else { + // Get *real* offsetParent + offsetParent = this.offsetParent(); + + // Get correct offsets + offset = this.offset(); + if ( !jQuery.nodeName( offsetParent[ 0 ], "html" ) ) { + parentOffset = offsetParent.offset(); + } + + // Add offsetParent borders + parentOffset.top += jQuery.css( offsetParent[ 0 ], "borderTopWidth", true ); + parentOffset.left += jQuery.css( offsetParent[ 0 ], "borderLeftWidth", true ); + } + + // Subtract parent offsets and element margins + return { + top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ), + left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true ) + }; + }, + + offsetParent: function() { + return this.map(function() { + var offsetParent = this.offsetParent || docElem; + + while ( offsetParent && ( !jQuery.nodeName( offsetParent, "html" ) && jQuery.css( offsetParent, "position" ) === "static" ) ) { + offsetParent = offsetParent.offsetParent; + } + + return offsetParent || docElem; + }); + } +}); + +// Create scrollLeft and scrollTop methods +jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function( method, prop ) { + var top = "pageYOffset" === prop; + + jQuery.fn[ method ] = function( val ) { + return access( this, function( elem, method, val ) { + var win = getWindow( elem ); + + if ( val === undefined ) { + return win ? win[ prop ] : elem[ method ]; + } + + if ( win ) { + win.scrollTo( + !top ? val : window.pageXOffset, + top ? val : window.pageYOffset + ); + + } else { + elem[ method ] = val; + } + }, method, val, arguments.length, null ); + }; +}); + +// Support: Safari<7+, Chrome<37+ +// Add the top/left cssHooks using jQuery.fn.position +// Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084 +// Blink bug: https://code.google.com/p/chromium/issues/detail?id=229280 +// getComputedStyle returns percent when specified for top/left/bottom/right; +// rather than make the css module depend on the offset module, just check for it here +jQuery.each( [ "top", "left" ], function( i, prop ) { + jQuery.cssHooks[ prop ] = addGetHookIf( support.pixelPosition, + function( elem, computed ) { + if ( computed ) { + computed = curCSS( elem, prop ); + // If curCSS returns percentage, fallback to offset + return rnumnonpx.test( computed ) ? + jQuery( elem ).position()[ prop ] + "px" : + computed; + } + } + ); +}); + + +// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods +jQuery.each( { Height: "height", Width: "width" }, function( name, type ) { + jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, function( defaultExtra, funcName ) { + // Margin is only for outerHeight, outerWidth + jQuery.fn[ funcName ] = function( margin, value ) { + var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ), + extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" ); + + return access( this, function( elem, type, value ) { + var doc; + + if ( jQuery.isWindow( elem ) ) { + // As of 5/8/2012 this will yield incorrect results for Mobile Safari, but there + // isn't a whole lot we can do. See pull request at this URL for discussion: + // https://github.com/jquery/jquery/pull/764 + return elem.document.documentElement[ "client" + name ]; + } + + // Get document width or height + if ( elem.nodeType === 9 ) { + doc = elem.documentElement; + + // Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height], + // whichever is greatest + return Math.max( + elem.body[ "scroll" + name ], doc[ "scroll" + name ], + elem.body[ "offset" + name ], doc[ "offset" + name ], + doc[ "client" + name ] + ); + } + + return value === undefined ? + // Get width or height on the element, requesting but not forcing parseFloat + jQuery.css( elem, type, extra ) : + + // Set width or height on the element + jQuery.style( elem, type, value, extra ); + }, type, chainable ? margin : undefined, chainable, null ); + }; + }); +}); + + +// The number of elements contained in the matched element set +jQuery.fn.size = function() { + return this.length; +}; + +jQuery.fn.andSelf = jQuery.fn.addBack; + + + + +// Register as a named AMD module, since jQuery can be concatenated with other +// files that may use define, but not via a proper concatenation script that +// understands anonymous AMD modules. A named AMD is safest and most robust +// way to register. Lowercase jquery is used because AMD module names are +// derived from file names, and jQuery is normally delivered in a lowercase +// file name. Do this after creating the global so that if an AMD module wants +// to call noConflict to hide this version of jQuery, it will work. + +// Note that for maximum portability, libraries that are not jQuery should +// declare themselves as anonymous modules, and avoid setting a global if an +// AMD loader is present. jQuery is a special case. For more information, see +// https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon + +if ( typeof define === "function" && define.amd ) { + define( "jquery", [], function() { + return jQuery; + }); +} + + + + +var + // Map over jQuery in case of overwrite + _jQuery = window.jQuery, + + // Map over the $ in case of overwrite + _$ = window.$; + +jQuery.noConflict = function( deep ) { + if ( window.$ === jQuery ) { + window.$ = _$; + } + + if ( deep && window.jQuery === jQuery ) { + window.jQuery = _jQuery; + } + + return jQuery; +}; + +// Expose jQuery and $ identifiers, even in AMD +// (#7102#comment:10, https://github.com/jquery/jquery/pull/557) +// and CommonJS for browser emulators (#13566) +if ( typeof noGlobal === strundefined ) { + window.jQuery = window.$ = jQuery; +} + + + + +return jQuery; + +})); diff --git a/src/legacy/design-studio/js/jqueryui-editable.js b/src/legacy/design-studio/js/jqueryui-editable.js new file mode 100644 index 0000000..83b9661 --- /dev/null +++ b/src/legacy/design-studio/js/jqueryui-editable.js @@ -0,0 +1,5065 @@ +/*! X-editable - v1.5.1 +* In-place editing with Twitter Bootstrap, jQuery UI or pure jQuery +* http://github.com/vitalets/x-editable +* Copyright (c) 2013 Vitaliy Potapov; Licensed MIT */ +/** +Form with single input element, two buttons and two states: normal/loading. +Applied as jQuery method to DIV tag (not to form tag!). This is because form can be in loading state when spinner shown. +Editableform is linked with one of input types, e.g. 'text', 'select' etc. + +@class editableform +@uses text +@uses textarea +**/ +(function ($) { + "use strict"; + + var EditableForm = function (div, options) { + this.options = $.extend({}, $.fn.editableform.defaults, options); + this.$div = $(div); //div, containing form. Not form tag. Not editable-element. + if(!this.options.scope) { + this.options.scope = this; + } + //nothing shown after init + }; + + EditableForm.prototype = { + constructor: EditableForm, + initInput: function() { //called once + //take input from options (as it is created in editable-element) + this.input = this.options.input; + + //set initial value + //todo: may be add check: typeof str === 'string' ? + this.value = this.input.str2value(this.options.value); + + //prerender: get input.$input + this.input.prerender(); + }, + initTemplate: function() { + this.$form = $($.fn.editableform.template); + }, + initButtons: function() { + var $btn = this.$form.find('.editable-buttons'); + $btn.append($.fn.editableform.buttons); + if(this.options.showbuttons === 'bottom') { + $btn.addClass('editable-buttons-bottom'); + } + }, + /** + Renders editableform + + @method render + **/ + render: function() { + //init loader + this.$loading = $($.fn.editableform.loading); + this.$div.empty().append(this.$loading); + + //init form template and buttons + this.initTemplate(); + if(this.options.showbuttons) { + this.initButtons(); + } else { + this.$form.find('.editable-buttons').remove(); + } + + //show loading state + this.showLoading(); + + //flag showing is form now saving value to server. + //It is needed to wait when closing form. + this.isSaving = false; + + /** + Fired when rendering starts + @event rendering + @param {Object} event event object + **/ + this.$div.triggerHandler('rendering'); + + //init input + this.initInput(); + + //append input to form + this.$form.find('div.editable-input').append(this.input.$tpl); + + //append form to container + this.$div.append(this.$form); + + //render input + $.when(this.input.render()) + .then($.proxy(function () { + //setup input to submit automatically when no buttons shown + if(!this.options.showbuttons) { + this.input.autosubmit(); + } + + //attach 'cancel' handler + this.$form.find('.editable-cancel').click($.proxy(this.cancel, this)); + + if(this.input.error) { + this.error(this.input.error); + this.$form.find('.editable-submit').attr('disabled', true); + this.input.$input.attr('disabled', true); + //prevent form from submitting + this.$form.submit(function(e){ e.preventDefault(); }); + } else { + this.error(false); + this.input.$input.removeAttr('disabled'); + this.$form.find('.editable-submit').removeAttr('disabled'); + var value = (this.value === null || this.value === undefined || this.value === '') ? this.options.defaultValue : this.value; + this.input.value2input(value); + //attach submit handler + this.$form.submit($.proxy(this.submit, this)); + } + + /** + Fired when form is rendered + @event rendered + @param {Object} event event object + **/ + this.$div.triggerHandler('rendered'); + + this.showForm(); + + //call postrender method to perform actions required visibility of form + if(this.input.postrender) { + this.input.postrender(); + } + }, this)); + }, + cancel: function() { + /** + Fired when form was cancelled by user + @event cancel + @param {Object} event event object + **/ + this.$div.triggerHandler('cancel'); + }, + showLoading: function() { + var w, h; + if(this.$form) { + //set loading size equal to form + w = this.$form.outerWidth(); + h = this.$form.outerHeight(); + if(w) { + this.$loading.width(w); + } + if(h) { + this.$loading.height(h); + } + this.$form.hide(); + } else { + //stretch loading to fill container width + w = this.$loading.parent().width(); + if(w) { + this.$loading.width(w); + } + } + this.$loading.show(); + }, + + showForm: function(activate) { + this.$loading.hide(); + this.$form.show(); + if(activate !== false) { + this.input.activate(); + } + /** + Fired when form is shown + @event show + @param {Object} event event object + **/ + this.$div.triggerHandler('show'); + }, + + error: function(msg) { + var $group = this.$form.find('.control-group'), + $block = this.$form.find('.editable-error-block'), + lines; + + if(msg === false) { + $group.removeClass($.fn.editableform.errorGroupClass); + $block.removeClass($.fn.editableform.errorBlockClass).empty().hide(); + } else { + //convert newline to <br> for more pretty error display + if(msg) { + lines = (''+msg).split('\n'); + for (var i = 0; i < lines.length; i++) { + lines[i] = $('<div>').text(lines[i]).html(); + } + msg = lines.join('<br>'); + } + $group.addClass($.fn.editableform.errorGroupClass); + $block.addClass($.fn.editableform.errorBlockClass).html(msg).show(); + } + }, + + submit: function(e) { + e.stopPropagation(); + e.preventDefault(); + + //get new value from input + var newValue = this.input.input2value(); + + //validation: if validate returns string or truthy value - means error + //if returns object like {newValue: '...'} => submitted value is reassigned to it + var error = this.validate(newValue); + if ($.type(error) === 'object' && error.newValue !== undefined) { + newValue = error.newValue; + this.input.value2input(newValue); + if(typeof error.msg === 'string') { + this.error(error.msg); + this.showForm(); + return; + } + } else if (error) { + this.error(error); + this.showForm(); + return; + } + + //if value not changed --> trigger 'nochange' event and return + /*jslint eqeq: true*/ + if (!this.options.savenochange && this.input.value2str(newValue) == this.input.value2str(this.value)) { + /*jslint eqeq: false*/ + /** + Fired when value not changed but form is submitted. Requires savenochange = false. + @event nochange + @param {Object} event event object + **/ + this.$div.triggerHandler('nochange'); + return; + } + + //convert value for submitting to server + var submitValue = this.input.value2submit(newValue); + + this.isSaving = true; + + //sending data to server + $.when(this.save(submitValue)) + .done($.proxy(function(response) { + this.isSaving = false; + + //run success callback + var res = typeof this.options.success === 'function' ? this.options.success.call(this.options.scope, response, newValue) : null; + + //if success callback returns false --> keep form open and do not activate input + if(res === false) { + this.error(false); + this.showForm(false); + return; + } + + //if success callback returns string --> keep form open, show error and activate input + if(typeof res === 'string') { + this.error(res); + this.showForm(); + return; + } + + //if success callback returns object like {newValue: <something>} --> use that value instead of submitted + //it is usefull if you want to chnage value in url-function + if(res && typeof res === 'object' && res.hasOwnProperty('newValue')) { + newValue = res.newValue; + } + + //clear error message + this.error(false); + this.value = newValue; + /** + Fired when form is submitted + @event save + @param {Object} event event object + @param {Object} params additional params + @param {mixed} params.newValue raw new value + @param {mixed} params.submitValue submitted value as string + @param {Object} params.response ajax response + + @example + $('#form-div').on('save'), function(e, params){ + if(params.newValue === 'username') {...} + }); + **/ + this.$div.triggerHandler('save', {newValue: newValue, submitValue: submitValue, response: response}); + }, this)) + .fail($.proxy(function(xhr) { + this.isSaving = false; + + var msg; + if(typeof this.options.error === 'function') { + msg = this.options.error.call(this.options.scope, xhr, newValue); + } else { + msg = typeof xhr === 'string' ? xhr : xhr.responseText || xhr.statusText || 'Unknown error!'; + } + + this.error(msg); + this.showForm(); + }, this)); + }, + + save: function(submitValue) { + //try parse composite pk defined as json string in data-pk + this.options.pk = $.fn.editableutils.tryParseJson(this.options.pk, true); + + var pk = (typeof this.options.pk === 'function') ? this.options.pk.call(this.options.scope) : this.options.pk, + /* + send on server in following cases: + 1. url is function + 2. url is string AND (pk defined OR send option = always) + */ + send = !!(typeof this.options.url === 'function' || (this.options.url && ((this.options.send === 'always') || (this.options.send === 'auto' && pk !== null && pk !== undefined)))), + params; + + if (send) { //send to server + this.showLoading(); + + //standard params + params = { + name: this.options.name || '', + value: submitValue, + pk: pk + }; + + //additional params + if(typeof this.options.params === 'function') { + params = this.options.params.call(this.options.scope, params); + } else { + //try parse json in single quotes (from data-params attribute) + this.options.params = $.fn.editableutils.tryParseJson(this.options.params, true); + $.extend(params, this.options.params); + } + + if(typeof this.options.url === 'function') { //user's function + return this.options.url.call(this.options.scope, params); + } else { + //send ajax to server and return deferred object + return $.ajax($.extend({ + url : this.options.url, + data : params, + type : 'POST' + }, this.options.ajaxOptions)); + } + } + }, + + validate: function (value) { + if (value === undefined) { + value = this.value; + } + if (typeof this.options.validate === 'function') { + return this.options.validate.call(this.options.scope, value); + } + }, + + option: function(key, value) { + if(key in this.options) { + this.options[key] = value; + } + + if(key === 'value') { + this.setValue(value); + } + + //do not pass option to input as it is passed in editable-element + }, + + setValue: function(value, convertStr) { + if(convertStr) { + this.value = this.input.str2value(value); + } else { + this.value = value; + } + + //if form is visible, update input + if(this.$form && this.$form.is(':visible')) { + this.input.value2input(this.value); + } + } + }; + + /* + Initialize editableform. Applied to jQuery object. + + @method $().editableform(options) + @params {Object} options + @example + var $form = $('<div>').editableform({ + type: 'text', + name: 'username', + url: '/post', + value: 'vitaliy' + }); + + //to display form you should call 'render' method + $form.editableform('render'); + */ + $.fn.editableform = function (option) { + var args = arguments; + return this.each(function () { + var $this = $(this), + data = $this.data('editableform'), + options = typeof option === 'object' && option; + if (!data) { + $this.data('editableform', (data = new EditableForm(this, options))); + } + + if (typeof option === 'string') { //call method + data[option].apply(data, Array.prototype.slice.call(args, 1)); + } + }); + }; + + //keep link to constructor to allow inheritance + $.fn.editableform.Constructor = EditableForm; + + //defaults + $.fn.editableform.defaults = { + /* see also defaults for input */ + + /** + Type of input. Can be <code>text|textarea|select|date|checklist</code> + + @property type + @type string + @default 'text' + **/ + type: 'text', + /** + Url for submit, e.g. <code>'/post'</code> + If function - it will be called instead of ajax. Function should return deferred object to run fail/done callbacks. + + @property url + @type string|function + @default null + @example + url: function(params) { + var d = new $.Deferred; + if(params.value === 'abc') { + return d.reject('error message'); //returning error via deferred object + } else { + //async saving data in js model + someModel.asyncSaveMethod({ + ..., + success: function(){ + d.resolve(); + } + }); + return d.promise(); + } + } + **/ + url:null, + /** + Additional params for submit. If defined as <code>object</code> - it is **appended** to original ajax data (pk, name and value). + If defined as <code>function</code> - returned object **overwrites** original ajax data. + @example + params: function(params) { + //originally params contain pk, name and value + params.a = 1; + return params; + } + + @property params + @type object|function + @default null + **/ + params:null, + /** + Name of field. Will be submitted on server. Can be taken from <code>id</code> attribute + + @property name + @type string + @default null + **/ + name: null, + /** + Primary key of editable object (e.g. record id in database). For composite keys use object, e.g. <code>{id: 1, lang: 'en'}</code>. + Can be calculated dynamically via function. + + @property pk + @type string|object|function + @default null + **/ + pk: null, + /** + Initial value. If not defined - will be taken from element's content. + For __select__ type should be defined (as it is ID of shown text). + + @property value + @type string|object + @default null + **/ + value: null, + /** + Value that will be displayed in input if original field value is empty (`null|undefined|''`). + + @property defaultValue + @type string|object + @default null + @since 1.4.6 + **/ + defaultValue: null, + /** + Strategy for sending data on server. Can be `auto|always|never`. + When 'auto' data will be sent on server **only if pk and url defined**, otherwise new value will be stored locally. + + @property send + @type string + @default 'auto' + **/ + send: 'auto', + /** + Function for client-side validation. If returns string - means validation not passed and string showed as error. + Since 1.5.1 you can modify submitted value by returning object from `validate`: + `{newValue: '...'}` or `{newValue: '...', msg: '...'}` + + @property validate + @type function + @default null + @example + validate: function(value) { + if($.trim(value) == '') { + return 'This field is required'; + } + } + **/ + validate: null, + /** + Success callback. Called when value successfully sent on server and **response status = 200**. + Usefull to work with json response. For example, if your backend response can be <code>{success: true}</code> + or <code>{success: false, msg: "server error"}</code> you can check it inside this callback. + If it returns **string** - means error occured and string is shown as error message. + If it returns **object like** <code>{newValue: <something>}</code> - it overwrites value, submitted by user. + Otherwise newValue simply rendered into element. + + @property success + @type function + @default null + @example + success: function(response, newValue) { + if(!response.success) return response.msg; + } + **/ + success: null, + /** + Error callback. Called when request failed (response status != 200). + Usefull when you want to parse error response and display a custom message. + Must return **string** - the message to be displayed in the error block. + + @property error + @type function + @default null + @since 1.4.4 + @example + error: function(response, newValue) { + if(response.status === 500) { + return 'Service unavailable. Please try later.'; + } else { + return response.responseText; + } + } + **/ + error: null, + /** + Additional options for submit ajax request. + List of values: http://api.jquery.com/jQuery.ajax + + @property ajaxOptions + @type object + @default null + @since 1.1.1 + @example + ajaxOptions: { + type: 'put', + dataType: 'json' + } + **/ + ajaxOptions: null, + /** + Where to show buttons: left(true)|bottom|false + Form without buttons is auto-submitted. + + @property showbuttons + @type boolean|string + @default true + @since 1.1.1 + **/ + showbuttons: true, + /** + Scope for callback methods (success, validate). + If <code>null</code> means editableform instance itself. + + @property scope + @type DOMElement|object + @default null + @since 1.2.0 + @private + **/ + scope: null, + /** + Whether to save or cancel value when it was not changed but form was submitted + + @property savenochange + @type boolean + @default false + @since 1.2.0 + **/ + savenochange: false + }; + + /* + Note: following params could redefined in engine: bootstrap or jqueryui: + Classes 'control-group' and 'editable-error-block' must always present! + */ + $.fn.editableform.template = '<form class="form-inline editableform">'+ + '<div class="control-group">' + + '<div><div class="editable-input"></div><div class="editable-buttons"></div></div>'+ + '<div class="editable-error-block"></div>' + + '</div>' + + '</form>'; + + //loading div + $.fn.editableform.loading = '<div class="editableform-loading"></div>'; + + //buttons + $.fn.editableform.buttons = '<button type="submit" class="editable-submit">ok</button>'+ + '<button type="button" class="editable-cancel">cancel</button>'; + + //error class attached to control-group + $.fn.editableform.errorGroupClass = null; + + //error class attached to editable-error-block + $.fn.editableform.errorBlockClass = 'editable-error'; + + //engine + $.fn.editableform.engine = 'jquery'; +}(window.jQuery)); + +/** +* EditableForm utilites +*/ +(function ($) { + "use strict"; + + //utils + $.fn.editableutils = { + /** + * classic JS inheritance function + */ + inherit: function (Child, Parent) { + var F = function() { }; + F.prototype = Parent.prototype; + Child.prototype = new F(); + Child.prototype.constructor = Child; + Child.superclass = Parent.prototype; + }, + + /** + * set caret position in input + * see http://stackoverflow.com/questions/499126/jquery-set-cursor-position-in-text-area + */ + setCursorPosition: function(elem, pos) { + if (elem.setSelectionRange) { + elem.setSelectionRange(pos, pos); + } else if (elem.createTextRange) { + var range = elem.createTextRange(); + range.collapse(true); + range.moveEnd('character', pos); + range.moveStart('character', pos); + range.select(); + } + }, + + /** + * function to parse JSON in *single* quotes. (jquery automatically parse only double quotes) + * That allows such code as: <a data-source="{'a': 'b', 'c': 'd'}"> + * safe = true --> means no exception will be thrown + * for details see http://stackoverflow.com/questions/7410348/how-to-set-json-format-to-html5-data-attributes-in-the-jquery + */ + tryParseJson: function(s, safe) { + if (typeof s === 'string' && s.length && s.match(/^[\{\[].*[\}\]]$/)) { + if (safe) { + try { + /*jslint evil: true*/ + s = (new Function('return ' + s))(); + /*jslint evil: false*/ + } catch (e) {} finally { + return s; + } + } else { + /*jslint evil: true*/ + s = (new Function('return ' + s))(); + /*jslint evil: false*/ + } + } + return s; + }, + + /** + * slice object by specified keys + */ + sliceObj: function(obj, keys, caseSensitive /* default: false */) { + var key, keyLower, newObj = {}; + + if (!$.isArray(keys) || !keys.length) { + return newObj; + } + + for (var i = 0; i < keys.length; i++) { + key = keys[i]; + if (obj.hasOwnProperty(key)) { + newObj[key] = obj[key]; + } + + if(caseSensitive === true) { + continue; + } + + //when getting data-* attributes via $.data() it's converted to lowercase. + //details: http://stackoverflow.com/questions/7602565/using-data-attributes-with-jquery + //workaround is code below. + keyLower = key.toLowerCase(); + if (obj.hasOwnProperty(keyLower)) { + newObj[key] = obj[keyLower]; + } + } + + return newObj; + }, + + /* + exclude complex objects from $.data() before pass to config + */ + getConfigData: function($element) { + var data = {}; + $.each($element.data(), function(k, v) { + if(typeof v !== 'object' || (v && typeof v === 'object' && (v.constructor === Object || v.constructor === Array))) { + data[k] = v; + } + }); + return data; + }, + + /* + returns keys of object + */ + objectKeys: function(o) { + if (Object.keys) { + return Object.keys(o); + } else { + if (o !== Object(o)) { + throw new TypeError('Object.keys called on a non-object'); + } + var k=[], p; + for (p in o) { + if (Object.prototype.hasOwnProperty.call(o,p)) { + k.push(p); + } + } + return k; + } + + }, + + /** + method to escape html. + **/ + escape: function(str) { + return $('<div>').text(str).html(); + }, + + /* + returns array items from sourceData having value property equal or inArray of 'value' + */ + itemsByValue: function(value, sourceData, valueProp) { + if(!sourceData || value === null) { + return []; + } + + if (typeof(valueProp) !== "function") { + var idKey = valueProp || 'value'; + valueProp = function (e) { return e[idKey]; }; + } + + var isValArray = $.isArray(value), + result = [], + that = this; + + $.each(sourceData, function(i, o) { + if(o.children) { + result = result.concat(that.itemsByValue(value, o.children, valueProp)); + } else { + /*jslint eqeq: true*/ + if(isValArray) { + if($.grep(value, function(v){ return v == (o && typeof o === 'object' ? valueProp(o) : o); }).length) { + result.push(o); + } + } else { + var itemValue = (o && (typeof o === 'object')) ? valueProp(o) : o; + if(value == itemValue) { + result.push(o); + } + } + /*jslint eqeq: false*/ + } + }); + + return result; + }, + + /* + Returns input by options: type, mode. + */ + createInput: function(options) { + var TypeConstructor, typeOptions, input, + type = options.type; + + //`date` is some kind of virtual type that is transformed to one of exact types + //depending on mode and core lib + if(type === 'date') { + //inline + if(options.mode === 'inline') { + if($.fn.editabletypes.datefield) { + type = 'datefield'; + } else if($.fn.editabletypes.dateuifield) { + type = 'dateuifield'; + } + //popup + } else { + if($.fn.editabletypes.date) { + type = 'date'; + } else if($.fn.editabletypes.dateui) { + type = 'dateui'; + } + } + + //if type still `date` and not exist in types, replace with `combodate` that is base input + if(type === 'date' && !$.fn.editabletypes.date) { + type = 'combodate'; + } + } + + //`datetime` should be datetimefield in 'inline' mode + if(type === 'datetime' && options.mode === 'inline') { + type = 'datetimefield'; + } + + //change wysihtml5 to textarea for jquery UI and plain versions + if(type === 'wysihtml5' && !$.fn.editabletypes[type]) { + type = 'textarea'; + } + + //create input of specified type. Input will be used for converting value, not in form + if(typeof $.fn.editabletypes[type] === 'function') { + TypeConstructor = $.fn.editabletypes[type]; + typeOptions = this.sliceObj(options, this.objectKeys(TypeConstructor.defaults)); + input = new TypeConstructor(typeOptions); + return input; + } else { + $.error('Unknown type: '+ type); + return false; + } + }, + + //see http://stackoverflow.com/questions/7264899/detect-css-transitions-using-javascript-and-without-modernizr + supportsTransitions: function () { + var b = document.body || document.documentElement, + s = b.style, + p = 'transition', + v = ['Moz', 'Webkit', 'Khtml', 'O', 'ms']; + + if(typeof s[p] === 'string') { + return true; + } + + // Tests for vendor specific prop + p = p.charAt(0).toUpperCase() + p.substr(1); + for(var i=0; i<v.length; i++) { + if(typeof s[v[i] + p] === 'string') { + return true; + } + } + return false; + } + + }; +}(window.jQuery)); + +/** +Attaches stand-alone container with editable-form to HTML element. Element is used only for positioning, value is not stored anywhere.<br> +This method applied internally in <code>$().editable()</code>. You should subscribe on it's events (save / cancel) to get profit of it.<br> +Final realization can be different: bootstrap-popover, jqueryui-tooltip, poshytip, inline-div. It depends on which js file you include.<br> +Applied as jQuery method. + +@class editableContainer +@uses editableform +**/ +(function ($) { + "use strict"; + + var Popup = function (element, options) { + this.init(element, options); + }; + + var Inline = function (element, options) { + this.init(element, options); + }; + + //methods + Popup.prototype = { + containerName: null, //method to call container on element + containerDataName: null, //object name in element's .data() + innerCss: null, //tbd in child class + containerClass: 'editable-container editable-popup', //css class applied to container element + defaults: {}, //container itself defaults + + init: function(element, options) { + this.$element = $(element); + //since 1.4.1 container do not use data-* directly as they already merged into options. + this.options = $.extend({}, $.fn.editableContainer.defaults, options); + this.splitOptions(); + + //set scope of form callbacks to element + this.formOptions.scope = this.$element[0]; + + this.initContainer(); + + //flag to hide container, when saving value will finish + this.delayedHide = false; + + //bind 'destroyed' listener to destroy container when element is removed from dom + this.$element.on('destroyed', $.proxy(function(){ + this.destroy(); + }, this)); + + //attach document handler to close containers on click / escape + if(!$(document).data('editable-handlers-attached')) { + //close all on escape + $(document).on('keyup.editable', function (e) { + if (e.which === 27) { + $('.editable-open').editableContainer('hide'); + //todo: return focus on element + } + }); + + //close containers when click outside + //(mousedown could be better than click, it closes everything also on drag drop) + $(document).on('click.editable', function(e) { + var $target = $(e.target), i, + exclude_classes = ['.editable-container', + '.ui-datepicker-header', + '.datepicker', //in inline mode datepicker is rendered into body + '.modal-backdrop', + '.bootstrap-wysihtml5-insert-image-modal', + '.bootstrap-wysihtml5-insert-link-modal' + ]; + + //check if element is detached. It occurs when clicking in bootstrap datepicker + if (!$.contains(document.documentElement, e.target)) { + return; + } + + //for some reason FF 20 generates extra event (click) in select2 widget with e.target = document + //we need to filter it via construction below. See https://github.com/vitalets/x-editable/issues/199 + //Possibly related to http://stackoverflow.com/questions/10119793/why-does-firefox-react-differently-from-webkit-and-ie-to-click-event-on-selec + if($target.is(document)) { + return; + } + + //if click inside one of exclude classes --> no nothing + for(i=0; i<exclude_classes.length; i++) { + if($target.is(exclude_classes[i]) || $target.parents(exclude_classes[i]).length) { + return; + } + } + + //close all open containers (except one - target) + Popup.prototype.closeOthers(e.target); + }); + + $(document).data('editable-handlers-attached', true); + } + }, + + //split options on containerOptions and formOptions + splitOptions: function() { + this.containerOptions = {}; + this.formOptions = {}; + + if(!$.fn[this.containerName]) { + throw new Error(this.containerName + ' not found. Have you included corresponding js file?'); + } + + //keys defined in container defaults go to container, others go to form + for(var k in this.options) { + if(k in this.defaults) { + this.containerOptions[k] = this.options[k]; + } else { + this.formOptions[k] = this.options[k]; + } + } + }, + + /* + Returns jquery object of container + @method tip() + */ + tip: function() { + return this.container() ? this.container().$tip : null; + }, + + /* returns container object */ + container: function() { + var container; + //first, try get it by `containerDataName` + if(this.containerDataName) { + if(container = this.$element.data(this.containerDataName)) { + return container; + } + } + //second, try `containerName` + container = this.$element.data(this.containerName); + return container; + }, + + /* call native method of underlying container, e.g. this.$element.popover('method') */ + call: function() { + this.$element[this.containerName].apply(this.$element, arguments); + }, + + initContainer: function(){ + this.call(this.containerOptions); + }, + + renderForm: function() { + this.$form + .editableform(this.formOptions) + .on({ + save: $.proxy(this.save, this), //click on submit button (value changed) + nochange: $.proxy(function(){ this.hide('nochange'); }, this), //click on submit button (value NOT changed) + cancel: $.proxy(function(){ this.hide('cancel'); }, this), //click on calcel button + show: $.proxy(function() { + if(this.delayedHide) { + this.hide(this.delayedHide.reason); + this.delayedHide = false; + } else { + this.setPosition(); + } + }, this), //re-position container every time form is shown (occurs each time after loading state) + rendering: $.proxy(this.setPosition, this), //this allows to place container correctly when loading shown + resize: $.proxy(this.setPosition, this), //this allows to re-position container when form size is changed + rendered: $.proxy(function(){ + /** + Fired when container is shown and form is rendered (for select will wait for loading dropdown options). + **Note:** Bootstrap popover has own `shown` event that now cannot be separated from x-editable's one. + The workaround is to check `arguments.length` that is always `2` for x-editable. + + @event shown + @param {Object} event event object + @example + $('#username').on('shown', function(e, editable) { + editable.input.$input.val('overwriting value of input..'); + }); + **/ + /* + TODO: added second param mainly to distinguish from bootstrap's shown event. It's a hotfix that will be solved in future versions via namespaced events. + */ + this.$element.triggerHandler('shown', $(this.options.scope).data('editable')); + }, this) + }) + .editableform('render'); + }, + + /** + Shows container with form + @method show() + @param {boolean} closeAll Whether to close all other editable containers when showing this one. Default true. + **/ + /* Note: poshytip owerwrites this method totally! */ + show: function (closeAll) { + this.$element.addClass('editable-open'); + if(closeAll !== false) { + //close all open containers (except this) + this.closeOthers(this.$element[0]); + } + + //show container itself + this.innerShow(); + this.tip().addClass(this.containerClass); + + /* + Currently, form is re-rendered on every show. + The main reason is that we dont know, what will container do with content when closed: + remove(), detach() or just hide() - it depends on container. + + Detaching form itself before hide and re-insert before show is good solution, + but visually it looks ugly --> container changes size before hide. + */ + + //if form already exist - delete previous data + if(this.$form) { + //todo: destroy prev data! + //this.$form.destroy(); + } + + this.$form = $('<div>'); + + //insert form into container body + if(this.tip().is(this.innerCss)) { + //for inline container + this.tip().append(this.$form); + } else { + this.tip().find(this.innerCss).append(this.$form); + } + + //render form + this.renderForm(); + }, + + /** + Hides container with form + @method hide() + @param {string} reason Reason caused hiding. Can be <code>save|cancel|onblur|nochange|undefined (=manual)</code> + **/ + hide: function(reason) { + if(!this.tip() || !this.tip().is(':visible') || !this.$element.hasClass('editable-open')) { + return; + } + + //if form is saving value, schedule hide + if(this.$form.data('editableform').isSaving) { + this.delayedHide = {reason: reason}; + return; + } else { + this.delayedHide = false; + } + + this.$element.removeClass('editable-open'); + this.innerHide(); + + /** + Fired when container was hidden. It occurs on both save or cancel. + **Note:** Bootstrap popover has own `hidden` event that now cannot be separated from x-editable's one. + The workaround is to check `arguments.length` that is always `2` for x-editable. + + @event hidden + @param {object} event event object + @param {string} reason Reason caused hiding. Can be <code>save|cancel|onblur|nochange|manual</code> + @example + $('#username').on('hidden', function(e, reason) { + if(reason === 'save' || reason === 'cancel') { + //auto-open next editable + $(this).closest('tr').next().find('.editable').editable('show'); + } + }); + **/ + this.$element.triggerHandler('hidden', reason || 'manual'); + }, + + /* internal show method. To be overwritten in child classes */ + innerShow: function () { + + }, + + /* internal hide method. To be overwritten in child classes */ + innerHide: function () { + + }, + + /** + Toggles container visibility (show / hide) + @method toggle() + @param {boolean} closeAll Whether to close all other editable containers when showing this one. Default true. + **/ + toggle: function(closeAll) { + if(this.container() && this.tip() && this.tip().is(':visible')) { + this.hide(); + } else { + this.show(closeAll); + } + }, + + /* + Updates the position of container when content changed. + @method setPosition() + */ + setPosition: function() { + //tbd in child class + }, + + save: function(e, params) { + /** + Fired when new value was submitted. You can use <code>$(this).data('editableContainer')</code> inside handler to access to editableContainer instance + + @event save + @param {Object} event event object + @param {Object} params additional params + @param {mixed} params.newValue submitted value + @param {Object} params.response ajax response + @example + $('#username').on('save', function(e, params) { + //assuming server response: '{success: true}' + var pk = $(this).data('editableContainer').options.pk; + if(params.response && params.response.success) { + alert('value: ' + params.newValue + ' with pk: ' + pk + ' saved!'); + } else { + alert('error!'); + } + }); + **/ + this.$element.triggerHandler('save', params); + + //hide must be after trigger, as saving value may require methods of plugin, applied to input + this.hide('save'); + }, + + /** + Sets new option + + @method option(key, value) + @param {string} key + @param {mixed} value + **/ + option: function(key, value) { + this.options[key] = value; + if(key in this.containerOptions) { + this.containerOptions[key] = value; + this.setContainerOption(key, value); + } else { + this.formOptions[key] = value; + if(this.$form) { + this.$form.editableform('option', key, value); + } + } + }, + + setContainerOption: function(key, value) { + this.call('option', key, value); + }, + + /** + Destroys the container instance + @method destroy() + **/ + destroy: function() { + this.hide(); + this.innerDestroy(); + this.$element.off('destroyed'); + this.$element.removeData('editableContainer'); + }, + + /* to be overwritten in child classes */ + innerDestroy: function() { + + }, + + /* + Closes other containers except one related to passed element. + Other containers can be cancelled or submitted (depends on onblur option) + */ + closeOthers: function(element) { + $('.editable-open').each(function(i, el){ + //do nothing with passed element and it's children + if(el === element || $(el).find(element).length) { + return; + } + + //otherwise cancel or submit all open containers + var $el = $(el), + ec = $el.data('editableContainer'); + + if(!ec) { + return; + } + + if(ec.options.onblur === 'cancel') { + $el.data('editableContainer').hide('onblur'); + } else if(ec.options.onblur === 'submit') { + $el.data('editableContainer').tip().find('form').submit(); + } + }); + + }, + + /** + Activates input of visible container (e.g. set focus) + @method activate() + **/ + activate: function() { + if(this.tip && this.tip().is(':visible') && this.$form) { + this.$form.data('editableform').input.activate(); + } + } + + }; + + /** + jQuery method to initialize editableContainer. + + @method $().editableContainer(options) + @params {Object} options + @example + $('#edit').editableContainer({ + type: 'text', + url: '/post', + pk: 1, + value: 'hello' + }); + **/ + $.fn.editableContainer = function (option) { + var args = arguments; + return this.each(function () { + var $this = $(this), + dataKey = 'editableContainer', + data = $this.data(dataKey), + options = typeof option === 'object' && option, + Constructor = (options.mode === 'inline') ? Inline : Popup; + + if (!data) { + $this.data(dataKey, (data = new Constructor(this, options))); + } + + if (typeof option === 'string') { //call method + data[option].apply(data, Array.prototype.slice.call(args, 1)); + } + }); + }; + + //store constructors + $.fn.editableContainer.Popup = Popup; + $.fn.editableContainer.Inline = Inline; + + //defaults + $.fn.editableContainer.defaults = { + /** + Initial value of form input + + @property value + @type mixed + @default null + @private + **/ + value: null, + /** + Placement of container relative to element. Can be <code>top|right|bottom|left</code>. Not used for inline container. + + @property placement + @type string + @default 'top' + **/ + placement: 'top', + /** + Whether to hide container on save/cancel. + + @property autohide + @type boolean + @default true + @private + **/ + autohide: true, + /** + Action when user clicks outside the container. Can be <code>cancel|submit|ignore</code>. + Setting <code>ignore</code> allows to have several containers open. + + @property onblur + @type string + @default 'cancel' + @since 1.1.1 + **/ + onblur: 'cancel', + + /** + Animation speed (inline mode only) + @property anim + @type string + @default false + **/ + anim: false, + + /** + Mode of editable, can be `popup` or `inline` + + @property mode + @type string + @default 'popup' + @since 1.4.0 + **/ + mode: 'popup' + }; + + /* + * workaround to have 'destroyed' event to destroy popover when element is destroyed + * see http://stackoverflow.com/questions/2200494/jquery-trigger-event-when-an-element-is-removed-from-the-dom + */ + jQuery.event.special.destroyed = { + remove: function(o) { + if (o.handler) { + o.handler(); + } + } + }; + +}(window.jQuery)); + +/** +* Editable Inline +* --------------------- +*/ +(function ($) { + "use strict"; + + //copy prototype from EditableContainer + //extend methods + $.extend($.fn.editableContainer.Inline.prototype, $.fn.editableContainer.Popup.prototype, { + containerName: 'editableform', + innerCss: '.editable-inline', + containerClass: 'editable-container editable-inline', //css class applied to container element + + initContainer: function(){ + //container is <span> element + this.$tip = $('<span></span>'); + + //convert anim to miliseconds (int) + if(!this.options.anim) { + this.options.anim = 0; + } + }, + + splitOptions: function() { + //all options are passed to form + this.containerOptions = {}; + this.formOptions = this.options; + }, + + tip: function() { + return this.$tip; + }, + + innerShow: function () { + this.$element.hide(); + this.tip().insertAfter(this.$element).show(); + }, + + innerHide: function () { + this.$tip.hide(this.options.anim, $.proxy(function() { + this.$element.show(); + this.innerDestroy(); + }, this)); + }, + + innerDestroy: function() { + if(this.tip()) { + this.tip().empty().remove(); + } + } + }); + +}(window.jQuery)); +/** +Makes editable any HTML element on the page. Applied as jQuery method. + +@class editable +@uses editableContainer +**/ +(function ($) { + "use strict"; + + var Editable = function (element, options) { + this.$element = $(element); + //data-* has more priority over js options: because dynamically created elements may change data-* + this.options = $.extend({}, $.fn.editable.defaults, options, $.fn.editableutils.getConfigData(this.$element)); + if(this.options.selector) { + this.initLive(); + } else { + this.init(); + } + + //check for transition support + if(this.options.highlight && !$.fn.editableutils.supportsTransitions()) { + this.options.highlight = false; + } + }; + + Editable.prototype = { + constructor: Editable, + init: function () { + var isValueByText = false, + doAutotext, finalize; + + //name + this.options.name = this.options.name || this.$element.attr('id'); + + //create input of specified type. Input needed already here to convert value for initial display (e.g. show text by id for select) + //also we set scope option to have access to element inside input specific callbacks (e. g. source as function) + this.options.scope = this.$element[0]; + this.input = $.fn.editableutils.createInput(this.options); + if(!this.input) { + return; + } + + //set value from settings or by element's text + if (this.options.value === undefined || this.options.value === null) { + this.value = this.input.html2value($.trim(this.$element.html())); + isValueByText = true; + } else { + /* + value can be string when received from 'data-value' attribute + for complext objects value can be set as json string in data-value attribute, + e.g. data-value="{city: 'Moscow', street: 'Lenina'}" + */ + this.options.value = $.fn.editableutils.tryParseJson(this.options.value, true); + if(typeof this.options.value === 'string') { + this.value = this.input.str2value(this.options.value); + } else { + this.value = this.options.value; + } + } + + //add 'editable' class to every editable element + this.$element.addClass('editable'); + + //specifically for "textarea" add class .editable-pre-wrapped to keep linebreaks + if(this.input.type === 'textarea') { + this.$element.addClass('editable-pre-wrapped'); + } + + //attach handler activating editable. In disabled mode it just prevent default action (useful for links) + if(this.options.toggle !== 'manual') { + this.$element.addClass('editable-click'); + this.$element.on(this.options.toggle + '.editable', $.proxy(function(e){ + //prevent following link if editable enabled + if(!this.options.disabled) { + e.preventDefault(); + } + + //stop propagation not required because in document click handler it checks event target + //e.stopPropagation(); + + if(this.options.toggle === 'mouseenter') { + //for hover only show container + this.show(); + } else { + //when toggle='click' we should not close all other containers as they will be closed automatically in document click listener + var closeAll = (this.options.toggle !== 'click'); + this.toggle(closeAll); + } + }, this)); + } else { + this.$element.attr('tabindex', -1); //do not stop focus on element when toggled manually + } + + //if display is function it's far more convinient to have autotext = always to render correctly on init + //see https://github.com/vitalets/x-editable-yii/issues/34 + if(typeof this.options.display === 'function') { + this.options.autotext = 'always'; + } + + //check conditions for autotext: + switch(this.options.autotext) { + case 'always': + doAutotext = true; + break; + case 'auto': + //if element text is empty and value is defined and value not generated by text --> run autotext + doAutotext = !$.trim(this.$element.text()).length && this.value !== null && this.value !== undefined && !isValueByText; + break; + default: + doAutotext = false; + } + + //depending on autotext run render() or just finilize init + $.when(doAutotext ? this.render() : true).then($.proxy(function() { + if(this.options.disabled) { + this.disable(); + } else { + this.enable(); + } + /** + Fired when element was initialized by `$().editable()` method. + Please note that you should setup `init` handler **before** applying `editable`. + + @event init + @param {Object} event event object + @param {Object} editable editable instance (as here it cannot accessed via data('editable')) + @since 1.2.0 + @example + $('#username').on('init', function(e, editable) { + alert('initialized ' + editable.options.name); + }); + $('#username').editable(); + **/ + this.$element.triggerHandler('init', this); + }, this)); + }, + + /* + Initializes parent element for live editables + */ + initLive: function() { + //store selector + var selector = this.options.selector; + //modify options for child elements + this.options.selector = false; + this.options.autotext = 'never'; + //listen toggle events + this.$element.on(this.options.toggle + '.editable', selector, $.proxy(function(e){ + var $target = $(e.target); + if(!$target.data('editable')) { + //if delegated element initially empty, we need to clear it's text (that was manually set to `empty` by user) + //see https://github.com/vitalets/x-editable/issues/137 + if($target.hasClass(this.options.emptyclass)) { + $target.empty(); + } + $target.editable(this.options).trigger(e); + } + }, this)); + }, + + /* + Renders value into element's text. + Can call custom display method from options. + Can return deferred object. + @method render() + @param {mixed} response server response (if exist) to pass into display function + */ + render: function(response) { + //do not display anything + if(this.options.display === false) { + return; + } + + //if input has `value2htmlFinal` method, we pass callback in third param to be called when source is loaded + if(this.input.value2htmlFinal) { + return this.input.value2html(this.value, this.$element[0], this.options.display, response); + //if display method defined --> use it + } else if(typeof this.options.display === 'function') { + return this.options.display.call(this.$element[0], this.value, response); + //else use input's original value2html() method + } else { + return this.input.value2html(this.value, this.$element[0]); + } + }, + + /** + Enables editable + @method enable() + **/ + enable: function() { + this.options.disabled = false; + this.$element.removeClass('editable-disabled'); + this.handleEmpty(this.isEmpty); + if(this.options.toggle !== 'manual') { + if(this.$element.attr('tabindex') === '-1') { + this.$element.removeAttr('tabindex'); + } + } + }, + + /** + Disables editable + @method disable() + **/ + disable: function() { + this.options.disabled = true; + this.hide(); + this.$element.addClass('editable-disabled'); + this.handleEmpty(this.isEmpty); + //do not stop focus on this element + this.$element.attr('tabindex', -1); + }, + + /** + Toggles enabled / disabled state of editable element + @method toggleDisabled() + **/ + toggleDisabled: function() { + if(this.options.disabled) { + this.enable(); + } else { + this.disable(); + } + }, + + /** + Sets new option + + @method option(key, value) + @param {string|object} key option name or object with several options + @param {mixed} value option new value + @example + $('.editable').editable('option', 'pk', 2); + **/ + option: function(key, value) { + //set option(s) by object + if(key && typeof key === 'object') { + $.each(key, $.proxy(function(k, v){ + this.option($.trim(k), v); + }, this)); + return; + } + + //set option by string + this.options[key] = value; + + //disabled + if(key === 'disabled') { + return value ? this.disable() : this.enable(); + } + + //value + if(key === 'value') { + this.setValue(value); + } + + //transfer new option to container! + if(this.container) { + this.container.option(key, value); + } + + //pass option to input directly (as it points to the same in form) + if(this.input.option) { + this.input.option(key, value); + } + + }, + + /* + * set emptytext if element is empty + */ + handleEmpty: function (isEmpty) { + //do not handle empty if we do not display anything + if(this.options.display === false) { + return; + } + + /* + isEmpty may be set directly as param of method. + It is required when we enable/disable field and can't rely on content + as node content is text: "Empty" that is not empty %) + */ + if(isEmpty !== undefined) { + this.isEmpty = isEmpty; + } else { + //detect empty + //for some inputs we need more smart check + //e.g. wysihtml5 may have <br>, <p></p>, <img> + if(typeof(this.input.isEmpty) === 'function') { + this.isEmpty = this.input.isEmpty(this.$element); + } else { + this.isEmpty = $.trim(this.$element.html()) === ''; + } + } + + //emptytext shown only for enabled + if(!this.options.disabled) { + if (this.isEmpty) { + this.$element.html(this.options.emptytext); + if(this.options.emptyclass) { + this.$element.addClass(this.options.emptyclass); + } + } else if(this.options.emptyclass) { + this.$element.removeClass(this.options.emptyclass); + } + } else { + //below required if element disable property was changed + if(this.isEmpty) { + this.$element.empty(); + if(this.options.emptyclass) { + this.$element.removeClass(this.options.emptyclass); + } + } + } + }, + + /** + Shows container with form + @method show() + @param {boolean} closeAll Whether to close all other editable containers when showing this one. Default true. + **/ + show: function (closeAll) { + if(this.options.disabled) { + return; + } + + //init editableContainer: popover, tooltip, inline, etc.. + if(!this.container) { + var containerOptions = $.extend({}, this.options, { + value: this.value, + input: this.input //pass input to form (as it is already created) + }); + this.$element.editableContainer(containerOptions); + //listen `save` event + this.$element.on("save.internal", $.proxy(this.save, this)); + this.container = this.$element.data('editableContainer'); + } else if(this.container.tip().is(':visible')) { + return; + } + + //show container + this.container.show(closeAll); + }, + + /** + Hides container with form + @method hide() + **/ + hide: function () { + if(this.container) { + this.container.hide(); + } + }, + + /** + Toggles container visibility (show / hide) + @method toggle() + @param {boolean} closeAll Whether to close all other editable containers when showing this one. Default true. + **/ + toggle: function(closeAll) { + if(this.container && this.container.tip().is(':visible')) { + this.hide(); + } else { + this.show(closeAll); + } + }, + + /* + * called when form was submitted + */ + save: function(e, params) { + //mark element with unsaved class if needed + if(this.options.unsavedclass) { + /* + Add unsaved css to element if: + - url is not user's function + - value was not sent to server + - params.response === undefined, that means data was not sent + - value changed + */ + var sent = false; + sent = sent || typeof this.options.url === 'function'; + sent = sent || this.options.display === false; + sent = sent || params.response !== undefined; + sent = sent || (this.options.savenochange && this.input.value2str(this.value) !== this.input.value2str(params.newValue)); + + if(sent) { + this.$element.removeClass(this.options.unsavedclass); + } else { + this.$element.addClass(this.options.unsavedclass); + } + } + + //highlight when saving + if(this.options.highlight) { + var $e = this.$element, + bgColor = $e.css('background-color'); + + $e.css('background-color', this.options.highlight); + setTimeout(function(){ + if(bgColor === 'transparent') { + bgColor = ''; + } + $e.css('background-color', bgColor); + $e.addClass('editable-bg-transition'); + setTimeout(function(){ + $e.removeClass('editable-bg-transition'); + }, 1700); + }, 10); + } + + //set new value + this.setValue(params.newValue, false, params.response); + + /** + Fired when new value was submitted. You can use <code>$(this).data('editable')</code> to access to editable instance + + @event save + @param {Object} event event object + @param {Object} params additional params + @param {mixed} params.newValue submitted value + @param {Object} params.response ajax response + @example + $('#username').on('save', function(e, params) { + alert('Saved value: ' + params.newValue); + }); + **/ + //event itself is triggered by editableContainer. Description here is only for documentation + }, + + validate: function () { + if (typeof this.options.validate === 'function') { + return this.options.validate.call(this, this.value); + } + }, + + /** + Sets new value of editable + @method setValue(value, convertStr) + @param {mixed} value new value + @param {boolean} convertStr whether to convert value from string to internal format + **/ + setValue: function(value, convertStr, response) { + if(convertStr) { + this.value = this.input.str2value(value); + } else { + this.value = value; + } + if(this.container) { + this.container.option('value', this.value); + } + $.when(this.render(response)) + .then($.proxy(function() { + this.handleEmpty(); + }, this)); + }, + + /** + Activates input of visible container (e.g. set focus) + @method activate() + **/ + activate: function() { + if(this.container) { + this.container.activate(); + } + }, + + /** + Removes editable feature from element + @method destroy() + **/ + destroy: function() { + this.disable(); + + if(this.container) { + this.container.destroy(); + } + + this.input.destroy(); + + if(this.options.toggle !== 'manual') { + this.$element.removeClass('editable-click'); + this.$element.off(this.options.toggle + '.editable'); + } + + this.$element.off("save.internal"); + + this.$element.removeClass('editable editable-open editable-disabled'); + this.$element.removeData('editable'); + } + }; + + /* EDITABLE PLUGIN DEFINITION + * ======================= */ + + /** + jQuery method to initialize editable element. + + @method $().editable(options) + @params {Object} options + @example + $('#username').editable({ + type: 'text', + url: '/post', + pk: 1 + }); + **/ + $.fn.editable = function (option) { + //special API methods returning non-jquery object + var result = {}, args = arguments, datakey = 'editable'; + switch (option) { + /** + Runs client-side validation for all matched editables + + @method validate() + @returns {Object} validation errors map + @example + $('#username, #fullname').editable('validate'); + // possible result: + { + username: "username is required", + fullname: "fullname should be minimum 3 letters length" + } + **/ + case 'validate': + this.each(function () { + var $this = $(this), data = $this.data(datakey), error; + if (data && (error = data.validate())) { + result[data.options.name] = error; + } + }); + return result; + + /** + Returns current values of editable elements. + Note that it returns an **object** with name-value pairs, not a value itself. It allows to get data from several elements. + If value of some editable is `null` or `undefined` it is excluded from result object. + When param `isSingle` is set to **true** - it is supposed you have single element and will return value of editable instead of object. + + @method getValue() + @param {bool} isSingle whether to return just value of single element + @returns {Object} object of element names and values + @example + $('#username, #fullname').editable('getValue'); + //result: + { + username: "superuser", + fullname: "John" + } + //isSingle = true + $('#username').editable('getValue', true); + //result "superuser" + **/ + case 'getValue': + if(arguments.length === 2 && arguments[1] === true) { //isSingle = true + result = this.eq(0).data(datakey).value; + } else { + this.each(function () { + var $this = $(this), data = $this.data(datakey); + if (data && data.value !== undefined && data.value !== null) { + result[data.options.name] = data.input.value2submit(data.value); + } + }); + } + return result; + + /** + This method collects values from several editable elements and submit them all to server. + Internally it runs client-side validation for all fields and submits only in case of success. + See <a href="#newrecord">creating new records</a> for details. + Since 1.5.1 `submit` can be applied to single element to send data programmatically. In that case + `url`, `success` and `error` is taken from initial options and you can just call `$('#username').editable('submit')`. + + @method submit(options) + @param {object} options + @param {object} options.url url to submit data + @param {object} options.data additional data to submit + @param {object} options.ajaxOptions additional ajax options + @param {function} options.error(obj) error handler + @param {function} options.success(obj,config) success handler + @returns {Object} jQuery object + **/ + case 'submit': //collects value, validate and submit to server for creating new record + var config = arguments[1] || {}, + $elems = this, + errors = this.editable('validate'); + + // validation ok + if($.isEmptyObject(errors)) { + var ajaxOptions = {}; + + // for single element use url, success etc from options + if($elems.length === 1) { + var editable = $elems.data('editable'); + //standard params + var params = { + name: editable.options.name || '', + value: editable.input.value2submit(editable.value), + pk: (typeof editable.options.pk === 'function') ? + editable.options.pk.call(editable.options.scope) : + editable.options.pk + }; + + //additional params + if(typeof editable.options.params === 'function') { + params = editable.options.params.call(editable.options.scope, params); + } else { + //try parse json in single quotes (from data-params attribute) + editable.options.params = $.fn.editableutils.tryParseJson(editable.options.params, true); + $.extend(params, editable.options.params); + } + + ajaxOptions = { + url: editable.options.url, + data: params, + type: 'POST' + }; + + // use success / error from options + config.success = config.success || editable.options.success; + config.error = config.error || editable.options.error; + + // multiple elements + } else { + var values = this.editable('getValue'); + + ajaxOptions = { + url: config.url, + data: values, + type: 'POST' + }; + } + + // ajax success callabck (response 200 OK) + ajaxOptions.success = typeof config.success === 'function' ? function(response) { + config.success.call($elems, response, config); + } : $.noop; + + // ajax error callabck + ajaxOptions.error = typeof config.error === 'function' ? function() { + config.error.apply($elems, arguments); + } : $.noop; + + // extend ajaxOptions + if(config.ajaxOptions) { + $.extend(ajaxOptions, config.ajaxOptions); + } + + // extra data + if(config.data) { + $.extend(ajaxOptions.data, config.data); + } + + // perform ajax request + $.ajax(ajaxOptions); + } else { //client-side validation error + if(typeof config.error === 'function') { + config.error.call($elems, errors); + } + } + return this; + } + + //return jquery object + return this.each(function () { + var $this = $(this), + data = $this.data(datakey), + options = typeof option === 'object' && option; + + //for delegated targets do not store `editable` object for element + //it's allows several different selectors. + //see: https://github.com/vitalets/x-editable/issues/312 + if(options && options.selector) { + data = new Editable(this, options); + return; + } + + if (!data) { + $this.data(datakey, (data = new Editable(this, options))); + } + + if (typeof option === 'string') { //call method + data[option].apply(data, Array.prototype.slice.call(args, 1)); + } + }); + }; + + + $.fn.editable.defaults = { + /** + Type of input. Can be <code>text|textarea|select|date|checklist</code> and more + + @property type + @type string + @default 'text' + **/ + type: 'text', + /** + Sets disabled state of editable + + @property disabled + @type boolean + @default false + **/ + disabled: false, + /** + How to toggle editable. Can be <code>click|dblclick|mouseenter|manual</code>. + When set to <code>manual</code> you should manually call <code>show/hide</code> methods of editable. + **Note**: if you call <code>show</code> or <code>toggle</code> inside **click** handler of some DOM element, + you need to apply <code>e.stopPropagation()</code> because containers are being closed on any click on document. + + @example + $('#edit-button').click(function(e) { + e.stopPropagation(); + $('#username').editable('toggle'); + }); + + @property toggle + @type string + @default 'click' + **/ + toggle: 'click', + /** + Text shown when element is empty. + + @property emptytext + @type string + @default 'Empty' + **/ + emptytext: 'Empty', + /** + Allows to automatically set element's text based on it's value. Can be <code>auto|always|never</code>. Useful for select and date. + For example, if dropdown list is <code>{1: 'a', 2: 'b'}</code> and element's value set to <code>1</code>, it's html will be automatically set to <code>'a'</code>. + <code>auto</code> - text will be automatically set only if element is empty. + <code>always|never</code> - always(never) try to set element's text. + + @property autotext + @type string + @default 'auto' + **/ + autotext: 'auto', + /** + Initial value of input. If not set, taken from element's text. + Note, that if element's text is empty - text is automatically generated from value and can be customized (see `autotext` option). + For example, to display currency sign: + @example + <a id="price" data-type="text" data-value="100"></a> + <script> + $('#price').editable({ + ... + display: function(value) { + $(this).text(value + '$'); + } + }) + </script> + + @property value + @type mixed + @default element's text + **/ + value: null, + /** + Callback to perform custom displaying of value in element's text. + If `null`, default input's display used. + If `false`, no displaying methods will be called, element's text will never change. + Runs under element's scope. + _**Parameters:**_ + + * `value` current value to be displayed + * `response` server response (if display called after ajax submit), since 1.4.0 + + For _inputs with source_ (select, checklist) parameters are different: + + * `value` current value to be displayed + * `sourceData` array of items for current input (e.g. dropdown items) + * `response` server response (if display called after ajax submit), since 1.4.0 + + To get currently selected items use `$.fn.editableutils.itemsByValue(value, sourceData)`. + + @property display + @type function|boolean + @default null + @since 1.2.0 + @example + display: function(value, sourceData) { + //display checklist as comma-separated values + var html = [], + checked = $.fn.editableutils.itemsByValue(value, sourceData); + + if(checked.length) { + $.each(checked, function(i, v) { html.push($.fn.editableutils.escape(v.text)); }); + $(this).html(html.join(', ')); + } else { + $(this).empty(); + } + } + **/ + display: null, + /** + Css class applied when editable text is empty. + + @property emptyclass + @type string + @since 1.4.1 + @default editable-empty + **/ + emptyclass: 'editable-empty', + /** + Css class applied when value was stored but not sent to server (`pk` is empty or `send = 'never'`). + You may set it to `null` if you work with editables locally and submit them together. + + @property unsavedclass + @type string + @since 1.4.1 + @default editable-unsaved + **/ + unsavedclass: 'editable-unsaved', + /** + If selector is provided, editable will be delegated to the specified targets. + Usefull for dynamically generated DOM elements. + **Please note**, that delegated targets can't be initialized with `emptytext` and `autotext` options, + as they actually become editable only after first click. + You should manually set class `editable-click` to these elements. + Also, if element originally empty you should add class `editable-empty`, set `data-value=""` and write emptytext into element: + + @property selector + @type string + @since 1.4.1 + @default null + @example + <div id="user"> + <!-- empty --> + <a href="#" data-name="username" data-type="text" class="editable-click editable-empty" data-value="" title="Username">Empty</a> + <!-- non-empty --> + <a href="#" data-name="group" data-type="select" data-source="/groups" data-value="1" class="editable-click" title="Group">Operator</a> + </div> + + <script> + $('#user').editable({ + selector: 'a', + url: '/post', + pk: 1 + }); + </script> + **/ + selector: null, + /** + Color used to highlight element after update. Implemented via CSS3 transition, works in modern browsers. + + @property highlight + @type string|boolean + @since 1.4.5 + @default #FFFF80 + **/ + highlight: '#FFFF80' + }; + +}(window.jQuery)); + +/** +AbstractInput - base class for all editable inputs. +It defines interface to be implemented by any input type. +To create your own input you can inherit from this class. + +@class abstractinput +**/ +(function ($) { + "use strict"; + + //types + $.fn.editabletypes = {}; + + var AbstractInput = function () { }; + + AbstractInput.prototype = { + /** + Initializes input + + @method init() + **/ + init: function(type, options, defaults) { + this.type = type; + this.options = $.extend({}, defaults, options); + }, + + /* + this method called before render to init $tpl that is inserted in DOM + */ + prerender: function() { + this.$tpl = $(this.options.tpl); //whole tpl as jquery object + this.$input = this.$tpl; //control itself, can be changed in render method + this.$clear = null; //clear button + this.error = null; //error message, if input cannot be rendered + }, + + /** + Renders input from tpl. Can return jQuery deferred object. + Can be overwritten in child objects + + @method render() + **/ + render: function() { + + }, + + /** + Sets element's html by value. + + @method value2html(value, element) + @param {mixed} value + @param {DOMElement} element + **/ + value2html: function(value, element) { + $(element)[this.options.escape ? 'text' : 'html']($.trim(value)); + }, + + /** + Converts element's html to value + + @method html2value(html) + @param {string} html + @returns {mixed} + **/ + html2value: function(html) { + return $('<div>').html(html).text(); + }, + + /** + Converts value to string (for internal compare). For submitting to server used value2submit(). + + @method value2str(value) + @param {mixed} value + @returns {string} + **/ + value2str: function(value) { + return value; + }, + + /** + Converts string received from server into value. Usually from `data-value` attribute. + + @method str2value(str) + @param {string} str + @returns {mixed} + **/ + str2value: function(str) { + return str; + }, + + /** + Converts value for submitting to server. Result can be string or object. + + @method value2submit(value) + @param {mixed} value + @returns {mixed} + **/ + value2submit: function(value) { + return value; + }, + + /** + Sets value of input. + + @method value2input(value) + @param {mixed} value + **/ + value2input: function(value) { + this.$input.val(value); + }, + + /** + Returns value of input. Value can be object (e.g. datepicker) + + @method input2value() + **/ + input2value: function() { + return this.$input.val(); + }, + + /** + Activates input. For text it sets focus. + + @method activate() + **/ + activate: function() { + if(this.$input.is(':visible')) { + this.$input.focus(); + } + }, + + /** + Creates input. + + @method clear() + **/ + clear: function() { + this.$input.val(null); + }, + + /** + method to escape html. + **/ + escape: function(str) { + return $('<div>').text(str).html(); + }, + + /** + attach handler to automatically submit form when value changed (useful when buttons not shown) + **/ + autosubmit: function() { + + }, + + /** + Additional actions when destroying element + **/ + destroy: function() { + }, + + // -------- helper functions -------- + setClass: function() { + if(this.options.inputclass) { + this.$input.addClass(this.options.inputclass); + } + }, + + setAttr: function(attr) { + if (this.options[attr] !== undefined && this.options[attr] !== null) { + this.$input.attr(attr, this.options[attr]); + } + }, + + option: function(key, value) { + this.options[key] = value; + } + + }; + + AbstractInput.defaults = { + /** + HTML template of input. Normally you should not change it. + + @property tpl + @type string + @default '' + **/ + tpl: '', + /** + CSS class automatically applied to input + + @property inputclass + @type string + @default null + **/ + inputclass: null, + + /** + If `true` - html will be escaped in content of element via $.text() method. + If `false` - html will not be escaped, $.html() used. + When you use own `display` function, this option obviosly has no effect. + + @property escape + @type boolean + @since 1.5.0 + @default true + **/ + escape: true, + + //scope for external methods (e.g. source defined as function) + //for internal use only + scope: null, + + //need to re-declare showbuttons here to get it's value from common config (passed only options existing in defaults) + showbuttons: true + }; + + $.extend($.fn.editabletypes, {abstractinput: AbstractInput}); + +}(window.jQuery)); + +/** +List - abstract class for inputs that have source option loaded from js array or via ajax + +@class list +@extends abstractinput +**/ +(function ($) { + "use strict"; + + var List = function (options) { + + }; + + $.fn.editableutils.inherit(List, $.fn.editabletypes.abstractinput); + + $.extend(List.prototype, { + render: function () { + var deferred = $.Deferred(); + + this.error = null; + this.onSourceReady(function () { + this.renderList(); + deferred.resolve(); + }, function () { + this.error = this.options.sourceError; + deferred.resolve(); + }); + + return deferred.promise(); + }, + + html2value: function (html) { + return null; //can't set value by text + }, + + value2html: function (value, element, display, response) { + var deferred = $.Deferred(), + success = function () { + if(typeof display === 'function') { + //custom display method + display.call(element, value, this.sourceData, response); + } else { + this.value2htmlFinal(value, element); + } + deferred.resolve(); + }; + + //for null value just call success without loading source + if(value === null) { + success.call(this); + } else { + this.onSourceReady(success, function () { deferred.resolve(); }); + } + + return deferred.promise(); + }, + + // ------------- additional functions ------------ + + onSourceReady: function (success, error) { + //run source if it function + var source; + if ($.isFunction(this.options.source)) { + source = this.options.source.call(this.options.scope); + this.sourceData = null; + //note: if function returns the same source as URL - sourceData will be taken from cahce and no extra request performed + } else { + source = this.options.source; + } + + //if allready loaded just call success + if(this.options.sourceCache && $.isArray(this.sourceData)) { + success.call(this); + return; + } + + //try parse json in single quotes (for double quotes jquery does automatically) + try { + source = $.fn.editableutils.tryParseJson(source, false); + } catch (e) { + error.call(this); + return; + } + + //loading from url + if (typeof source === 'string') { + //try to get sourceData from cache + if(this.options.sourceCache) { + var cacheID = source, + cache; + + if (!$(document).data(cacheID)) { + $(document).data(cacheID, {}); + } + cache = $(document).data(cacheID); + + //check for cached data + if (cache.loading === false && cache.sourceData) { //take source from cache + this.sourceData = cache.sourceData; + this.doPrepend(); + success.call(this); + return; + } else if (cache.loading === true) { //cache is loading, put callback in stack to be called later + cache.callbacks.push($.proxy(function () { + this.sourceData = cache.sourceData; + this.doPrepend(); + success.call(this); + }, this)); + + //also collecting error callbacks + cache.err_callbacks.push($.proxy(error, this)); + return; + } else { //no cache yet, activate it + cache.loading = true; + cache.callbacks = []; + cache.err_callbacks = []; + } + } + + //ajaxOptions for source. Can be overwritten bt options.sourceOptions + var ajaxOptions = $.extend({ + url: source, + type: 'get', + cache: false, + dataType: 'json', + success: $.proxy(function (data) { + if(cache) { + cache.loading = false; + } + this.sourceData = this.makeArray(data); + if($.isArray(this.sourceData)) { + if(cache) { + //store result in cache + cache.sourceData = this.sourceData; + //run success callbacks for other fields waiting for this source + $.each(cache.callbacks, function () { this.call(); }); + } + this.doPrepend(); + success.call(this); + } else { + error.call(this); + if(cache) { + //run error callbacks for other fields waiting for this source + $.each(cache.err_callbacks, function () { this.call(); }); + } + } + }, this), + error: $.proxy(function () { + error.call(this); + if(cache) { + cache.loading = false; + //run error callbacks for other fields + $.each(cache.err_callbacks, function () { this.call(); }); + } + }, this) + }, this.options.sourceOptions); + + //loading sourceData from server + $.ajax(ajaxOptions); + + } else { //options as json/array + this.sourceData = this.makeArray(source); + + if($.isArray(this.sourceData)) { + this.doPrepend(); + success.call(this); + } else { + error.call(this); + } + } + }, + + doPrepend: function () { + if(this.options.prepend === null || this.options.prepend === undefined) { + return; + } + + if(!$.isArray(this.prependData)) { + //run prepend if it is function (once) + if ($.isFunction(this.options.prepend)) { + this.options.prepend = this.options.prepend.call(this.options.scope); + } + + //try parse json in single quotes + this.options.prepend = $.fn.editableutils.tryParseJson(this.options.prepend, true); + + //convert prepend from string to object + if (typeof this.options.prepend === 'string') { + this.options.prepend = {'': this.options.prepend}; + } + + this.prependData = this.makeArray(this.options.prepend); + } + + if($.isArray(this.prependData) && $.isArray(this.sourceData)) { + this.sourceData = this.prependData.concat(this.sourceData); + } + }, + + /* + renders input list + */ + renderList: function() { + // this method should be overwritten in child class + }, + + /* + set element's html by value + */ + value2htmlFinal: function(value, element) { + // this method should be overwritten in child class + }, + + /** + * convert data to array suitable for sourceData, e.g. [{value: 1, text: 'abc'}, {...}] + */ + makeArray: function(data) { + var count, obj, result = [], item, iterateItem; + if(!data || typeof data === 'string') { + return null; + } + + if($.isArray(data)) { //array + /* + function to iterate inside item of array if item is object. + Caclulates count of keys in item and store in obj. + */ + iterateItem = function (k, v) { + obj = {value: k, text: v}; + if(count++ >= 2) { + return false;// exit from `each` if item has more than one key. + } + }; + + for(var i = 0; i < data.length; i++) { + item = data[i]; + if(typeof item === 'object') { + count = 0; //count of keys inside item + $.each(item, iterateItem); + //case: [{val1: 'text1'}, {val2: 'text2} ...] + if(count === 1) { + result.push(obj); + //case: [{value: 1, text: 'text1'}, {value: 2, text: 'text2'}, ...] + } else if(count > 1) { + //removed check of existance: item.hasOwnProperty('value') && item.hasOwnProperty('text') + if(item.children) { + item.children = this.makeArray(item.children); + } + result.push(item); + } + } else { + //case: ['text1', 'text2' ...] + result.push({value: item, text: item}); + } + } + } else { //case: {val1: 'text1', val2: 'text2, ...} + $.each(data, function (k, v) { + result.push({value: k, text: v}); + }); + } + return result; + }, + + option: function(key, value) { + this.options[key] = value; + if(key === 'source') { + this.sourceData = null; + } + if(key === 'prepend') { + this.prependData = null; + } + } + + }); + + List.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, { + /** + Source data for list. + If **array** - it should be in format: `[{value: 1, text: "text1"}, {value: 2, text: "text2"}, ...]` + For compability, object format is also supported: `{"1": "text1", "2": "text2" ...}` but it does not guarantee elements order. + + If **string** - considered ajax url to load items. In that case results will be cached for fields with the same source and name. See also `sourceCache` option. + + If **function**, it should return data in format above (since 1.4.0). + + Since 1.4.1 key `children` supported to render OPTGROUP (for **select** input only). + `[{text: "group1", children: [{value: 1, text: "text1"}, {value: 2, text: "text2"}]}, ...]` + + + @property source + @type string | array | object | function + @default null + **/ + source: null, + /** + Data automatically prepended to the beginning of dropdown list. + + @property prepend + @type string | array | object | function + @default false + **/ + prepend: false, + /** + Error message when list cannot be loaded (e.g. ajax error) + + @property sourceError + @type string + @default Error when loading list + **/ + sourceError: 'Error when loading list', + /** + if <code>true</code> and source is **string url** - results will be cached for fields with the same source. + Usefull for editable column in grid to prevent extra requests. + + @property sourceCache + @type boolean + @default true + @since 1.2.0 + **/ + sourceCache: true, + /** + Additional ajax options to be used in $.ajax() when loading list from server. + Useful to send extra parameters (`data` key) or change request method (`type` key). + + @property sourceOptions + @type object|function + @default null + @since 1.5.0 + **/ + sourceOptions: null + }); + + $.fn.editabletypes.list = List; + +}(window.jQuery)); + +/** +Text input + +@class text +@extends abstractinput +@final +@example +<a href="#" id="username" data-type="text" data-pk="1">awesome</a> +<script> +$(function(){ + $('#username').editable({ + url: '/post', + title: 'Enter username' + }); +}); +</script> +**/ +(function ($) { + "use strict"; + + var Text = function (options) { + this.init('text', options, Text.defaults); + }; + + $.fn.editableutils.inherit(Text, $.fn.editabletypes.abstractinput); + + $.extend(Text.prototype, { + render: function() { + this.renderClear(); + this.setClass(); + this.setAttr('placeholder'); + }, + + activate: function() { + if(this.$input.is(':visible')) { + this.$input.focus(); + $.fn.editableutils.setCursorPosition(this.$input.get(0), this.$input.val().length); + if(this.toggleClear) { + this.toggleClear(); + } + } + }, + + //render clear button + renderClear: function() { + if (this.options.clear) { + this.$clear = $('<span class="editable-clear-x"></span>'); + this.$input.after(this.$clear) + .css('padding-right', 24) + .keyup($.proxy(function(e) { + //arrows, enter, tab, etc + if(~$.inArray(e.keyCode, [40,38,9,13,27])) { + return; + } + + clearTimeout(this.t); + var that = this; + this.t = setTimeout(function() { + that.toggleClear(e); + }, 100); + + }, this)) + .parent().css('position', 'relative'); + + this.$clear.click($.proxy(this.clear, this)); + } + }, + + postrender: function() { + /* + //now `clear` is positioned via css + if(this.$clear) { + //can position clear button only here, when form is shown and height can be calculated +// var h = this.$input.outerHeight(true) || 20, + var h = this.$clear.parent().height(), + delta = (h - this.$clear.height()) / 2; + + //this.$clear.css({bottom: delta, right: delta}); + } + */ + }, + + //show / hide clear button + toggleClear: function(e) { + if(!this.$clear) { + return; + } + + var len = this.$input.val().length, + visible = this.$clear.is(':visible'); + + if(len && !visible) { + this.$clear.show(); + } + + if(!len && visible) { + this.$clear.hide(); + } + }, + + clear: function() { + this.$clear.hide(); + this.$input.val('').focus(); + } + }); + + Text.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, { + /** + @property tpl + @default <input type="text"> + **/ + tpl: '<input type="text">', + /** + Placeholder attribute of input. Shown when input is empty. + + @property placeholder + @type string + @default null + **/ + placeholder: null, + + /** + Whether to show `clear` button + + @property clear + @type boolean + @default true + **/ + clear: true + }); + + $.fn.editabletypes.text = Text; + +}(window.jQuery)); + +/** +Textarea input + +@class textarea +@extends abstractinput +@final +@example +<a href="#" id="comments" data-type="textarea" data-pk="1">awesome comment!</a> +<script> +$(function(){ + $('#comments').editable({ + url: '/post', + title: 'Enter comments', + rows: 10 + }); +}); +</script> +**/ +(function ($) { + "use strict"; + + var Textarea = function (options) { + this.init('textarea', options, Textarea.defaults); + }; + + $.fn.editableutils.inherit(Textarea, $.fn.editabletypes.abstractinput); + + $.extend(Textarea.prototype, { + render: function () { + this.setClass(); + this.setAttr('placeholder'); + this.setAttr('rows'); + + //ctrl + enter + this.$input.keydown(function (e) { + if (e.ctrlKey && e.which === 13) { + $(this).closest('form').submit(); + } + }); + }, + + //using `white-space: pre-wrap` solves \n <--> BR conversion very elegant! + /* + value2html: function(value, element) { + var html = '', lines; + if(value) { + lines = value.split("\n"); + for (var i = 0; i < lines.length; i++) { + lines[i] = $('<div>').text(lines[i]).html(); + } + html = lines.join('<br>'); + } + $(element).html(html); + }, + + html2value: function(html) { + if(!html) { + return ''; + } + + var regex = new RegExp(String.fromCharCode(10), 'g'); + var lines = html.split(/<br\s*\/?>/i); + for (var i = 0; i < lines.length; i++) { + var text = $('<div>').html(lines[i]).text(); + + // Remove newline characters (\n) to avoid them being converted by value2html() method + // thus adding extra <br> tags + text = text.replace(regex, ''); + + lines[i] = text; + } + return lines.join("\n"); + }, + */ + activate: function() { + $.fn.editabletypes.text.prototype.activate.call(this); + } + }); + + Textarea.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, { + /** + @property tpl + @default <textarea></textarea> + **/ + tpl:'<textarea></textarea>', + /** + @property inputclass + @default input-large + **/ + inputclass: 'input-large', + /** + Placeholder attribute of input. Shown when input is empty. + + @property placeholder + @type string + @default null + **/ + placeholder: null, + /** + Number of rows in textarea + + @property rows + @type integer + @default 7 + **/ + rows: 7 + }); + + $.fn.editabletypes.textarea = Textarea; + +}(window.jQuery)); + +/** +Select (dropdown) + +@class select +@extends list +@final +@example +<a href="#" id="status" data-type="select" data-pk="1" data-url="/post" data-title="Select status"></a> +<script> +$(function(){ + $('#status').editable({ + value: 2, + source: [ + {value: 1, text: 'Active'}, + {value: 2, text: 'Blocked'}, + {value: 3, text: 'Deleted'} + ] + }); +}); +</script> +**/ +(function ($) { + "use strict"; + + var Select = function (options) { + this.init('select', options, Select.defaults); + }; + + $.fn.editableutils.inherit(Select, $.fn.editabletypes.list); + + $.extend(Select.prototype, { + renderList: function() { + this.$input.empty(); + + var fillItems = function($el, data) { + var attr; + if($.isArray(data)) { + for(var i=0; i<data.length; i++) { + attr = {}; + if(data[i].children) { + attr.label = data[i].text; + $el.append(fillItems($('<optgroup>', attr), data[i].children)); + } else { + attr.value = data[i].value; + if(data[i].disabled) { + attr.disabled = true; + } + $el.append($('<option>', attr).text(data[i].text)); + } + } + } + return $el; + }; + + fillItems(this.$input, this.sourceData); + + this.setClass(); + + //enter submit + this.$input.on('keydown.editable', function (e) { + if (e.which === 13) { + $(this).closest('form').submit(); + } + }); + }, + + value2htmlFinal: function(value, element) { + var text = '', + items = $.fn.editableutils.itemsByValue(value, this.sourceData); + + if(items.length) { + text = items[0].text; + } + + //$(element).text(text); + $.fn.editabletypes.abstractinput.prototype.value2html.call(this, text, element); + }, + + autosubmit: function() { + this.$input.off('keydown.editable').on('change.editable', function(){ + $(this).closest('form').submit(); + }); + } + }); + + Select.defaults = $.extend({}, $.fn.editabletypes.list.defaults, { + /** + @property tpl + @default <select></select> + **/ + tpl:'<select></select>' + }); + + $.fn.editabletypes.select = Select; + +}(window.jQuery)); + +/** +List of checkboxes. +Internally value stored as javascript array of values. + +@class checklist +@extends list +@final +@example +<a href="#" id="options" data-type="checklist" data-pk="1" data-url="/post" data-title="Select options"></a> +<script> +$(function(){ + $('#options').editable({ + value: [2, 3], + source: [ + {value: 1, text: 'option1'}, + {value: 2, text: 'option2'}, + {value: 3, text: 'option3'} + ] + }); +}); +</script> +**/ +(function ($) { + "use strict"; + + var Checklist = function (options) { + this.init('checklist', options, Checklist.defaults); + }; + + $.fn.editableutils.inherit(Checklist, $.fn.editabletypes.list); + + $.extend(Checklist.prototype, { + renderList: function() { + var $label, $div; + + this.$tpl.empty(); + + if(!$.isArray(this.sourceData)) { + return; + } + + for(var i=0; i<this.sourceData.length; i++) { + $label = $('<label>').append($('<input>', { + type: 'checkbox', + value: this.sourceData[i].value + })) + .append($('<span>').text(' '+this.sourceData[i].text)); + + $('<div>').append($label).appendTo(this.$tpl); + } + + this.$input = this.$tpl.find('input[type="checkbox"]'); + this.setClass(); + }, + + value2str: function(value) { + return $.isArray(value) ? value.sort().join($.trim(this.options.separator)) : ''; + }, + + //parse separated string + str2value: function(str) { + var reg, value = null; + if(typeof str === 'string' && str.length) { + reg = new RegExp('\\s*'+$.trim(this.options.separator)+'\\s*'); + value = str.split(reg); + } else if($.isArray(str)) { + value = str; + } else { + value = [str]; + } + return value; + }, + + //set checked on required checkboxes + value2input: function(value) { + this.$input.prop('checked', false); + if($.isArray(value) && value.length) { + this.$input.each(function(i, el) { + var $el = $(el); + // cannot use $.inArray as it performs strict comparison + $.each(value, function(j, val){ + /*jslint eqeq: true*/ + if($el.val() == val) { + /*jslint eqeq: false*/ + $el.prop('checked', true); + } + }); + }); + } + }, + + input2value: function() { + var checked = []; + this.$input.filter(':checked').each(function(i, el) { + checked.push($(el).val()); + }); + return checked; + }, + + //collect text of checked boxes + value2htmlFinal: function(value, element) { + var html = [], + checked = $.fn.editableutils.itemsByValue(value, this.sourceData), + escape = this.options.escape; + + if(checked.length) { + $.each(checked, function(i, v) { + var text = escape ? $.fn.editableutils.escape(v.text) : v.text; + html.push(text); + }); + $(element).html(html.join('<br>')); + } else { + $(element).empty(); + } + }, + + activate: function() { + this.$input.first().focus(); + }, + + autosubmit: function() { + this.$input.on('keydown', function(e){ + if (e.which === 13) { + $(this).closest('form').submit(); + } + }); + } + }); + + Checklist.defaults = $.extend({}, $.fn.editabletypes.list.defaults, { + /** + @property tpl + @default <div></div> + **/ + tpl:'<div class="editable-checklist"></div>', + + /** + @property inputclass + @type string + @default null + **/ + inputclass: null, + + /** + Separator of values when reading from `data-value` attribute + + @property separator + @type string + @default ',' + **/ + separator: ',' + }); + + $.fn.editabletypes.checklist = Checklist; + +}(window.jQuery)); + +/** +HTML5 input types. +Following types are supported: + +* password +* email +* url +* tel +* number +* range +* time + +Learn more about html5 inputs: +http://www.w3.org/wiki/HTML5_form_additions +To check browser compatibility please see: +https://developer.mozilla.org/en-US/docs/HTML/Element/Input + +@class html5types +@extends text +@final +@since 1.3.0 +@example +<a href="#" id="email" data-type="email" data-pk="1">admin@example.com</a> +<script> +$(function(){ + $('#email').editable({ + url: '/post', + title: 'Enter email' + }); +}); +</script> +**/ + +/** +@property tpl +@default depends on type +**/ + +/* +Password +*/ +(function ($) { + "use strict"; + + var Password = function (options) { + this.init('password', options, Password.defaults); + }; + $.fn.editableutils.inherit(Password, $.fn.editabletypes.text); + $.extend(Password.prototype, { + //do not display password, show '[hidden]' instead + value2html: function(value, element) { + if(value) { + $(element).text('[hidden]'); + } else { + $(element).empty(); + } + }, + //as password not displayed, should not set value by html + html2value: function(html) { + return null; + } + }); + Password.defaults = $.extend({}, $.fn.editabletypes.text.defaults, { + tpl: '<input type="password">' + }); + $.fn.editabletypes.password = Password; +}(window.jQuery)); + + +/* +Email +*/ +(function ($) { + "use strict"; + + var Email = function (options) { + this.init('email', options, Email.defaults); + }; + $.fn.editableutils.inherit(Email, $.fn.editabletypes.text); + Email.defaults = $.extend({}, $.fn.editabletypes.text.defaults, { + tpl: '<input type="email">' + }); + $.fn.editabletypes.email = Email; +}(window.jQuery)); + + +/* +Url +*/ +(function ($) { + "use strict"; + + var Url = function (options) { + this.init('url', options, Url.defaults); + }; + $.fn.editableutils.inherit(Url, $.fn.editabletypes.text); + Url.defaults = $.extend({}, $.fn.editabletypes.text.defaults, { + tpl: '<input type="url">' + }); + $.fn.editabletypes.url = Url; +}(window.jQuery)); + + +/* +Tel +*/ +(function ($) { + "use strict"; + + var Tel = function (options) { + this.init('tel', options, Tel.defaults); + }; + $.fn.editableutils.inherit(Tel, $.fn.editabletypes.text); + Tel.defaults = $.extend({}, $.fn.editabletypes.text.defaults, { + tpl: '<input type="tel">' + }); + $.fn.editabletypes.tel = Tel; +}(window.jQuery)); + + +/* +Number +*/ +(function ($) { + "use strict"; + + var NumberInput = function (options) { + this.init('number', options, NumberInput.defaults); + }; + $.fn.editableutils.inherit(NumberInput, $.fn.editabletypes.text); + $.extend(NumberInput.prototype, { + render: function () { + NumberInput.superclass.render.call(this); + this.setAttr('min'); + this.setAttr('max'); + this.setAttr('step'); + }, + postrender: function() { + if(this.$clear) { + //increase right ffset for up/down arrows + this.$clear.css({right: 24}); + /* + //can position clear button only here, when form is shown and height can be calculated + var h = this.$input.outerHeight(true) || 20, + delta = (h - this.$clear.height()) / 2; + + //add 12px to offset right for up/down arrows + this.$clear.css({top: delta, right: delta + 16}); + */ + } + } + }); + NumberInput.defaults = $.extend({}, $.fn.editabletypes.text.defaults, { + tpl: '<input type="number">', + inputclass: 'input-mini', + min: null, + max: null, + step: null + }); + $.fn.editabletypes.number = NumberInput; +}(window.jQuery)); + + +/* +Range (inherit from number) +*/ +(function ($) { + "use strict"; + + var Range = function (options) { + this.init('range', options, Range.defaults); + }; + $.fn.editableutils.inherit(Range, $.fn.editabletypes.number); + $.extend(Range.prototype, { + render: function () { + this.$input = this.$tpl.filter('input'); + + this.setClass(); + this.setAttr('min'); + this.setAttr('max'); + this.setAttr('step'); + + this.$input.on('input', function(){ + $(this).siblings('output').text($(this).val()); + }); + }, + activate: function() { + this.$input.focus(); + } + }); + Range.defaults = $.extend({}, $.fn.editabletypes.number.defaults, { + tpl: '<input type="range"><output style="width: 30px; display: inline-block"></output>', + inputclass: 'input-medium' + }); + $.fn.editabletypes.range = Range; +}(window.jQuery)); + +/* +Time +*/ +(function ($) { + "use strict"; + + var Time = function (options) { + this.init('time', options, Time.defaults); + }; + //inherit from abstract, as inheritance from text gives selection error. + $.fn.editableutils.inherit(Time, $.fn.editabletypes.abstractinput); + $.extend(Time.prototype, { + render: function() { + this.setClass(); + } + }); + Time.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, { + tpl: '<input type="time">' + }); + $.fn.editabletypes.time = Time; +}(window.jQuery)); + +/** +Select2 input. Based on amazing work of Igor Vaynberg https://github.com/ivaynberg/select2. +Please see [original select2 docs](http://ivaynberg.github.com/select2) for detailed description and options. + +You should manually download and include select2 distributive: + + <link href="select2/select2.css" rel="stylesheet" type="text/css"></link> + <script src="select2/select2.js"></script> + +To make it **bootstrap-styled** you can use css from [here](https://github.com/t0m/select2-bootstrap-css): + + <link href="select2-bootstrap.css" rel="stylesheet" type="text/css"></link> + +**Note:** currently `autotext` feature does not work for select2 with `ajax` remote source. +You need initially put both `data-value` and element's text youself: + + <a href="#" data-type="select2" data-value="1">Text1</a> + + +@class select2 +@extends abstractinput +@since 1.4.1 +@final +@example +<a href="#" id="country" data-type="select2" data-pk="1" data-value="ru" data-url="/post" data-title="Select country"></a> +<script> +$(function(){ + //local source + $('#country').editable({ + source: [ + {id: 'gb', text: 'Great Britain'}, + {id: 'us', text: 'United States'}, + {id: 'ru', text: 'Russia'} + ], + select2: { + multiple: true + } + }); + //remote source (simple) + $('#country').editable({ + source: '/getCountries', + select2: { + placeholder: 'Select Country', + minimumInputLength: 1 + } + }); + //remote source (advanced) + $('#country').editable({ + select2: { + placeholder: 'Select Country', + allowClear: true, + minimumInputLength: 3, + id: function (item) { + return item.CountryId; + }, + ajax: { + url: '/getCountries', + dataType: 'json', + data: function (term, page) { + return { query: term }; + }, + results: function (data, page) { + return { results: data }; + } + }, + formatResult: function (item) { + return item.CountryName; + }, + formatSelection: function (item) { + return item.CountryName; + }, + initSelection: function (element, callback) { + return $.get('/getCountryById', { query: element.val() }, function (data) { + callback(data); + }); + } + } + }); +}); +</script> +**/ +(function ($) { + "use strict"; + + var Constructor = function (options) { + this.init('select2', options, Constructor.defaults); + + options.select2 = options.select2 || {}; + + this.sourceData = null; + + //placeholder + if(options.placeholder) { + options.select2.placeholder = options.placeholder; + } + + //if not `tags` mode, use source + if(!options.select2.tags && options.source) { + var source = options.source; + //if source is function, call it (once!) + if ($.isFunction(options.source)) { + source = options.source.call(options.scope); + } + + if (typeof source === 'string') { + options.select2.ajax = options.select2.ajax || {}; + //some default ajax params + if(!options.select2.ajax.data) { + options.select2.ajax.data = function(term) {return { query:term };}; + } + if(!options.select2.ajax.results) { + options.select2.ajax.results = function(data) { return {results:data };}; + } + options.select2.ajax.url = source; + } else { + //check format and convert x-editable format to select2 format (if needed) + this.sourceData = this.convertSource(source); + options.select2.data = this.sourceData; + } + } + + //overriding objects in config (as by default jQuery extend() is not recursive) + this.options.select2 = $.extend({}, Constructor.defaults.select2, options.select2); + + //detect whether it is multi-valued + this.isMultiple = this.options.select2.tags || this.options.select2.multiple; + this.isRemote = ('ajax' in this.options.select2); + + //store function returning ID of item + //should be here as used inautotext for local source + this.idFunc = this.options.select2.id; + if (typeof(this.idFunc) !== "function") { + var idKey = this.idFunc || 'id'; + this.idFunc = function (e) { return e[idKey]; }; + } + + //store function that renders text in select2 + this.formatSelection = this.options.select2.formatSelection; + if (typeof(this.formatSelection) !== "function") { + this.formatSelection = function (e) { return e.text; }; + } + }; + + $.fn.editableutils.inherit(Constructor, $.fn.editabletypes.abstractinput); + + $.extend(Constructor.prototype, { + render: function() { + this.setClass(); + + //can not apply select2 here as it calls initSelection + //over input that does not have correct value yet. + //apply select2 only in value2input + //this.$input.select2(this.options.select2); + + //when data is loaded via ajax, we need to know when it's done to populate listData + if(this.isRemote) { + //listen to loaded event to populate data + this.$input.on('select2-loaded', $.proxy(function(e) { + this.sourceData = e.items.results; + }, this)); + } + + //trigger resize of editableform to re-position container in multi-valued mode + if(this.isMultiple) { + this.$input.on('change', function() { + $(this).closest('form').parent().triggerHandler('resize'); + }); + } + }, + + value2html: function(value, element) { + var text = '', data, + that = this; + + if(this.options.select2.tags) { //in tags mode just assign value + data = value; + //data = $.fn.editableutils.itemsByValue(value, this.options.select2.tags, this.idFunc); + } else if(this.sourceData) { + data = $.fn.editableutils.itemsByValue(value, this.sourceData, this.idFunc); + } else { + //can not get list of possible values + //(e.g. autotext for select2 with ajax source) + } + + //data may be array (when multiple values allowed) + if($.isArray(data)) { + //collect selected data and show with separator + text = []; + $.each(data, function(k, v){ + text.push(v && typeof v === 'object' ? that.formatSelection(v) : v); + }); + } else if(data) { + text = that.formatSelection(data); + } + + text = $.isArray(text) ? text.join(this.options.viewseparator) : text; + + //$(element).text(text); + Constructor.superclass.value2html.call(this, text, element); + }, + + html2value: function(html) { + return this.options.select2.tags ? this.str2value(html, this.options.viewseparator) : null; + }, + + value2input: function(value) { + // if value array => join it anyway + if($.isArray(value)) { + value = value.join(this.getSeparator()); + } + + //for remote source just set value, text is updated by initSelection + if(!this.$input.data('select2')) { + this.$input.val(value); + this.$input.select2(this.options.select2); + } else { + //second argument needed to separate initial change from user's click (for autosubmit) + this.$input.val(value).trigger('change', true); + + //Uncaught Error: cannot call val() if initSelection() is not defined + //this.$input.select2('val', value); + } + + // if defined remote source AND no multiple mode AND no user's initSelection provided --> + // we should somehow get text for provided id. + // The solution is to use element's text as text for that id (exclude empty) + if(this.isRemote && !this.isMultiple && !this.options.select2.initSelection) { + // customId and customText are methods to extract `id` and `text` from data object + // we can use this workaround only if user did not define these methods + // otherwise we cant construct data object + var customId = this.options.select2.id, + customText = this.options.select2.formatSelection; + + if(!customId && !customText) { + var $el = $(this.options.scope); + if (!$el.data('editable').isEmpty) { + var data = {id: value, text: $el.text()}; + this.$input.select2('data', data); + } + } + } + }, + + input2value: function() { + return this.$input.select2('val'); + }, + + str2value: function(str, separator) { + if(typeof str !== 'string' || !this.isMultiple) { + return str; + } + + separator = separator || this.getSeparator(); + + var val, i, l; + + if (str === null || str.length < 1) { + return null; + } + val = str.split(separator); + for (i = 0, l = val.length; i < l; i = i + 1) { + val[i] = $.trim(val[i]); + } + + return val; + }, + + autosubmit: function() { + this.$input.on('change', function(e, isInitial){ + if(!isInitial) { + $(this).closest('form').submit(); + } + }); + }, + + getSeparator: function() { + return this.options.select2.separator || $.fn.select2.defaults.separator; + }, + + /* + Converts source from x-editable format: {value: 1, text: "1"} to + select2 format: {id: 1, text: "1"} + */ + convertSource: function(source) { + if($.isArray(source) && source.length && source[0].value !== undefined) { + for(var i = 0; i<source.length; i++) { + if(source[i].value !== undefined) { + source[i].id = source[i].value; + delete source[i].value; + } + } + } + return source; + }, + + destroy: function() { + if(this.$input.data('select2')) { + this.$input.select2('destroy'); + } + } + + }); + + Constructor.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, { + /** + @property tpl + @default <input type="hidden"> + **/ + tpl:'<input type="hidden">', + /** + Configuration of select2. [Full list of options](http://ivaynberg.github.com/select2). + + @property select2 + @type object + @default null + **/ + select2: null, + /** + Placeholder attribute of select + + @property placeholder + @type string + @default null + **/ + placeholder: null, + /** + Source data for select. It will be assigned to select2 `data` property and kept here just for convenience. + Please note, that format is different from simple `select` input: use 'id' instead of 'value'. + E.g. `[{id: 1, text: "text1"}, {id: 2, text: "text2"}, ...]`. + + @property source + @type array|string|function + @default null + **/ + source: null, + /** + Separator used to display tags. + + @property viewseparator + @type string + @default ', ' + **/ + viewseparator: ', ' + }); + + $.fn.editabletypes.select2 = Constructor; + +}(window.jQuery)); + +/** +* Combodate - 1.0.5 +* Dropdown date and time picker. +* Converts text input into dropdowns to pick day, month, year, hour, minute and second. +* Uses momentjs as datetime library http://momentjs.com. +* For i18n include corresponding file from https://github.com/timrwood/moment/tree/master/lang +* +* Confusion at noon and midnight - see http://en.wikipedia.org/wiki/12-hour_clock#Confusion_at_noon_and_midnight +* In combodate: +* 12:00 pm --> 12:00 (24-h format, midday) +* 12:00 am --> 00:00 (24-h format, midnight, start of day) +* +* Differs from momentjs parse rules: +* 00:00 pm, 12:00 pm --> 12:00 (24-h format, day not change) +* 00:00 am, 12:00 am --> 00:00 (24-h format, day not change) +* +* +* Author: Vitaliy Potapov +* Project page: http://github.com/vitalets/combodate +* Copyright (c) 2012 Vitaliy Potapov. Released under MIT License. +**/ +(function ($) { + + var Combodate = function (element, options) { + this.$element = $(element); + if(!this.$element.is('input')) { + $.error('Combodate should be applied to INPUT element'); + return; + } + this.options = $.extend({}, $.fn.combodate.defaults, options, this.$element.data()); + this.init(); + }; + + Combodate.prototype = { + constructor: Combodate, + init: function () { + this.map = { + //key regexp moment.method + day: ['D', 'date'], + month: ['M', 'month'], + year: ['Y', 'year'], + hour: ['[Hh]', 'hours'], + minute: ['m', 'minutes'], + second: ['s', 'seconds'], + ampm: ['[Aa]', ''] + }; + + this.$widget = $('<span class="combodate"></span>').html(this.getTemplate()); + + this.initCombos(); + + //update original input on change + this.$widget.on('change', 'select', $.proxy(function(e) { + this.$element.val(this.getValue()).change(); + // update days count if month or year changes + if (this.options.smartDays) { + if ($(e.target).is('.month') || $(e.target).is('.year')) { + this.fillCombo('day'); + } + } + }, this)); + + this.$widget.find('select').css('width', 'auto'); + + // hide original input and insert widget + this.$element.hide().after(this.$widget); + + // set initial value + this.setValue(this.$element.val() || this.options.value); + }, + + /* + Replace tokens in template with <select> elements + */ + getTemplate: function() { + var tpl = this.options.template; + + //first pass + $.each(this.map, function(k, v) { + v = v[0]; + var r = new RegExp(v+'+'), + token = v.length > 1 ? v.substring(1, 2) : v; + + tpl = tpl.replace(r, '{'+token+'}'); + }); + + //replace spaces with + tpl = tpl.replace(/ /g, ' '); + + //second pass + $.each(this.map, function(k, v) { + v = v[0]; + var token = v.length > 1 ? v.substring(1, 2) : v; + + tpl = tpl.replace('{'+token+'}', '<select class="'+k+'"></select>'); + }); + + return tpl; + }, + + /* + Initialize combos that presents in template + */ + initCombos: function() { + for (var k in this.map) { + var $c = this.$widget.find('.'+k); + // set properties like this.$day, this.$month etc. + this['$'+k] = $c.length ? $c : null; + // fill with items + this.fillCombo(k); + } + }, + + /* + Fill combo with items + */ + fillCombo: function(k) { + var $combo = this['$'+k]; + if (!$combo) { + return; + } + + // define method name to fill items, e.g `fillDays` + var f = 'fill' + k.charAt(0).toUpperCase() + k.slice(1); + var items = this[f](); + var value = $combo.val(); + + $combo.empty(); + for(var i=0; i<items.length; i++) { + $combo.append('<option value="'+items[i][0]+'">'+items[i][1]+'</option>'); + } + + $combo.val(value); + }, + + /* + Initialize items of combos. Handles `firstItem` option + */ + fillCommon: function(key) { + var values = [], + relTime; + + if(this.options.firstItem === 'name') { + //need both to support moment ver < 2 and >= 2 + relTime = moment.relativeTime || moment.langData()._relativeTime; + var header = typeof relTime[key] === 'function' ? relTime[key](1, true, key, false) : relTime[key]; + //take last entry (see momentjs lang files structure) + header = header.split(' ').reverse()[0]; + values.push(['', header]); + } else if(this.options.firstItem === 'empty') { + values.push(['', '']); + } + return values; + }, + + + /* + fill day + */ + fillDay: function() { + var items = this.fillCommon('d'), name, i, + twoDigit = this.options.template.indexOf('DD') !== -1, + daysCount = 31; + + // detect days count (depends on month and year) + // originally https://github.com/vitalets/combodate/pull/7 + if (this.options.smartDays && this.$month && this.$year) { + var month = parseInt(this.$month.val(), 10); + var year = parseInt(this.$year.val(), 10); + + if (!isNaN(month) && !isNaN(year)) { + daysCount = moment([year, month]).daysInMonth(); + } + } + + for (i = 1; i <= daysCount; i++) { + name = twoDigit ? this.leadZero(i) : i; + items.push([i, name]); + } + return items; + }, + + /* + fill month + */ + fillMonth: function() { + var items = this.fillCommon('M'), name, i, + longNames = this.options.template.indexOf('MMMM') !== -1, + shortNames = this.options.template.indexOf('MMM') !== -1, + twoDigit = this.options.template.indexOf('MM') !== -1; + + for(i=0; i<=11; i++) { + if(longNames) { + //see https://github.com/timrwood/momentjs.com/pull/36 + name = moment().date(1).month(i).format('MMMM'); + } else if(shortNames) { + name = moment().date(1).month(i).format('MMM'); + } else if(twoDigit) { + name = this.leadZero(i+1); + } else { + name = i+1; + } + items.push([i, name]); + } + return items; + }, + + /* + fill year + */ + fillYear: function() { + var items = [], name, i, + longNames = this.options.template.indexOf('YYYY') !== -1; + + for(i=this.options.maxYear; i>=this.options.minYear; i--) { + name = longNames ? i : (i+'').substring(2); + items[this.options.yearDescending ? 'push' : 'unshift']([i, name]); + } + + items = this.fillCommon('y').concat(items); + + return items; + }, + + /* + fill hour + */ + fillHour: function() { + var items = this.fillCommon('h'), name, i, + h12 = this.options.template.indexOf('h') !== -1, + h24 = this.options.template.indexOf('H') !== -1, + twoDigit = this.options.template.toLowerCase().indexOf('hh') !== -1, + min = h12 ? 1 : 0, + max = h12 ? 12 : 23; + + for(i=min; i<=max; i++) { + name = twoDigit ? this.leadZero(i) : i; + items.push([i, name]); + } + return items; + }, + + /* + fill minute + */ + fillMinute: function() { + var items = this.fillCommon('m'), name, i, + twoDigit = this.options.template.indexOf('mm') !== -1; + + for(i=0; i<=59; i+= this.options.minuteStep) { + name = twoDigit ? this.leadZero(i) : i; + items.push([i, name]); + } + return items; + }, + + /* + fill second + */ + fillSecond: function() { + var items = this.fillCommon('s'), name, i, + twoDigit = this.options.template.indexOf('ss') !== -1; + + for(i=0; i<=59; i+= this.options.secondStep) { + name = twoDigit ? this.leadZero(i) : i; + items.push([i, name]); + } + return items; + }, + + /* + fill ampm + */ + fillAmpm: function() { + var ampmL = this.options.template.indexOf('a') !== -1, + ampmU = this.options.template.indexOf('A') !== -1, + items = [ + ['am', ampmL ? 'am' : 'AM'], + ['pm', ampmL ? 'pm' : 'PM'] + ]; + return items; + }, + + /* + Returns current date value from combos. + If format not specified - `options.format` used. + If format = `null` - Moment object returned. + */ + getValue: function(format) { + var dt, values = {}, + that = this, + notSelected = false; + + //getting selected values + $.each(this.map, function(k, v) { + if(k === 'ampm') { + return; + } + var def = k === 'day' ? 1 : 0; + + values[k] = that['$'+k] ? parseInt(that['$'+k].val(), 10) : def; + + if(isNaN(values[k])) { + notSelected = true; + return false; + } + }); + + //if at least one visible combo not selected - return empty string + if(notSelected) { + return ''; + } + + //convert hours 12h --> 24h + if(this.$ampm) { + //12:00 pm --> 12:00 (24-h format, midday), 12:00 am --> 00:00 (24-h format, midnight, start of day) + if(values.hour === 12) { + values.hour = this.$ampm.val() === 'am' ? 0 : 12; + } else { + values.hour = this.$ampm.val() === 'am' ? values.hour : values.hour+12; + } + } + + dt = moment([values.year, values.month, values.day, values.hour, values.minute, values.second]); + + //highlight invalid date + this.highlight(dt); + + format = format === undefined ? this.options.format : format; + if(format === null) { + return dt.isValid() ? dt : null; + } else { + return dt.isValid() ? dt.format(format) : ''; + } + }, + + setValue: function(value) { + if(!value) { + return; + } + + var dt = typeof value === 'string' ? moment(value, this.options.format) : moment(value), + that = this, + values = {}; + + //function to find nearest value in select options + function getNearest($select, value) { + var delta = {}; + $select.children('option').each(function(i, opt){ + var optValue = $(opt).attr('value'), + distance; + + if(optValue === '') return; + distance = Math.abs(optValue - value); + if(typeof delta.distance === 'undefined' || distance < delta.distance) { + delta = {value: optValue, distance: distance}; + } + }); + return delta.value; + } + + if(dt.isValid()) { + //read values from date object + $.each(this.map, function(k, v) { + if(k === 'ampm') { + return; + } + values[k] = dt[v[1]](); + }); + + if(this.$ampm) { + //12:00 pm --> 12:00 (24-h format, midday), 12:00 am --> 00:00 (24-h format, midnight, start of day) + if(values.hour >= 12) { + values.ampm = 'pm'; + if(values.hour > 12) { + values.hour -= 12; + } + } else { + values.ampm = 'am'; + if(values.hour === 0) { + values.hour = 12; + } + } + } + + $.each(values, function(k, v) { + //call val() for each existing combo, e.g. this.$hour.val() + if(that['$'+k]) { + + if(k === 'minute' && that.options.minuteStep > 1 && that.options.roundTime) { + v = getNearest(that['$'+k], v); + } + + if(k === 'second' && that.options.secondStep > 1 && that.options.roundTime) { + v = getNearest(that['$'+k], v); + } + + that['$'+k].val(v); + } + }); + + // update days count + if (this.options.smartDays) { + this.fillCombo('day'); + } + + this.$element.val(dt.format(this.options.format)).change(); + } + }, + + /* + highlight combos if date is invalid + */ + highlight: function(dt) { + if(!dt.isValid()) { + if(this.options.errorClass) { + this.$widget.addClass(this.options.errorClass); + } else { + //store original border color + if(!this.borderColor) { + this.borderColor = this.$widget.find('select').css('border-color'); + } + this.$widget.find('select').css('border-color', 'red'); + } + } else { + if(this.options.errorClass) { + this.$widget.removeClass(this.options.errorClass); + } else { + this.$widget.find('select').css('border-color', this.borderColor); + } + } + }, + + leadZero: function(v) { + return v <= 9 ? '0' + v : v; + }, + + destroy: function() { + this.$widget.remove(); + this.$element.removeData('combodate').show(); + } + + //todo: clear method + }; + + $.fn.combodate = function ( option ) { + var d, args = Array.apply(null, arguments); + args.shift(); + + //getValue returns date as string / object (not jQuery object) + if(option === 'getValue' && this.length && (d = this.eq(0).data('combodate'))) { + return d.getValue.apply(d, args); + } + + return this.each(function () { + var $this = $(this), + data = $this.data('combodate'), + options = typeof option == 'object' && option; + if (!data) { + $this.data('combodate', (data = new Combodate(this, options))); + } + if (typeof option == 'string' && typeof data[option] == 'function') { + data[option].apply(data, args); + } + }); + }; + + $.fn.combodate.defaults = { + //in this format value stored in original input + format: 'DD-MM-YYYY HH:mm', + //in this format items in dropdowns are displayed + template: 'D / MMM / YYYY H : mm', + //initial value, can be `new Date()` + value: null, + minYear: 1970, + maxYear: 2015, + yearDescending: true, + minuteStep: 5, + secondStep: 1, + firstItem: 'empty', //'name', 'empty', 'none' + errorClass: null, + roundTime: true, // whether to round minutes and seconds if step > 1 + smartDays: false // whether days in combo depend on selected month: 31, 30, 28 + }; + +}(window.jQuery)); +/** +Combodate input - dropdown date and time picker. +Based on [combodate](http://vitalets.github.com/combodate) plugin (included). To use it you should manually include [momentjs](http://momentjs.com). + + <script src="js/moment.min.js"></script> + +Allows to input: + +* only date +* only time +* both date and time + +Please note, that format is taken from momentjs and **not compatible** with bootstrap-datepicker / jquery UI datepicker. +Internally value stored as `momentjs` object. + +@class combodate +@extends abstractinput +@final +@since 1.4.0 +@example +<a href="#" id="dob" data-type="combodate" data-pk="1" data-url="/post" data-value="1984-05-15" data-title="Select date"></a> +<script> +$(function(){ + $('#dob').editable({ + format: 'YYYY-MM-DD', + viewformat: 'DD.MM.YYYY', + template: 'D / MMMM / YYYY', + combodate: { + minYear: 2000, + maxYear: 2015, + minuteStep: 1 + } + } + }); +}); +</script> +**/ + +/*global moment*/ + +(function ($) { + "use strict"; + + var Constructor = function (options) { + this.init('combodate', options, Constructor.defaults); + + //by default viewformat equals to format + if(!this.options.viewformat) { + this.options.viewformat = this.options.format; + } + + //try parse combodate config defined as json string in data-combodate + options.combodate = $.fn.editableutils.tryParseJson(options.combodate, true); + + //overriding combodate config (as by default jQuery extend() is not recursive) + this.options.combodate = $.extend({}, Constructor.defaults.combodate, options.combodate, { + format: this.options.format, + template: this.options.template + }); + }; + + $.fn.editableutils.inherit(Constructor, $.fn.editabletypes.abstractinput); + + $.extend(Constructor.prototype, { + render: function () { + this.$input.combodate(this.options.combodate); + + if($.fn.editableform.engine === 'bs3') { + this.$input.siblings().find('select').addClass('form-control'); + } + + if(this.options.inputclass) { + this.$input.siblings().find('select').addClass(this.options.inputclass); + } + //"clear" link + /* + if(this.options.clear) { + this.$clear = $('<a href="#"></a>').html(this.options.clear).click($.proxy(function(e){ + e.preventDefault(); + e.stopPropagation(); + this.clear(); + }, this)); + + this.$tpl.parent().append($('<div class="editable-clear">').append(this.$clear)); + } + */ + }, + + value2html: function(value, element) { + var text = value ? value.format(this.options.viewformat) : ''; + //$(element).text(text); + Constructor.superclass.value2html.call(this, text, element); + }, + + html2value: function(html) { + return html ? moment(html, this.options.viewformat) : null; + }, + + value2str: function(value) { + return value ? value.format(this.options.format) : ''; + }, + + str2value: function(str) { + return str ? moment(str, this.options.format) : null; + }, + + value2submit: function(value) { + return this.value2str(value); + }, + + value2input: function(value) { + this.$input.combodate('setValue', value); + }, + + input2value: function() { + return this.$input.combodate('getValue', null); + }, + + activate: function() { + this.$input.siblings('.combodate').find('select').eq(0).focus(); + }, + + /* + clear: function() { + this.$input.data('datepicker').date = null; + this.$input.find('.active').removeClass('active'); + }, + */ + + autosubmit: function() { + + } + + }); + + Constructor.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, { + /** + @property tpl + @default <input type="text"> + **/ + tpl:'<input type="text">', + /** + @property inputclass + @default null + **/ + inputclass: null, + /** + Format used for sending value to server. Also applied when converting date from <code>data-value</code> attribute.<br> + See list of tokens in [momentjs docs](http://momentjs.com/docs/#/parsing/string-format) + + @property format + @type string + @default YYYY-MM-DD + **/ + format:'YYYY-MM-DD', + /** + Format used for displaying date. Also applied when converting date from element's text on init. + If not specified equals to `format`. + + @property viewformat + @type string + @default null + **/ + viewformat: null, + /** + Template used for displaying dropdowns. + + @property template + @type string + @default D / MMM / YYYY + **/ + template: 'D / MMM / YYYY', + /** + Configuration of combodate. + Full list of options: http://vitalets.github.com/combodate/#docs + + @property combodate + @type object + @default null + **/ + combodate: null + + /* + (not implemented yet) + Text shown as clear date button. + If <code>false</code> clear button will not be rendered. + + @property clear + @type boolean|string + @default 'x clear' + */ + //clear: '× clear' + }); + + $.fn.editabletypes.combodate = Constructor; + +}(window.jQuery)); + +/* +Editableform based on jQuery UI +*/ +(function ($) { + "use strict"; + + $.extend($.fn.editableform.Constructor.prototype, { + initButtons: function() { + var $btn = this.$form.find('.editable-buttons'); + $btn.append($.fn.editableform.buttons); + if(this.options.showbuttons === 'bottom') { + $btn.addClass('editable-buttons-bottom'); + } + + this.$form.find('.editable-submit').button({ + icons: { primary: "ui-icon-check" }, + text: false + }).removeAttr('title'); + this.$form.find('.editable-cancel').button({ + icons: { primary: "ui-icon-closethick" }, + text: false + }).removeAttr('title'); + } + }); + + //error classes + $.fn.editableform.errorGroupClass = null; + $.fn.editableform.errorBlockClass = 'ui-state-error'; + //engine + $.fn.editableform.engine = 'jquery-ui'; + +}(window.jQuery)); +/** +* Editable jQuery UI Tooltip +* --------------------- +* requires jquery ui 1.9.x +*/ +(function ($) { + "use strict"; + + //extend methods + $.extend($.fn.editableContainer.Popup.prototype, { + containerName: 'tooltip', //jQuery method, aplying the widget + //object name in element's .data() + containerDataName: 'ui-tooltip', + innerCss: '.ui-tooltip-content', + defaults: $.ui.tooltip.prototype.options, + + //split options on containerOptions and formOptions + splitOptions: function() { + this.containerOptions = {}; + this.formOptions = {}; + + //check that jQueryUI build contains tooltip widget + if(!$.ui[this.containerName]) { + $.error('Please use jQueryUI with "tooltip" widget! http://jqueryui.com/download'); + return; + } + + //defaults for tooltip + for(var k in this.options) { + if(k in this.defaults) { + this.containerOptions[k] = this.options[k]; + } else { + this.formOptions[k] = this.options[k]; + } + } + }, + + initContainer: function(){ + this.handlePlacement(); + $.extend(this.containerOptions, { + items: '*', + content: ' ', + track: false, + open: $.proxy(function() { + //disable events hiding tooltip by default + this.container()._on(this.container().element, { + mouseleave: function(e){ e.stopImmediatePropagation(); }, + focusout: function(e){ e.stopImmediatePropagation(); } + }); + }, this) + }); + + this.call(this.containerOptions); + + //disable standart triggering tooltip events + this.container()._off(this.container().element, 'mouseover focusin'); + }, + + tip: function() { + return this.container() ? this.container()._find(this.container().element) : null; + }, + + innerShow: function() { + this.call('open'); + var label = this.options.title || this.$element.data( "ui-tooltip-title") || this.$element.data( "originalTitle"); + this.tip().find(this.innerCss).empty().append($('<label>').text(label)); + }, + + innerHide: function() { + this.call('close'); + }, + + innerDestroy: function() { + /* tooltip destroys itself on hide */ + }, + + setPosition: function() { + this.tip().position( $.extend({ + of: this.$element + }, this.containerOptions.position ) ); + }, + + handlePlacement: function() { + var pos; + switch(this.options.placement) { + case 'top': + pos = { + my: "center bottom-5", + at: "center top", + collision: 'flipfit' + }; + break; + case 'right': + pos = { + my: "left+5 center", + at: "right center", + collision: 'flipfit' + }; + break; + case 'bottom': + pos = { + my: "center top+5", + at: "center bottom", + collision: 'flipfit' + }; + break; + case 'left': + pos = { + my: "right-5 center", + at: "left center", + collision: 'flipfit' + }; + break; + } + + this.containerOptions.position = pos; + } + + }); + +}(window.jQuery)); + +/** +jQuery UI Datepicker. +Description and examples: http://jqueryui.com/datepicker. +This input is also accessible as **date** type. Do not use it together with __bootstrap-datepicker__ as both apply <code>$().datepicker()</code> method. +For **i18n** you should include js file from here: https://github.com/jquery/jquery-ui/tree/master/ui/i18n. + +@class dateui +@extends abstractinput +@final +@example +<a href="#" id="dob" data-type="date" data-pk="1" data-url="/post" data-title="Select date">15/05/1984</a> +<script> +$(function(){ + $('#dob').editable({ + format: 'yyyy-mm-dd', + viewformat: 'dd/mm/yyyy', + datepicker: { + firstDay: 1 + } + } + }); +}); +</script> +**/ +(function ($) { + "use strict"; + + var DateUI = function (options) { + this.init('dateui', options, DateUI.defaults); + this.initPicker(options, DateUI.defaults); + }; + + $.fn.editableutils.inherit(DateUI, $.fn.editabletypes.abstractinput); + + $.extend(DateUI.prototype, { + initPicker: function(options, defaults) { + //by default viewformat equals to format + if(!this.options.viewformat) { + this.options.viewformat = this.options.format; + } + + //correct formats: replace yyyy with yy (for compatibility with bootstrap datepicker) + this.options.viewformat = this.options.viewformat.replace('yyyy', 'yy'); + this.options.format = this.options.format.replace('yyyy', 'yy'); + + //overriding datepicker config (as by default jQuery extend() is not recursive) + //since 1.4 datepicker internally uses viewformat instead of format. Format is for submit only + this.options.datepicker = $.extend({}, defaults.datepicker, options.datepicker, { + dateFormat: this.options.viewformat + }); + }, + + render: function () { + this.$input.datepicker(this.options.datepicker); + + //"clear" link + if(this.options.clear) { + this.$clear = $('<a href="#"></a>').html(this.options.clear).click($.proxy(function(e){ + e.preventDefault(); + e.stopPropagation(); + this.clear(); + }, this)); + + this.$tpl.parent().append($('<div class="editable-clear">').append(this.$clear)); + } + }, + + value2html: function(value, element) { + var text = $.datepicker.formatDate(this.options.viewformat, value); + DateUI.superclass.value2html.call(this, text, element); + }, + + html2value: function(html) { + if(typeof html !== 'string') { + return html; + } + + //if string does not match format, UI datepicker throws exception + var d; + try { + d = $.datepicker.parseDate(this.options.viewformat, html); + } catch(e) {} + + return d; + }, + + value2str: function(value) { + return $.datepicker.formatDate(this.options.format, value); + }, + + str2value: function(str) { + if(typeof str !== 'string') { + return str; + } + + //if string does not match format, UI datepicker throws exception + var d; + try { + d = $.datepicker.parseDate(this.options.format, str); + } catch(e) {} + + return d; + }, + + value2submit: function(value) { + return this.value2str(value); + }, + + value2input: function(value) { + this.$input.datepicker('setDate', value); + }, + + input2value: function() { + return this.$input.datepicker('getDate'); + }, + + activate: function() { + }, + + clear: function() { + this.$input.datepicker('setDate', null); + // submit automatically whe that are no buttons + if(this.isAutosubmit) { + this.submit(); + } + }, + + autosubmit: function() { + this.isAutosubmit = true; + this.$input.on('mouseup', 'table.ui-datepicker-calendar a.ui-state-default', $.proxy(this.submit, this)); + }, + + submit: function() { + var $form = this.$input.closest('form'); + setTimeout(function() { + $form.submit(); + }, 200); + } + + }); + + DateUI.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, { + /** + @property tpl + @default <div></div> + **/ + tpl:'<div class="editable-date"></div>', + /** + @property inputclass + @default null + **/ + inputclass: null, + /** + Format used for sending value to server. Also applied when converting date from <code>data-value</code> attribute.<br> + Full list of tokens: http://docs.jquery.com/UI/Datepicker/formatDate + + @property format + @type string + @default yyyy-mm-dd + **/ + format:'yyyy-mm-dd', + /** + Format used for displaying date. Also applied when converting date from element's text on init. + If not specified equals to <code>format</code> + + @property viewformat + @type string + @default null + **/ + viewformat: null, + + /** + Configuration of datepicker. + Full list of options: http://api.jqueryui.com/datepicker + + @property datepicker + @type object + @default { + firstDay: 0, + changeYear: true, + changeMonth: true + } + **/ + datepicker: { + firstDay: 0, + changeYear: true, + changeMonth: true, + showOtherMonths: true + }, + /** + Text shown as clear date button. + If <code>false</code> clear button will not be rendered. + + @property clear + @type boolean|string + @default 'x clear' + **/ + clear: '× clear' + }); + + $.fn.editabletypes.dateui = DateUI; + +}(window.jQuery)); + +/** +jQuery UI datefield input - modification for inline mode. +Shows normal <input type="text"> and binds popup datepicker. +Automatically shown in inline mode. + +@class dateuifield +@extends dateui + +@since 1.4.0 +**/ +(function ($) { + "use strict"; + + var DateUIField = function (options) { + this.init('dateuifield', options, DateUIField.defaults); + this.initPicker(options, DateUIField.defaults); + }; + + $.fn.editableutils.inherit(DateUIField, $.fn.editabletypes.dateui); + + $.extend(DateUIField.prototype, { + render: function () { + // this.$input = this.$tpl.find('input'); + this.$input.datepicker(this.options.datepicker); + $.fn.editabletypes.text.prototype.renderClear.call(this); + }, + + value2input: function(value) { + this.$input.val($.datepicker.formatDate(this.options.viewformat, value)); + }, + + input2value: function() { + return this.html2value(this.$input.val()); + }, + + activate: function() { + $.fn.editabletypes.text.prototype.activate.call(this); + }, + + toggleClear: function() { + $.fn.editabletypes.text.prototype.toggleClear.call(this); + }, + + autosubmit: function() { + //reset autosubmit to empty + } + }); + + DateUIField.defaults = $.extend({}, $.fn.editabletypes.dateui.defaults, { + /** + @property tpl + @default <input type="text"> + **/ + tpl: '<input type="text"/>', + /** + @property inputclass + @default null + **/ + inputclass: null, + + /* datepicker config */ + datepicker: { + showOn: "button", + buttonImage: "http://jqueryui.com/resources/demos/datepicker/images/calendar.gif", + buttonImageOnly: true, + firstDay: 0, + changeYear: true, + changeMonth: true, + showOtherMonths: true + }, + + /* disable clear link */ + clear: false + }); + + $.fn.editabletypes.dateuifield = DateUIField; + +}(window.jQuery)); \ No newline at end of file diff --git a/src/legacy/design-studio/js/lodash.js b/src/legacy/design-studio/js/lodash.js new file mode 100644 index 0000000..b39ddce --- /dev/null +++ b/src/legacy/design-studio/js/lodash.js @@ -0,0 +1,17084 @@ +/** + * @license + * Lodash <https://lodash.com/> + * Copyright JS Foundation and other contributors <https://js.foundation/> + * Released under MIT license <https://lodash.com/license> + * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE> + * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors + */ +;(function() { + + /** Used as a safe reference for `undefined` in pre-ES5 environments. */ + var undefined; + + /** Used as the semantic version number. */ + var VERSION = '4.17.4'; + + /** Used as the size to enable large array optimizations. */ + var LARGE_ARRAY_SIZE = 200; + + /** Error message constants. */ + var CORE_ERROR_TEXT = 'Unsupported core-js use. Try https://npms.io/search?q=ponyfill.', + FUNC_ERROR_TEXT = 'Expected a function'; + + /** Used to stand-in for `undefined` hash values. */ + var HASH_UNDEFINED = '__lodash_hash_undefined__'; + + /** Used as the maximum memoize cache size. */ + var MAX_MEMOIZE_SIZE = 500; + + /** Used as the internal argument placeholder. */ + var PLACEHOLDER = '__lodash_placeholder__'; + + /** Used to compose bitmasks for cloning. */ + var CLONE_DEEP_FLAG = 1, + CLONE_FLAT_FLAG = 2, + CLONE_SYMBOLS_FLAG = 4; + + /** Used to compose bitmasks for value comparisons. */ + var COMPARE_PARTIAL_FLAG = 1, + COMPARE_UNORDERED_FLAG = 2; + + /** Used to compose bitmasks for function metadata. */ + var WRAP_BIND_FLAG = 1, + WRAP_BIND_KEY_FLAG = 2, + WRAP_CURRY_BOUND_FLAG = 4, + WRAP_CURRY_FLAG = 8, + WRAP_CURRY_RIGHT_FLAG = 16, + WRAP_PARTIAL_FLAG = 32, + WRAP_PARTIAL_RIGHT_FLAG = 64, + WRAP_ARY_FLAG = 128, + WRAP_REARG_FLAG = 256, + WRAP_FLIP_FLAG = 512; + + /** Used as default options for `_.truncate`. */ + var DEFAULT_TRUNC_LENGTH = 30, + DEFAULT_TRUNC_OMISSION = '...'; + + /** Used to detect hot functions by number of calls within a span of milliseconds. */ + var HOT_COUNT = 800, + HOT_SPAN = 16; + + /** Used to indicate the type of lazy iteratees. */ + var LAZY_FILTER_FLAG = 1, + LAZY_MAP_FLAG = 2, + LAZY_WHILE_FLAG = 3; + + /** Used as references for various `Number` constants. */ + var INFINITY = 1 / 0, + MAX_SAFE_INTEGER = 9007199254740991, + MAX_INTEGER = 1.7976931348623157e+308, + NAN = 0 / 0; + + /** Used as references for the maximum length and index of an array. */ + var MAX_ARRAY_LENGTH = 4294967295, + MAX_ARRAY_INDEX = MAX_ARRAY_LENGTH - 1, + HALF_MAX_ARRAY_LENGTH = MAX_ARRAY_LENGTH >>> 1; + + /** Used to associate wrap methods with their bit flags. */ + var wrapFlags = [ + ['ary', WRAP_ARY_FLAG], + ['bind', WRAP_BIND_FLAG], + ['bindKey', WRAP_BIND_KEY_FLAG], + ['curry', WRAP_CURRY_FLAG], + ['curryRight', WRAP_CURRY_RIGHT_FLAG], + ['flip', WRAP_FLIP_FLAG], + ['partial', WRAP_PARTIAL_FLAG], + ['partialRight', WRAP_PARTIAL_RIGHT_FLAG], + ['rearg', WRAP_REARG_FLAG] + ]; + + /** `Object#toString` result references. */ + var argsTag = '[object Arguments]', + arrayTag = '[object Array]', + asyncTag = '[object AsyncFunction]', + boolTag = '[object Boolean]', + dateTag = '[object Date]', + domExcTag = '[object DOMException]', + errorTag = '[object Error]', + funcTag = '[object Function]', + genTag = '[object GeneratorFunction]', + mapTag = '[object Map]', + numberTag = '[object Number]', + nullTag = '[object Null]', + objectTag = '[object Object]', + promiseTag = '[object Promise]', + proxyTag = '[object Proxy]', + regexpTag = '[object RegExp]', + setTag = '[object Set]', + stringTag = '[object String]', + symbolTag = '[object Symbol]', + undefinedTag = '[object Undefined]', + weakMapTag = '[object WeakMap]', + weakSetTag = '[object WeakSet]'; + + var arrayBufferTag = '[object ArrayBuffer]', + dataViewTag = '[object DataView]', + float32Tag = '[object Float32Array]', + float64Tag = '[object Float64Array]', + int8Tag = '[object Int8Array]', + int16Tag = '[object Int16Array]', + int32Tag = '[object Int32Array]', + uint8Tag = '[object Uint8Array]', + uint8ClampedTag = '[object Uint8ClampedArray]', + uint16Tag = '[object Uint16Array]', + uint32Tag = '[object Uint32Array]'; + + /** Used to match empty string literals in compiled template source. */ + var reEmptyStringLeading = /\b__p \+= '';/g, + reEmptyStringMiddle = /\b(__p \+=) '' \+/g, + reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g; + + /** Used to match HTML entities and HTML characters. */ + var reEscapedHtml = /&(?:amp|lt|gt|quot|#39);/g, + reUnescapedHtml = /[&<>"']/g, + reHasEscapedHtml = RegExp(reEscapedHtml.source), + reHasUnescapedHtml = RegExp(reUnescapedHtml.source); + + /** Used to match template delimiters. */ + var reEscape = /<%-([\s\S]+?)%>/g, + reEvaluate = /<%([\s\S]+?)%>/g, + reInterpolate = /<%=([\s\S]+?)%>/g; + + /** Used to match property names within property paths. */ + var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/, + reIsPlainProp = /^\w*$/, + reLeadingDot = /^\./, + rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g; + + /** + * Used to match `RegExp` + * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns). + */ + var reRegExpChar = /[\\^$.*+?()[\]{}|]/g, + reHasRegExpChar = RegExp(reRegExpChar.source); + + /** Used to match leading and trailing whitespace. */ + var reTrim = /^\s+|\s+$/g, + reTrimStart = /^\s+/, + reTrimEnd = /\s+$/; + + /** Used to match wrap detail comments. */ + var reWrapComment = /\{(?:\n\/\* \[wrapped with .+\] \*\/)?\n?/, + reWrapDetails = /\{\n\/\* \[wrapped with (.+)\] \*/, + reSplitDetails = /,? & /; + + /** Used to match words composed of alphanumeric characters. */ + var reAsciiWord = /[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g; + + /** Used to match backslashes in property paths. */ + var reEscapeChar = /\\(\\)?/g; + + /** + * Used to match + * [ES template delimiters](http://ecma-international.org/ecma-262/7.0/#sec-template-literal-lexical-components). + */ + var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g; + + /** Used to match `RegExp` flags from their coerced string values. */ + var reFlags = /\w*$/; + + /** Used to detect bad signed hexadecimal string values. */ + var reIsBadHex = /^[-+]0x[0-9a-f]+$/i; + + /** Used to detect binary string values. */ + var reIsBinary = /^0b[01]+$/i; + + /** Used to detect host constructors (Safari). */ + var reIsHostCtor = /^\[object .+?Constructor\]$/; + + /** Used to detect octal string values. */ + var reIsOctal = /^0o[0-7]+$/i; + + /** Used to detect unsigned integer values. */ + var reIsUint = /^(?:0|[1-9]\d*)$/; + + /** Used to match Latin Unicode letters (excluding mathematical operators). */ + var reLatin = /[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g; + + /** Used to ensure capturing order of template delimiters. */ + var reNoMatch = /($^)/; + + /** Used to match unescaped characters in compiled string literals. */ + var reUnescapedString = /['\n\r\u2028\u2029\\]/g; + + /** Used to compose unicode character classes. */ + var rsAstralRange = '\\ud800-\\udfff', + rsComboMarksRange = '\\u0300-\\u036f', + reComboHalfMarksRange = '\\ufe20-\\ufe2f', + rsComboSymbolsRange = '\\u20d0-\\u20ff', + rsComboRange = rsComboMarksRange + reComboHalfMarksRange + rsComboSymbolsRange, + rsDingbatRange = '\\u2700-\\u27bf', + rsLowerRange = 'a-z\\xdf-\\xf6\\xf8-\\xff', + rsMathOpRange = '\\xac\\xb1\\xd7\\xf7', + rsNonCharRange = '\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf', + rsPunctuationRange = '\\u2000-\\u206f', + rsSpaceRange = ' \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000', + rsUpperRange = 'A-Z\\xc0-\\xd6\\xd8-\\xde', + rsVarRange = '\\ufe0e\\ufe0f', + rsBreakRange = rsMathOpRange + rsNonCharRange + rsPunctuationRange + rsSpaceRange; + + /** Used to compose unicode capture groups. */ + var rsApos = "['\u2019]", + rsAstral = '[' + rsAstralRange + ']', + rsBreak = '[' + rsBreakRange + ']', + rsCombo = '[' + rsComboRange + ']', + rsDigits = '\\d+', + rsDingbat = '[' + rsDingbatRange + ']', + rsLower = '[' + rsLowerRange + ']', + rsMisc = '[^' + rsAstralRange + rsBreakRange + rsDigits + rsDingbatRange + rsLowerRange + rsUpperRange + ']', + rsFitz = '\\ud83c[\\udffb-\\udfff]', + rsModifier = '(?:' + rsCombo + '|' + rsFitz + ')', + rsNonAstral = '[^' + rsAstralRange + ']', + rsRegional = '(?:\\ud83c[\\udde6-\\uddff]){2}', + rsSurrPair = '[\\ud800-\\udbff][\\udc00-\\udfff]', + rsUpper = '[' + rsUpperRange + ']', + rsZWJ = '\\u200d'; + + /** Used to compose unicode regexes. */ + var rsMiscLower = '(?:' + rsLower + '|' + rsMisc + ')', + rsMiscUpper = '(?:' + rsUpper + '|' + rsMisc + ')', + rsOptContrLower = '(?:' + rsApos + '(?:d|ll|m|re|s|t|ve))?', + rsOptContrUpper = '(?:' + rsApos + '(?:D|LL|M|RE|S|T|VE))?', + reOptMod = rsModifier + '?', + rsOptVar = '[' + rsVarRange + ']?', + rsOptJoin = '(?:' + rsZWJ + '(?:' + [rsNonAstral, rsRegional, rsSurrPair].join('|') + ')' + rsOptVar + reOptMod + ')*', + rsOrdLower = '\\d*(?:(?:1st|2nd|3rd|(?![123])\\dth)\\b)', + rsOrdUpper = '\\d*(?:(?:1ST|2ND|3RD|(?![123])\\dTH)\\b)', + rsSeq = rsOptVar + reOptMod + rsOptJoin, + rsEmoji = '(?:' + [rsDingbat, rsRegional, rsSurrPair].join('|') + ')' + rsSeq, + rsSymbol = '(?:' + [rsNonAstral + rsCombo + '?', rsCombo, rsRegional, rsSurrPair, rsAstral].join('|') + ')'; + + /** Used to match apostrophes. */ + var reApos = RegExp(rsApos, 'g'); + + /** + * Used to match [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks) and + * [combining diacritical marks for symbols](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks_for_Symbols). + */ + var reComboMark = RegExp(rsCombo, 'g'); + + /** Used to match [string symbols](https://mathiasbynens.be/notes/javascript-unicode). */ + var reUnicode = RegExp(rsFitz + '(?=' + rsFitz + ')|' + rsSymbol + rsSeq, 'g'); + + /** Used to match complex or compound words. */ + var reUnicodeWord = RegExp([ + rsUpper + '?' + rsLower + '+' + rsOptContrLower + '(?=' + [rsBreak, rsUpper, '$'].join('|') + ')', + rsMiscUpper + '+' + rsOptContrUpper + '(?=' + [rsBreak, rsUpper + rsMiscLower, '$'].join('|') + ')', + rsUpper + '?' + rsMiscLower + '+' + rsOptContrLower, + rsUpper + '+' + rsOptContrUpper, + rsOrdUpper, + rsOrdLower, + rsDigits, + rsEmoji + ].join('|'), 'g'); + + /** Used to detect strings with [zero-width joiners or code points from the astral planes](http://eev.ee/blog/2015/09/12/dark-corners-of-unicode/). */ + var reHasUnicode = RegExp('[' + rsZWJ + rsAstralRange + rsComboRange + rsVarRange + ']'); + + /** Used to detect strings that need a more robust regexp to match words. */ + var reHasUnicodeWord = /[a-z][A-Z]|[A-Z]{2,}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/; + + /** Used to assign default `context` object properties. */ + var contextProps = [ + 'Array', 'Buffer', 'DataView', 'Date', 'Error', 'Float32Array', 'Float64Array', + 'Function', 'Int8Array', 'Int16Array', 'Int32Array', 'Map', 'Math', 'Object', + 'Promise', 'RegExp', 'Set', 'String', 'Symbol', 'TypeError', 'Uint8Array', + 'Uint8ClampedArray', 'Uint16Array', 'Uint32Array', 'WeakMap', + '_', 'clearTimeout', 'isFinite', 'parseInt', 'setTimeout' + ]; + + /** Used to make template sourceURLs easier to identify. */ + var templateCounter = -1; + + /** Used to identify `toStringTag` values of typed arrays. */ + var typedArrayTags = {}; + typedArrayTags[float32Tag] = typedArrayTags[float64Tag] = + typedArrayTags[int8Tag] = typedArrayTags[int16Tag] = + typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] = + typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] = + typedArrayTags[uint32Tag] = true; + typedArrayTags[argsTag] = typedArrayTags[arrayTag] = + typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] = + typedArrayTags[dataViewTag] = typedArrayTags[dateTag] = + typedArrayTags[errorTag] = typedArrayTags[funcTag] = + typedArrayTags[mapTag] = typedArrayTags[numberTag] = + typedArrayTags[objectTag] = typedArrayTags[regexpTag] = + typedArrayTags[setTag] = typedArrayTags[stringTag] = + typedArrayTags[weakMapTag] = false; + + /** Used to identify `toStringTag` values supported by `_.clone`. */ + var cloneableTags = {}; + cloneableTags[argsTag] = cloneableTags[arrayTag] = + cloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] = + cloneableTags[boolTag] = cloneableTags[dateTag] = + cloneableTags[float32Tag] = cloneableTags[float64Tag] = + cloneableTags[int8Tag] = cloneableTags[int16Tag] = + cloneableTags[int32Tag] = cloneableTags[mapTag] = + cloneableTags[numberTag] = cloneableTags[objectTag] = + cloneableTags[regexpTag] = cloneableTags[setTag] = + cloneableTags[stringTag] = cloneableTags[symbolTag] = + cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] = + cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true; + cloneableTags[errorTag] = cloneableTags[funcTag] = + cloneableTags[weakMapTag] = false; + + /** Used to map Latin Unicode letters to basic Latin letters. */ + var deburredLetters = { + // Latin-1 Supplement block. + '\xc0': 'A', '\xc1': 'A', '\xc2': 'A', '\xc3': 'A', '\xc4': 'A', '\xc5': 'A', + '\xe0': 'a', '\xe1': 'a', '\xe2': 'a', '\xe3': 'a', '\xe4': 'a', '\xe5': 'a', + '\xc7': 'C', '\xe7': 'c', + '\xd0': 'D', '\xf0': 'd', + '\xc8': 'E', '\xc9': 'E', '\xca': 'E', '\xcb': 'E', + '\xe8': 'e', '\xe9': 'e', '\xea': 'e', '\xeb': 'e', + '\xcc': 'I', '\xcd': 'I', '\xce': 'I', '\xcf': 'I', + '\xec': 'i', '\xed': 'i', '\xee': 'i', '\xef': 'i', + '\xd1': 'N', '\xf1': 'n', + '\xd2': 'O', '\xd3': 'O', '\xd4': 'O', '\xd5': 'O', '\xd6': 'O', '\xd8': 'O', + '\xf2': 'o', '\xf3': 'o', '\xf4': 'o', '\xf5': 'o', '\xf6': 'o', '\xf8': 'o', + '\xd9': 'U', '\xda': 'U', '\xdb': 'U', '\xdc': 'U', + '\xf9': 'u', '\xfa': 'u', '\xfb': 'u', '\xfc': 'u', + '\xdd': 'Y', '\xfd': 'y', '\xff': 'y', + '\xc6': 'Ae', '\xe6': 'ae', + '\xde': 'Th', '\xfe': 'th', + '\xdf': 'ss', + // Latin Extended-A block. + '\u0100': 'A', '\u0102': 'A', '\u0104': 'A', + '\u0101': 'a', '\u0103': 'a', '\u0105': 'a', + '\u0106': 'C', '\u0108': 'C', '\u010a': 'C', '\u010c': 'C', + '\u0107': 'c', '\u0109': 'c', '\u010b': 'c', '\u010d': 'c', + '\u010e': 'D', '\u0110': 'D', '\u010f': 'd', '\u0111': 'd', + '\u0112': 'E', '\u0114': 'E', '\u0116': 'E', '\u0118': 'E', '\u011a': 'E', + '\u0113': 'e', '\u0115': 'e', '\u0117': 'e', '\u0119': 'e', '\u011b': 'e', + '\u011c': 'G', '\u011e': 'G', '\u0120': 'G', '\u0122': 'G', + '\u011d': 'g', '\u011f': 'g', '\u0121': 'g', '\u0123': 'g', + '\u0124': 'H', '\u0126': 'H', '\u0125': 'h', '\u0127': 'h', + '\u0128': 'I', '\u012a': 'I', '\u012c': 'I', '\u012e': 'I', '\u0130': 'I', + '\u0129': 'i', '\u012b': 'i', '\u012d': 'i', '\u012f': 'i', '\u0131': 'i', + '\u0134': 'J', '\u0135': 'j', + '\u0136': 'K', '\u0137': 'k', '\u0138': 'k', + '\u0139': 'L', '\u013b': 'L', '\u013d': 'L', '\u013f': 'L', '\u0141': 'L', + '\u013a': 'l', '\u013c': 'l', '\u013e': 'l', '\u0140': 'l', '\u0142': 'l', + '\u0143': 'N', '\u0145': 'N', '\u0147': 'N', '\u014a': 'N', + '\u0144': 'n', '\u0146': 'n', '\u0148': 'n', '\u014b': 'n', + '\u014c': 'O', '\u014e': 'O', '\u0150': 'O', + '\u014d': 'o', '\u014f': 'o', '\u0151': 'o', + '\u0154': 'R', '\u0156': 'R', '\u0158': 'R', + '\u0155': 'r', '\u0157': 'r', '\u0159': 'r', + '\u015a': 'S', '\u015c': 'S', '\u015e': 'S', '\u0160': 'S', + '\u015b': 's', '\u015d': 's', '\u015f': 's', '\u0161': 's', + '\u0162': 'T', '\u0164': 'T', '\u0166': 'T', + '\u0163': 't', '\u0165': 't', '\u0167': 't', + '\u0168': 'U', '\u016a': 'U', '\u016c': 'U', '\u016e': 'U', '\u0170': 'U', '\u0172': 'U', + '\u0169': 'u', '\u016b': 'u', '\u016d': 'u', '\u016f': 'u', '\u0171': 'u', '\u0173': 'u', + '\u0174': 'W', '\u0175': 'w', + '\u0176': 'Y', '\u0177': 'y', '\u0178': 'Y', + '\u0179': 'Z', '\u017b': 'Z', '\u017d': 'Z', + '\u017a': 'z', '\u017c': 'z', '\u017e': 'z', + '\u0132': 'IJ', '\u0133': 'ij', + '\u0152': 'Oe', '\u0153': 'oe', + '\u0149': "'n", '\u017f': 's' + }; + + /** Used to map characters to HTML entities. */ + var htmlEscapes = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''' + }; + + /** Used to map HTML entities to characters. */ + var htmlUnescapes = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + ''': "'" + }; + + /** Used to escape characters for inclusion in compiled string literals. */ + var stringEscapes = { + '\\': '\\', + "'": "'", + '\n': 'n', + '\r': 'r', + '\u2028': 'u2028', + '\u2029': 'u2029' + }; + + /** Built-in method references without a dependency on `root`. */ + var freeParseFloat = parseFloat, + freeParseInt = parseInt; + + /** Detect free variable `global` from Node.js. */ + var freeGlobal = typeof global == 'object' && global && global.Object === Object && global; + + /** Detect free variable `self`. */ + var freeSelf = typeof self == 'object' && self && self.Object === Object && self; + + /** Used as a reference to the global object. */ + var root = freeGlobal || freeSelf || Function('return this')(); + + /** Detect free variable `exports`. */ + var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports; + + /** Detect free variable `module`. */ + var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module; + + /** Detect the popular CommonJS extension `module.exports`. */ + var moduleExports = freeModule && freeModule.exports === freeExports; + + /** Detect free variable `process` from Node.js. */ + var freeProcess = moduleExports && freeGlobal.process; + + /** Used to access faster Node.js helpers. */ + var nodeUtil = (function() { + try { + return freeProcess && freeProcess.binding && freeProcess.binding('util'); + } catch (e) {} + }()); + + /* Node.js helper references. */ + var nodeIsArrayBuffer = nodeUtil && nodeUtil.isArrayBuffer, + nodeIsDate = nodeUtil && nodeUtil.isDate, + nodeIsMap = nodeUtil && nodeUtil.isMap, + nodeIsRegExp = nodeUtil && nodeUtil.isRegExp, + nodeIsSet = nodeUtil && nodeUtil.isSet, + nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray; + + /*--------------------------------------------------------------------------*/ + + /** + * Adds the key-value `pair` to `map`. + * + * @private + * @param {Object} map The map to modify. + * @param {Array} pair The key-value pair to add. + * @returns {Object} Returns `map`. + */ + function addMapEntry(map, pair) { + // Don't return `map.set` because it's not chainable in IE 11. + map.set(pair[0], pair[1]); + return map; + } + + /** + * Adds `value` to `set`. + * + * @private + * @param {Object} set The set to modify. + * @param {*} value The value to add. + * @returns {Object} Returns `set`. + */ + function addSetEntry(set, value) { + // Don't return `set.add` because it's not chainable in IE 11. + set.add(value); + return set; + } + + /** + * A faster alternative to `Function#apply`, this function invokes `func` + * with the `this` binding of `thisArg` and the arguments of `args`. + * + * @private + * @param {Function} func The function to invoke. + * @param {*} thisArg The `this` binding of `func`. + * @param {Array} args The arguments to invoke `func` with. + * @returns {*} Returns the result of `func`. + */ + function apply(func, thisArg, args) { + switch (args.length) { + case 0: return func.call(thisArg); + case 1: return func.call(thisArg, args[0]); + case 2: return func.call(thisArg, args[0], args[1]); + case 3: return func.call(thisArg, args[0], args[1], args[2]); + } + return func.apply(thisArg, args); + } + + /** + * A specialized version of `baseAggregator` for arrays. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} setter The function to set `accumulator` values. + * @param {Function} iteratee The iteratee to transform keys. + * @param {Object} accumulator The initial aggregated object. + * @returns {Function} Returns `accumulator`. + */ + function arrayAggregator(array, setter, iteratee, accumulator) { + var index = -1, + length = array == null ? 0 : array.length; + + while (++index < length) { + var value = array[index]; + setter(accumulator, value, iteratee(value), array); + } + return accumulator; + } + + /** + * A specialized version of `_.forEach` for arrays without support for + * iteratee shorthands. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns `array`. + */ + function arrayEach(array, iteratee) { + var index = -1, + length = array == null ? 0 : array.length; + + while (++index < length) { + if (iteratee(array[index], index, array) === false) { + break; + } + } + return array; + } + + /** + * A specialized version of `_.forEachRight` for arrays without support for + * iteratee shorthands. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns `array`. + */ + function arrayEachRight(array, iteratee) { + var length = array == null ? 0 : array.length; + + while (length--) { + if (iteratee(array[length], length, array) === false) { + break; + } + } + return array; + } + + /** + * A specialized version of `_.every` for arrays without support for + * iteratee shorthands. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {boolean} Returns `true` if all elements pass the predicate check, + * else `false`. + */ + function arrayEvery(array, predicate) { + var index = -1, + length = array == null ? 0 : array.length; + + while (++index < length) { + if (!predicate(array[index], index, array)) { + return false; + } + } + return true; + } + + /** + * A specialized version of `_.filter` for arrays without support for + * iteratee shorthands. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {Array} Returns the new filtered array. + */ + function arrayFilter(array, predicate) { + var index = -1, + length = array == null ? 0 : array.length, + resIndex = 0, + result = []; + + while (++index < length) { + var value = array[index]; + if (predicate(value, index, array)) { + result[resIndex++] = value; + } + } + return result; + } + + /** + * A specialized version of `_.includes` for arrays without support for + * specifying an index to search from. + * + * @private + * @param {Array} [array] The array to inspect. + * @param {*} target The value to search for. + * @returns {boolean} Returns `true` if `target` is found, else `false`. + */ + function arrayIncludes(array, value) { + var length = array == null ? 0 : array.length; + return !!length && baseIndexOf(array, value, 0) > -1; + } + + /** + * This function is like `arrayIncludes` except that it accepts a comparator. + * + * @private + * @param {Array} [array] The array to inspect. + * @param {*} target The value to search for. + * @param {Function} comparator The comparator invoked per element. + * @returns {boolean} Returns `true` if `target` is found, else `false`. + */ + function arrayIncludesWith(array, value, comparator) { + var index = -1, + length = array == null ? 0 : array.length; + + while (++index < length) { + if (comparator(value, array[index])) { + return true; + } + } + return false; + } + + /** + * A specialized version of `_.map` for arrays without support for iteratee + * shorthands. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns the new mapped array. + */ + function arrayMap(array, iteratee) { + var index = -1, + length = array == null ? 0 : array.length, + result = Array(length); + + while (++index < length) { + result[index] = iteratee(array[index], index, array); + } + return result; + } + + /** + * Appends the elements of `values` to `array`. + * + * @private + * @param {Array} array The array to modify. + * @param {Array} values The values to append. + * @returns {Array} Returns `array`. + */ + function arrayPush(array, values) { + var index = -1, + length = values.length, + offset = array.length; + + while (++index < length) { + array[offset + index] = values[index]; + } + return array; + } + + /** + * A specialized version of `_.reduce` for arrays without support for + * iteratee shorthands. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {*} [accumulator] The initial value. + * @param {boolean} [initAccum] Specify using the first element of `array` as + * the initial value. + * @returns {*} Returns the accumulated value. + */ + function arrayReduce(array, iteratee, accumulator, initAccum) { + var index = -1, + length = array == null ? 0 : array.length; + + if (initAccum && length) { + accumulator = array[++index]; + } + while (++index < length) { + accumulator = iteratee(accumulator, array[index], index, array); + } + return accumulator; + } + + /** + * A specialized version of `_.reduceRight` for arrays without support for + * iteratee shorthands. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {*} [accumulator] The initial value. + * @param {boolean} [initAccum] Specify using the last element of `array` as + * the initial value. + * @returns {*} Returns the accumulated value. + */ + function arrayReduceRight(array, iteratee, accumulator, initAccum) { + var length = array == null ? 0 : array.length; + if (initAccum && length) { + accumulator = array[--length]; + } + while (length--) { + accumulator = iteratee(accumulator, array[length], length, array); + } + return accumulator; + } + + /** + * A specialized version of `_.some` for arrays without support for iteratee + * shorthands. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {boolean} Returns `true` if any element passes the predicate check, + * else `false`. + */ + function arraySome(array, predicate) { + var index = -1, + length = array == null ? 0 : array.length; + + while (++index < length) { + if (predicate(array[index], index, array)) { + return true; + } + } + return false; + } + + /** + * Gets the size of an ASCII `string`. + * + * @private + * @param {string} string The string inspect. + * @returns {number} Returns the string size. + */ + var asciiSize = baseProperty('length'); + + /** + * Converts an ASCII `string` to an array. + * + * @private + * @param {string} string The string to convert. + * @returns {Array} Returns the converted array. + */ + function asciiToArray(string) { + return string.split(''); + } + + /** + * Splits an ASCII `string` into an array of its words. + * + * @private + * @param {string} The string to inspect. + * @returns {Array} Returns the words of `string`. + */ + function asciiWords(string) { + return string.match(reAsciiWord) || []; + } + + /** + * The base implementation of methods like `_.findKey` and `_.findLastKey`, + * without support for iteratee shorthands, which iterates over `collection` + * using `eachFunc`. + * + * @private + * @param {Array|Object} collection The collection to inspect. + * @param {Function} predicate The function invoked per iteration. + * @param {Function} eachFunc The function to iterate over `collection`. + * @returns {*} Returns the found element or its key, else `undefined`. + */ + function baseFindKey(collection, predicate, eachFunc) { + var result; + eachFunc(collection, function(value, key, collection) { + if (predicate(value, key, collection)) { + result = key; + return false; + } + }); + return result; + } + + /** + * The base implementation of `_.findIndex` and `_.findLastIndex` without + * support for iteratee shorthands. + * + * @private + * @param {Array} array The array to inspect. + * @param {Function} predicate The function invoked per iteration. + * @param {number} fromIndex The index to search from. + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {number} Returns the index of the matched value, else `-1`. + */ + function baseFindIndex(array, predicate, fromIndex, fromRight) { + var length = array.length, + index = fromIndex + (fromRight ? 1 : -1); + + while ((fromRight ? index-- : ++index < length)) { + if (predicate(array[index], index, array)) { + return index; + } + } + return -1; + } + + /** + * The base implementation of `_.indexOf` without `fromIndex` bounds checks. + * + * @private + * @param {Array} array The array to inspect. + * @param {*} value The value to search for. + * @param {number} fromIndex The index to search from. + * @returns {number} Returns the index of the matched value, else `-1`. + */ + function baseIndexOf(array, value, fromIndex) { + return value === value + ? strictIndexOf(array, value, fromIndex) + : baseFindIndex(array, baseIsNaN, fromIndex); + } + + /** + * This function is like `baseIndexOf` except that it accepts a comparator. + * + * @private + * @param {Array} array The array to inspect. + * @param {*} value The value to search for. + * @param {number} fromIndex The index to search from. + * @param {Function} comparator The comparator invoked per element. + * @returns {number} Returns the index of the matched value, else `-1`. + */ + function baseIndexOfWith(array, value, fromIndex, comparator) { + var index = fromIndex - 1, + length = array.length; + + while (++index < length) { + if (comparator(array[index], value)) { + return index; + } + } + return -1; + } + + /** + * The base implementation of `_.isNaN` without support for number objects. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`. + */ + function baseIsNaN(value) { + return value !== value; + } + + /** + * The base implementation of `_.mean` and `_.meanBy` without support for + * iteratee shorthands. + * + * @private + * @param {Array} array The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {number} Returns the mean. + */ + function baseMean(array, iteratee) { + var length = array == null ? 0 : array.length; + return length ? (baseSum(array, iteratee) / length) : NAN; + } + + /** + * The base implementation of `_.property` without support for deep paths. + * + * @private + * @param {string} key The key of the property to get. + * @returns {Function} Returns the new accessor function. + */ + function baseProperty(key) { + return function(object) { + return object == null ? undefined : object[key]; + }; + } + + /** + * The base implementation of `_.propertyOf` without support for deep paths. + * + * @private + * @param {Object} object The object to query. + * @returns {Function} Returns the new accessor function. + */ + function basePropertyOf(object) { + return function(key) { + return object == null ? undefined : object[key]; + }; + } + + /** + * The base implementation of `_.reduce` and `_.reduceRight`, without support + * for iteratee shorthands, which iterates over `collection` using `eachFunc`. + * + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {*} accumulator The initial value. + * @param {boolean} initAccum Specify using the first or last element of + * `collection` as the initial value. + * @param {Function} eachFunc The function to iterate over `collection`. + * @returns {*} Returns the accumulated value. + */ + function baseReduce(collection, iteratee, accumulator, initAccum, eachFunc) { + eachFunc(collection, function(value, index, collection) { + accumulator = initAccum + ? (initAccum = false, value) + : iteratee(accumulator, value, index, collection); + }); + return accumulator; + } + + /** + * The base implementation of `_.sortBy` which uses `comparer` to define the + * sort order of `array` and replaces criteria objects with their corresponding + * values. + * + * @private + * @param {Array} array The array to sort. + * @param {Function} comparer The function to define sort order. + * @returns {Array} Returns `array`. + */ + function baseSortBy(array, comparer) { + var length = array.length; + + array.sort(comparer); + while (length--) { + array[length] = array[length].value; + } + return array; + } + + /** + * The base implementation of `_.sum` and `_.sumBy` without support for + * iteratee shorthands. + * + * @private + * @param {Array} array The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {number} Returns the sum. + */ + function baseSum(array, iteratee) { + var result, + index = -1, + length = array.length; + + while (++index < length) { + var current = iteratee(array[index]); + if (current !== undefined) { + result = result === undefined ? current : (result + current); + } + } + return result; + } + + /** + * The base implementation of `_.times` without support for iteratee shorthands + * or max array length checks. + * + * @private + * @param {number} n The number of times to invoke `iteratee`. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns the array of results. + */ + function baseTimes(n, iteratee) { + var index = -1, + result = Array(n); + + while (++index < n) { + result[index] = iteratee(index); + } + return result; + } + + /** + * The base implementation of `_.toPairs` and `_.toPairsIn` which creates an array + * of key-value pairs for `object` corresponding to the property names of `props`. + * + * @private + * @param {Object} object The object to query. + * @param {Array} props The property names to get values for. + * @returns {Object} Returns the key-value pairs. + */ + function baseToPairs(object, props) { + return arrayMap(props, function(key) { + return [key, object[key]]; + }); + } + + /** + * The base implementation of `_.unary` without support for storing metadata. + * + * @private + * @param {Function} func The function to cap arguments for. + * @returns {Function} Returns the new capped function. + */ + function baseUnary(func) { + return function(value) { + return func(value); + }; + } + + /** + * The base implementation of `_.values` and `_.valuesIn` which creates an + * array of `object` property values corresponding to the property names + * of `props`. + * + * @private + * @param {Object} object The object to query. + * @param {Array} props The property names to get values for. + * @returns {Object} Returns the array of property values. + */ + function baseValues(object, props) { + return arrayMap(props, function(key) { + return object[key]; + }); + } + + /** + * Checks if a `cache` value for `key` exists. + * + * @private + * @param {Object} cache The cache to query. + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function cacheHas(cache, key) { + return cache.has(key); + } + + /** + * Used by `_.trim` and `_.trimStart` to get the index of the first string symbol + * that is not found in the character symbols. + * + * @private + * @param {Array} strSymbols The string symbols to inspect. + * @param {Array} chrSymbols The character symbols to find. + * @returns {number} Returns the index of the first unmatched string symbol. + */ + function charsStartIndex(strSymbols, chrSymbols) { + var index = -1, + length = strSymbols.length; + + while (++index < length && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {} + return index; + } + + /** + * Used by `_.trim` and `_.trimEnd` to get the index of the last string symbol + * that is not found in the character symbols. + * + * @private + * @param {Array} strSymbols The string symbols to inspect. + * @param {Array} chrSymbols The character symbols to find. + * @returns {number} Returns the index of the last unmatched string symbol. + */ + function charsEndIndex(strSymbols, chrSymbols) { + var index = strSymbols.length; + + while (index-- && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {} + return index; + } + + /** + * Gets the number of `placeholder` occurrences in `array`. + * + * @private + * @param {Array} array The array to inspect. + * @param {*} placeholder The placeholder to search for. + * @returns {number} Returns the placeholder count. + */ + function countHolders(array, placeholder) { + var length = array.length, + result = 0; + + while (length--) { + if (array[length] === placeholder) { + ++result; + } + } + return result; + } + + /** + * Used by `_.deburr` to convert Latin-1 Supplement and Latin Extended-A + * letters to basic Latin letters. + * + * @private + * @param {string} letter The matched letter to deburr. + * @returns {string} Returns the deburred letter. + */ + var deburrLetter = basePropertyOf(deburredLetters); + + /** + * Used by `_.escape` to convert characters to HTML entities. + * + * @private + * @param {string} chr The matched character to escape. + * @returns {string} Returns the escaped character. + */ + var escapeHtmlChar = basePropertyOf(htmlEscapes); + + /** + * Used by `_.template` to escape characters for inclusion in compiled string literals. + * + * @private + * @param {string} chr The matched character to escape. + * @returns {string} Returns the escaped character. + */ + function escapeStringChar(chr) { + return '\\' + stringEscapes[chr]; + } + + /** + * Gets the value at `key` of `object`. + * + * @private + * @param {Object} [object] The object to query. + * @param {string} key The key of the property to get. + * @returns {*} Returns the property value. + */ + function getValue(object, key) { + return object == null ? undefined : object[key]; + } + + /** + * Checks if `string` contains Unicode symbols. + * + * @private + * @param {string} string The string to inspect. + * @returns {boolean} Returns `true` if a symbol is found, else `false`. + */ + function hasUnicode(string) { + return reHasUnicode.test(string); + } + + /** + * Checks if `string` contains a word composed of Unicode symbols. + * + * @private + * @param {string} string The string to inspect. + * @returns {boolean} Returns `true` if a word is found, else `false`. + */ + function hasUnicodeWord(string) { + return reHasUnicodeWord.test(string); + } + + /** + * Converts `iterator` to an array. + * + * @private + * @param {Object} iterator The iterator to convert. + * @returns {Array} Returns the converted array. + */ + function iteratorToArray(iterator) { + var data, + result = []; + + while (!(data = iterator.next()).done) { + result.push(data.value); + } + return result; + } + + /** + * Converts `map` to its key-value pairs. + * + * @private + * @param {Object} map The map to convert. + * @returns {Array} Returns the key-value pairs. + */ + function mapToArray(map) { + var index = -1, + result = Array(map.size); + + map.forEach(function(value, key) { + result[++index] = [key, value]; + }); + return result; + } + + /** + * Creates a unary function that invokes `func` with its argument transformed. + * + * @private + * @param {Function} func The function to wrap. + * @param {Function} transform The argument transform. + * @returns {Function} Returns the new function. + */ + function overArg(func, transform) { + return function(arg) { + return func(transform(arg)); + }; + } + + /** + * Replaces all `placeholder` elements in `array` with an internal placeholder + * and returns an array of their indexes. + * + * @private + * @param {Array} array The array to modify. + * @param {*} placeholder The placeholder to replace. + * @returns {Array} Returns the new array of placeholder indexes. + */ + function replaceHolders(array, placeholder) { + var index = -1, + length = array.length, + resIndex = 0, + result = []; + + while (++index < length) { + var value = array[index]; + if (value === placeholder || value === PLACEHOLDER) { + array[index] = PLACEHOLDER; + result[resIndex++] = index; + } + } + return result; + } + + /** + * Converts `set` to an array of its values. + * + * @private + * @param {Object} set The set to convert. + * @returns {Array} Returns the values. + */ + function setToArray(set) { + var index = -1, + result = Array(set.size); + + set.forEach(function(value) { + result[++index] = value; + }); + return result; + } + + /** + * Converts `set` to its value-value pairs. + * + * @private + * @param {Object} set The set to convert. + * @returns {Array} Returns the value-value pairs. + */ + function setToPairs(set) { + var index = -1, + result = Array(set.size); + + set.forEach(function(value) { + result[++index] = [value, value]; + }); + return result; + } + + /** + * A specialized version of `_.indexOf` which performs strict equality + * comparisons of values, i.e. `===`. + * + * @private + * @param {Array} array The array to inspect. + * @param {*} value The value to search for. + * @param {number} fromIndex The index to search from. + * @returns {number} Returns the index of the matched value, else `-1`. + */ + function strictIndexOf(array, value, fromIndex) { + var index = fromIndex - 1, + length = array.length; + + while (++index < length) { + if (array[index] === value) { + return index; + } + } + return -1; + } + + /** + * A specialized version of `_.lastIndexOf` which performs strict equality + * comparisons of values, i.e. `===`. + * + * @private + * @param {Array} array The array to inspect. + * @param {*} value The value to search for. + * @param {number} fromIndex The index to search from. + * @returns {number} Returns the index of the matched value, else `-1`. + */ + function strictLastIndexOf(array, value, fromIndex) { + var index = fromIndex + 1; + while (index--) { + if (array[index] === value) { + return index; + } + } + return index; + } + + /** + * Gets the number of symbols in `string`. + * + * @private + * @param {string} string The string to inspect. + * @returns {number} Returns the string size. + */ + function stringSize(string) { + return hasUnicode(string) + ? unicodeSize(string) + : asciiSize(string); + } + + /** + * Converts `string` to an array. + * + * @private + * @param {string} string The string to convert. + * @returns {Array} Returns the converted array. + */ + function stringToArray(string) { + return hasUnicode(string) + ? unicodeToArray(string) + : asciiToArray(string); + } + + /** + * Used by `_.unescape` to convert HTML entities to characters. + * + * @private + * @param {string} chr The matched character to unescape. + * @returns {string} Returns the unescaped character. + */ + var unescapeHtmlChar = basePropertyOf(htmlUnescapes); + + /** + * Gets the size of a Unicode `string`. + * + * @private + * @param {string} string The string inspect. + * @returns {number} Returns the string size. + */ + function unicodeSize(string) { + var result = reUnicode.lastIndex = 0; + while (reUnicode.test(string)) { + ++result; + } + return result; + } + + /** + * Converts a Unicode `string` to an array. + * + * @private + * @param {string} string The string to convert. + * @returns {Array} Returns the converted array. + */ + function unicodeToArray(string) { + return string.match(reUnicode) || []; + } + + /** + * Splits a Unicode `string` into an array of its words. + * + * @private + * @param {string} The string to inspect. + * @returns {Array} Returns the words of `string`. + */ + function unicodeWords(string) { + return string.match(reUnicodeWord) || []; + } + + /*--------------------------------------------------------------------------*/ + + /** + * Create a new pristine `lodash` function using the `context` object. + * + * @static + * @memberOf _ + * @since 1.1.0 + * @category Util + * @param {Object} [context=root] The context object. + * @returns {Function} Returns a new `lodash` function. + * @example + * + * _.mixin({ 'foo': _.constant('foo') }); + * + * var lodash = _.runInContext(); + * lodash.mixin({ 'bar': lodash.constant('bar') }); + * + * _.isFunction(_.foo); + * // => true + * _.isFunction(_.bar); + * // => false + * + * lodash.isFunction(lodash.foo); + * // => false + * lodash.isFunction(lodash.bar); + * // => true + * + * // Create a suped-up `defer` in Node.js. + * var defer = _.runInContext({ 'setTimeout': setImmediate }).defer; + */ + var runInContext = (function runInContext(context) { + context = context == null ? root : _.defaults(root.Object(), context, _.pick(root, contextProps)); + + /** Built-in constructor references. */ + var Array = context.Array, + Date = context.Date, + Error = context.Error, + Function = context.Function, + Math = context.Math, + Object = context.Object, + RegExp = context.RegExp, + String = context.String, + TypeError = context.TypeError; + + /** Used for built-in method references. */ + var arrayProto = Array.prototype, + funcProto = Function.prototype, + objectProto = Object.prototype; + + /** Used to detect overreaching core-js shims. */ + var coreJsData = context['__core-js_shared__']; + + /** Used to resolve the decompiled source of functions. */ + var funcToString = funcProto.toString; + + /** Used to check objects for own properties. */ + var hasOwnProperty = objectProto.hasOwnProperty; + + /** Used to generate unique IDs. */ + var idCounter = 0; + + /** Used to detect methods masquerading as native. */ + var maskSrcKey = (function() { + var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || ''); + return uid ? ('Symbol(src)_1.' + uid) : ''; + }()); + + /** + * Used to resolve the + * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) + * of values. + */ + var nativeObjectToString = objectProto.toString; + + /** Used to infer the `Object` constructor. */ + var objectCtorString = funcToString.call(Object); + + /** Used to restore the original `_` reference in `_.noConflict`. */ + var oldDash = root._; + + /** Used to detect if a method is native. */ + var reIsNative = RegExp('^' + + funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\$&') + .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' + ); + + /** Built-in value references. */ + var Buffer = moduleExports ? context.Buffer : undefined, + Symbol = context.Symbol, + Uint8Array = context.Uint8Array, + allocUnsafe = Buffer ? Buffer.allocUnsafe : undefined, + getPrototype = overArg(Object.getPrototypeOf, Object), + objectCreate = Object.create, + propertyIsEnumerable = objectProto.propertyIsEnumerable, + splice = arrayProto.splice, + spreadableSymbol = Symbol ? Symbol.isConcatSpreadable : undefined, + symIterator = Symbol ? Symbol.iterator : undefined, + symToStringTag = Symbol ? Symbol.toStringTag : undefined; + + var defineProperty = (function() { + try { + var func = getNative(Object, 'defineProperty'); + func({}, '', {}); + return func; + } catch (e) {} + }()); + + /** Mocked built-ins. */ + var ctxClearTimeout = context.clearTimeout !== root.clearTimeout && context.clearTimeout, + ctxNow = Date && Date.now !== root.Date.now && Date.now, + ctxSetTimeout = context.setTimeout !== root.setTimeout && context.setTimeout; + + /* Built-in method references for those with the same name as other `lodash` methods. */ + var nativeCeil = Math.ceil, + nativeFloor = Math.floor, + nativeGetSymbols = Object.getOwnPropertySymbols, + nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined, + nativeIsFinite = context.isFinite, + nativeJoin = arrayProto.join, + nativeKeys = overArg(Object.keys, Object), + nativeMax = Math.max, + nativeMin = Math.min, + nativeNow = Date.now, + nativeParseInt = context.parseInt, + nativeRandom = Math.random, + nativeReverse = arrayProto.reverse; + + /* Built-in method references that are verified to be native. */ + var DataView = getNative(context, 'DataView'), + Map = getNative(context, 'Map'), + Promise = getNative(context, 'Promise'), + Set = getNative(context, 'Set'), + WeakMap = getNative(context, 'WeakMap'), + nativeCreate = getNative(Object, 'create'); + + /** Used to store function metadata. */ + var metaMap = WeakMap && new WeakMap; + + /** Used to lookup unminified function names. */ + var realNames = {}; + + /** Used to detect maps, sets, and weakmaps. */ + var dataViewCtorString = toSource(DataView), + mapCtorString = toSource(Map), + promiseCtorString = toSource(Promise), + setCtorString = toSource(Set), + weakMapCtorString = toSource(WeakMap); + + /** Used to convert symbols to primitives and strings. */ + var symbolProto = Symbol ? Symbol.prototype : undefined, + symbolValueOf = symbolProto ? symbolProto.valueOf : undefined, + symbolToString = symbolProto ? symbolProto.toString : undefined; + + /*------------------------------------------------------------------------*/ + + /** + * Creates a `lodash` object which wraps `value` to enable implicit method + * chain sequences. Methods that operate on and return arrays, collections, + * and functions can be chained together. Methods that retrieve a single value + * or may return a primitive value will automatically end the chain sequence + * and return the unwrapped value. Otherwise, the value must be unwrapped + * with `_#value`. + * + * Explicit chain sequences, which must be unwrapped with `_#value`, may be + * enabled using `_.chain`. + * + * The execution of chained methods is lazy, that is, it's deferred until + * `_#value` is implicitly or explicitly called. + * + * Lazy evaluation allows several methods to support shortcut fusion. + * Shortcut fusion is an optimization to merge iteratee calls; this avoids + * the creation of intermediate arrays and can greatly reduce the number of + * iteratee executions. Sections of a chain sequence qualify for shortcut + * fusion if the section is applied to an array and iteratees accept only + * one argument. The heuristic for whether a section qualifies for shortcut + * fusion is subject to change. + * + * Chaining is supported in custom builds as long as the `_#value` method is + * directly or indirectly included in the build. + * + * In addition to lodash methods, wrappers have `Array` and `String` methods. + * + * The wrapper `Array` methods are: + * `concat`, `join`, `pop`, `push`, `shift`, `sort`, `splice`, and `unshift` + * + * The wrapper `String` methods are: + * `replace` and `split` + * + * The wrapper methods that support shortcut fusion are: + * `at`, `compact`, `drop`, `dropRight`, `dropWhile`, `filter`, `find`, + * `findLast`, `head`, `initial`, `last`, `map`, `reject`, `reverse`, `slice`, + * `tail`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`, and `toArray` + * + * The chainable wrapper methods are: + * `after`, `ary`, `assign`, `assignIn`, `assignInWith`, `assignWith`, `at`, + * `before`, `bind`, `bindAll`, `bindKey`, `castArray`, `chain`, `chunk`, + * `commit`, `compact`, `concat`, `conforms`, `constant`, `countBy`, `create`, + * `curry`, `debounce`, `defaults`, `defaultsDeep`, `defer`, `delay`, + * `difference`, `differenceBy`, `differenceWith`, `drop`, `dropRight`, + * `dropRightWhile`, `dropWhile`, `extend`, `extendWith`, `fill`, `filter`, + * `flatMap`, `flatMapDeep`, `flatMapDepth`, `flatten`, `flattenDeep`, + * `flattenDepth`, `flip`, `flow`, `flowRight`, `fromPairs`, `functions`, + * `functionsIn`, `groupBy`, `initial`, `intersection`, `intersectionBy`, + * `intersectionWith`, `invert`, `invertBy`, `invokeMap`, `iteratee`, `keyBy`, + * `keys`, `keysIn`, `map`, `mapKeys`, `mapValues`, `matches`, `matchesProperty`, + * `memoize`, `merge`, `mergeWith`, `method`, `methodOf`, `mixin`, `negate`, + * `nthArg`, `omit`, `omitBy`, `once`, `orderBy`, `over`, `overArgs`, + * `overEvery`, `overSome`, `partial`, `partialRight`, `partition`, `pick`, + * `pickBy`, `plant`, `property`, `propertyOf`, `pull`, `pullAll`, `pullAllBy`, + * `pullAllWith`, `pullAt`, `push`, `range`, `rangeRight`, `rearg`, `reject`, + * `remove`, `rest`, `reverse`, `sampleSize`, `set`, `setWith`, `shuffle`, + * `slice`, `sort`, `sortBy`, `splice`, `spread`, `tail`, `take`, `takeRight`, + * `takeRightWhile`, `takeWhile`, `tap`, `throttle`, `thru`, `toArray`, + * `toPairs`, `toPairsIn`, `toPath`, `toPlainObject`, `transform`, `unary`, + * `union`, `unionBy`, `unionWith`, `uniq`, `uniqBy`, `uniqWith`, `unset`, + * `unshift`, `unzip`, `unzipWith`, `update`, `updateWith`, `values`, + * `valuesIn`, `without`, `wrap`, `xor`, `xorBy`, `xorWith`, `zip`, + * `zipObject`, `zipObjectDeep`, and `zipWith` + * + * The wrapper methods that are **not** chainable by default are: + * `add`, `attempt`, `camelCase`, `capitalize`, `ceil`, `clamp`, `clone`, + * `cloneDeep`, `cloneDeepWith`, `cloneWith`, `conformsTo`, `deburr`, + * `defaultTo`, `divide`, `each`, `eachRight`, `endsWith`, `eq`, `escape`, + * `escapeRegExp`, `every`, `find`, `findIndex`, `findKey`, `findLast`, + * `findLastIndex`, `findLastKey`, `first`, `floor`, `forEach`, `forEachRight`, + * `forIn`, `forInRight`, `forOwn`, `forOwnRight`, `get`, `gt`, `gte`, `has`, + * `hasIn`, `head`, `identity`, `includes`, `indexOf`, `inRange`, `invoke`, + * `isArguments`, `isArray`, `isArrayBuffer`, `isArrayLike`, `isArrayLikeObject`, + * `isBoolean`, `isBuffer`, `isDate`, `isElement`, `isEmpty`, `isEqual`, + * `isEqualWith`, `isError`, `isFinite`, `isFunction`, `isInteger`, `isLength`, + * `isMap`, `isMatch`, `isMatchWith`, `isNaN`, `isNative`, `isNil`, `isNull`, + * `isNumber`, `isObject`, `isObjectLike`, `isPlainObject`, `isRegExp`, + * `isSafeInteger`, `isSet`, `isString`, `isUndefined`, `isTypedArray`, + * `isWeakMap`, `isWeakSet`, `join`, `kebabCase`, `last`, `lastIndexOf`, + * `lowerCase`, `lowerFirst`, `lt`, `lte`, `max`, `maxBy`, `mean`, `meanBy`, + * `min`, `minBy`, `multiply`, `noConflict`, `noop`, `now`, `nth`, `pad`, + * `padEnd`, `padStart`, `parseInt`, `pop`, `random`, `reduce`, `reduceRight`, + * `repeat`, `result`, `round`, `runInContext`, `sample`, `shift`, `size`, + * `snakeCase`, `some`, `sortedIndex`, `sortedIndexBy`, `sortedLastIndex`, + * `sortedLastIndexBy`, `startCase`, `startsWith`, `stubArray`, `stubFalse`, + * `stubObject`, `stubString`, `stubTrue`, `subtract`, `sum`, `sumBy`, + * `template`, `times`, `toFinite`, `toInteger`, `toJSON`, `toLength`, + * `toLower`, `toNumber`, `toSafeInteger`, `toString`, `toUpper`, `trim`, + * `trimEnd`, `trimStart`, `truncate`, `unescape`, `uniqueId`, `upperCase`, + * `upperFirst`, `value`, and `words` + * + * @name _ + * @constructor + * @category Seq + * @param {*} value The value to wrap in a `lodash` instance. + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * function square(n) { + * return n * n; + * } + * + * var wrapped = _([1, 2, 3]); + * + * // Returns an unwrapped value. + * wrapped.reduce(_.add); + * // => 6 + * + * // Returns a wrapped value. + * var squares = wrapped.map(square); + * + * _.isArray(squares); + * // => false + * + * _.isArray(squares.value()); + * // => true + */ + function lodash(value) { + if (isObjectLike(value) && !isArray(value) && !(value instanceof LazyWrapper)) { + if (value instanceof LodashWrapper) { + return value; + } + if (hasOwnProperty.call(value, '__wrapped__')) { + return wrapperClone(value); + } + } + return new LodashWrapper(value); + } + + /** + * The base implementation of `_.create` without support for assigning + * properties to the created object. + * + * @private + * @param {Object} proto The object to inherit from. + * @returns {Object} Returns the new object. + */ + var baseCreate = (function() { + function object() {} + return function(proto) { + if (!isObject(proto)) { + return {}; + } + if (objectCreate) { + return objectCreate(proto); + } + object.prototype = proto; + var result = new object; + object.prototype = undefined; + return result; + }; + }()); + + /** + * The function whose prototype chain sequence wrappers inherit from. + * + * @private + */ + function baseLodash() { + // No operation performed. + } + + /** + * The base constructor for creating `lodash` wrapper objects. + * + * @private + * @param {*} value The value to wrap. + * @param {boolean} [chainAll] Enable explicit method chain sequences. + */ + function LodashWrapper(value, chainAll) { + this.__wrapped__ = value; + this.__actions__ = []; + this.__chain__ = !!chainAll; + this.__index__ = 0; + this.__values__ = undefined; + } + + /** + * By default, the template delimiters used by lodash are like those in + * embedded Ruby (ERB) as well as ES2015 template strings. Change the + * following template settings to use alternative delimiters. + * + * @static + * @memberOf _ + * @type {Object} + */ + lodash.templateSettings = { + + /** + * Used to detect `data` property values to be HTML-escaped. + * + * @memberOf _.templateSettings + * @type {RegExp} + */ + 'escape': reEscape, + + /** + * Used to detect code to be evaluated. + * + * @memberOf _.templateSettings + * @type {RegExp} + */ + 'evaluate': reEvaluate, + + /** + * Used to detect `data` property values to inject. + * + * @memberOf _.templateSettings + * @type {RegExp} + */ + 'interpolate': reInterpolate, + + /** + * Used to reference the data object in the template text. + * + * @memberOf _.templateSettings + * @type {string} + */ + 'variable': '', + + /** + * Used to import variables into the compiled template. + * + * @memberOf _.templateSettings + * @type {Object} + */ + 'imports': { + + /** + * A reference to the `lodash` function. + * + * @memberOf _.templateSettings.imports + * @type {Function} + */ + '_': lodash + } + }; + + // Ensure wrappers are instances of `baseLodash`. + lodash.prototype = baseLodash.prototype; + lodash.prototype.constructor = lodash; + + LodashWrapper.prototype = baseCreate(baseLodash.prototype); + LodashWrapper.prototype.constructor = LodashWrapper; + + /*------------------------------------------------------------------------*/ + + /** + * Creates a lazy wrapper object which wraps `value` to enable lazy evaluation. + * + * @private + * @constructor + * @param {*} value The value to wrap. + */ + function LazyWrapper(value) { + this.__wrapped__ = value; + this.__actions__ = []; + this.__dir__ = 1; + this.__filtered__ = false; + this.__iteratees__ = []; + this.__takeCount__ = MAX_ARRAY_LENGTH; + this.__views__ = []; + } + + /** + * Creates a clone of the lazy wrapper object. + * + * @private + * @name clone + * @memberOf LazyWrapper + * @returns {Object} Returns the cloned `LazyWrapper` object. + */ + function lazyClone() { + var result = new LazyWrapper(this.__wrapped__); + result.__actions__ = copyArray(this.__actions__); + result.__dir__ = this.__dir__; + result.__filtered__ = this.__filtered__; + result.__iteratees__ = copyArray(this.__iteratees__); + result.__takeCount__ = this.__takeCount__; + result.__views__ = copyArray(this.__views__); + return result; + } + + /** + * Reverses the direction of lazy iteration. + * + * @private + * @name reverse + * @memberOf LazyWrapper + * @returns {Object} Returns the new reversed `LazyWrapper` object. + */ + function lazyReverse() { + if (this.__filtered__) { + var result = new LazyWrapper(this); + result.__dir__ = -1; + result.__filtered__ = true; + } else { + result = this.clone(); + result.__dir__ *= -1; + } + return result; + } + + /** + * Extracts the unwrapped value from its lazy wrapper. + * + * @private + * @name value + * @memberOf LazyWrapper + * @returns {*} Returns the unwrapped value. + */ + function lazyValue() { + var array = this.__wrapped__.value(), + dir = this.__dir__, + isArr = isArray(array), + isRight = dir < 0, + arrLength = isArr ? array.length : 0, + view = getView(0, arrLength, this.__views__), + start = view.start, + end = view.end, + length = end - start, + index = isRight ? end : (start - 1), + iteratees = this.__iteratees__, + iterLength = iteratees.length, + resIndex = 0, + takeCount = nativeMin(length, this.__takeCount__); + + if (!isArr || (!isRight && arrLength == length && takeCount == length)) { + return baseWrapperValue(array, this.__actions__); + } + var result = []; + + outer: + while (length-- && resIndex < takeCount) { + index += dir; + + var iterIndex = -1, + value = array[index]; + + while (++iterIndex < iterLength) { + var data = iteratees[iterIndex], + iteratee = data.iteratee, + type = data.type, + computed = iteratee(value); + + if (type == LAZY_MAP_FLAG) { + value = computed; + } else if (!computed) { + if (type == LAZY_FILTER_FLAG) { + continue outer; + } else { + break outer; + } + } + } + result[resIndex++] = value; + } + return result; + } + + // Ensure `LazyWrapper` is an instance of `baseLodash`. + LazyWrapper.prototype = baseCreate(baseLodash.prototype); + LazyWrapper.prototype.constructor = LazyWrapper; + + /*------------------------------------------------------------------------*/ + + /** + * Creates a hash object. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ + function Hash(entries) { + var index = -1, + length = entries == null ? 0 : entries.length; + + this.clear(); + while (++index < length) { + var entry = entries[index]; + this.set(entry[0], entry[1]); + } + } + + /** + * Removes all key-value entries from the hash. + * + * @private + * @name clear + * @memberOf Hash + */ + function hashClear() { + this.__data__ = nativeCreate ? nativeCreate(null) : {}; + this.size = 0; + } + + /** + * Removes `key` and its value from the hash. + * + * @private + * @name delete + * @memberOf Hash + * @param {Object} hash The hash to modify. + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ + function hashDelete(key) { + var result = this.has(key) && delete this.__data__[key]; + this.size -= result ? 1 : 0; + return result; + } + + /** + * Gets the hash value for `key`. + * + * @private + * @name get + * @memberOf Hash + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ + function hashGet(key) { + var data = this.__data__; + if (nativeCreate) { + var result = data[key]; + return result === HASH_UNDEFINED ? undefined : result; + } + return hasOwnProperty.call(data, key) ? data[key] : undefined; + } + + /** + * Checks if a hash value for `key` exists. + * + * @private + * @name has + * @memberOf Hash + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function hashHas(key) { + var data = this.__data__; + return nativeCreate ? (data[key] !== undefined) : hasOwnProperty.call(data, key); + } + + /** + * Sets the hash `key` to `value`. + * + * @private + * @name set + * @memberOf Hash + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the hash instance. + */ + function hashSet(key, value) { + var data = this.__data__; + this.size += this.has(key) ? 0 : 1; + data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value; + return this; + } + + // Add methods to `Hash`. + Hash.prototype.clear = hashClear; + Hash.prototype['delete'] = hashDelete; + Hash.prototype.get = hashGet; + Hash.prototype.has = hashHas; + Hash.prototype.set = hashSet; + + /*------------------------------------------------------------------------*/ + + /** + * Creates an list cache object. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ + function ListCache(entries) { + var index = -1, + length = entries == null ? 0 : entries.length; + + this.clear(); + while (++index < length) { + var entry = entries[index]; + this.set(entry[0], entry[1]); + } + } + + /** + * Removes all key-value entries from the list cache. + * + * @private + * @name clear + * @memberOf ListCache + */ + function listCacheClear() { + this.__data__ = []; + this.size = 0; + } + + /** + * Removes `key` and its value from the list cache. + * + * @private + * @name delete + * @memberOf ListCache + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ + function listCacheDelete(key) { + var data = this.__data__, + index = assocIndexOf(data, key); + + if (index < 0) { + return false; + } + var lastIndex = data.length - 1; + if (index == lastIndex) { + data.pop(); + } else { + splice.call(data, index, 1); + } + --this.size; + return true; + } + + /** + * Gets the list cache value for `key`. + * + * @private + * @name get + * @memberOf ListCache + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ + function listCacheGet(key) { + var data = this.__data__, + index = assocIndexOf(data, key); + + return index < 0 ? undefined : data[index][1]; + } + + /** + * Checks if a list cache value for `key` exists. + * + * @private + * @name has + * @memberOf ListCache + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function listCacheHas(key) { + return assocIndexOf(this.__data__, key) > -1; + } + + /** + * Sets the list cache `key` to `value`. + * + * @private + * @name set + * @memberOf ListCache + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the list cache instance. + */ + function listCacheSet(key, value) { + var data = this.__data__, + index = assocIndexOf(data, key); + + if (index < 0) { + ++this.size; + data.push([key, value]); + } else { + data[index][1] = value; + } + return this; + } + + // Add methods to `ListCache`. + ListCache.prototype.clear = listCacheClear; + ListCache.prototype['delete'] = listCacheDelete; + ListCache.prototype.get = listCacheGet; + ListCache.prototype.has = listCacheHas; + ListCache.prototype.set = listCacheSet; + + /*------------------------------------------------------------------------*/ + + /** + * Creates a map cache object to store key-value pairs. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ + function MapCache(entries) { + var index = -1, + length = entries == null ? 0 : entries.length; + + this.clear(); + while (++index < length) { + var entry = entries[index]; + this.set(entry[0], entry[1]); + } + } + + /** + * Removes all key-value entries from the map. + * + * @private + * @name clear + * @memberOf MapCache + */ + function mapCacheClear() { + this.size = 0; + this.__data__ = { + 'hash': new Hash, + 'map': new (Map || ListCache), + 'string': new Hash + }; + } + + /** + * Removes `key` and its value from the map. + * + * @private + * @name delete + * @memberOf MapCache + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ + function mapCacheDelete(key) { + var result = getMapData(this, key)['delete'](key); + this.size -= result ? 1 : 0; + return result; + } + + /** + * Gets the map value for `key`. + * + * @private + * @name get + * @memberOf MapCache + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ + function mapCacheGet(key) { + return getMapData(this, key).get(key); + } + + /** + * Checks if a map value for `key` exists. + * + * @private + * @name has + * @memberOf MapCache + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function mapCacheHas(key) { + return getMapData(this, key).has(key); + } + + /** + * Sets the map `key` to `value`. + * + * @private + * @name set + * @memberOf MapCache + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the map cache instance. + */ + function mapCacheSet(key, value) { + var data = getMapData(this, key), + size = data.size; + + data.set(key, value); + this.size += data.size == size ? 0 : 1; + return this; + } + + // Add methods to `MapCache`. + MapCache.prototype.clear = mapCacheClear; + MapCache.prototype['delete'] = mapCacheDelete; + MapCache.prototype.get = mapCacheGet; + MapCache.prototype.has = mapCacheHas; + MapCache.prototype.set = mapCacheSet; + + /*------------------------------------------------------------------------*/ + + /** + * + * Creates an array cache object to store unique values. + * + * @private + * @constructor + * @param {Array} [values] The values to cache. + */ + function SetCache(values) { + var index = -1, + length = values == null ? 0 : values.length; + + this.__data__ = new MapCache; + while (++index < length) { + this.add(values[index]); + } + } + + /** + * Adds `value` to the array cache. + * + * @private + * @name add + * @memberOf SetCache + * @alias push + * @param {*} value The value to cache. + * @returns {Object} Returns the cache instance. + */ + function setCacheAdd(value) { + this.__data__.set(value, HASH_UNDEFINED); + return this; + } + + /** + * Checks if `value` is in the array cache. + * + * @private + * @name has + * @memberOf SetCache + * @param {*} value The value to search for. + * @returns {number} Returns `true` if `value` is found, else `false`. + */ + function setCacheHas(value) { + return this.__data__.has(value); + } + + // Add methods to `SetCache`. + SetCache.prototype.add = SetCache.prototype.push = setCacheAdd; + SetCache.prototype.has = setCacheHas; + + /*------------------------------------------------------------------------*/ + + /** + * Creates a stack cache object to store key-value pairs. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ + function Stack(entries) { + var data = this.__data__ = new ListCache(entries); + this.size = data.size; + } + + /** + * Removes all key-value entries from the stack. + * + * @private + * @name clear + * @memberOf Stack + */ + function stackClear() { + this.__data__ = new ListCache; + this.size = 0; + } + + /** + * Removes `key` and its value from the stack. + * + * @private + * @name delete + * @memberOf Stack + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ + function stackDelete(key) { + var data = this.__data__, + result = data['delete'](key); + + this.size = data.size; + return result; + } + + /** + * Gets the stack value for `key`. + * + * @private + * @name get + * @memberOf Stack + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ + function stackGet(key) { + return this.__data__.get(key); + } + + /** + * Checks if a stack value for `key` exists. + * + * @private + * @name has + * @memberOf Stack + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function stackHas(key) { + return this.__data__.has(key); + } + + /** + * Sets the stack `key` to `value`. + * + * @private + * @name set + * @memberOf Stack + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the stack cache instance. + */ + function stackSet(key, value) { + var data = this.__data__; + if (data instanceof ListCache) { + var pairs = data.__data__; + if (!Map || (pairs.length < LARGE_ARRAY_SIZE - 1)) { + pairs.push([key, value]); + this.size = ++data.size; + return this; + } + data = this.__data__ = new MapCache(pairs); + } + data.set(key, value); + this.size = data.size; + return this; + } + + // Add methods to `Stack`. + Stack.prototype.clear = stackClear; + Stack.prototype['delete'] = stackDelete; + Stack.prototype.get = stackGet; + Stack.prototype.has = stackHas; + Stack.prototype.set = stackSet; + + /*------------------------------------------------------------------------*/ + + /** + * Creates an array of the enumerable property names of the array-like `value`. + * + * @private + * @param {*} value The value to query. + * @param {boolean} inherited Specify returning inherited property names. + * @returns {Array} Returns the array of property names. + */ + function arrayLikeKeys(value, inherited) { + var isArr = isArray(value), + isArg = !isArr && isArguments(value), + isBuff = !isArr && !isArg && isBuffer(value), + isType = !isArr && !isArg && !isBuff && isTypedArray(value), + skipIndexes = isArr || isArg || isBuff || isType, + result = skipIndexes ? baseTimes(value.length, String) : [], + length = result.length; + + for (var key in value) { + if ((inherited || hasOwnProperty.call(value, key)) && + !(skipIndexes && ( + // Safari 9 has enumerable `arguments.length` in strict mode. + key == 'length' || + // Node.js 0.10 has enumerable non-index properties on buffers. + (isBuff && (key == 'offset' || key == 'parent')) || + // PhantomJS 2 has enumerable non-index properties on typed arrays. + (isType && (key == 'buffer' || key == 'byteLength' || key == 'byteOffset')) || + // Skip index properties. + isIndex(key, length) + ))) { + result.push(key); + } + } + return result; + } + + /** + * A specialized version of `_.sample` for arrays. + * + * @private + * @param {Array} array The array to sample. + * @returns {*} Returns the random element. + */ + function arraySample(array) { + var length = array.length; + return length ? array[baseRandom(0, length - 1)] : undefined; + } + + /** + * A specialized version of `_.sampleSize` for arrays. + * + * @private + * @param {Array} array The array to sample. + * @param {number} n The number of elements to sample. + * @returns {Array} Returns the random elements. + */ + function arraySampleSize(array, n) { + return shuffleSelf(copyArray(array), baseClamp(n, 0, array.length)); + } + + /** + * A specialized version of `_.shuffle` for arrays. + * + * @private + * @param {Array} array The array to shuffle. + * @returns {Array} Returns the new shuffled array. + */ + function arrayShuffle(array) { + return shuffleSelf(copyArray(array)); + } + + /** + * This function is like `assignValue` except that it doesn't assign + * `undefined` values. + * + * @private + * @param {Object} object The object to modify. + * @param {string} key The key of the property to assign. + * @param {*} value The value to assign. + */ + function assignMergeValue(object, key, value) { + if ((value !== undefined && !eq(object[key], value)) || + (value === undefined && !(key in object))) { + baseAssignValue(object, key, value); + } + } + + /** + * Assigns `value` to `key` of `object` if the existing value is not equivalent + * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * for equality comparisons. + * + * @private + * @param {Object} object The object to modify. + * @param {string} key The key of the property to assign. + * @param {*} value The value to assign. + */ + function assignValue(object, key, value) { + var objValue = object[key]; + if (!(hasOwnProperty.call(object, key) && eq(objValue, value)) || + (value === undefined && !(key in object))) { + baseAssignValue(object, key, value); + } + } + + /** + * Gets the index at which the `key` is found in `array` of key-value pairs. + * + * @private + * @param {Array} array The array to inspect. + * @param {*} key The key to search for. + * @returns {number} Returns the index of the matched value, else `-1`. + */ + function assocIndexOf(array, key) { + var length = array.length; + while (length--) { + if (eq(array[length][0], key)) { + return length; + } + } + return -1; + } + + /** + * Aggregates elements of `collection` on `accumulator` with keys transformed + * by `iteratee` and values set by `setter`. + * + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} setter The function to set `accumulator` values. + * @param {Function} iteratee The iteratee to transform keys. + * @param {Object} accumulator The initial aggregated object. + * @returns {Function} Returns `accumulator`. + */ + function baseAggregator(collection, setter, iteratee, accumulator) { + baseEach(collection, function(value, key, collection) { + setter(accumulator, value, iteratee(value), collection); + }); + return accumulator; + } + + /** + * The base implementation of `_.assign` without support for multiple sources + * or `customizer` functions. + * + * @private + * @param {Object} object The destination object. + * @param {Object} source The source object. + * @returns {Object} Returns `object`. + */ + function baseAssign(object, source) { + return object && copyObject(source, keys(source), object); + } + + /** + * The base implementation of `_.assignIn` without support for multiple sources + * or `customizer` functions. + * + * @private + * @param {Object} object The destination object. + * @param {Object} source The source object. + * @returns {Object} Returns `object`. + */ + function baseAssignIn(object, source) { + return object && copyObject(source, keysIn(source), object); + } + + /** + * The base implementation of `assignValue` and `assignMergeValue` without + * value checks. + * + * @private + * @param {Object} object The object to modify. + * @param {string} key The key of the property to assign. + * @param {*} value The value to assign. + */ + function baseAssignValue(object, key, value) { + if (key == '__proto__' && defineProperty) { + defineProperty(object, key, { + 'configurable': true, + 'enumerable': true, + 'value': value, + 'writable': true + }); + } else { + object[key] = value; + } + } + + /** + * The base implementation of `_.at` without support for individual paths. + * + * @private + * @param {Object} object The object to iterate over. + * @param {string[]} paths The property paths to pick. + * @returns {Array} Returns the picked elements. + */ + function baseAt(object, paths) { + var index = -1, + length = paths.length, + result = Array(length), + skip = object == null; + + while (++index < length) { + result[index] = skip ? undefined : get(object, paths[index]); + } + return result; + } + + /** + * The base implementation of `_.clamp` which doesn't coerce arguments. + * + * @private + * @param {number} number The number to clamp. + * @param {number} [lower] The lower bound. + * @param {number} upper The upper bound. + * @returns {number} Returns the clamped number. + */ + function baseClamp(number, lower, upper) { + if (number === number) { + if (upper !== undefined) { + number = number <= upper ? number : upper; + } + if (lower !== undefined) { + number = number >= lower ? number : lower; + } + } + return number; + } + + /** + * The base implementation of `_.clone` and `_.cloneDeep` which tracks + * traversed objects. + * + * @private + * @param {*} value The value to clone. + * @param {boolean} bitmask The bitmask flags. + * 1 - Deep clone + * 2 - Flatten inherited properties + * 4 - Clone symbols + * @param {Function} [customizer] The function to customize cloning. + * @param {string} [key] The key of `value`. + * @param {Object} [object] The parent object of `value`. + * @param {Object} [stack] Tracks traversed objects and their clone counterparts. + * @returns {*} Returns the cloned value. + */ + function baseClone(value, bitmask, customizer, key, object, stack) { + var result, + isDeep = bitmask & CLONE_DEEP_FLAG, + isFlat = bitmask & CLONE_FLAT_FLAG, + isFull = bitmask & CLONE_SYMBOLS_FLAG; + + if (customizer) { + result = object ? customizer(value, key, object, stack) : customizer(value); + } + if (result !== undefined) { + return result; + } + if (!isObject(value)) { + return value; + } + var isArr = isArray(value); + if (isArr) { + result = initCloneArray(value); + if (!isDeep) { + return copyArray(value, result); + } + } else { + var tag = getTag(value), + isFunc = tag == funcTag || tag == genTag; + + if (isBuffer(value)) { + return cloneBuffer(value, isDeep); + } + if (tag == objectTag || tag == argsTag || (isFunc && !object)) { + result = (isFlat || isFunc) ? {} : initCloneObject(value); + if (!isDeep) { + return isFlat + ? copySymbolsIn(value, baseAssignIn(result, value)) + : copySymbols(value, baseAssign(result, value)); + } + } else { + if (!cloneableTags[tag]) { + return object ? value : {}; + } + result = initCloneByTag(value, tag, baseClone, isDeep); + } + } + // Check for circular references and return its corresponding clone. + stack || (stack = new Stack); + var stacked = stack.get(value); + if (stacked) { + return stacked; + } + stack.set(value, result); + + var keysFunc = isFull + ? (isFlat ? getAllKeysIn : getAllKeys) + : (isFlat ? keysIn : keys); + + var props = isArr ? undefined : keysFunc(value); + arrayEach(props || value, function(subValue, key) { + if (props) { + key = subValue; + subValue = value[key]; + } + // Recursively populate clone (susceptible to call stack limits). + assignValue(result, key, baseClone(subValue, bitmask, customizer, key, value, stack)); + }); + return result; + } + + /** + * The base implementation of `_.conforms` which doesn't clone `source`. + * + * @private + * @param {Object} source The object of property predicates to conform to. + * @returns {Function} Returns the new spec function. + */ + function baseConforms(source) { + var props = keys(source); + return function(object) { + return baseConformsTo(object, source, props); + }; + } + + /** + * The base implementation of `_.conformsTo` which accepts `props` to check. + * + * @private + * @param {Object} object The object to inspect. + * @param {Object} source The object of property predicates to conform to. + * @returns {boolean} Returns `true` if `object` conforms, else `false`. + */ + function baseConformsTo(object, source, props) { + var length = props.length; + if (object == null) { + return !length; + } + object = Object(object); + while (length--) { + var key = props[length], + predicate = source[key], + value = object[key]; + + if ((value === undefined && !(key in object)) || !predicate(value)) { + return false; + } + } + return true; + } + + /** + * The base implementation of `_.delay` and `_.defer` which accepts `args` + * to provide to `func`. + * + * @private + * @param {Function} func The function to delay. + * @param {number} wait The number of milliseconds to delay invocation. + * @param {Array} args The arguments to provide to `func`. + * @returns {number|Object} Returns the timer id or timeout object. + */ + function baseDelay(func, wait, args) { + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + return setTimeout(function() { func.apply(undefined, args); }, wait); + } + + /** + * The base implementation of methods like `_.difference` without support + * for excluding multiple arrays or iteratee shorthands. + * + * @private + * @param {Array} array The array to inspect. + * @param {Array} values The values to exclude. + * @param {Function} [iteratee] The iteratee invoked per element. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns the new array of filtered values. + */ + function baseDifference(array, values, iteratee, comparator) { + var index = -1, + includes = arrayIncludes, + isCommon = true, + length = array.length, + result = [], + valuesLength = values.length; + + if (!length) { + return result; + } + if (iteratee) { + values = arrayMap(values, baseUnary(iteratee)); + } + if (comparator) { + includes = arrayIncludesWith; + isCommon = false; + } + else if (values.length >= LARGE_ARRAY_SIZE) { + includes = cacheHas; + isCommon = false; + values = new SetCache(values); + } + outer: + while (++index < length) { + var value = array[index], + computed = iteratee == null ? value : iteratee(value); + + value = (comparator || value !== 0) ? value : 0; + if (isCommon && computed === computed) { + var valuesIndex = valuesLength; + while (valuesIndex--) { + if (values[valuesIndex] === computed) { + continue outer; + } + } + result.push(value); + } + else if (!includes(values, computed, comparator)) { + result.push(value); + } + } + return result; + } + + /** + * The base implementation of `_.forEach` without support for iteratee shorthands. + * + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array|Object} Returns `collection`. + */ + var baseEach = createBaseEach(baseForOwn); + + /** + * The base implementation of `_.forEachRight` without support for iteratee shorthands. + * + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array|Object} Returns `collection`. + */ + var baseEachRight = createBaseEach(baseForOwnRight, true); + + /** + * The base implementation of `_.every` without support for iteratee shorthands. + * + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {boolean} Returns `true` if all elements pass the predicate check, + * else `false` + */ + function baseEvery(collection, predicate) { + var result = true; + baseEach(collection, function(value, index, collection) { + result = !!predicate(value, index, collection); + return result; + }); + return result; + } + + /** + * The base implementation of methods like `_.max` and `_.min` which accepts a + * `comparator` to determine the extremum value. + * + * @private + * @param {Array} array The array to iterate over. + * @param {Function} iteratee The iteratee invoked per iteration. + * @param {Function} comparator The comparator used to compare values. + * @returns {*} Returns the extremum value. + */ + function baseExtremum(array, iteratee, comparator) { + var index = -1, + length = array.length; + + while (++index < length) { + var value = array[index], + current = iteratee(value); + + if (current != null && (computed === undefined + ? (current === current && !isSymbol(current)) + : comparator(current, computed) + )) { + var computed = current, + result = value; + } + } + return result; + } + + /** + * The base implementation of `_.fill` without an iteratee call guard. + * + * @private + * @param {Array} array The array to fill. + * @param {*} value The value to fill `array` with. + * @param {number} [start=0] The start position. + * @param {number} [end=array.length] The end position. + * @returns {Array} Returns `array`. + */ + function baseFill(array, value, start, end) { + var length = array.length; + + start = toInteger(start); + if (start < 0) { + start = -start > length ? 0 : (length + start); + } + end = (end === undefined || end > length) ? length : toInteger(end); + if (end < 0) { + end += length; + } + end = start > end ? 0 : toLength(end); + while (start < end) { + array[start++] = value; + } + return array; + } + + /** + * The base implementation of `_.filter` without support for iteratee shorthands. + * + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {Array} Returns the new filtered array. + */ + function baseFilter(collection, predicate) { + var result = []; + baseEach(collection, function(value, index, collection) { + if (predicate(value, index, collection)) { + result.push(value); + } + }); + return result; + } + + /** + * The base implementation of `_.flatten` with support for restricting flattening. + * + * @private + * @param {Array} array The array to flatten. + * @param {number} depth The maximum recursion depth. + * @param {boolean} [predicate=isFlattenable] The function invoked per iteration. + * @param {boolean} [isStrict] Restrict to values that pass `predicate` checks. + * @param {Array} [result=[]] The initial result value. + * @returns {Array} Returns the new flattened array. + */ + function baseFlatten(array, depth, predicate, isStrict, result) { + var index = -1, + length = array.length; + + predicate || (predicate = isFlattenable); + result || (result = []); + + while (++index < length) { + var value = array[index]; + if (depth > 0 && predicate(value)) { + if (depth > 1) { + // Recursively flatten arrays (susceptible to call stack limits). + baseFlatten(value, depth - 1, predicate, isStrict, result); + } else { + arrayPush(result, value); + } + } else if (!isStrict) { + result[result.length] = value; + } + } + return result; + } + + /** + * The base implementation of `baseForOwn` which iterates over `object` + * properties returned by `keysFunc` and invokes `iteratee` for each property. + * Iteratee functions may exit iteration early by explicitly returning `false`. + * + * @private + * @param {Object} object The object to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {Function} keysFunc The function to get the keys of `object`. + * @returns {Object} Returns `object`. + */ + var baseFor = createBaseFor(); + + /** + * This function is like `baseFor` except that it iterates over properties + * in the opposite order. + * + * @private + * @param {Object} object The object to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {Function} keysFunc The function to get the keys of `object`. + * @returns {Object} Returns `object`. + */ + var baseForRight = createBaseFor(true); + + /** + * The base implementation of `_.forOwn` without support for iteratee shorthands. + * + * @private + * @param {Object} object The object to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Object} Returns `object`. + */ + function baseForOwn(object, iteratee) { + return object && baseFor(object, iteratee, keys); + } + + /** + * The base implementation of `_.forOwnRight` without support for iteratee shorthands. + * + * @private + * @param {Object} object The object to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Object} Returns `object`. + */ + function baseForOwnRight(object, iteratee) { + return object && baseForRight(object, iteratee, keys); + } + + /** + * The base implementation of `_.functions` which creates an array of + * `object` function property names filtered from `props`. + * + * @private + * @param {Object} object The object to inspect. + * @param {Array} props The property names to filter. + * @returns {Array} Returns the function names. + */ + function baseFunctions(object, props) { + return arrayFilter(props, function(key) { + return isFunction(object[key]); + }); + } + + /** + * The base implementation of `_.get` without support for default values. + * + * @private + * @param {Object} object The object to query. + * @param {Array|string} path The path of the property to get. + * @returns {*} Returns the resolved value. + */ + function baseGet(object, path) { + path = castPath(path, object); + + var index = 0, + length = path.length; + + while (object != null && index < length) { + object = object[toKey(path[index++])]; + } + return (index && index == length) ? object : undefined; + } + + /** + * The base implementation of `getAllKeys` and `getAllKeysIn` which uses + * `keysFunc` and `symbolsFunc` to get the enumerable property names and + * symbols of `object`. + * + * @private + * @param {Object} object The object to query. + * @param {Function} keysFunc The function to get the keys of `object`. + * @param {Function} symbolsFunc The function to get the symbols of `object`. + * @returns {Array} Returns the array of property names and symbols. + */ + function baseGetAllKeys(object, keysFunc, symbolsFunc) { + var result = keysFunc(object); + return isArray(object) ? result : arrayPush(result, symbolsFunc(object)); + } + + /** + * The base implementation of `getTag` without fallbacks for buggy environments. + * + * @private + * @param {*} value The value to query. + * @returns {string} Returns the `toStringTag`. + */ + function baseGetTag(value) { + if (value == null) { + return value === undefined ? undefinedTag : nullTag; + } + return (symToStringTag && symToStringTag in Object(value)) + ? getRawTag(value) + : objectToString(value); + } + + /** + * The base implementation of `_.gt` which doesn't coerce arguments. + * + * @private + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if `value` is greater than `other`, + * else `false`. + */ + function baseGt(value, other) { + return value > other; + } + + /** + * The base implementation of `_.has` without support for deep paths. + * + * @private + * @param {Object} [object] The object to query. + * @param {Array|string} key The key to check. + * @returns {boolean} Returns `true` if `key` exists, else `false`. + */ + function baseHas(object, key) { + return object != null && hasOwnProperty.call(object, key); + } + + /** + * The base implementation of `_.hasIn` without support for deep paths. + * + * @private + * @param {Object} [object] The object to query. + * @param {Array|string} key The key to check. + * @returns {boolean} Returns `true` if `key` exists, else `false`. + */ + function baseHasIn(object, key) { + return object != null && key in Object(object); + } + + /** + * The base implementation of `_.inRange` which doesn't coerce arguments. + * + * @private + * @param {number} number The number to check. + * @param {number} start The start of the range. + * @param {number} end The end of the range. + * @returns {boolean} Returns `true` if `number` is in the range, else `false`. + */ + function baseInRange(number, start, end) { + return number >= nativeMin(start, end) && number < nativeMax(start, end); + } + + /** + * The base implementation of methods like `_.intersection`, without support + * for iteratee shorthands, that accepts an array of arrays to inspect. + * + * @private + * @param {Array} arrays The arrays to inspect. + * @param {Function} [iteratee] The iteratee invoked per element. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns the new array of shared values. + */ + function baseIntersection(arrays, iteratee, comparator) { + var includes = comparator ? arrayIncludesWith : arrayIncludes, + length = arrays[0].length, + othLength = arrays.length, + othIndex = othLength, + caches = Array(othLength), + maxLength = Infinity, + result = []; + + while (othIndex--) { + var array = arrays[othIndex]; + if (othIndex && iteratee) { + array = arrayMap(array, baseUnary(iteratee)); + } + maxLength = nativeMin(array.length, maxLength); + caches[othIndex] = !comparator && (iteratee || (length >= 120 && array.length >= 120)) + ? new SetCache(othIndex && array) + : undefined; + } + array = arrays[0]; + + var index = -1, + seen = caches[0]; + + outer: + while (++index < length && result.length < maxLength) { + var value = array[index], + computed = iteratee ? iteratee(value) : value; + + value = (comparator || value !== 0) ? value : 0; + if (!(seen + ? cacheHas(seen, computed) + : includes(result, computed, comparator) + )) { + othIndex = othLength; + while (--othIndex) { + var cache = caches[othIndex]; + if (!(cache + ? cacheHas(cache, computed) + : includes(arrays[othIndex], computed, comparator)) + ) { + continue outer; + } + } + if (seen) { + seen.push(computed); + } + result.push(value); + } + } + return result; + } + + /** + * The base implementation of `_.invert` and `_.invertBy` which inverts + * `object` with values transformed by `iteratee` and set by `setter`. + * + * @private + * @param {Object} object The object to iterate over. + * @param {Function} setter The function to set `accumulator` values. + * @param {Function} iteratee The iteratee to transform values. + * @param {Object} accumulator The initial inverted object. + * @returns {Function} Returns `accumulator`. + */ + function baseInverter(object, setter, iteratee, accumulator) { + baseForOwn(object, function(value, key, object) { + setter(accumulator, iteratee(value), key, object); + }); + return accumulator; + } + + /** + * The base implementation of `_.invoke` without support for individual + * method arguments. + * + * @private + * @param {Object} object The object to query. + * @param {Array|string} path The path of the method to invoke. + * @param {Array} args The arguments to invoke the method with. + * @returns {*} Returns the result of the invoked method. + */ + function baseInvoke(object, path, args) { + path = castPath(path, object); + object = parent(object, path); + var func = object == null ? object : object[toKey(last(path))]; + return func == null ? undefined : apply(func, object, args); + } + + /** + * The base implementation of `_.isArguments`. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an `arguments` object, + */ + function baseIsArguments(value) { + return isObjectLike(value) && baseGetTag(value) == argsTag; + } + + /** + * The base implementation of `_.isArrayBuffer` without Node.js optimizations. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an array buffer, else `false`. + */ + function baseIsArrayBuffer(value) { + return isObjectLike(value) && baseGetTag(value) == arrayBufferTag; + } + + /** + * The base implementation of `_.isDate` without Node.js optimizations. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a date object, else `false`. + */ + function baseIsDate(value) { + return isObjectLike(value) && baseGetTag(value) == dateTag; + } + + /** + * The base implementation of `_.isEqual` which supports partial comparisons + * and tracks traversed objects. + * + * @private + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @param {boolean} bitmask The bitmask flags. + * 1 - Unordered comparison + * 2 - Partial comparison + * @param {Function} [customizer] The function to customize comparisons. + * @param {Object} [stack] Tracks traversed `value` and `other` objects. + * @returns {boolean} Returns `true` if the values are equivalent, else `false`. + */ + function baseIsEqual(value, other, bitmask, customizer, stack) { + if (value === other) { + return true; + } + if (value == null || other == null || (!isObjectLike(value) && !isObjectLike(other))) { + return value !== value && other !== other; + } + return baseIsEqualDeep(value, other, bitmask, customizer, baseIsEqual, stack); + } + + /** + * A specialized version of `baseIsEqual` for arrays and objects which performs + * deep comparisons and tracks traversed objects enabling objects with circular + * references to be compared. + * + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. + * @param {Function} customizer The function to customize comparisons. + * @param {Function} equalFunc The function to determine equivalents of values. + * @param {Object} [stack] Tracks traversed `object` and `other` objects. + * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. + */ + function baseIsEqualDeep(object, other, bitmask, customizer, equalFunc, stack) { + var objIsArr = isArray(object), + othIsArr = isArray(other), + objTag = objIsArr ? arrayTag : getTag(object), + othTag = othIsArr ? arrayTag : getTag(other); + + objTag = objTag == argsTag ? objectTag : objTag; + othTag = othTag == argsTag ? objectTag : othTag; + + var objIsObj = objTag == objectTag, + othIsObj = othTag == objectTag, + isSameTag = objTag == othTag; + + if (isSameTag && isBuffer(object)) { + if (!isBuffer(other)) { + return false; + } + objIsArr = true; + objIsObj = false; + } + if (isSameTag && !objIsObj) { + stack || (stack = new Stack); + return (objIsArr || isTypedArray(object)) + ? equalArrays(object, other, bitmask, customizer, equalFunc, stack) + : equalByTag(object, other, objTag, bitmask, customizer, equalFunc, stack); + } + if (!(bitmask & COMPARE_PARTIAL_FLAG)) { + var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'), + othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__'); + + if (objIsWrapped || othIsWrapped) { + var objUnwrapped = objIsWrapped ? object.value() : object, + othUnwrapped = othIsWrapped ? other.value() : other; + + stack || (stack = new Stack); + return equalFunc(objUnwrapped, othUnwrapped, bitmask, customizer, stack); + } + } + if (!isSameTag) { + return false; + } + stack || (stack = new Stack); + return equalObjects(object, other, bitmask, customizer, equalFunc, stack); + } + + /** + * The base implementation of `_.isMap` without Node.js optimizations. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a map, else `false`. + */ + function baseIsMap(value) { + return isObjectLike(value) && getTag(value) == mapTag; + } + + /** + * The base implementation of `_.isMatch` without support for iteratee shorthands. + * + * @private + * @param {Object} object The object to inspect. + * @param {Object} source The object of property values to match. + * @param {Array} matchData The property names, values, and compare flags to match. + * @param {Function} [customizer] The function to customize comparisons. + * @returns {boolean} Returns `true` if `object` is a match, else `false`. + */ + function baseIsMatch(object, source, matchData, customizer) { + var index = matchData.length, + length = index, + noCustomizer = !customizer; + + if (object == null) { + return !length; + } + object = Object(object); + while (index--) { + var data = matchData[index]; + if ((noCustomizer && data[2]) + ? data[1] !== object[data[0]] + : !(data[0] in object) + ) { + return false; + } + } + while (++index < length) { + data = matchData[index]; + var key = data[0], + objValue = object[key], + srcValue = data[1]; + + if (noCustomizer && data[2]) { + if (objValue === undefined && !(key in object)) { + return false; + } + } else { + var stack = new Stack; + if (customizer) { + var result = customizer(objValue, srcValue, key, object, source, stack); + } + if (!(result === undefined + ? baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG, customizer, stack) + : result + )) { + return false; + } + } + } + return true; + } + + /** + * The base implementation of `_.isNative` without bad shim checks. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a native function, + * else `false`. + */ + function baseIsNative(value) { + if (!isObject(value) || isMasked(value)) { + return false; + } + var pattern = isFunction(value) ? reIsNative : reIsHostCtor; + return pattern.test(toSource(value)); + } + + /** + * The base implementation of `_.isRegExp` without Node.js optimizations. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a regexp, else `false`. + */ + function baseIsRegExp(value) { + return isObjectLike(value) && baseGetTag(value) == regexpTag; + } + + /** + * The base implementation of `_.isSet` without Node.js optimizations. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a set, else `false`. + */ + function baseIsSet(value) { + return isObjectLike(value) && getTag(value) == setTag; + } + + /** + * The base implementation of `_.isTypedArray` without Node.js optimizations. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a typed array, else `false`. + */ + function baseIsTypedArray(value) { + return isObjectLike(value) && + isLength(value.length) && !!typedArrayTags[baseGetTag(value)]; + } + + /** + * The base implementation of `_.iteratee`. + * + * @private + * @param {*} [value=_.identity] The value to convert to an iteratee. + * @returns {Function} Returns the iteratee. + */ + function baseIteratee(value) { + // Don't store the `typeof` result in a variable to avoid a JIT bug in Safari 9. + // See https://bugs.webkit.org/show_bug.cgi?id=156034 for more details. + if (typeof value == 'function') { + return value; + } + if (value == null) { + return identity; + } + if (typeof value == 'object') { + return isArray(value) + ? baseMatchesProperty(value[0], value[1]) + : baseMatches(value); + } + return property(value); + } + + /** + * The base implementation of `_.keys` which doesn't treat sparse arrays as dense. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + */ + function baseKeys(object) { + if (!isPrototype(object)) { + return nativeKeys(object); + } + var result = []; + for (var key in Object(object)) { + if (hasOwnProperty.call(object, key) && key != 'constructor') { + result.push(key); + } + } + return result; + } + + /** + * The base implementation of `_.keysIn` which doesn't treat sparse arrays as dense. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + */ + function baseKeysIn(object) { + if (!isObject(object)) { + return nativeKeysIn(object); + } + var isProto = isPrototype(object), + result = []; + + for (var key in object) { + if (!(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) { + result.push(key); + } + } + return result; + } + + /** + * The base implementation of `_.lt` which doesn't coerce arguments. + * + * @private + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if `value` is less than `other`, + * else `false`. + */ + function baseLt(value, other) { + return value < other; + } + + /** + * The base implementation of `_.map` without support for iteratee shorthands. + * + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns the new mapped array. + */ + function baseMap(collection, iteratee) { + var index = -1, + result = isArrayLike(collection) ? Array(collection.length) : []; + + baseEach(collection, function(value, key, collection) { + result[++index] = iteratee(value, key, collection); + }); + return result; + } + + /** + * The base implementation of `_.matches` which doesn't clone `source`. + * + * @private + * @param {Object} source The object of property values to match. + * @returns {Function} Returns the new spec function. + */ + function baseMatches(source) { + var matchData = getMatchData(source); + if (matchData.length == 1 && matchData[0][2]) { + return matchesStrictComparable(matchData[0][0], matchData[0][1]); + } + return function(object) { + return object === source || baseIsMatch(object, source, matchData); + }; + } + + /** + * The base implementation of `_.matchesProperty` which doesn't clone `srcValue`. + * + * @private + * @param {string} path The path of the property to get. + * @param {*} srcValue The value to match. + * @returns {Function} Returns the new spec function. + */ + function baseMatchesProperty(path, srcValue) { + if (isKey(path) && isStrictComparable(srcValue)) { + return matchesStrictComparable(toKey(path), srcValue); + } + return function(object) { + var objValue = get(object, path); + return (objValue === undefined && objValue === srcValue) + ? hasIn(object, path) + : baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG); + }; + } + + /** + * The base implementation of `_.merge` without support for multiple sources. + * + * @private + * @param {Object} object The destination object. + * @param {Object} source The source object. + * @param {number} srcIndex The index of `source`. + * @param {Function} [customizer] The function to customize merged values. + * @param {Object} [stack] Tracks traversed source values and their merged + * counterparts. + */ + function baseMerge(object, source, srcIndex, customizer, stack) { + if (object === source) { + return; + } + baseFor(source, function(srcValue, key) { + if (isObject(srcValue)) { + stack || (stack = new Stack); + baseMergeDeep(object, source, key, srcIndex, baseMerge, customizer, stack); + } + else { + var newValue = customizer + ? customizer(object[key], srcValue, (key + ''), object, source, stack) + : undefined; + + if (newValue === undefined) { + newValue = srcValue; + } + assignMergeValue(object, key, newValue); + } + }, keysIn); + } + + /** + * A specialized version of `baseMerge` for arrays and objects which performs + * deep merges and tracks traversed objects enabling objects with circular + * references to be merged. + * + * @private + * @param {Object} object The destination object. + * @param {Object} source The source object. + * @param {string} key The key of the value to merge. + * @param {number} srcIndex The index of `source`. + * @param {Function} mergeFunc The function to merge values. + * @param {Function} [customizer] The function to customize assigned values. + * @param {Object} [stack] Tracks traversed source values and their merged + * counterparts. + */ + function baseMergeDeep(object, source, key, srcIndex, mergeFunc, customizer, stack) { + var objValue = object[key], + srcValue = source[key], + stacked = stack.get(srcValue); + + if (stacked) { + assignMergeValue(object, key, stacked); + return; + } + var newValue = customizer + ? customizer(objValue, srcValue, (key + ''), object, source, stack) + : undefined; + + var isCommon = newValue === undefined; + + if (isCommon) { + var isArr = isArray(srcValue), + isBuff = !isArr && isBuffer(srcValue), + isTyped = !isArr && !isBuff && isTypedArray(srcValue); + + newValue = srcValue; + if (isArr || isBuff || isTyped) { + if (isArray(objValue)) { + newValue = objValue; + } + else if (isArrayLikeObject(objValue)) { + newValue = copyArray(objValue); + } + else if (isBuff) { + isCommon = false; + newValue = cloneBuffer(srcValue, true); + } + else if (isTyped) { + isCommon = false; + newValue = cloneTypedArray(srcValue, true); + } + else { + newValue = []; + } + } + else if (isPlainObject(srcValue) || isArguments(srcValue)) { + newValue = objValue; + if (isArguments(objValue)) { + newValue = toPlainObject(objValue); + } + else if (!isObject(objValue) || (srcIndex && isFunction(objValue))) { + newValue = initCloneObject(srcValue); + } + } + else { + isCommon = false; + } + } + if (isCommon) { + // Recursively merge objects and arrays (susceptible to call stack limits). + stack.set(srcValue, newValue); + mergeFunc(newValue, srcValue, srcIndex, customizer, stack); + stack['delete'](srcValue); + } + assignMergeValue(object, key, newValue); + } + + /** + * The base implementation of `_.nth` which doesn't coerce arguments. + * + * @private + * @param {Array} array The array to query. + * @param {number} n The index of the element to return. + * @returns {*} Returns the nth element of `array`. + */ + function baseNth(array, n) { + var length = array.length; + if (!length) { + return; + } + n += n < 0 ? length : 0; + return isIndex(n, length) ? array[n] : undefined; + } + + /** + * The base implementation of `_.orderBy` without param guards. + * + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {Function[]|Object[]|string[]} iteratees The iteratees to sort by. + * @param {string[]} orders The sort orders of `iteratees`. + * @returns {Array} Returns the new sorted array. + */ + function baseOrderBy(collection, iteratees, orders) { + var index = -1; + iteratees = arrayMap(iteratees.length ? iteratees : [identity], baseUnary(getIteratee())); + + var result = baseMap(collection, function(value, key, collection) { + var criteria = arrayMap(iteratees, function(iteratee) { + return iteratee(value); + }); + return { 'criteria': criteria, 'index': ++index, 'value': value }; + }); + + return baseSortBy(result, function(object, other) { + return compareMultiple(object, other, orders); + }); + } + + /** + * The base implementation of `_.pick` without support for individual + * property identifiers. + * + * @private + * @param {Object} object The source object. + * @param {string[]} paths The property paths to pick. + * @returns {Object} Returns the new object. + */ + function basePick(object, paths) { + return basePickBy(object, paths, function(value, path) { + return hasIn(object, path); + }); + } + + /** + * The base implementation of `_.pickBy` without support for iteratee shorthands. + * + * @private + * @param {Object} object The source object. + * @param {string[]} paths The property paths to pick. + * @param {Function} predicate The function invoked per property. + * @returns {Object} Returns the new object. + */ + function basePickBy(object, paths, predicate) { + var index = -1, + length = paths.length, + result = {}; + + while (++index < length) { + var path = paths[index], + value = baseGet(object, path); + + if (predicate(value, path)) { + baseSet(result, castPath(path, object), value); + } + } + return result; + } + + /** + * A specialized version of `baseProperty` which supports deep paths. + * + * @private + * @param {Array|string} path The path of the property to get. + * @returns {Function} Returns the new accessor function. + */ + function basePropertyDeep(path) { + return function(object) { + return baseGet(object, path); + }; + } + + /** + * The base implementation of `_.pullAllBy` without support for iteratee + * shorthands. + * + * @private + * @param {Array} array The array to modify. + * @param {Array} values The values to remove. + * @param {Function} [iteratee] The iteratee invoked per element. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns `array`. + */ + function basePullAll(array, values, iteratee, comparator) { + var indexOf = comparator ? baseIndexOfWith : baseIndexOf, + index = -1, + length = values.length, + seen = array; + + if (array === values) { + values = copyArray(values); + } + if (iteratee) { + seen = arrayMap(array, baseUnary(iteratee)); + } + while (++index < length) { + var fromIndex = 0, + value = values[index], + computed = iteratee ? iteratee(value) : value; + + while ((fromIndex = indexOf(seen, computed, fromIndex, comparator)) > -1) { + if (seen !== array) { + splice.call(seen, fromIndex, 1); + } + splice.call(array, fromIndex, 1); + } + } + return array; + } + + /** + * The base implementation of `_.pullAt` without support for individual + * indexes or capturing the removed elements. + * + * @private + * @param {Array} array The array to modify. + * @param {number[]} indexes The indexes of elements to remove. + * @returns {Array} Returns `array`. + */ + function basePullAt(array, indexes) { + var length = array ? indexes.length : 0, + lastIndex = length - 1; + + while (length--) { + var index = indexes[length]; + if (length == lastIndex || index !== previous) { + var previous = index; + if (isIndex(index)) { + splice.call(array, index, 1); + } else { + baseUnset(array, index); + } + } + } + return array; + } + + /** + * The base implementation of `_.random` without support for returning + * floating-point numbers. + * + * @private + * @param {number} lower The lower bound. + * @param {number} upper The upper bound. + * @returns {number} Returns the random number. + */ + function baseRandom(lower, upper) { + return lower + nativeFloor(nativeRandom() * (upper - lower + 1)); + } + + /** + * The base implementation of `_.range` and `_.rangeRight` which doesn't + * coerce arguments. + * + * @private + * @param {number} start The start of the range. + * @param {number} end The end of the range. + * @param {number} step The value to increment or decrement by. + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Array} Returns the range of numbers. + */ + function baseRange(start, end, step, fromRight) { + var index = -1, + length = nativeMax(nativeCeil((end - start) / (step || 1)), 0), + result = Array(length); + + while (length--) { + result[fromRight ? length : ++index] = start; + start += step; + } + return result; + } + + /** + * The base implementation of `_.repeat` which doesn't coerce arguments. + * + * @private + * @param {string} string The string to repeat. + * @param {number} n The number of times to repeat the string. + * @returns {string} Returns the repeated string. + */ + function baseRepeat(string, n) { + var result = ''; + if (!string || n < 1 || n > MAX_SAFE_INTEGER) { + return result; + } + // Leverage the exponentiation by squaring algorithm for a faster repeat. + // See https://en.wikipedia.org/wiki/Exponentiation_by_squaring for more details. + do { + if (n % 2) { + result += string; + } + n = nativeFloor(n / 2); + if (n) { + string += string; + } + } while (n); + + return result; + } + + /** + * The base implementation of `_.rest` which doesn't validate or coerce arguments. + * + * @private + * @param {Function} func The function to apply a rest parameter to. + * @param {number} [start=func.length-1] The start position of the rest parameter. + * @returns {Function} Returns the new function. + */ + function baseRest(func, start) { + return setToString(overRest(func, start, identity), func + ''); + } + + /** + * The base implementation of `_.sample`. + * + * @private + * @param {Array|Object} collection The collection to sample. + * @returns {*} Returns the random element. + */ + function baseSample(collection) { + return arraySample(values(collection)); + } + + /** + * The base implementation of `_.sampleSize` without param guards. + * + * @private + * @param {Array|Object} collection The collection to sample. + * @param {number} n The number of elements to sample. + * @returns {Array} Returns the random elements. + */ + function baseSampleSize(collection, n) { + var array = values(collection); + return shuffleSelf(array, baseClamp(n, 0, array.length)); + } + + /** + * The base implementation of `_.set`. + * + * @private + * @param {Object} object The object to modify. + * @param {Array|string} path The path of the property to set. + * @param {*} value The value to set. + * @param {Function} [customizer] The function to customize path creation. + * @returns {Object} Returns `object`. + */ + function baseSet(object, path, value, customizer) { + if (!isObject(object)) { + return object; + } + path = castPath(path, object); + + var index = -1, + length = path.length, + lastIndex = length - 1, + nested = object; + + while (nested != null && ++index < length) { + var key = toKey(path[index]), + newValue = value; + + if (index != lastIndex) { + var objValue = nested[key]; + newValue = customizer ? customizer(objValue, key, nested) : undefined; + if (newValue === undefined) { + newValue = isObject(objValue) + ? objValue + : (isIndex(path[index + 1]) ? [] : {}); + } + } + assignValue(nested, key, newValue); + nested = nested[key]; + } + return object; + } + + /** + * The base implementation of `setData` without support for hot loop shorting. + * + * @private + * @param {Function} func The function to associate metadata with. + * @param {*} data The metadata. + * @returns {Function} Returns `func`. + */ + var baseSetData = !metaMap ? identity : function(func, data) { + metaMap.set(func, data); + return func; + }; + + /** + * The base implementation of `setToString` without support for hot loop shorting. + * + * @private + * @param {Function} func The function to modify. + * @param {Function} string The `toString` result. + * @returns {Function} Returns `func`. + */ + var baseSetToString = !defineProperty ? identity : function(func, string) { + return defineProperty(func, 'toString', { + 'configurable': true, + 'enumerable': false, + 'value': constant(string), + 'writable': true + }); + }; + + /** + * The base implementation of `_.shuffle`. + * + * @private + * @param {Array|Object} collection The collection to shuffle. + * @returns {Array} Returns the new shuffled array. + */ + function baseShuffle(collection) { + return shuffleSelf(values(collection)); + } + + /** + * The base implementation of `_.slice` without an iteratee call guard. + * + * @private + * @param {Array} array The array to slice. + * @param {number} [start=0] The start position. + * @param {number} [end=array.length] The end position. + * @returns {Array} Returns the slice of `array`. + */ + function baseSlice(array, start, end) { + var index = -1, + length = array.length; + + if (start < 0) { + start = -start > length ? 0 : (length + start); + } + end = end > length ? length : end; + if (end < 0) { + end += length; + } + length = start > end ? 0 : ((end - start) >>> 0); + start >>>= 0; + + var result = Array(length); + while (++index < length) { + result[index] = array[index + start]; + } + return result; + } + + /** + * The base implementation of `_.some` without support for iteratee shorthands. + * + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {boolean} Returns `true` if any element passes the predicate check, + * else `false`. + */ + function baseSome(collection, predicate) { + var result; + + baseEach(collection, function(value, index, collection) { + result = predicate(value, index, collection); + return !result; + }); + return !!result; + } + + /** + * The base implementation of `_.sortedIndex` and `_.sortedLastIndex` which + * performs a binary search of `array` to determine the index at which `value` + * should be inserted into `array` in order to maintain its sort order. + * + * @private + * @param {Array} array The sorted array to inspect. + * @param {*} value The value to evaluate. + * @param {boolean} [retHighest] Specify returning the highest qualified index. + * @returns {number} Returns the index at which `value` should be inserted + * into `array`. + */ + function baseSortedIndex(array, value, retHighest) { + var low = 0, + high = array == null ? low : array.length; + + if (typeof value == 'number' && value === value && high <= HALF_MAX_ARRAY_LENGTH) { + while (low < high) { + var mid = (low + high) >>> 1, + computed = array[mid]; + + if (computed !== null && !isSymbol(computed) && + (retHighest ? (computed <= value) : (computed < value))) { + low = mid + 1; + } else { + high = mid; + } + } + return high; + } + return baseSortedIndexBy(array, value, identity, retHighest); + } + + /** + * The base implementation of `_.sortedIndexBy` and `_.sortedLastIndexBy` + * which invokes `iteratee` for `value` and each element of `array` to compute + * their sort ranking. The iteratee is invoked with one argument; (value). + * + * @private + * @param {Array} array The sorted array to inspect. + * @param {*} value The value to evaluate. + * @param {Function} iteratee The iteratee invoked per element. + * @param {boolean} [retHighest] Specify returning the highest qualified index. + * @returns {number} Returns the index at which `value` should be inserted + * into `array`. + */ + function baseSortedIndexBy(array, value, iteratee, retHighest) { + value = iteratee(value); + + var low = 0, + high = array == null ? 0 : array.length, + valIsNaN = value !== value, + valIsNull = value === null, + valIsSymbol = isSymbol(value), + valIsUndefined = value === undefined; + + while (low < high) { + var mid = nativeFloor((low + high) / 2), + computed = iteratee(array[mid]), + othIsDefined = computed !== undefined, + othIsNull = computed === null, + othIsReflexive = computed === computed, + othIsSymbol = isSymbol(computed); + + if (valIsNaN) { + var setLow = retHighest || othIsReflexive; + } else if (valIsUndefined) { + setLow = othIsReflexive && (retHighest || othIsDefined); + } else if (valIsNull) { + setLow = othIsReflexive && othIsDefined && (retHighest || !othIsNull); + } else if (valIsSymbol) { + setLow = othIsReflexive && othIsDefined && !othIsNull && (retHighest || !othIsSymbol); + } else if (othIsNull || othIsSymbol) { + setLow = false; + } else { + setLow = retHighest ? (computed <= value) : (computed < value); + } + if (setLow) { + low = mid + 1; + } else { + high = mid; + } + } + return nativeMin(high, MAX_ARRAY_INDEX); + } + + /** + * The base implementation of `_.sortedUniq` and `_.sortedUniqBy` without + * support for iteratee shorthands. + * + * @private + * @param {Array} array The array to inspect. + * @param {Function} [iteratee] The iteratee invoked per element. + * @returns {Array} Returns the new duplicate free array. + */ + function baseSortedUniq(array, iteratee) { + var index = -1, + length = array.length, + resIndex = 0, + result = []; + + while (++index < length) { + var value = array[index], + computed = iteratee ? iteratee(value) : value; + + if (!index || !eq(computed, seen)) { + var seen = computed; + result[resIndex++] = value === 0 ? 0 : value; + } + } + return result; + } + + /** + * The base implementation of `_.toNumber` which doesn't ensure correct + * conversions of binary, hexadecimal, or octal string values. + * + * @private + * @param {*} value The value to process. + * @returns {number} Returns the number. + */ + function baseToNumber(value) { + if (typeof value == 'number') { + return value; + } + if (isSymbol(value)) { + return NAN; + } + return +value; + } + + /** + * The base implementation of `_.toString` which doesn't convert nullish + * values to empty strings. + * + * @private + * @param {*} value The value to process. + * @returns {string} Returns the string. + */ + function baseToString(value) { + // Exit early for strings to avoid a performance hit in some environments. + if (typeof value == 'string') { + return value; + } + if (isArray(value)) { + // Recursively convert values (susceptible to call stack limits). + return arrayMap(value, baseToString) + ''; + } + if (isSymbol(value)) { + return symbolToString ? symbolToString.call(value) : ''; + } + var result = (value + ''); + return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result; + } + + /** + * The base implementation of `_.uniqBy` without support for iteratee shorthands. + * + * @private + * @param {Array} array The array to inspect. + * @param {Function} [iteratee] The iteratee invoked per element. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns the new duplicate free array. + */ + function baseUniq(array, iteratee, comparator) { + var index = -1, + includes = arrayIncludes, + length = array.length, + isCommon = true, + result = [], + seen = result; + + if (comparator) { + isCommon = false; + includes = arrayIncludesWith; + } + else if (length >= LARGE_ARRAY_SIZE) { + var set = iteratee ? null : createSet(array); + if (set) { + return setToArray(set); + } + isCommon = false; + includes = cacheHas; + seen = new SetCache; + } + else { + seen = iteratee ? [] : result; + } + outer: + while (++index < length) { + var value = array[index], + computed = iteratee ? iteratee(value) : value; + + value = (comparator || value !== 0) ? value : 0; + if (isCommon && computed === computed) { + var seenIndex = seen.length; + while (seenIndex--) { + if (seen[seenIndex] === computed) { + continue outer; + } + } + if (iteratee) { + seen.push(computed); + } + result.push(value); + } + else if (!includes(seen, computed, comparator)) { + if (seen !== result) { + seen.push(computed); + } + result.push(value); + } + } + return result; + } + + /** + * The base implementation of `_.unset`. + * + * @private + * @param {Object} object The object to modify. + * @param {Array|string} path The property path to unset. + * @returns {boolean} Returns `true` if the property is deleted, else `false`. + */ + function baseUnset(object, path) { + path = castPath(path, object); + object = parent(object, path); + return object == null || delete object[toKey(last(path))]; + } + + /** + * The base implementation of `_.update`. + * + * @private + * @param {Object} object The object to modify. + * @param {Array|string} path The path of the property to update. + * @param {Function} updater The function to produce the updated value. + * @param {Function} [customizer] The function to customize path creation. + * @returns {Object} Returns `object`. + */ + function baseUpdate(object, path, updater, customizer) { + return baseSet(object, path, updater(baseGet(object, path)), customizer); + } + + /** + * The base implementation of methods like `_.dropWhile` and `_.takeWhile` + * without support for iteratee shorthands. + * + * @private + * @param {Array} array The array to query. + * @param {Function} predicate The function invoked per iteration. + * @param {boolean} [isDrop] Specify dropping elements instead of taking them. + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Array} Returns the slice of `array`. + */ + function baseWhile(array, predicate, isDrop, fromRight) { + var length = array.length, + index = fromRight ? length : -1; + + while ((fromRight ? index-- : ++index < length) && + predicate(array[index], index, array)) {} + + return isDrop + ? baseSlice(array, (fromRight ? 0 : index), (fromRight ? index + 1 : length)) + : baseSlice(array, (fromRight ? index + 1 : 0), (fromRight ? length : index)); + } + + /** + * The base implementation of `wrapperValue` which returns the result of + * performing a sequence of actions on the unwrapped `value`, where each + * successive action is supplied the return value of the previous. + * + * @private + * @param {*} value The unwrapped value. + * @param {Array} actions Actions to perform to resolve the unwrapped value. + * @returns {*} Returns the resolved value. + */ + function baseWrapperValue(value, actions) { + var result = value; + if (result instanceof LazyWrapper) { + result = result.value(); + } + return arrayReduce(actions, function(result, action) { + return action.func.apply(action.thisArg, arrayPush([result], action.args)); + }, result); + } + + /** + * The base implementation of methods like `_.xor`, without support for + * iteratee shorthands, that accepts an array of arrays to inspect. + * + * @private + * @param {Array} arrays The arrays to inspect. + * @param {Function} [iteratee] The iteratee invoked per element. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns the new array of values. + */ + function baseXor(arrays, iteratee, comparator) { + var length = arrays.length; + if (length < 2) { + return length ? baseUniq(arrays[0]) : []; + } + var index = -1, + result = Array(length); + + while (++index < length) { + var array = arrays[index], + othIndex = -1; + + while (++othIndex < length) { + if (othIndex != index) { + result[index] = baseDifference(result[index] || array, arrays[othIndex], iteratee, comparator); + } + } + } + return baseUniq(baseFlatten(result, 1), iteratee, comparator); + } + + /** + * This base implementation of `_.zipObject` which assigns values using `assignFunc`. + * + * @private + * @param {Array} props The property identifiers. + * @param {Array} values The property values. + * @param {Function} assignFunc The function to assign values. + * @returns {Object} Returns the new object. + */ + function baseZipObject(props, values, assignFunc) { + var index = -1, + length = props.length, + valsLength = values.length, + result = {}; + + while (++index < length) { + var value = index < valsLength ? values[index] : undefined; + assignFunc(result, props[index], value); + } + return result; + } + + /** + * Casts `value` to an empty array if it's not an array like object. + * + * @private + * @param {*} value The value to inspect. + * @returns {Array|Object} Returns the cast array-like object. + */ + function castArrayLikeObject(value) { + return isArrayLikeObject(value) ? value : []; + } + + /** + * Casts `value` to `identity` if it's not a function. + * + * @private + * @param {*} value The value to inspect. + * @returns {Function} Returns cast function. + */ + function castFunction(value) { + return typeof value == 'function' ? value : identity; + } + + /** + * Casts `value` to a path array if it's not one. + * + * @private + * @param {*} value The value to inspect. + * @param {Object} [object] The object to query keys on. + * @returns {Array} Returns the cast property path array. + */ + function castPath(value, object) { + if (isArray(value)) { + return value; + } + return isKey(value, object) ? [value] : stringToPath(toString(value)); + } + + /** + * A `baseRest` alias which can be replaced with `identity` by module + * replacement plugins. + * + * @private + * @type {Function} + * @param {Function} func The function to apply a rest parameter to. + * @returns {Function} Returns the new function. + */ + var castRest = baseRest; + + /** + * Casts `array` to a slice if it's needed. + * + * @private + * @param {Array} array The array to inspect. + * @param {number} start The start position. + * @param {number} [end=array.length] The end position. + * @returns {Array} Returns the cast slice. + */ + function castSlice(array, start, end) { + var length = array.length; + end = end === undefined ? length : end; + return (!start && end >= length) ? array : baseSlice(array, start, end); + } + + /** + * A simple wrapper around the global [`clearTimeout`](https://mdn.io/clearTimeout). + * + * @private + * @param {number|Object} id The timer id or timeout object of the timer to clear. + */ + var clearTimeout = ctxClearTimeout || function(id) { + return root.clearTimeout(id); + }; + + /** + * Creates a clone of `buffer`. + * + * @private + * @param {Buffer} buffer The buffer to clone. + * @param {boolean} [isDeep] Specify a deep clone. + * @returns {Buffer} Returns the cloned buffer. + */ + function cloneBuffer(buffer, isDeep) { + if (isDeep) { + return buffer.slice(); + } + var length = buffer.length, + result = allocUnsafe ? allocUnsafe(length) : new buffer.constructor(length); + + buffer.copy(result); + return result; + } + + /** + * Creates a clone of `arrayBuffer`. + * + * @private + * @param {ArrayBuffer} arrayBuffer The array buffer to clone. + * @returns {ArrayBuffer} Returns the cloned array buffer. + */ + function cloneArrayBuffer(arrayBuffer) { + var result = new arrayBuffer.constructor(arrayBuffer.byteLength); + new Uint8Array(result).set(new Uint8Array(arrayBuffer)); + return result; + } + + /** + * Creates a clone of `dataView`. + * + * @private + * @param {Object} dataView The data view to clone. + * @param {boolean} [isDeep] Specify a deep clone. + * @returns {Object} Returns the cloned data view. + */ + function cloneDataView(dataView, isDeep) { + var buffer = isDeep ? cloneArrayBuffer(dataView.buffer) : dataView.buffer; + return new dataView.constructor(buffer, dataView.byteOffset, dataView.byteLength); + } + + /** + * Creates a clone of `map`. + * + * @private + * @param {Object} map The map to clone. + * @param {Function} cloneFunc The function to clone values. + * @param {boolean} [isDeep] Specify a deep clone. + * @returns {Object} Returns the cloned map. + */ + function cloneMap(map, isDeep, cloneFunc) { + var array = isDeep ? cloneFunc(mapToArray(map), CLONE_DEEP_FLAG) : mapToArray(map); + return arrayReduce(array, addMapEntry, new map.constructor); + } + + /** + * Creates a clone of `regexp`. + * + * @private + * @param {Object} regexp The regexp to clone. + * @returns {Object} Returns the cloned regexp. + */ + function cloneRegExp(regexp) { + var result = new regexp.constructor(regexp.source, reFlags.exec(regexp)); + result.lastIndex = regexp.lastIndex; + return result; + } + + /** + * Creates a clone of `set`. + * + * @private + * @param {Object} set The set to clone. + * @param {Function} cloneFunc The function to clone values. + * @param {boolean} [isDeep] Specify a deep clone. + * @returns {Object} Returns the cloned set. + */ + function cloneSet(set, isDeep, cloneFunc) { + var array = isDeep ? cloneFunc(setToArray(set), CLONE_DEEP_FLAG) : setToArray(set); + return arrayReduce(array, addSetEntry, new set.constructor); + } + + /** + * Creates a clone of the `symbol` object. + * + * @private + * @param {Object} symbol The symbol object to clone. + * @returns {Object} Returns the cloned symbol object. + */ + function cloneSymbol(symbol) { + return symbolValueOf ? Object(symbolValueOf.call(symbol)) : {}; + } + + /** + * Creates a clone of `typedArray`. + * + * @private + * @param {Object} typedArray The typed array to clone. + * @param {boolean} [isDeep] Specify a deep clone. + * @returns {Object} Returns the cloned typed array. + */ + function cloneTypedArray(typedArray, isDeep) { + var buffer = isDeep ? cloneArrayBuffer(typedArray.buffer) : typedArray.buffer; + return new typedArray.constructor(buffer, typedArray.byteOffset, typedArray.length); + } + + /** + * Compares values to sort them in ascending order. + * + * @private + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {number} Returns the sort order indicator for `value`. + */ + function compareAscending(value, other) { + if (value !== other) { + var valIsDefined = value !== undefined, + valIsNull = value === null, + valIsReflexive = value === value, + valIsSymbol = isSymbol(value); + + var othIsDefined = other !== undefined, + othIsNull = other === null, + othIsReflexive = other === other, + othIsSymbol = isSymbol(other); + + if ((!othIsNull && !othIsSymbol && !valIsSymbol && value > other) || + (valIsSymbol && othIsDefined && othIsReflexive && !othIsNull && !othIsSymbol) || + (valIsNull && othIsDefined && othIsReflexive) || + (!valIsDefined && othIsReflexive) || + !valIsReflexive) { + return 1; + } + if ((!valIsNull && !valIsSymbol && !othIsSymbol && value < other) || + (othIsSymbol && valIsDefined && valIsReflexive && !valIsNull && !valIsSymbol) || + (othIsNull && valIsDefined && valIsReflexive) || + (!othIsDefined && valIsReflexive) || + !othIsReflexive) { + return -1; + } + } + return 0; + } + + /** + * Used by `_.orderBy` to compare multiple properties of a value to another + * and stable sort them. + * + * If `orders` is unspecified, all values are sorted in ascending order. Otherwise, + * specify an order of "desc" for descending or "asc" for ascending sort order + * of corresponding values. + * + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @param {boolean[]|string[]} orders The order to sort by for each property. + * @returns {number} Returns the sort order indicator for `object`. + */ + function compareMultiple(object, other, orders) { + var index = -1, + objCriteria = object.criteria, + othCriteria = other.criteria, + length = objCriteria.length, + ordersLength = orders.length; + + while (++index < length) { + var result = compareAscending(objCriteria[index], othCriteria[index]); + if (result) { + if (index >= ordersLength) { + return result; + } + var order = orders[index]; + return result * (order == 'desc' ? -1 : 1); + } + } + // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications + // that causes it, under certain circumstances, to provide the same value for + // `object` and `other`. See https://github.com/jashkenas/underscore/pull/1247 + // for more details. + // + // This also ensures a stable sort in V8 and other engines. + // See https://bugs.chromium.org/p/v8/issues/detail?id=90 for more details. + return object.index - other.index; + } + + /** + * Creates an array that is the composition of partially applied arguments, + * placeholders, and provided arguments into a single array of arguments. + * + * @private + * @param {Array} args The provided arguments. + * @param {Array} partials The arguments to prepend to those provided. + * @param {Array} holders The `partials` placeholder indexes. + * @params {boolean} [isCurried] Specify composing for a curried function. + * @returns {Array} Returns the new array of composed arguments. + */ + function composeArgs(args, partials, holders, isCurried) { + var argsIndex = -1, + argsLength = args.length, + holdersLength = holders.length, + leftIndex = -1, + leftLength = partials.length, + rangeLength = nativeMax(argsLength - holdersLength, 0), + result = Array(leftLength + rangeLength), + isUncurried = !isCurried; + + while (++leftIndex < leftLength) { + result[leftIndex] = partials[leftIndex]; + } + while (++argsIndex < holdersLength) { + if (isUncurried || argsIndex < argsLength) { + result[holders[argsIndex]] = args[argsIndex]; + } + } + while (rangeLength--) { + result[leftIndex++] = args[argsIndex++]; + } + return result; + } + + /** + * This function is like `composeArgs` except that the arguments composition + * is tailored for `_.partialRight`. + * + * @private + * @param {Array} args The provided arguments. + * @param {Array} partials The arguments to append to those provided. + * @param {Array} holders The `partials` placeholder indexes. + * @params {boolean} [isCurried] Specify composing for a curried function. + * @returns {Array} Returns the new array of composed arguments. + */ + function composeArgsRight(args, partials, holders, isCurried) { + var argsIndex = -1, + argsLength = args.length, + holdersIndex = -1, + holdersLength = holders.length, + rightIndex = -1, + rightLength = partials.length, + rangeLength = nativeMax(argsLength - holdersLength, 0), + result = Array(rangeLength + rightLength), + isUncurried = !isCurried; + + while (++argsIndex < rangeLength) { + result[argsIndex] = args[argsIndex]; + } + var offset = argsIndex; + while (++rightIndex < rightLength) { + result[offset + rightIndex] = partials[rightIndex]; + } + while (++holdersIndex < holdersLength) { + if (isUncurried || argsIndex < argsLength) { + result[offset + holders[holdersIndex]] = args[argsIndex++]; + } + } + return result; + } + + /** + * Copies the values of `source` to `array`. + * + * @private + * @param {Array} source The array to copy values from. + * @param {Array} [array=[]] The array to copy values to. + * @returns {Array} Returns `array`. + */ + function copyArray(source, array) { + var index = -1, + length = source.length; + + array || (array = Array(length)); + while (++index < length) { + array[index] = source[index]; + } + return array; + } + + /** + * Copies properties of `source` to `object`. + * + * @private + * @param {Object} source The object to copy properties from. + * @param {Array} props The property identifiers to copy. + * @param {Object} [object={}] The object to copy properties to. + * @param {Function} [customizer] The function to customize copied values. + * @returns {Object} Returns `object`. + */ + function copyObject(source, props, object, customizer) { + var isNew = !object; + object || (object = {}); + + var index = -1, + length = props.length; + + while (++index < length) { + var key = props[index]; + + var newValue = customizer + ? customizer(object[key], source[key], key, object, source) + : undefined; + + if (newValue === undefined) { + newValue = source[key]; + } + if (isNew) { + baseAssignValue(object, key, newValue); + } else { + assignValue(object, key, newValue); + } + } + return object; + } + + /** + * Copies own symbols of `source` to `object`. + * + * @private + * @param {Object} source The object to copy symbols from. + * @param {Object} [object={}] The object to copy symbols to. + * @returns {Object} Returns `object`. + */ + function copySymbols(source, object) { + return copyObject(source, getSymbols(source), object); + } + + /** + * Copies own and inherited symbols of `source` to `object`. + * + * @private + * @param {Object} source The object to copy symbols from. + * @param {Object} [object={}] The object to copy symbols to. + * @returns {Object} Returns `object`. + */ + function copySymbolsIn(source, object) { + return copyObject(source, getSymbolsIn(source), object); + } + + /** + * Creates a function like `_.groupBy`. + * + * @private + * @param {Function} setter The function to set accumulator values. + * @param {Function} [initializer] The accumulator object initializer. + * @returns {Function} Returns the new aggregator function. + */ + function createAggregator(setter, initializer) { + return function(collection, iteratee) { + var func = isArray(collection) ? arrayAggregator : baseAggregator, + accumulator = initializer ? initializer() : {}; + + return func(collection, setter, getIteratee(iteratee, 2), accumulator); + }; + } + + /** + * Creates a function like `_.assign`. + * + * @private + * @param {Function} assigner The function to assign values. + * @returns {Function} Returns the new assigner function. + */ + function createAssigner(assigner) { + return baseRest(function(object, sources) { + var index = -1, + length = sources.length, + customizer = length > 1 ? sources[length - 1] : undefined, + guard = length > 2 ? sources[2] : undefined; + + customizer = (assigner.length > 3 && typeof customizer == 'function') + ? (length--, customizer) + : undefined; + + if (guard && isIterateeCall(sources[0], sources[1], guard)) { + customizer = length < 3 ? undefined : customizer; + length = 1; + } + object = Object(object); + while (++index < length) { + var source = sources[index]; + if (source) { + assigner(object, source, index, customizer); + } + } + return object; + }); + } + + /** + * Creates a `baseEach` or `baseEachRight` function. + * + * @private + * @param {Function} eachFunc The function to iterate over a collection. + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Function} Returns the new base function. + */ + function createBaseEach(eachFunc, fromRight) { + return function(collection, iteratee) { + if (collection == null) { + return collection; + } + if (!isArrayLike(collection)) { + return eachFunc(collection, iteratee); + } + var length = collection.length, + index = fromRight ? length : -1, + iterable = Object(collection); + + while ((fromRight ? index-- : ++index < length)) { + if (iteratee(iterable[index], index, iterable) === false) { + break; + } + } + return collection; + }; + } + + /** + * Creates a base function for methods like `_.forIn` and `_.forOwn`. + * + * @private + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Function} Returns the new base function. + */ + function createBaseFor(fromRight) { + return function(object, iteratee, keysFunc) { + var index = -1, + iterable = Object(object), + props = keysFunc(object), + length = props.length; + + while (length--) { + var key = props[fromRight ? length : ++index]; + if (iteratee(iterable[key], key, iterable) === false) { + break; + } + } + return object; + }; + } + + /** + * Creates a function that wraps `func` to invoke it with the optional `this` + * binding of `thisArg`. + * + * @private + * @param {Function} func The function to wrap. + * @param {number} bitmask The bitmask flags. See `createWrap` for more details. + * @param {*} [thisArg] The `this` binding of `func`. + * @returns {Function} Returns the new wrapped function. + */ + function createBind(func, bitmask, thisArg) { + var isBind = bitmask & WRAP_BIND_FLAG, + Ctor = createCtor(func); + + function wrapper() { + var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func; + return fn.apply(isBind ? thisArg : this, arguments); + } + return wrapper; + } + + /** + * Creates a function like `_.lowerFirst`. + * + * @private + * @param {string} methodName The name of the `String` case method to use. + * @returns {Function} Returns the new case function. + */ + function createCaseFirst(methodName) { + return function(string) { + string = toString(string); + + var strSymbols = hasUnicode(string) + ? stringToArray(string) + : undefined; + + var chr = strSymbols + ? strSymbols[0] + : string.charAt(0); + + var trailing = strSymbols + ? castSlice(strSymbols, 1).join('') + : string.slice(1); + + return chr[methodName]() + trailing; + }; + } + + /** + * Creates a function like `_.camelCase`. + * + * @private + * @param {Function} callback The function to combine each word. + * @returns {Function} Returns the new compounder function. + */ + function createCompounder(callback) { + return function(string) { + return arrayReduce(words(deburr(string).replace(reApos, '')), callback, ''); + }; + } + + /** + * Creates a function that produces an instance of `Ctor` regardless of + * whether it was invoked as part of a `new` expression or by `call` or `apply`. + * + * @private + * @param {Function} Ctor The constructor to wrap. + * @returns {Function} Returns the new wrapped function. + */ + function createCtor(Ctor) { + return function() { + // Use a `switch` statement to work with class constructors. See + // http://ecma-international.org/ecma-262/7.0/#sec-ecmascript-function-objects-call-thisargument-argumentslist + // for more details. + var args = arguments; + switch (args.length) { + case 0: return new Ctor; + case 1: return new Ctor(args[0]); + case 2: return new Ctor(args[0], args[1]); + case 3: return new Ctor(args[0], args[1], args[2]); + case 4: return new Ctor(args[0], args[1], args[2], args[3]); + case 5: return new Ctor(args[0], args[1], args[2], args[3], args[4]); + case 6: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5]); + case 7: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5], args[6]); + } + var thisBinding = baseCreate(Ctor.prototype), + result = Ctor.apply(thisBinding, args); + + // Mimic the constructor's `return` behavior. + // See https://es5.github.io/#x13.2.2 for more details. + return isObject(result) ? result : thisBinding; + }; + } + + /** + * Creates a function that wraps `func` to enable currying. + * + * @private + * @param {Function} func The function to wrap. + * @param {number} bitmask The bitmask flags. See `createWrap` for more details. + * @param {number} arity The arity of `func`. + * @returns {Function} Returns the new wrapped function. + */ + function createCurry(func, bitmask, arity) { + var Ctor = createCtor(func); + + function wrapper() { + var length = arguments.length, + args = Array(length), + index = length, + placeholder = getHolder(wrapper); + + while (index--) { + args[index] = arguments[index]; + } + var holders = (length < 3 && args[0] !== placeholder && args[length - 1] !== placeholder) + ? [] + : replaceHolders(args, placeholder); + + length -= holders.length; + if (length < arity) { + return createRecurry( + func, bitmask, createHybrid, wrapper.placeholder, undefined, + args, holders, undefined, undefined, arity - length); + } + var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func; + return apply(fn, this, args); + } + return wrapper; + } + + /** + * Creates a `_.find` or `_.findLast` function. + * + * @private + * @param {Function} findIndexFunc The function to find the collection index. + * @returns {Function} Returns the new find function. + */ + function createFind(findIndexFunc) { + return function(collection, predicate, fromIndex) { + var iterable = Object(collection); + if (!isArrayLike(collection)) { + var iteratee = getIteratee(predicate, 3); + collection = keys(collection); + predicate = function(key) { return iteratee(iterable[key], key, iterable); }; + } + var index = findIndexFunc(collection, predicate, fromIndex); + return index > -1 ? iterable[iteratee ? collection[index] : index] : undefined; + }; + } + + /** + * Creates a `_.flow` or `_.flowRight` function. + * + * @private + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Function} Returns the new flow function. + */ + function createFlow(fromRight) { + return flatRest(function(funcs) { + var length = funcs.length, + index = length, + prereq = LodashWrapper.prototype.thru; + + if (fromRight) { + funcs.reverse(); + } + while (index--) { + var func = funcs[index]; + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + if (prereq && !wrapper && getFuncName(func) == 'wrapper') { + var wrapper = new LodashWrapper([], true); + } + } + index = wrapper ? index : length; + while (++index < length) { + func = funcs[index]; + + var funcName = getFuncName(func), + data = funcName == 'wrapper' ? getData(func) : undefined; + + if (data && isLaziable(data[0]) && + data[1] == (WRAP_ARY_FLAG | WRAP_CURRY_FLAG | WRAP_PARTIAL_FLAG | WRAP_REARG_FLAG) && + !data[4].length && data[9] == 1 + ) { + wrapper = wrapper[getFuncName(data[0])].apply(wrapper, data[3]); + } else { + wrapper = (func.length == 1 && isLaziable(func)) + ? wrapper[funcName]() + : wrapper.thru(func); + } + } + return function() { + var args = arguments, + value = args[0]; + + if (wrapper && args.length == 1 && isArray(value)) { + return wrapper.plant(value).value(); + } + var index = 0, + result = length ? funcs[index].apply(this, args) : value; + + while (++index < length) { + result = funcs[index].call(this, result); + } + return result; + }; + }); + } + + /** + * Creates a function that wraps `func` to invoke it with optional `this` + * binding of `thisArg`, partial application, and currying. + * + * @private + * @param {Function|string} func The function or method name to wrap. + * @param {number} bitmask The bitmask flags. See `createWrap` for more details. + * @param {*} [thisArg] The `this` binding of `func`. + * @param {Array} [partials] The arguments to prepend to those provided to + * the new function. + * @param {Array} [holders] The `partials` placeholder indexes. + * @param {Array} [partialsRight] The arguments to append to those provided + * to the new function. + * @param {Array} [holdersRight] The `partialsRight` placeholder indexes. + * @param {Array} [argPos] The argument positions of the new function. + * @param {number} [ary] The arity cap of `func`. + * @param {number} [arity] The arity of `func`. + * @returns {Function} Returns the new wrapped function. + */ + function createHybrid(func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity) { + var isAry = bitmask & WRAP_ARY_FLAG, + isBind = bitmask & WRAP_BIND_FLAG, + isBindKey = bitmask & WRAP_BIND_KEY_FLAG, + isCurried = bitmask & (WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG), + isFlip = bitmask & WRAP_FLIP_FLAG, + Ctor = isBindKey ? undefined : createCtor(func); + + function wrapper() { + var length = arguments.length, + args = Array(length), + index = length; + + while (index--) { + args[index] = arguments[index]; + } + if (isCurried) { + var placeholder = getHolder(wrapper), + holdersCount = countHolders(args, placeholder); + } + if (partials) { + args = composeArgs(args, partials, holders, isCurried); + } + if (partialsRight) { + args = composeArgsRight(args, partialsRight, holdersRight, isCurried); + } + length -= holdersCount; + if (isCurried && length < arity) { + var newHolders = replaceHolders(args, placeholder); + return createRecurry( + func, bitmask, createHybrid, wrapper.placeholder, thisArg, + args, newHolders, argPos, ary, arity - length + ); + } + var thisBinding = isBind ? thisArg : this, + fn = isBindKey ? thisBinding[func] : func; + + length = args.length; + if (argPos) { + args = reorder(args, argPos); + } else if (isFlip && length > 1) { + args.reverse(); + } + if (isAry && ary < length) { + args.length = ary; + } + if (this && this !== root && this instanceof wrapper) { + fn = Ctor || createCtor(fn); + } + return fn.apply(thisBinding, args); + } + return wrapper; + } + + /** + * Creates a function like `_.invertBy`. + * + * @private + * @param {Function} setter The function to set accumulator values. + * @param {Function} toIteratee The function to resolve iteratees. + * @returns {Function} Returns the new inverter function. + */ + function createInverter(setter, toIteratee) { + return function(object, iteratee) { + return baseInverter(object, setter, toIteratee(iteratee), {}); + }; + } + + /** + * Creates a function that performs a mathematical operation on two values. + * + * @private + * @param {Function} operator The function to perform the operation. + * @param {number} [defaultValue] The value used for `undefined` arguments. + * @returns {Function} Returns the new mathematical operation function. + */ + function createMathOperation(operator, defaultValue) { + return function(value, other) { + var result; + if (value === undefined && other === undefined) { + return defaultValue; + } + if (value !== undefined) { + result = value; + } + if (other !== undefined) { + if (result === undefined) { + return other; + } + if (typeof value == 'string' || typeof other == 'string') { + value = baseToString(value); + other = baseToString(other); + } else { + value = baseToNumber(value); + other = baseToNumber(other); + } + result = operator(value, other); + } + return result; + }; + } + + /** + * Creates a function like `_.over`. + * + * @private + * @param {Function} arrayFunc The function to iterate over iteratees. + * @returns {Function} Returns the new over function. + */ + function createOver(arrayFunc) { + return flatRest(function(iteratees) { + iteratees = arrayMap(iteratees, baseUnary(getIteratee())); + return baseRest(function(args) { + var thisArg = this; + return arrayFunc(iteratees, function(iteratee) { + return apply(iteratee, thisArg, args); + }); + }); + }); + } + + /** + * Creates the padding for `string` based on `length`. The `chars` string + * is truncated if the number of characters exceeds `length`. + * + * @private + * @param {number} length The padding length. + * @param {string} [chars=' '] The string used as padding. + * @returns {string} Returns the padding for `string`. + */ + function createPadding(length, chars) { + chars = chars === undefined ? ' ' : baseToString(chars); + + var charsLength = chars.length; + if (charsLength < 2) { + return charsLength ? baseRepeat(chars, length) : chars; + } + var result = baseRepeat(chars, nativeCeil(length / stringSize(chars))); + return hasUnicode(chars) + ? castSlice(stringToArray(result), 0, length).join('') + : result.slice(0, length); + } + + /** + * Creates a function that wraps `func` to invoke it with the `this` binding + * of `thisArg` and `partials` prepended to the arguments it receives. + * + * @private + * @param {Function} func The function to wrap. + * @param {number} bitmask The bitmask flags. See `createWrap` for more details. + * @param {*} thisArg The `this` binding of `func`. + * @param {Array} partials The arguments to prepend to those provided to + * the new function. + * @returns {Function} Returns the new wrapped function. + */ + function createPartial(func, bitmask, thisArg, partials) { + var isBind = bitmask & WRAP_BIND_FLAG, + Ctor = createCtor(func); + + function wrapper() { + var argsIndex = -1, + argsLength = arguments.length, + leftIndex = -1, + leftLength = partials.length, + args = Array(leftLength + argsLength), + fn = (this && this !== root && this instanceof wrapper) ? Ctor : func; + + while (++leftIndex < leftLength) { + args[leftIndex] = partials[leftIndex]; + } + while (argsLength--) { + args[leftIndex++] = arguments[++argsIndex]; + } + return apply(fn, isBind ? thisArg : this, args); + } + return wrapper; + } + + /** + * Creates a `_.range` or `_.rangeRight` function. + * + * @private + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Function} Returns the new range function. + */ + function createRange(fromRight) { + return function(start, end, step) { + if (step && typeof step != 'number' && isIterateeCall(start, end, step)) { + end = step = undefined; + } + // Ensure the sign of `-0` is preserved. + start = toFinite(start); + if (end === undefined) { + end = start; + start = 0; + } else { + end = toFinite(end); + } + step = step === undefined ? (start < end ? 1 : -1) : toFinite(step); + return baseRange(start, end, step, fromRight); + }; + } + + /** + * Creates a function that performs a relational operation on two values. + * + * @private + * @param {Function} operator The function to perform the operation. + * @returns {Function} Returns the new relational operation function. + */ + function createRelationalOperation(operator) { + return function(value, other) { + if (!(typeof value == 'string' && typeof other == 'string')) { + value = toNumber(value); + other = toNumber(other); + } + return operator(value, other); + }; + } + + /** + * Creates a function that wraps `func` to continue currying. + * + * @private + * @param {Function} func The function to wrap. + * @param {number} bitmask The bitmask flags. See `createWrap` for more details. + * @param {Function} wrapFunc The function to create the `func` wrapper. + * @param {*} placeholder The placeholder value. + * @param {*} [thisArg] The `this` binding of `func`. + * @param {Array} [partials] The arguments to prepend to those provided to + * the new function. + * @param {Array} [holders] The `partials` placeholder indexes. + * @param {Array} [argPos] The argument positions of the new function. + * @param {number} [ary] The arity cap of `func`. + * @param {number} [arity] The arity of `func`. + * @returns {Function} Returns the new wrapped function. + */ + function createRecurry(func, bitmask, wrapFunc, placeholder, thisArg, partials, holders, argPos, ary, arity) { + var isCurry = bitmask & WRAP_CURRY_FLAG, + newHolders = isCurry ? holders : undefined, + newHoldersRight = isCurry ? undefined : holders, + newPartials = isCurry ? partials : undefined, + newPartialsRight = isCurry ? undefined : partials; + + bitmask |= (isCurry ? WRAP_PARTIAL_FLAG : WRAP_PARTIAL_RIGHT_FLAG); + bitmask &= ~(isCurry ? WRAP_PARTIAL_RIGHT_FLAG : WRAP_PARTIAL_FLAG); + + if (!(bitmask & WRAP_CURRY_BOUND_FLAG)) { + bitmask &= ~(WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG); + } + var newData = [ + func, bitmask, thisArg, newPartials, newHolders, newPartialsRight, + newHoldersRight, argPos, ary, arity + ]; + + var result = wrapFunc.apply(undefined, newData); + if (isLaziable(func)) { + setData(result, newData); + } + result.placeholder = placeholder; + return setWrapToString(result, func, bitmask); + } + + /** + * Creates a function like `_.round`. + * + * @private + * @param {string} methodName The name of the `Math` method to use when rounding. + * @returns {Function} Returns the new round function. + */ + function createRound(methodName) { + var func = Math[methodName]; + return function(number, precision) { + number = toNumber(number); + precision = precision == null ? 0 : nativeMin(toInteger(precision), 292); + if (precision) { + // Shift with exponential notation to avoid floating-point issues. + // See [MDN](https://mdn.io/round#Examples) for more details. + var pair = (toString(number) + 'e').split('e'), + value = func(pair[0] + 'e' + (+pair[1] + precision)); + + pair = (toString(value) + 'e').split('e'); + return +(pair[0] + 'e' + (+pair[1] - precision)); + } + return func(number); + }; + } + + /** + * Creates a set object of `values`. + * + * @private + * @param {Array} values The values to add to the set. + * @returns {Object} Returns the new set. + */ + var createSet = !(Set && (1 / setToArray(new Set([,-0]))[1]) == INFINITY) ? noop : function(values) { + return new Set(values); + }; + + /** + * Creates a `_.toPairs` or `_.toPairsIn` function. + * + * @private + * @param {Function} keysFunc The function to get the keys of a given object. + * @returns {Function} Returns the new pairs function. + */ + function createToPairs(keysFunc) { + return function(object) { + var tag = getTag(object); + if (tag == mapTag) { + return mapToArray(object); + } + if (tag == setTag) { + return setToPairs(object); + } + return baseToPairs(object, keysFunc(object)); + }; + } + + /** + * Creates a function that either curries or invokes `func` with optional + * `this` binding and partially applied arguments. + * + * @private + * @param {Function|string} func The function or method name to wrap. + * @param {number} bitmask The bitmask flags. + * 1 - `_.bind` + * 2 - `_.bindKey` + * 4 - `_.curry` or `_.curryRight` of a bound function + * 8 - `_.curry` + * 16 - `_.curryRight` + * 32 - `_.partial` + * 64 - `_.partialRight` + * 128 - `_.rearg` + * 256 - `_.ary` + * 512 - `_.flip` + * @param {*} [thisArg] The `this` binding of `func`. + * @param {Array} [partials] The arguments to be partially applied. + * @param {Array} [holders] The `partials` placeholder indexes. + * @param {Array} [argPos] The argument positions of the new function. + * @param {number} [ary] The arity cap of `func`. + * @param {number} [arity] The arity of `func`. + * @returns {Function} Returns the new wrapped function. + */ + function createWrap(func, bitmask, thisArg, partials, holders, argPos, ary, arity) { + var isBindKey = bitmask & WRAP_BIND_KEY_FLAG; + if (!isBindKey && typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + var length = partials ? partials.length : 0; + if (!length) { + bitmask &= ~(WRAP_PARTIAL_FLAG | WRAP_PARTIAL_RIGHT_FLAG); + partials = holders = undefined; + } + ary = ary === undefined ? ary : nativeMax(toInteger(ary), 0); + arity = arity === undefined ? arity : toInteger(arity); + length -= holders ? holders.length : 0; + + if (bitmask & WRAP_PARTIAL_RIGHT_FLAG) { + var partialsRight = partials, + holdersRight = holders; + + partials = holders = undefined; + } + var data = isBindKey ? undefined : getData(func); + + var newData = [ + func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, + argPos, ary, arity + ]; + + if (data) { + mergeData(newData, data); + } + func = newData[0]; + bitmask = newData[1]; + thisArg = newData[2]; + partials = newData[3]; + holders = newData[4]; + arity = newData[9] = newData[9] === undefined + ? (isBindKey ? 0 : func.length) + : nativeMax(newData[9] - length, 0); + + if (!arity && bitmask & (WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG)) { + bitmask &= ~(WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG); + } + if (!bitmask || bitmask == WRAP_BIND_FLAG) { + var result = createBind(func, bitmask, thisArg); + } else if (bitmask == WRAP_CURRY_FLAG || bitmask == WRAP_CURRY_RIGHT_FLAG) { + result = createCurry(func, bitmask, arity); + } else if ((bitmask == WRAP_PARTIAL_FLAG || bitmask == (WRAP_BIND_FLAG | WRAP_PARTIAL_FLAG)) && !holders.length) { + result = createPartial(func, bitmask, thisArg, partials); + } else { + result = createHybrid.apply(undefined, newData); + } + var setter = data ? baseSetData : setData; + return setWrapToString(setter(result, newData), func, bitmask); + } + + /** + * Used by `_.defaults` to customize its `_.assignIn` use to assign properties + * of source objects to the destination object for all destination properties + * that resolve to `undefined`. + * + * @private + * @param {*} objValue The destination value. + * @param {*} srcValue The source value. + * @param {string} key The key of the property to assign. + * @param {Object} object The parent object of `objValue`. + * @returns {*} Returns the value to assign. + */ + function customDefaultsAssignIn(objValue, srcValue, key, object) { + if (objValue === undefined || + (eq(objValue, objectProto[key]) && !hasOwnProperty.call(object, key))) { + return srcValue; + } + return objValue; + } + + /** + * Used by `_.defaultsDeep` to customize its `_.merge` use to merge source + * objects into destination objects that are passed thru. + * + * @private + * @param {*} objValue The destination value. + * @param {*} srcValue The source value. + * @param {string} key The key of the property to merge. + * @param {Object} object The parent object of `objValue`. + * @param {Object} source The parent object of `srcValue`. + * @param {Object} [stack] Tracks traversed source values and their merged + * counterparts. + * @returns {*} Returns the value to assign. + */ + function customDefaultsMerge(objValue, srcValue, key, object, source, stack) { + if (isObject(objValue) && isObject(srcValue)) { + // Recursively merge objects and arrays (susceptible to call stack limits). + stack.set(srcValue, objValue); + baseMerge(objValue, srcValue, undefined, customDefaultsMerge, stack); + stack['delete'](srcValue); + } + return objValue; + } + + /** + * Used by `_.omit` to customize its `_.cloneDeep` use to only clone plain + * objects. + * + * @private + * @param {*} value The value to inspect. + * @param {string} key The key of the property to inspect. + * @returns {*} Returns the uncloned value or `undefined` to defer cloning to `_.cloneDeep`. + */ + function customOmitClone(value) { + return isPlainObject(value) ? undefined : value; + } + + /** + * A specialized version of `baseIsEqualDeep` for arrays with support for + * partial deep comparisons. + * + * @private + * @param {Array} array The array to compare. + * @param {Array} other The other array to compare. + * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. + * @param {Function} customizer The function to customize comparisons. + * @param {Function} equalFunc The function to determine equivalents of values. + * @param {Object} stack Tracks traversed `array` and `other` objects. + * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`. + */ + function equalArrays(array, other, bitmask, customizer, equalFunc, stack) { + var isPartial = bitmask & COMPARE_PARTIAL_FLAG, + arrLength = array.length, + othLength = other.length; + + if (arrLength != othLength && !(isPartial && othLength > arrLength)) { + return false; + } + // Assume cyclic values are equal. + var stacked = stack.get(array); + if (stacked && stack.get(other)) { + return stacked == other; + } + var index = -1, + result = true, + seen = (bitmask & COMPARE_UNORDERED_FLAG) ? new SetCache : undefined; + + stack.set(array, other); + stack.set(other, array); + + // Ignore non-index properties. + while (++index < arrLength) { + var arrValue = array[index], + othValue = other[index]; + + if (customizer) { + var compared = isPartial + ? customizer(othValue, arrValue, index, other, array, stack) + : customizer(arrValue, othValue, index, array, other, stack); + } + if (compared !== undefined) { + if (compared) { + continue; + } + result = false; + break; + } + // Recursively compare arrays (susceptible to call stack limits). + if (seen) { + if (!arraySome(other, function(othValue, othIndex) { + if (!cacheHas(seen, othIndex) && + (arrValue === othValue || equalFunc(arrValue, othValue, bitmask, customizer, stack))) { + return seen.push(othIndex); + } + })) { + result = false; + break; + } + } else if (!( + arrValue === othValue || + equalFunc(arrValue, othValue, bitmask, customizer, stack) + )) { + result = false; + break; + } + } + stack['delete'](array); + stack['delete'](other); + return result; + } + + /** + * A specialized version of `baseIsEqualDeep` for comparing objects of + * the same `toStringTag`. + * + * **Note:** This function only supports comparing values with tags of + * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`. + * + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @param {string} tag The `toStringTag` of the objects to compare. + * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. + * @param {Function} customizer The function to customize comparisons. + * @param {Function} equalFunc The function to determine equivalents of values. + * @param {Object} stack Tracks traversed `object` and `other` objects. + * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. + */ + function equalByTag(object, other, tag, bitmask, customizer, equalFunc, stack) { + switch (tag) { + case dataViewTag: + if ((object.byteLength != other.byteLength) || + (object.byteOffset != other.byteOffset)) { + return false; + } + object = object.buffer; + other = other.buffer; + + case arrayBufferTag: + if ((object.byteLength != other.byteLength) || + !equalFunc(new Uint8Array(object), new Uint8Array(other))) { + return false; + } + return true; + + case boolTag: + case dateTag: + case numberTag: + // Coerce booleans to `1` or `0` and dates to milliseconds. + // Invalid dates are coerced to `NaN`. + return eq(+object, +other); + + case errorTag: + return object.name == other.name && object.message == other.message; + + case regexpTag: + case stringTag: + // Coerce regexes to strings and treat strings, primitives and objects, + // as equal. See http://www.ecma-international.org/ecma-262/7.0/#sec-regexp.prototype.tostring + // for more details. + return object == (other + ''); + + case mapTag: + var convert = mapToArray; + + case setTag: + var isPartial = bitmask & COMPARE_PARTIAL_FLAG; + convert || (convert = setToArray); + + if (object.size != other.size && !isPartial) { + return false; + } + // Assume cyclic values are equal. + var stacked = stack.get(object); + if (stacked) { + return stacked == other; + } + bitmask |= COMPARE_UNORDERED_FLAG; + + // Recursively compare objects (susceptible to call stack limits). + stack.set(object, other); + var result = equalArrays(convert(object), convert(other), bitmask, customizer, equalFunc, stack); + stack['delete'](object); + return result; + + case symbolTag: + if (symbolValueOf) { + return symbolValueOf.call(object) == symbolValueOf.call(other); + } + } + return false; + } + + /** + * A specialized version of `baseIsEqualDeep` for objects with support for + * partial deep comparisons. + * + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. + * @param {Function} customizer The function to customize comparisons. + * @param {Function} equalFunc The function to determine equivalents of values. + * @param {Object} stack Tracks traversed `object` and `other` objects. + * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. + */ + function equalObjects(object, other, bitmask, customizer, equalFunc, stack) { + var isPartial = bitmask & COMPARE_PARTIAL_FLAG, + objProps = getAllKeys(object), + objLength = objProps.length, + othProps = getAllKeys(other), + othLength = othProps.length; + + if (objLength != othLength && !isPartial) { + return false; + } + var index = objLength; + while (index--) { + var key = objProps[index]; + if (!(isPartial ? key in other : hasOwnProperty.call(other, key))) { + return false; + } + } + // Assume cyclic values are equal. + var stacked = stack.get(object); + if (stacked && stack.get(other)) { + return stacked == other; + } + var result = true; + stack.set(object, other); + stack.set(other, object); + + var skipCtor = isPartial; + while (++index < objLength) { + key = objProps[index]; + var objValue = object[key], + othValue = other[key]; + + if (customizer) { + var compared = isPartial + ? customizer(othValue, objValue, key, other, object, stack) + : customizer(objValue, othValue, key, object, other, stack); + } + // Recursively compare objects (susceptible to call stack limits). + if (!(compared === undefined + ? (objValue === othValue || equalFunc(objValue, othValue, bitmask, customizer, stack)) + : compared + )) { + result = false; + break; + } + skipCtor || (skipCtor = key == 'constructor'); + } + if (result && !skipCtor) { + var objCtor = object.constructor, + othCtor = other.constructor; + + // Non `Object` object instances with different constructors are not equal. + if (objCtor != othCtor && + ('constructor' in object && 'constructor' in other) && + !(typeof objCtor == 'function' && objCtor instanceof objCtor && + typeof othCtor == 'function' && othCtor instanceof othCtor)) { + result = false; + } + } + stack['delete'](object); + stack['delete'](other); + return result; + } + + /** + * A specialized version of `baseRest` which flattens the rest array. + * + * @private + * @param {Function} func The function to apply a rest parameter to. + * @returns {Function} Returns the new function. + */ + function flatRest(func) { + return setToString(overRest(func, undefined, flatten), func + ''); + } + + /** + * Creates an array of own enumerable property names and symbols of `object`. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names and symbols. + */ + function getAllKeys(object) { + return baseGetAllKeys(object, keys, getSymbols); + } + + /** + * Creates an array of own and inherited enumerable property names and + * symbols of `object`. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names and symbols. + */ + function getAllKeysIn(object) { + return baseGetAllKeys(object, keysIn, getSymbolsIn); + } + + /** + * Gets metadata for `func`. + * + * @private + * @param {Function} func The function to query. + * @returns {*} Returns the metadata for `func`. + */ + var getData = !metaMap ? noop : function(func) { + return metaMap.get(func); + }; + + /** + * Gets the name of `func`. + * + * @private + * @param {Function} func The function to query. + * @returns {string} Returns the function name. + */ + function getFuncName(func) { + var result = (func.name + ''), + array = realNames[result], + length = hasOwnProperty.call(realNames, result) ? array.length : 0; + + while (length--) { + var data = array[length], + otherFunc = data.func; + if (otherFunc == null || otherFunc == func) { + return data.name; + } + } + return result; + } + + /** + * Gets the argument placeholder value for `func`. + * + * @private + * @param {Function} func The function to inspect. + * @returns {*} Returns the placeholder value. + */ + function getHolder(func) { + var object = hasOwnProperty.call(lodash, 'placeholder') ? lodash : func; + return object.placeholder; + } + + /** + * Gets the appropriate "iteratee" function. If `_.iteratee` is customized, + * this function returns the custom method, otherwise it returns `baseIteratee`. + * If arguments are provided, the chosen function is invoked with them and + * its result is returned. + * + * @private + * @param {*} [value] The value to convert to an iteratee. + * @param {number} [arity] The arity of the created iteratee. + * @returns {Function} Returns the chosen function or its result. + */ + function getIteratee() { + var result = lodash.iteratee || iteratee; + result = result === iteratee ? baseIteratee : result; + return arguments.length ? result(arguments[0], arguments[1]) : result; + } + + /** + * Gets the data for `map`. + * + * @private + * @param {Object} map The map to query. + * @param {string} key The reference key. + * @returns {*} Returns the map data. + */ + function getMapData(map, key) { + var data = map.__data__; + return isKeyable(key) + ? data[typeof key == 'string' ? 'string' : 'hash'] + : data.map; + } + + /** + * Gets the property names, values, and compare flags of `object`. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the match data of `object`. + */ + function getMatchData(object) { + var result = keys(object), + length = result.length; + + while (length--) { + var key = result[length], + value = object[key]; + + result[length] = [key, value, isStrictComparable(value)]; + } + return result; + } + + /** + * Gets the native function at `key` of `object`. + * + * @private + * @param {Object} object The object to query. + * @param {string} key The key of the method to get. + * @returns {*} Returns the function if it's native, else `undefined`. + */ + function getNative(object, key) { + var value = getValue(object, key); + return baseIsNative(value) ? value : undefined; + } + + /** + * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values. + * + * @private + * @param {*} value The value to query. + * @returns {string} Returns the raw `toStringTag`. + */ + function getRawTag(value) { + var isOwn = hasOwnProperty.call(value, symToStringTag), + tag = value[symToStringTag]; + + try { + value[symToStringTag] = undefined; + var unmasked = true; + } catch (e) {} + + var result = nativeObjectToString.call(value); + if (unmasked) { + if (isOwn) { + value[symToStringTag] = tag; + } else { + delete value[symToStringTag]; + } + } + return result; + } + + /** + * Creates an array of the own enumerable symbols of `object`. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of symbols. + */ + var getSymbols = !nativeGetSymbols ? stubArray : function(object) { + if (object == null) { + return []; + } + object = Object(object); + return arrayFilter(nativeGetSymbols(object), function(symbol) { + return propertyIsEnumerable.call(object, symbol); + }); + }; + + /** + * Creates an array of the own and inherited enumerable symbols of `object`. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of symbols. + */ + var getSymbolsIn = !nativeGetSymbols ? stubArray : function(object) { + var result = []; + while (object) { + arrayPush(result, getSymbols(object)); + object = getPrototype(object); + } + return result; + }; + + /** + * Gets the `toStringTag` of `value`. + * + * @private + * @param {*} value The value to query. + * @returns {string} Returns the `toStringTag`. + */ + var getTag = baseGetTag; + + // Fallback for data views, maps, sets, and weak maps in IE 11 and promises in Node.js < 6. + if ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) || + (Map && getTag(new Map) != mapTag) || + (Promise && getTag(Promise.resolve()) != promiseTag) || + (Set && getTag(new Set) != setTag) || + (WeakMap && getTag(new WeakMap) != weakMapTag)) { + getTag = function(value) { + var result = baseGetTag(value), + Ctor = result == objectTag ? value.constructor : undefined, + ctorString = Ctor ? toSource(Ctor) : ''; + + if (ctorString) { + switch (ctorString) { + case dataViewCtorString: return dataViewTag; + case mapCtorString: return mapTag; + case promiseCtorString: return promiseTag; + case setCtorString: return setTag; + case weakMapCtorString: return weakMapTag; + } + } + return result; + }; + } + + /** + * Gets the view, applying any `transforms` to the `start` and `end` positions. + * + * @private + * @param {number} start The start of the view. + * @param {number} end The end of the view. + * @param {Array} transforms The transformations to apply to the view. + * @returns {Object} Returns an object containing the `start` and `end` + * positions of the view. + */ + function getView(start, end, transforms) { + var index = -1, + length = transforms.length; + + while (++index < length) { + var data = transforms[index], + size = data.size; + + switch (data.type) { + case 'drop': start += size; break; + case 'dropRight': end -= size; break; + case 'take': end = nativeMin(end, start + size); break; + case 'takeRight': start = nativeMax(start, end - size); break; + } + } + return { 'start': start, 'end': end }; + } + + /** + * Extracts wrapper details from the `source` body comment. + * + * @private + * @param {string} source The source to inspect. + * @returns {Array} Returns the wrapper details. + */ + function getWrapDetails(source) { + var match = source.match(reWrapDetails); + return match ? match[1].split(reSplitDetails) : []; + } + + /** + * Checks if `path` exists on `object`. + * + * @private + * @param {Object} object The object to query. + * @param {Array|string} path The path to check. + * @param {Function} hasFunc The function to check properties. + * @returns {boolean} Returns `true` if `path` exists, else `false`. + */ + function hasPath(object, path, hasFunc) { + path = castPath(path, object); + + var index = -1, + length = path.length, + result = false; + + while (++index < length) { + var key = toKey(path[index]); + if (!(result = object != null && hasFunc(object, key))) { + break; + } + object = object[key]; + } + if (result || ++index != length) { + return result; + } + length = object == null ? 0 : object.length; + return !!length && isLength(length) && isIndex(key, length) && + (isArray(object) || isArguments(object)); + } + + /** + * Initializes an array clone. + * + * @private + * @param {Array} array The array to clone. + * @returns {Array} Returns the initialized clone. + */ + function initCloneArray(array) { + var length = array.length, + result = array.constructor(length); + + // Add properties assigned by `RegExp#exec`. + if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) { + result.index = array.index; + result.input = array.input; + } + return result; + } + + /** + * Initializes an object clone. + * + * @private + * @param {Object} object The object to clone. + * @returns {Object} Returns the initialized clone. + */ + function initCloneObject(object) { + return (typeof object.constructor == 'function' && !isPrototype(object)) + ? baseCreate(getPrototype(object)) + : {}; + } + + /** + * Initializes an object clone based on its `toStringTag`. + * + * **Note:** This function only supports cloning values with tags of + * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`. + * + * @private + * @param {Object} object The object to clone. + * @param {string} tag The `toStringTag` of the object to clone. + * @param {Function} cloneFunc The function to clone values. + * @param {boolean} [isDeep] Specify a deep clone. + * @returns {Object} Returns the initialized clone. + */ + function initCloneByTag(object, tag, cloneFunc, isDeep) { + var Ctor = object.constructor; + switch (tag) { + case arrayBufferTag: + return cloneArrayBuffer(object); + + case boolTag: + case dateTag: + return new Ctor(+object); + + case dataViewTag: + return cloneDataView(object, isDeep); + + case float32Tag: case float64Tag: + case int8Tag: case int16Tag: case int32Tag: + case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag: + return cloneTypedArray(object, isDeep); + + case mapTag: + return cloneMap(object, isDeep, cloneFunc); + + case numberTag: + case stringTag: + return new Ctor(object); + + case regexpTag: + return cloneRegExp(object); + + case setTag: + return cloneSet(object, isDeep, cloneFunc); + + case symbolTag: + return cloneSymbol(object); + } + } + + /** + * Inserts wrapper `details` in a comment at the top of the `source` body. + * + * @private + * @param {string} source The source to modify. + * @returns {Array} details The details to insert. + * @returns {string} Returns the modified source. + */ + function insertWrapDetails(source, details) { + var length = details.length; + if (!length) { + return source; + } + var lastIndex = length - 1; + details[lastIndex] = (length > 1 ? '& ' : '') + details[lastIndex]; + details = details.join(length > 2 ? ', ' : ' '); + return source.replace(reWrapComment, '{\n/* [wrapped with ' + details + '] */\n'); + } + + /** + * Checks if `value` is a flattenable `arguments` object or array. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is flattenable, else `false`. + */ + function isFlattenable(value) { + return isArray(value) || isArguments(value) || + !!(spreadableSymbol && value && value[spreadableSymbol]); + } + + /** + * Checks if `value` is a valid array-like index. + * + * @private + * @param {*} value The value to check. + * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index. + * @returns {boolean} Returns `true` if `value` is a valid index, else `false`. + */ + function isIndex(value, length) { + length = length == null ? MAX_SAFE_INTEGER : length; + return !!length && + (typeof value == 'number' || reIsUint.test(value)) && + (value > -1 && value % 1 == 0 && value < length); + } + + /** + * Checks if the given arguments are from an iteratee call. + * + * @private + * @param {*} value The potential iteratee value argument. + * @param {*} index The potential iteratee index or key argument. + * @param {*} object The potential iteratee object argument. + * @returns {boolean} Returns `true` if the arguments are from an iteratee call, + * else `false`. + */ + function isIterateeCall(value, index, object) { + if (!isObject(object)) { + return false; + } + var type = typeof index; + if (type == 'number' + ? (isArrayLike(object) && isIndex(index, object.length)) + : (type == 'string' && index in object) + ) { + return eq(object[index], value); + } + return false; + } + + /** + * Checks if `value` is a property name and not a property path. + * + * @private + * @param {*} value The value to check. + * @param {Object} [object] The object to query keys on. + * @returns {boolean} Returns `true` if `value` is a property name, else `false`. + */ + function isKey(value, object) { + if (isArray(value)) { + return false; + } + var type = typeof value; + if (type == 'number' || type == 'symbol' || type == 'boolean' || + value == null || isSymbol(value)) { + return true; + } + return reIsPlainProp.test(value) || !reIsDeepProp.test(value) || + (object != null && value in Object(object)); + } + + /** + * Checks if `value` is suitable for use as unique object key. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is suitable, else `false`. + */ + function isKeyable(value) { + var type = typeof value; + return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean') + ? (value !== '__proto__') + : (value === null); + } + + /** + * Checks if `func` has a lazy counterpart. + * + * @private + * @param {Function} func The function to check. + * @returns {boolean} Returns `true` if `func` has a lazy counterpart, + * else `false`. + */ + function isLaziable(func) { + var funcName = getFuncName(func), + other = lodash[funcName]; + + if (typeof other != 'function' || !(funcName in LazyWrapper.prototype)) { + return false; + } + if (func === other) { + return true; + } + var data = getData(other); + return !!data && func === data[0]; + } + + /** + * Checks if `func` has its source masked. + * + * @private + * @param {Function} func The function to check. + * @returns {boolean} Returns `true` if `func` is masked, else `false`. + */ + function isMasked(func) { + return !!maskSrcKey && (maskSrcKey in func); + } + + /** + * Checks if `func` is capable of being masked. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `func` is maskable, else `false`. + */ + var isMaskable = coreJsData ? isFunction : stubFalse; + + /** + * Checks if `value` is likely a prototype object. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a prototype, else `false`. + */ + function isPrototype(value) { + var Ctor = value && value.constructor, + proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto; + + return value === proto; + } + + /** + * Checks if `value` is suitable for strict equality comparisons, i.e. `===`. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` if suitable for strict + * equality comparisons, else `false`. + */ + function isStrictComparable(value) { + return value === value && !isObject(value); + } + + /** + * A specialized version of `matchesProperty` for source values suitable + * for strict equality comparisons, i.e. `===`. + * + * @private + * @param {string} key The key of the property to get. + * @param {*} srcValue The value to match. + * @returns {Function} Returns the new spec function. + */ + function matchesStrictComparable(key, srcValue) { + return function(object) { + if (object == null) { + return false; + } + return object[key] === srcValue && + (srcValue !== undefined || (key in Object(object))); + }; + } + + /** + * A specialized version of `_.memoize` which clears the memoized function's + * cache when it exceeds `MAX_MEMOIZE_SIZE`. + * + * @private + * @param {Function} func The function to have its output memoized. + * @returns {Function} Returns the new memoized function. + */ + function memoizeCapped(func) { + var result = memoize(func, function(key) { + if (cache.size === MAX_MEMOIZE_SIZE) { + cache.clear(); + } + return key; + }); + + var cache = result.cache; + return result; + } + + /** + * Merges the function metadata of `source` into `data`. + * + * Merging metadata reduces the number of wrappers used to invoke a function. + * This is possible because methods like `_.bind`, `_.curry`, and `_.partial` + * may be applied regardless of execution order. Methods like `_.ary` and + * `_.rearg` modify function arguments, making the order in which they are + * executed important, preventing the merging of metadata. However, we make + * an exception for a safe combined case where curried functions have `_.ary` + * and or `_.rearg` applied. + * + * @private + * @param {Array} data The destination metadata. + * @param {Array} source The source metadata. + * @returns {Array} Returns `data`. + */ + function mergeData(data, source) { + var bitmask = data[1], + srcBitmask = source[1], + newBitmask = bitmask | srcBitmask, + isCommon = newBitmask < (WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG | WRAP_ARY_FLAG); + + var isCombo = + ((srcBitmask == WRAP_ARY_FLAG) && (bitmask == WRAP_CURRY_FLAG)) || + ((srcBitmask == WRAP_ARY_FLAG) && (bitmask == WRAP_REARG_FLAG) && (data[7].length <= source[8])) || + ((srcBitmask == (WRAP_ARY_FLAG | WRAP_REARG_FLAG)) && (source[7].length <= source[8]) && (bitmask == WRAP_CURRY_FLAG)); + + // Exit early if metadata can't be merged. + if (!(isCommon || isCombo)) { + return data; + } + // Use source `thisArg` if available. + if (srcBitmask & WRAP_BIND_FLAG) { + data[2] = source[2]; + // Set when currying a bound function. + newBitmask |= bitmask & WRAP_BIND_FLAG ? 0 : WRAP_CURRY_BOUND_FLAG; + } + // Compose partial arguments. + var value = source[3]; + if (value) { + var partials = data[3]; + data[3] = partials ? composeArgs(partials, value, source[4]) : value; + data[4] = partials ? replaceHolders(data[3], PLACEHOLDER) : source[4]; + } + // Compose partial right arguments. + value = source[5]; + if (value) { + partials = data[5]; + data[5] = partials ? composeArgsRight(partials, value, source[6]) : value; + data[6] = partials ? replaceHolders(data[5], PLACEHOLDER) : source[6]; + } + // Use source `argPos` if available. + value = source[7]; + if (value) { + data[7] = value; + } + // Use source `ary` if it's smaller. + if (srcBitmask & WRAP_ARY_FLAG) { + data[8] = data[8] == null ? source[8] : nativeMin(data[8], source[8]); + } + // Use source `arity` if one is not provided. + if (data[9] == null) { + data[9] = source[9]; + } + // Use source `func` and merge bitmasks. + data[0] = source[0]; + data[1] = newBitmask; + + return data; + } + + /** + * This function is like + * [`Object.keys`](http://ecma-international.org/ecma-262/7.0/#sec-object.keys) + * except that it includes inherited enumerable properties. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + */ + function nativeKeysIn(object) { + var result = []; + if (object != null) { + for (var key in Object(object)) { + result.push(key); + } + } + return result; + } + + /** + * Converts `value` to a string using `Object.prototype.toString`. + * + * @private + * @param {*} value The value to convert. + * @returns {string} Returns the converted string. + */ + function objectToString(value) { + return nativeObjectToString.call(value); + } + + /** + * A specialized version of `baseRest` which transforms the rest array. + * + * @private + * @param {Function} func The function to apply a rest parameter to. + * @param {number} [start=func.length-1] The start position of the rest parameter. + * @param {Function} transform The rest array transform. + * @returns {Function} Returns the new function. + */ + function overRest(func, start, transform) { + start = nativeMax(start === undefined ? (func.length - 1) : start, 0); + return function() { + var args = arguments, + index = -1, + length = nativeMax(args.length - start, 0), + array = Array(length); + + while (++index < length) { + array[index] = args[start + index]; + } + index = -1; + var otherArgs = Array(start + 1); + while (++index < start) { + otherArgs[index] = args[index]; + } + otherArgs[start] = transform(array); + return apply(func, this, otherArgs); + }; + } + + /** + * Gets the parent value at `path` of `object`. + * + * @private + * @param {Object} object The object to query. + * @param {Array} path The path to get the parent value of. + * @returns {*} Returns the parent value. + */ + function parent(object, path) { + return path.length < 2 ? object : baseGet(object, baseSlice(path, 0, -1)); + } + + /** + * Reorder `array` according to the specified indexes where the element at + * the first index is assigned as the first element, the element at + * the second index is assigned as the second element, and so on. + * + * @private + * @param {Array} array The array to reorder. + * @param {Array} indexes The arranged array indexes. + * @returns {Array} Returns `array`. + */ + function reorder(array, indexes) { + var arrLength = array.length, + length = nativeMin(indexes.length, arrLength), + oldArray = copyArray(array); + + while (length--) { + var index = indexes[length]; + array[length] = isIndex(index, arrLength) ? oldArray[index] : undefined; + } + return array; + } + + /** + * Sets metadata for `func`. + * + * **Note:** If this function becomes hot, i.e. is invoked a lot in a short + * period of time, it will trip its breaker and transition to an identity + * function to avoid garbage collection pauses in V8. See + * [V8 issue 2070](https://bugs.chromium.org/p/v8/issues/detail?id=2070) + * for more details. + * + * @private + * @param {Function} func The function to associate metadata with. + * @param {*} data The metadata. + * @returns {Function} Returns `func`. + */ + var setData = shortOut(baseSetData); + + /** + * A simple wrapper around the global [`setTimeout`](https://mdn.io/setTimeout). + * + * @private + * @param {Function} func The function to delay. + * @param {number} wait The number of milliseconds to delay invocation. + * @returns {number|Object} Returns the timer id or timeout object. + */ + var setTimeout = ctxSetTimeout || function(func, wait) { + return root.setTimeout(func, wait); + }; + + /** + * Sets the `toString` method of `func` to return `string`. + * + * @private + * @param {Function} func The function to modify. + * @param {Function} string The `toString` result. + * @returns {Function} Returns `func`. + */ + var setToString = shortOut(baseSetToString); + + /** + * Sets the `toString` method of `wrapper` to mimic the source of `reference` + * with wrapper details in a comment at the top of the source body. + * + * @private + * @param {Function} wrapper The function to modify. + * @param {Function} reference The reference function. + * @param {number} bitmask The bitmask flags. See `createWrap` for more details. + * @returns {Function} Returns `wrapper`. + */ + function setWrapToString(wrapper, reference, bitmask) { + var source = (reference + ''); + return setToString(wrapper, insertWrapDetails(source, updateWrapDetails(getWrapDetails(source), bitmask))); + } + + /** + * Creates a function that'll short out and invoke `identity` instead + * of `func` when it's called `HOT_COUNT` or more times in `HOT_SPAN` + * milliseconds. + * + * @private + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new shortable function. + */ + function shortOut(func) { + var count = 0, + lastCalled = 0; + + return function() { + var stamp = nativeNow(), + remaining = HOT_SPAN - (stamp - lastCalled); + + lastCalled = stamp; + if (remaining > 0) { + if (++count >= HOT_COUNT) { + return arguments[0]; + } + } else { + count = 0; + } + return func.apply(undefined, arguments); + }; + } + + /** + * A specialized version of `_.shuffle` which mutates and sets the size of `array`. + * + * @private + * @param {Array} array The array to shuffle. + * @param {number} [size=array.length] The size of `array`. + * @returns {Array} Returns `array`. + */ + function shuffleSelf(array, size) { + var index = -1, + length = array.length, + lastIndex = length - 1; + + size = size === undefined ? length : size; + while (++index < size) { + var rand = baseRandom(index, lastIndex), + value = array[rand]; + + array[rand] = array[index]; + array[index] = value; + } + array.length = size; + return array; + } + + /** + * Converts `string` to a property path array. + * + * @private + * @param {string} string The string to convert. + * @returns {Array} Returns the property path array. + */ + var stringToPath = memoizeCapped(function(string) { + var result = []; + if (reLeadingDot.test(string)) { + result.push(''); + } + string.replace(rePropName, function(match, number, quote, string) { + result.push(quote ? string.replace(reEscapeChar, '$1') : (number || match)); + }); + return result; + }); + + /** + * Converts `value` to a string key if it's not a string or symbol. + * + * @private + * @param {*} value The value to inspect. + * @returns {string|symbol} Returns the key. + */ + function toKey(value) { + if (typeof value == 'string' || isSymbol(value)) { + return value; + } + var result = (value + ''); + return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result; + } + + /** + * Converts `func` to its source code. + * + * @private + * @param {Function} func The function to convert. + * @returns {string} Returns the source code. + */ + function toSource(func) { + if (func != null) { + try { + return funcToString.call(func); + } catch (e) {} + try { + return (func + ''); + } catch (e) {} + } + return ''; + } + + /** + * Updates wrapper `details` based on `bitmask` flags. + * + * @private + * @returns {Array} details The details to modify. + * @param {number} bitmask The bitmask flags. See `createWrap` for more details. + * @returns {Array} Returns `details`. + */ + function updateWrapDetails(details, bitmask) { + arrayEach(wrapFlags, function(pair) { + var value = '_.' + pair[0]; + if ((bitmask & pair[1]) && !arrayIncludes(details, value)) { + details.push(value); + } + }); + return details.sort(); + } + + /** + * Creates a clone of `wrapper`. + * + * @private + * @param {Object} wrapper The wrapper to clone. + * @returns {Object} Returns the cloned wrapper. + */ + function wrapperClone(wrapper) { + if (wrapper instanceof LazyWrapper) { + return wrapper.clone(); + } + var result = new LodashWrapper(wrapper.__wrapped__, wrapper.__chain__); + result.__actions__ = copyArray(wrapper.__actions__); + result.__index__ = wrapper.__index__; + result.__values__ = wrapper.__values__; + return result; + } + + /*------------------------------------------------------------------------*/ + + /** + * Creates an array of elements split into groups the length of `size`. + * If `array` can't be split evenly, the final chunk will be the remaining + * elements. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Array + * @param {Array} array The array to process. + * @param {number} [size=1] The length of each chunk + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {Array} Returns the new array of chunks. + * @example + * + * _.chunk(['a', 'b', 'c', 'd'], 2); + * // => [['a', 'b'], ['c', 'd']] + * + * _.chunk(['a', 'b', 'c', 'd'], 3); + * // => [['a', 'b', 'c'], ['d']] + */ + function chunk(array, size, guard) { + if ((guard ? isIterateeCall(array, size, guard) : size === undefined)) { + size = 1; + } else { + size = nativeMax(toInteger(size), 0); + } + var length = array == null ? 0 : array.length; + if (!length || size < 1) { + return []; + } + var index = 0, + resIndex = 0, + result = Array(nativeCeil(length / size)); + + while (index < length) { + result[resIndex++] = baseSlice(array, index, (index += size)); + } + return result; + } + + /** + * Creates an array with all falsey values removed. The values `false`, `null`, + * `0`, `""`, `undefined`, and `NaN` are falsey. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {Array} array The array to compact. + * @returns {Array} Returns the new array of filtered values. + * @example + * + * _.compact([0, 1, false, 2, '', 3]); + * // => [1, 2, 3] + */ + function compact(array) { + var index = -1, + length = array == null ? 0 : array.length, + resIndex = 0, + result = []; + + while (++index < length) { + var value = array[index]; + if (value) { + result[resIndex++] = value; + } + } + return result; + } + + /** + * Creates a new array concatenating `array` with any additional arrays + * and/or values. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to concatenate. + * @param {...*} [values] The values to concatenate. + * @returns {Array} Returns the new concatenated array. + * @example + * + * var array = [1]; + * var other = _.concat(array, 2, [3], [[4]]); + * + * console.log(other); + * // => [1, 2, 3, [4]] + * + * console.log(array); + * // => [1] + */ + function concat() { + var length = arguments.length; + if (!length) { + return []; + } + var args = Array(length - 1), + array = arguments[0], + index = length; + + while (index--) { + args[index - 1] = arguments[index]; + } + return arrayPush(isArray(array) ? copyArray(array) : [array], baseFlatten(args, 1)); + } + + /** + * Creates an array of `array` values not included in the other given arrays + * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * for equality comparisons. The order and references of result values are + * determined by the first array. + * + * **Note:** Unlike `_.pullAll`, this method returns a new array. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {...Array} [values] The values to exclude. + * @returns {Array} Returns the new array of filtered values. + * @see _.without, _.xor + * @example + * + * _.difference([2, 1], [2, 3]); + * // => [1] + */ + var difference = baseRest(function(array, values) { + return isArrayLikeObject(array) + ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true)) + : []; + }); + + /** + * This method is like `_.difference` except that it accepts `iteratee` which + * is invoked for each element of `array` and `values` to generate the criterion + * by which they're compared. The order and references of result values are + * determined by the first array. The iteratee is invoked with one argument: + * (value). + * + * **Note:** Unlike `_.pullAllBy`, this method returns a new array. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {...Array} [values] The values to exclude. + * @param {Function} [iteratee=_.identity] The iteratee invoked per element. + * @returns {Array} Returns the new array of filtered values. + * @example + * + * _.differenceBy([2.1, 1.2], [2.3, 3.4], Math.floor); + * // => [1.2] + * + * // The `_.property` iteratee shorthand. + * _.differenceBy([{ 'x': 2 }, { 'x': 1 }], [{ 'x': 1 }], 'x'); + * // => [{ 'x': 2 }] + */ + var differenceBy = baseRest(function(array, values) { + var iteratee = last(values); + if (isArrayLikeObject(iteratee)) { + iteratee = undefined; + } + return isArrayLikeObject(array) + ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true), getIteratee(iteratee, 2)) + : []; + }); + + /** + * This method is like `_.difference` except that it accepts `comparator` + * which is invoked to compare elements of `array` to `values`. The order and + * references of result values are determined by the first array. The comparator + * is invoked with two arguments: (arrVal, othVal). + * + * **Note:** Unlike `_.pullAllWith`, this method returns a new array. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {...Array} [values] The values to exclude. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns the new array of filtered values. + * @example + * + * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]; + * + * _.differenceWith(objects, [{ 'x': 1, 'y': 2 }], _.isEqual); + * // => [{ 'x': 2, 'y': 1 }] + */ + var differenceWith = baseRest(function(array, values) { + var comparator = last(values); + if (isArrayLikeObject(comparator)) { + comparator = undefined; + } + return isArrayLikeObject(array) + ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true), undefined, comparator) + : []; + }); + + /** + * Creates a slice of `array` with `n` elements dropped from the beginning. + * + * @static + * @memberOf _ + * @since 0.5.0 + * @category Array + * @param {Array} array The array to query. + * @param {number} [n=1] The number of elements to drop. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.drop([1, 2, 3]); + * // => [2, 3] + * + * _.drop([1, 2, 3], 2); + * // => [3] + * + * _.drop([1, 2, 3], 5); + * // => [] + * + * _.drop([1, 2, 3], 0); + * // => [1, 2, 3] + */ + function drop(array, n, guard) { + var length = array == null ? 0 : array.length; + if (!length) { + return []; + } + n = (guard || n === undefined) ? 1 : toInteger(n); + return baseSlice(array, n < 0 ? 0 : n, length); + } + + /** + * Creates a slice of `array` with `n` elements dropped from the end. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Array + * @param {Array} array The array to query. + * @param {number} [n=1] The number of elements to drop. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.dropRight([1, 2, 3]); + * // => [1, 2] + * + * _.dropRight([1, 2, 3], 2); + * // => [1] + * + * _.dropRight([1, 2, 3], 5); + * // => [] + * + * _.dropRight([1, 2, 3], 0); + * // => [1, 2, 3] + */ + function dropRight(array, n, guard) { + var length = array == null ? 0 : array.length; + if (!length) { + return []; + } + n = (guard || n === undefined) ? 1 : toInteger(n); + n = length - n; + return baseSlice(array, 0, n < 0 ? 0 : n); + } + + /** + * Creates a slice of `array` excluding elements dropped from the end. + * Elements are dropped until `predicate` returns falsey. The predicate is + * invoked with three arguments: (value, index, array). + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Array + * @param {Array} array The array to query. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @returns {Array} Returns the slice of `array`. + * @example + * + * var users = [ + * { 'user': 'barney', 'active': true }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': false } + * ]; + * + * _.dropRightWhile(users, function(o) { return !o.active; }); + * // => objects for ['barney'] + * + * // The `_.matches` iteratee shorthand. + * _.dropRightWhile(users, { 'user': 'pebbles', 'active': false }); + * // => objects for ['barney', 'fred'] + * + * // The `_.matchesProperty` iteratee shorthand. + * _.dropRightWhile(users, ['active', false]); + * // => objects for ['barney'] + * + * // The `_.property` iteratee shorthand. + * _.dropRightWhile(users, 'active'); + * // => objects for ['barney', 'fred', 'pebbles'] + */ + function dropRightWhile(array, predicate) { + return (array && array.length) + ? baseWhile(array, getIteratee(predicate, 3), true, true) + : []; + } + + /** + * Creates a slice of `array` excluding elements dropped from the beginning. + * Elements are dropped until `predicate` returns falsey. The predicate is + * invoked with three arguments: (value, index, array). + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Array + * @param {Array} array The array to query. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @returns {Array} Returns the slice of `array`. + * @example + * + * var users = [ + * { 'user': 'barney', 'active': false }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': true } + * ]; + * + * _.dropWhile(users, function(o) { return !o.active; }); + * // => objects for ['pebbles'] + * + * // The `_.matches` iteratee shorthand. + * _.dropWhile(users, { 'user': 'barney', 'active': false }); + * // => objects for ['fred', 'pebbles'] + * + * // The `_.matchesProperty` iteratee shorthand. + * _.dropWhile(users, ['active', false]); + * // => objects for ['pebbles'] + * + * // The `_.property` iteratee shorthand. + * _.dropWhile(users, 'active'); + * // => objects for ['barney', 'fred', 'pebbles'] + */ + function dropWhile(array, predicate) { + return (array && array.length) + ? baseWhile(array, getIteratee(predicate, 3), true) + : []; + } + + /** + * Fills elements of `array` with `value` from `start` up to, but not + * including, `end`. + * + * **Note:** This method mutates `array`. + * + * @static + * @memberOf _ + * @since 3.2.0 + * @category Array + * @param {Array} array The array to fill. + * @param {*} value The value to fill `array` with. + * @param {number} [start=0] The start position. + * @param {number} [end=array.length] The end position. + * @returns {Array} Returns `array`. + * @example + * + * var array = [1, 2, 3]; + * + * _.fill(array, 'a'); + * console.log(array); + * // => ['a', 'a', 'a'] + * + * _.fill(Array(3), 2); + * // => [2, 2, 2] + * + * _.fill([4, 6, 8, 10], '*', 1, 3); + * // => [4, '*', '*', 10] + */ + function fill(array, value, start, end) { + var length = array == null ? 0 : array.length; + if (!length) { + return []; + } + if (start && typeof start != 'number' && isIterateeCall(array, value, start)) { + start = 0; + end = length; + } + return baseFill(array, value, start, end); + } + + /** + * This method is like `_.find` except that it returns the index of the first + * element `predicate` returns truthy for instead of the element itself. + * + * @static + * @memberOf _ + * @since 1.1.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @param {number} [fromIndex=0] The index to search from. + * @returns {number} Returns the index of the found element, else `-1`. + * @example + * + * var users = [ + * { 'user': 'barney', 'active': false }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': true } + * ]; + * + * _.findIndex(users, function(o) { return o.user == 'barney'; }); + * // => 0 + * + * // The `_.matches` iteratee shorthand. + * _.findIndex(users, { 'user': 'fred', 'active': false }); + * // => 1 + * + * // The `_.matchesProperty` iteratee shorthand. + * _.findIndex(users, ['active', false]); + * // => 0 + * + * // The `_.property` iteratee shorthand. + * _.findIndex(users, 'active'); + * // => 2 + */ + function findIndex(array, predicate, fromIndex) { + var length = array == null ? 0 : array.length; + if (!length) { + return -1; + } + var index = fromIndex == null ? 0 : toInteger(fromIndex); + if (index < 0) { + index = nativeMax(length + index, 0); + } + return baseFindIndex(array, getIteratee(predicate, 3), index); + } + + /** + * This method is like `_.findIndex` except that it iterates over elements + * of `collection` from right to left. + * + * @static + * @memberOf _ + * @since 2.0.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @param {number} [fromIndex=array.length-1] The index to search from. + * @returns {number} Returns the index of the found element, else `-1`. + * @example + * + * var users = [ + * { 'user': 'barney', 'active': true }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': false } + * ]; + * + * _.findLastIndex(users, function(o) { return o.user == 'pebbles'; }); + * // => 2 + * + * // The `_.matches` iteratee shorthand. + * _.findLastIndex(users, { 'user': 'barney', 'active': true }); + * // => 0 + * + * // The `_.matchesProperty` iteratee shorthand. + * _.findLastIndex(users, ['active', false]); + * // => 2 + * + * // The `_.property` iteratee shorthand. + * _.findLastIndex(users, 'active'); + * // => 0 + */ + function findLastIndex(array, predicate, fromIndex) { + var length = array == null ? 0 : array.length; + if (!length) { + return -1; + } + var index = length - 1; + if (fromIndex !== undefined) { + index = toInteger(fromIndex); + index = fromIndex < 0 + ? nativeMax(length + index, 0) + : nativeMin(index, length - 1); + } + return baseFindIndex(array, getIteratee(predicate, 3), index, true); + } + + /** + * Flattens `array` a single level deep. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {Array} array The array to flatten. + * @returns {Array} Returns the new flattened array. + * @example + * + * _.flatten([1, [2, [3, [4]], 5]]); + * // => [1, 2, [3, [4]], 5] + */ + function flatten(array) { + var length = array == null ? 0 : array.length; + return length ? baseFlatten(array, 1) : []; + } + + /** + * Recursively flattens `array`. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Array + * @param {Array} array The array to flatten. + * @returns {Array} Returns the new flattened array. + * @example + * + * _.flattenDeep([1, [2, [3, [4]], 5]]); + * // => [1, 2, 3, 4, 5] + */ + function flattenDeep(array) { + var length = array == null ? 0 : array.length; + return length ? baseFlatten(array, INFINITY) : []; + } + + /** + * Recursively flatten `array` up to `depth` times. + * + * @static + * @memberOf _ + * @since 4.4.0 + * @category Array + * @param {Array} array The array to flatten. + * @param {number} [depth=1] The maximum recursion depth. + * @returns {Array} Returns the new flattened array. + * @example + * + * var array = [1, [2, [3, [4]], 5]]; + * + * _.flattenDepth(array, 1); + * // => [1, 2, [3, [4]], 5] + * + * _.flattenDepth(array, 2); + * // => [1, 2, 3, [4], 5] + */ + function flattenDepth(array, depth) { + var length = array == null ? 0 : array.length; + if (!length) { + return []; + } + depth = depth === undefined ? 1 : toInteger(depth); + return baseFlatten(array, depth); + } + + /** + * The inverse of `_.toPairs`; this method returns an object composed + * from key-value `pairs`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} pairs The key-value pairs. + * @returns {Object} Returns the new object. + * @example + * + * _.fromPairs([['a', 1], ['b', 2]]); + * // => { 'a': 1, 'b': 2 } + */ + function fromPairs(pairs) { + var index = -1, + length = pairs == null ? 0 : pairs.length, + result = {}; + + while (++index < length) { + var pair = pairs[index]; + result[pair[0]] = pair[1]; + } + return result; + } + + /** + * Gets the first element of `array`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @alias first + * @category Array + * @param {Array} array The array to query. + * @returns {*} Returns the first element of `array`. + * @example + * + * _.head([1, 2, 3]); + * // => 1 + * + * _.head([]); + * // => undefined + */ + function head(array) { + return (array && array.length) ? array[0] : undefined; + } + + /** + * Gets the index at which the first occurrence of `value` is found in `array` + * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * for equality comparisons. If `fromIndex` is negative, it's used as the + * offset from the end of `array`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {*} value The value to search for. + * @param {number} [fromIndex=0] The index to search from. + * @returns {number} Returns the index of the matched value, else `-1`. + * @example + * + * _.indexOf([1, 2, 1, 2], 2); + * // => 1 + * + * // Search from the `fromIndex`. + * _.indexOf([1, 2, 1, 2], 2, 2); + * // => 3 + */ + function indexOf(array, value, fromIndex) { + var length = array == null ? 0 : array.length; + if (!length) { + return -1; + } + var index = fromIndex == null ? 0 : toInteger(fromIndex); + if (index < 0) { + index = nativeMax(length + index, 0); + } + return baseIndexOf(array, value, index); + } + + /** + * Gets all but the last element of `array`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {Array} array The array to query. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.initial([1, 2, 3]); + * // => [1, 2] + */ + function initial(array) { + var length = array == null ? 0 : array.length; + return length ? baseSlice(array, 0, -1) : []; + } + + /** + * Creates an array of unique values that are included in all given arrays + * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * for equality comparisons. The order and references of result values are + * determined by the first array. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @returns {Array} Returns the new array of intersecting values. + * @example + * + * _.intersection([2, 1], [2, 3]); + * // => [2] + */ + var intersection = baseRest(function(arrays) { + var mapped = arrayMap(arrays, castArrayLikeObject); + return (mapped.length && mapped[0] === arrays[0]) + ? baseIntersection(mapped) + : []; + }); + + /** + * This method is like `_.intersection` except that it accepts `iteratee` + * which is invoked for each element of each `arrays` to generate the criterion + * by which they're compared. The order and references of result values are + * determined by the first array. The iteratee is invoked with one argument: + * (value). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @param {Function} [iteratee=_.identity] The iteratee invoked per element. + * @returns {Array} Returns the new array of intersecting values. + * @example + * + * _.intersectionBy([2.1, 1.2], [2.3, 3.4], Math.floor); + * // => [2.1] + * + * // The `_.property` iteratee shorthand. + * _.intersectionBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x'); + * // => [{ 'x': 1 }] + */ + var intersectionBy = baseRest(function(arrays) { + var iteratee = last(arrays), + mapped = arrayMap(arrays, castArrayLikeObject); + + if (iteratee === last(mapped)) { + iteratee = undefined; + } else { + mapped.pop(); + } + return (mapped.length && mapped[0] === arrays[0]) + ? baseIntersection(mapped, getIteratee(iteratee, 2)) + : []; + }); + + /** + * This method is like `_.intersection` except that it accepts `comparator` + * which is invoked to compare elements of `arrays`. The order and references + * of result values are determined by the first array. The comparator is + * invoked with two arguments: (arrVal, othVal). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns the new array of intersecting values. + * @example + * + * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]; + * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }]; + * + * _.intersectionWith(objects, others, _.isEqual); + * // => [{ 'x': 1, 'y': 2 }] + */ + var intersectionWith = baseRest(function(arrays) { + var comparator = last(arrays), + mapped = arrayMap(arrays, castArrayLikeObject); + + comparator = typeof comparator == 'function' ? comparator : undefined; + if (comparator) { + mapped.pop(); + } + return (mapped.length && mapped[0] === arrays[0]) + ? baseIntersection(mapped, undefined, comparator) + : []; + }); + + /** + * Converts all elements in `array` into a string separated by `separator`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to convert. + * @param {string} [separator=','] The element separator. + * @returns {string} Returns the joined string. + * @example + * + * _.join(['a', 'b', 'c'], '~'); + * // => 'a~b~c' + */ + function join(array, separator) { + return array == null ? '' : nativeJoin.call(array, separator); + } + + /** + * Gets the last element of `array`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {Array} array The array to query. + * @returns {*} Returns the last element of `array`. + * @example + * + * _.last([1, 2, 3]); + * // => 3 + */ + function last(array) { + var length = array == null ? 0 : array.length; + return length ? array[length - 1] : undefined; + } + + /** + * This method is like `_.indexOf` except that it iterates over elements of + * `array` from right to left. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {*} value The value to search for. + * @param {number} [fromIndex=array.length-1] The index to search from. + * @returns {number} Returns the index of the matched value, else `-1`. + * @example + * + * _.lastIndexOf([1, 2, 1, 2], 2); + * // => 3 + * + * // Search from the `fromIndex`. + * _.lastIndexOf([1, 2, 1, 2], 2, 2); + * // => 1 + */ + function lastIndexOf(array, value, fromIndex) { + var length = array == null ? 0 : array.length; + if (!length) { + return -1; + } + var index = length; + if (fromIndex !== undefined) { + index = toInteger(fromIndex); + index = index < 0 ? nativeMax(length + index, 0) : nativeMin(index, length - 1); + } + return value === value + ? strictLastIndexOf(array, value, index) + : baseFindIndex(array, baseIsNaN, index, true); + } + + /** + * Gets the element at index `n` of `array`. If `n` is negative, the nth + * element from the end is returned. + * + * @static + * @memberOf _ + * @since 4.11.0 + * @category Array + * @param {Array} array The array to query. + * @param {number} [n=0] The index of the element to return. + * @returns {*} Returns the nth element of `array`. + * @example + * + * var array = ['a', 'b', 'c', 'd']; + * + * _.nth(array, 1); + * // => 'b' + * + * _.nth(array, -2); + * // => 'c'; + */ + function nth(array, n) { + return (array && array.length) ? baseNth(array, toInteger(n)) : undefined; + } + + /** + * Removes all given values from `array` using + * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * for equality comparisons. + * + * **Note:** Unlike `_.without`, this method mutates `array`. Use `_.remove` + * to remove elements from an array by predicate. + * + * @static + * @memberOf _ + * @since 2.0.0 + * @category Array + * @param {Array} array The array to modify. + * @param {...*} [values] The values to remove. + * @returns {Array} Returns `array`. + * @example + * + * var array = ['a', 'b', 'c', 'a', 'b', 'c']; + * + * _.pull(array, 'a', 'c'); + * console.log(array); + * // => ['b', 'b'] + */ + var pull = baseRest(pullAll); + + /** + * This method is like `_.pull` except that it accepts an array of values to remove. + * + * **Note:** Unlike `_.difference`, this method mutates `array`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to modify. + * @param {Array} values The values to remove. + * @returns {Array} Returns `array`. + * @example + * + * var array = ['a', 'b', 'c', 'a', 'b', 'c']; + * + * _.pullAll(array, ['a', 'c']); + * console.log(array); + * // => ['b', 'b'] + */ + function pullAll(array, values) { + return (array && array.length && values && values.length) + ? basePullAll(array, values) + : array; + } + + /** + * This method is like `_.pullAll` except that it accepts `iteratee` which is + * invoked for each element of `array` and `values` to generate the criterion + * by which they're compared. The iteratee is invoked with one argument: (value). + * + * **Note:** Unlike `_.differenceBy`, this method mutates `array`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to modify. + * @param {Array} values The values to remove. + * @param {Function} [iteratee=_.identity] The iteratee invoked per element. + * @returns {Array} Returns `array`. + * @example + * + * var array = [{ 'x': 1 }, { 'x': 2 }, { 'x': 3 }, { 'x': 1 }]; + * + * _.pullAllBy(array, [{ 'x': 1 }, { 'x': 3 }], 'x'); + * console.log(array); + * // => [{ 'x': 2 }] + */ + function pullAllBy(array, values, iteratee) { + return (array && array.length && values && values.length) + ? basePullAll(array, values, getIteratee(iteratee, 2)) + : array; + } + + /** + * This method is like `_.pullAll` except that it accepts `comparator` which + * is invoked to compare elements of `array` to `values`. The comparator is + * invoked with two arguments: (arrVal, othVal). + * + * **Note:** Unlike `_.differenceWith`, this method mutates `array`. + * + * @static + * @memberOf _ + * @since 4.6.0 + * @category Array + * @param {Array} array The array to modify. + * @param {Array} values The values to remove. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns `array`. + * @example + * + * var array = [{ 'x': 1, 'y': 2 }, { 'x': 3, 'y': 4 }, { 'x': 5, 'y': 6 }]; + * + * _.pullAllWith(array, [{ 'x': 3, 'y': 4 }], _.isEqual); + * console.log(array); + * // => [{ 'x': 1, 'y': 2 }, { 'x': 5, 'y': 6 }] + */ + function pullAllWith(array, values, comparator) { + return (array && array.length && values && values.length) + ? basePullAll(array, values, undefined, comparator) + : array; + } + + /** + * Removes elements from `array` corresponding to `indexes` and returns an + * array of removed elements. + * + * **Note:** Unlike `_.at`, this method mutates `array`. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Array + * @param {Array} array The array to modify. + * @param {...(number|number[])} [indexes] The indexes of elements to remove. + * @returns {Array} Returns the new array of removed elements. + * @example + * + * var array = ['a', 'b', 'c', 'd']; + * var pulled = _.pullAt(array, [1, 3]); + * + * console.log(array); + * // => ['a', 'c'] + * + * console.log(pulled); + * // => ['b', 'd'] + */ + var pullAt = flatRest(function(array, indexes) { + var length = array == null ? 0 : array.length, + result = baseAt(array, indexes); + + basePullAt(array, arrayMap(indexes, function(index) { + return isIndex(index, length) ? +index : index; + }).sort(compareAscending)); + + return result; + }); + + /** + * Removes all elements from `array` that `predicate` returns truthy for + * and returns an array of the removed elements. The predicate is invoked + * with three arguments: (value, index, array). + * + * **Note:** Unlike `_.filter`, this method mutates `array`. Use `_.pull` + * to pull elements from an array by value. + * + * @static + * @memberOf _ + * @since 2.0.0 + * @category Array + * @param {Array} array The array to modify. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @returns {Array} Returns the new array of removed elements. + * @example + * + * var array = [1, 2, 3, 4]; + * var evens = _.remove(array, function(n) { + * return n % 2 == 0; + * }); + * + * console.log(array); + * // => [1, 3] + * + * console.log(evens); + * // => [2, 4] + */ + function remove(array, predicate) { + var result = []; + if (!(array && array.length)) { + return result; + } + var index = -1, + indexes = [], + length = array.length; + + predicate = getIteratee(predicate, 3); + while (++index < length) { + var value = array[index]; + if (predicate(value, index, array)) { + result.push(value); + indexes.push(index); + } + } + basePullAt(array, indexes); + return result; + } + + /** + * Reverses `array` so that the first element becomes the last, the second + * element becomes the second to last, and so on. + * + * **Note:** This method mutates `array` and is based on + * [`Array#reverse`](https://mdn.io/Array/reverse). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to modify. + * @returns {Array} Returns `array`. + * @example + * + * var array = [1, 2, 3]; + * + * _.reverse(array); + * // => [3, 2, 1] + * + * console.log(array); + * // => [3, 2, 1] + */ + function reverse(array) { + return array == null ? array : nativeReverse.call(array); + } + + /** + * Creates a slice of `array` from `start` up to, but not including, `end`. + * + * **Note:** This method is used instead of + * [`Array#slice`](https://mdn.io/Array/slice) to ensure dense arrays are + * returned. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Array + * @param {Array} array The array to slice. + * @param {number} [start=0] The start position. + * @param {number} [end=array.length] The end position. + * @returns {Array} Returns the slice of `array`. + */ + function slice(array, start, end) { + var length = array == null ? 0 : array.length; + if (!length) { + return []; + } + if (end && typeof end != 'number' && isIterateeCall(array, start, end)) { + start = 0; + end = length; + } + else { + start = start == null ? 0 : toInteger(start); + end = end === undefined ? length : toInteger(end); + } + return baseSlice(array, start, end); + } + + /** + * Uses a binary search to determine the lowest index at which `value` + * should be inserted into `array` in order to maintain its sort order. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {Array} array The sorted array to inspect. + * @param {*} value The value to evaluate. + * @returns {number} Returns the index at which `value` should be inserted + * into `array`. + * @example + * + * _.sortedIndex([30, 50], 40); + * // => 1 + */ + function sortedIndex(array, value) { + return baseSortedIndex(array, value); + } + + /** + * This method is like `_.sortedIndex` except that it accepts `iteratee` + * which is invoked for `value` and each element of `array` to compute their + * sort ranking. The iteratee is invoked with one argument: (value). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The sorted array to inspect. + * @param {*} value The value to evaluate. + * @param {Function} [iteratee=_.identity] The iteratee invoked per element. + * @returns {number} Returns the index at which `value` should be inserted + * into `array`. + * @example + * + * var objects = [{ 'x': 4 }, { 'x': 5 }]; + * + * _.sortedIndexBy(objects, { 'x': 4 }, function(o) { return o.x; }); + * // => 0 + * + * // The `_.property` iteratee shorthand. + * _.sortedIndexBy(objects, { 'x': 4 }, 'x'); + * // => 0 + */ + function sortedIndexBy(array, value, iteratee) { + return baseSortedIndexBy(array, value, getIteratee(iteratee, 2)); + } + + /** + * This method is like `_.indexOf` except that it performs a binary + * search on a sorted `array`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {*} value The value to search for. + * @returns {number} Returns the index of the matched value, else `-1`. + * @example + * + * _.sortedIndexOf([4, 5, 5, 5, 6], 5); + * // => 1 + */ + function sortedIndexOf(array, value) { + var length = array == null ? 0 : array.length; + if (length) { + var index = baseSortedIndex(array, value); + if (index < length && eq(array[index], value)) { + return index; + } + } + return -1; + } + + /** + * This method is like `_.sortedIndex` except that it returns the highest + * index at which `value` should be inserted into `array` in order to + * maintain its sort order. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Array + * @param {Array} array The sorted array to inspect. + * @param {*} value The value to evaluate. + * @returns {number} Returns the index at which `value` should be inserted + * into `array`. + * @example + * + * _.sortedLastIndex([4, 5, 5, 5, 6], 5); + * // => 4 + */ + function sortedLastIndex(array, value) { + return baseSortedIndex(array, value, true); + } + + /** + * This method is like `_.sortedLastIndex` except that it accepts `iteratee` + * which is invoked for `value` and each element of `array` to compute their + * sort ranking. The iteratee is invoked with one argument: (value). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The sorted array to inspect. + * @param {*} value The value to evaluate. + * @param {Function} [iteratee=_.identity] The iteratee invoked per element. + * @returns {number} Returns the index at which `value` should be inserted + * into `array`. + * @example + * + * var objects = [{ 'x': 4 }, { 'x': 5 }]; + * + * _.sortedLastIndexBy(objects, { 'x': 4 }, function(o) { return o.x; }); + * // => 1 + * + * // The `_.property` iteratee shorthand. + * _.sortedLastIndexBy(objects, { 'x': 4 }, 'x'); + * // => 1 + */ + function sortedLastIndexBy(array, value, iteratee) { + return baseSortedIndexBy(array, value, getIteratee(iteratee, 2), true); + } + + /** + * This method is like `_.lastIndexOf` except that it performs a binary + * search on a sorted `array`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {*} value The value to search for. + * @returns {number} Returns the index of the matched value, else `-1`. + * @example + * + * _.sortedLastIndexOf([4, 5, 5, 5, 6], 5); + * // => 3 + */ + function sortedLastIndexOf(array, value) { + var length = array == null ? 0 : array.length; + if (length) { + var index = baseSortedIndex(array, value, true) - 1; + if (eq(array[index], value)) { + return index; + } + } + return -1; + } + + /** + * This method is like `_.uniq` except that it's designed and optimized + * for sorted arrays. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to inspect. + * @returns {Array} Returns the new duplicate free array. + * @example + * + * _.sortedUniq([1, 1, 2]); + * // => [1, 2] + */ + function sortedUniq(array) { + return (array && array.length) + ? baseSortedUniq(array) + : []; + } + + /** + * This method is like `_.uniqBy` except that it's designed and optimized + * for sorted arrays. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {Function} [iteratee] The iteratee invoked per element. + * @returns {Array} Returns the new duplicate free array. + * @example + * + * _.sortedUniqBy([1.1, 1.2, 2.3, 2.4], Math.floor); + * // => [1.1, 2.3] + */ + function sortedUniqBy(array, iteratee) { + return (array && array.length) + ? baseSortedUniq(array, getIteratee(iteratee, 2)) + : []; + } + + /** + * Gets all but the first element of `array`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to query. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.tail([1, 2, 3]); + * // => [2, 3] + */ + function tail(array) { + var length = array == null ? 0 : array.length; + return length ? baseSlice(array, 1, length) : []; + } + + /** + * Creates a slice of `array` with `n` elements taken from the beginning. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {Array} array The array to query. + * @param {number} [n=1] The number of elements to take. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.take([1, 2, 3]); + * // => [1] + * + * _.take([1, 2, 3], 2); + * // => [1, 2] + * + * _.take([1, 2, 3], 5); + * // => [1, 2, 3] + * + * _.take([1, 2, 3], 0); + * // => [] + */ + function take(array, n, guard) { + if (!(array && array.length)) { + return []; + } + n = (guard || n === undefined) ? 1 : toInteger(n); + return baseSlice(array, 0, n < 0 ? 0 : n); + } + + /** + * Creates a slice of `array` with `n` elements taken from the end. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Array + * @param {Array} array The array to query. + * @param {number} [n=1] The number of elements to take. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.takeRight([1, 2, 3]); + * // => [3] + * + * _.takeRight([1, 2, 3], 2); + * // => [2, 3] + * + * _.takeRight([1, 2, 3], 5); + * // => [1, 2, 3] + * + * _.takeRight([1, 2, 3], 0); + * // => [] + */ + function takeRight(array, n, guard) { + var length = array == null ? 0 : array.length; + if (!length) { + return []; + } + n = (guard || n === undefined) ? 1 : toInteger(n); + n = length - n; + return baseSlice(array, n < 0 ? 0 : n, length); + } + + /** + * Creates a slice of `array` with elements taken from the end. Elements are + * taken until `predicate` returns falsey. The predicate is invoked with + * three arguments: (value, index, array). + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Array + * @param {Array} array The array to query. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @returns {Array} Returns the slice of `array`. + * @example + * + * var users = [ + * { 'user': 'barney', 'active': true }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': false } + * ]; + * + * _.takeRightWhile(users, function(o) { return !o.active; }); + * // => objects for ['fred', 'pebbles'] + * + * // The `_.matches` iteratee shorthand. + * _.takeRightWhile(users, { 'user': 'pebbles', 'active': false }); + * // => objects for ['pebbles'] + * + * // The `_.matchesProperty` iteratee shorthand. + * _.takeRightWhile(users, ['active', false]); + * // => objects for ['fred', 'pebbles'] + * + * // The `_.property` iteratee shorthand. + * _.takeRightWhile(users, 'active'); + * // => [] + */ + function takeRightWhile(array, predicate) { + return (array && array.length) + ? baseWhile(array, getIteratee(predicate, 3), false, true) + : []; + } + + /** + * Creates a slice of `array` with elements taken from the beginning. Elements + * are taken until `predicate` returns falsey. The predicate is invoked with + * three arguments: (value, index, array). + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Array + * @param {Array} array The array to query. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @returns {Array} Returns the slice of `array`. + * @example + * + * var users = [ + * { 'user': 'barney', 'active': false }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': true } + * ]; + * + * _.takeWhile(users, function(o) { return !o.active; }); + * // => objects for ['barney', 'fred'] + * + * // The `_.matches` iteratee shorthand. + * _.takeWhile(users, { 'user': 'barney', 'active': false }); + * // => objects for ['barney'] + * + * // The `_.matchesProperty` iteratee shorthand. + * _.takeWhile(users, ['active', false]); + * // => objects for ['barney', 'fred'] + * + * // The `_.property` iteratee shorthand. + * _.takeWhile(users, 'active'); + * // => [] + */ + function takeWhile(array, predicate) { + return (array && array.length) + ? baseWhile(array, getIteratee(predicate, 3)) + : []; + } + + /** + * Creates an array of unique values, in order, from all given arrays using + * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * for equality comparisons. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @returns {Array} Returns the new array of combined values. + * @example + * + * _.union([2], [1, 2]); + * // => [2, 1] + */ + var union = baseRest(function(arrays) { + return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true)); + }); + + /** + * This method is like `_.union` except that it accepts `iteratee` which is + * invoked for each element of each `arrays` to generate the criterion by + * which uniqueness is computed. Result values are chosen from the first + * array in which the value occurs. The iteratee is invoked with one argument: + * (value). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @param {Function} [iteratee=_.identity] The iteratee invoked per element. + * @returns {Array} Returns the new array of combined values. + * @example + * + * _.unionBy([2.1], [1.2, 2.3], Math.floor); + * // => [2.1, 1.2] + * + * // The `_.property` iteratee shorthand. + * _.unionBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x'); + * // => [{ 'x': 1 }, { 'x': 2 }] + */ + var unionBy = baseRest(function(arrays) { + var iteratee = last(arrays); + if (isArrayLikeObject(iteratee)) { + iteratee = undefined; + } + return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true), getIteratee(iteratee, 2)); + }); + + /** + * This method is like `_.union` except that it accepts `comparator` which + * is invoked to compare elements of `arrays`. Result values are chosen from + * the first array in which the value occurs. The comparator is invoked + * with two arguments: (arrVal, othVal). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns the new array of combined values. + * @example + * + * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]; + * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }]; + * + * _.unionWith(objects, others, _.isEqual); + * // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }] + */ + var unionWith = baseRest(function(arrays) { + var comparator = last(arrays); + comparator = typeof comparator == 'function' ? comparator : undefined; + return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true), undefined, comparator); + }); + + /** + * Creates a duplicate-free version of an array, using + * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * for equality comparisons, in which only the first occurrence of each element + * is kept. The order of result values is determined by the order they occur + * in the array. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {Array} array The array to inspect. + * @returns {Array} Returns the new duplicate free array. + * @example + * + * _.uniq([2, 1, 2]); + * // => [2, 1] + */ + function uniq(array) { + return (array && array.length) ? baseUniq(array) : []; + } + + /** + * This method is like `_.uniq` except that it accepts `iteratee` which is + * invoked for each element in `array` to generate the criterion by which + * uniqueness is computed. The order of result values is determined by the + * order they occur in the array. The iteratee is invoked with one argument: + * (value). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {Function} [iteratee=_.identity] The iteratee invoked per element. + * @returns {Array} Returns the new duplicate free array. + * @example + * + * _.uniqBy([2.1, 1.2, 2.3], Math.floor); + * // => [2.1, 1.2] + * + * // The `_.property` iteratee shorthand. + * _.uniqBy([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x'); + * // => [{ 'x': 1 }, { 'x': 2 }] + */ + function uniqBy(array, iteratee) { + return (array && array.length) ? baseUniq(array, getIteratee(iteratee, 2)) : []; + } + + /** + * This method is like `_.uniq` except that it accepts `comparator` which + * is invoked to compare elements of `array`. The order of result values is + * determined by the order they occur in the array.The comparator is invoked + * with two arguments: (arrVal, othVal). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns the new duplicate free array. + * @example + * + * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 2 }]; + * + * _.uniqWith(objects, _.isEqual); + * // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }] + */ + function uniqWith(array, comparator) { + comparator = typeof comparator == 'function' ? comparator : undefined; + return (array && array.length) ? baseUniq(array, undefined, comparator) : []; + } + + /** + * This method is like `_.zip` except that it accepts an array of grouped + * elements and creates an array regrouping the elements to their pre-zip + * configuration. + * + * @static + * @memberOf _ + * @since 1.2.0 + * @category Array + * @param {Array} array The array of grouped elements to process. + * @returns {Array} Returns the new array of regrouped elements. + * @example + * + * var zipped = _.zip(['a', 'b'], [1, 2], [true, false]); + * // => [['a', 1, true], ['b', 2, false]] + * + * _.unzip(zipped); + * // => [['a', 'b'], [1, 2], [true, false]] + */ + function unzip(array) { + if (!(array && array.length)) { + return []; + } + var length = 0; + array = arrayFilter(array, function(group) { + if (isArrayLikeObject(group)) { + length = nativeMax(group.length, length); + return true; + } + }); + return baseTimes(length, function(index) { + return arrayMap(array, baseProperty(index)); + }); + } + + /** + * This method is like `_.unzip` except that it accepts `iteratee` to specify + * how regrouped values should be combined. The iteratee is invoked with the + * elements of each group: (...group). + * + * @static + * @memberOf _ + * @since 3.8.0 + * @category Array + * @param {Array} array The array of grouped elements to process. + * @param {Function} [iteratee=_.identity] The function to combine + * regrouped values. + * @returns {Array} Returns the new array of regrouped elements. + * @example + * + * var zipped = _.zip([1, 2], [10, 20], [100, 200]); + * // => [[1, 10, 100], [2, 20, 200]] + * + * _.unzipWith(zipped, _.add); + * // => [3, 30, 300] + */ + function unzipWith(array, iteratee) { + if (!(array && array.length)) { + return []; + } + var result = unzip(array); + if (iteratee == null) { + return result; + } + return arrayMap(result, function(group) { + return apply(iteratee, undefined, group); + }); + } + + /** + * Creates an array excluding all given values using + * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * for equality comparisons. + * + * **Note:** Unlike `_.pull`, this method returns a new array. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {...*} [values] The values to exclude. + * @returns {Array} Returns the new array of filtered values. + * @see _.difference, _.xor + * @example + * + * _.without([2, 1, 2, 3], 1, 2); + * // => [3] + */ + var without = baseRest(function(array, values) { + return isArrayLikeObject(array) + ? baseDifference(array, values) + : []; + }); + + /** + * Creates an array of unique values that is the + * [symmetric difference](https://en.wikipedia.org/wiki/Symmetric_difference) + * of the given arrays. The order of result values is determined by the order + * they occur in the arrays. + * + * @static + * @memberOf _ + * @since 2.4.0 + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @returns {Array} Returns the new array of filtered values. + * @see _.difference, _.without + * @example + * + * _.xor([2, 1], [2, 3]); + * // => [1, 3] + */ + var xor = baseRest(function(arrays) { + return baseXor(arrayFilter(arrays, isArrayLikeObject)); + }); + + /** + * This method is like `_.xor` except that it accepts `iteratee` which is + * invoked for each element of each `arrays` to generate the criterion by + * which by which they're compared. The order of result values is determined + * by the order they occur in the arrays. The iteratee is invoked with one + * argument: (value). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @param {Function} [iteratee=_.identity] The iteratee invoked per element. + * @returns {Array} Returns the new array of filtered values. + * @example + * + * _.xorBy([2.1, 1.2], [2.3, 3.4], Math.floor); + * // => [1.2, 3.4] + * + * // The `_.property` iteratee shorthand. + * _.xorBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x'); + * // => [{ 'x': 2 }] + */ + var xorBy = baseRest(function(arrays) { + var iteratee = last(arrays); + if (isArrayLikeObject(iteratee)) { + iteratee = undefined; + } + return baseXor(arrayFilter(arrays, isArrayLikeObject), getIteratee(iteratee, 2)); + }); + + /** + * This method is like `_.xor` except that it accepts `comparator` which is + * invoked to compare elements of `arrays`. The order of result values is + * determined by the order they occur in the arrays. The comparator is invoked + * with two arguments: (arrVal, othVal). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns the new array of filtered values. + * @example + * + * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]; + * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }]; + * + * _.xorWith(objects, others, _.isEqual); + * // => [{ 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }] + */ + var xorWith = baseRest(function(arrays) { + var comparator = last(arrays); + comparator = typeof comparator == 'function' ? comparator : undefined; + return baseXor(arrayFilter(arrays, isArrayLikeObject), undefined, comparator); + }); + + /** + * Creates an array of grouped elements, the first of which contains the + * first elements of the given arrays, the second of which contains the + * second elements of the given arrays, and so on. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {...Array} [arrays] The arrays to process. + * @returns {Array} Returns the new array of grouped elements. + * @example + * + * _.zip(['a', 'b'], [1, 2], [true, false]); + * // => [['a', 1, true], ['b', 2, false]] + */ + var zip = baseRest(unzip); + + /** + * This method is like `_.fromPairs` except that it accepts two arrays, + * one of property identifiers and one of corresponding values. + * + * @static + * @memberOf _ + * @since 0.4.0 + * @category Array + * @param {Array} [props=[]] The property identifiers. + * @param {Array} [values=[]] The property values. + * @returns {Object} Returns the new object. + * @example + * + * _.zipObject(['a', 'b'], [1, 2]); + * // => { 'a': 1, 'b': 2 } + */ + function zipObject(props, values) { + return baseZipObject(props || [], values || [], assignValue); + } + + /** + * This method is like `_.zipObject` except that it supports property paths. + * + * @static + * @memberOf _ + * @since 4.1.0 + * @category Array + * @param {Array} [props=[]] The property identifiers. + * @param {Array} [values=[]] The property values. + * @returns {Object} Returns the new object. + * @example + * + * _.zipObjectDeep(['a.b[0].c', 'a.b[1].d'], [1, 2]); + * // => { 'a': { 'b': [{ 'c': 1 }, { 'd': 2 }] } } + */ + function zipObjectDeep(props, values) { + return baseZipObject(props || [], values || [], baseSet); + } + + /** + * This method is like `_.zip` except that it accepts `iteratee` to specify + * how grouped values should be combined. The iteratee is invoked with the + * elements of each group: (...group). + * + * @static + * @memberOf _ + * @since 3.8.0 + * @category Array + * @param {...Array} [arrays] The arrays to process. + * @param {Function} [iteratee=_.identity] The function to combine + * grouped values. + * @returns {Array} Returns the new array of grouped elements. + * @example + * + * _.zipWith([1, 2], [10, 20], [100, 200], function(a, b, c) { + * return a + b + c; + * }); + * // => [111, 222] + */ + var zipWith = baseRest(function(arrays) { + var length = arrays.length, + iteratee = length > 1 ? arrays[length - 1] : undefined; + + iteratee = typeof iteratee == 'function' ? (arrays.pop(), iteratee) : undefined; + return unzipWith(arrays, iteratee); + }); + + /*------------------------------------------------------------------------*/ + + /** + * Creates a `lodash` wrapper instance that wraps `value` with explicit method + * chain sequences enabled. The result of such sequences must be unwrapped + * with `_#value`. + * + * @static + * @memberOf _ + * @since 1.3.0 + * @category Seq + * @param {*} value The value to wrap. + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36 }, + * { 'user': 'fred', 'age': 40 }, + * { 'user': 'pebbles', 'age': 1 } + * ]; + * + * var youngest = _ + * .chain(users) + * .sortBy('age') + * .map(function(o) { + * return o.user + ' is ' + o.age; + * }) + * .head() + * .value(); + * // => 'pebbles is 1' + */ + function chain(value) { + var result = lodash(value); + result.__chain__ = true; + return result; + } + + /** + * This method invokes `interceptor` and returns `value`. The interceptor + * is invoked with one argument; (value). The purpose of this method is to + * "tap into" a method chain sequence in order to modify intermediate results. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Seq + * @param {*} value The value to provide to `interceptor`. + * @param {Function} interceptor The function to invoke. + * @returns {*} Returns `value`. + * @example + * + * _([1, 2, 3]) + * .tap(function(array) { + * // Mutate input array. + * array.pop(); + * }) + * .reverse() + * .value(); + * // => [2, 1] + */ + function tap(value, interceptor) { + interceptor(value); + return value; + } + + /** + * This method is like `_.tap` except that it returns the result of `interceptor`. + * The purpose of this method is to "pass thru" values replacing intermediate + * results in a method chain sequence. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Seq + * @param {*} value The value to provide to `interceptor`. + * @param {Function} interceptor The function to invoke. + * @returns {*} Returns the result of `interceptor`. + * @example + * + * _(' abc ') + * .chain() + * .trim() + * .thru(function(value) { + * return [value]; + * }) + * .value(); + * // => ['abc'] + */ + function thru(value, interceptor) { + return interceptor(value); + } + + /** + * This method is the wrapper version of `_.at`. + * + * @name at + * @memberOf _ + * @since 1.0.0 + * @category Seq + * @param {...(string|string[])} [paths] The property paths to pick. + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * var object = { 'a': [{ 'b': { 'c': 3 } }, 4] }; + * + * _(object).at(['a[0].b.c', 'a[1]']).value(); + * // => [3, 4] + */ + var wrapperAt = flatRest(function(paths) { + var length = paths.length, + start = length ? paths[0] : 0, + value = this.__wrapped__, + interceptor = function(object) { return baseAt(object, paths); }; + + if (length > 1 || this.__actions__.length || + !(value instanceof LazyWrapper) || !isIndex(start)) { + return this.thru(interceptor); + } + value = value.slice(start, +start + (length ? 1 : 0)); + value.__actions__.push({ + 'func': thru, + 'args': [interceptor], + 'thisArg': undefined + }); + return new LodashWrapper(value, this.__chain__).thru(function(array) { + if (length && !array.length) { + array.push(undefined); + } + return array; + }); + }); + + /** + * Creates a `lodash` wrapper instance with explicit method chain sequences enabled. + * + * @name chain + * @memberOf _ + * @since 0.1.0 + * @category Seq + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36 }, + * { 'user': 'fred', 'age': 40 } + * ]; + * + * // A sequence without explicit chaining. + * _(users).head(); + * // => { 'user': 'barney', 'age': 36 } + * + * // A sequence with explicit chaining. + * _(users) + * .chain() + * .head() + * .pick('user') + * .value(); + * // => { 'user': 'barney' } + */ + function wrapperChain() { + return chain(this); + } + + /** + * Executes the chain sequence and returns the wrapped result. + * + * @name commit + * @memberOf _ + * @since 3.2.0 + * @category Seq + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * var array = [1, 2]; + * var wrapped = _(array).push(3); + * + * console.log(array); + * // => [1, 2] + * + * wrapped = wrapped.commit(); + * console.log(array); + * // => [1, 2, 3] + * + * wrapped.last(); + * // => 3 + * + * console.log(array); + * // => [1, 2, 3] + */ + function wrapperCommit() { + return new LodashWrapper(this.value(), this.__chain__); + } + + /** + * Gets the next value on a wrapped object following the + * [iterator protocol](https://mdn.io/iteration_protocols#iterator). + * + * @name next + * @memberOf _ + * @since 4.0.0 + * @category Seq + * @returns {Object} Returns the next iterator value. + * @example + * + * var wrapped = _([1, 2]); + * + * wrapped.next(); + * // => { 'done': false, 'value': 1 } + * + * wrapped.next(); + * // => { 'done': false, 'value': 2 } + * + * wrapped.next(); + * // => { 'done': true, 'value': undefined } + */ + function wrapperNext() { + if (this.__values__ === undefined) { + this.__values__ = toArray(this.value()); + } + var done = this.__index__ >= this.__values__.length, + value = done ? undefined : this.__values__[this.__index__++]; + + return { 'done': done, 'value': value }; + } + + /** + * Enables the wrapper to be iterable. + * + * @name Symbol.iterator + * @memberOf _ + * @since 4.0.0 + * @category Seq + * @returns {Object} Returns the wrapper object. + * @example + * + * var wrapped = _([1, 2]); + * + * wrapped[Symbol.iterator]() === wrapped; + * // => true + * + * Array.from(wrapped); + * // => [1, 2] + */ + function wrapperToIterator() { + return this; + } + + /** + * Creates a clone of the chain sequence planting `value` as the wrapped value. + * + * @name plant + * @memberOf _ + * @since 3.2.0 + * @category Seq + * @param {*} value The value to plant. + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * function square(n) { + * return n * n; + * } + * + * var wrapped = _([1, 2]).map(square); + * var other = wrapped.plant([3, 4]); + * + * other.value(); + * // => [9, 16] + * + * wrapped.value(); + * // => [1, 4] + */ + function wrapperPlant(value) { + var result, + parent = this; + + while (parent instanceof baseLodash) { + var clone = wrapperClone(parent); + clone.__index__ = 0; + clone.__values__ = undefined; + if (result) { + previous.__wrapped__ = clone; + } else { + result = clone; + } + var previous = clone; + parent = parent.__wrapped__; + } + previous.__wrapped__ = value; + return result; + } + + /** + * This method is the wrapper version of `_.reverse`. + * + * **Note:** This method mutates the wrapped array. + * + * @name reverse + * @memberOf _ + * @since 0.1.0 + * @category Seq + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * var array = [1, 2, 3]; + * + * _(array).reverse().value() + * // => [3, 2, 1] + * + * console.log(array); + * // => [3, 2, 1] + */ + function wrapperReverse() { + var value = this.__wrapped__; + if (value instanceof LazyWrapper) { + var wrapped = value; + if (this.__actions__.length) { + wrapped = new LazyWrapper(this); + } + wrapped = wrapped.reverse(); + wrapped.__actions__.push({ + 'func': thru, + 'args': [reverse], + 'thisArg': undefined + }); + return new LodashWrapper(wrapped, this.__chain__); + } + return this.thru(reverse); + } + + /** + * Executes the chain sequence to resolve the unwrapped value. + * + * @name value + * @memberOf _ + * @since 0.1.0 + * @alias toJSON, valueOf + * @category Seq + * @returns {*} Returns the resolved unwrapped value. + * @example + * + * _([1, 2, 3]).value(); + * // => [1, 2, 3] + */ + function wrapperValue() { + return baseWrapperValue(this.__wrapped__, this.__actions__); + } + + /*------------------------------------------------------------------------*/ + + /** + * Creates an object composed of keys generated from the results of running + * each element of `collection` thru `iteratee`. The corresponding value of + * each key is the number of times the key was returned by `iteratee`. The + * iteratee is invoked with one argument: (value). + * + * @static + * @memberOf _ + * @since 0.5.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The iteratee to transform keys. + * @returns {Object} Returns the composed aggregate object. + * @example + * + * _.countBy([6.1, 4.2, 6.3], Math.floor); + * // => { '4': 1, '6': 2 } + * + * // The `_.property` iteratee shorthand. + * _.countBy(['one', 'two', 'three'], 'length'); + * // => { '3': 2, '5': 1 } + */ + var countBy = createAggregator(function(result, value, key) { + if (hasOwnProperty.call(result, key)) { + ++result[key]; + } else { + baseAssignValue(result, key, 1); + } + }); + + /** + * Checks if `predicate` returns truthy for **all** elements of `collection`. + * Iteration is stopped once `predicate` returns falsey. The predicate is + * invoked with three arguments: (value, index|key, collection). + * + * **Note:** This method returns `true` for + * [empty collections](https://en.wikipedia.org/wiki/Empty_set) because + * [everything is true](https://en.wikipedia.org/wiki/Vacuous_truth) of + * elements of empty collections. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {boolean} Returns `true` if all elements pass the predicate check, + * else `false`. + * @example + * + * _.every([true, 1, null, 'yes'], Boolean); + * // => false + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': false }, + * { 'user': 'fred', 'age': 40, 'active': false } + * ]; + * + * // The `_.matches` iteratee shorthand. + * _.every(users, { 'user': 'barney', 'active': false }); + * // => false + * + * // The `_.matchesProperty` iteratee shorthand. + * _.every(users, ['active', false]); + * // => true + * + * // The `_.property` iteratee shorthand. + * _.every(users, 'active'); + * // => false + */ + function every(collection, predicate, guard) { + var func = isArray(collection) ? arrayEvery : baseEvery; + if (guard && isIterateeCall(collection, predicate, guard)) { + predicate = undefined; + } + return func(collection, getIteratee(predicate, 3)); + } + + /** + * Iterates over elements of `collection`, returning an array of all elements + * `predicate` returns truthy for. The predicate is invoked with three + * arguments: (value, index|key, collection). + * + * **Note:** Unlike `_.remove`, this method returns a new array. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @returns {Array} Returns the new filtered array. + * @see _.reject + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': true }, + * { 'user': 'fred', 'age': 40, 'active': false } + * ]; + * + * _.filter(users, function(o) { return !o.active; }); + * // => objects for ['fred'] + * + * // The `_.matches` iteratee shorthand. + * _.filter(users, { 'age': 36, 'active': true }); + * // => objects for ['barney'] + * + * // The `_.matchesProperty` iteratee shorthand. + * _.filter(users, ['active', false]); + * // => objects for ['fred'] + * + * // The `_.property` iteratee shorthand. + * _.filter(users, 'active'); + * // => objects for ['barney'] + */ + function filter(collection, predicate) { + var func = isArray(collection) ? arrayFilter : baseFilter; + return func(collection, getIteratee(predicate, 3)); + } + + /** + * Iterates over elements of `collection`, returning the first element + * `predicate` returns truthy for. The predicate is invoked with three + * arguments: (value, index|key, collection). + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to inspect. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @param {number} [fromIndex=0] The index to search from. + * @returns {*} Returns the matched element, else `undefined`. + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': true }, + * { 'user': 'fred', 'age': 40, 'active': false }, + * { 'user': 'pebbles', 'age': 1, 'active': true } + * ]; + * + * _.find(users, function(o) { return o.age < 40; }); + * // => object for 'barney' + * + * // The `_.matches` iteratee shorthand. + * _.find(users, { 'age': 1, 'active': true }); + * // => object for 'pebbles' + * + * // The `_.matchesProperty` iteratee shorthand. + * _.find(users, ['active', false]); + * // => object for 'fred' + * + * // The `_.property` iteratee shorthand. + * _.find(users, 'active'); + * // => object for 'barney' + */ + var find = createFind(findIndex); + + /** + * This method is like `_.find` except that it iterates over elements of + * `collection` from right to left. + * + * @static + * @memberOf _ + * @since 2.0.0 + * @category Collection + * @param {Array|Object} collection The collection to inspect. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @param {number} [fromIndex=collection.length-1] The index to search from. + * @returns {*} Returns the matched element, else `undefined`. + * @example + * + * _.findLast([1, 2, 3, 4], function(n) { + * return n % 2 == 1; + * }); + * // => 3 + */ + var findLast = createFind(findLastIndex); + + /** + * Creates a flattened array of values by running each element in `collection` + * thru `iteratee` and flattening the mapped results. The iteratee is invoked + * with three arguments: (value, index|key, collection). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Array} Returns the new flattened array. + * @example + * + * function duplicate(n) { + * return [n, n]; + * } + * + * _.flatMap([1, 2], duplicate); + * // => [1, 1, 2, 2] + */ + function flatMap(collection, iteratee) { + return baseFlatten(map(collection, iteratee), 1); + } + + /** + * This method is like `_.flatMap` except that it recursively flattens the + * mapped results. + * + * @static + * @memberOf _ + * @since 4.7.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Array} Returns the new flattened array. + * @example + * + * function duplicate(n) { + * return [[[n, n]]]; + * } + * + * _.flatMapDeep([1, 2], duplicate); + * // => [1, 1, 2, 2] + */ + function flatMapDeep(collection, iteratee) { + return baseFlatten(map(collection, iteratee), INFINITY); + } + + /** + * This method is like `_.flatMap` except that it recursively flattens the + * mapped results up to `depth` times. + * + * @static + * @memberOf _ + * @since 4.7.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {number} [depth=1] The maximum recursion depth. + * @returns {Array} Returns the new flattened array. + * @example + * + * function duplicate(n) { + * return [[[n, n]]]; + * } + * + * _.flatMapDepth([1, 2], duplicate, 2); + * // => [[1, 1], [2, 2]] + */ + function flatMapDepth(collection, iteratee, depth) { + depth = depth === undefined ? 1 : toInteger(depth); + return baseFlatten(map(collection, iteratee), depth); + } + + /** + * Iterates over elements of `collection` and invokes `iteratee` for each element. + * The iteratee is invoked with three arguments: (value, index|key, collection). + * Iteratee functions may exit iteration early by explicitly returning `false`. + * + * **Note:** As with other "Collections" methods, objects with a "length" + * property are iterated like arrays. To avoid this behavior use `_.forIn` + * or `_.forOwn` for object iteration. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @alias each + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Array|Object} Returns `collection`. + * @see _.forEachRight + * @example + * + * _.forEach([1, 2], function(value) { + * console.log(value); + * }); + * // => Logs `1` then `2`. + * + * _.forEach({ 'a': 1, 'b': 2 }, function(value, key) { + * console.log(key); + * }); + * // => Logs 'a' then 'b' (iteration order is not guaranteed). + */ + function forEach(collection, iteratee) { + var func = isArray(collection) ? arrayEach : baseEach; + return func(collection, getIteratee(iteratee, 3)); + } + + /** + * This method is like `_.forEach` except that it iterates over elements of + * `collection` from right to left. + * + * @static + * @memberOf _ + * @since 2.0.0 + * @alias eachRight + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Array|Object} Returns `collection`. + * @see _.forEach + * @example + * + * _.forEachRight([1, 2], function(value) { + * console.log(value); + * }); + * // => Logs `2` then `1`. + */ + function forEachRight(collection, iteratee) { + var func = isArray(collection) ? arrayEachRight : baseEachRight; + return func(collection, getIteratee(iteratee, 3)); + } + + /** + * Creates an object composed of keys generated from the results of running + * each element of `collection` thru `iteratee`. The order of grouped values + * is determined by the order they occur in `collection`. The corresponding + * value of each key is an array of elements responsible for generating the + * key. The iteratee is invoked with one argument: (value). + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The iteratee to transform keys. + * @returns {Object} Returns the composed aggregate object. + * @example + * + * _.groupBy([6.1, 4.2, 6.3], Math.floor); + * // => { '4': [4.2], '6': [6.1, 6.3] } + * + * // The `_.property` iteratee shorthand. + * _.groupBy(['one', 'two', 'three'], 'length'); + * // => { '3': ['one', 'two'], '5': ['three'] } + */ + var groupBy = createAggregator(function(result, value, key) { + if (hasOwnProperty.call(result, key)) { + result[key].push(value); + } else { + baseAssignValue(result, key, [value]); + } + }); + + /** + * Checks if `value` is in `collection`. If `collection` is a string, it's + * checked for a substring of `value`, otherwise + * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * is used for equality comparisons. If `fromIndex` is negative, it's used as + * the offset from the end of `collection`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object|string} collection The collection to inspect. + * @param {*} value The value to search for. + * @param {number} [fromIndex=0] The index to search from. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.reduce`. + * @returns {boolean} Returns `true` if `value` is found, else `false`. + * @example + * + * _.includes([1, 2, 3], 1); + * // => true + * + * _.includes([1, 2, 3], 1, 2); + * // => false + * + * _.includes({ 'a': 1, 'b': 2 }, 1); + * // => true + * + * _.includes('abcd', 'bc'); + * // => true + */ + function includes(collection, value, fromIndex, guard) { + collection = isArrayLike(collection) ? collection : values(collection); + fromIndex = (fromIndex && !guard) ? toInteger(fromIndex) : 0; + + var length = collection.length; + if (fromIndex < 0) { + fromIndex = nativeMax(length + fromIndex, 0); + } + return isString(collection) + ? (fromIndex <= length && collection.indexOf(value, fromIndex) > -1) + : (!!length && baseIndexOf(collection, value, fromIndex) > -1); + } + + /** + * Invokes the method at `path` of each element in `collection`, returning + * an array of the results of each invoked method. Any additional arguments + * are provided to each invoked method. If `path` is a function, it's invoked + * for, and `this` bound to, each element in `collection`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Array|Function|string} path The path of the method to invoke or + * the function invoked per iteration. + * @param {...*} [args] The arguments to invoke each method with. + * @returns {Array} Returns the array of results. + * @example + * + * _.invokeMap([[5, 1, 7], [3, 2, 1]], 'sort'); + * // => [[1, 5, 7], [1, 2, 3]] + * + * _.invokeMap([123, 456], String.prototype.split, ''); + * // => [['1', '2', '3'], ['4', '5', '6']] + */ + var invokeMap = baseRest(function(collection, path, args) { + var index = -1, + isFunc = typeof path == 'function', + result = isArrayLike(collection) ? Array(collection.length) : []; + + baseEach(collection, function(value) { + result[++index] = isFunc ? apply(path, value, args) : baseInvoke(value, path, args); + }); + return result; + }); + + /** + * Creates an object composed of keys generated from the results of running + * each element of `collection` thru `iteratee`. The corresponding value of + * each key is the last element responsible for generating the key. The + * iteratee is invoked with one argument: (value). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The iteratee to transform keys. + * @returns {Object} Returns the composed aggregate object. + * @example + * + * var array = [ + * { 'dir': 'left', 'code': 97 }, + * { 'dir': 'right', 'code': 100 } + * ]; + * + * _.keyBy(array, function(o) { + * return String.fromCharCode(o.code); + * }); + * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } } + * + * _.keyBy(array, 'dir'); + * // => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } } + */ + var keyBy = createAggregator(function(result, value, key) { + baseAssignValue(result, key, value); + }); + + /** + * Creates an array of values by running each element in `collection` thru + * `iteratee`. The iteratee is invoked with three arguments: + * (value, index|key, collection). + * + * Many lodash methods are guarded to work as iteratees for methods like + * `_.every`, `_.filter`, `_.map`, `_.mapValues`, `_.reject`, and `_.some`. + * + * The guarded methods are: + * `ary`, `chunk`, `curry`, `curryRight`, `drop`, `dropRight`, `every`, + * `fill`, `invert`, `parseInt`, `random`, `range`, `rangeRight`, `repeat`, + * `sampleSize`, `slice`, `some`, `sortBy`, `split`, `take`, `takeRight`, + * `template`, `trim`, `trimEnd`, `trimStart`, and `words` + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Array} Returns the new mapped array. + * @example + * + * function square(n) { + * return n * n; + * } + * + * _.map([4, 8], square); + * // => [16, 64] + * + * _.map({ 'a': 4, 'b': 8 }, square); + * // => [16, 64] (iteration order is not guaranteed) + * + * var users = [ + * { 'user': 'barney' }, + * { 'user': 'fred' } + * ]; + * + * // The `_.property` iteratee shorthand. + * _.map(users, 'user'); + * // => ['barney', 'fred'] + */ + function map(collection, iteratee) { + var func = isArray(collection) ? arrayMap : baseMap; + return func(collection, getIteratee(iteratee, 3)); + } + + /** + * This method is like `_.sortBy` except that it allows specifying the sort + * orders of the iteratees to sort by. If `orders` is unspecified, all values + * are sorted in ascending order. Otherwise, specify an order of "desc" for + * descending or "asc" for ascending sort order of corresponding values. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Array[]|Function[]|Object[]|string[]} [iteratees=[_.identity]] + * The iteratees to sort by. + * @param {string[]} [orders] The sort orders of `iteratees`. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.reduce`. + * @returns {Array} Returns the new sorted array. + * @example + * + * var users = [ + * { 'user': 'fred', 'age': 48 }, + * { 'user': 'barney', 'age': 34 }, + * { 'user': 'fred', 'age': 40 }, + * { 'user': 'barney', 'age': 36 } + * ]; + * + * // Sort by `user` in ascending order and by `age` in descending order. + * _.orderBy(users, ['user', 'age'], ['asc', 'desc']); + * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]] + */ + function orderBy(collection, iteratees, orders, guard) { + if (collection == null) { + return []; + } + if (!isArray(iteratees)) { + iteratees = iteratees == null ? [] : [iteratees]; + } + orders = guard ? undefined : orders; + if (!isArray(orders)) { + orders = orders == null ? [] : [orders]; + } + return baseOrderBy(collection, iteratees, orders); + } + + /** + * Creates an array of elements split into two groups, the first of which + * contains elements `predicate` returns truthy for, the second of which + * contains elements `predicate` returns falsey for. The predicate is + * invoked with one argument: (value). + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @returns {Array} Returns the array of grouped elements. + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': false }, + * { 'user': 'fred', 'age': 40, 'active': true }, + * { 'user': 'pebbles', 'age': 1, 'active': false } + * ]; + * + * _.partition(users, function(o) { return o.active; }); + * // => objects for [['fred'], ['barney', 'pebbles']] + * + * // The `_.matches` iteratee shorthand. + * _.partition(users, { 'age': 1, 'active': false }); + * // => objects for [['pebbles'], ['barney', 'fred']] + * + * // The `_.matchesProperty` iteratee shorthand. + * _.partition(users, ['active', false]); + * // => objects for [['barney', 'pebbles'], ['fred']] + * + * // The `_.property` iteratee shorthand. + * _.partition(users, 'active'); + * // => objects for [['fred'], ['barney', 'pebbles']] + */ + var partition = createAggregator(function(result, value, key) { + result[key ? 0 : 1].push(value); + }, function() { return [[], []]; }); + + /** + * Reduces `collection` to a value which is the accumulated result of running + * each element in `collection` thru `iteratee`, where each successive + * invocation is supplied the return value of the previous. If `accumulator` + * is not given, the first element of `collection` is used as the initial + * value. The iteratee is invoked with four arguments: + * (accumulator, value, index|key, collection). + * + * Many lodash methods are guarded to work as iteratees for methods like + * `_.reduce`, `_.reduceRight`, and `_.transform`. + * + * The guarded methods are: + * `assign`, `defaults`, `defaultsDeep`, `includes`, `merge`, `orderBy`, + * and `sortBy` + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {*} [accumulator] The initial value. + * @returns {*} Returns the accumulated value. + * @see _.reduceRight + * @example + * + * _.reduce([1, 2], function(sum, n) { + * return sum + n; + * }, 0); + * // => 3 + * + * _.reduce({ 'a': 1, 'b': 2, 'c': 1 }, function(result, value, key) { + * (result[value] || (result[value] = [])).push(key); + * return result; + * }, {}); + * // => { '1': ['a', 'c'], '2': ['b'] } (iteration order is not guaranteed) + */ + function reduce(collection, iteratee, accumulator) { + var func = isArray(collection) ? arrayReduce : baseReduce, + initAccum = arguments.length < 3; + + return func(collection, getIteratee(iteratee, 4), accumulator, initAccum, baseEach); + } + + /** + * This method is like `_.reduce` except that it iterates over elements of + * `collection` from right to left. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {*} [accumulator] The initial value. + * @returns {*} Returns the accumulated value. + * @see _.reduce + * @example + * + * var array = [[0, 1], [2, 3], [4, 5]]; + * + * _.reduceRight(array, function(flattened, other) { + * return flattened.concat(other); + * }, []); + * // => [4, 5, 2, 3, 0, 1] + */ + function reduceRight(collection, iteratee, accumulator) { + var func = isArray(collection) ? arrayReduceRight : baseReduce, + initAccum = arguments.length < 3; + + return func(collection, getIteratee(iteratee, 4), accumulator, initAccum, baseEachRight); + } + + /** + * The opposite of `_.filter`; this method returns the elements of `collection` + * that `predicate` does **not** return truthy for. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @returns {Array} Returns the new filtered array. + * @see _.filter + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': false }, + * { 'user': 'fred', 'age': 40, 'active': true } + * ]; + * + * _.reject(users, function(o) { return !o.active; }); + * // => objects for ['fred'] + * + * // The `_.matches` iteratee shorthand. + * _.reject(users, { 'age': 40, 'active': true }); + * // => objects for ['barney'] + * + * // The `_.matchesProperty` iteratee shorthand. + * _.reject(users, ['active', false]); + * // => objects for ['fred'] + * + * // The `_.property` iteratee shorthand. + * _.reject(users, 'active'); + * // => objects for ['barney'] + */ + function reject(collection, predicate) { + var func = isArray(collection) ? arrayFilter : baseFilter; + return func(collection, negate(getIteratee(predicate, 3))); + } + + /** + * Gets a random element from `collection`. + * + * @static + * @memberOf _ + * @since 2.0.0 + * @category Collection + * @param {Array|Object} collection The collection to sample. + * @returns {*} Returns the random element. + * @example + * + * _.sample([1, 2, 3, 4]); + * // => 2 + */ + function sample(collection) { + var func = isArray(collection) ? arraySample : baseSample; + return func(collection); + } + + /** + * Gets `n` random elements at unique keys from `collection` up to the + * size of `collection`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Collection + * @param {Array|Object} collection The collection to sample. + * @param {number} [n=1] The number of elements to sample. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {Array} Returns the random elements. + * @example + * + * _.sampleSize([1, 2, 3], 2); + * // => [3, 1] + * + * _.sampleSize([1, 2, 3], 4); + * // => [2, 3, 1] + */ + function sampleSize(collection, n, guard) { + if ((guard ? isIterateeCall(collection, n, guard) : n === undefined)) { + n = 1; + } else { + n = toInteger(n); + } + var func = isArray(collection) ? arraySampleSize : baseSampleSize; + return func(collection, n); + } + + /** + * Creates an array of shuffled values, using a version of the + * [Fisher-Yates shuffle](https://en.wikipedia.org/wiki/Fisher-Yates_shuffle). + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to shuffle. + * @returns {Array} Returns the new shuffled array. + * @example + * + * _.shuffle([1, 2, 3, 4]); + * // => [4, 1, 3, 2] + */ + function shuffle(collection) { + var func = isArray(collection) ? arrayShuffle : baseShuffle; + return func(collection); + } + + /** + * Gets the size of `collection` by returning its length for array-like + * values or the number of own enumerable string keyed properties for objects. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object|string} collection The collection to inspect. + * @returns {number} Returns the collection size. + * @example + * + * _.size([1, 2, 3]); + * // => 3 + * + * _.size({ 'a': 1, 'b': 2 }); + * // => 2 + * + * _.size('pebbles'); + * // => 7 + */ + function size(collection) { + if (collection == null) { + return 0; + } + if (isArrayLike(collection)) { + return isString(collection) ? stringSize(collection) : collection.length; + } + var tag = getTag(collection); + if (tag == mapTag || tag == setTag) { + return collection.size; + } + return baseKeys(collection).length; + } + + /** + * Checks if `predicate` returns truthy for **any** element of `collection`. + * Iteration is stopped once `predicate` returns truthy. The predicate is + * invoked with three arguments: (value, index|key, collection). + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {boolean} Returns `true` if any element passes the predicate check, + * else `false`. + * @example + * + * _.some([null, 0, 'yes', false], Boolean); + * // => true + * + * var users = [ + * { 'user': 'barney', 'active': true }, + * { 'user': 'fred', 'active': false } + * ]; + * + * // The `_.matches` iteratee shorthand. + * _.some(users, { 'user': 'barney', 'active': false }); + * // => false + * + * // The `_.matchesProperty` iteratee shorthand. + * _.some(users, ['active', false]); + * // => true + * + * // The `_.property` iteratee shorthand. + * _.some(users, 'active'); + * // => true + */ + function some(collection, predicate, guard) { + var func = isArray(collection) ? arraySome : baseSome; + if (guard && isIterateeCall(collection, predicate, guard)) { + predicate = undefined; + } + return func(collection, getIteratee(predicate, 3)); + } + + /** + * Creates an array of elements, sorted in ascending order by the results of + * running each element in a collection thru each iteratee. This method + * performs a stable sort, that is, it preserves the original sort order of + * equal elements. The iteratees are invoked with one argument: (value). + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {...(Function|Function[])} [iteratees=[_.identity]] + * The iteratees to sort by. + * @returns {Array} Returns the new sorted array. + * @example + * + * var users = [ + * { 'user': 'fred', 'age': 48 }, + * { 'user': 'barney', 'age': 36 }, + * { 'user': 'fred', 'age': 40 }, + * { 'user': 'barney', 'age': 34 } + * ]; + * + * _.sortBy(users, [function(o) { return o.user; }]); + * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]] + * + * _.sortBy(users, ['user', 'age']); + * // => objects for [['barney', 34], ['barney', 36], ['fred', 40], ['fred', 48]] + */ + var sortBy = baseRest(function(collection, iteratees) { + if (collection == null) { + return []; + } + var length = iteratees.length; + if (length > 1 && isIterateeCall(collection, iteratees[0], iteratees[1])) { + iteratees = []; + } else if (length > 2 && isIterateeCall(iteratees[0], iteratees[1], iteratees[2])) { + iteratees = [iteratees[0]]; + } + return baseOrderBy(collection, baseFlatten(iteratees, 1), []); + }); + + /*------------------------------------------------------------------------*/ + + /** + * Gets the timestamp of the number of milliseconds that have elapsed since + * the Unix epoch (1 January 1970 00:00:00 UTC). + * + * @static + * @memberOf _ + * @since 2.4.0 + * @category Date + * @returns {number} Returns the timestamp. + * @example + * + * _.defer(function(stamp) { + * console.log(_.now() - stamp); + * }, _.now()); + * // => Logs the number of milliseconds it took for the deferred invocation. + */ + var now = ctxNow || function() { + return root.Date.now(); + }; + + /*------------------------------------------------------------------------*/ + + /** + * The opposite of `_.before`; this method creates a function that invokes + * `func` once it's called `n` or more times. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {number} n The number of calls before `func` is invoked. + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new restricted function. + * @example + * + * var saves = ['profile', 'settings']; + * + * var done = _.after(saves.length, function() { + * console.log('done saving!'); + * }); + * + * _.forEach(saves, function(type) { + * asyncSave({ 'type': type, 'complete': done }); + * }); + * // => Logs 'done saving!' after the two async saves have completed. + */ + function after(n, func) { + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + n = toInteger(n); + return function() { + if (--n < 1) { + return func.apply(this, arguments); + } + }; + } + + /** + * Creates a function that invokes `func`, with up to `n` arguments, + * ignoring any additional arguments. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Function + * @param {Function} func The function to cap arguments for. + * @param {number} [n=func.length] The arity cap. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {Function} Returns the new capped function. + * @example + * + * _.map(['6', '8', '10'], _.ary(parseInt, 1)); + * // => [6, 8, 10] + */ + function ary(func, n, guard) { + n = guard ? undefined : n; + n = (func && n == null) ? func.length : n; + return createWrap(func, WRAP_ARY_FLAG, undefined, undefined, undefined, undefined, n); + } + + /** + * Creates a function that invokes `func`, with the `this` binding and arguments + * of the created function, while it's called less than `n` times. Subsequent + * calls to the created function return the result of the last `func` invocation. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Function + * @param {number} n The number of calls at which `func` is no longer invoked. + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new restricted function. + * @example + * + * jQuery(element).on('click', _.before(5, addContactToList)); + * // => Allows adding up to 4 contacts to the list. + */ + function before(n, func) { + var result; + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + n = toInteger(n); + return function() { + if (--n > 0) { + result = func.apply(this, arguments); + } + if (n <= 1) { + func = undefined; + } + return result; + }; + } + + /** + * Creates a function that invokes `func` with the `this` binding of `thisArg` + * and `partials` prepended to the arguments it receives. + * + * The `_.bind.placeholder` value, which defaults to `_` in monolithic builds, + * may be used as a placeholder for partially applied arguments. + * + * **Note:** Unlike native `Function#bind`, this method doesn't set the "length" + * property of bound functions. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {Function} func The function to bind. + * @param {*} thisArg The `this` binding of `func`. + * @param {...*} [partials] The arguments to be partially applied. + * @returns {Function} Returns the new bound function. + * @example + * + * function greet(greeting, punctuation) { + * return greeting + ' ' + this.user + punctuation; + * } + * + * var object = { 'user': 'fred' }; + * + * var bound = _.bind(greet, object, 'hi'); + * bound('!'); + * // => 'hi fred!' + * + * // Bound with placeholders. + * var bound = _.bind(greet, object, _, '!'); + * bound('hi'); + * // => 'hi fred!' + */ + var bind = baseRest(function(func, thisArg, partials) { + var bitmask = WRAP_BIND_FLAG; + if (partials.length) { + var holders = replaceHolders(partials, getHolder(bind)); + bitmask |= WRAP_PARTIAL_FLAG; + } + return createWrap(func, bitmask, thisArg, partials, holders); + }); + + /** + * Creates a function that invokes the method at `object[key]` with `partials` + * prepended to the arguments it receives. + * + * This method differs from `_.bind` by allowing bound functions to reference + * methods that may be redefined or don't yet exist. See + * [Peter Michaux's article](http://peter.michaux.ca/articles/lazy-function-definition-pattern) + * for more details. + * + * The `_.bindKey.placeholder` value, which defaults to `_` in monolithic + * builds, may be used as a placeholder for partially applied arguments. + * + * @static + * @memberOf _ + * @since 0.10.0 + * @category Function + * @param {Object} object The object to invoke the method on. + * @param {string} key The key of the method. + * @param {...*} [partials] The arguments to be partially applied. + * @returns {Function} Returns the new bound function. + * @example + * + * var object = { + * 'user': 'fred', + * 'greet': function(greeting, punctuation) { + * return greeting + ' ' + this.user + punctuation; + * } + * }; + * + * var bound = _.bindKey(object, 'greet', 'hi'); + * bound('!'); + * // => 'hi fred!' + * + * object.greet = function(greeting, punctuation) { + * return greeting + 'ya ' + this.user + punctuation; + * }; + * + * bound('!'); + * // => 'hiya fred!' + * + * // Bound with placeholders. + * var bound = _.bindKey(object, 'greet', _, '!'); + * bound('hi'); + * // => 'hiya fred!' + */ + var bindKey = baseRest(function(object, key, partials) { + var bitmask = WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG; + if (partials.length) { + var holders = replaceHolders(partials, getHolder(bindKey)); + bitmask |= WRAP_PARTIAL_FLAG; + } + return createWrap(key, bitmask, object, partials, holders); + }); + + /** + * Creates a function that accepts arguments of `func` and either invokes + * `func` returning its result, if at least `arity` number of arguments have + * been provided, or returns a function that accepts the remaining `func` + * arguments, and so on. The arity of `func` may be specified if `func.length` + * is not sufficient. + * + * The `_.curry.placeholder` value, which defaults to `_` in monolithic builds, + * may be used as a placeholder for provided arguments. + * + * **Note:** This method doesn't set the "length" property of curried functions. + * + * @static + * @memberOf _ + * @since 2.0.0 + * @category Function + * @param {Function} func The function to curry. + * @param {number} [arity=func.length] The arity of `func`. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {Function} Returns the new curried function. + * @example + * + * var abc = function(a, b, c) { + * return [a, b, c]; + * }; + * + * var curried = _.curry(abc); + * + * curried(1)(2)(3); + * // => [1, 2, 3] + * + * curried(1, 2)(3); + * // => [1, 2, 3] + * + * curried(1, 2, 3); + * // => [1, 2, 3] + * + * // Curried with placeholders. + * curried(1)(_, 3)(2); + * // => [1, 2, 3] + */ + function curry(func, arity, guard) { + arity = guard ? undefined : arity; + var result = createWrap(func, WRAP_CURRY_FLAG, undefined, undefined, undefined, undefined, undefined, arity); + result.placeholder = curry.placeholder; + return result; + } + + /** + * This method is like `_.curry` except that arguments are applied to `func` + * in the manner of `_.partialRight` instead of `_.partial`. + * + * The `_.curryRight.placeholder` value, which defaults to `_` in monolithic + * builds, may be used as a placeholder for provided arguments. + * + * **Note:** This method doesn't set the "length" property of curried functions. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Function + * @param {Function} func The function to curry. + * @param {number} [arity=func.length] The arity of `func`. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {Function} Returns the new curried function. + * @example + * + * var abc = function(a, b, c) { + * return [a, b, c]; + * }; + * + * var curried = _.curryRight(abc); + * + * curried(3)(2)(1); + * // => [1, 2, 3] + * + * curried(2, 3)(1); + * // => [1, 2, 3] + * + * curried(1, 2, 3); + * // => [1, 2, 3] + * + * // Curried with placeholders. + * curried(3)(1, _)(2); + * // => [1, 2, 3] + */ + function curryRight(func, arity, guard) { + arity = guard ? undefined : arity; + var result = createWrap(func, WRAP_CURRY_RIGHT_FLAG, undefined, undefined, undefined, undefined, undefined, arity); + result.placeholder = curryRight.placeholder; + return result; + } + + /** + * Creates a debounced function that delays invoking `func` until after `wait` + * milliseconds have elapsed since the last time the debounced function was + * invoked. The debounced function comes with a `cancel` method to cancel + * delayed `func` invocations and a `flush` method to immediately invoke them. + * Provide `options` to indicate whether `func` should be invoked on the + * leading and/or trailing edge of the `wait` timeout. The `func` is invoked + * with the last arguments provided to the debounced function. Subsequent + * calls to the debounced function return the result of the last `func` + * invocation. + * + * **Note:** If `leading` and `trailing` options are `true`, `func` is + * invoked on the trailing edge of the timeout only if the debounced function + * is invoked more than once during the `wait` timeout. + * + * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred + * until to the next tick, similar to `setTimeout` with a timeout of `0`. + * + * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/) + * for details over the differences between `_.debounce` and `_.throttle`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {Function} func The function to debounce. + * @param {number} [wait=0] The number of milliseconds to delay. + * @param {Object} [options={}] The options object. + * @param {boolean} [options.leading=false] + * Specify invoking on the leading edge of the timeout. + * @param {number} [options.maxWait] + * The maximum time `func` is allowed to be delayed before it's invoked. + * @param {boolean} [options.trailing=true] + * Specify invoking on the trailing edge of the timeout. + * @returns {Function} Returns the new debounced function. + * @example + * + * // Avoid costly calculations while the window size is in flux. + * jQuery(window).on('resize', _.debounce(calculateLayout, 150)); + * + * // Invoke `sendMail` when clicked, debouncing subsequent calls. + * jQuery(element).on('click', _.debounce(sendMail, 300, { + * 'leading': true, + * 'trailing': false + * })); + * + * // Ensure `batchLog` is invoked once after 1 second of debounced calls. + * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 }); + * var source = new EventSource('/stream'); + * jQuery(source).on('message', debounced); + * + * // Cancel the trailing debounced invocation. + * jQuery(window).on('popstate', debounced.cancel); + */ + function debounce(func, wait, options) { + var lastArgs, + lastThis, + maxWait, + result, + timerId, + lastCallTime, + lastInvokeTime = 0, + leading = false, + maxing = false, + trailing = true; + + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + wait = toNumber(wait) || 0; + if (isObject(options)) { + leading = !!options.leading; + maxing = 'maxWait' in options; + maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait; + trailing = 'trailing' in options ? !!options.trailing : trailing; + } + + function invokeFunc(time) { + var args = lastArgs, + thisArg = lastThis; + + lastArgs = lastThis = undefined; + lastInvokeTime = time; + result = func.apply(thisArg, args); + return result; + } + + function leadingEdge(time) { + // Reset any `maxWait` timer. + lastInvokeTime = time; + // Start the timer for the trailing edge. + timerId = setTimeout(timerExpired, wait); + // Invoke the leading edge. + return leading ? invokeFunc(time) : result; + } + + function remainingWait(time) { + var timeSinceLastCall = time - lastCallTime, + timeSinceLastInvoke = time - lastInvokeTime, + result = wait - timeSinceLastCall; + + return maxing ? nativeMin(result, maxWait - timeSinceLastInvoke) : result; + } + + function shouldInvoke(time) { + var timeSinceLastCall = time - lastCallTime, + timeSinceLastInvoke = time - lastInvokeTime; + + // Either this is the first call, activity has stopped and we're at the + // trailing edge, the system time has gone backwards and we're treating + // it as the trailing edge, or we've hit the `maxWait` limit. + return (lastCallTime === undefined || (timeSinceLastCall >= wait) || + (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait)); + } + + function timerExpired() { + var time = now(); + if (shouldInvoke(time)) { + return trailingEdge(time); + } + // Restart the timer. + timerId = setTimeout(timerExpired, remainingWait(time)); + } + + function trailingEdge(time) { + timerId = undefined; + + // Only invoke if we have `lastArgs` which means `func` has been + // debounced at least once. + if (trailing && lastArgs) { + return invokeFunc(time); + } + lastArgs = lastThis = undefined; + return result; + } + + function cancel() { + if (timerId !== undefined) { + clearTimeout(timerId); + } + lastInvokeTime = 0; + lastArgs = lastCallTime = lastThis = timerId = undefined; + } + + function flush() { + return timerId === undefined ? result : trailingEdge(now()); + } + + function debounced() { + var time = now(), + isInvoking = shouldInvoke(time); + + lastArgs = arguments; + lastThis = this; + lastCallTime = time; + + if (isInvoking) { + if (timerId === undefined) { + return leadingEdge(lastCallTime); + } + if (maxing) { + // Handle invocations in a tight loop. + timerId = setTimeout(timerExpired, wait); + return invokeFunc(lastCallTime); + } + } + if (timerId === undefined) { + timerId = setTimeout(timerExpired, wait); + } + return result; + } + debounced.cancel = cancel; + debounced.flush = flush; + return debounced; + } + + /** + * Defers invoking the `func` until the current call stack has cleared. Any + * additional arguments are provided to `func` when it's invoked. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {Function} func The function to defer. + * @param {...*} [args] The arguments to invoke `func` with. + * @returns {number} Returns the timer id. + * @example + * + * _.defer(function(text) { + * console.log(text); + * }, 'deferred'); + * // => Logs 'deferred' after one millisecond. + */ + var defer = baseRest(function(func, args) { + return baseDelay(func, 1, args); + }); + + /** + * Invokes `func` after `wait` milliseconds. Any additional arguments are + * provided to `func` when it's invoked. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {Function} func The function to delay. + * @param {number} wait The number of milliseconds to delay invocation. + * @param {...*} [args] The arguments to invoke `func` with. + * @returns {number} Returns the timer id. + * @example + * + * _.delay(function(text) { + * console.log(text); + * }, 1000, 'later'); + * // => Logs 'later' after one second. + */ + var delay = baseRest(function(func, wait, args) { + return baseDelay(func, toNumber(wait) || 0, args); + }); + + /** + * Creates a function that invokes `func` with arguments reversed. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Function + * @param {Function} func The function to flip arguments for. + * @returns {Function} Returns the new flipped function. + * @example + * + * var flipped = _.flip(function() { + * return _.toArray(arguments); + * }); + * + * flipped('a', 'b', 'c', 'd'); + * // => ['d', 'c', 'b', 'a'] + */ + function flip(func) { + return createWrap(func, WRAP_FLIP_FLAG); + } + + /** + * Creates a function that memoizes the result of `func`. If `resolver` is + * provided, it determines the cache key for storing the result based on the + * arguments provided to the memoized function. By default, the first argument + * provided to the memoized function is used as the map cache key. The `func` + * is invoked with the `this` binding of the memoized function. + * + * **Note:** The cache is exposed as the `cache` property on the memoized + * function. Its creation may be customized by replacing the `_.memoize.Cache` + * constructor with one whose instances implement the + * [`Map`](http://ecma-international.org/ecma-262/7.0/#sec-properties-of-the-map-prototype-object) + * method interface of `clear`, `delete`, `get`, `has`, and `set`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {Function} func The function to have its output memoized. + * @param {Function} [resolver] The function to resolve the cache key. + * @returns {Function} Returns the new memoized function. + * @example + * + * var object = { 'a': 1, 'b': 2 }; + * var other = { 'c': 3, 'd': 4 }; + * + * var values = _.memoize(_.values); + * values(object); + * // => [1, 2] + * + * values(other); + * // => [3, 4] + * + * object.a = 2; + * values(object); + * // => [1, 2] + * + * // Modify the result cache. + * values.cache.set(object, ['a', 'b']); + * values(object); + * // => ['a', 'b'] + * + * // Replace `_.memoize.Cache`. + * _.memoize.Cache = WeakMap; + */ + function memoize(func, resolver) { + if (typeof func != 'function' || (resolver != null && typeof resolver != 'function')) { + throw new TypeError(FUNC_ERROR_TEXT); + } + var memoized = function() { + var args = arguments, + key = resolver ? resolver.apply(this, args) : args[0], + cache = memoized.cache; + + if (cache.has(key)) { + return cache.get(key); + } + var result = func.apply(this, args); + memoized.cache = cache.set(key, result) || cache; + return result; + }; + memoized.cache = new (memoize.Cache || MapCache); + return memoized; + } + + // Expose `MapCache`. + memoize.Cache = MapCache; + + /** + * Creates a function that negates the result of the predicate `func`. The + * `func` predicate is invoked with the `this` binding and arguments of the + * created function. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Function + * @param {Function} predicate The predicate to negate. + * @returns {Function} Returns the new negated function. + * @example + * + * function isEven(n) { + * return n % 2 == 0; + * } + * + * _.filter([1, 2, 3, 4, 5, 6], _.negate(isEven)); + * // => [1, 3, 5] + */ + function negate(predicate) { + if (typeof predicate != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + return function() { + var args = arguments; + switch (args.length) { + case 0: return !predicate.call(this); + case 1: return !predicate.call(this, args[0]); + case 2: return !predicate.call(this, args[0], args[1]); + case 3: return !predicate.call(this, args[0], args[1], args[2]); + } + return !predicate.apply(this, args); + }; + } + + /** + * Creates a function that is restricted to invoking `func` once. Repeat calls + * to the function return the value of the first invocation. The `func` is + * invoked with the `this` binding and arguments of the created function. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new restricted function. + * @example + * + * var initialize = _.once(createApplication); + * initialize(); + * initialize(); + * // => `createApplication` is invoked once + */ + function once(func) { + return before(2, func); + } + + /** + * Creates a function that invokes `func` with its arguments transformed. + * + * @static + * @since 4.0.0 + * @memberOf _ + * @category Function + * @param {Function} func The function to wrap. + * @param {...(Function|Function[])} [transforms=[_.identity]] + * The argument transforms. + * @returns {Function} Returns the new function. + * @example + * + * function doubled(n) { + * return n * 2; + * } + * + * function square(n) { + * return n * n; + * } + * + * var func = _.overArgs(function(x, y) { + * return [x, y]; + * }, [square, doubled]); + * + * func(9, 3); + * // => [81, 6] + * + * func(10, 5); + * // => [100, 10] + */ + var overArgs = castRest(function(func, transforms) { + transforms = (transforms.length == 1 && isArray(transforms[0])) + ? arrayMap(transforms[0], baseUnary(getIteratee())) + : arrayMap(baseFlatten(transforms, 1), baseUnary(getIteratee())); + + var funcsLength = transforms.length; + return baseRest(function(args) { + var index = -1, + length = nativeMin(args.length, funcsLength); + + while (++index < length) { + args[index] = transforms[index].call(this, args[index]); + } + return apply(func, this, args); + }); + }); + + /** + * Creates a function that invokes `func` with `partials` prepended to the + * arguments it receives. This method is like `_.bind` except it does **not** + * alter the `this` binding. + * + * The `_.partial.placeholder` value, which defaults to `_` in monolithic + * builds, may be used as a placeholder for partially applied arguments. + * + * **Note:** This method doesn't set the "length" property of partially + * applied functions. + * + * @static + * @memberOf _ + * @since 0.2.0 + * @category Function + * @param {Function} func The function to partially apply arguments to. + * @param {...*} [partials] The arguments to be partially applied. + * @returns {Function} Returns the new partially applied function. + * @example + * + * function greet(greeting, name) { + * return greeting + ' ' + name; + * } + * + * var sayHelloTo = _.partial(greet, 'hello'); + * sayHelloTo('fred'); + * // => 'hello fred' + * + * // Partially applied with placeholders. + * var greetFred = _.partial(greet, _, 'fred'); + * greetFred('hi'); + * // => 'hi fred' + */ + var partial = baseRest(function(func, partials) { + var holders = replaceHolders(partials, getHolder(partial)); + return createWrap(func, WRAP_PARTIAL_FLAG, undefined, partials, holders); + }); + + /** + * This method is like `_.partial` except that partially applied arguments + * are appended to the arguments it receives. + * + * The `_.partialRight.placeholder` value, which defaults to `_` in monolithic + * builds, may be used as a placeholder for partially applied arguments. + * + * **Note:** This method doesn't set the "length" property of partially + * applied functions. + * + * @static + * @memberOf _ + * @since 1.0.0 + * @category Function + * @param {Function} func The function to partially apply arguments to. + * @param {...*} [partials] The arguments to be partially applied. + * @returns {Function} Returns the new partially applied function. + * @example + * + * function greet(greeting, name) { + * return greeting + ' ' + name; + * } + * + * var greetFred = _.partialRight(greet, 'fred'); + * greetFred('hi'); + * // => 'hi fred' + * + * // Partially applied with placeholders. + * var sayHelloTo = _.partialRight(greet, 'hello', _); + * sayHelloTo('fred'); + * // => 'hello fred' + */ + var partialRight = baseRest(function(func, partials) { + var holders = replaceHolders(partials, getHolder(partialRight)); + return createWrap(func, WRAP_PARTIAL_RIGHT_FLAG, undefined, partials, holders); + }); + + /** + * Creates a function that invokes `func` with arguments arranged according + * to the specified `indexes` where the argument value at the first index is + * provided as the first argument, the argument value at the second index is + * provided as the second argument, and so on. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Function + * @param {Function} func The function to rearrange arguments for. + * @param {...(number|number[])} indexes The arranged argument indexes. + * @returns {Function} Returns the new function. + * @example + * + * var rearged = _.rearg(function(a, b, c) { + * return [a, b, c]; + * }, [2, 0, 1]); + * + * rearged('b', 'c', 'a') + * // => ['a', 'b', 'c'] + */ + var rearg = flatRest(function(func, indexes) { + return createWrap(func, WRAP_REARG_FLAG, undefined, undefined, undefined, indexes); + }); + + /** + * Creates a function that invokes `func` with the `this` binding of the + * created function and arguments from `start` and beyond provided as + * an array. + * + * **Note:** This method is based on the + * [rest parameter](https://mdn.io/rest_parameters). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Function + * @param {Function} func The function to apply a rest parameter to. + * @param {number} [start=func.length-1] The start position of the rest parameter. + * @returns {Function} Returns the new function. + * @example + * + * var say = _.rest(function(what, names) { + * return what + ' ' + _.initial(names).join(', ') + + * (_.size(names) > 1 ? ', & ' : '') + _.last(names); + * }); + * + * say('hello', 'fred', 'barney', 'pebbles'); + * // => 'hello fred, barney, & pebbles' + */ + function rest(func, start) { + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + start = start === undefined ? start : toInteger(start); + return baseRest(func, start); + } + + /** + * Creates a function that invokes `func` with the `this` binding of the + * create function and an array of arguments much like + * [`Function#apply`](http://www.ecma-international.org/ecma-262/7.0/#sec-function.prototype.apply). + * + * **Note:** This method is based on the + * [spread operator](https://mdn.io/spread_operator). + * + * @static + * @memberOf _ + * @since 3.2.0 + * @category Function + * @param {Function} func The function to spread arguments over. + * @param {number} [start=0] The start position of the spread. + * @returns {Function} Returns the new function. + * @example + * + * var say = _.spread(function(who, what) { + * return who + ' says ' + what; + * }); + * + * say(['fred', 'hello']); + * // => 'fred says hello' + * + * var numbers = Promise.all([ + * Promise.resolve(40), + * Promise.resolve(36) + * ]); + * + * numbers.then(_.spread(function(x, y) { + * return x + y; + * })); + * // => a Promise of 76 + */ + function spread(func, start) { + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + start = start == null ? 0 : nativeMax(toInteger(start), 0); + return baseRest(function(args) { + var array = args[start], + otherArgs = castSlice(args, 0, start); + + if (array) { + arrayPush(otherArgs, array); + } + return apply(func, this, otherArgs); + }); + } + + /** + * Creates a throttled function that only invokes `func` at most once per + * every `wait` milliseconds. The throttled function comes with a `cancel` + * method to cancel delayed `func` invocations and a `flush` method to + * immediately invoke them. Provide `options` to indicate whether `func` + * should be invoked on the leading and/or trailing edge of the `wait` + * timeout. The `func` is invoked with the last arguments provided to the + * throttled function. Subsequent calls to the throttled function return the + * result of the last `func` invocation. + * + * **Note:** If `leading` and `trailing` options are `true`, `func` is + * invoked on the trailing edge of the timeout only if the throttled function + * is invoked more than once during the `wait` timeout. + * + * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred + * until to the next tick, similar to `setTimeout` with a timeout of `0`. + * + * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/) + * for details over the differences between `_.throttle` and `_.debounce`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {Function} func The function to throttle. + * @param {number} [wait=0] The number of milliseconds to throttle invocations to. + * @param {Object} [options={}] The options object. + * @param {boolean} [options.leading=true] + * Specify invoking on the leading edge of the timeout. + * @param {boolean} [options.trailing=true] + * Specify invoking on the trailing edge of the timeout. + * @returns {Function} Returns the new throttled function. + * @example + * + * // Avoid excessively updating the position while scrolling. + * jQuery(window).on('scroll', _.throttle(updatePosition, 100)); + * + * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes. + * var throttled = _.throttle(renewToken, 300000, { 'trailing': false }); + * jQuery(element).on('click', throttled); + * + * // Cancel the trailing throttled invocation. + * jQuery(window).on('popstate', throttled.cancel); + */ + function throttle(func, wait, options) { + var leading = true, + trailing = true; + + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + if (isObject(options)) { + leading = 'leading' in options ? !!options.leading : leading; + trailing = 'trailing' in options ? !!options.trailing : trailing; + } + return debounce(func, wait, { + 'leading': leading, + 'maxWait': wait, + 'trailing': trailing + }); + } + + /** + * Creates a function that accepts up to one argument, ignoring any + * additional arguments. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Function + * @param {Function} func The function to cap arguments for. + * @returns {Function} Returns the new capped function. + * @example + * + * _.map(['6', '8', '10'], _.unary(parseInt)); + * // => [6, 8, 10] + */ + function unary(func) { + return ary(func, 1); + } + + /** + * Creates a function that provides `value` to `wrapper` as its first + * argument. Any additional arguments provided to the function are appended + * to those provided to the `wrapper`. The wrapper is invoked with the `this` + * binding of the created function. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {*} value The value to wrap. + * @param {Function} [wrapper=identity] The wrapper function. + * @returns {Function} Returns the new function. + * @example + * + * var p = _.wrap(_.escape, function(func, text) { + * return '<p>' + func(text) + '</p>'; + * }); + * + * p('fred, barney, & pebbles'); + * // => '<p>fred, barney, & pebbles</p>' + */ + function wrap(value, wrapper) { + return partial(castFunction(wrapper), value); + } + + /*------------------------------------------------------------------------*/ + + /** + * Casts `value` as an array if it's not one. + * + * @static + * @memberOf _ + * @since 4.4.0 + * @category Lang + * @param {*} value The value to inspect. + * @returns {Array} Returns the cast array. + * @example + * + * _.castArray(1); + * // => [1] + * + * _.castArray({ 'a': 1 }); + * // => [{ 'a': 1 }] + * + * _.castArray('abc'); + * // => ['abc'] + * + * _.castArray(null); + * // => [null] + * + * _.castArray(undefined); + * // => [undefined] + * + * _.castArray(); + * // => [] + * + * var array = [1, 2, 3]; + * console.log(_.castArray(array) === array); + * // => true + */ + function castArray() { + if (!arguments.length) { + return []; + } + var value = arguments[0]; + return isArray(value) ? value : [value]; + } + + /** + * Creates a shallow clone of `value`. + * + * **Note:** This method is loosely based on the + * [structured clone algorithm](https://mdn.io/Structured_clone_algorithm) + * and supports cloning arrays, array buffers, booleans, date objects, maps, + * numbers, `Object` objects, regexes, sets, strings, symbols, and typed + * arrays. The own enumerable properties of `arguments` objects are cloned + * as plain objects. An empty object is returned for uncloneable values such + * as error objects, functions, DOM nodes, and WeakMaps. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to clone. + * @returns {*} Returns the cloned value. + * @see _.cloneDeep + * @example + * + * var objects = [{ 'a': 1 }, { 'b': 2 }]; + * + * var shallow = _.clone(objects); + * console.log(shallow[0] === objects[0]); + * // => true + */ + function clone(value) { + return baseClone(value, CLONE_SYMBOLS_FLAG); + } + + /** + * This method is like `_.clone` except that it accepts `customizer` which + * is invoked to produce the cloned value. If `customizer` returns `undefined`, + * cloning is handled by the method instead. The `customizer` is invoked with + * up to four arguments; (value [, index|key, object, stack]). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to clone. + * @param {Function} [customizer] The function to customize cloning. + * @returns {*} Returns the cloned value. + * @see _.cloneDeepWith + * @example + * + * function customizer(value) { + * if (_.isElement(value)) { + * return value.cloneNode(false); + * } + * } + * + * var el = _.cloneWith(document.body, customizer); + * + * console.log(el === document.body); + * // => false + * console.log(el.nodeName); + * // => 'BODY' + * console.log(el.childNodes.length); + * // => 0 + */ + function cloneWith(value, customizer) { + customizer = typeof customizer == 'function' ? customizer : undefined; + return baseClone(value, CLONE_SYMBOLS_FLAG, customizer); + } + + /** + * This method is like `_.clone` except that it recursively clones `value`. + * + * @static + * @memberOf _ + * @since 1.0.0 + * @category Lang + * @param {*} value The value to recursively clone. + * @returns {*} Returns the deep cloned value. + * @see _.clone + * @example + * + * var objects = [{ 'a': 1 }, { 'b': 2 }]; + * + * var deep = _.cloneDeep(objects); + * console.log(deep[0] === objects[0]); + * // => false + */ + function cloneDeep(value) { + return baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG); + } + + /** + * This method is like `_.cloneWith` except that it recursively clones `value`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to recursively clone. + * @param {Function} [customizer] The function to customize cloning. + * @returns {*} Returns the deep cloned value. + * @see _.cloneWith + * @example + * + * function customizer(value) { + * if (_.isElement(value)) { + * return value.cloneNode(true); + * } + * } + * + * var el = _.cloneDeepWith(document.body, customizer); + * + * console.log(el === document.body); + * // => false + * console.log(el.nodeName); + * // => 'BODY' + * console.log(el.childNodes.length); + * // => 20 + */ + function cloneDeepWith(value, customizer) { + customizer = typeof customizer == 'function' ? customizer : undefined; + return baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG, customizer); + } + + /** + * Checks if `object` conforms to `source` by invoking the predicate + * properties of `source` with the corresponding property values of `object`. + * + * **Note:** This method is equivalent to `_.conforms` when `source` is + * partially applied. + * + * @static + * @memberOf _ + * @since 4.14.0 + * @category Lang + * @param {Object} object The object to inspect. + * @param {Object} source The object of property predicates to conform to. + * @returns {boolean} Returns `true` if `object` conforms, else `false`. + * @example + * + * var object = { 'a': 1, 'b': 2 }; + * + * _.conformsTo(object, { 'b': function(n) { return n > 1; } }); + * // => true + * + * _.conformsTo(object, { 'b': function(n) { return n > 2; } }); + * // => false + */ + function conformsTo(object, source) { + return source == null || baseConformsTo(object, source, keys(source)); + } + + /** + * Performs a + * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * comparison between two values to determine if they are equivalent. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if the values are equivalent, else `false`. + * @example + * + * var object = { 'a': 1 }; + * var other = { 'a': 1 }; + * + * _.eq(object, object); + * // => true + * + * _.eq(object, other); + * // => false + * + * _.eq('a', 'a'); + * // => true + * + * _.eq('a', Object('a')); + * // => false + * + * _.eq(NaN, NaN); + * // => true + */ + function eq(value, other) { + return value === other || (value !== value && other !== other); + } + + /** + * Checks if `value` is greater than `other`. + * + * @static + * @memberOf _ + * @since 3.9.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if `value` is greater than `other`, + * else `false`. + * @see _.lt + * @example + * + * _.gt(3, 1); + * // => true + * + * _.gt(3, 3); + * // => false + * + * _.gt(1, 3); + * // => false + */ + var gt = createRelationalOperation(baseGt); + + /** + * Checks if `value` is greater than or equal to `other`. + * + * @static + * @memberOf _ + * @since 3.9.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if `value` is greater than or equal to + * `other`, else `false`. + * @see _.lte + * @example + * + * _.gte(3, 1); + * // => true + * + * _.gte(3, 3); + * // => true + * + * _.gte(1, 3); + * // => false + */ + var gte = createRelationalOperation(function(value, other) { + return value >= other; + }); + + /** + * Checks if `value` is likely an `arguments` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an `arguments` object, + * else `false`. + * @example + * + * _.isArguments(function() { return arguments; }()); + * // => true + * + * _.isArguments([1, 2, 3]); + * // => false + */ + var isArguments = baseIsArguments(function() { return arguments; }()) ? baseIsArguments : function(value) { + return isObjectLike(value) && hasOwnProperty.call(value, 'callee') && + !propertyIsEnumerable.call(value, 'callee'); + }; + + /** + * Checks if `value` is classified as an `Array` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an array, else `false`. + * @example + * + * _.isArray([1, 2, 3]); + * // => true + * + * _.isArray(document.body.children); + * // => false + * + * _.isArray('abc'); + * // => false + * + * _.isArray(_.noop); + * // => false + */ + var isArray = Array.isArray; + + /** + * Checks if `value` is classified as an `ArrayBuffer` object. + * + * @static + * @memberOf _ + * @since 4.3.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an array buffer, else `false`. + * @example + * + * _.isArrayBuffer(new ArrayBuffer(2)); + * // => true + * + * _.isArrayBuffer(new Array(2)); + * // => false + */ + var isArrayBuffer = nodeIsArrayBuffer ? baseUnary(nodeIsArrayBuffer) : baseIsArrayBuffer; + + /** + * Checks if `value` is array-like. A value is considered array-like if it's + * not a function and has a `value.length` that's an integer greater than or + * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is array-like, else `false`. + * @example + * + * _.isArrayLike([1, 2, 3]); + * // => true + * + * _.isArrayLike(document.body.children); + * // => true + * + * _.isArrayLike('abc'); + * // => true + * + * _.isArrayLike(_.noop); + * // => false + */ + function isArrayLike(value) { + return value != null && isLength(value.length) && !isFunction(value); + } + + /** + * This method is like `_.isArrayLike` except that it also checks if `value` + * is an object. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an array-like object, + * else `false`. + * @example + * + * _.isArrayLikeObject([1, 2, 3]); + * // => true + * + * _.isArrayLikeObject(document.body.children); + * // => true + * + * _.isArrayLikeObject('abc'); + * // => false + * + * _.isArrayLikeObject(_.noop); + * // => false + */ + function isArrayLikeObject(value) { + return isObjectLike(value) && isArrayLike(value); + } + + /** + * Checks if `value` is classified as a boolean primitive or object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a boolean, else `false`. + * @example + * + * _.isBoolean(false); + * // => true + * + * _.isBoolean(null); + * // => false + */ + function isBoolean(value) { + return value === true || value === false || + (isObjectLike(value) && baseGetTag(value) == boolTag); + } + + /** + * Checks if `value` is a buffer. + * + * @static + * @memberOf _ + * @since 4.3.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a buffer, else `false`. + * @example + * + * _.isBuffer(new Buffer(2)); + * // => true + * + * _.isBuffer(new Uint8Array(2)); + * // => false + */ + var isBuffer = nativeIsBuffer || stubFalse; + + /** + * Checks if `value` is classified as a `Date` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a date object, else `false`. + * @example + * + * _.isDate(new Date); + * // => true + * + * _.isDate('Mon April 23 2012'); + * // => false + */ + var isDate = nodeIsDate ? baseUnary(nodeIsDate) : baseIsDate; + + /** + * Checks if `value` is likely a DOM element. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a DOM element, else `false`. + * @example + * + * _.isElement(document.body); + * // => true + * + * _.isElement('<body>'); + * // => false + */ + function isElement(value) { + return isObjectLike(value) && value.nodeType === 1 && !isPlainObject(value); + } + + /** + * Checks if `value` is an empty object, collection, map, or set. + * + * Objects are considered empty if they have no own enumerable string keyed + * properties. + * + * Array-like values such as `arguments` objects, arrays, buffers, strings, or + * jQuery-like collections are considered empty if they have a `length` of `0`. + * Similarly, maps and sets are considered empty if they have a `size` of `0`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is empty, else `false`. + * @example + * + * _.isEmpty(null); + * // => true + * + * _.isEmpty(true); + * // => true + * + * _.isEmpty(1); + * // => true + * + * _.isEmpty([1, 2, 3]); + * // => false + * + * _.isEmpty({ 'a': 1 }); + * // => false + */ + function isEmpty(value) { + if (value == null) { + return true; + } + if (isArrayLike(value) && + (isArray(value) || typeof value == 'string' || typeof value.splice == 'function' || + isBuffer(value) || isTypedArray(value) || isArguments(value))) { + return !value.length; + } + var tag = getTag(value); + if (tag == mapTag || tag == setTag) { + return !value.size; + } + if (isPrototype(value)) { + return !baseKeys(value).length; + } + for (var key in value) { + if (hasOwnProperty.call(value, key)) { + return false; + } + } + return true; + } + + /** + * Performs a deep comparison between two values to determine if they are + * equivalent. + * + * **Note:** This method supports comparing arrays, array buffers, booleans, + * date objects, error objects, maps, numbers, `Object` objects, regexes, + * sets, strings, symbols, and typed arrays. `Object` objects are compared + * by their own, not inherited, enumerable properties. Functions and DOM + * nodes are compared by strict equality, i.e. `===`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if the values are equivalent, else `false`. + * @example + * + * var object = { 'a': 1 }; + * var other = { 'a': 1 }; + * + * _.isEqual(object, other); + * // => true + * + * object === other; + * // => false + */ + function isEqual(value, other) { + return baseIsEqual(value, other); + } + + /** + * This method is like `_.isEqual` except that it accepts `customizer` which + * is invoked to compare values. If `customizer` returns `undefined`, comparisons + * are handled by the method instead. The `customizer` is invoked with up to + * six arguments: (objValue, othValue [, index|key, object, other, stack]). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @param {Function} [customizer] The function to customize comparisons. + * @returns {boolean} Returns `true` if the values are equivalent, else `false`. + * @example + * + * function isGreeting(value) { + * return /^h(?:i|ello)$/.test(value); + * } + * + * function customizer(objValue, othValue) { + * if (isGreeting(objValue) && isGreeting(othValue)) { + * return true; + * } + * } + * + * var array = ['hello', 'goodbye']; + * var other = ['hi', 'goodbye']; + * + * _.isEqualWith(array, other, customizer); + * // => true + */ + function isEqualWith(value, other, customizer) { + customizer = typeof customizer == 'function' ? customizer : undefined; + var result = customizer ? customizer(value, other) : undefined; + return result === undefined ? baseIsEqual(value, other, undefined, customizer) : !!result; + } + + /** + * Checks if `value` is an `Error`, `EvalError`, `RangeError`, `ReferenceError`, + * `SyntaxError`, `TypeError`, or `URIError` object. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an error object, else `false`. + * @example + * + * _.isError(new Error); + * // => true + * + * _.isError(Error); + * // => false + */ + function isError(value) { + if (!isObjectLike(value)) { + return false; + } + var tag = baseGetTag(value); + return tag == errorTag || tag == domExcTag || + (typeof value.message == 'string' && typeof value.name == 'string' && !isPlainObject(value)); + } + + /** + * Checks if `value` is a finite primitive number. + * + * **Note:** This method is based on + * [`Number.isFinite`](https://mdn.io/Number/isFinite). + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a finite number, else `false`. + * @example + * + * _.isFinite(3); + * // => true + * + * _.isFinite(Number.MIN_VALUE); + * // => true + * + * _.isFinite(Infinity); + * // => false + * + * _.isFinite('3'); + * // => false + */ + function isFinite(value) { + return typeof value == 'number' && nativeIsFinite(value); + } + + /** + * Checks if `value` is classified as a `Function` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a function, else `false`. + * @example + * + * _.isFunction(_); + * // => true + * + * _.isFunction(/abc/); + * // => false + */ + function isFunction(value) { + if (!isObject(value)) { + return false; + } + // The use of `Object#toString` avoids issues with the `typeof` operator + // in Safari 9 which returns 'object' for typed arrays and other constructors. + var tag = baseGetTag(value); + return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag; + } + + /** + * Checks if `value` is an integer. + * + * **Note:** This method is based on + * [`Number.isInteger`](https://mdn.io/Number/isInteger). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an integer, else `false`. + * @example + * + * _.isInteger(3); + * // => true + * + * _.isInteger(Number.MIN_VALUE); + * // => false + * + * _.isInteger(Infinity); + * // => false + * + * _.isInteger('3'); + * // => false + */ + function isInteger(value) { + return typeof value == 'number' && value == toInteger(value); + } + + /** + * Checks if `value` is a valid array-like length. + * + * **Note:** This method is loosely based on + * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a valid length, else `false`. + * @example + * + * _.isLength(3); + * // => true + * + * _.isLength(Number.MIN_VALUE); + * // => false + * + * _.isLength(Infinity); + * // => false + * + * _.isLength('3'); + * // => false + */ + function isLength(value) { + return typeof value == 'number' && + value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER; + } + + /** + * Checks if `value` is the + * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) + * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an object, else `false`. + * @example + * + * _.isObject({}); + * // => true + * + * _.isObject([1, 2, 3]); + * // => true + * + * _.isObject(_.noop); + * // => true + * + * _.isObject(null); + * // => false + */ + function isObject(value) { + var type = typeof value; + return value != null && (type == 'object' || type == 'function'); + } + + /** + * Checks if `value` is object-like. A value is object-like if it's not `null` + * and has a `typeof` result of "object". + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is object-like, else `false`. + * @example + * + * _.isObjectLike({}); + * // => true + * + * _.isObjectLike([1, 2, 3]); + * // => true + * + * _.isObjectLike(_.noop); + * // => false + * + * _.isObjectLike(null); + * // => false + */ + function isObjectLike(value) { + return value != null && typeof value == 'object'; + } + + /** + * Checks if `value` is classified as a `Map` object. + * + * @static + * @memberOf _ + * @since 4.3.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a map, else `false`. + * @example + * + * _.isMap(new Map); + * // => true + * + * _.isMap(new WeakMap); + * // => false + */ + var isMap = nodeIsMap ? baseUnary(nodeIsMap) : baseIsMap; + + /** + * Performs a partial deep comparison between `object` and `source` to + * determine if `object` contains equivalent property values. + * + * **Note:** This method is equivalent to `_.matches` when `source` is + * partially applied. + * + * Partial comparisons will match empty array and empty object `source` + * values against any array or object value, respectively. See `_.isEqual` + * for a list of supported value comparisons. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Lang + * @param {Object} object The object to inspect. + * @param {Object} source The object of property values to match. + * @returns {boolean} Returns `true` if `object` is a match, else `false`. + * @example + * + * var object = { 'a': 1, 'b': 2 }; + * + * _.isMatch(object, { 'b': 2 }); + * // => true + * + * _.isMatch(object, { 'b': 1 }); + * // => false + */ + function isMatch(object, source) { + return object === source || baseIsMatch(object, source, getMatchData(source)); + } + + /** + * This method is like `_.isMatch` except that it accepts `customizer` which + * is invoked to compare values. If `customizer` returns `undefined`, comparisons + * are handled by the method instead. The `customizer` is invoked with five + * arguments: (objValue, srcValue, index|key, object, source). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {Object} object The object to inspect. + * @param {Object} source The object of property values to match. + * @param {Function} [customizer] The function to customize comparisons. + * @returns {boolean} Returns `true` if `object` is a match, else `false`. + * @example + * + * function isGreeting(value) { + * return /^h(?:i|ello)$/.test(value); + * } + * + * function customizer(objValue, srcValue) { + * if (isGreeting(objValue) && isGreeting(srcValue)) { + * return true; + * } + * } + * + * var object = { 'greeting': 'hello' }; + * var source = { 'greeting': 'hi' }; + * + * _.isMatchWith(object, source, customizer); + * // => true + */ + function isMatchWith(object, source, customizer) { + customizer = typeof customizer == 'function' ? customizer : undefined; + return baseIsMatch(object, source, getMatchData(source), customizer); + } + + /** + * Checks if `value` is `NaN`. + * + * **Note:** This method is based on + * [`Number.isNaN`](https://mdn.io/Number/isNaN) and is not the same as + * global [`isNaN`](https://mdn.io/isNaN) which returns `true` for + * `undefined` and other non-number values. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`. + * @example + * + * _.isNaN(NaN); + * // => true + * + * _.isNaN(new Number(NaN)); + * // => true + * + * isNaN(undefined); + * // => true + * + * _.isNaN(undefined); + * // => false + */ + function isNaN(value) { + // An `NaN` primitive is the only value that is not equal to itself. + // Perform the `toStringTag` check first to avoid errors with some + // ActiveX objects in IE. + return isNumber(value) && value != +value; + } + + /** + * Checks if `value` is a pristine native function. + * + * **Note:** This method can't reliably detect native functions in the presence + * of the core-js package because core-js circumvents this kind of detection. + * Despite multiple requests, the core-js maintainer has made it clear: any + * attempt to fix the detection will be obstructed. As a result, we're left + * with little choice but to throw an error. Unfortunately, this also affects + * packages, like [babel-polyfill](https://www.npmjs.com/package/babel-polyfill), + * which rely on core-js. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a native function, + * else `false`. + * @example + * + * _.isNative(Array.prototype.push); + * // => true + * + * _.isNative(_); + * // => false + */ + function isNative(value) { + if (isMaskable(value)) { + throw new Error(CORE_ERROR_TEXT); + } + return baseIsNative(value); + } + + /** + * Checks if `value` is `null`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is `null`, else `false`. + * @example + * + * _.isNull(null); + * // => true + * + * _.isNull(void 0); + * // => false + */ + function isNull(value) { + return value === null; + } + + /** + * Checks if `value` is `null` or `undefined`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is nullish, else `false`. + * @example + * + * _.isNil(null); + * // => true + * + * _.isNil(void 0); + * // => true + * + * _.isNil(NaN); + * // => false + */ + function isNil(value) { + return value == null; + } + + /** + * Checks if `value` is classified as a `Number` primitive or object. + * + * **Note:** To exclude `Infinity`, `-Infinity`, and `NaN`, which are + * classified as numbers, use the `_.isFinite` method. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a number, else `false`. + * @example + * + * _.isNumber(3); + * // => true + * + * _.isNumber(Number.MIN_VALUE); + * // => true + * + * _.isNumber(Infinity); + * // => true + * + * _.isNumber('3'); + * // => false + */ + function isNumber(value) { + return typeof value == 'number' || + (isObjectLike(value) && baseGetTag(value) == numberTag); + } + + /** + * Checks if `value` is a plain object, that is, an object created by the + * `Object` constructor or one with a `[[Prototype]]` of `null`. + * + * @static + * @memberOf _ + * @since 0.8.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a plain object, else `false`. + * @example + * + * function Foo() { + * this.a = 1; + * } + * + * _.isPlainObject(new Foo); + * // => false + * + * _.isPlainObject([1, 2, 3]); + * // => false + * + * _.isPlainObject({ 'x': 0, 'y': 0 }); + * // => true + * + * _.isPlainObject(Object.create(null)); + * // => true + */ + function isPlainObject(value) { + if (!isObjectLike(value) || baseGetTag(value) != objectTag) { + return false; + } + var proto = getPrototype(value); + if (proto === null) { + return true; + } + var Ctor = hasOwnProperty.call(proto, 'constructor') && proto.constructor; + return typeof Ctor == 'function' && Ctor instanceof Ctor && + funcToString.call(Ctor) == objectCtorString; + } + + /** + * Checks if `value` is classified as a `RegExp` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a regexp, else `false`. + * @example + * + * _.isRegExp(/abc/); + * // => true + * + * _.isRegExp('/abc/'); + * // => false + */ + var isRegExp = nodeIsRegExp ? baseUnary(nodeIsRegExp) : baseIsRegExp; + + /** + * Checks if `value` is a safe integer. An integer is safe if it's an IEEE-754 + * double precision number which isn't the result of a rounded unsafe integer. + * + * **Note:** This method is based on + * [`Number.isSafeInteger`](https://mdn.io/Number/isSafeInteger). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a safe integer, else `false`. + * @example + * + * _.isSafeInteger(3); + * // => true + * + * _.isSafeInteger(Number.MIN_VALUE); + * // => false + * + * _.isSafeInteger(Infinity); + * // => false + * + * _.isSafeInteger('3'); + * // => false + */ + function isSafeInteger(value) { + return isInteger(value) && value >= -MAX_SAFE_INTEGER && value <= MAX_SAFE_INTEGER; + } + + /** + * Checks if `value` is classified as a `Set` object. + * + * @static + * @memberOf _ + * @since 4.3.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a set, else `false`. + * @example + * + * _.isSet(new Set); + * // => true + * + * _.isSet(new WeakSet); + * // => false + */ + var isSet = nodeIsSet ? baseUnary(nodeIsSet) : baseIsSet; + + /** + * Checks if `value` is classified as a `String` primitive or object. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a string, else `false`. + * @example + * + * _.isString('abc'); + * // => true + * + * _.isString(1); + * // => false + */ + function isString(value) { + return typeof value == 'string' || + (!isArray(value) && isObjectLike(value) && baseGetTag(value) == stringTag); + } + + /** + * Checks if `value` is classified as a `Symbol` primitive or object. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a symbol, else `false`. + * @example + * + * _.isSymbol(Symbol.iterator); + * // => true + * + * _.isSymbol('abc'); + * // => false + */ + function isSymbol(value) { + return typeof value == 'symbol' || + (isObjectLike(value) && baseGetTag(value) == symbolTag); + } + + /** + * Checks if `value` is classified as a typed array. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a typed array, else `false`. + * @example + * + * _.isTypedArray(new Uint8Array); + * // => true + * + * _.isTypedArray([]); + * // => false + */ + var isTypedArray = nodeIsTypedArray ? baseUnary(nodeIsTypedArray) : baseIsTypedArray; + + /** + * Checks if `value` is `undefined`. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`. + * @example + * + * _.isUndefined(void 0); + * // => true + * + * _.isUndefined(null); + * // => false + */ + function isUndefined(value) { + return value === undefined; + } + + /** + * Checks if `value` is classified as a `WeakMap` object. + * + * @static + * @memberOf _ + * @since 4.3.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a weak map, else `false`. + * @example + * + * _.isWeakMap(new WeakMap); + * // => true + * + * _.isWeakMap(new Map); + * // => false + */ + function isWeakMap(value) { + return isObjectLike(value) && getTag(value) == weakMapTag; + } + + /** + * Checks if `value` is classified as a `WeakSet` object. + * + * @static + * @memberOf _ + * @since 4.3.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a weak set, else `false`. + * @example + * + * _.isWeakSet(new WeakSet); + * // => true + * + * _.isWeakSet(new Set); + * // => false + */ + function isWeakSet(value) { + return isObjectLike(value) && baseGetTag(value) == weakSetTag; + } + + /** + * Checks if `value` is less than `other`. + * + * @static + * @memberOf _ + * @since 3.9.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if `value` is less than `other`, + * else `false`. + * @see _.gt + * @example + * + * _.lt(1, 3); + * // => true + * + * _.lt(3, 3); + * // => false + * + * _.lt(3, 1); + * // => false + */ + var lt = createRelationalOperation(baseLt); + + /** + * Checks if `value` is less than or equal to `other`. + * + * @static + * @memberOf _ + * @since 3.9.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if `value` is less than or equal to + * `other`, else `false`. + * @see _.gte + * @example + * + * _.lte(1, 3); + * // => true + * + * _.lte(3, 3); + * // => true + * + * _.lte(3, 1); + * // => false + */ + var lte = createRelationalOperation(function(value, other) { + return value <= other; + }); + + /** + * Converts `value` to an array. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Lang + * @param {*} value The value to convert. + * @returns {Array} Returns the converted array. + * @example + * + * _.toArray({ 'a': 1, 'b': 2 }); + * // => [1, 2] + * + * _.toArray('abc'); + * // => ['a', 'b', 'c'] + * + * _.toArray(1); + * // => [] + * + * _.toArray(null); + * // => [] + */ + function toArray(value) { + if (!value) { + return []; + } + if (isArrayLike(value)) { + return isString(value) ? stringToArray(value) : copyArray(value); + } + if (symIterator && value[symIterator]) { + return iteratorToArray(value[symIterator]()); + } + var tag = getTag(value), + func = tag == mapTag ? mapToArray : (tag == setTag ? setToArray : values); + + return func(value); + } + + /** + * Converts `value` to a finite number. + * + * @static + * @memberOf _ + * @since 4.12.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {number} Returns the converted number. + * @example + * + * _.toFinite(3.2); + * // => 3.2 + * + * _.toFinite(Number.MIN_VALUE); + * // => 5e-324 + * + * _.toFinite(Infinity); + * // => 1.7976931348623157e+308 + * + * _.toFinite('3.2'); + * // => 3.2 + */ + function toFinite(value) { + if (!value) { + return value === 0 ? value : 0; + } + value = toNumber(value); + if (value === INFINITY || value === -INFINITY) { + var sign = (value < 0 ? -1 : 1); + return sign * MAX_INTEGER; + } + return value === value ? value : 0; + } + + /** + * Converts `value` to an integer. + * + * **Note:** This method is loosely based on + * [`ToInteger`](http://www.ecma-international.org/ecma-262/7.0/#sec-tointeger). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {number} Returns the converted integer. + * @example + * + * _.toInteger(3.2); + * // => 3 + * + * _.toInteger(Number.MIN_VALUE); + * // => 0 + * + * _.toInteger(Infinity); + * // => 1.7976931348623157e+308 + * + * _.toInteger('3.2'); + * // => 3 + */ + function toInteger(value) { + var result = toFinite(value), + remainder = result % 1; + + return result === result ? (remainder ? result - remainder : result) : 0; + } + + /** + * Converts `value` to an integer suitable for use as the length of an + * array-like object. + * + * **Note:** This method is based on + * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {number} Returns the converted integer. + * @example + * + * _.toLength(3.2); + * // => 3 + * + * _.toLength(Number.MIN_VALUE); + * // => 0 + * + * _.toLength(Infinity); + * // => 4294967295 + * + * _.toLength('3.2'); + * // => 3 + */ + function toLength(value) { + return value ? baseClamp(toInteger(value), 0, MAX_ARRAY_LENGTH) : 0; + } + + /** + * Converts `value` to a number. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to process. + * @returns {number} Returns the number. + * @example + * + * _.toNumber(3.2); + * // => 3.2 + * + * _.toNumber(Number.MIN_VALUE); + * // => 5e-324 + * + * _.toNumber(Infinity); + * // => Infinity + * + * _.toNumber('3.2'); + * // => 3.2 + */ + function toNumber(value) { + if (typeof value == 'number') { + return value; + } + if (isSymbol(value)) { + return NAN; + } + if (isObject(value)) { + var other = typeof value.valueOf == 'function' ? value.valueOf() : value; + value = isObject(other) ? (other + '') : other; + } + if (typeof value != 'string') { + return value === 0 ? value : +value; + } + value = value.replace(reTrim, ''); + var isBinary = reIsBinary.test(value); + return (isBinary || reIsOctal.test(value)) + ? freeParseInt(value.slice(2), isBinary ? 2 : 8) + : (reIsBadHex.test(value) ? NAN : +value); + } + + /** + * Converts `value` to a plain object flattening inherited enumerable string + * keyed properties of `value` to own properties of the plain object. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {Object} Returns the converted plain object. + * @example + * + * function Foo() { + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.assign({ 'a': 1 }, new Foo); + * // => { 'a': 1, 'b': 2 } + * + * _.assign({ 'a': 1 }, _.toPlainObject(new Foo)); + * // => { 'a': 1, 'b': 2, 'c': 3 } + */ + function toPlainObject(value) { + return copyObject(value, keysIn(value)); + } + + /** + * Converts `value` to a safe integer. A safe integer can be compared and + * represented correctly. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {number} Returns the converted integer. + * @example + * + * _.toSafeInteger(3.2); + * // => 3 + * + * _.toSafeInteger(Number.MIN_VALUE); + * // => 0 + * + * _.toSafeInteger(Infinity); + * // => 9007199254740991 + * + * _.toSafeInteger('3.2'); + * // => 3 + */ + function toSafeInteger(value) { + return value + ? baseClamp(toInteger(value), -MAX_SAFE_INTEGER, MAX_SAFE_INTEGER) + : (value === 0 ? value : 0); + } + + /** + * Converts `value` to a string. An empty string is returned for `null` + * and `undefined` values. The sign of `-0` is preserved. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {string} Returns the converted string. + * @example + * + * _.toString(null); + * // => '' + * + * _.toString(-0); + * // => '-0' + * + * _.toString([1, 2, 3]); + * // => '1,2,3' + */ + function toString(value) { + return value == null ? '' : baseToString(value); + } + + /*------------------------------------------------------------------------*/ + + /** + * Assigns own enumerable string keyed properties of source objects to the + * destination object. Source objects are applied from left to right. + * Subsequent sources overwrite property assignments of previous sources. + * + * **Note:** This method mutates `object` and is loosely based on + * [`Object.assign`](https://mdn.io/Object/assign). + * + * @static + * @memberOf _ + * @since 0.10.0 + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @returns {Object} Returns `object`. + * @see _.assignIn + * @example + * + * function Foo() { + * this.a = 1; + * } + * + * function Bar() { + * this.c = 3; + * } + * + * Foo.prototype.b = 2; + * Bar.prototype.d = 4; + * + * _.assign({ 'a': 0 }, new Foo, new Bar); + * // => { 'a': 1, 'c': 3 } + */ + var assign = createAssigner(function(object, source) { + if (isPrototype(source) || isArrayLike(source)) { + copyObject(source, keys(source), object); + return; + } + for (var key in source) { + if (hasOwnProperty.call(source, key)) { + assignValue(object, key, source[key]); + } + } + }); + + /** + * This method is like `_.assign` except that it iterates over own and + * inherited source properties. + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @alias extend + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @returns {Object} Returns `object`. + * @see _.assign + * @example + * + * function Foo() { + * this.a = 1; + * } + * + * function Bar() { + * this.c = 3; + * } + * + * Foo.prototype.b = 2; + * Bar.prototype.d = 4; + * + * _.assignIn({ 'a': 0 }, new Foo, new Bar); + * // => { 'a': 1, 'b': 2, 'c': 3, 'd': 4 } + */ + var assignIn = createAssigner(function(object, source) { + copyObject(source, keysIn(source), object); + }); + + /** + * This method is like `_.assignIn` except that it accepts `customizer` + * which is invoked to produce the assigned values. If `customizer` returns + * `undefined`, assignment is handled by the method instead. The `customizer` + * is invoked with five arguments: (objValue, srcValue, key, object, source). + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @alias extendWith + * @category Object + * @param {Object} object The destination object. + * @param {...Object} sources The source objects. + * @param {Function} [customizer] The function to customize assigned values. + * @returns {Object} Returns `object`. + * @see _.assignWith + * @example + * + * function customizer(objValue, srcValue) { + * return _.isUndefined(objValue) ? srcValue : objValue; + * } + * + * var defaults = _.partialRight(_.assignInWith, customizer); + * + * defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 }); + * // => { 'a': 1, 'b': 2 } + */ + var assignInWith = createAssigner(function(object, source, srcIndex, customizer) { + copyObject(source, keysIn(source), object, customizer); + }); + + /** + * This method is like `_.assign` except that it accepts `customizer` + * which is invoked to produce the assigned values. If `customizer` returns + * `undefined`, assignment is handled by the method instead. The `customizer` + * is invoked with five arguments: (objValue, srcValue, key, object, source). + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The destination object. + * @param {...Object} sources The source objects. + * @param {Function} [customizer] The function to customize assigned values. + * @returns {Object} Returns `object`. + * @see _.assignInWith + * @example + * + * function customizer(objValue, srcValue) { + * return _.isUndefined(objValue) ? srcValue : objValue; + * } + * + * var defaults = _.partialRight(_.assignWith, customizer); + * + * defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 }); + * // => { 'a': 1, 'b': 2 } + */ + var assignWith = createAssigner(function(object, source, srcIndex, customizer) { + copyObject(source, keys(source), object, customizer); + }); + + /** + * Creates an array of values corresponding to `paths` of `object`. + * + * @static + * @memberOf _ + * @since 1.0.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {...(string|string[])} [paths] The property paths to pick. + * @returns {Array} Returns the picked values. + * @example + * + * var object = { 'a': [{ 'b': { 'c': 3 } }, 4] }; + * + * _.at(object, ['a[0].b.c', 'a[1]']); + * // => [3, 4] + */ + var at = flatRest(baseAt); + + /** + * Creates an object that inherits from the `prototype` object. If a + * `properties` object is given, its own enumerable string keyed properties + * are assigned to the created object. + * + * @static + * @memberOf _ + * @since 2.3.0 + * @category Object + * @param {Object} prototype The object to inherit from. + * @param {Object} [properties] The properties to assign to the object. + * @returns {Object} Returns the new object. + * @example + * + * function Shape() { + * this.x = 0; + * this.y = 0; + * } + * + * function Circle() { + * Shape.call(this); + * } + * + * Circle.prototype = _.create(Shape.prototype, { + * 'constructor': Circle + * }); + * + * var circle = new Circle; + * circle instanceof Circle; + * // => true + * + * circle instanceof Shape; + * // => true + */ + function create(prototype, properties) { + var result = baseCreate(prototype); + return properties == null ? result : baseAssign(result, properties); + } + + /** + * Assigns own and inherited enumerable string keyed properties of source + * objects to the destination object for all destination properties that + * resolve to `undefined`. Source objects are applied from left to right. + * Once a property is set, additional values of the same property are ignored. + * + * **Note:** This method mutates `object`. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @returns {Object} Returns `object`. + * @see _.defaultsDeep + * @example + * + * _.defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 }); + * // => { 'a': 1, 'b': 2 } + */ + var defaults = baseRest(function(args) { + args.push(undefined, customDefaultsAssignIn); + return apply(assignInWith, undefined, args); + }); + + /** + * This method is like `_.defaults` except that it recursively assigns + * default properties. + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 3.10.0 + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @returns {Object} Returns `object`. + * @see _.defaults + * @example + * + * _.defaultsDeep({ 'a': { 'b': 2 } }, { 'a': { 'b': 1, 'c': 3 } }); + * // => { 'a': { 'b': 2, 'c': 3 } } + */ + var defaultsDeep = baseRest(function(args) { + args.push(undefined, customDefaultsMerge); + return apply(mergeWith, undefined, args); + }); + + /** + * This method is like `_.find` except that it returns the key of the first + * element `predicate` returns truthy for instead of the element itself. + * + * @static + * @memberOf _ + * @since 1.1.0 + * @category Object + * @param {Object} object The object to inspect. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @returns {string|undefined} Returns the key of the matched element, + * else `undefined`. + * @example + * + * var users = { + * 'barney': { 'age': 36, 'active': true }, + * 'fred': { 'age': 40, 'active': false }, + * 'pebbles': { 'age': 1, 'active': true } + * }; + * + * _.findKey(users, function(o) { return o.age < 40; }); + * // => 'barney' (iteration order is not guaranteed) + * + * // The `_.matches` iteratee shorthand. + * _.findKey(users, { 'age': 1, 'active': true }); + * // => 'pebbles' + * + * // The `_.matchesProperty` iteratee shorthand. + * _.findKey(users, ['active', false]); + * // => 'fred' + * + * // The `_.property` iteratee shorthand. + * _.findKey(users, 'active'); + * // => 'barney' + */ + function findKey(object, predicate) { + return baseFindKey(object, getIteratee(predicate, 3), baseForOwn); + } + + /** + * This method is like `_.findKey` except that it iterates over elements of + * a collection in the opposite order. + * + * @static + * @memberOf _ + * @since 2.0.0 + * @category Object + * @param {Object} object The object to inspect. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @returns {string|undefined} Returns the key of the matched element, + * else `undefined`. + * @example + * + * var users = { + * 'barney': { 'age': 36, 'active': true }, + * 'fred': { 'age': 40, 'active': false }, + * 'pebbles': { 'age': 1, 'active': true } + * }; + * + * _.findLastKey(users, function(o) { return o.age < 40; }); + * // => returns 'pebbles' assuming `_.findKey` returns 'barney' + * + * // The `_.matches` iteratee shorthand. + * _.findLastKey(users, { 'age': 36, 'active': true }); + * // => 'barney' + * + * // The `_.matchesProperty` iteratee shorthand. + * _.findLastKey(users, ['active', false]); + * // => 'fred' + * + * // The `_.property` iteratee shorthand. + * _.findLastKey(users, 'active'); + * // => 'pebbles' + */ + function findLastKey(object, predicate) { + return baseFindKey(object, getIteratee(predicate, 3), baseForOwnRight); + } + + /** + * Iterates over own and inherited enumerable string keyed properties of an + * object and invokes `iteratee` for each property. The iteratee is invoked + * with three arguments: (value, key, object). Iteratee functions may exit + * iteration early by explicitly returning `false`. + * + * @static + * @memberOf _ + * @since 0.3.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Object} Returns `object`. + * @see _.forInRight + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.forIn(new Foo, function(value, key) { + * console.log(key); + * }); + * // => Logs 'a', 'b', then 'c' (iteration order is not guaranteed). + */ + function forIn(object, iteratee) { + return object == null + ? object + : baseFor(object, getIteratee(iteratee, 3), keysIn); + } + + /** + * This method is like `_.forIn` except that it iterates over properties of + * `object` in the opposite order. + * + * @static + * @memberOf _ + * @since 2.0.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Object} Returns `object`. + * @see _.forIn + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.forInRight(new Foo, function(value, key) { + * console.log(key); + * }); + * // => Logs 'c', 'b', then 'a' assuming `_.forIn` logs 'a', 'b', then 'c'. + */ + function forInRight(object, iteratee) { + return object == null + ? object + : baseForRight(object, getIteratee(iteratee, 3), keysIn); + } + + /** + * Iterates over own enumerable string keyed properties of an object and + * invokes `iteratee` for each property. The iteratee is invoked with three + * arguments: (value, key, object). Iteratee functions may exit iteration + * early by explicitly returning `false`. + * + * @static + * @memberOf _ + * @since 0.3.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Object} Returns `object`. + * @see _.forOwnRight + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.forOwn(new Foo, function(value, key) { + * console.log(key); + * }); + * // => Logs 'a' then 'b' (iteration order is not guaranteed). + */ + function forOwn(object, iteratee) { + return object && baseForOwn(object, getIteratee(iteratee, 3)); + } + + /** + * This method is like `_.forOwn` except that it iterates over properties of + * `object` in the opposite order. + * + * @static + * @memberOf _ + * @since 2.0.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Object} Returns `object`. + * @see _.forOwn + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.forOwnRight(new Foo, function(value, key) { + * console.log(key); + * }); + * // => Logs 'b' then 'a' assuming `_.forOwn` logs 'a' then 'b'. + */ + function forOwnRight(object, iteratee) { + return object && baseForOwnRight(object, getIteratee(iteratee, 3)); + } + + /** + * Creates an array of function property names from own enumerable properties + * of `object`. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The object to inspect. + * @returns {Array} Returns the function names. + * @see _.functionsIn + * @example + * + * function Foo() { + * this.a = _.constant('a'); + * this.b = _.constant('b'); + * } + * + * Foo.prototype.c = _.constant('c'); + * + * _.functions(new Foo); + * // => ['a', 'b'] + */ + function functions(object) { + return object == null ? [] : baseFunctions(object, keys(object)); + } + + /** + * Creates an array of function property names from own and inherited + * enumerable properties of `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The object to inspect. + * @returns {Array} Returns the function names. + * @see _.functions + * @example + * + * function Foo() { + * this.a = _.constant('a'); + * this.b = _.constant('b'); + * } + * + * Foo.prototype.c = _.constant('c'); + * + * _.functionsIn(new Foo); + * // => ['a', 'b', 'c'] + */ + function functionsIn(object) { + return object == null ? [] : baseFunctions(object, keysIn(object)); + } + + /** + * Gets the value at `path` of `object`. If the resolved value is + * `undefined`, the `defaultValue` is returned in its place. + * + * @static + * @memberOf _ + * @since 3.7.0 + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path of the property to get. + * @param {*} [defaultValue] The value returned for `undefined` resolved values. + * @returns {*} Returns the resolved value. + * @example + * + * var object = { 'a': [{ 'b': { 'c': 3 } }] }; + * + * _.get(object, 'a[0].b.c'); + * // => 3 + * + * _.get(object, ['a', '0', 'b', 'c']); + * // => 3 + * + * _.get(object, 'a.b.c', 'default'); + * // => 'default' + */ + function get(object, path, defaultValue) { + var result = object == null ? undefined : baseGet(object, path); + return result === undefined ? defaultValue : result; + } + + /** + * Checks if `path` is a direct property of `object`. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path to check. + * @returns {boolean} Returns `true` if `path` exists, else `false`. + * @example + * + * var object = { 'a': { 'b': 2 } }; + * var other = _.create({ 'a': _.create({ 'b': 2 }) }); + * + * _.has(object, 'a'); + * // => true + * + * _.has(object, 'a.b'); + * // => true + * + * _.has(object, ['a', 'b']); + * // => true + * + * _.has(other, 'a'); + * // => false + */ + function has(object, path) { + return object != null && hasPath(object, path, baseHas); + } + + /** + * Checks if `path` is a direct or inherited property of `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path to check. + * @returns {boolean} Returns `true` if `path` exists, else `false`. + * @example + * + * var object = _.create({ 'a': _.create({ 'b': 2 }) }); + * + * _.hasIn(object, 'a'); + * // => true + * + * _.hasIn(object, 'a.b'); + * // => true + * + * _.hasIn(object, ['a', 'b']); + * // => true + * + * _.hasIn(object, 'b'); + * // => false + */ + function hasIn(object, path) { + return object != null && hasPath(object, path, baseHasIn); + } + + /** + * Creates an object composed of the inverted keys and values of `object`. + * If `object` contains duplicate values, subsequent values overwrite + * property assignments of previous values. + * + * @static + * @memberOf _ + * @since 0.7.0 + * @category Object + * @param {Object} object The object to invert. + * @returns {Object} Returns the new inverted object. + * @example + * + * var object = { 'a': 1, 'b': 2, 'c': 1 }; + * + * _.invert(object); + * // => { '1': 'c', '2': 'b' } + */ + var invert = createInverter(function(result, value, key) { + result[value] = key; + }, constant(identity)); + + /** + * This method is like `_.invert` except that the inverted object is generated + * from the results of running each element of `object` thru `iteratee`. The + * corresponding inverted value of each inverted key is an array of keys + * responsible for generating the inverted value. The iteratee is invoked + * with one argument: (value). + * + * @static + * @memberOf _ + * @since 4.1.0 + * @category Object + * @param {Object} object The object to invert. + * @param {Function} [iteratee=_.identity] The iteratee invoked per element. + * @returns {Object} Returns the new inverted object. + * @example + * + * var object = { 'a': 1, 'b': 2, 'c': 1 }; + * + * _.invertBy(object); + * // => { '1': ['a', 'c'], '2': ['b'] } + * + * _.invertBy(object, function(value) { + * return 'group' + value; + * }); + * // => { 'group1': ['a', 'c'], 'group2': ['b'] } + */ + var invertBy = createInverter(function(result, value, key) { + if (hasOwnProperty.call(result, value)) { + result[value].push(key); + } else { + result[value] = [key]; + } + }, getIteratee); + + /** + * Invokes the method at `path` of `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path of the method to invoke. + * @param {...*} [args] The arguments to invoke the method with. + * @returns {*} Returns the result of the invoked method. + * @example + * + * var object = { 'a': [{ 'b': { 'c': [1, 2, 3, 4] } }] }; + * + * _.invoke(object, 'a[0].b.c.slice', 1, 3); + * // => [2, 3] + */ + var invoke = baseRest(baseInvoke); + + /** + * Creates an array of the own enumerable property names of `object`. + * + * **Note:** Non-object values are coerced to objects. See the + * [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys) + * for more details. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.keys(new Foo); + * // => ['a', 'b'] (iteration order is not guaranteed) + * + * _.keys('hi'); + * // => ['0', '1'] + */ + function keys(object) { + return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object); + } + + /** + * Creates an array of the own and inherited enumerable property names of `object`. + * + * **Note:** Non-object values are coerced to objects. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.keysIn(new Foo); + * // => ['a', 'b', 'c'] (iteration order is not guaranteed) + */ + function keysIn(object) { + return isArrayLike(object) ? arrayLikeKeys(object, true) : baseKeysIn(object); + } + + /** + * The opposite of `_.mapValues`; this method creates an object with the + * same values as `object` and keys generated by running each own enumerable + * string keyed property of `object` thru `iteratee`. The iteratee is invoked + * with three arguments: (value, key, object). + * + * @static + * @memberOf _ + * @since 3.8.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Object} Returns the new mapped object. + * @see _.mapValues + * @example + * + * _.mapKeys({ 'a': 1, 'b': 2 }, function(value, key) { + * return key + value; + * }); + * // => { 'a1': 1, 'b2': 2 } + */ + function mapKeys(object, iteratee) { + var result = {}; + iteratee = getIteratee(iteratee, 3); + + baseForOwn(object, function(value, key, object) { + baseAssignValue(result, iteratee(value, key, object), value); + }); + return result; + } + + /** + * Creates an object with the same keys as `object` and values generated + * by running each own enumerable string keyed property of `object` thru + * `iteratee`. The iteratee is invoked with three arguments: + * (value, key, object). + * + * @static + * @memberOf _ + * @since 2.4.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Object} Returns the new mapped object. + * @see _.mapKeys + * @example + * + * var users = { + * 'fred': { 'user': 'fred', 'age': 40 }, + * 'pebbles': { 'user': 'pebbles', 'age': 1 } + * }; + * + * _.mapValues(users, function(o) { return o.age; }); + * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed) + * + * // The `_.property` iteratee shorthand. + * _.mapValues(users, 'age'); + * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed) + */ + function mapValues(object, iteratee) { + var result = {}; + iteratee = getIteratee(iteratee, 3); + + baseForOwn(object, function(value, key, object) { + baseAssignValue(result, key, iteratee(value, key, object)); + }); + return result; + } + + /** + * This method is like `_.assign` except that it recursively merges own and + * inherited enumerable string keyed properties of source objects into the + * destination object. Source properties that resolve to `undefined` are + * skipped if a destination value exists. Array and plain object properties + * are merged recursively. Other objects and value types are overridden by + * assignment. Source objects are applied from left to right. Subsequent + * sources overwrite property assignments of previous sources. + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 0.5.0 + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @returns {Object} Returns `object`. + * @example + * + * var object = { + * 'a': [{ 'b': 2 }, { 'd': 4 }] + * }; + * + * var other = { + * 'a': [{ 'c': 3 }, { 'e': 5 }] + * }; + * + * _.merge(object, other); + * // => { 'a': [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] } + */ + var merge = createAssigner(function(object, source, srcIndex) { + baseMerge(object, source, srcIndex); + }); + + /** + * This method is like `_.merge` except that it accepts `customizer` which + * is invoked to produce the merged values of the destination and source + * properties. If `customizer` returns `undefined`, merging is handled by the + * method instead. The `customizer` is invoked with six arguments: + * (objValue, srcValue, key, object, source, stack). + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The destination object. + * @param {...Object} sources The source objects. + * @param {Function} customizer The function to customize assigned values. + * @returns {Object} Returns `object`. + * @example + * + * function customizer(objValue, srcValue) { + * if (_.isArray(objValue)) { + * return objValue.concat(srcValue); + * } + * } + * + * var object = { 'a': [1], 'b': [2] }; + * var other = { 'a': [3], 'b': [4] }; + * + * _.mergeWith(object, other, customizer); + * // => { 'a': [1, 3], 'b': [2, 4] } + */ + var mergeWith = createAssigner(function(object, source, srcIndex, customizer) { + baseMerge(object, source, srcIndex, customizer); + }); + + /** + * The opposite of `_.pick`; this method creates an object composed of the + * own and inherited enumerable property paths of `object` that are not omitted. + * + * **Note:** This method is considerably slower than `_.pick`. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The source object. + * @param {...(string|string[])} [paths] The property paths to omit. + * @returns {Object} Returns the new object. + * @example + * + * var object = { 'a': 1, 'b': '2', 'c': 3 }; + * + * _.omit(object, ['a', 'c']); + * // => { 'b': '2' } + */ + var omit = flatRest(function(object, paths) { + var result = {}; + if (object == null) { + return result; + } + var isDeep = false; + paths = arrayMap(paths, function(path) { + path = castPath(path, object); + isDeep || (isDeep = path.length > 1); + return path; + }); + copyObject(object, getAllKeysIn(object), result); + if (isDeep) { + result = baseClone(result, CLONE_DEEP_FLAG | CLONE_FLAT_FLAG | CLONE_SYMBOLS_FLAG, customOmitClone); + } + var length = paths.length; + while (length--) { + baseUnset(result, paths[length]); + } + return result; + }); + + /** + * The opposite of `_.pickBy`; this method creates an object composed of + * the own and inherited enumerable string keyed properties of `object` that + * `predicate` doesn't return truthy for. The predicate is invoked with two + * arguments: (value, key). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The source object. + * @param {Function} [predicate=_.identity] The function invoked per property. + * @returns {Object} Returns the new object. + * @example + * + * var object = { 'a': 1, 'b': '2', 'c': 3 }; + * + * _.omitBy(object, _.isNumber); + * // => { 'b': '2' } + */ + function omitBy(object, predicate) { + return pickBy(object, negate(getIteratee(predicate))); + } + + /** + * Creates an object composed of the picked `object` properties. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The source object. + * @param {...(string|string[])} [paths] The property paths to pick. + * @returns {Object} Returns the new object. + * @example + * + * var object = { 'a': 1, 'b': '2', 'c': 3 }; + * + * _.pick(object, ['a', 'c']); + * // => { 'a': 1, 'c': 3 } + */ + var pick = flatRest(function(object, paths) { + return object == null ? {} : basePick(object, paths); + }); + + /** + * Creates an object composed of the `object` properties `predicate` returns + * truthy for. The predicate is invoked with two arguments: (value, key). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The source object. + * @param {Function} [predicate=_.identity] The function invoked per property. + * @returns {Object} Returns the new object. + * @example + * + * var object = { 'a': 1, 'b': '2', 'c': 3 }; + * + * _.pickBy(object, _.isNumber); + * // => { 'a': 1, 'c': 3 } + */ + function pickBy(object, predicate) { + if (object == null) { + return {}; + } + var props = arrayMap(getAllKeysIn(object), function(prop) { + return [prop]; + }); + predicate = getIteratee(predicate); + return basePickBy(object, props, function(value, path) { + return predicate(value, path[0]); + }); + } + + /** + * This method is like `_.get` except that if the resolved value is a + * function it's invoked with the `this` binding of its parent object and + * its result is returned. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path of the property to resolve. + * @param {*} [defaultValue] The value returned for `undefined` resolved values. + * @returns {*} Returns the resolved value. + * @example + * + * var object = { 'a': [{ 'b': { 'c1': 3, 'c2': _.constant(4) } }] }; + * + * _.result(object, 'a[0].b.c1'); + * // => 3 + * + * _.result(object, 'a[0].b.c2'); + * // => 4 + * + * _.result(object, 'a[0].b.c3', 'default'); + * // => 'default' + * + * _.result(object, 'a[0].b.c3', _.constant('default')); + * // => 'default' + */ + function result(object, path, defaultValue) { + path = castPath(path, object); + + var index = -1, + length = path.length; + + // Ensure the loop is entered when path is empty. + if (!length) { + length = 1; + object = undefined; + } + while (++index < length) { + var value = object == null ? undefined : object[toKey(path[index])]; + if (value === undefined) { + index = length; + value = defaultValue; + } + object = isFunction(value) ? value.call(object) : value; + } + return object; + } + + /** + * Sets the value at `path` of `object`. If a portion of `path` doesn't exist, + * it's created. Arrays are created for missing index properties while objects + * are created for all other missing properties. Use `_.setWith` to customize + * `path` creation. + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 3.7.0 + * @category Object + * @param {Object} object The object to modify. + * @param {Array|string} path The path of the property to set. + * @param {*} value The value to set. + * @returns {Object} Returns `object`. + * @example + * + * var object = { 'a': [{ 'b': { 'c': 3 } }] }; + * + * _.set(object, 'a[0].b.c', 4); + * console.log(object.a[0].b.c); + * // => 4 + * + * _.set(object, ['x', '0', 'y', 'z'], 5); + * console.log(object.x[0].y.z); + * // => 5 + */ + function set(object, path, value) { + return object == null ? object : baseSet(object, path, value); + } + + /** + * This method is like `_.set` except that it accepts `customizer` which is + * invoked to produce the objects of `path`. If `customizer` returns `undefined` + * path creation is handled by the method instead. The `customizer` is invoked + * with three arguments: (nsValue, key, nsObject). + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The object to modify. + * @param {Array|string} path The path of the property to set. + * @param {*} value The value to set. + * @param {Function} [customizer] The function to customize assigned values. + * @returns {Object} Returns `object`. + * @example + * + * var object = {}; + * + * _.setWith(object, '[0][1]', 'a', Object); + * // => { '0': { '1': 'a' } } + */ + function setWith(object, path, value, customizer) { + customizer = typeof customizer == 'function' ? customizer : undefined; + return object == null ? object : baseSet(object, path, value, customizer); + } + + /** + * Creates an array of own enumerable string keyed-value pairs for `object` + * which can be consumed by `_.fromPairs`. If `object` is a map or set, its + * entries are returned. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @alias entries + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the key-value pairs. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.toPairs(new Foo); + * // => [['a', 1], ['b', 2]] (iteration order is not guaranteed) + */ + var toPairs = createToPairs(keys); + + /** + * Creates an array of own and inherited enumerable string keyed-value pairs + * for `object` which can be consumed by `_.fromPairs`. If `object` is a map + * or set, its entries are returned. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @alias entriesIn + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the key-value pairs. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.toPairsIn(new Foo); + * // => [['a', 1], ['b', 2], ['c', 3]] (iteration order is not guaranteed) + */ + var toPairsIn = createToPairs(keysIn); + + /** + * An alternative to `_.reduce`; this method transforms `object` to a new + * `accumulator` object which is the result of running each of its own + * enumerable string keyed properties thru `iteratee`, with each invocation + * potentially mutating the `accumulator` object. If `accumulator` is not + * provided, a new object with the same `[[Prototype]]` will be used. The + * iteratee is invoked with four arguments: (accumulator, value, key, object). + * Iteratee functions may exit iteration early by explicitly returning `false`. + * + * @static + * @memberOf _ + * @since 1.3.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {*} [accumulator] The custom accumulator value. + * @returns {*} Returns the accumulated value. + * @example + * + * _.transform([2, 3, 4], function(result, n) { + * result.push(n *= n); + * return n % 2 == 0; + * }, []); + * // => [4, 9] + * + * _.transform({ 'a': 1, 'b': 2, 'c': 1 }, function(result, value, key) { + * (result[value] || (result[value] = [])).push(key); + * }, {}); + * // => { '1': ['a', 'c'], '2': ['b'] } + */ + function transform(object, iteratee, accumulator) { + var isArr = isArray(object), + isArrLike = isArr || isBuffer(object) || isTypedArray(object); + + iteratee = getIteratee(iteratee, 4); + if (accumulator == null) { + var Ctor = object && object.constructor; + if (isArrLike) { + accumulator = isArr ? new Ctor : []; + } + else if (isObject(object)) { + accumulator = isFunction(Ctor) ? baseCreate(getPrototype(object)) : {}; + } + else { + accumulator = {}; + } + } + (isArrLike ? arrayEach : baseForOwn)(object, function(value, index, object) { + return iteratee(accumulator, value, index, object); + }); + return accumulator; + } + + /** + * Removes the property at `path` of `object`. + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The object to modify. + * @param {Array|string} path The path of the property to unset. + * @returns {boolean} Returns `true` if the property is deleted, else `false`. + * @example + * + * var object = { 'a': [{ 'b': { 'c': 7 } }] }; + * _.unset(object, 'a[0].b.c'); + * // => true + * + * console.log(object); + * // => { 'a': [{ 'b': {} }] }; + * + * _.unset(object, ['a', '0', 'b', 'c']); + * // => true + * + * console.log(object); + * // => { 'a': [{ 'b': {} }] }; + */ + function unset(object, path) { + return object == null ? true : baseUnset(object, path); + } + + /** + * This method is like `_.set` except that accepts `updater` to produce the + * value to set. Use `_.updateWith` to customize `path` creation. The `updater` + * is invoked with one argument: (value). + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 4.6.0 + * @category Object + * @param {Object} object The object to modify. + * @param {Array|string} path The path of the property to set. + * @param {Function} updater The function to produce the updated value. + * @returns {Object} Returns `object`. + * @example + * + * var object = { 'a': [{ 'b': { 'c': 3 } }] }; + * + * _.update(object, 'a[0].b.c', function(n) { return n * n; }); + * console.log(object.a[0].b.c); + * // => 9 + * + * _.update(object, 'x[0].y.z', function(n) { return n ? n + 1 : 0; }); + * console.log(object.x[0].y.z); + * // => 0 + */ + function update(object, path, updater) { + return object == null ? object : baseUpdate(object, path, castFunction(updater)); + } + + /** + * This method is like `_.update` except that it accepts `customizer` which is + * invoked to produce the objects of `path`. If `customizer` returns `undefined` + * path creation is handled by the method instead. The `customizer` is invoked + * with three arguments: (nsValue, key, nsObject). + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 4.6.0 + * @category Object + * @param {Object} object The object to modify. + * @param {Array|string} path The path of the property to set. + * @param {Function} updater The function to produce the updated value. + * @param {Function} [customizer] The function to customize assigned values. + * @returns {Object} Returns `object`. + * @example + * + * var object = {}; + * + * _.updateWith(object, '[0][1]', _.constant('a'), Object); + * // => { '0': { '1': 'a' } } + */ + function updateWith(object, path, updater, customizer) { + customizer = typeof customizer == 'function' ? customizer : undefined; + return object == null ? object : baseUpdate(object, path, castFunction(updater), customizer); + } + + /** + * Creates an array of the own enumerable string keyed property values of `object`. + * + * **Note:** Non-object values are coerced to objects. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property values. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.values(new Foo); + * // => [1, 2] (iteration order is not guaranteed) + * + * _.values('hi'); + * // => ['h', 'i'] + */ + function values(object) { + return object == null ? [] : baseValues(object, keys(object)); + } + + /** + * Creates an array of the own and inherited enumerable string keyed property + * values of `object`. + * + * **Note:** Non-object values are coerced to objects. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property values. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.valuesIn(new Foo); + * // => [1, 2, 3] (iteration order is not guaranteed) + */ + function valuesIn(object) { + return object == null ? [] : baseValues(object, keysIn(object)); + } + + /*------------------------------------------------------------------------*/ + + /** + * Clamps `number` within the inclusive `lower` and `upper` bounds. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Number + * @param {number} number The number to clamp. + * @param {number} [lower] The lower bound. + * @param {number} upper The upper bound. + * @returns {number} Returns the clamped number. + * @example + * + * _.clamp(-10, -5, 5); + * // => -5 + * + * _.clamp(10, -5, 5); + * // => 5 + */ + function clamp(number, lower, upper) { + if (upper === undefined) { + upper = lower; + lower = undefined; + } + if (upper !== undefined) { + upper = toNumber(upper); + upper = upper === upper ? upper : 0; + } + if (lower !== undefined) { + lower = toNumber(lower); + lower = lower === lower ? lower : 0; + } + return baseClamp(toNumber(number), lower, upper); + } + + /** + * Checks if `n` is between `start` and up to, but not including, `end`. If + * `end` is not specified, it's set to `start` with `start` then set to `0`. + * If `start` is greater than `end` the params are swapped to support + * negative ranges. + * + * @static + * @memberOf _ + * @since 3.3.0 + * @category Number + * @param {number} number The number to check. + * @param {number} [start=0] The start of the range. + * @param {number} end The end of the range. + * @returns {boolean} Returns `true` if `number` is in the range, else `false`. + * @see _.range, _.rangeRight + * @example + * + * _.inRange(3, 2, 4); + * // => true + * + * _.inRange(4, 8); + * // => true + * + * _.inRange(4, 2); + * // => false + * + * _.inRange(2, 2); + * // => false + * + * _.inRange(1.2, 2); + * // => true + * + * _.inRange(5.2, 4); + * // => false + * + * _.inRange(-3, -2, -6); + * // => true + */ + function inRange(number, start, end) { + start = toFinite(start); + if (end === undefined) { + end = start; + start = 0; + } else { + end = toFinite(end); + } + number = toNumber(number); + return baseInRange(number, start, end); + } + + /** + * Produces a random number between the inclusive `lower` and `upper` bounds. + * If only one argument is provided a number between `0` and the given number + * is returned. If `floating` is `true`, or either `lower` or `upper` are + * floats, a floating-point number is returned instead of an integer. + * + * **Note:** JavaScript follows the IEEE-754 standard for resolving + * floating-point values which can produce unexpected results. + * + * @static + * @memberOf _ + * @since 0.7.0 + * @category Number + * @param {number} [lower=0] The lower bound. + * @param {number} [upper=1] The upper bound. + * @param {boolean} [floating] Specify returning a floating-point number. + * @returns {number} Returns the random number. + * @example + * + * _.random(0, 5); + * // => an integer between 0 and 5 + * + * _.random(5); + * // => also an integer between 0 and 5 + * + * _.random(5, true); + * // => a floating-point number between 0 and 5 + * + * _.random(1.2, 5.2); + * // => a floating-point number between 1.2 and 5.2 + */ + function random(lower, upper, floating) { + if (floating && typeof floating != 'boolean' && isIterateeCall(lower, upper, floating)) { + upper = floating = undefined; + } + if (floating === undefined) { + if (typeof upper == 'boolean') { + floating = upper; + upper = undefined; + } + else if (typeof lower == 'boolean') { + floating = lower; + lower = undefined; + } + } + if (lower === undefined && upper === undefined) { + lower = 0; + upper = 1; + } + else { + lower = toFinite(lower); + if (upper === undefined) { + upper = lower; + lower = 0; + } else { + upper = toFinite(upper); + } + } + if (lower > upper) { + var temp = lower; + lower = upper; + upper = temp; + } + if (floating || lower % 1 || upper % 1) { + var rand = nativeRandom(); + return nativeMin(lower + (rand * (upper - lower + freeParseFloat('1e-' + ((rand + '').length - 1)))), upper); + } + return baseRandom(lower, upper); + } + + /*------------------------------------------------------------------------*/ + + /** + * Converts `string` to [camel case](https://en.wikipedia.org/wiki/CamelCase). + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the camel cased string. + * @example + * + * _.camelCase('Foo Bar'); + * // => 'fooBar' + * + * _.camelCase('--foo-bar--'); + * // => 'fooBar' + * + * _.camelCase('__FOO_BAR__'); + * // => 'fooBar' + */ + var camelCase = createCompounder(function(result, word, index) { + word = word.toLowerCase(); + return result + (index ? capitalize(word) : word); + }); + + /** + * Converts the first character of `string` to upper case and the remaining + * to lower case. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to capitalize. + * @returns {string} Returns the capitalized string. + * @example + * + * _.capitalize('FRED'); + * // => 'Fred' + */ + function capitalize(string) { + return upperFirst(toString(string).toLowerCase()); + } + + /** + * Deburrs `string` by converting + * [Latin-1 Supplement](https://en.wikipedia.org/wiki/Latin-1_Supplement_(Unicode_block)#Character_table) + * and [Latin Extended-A](https://en.wikipedia.org/wiki/Latin_Extended-A) + * letters to basic Latin letters and removing + * [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks). + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to deburr. + * @returns {string} Returns the deburred string. + * @example + * + * _.deburr('déjà vu'); + * // => 'deja vu' + */ + function deburr(string) { + string = toString(string); + return string && string.replace(reLatin, deburrLetter).replace(reComboMark, ''); + } + + /** + * Checks if `string` ends with the given target string. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to inspect. + * @param {string} [target] The string to search for. + * @param {number} [position=string.length] The position to search up to. + * @returns {boolean} Returns `true` if `string` ends with `target`, + * else `false`. + * @example + * + * _.endsWith('abc', 'c'); + * // => true + * + * _.endsWith('abc', 'b'); + * // => false + * + * _.endsWith('abc', 'b', 2); + * // => true + */ + function endsWith(string, target, position) { + string = toString(string); + target = baseToString(target); + + var length = string.length; + position = position === undefined + ? length + : baseClamp(toInteger(position), 0, length); + + var end = position; + position -= target.length; + return position >= 0 && string.slice(position, end) == target; + } + + /** + * Converts the characters "&", "<", ">", '"', and "'" in `string` to their + * corresponding HTML entities. + * + * **Note:** No other characters are escaped. To escape additional + * characters use a third-party library like [_he_](https://mths.be/he). + * + * Though the ">" character is escaped for symmetry, characters like + * ">" and "/" don't need escaping in HTML and have no special meaning + * unless they're part of a tag or unquoted attribute value. See + * [Mathias Bynens's article](https://mathiasbynens.be/notes/ambiguous-ampersands) + * (under "semi-related fun fact") for more details. + * + * When working with HTML you should always + * [quote attribute values](http://wonko.com/post/html-escaping) to reduce + * XSS vectors. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category String + * @param {string} [string=''] The string to escape. + * @returns {string} Returns the escaped string. + * @example + * + * _.escape('fred, barney, & pebbles'); + * // => 'fred, barney, & pebbles' + */ + function escape(string) { + string = toString(string); + return (string && reHasUnescapedHtml.test(string)) + ? string.replace(reUnescapedHtml, escapeHtmlChar) + : string; + } + + /** + * Escapes the `RegExp` special characters "^", "$", "\", ".", "*", "+", + * "?", "(", ")", "[", "]", "{", "}", and "|" in `string`. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to escape. + * @returns {string} Returns the escaped string. + * @example + * + * _.escapeRegExp('[lodash](https://lodash.com/)'); + * // => '\[lodash\]\(https://lodash\.com/\)' + */ + function escapeRegExp(string) { + string = toString(string); + return (string && reHasRegExpChar.test(string)) + ? string.replace(reRegExpChar, '\\$&') + : string; + } + + /** + * Converts `string` to + * [kebab case](https://en.wikipedia.org/wiki/Letter_case#Special_case_styles). + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the kebab cased string. + * @example + * + * _.kebabCase('Foo Bar'); + * // => 'foo-bar' + * + * _.kebabCase('fooBar'); + * // => 'foo-bar' + * + * _.kebabCase('__FOO_BAR__'); + * // => 'foo-bar' + */ + var kebabCase = createCompounder(function(result, word, index) { + return result + (index ? '-' : '') + word.toLowerCase(); + }); + + /** + * Converts `string`, as space separated words, to lower case. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the lower cased string. + * @example + * + * _.lowerCase('--Foo-Bar--'); + * // => 'foo bar' + * + * _.lowerCase('fooBar'); + * // => 'foo bar' + * + * _.lowerCase('__FOO_BAR__'); + * // => 'foo bar' + */ + var lowerCase = createCompounder(function(result, word, index) { + return result + (index ? ' ' : '') + word.toLowerCase(); + }); + + /** + * Converts the first character of `string` to lower case. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the converted string. + * @example + * + * _.lowerFirst('Fred'); + * // => 'fred' + * + * _.lowerFirst('FRED'); + * // => 'fRED' + */ + var lowerFirst = createCaseFirst('toLowerCase'); + + /** + * Pads `string` on the left and right sides if it's shorter than `length`. + * Padding characters are truncated if they can't be evenly divided by `length`. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to pad. + * @param {number} [length=0] The padding length. + * @param {string} [chars=' '] The string used as padding. + * @returns {string} Returns the padded string. + * @example + * + * _.pad('abc', 8); + * // => ' abc ' + * + * _.pad('abc', 8, '_-'); + * // => '_-abc_-_' + * + * _.pad('abc', 3); + * // => 'abc' + */ + function pad(string, length, chars) { + string = toString(string); + length = toInteger(length); + + var strLength = length ? stringSize(string) : 0; + if (!length || strLength >= length) { + return string; + } + var mid = (length - strLength) / 2; + return ( + createPadding(nativeFloor(mid), chars) + + string + + createPadding(nativeCeil(mid), chars) + ); + } + + /** + * Pads `string` on the right side if it's shorter than `length`. Padding + * characters are truncated if they exceed `length`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to pad. + * @param {number} [length=0] The padding length. + * @param {string} [chars=' '] The string used as padding. + * @returns {string} Returns the padded string. + * @example + * + * _.padEnd('abc', 6); + * // => 'abc ' + * + * _.padEnd('abc', 6, '_-'); + * // => 'abc_-_' + * + * _.padEnd('abc', 3); + * // => 'abc' + */ + function padEnd(string, length, chars) { + string = toString(string); + length = toInteger(length); + + var strLength = length ? stringSize(string) : 0; + return (length && strLength < length) + ? (string + createPadding(length - strLength, chars)) + : string; + } + + /** + * Pads `string` on the left side if it's shorter than `length`. Padding + * characters are truncated if they exceed `length`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to pad. + * @param {number} [length=0] The padding length. + * @param {string} [chars=' '] The string used as padding. + * @returns {string} Returns the padded string. + * @example + * + * _.padStart('abc', 6); + * // => ' abc' + * + * _.padStart('abc', 6, '_-'); + * // => '_-_abc' + * + * _.padStart('abc', 3); + * // => 'abc' + */ + function padStart(string, length, chars) { + string = toString(string); + length = toInteger(length); + + var strLength = length ? stringSize(string) : 0; + return (length && strLength < length) + ? (createPadding(length - strLength, chars) + string) + : string; + } + + /** + * Converts `string` to an integer of the specified radix. If `radix` is + * `undefined` or `0`, a `radix` of `10` is used unless `value` is a + * hexadecimal, in which case a `radix` of `16` is used. + * + * **Note:** This method aligns with the + * [ES5 implementation](https://es5.github.io/#x15.1.2.2) of `parseInt`. + * + * @static + * @memberOf _ + * @since 1.1.0 + * @category String + * @param {string} string The string to convert. + * @param {number} [radix=10] The radix to interpret `value` by. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {number} Returns the converted integer. + * @example + * + * _.parseInt('08'); + * // => 8 + * + * _.map(['6', '08', '10'], _.parseInt); + * // => [6, 8, 10] + */ + function parseInt(string, radix, guard) { + if (guard || radix == null) { + radix = 0; + } else if (radix) { + radix = +radix; + } + return nativeParseInt(toString(string).replace(reTrimStart, ''), radix || 0); + } + + /** + * Repeats the given string `n` times. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to repeat. + * @param {number} [n=1] The number of times to repeat the string. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {string} Returns the repeated string. + * @example + * + * _.repeat('*', 3); + * // => '***' + * + * _.repeat('abc', 2); + * // => 'abcabc' + * + * _.repeat('abc', 0); + * // => '' + */ + function repeat(string, n, guard) { + if ((guard ? isIterateeCall(string, n, guard) : n === undefined)) { + n = 1; + } else { + n = toInteger(n); + } + return baseRepeat(toString(string), n); + } + + /** + * Replaces matches for `pattern` in `string` with `replacement`. + * + * **Note:** This method is based on + * [`String#replace`](https://mdn.io/String/replace). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to modify. + * @param {RegExp|string} pattern The pattern to replace. + * @param {Function|string} replacement The match replacement. + * @returns {string} Returns the modified string. + * @example + * + * _.replace('Hi Fred', 'Fred', 'Barney'); + * // => 'Hi Barney' + */ + function replace() { + var args = arguments, + string = toString(args[0]); + + return args.length < 3 ? string : string.replace(args[1], args[2]); + } + + /** + * Converts `string` to + * [snake case](https://en.wikipedia.org/wiki/Snake_case). + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the snake cased string. + * @example + * + * _.snakeCase('Foo Bar'); + * // => 'foo_bar' + * + * _.snakeCase('fooBar'); + * // => 'foo_bar' + * + * _.snakeCase('--FOO-BAR--'); + * // => 'foo_bar' + */ + var snakeCase = createCompounder(function(result, word, index) { + return result + (index ? '_' : '') + word.toLowerCase(); + }); + + /** + * Splits `string` by `separator`. + * + * **Note:** This method is based on + * [`String#split`](https://mdn.io/String/split). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to split. + * @param {RegExp|string} separator The separator pattern to split by. + * @param {number} [limit] The length to truncate results to. + * @returns {Array} Returns the string segments. + * @example + * + * _.split('a-b-c', '-', 2); + * // => ['a', 'b'] + */ + function split(string, separator, limit) { + if (limit && typeof limit != 'number' && isIterateeCall(string, separator, limit)) { + separator = limit = undefined; + } + limit = limit === undefined ? MAX_ARRAY_LENGTH : limit >>> 0; + if (!limit) { + return []; + } + string = toString(string); + if (string && ( + typeof separator == 'string' || + (separator != null && !isRegExp(separator)) + )) { + separator = baseToString(separator); + if (!separator && hasUnicode(string)) { + return castSlice(stringToArray(string), 0, limit); + } + } + return string.split(separator, limit); + } + + /** + * Converts `string` to + * [start case](https://en.wikipedia.org/wiki/Letter_case#Stylistic_or_specialised_usage). + * + * @static + * @memberOf _ + * @since 3.1.0 + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the start cased string. + * @example + * + * _.startCase('--foo-bar--'); + * // => 'Foo Bar' + * + * _.startCase('fooBar'); + * // => 'Foo Bar' + * + * _.startCase('__FOO_BAR__'); + * // => 'FOO BAR' + */ + var startCase = createCompounder(function(result, word, index) { + return result + (index ? ' ' : '') + upperFirst(word); + }); + + /** + * Checks if `string` starts with the given target string. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to inspect. + * @param {string} [target] The string to search for. + * @param {number} [position=0] The position to search from. + * @returns {boolean} Returns `true` if `string` starts with `target`, + * else `false`. + * @example + * + * _.startsWith('abc', 'a'); + * // => true + * + * _.startsWith('abc', 'b'); + * // => false + * + * _.startsWith('abc', 'b', 1); + * // => true + */ + function startsWith(string, target, position) { + string = toString(string); + position = position == null + ? 0 + : baseClamp(toInteger(position), 0, string.length); + + target = baseToString(target); + return string.slice(position, position + target.length) == target; + } + + /** + * Creates a compiled template function that can interpolate data properties + * in "interpolate" delimiters, HTML-escape interpolated data properties in + * "escape" delimiters, and execute JavaScript in "evaluate" delimiters. Data + * properties may be accessed as free variables in the template. If a setting + * object is given, it takes precedence over `_.templateSettings` values. + * + * **Note:** In the development build `_.template` utilizes + * [sourceURLs](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl) + * for easier debugging. + * + * For more information on precompiling templates see + * [lodash's custom builds documentation](https://lodash.com/custom-builds). + * + * For more information on Chrome extension sandboxes see + * [Chrome's extensions documentation](https://developer.chrome.com/extensions/sandboxingEval). + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category String + * @param {string} [string=''] The template string. + * @param {Object} [options={}] The options object. + * @param {RegExp} [options.escape=_.templateSettings.escape] + * The HTML "escape" delimiter. + * @param {RegExp} [options.evaluate=_.templateSettings.evaluate] + * The "evaluate" delimiter. + * @param {Object} [options.imports=_.templateSettings.imports] + * An object to import into the template as free variables. + * @param {RegExp} [options.interpolate=_.templateSettings.interpolate] + * The "interpolate" delimiter. + * @param {string} [options.sourceURL='lodash.templateSources[n]'] + * The sourceURL of the compiled template. + * @param {string} [options.variable='obj'] + * The data object variable name. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {Function} Returns the compiled template function. + * @example + * + * // Use the "interpolate" delimiter to create a compiled template. + * var compiled = _.template('hello <%= user %>!'); + * compiled({ 'user': 'fred' }); + * // => 'hello fred!' + * + * // Use the HTML "escape" delimiter to escape data property values. + * var compiled = _.template('<b><%- value %></b>'); + * compiled({ 'value': '<script>' }); + * // => '<b><script></b>' + * + * // Use the "evaluate" delimiter to execute JavaScript and generate HTML. + * var compiled = _.template('<% _.forEach(users, function(user) { %><li><%- user %></li><% }); %>'); + * compiled({ 'users': ['fred', 'barney'] }); + * // => '<li>fred</li><li>barney</li>' + * + * // Use the internal `print` function in "evaluate" delimiters. + * var compiled = _.template('<% print("hello " + user); %>!'); + * compiled({ 'user': 'barney' }); + * // => 'hello barney!' + * + * // Use the ES template literal delimiter as an "interpolate" delimiter. + * // Disable support by replacing the "interpolate" delimiter. + * var compiled = _.template('hello ${ user }!'); + * compiled({ 'user': 'pebbles' }); + * // => 'hello pebbles!' + * + * // Use backslashes to treat delimiters as plain text. + * var compiled = _.template('<%= "\\<%- value %\\>" %>'); + * compiled({ 'value': 'ignored' }); + * // => '<%- value %>' + * + * // Use the `imports` option to import `jQuery` as `jq`. + * var text = '<% jq.each(users, function(user) { %><li><%- user %></li><% }); %>'; + * var compiled = _.template(text, { 'imports': { 'jq': jQuery } }); + * compiled({ 'users': ['fred', 'barney'] }); + * // => '<li>fred</li><li>barney</li>' + * + * // Use the `sourceURL` option to specify a custom sourceURL for the template. + * var compiled = _.template('hello <%= user %>!', { 'sourceURL': '/basic/greeting.jst' }); + * compiled(data); + * // => Find the source of "greeting.jst" under the Sources tab or Resources panel of the web inspector. + * + * // Use the `variable` option to ensure a with-statement isn't used in the compiled template. + * var compiled = _.template('hi <%= data.user %>!', { 'variable': 'data' }); + * compiled.source; + * // => function(data) { + * // var __t, __p = ''; + * // __p += 'hi ' + ((__t = ( data.user )) == null ? '' : __t) + '!'; + * // return __p; + * // } + * + * // Use custom template delimiters. + * _.templateSettings.interpolate = /{{([\s\S]+?)}}/g; + * var compiled = _.template('hello {{ user }}!'); + * compiled({ 'user': 'mustache' }); + * // => 'hello mustache!' + * + * // Use the `source` property to inline compiled templates for meaningful + * // line numbers in error messages and stack traces. + * fs.writeFileSync(path.join(process.cwd(), 'jst.js'), '\ + * var JST = {\ + * "main": ' + _.template(mainText).source + '\ + * };\ + * '); + */ + function template(string, options, guard) { + // Based on John Resig's `tmpl` implementation + // (http://ejohn.org/blog/javascript-micro-templating/) + // and Laura Doktorova's doT.js (https://github.com/olado/doT). + var settings = lodash.templateSettings; + + if (guard && isIterateeCall(string, options, guard)) { + options = undefined; + } + string = toString(string); + options = assignInWith({}, options, settings, customDefaultsAssignIn); + + var imports = assignInWith({}, options.imports, settings.imports, customDefaultsAssignIn), + importsKeys = keys(imports), + importsValues = baseValues(imports, importsKeys); + + var isEscaping, + isEvaluating, + index = 0, + interpolate = options.interpolate || reNoMatch, + source = "__p += '"; + + // Compile the regexp to match each delimiter. + var reDelimiters = RegExp( + (options.escape || reNoMatch).source + '|' + + interpolate.source + '|' + + (interpolate === reInterpolate ? reEsTemplate : reNoMatch).source + '|' + + (options.evaluate || reNoMatch).source + '|$' + , 'g'); + + // Use a sourceURL for easier debugging. + var sourceURL = '//# sourceURL=' + + ('sourceURL' in options + ? options.sourceURL + : ('lodash.templateSources[' + (++templateCounter) + ']') + ) + '\n'; + + string.replace(reDelimiters, function(match, escapeValue, interpolateValue, esTemplateValue, evaluateValue, offset) { + interpolateValue || (interpolateValue = esTemplateValue); + + // Escape characters that can't be included in string literals. + source += string.slice(index, offset).replace(reUnescapedString, escapeStringChar); + + // Replace delimiters with snippets. + if (escapeValue) { + isEscaping = true; + source += "' +\n__e(" + escapeValue + ") +\n'"; + } + if (evaluateValue) { + isEvaluating = true; + source += "';\n" + evaluateValue + ";\n__p += '"; + } + if (interpolateValue) { + source += "' +\n((__t = (" + interpolateValue + ")) == null ? '' : __t) +\n'"; + } + index = offset + match.length; + + // The JS engine embedded in Adobe products needs `match` returned in + // order to produce the correct `offset` value. + return match; + }); + + source += "';\n"; + + // If `variable` is not specified wrap a with-statement around the generated + // code to add the data object to the top of the scope chain. + var variable = options.variable; + if (!variable) { + source = 'with (obj) {\n' + source + '\n}\n'; + } + // Cleanup code by stripping empty strings. + source = (isEvaluating ? source.replace(reEmptyStringLeading, '') : source) + .replace(reEmptyStringMiddle, '$1') + .replace(reEmptyStringTrailing, '$1;'); + + // Frame code as the function body. + source = 'function(' + (variable || 'obj') + ') {\n' + + (variable + ? '' + : 'obj || (obj = {});\n' + ) + + "var __t, __p = ''" + + (isEscaping + ? ', __e = _.escape' + : '' + ) + + (isEvaluating + ? ', __j = Array.prototype.join;\n' + + "function print() { __p += __j.call(arguments, '') }\n" + : ';\n' + ) + + source + + 'return __p\n}'; + + var result = attempt(function() { + return Function(importsKeys, sourceURL + 'return ' + source) + .apply(undefined, importsValues); + }); + + // Provide the compiled function's source by its `toString` method or + // the `source` property as a convenience for inlining compiled templates. + result.source = source; + if (isError(result)) { + throw result; + } + return result; + } + + /** + * Converts `string`, as a whole, to lower case just like + * [String#toLowerCase](https://mdn.io/toLowerCase). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the lower cased string. + * @example + * + * _.toLower('--Foo-Bar--'); + * // => '--foo-bar--' + * + * _.toLower('fooBar'); + * // => 'foobar' + * + * _.toLower('__FOO_BAR__'); + * // => '__foo_bar__' + */ + function toLower(value) { + return toString(value).toLowerCase(); + } + + /** + * Converts `string`, as a whole, to upper case just like + * [String#toUpperCase](https://mdn.io/toUpperCase). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the upper cased string. + * @example + * + * _.toUpper('--foo-bar--'); + * // => '--FOO-BAR--' + * + * _.toUpper('fooBar'); + * // => 'FOOBAR' + * + * _.toUpper('__foo_bar__'); + * // => '__FOO_BAR__' + */ + function toUpper(value) { + return toString(value).toUpperCase(); + } + + /** + * Removes leading and trailing whitespace or specified characters from `string`. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to trim. + * @param {string} [chars=whitespace] The characters to trim. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {string} Returns the trimmed string. + * @example + * + * _.trim(' abc '); + * // => 'abc' + * + * _.trim('-_-abc-_-', '_-'); + * // => 'abc' + * + * _.map([' foo ', ' bar '], _.trim); + * // => ['foo', 'bar'] + */ + function trim(string, chars, guard) { + string = toString(string); + if (string && (guard || chars === undefined)) { + return string.replace(reTrim, ''); + } + if (!string || !(chars = baseToString(chars))) { + return string; + } + var strSymbols = stringToArray(string), + chrSymbols = stringToArray(chars), + start = charsStartIndex(strSymbols, chrSymbols), + end = charsEndIndex(strSymbols, chrSymbols) + 1; + + return castSlice(strSymbols, start, end).join(''); + } + + /** + * Removes trailing whitespace or specified characters from `string`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to trim. + * @param {string} [chars=whitespace] The characters to trim. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {string} Returns the trimmed string. + * @example + * + * _.trimEnd(' abc '); + * // => ' abc' + * + * _.trimEnd('-_-abc-_-', '_-'); + * // => '-_-abc' + */ + function trimEnd(string, chars, guard) { + string = toString(string); + if (string && (guard || chars === undefined)) { + return string.replace(reTrimEnd, ''); + } + if (!string || !(chars = baseToString(chars))) { + return string; + } + var strSymbols = stringToArray(string), + end = charsEndIndex(strSymbols, stringToArray(chars)) + 1; + + return castSlice(strSymbols, 0, end).join(''); + } + + /** + * Removes leading whitespace or specified characters from `string`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to trim. + * @param {string} [chars=whitespace] The characters to trim. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {string} Returns the trimmed string. + * @example + * + * _.trimStart(' abc '); + * // => 'abc ' + * + * _.trimStart('-_-abc-_-', '_-'); + * // => 'abc-_-' + */ + function trimStart(string, chars, guard) { + string = toString(string); + if (string && (guard || chars === undefined)) { + return string.replace(reTrimStart, ''); + } + if (!string || !(chars = baseToString(chars))) { + return string; + } + var strSymbols = stringToArray(string), + start = charsStartIndex(strSymbols, stringToArray(chars)); + + return castSlice(strSymbols, start).join(''); + } + + /** + * Truncates `string` if it's longer than the given maximum string length. + * The last characters of the truncated string are replaced with the omission + * string which defaults to "...". + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to truncate. + * @param {Object} [options={}] The options object. + * @param {number} [options.length=30] The maximum string length. + * @param {string} [options.omission='...'] The string to indicate text is omitted. + * @param {RegExp|string} [options.separator] The separator pattern to truncate to. + * @returns {string} Returns the truncated string. + * @example + * + * _.truncate('hi-diddly-ho there, neighborino'); + * // => 'hi-diddly-ho there, neighbo...' + * + * _.truncate('hi-diddly-ho there, neighborino', { + * 'length': 24, + * 'separator': ' ' + * }); + * // => 'hi-diddly-ho there,...' + * + * _.truncate('hi-diddly-ho there, neighborino', { + * 'length': 24, + * 'separator': /,? +/ + * }); + * // => 'hi-diddly-ho there...' + * + * _.truncate('hi-diddly-ho there, neighborino', { + * 'omission': ' [...]' + * }); + * // => 'hi-diddly-ho there, neig [...]' + */ + function truncate(string, options) { + var length = DEFAULT_TRUNC_LENGTH, + omission = DEFAULT_TRUNC_OMISSION; + + if (isObject(options)) { + var separator = 'separator' in options ? options.separator : separator; + length = 'length' in options ? toInteger(options.length) : length; + omission = 'omission' in options ? baseToString(options.omission) : omission; + } + string = toString(string); + + var strLength = string.length; + if (hasUnicode(string)) { + var strSymbols = stringToArray(string); + strLength = strSymbols.length; + } + if (length >= strLength) { + return string; + } + var end = length - stringSize(omission); + if (end < 1) { + return omission; + } + var result = strSymbols + ? castSlice(strSymbols, 0, end).join('') + : string.slice(0, end); + + if (separator === undefined) { + return result + omission; + } + if (strSymbols) { + end += (result.length - end); + } + if (isRegExp(separator)) { + if (string.slice(end).search(separator)) { + var match, + substring = result; + + if (!separator.global) { + separator = RegExp(separator.source, toString(reFlags.exec(separator)) + 'g'); + } + separator.lastIndex = 0; + while ((match = separator.exec(substring))) { + var newEnd = match.index; + } + result = result.slice(0, newEnd === undefined ? end : newEnd); + } + } else if (string.indexOf(baseToString(separator), end) != end) { + var index = result.lastIndexOf(separator); + if (index > -1) { + result = result.slice(0, index); + } + } + return result + omission; + } + + /** + * The inverse of `_.escape`; this method converts the HTML entities + * `&`, `<`, `>`, `"`, and `'` in `string` to + * their corresponding characters. + * + * **Note:** No other HTML entities are unescaped. To unescape additional + * HTML entities use a third-party library like [_he_](https://mths.be/he). + * + * @static + * @memberOf _ + * @since 0.6.0 + * @category String + * @param {string} [string=''] The string to unescape. + * @returns {string} Returns the unescaped string. + * @example + * + * _.unescape('fred, barney, & pebbles'); + * // => 'fred, barney, & pebbles' + */ + function unescape(string) { + string = toString(string); + return (string && reHasEscapedHtml.test(string)) + ? string.replace(reEscapedHtml, unescapeHtmlChar) + : string; + } + + /** + * Converts `string`, as space separated words, to upper case. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the upper cased string. + * @example + * + * _.upperCase('--foo-bar'); + * // => 'FOO BAR' + * + * _.upperCase('fooBar'); + * // => 'FOO BAR' + * + * _.upperCase('__foo_bar__'); + * // => 'FOO BAR' + */ + var upperCase = createCompounder(function(result, word, index) { + return result + (index ? ' ' : '') + word.toUpperCase(); + }); + + /** + * Converts the first character of `string` to upper case. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the converted string. + * @example + * + * _.upperFirst('fred'); + * // => 'Fred' + * + * _.upperFirst('FRED'); + * // => 'FRED' + */ + var upperFirst = createCaseFirst('toUpperCase'); + + /** + * Splits `string` into an array of its words. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to inspect. + * @param {RegExp|string} [pattern] The pattern to match words. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {Array} Returns the words of `string`. + * @example + * + * _.words('fred, barney, & pebbles'); + * // => ['fred', 'barney', 'pebbles'] + * + * _.words('fred, barney, & pebbles', /[^, ]+/g); + * // => ['fred', 'barney', '&', 'pebbles'] + */ + function words(string, pattern, guard) { + string = toString(string); + pattern = guard ? undefined : pattern; + + if (pattern === undefined) { + return hasUnicodeWord(string) ? unicodeWords(string) : asciiWords(string); + } + return string.match(pattern) || []; + } + + /*------------------------------------------------------------------------*/ + + /** + * Attempts to invoke `func`, returning either the result or the caught error + * object. Any additional arguments are provided to `func` when it's invoked. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Util + * @param {Function} func The function to attempt. + * @param {...*} [args] The arguments to invoke `func` with. + * @returns {*} Returns the `func` result or error object. + * @example + * + * // Avoid throwing errors for invalid selectors. + * var elements = _.attempt(function(selector) { + * return document.querySelectorAll(selector); + * }, '>_>'); + * + * if (_.isError(elements)) { + * elements = []; + * } + */ + var attempt = baseRest(function(func, args) { + try { + return apply(func, undefined, args); + } catch (e) { + return isError(e) ? e : new Error(e); + } + }); + + /** + * Binds methods of an object to the object itself, overwriting the existing + * method. + * + * **Note:** This method doesn't set the "length" property of bound functions. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Util + * @param {Object} object The object to bind and assign the bound methods to. + * @param {...(string|string[])} methodNames The object method names to bind. + * @returns {Object} Returns `object`. + * @example + * + * var view = { + * 'label': 'docs', + * 'click': function() { + * console.log('clicked ' + this.label); + * } + * }; + * + * _.bindAll(view, ['click']); + * jQuery(element).on('click', view.click); + * // => Logs 'clicked docs' when clicked. + */ + var bindAll = flatRest(function(object, methodNames) { + arrayEach(methodNames, function(key) { + key = toKey(key); + baseAssignValue(object, key, bind(object[key], object)); + }); + return object; + }); + + /** + * Creates a function that iterates over `pairs` and invokes the corresponding + * function of the first predicate to return truthy. The predicate-function + * pairs are invoked with the `this` binding and arguments of the created + * function. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Util + * @param {Array} pairs The predicate-function pairs. + * @returns {Function} Returns the new composite function. + * @example + * + * var func = _.cond([ + * [_.matches({ 'a': 1 }), _.constant('matches A')], + * [_.conforms({ 'b': _.isNumber }), _.constant('matches B')], + * [_.stubTrue, _.constant('no match')] + * ]); + * + * func({ 'a': 1, 'b': 2 }); + * // => 'matches A' + * + * func({ 'a': 0, 'b': 1 }); + * // => 'matches B' + * + * func({ 'a': '1', 'b': '2' }); + * // => 'no match' + */ + function cond(pairs) { + var length = pairs == null ? 0 : pairs.length, + toIteratee = getIteratee(); + + pairs = !length ? [] : arrayMap(pairs, function(pair) { + if (typeof pair[1] != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + return [toIteratee(pair[0]), pair[1]]; + }); + + return baseRest(function(args) { + var index = -1; + while (++index < length) { + var pair = pairs[index]; + if (apply(pair[0], this, args)) { + return apply(pair[1], this, args); + } + } + }); + } + + /** + * Creates a function that invokes the predicate properties of `source` with + * the corresponding property values of a given object, returning `true` if + * all predicates return truthy, else `false`. + * + * **Note:** The created function is equivalent to `_.conformsTo` with + * `source` partially applied. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Util + * @param {Object} source The object of property predicates to conform to. + * @returns {Function} Returns the new spec function. + * @example + * + * var objects = [ + * { 'a': 2, 'b': 1 }, + * { 'a': 1, 'b': 2 } + * ]; + * + * _.filter(objects, _.conforms({ 'b': function(n) { return n > 1; } })); + * // => [{ 'a': 1, 'b': 2 }] + */ + function conforms(source) { + return baseConforms(baseClone(source, CLONE_DEEP_FLAG)); + } + + /** + * Creates a function that returns `value`. + * + * @static + * @memberOf _ + * @since 2.4.0 + * @category Util + * @param {*} value The value to return from the new function. + * @returns {Function} Returns the new constant function. + * @example + * + * var objects = _.times(2, _.constant({ 'a': 1 })); + * + * console.log(objects); + * // => [{ 'a': 1 }, { 'a': 1 }] + * + * console.log(objects[0] === objects[1]); + * // => true + */ + function constant(value) { + return function() { + return value; + }; + } + + /** + * Checks `value` to determine whether a default value should be returned in + * its place. The `defaultValue` is returned if `value` is `NaN`, `null`, + * or `undefined`. + * + * @static + * @memberOf _ + * @since 4.14.0 + * @category Util + * @param {*} value The value to check. + * @param {*} defaultValue The default value. + * @returns {*} Returns the resolved value. + * @example + * + * _.defaultTo(1, 10); + * // => 1 + * + * _.defaultTo(undefined, 10); + * // => 10 + */ + function defaultTo(value, defaultValue) { + return (value == null || value !== value) ? defaultValue : value; + } + + /** + * Creates a function that returns the result of invoking the given functions + * with the `this` binding of the created function, where each successive + * invocation is supplied the return value of the previous. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Util + * @param {...(Function|Function[])} [funcs] The functions to invoke. + * @returns {Function} Returns the new composite function. + * @see _.flowRight + * @example + * + * function square(n) { + * return n * n; + * } + * + * var addSquare = _.flow([_.add, square]); + * addSquare(1, 2); + * // => 9 + */ + var flow = createFlow(); + + /** + * This method is like `_.flow` except that it creates a function that + * invokes the given functions from right to left. + * + * @static + * @since 3.0.0 + * @memberOf _ + * @category Util + * @param {...(Function|Function[])} [funcs] The functions to invoke. + * @returns {Function} Returns the new composite function. + * @see _.flow + * @example + * + * function square(n) { + * return n * n; + * } + * + * var addSquare = _.flowRight([square, _.add]); + * addSquare(1, 2); + * // => 9 + */ + var flowRight = createFlow(true); + + /** + * This method returns the first argument it receives. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Util + * @param {*} value Any value. + * @returns {*} Returns `value`. + * @example + * + * var object = { 'a': 1 }; + * + * console.log(_.identity(object) === object); + * // => true + */ + function identity(value) { + return value; + } + + /** + * Creates a function that invokes `func` with the arguments of the created + * function. If `func` is a property name, the created function returns the + * property value for a given element. If `func` is an array or object, the + * created function returns `true` for elements that contain the equivalent + * source properties, otherwise it returns `false`. + * + * @static + * @since 4.0.0 + * @memberOf _ + * @category Util + * @param {*} [func=_.identity] The value to convert to a callback. + * @returns {Function} Returns the callback. + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': true }, + * { 'user': 'fred', 'age': 40, 'active': false } + * ]; + * + * // The `_.matches` iteratee shorthand. + * _.filter(users, _.iteratee({ 'user': 'barney', 'active': true })); + * // => [{ 'user': 'barney', 'age': 36, 'active': true }] + * + * // The `_.matchesProperty` iteratee shorthand. + * _.filter(users, _.iteratee(['user', 'fred'])); + * // => [{ 'user': 'fred', 'age': 40 }] + * + * // The `_.property` iteratee shorthand. + * _.map(users, _.iteratee('user')); + * // => ['barney', 'fred'] + * + * // Create custom iteratee shorthands. + * _.iteratee = _.wrap(_.iteratee, function(iteratee, func) { + * return !_.isRegExp(func) ? iteratee(func) : function(string) { + * return func.test(string); + * }; + * }); + * + * _.filter(['abc', 'def'], /ef/); + * // => ['def'] + */ + function iteratee(func) { + return baseIteratee(typeof func == 'function' ? func : baseClone(func, CLONE_DEEP_FLAG)); + } + + /** + * Creates a function that performs a partial deep comparison between a given + * object and `source`, returning `true` if the given object has equivalent + * property values, else `false`. + * + * **Note:** The created function is equivalent to `_.isMatch` with `source` + * partially applied. + * + * Partial comparisons will match empty array and empty object `source` + * values against any array or object value, respectively. See `_.isEqual` + * for a list of supported value comparisons. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Util + * @param {Object} source The object of property values to match. + * @returns {Function} Returns the new spec function. + * @example + * + * var objects = [ + * { 'a': 1, 'b': 2, 'c': 3 }, + * { 'a': 4, 'b': 5, 'c': 6 } + * ]; + * + * _.filter(objects, _.matches({ 'a': 4, 'c': 6 })); + * // => [{ 'a': 4, 'b': 5, 'c': 6 }] + */ + function matches(source) { + return baseMatches(baseClone(source, CLONE_DEEP_FLAG)); + } + + /** + * Creates a function that performs a partial deep comparison between the + * value at `path` of a given object to `srcValue`, returning `true` if the + * object value is equivalent, else `false`. + * + * **Note:** Partial comparisons will match empty array and empty object + * `srcValue` values against any array or object value, respectively. See + * `_.isEqual` for a list of supported value comparisons. + * + * @static + * @memberOf _ + * @since 3.2.0 + * @category Util + * @param {Array|string} path The path of the property to get. + * @param {*} srcValue The value to match. + * @returns {Function} Returns the new spec function. + * @example + * + * var objects = [ + * { 'a': 1, 'b': 2, 'c': 3 }, + * { 'a': 4, 'b': 5, 'c': 6 } + * ]; + * + * _.find(objects, _.matchesProperty('a', 4)); + * // => { 'a': 4, 'b': 5, 'c': 6 } + */ + function matchesProperty(path, srcValue) { + return baseMatchesProperty(path, baseClone(srcValue, CLONE_DEEP_FLAG)); + } + + /** + * Creates a function that invokes the method at `path` of a given object. + * Any additional arguments are provided to the invoked method. + * + * @static + * @memberOf _ + * @since 3.7.0 + * @category Util + * @param {Array|string} path The path of the method to invoke. + * @param {...*} [args] The arguments to invoke the method with. + * @returns {Function} Returns the new invoker function. + * @example + * + * var objects = [ + * { 'a': { 'b': _.constant(2) } }, + * { 'a': { 'b': _.constant(1) } } + * ]; + * + * _.map(objects, _.method('a.b')); + * // => [2, 1] + * + * _.map(objects, _.method(['a', 'b'])); + * // => [2, 1] + */ + var method = baseRest(function(path, args) { + return function(object) { + return baseInvoke(object, path, args); + }; + }); + + /** + * The opposite of `_.method`; this method creates a function that invokes + * the method at a given path of `object`. Any additional arguments are + * provided to the invoked method. + * + * @static + * @memberOf _ + * @since 3.7.0 + * @category Util + * @param {Object} object The object to query. + * @param {...*} [args] The arguments to invoke the method with. + * @returns {Function} Returns the new invoker function. + * @example + * + * var array = _.times(3, _.constant), + * object = { 'a': array, 'b': array, 'c': array }; + * + * _.map(['a[2]', 'c[0]'], _.methodOf(object)); + * // => [2, 0] + * + * _.map([['a', '2'], ['c', '0']], _.methodOf(object)); + * // => [2, 0] + */ + var methodOf = baseRest(function(object, args) { + return function(path) { + return baseInvoke(object, path, args); + }; + }); + + /** + * Adds all own enumerable string keyed function properties of a source + * object to the destination object. If `object` is a function, then methods + * are added to its prototype as well. + * + * **Note:** Use `_.runInContext` to create a pristine `lodash` function to + * avoid conflicts caused by modifying the original. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Util + * @param {Function|Object} [object=lodash] The destination object. + * @param {Object} source The object of functions to add. + * @param {Object} [options={}] The options object. + * @param {boolean} [options.chain=true] Specify whether mixins are chainable. + * @returns {Function|Object} Returns `object`. + * @example + * + * function vowels(string) { + * return _.filter(string, function(v) { + * return /[aeiou]/i.test(v); + * }); + * } + * + * _.mixin({ 'vowels': vowels }); + * _.vowels('fred'); + * // => ['e'] + * + * _('fred').vowels().value(); + * // => ['e'] + * + * _.mixin({ 'vowels': vowels }, { 'chain': false }); + * _('fred').vowels(); + * // => ['e'] + */ + function mixin(object, source, options) { + var props = keys(source), + methodNames = baseFunctions(source, props); + + if (options == null && + !(isObject(source) && (methodNames.length || !props.length))) { + options = source; + source = object; + object = this; + methodNames = baseFunctions(source, keys(source)); + } + var chain = !(isObject(options) && 'chain' in options) || !!options.chain, + isFunc = isFunction(object); + + arrayEach(methodNames, function(methodName) { + var func = source[methodName]; + object[methodName] = func; + if (isFunc) { + object.prototype[methodName] = function() { + var chainAll = this.__chain__; + if (chain || chainAll) { + var result = object(this.__wrapped__), + actions = result.__actions__ = copyArray(this.__actions__); + + actions.push({ 'func': func, 'args': arguments, 'thisArg': object }); + result.__chain__ = chainAll; + return result; + } + return func.apply(object, arrayPush([this.value()], arguments)); + }; + } + }); + + return object; + } + + /** + * Reverts the `_` variable to its previous value and returns a reference to + * the `lodash` function. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Util + * @returns {Function} Returns the `lodash` function. + * @example + * + * var lodash = _.noConflict(); + */ + function noConflict() { + if (root._ === this) { + root._ = oldDash; + } + return this; + } + + /** + * This method returns `undefined`. + * + * @static + * @memberOf _ + * @since 2.3.0 + * @category Util + * @example + * + * _.times(2, _.noop); + * // => [undefined, undefined] + */ + function noop() { + // No operation performed. + } + + /** + * Creates a function that gets the argument at index `n`. If `n` is negative, + * the nth argument from the end is returned. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Util + * @param {number} [n=0] The index of the argument to return. + * @returns {Function} Returns the new pass-thru function. + * @example + * + * var func = _.nthArg(1); + * func('a', 'b', 'c', 'd'); + * // => 'b' + * + * var func = _.nthArg(-2); + * func('a', 'b', 'c', 'd'); + * // => 'c' + */ + function nthArg(n) { + n = toInteger(n); + return baseRest(function(args) { + return baseNth(args, n); + }); + } + + /** + * Creates a function that invokes `iteratees` with the arguments it receives + * and returns their results. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Util + * @param {...(Function|Function[])} [iteratees=[_.identity]] + * The iteratees to invoke. + * @returns {Function} Returns the new function. + * @example + * + * var func = _.over([Math.max, Math.min]); + * + * func(1, 2, 3, 4); + * // => [4, 1] + */ + var over = createOver(arrayMap); + + /** + * Creates a function that checks if **all** of the `predicates` return + * truthy when invoked with the arguments it receives. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Util + * @param {...(Function|Function[])} [predicates=[_.identity]] + * The predicates to check. + * @returns {Function} Returns the new function. + * @example + * + * var func = _.overEvery([Boolean, isFinite]); + * + * func('1'); + * // => true + * + * func(null); + * // => false + * + * func(NaN); + * // => false + */ + var overEvery = createOver(arrayEvery); + + /** + * Creates a function that checks if **any** of the `predicates` return + * truthy when invoked with the arguments it receives. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Util + * @param {...(Function|Function[])} [predicates=[_.identity]] + * The predicates to check. + * @returns {Function} Returns the new function. + * @example + * + * var func = _.overSome([Boolean, isFinite]); + * + * func('1'); + * // => true + * + * func(null); + * // => true + * + * func(NaN); + * // => false + */ + var overSome = createOver(arraySome); + + /** + * Creates a function that returns the value at `path` of a given object. + * + * @static + * @memberOf _ + * @since 2.4.0 + * @category Util + * @param {Array|string} path The path of the property to get. + * @returns {Function} Returns the new accessor function. + * @example + * + * var objects = [ + * { 'a': { 'b': 2 } }, + * { 'a': { 'b': 1 } } + * ]; + * + * _.map(objects, _.property('a.b')); + * // => [2, 1] + * + * _.map(_.sortBy(objects, _.property(['a', 'b'])), 'a.b'); + * // => [1, 2] + */ + function property(path) { + return isKey(path) ? baseProperty(toKey(path)) : basePropertyDeep(path); + } + + /** + * The opposite of `_.property`; this method creates a function that returns + * the value at a given path of `object`. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Util + * @param {Object} object The object to query. + * @returns {Function} Returns the new accessor function. + * @example + * + * var array = [0, 1, 2], + * object = { 'a': array, 'b': array, 'c': array }; + * + * _.map(['a[2]', 'c[0]'], _.propertyOf(object)); + * // => [2, 0] + * + * _.map([['a', '2'], ['c', '0']], _.propertyOf(object)); + * // => [2, 0] + */ + function propertyOf(object) { + return function(path) { + return object == null ? undefined : baseGet(object, path); + }; + } + + /** + * Creates an array of numbers (positive and/or negative) progressing from + * `start` up to, but not including, `end`. A step of `-1` is used if a negative + * `start` is specified without an `end` or `step`. If `end` is not specified, + * it's set to `start` with `start` then set to `0`. + * + * **Note:** JavaScript follows the IEEE-754 standard for resolving + * floating-point values which can produce unexpected results. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Util + * @param {number} [start=0] The start of the range. + * @param {number} end The end of the range. + * @param {number} [step=1] The value to increment or decrement by. + * @returns {Array} Returns the range of numbers. + * @see _.inRange, _.rangeRight + * @example + * + * _.range(4); + * // => [0, 1, 2, 3] + * + * _.range(-4); + * // => [0, -1, -2, -3] + * + * _.range(1, 5); + * // => [1, 2, 3, 4] + * + * _.range(0, 20, 5); + * // => [0, 5, 10, 15] + * + * _.range(0, -4, -1); + * // => [0, -1, -2, -3] + * + * _.range(1, 4, 0); + * // => [1, 1, 1] + * + * _.range(0); + * // => [] + */ + var range = createRange(); + + /** + * This method is like `_.range` except that it populates values in + * descending order. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Util + * @param {number} [start=0] The start of the range. + * @param {number} end The end of the range. + * @param {number} [step=1] The value to increment or decrement by. + * @returns {Array} Returns the range of numbers. + * @see _.inRange, _.range + * @example + * + * _.rangeRight(4); + * // => [3, 2, 1, 0] + * + * _.rangeRight(-4); + * // => [-3, -2, -1, 0] + * + * _.rangeRight(1, 5); + * // => [4, 3, 2, 1] + * + * _.rangeRight(0, 20, 5); + * // => [15, 10, 5, 0] + * + * _.rangeRight(0, -4, -1); + * // => [-3, -2, -1, 0] + * + * _.rangeRight(1, 4, 0); + * // => [1, 1, 1] + * + * _.rangeRight(0); + * // => [] + */ + var rangeRight = createRange(true); + + /** + * This method returns a new empty array. + * + * @static + * @memberOf _ + * @since 4.13.0 + * @category Util + * @returns {Array} Returns the new empty array. + * @example + * + * var arrays = _.times(2, _.stubArray); + * + * console.log(arrays); + * // => [[], []] + * + * console.log(arrays[0] === arrays[1]); + * // => false + */ + function stubArray() { + return []; + } + + /** + * This method returns `false`. + * + * @static + * @memberOf _ + * @since 4.13.0 + * @category Util + * @returns {boolean} Returns `false`. + * @example + * + * _.times(2, _.stubFalse); + * // => [false, false] + */ + function stubFalse() { + return false; + } + + /** + * This method returns a new empty object. + * + * @static + * @memberOf _ + * @since 4.13.0 + * @category Util + * @returns {Object} Returns the new empty object. + * @example + * + * var objects = _.times(2, _.stubObject); + * + * console.log(objects); + * // => [{}, {}] + * + * console.log(objects[0] === objects[1]); + * // => false + */ + function stubObject() { + return {}; + } + + /** + * This method returns an empty string. + * + * @static + * @memberOf _ + * @since 4.13.0 + * @category Util + * @returns {string} Returns the empty string. + * @example + * + * _.times(2, _.stubString); + * // => ['', ''] + */ + function stubString() { + return ''; + } + + /** + * This method returns `true`. + * + * @static + * @memberOf _ + * @since 4.13.0 + * @category Util + * @returns {boolean} Returns `true`. + * @example + * + * _.times(2, _.stubTrue); + * // => [true, true] + */ + function stubTrue() { + return true; + } + + /** + * Invokes the iteratee `n` times, returning an array of the results of + * each invocation. The iteratee is invoked with one argument; (index). + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Util + * @param {number} n The number of times to invoke `iteratee`. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Array} Returns the array of results. + * @example + * + * _.times(3, String); + * // => ['0', '1', '2'] + * + * _.times(4, _.constant(0)); + * // => [0, 0, 0, 0] + */ + function times(n, iteratee) { + n = toInteger(n); + if (n < 1 || n > MAX_SAFE_INTEGER) { + return []; + } + var index = MAX_ARRAY_LENGTH, + length = nativeMin(n, MAX_ARRAY_LENGTH); + + iteratee = getIteratee(iteratee); + n -= MAX_ARRAY_LENGTH; + + var result = baseTimes(length, iteratee); + while (++index < n) { + iteratee(index); + } + return result; + } + + /** + * Converts `value` to a property path array. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Util + * @param {*} value The value to convert. + * @returns {Array} Returns the new property path array. + * @example + * + * _.toPath('a.b.c'); + * // => ['a', 'b', 'c'] + * + * _.toPath('a[0].b.c'); + * // => ['a', '0', 'b', 'c'] + */ + function toPath(value) { + if (isArray(value)) { + return arrayMap(value, toKey); + } + return isSymbol(value) ? [value] : copyArray(stringToPath(toString(value))); + } + + /** + * Generates a unique ID. If `prefix` is given, the ID is appended to it. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Util + * @param {string} [prefix=''] The value to prefix the ID with. + * @returns {string} Returns the unique ID. + * @example + * + * _.uniqueId('contact_'); + * // => 'contact_104' + * + * _.uniqueId(); + * // => '105' + */ + function uniqueId(prefix) { + var id = ++idCounter; + return toString(prefix) + id; + } + + /*------------------------------------------------------------------------*/ + + /** + * Adds two numbers. + * + * @static + * @memberOf _ + * @since 3.4.0 + * @category Math + * @param {number} augend The first number in an addition. + * @param {number} addend The second number in an addition. + * @returns {number} Returns the total. + * @example + * + * _.add(6, 4); + * // => 10 + */ + var add = createMathOperation(function(augend, addend) { + return augend + addend; + }, 0); + + /** + * Computes `number` rounded up to `precision`. + * + * @static + * @memberOf _ + * @since 3.10.0 + * @category Math + * @param {number} number The number to round up. + * @param {number} [precision=0] The precision to round up to. + * @returns {number} Returns the rounded up number. + * @example + * + * _.ceil(4.006); + * // => 5 + * + * _.ceil(6.004, 2); + * // => 6.01 + * + * _.ceil(6040, -2); + * // => 6100 + */ + var ceil = createRound('ceil'); + + /** + * Divide two numbers. + * + * @static + * @memberOf _ + * @since 4.7.0 + * @category Math + * @param {number} dividend The first number in a division. + * @param {number} divisor The second number in a division. + * @returns {number} Returns the quotient. + * @example + * + * _.divide(6, 4); + * // => 1.5 + */ + var divide = createMathOperation(function(dividend, divisor) { + return dividend / divisor; + }, 1); + + /** + * Computes `number` rounded down to `precision`. + * + * @static + * @memberOf _ + * @since 3.10.0 + * @category Math + * @param {number} number The number to round down. + * @param {number} [precision=0] The precision to round down to. + * @returns {number} Returns the rounded down number. + * @example + * + * _.floor(4.006); + * // => 4 + * + * _.floor(0.046, 2); + * // => 0.04 + * + * _.floor(4060, -2); + * // => 4000 + */ + var floor = createRound('floor'); + + /** + * Computes the maximum value of `array`. If `array` is empty or falsey, + * `undefined` is returned. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Math + * @param {Array} array The array to iterate over. + * @returns {*} Returns the maximum value. + * @example + * + * _.max([4, 2, 8, 6]); + * // => 8 + * + * _.max([]); + * // => undefined + */ + function max(array) { + return (array && array.length) + ? baseExtremum(array, identity, baseGt) + : undefined; + } + + /** + * This method is like `_.max` except that it accepts `iteratee` which is + * invoked for each element in `array` to generate the criterion by which + * the value is ranked. The iteratee is invoked with one argument: (value). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Math + * @param {Array} array The array to iterate over. + * @param {Function} [iteratee=_.identity] The iteratee invoked per element. + * @returns {*} Returns the maximum value. + * @example + * + * var objects = [{ 'n': 1 }, { 'n': 2 }]; + * + * _.maxBy(objects, function(o) { return o.n; }); + * // => { 'n': 2 } + * + * // The `_.property` iteratee shorthand. + * _.maxBy(objects, 'n'); + * // => { 'n': 2 } + */ + function maxBy(array, iteratee) { + return (array && array.length) + ? baseExtremum(array, getIteratee(iteratee, 2), baseGt) + : undefined; + } + + /** + * Computes the mean of the values in `array`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Math + * @param {Array} array The array to iterate over. + * @returns {number} Returns the mean. + * @example + * + * _.mean([4, 2, 8, 6]); + * // => 5 + */ + function mean(array) { + return baseMean(array, identity); + } + + /** + * This method is like `_.mean` except that it accepts `iteratee` which is + * invoked for each element in `array` to generate the value to be averaged. + * The iteratee is invoked with one argument: (value). + * + * @static + * @memberOf _ + * @since 4.7.0 + * @category Math + * @param {Array} array The array to iterate over. + * @param {Function} [iteratee=_.identity] The iteratee invoked per element. + * @returns {number} Returns the mean. + * @example + * + * var objects = [{ 'n': 4 }, { 'n': 2 }, { 'n': 8 }, { 'n': 6 }]; + * + * _.meanBy(objects, function(o) { return o.n; }); + * // => 5 + * + * // The `_.property` iteratee shorthand. + * _.meanBy(objects, 'n'); + * // => 5 + */ + function meanBy(array, iteratee) { + return baseMean(array, getIteratee(iteratee, 2)); + } + + /** + * Computes the minimum value of `array`. If `array` is empty or falsey, + * `undefined` is returned. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Math + * @param {Array} array The array to iterate over. + * @returns {*} Returns the minimum value. + * @example + * + * _.min([4, 2, 8, 6]); + * // => 2 + * + * _.min([]); + * // => undefined + */ + function min(array) { + return (array && array.length) + ? baseExtremum(array, identity, baseLt) + : undefined; + } + + /** + * This method is like `_.min` except that it accepts `iteratee` which is + * invoked for each element in `array` to generate the criterion by which + * the value is ranked. The iteratee is invoked with one argument: (value). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Math + * @param {Array} array The array to iterate over. + * @param {Function} [iteratee=_.identity] The iteratee invoked per element. + * @returns {*} Returns the minimum value. + * @example + * + * var objects = [{ 'n': 1 }, { 'n': 2 }]; + * + * _.minBy(objects, function(o) { return o.n; }); + * // => { 'n': 1 } + * + * // The `_.property` iteratee shorthand. + * _.minBy(objects, 'n'); + * // => { 'n': 1 } + */ + function minBy(array, iteratee) { + return (array && array.length) + ? baseExtremum(array, getIteratee(iteratee, 2), baseLt) + : undefined; + } + + /** + * Multiply two numbers. + * + * @static + * @memberOf _ + * @since 4.7.0 + * @category Math + * @param {number} multiplier The first number in a multiplication. + * @param {number} multiplicand The second number in a multiplication. + * @returns {number} Returns the product. + * @example + * + * _.multiply(6, 4); + * // => 24 + */ + var multiply = createMathOperation(function(multiplier, multiplicand) { + return multiplier * multiplicand; + }, 1); + + /** + * Computes `number` rounded to `precision`. + * + * @static + * @memberOf _ + * @since 3.10.0 + * @category Math + * @param {number} number The number to round. + * @param {number} [precision=0] The precision to round to. + * @returns {number} Returns the rounded number. + * @example + * + * _.round(4.006); + * // => 4 + * + * _.round(4.006, 2); + * // => 4.01 + * + * _.round(4060, -2); + * // => 4100 + */ + var round = createRound('round'); + + /** + * Subtract two numbers. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Math + * @param {number} minuend The first number in a subtraction. + * @param {number} subtrahend The second number in a subtraction. + * @returns {number} Returns the difference. + * @example + * + * _.subtract(6, 4); + * // => 2 + */ + var subtract = createMathOperation(function(minuend, subtrahend) { + return minuend - subtrahend; + }, 0); + + /** + * Computes the sum of the values in `array`. + * + * @static + * @memberOf _ + * @since 3.4.0 + * @category Math + * @param {Array} array The array to iterate over. + * @returns {number} Returns the sum. + * @example + * + * _.sum([4, 2, 8, 6]); + * // => 20 + */ + function sum(array) { + return (array && array.length) + ? baseSum(array, identity) + : 0; + } + + /** + * This method is like `_.sum` except that it accepts `iteratee` which is + * invoked for each element in `array` to generate the value to be summed. + * The iteratee is invoked with one argument: (value). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Math + * @param {Array} array The array to iterate over. + * @param {Function} [iteratee=_.identity] The iteratee invoked per element. + * @returns {number} Returns the sum. + * @example + * + * var objects = [{ 'n': 4 }, { 'n': 2 }, { 'n': 8 }, { 'n': 6 }]; + * + * _.sumBy(objects, function(o) { return o.n; }); + * // => 20 + * + * // The `_.property` iteratee shorthand. + * _.sumBy(objects, 'n'); + * // => 20 + */ + function sumBy(array, iteratee) { + return (array && array.length) + ? baseSum(array, getIteratee(iteratee, 2)) + : 0; + } + + /*------------------------------------------------------------------------*/ + + // Add methods that return wrapped values in chain sequences. + lodash.after = after; + lodash.ary = ary; + lodash.assign = assign; + lodash.assignIn = assignIn; + lodash.assignInWith = assignInWith; + lodash.assignWith = assignWith; + lodash.at = at; + lodash.before = before; + lodash.bind = bind; + lodash.bindAll = bindAll; + lodash.bindKey = bindKey; + lodash.castArray = castArray; + lodash.chain = chain; + lodash.chunk = chunk; + lodash.compact = compact; + lodash.concat = concat; + lodash.cond = cond; + lodash.conforms = conforms; + lodash.constant = constant; + lodash.countBy = countBy; + lodash.create = create; + lodash.curry = curry; + lodash.curryRight = curryRight; + lodash.debounce = debounce; + lodash.defaults = defaults; + lodash.defaultsDeep = defaultsDeep; + lodash.defer = defer; + lodash.delay = delay; + lodash.difference = difference; + lodash.differenceBy = differenceBy; + lodash.differenceWith = differenceWith; + lodash.drop = drop; + lodash.dropRight = dropRight; + lodash.dropRightWhile = dropRightWhile; + lodash.dropWhile = dropWhile; + lodash.fill = fill; + lodash.filter = filter; + lodash.flatMap = flatMap; + lodash.flatMapDeep = flatMapDeep; + lodash.flatMapDepth = flatMapDepth; + lodash.flatten = flatten; + lodash.flattenDeep = flattenDeep; + lodash.flattenDepth = flattenDepth; + lodash.flip = flip; + lodash.flow = flow; + lodash.flowRight = flowRight; + lodash.fromPairs = fromPairs; + lodash.functions = functions; + lodash.functionsIn = functionsIn; + lodash.groupBy = groupBy; + lodash.initial = initial; + lodash.intersection = intersection; + lodash.intersectionBy = intersectionBy; + lodash.intersectionWith = intersectionWith; + lodash.invert = invert; + lodash.invertBy = invertBy; + lodash.invokeMap = invokeMap; + lodash.iteratee = iteratee; + lodash.keyBy = keyBy; + lodash.keys = keys; + lodash.keysIn = keysIn; + lodash.map = map; + lodash.mapKeys = mapKeys; + lodash.mapValues = mapValues; + lodash.matches = matches; + lodash.matchesProperty = matchesProperty; + lodash.memoize = memoize; + lodash.merge = merge; + lodash.mergeWith = mergeWith; + lodash.method = method; + lodash.methodOf = methodOf; + lodash.mixin = mixin; + lodash.negate = negate; + lodash.nthArg = nthArg; + lodash.omit = omit; + lodash.omitBy = omitBy; + lodash.once = once; + lodash.orderBy = orderBy; + lodash.over = over; + lodash.overArgs = overArgs; + lodash.overEvery = overEvery; + lodash.overSome = overSome; + lodash.partial = partial; + lodash.partialRight = partialRight; + lodash.partition = partition; + lodash.pick = pick; + lodash.pickBy = pickBy; + lodash.property = property; + lodash.propertyOf = propertyOf; + lodash.pull = pull; + lodash.pullAll = pullAll; + lodash.pullAllBy = pullAllBy; + lodash.pullAllWith = pullAllWith; + lodash.pullAt = pullAt; + lodash.range = range; + lodash.rangeRight = rangeRight; + lodash.rearg = rearg; + lodash.reject = reject; + lodash.remove = remove; + lodash.rest = rest; + lodash.reverse = reverse; + lodash.sampleSize = sampleSize; + lodash.set = set; + lodash.setWith = setWith; + lodash.shuffle = shuffle; + lodash.slice = slice; + lodash.sortBy = sortBy; + lodash.sortedUniq = sortedUniq; + lodash.sortedUniqBy = sortedUniqBy; + lodash.split = split; + lodash.spread = spread; + lodash.tail = tail; + lodash.take = take; + lodash.takeRight = takeRight; + lodash.takeRightWhile = takeRightWhile; + lodash.takeWhile = takeWhile; + lodash.tap = tap; + lodash.throttle = throttle; + lodash.thru = thru; + lodash.toArray = toArray; + lodash.toPairs = toPairs; + lodash.toPairsIn = toPairsIn; + lodash.toPath = toPath; + lodash.toPlainObject = toPlainObject; + lodash.transform = transform; + lodash.unary = unary; + lodash.union = union; + lodash.unionBy = unionBy; + lodash.unionWith = unionWith; + lodash.uniq = uniq; + lodash.uniqBy = uniqBy; + lodash.uniqWith = uniqWith; + lodash.unset = unset; + lodash.unzip = unzip; + lodash.unzipWith = unzipWith; + lodash.update = update; + lodash.updateWith = updateWith; + lodash.values = values; + lodash.valuesIn = valuesIn; + lodash.without = without; + lodash.words = words; + lodash.wrap = wrap; + lodash.xor = xor; + lodash.xorBy = xorBy; + lodash.xorWith = xorWith; + lodash.zip = zip; + lodash.zipObject = zipObject; + lodash.zipObjectDeep = zipObjectDeep; + lodash.zipWith = zipWith; + + // Add aliases. + lodash.entries = toPairs; + lodash.entriesIn = toPairsIn; + lodash.extend = assignIn; + lodash.extendWith = assignInWith; + + // Add methods to `lodash.prototype`. + mixin(lodash, lodash); + + /*------------------------------------------------------------------------*/ + + // Add methods that return unwrapped values in chain sequences. + lodash.add = add; + lodash.attempt = attempt; + lodash.camelCase = camelCase; + lodash.capitalize = capitalize; + lodash.ceil = ceil; + lodash.clamp = clamp; + lodash.clone = clone; + lodash.cloneDeep = cloneDeep; + lodash.cloneDeepWith = cloneDeepWith; + lodash.cloneWith = cloneWith; + lodash.conformsTo = conformsTo; + lodash.deburr = deburr; + lodash.defaultTo = defaultTo; + lodash.divide = divide; + lodash.endsWith = endsWith; + lodash.eq = eq; + lodash.escape = escape; + lodash.escapeRegExp = escapeRegExp; + lodash.every = every; + lodash.find = find; + lodash.findIndex = findIndex; + lodash.findKey = findKey; + lodash.findLast = findLast; + lodash.findLastIndex = findLastIndex; + lodash.findLastKey = findLastKey; + lodash.floor = floor; + lodash.forEach = forEach; + lodash.forEachRight = forEachRight; + lodash.forIn = forIn; + lodash.forInRight = forInRight; + lodash.forOwn = forOwn; + lodash.forOwnRight = forOwnRight; + lodash.get = get; + lodash.gt = gt; + lodash.gte = gte; + lodash.has = has; + lodash.hasIn = hasIn; + lodash.head = head; + lodash.identity = identity; + lodash.includes = includes; + lodash.indexOf = indexOf; + lodash.inRange = inRange; + lodash.invoke = invoke; + lodash.isArguments = isArguments; + lodash.isArray = isArray; + lodash.isArrayBuffer = isArrayBuffer; + lodash.isArrayLike = isArrayLike; + lodash.isArrayLikeObject = isArrayLikeObject; + lodash.isBoolean = isBoolean; + lodash.isBuffer = isBuffer; + lodash.isDate = isDate; + lodash.isElement = isElement; + lodash.isEmpty = isEmpty; + lodash.isEqual = isEqual; + lodash.isEqualWith = isEqualWith; + lodash.isError = isError; + lodash.isFinite = isFinite; + lodash.isFunction = isFunction; + lodash.isInteger = isInteger; + lodash.isLength = isLength; + lodash.isMap = isMap; + lodash.isMatch = isMatch; + lodash.isMatchWith = isMatchWith; + lodash.isNaN = isNaN; + lodash.isNative = isNative; + lodash.isNil = isNil; + lodash.isNull = isNull; + lodash.isNumber = isNumber; + lodash.isObject = isObject; + lodash.isObjectLike = isObjectLike; + lodash.isPlainObject = isPlainObject; + lodash.isRegExp = isRegExp; + lodash.isSafeInteger = isSafeInteger; + lodash.isSet = isSet; + lodash.isString = isString; + lodash.isSymbol = isSymbol; + lodash.isTypedArray = isTypedArray; + lodash.isUndefined = isUndefined; + lodash.isWeakMap = isWeakMap; + lodash.isWeakSet = isWeakSet; + lodash.join = join; + lodash.kebabCase = kebabCase; + lodash.last = last; + lodash.lastIndexOf = lastIndexOf; + lodash.lowerCase = lowerCase; + lodash.lowerFirst = lowerFirst; + lodash.lt = lt; + lodash.lte = lte; + lodash.max = max; + lodash.maxBy = maxBy; + lodash.mean = mean; + lodash.meanBy = meanBy; + lodash.min = min; + lodash.minBy = minBy; + lodash.stubArray = stubArray; + lodash.stubFalse = stubFalse; + lodash.stubObject = stubObject; + lodash.stubString = stubString; + lodash.stubTrue = stubTrue; + lodash.multiply = multiply; + lodash.nth = nth; + lodash.noConflict = noConflict; + lodash.noop = noop; + lodash.now = now; + lodash.pad = pad; + lodash.padEnd = padEnd; + lodash.padStart = padStart; + lodash.parseInt = parseInt; + lodash.random = random; + lodash.reduce = reduce; + lodash.reduceRight = reduceRight; + lodash.repeat = repeat; + lodash.replace = replace; + lodash.result = result; + lodash.round = round; + lodash.runInContext = runInContext; + lodash.sample = sample; + lodash.size = size; + lodash.snakeCase = snakeCase; + lodash.some = some; + lodash.sortedIndex = sortedIndex; + lodash.sortedIndexBy = sortedIndexBy; + lodash.sortedIndexOf = sortedIndexOf; + lodash.sortedLastIndex = sortedLastIndex; + lodash.sortedLastIndexBy = sortedLastIndexBy; + lodash.sortedLastIndexOf = sortedLastIndexOf; + lodash.startCase = startCase; + lodash.startsWith = startsWith; + lodash.subtract = subtract; + lodash.sum = sum; + lodash.sumBy = sumBy; + lodash.template = template; + lodash.times = times; + lodash.toFinite = toFinite; + lodash.toInteger = toInteger; + lodash.toLength = toLength; + lodash.toLower = toLower; + lodash.toNumber = toNumber; + lodash.toSafeInteger = toSafeInteger; + lodash.toString = toString; + lodash.toUpper = toUpper; + lodash.trim = trim; + lodash.trimEnd = trimEnd; + lodash.trimStart = trimStart; + lodash.truncate = truncate; + lodash.unescape = unescape; + lodash.uniqueId = uniqueId; + lodash.upperCase = upperCase; + lodash.upperFirst = upperFirst; + + // Add aliases. + lodash.each = forEach; + lodash.eachRight = forEachRight; + lodash.first = head; + + mixin(lodash, (function() { + var source = {}; + baseForOwn(lodash, function(func, methodName) { + if (!hasOwnProperty.call(lodash.prototype, methodName)) { + source[methodName] = func; + } + }); + return source; + }()), { 'chain': false }); + + /*------------------------------------------------------------------------*/ + + /** + * The semantic version number. + * + * @static + * @memberOf _ + * @type {string} + */ + lodash.VERSION = VERSION; + + // Assign default placeholders. + arrayEach(['bind', 'bindKey', 'curry', 'curryRight', 'partial', 'partialRight'], function(methodName) { + lodash[methodName].placeholder = lodash; + }); + + // Add `LazyWrapper` methods for `_.drop` and `_.take` variants. + arrayEach(['drop', 'take'], function(methodName, index) { + LazyWrapper.prototype[methodName] = function(n) { + n = n === undefined ? 1 : nativeMax(toInteger(n), 0); + + var result = (this.__filtered__ && !index) + ? new LazyWrapper(this) + : this.clone(); + + if (result.__filtered__) { + result.__takeCount__ = nativeMin(n, result.__takeCount__); + } else { + result.__views__.push({ + 'size': nativeMin(n, MAX_ARRAY_LENGTH), + 'type': methodName + (result.__dir__ < 0 ? 'Right' : '') + }); + } + return result; + }; + + LazyWrapper.prototype[methodName + 'Right'] = function(n) { + return this.reverse()[methodName](n).reverse(); + }; + }); + + // Add `LazyWrapper` methods that accept an `iteratee` value. + arrayEach(['filter', 'map', 'takeWhile'], function(methodName, index) { + var type = index + 1, + isFilter = type == LAZY_FILTER_FLAG || type == LAZY_WHILE_FLAG; + + LazyWrapper.prototype[methodName] = function(iteratee) { + var result = this.clone(); + result.__iteratees__.push({ + 'iteratee': getIteratee(iteratee, 3), + 'type': type + }); + result.__filtered__ = result.__filtered__ || isFilter; + return result; + }; + }); + + // Add `LazyWrapper` methods for `_.head` and `_.last`. + arrayEach(['head', 'last'], function(methodName, index) { + var takeName = 'take' + (index ? 'Right' : ''); + + LazyWrapper.prototype[methodName] = function() { + return this[takeName](1).value()[0]; + }; + }); + + // Add `LazyWrapper` methods for `_.initial` and `_.tail`. + arrayEach(['initial', 'tail'], function(methodName, index) { + var dropName = 'drop' + (index ? '' : 'Right'); + + LazyWrapper.prototype[methodName] = function() { + return this.__filtered__ ? new LazyWrapper(this) : this[dropName](1); + }; + }); + + LazyWrapper.prototype.compact = function() { + return this.filter(identity); + }; + + LazyWrapper.prototype.find = function(predicate) { + return this.filter(predicate).head(); + }; + + LazyWrapper.prototype.findLast = function(predicate) { + return this.reverse().find(predicate); + }; + + LazyWrapper.prototype.invokeMap = baseRest(function(path, args) { + if (typeof path == 'function') { + return new LazyWrapper(this); + } + return this.map(function(value) { + return baseInvoke(value, path, args); + }); + }); + + LazyWrapper.prototype.reject = function(predicate) { + return this.filter(negate(getIteratee(predicate))); + }; + + LazyWrapper.prototype.slice = function(start, end) { + start = toInteger(start); + + var result = this; + if (result.__filtered__ && (start > 0 || end < 0)) { + return new LazyWrapper(result); + } + if (start < 0) { + result = result.takeRight(-start); + } else if (start) { + result = result.drop(start); + } + if (end !== undefined) { + end = toInteger(end); + result = end < 0 ? result.dropRight(-end) : result.take(end - start); + } + return result; + }; + + LazyWrapper.prototype.takeRightWhile = function(predicate) { + return this.reverse().takeWhile(predicate).reverse(); + }; + + LazyWrapper.prototype.toArray = function() { + return this.take(MAX_ARRAY_LENGTH); + }; + + // Add `LazyWrapper` methods to `lodash.prototype`. + baseForOwn(LazyWrapper.prototype, function(func, methodName) { + var checkIteratee = /^(?:filter|find|map|reject)|While$/.test(methodName), + isTaker = /^(?:head|last)$/.test(methodName), + lodashFunc = lodash[isTaker ? ('take' + (methodName == 'last' ? 'Right' : '')) : methodName], + retUnwrapped = isTaker || /^find/.test(methodName); + + if (!lodashFunc) { + return; + } + lodash.prototype[methodName] = function() { + var value = this.__wrapped__, + args = isTaker ? [1] : arguments, + isLazy = value instanceof LazyWrapper, + iteratee = args[0], + useLazy = isLazy || isArray(value); + + var interceptor = function(value) { + var result = lodashFunc.apply(lodash, arrayPush([value], args)); + return (isTaker && chainAll) ? result[0] : result; + }; + + if (useLazy && checkIteratee && typeof iteratee == 'function' && iteratee.length != 1) { + // Avoid lazy use if the iteratee has a "length" value other than `1`. + isLazy = useLazy = false; + } + var chainAll = this.__chain__, + isHybrid = !!this.__actions__.length, + isUnwrapped = retUnwrapped && !chainAll, + onlyLazy = isLazy && !isHybrid; + + if (!retUnwrapped && useLazy) { + value = onlyLazy ? value : new LazyWrapper(this); + var result = func.apply(value, args); + result.__actions__.push({ 'func': thru, 'args': [interceptor], 'thisArg': undefined }); + return new LodashWrapper(result, chainAll); + } + if (isUnwrapped && onlyLazy) { + return func.apply(this, args); + } + result = this.thru(interceptor); + return isUnwrapped ? (isTaker ? result.value()[0] : result.value()) : result; + }; + }); + + // Add `Array` methods to `lodash.prototype`. + arrayEach(['pop', 'push', 'shift', 'sort', 'splice', 'unshift'], function(methodName) { + var func = arrayProto[methodName], + chainName = /^(?:push|sort|unshift)$/.test(methodName) ? 'tap' : 'thru', + retUnwrapped = /^(?:pop|shift)$/.test(methodName); + + lodash.prototype[methodName] = function() { + var args = arguments; + if (retUnwrapped && !this.__chain__) { + var value = this.value(); + return func.apply(isArray(value) ? value : [], args); + } + return this[chainName](function(value) { + return func.apply(isArray(value) ? value : [], args); + }); + }; + }); + + // Map minified method names to their real names. + baseForOwn(LazyWrapper.prototype, function(func, methodName) { + var lodashFunc = lodash[methodName]; + if (lodashFunc) { + var key = (lodashFunc.name + ''), + names = realNames[key] || (realNames[key] = []); + + names.push({ 'name': methodName, 'func': lodashFunc }); + } + }); + + realNames[createHybrid(undefined, WRAP_BIND_KEY_FLAG).name] = [{ + 'name': 'wrapper', + 'func': undefined + }]; + + // Add methods to `LazyWrapper`. + LazyWrapper.prototype.clone = lazyClone; + LazyWrapper.prototype.reverse = lazyReverse; + LazyWrapper.prototype.value = lazyValue; + + // Add chain sequence methods to the `lodash` wrapper. + lodash.prototype.at = wrapperAt; + lodash.prototype.chain = wrapperChain; + lodash.prototype.commit = wrapperCommit; + lodash.prototype.next = wrapperNext; + lodash.prototype.plant = wrapperPlant; + lodash.prototype.reverse = wrapperReverse; + lodash.prototype.toJSON = lodash.prototype.valueOf = lodash.prototype.value = wrapperValue; + + // Add lazy aliases. + lodash.prototype.first = lodash.prototype.head; + + if (symIterator) { + lodash.prototype[symIterator] = wrapperToIterator; + } + return lodash; + }); + + /*--------------------------------------------------------------------------*/ + + // Export lodash. + var _ = runInContext(); + + // Some AMD build optimizers, like r.js, check for condition patterns like: + if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) { + // Expose Lodash on the global object to prevent errors when Lodash is + // loaded by a script tag in the presence of an AMD loader. + // See http://requirejs.org/docs/errors.html#mismatch for more details. + // Use `_.noConflict` to remove Lodash from the global object. + root._ = _; + + // Define as an anonymous module so, through path mapping, it can be + // referenced as the "underscore" module. + define(function() { + return _; + }); + } + // Check for `exports` after `define` in case a build optimizer adds it. + else if (freeModule) { + // Export for Node.js. + (freeModule.exports = _)._ = _; + // Export for CommonJS support. + freeExports._ = _; + } + else { + // Export to the global object. + root._ = _; + } +}.call(this)); diff --git a/src/legacy/design-studio/js/promise-polyfill.js b/src/legacy/design-studio/js/promise-polyfill.js new file mode 100644 index 0000000..1619c9c --- /dev/null +++ b/src/legacy/design-studio/js/promise-polyfill.js @@ -0,0 +1,286 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory() : + typeof define === 'function' && define.amd ? define(factory) : + (factory()); +}(this, (function () { 'use strict'; + +/** + * @this {Promise} + */ +function finallyConstructor(callback) { + var constructor = this.constructor; + return this.then( + function(value) { + return constructor.resolve(callback()).then(function() { + return value; + }); + }, + function(reason) { + return constructor.resolve(callback()).then(function() { + return constructor.reject(reason); + }); + } + ); +} + +// Store setTimeout reference so promise-polyfill will be unaffected by +// other code modifying setTimeout (like sinon.useFakeTimers()) +var setTimeoutFunc = setTimeout; + +function noop() {} + +// Polyfill for Function.prototype.bind +function bind(fn, thisArg) { + return function() { + fn.apply(thisArg, arguments); + }; +} + +/** + * @constructor + * @param {Function} fn + */ +function Promise(fn) { + if (!(this instanceof Promise)) + throw new TypeError('Promises must be constructed via new'); + if (typeof fn !== 'function') throw new TypeError('not a function'); + /** @type {!number} */ + this._state = 0; + /** @type {!boolean} */ + this._handled = false; + /** @type {Promise|undefined} */ + this._value = undefined; + /** @type {!Array<!Function>} */ + this._deferreds = []; + + doResolve(fn, this); +} + +function handle(self, deferred) { + while (self._state === 3) { + self = self._value; + } + if (self._state === 0) { + self._deferreds.push(deferred); + return; + } + self._handled = true; + Promise._immediateFn(function() { + var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected; + if (cb === null) { + (self._state === 1 ? resolve : reject)(deferred.promise, self._value); + return; + } + var ret; + try { + ret = cb(self._value); + } catch (e) { + reject(deferred.promise, e); + return; + } + resolve(deferred.promise, ret); + }); +} + +function resolve(self, newValue) { + try { + // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure + if (newValue === self) + throw new TypeError('A promise cannot be resolved with itself.'); + if ( + newValue && + (typeof newValue === 'object' || typeof newValue === 'function') + ) { + var then = newValue.then; + if (newValue instanceof Promise) { + self._state = 3; + self._value = newValue; + finale(self); + return; + } else if (typeof then === 'function') { + doResolve(bind(then, newValue), self); + return; + } + } + self._state = 1; + self._value = newValue; + finale(self); + } catch (e) { + reject(self, e); + } +} + +function reject(self, newValue) { + self._state = 2; + self._value = newValue; + finale(self); +} + +function finale(self) { + if (self._state === 2 && self._deferreds.length === 0) { + Promise._immediateFn(function() { + if (!self._handled) { + Promise._unhandledRejectionFn(self._value); + } + }); + } + + for (var i = 0, len = self._deferreds.length; i < len; i++) { + handle(self, self._deferreds[i]); + } + self._deferreds = null; +} + +/** + * @constructor + */ +function Handler(onFulfilled, onRejected, promise) { + this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null; + this.onRejected = typeof onRejected === 'function' ? onRejected : null; + this.promise = promise; +} + +/** + * Take a potentially misbehaving resolver function and make sure + * onFulfilled and onRejected are only called once. + * + * Makes no guarantees about asynchrony. + */ +function doResolve(fn, self) { + var done = false; + try { + fn( + function(value) { + if (done) return; + done = true; + resolve(self, value); + }, + function(reason) { + if (done) return; + done = true; + reject(self, reason); + } + ); + } catch (ex) { + if (done) return; + done = true; + reject(self, ex); + } +} + +Promise.prototype['catch'] = function(onRejected) { + return this.then(null, onRejected); +}; + +Promise.prototype.then = function(onFulfilled, onRejected) { + // @ts-ignore + var prom = new this.constructor(noop); + + handle(this, new Handler(onFulfilled, onRejected, prom)); + return prom; +}; + +Promise.prototype['finally'] = finallyConstructor; + +Promise.all = function(arr) { + return new Promise(function(resolve, reject) { + if (!arr || typeof arr.length === 'undefined') + throw new TypeError('Promise.all accepts an array'); + var args = Array.prototype.slice.call(arr); + if (args.length === 0) return resolve([]); + var remaining = args.length; + + function res(i, val) { + try { + if (val && (typeof val === 'object' || typeof val === 'function')) { + var then = val.then; + if (typeof then === 'function') { + then.call( + val, + function(val) { + res(i, val); + }, + reject + ); + return; + } + } + args[i] = val; + if (--remaining === 0) { + resolve(args); + } + } catch (ex) { + reject(ex); + } + } + + for (var i = 0; i < args.length; i++) { + res(i, args[i]); + } + }); +}; + +Promise.resolve = function(value) { + if (value && typeof value === 'object' && value.constructor === Promise) { + return value; + } + + return new Promise(function(resolve) { + resolve(value); + }); +}; + +Promise.reject = function(value) { + return new Promise(function(resolve, reject) { + reject(value); + }); +}; + +Promise.race = function(values) { + return new Promise(function(resolve, reject) { + for (var i = 0, len = values.length; i < len; i++) { + values[i].then(resolve, reject); + } + }); +}; + +// Use polyfill for setImmediate for performance gains +Promise._immediateFn = + (typeof setImmediate === 'function' && + function(fn) { + setImmediate(fn); + }) || + function(fn) { + setTimeoutFunc(fn, 0); + }; + +Promise._unhandledRejectionFn = function _unhandledRejectionFn(err) { + if (typeof console !== 'undefined' && console) { + console.warn('Possible Unhandled Promise Rejection:', err); // eslint-disable-line no-console + } +}; + +/** @suppress {undefinedVars} */ +var globalNS = (function() { + // the only reliable means to get the global object is + // `Function('return this')()` + // However, this causes CSP violations in Chrome apps. + if (typeof self !== 'undefined') { + return self; + } + if (typeof window !== 'undefined') { + return window; + } + if (typeof global !== 'undefined') { + return global; + } + throw new Error('unable to locate global object'); +})(); + +if (!('Promise' in globalNS)) { + globalNS['Promise'] = Promise; +} else if (!globalNS.Promise.prototype['finally']) { + globalNS.Promise.prototype['finally'] = finallyConstructor; +} + +}))); \ No newline at end of file diff --git a/src/legacy/design-studio/js/querystring.js b/src/legacy/design-studio/js/querystring.js new file mode 100644 index 0000000..5a8e3f1 --- /dev/null +++ b/src/legacy/design-studio/js/querystring.js @@ -0,0 +1,187 @@ +/* this independent library is looking for a home + it provides round-trip query string parsing & generating + Copyright 2016 Gordon Woodhull, MIT License + */ + +var querystring = (function() { + var listsep_ = '|'; + function read_query(type, val) { + switch(type) { + case 'boolean': + return val === 'true'; + case 'number': + return +val; + case 'string': + return val; + case 'array': + return val.split(listsep_); + default: throw new Error('unsupported query type ' + type); + } + } + + function write_query(type, val) { + switch(type) { + case 'array': + return val.join(listsep_); + case 'boolean': + case 'number': + case 'string': + return '' + val; + default: throw new Error('unsupported query type ' + type); + } + } + + function query_type(val) { + return Array.isArray(val) ? 'array' : typeof val; + } + + // we could probably depend on _, but _.pick is the only thing we need atm + function pick(object, fields) { + return fields.reduce(function(reduced, key) { + if(key in object) + reduced[key] = object[key]; + return reduced; + }, {}); + } + + function create_tracker(options, domain, args) { + var qs = querystring.parse(); + var settings = {}; + + function update_interesting(qs) { + var interesting = Object.keys(options) + .filter(function(k) { + return qs[options[k].query] !== write_query(query_type(options[k].default), options[k].default); + }).map(function(k) { + return options[k].query || k; + }); + var interested = pick(qs, interesting); + querystring.update(interested); + } + + function do_option(key, opt, callback) { + settings[key] = opt.default; + var query = opt.query = opt.query || key; + var type = query_type(opt.default); + if(query in qs) + settings[key] = read_query(type, qs[query]); + + function update_setting(opt, val) { + settings[key] = val; + if(opt.query) { + qs[opt.query] = write_query(type, val); + update_interesting(qs); + } + } + if(opt.selector) { + switch(type) { + case 'boolean': + if(!opt.set) + opt.set = function(val) { + $(opt.selector) + .prop('checked', val); + }; + if(!opt.subscribe) + opt.subscribe = function(k) { + $(opt.selector) + .change(function() { + var val = $(this).is(':checked'); + k(val); + }); + }; + break; + case 'number': + case 'string': + if(!opt.set) + opt.set = function(val) { + $(opt.selector) + .val(val); + }; + if(!opt.subscribe) + opt.subscribe = function(k) { + $(opt.selector) + .change(function() { + var val = $(this).val(); + k(val); + }); + }; + break; + default: throw new Error('unsupported selector type ' + type); + } + } + if(opt.set) + opt.set(settings[key]); + if(opt.subscribe) + opt.subscribe(function(val) { + update_setting(opt, val); + callback && callback(val); + }); + } + + for(var key in options) { + var callback = function(opt, val) { + args[0] = val; + if(opt.exert && !opt.dont_exert_after_subscribe) + opt.exert.apply(opt, args); + if(domain && domain.on_exert) + domain.on_exert(opt); + }; + do_option(key, options[key], callback.bind(null, options[key])); + } + + return { + vals: settings, + exert: function() { + for(var key in options) + if(options[key].exert) { + args[0] = settings[key]; + options[key].exert.apply(options[key], args); + } + } + }; + } + + return { + listsep: function(s) { + if(!arguments.length) + return listsep_; + listsep_ = s; + return this; + }, + parse: function(opts) { + opts = opts || {}; + return (function(a) { + if (a == "") return {}; + var b = {}; + for (var i = 0; i < a.length; ++i) + { + var p=a[i].split('=', 2); + if (p.length == 1) + b[p[0]] = opts.boolean ? true : ""; + else + b[p[0]] = decodeURIComponent(p[1].replace(/\+/g, " ")); + } + return b; + })(window.location.search.substr(1).split('&')); + }, + generate: function(m) { + var parts = []; + for(var k in m) + parts.push(k + '=' + encodeURIComponent(m[k])); + return parts.length ? parts.join('&') : ''; + }, + update: function(m) { + var url = window.location.protocol + '//' + window.location.host + window.location.pathname; + var params = this.generate(m); + if(params) + url += '?' + params; + window.history.pushState(null, null, url); + return this; + }, + option_tracker: function(options, domain /* ... arguments for exert ... */) { + var args = Array.prototype.slice.call(arguments, 2); + args.unshift(0); + return create_tracker(options, domain, args); + } + }; +})(); diff --git a/src/legacy/design-studio/js/queue.js b/src/legacy/design-studio/js/queue.js new file mode 100644 index 0000000..2d1020d --- /dev/null +++ b/src/legacy/design-studio/js/queue.js @@ -0,0 +1,106 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define('queue', factory) : + (global.queue = factory()); +}(this, function () { 'use strict'; + + var slice = [].slice; + + function noop() {} + + var noabort = {}; + var success = [null]; + function newQueue(concurrency) { + if (!(concurrency >= 1)) throw new Error; + + var q, + tasks = [], + results = [], + waiting = 0, + active = 0, + ended = 0, + starting, // inside a synchronous task callback? + error, + callback = noop, + callbackAll = true; + + function start() { + if (starting) return; // let the current task complete + while (starting = waiting && active < concurrency) { + var i = ended + active, + t = tasks[i], + j = t.length - 1, + c = t[j]; + t[j] = end(i); + --waiting, ++active, tasks[i] = c.apply(null, t) || noabort; + } + } + + function end(i) { + return function(e, r) { + if (!tasks[i]) throw new Error; // detect multiple callbacks + --active, ++ended, tasks[i] = null; + if (error != null) return; // only report the first error + if (e != null) { + abort(e); + } else { + results[i] = r; + if (waiting) start(); + else if (!active) notify(); + } + }; + } + + function abort(e) { + error = e; // ignore new tasks and squelch active callbacks + waiting = NaN; // stop queued tasks from starting + notify(); + } + + function notify() { + if (error != null) callback(error); + else if (callbackAll) callback(null, results); + else callback.apply(null, success.concat(results)); + } + + return q = { + defer: function(f) { + if (callback !== noop) throw new Error; + var t = slice.call(arguments, 1); + t.push(f); + ++waiting, tasks.push(t); + start(); + return q; + }, + abort: function() { + if (error == null) { + var i = ended + active, t; + while (--i >= 0) (t = tasks[i]) && t.abort && t.abort(); + abort(new Error("abort")); + } + return q; + }, + await: function(f) { + if (callback !== noop) throw new Error; + callback = f, callbackAll = false; + if (!waiting && !active) notify(); + return q; + }, + awaitAll: function(f) { + if (callback !== noop) throw new Error; + callback = f, callbackAll = true; + if (!waiting && !active) notify(); + return q; + } + }; + } + + function queue(concurrency) { + return newQueue(arguments.length ? +concurrency : Infinity); + } + + queue.version = "1.2.1"; + + return queue; + +})); \ No newline at end of file diff --git a/src/legacy/design-studio/js/tether.js b/src/legacy/design-studio/js/tether.js new file mode 100644 index 0000000..ea141f0 --- /dev/null +++ b/src/legacy/design-studio/js/tether.js @@ -0,0 +1,1811 @@ +/*! tether 1.4.0 */ + +(function(root, factory) { + if (typeof define === 'function' && define.amd) { + define(factory); + } else if (typeof exports === 'object') { + module.exports = factory(require, exports, module); + } else { + root.Tether = factory(); + } +}(this, function(require, exports, module) { + +'use strict'; + +var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + +var TetherBase = undefined; +if (typeof TetherBase === 'undefined') { + TetherBase = { modules: [] }; +} + +var zeroElement = null; + +// Same as native getBoundingClientRect, except it takes into account parent <frame> offsets +// if the element lies within a nested document (<frame> or <iframe>-like). +function getActualBoundingClientRect(node) { + var boundingRect = node.getBoundingClientRect(); + + // The original object returned by getBoundingClientRect is immutable, so we clone it + // We can't use extend because the properties are not considered part of the object by hasOwnProperty in IE9 + var rect = {}; + for (var k in boundingRect) { + rect[k] = boundingRect[k]; + } + + if (node.ownerDocument !== document) { + var _frameElement = node.ownerDocument.defaultView.frameElement; + if (_frameElement) { + var frameRect = getActualBoundingClientRect(_frameElement); + rect.top += frameRect.top; + rect.bottom += frameRect.top; + rect.left += frameRect.left; + rect.right += frameRect.left; + } + } + + return rect; +} + +function getScrollParents(el) { + // In firefox if the el is inside an iframe with display: none; window.getComputedStyle() will return null; + // https://bugzilla.mozilla.org/show_bug.cgi?id=548397 + var computedStyle = getComputedStyle(el) || {}; + var position = computedStyle.position; + var parents = []; + + if (position === 'fixed') { + return [el]; + } + + var parent = el; + while ((parent = parent.parentNode) && parent && parent.nodeType === 1) { + var style = undefined; + try { + style = getComputedStyle(parent); + } catch (err) {} + + if (typeof style === 'undefined' || style === null) { + parents.push(parent); + return parents; + } + + var _style = style; + var overflow = _style.overflow; + var overflowX = _style.overflowX; + var overflowY = _style.overflowY; + + if (/(auto|scroll)/.test(overflow + overflowY + overflowX)) { + if (position !== 'absolute' || ['relative', 'absolute', 'fixed'].indexOf(style.position) >= 0) { + parents.push(parent); + } + } + } + + parents.push(el.ownerDocument.body); + + // If the node is within a frame, account for the parent window scroll + if (el.ownerDocument !== document) { + parents.push(el.ownerDocument.defaultView); + } + + return parents; +} + +var uniqueId = (function () { + var id = 0; + return function () { + return ++id; + }; +})(); + +var zeroPosCache = {}; +var getOrigin = function getOrigin() { + // getBoundingClientRect is unfortunately too accurate. It introduces a pixel or two of + // jitter as the user scrolls that messes with our ability to detect if two positions + // are equivilant or not. We place an element at the top left of the page that will + // get the same jitter, so we can cancel the two out. + var node = zeroElement; + if (!node || !document.body.contains(node)) { + node = document.createElement('div'); + node.setAttribute('data-tether-id', uniqueId()); + extend(node.style, { + top: 0, + left: 0, + position: 'absolute' + }); + + document.body.appendChild(node); + + zeroElement = node; + } + + var id = node.getAttribute('data-tether-id'); + if (typeof zeroPosCache[id] === 'undefined') { + zeroPosCache[id] = getActualBoundingClientRect(node); + + // Clear the cache when this position call is done + defer(function () { + delete zeroPosCache[id]; + }); + } + + return zeroPosCache[id]; +}; + +function removeUtilElements() { + if (zeroElement) { + document.body.removeChild(zeroElement); + } + zeroElement = null; +}; + +function getBounds(el) { + var doc = undefined; + if (el === document) { + doc = document; + el = document.documentElement; + } else { + doc = el.ownerDocument; + } + + var docEl = doc.documentElement; + + var box = getActualBoundingClientRect(el); + + var origin = getOrigin(); + + box.top -= origin.top; + box.left -= origin.left; + + if (typeof box.width === 'undefined') { + box.width = document.body.scrollWidth - box.left - box.right; + } + if (typeof box.height === 'undefined') { + box.height = document.body.scrollHeight - box.top - box.bottom; + } + + box.top = box.top - docEl.clientTop; + box.left = box.left - docEl.clientLeft; + box.right = doc.body.clientWidth - box.width - box.left; + box.bottom = doc.body.clientHeight - box.height - box.top; + + return box; +} + +function getOffsetParent(el) { + return el.offsetParent || document.documentElement; +} + +var _scrollBarSize = null; +function getScrollBarSize() { + if (_scrollBarSize) { + return _scrollBarSize; + } + var inner = document.createElement('div'); + inner.style.width = '100%'; + inner.style.height = '200px'; + + var outer = document.createElement('div'); + extend(outer.style, { + position: 'absolute', + top: 0, + left: 0, + pointerEvents: 'none', + visibility: 'hidden', + width: '200px', + height: '150px', + overflow: 'hidden' + }); + + outer.appendChild(inner); + + document.body.appendChild(outer); + + var widthContained = inner.offsetWidth; + outer.style.overflow = 'scroll'; + var widthScroll = inner.offsetWidth; + + if (widthContained === widthScroll) { + widthScroll = outer.clientWidth; + } + + document.body.removeChild(outer); + + var width = widthContained - widthScroll; + + _scrollBarSize = { width: width, height: width }; + return _scrollBarSize; +} + +function extend() { + var out = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; + + var args = []; + + Array.prototype.push.apply(args, arguments); + + args.slice(1).forEach(function (obj) { + if (obj) { + for (var key in obj) { + if (({}).hasOwnProperty.call(obj, key)) { + out[key] = obj[key]; + } + } + } + }); + + return out; +} + +function removeClass(el, name) { + if (typeof el.classList !== 'undefined') { + name.split(' ').forEach(function (cls) { + if (cls.trim()) { + el.classList.remove(cls); + } + }); + } else { + var regex = new RegExp('(^| )' + name.split(' ').join('|') + '( |$)', 'gi'); + var className = getClassName(el).replace(regex, ' '); + setClassName(el, className); + } +} + +function addClass(el, name) { + if (typeof el.classList !== 'undefined') { + name.split(' ').forEach(function (cls) { + if (cls.trim()) { + el.classList.add(cls); + } + }); + } else { + removeClass(el, name); + var cls = getClassName(el) + (' ' + name); + setClassName(el, cls); + } +} + +function hasClass(el, name) { + if (typeof el.classList !== 'undefined') { + return el.classList.contains(name); + } + var className = getClassName(el); + return new RegExp('(^| )' + name + '( |$)', 'gi').test(className); +} + +function getClassName(el) { + // Can't use just SVGAnimatedString here since nodes within a Frame in IE have + // completely separately SVGAnimatedString base classes + if (el.className instanceof el.ownerDocument.defaultView.SVGAnimatedString) { + return el.className.baseVal; + } + return el.className; +} + +function setClassName(el, className) { + el.setAttribute('class', className); +} + +function updateClasses(el, add, all) { + // Of the set of 'all' classes, we need the 'add' classes, and only the + // 'add' classes to be set. + all.forEach(function (cls) { + if (add.indexOf(cls) === -1 && hasClass(el, cls)) { + removeClass(el, cls); + } + }); + + add.forEach(function (cls) { + if (!hasClass(el, cls)) { + addClass(el, cls); + } + }); +} + +var deferred = []; + +var defer = function defer(fn) { + deferred.push(fn); +}; + +var flush = function flush() { + var fn = undefined; + while (fn = deferred.pop()) { + fn(); + } +}; + +var Evented = (function () { + function Evented() { + _classCallCheck(this, Evented); + } + + _createClass(Evented, [{ + key: 'on', + value: function on(event, handler, ctx) { + var once = arguments.length <= 3 || arguments[3] === undefined ? false : arguments[3]; + + if (typeof this.bindings === 'undefined') { + this.bindings = {}; + } + if (typeof this.bindings[event] === 'undefined') { + this.bindings[event] = []; + } + this.bindings[event].push({ handler: handler, ctx: ctx, once: once }); + } + }, { + key: 'once', + value: function once(event, handler, ctx) { + this.on(event, handler, ctx, true); + } + }, { + key: 'off', + value: function off(event, handler) { + if (typeof this.bindings === 'undefined' || typeof this.bindings[event] === 'undefined') { + return; + } + + if (typeof handler === 'undefined') { + delete this.bindings[event]; + } else { + var i = 0; + while (i < this.bindings[event].length) { + if (this.bindings[event][i].handler === handler) { + this.bindings[event].splice(i, 1); + } else { + ++i; + } + } + } + } + }, { + key: 'trigger', + value: function trigger(event) { + if (typeof this.bindings !== 'undefined' && this.bindings[event]) { + var i = 0; + + for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + args[_key - 1] = arguments[_key]; + } + + while (i < this.bindings[event].length) { + var _bindings$event$i = this.bindings[event][i]; + var handler = _bindings$event$i.handler; + var ctx = _bindings$event$i.ctx; + var once = _bindings$event$i.once; + + var context = ctx; + if (typeof context === 'undefined') { + context = this; + } + + handler.apply(context, args); + + if (once) { + this.bindings[event].splice(i, 1); + } else { + ++i; + } + } + } + } + }]); + + return Evented; +})(); + +TetherBase.Utils = { + getActualBoundingClientRect: getActualBoundingClientRect, + getScrollParents: getScrollParents, + getBounds: getBounds, + getOffsetParent: getOffsetParent, + extend: extend, + addClass: addClass, + removeClass: removeClass, + hasClass: hasClass, + updateClasses: updateClasses, + defer: defer, + flush: flush, + uniqueId: uniqueId, + Evented: Evented, + getScrollBarSize: getScrollBarSize, + removeUtilElements: removeUtilElements +}; +/* globals TetherBase, performance */ + +'use strict'; + +var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } }; })(); + +var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + +var _get = function get(_x6, _x7, _x8) { var _again = true; _function: while (_again) { var object = _x6, property = _x7, receiver = _x8; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x6 = parent; _x7 = property; _x8 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + +function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +if (typeof TetherBase === 'undefined') { + throw new Error('You must include the utils.js file before tether.js'); +} + +var _TetherBase$Utils = TetherBase.Utils; +var getScrollParents = _TetherBase$Utils.getScrollParents; +var getBounds = _TetherBase$Utils.getBounds; +var getOffsetParent = _TetherBase$Utils.getOffsetParent; +var extend = _TetherBase$Utils.extend; +var addClass = _TetherBase$Utils.addClass; +var removeClass = _TetherBase$Utils.removeClass; +var updateClasses = _TetherBase$Utils.updateClasses; +var defer = _TetherBase$Utils.defer; +var flush = _TetherBase$Utils.flush; +var getScrollBarSize = _TetherBase$Utils.getScrollBarSize; +var removeUtilElements = _TetherBase$Utils.removeUtilElements; + +function within(a, b) { + var diff = arguments.length <= 2 || arguments[2] === undefined ? 1 : arguments[2]; + + return a + diff >= b && b >= a - diff; +} + +var transformKey = (function () { + if (typeof document === 'undefined') { + return ''; + } + var el = document.createElement('div'); + + var transforms = ['transform', 'WebkitTransform', 'OTransform', 'MozTransform', 'msTransform']; + for (var i = 0; i < transforms.length; ++i) { + var key = transforms[i]; + if (el.style[key] !== undefined) { + return key; + } + } +})(); + +var tethers = []; + +var position = function position() { + tethers.forEach(function (tether) { + tether.position(false); + }); + flush(); +}; + +function now() { + if (typeof performance !== 'undefined' && typeof performance.now !== 'undefined') { + return performance.now(); + } + return +new Date(); +} + +(function () { + var lastCall = null; + var lastDuration = null; + var pendingTimeout = null; + + var tick = function tick() { + if (typeof lastDuration !== 'undefined' && lastDuration > 16) { + // We voluntarily throttle ourselves if we can't manage 60fps + lastDuration = Math.min(lastDuration - 16, 250); + + // Just in case this is the last event, remember to position just once more + pendingTimeout = setTimeout(tick, 250); + return; + } + + if (typeof lastCall !== 'undefined' && now() - lastCall < 10) { + // Some browsers call events a little too frequently, refuse to run more than is reasonable + return; + } + + if (pendingTimeout != null) { + clearTimeout(pendingTimeout); + pendingTimeout = null; + } + + lastCall = now(); + position(); + lastDuration = now() - lastCall; + }; + + if (typeof window !== 'undefined' && typeof window.addEventListener !== 'undefined') { + ['resize', 'scroll', 'touchmove'].forEach(function (event) { + window.addEventListener(event, tick); + }); + } +})(); + +var MIRROR_LR = { + center: 'center', + left: 'right', + right: 'left' +}; + +var MIRROR_TB = { + middle: 'middle', + top: 'bottom', + bottom: 'top' +}; + +var OFFSET_MAP = { + top: 0, + left: 0, + middle: '50%', + center: '50%', + bottom: '100%', + right: '100%' +}; + +var autoToFixedAttachment = function autoToFixedAttachment(attachment, relativeToAttachment) { + var left = attachment.left; + var top = attachment.top; + + if (left === 'auto') { + left = MIRROR_LR[relativeToAttachment.left]; + } + + if (top === 'auto') { + top = MIRROR_TB[relativeToAttachment.top]; + } + + return { left: left, top: top }; +}; + +var attachmentToOffset = function attachmentToOffset(attachment) { + var left = attachment.left; + var top = attachment.top; + + if (typeof OFFSET_MAP[attachment.left] !== 'undefined') { + left = OFFSET_MAP[attachment.left]; + } + + if (typeof OFFSET_MAP[attachment.top] !== 'undefined') { + top = OFFSET_MAP[attachment.top]; + } + + return { left: left, top: top }; +}; + +function addOffset() { + var out = { top: 0, left: 0 }; + + for (var _len = arguments.length, offsets = Array(_len), _key = 0; _key < _len; _key++) { + offsets[_key] = arguments[_key]; + } + + offsets.forEach(function (_ref) { + var top = _ref.top; + var left = _ref.left; + + if (typeof top === 'string') { + top = parseFloat(top, 10); + } + if (typeof left === 'string') { + left = parseFloat(left, 10); + } + + out.top += top; + out.left += left; + }); + + return out; +} + +function offsetToPx(offset, size) { + if (typeof offset.left === 'string' && offset.left.indexOf('%') !== -1) { + offset.left = parseFloat(offset.left, 10) / 100 * size.width; + } + if (typeof offset.top === 'string' && offset.top.indexOf('%') !== -1) { + offset.top = parseFloat(offset.top, 10) / 100 * size.height; + } + + return offset; +} + +var parseOffset = function parseOffset(value) { + var _value$split = value.split(' '); + + var _value$split2 = _slicedToArray(_value$split, 2); + + var top = _value$split2[0]; + var left = _value$split2[1]; + + return { top: top, left: left }; +}; +var parseAttachment = parseOffset; + +var TetherClass = (function (_Evented) { + _inherits(TetherClass, _Evented); + + function TetherClass(options) { + var _this = this; + + _classCallCheck(this, TetherClass); + + _get(Object.getPrototypeOf(TetherClass.prototype), 'constructor', this).call(this); + this.position = this.position.bind(this); + + tethers.push(this); + + this.history = []; + + this.setOptions(options, false); + + TetherBase.modules.forEach(function (module) { + if (typeof module.initialize !== 'undefined') { + module.initialize.call(_this); + } + }); + + this.position(); + } + + _createClass(TetherClass, [{ + key: 'getClass', + value: function getClass() { + var key = arguments.length <= 0 || arguments[0] === undefined ? '' : arguments[0]; + var classes = this.options.classes; + + if (typeof classes !== 'undefined' && classes[key]) { + return this.options.classes[key]; + } else if (this.options.classPrefix) { + return this.options.classPrefix + '-' + key; + } else { + return key; + } + } + }, { + key: 'setOptions', + value: function setOptions(options) { + var _this2 = this; + + var pos = arguments.length <= 1 || arguments[1] === undefined ? true : arguments[1]; + + var defaults = { + offset: '0 0', + targetOffset: '0 0', + targetAttachment: 'auto auto', + classPrefix: 'tether' + }; + + this.options = extend(defaults, options); + + var _options = this.options; + var element = _options.element; + var target = _options.target; + var targetModifier = _options.targetModifier; + + this.element = element; + this.target = target; + this.targetModifier = targetModifier; + + if (this.target === 'viewport') { + this.target = document.body; + this.targetModifier = 'visible'; + } else if (this.target === 'scroll-handle') { + this.target = document.body; + this.targetModifier = 'scroll-handle'; + } + + ['element', 'target'].forEach(function (key) { + if (typeof _this2[key] === 'undefined') { + throw new Error('Tether Error: Both element and target must be defined'); + } + + if (typeof _this2[key].jquery !== 'undefined') { + _this2[key] = _this2[key][0]; + } else if (typeof _this2[key] === 'string') { + _this2[key] = document.querySelector(_this2[key]); + } + }); + + addClass(this.element, this.getClass('element')); + if (!(this.options.addTargetClasses === false)) { + addClass(this.target, this.getClass('target')); + } + + if (!this.options.attachment) { + throw new Error('Tether Error: You must provide an attachment'); + } + + this.targetAttachment = parseAttachment(this.options.targetAttachment); + this.attachment = parseAttachment(this.options.attachment); + this.offset = parseOffset(this.options.offset); + this.targetOffset = parseOffset(this.options.targetOffset); + + if (typeof this.scrollParents !== 'undefined') { + this.disable(); + } + + if (this.targetModifier === 'scroll-handle') { + this.scrollParents = [this.target]; + } else { + this.scrollParents = getScrollParents(this.target); + } + + if (!(this.options.enabled === false)) { + this.enable(pos); + } + } + }, { + key: 'getTargetBounds', + value: function getTargetBounds() { + if (typeof this.targetModifier !== 'undefined') { + if (this.targetModifier === 'visible') { + if (this.target === document.body) { + return { top: pageYOffset, left: pageXOffset, height: innerHeight, width: innerWidth }; + } else { + var bounds = getBounds(this.target); + + var out = { + height: bounds.height, + width: bounds.width, + top: bounds.top, + left: bounds.left + }; + + out.height = Math.min(out.height, bounds.height - (pageYOffset - bounds.top)); + out.height = Math.min(out.height, bounds.height - (bounds.top + bounds.height - (pageYOffset + innerHeight))); + out.height = Math.min(innerHeight, out.height); + out.height -= 2; + + out.width = Math.min(out.width, bounds.width - (pageXOffset - bounds.left)); + out.width = Math.min(out.width, bounds.width - (bounds.left + bounds.width - (pageXOffset + innerWidth))); + out.width = Math.min(innerWidth, out.width); + out.width -= 2; + + if (out.top < pageYOffset) { + out.top = pageYOffset; + } + if (out.left < pageXOffset) { + out.left = pageXOffset; + } + + return out; + } + } else if (this.targetModifier === 'scroll-handle') { + var bounds = undefined; + var target = this.target; + if (target === document.body) { + target = document.documentElement; + + bounds = { + left: pageXOffset, + top: pageYOffset, + height: innerHeight, + width: innerWidth + }; + } else { + bounds = getBounds(target); + } + + var style = getComputedStyle(target); + + var hasBottomScroll = target.scrollWidth > target.clientWidth || [style.overflow, style.overflowX].indexOf('scroll') >= 0 || this.target !== document.body; + + var scrollBottom = 0; + if (hasBottomScroll) { + scrollBottom = 15; + } + + var height = bounds.height - parseFloat(style.borderTopWidth) - parseFloat(style.borderBottomWidth) - scrollBottom; + + var out = { + width: 15, + height: height * 0.975 * (height / target.scrollHeight), + left: bounds.left + bounds.width - parseFloat(style.borderLeftWidth) - 15 + }; + + var fitAdj = 0; + if (height < 408 && this.target === document.body) { + fitAdj = -0.00011 * Math.pow(height, 2) - 0.00727 * height + 22.58; + } + + if (this.target !== document.body) { + out.height = Math.max(out.height, 24); + } + + var scrollPercentage = this.target.scrollTop / (target.scrollHeight - height); + out.top = scrollPercentage * (height - out.height - fitAdj) + bounds.top + parseFloat(style.borderTopWidth); + + if (this.target === document.body) { + out.height = Math.max(out.height, 24); + } + + return out; + } + } else { + return getBounds(this.target); + } + } + }, { + key: 'clearCache', + value: function clearCache() { + this._cache = {}; + } + }, { + key: 'cache', + value: function cache(k, getter) { + // More than one module will often need the same DOM info, so + // we keep a cache which is cleared on each position call + if (typeof this._cache === 'undefined') { + this._cache = {}; + } + + if (typeof this._cache[k] === 'undefined') { + this._cache[k] = getter.call(this); + } + + return this._cache[k]; + } + }, { + key: 'enable', + value: function enable() { + var _this3 = this; + + var pos = arguments.length <= 0 || arguments[0] === undefined ? true : arguments[0]; + + if (!(this.options.addTargetClasses === false)) { + addClass(this.target, this.getClass('enabled')); + } + addClass(this.element, this.getClass('enabled')); + this.enabled = true; + + this.scrollParents.forEach(function (parent) { + if (parent !== _this3.target.ownerDocument) { + parent.addEventListener('scroll', _this3.position); + } + }); + + if (pos) { + this.position(); + } + } + }, { + key: 'disable', + value: function disable() { + var _this4 = this; + + removeClass(this.target, this.getClass('enabled')); + removeClass(this.element, this.getClass('enabled')); + this.enabled = false; + + if (typeof this.scrollParents !== 'undefined') { + this.scrollParents.forEach(function (parent) { + parent.removeEventListener('scroll', _this4.position); + }); + } + } + }, { + key: 'destroy', + value: function destroy() { + var _this5 = this; + + this.disable(); + + tethers.forEach(function (tether, i) { + if (tether === _this5) { + tethers.splice(i, 1); + } + }); + + // Remove any elements we were using for convenience from the DOM + if (tethers.length === 0) { + removeUtilElements(); + } + } + }, { + key: 'updateAttachClasses', + value: function updateAttachClasses(elementAttach, targetAttach) { + var _this6 = this; + + elementAttach = elementAttach || this.attachment; + targetAttach = targetAttach || this.targetAttachment; + var sides = ['left', 'top', 'bottom', 'right', 'middle', 'center']; + + if (typeof this._addAttachClasses !== 'undefined' && this._addAttachClasses.length) { + // updateAttachClasses can be called more than once in a position call, so + // we need to clean up after ourselves such that when the last defer gets + // ran it doesn't add any extra classes from previous calls. + this._addAttachClasses.splice(0, this._addAttachClasses.length); + } + + if (typeof this._addAttachClasses === 'undefined') { + this._addAttachClasses = []; + } + var add = this._addAttachClasses; + + if (elementAttach.top) { + add.push(this.getClass('element-attached') + '-' + elementAttach.top); + } + if (elementAttach.left) { + add.push(this.getClass('element-attached') + '-' + elementAttach.left); + } + if (targetAttach.top) { + add.push(this.getClass('target-attached') + '-' + targetAttach.top); + } + if (targetAttach.left) { + add.push(this.getClass('target-attached') + '-' + targetAttach.left); + } + + var all = []; + sides.forEach(function (side) { + all.push(_this6.getClass('element-attached') + '-' + side); + all.push(_this6.getClass('target-attached') + '-' + side); + }); + + defer(function () { + if (!(typeof _this6._addAttachClasses !== 'undefined')) { + return; + } + + updateClasses(_this6.element, _this6._addAttachClasses, all); + if (!(_this6.options.addTargetClasses === false)) { + updateClasses(_this6.target, _this6._addAttachClasses, all); + } + + delete _this6._addAttachClasses; + }); + } + }, { + key: 'position', + value: function position() { + var _this7 = this; + + var flushChanges = arguments.length <= 0 || arguments[0] === undefined ? true : arguments[0]; + + // flushChanges commits the changes immediately, leave true unless you are positioning multiple + // tethers (in which case call Tether.Utils.flush yourself when you're done) + + if (!this.enabled) { + return; + } + + this.clearCache(); + + // Turn 'auto' attachments into the appropriate corner or edge + var targetAttachment = autoToFixedAttachment(this.targetAttachment, this.attachment); + + this.updateAttachClasses(this.attachment, targetAttachment); + + var elementPos = this.cache('element-bounds', function () { + return getBounds(_this7.element); + }); + + var width = elementPos.width; + var height = elementPos.height; + + if (width === 0 && height === 0 && typeof this.lastSize !== 'undefined') { + var _lastSize = this.lastSize; + + // We cache the height and width to make it possible to position elements that are + // getting hidden. + width = _lastSize.width; + height = _lastSize.height; + } else { + this.lastSize = { width: width, height: height }; + } + + var targetPos = this.cache('target-bounds', function () { + return _this7.getTargetBounds(); + }); + var targetSize = targetPos; + + // Get an actual px offset from the attachment + var offset = offsetToPx(attachmentToOffset(this.attachment), { width: width, height: height }); + var targetOffset = offsetToPx(attachmentToOffset(targetAttachment), targetSize); + + var manualOffset = offsetToPx(this.offset, { width: width, height: height }); + var manualTargetOffset = offsetToPx(this.targetOffset, targetSize); + + // Add the manually provided offset + offset = addOffset(offset, manualOffset); + targetOffset = addOffset(targetOffset, manualTargetOffset); + + // It's now our goal to make (element position + offset) == (target position + target offset) + var left = targetPos.left + targetOffset.left - offset.left; + var top = targetPos.top + targetOffset.top - offset.top; + + for (var i = 0; i < TetherBase.modules.length; ++i) { + var _module2 = TetherBase.modules[i]; + var ret = _module2.position.call(this, { + left: left, + top: top, + targetAttachment: targetAttachment, + targetPos: targetPos, + elementPos: elementPos, + offset: offset, + targetOffset: targetOffset, + manualOffset: manualOffset, + manualTargetOffset: manualTargetOffset, + scrollbarSize: scrollbarSize, + attachment: this.attachment + }); + + if (ret === false) { + return false; + } else if (typeof ret === 'undefined' || typeof ret !== 'object') { + continue; + } else { + top = ret.top; + left = ret.left; + } + } + + // We describe the position three different ways to give the optimizer + // a chance to decide the best possible way to position the element + // with the fewest repaints. + var next = { + // It's position relative to the page (absolute positioning when + // the element is a child of the body) + page: { + top: top, + left: left + }, + + // It's position relative to the viewport (fixed positioning) + viewport: { + top: top - pageYOffset, + bottom: pageYOffset - top - height + innerHeight, + left: left - pageXOffset, + right: pageXOffset - left - width + innerWidth + } + }; + + var doc = this.target.ownerDocument; + var win = doc.defaultView; + + var scrollbarSize = undefined; + if (win.innerHeight > doc.documentElement.clientHeight) { + scrollbarSize = this.cache('scrollbar-size', getScrollBarSize); + next.viewport.bottom -= scrollbarSize.height; + } + + if (win.innerWidth > doc.documentElement.clientWidth) { + scrollbarSize = this.cache('scrollbar-size', getScrollBarSize); + next.viewport.right -= scrollbarSize.width; + } + + if (['', 'static'].indexOf(doc.body.style.position) === -1 || ['', 'static'].indexOf(doc.body.parentElement.style.position) === -1) { + // Absolute positioning in the body will be relative to the page, not the 'initial containing block' + next.page.bottom = doc.body.scrollHeight - top - height; + next.page.right = doc.body.scrollWidth - left - width; + } + + if (typeof this.options.optimizations !== 'undefined' && this.options.optimizations.moveElement !== false && !(typeof this.targetModifier !== 'undefined')) { + (function () { + var offsetParent = _this7.cache('target-offsetparent', function () { + return getOffsetParent(_this7.target); + }); + var offsetPosition = _this7.cache('target-offsetparent-bounds', function () { + return getBounds(offsetParent); + }); + var offsetParentStyle = getComputedStyle(offsetParent); + var offsetParentSize = offsetPosition; + + var offsetBorder = {}; + ['Top', 'Left', 'Bottom', 'Right'].forEach(function (side) { + offsetBorder[side.toLowerCase()] = parseFloat(offsetParentStyle['border' + side + 'Width']); + }); + + offsetPosition.right = doc.body.scrollWidth - offsetPosition.left - offsetParentSize.width + offsetBorder.right; + offsetPosition.bottom = doc.body.scrollHeight - offsetPosition.top - offsetParentSize.height + offsetBorder.bottom; + + if (next.page.top >= offsetPosition.top + offsetBorder.top && next.page.bottom >= offsetPosition.bottom) { + if (next.page.left >= offsetPosition.left + offsetBorder.left && next.page.right >= offsetPosition.right) { + // We're within the visible part of the target's scroll parent + var scrollTop = offsetParent.scrollTop; + var scrollLeft = offsetParent.scrollLeft; + + // It's position relative to the target's offset parent (absolute positioning when + // the element is moved to be a child of the target's offset parent). + next.offset = { + top: next.page.top - offsetPosition.top + scrollTop - offsetBorder.top, + left: next.page.left - offsetPosition.left + scrollLeft - offsetBorder.left + }; + } + } + })(); + } + + // We could also travel up the DOM and try each containing context, rather than only + // looking at the body, but we're gonna get diminishing returns. + + this.move(next); + + this.history.unshift(next); + + if (this.history.length > 3) { + this.history.pop(); + } + + if (flushChanges) { + flush(); + } + + return true; + } + + // THE ISSUE + }, { + key: 'move', + value: function move(pos) { + var _this8 = this; + + if (!(typeof this.element.parentNode !== 'undefined')) { + return; + } + + var same = {}; + + for (var type in pos) { + same[type] = {}; + + for (var key in pos[type]) { + var found = false; + + for (var i = 0; i < this.history.length; ++i) { + var point = this.history[i]; + if (typeof point[type] !== 'undefined' && !within(point[type][key], pos[type][key])) { + found = true; + break; + } + } + + if (!found) { + same[type][key] = true; + } + } + } + + var css = { top: '', left: '', right: '', bottom: '' }; + + var transcribe = function transcribe(_same, _pos) { + var hasOptimizations = typeof _this8.options.optimizations !== 'undefined'; + var gpu = hasOptimizations ? _this8.options.optimizations.gpu : null; + if (gpu !== false) { + var yPos = undefined, + xPos = undefined; + if (_same.top) { + css.top = 0; + yPos = _pos.top; + } else { + css.bottom = 0; + yPos = -_pos.bottom; + } + + if (_same.left) { + css.left = 0; + xPos = _pos.left; + } else { + css.right = 0; + xPos = -_pos.right; + } + + if (window.matchMedia) { + // HubSpot/tether#207 + var retina = window.matchMedia('only screen and (min-resolution: 1.3dppx)').matches || window.matchMedia('only screen and (-webkit-min-device-pixel-ratio: 1.3)').matches; + if (!retina) { + xPos = Math.round(xPos); + yPos = Math.round(yPos); + } + } + + css[transformKey] = 'translateX(' + xPos + 'px) translateY(' + yPos + 'px)'; + + if (transformKey !== 'msTransform') { + // The Z transform will keep this in the GPU (faster, and prevents artifacts), + // but IE9 doesn't support 3d transforms and will choke. + css[transformKey] += " translateZ(0)"; + } + } else { + if (_same.top) { + css.top = _pos.top + 'px'; + } else { + css.bottom = _pos.bottom + 'px'; + } + + if (_same.left) { + css.left = _pos.left + 'px'; + } else { + css.right = _pos.right + 'px'; + } + } + }; + + var moved = false; + if ((same.page.top || same.page.bottom) && (same.page.left || same.page.right)) { + css.position = 'absolute'; + transcribe(same.page, pos.page); + } else if ((same.viewport.top || same.viewport.bottom) && (same.viewport.left || same.viewport.right)) { + css.position = 'fixed'; + transcribe(same.viewport, pos.viewport); + } else if (typeof same.offset !== 'undefined' && same.offset.top && same.offset.left) { + (function () { + css.position = 'absolute'; + var offsetParent = _this8.cache('target-offsetparent', function () { + return getOffsetParent(_this8.target); + }); + + if (getOffsetParent(_this8.element) !== offsetParent) { + defer(function () { + _this8.element.parentNode.removeChild(_this8.element); + offsetParent.appendChild(_this8.element); + }); + } + + transcribe(same.offset, pos.offset); + moved = true; + })(); + } else { + css.position = 'absolute'; + transcribe({ top: true, left: true }, pos.page); + } + + if (!moved) { + if (this.options.bodyElement) { + this.options.bodyElement.appendChild(this.element); + } else { + var offsetParentIsBody = true; + var currentNode = this.element.parentNode; + while (currentNode && currentNode.nodeType === 1 && currentNode.tagName !== 'BODY') { + if (getComputedStyle(currentNode).position !== 'static') { + offsetParentIsBody = false; + break; + } + + currentNode = currentNode.parentNode; + } + + if (!offsetParentIsBody) { + this.element.parentNode.removeChild(this.element); + this.element.ownerDocument.body.appendChild(this.element); + } + } + } + + // Any css change will trigger a repaint, so let's avoid one if nothing changed + var writeCSS = {}; + var write = false; + for (var key in css) { + var val = css[key]; + var elVal = this.element.style[key]; + + if (elVal !== val) { + write = true; + writeCSS[key] = val; + } + } + + if (write) { + defer(function () { + extend(_this8.element.style, writeCSS); + _this8.trigger('repositioned'); + }); + } + } + }]); + + return TetherClass; +})(Evented); + +TetherClass.modules = []; + +TetherBase.position = position; + +var Tether = extend(TetherClass, TetherBase); +/* globals TetherBase */ + +'use strict'; + +var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } }; })(); + +var _TetherBase$Utils = TetherBase.Utils; +var getBounds = _TetherBase$Utils.getBounds; +var extend = _TetherBase$Utils.extend; +var updateClasses = _TetherBase$Utils.updateClasses; +var defer = _TetherBase$Utils.defer; + +var BOUNDS_FORMAT = ['left', 'top', 'right', 'bottom']; + +function getBoundingRect(tether, to) { + if (to === 'scrollParent') { + to = tether.scrollParents[0]; + } else if (to === 'window') { + to = [pageXOffset, pageYOffset, innerWidth + pageXOffset, innerHeight + pageYOffset]; + } + + if (to === document) { + to = to.documentElement; + } + + if (typeof to.nodeType !== 'undefined') { + (function () { + var node = to; + var size = getBounds(to); + var pos = size; + var style = getComputedStyle(to); + + to = [pos.left, pos.top, size.width + pos.left, size.height + pos.top]; + + // Account any parent Frames scroll offset + if (node.ownerDocument !== document) { + var win = node.ownerDocument.defaultView; + to[0] += win.pageXOffset; + to[1] += win.pageYOffset; + to[2] += win.pageXOffset; + to[3] += win.pageYOffset; + } + + BOUNDS_FORMAT.forEach(function (side, i) { + side = side[0].toUpperCase() + side.substr(1); + if (side === 'Top' || side === 'Left') { + to[i] += parseFloat(style['border' + side + 'Width']); + } else { + to[i] -= parseFloat(style['border' + side + 'Width']); + } + }); + })(); + } + + return to; +} + +TetherBase.modules.push({ + position: function position(_ref) { + var _this = this; + + var top = _ref.top; + var left = _ref.left; + var targetAttachment = _ref.targetAttachment; + + if (!this.options.constraints) { + return true; + } + + var _cache = this.cache('element-bounds', function () { + return getBounds(_this.element); + }); + + var height = _cache.height; + var width = _cache.width; + + if (width === 0 && height === 0 && typeof this.lastSize !== 'undefined') { + var _lastSize = this.lastSize; + + // Handle the item getting hidden as a result of our positioning without glitching + // the classes in and out + width = _lastSize.width; + height = _lastSize.height; + } + + var targetSize = this.cache('target-bounds', function () { + return _this.getTargetBounds(); + }); + + var targetHeight = targetSize.height; + var targetWidth = targetSize.width; + + var allClasses = [this.getClass('pinned'), this.getClass('out-of-bounds')]; + + this.options.constraints.forEach(function (constraint) { + var outOfBoundsClass = constraint.outOfBoundsClass; + var pinnedClass = constraint.pinnedClass; + + if (outOfBoundsClass) { + allClasses.push(outOfBoundsClass); + } + if (pinnedClass) { + allClasses.push(pinnedClass); + } + }); + + allClasses.forEach(function (cls) { + ['left', 'top', 'right', 'bottom'].forEach(function (side) { + allClasses.push(cls + '-' + side); + }); + }); + + var addClasses = []; + + var tAttachment = extend({}, targetAttachment); + var eAttachment = extend({}, this.attachment); + + this.options.constraints.forEach(function (constraint) { + var to = constraint.to; + var attachment = constraint.attachment; + var pin = constraint.pin; + + if (typeof attachment === 'undefined') { + attachment = ''; + } + + var changeAttachX = undefined, + changeAttachY = undefined; + if (attachment.indexOf(' ') >= 0) { + var _attachment$split = attachment.split(' '); + + var _attachment$split2 = _slicedToArray(_attachment$split, 2); + + changeAttachY = _attachment$split2[0]; + changeAttachX = _attachment$split2[1]; + } else { + changeAttachX = changeAttachY = attachment; + } + + var bounds = getBoundingRect(_this, to); + + if (changeAttachY === 'target' || changeAttachY === 'both') { + if (top < bounds[1] && tAttachment.top === 'top') { + top += targetHeight; + tAttachment.top = 'bottom'; + } + + if (top + height > bounds[3] && tAttachment.top === 'bottom') { + top -= targetHeight; + tAttachment.top = 'top'; + } + } + + if (changeAttachY === 'together') { + if (tAttachment.top === 'top') { + if (eAttachment.top === 'bottom' && top < bounds[1]) { + top += targetHeight; + tAttachment.top = 'bottom'; + + top += height; + eAttachment.top = 'top'; + } else if (eAttachment.top === 'top' && top + height > bounds[3] && top - (height - targetHeight) >= bounds[1]) { + top -= height - targetHeight; + tAttachment.top = 'bottom'; + + eAttachment.top = 'bottom'; + } + } + + if (tAttachment.top === 'bottom') { + if (eAttachment.top === 'top' && top + height > bounds[3]) { + top -= targetHeight; + tAttachment.top = 'top'; + + top -= height; + eAttachment.top = 'bottom'; + } else if (eAttachment.top === 'bottom' && top < bounds[1] && top + (height * 2 - targetHeight) <= bounds[3]) { + top += height - targetHeight; + tAttachment.top = 'top'; + + eAttachment.top = 'top'; + } + } + + if (tAttachment.top === 'middle') { + if (top + height > bounds[3] && eAttachment.top === 'top') { + top -= height; + eAttachment.top = 'bottom'; + } else if (top < bounds[1] && eAttachment.top === 'bottom') { + top += height; + eAttachment.top = 'top'; + } + } + } + + if (changeAttachX === 'target' || changeAttachX === 'both') { + if (left < bounds[0] && tAttachment.left === 'left') { + left += targetWidth; + tAttachment.left = 'right'; + } + + if (left + width > bounds[2] && tAttachment.left === 'right') { + left -= targetWidth; + tAttachment.left = 'left'; + } + } + + if (changeAttachX === 'together') { + if (left < bounds[0] && tAttachment.left === 'left') { + if (eAttachment.left === 'right') { + left += targetWidth; + tAttachment.left = 'right'; + + left += width; + eAttachment.left = 'left'; + } else if (eAttachment.left === 'left') { + left += targetWidth; + tAttachment.left = 'right'; + + left -= width; + eAttachment.left = 'right'; + } + } else if (left + width > bounds[2] && tAttachment.left === 'right') { + if (eAttachment.left === 'left') { + left -= targetWidth; + tAttachment.left = 'left'; + + left -= width; + eAttachment.left = 'right'; + } else if (eAttachment.left === 'right') { + left -= targetWidth; + tAttachment.left = 'left'; + + left += width; + eAttachment.left = 'left'; + } + } else if (tAttachment.left === 'center') { + if (left + width > bounds[2] && eAttachment.left === 'left') { + left -= width; + eAttachment.left = 'right'; + } else if (left < bounds[0] && eAttachment.left === 'right') { + left += width; + eAttachment.left = 'left'; + } + } + } + + if (changeAttachY === 'element' || changeAttachY === 'both') { + if (top < bounds[1] && eAttachment.top === 'bottom') { + top += height; + eAttachment.top = 'top'; + } + + if (top + height > bounds[3] && eAttachment.top === 'top') { + top -= height; + eAttachment.top = 'bottom'; + } + } + + if (changeAttachX === 'element' || changeAttachX === 'both') { + if (left < bounds[0]) { + if (eAttachment.left === 'right') { + left += width; + eAttachment.left = 'left'; + } else if (eAttachment.left === 'center') { + left += width / 2; + eAttachment.left = 'left'; + } + } + + if (left + width > bounds[2]) { + if (eAttachment.left === 'left') { + left -= width; + eAttachment.left = 'right'; + } else if (eAttachment.left === 'center') { + left -= width / 2; + eAttachment.left = 'right'; + } + } + } + + if (typeof pin === 'string') { + pin = pin.split(',').map(function (p) { + return p.trim(); + }); + } else if (pin === true) { + pin = ['top', 'left', 'right', 'bottom']; + } + + pin = pin || []; + + var pinned = []; + var oob = []; + + if (top < bounds[1]) { + if (pin.indexOf('top') >= 0) { + top = bounds[1]; + pinned.push('top'); + } else { + oob.push('top'); + } + } + + if (top + height > bounds[3]) { + if (pin.indexOf('bottom') >= 0) { + top = bounds[3] - height; + pinned.push('bottom'); + } else { + oob.push('bottom'); + } + } + + if (left < bounds[0]) { + if (pin.indexOf('left') >= 0) { + left = bounds[0]; + pinned.push('left'); + } else { + oob.push('left'); + } + } + + if (left + width > bounds[2]) { + if (pin.indexOf('right') >= 0) { + left = bounds[2] - width; + pinned.push('right'); + } else { + oob.push('right'); + } + } + + if (pinned.length) { + (function () { + var pinnedClass = undefined; + if (typeof _this.options.pinnedClass !== 'undefined') { + pinnedClass = _this.options.pinnedClass; + } else { + pinnedClass = _this.getClass('pinned'); + } + + addClasses.push(pinnedClass); + pinned.forEach(function (side) { + addClasses.push(pinnedClass + '-' + side); + }); + })(); + } + + if (oob.length) { + (function () { + var oobClass = undefined; + if (typeof _this.options.outOfBoundsClass !== 'undefined') { + oobClass = _this.options.outOfBoundsClass; + } else { + oobClass = _this.getClass('out-of-bounds'); + } + + addClasses.push(oobClass); + oob.forEach(function (side) { + addClasses.push(oobClass + '-' + side); + }); + })(); + } + + if (pinned.indexOf('left') >= 0 || pinned.indexOf('right') >= 0) { + eAttachment.left = tAttachment.left = false; + } + if (pinned.indexOf('top') >= 0 || pinned.indexOf('bottom') >= 0) { + eAttachment.top = tAttachment.top = false; + } + + if (tAttachment.top !== targetAttachment.top || tAttachment.left !== targetAttachment.left || eAttachment.top !== _this.attachment.top || eAttachment.left !== _this.attachment.left) { + _this.updateAttachClasses(eAttachment, tAttachment); + _this.trigger('update', { + attachment: eAttachment, + targetAttachment: tAttachment + }); + } + }); + + defer(function () { + if (!(_this.options.addTargetClasses === false)) { + updateClasses(_this.target, addClasses, allClasses); + } + updateClasses(_this.element, addClasses, allClasses); + }); + + return { top: top, left: left }; + } +}); +/* globals TetherBase */ + +'use strict'; + +var _TetherBase$Utils = TetherBase.Utils; +var getBounds = _TetherBase$Utils.getBounds; +var updateClasses = _TetherBase$Utils.updateClasses; +var defer = _TetherBase$Utils.defer; + +TetherBase.modules.push({ + position: function position(_ref) { + var _this = this; + + var top = _ref.top; + var left = _ref.left; + + var _cache = this.cache('element-bounds', function () { + return getBounds(_this.element); + }); + + var height = _cache.height; + var width = _cache.width; + + var targetPos = this.getTargetBounds(); + + var bottom = top + height; + var right = left + width; + + var abutted = []; + if (top <= targetPos.bottom && bottom >= targetPos.top) { + ['left', 'right'].forEach(function (side) { + var targetPosSide = targetPos[side]; + if (targetPosSide === left || targetPosSide === right) { + abutted.push(side); + } + }); + } + + if (left <= targetPos.right && right >= targetPos.left) { + ['top', 'bottom'].forEach(function (side) { + var targetPosSide = targetPos[side]; + if (targetPosSide === top || targetPosSide === bottom) { + abutted.push(side); + } + }); + } + + var allClasses = []; + var addClasses = []; + + var sides = ['left', 'top', 'right', 'bottom']; + allClasses.push(this.getClass('abutted')); + sides.forEach(function (side) { + allClasses.push(_this.getClass('abutted') + '-' + side); + }); + + if (abutted.length) { + addClasses.push(this.getClass('abutted')); + } + + abutted.forEach(function (side) { + addClasses.push(_this.getClass('abutted') + '-' + side); + }); + + defer(function () { + if (!(_this.options.addTargetClasses === false)) { + updateClasses(_this.target, addClasses, allClasses); + } + updateClasses(_this.element, addClasses, allClasses); + }); + + return true; + } +}); +/* globals TetherBase */ + +'use strict'; + +var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } }; })(); + +TetherBase.modules.push({ + position: function position(_ref) { + var top = _ref.top; + var left = _ref.left; + + if (!this.options.shift) { + return; + } + + var shift = this.options.shift; + if (typeof this.options.shift === 'function') { + shift = this.options.shift.call(this, { top: top, left: left }); + } + + var shiftTop = undefined, + shiftLeft = undefined; + if (typeof shift === 'string') { + shift = shift.split(' '); + shift[1] = shift[1] || shift[0]; + + var _shift = shift; + + var _shift2 = _slicedToArray(_shift, 2); + + shiftTop = _shift2[0]; + shiftLeft = _shift2[1]; + + shiftTop = parseFloat(shiftTop, 10); + shiftLeft = parseFloat(shiftLeft, 10); + } else { + shiftTop = shift.top; + shiftLeft = shift.left; + } + + top += shiftTop; + left += shiftLeft; + + return { top: top, left: left }; + } +}); +return Tether; + +})); diff --git a/src/legacy/design-studio/js/yoga-layout.js b/src/legacy/design-studio/js/yoga-layout.js new file mode 100644 index 0000000..992e357 --- /dev/null +++ b/src/legacy/design-studio/js/yoga-layout.js @@ -0,0 +1,10202 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global.yogaLayout = factory()); +}(this, (function () { 'use strict'; + +/** + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + * @format + */ + +var CONSTANTS = { + ALIGN_COUNT: 8, + ALIGN_AUTO: 0, + ALIGN_FLEX_START: 1, + ALIGN_CENTER: 2, + ALIGN_FLEX_END: 3, + ALIGN_STRETCH: 4, + ALIGN_BASELINE: 5, + ALIGN_SPACE_BETWEEN: 6, + ALIGN_SPACE_AROUND: 7, + + DIMENSION_COUNT: 2, + DIMENSION_WIDTH: 0, + DIMENSION_HEIGHT: 1, + + DIRECTION_COUNT: 3, + DIRECTION_INHERIT: 0, + DIRECTION_LTR: 1, + DIRECTION_RTL: 2, + + DISPLAY_COUNT: 2, + DISPLAY_FLEX: 0, + DISPLAY_NONE: 1, + + EDGE_COUNT: 9, + EDGE_LEFT: 0, + EDGE_TOP: 1, + EDGE_RIGHT: 2, + EDGE_BOTTOM: 3, + EDGE_START: 4, + EDGE_END: 5, + EDGE_HORIZONTAL: 6, + EDGE_VERTICAL: 7, + EDGE_ALL: 8, + + EXPERIMENTAL_FEATURE_COUNT: 1, + EXPERIMENTAL_FEATURE_WEB_FLEX_BASIS: 0, + + FLEX_DIRECTION_COUNT: 4, + FLEX_DIRECTION_COLUMN: 0, + FLEX_DIRECTION_COLUMN_REVERSE: 1, + FLEX_DIRECTION_ROW: 2, + FLEX_DIRECTION_ROW_REVERSE: 3, + + JUSTIFY_COUNT: 6, + JUSTIFY_FLEX_START: 0, + JUSTIFY_CENTER: 1, + JUSTIFY_FLEX_END: 2, + JUSTIFY_SPACE_BETWEEN: 3, + JUSTIFY_SPACE_AROUND: 4, + JUSTIFY_SPACE_EVENLY: 5, + + LOG_LEVEL_COUNT: 6, + LOG_LEVEL_ERROR: 0, + LOG_LEVEL_WARN: 1, + LOG_LEVEL_INFO: 2, + LOG_LEVEL_DEBUG: 3, + LOG_LEVEL_VERBOSE: 4, + LOG_LEVEL_FATAL: 5, + + MEASURE_MODE_COUNT: 3, + MEASURE_MODE_UNDEFINED: 0, + MEASURE_MODE_EXACTLY: 1, + MEASURE_MODE_AT_MOST: 2, + + NODE_TYPE_COUNT: 2, + NODE_TYPE_DEFAULT: 0, + NODE_TYPE_TEXT: 1, + + OVERFLOW_COUNT: 3, + OVERFLOW_VISIBLE: 0, + OVERFLOW_HIDDEN: 1, + OVERFLOW_SCROLL: 2, + + POSITION_TYPE_COUNT: 2, + POSITION_TYPE_RELATIVE: 0, + POSITION_TYPE_ABSOLUTE: 1, + + PRINT_OPTIONS_COUNT: 3, + PRINT_OPTIONS_LAYOUT: 1, + PRINT_OPTIONS_STYLE: 2, + PRINT_OPTIONS_CHILDREN: 4, + + UNIT_COUNT: 4, + UNIT_UNDEFINED: 0, + UNIT_POINT: 1, + UNIT_PERCENT: 2, + UNIT_AUTO: 3, + + WRAP_COUNT: 3, + WRAP_NO_WRAP: 0, + WRAP_WRAP: 1, + WRAP_WRAP_REVERSE: 2 +}; + +var YGEnums = CONSTANTS; + +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +/** + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + * @format + */ + + + +var Layout = function () { + function Layout(left, right, top, bottom, width, height) { + _classCallCheck(this, Layout); + + this.left = left; + this.right = right; + this.top = top; + this.bottom = bottom; + this.width = width; + this.height = height; + } + + _createClass(Layout, [{ + key: 'fromJS', + value: function fromJS(expose) { + expose(this.left, this.right, this.top, this.bottom, this.width, this.height); + } + }, { + key: 'toString', + value: function toString() { + return '<Layout#' + this.left + ':' + this.right + ';' + this.top + ':' + this.bottom + ';' + this.width + ':' + this.height + '>'; + } + }]); + + return Layout; +}(); + +var Size = function () { + _createClass(Size, null, [{ + key: 'fromJS', + value: function fromJS(_ref) { + var width = _ref.width, + height = _ref.height; + + return new Size(width, height); + } + }]); + + function Size(width, height) { + _classCallCheck(this, Size); + + this.width = width; + this.height = height; + } + + _createClass(Size, [{ + key: 'fromJS', + value: function fromJS(expose) { + expose(this.width, this.height); + } + }, { + key: 'toString', + value: function toString() { + return '<Size#' + this.width + 'x' + this.height + '>'; + } + }]); + + return Size; +}(); + +var Value = function () { + function Value(unit, value) { + _classCallCheck(this, Value); + + this.unit = unit; + this.value = value; + } + + _createClass(Value, [{ + key: 'fromJS', + value: function fromJS(expose) { + expose(this.unit, this.value); + } + }, { + key: 'toString', + value: function toString() { + switch (this.unit) { + case YGEnums.UNIT_POINT: + return String(this.value); + case YGEnums.UNIT_PERCENT: + return this.value + '%'; + case YGEnums.UNIT_AUTO: + return 'auto'; + default: + { + return this.value + '?'; + } + } + } + }, { + key: 'valueOf', + value: function valueOf() { + return this.value; + } + }]); + + return Value; +}(); + +var entryCommon = function (bind, lib) { + function patch(prototype, name, fn) { + var original = prototype[name]; + + prototype[name] = function () { + for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + return fn.call.apply(fn, [this, original].concat(args)); + }; + } + + var _arr = ['setPosition', 'setMargin', 'setFlexBasis', 'setWidth', 'setHeight', 'setMinWidth', 'setMinHeight', 'setMaxWidth', 'setMaxHeight', 'setPadding']; + + var _loop = function _loop() { + var _methods; + + var fnName = _arr[_i]; + var methods = (_methods = {}, _defineProperty(_methods, YGEnums.UNIT_POINT, lib.Node.prototype[fnName]), _defineProperty(_methods, YGEnums.UNIT_PERCENT, lib.Node.prototype[fnName + 'Percent']), _defineProperty(_methods, YGEnums.UNIT_AUTO, lib.Node.prototype[fnName + 'Auto']), _methods); + + patch(lib.Node.prototype, fnName, function (original) { + for (var _len2 = arguments.length, args = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { + args[_key2 - 1] = arguments[_key2]; + } + + // We patch all these functions to add support for the following calls: + // .setWidth(100) / .setWidth("100%") / .setWidth(.getWidth()) / .setWidth("auto") + + var value = args.pop(); + var unit = void 0, + asNumber = void 0; + + if (value === 'auto') { + unit = YGEnums.UNIT_AUTO; + asNumber = undefined; + } else if (value instanceof Value) { + unit = value.unit; + asNumber = value.valueOf(); + } else { + unit = typeof value === 'string' && value.endsWith('%') ? YGEnums.UNIT_PERCENT : YGEnums.UNIT_POINT; + asNumber = parseFloat(value); + if (!Number.isNaN(value) && Number.isNaN(asNumber)) { + throw new Error('Invalid value ' + value + ' for ' + fnName); + } + } + + if (!methods[unit]) throw new Error('Failed to execute "' + fnName + '": Unsupported unit \'' + value + '\''); + + if (asNumber !== undefined) { + var _methods$unit; + + return (_methods$unit = methods[unit]).call.apply(_methods$unit, [this].concat(args, [asNumber])); + } else { + var _methods$unit2; + + return (_methods$unit2 = methods[unit]).call.apply(_methods$unit2, [this].concat(args)); + } + }); + }; + + for (var _i = 0; _i < _arr.length; _i++) { + _loop(); + } + + patch(lib.Config.prototype, 'free', function () { + // Since we handle the memory allocation ourselves (via lib.Config.create), + // we also need to handle the deallocation + lib.Config.destroy(this); + }); + + patch(lib.Node, 'create', function (_, config) { + // We decide the constructor we want to call depending on the parameters + return config ? lib.Node.createWithConfig(config) : lib.Node.createDefault(); + }); + + patch(lib.Node.prototype, 'free', function () { + // Since we handle the memory allocation ourselves (via lib.Node.create), + // we also need to handle the deallocation + lib.Node.destroy(this); + }); + + patch(lib.Node.prototype, 'freeRecursive', function () { + for (var t = 0, T = this.getChildCount(); t < T; ++t) { + this.getChild(0).freeRecursive(); + } + this.free(); + }); + + patch(lib.Node.prototype, 'setMeasureFunc', function (original, measureFunc) { + // This patch is just a convenience patch, since it helps write more + // idiomatic source code (such as .setMeasureFunc(null)) + // We also automatically convert the return value of the measureFunc + // to a Size object, so that we can return anything that has .width and + // .height properties + if (measureFunc) { + return original.call(this, function () { + return Size.fromJS(measureFunc.apply(undefined, arguments)); + }); + } else { + return this.unsetMeasureFunc(); + } + }); + + patch(lib.Node.prototype, 'calculateLayout', function (original) { + var width = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : NaN; + var height = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : NaN; + var direction = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : YGEnums.DIRECTION_LTR; + + // Just a small patch to add support for the function default parameters + return original.call(this, width, height, direction); + }); + + return _extends({ + Config: lib.Config, + Node: lib.Node, + Layout: bind('Layout', Layout), + Size: bind('Size', Size), + Value: bind('Value', Value), + getInstanceCount: function getInstanceCount() { + return lib.getInstanceCount.apply(lib, arguments); + } + }, YGEnums); +}; + +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 nbind = createCommonjsModule(function (module) { +(function (root, wrapper) { + if (typeof undefined == "function" && undefined.amd) undefined([], function () { + return wrapper; + });else if ('object' == "object" && module.exports) module.exports = wrapper;else (root.nbind = root.nbind || {}).init = wrapper; +})(commonjsGlobal, function (Module, cb) { + if (typeof Module == "function") { + cb = Module;Module = {}; + }Module.onRuntimeInitialized = function (init, cb) { + return function () { + if (init) init.apply(this, arguments);try { + Module.ccall("nbind_init"); + } catch (err) { + cb(err);return; + }cb(null, { bind: Module._nbind_value, reflect: Module.NBind.reflect, queryType: Module.NBind.queryType, toggleLightGC: Module.toggleLightGC, lib: Module }); + }; + }(Module.onRuntimeInitialized, cb);var Module;if (!Module) Module = (typeof Module !== "undefined" ? Module : null) || {};var moduleOverrides = {};for (var key in Module) { + if (Module.hasOwnProperty(key)) { + moduleOverrides[key] = Module[key]; + } + }var ENVIRONMENT_IS_WEB = false;var ENVIRONMENT_IS_WORKER = false;var ENVIRONMENT_IS_NODE = false;var ENVIRONMENT_IS_SHELL = false;if (Module["ENVIRONMENT"]) { + if (Module["ENVIRONMENT"] === "WEB") { + ENVIRONMENT_IS_WEB = true; + } else if (Module["ENVIRONMENT"] === "WORKER") { + ENVIRONMENT_IS_WORKER = true; + } else if (Module["ENVIRONMENT"] === "NODE") { + ENVIRONMENT_IS_NODE = true; + } else if (Module["ENVIRONMENT"] === "SHELL") { + ENVIRONMENT_IS_SHELL = true; + } else { + throw new Error("The provided Module['ENVIRONMENT'] value is not valid. It must be one of: WEB|WORKER|NODE|SHELL."); + } + } else { + ENVIRONMENT_IS_WEB = typeof window === "object";ENVIRONMENT_IS_WORKER = typeof importScripts === "function";ENVIRONMENT_IS_NODE = typeof process === "object" && typeof commonjsRequire === "function" && !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_WORKER;ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_WORKER; + }if (ENVIRONMENT_IS_NODE) { + if (!Module["print"]) Module["print"] = console.log;if (!Module["printErr"]) Module["printErr"] = console.warn;var nodeFS;var nodePath;Module["read"] = function shell_read(filename, binary) { + if (!nodeFS) nodeFS = {}("");if (!nodePath) nodePath = {}("");filename = nodePath["normalize"](filename);var ret = nodeFS["readFileSync"](filename);return binary ? ret : ret.toString(); + };Module["readBinary"] = function readBinary(filename) { + var ret = Module["read"](filename, true);if (!ret.buffer) { + ret = new Uint8Array(ret); + }assert(ret.buffer);return ret; + };Module["load"] = function load(f) { + globalEval(read(f)); + };if (!Module["thisProgram"]) { + if (process["argv"].length > 1) { + Module["thisProgram"] = process["argv"][1].replace(/\\/g, "/"); + } else { + Module["thisProgram"] = "unknown-program"; + } + }Module["arguments"] = process["argv"].slice(2);{ + module["exports"] = Module; + }process["on"]("uncaughtException", function (ex) { + if (!(ex instanceof ExitStatus)) { + throw ex; + } + });Module["inspect"] = function () { + return "[Emscripten Module object]"; + }; + } else if (ENVIRONMENT_IS_SHELL) { + if (!Module["print"]) Module["print"] = print;if (typeof printErr != "undefined") Module["printErr"] = printErr;if (typeof read != "undefined") { + Module["read"] = read; + } else { + Module["read"] = function shell_read() { + throw "no read() available"; + }; + }Module["readBinary"] = function readBinary(f) { + if (typeof readbuffer === "function") { + return new Uint8Array(readbuffer(f)); + }var data = read(f, "binary");assert(typeof data === "object");return data; + };if (typeof scriptArgs != "undefined") { + Module["arguments"] = scriptArgs; + } else if (typeof arguments != "undefined") { + Module["arguments"] = arguments; + }if (typeof quit === "function") { + Module["quit"] = function (status, toThrow) { + quit(status); + }; + } + } else if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { + Module["read"] = function shell_read(url) { + var xhr = new XMLHttpRequest();xhr.open("GET", url, false);xhr.send(null);return xhr.responseText; + };if (ENVIRONMENT_IS_WORKER) { + Module["readBinary"] = function readBinary(url) { + var xhr = new XMLHttpRequest();xhr.open("GET", url, false);xhr.responseType = "arraybuffer";xhr.send(null);return new Uint8Array(xhr.response); + }; + }Module["readAsync"] = function readAsync(url, onload, onerror) { + var xhr = new XMLHttpRequest();xhr.open("GET", url, true);xhr.responseType = "arraybuffer";xhr.onload = function xhr_onload() { + if (xhr.status == 200 || xhr.status == 0 && xhr.response) { + onload(xhr.response); + } else { + onerror(); + } + };xhr.onerror = onerror;xhr.send(null); + };if (typeof arguments != "undefined") { + Module["arguments"] = arguments; + }if (typeof console !== "undefined") { + if (!Module["print"]) Module["print"] = function shell_print(x) { + console.log(x); + };if (!Module["printErr"]) Module["printErr"] = function shell_printErr(x) { + console.warn(x); + }; + } else { + var TRY_USE_DUMP = false;if (!Module["print"]) Module["print"] = TRY_USE_DUMP && typeof dump !== "undefined" ? function (x) { + dump(x); + } : function (x) {}; + }if (ENVIRONMENT_IS_WORKER) { + Module["load"] = importScripts; + }if (typeof Module["setWindowTitle"] === "undefined") { + Module["setWindowTitle"] = function (title) { + document.title = title; + }; + } + } else { + throw "Unknown runtime environment. Where are we?"; + }function globalEval(x) { + eval.call(null, x); + }if (!Module["load"] && Module["read"]) { + Module["load"] = function load(f) { + globalEval(Module["read"](f)); + }; + }if (!Module["print"]) { + Module["print"] = function () {}; + }if (!Module["printErr"]) { + Module["printErr"] = Module["print"]; + }if (!Module["arguments"]) { + Module["arguments"] = []; + }if (!Module["thisProgram"]) { + Module["thisProgram"] = "./this.program"; + }if (!Module["quit"]) { + Module["quit"] = function (status, toThrow) { + throw toThrow; + }; + }Module.print = Module["print"];Module.printErr = Module["printErr"];Module["preRun"] = [];Module["postRun"] = [];for (var key in moduleOverrides) { + if (moduleOverrides.hasOwnProperty(key)) { + Module[key] = moduleOverrides[key]; + } + }moduleOverrides = undefined;var Runtime = { setTempRet0: function (value) { + tempRet0 = value;return value; + }, getTempRet0: function () { + return tempRet0; + }, stackSave: function () { + return STACKTOP; + }, stackRestore: function (stackTop) { + STACKTOP = stackTop; + }, getNativeTypeSize: function (type) { + switch (type) {case "i1":case "i8": + return 1;case "i16": + return 2;case "i32": + return 4;case "i64": + return 8;case "float": + return 4;case "double": + return 8;default: + { + if (type[type.length - 1] === "*") { + return Runtime.QUANTUM_SIZE; + } else if (type[0] === "i") { + var bits = parseInt(type.substr(1));assert(bits % 8 === 0);return bits / 8; + } else { + return 0; + } + }} + }, getNativeFieldSize: function (type) { + return Math.max(Runtime.getNativeTypeSize(type), Runtime.QUANTUM_SIZE); + }, STACK_ALIGN: 16, prepVararg: function (ptr, type) { + if (type === "double" || type === "i64") { + if (ptr & 7) { + assert((ptr & 7) === 4);ptr += 4; + } + } else { + assert((ptr & 3) === 0); + }return ptr; + }, getAlignSize: function (type, size, vararg) { + if (!vararg && (type == "i64" || type == "double")) return 8;if (!type) return Math.min(size, 8);return Math.min(size || (type ? Runtime.getNativeFieldSize(type) : 0), Runtime.QUANTUM_SIZE); + }, dynCall: function (sig, ptr, args) { + if (args && args.length) { + return Module["dynCall_" + sig].apply(null, [ptr].concat(args)); + } else { + return Module["dynCall_" + sig].call(null, ptr); + } + }, functionPointers: [], addFunction: function (func) { + for (var i = 0; i < Runtime.functionPointers.length; i++) { + if (!Runtime.functionPointers[i]) { + Runtime.functionPointers[i] = func;return 2 * (1 + i); + } + }throw "Finished up all reserved function pointers. Use a higher value for RESERVED_FUNCTION_POINTERS."; + }, removeFunction: function (index) { + Runtime.functionPointers[(index - 2) / 2] = null; + }, warnOnce: function (text) { + if (!Runtime.warnOnce.shown) Runtime.warnOnce.shown = {};if (!Runtime.warnOnce.shown[text]) { + Runtime.warnOnce.shown[text] = 1;Module.printErr(text); + } + }, funcWrappers: {}, getFuncWrapper: function (func, sig) { + if (!func) return;assert(sig);if (!Runtime.funcWrappers[sig]) { + Runtime.funcWrappers[sig] = {}; + }var sigCache = Runtime.funcWrappers[sig];if (!sigCache[func]) { + if (sig.length === 1) { + sigCache[func] = function dynCall_wrapper() { + return Runtime.dynCall(sig, func); + }; + } else if (sig.length === 2) { + sigCache[func] = function dynCall_wrapper(arg) { + return Runtime.dynCall(sig, func, [arg]); + }; + } else { + sigCache[func] = function dynCall_wrapper() { + return Runtime.dynCall(sig, func, Array.prototype.slice.call(arguments)); + }; + } + }return sigCache[func]; + }, getCompilerSetting: function (name) { + throw "You must build with -s RETAIN_COMPILER_SETTINGS=1 for Runtime.getCompilerSetting or emscripten_get_compiler_setting to work"; + }, stackAlloc: function (size) { + var ret = STACKTOP;STACKTOP = STACKTOP + size | 0;STACKTOP = STACKTOP + 15 & -16;return ret; + }, staticAlloc: function (size) { + var ret = STATICTOP;STATICTOP = STATICTOP + size | 0;STATICTOP = STATICTOP + 15 & -16;return ret; + }, dynamicAlloc: function (size) { + var ret = HEAP32[DYNAMICTOP_PTR >> 2];var end = (ret + size + 15 | 0) & -16;HEAP32[DYNAMICTOP_PTR >> 2] = end;if (end >= TOTAL_MEMORY) { + var success = enlargeMemory();if (!success) { + HEAP32[DYNAMICTOP_PTR >> 2] = ret;return 0; + } + }return ret; + }, alignMemory: function (size, quantum) { + var ret = size = Math.ceil(size / (quantum ? quantum : 16)) * (quantum ? quantum : 16);return ret; + }, makeBigInt: function (low, high, unsigned) { + var ret = unsigned ? +(low >>> 0) + +(high >>> 0) * +4294967296 : +(low >>> 0) + +(high | 0) * +4294967296;return ret; + }, GLOBAL_BASE: 8, QUANTUM_SIZE: 4, __dummy__: 0 };Module["Runtime"] = Runtime;var ABORT = 0;function assert(condition, text) { + if (!condition) { + abort("Assertion failed: " + text); + } + }function getCFunc(ident) { + var func = Module["_" + ident];if (!func) { + try { + func = eval("_" + ident); + } catch (e) {} + }assert(func, "Cannot call unknown function " + ident + " (perhaps LLVM optimizations or closure removed it?)");return func; + }var cwrap, ccall;(function () { + var JSfuncs = { "stackSave": function () { + Runtime.stackSave(); + }, "stackRestore": function () { + Runtime.stackRestore(); + }, "arrayToC": function (arr) { + var ret = Runtime.stackAlloc(arr.length);writeArrayToMemory(arr, ret);return ret; + }, "stringToC": function (str) { + var ret = 0;if (str !== null && str !== undefined && str !== 0) { + var len = (str.length << 2) + 1;ret = Runtime.stackAlloc(len);stringToUTF8(str, ret, len); + }return ret; + } };var toC = { "string": JSfuncs["stringToC"], "array": JSfuncs["arrayToC"] };ccall = function ccallFunc(ident, returnType, argTypes, args, opts) { + var func = getCFunc(ident);var cArgs = [];var stack = 0;if (args) { + for (var i = 0; i < args.length; i++) { + var converter = toC[argTypes[i]];if (converter) { + if (stack === 0) stack = Runtime.stackSave();cArgs[i] = converter(args[i]); + } else { + cArgs[i] = args[i]; + } + } + }var ret = func.apply(null, cArgs);if (returnType === "string") ret = Pointer_stringify(ret);if (stack !== 0) { + if (opts && opts.async) { + EmterpreterAsync.asyncFinalizers.push(function () { + Runtime.stackRestore(stack); + });return; + }Runtime.stackRestore(stack); + }return ret; + };var sourceRegex = /^function\s*[a-zA-Z$_0-9]*\s*\(([^)]*)\)\s*{\s*([^*]*?)[\s;]*(?:return\s*(.*?)[;\s]*)?}$/;function parseJSFunc(jsfunc) { + var parsed = jsfunc.toString().match(sourceRegex).slice(1);return { arguments: parsed[0], body: parsed[1], returnValue: parsed[2] }; + }var JSsource = null;function ensureJSsource() { + if (!JSsource) { + JSsource = {};for (var fun in JSfuncs) { + if (JSfuncs.hasOwnProperty(fun)) { + JSsource[fun] = parseJSFunc(JSfuncs[fun]); + } + } + } + }cwrap = function cwrap(ident, returnType, argTypes) { + argTypes = argTypes || [];var cfunc = getCFunc(ident);var numericArgs = argTypes.every(function (type) { + return type === "number"; + });var numericRet = returnType !== "string";if (numericRet && numericArgs) { + return cfunc; + }var argNames = argTypes.map(function (x, i) { + return "$" + i; + });var funcstr = "(function(" + argNames.join(",") + ") {";var nargs = argTypes.length;if (!numericArgs) { + ensureJSsource();funcstr += "var stack = " + JSsource["stackSave"].body + ";";for (var i = 0; i < nargs; i++) { + var arg = argNames[i], + type = argTypes[i];if (type === "number") continue;var convertCode = JSsource[type + "ToC"];funcstr += "var " + convertCode.arguments + " = " + arg + ";";funcstr += convertCode.body + ";";funcstr += arg + "=(" + convertCode.returnValue + ");"; + } + }var cfuncname = parseJSFunc(function () { + return cfunc; + }).returnValue;funcstr += "var ret = " + cfuncname + "(" + argNames.join(",") + ");";if (!numericRet) { + var strgfy = parseJSFunc(function () { + return Pointer_stringify; + }).returnValue;funcstr += "ret = " + strgfy + "(ret);"; + }if (!numericArgs) { + ensureJSsource();funcstr += JSsource["stackRestore"].body.replace("()", "(stack)") + ";"; + }funcstr += "return ret})";return eval(funcstr); + }; + })();Module["ccall"] = ccall;Module["cwrap"] = cwrap;function setValue(ptr, value, type, noSafe) { + type = type || "i8";if (type.charAt(type.length - 1) === "*") type = "i32";switch (type) {case "i1": + HEAP8[ptr >> 0] = value;break;case "i8": + HEAP8[ptr >> 0] = value;break;case "i16": + HEAP16[ptr >> 1] = value;break;case "i32": + HEAP32[ptr >> 2] = value;break;case "i64": + tempI64 = [value >>> 0, (tempDouble = value, +Math_abs(tempDouble) >= +1 ? tempDouble > +0 ? (Math_min(+Math_floor(tempDouble / +4294967296), +4294967295) | 0) >>> 0 : ~~+Math_ceil((tempDouble - +(~~tempDouble >>> 0)) / +4294967296) >>> 0 : 0)], HEAP32[ptr >> 2] = tempI64[0], HEAP32[ptr + 4 >> 2] = tempI64[1];break;case "float": + HEAPF32[ptr >> 2] = value;break;case "double": + HEAPF64[ptr >> 3] = value;break;default: + abort("invalid type for setValue: " + type);} + }Module["setValue"] = setValue;function getValue(ptr, type, noSafe) { + type = type || "i8";if (type.charAt(type.length - 1) === "*") type = "i32";switch (type) {case "i1": + return HEAP8[ptr >> 0];case "i8": + return HEAP8[ptr >> 0];case "i16": + return HEAP16[ptr >> 1];case "i32": + return HEAP32[ptr >> 2];case "i64": + return HEAP32[ptr >> 2];case "float": + return HEAPF32[ptr >> 2];case "double": + return HEAPF64[ptr >> 3];default: + abort("invalid type for setValue: " + type);}return null; + }Module["getValue"] = getValue;var ALLOC_NORMAL = 0;var ALLOC_STACK = 1;var ALLOC_STATIC = 2;var ALLOC_DYNAMIC = 3;var ALLOC_NONE = 4;Module["ALLOC_NORMAL"] = ALLOC_NORMAL;Module["ALLOC_STACK"] = ALLOC_STACK;Module["ALLOC_STATIC"] = ALLOC_STATIC;Module["ALLOC_DYNAMIC"] = ALLOC_DYNAMIC;Module["ALLOC_NONE"] = ALLOC_NONE;function allocate(slab, types, allocator, ptr) { + var zeroinit, size;if (typeof slab === "number") { + zeroinit = true;size = slab; + } else { + zeroinit = false;size = slab.length; + }var singleType = typeof types === "string" ? types : null;var ret;if (allocator == ALLOC_NONE) { + ret = ptr; + } else { + ret = [typeof _malloc === "function" ? _malloc : Runtime.staticAlloc, Runtime.stackAlloc, Runtime.staticAlloc, Runtime.dynamicAlloc][allocator === undefined ? ALLOC_STATIC : allocator](Math.max(size, singleType ? 1 : types.length)); + }if (zeroinit) { + var ptr = ret, + stop;assert((ret & 3) == 0);stop = ret + (size & ~3);for (; ptr < stop; ptr += 4) { + HEAP32[ptr >> 2] = 0; + }stop = ret + size;while (ptr < stop) { + HEAP8[ptr++ >> 0] = 0; + }return ret; + }if (singleType === "i8") { + if (slab.subarray || slab.slice) { + HEAPU8.set(slab, ret); + } else { + HEAPU8.set(new Uint8Array(slab), ret); + }return ret; + }var i = 0, + type, + typeSize, + previousType;while (i < size) { + var curr = slab[i];if (typeof curr === "function") { + curr = Runtime.getFunctionIndex(curr); + }type = singleType || types[i];if (type === 0) { + i++;continue; + }if (type == "i64") type = "i32";setValue(ret + i, curr, type);if (previousType !== type) { + typeSize = Runtime.getNativeTypeSize(type);previousType = type; + }i += typeSize; + }return ret; + }Module["allocate"] = allocate;function getMemory(size) { + if (!staticSealed) return Runtime.staticAlloc(size);if (!runtimeInitialized) return Runtime.dynamicAlloc(size);return _malloc(size); + }Module["getMemory"] = getMemory;function Pointer_stringify(ptr, length) { + if (length === 0 || !ptr) return "";var hasUtf = 0;var t;var i = 0;while (1) { + t = HEAPU8[ptr + i >> 0];hasUtf |= t;if (t == 0 && !length) break;i++;if (length && i == length) break; + }if (!length) length = i;var ret = "";if (hasUtf < 128) { + var MAX_CHUNK = 1024;var curr;while (length > 0) { + curr = String.fromCharCode.apply(String, HEAPU8.subarray(ptr, ptr + Math.min(length, MAX_CHUNK)));ret = ret ? ret + curr : curr;ptr += MAX_CHUNK;length -= MAX_CHUNK; + }return ret; + }return Module["UTF8ToString"](ptr); + }Module["Pointer_stringify"] = Pointer_stringify;function AsciiToString(ptr) { + var str = "";while (1) { + var ch = HEAP8[ptr++ >> 0];if (!ch) return str;str += String.fromCharCode(ch); + } + }Module["AsciiToString"] = AsciiToString;function stringToAscii(str, outPtr) { + return writeAsciiToMemory(str, outPtr, false); + }Module["stringToAscii"] = stringToAscii;var UTF8Decoder = typeof TextDecoder !== "undefined" ? new TextDecoder("utf8") : undefined;function UTF8ArrayToString(u8Array, idx) { + var endPtr = idx;while (u8Array[endPtr]) ++endPtr;if (endPtr - idx > 16 && u8Array.subarray && UTF8Decoder) { + return UTF8Decoder.decode(u8Array.subarray(idx, endPtr)); + } else { + var u0, u1, u2, u3, u4, u5;var str = "";while (1) { + u0 = u8Array[idx++];if (!u0) return str;if (!(u0 & 128)) { + str += String.fromCharCode(u0);continue; + }u1 = u8Array[idx++] & 63;if ((u0 & 224) == 192) { + str += String.fromCharCode((u0 & 31) << 6 | u1);continue; + }u2 = u8Array[idx++] & 63;if ((u0 & 240) == 224) { + u0 = (u0 & 15) << 12 | u1 << 6 | u2; + } else { + u3 = u8Array[idx++] & 63;if ((u0 & 248) == 240) { + u0 = (u0 & 7) << 18 | u1 << 12 | u2 << 6 | u3; + } else { + u4 = u8Array[idx++] & 63;if ((u0 & 252) == 248) { + u0 = (u0 & 3) << 24 | u1 << 18 | u2 << 12 | u3 << 6 | u4; + } else { + u5 = u8Array[idx++] & 63;u0 = (u0 & 1) << 30 | u1 << 24 | u2 << 18 | u3 << 12 | u4 << 6 | u5; + } + } + }if (u0 < 65536) { + str += String.fromCharCode(u0); + } else { + var ch = u0 - 65536;str += String.fromCharCode(55296 | ch >> 10, 56320 | ch & 1023); + } + } + } + }Module["UTF8ArrayToString"] = UTF8ArrayToString;function UTF8ToString(ptr) { + return UTF8ArrayToString(HEAPU8, ptr); + }Module["UTF8ToString"] = UTF8ToString;function stringToUTF8Array(str, outU8Array, outIdx, maxBytesToWrite) { + if (!(maxBytesToWrite > 0)) return 0;var startIdx = outIdx;var endIdx = outIdx + maxBytesToWrite - 1;for (var i = 0; i < str.length; ++i) { + var u = str.charCodeAt(i);if (u >= 55296 && u <= 57343) u = 65536 + ((u & 1023) << 10) | str.charCodeAt(++i) & 1023;if (u <= 127) { + if (outIdx >= endIdx) break;outU8Array[outIdx++] = u; + } else if (u <= 2047) { + if (outIdx + 1 >= endIdx) break;outU8Array[outIdx++] = 192 | u >> 6;outU8Array[outIdx++] = 128 | u & 63; + } else if (u <= 65535) { + if (outIdx + 2 >= endIdx) break;outU8Array[outIdx++] = 224 | u >> 12;outU8Array[outIdx++] = 128 | u >> 6 & 63;outU8Array[outIdx++] = 128 | u & 63; + } else if (u <= 2097151) { + if (outIdx + 3 >= endIdx) break;outU8Array[outIdx++] = 240 | u >> 18;outU8Array[outIdx++] = 128 | u >> 12 & 63;outU8Array[outIdx++] = 128 | u >> 6 & 63;outU8Array[outIdx++] = 128 | u & 63; + } else if (u <= 67108863) { + if (outIdx + 4 >= endIdx) break;outU8Array[outIdx++] = 248 | u >> 24;outU8Array[outIdx++] = 128 | u >> 18 & 63;outU8Array[outIdx++] = 128 | u >> 12 & 63;outU8Array[outIdx++] = 128 | u >> 6 & 63;outU8Array[outIdx++] = 128 | u & 63; + } else { + if (outIdx + 5 >= endIdx) break;outU8Array[outIdx++] = 252 | u >> 30;outU8Array[outIdx++] = 128 | u >> 24 & 63;outU8Array[outIdx++] = 128 | u >> 18 & 63;outU8Array[outIdx++] = 128 | u >> 12 & 63;outU8Array[outIdx++] = 128 | u >> 6 & 63;outU8Array[outIdx++] = 128 | u & 63; + } + }outU8Array[outIdx] = 0;return outIdx - startIdx; + }Module["stringToUTF8Array"] = stringToUTF8Array;function stringToUTF8(str, outPtr, maxBytesToWrite) { + return stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite); + }Module["stringToUTF8"] = stringToUTF8;function lengthBytesUTF8(str) { + var len = 0;for (var i = 0; i < str.length; ++i) { + var u = str.charCodeAt(i);if (u >= 55296 && u <= 57343) u = 65536 + ((u & 1023) << 10) | str.charCodeAt(++i) & 1023;if (u <= 127) { + ++len; + } else if (u <= 2047) { + len += 2; + } else if (u <= 65535) { + len += 3; + } else if (u <= 2097151) { + len += 4; + } else if (u <= 67108863) { + len += 5; + } else { + len += 6; + } + }return len; + }Module["lengthBytesUTF8"] = lengthBytesUTF8;var UTF16Decoder = typeof TextDecoder !== "undefined" ? new TextDecoder("utf-16le") : undefined;function demangle(func) { + var __cxa_demangle_func = Module["___cxa_demangle"] || Module["__cxa_demangle"];if (__cxa_demangle_func) { + try { + var s = func.substr(1);var len = lengthBytesUTF8(s) + 1;var buf = _malloc(len);stringToUTF8(s, buf, len);var status = _malloc(4);var ret = __cxa_demangle_func(buf, 0, 0, status);if (getValue(status, "i32") === 0 && ret) { + return Pointer_stringify(ret); + } + } catch (e) {} finally { + if (buf) _free(buf);if (status) _free(status);if (ret) _free(ret); + }return func; + }Runtime.warnOnce("warning: build with -s DEMANGLE_SUPPORT=1 to link in libcxxabi demangling");return func; + }function demangleAll(text) { + var regex = /__Z[\w\d_]+/g;return text.replace(regex, function (x) { + var y = demangle(x);return x === y ? x : x + " [" + y + "]"; + }); + }function jsStackTrace() { + var err = new Error();if (!err.stack) { + try { + throw new Error(0); + } catch (e) { + err = e; + }if (!err.stack) { + return "(no stack trace available)"; + } + }return err.stack.toString(); + }function stackTrace() { + var js = jsStackTrace();if (Module["extraStackTrace"]) js += "\n" + Module["extraStackTrace"]();return demangleAll(js); + }Module["stackTrace"] = stackTrace;var HEAP, buffer, HEAP8, HEAPU8, HEAP16, HEAPU16, HEAP32, HEAPU32, HEAPF32, HEAPF64;function updateGlobalBufferViews() { + Module["HEAP8"] = HEAP8 = new Int8Array(buffer);Module["HEAP16"] = HEAP16 = new Int16Array(buffer);Module["HEAP32"] = HEAP32 = new Int32Array(buffer);Module["HEAPU8"] = HEAPU8 = new Uint8Array(buffer);Module["HEAPU16"] = HEAPU16 = new Uint16Array(buffer);Module["HEAPU32"] = HEAPU32 = new Uint32Array(buffer);Module["HEAPF32"] = HEAPF32 = new Float32Array(buffer);Module["HEAPF64"] = HEAPF64 = new Float64Array(buffer); + }var STATIC_BASE, STATICTOP, staticSealed;var STACK_BASE, STACKTOP, STACK_MAX;var DYNAMIC_BASE, DYNAMICTOP_PTR;STATIC_BASE = STATICTOP = STACK_BASE = STACKTOP = STACK_MAX = DYNAMIC_BASE = DYNAMICTOP_PTR = 0;staticSealed = false;function abortOnCannotGrowMemory() { + abort("Cannot enlarge memory arrays. Either (1) compile with -s TOTAL_MEMORY=X with X higher than the current value " + TOTAL_MEMORY + ", (2) compile with -s ALLOW_MEMORY_GROWTH=1 which allows increasing the size at runtime but prevents some optimizations, (3) set Module.TOTAL_MEMORY to a higher value before the program runs, or (4) if you want malloc to return NULL (0) instead of this abort, compile with -s ABORTING_MALLOC=0 "); + }function enlargeMemory() { + abortOnCannotGrowMemory(); + }var TOTAL_STACK = Module["TOTAL_STACK"] || 5242880;var TOTAL_MEMORY = Module["TOTAL_MEMORY"] || 134217728;if (TOTAL_MEMORY < TOTAL_STACK) Module.printErr("TOTAL_MEMORY should be larger than TOTAL_STACK, was " + TOTAL_MEMORY + "! (TOTAL_STACK=" + TOTAL_STACK + ")");if (Module["buffer"]) { + buffer = Module["buffer"]; + } else { + { + buffer = new ArrayBuffer(TOTAL_MEMORY); + } + }updateGlobalBufferViews();function getTotalMemory() { + return TOTAL_MEMORY; + }HEAP32[0] = 1668509029;HEAP16[1] = 25459;if (HEAPU8[2] !== 115 || HEAPU8[3] !== 99) throw "Runtime error: expected the system to be little-endian!";Module["HEAP"] = HEAP;Module["buffer"] = buffer;Module["HEAP8"] = HEAP8;Module["HEAP16"] = HEAP16;Module["HEAP32"] = HEAP32;Module["HEAPU8"] = HEAPU8;Module["HEAPU16"] = HEAPU16;Module["HEAPU32"] = HEAPU32;Module["HEAPF32"] = HEAPF32;Module["HEAPF64"] = HEAPF64;function callRuntimeCallbacks(callbacks) { + while (callbacks.length > 0) { + var callback = callbacks.shift();if (typeof callback == "function") { + callback();continue; + }var func = callback.func;if (typeof func === "number") { + if (callback.arg === undefined) { + Module["dynCall_v"](func); + } else { + Module["dynCall_vi"](func, callback.arg); + } + } else { + func(callback.arg === undefined ? null : callback.arg); + } + } + }var __ATPRERUN__ = [];var __ATINIT__ = [];var __ATMAIN__ = [];var __ATEXIT__ = [];var __ATPOSTRUN__ = [];var runtimeInitialized = false;function preRun() { + if (Module["preRun"]) { + if (typeof Module["preRun"] == "function") Module["preRun"] = [Module["preRun"]];while (Module["preRun"].length) { + addOnPreRun(Module["preRun"].shift()); + } + }callRuntimeCallbacks(__ATPRERUN__); + }function ensureInitRuntime() { + if (runtimeInitialized) return;runtimeInitialized = true;callRuntimeCallbacks(__ATINIT__); + }function preMain() { + callRuntimeCallbacks(__ATMAIN__); + }function exitRuntime() { + callRuntimeCallbacks(__ATEXIT__); }function postRun() { + if (Module["postRun"]) { + if (typeof Module["postRun"] == "function") Module["postRun"] = [Module["postRun"]];while (Module["postRun"].length) { + addOnPostRun(Module["postRun"].shift()); + } + }callRuntimeCallbacks(__ATPOSTRUN__); + }function addOnPreRun(cb) { + __ATPRERUN__.unshift(cb); + }Module["addOnPreRun"] = addOnPreRun;function addOnInit(cb) { + __ATINIT__.unshift(cb); + }Module["addOnInit"] = addOnInit;function addOnPreMain(cb) { + __ATMAIN__.unshift(cb); + }Module["addOnPreMain"] = addOnPreMain;function addOnExit(cb) { + __ATEXIT__.unshift(cb); + }Module["addOnExit"] = addOnExit;function addOnPostRun(cb) { + __ATPOSTRUN__.unshift(cb); + }Module["addOnPostRun"] = addOnPostRun;function intArrayFromString(stringy, dontAddNull, length) { + var len = length > 0 ? length : lengthBytesUTF8(stringy) + 1;var u8array = new Array(len);var numBytesWritten = stringToUTF8Array(stringy, u8array, 0, u8array.length);if (dontAddNull) u8array.length = numBytesWritten;return u8array; + }Module["intArrayFromString"] = intArrayFromString;function intArrayToString(array) { + var ret = [];for (var i = 0; i < array.length; i++) { + var chr = array[i];if (chr > 255) { + chr &= 255; + }ret.push(String.fromCharCode(chr)); + }return ret.join(""); + }Module["intArrayToString"] = intArrayToString;function writeStringToMemory(string, buffer, dontAddNull) { + Runtime.warnOnce("writeStringToMemory is deprecated and should not be called! Use stringToUTF8() instead!");var lastChar, end;if (dontAddNull) { + end = buffer + lengthBytesUTF8(string);lastChar = HEAP8[end]; + }stringToUTF8(string, buffer, Infinity);if (dontAddNull) HEAP8[end] = lastChar; + }Module["writeStringToMemory"] = writeStringToMemory;function writeArrayToMemory(array, buffer) { + HEAP8.set(array, buffer); + }Module["writeArrayToMemory"] = writeArrayToMemory;function writeAsciiToMemory(str, buffer, dontAddNull) { + for (var i = 0; i < str.length; ++i) { + HEAP8[buffer++ >> 0] = str.charCodeAt(i); + }if (!dontAddNull) HEAP8[buffer >> 0] = 0; + }Module["writeAsciiToMemory"] = writeAsciiToMemory;if (!Math["imul"] || Math["imul"](4294967295, 5) !== -5) Math["imul"] = function imul(a, b) { + var ah = a >>> 16;var al = a & 65535;var bh = b >>> 16;var bl = b & 65535;return al * bl + (ah * bl + al * bh << 16) | 0; + };Math.imul = Math["imul"];if (!Math["fround"]) { + var froundBuffer = new Float32Array(1);Math["fround"] = function (x) { + froundBuffer[0] = x;return froundBuffer[0]; + }; + }Math.fround = Math["fround"];if (!Math["clz32"]) Math["clz32"] = function (x) { + x = x >>> 0;for (var i = 0; i < 32; i++) { + if (x & 1 << 31 - i) return i; + }return 32; + };Math.clz32 = Math["clz32"];if (!Math["trunc"]) Math["trunc"] = function (x) { + return x < 0 ? Math.ceil(x) : Math.floor(x); + };Math.trunc = Math["trunc"];var Math_abs = Math.abs;var Math_ceil = Math.ceil;var Math_floor = Math.floor;var Math_min = Math.min;var runDependencies = 0;var runDependencyWatcher = null;var dependenciesFulfilled = null;function getUniqueRunDependency(id) { + return id; + }function addRunDependency(id) { + runDependencies++;if (Module["monitorRunDependencies"]) { + Module["monitorRunDependencies"](runDependencies); + } + }Module["addRunDependency"] = addRunDependency;function removeRunDependency(id) { + runDependencies--;if (Module["monitorRunDependencies"]) { + Module["monitorRunDependencies"](runDependencies); + }if (runDependencies == 0) { + if (runDependencyWatcher !== null) { + clearInterval(runDependencyWatcher);runDependencyWatcher = null; + }if (dependenciesFulfilled) { + var callback = dependenciesFulfilled;dependenciesFulfilled = null;callback(); + } + } + }Module["removeRunDependency"] = removeRunDependency;Module["preloadedImages"] = {};Module["preloadedAudios"] = {};var ASM_CONSTS = [function ($0, $1, $2, $3, $4, $5, $6, $7) { + return _nbind.callbackSignatureList[$0].apply(this, arguments); + }];function _emscripten_asm_const_iiiiiiii(code, a0, a1, a2, a3, a4, a5, a6) { + return ASM_CONSTS[code](a0, a1, a2, a3, a4, a5, a6); + }function _emscripten_asm_const_iiiii(code, a0, a1, a2, a3) { + return ASM_CONSTS[code](a0, a1, a2, a3); + }function _emscripten_asm_const_iiidddddd(code, a0, a1, a2, a3, a4, a5, a6, a7) { + return ASM_CONSTS[code](a0, a1, a2, a3, a4, a5, a6, a7); + }function _emscripten_asm_const_iiididi(code, a0, a1, a2, a3, a4, a5) { + return ASM_CONSTS[code](a0, a1, a2, a3, a4, a5); + }function _emscripten_asm_const_iiii(code, a0, a1, a2) { + return ASM_CONSTS[code](a0, a1, a2); + }function _emscripten_asm_const_iiiid(code, a0, a1, a2, a3) { + return ASM_CONSTS[code](a0, a1, a2, a3); + }function _emscripten_asm_const_iiiiii(code, a0, a1, a2, a3, a4) { + return ASM_CONSTS[code](a0, a1, a2, a3, a4); + }STATIC_BASE = Runtime.GLOBAL_BASE;STATICTOP = STATIC_BASE + 12800;__ATINIT__.push({ func: function () { + __GLOBAL__sub_I_Yoga_cpp(); + } }, { func: function () { + __GLOBAL__sub_I_nbind_cc(); + } }, { func: function () { + __GLOBAL__sub_I_common_cc(); + } }, { func: function () { + __GLOBAL__sub_I_Binding_cc(); + } });allocate([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 127, 0, 0, 192, 127, 0, 0, 192, 127, 0, 0, 192, 127, 3, 0, 0, 0, 0, 0, 192, 127, 0, 0, 0, 0, 0, 0, 192, 127, 0, 0, 0, 0, 0, 0, 192, 127, 0, 0, 0, 0, 0, 0, 192, 127, 0, 0, 0, 0, 0, 0, 192, 127, 0, 0, 0, 0, 0, 0, 192, 127, 0, 0, 0, 0, 0, 0, 192, 127, 0, 0, 0, 0, 0, 0, 192, 127, 0, 0, 0, 0, 0, 0, 192, 127, 0, 0, 0, 0, 0, 0, 192, 127, 0, 0, 0, 0, 0, 0, 192, 127, 0, 0, 0, 0, 0, 0, 192, 127, 0, 0, 0, 0, 0, 0, 192, 127, 0, 0, 0, 0, 0, 0, 192, 127, 0, 0, 0, 0, 0, 0, 192, 127, 0, 0, 0, 0, 0, 0, 192, 127, 0, 0, 0, 0, 0, 0, 192, 127, 0, 0, 0, 0, 0, 0, 192, 127, 0, 0, 0, 0, 0, 0, 192, 127, 0, 0, 0, 0, 0, 0, 192, 127, 0, 0, 0, 0, 0, 0, 192, 127, 0, 0, 0, 0, 0, 0, 192, 127, 0, 0, 0, 0, 0, 0, 192, 127, 0, 0, 0, 0, 0, 0, 192, 127, 0, 0, 0, 0, 0, 0, 192, 127, 0, 0, 0, 0, 0, 0, 192, 127, 0, 0, 0, 0, 0, 0, 192, 127, 0, 0, 0, 0, 0, 0, 192, 127, 0, 0, 0, 0, 0, 0, 192, 127, 0, 0, 0, 0, 0, 0, 192, 127, 0, 0, 0, 0, 0, 0, 192, 127, 0, 0, 0, 0, 0, 0, 192, 127, 0, 0, 0, 0, 0, 0, 192, 127, 0, 0, 0, 0, 0, 0, 192, 127, 0, 0, 0, 0, 0, 0, 192, 127, 0, 0, 0, 0, 0, 0, 192, 127, 0, 0, 0, 0, 0, 0, 192, 127, 3, 0, 0, 0, 0, 0, 192, 127, 3, 0, 0, 0, 0, 0, 192, 127, 0, 0, 0, 0, 0, 0, 192, 127, 0, 0, 0, 0, 0, 0, 192, 127, 0, 0, 0, 0, 0, 0, 192, 127, 0, 0, 0, 0, 0, 0, 192, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 127, 0, 0, 192, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 127, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 127, 0, 0, 192, 127, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 128, 191, 0, 0, 128, 191, 0, 0, 192, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 63, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 190, 12, 0, 0, 200, 12, 0, 0, 208, 12, 0, 0, 216, 12, 0, 0, 230, 12, 0, 0, 242, 12, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 192, 127, 3, 0, 0, 0, 180, 45, 0, 0, 181, 45, 0, 0, 182, 45, 0, 0, 181, 45, 0, 0, 182, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 183, 45, 0, 0, 181, 45, 0, 0, 181, 45, 0, 0, 181, 45, 0, 0, 181, 45, 0, 0, 181, 45, 0, 0, 181, 45, 0, 0, 184, 45, 0, 0, 185, 45, 0, 0, 181, 45, 0, 0, 181, 45, 0, 0, 182, 45, 0, 0, 186, 45, 0, 0, 185, 45, 0, 0, 148, 4, 0, 0, 3, 0, 0, 0, 187, 45, 0, 0, 164, 4, 0, 0, 188, 45, 0, 0, 2, 0, 0, 0, 189, 45, 0, 0, 164, 4, 0, 0, 188, 45, 0, 0, 185, 45, 0, 0, 164, 4, 0, 0, 185, 45, 0, 0, 164, 4, 0, 0, 188, 45, 0, 0, 181, 45, 0, 0, 182, 45, 0, 0, 181, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 5, 0, 0, 0, 6, 0, 0, 0, 1, 0, 0, 0, 7, 0, 0, 0, 183, 45, 0, 0, 182, 45, 0, 0, 181, 45, 0, 0, 190, 45, 0, 0, 190, 45, 0, 0, 182, 45, 0, 0, 182, 45, 0, 0, 185, 45, 0, 0, 181, 45, 0, 0, 185, 45, 0, 0, 182, 45, 0, 0, 181, 45, 0, 0, 185, 45, 0, 0, 182, 45, 0, 0, 185, 45, 0, 0, 48, 5, 0, 0, 3, 0, 0, 0, 56, 5, 0, 0, 1, 0, 0, 0, 189, 45, 0, 0, 185, 45, 0, 0, 164, 4, 0, 0, 76, 5, 0, 0, 2, 0, 0, 0, 191, 45, 0, 0, 186, 45, 0, 0, 182, 45, 0, 0, 185, 45, 0, 0, 192, 45, 0, 0, 185, 45, 0, 0, 182, 45, 0, 0, 186, 45, 0, 0, 185, 45, 0, 0, 76, 5, 0, 0, 76, 5, 0, 0, 136, 5, 0, 0, 182, 45, 0, 0, 181, 45, 0, 0, 2, 0, 0, 0, 190, 45, 0, 0, 136, 5, 0, 0, 56, 19, 0, 0, 156, 5, 0, 0, 2, 0, 0, 0, 184, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 8, 0, 0, 0, 9, 0, 0, 0, 1, 0, 0, 0, 10, 0, 0, 0, 204, 5, 0, 0, 181, 45, 0, 0, 181, 45, 0, 0, 2, 0, 0, 0, 180, 45, 0, 0, 204, 5, 0, 0, 2, 0, 0, 0, 195, 45, 0, 0, 236, 5, 0, 0, 97, 19, 0, 0, 198, 45, 0, 0, 211, 45, 0, 0, 212, 45, 0, 0, 213, 45, 0, 0, 214, 45, 0, 0, 215, 45, 0, 0, 188, 45, 0, 0, 182, 45, 0, 0, 216, 45, 0, 0, 217, 45, 0, 0, 218, 45, 0, 0, 219, 45, 0, 0, 192, 45, 0, 0, 181, 45, 0, 0, 0, 0, 0, 0, 185, 45, 0, 0, 110, 19, 0, 0, 186, 45, 0, 0, 115, 19, 0, 0, 221, 45, 0, 0, 120, 19, 0, 0, 148, 4, 0, 0, 132, 19, 0, 0, 96, 6, 0, 0, 145, 19, 0, 0, 222, 45, 0, 0, 164, 19, 0, 0, 223, 45, 0, 0, 173, 19, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 104, 6, 0, 0, 1, 0, 0, 0, 187, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 11, 0, 0, 0, 12, 0, 0, 0, 1, 0, 0, 0, 13, 0, 0, 0, 185, 45, 0, 0, 224, 45, 0, 0, 164, 6, 0, 0, 188, 45, 0, 0, 172, 6, 0, 0, 180, 6, 0, 0, 2, 0, 0, 0, 188, 6, 0, 0, 7, 0, 0, 0, 224, 45, 0, 0, 7, 0, 0, 0, 164, 6, 0, 0, 1, 0, 0, 0, 213, 45, 0, 0, 185, 45, 0, 0, 224, 45, 0, 0, 172, 6, 0, 0, 185, 45, 0, 0, 224, 45, 0, 0, 164, 6, 0, 0, 185, 45, 0, 0, 224, 45, 0, 0, 211, 45, 0, 0, 211, 45, 0, 0, 222, 45, 0, 0, 211, 45, 0, 0, 224, 45, 0, 0, 222, 45, 0, 0, 211, 45, 0, 0, 224, 45, 0, 0, 172, 6, 0, 0, 222, 45, 0, 0, 211, 45, 0, 0, 224, 45, 0, 0, 188, 45, 0, 0, 222, 45, 0, 0, 211, 45, 0, 0, 40, 7, 0, 0, 188, 45, 0, 0, 2, 0, 0, 0, 224, 45, 0, 0, 185, 45, 0, 0, 188, 45, 0, 0, 188, 45, 0, 0, 188, 45, 0, 0, 188, 45, 0, 0, 222, 45, 0, 0, 224, 45, 0, 0, 148, 4, 0, 0, 185, 45, 0, 0, 148, 4, 0, 0, 148, 4, 0, 0, 148, 4, 0, 0, 148, 4, 0, 0, 148, 4, 0, 0, 185, 45, 0, 0, 164, 6, 0, 0, 148, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 14, 0, 0, 0, 15, 0, 0, 0, 1, 0, 0, 0, 16, 0, 0, 0, 148, 7, 0, 0, 2, 0, 0, 0, 225, 45, 0, 0, 183, 45, 0, 0, 188, 45, 0, 0, 168, 7, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 234, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 9, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 2, 0, 0, 0, 242, 45, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 111, 117, 108, 100, 32, 110, 111, 116, 32, 97, 108, 108, 111, 99, 97, 116, 101, 32, 109, 101, 109, 111, 114, 121, 32, 102, 111, 114, 32, 110, 111, 100, 101, 0, 67, 97, 110, 110, 111, 116, 32, 114, 101, 115, 101, 116, 32, 97, 32, 110, 111, 100, 101, 32, 119, 104, 105, 99, 104, 32, 115, 116, 105, 108, 108, 32, 104, 97, 115, 32, 99, 104, 105, 108, 100, 114, 101, 110, 32, 97, 116, 116, 97, 99, 104, 101, 100, 0, 67, 97, 110, 110, 111, 116, 32, 114, 101, 115, 101, 116, 32, 97, 32, 110, 111, 100, 101, 32, 115, 116, 105, 108, 108, 32, 97, 116, 116, 97, 99, 104, 101, 100, 32, 116, 111, 32, 97, 32, 112, 97, 114, 101, 110, 116, 0, 67, 111, 117, 108, 100, 32, 110, 111, 116, 32, 97, 108, 108, 111, 99, 97, 116, 101, 32, 109, 101, 109, 111, 114, 121, 32, 102, 111, 114, 32, 99, 111, 110, 102, 105, 103, 0, 67, 97, 110, 110, 111, 116, 32, 115, 101, 116, 32, 109, 101, 97, 115, 117, 114, 101, 32, 102, 117, 110, 99, 116, 105, 111, 110, 58, 32, 78, 111, 100, 101, 115, 32, 119, 105, 116, 104, 32, 109, 101, 97, 115, 117, 114, 101, 32, 102, 117, 110, 99, 116, 105, 111, 110, 115, 32, 99, 97, 110, 110, 111, 116, 32, 104, 97, 118, 101, 32, 99, 104, 105, 108, 100, 114, 101, 110, 46, 0, 67, 104, 105, 108, 100, 32, 97, 108, 114, 101, 97, 100, 121, 32, 104, 97, 115, 32, 97, 32, 112, 97, 114, 101, 110, 116, 44, 32, 105, 116, 32, 109, 117, 115, 116, 32, 98, 101, 32, 114, 101, 109, 111, 118, 101, 100, 32, 102, 105, 114, 115, 116, 46, 0, 67, 97, 110, 110, 111, 116, 32, 97, 100, 100, 32, 99, 104, 105, 108, 100, 58, 32, 78, 111, 100, 101, 115, 32, 119, 105, 116, 104, 32, 109, 101, 97, 115, 117, 114, 101, 32, 102, 117, 110, 99, 116, 105, 111, 110, 115, 32, 99, 97, 110, 110, 111, 116, 32, 104, 97, 118, 101, 32, 99, 104, 105, 108, 100, 114, 101, 110, 46, 0, 79, 110, 108, 121, 32, 108, 101, 97, 102, 32, 110, 111, 100, 101, 115, 32, 119, 105, 116, 104, 32, 99, 117, 115, 116, 111, 109, 32, 109, 101, 97, 115, 117, 114, 101, 32, 102, 117, 110, 99, 116, 105, 111, 110, 115, 115, 104, 111, 117, 108, 100, 32, 109, 97, 110, 117, 97, 108, 108, 121, 32, 109, 97, 114, 107, 32, 116, 104, 101, 109, 115, 101, 108, 118, 101, 115, 32, 97, 115, 32, 100, 105, 114, 116, 121, 0, 67, 97, 110, 110, 111, 116, 32, 103, 101, 116, 32, 108, 97, 121, 111, 117, 116, 32, 112, 114, 111, 112, 101, 114, 116, 105, 101, 115, 32, 111, 102, 32, 109, 117, 108, 116, 105, 45, 101, 100, 103, 101, 32, 115, 104, 111, 114, 116, 104, 97, 110, 100, 115, 0, 37, 115, 37, 100, 46, 123, 91, 115, 107, 105, 112, 112, 101, 100, 93, 32, 0, 119, 109, 58, 32, 37, 115, 44, 32, 104, 109, 58, 32, 37, 115, 44, 32, 97, 119, 58, 32, 37, 102, 32, 97, 104, 58, 32, 37, 102, 32, 61, 62, 32, 100, 58, 32, 40, 37, 102, 44, 32, 37, 102, 41, 32, 37, 115, 10, 0, 37, 115, 37, 100, 46, 123, 37, 115, 0, 42, 0, 119, 109, 58, 32, 37, 115, 44, 32, 104, 109, 58, 32, 37, 115, 44, 32, 97, 119, 58, 32, 37, 102, 32, 97, 104, 58, 32, 37, 102, 32, 37, 115, 10, 0, 37, 115, 37, 100, 46, 125, 37, 115, 0, 119, 109, 58, 32, 37, 115, 44, 32, 104, 109, 58, 32, 37, 115, 44, 32, 100, 58, 32, 40, 37, 102, 44, 32, 37, 102, 41, 32, 37, 115, 10, 0, 79, 117, 116, 32, 111, 102, 32, 99, 97, 99, 104, 101, 32, 101, 110, 116, 114, 105, 101, 115, 33, 10, 0, 83, 99, 97, 108, 101, 32, 102, 97, 99, 116, 111, 114, 32, 115, 104, 111, 117, 108, 100, 32, 110, 111, 116, 32, 98, 101, 32, 108, 101, 115, 115, 32, 116, 104, 97, 110, 32, 122, 101, 114, 111, 0, 105, 110, 105, 116, 105, 97, 108, 0, 37, 115, 10, 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 0, 85, 78, 68, 69, 70, 73, 78, 69, 68, 0, 69, 88, 65, 67, 84, 76, 89, 0, 65, 84, 95, 77, 79, 83, 84, 0, 76, 65, 89, 95, 85, 78, 68, 69, 70, 73, 78, 69, 68, 0, 76, 65, 89, 95, 69, 88, 65, 67, 84, 76, 89, 0, 76, 65, 89, 95, 65, 84, 95, 77, 79, 83, 84, 0, 97, 118, 97, 105, 108, 97, 98, 108, 101, 87, 105, 100, 116, 104, 32, 105, 115, 32, 105, 110, 100, 101, 102, 105, 110, 105, 116, 101, 32, 115, 111, 32, 119, 105, 100, 116, 104, 77, 101, 97, 115, 117, 114, 101, 77, 111, 100, 101, 32, 109, 117, 115, 116, 32, 98, 101, 32, 89, 71, 77, 101, 97, 115, 117, 114, 101, 77, 111, 100, 101, 85, 110, 100, 101, 102, 105, 110, 101, 100, 0, 97, 118, 97, 105, 108, 97, 98, 108, 101, 72, 101, 105, 103, 104, 116, 32, 105, 115, 32, 105, 110, 100, 101, 102, 105, 110, 105, 116, 101, 32, 115, 111, 32, 104, 101, 105, 103, 104, 116, 77, 101, 97, 115, 117, 114, 101, 77, 111, 100, 101, 32, 109, 117, 115, 116, 32, 98, 101, 32, 89, 71, 77, 101, 97, 115, 117, 114, 101, 77, 111, 100, 101, 85, 110, 100, 101, 102, 105, 110, 101, 100, 0, 102, 108, 101, 120, 0, 115, 116, 114, 101, 116, 99, 104, 0, 109, 117, 108, 116, 105, 108, 105, 110, 101, 45, 115, 116, 114, 101, 116, 99, 104, 0, 69, 120, 112, 101, 99, 116, 101, 100, 32, 110, 111, 100, 101, 32, 116, 111, 32, 104, 97, 118, 101, 32, 99, 117, 115, 116, 111, 109, 32, 109, 101, 97, 115, 117, 114, 101, 32, 102, 117, 110, 99, 116, 105, 111, 110, 0, 109, 101, 97, 115, 117, 114, 101, 0, 69, 120, 112, 101, 99, 116, 32, 99, 117, 115, 116, 111, 109, 32, 98, 97, 115, 101, 108, 105, 110, 101, 32, 102, 117, 110, 99, 116, 105, 111, 110, 32, 116, 111, 32, 110, 111, 116, 32, 114, 101, 116, 117, 114, 110, 32, 78, 97, 78, 0, 97, 98, 115, 45, 109, 101, 97, 115, 117, 114, 101, 0, 97, 98, 115, 45, 108, 97, 121, 111, 117, 116, 0, 78, 111, 100, 101, 0, 99, 114, 101, 97, 116, 101, 68, 101, 102, 97, 117, 108, 116, 0, 99, 114, 101, 97, 116, 101, 87, 105, 116, 104, 67, 111, 110, 102, 105, 103, 0, 100, 101, 115, 116, 114, 111, 121, 0, 114, 101, 115, 101, 116, 0, 99, 111, 112, 121, 83, 116, 121, 108, 101, 0, 115, 101, 116, 80, 111, 115, 105, 116, 105, 111, 110, 84, 121, 112, 101, 0, 115, 101, 116, 80, 111, 115, 105, 116, 105, 111, 110, 0, 115, 101, 116, 80, 111, 115, 105, 116, 105, 111, 110, 80, 101, 114, 99, 101, 110, 116, 0, 115, 101, 116, 65, 108, 105, 103, 110, 67, 111, 110, 116, 101, 110, 116, 0, 115, 101, 116, 65, 108, 105, 103, 110, 73, 116, 101, 109, 115, 0, 115, 101, 116, 65, 108, 105, 103, 110, 83, 101, 108, 102, 0, 115, 101, 116, 70, 108, 101, 120, 68, 105, 114, 101, 99, 116, 105, 111, 110, 0, 115, 101, 116, 70, 108, 101, 120, 87, 114, 97, 112, 0, 115, 101, 116, 74, 117, 115, 116, 105, 102, 121, 67, 111, 110, 116, 101, 110, 116, 0, 115, 101, 116, 77, 97, 114, 103, 105, 110, 0, 115, 101, 116, 77, 97, 114, 103, 105, 110, 80, 101, 114, 99, 101, 110, 116, 0, 115, 101, 116, 77, 97, 114, 103, 105, 110, 65, 117, 116, 111, 0, 115, 101, 116, 79, 118, 101, 114, 102, 108, 111, 119, 0, 115, 101, 116, 68, 105, 115, 112, 108, 97, 121, 0, 115, 101, 116, 70, 108, 101, 120, 0, 115, 101, 116, 70, 108, 101, 120, 66, 97, 115, 105, 115, 0, 115, 101, 116, 70, 108, 101, 120, 66, 97, 115, 105, 115, 80, 101, 114, 99, 101, 110, 116, 0, 115, 101, 116, 70, 108, 101, 120, 71, 114, 111, 119, 0, 115, 101, 116, 70, 108, 101, 120, 83, 104, 114, 105, 110, 107, 0, 115, 101, 116, 87, 105, 100, 116, 104, 0, 115, 101, 116, 87, 105, 100, 116, 104, 80, 101, 114, 99, 101, 110, 116, 0, 115, 101, 116, 87, 105, 100, 116, 104, 65, 117, 116, 111, 0, 115, 101, 116, 72, 101, 105, 103, 104, 116, 0, 115, 101, 116, 72, 101, 105, 103, 104, 116, 80, 101, 114, 99, 101, 110, 116, 0, 115, 101, 116, 72, 101, 105, 103, 104, 116, 65, 117, 116, 111, 0, 115, 101, 116, 77, 105, 110, 87, 105, 100, 116, 104, 0, 115, 101, 116, 77, 105, 110, 87, 105, 100, 116, 104, 80, 101, 114, 99, 101, 110, 116, 0, 115, 101, 116, 77, 105, 110, 72, 101, 105, 103, 104, 116, 0, 115, 101, 116, 77, 105, 110, 72, 101, 105, 103, 104, 116, 80, 101, 114, 99, 101, 110, 116, 0, 115, 101, 116, 77, 97, 120, 87, 105, 100, 116, 104, 0, 115, 101, 116, 77, 97, 120, 87, 105, 100, 116, 104, 80, 101, 114, 99, 101, 110, 116, 0, 115, 101, 116, 77, 97, 120, 72, 101, 105, 103, 104, 116, 0, 115, 101, 116, 77, 97, 120, 72, 101, 105, 103, 104, 116, 80, 101, 114, 99, 101, 110, 116, 0, 115, 101, 116, 65, 115, 112, 101, 99, 116, 82, 97, 116, 105, 111, 0, 115, 101, 116, 66, 111, 114, 100, 101, 114, 0, 115, 101, 116, 80, 97, 100, 100, 105, 110, 103, 0, 115, 101, 116, 80, 97, 100, 100, 105, 110, 103, 80, 101, 114, 99, 101, 110, 116, 0, 103, 101, 116, 80, 111, 115, 105, 116, 105, 111, 110, 84, 121, 112, 101, 0, 103, 101, 116, 80, 111, 115, 105, 116, 105, 111, 110, 0, 103, 101, 116, 65, 108, 105, 103, 110, 67, 111, 110, 116, 101, 110, 116, 0, 103, 101, 116, 65, 108, 105, 103, 110, 73, 116, 101, 109, 115, 0, 103, 101, 116, 65, 108, 105, 103, 110, 83, 101, 108, 102, 0, 103, 101, 116, 70, 108, 101, 120, 68, 105, 114, 101, 99, 116, 105, 111, 110, 0, 103, 101, 116, 70, 108, 101, 120, 87, 114, 97, 112, 0, 103, 101, 116, 74, 117, 115, 116, 105, 102, 121, 67, 111, 110, 116, 101, 110, 116, 0, 103, 101, 116, 77, 97, 114, 103, 105, 110, 0, 103, 101, 116, 70, 108, 101, 120, 66, 97, 115, 105, 115, 0, 103, 101, 116, 70, 108, 101, 120, 71, 114, 111, 119, 0, 103, 101, 116, 70, 108, 101, 120, 83, 104, 114, 105, 110, 107, 0, 103, 101, 116, 87, 105, 100, 116, 104, 0, 103, 101, 116, 72, 101, 105, 103, 104, 116, 0, 103, 101, 116, 77, 105, 110, 87, 105, 100, 116, 104, 0, 103, 101, 116, 77, 105, 110, 72, 101, 105, 103, 104, 116, 0, 103, 101, 116, 77, 97, 120, 87, 105, 100, 116, 104, 0, 103, 101, 116, 77, 97, 120, 72, 101, 105, 103, 104, 116, 0, 103, 101, 116, 65, 115, 112, 101, 99, 116, 82, 97, 116, 105, 111, 0, 103, 101, 116, 66, 111, 114, 100, 101, 114, 0, 103, 101, 116, 79, 118, 101, 114, 102, 108, 111, 119, 0, 103, 101, 116, 68, 105, 115, 112, 108, 97, 121, 0, 103, 101, 116, 80, 97, 100, 100, 105, 110, 103, 0, 105, 110, 115, 101, 114, 116, 67, 104, 105, 108, 100, 0, 114, 101, 109, 111, 118, 101, 67, 104, 105, 108, 100, 0, 103, 101, 116, 67, 104, 105, 108, 100, 67, 111, 117, 110, 116, 0, 103, 101, 116, 80, 97, 114, 101, 110, 116, 0, 103, 101, 116, 67, 104, 105, 108, 100, 0, 115, 101, 116, 77, 101, 97, 115, 117, 114, 101, 70, 117, 110, 99, 0, 117, 110, 115, 101, 116, 77, 101, 97, 115, 117, 114, 101, 70, 117, 110, 99, 0, 109, 97, 114, 107, 68, 105, 114, 116, 121, 0, 105, 115, 68, 105, 114, 116, 121, 0, 99, 97, 108, 99, 117, 108, 97, 116, 101, 76, 97, 121, 111, 117, 116, 0, 103, 101, 116, 67, 111, 109, 112, 117, 116, 101, 100, 76, 101, 102, 116, 0, 103, 101, 116, 67, 111, 109, 112, 117, 116, 101, 100, 82, 105, 103, 104, 116, 0, 103, 101, 116, 67, 111, 109, 112, 117, 116, 101, 100, 84, 111, 112, 0, 103, 101, 116, 67, 111, 109, 112, 117, 116, 101, 100, 66, 111, 116, 116, 111, 109, 0, 103, 101, 116, 67, 111, 109, 112, 117, 116, 101, 100, 87, 105, 100, 116, 104, 0, 103, 101, 116, 67, 111, 109, 112, 117, 116, 101, 100, 72, 101, 105, 103, 104, 116, 0, 103, 101, 116, 67, 111, 109, 112, 117, 116, 101, 100, 76, 97, 121, 111, 117, 116, 0, 103, 101, 116, 67, 111, 109, 112, 117, 116, 101, 100, 77, 97, 114, 103, 105, 110, 0, 103, 101, 116, 67, 111, 109, 112, 117, 116, 101, 100, 66, 111, 114, 100, 101, 114, 0, 103, 101, 116, 67, 111, 109, 112, 117, 116, 101, 100, 80, 97, 100, 100, 105, 110, 103, 0, 67, 111, 110, 102, 105, 103, 0, 99, 114, 101, 97, 116, 101, 0, 115, 101, 116, 69, 120, 112, 101, 114, 105, 109, 101, 110, 116, 97, 108, 70, 101, 97, 116, 117, 114, 101, 69, 110, 97, 98, 108, 101, 100, 0, 115, 101, 116, 80, 111, 105, 110, 116, 83, 99, 97, 108, 101, 70, 97, 99, 116, 111, 114, 0, 105, 115, 69, 120, 112, 101, 114, 105, 109, 101, 110, 116, 97, 108, 70, 101, 97, 116, 117, 114, 101, 69, 110, 97, 98, 108, 101, 100, 0, 86, 97, 108, 117, 101, 0, 76, 97, 121, 111, 117, 116, 0, 83, 105, 122, 101, 0, 103, 101, 116, 73, 110, 115, 116, 97, 110, 99, 101, 67, 111, 117, 110, 116, 0, 73, 110, 116, 54, 52, 0, 1, 1, 1, 2, 2, 4, 4, 4, 4, 8, 8, 4, 8, 118, 111, 105, 100, 0, 98, 111, 111, 108, 0, 115, 116, 100, 58, 58, 115, 116, 114, 105, 110, 103, 0, 99, 98, 70, 117, 110, 99, 116, 105, 111, 110, 32, 38, 0, 99, 111, 110, 115, 116, 32, 99, 98, 70, 117, 110, 99, 116, 105, 111, 110, 32, 38, 0, 69, 120, 116, 101, 114, 110, 97, 108, 0, 66, 117, 102, 102, 101, 114, 0, 78, 66, 105, 110, 100, 73, 68, 0, 78, 66, 105, 110, 100, 0, 98, 105, 110, 100, 95, 118, 97, 108, 117, 101, 0, 114, 101, 102, 108, 101, 99, 116, 0, 113, 117, 101, 114, 121, 84, 121, 112, 101, 0, 108, 97, 108, 108, 111, 99, 0, 108, 114, 101, 115, 101, 116, 0, 123, 114, 101, 116, 117, 114, 110, 40, 95, 110, 98, 105, 110, 100, 46, 99, 97, 108, 108, 98, 97, 99, 107, 83, 105, 103, 110, 97, 116, 117, 114, 101, 76, 105, 115, 116, 91, 36, 48, 93, 46, 97, 112, 112, 108, 121, 40, 116, 104, 105, 115, 44, 97, 114, 103, 117, 109, 101, 110, 116, 115, 41, 41, 59, 125, 0, 95, 110, 98, 105, 110, 100, 95, 110, 101, 119, 0, 17, 0, 10, 0, 17, 17, 17, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 17, 0, 15, 10, 17, 17, 17, 3, 10, 7, 0, 1, 19, 9, 11, 11, 0, 0, 9, 6, 11, 0, 0, 11, 0, 6, 17, 0, 0, 0, 17, 17, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 17, 0, 10, 10, 17, 17, 17, 0, 10, 0, 0, 2, 0, 9, 11, 0, 0, 0, 9, 0, 11, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 12, 0, 0, 0, 0, 9, 12, 0, 0, 0, 0, 0, 12, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 4, 13, 0, 0, 0, 0, 9, 14, 0, 0, 0, 0, 0, 14, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 15, 0, 0, 0, 0, 9, 16, 0, 0, 0, 0, 0, 16, 0, 0, 16, 0, 0, 18, 0, 0, 0, 18, 18, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0, 18, 18, 18, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 10, 0, 0, 0, 0, 9, 11, 0, 0, 0, 0, 0, 11, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 12, 0, 0, 0, 0, 9, 12, 0, 0, 0, 0, 0, 12, 0, 0, 12, 0, 0, 45, 43, 32, 32, 32, 48, 88, 48, 120, 0, 40, 110, 117, 108, 108, 41, 0, 45, 48, 88, 43, 48, 88, 32, 48, 88, 45, 48, 120, 43, 48, 120, 32, 48, 120, 0, 105, 110, 102, 0, 73, 78, 70, 0, 110, 97, 110, 0, 78, 65, 78, 0, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 65, 66, 67, 68, 69, 70, 46, 0, 84, 33, 34, 25, 13, 1, 2, 3, 17, 75, 28, 12, 16, 4, 11, 29, 18, 30, 39, 104, 110, 111, 112, 113, 98, 32, 5, 6, 15, 19, 20, 21, 26, 8, 22, 7, 40, 36, 23, 24, 9, 10, 14, 27, 31, 37, 35, 131, 130, 125, 38, 42, 43, 60, 61, 62, 63, 67, 71, 74, 77, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 99, 100, 101, 102, 103, 105, 106, 107, 108, 114, 115, 116, 121, 122, 123, 124, 0, 73, 108, 108, 101, 103, 97, 108, 32, 98, 121, 116, 101, 32, 115, 101, 113, 117, 101, 110, 99, 101, 0, 68, 111, 109, 97, 105, 110, 32, 101, 114, 114, 111, 114, 0, 82, 101, 115, 117, 108, 116, 32, 110, 111, 116, 32, 114, 101, 112, 114, 101, 115, 101, 110, 116, 97, 98, 108, 101, 0, 78, 111, 116, 32, 97, 32, 116, 116, 121, 0, 80, 101, 114, 109, 105, 115, 115, 105, 111, 110, 32, 100, 101, 110, 105, 101, 100, 0, 79, 112, 101, 114, 97, 116, 105, 111, 110, 32, 110, 111, 116, 32, 112, 101, 114, 109, 105, 116, 116, 101, 100, 0, 78, 111, 32, 115, 117, 99, 104, 32, 102, 105, 108, 101, 32, 111, 114, 32, 100, 105, 114, 101, 99, 116, 111, 114, 121, 0, 78, 111, 32, 115, 117, 99, 104, 32, 112, 114, 111, 99, 101, 115, 115, 0, 70, 105, 108, 101, 32, 101, 120, 105, 115, 116, 115, 0, 86, 97, 108, 117, 101, 32, 116, 111, 111, 32, 108, 97, 114, 103, 101, 32, 102, 111, 114, 32, 100, 97, 116, 97, 32, 116, 121, 112, 101, 0, 78, 111, 32, 115, 112, 97, 99, 101, 32, 108, 101, 102, 116, 32, 111, 110, 32, 100, 101, 118, 105, 99, 101, 0, 79, 117, 116, 32, 111, 102, 32, 109, 101, 109, 111, 114, 121, 0, 82, 101, 115, 111, 117, 114, 99, 101, 32, 98, 117, 115, 121, 0, 73, 110, 116, 101, 114, 114, 117, 112, 116, 101, 100, 32, 115, 121, 115, 116, 101, 109, 32, 99, 97, 108, 108, 0, 82, 101, 115, 111, 117, 114, 99, 101, 32, 116, 101, 109, 112, 111, 114, 97, 114, 105, 108, 121, 32, 117, 110, 97, 118, 97, 105, 108, 97, 98, 108, 101, 0, 73, 110, 118, 97, 108, 105, 100, 32, 115, 101, 101, 107, 0, 67, 114, 111, 115, 115, 45, 100, 101, 118, 105, 99, 101, 32, 108, 105, 110, 107, 0, 82, 101, 97, 100, 45, 111, 110, 108, 121, 32, 102, 105, 108, 101, 32, 115, 121, 115, 116, 101, 109, 0, 68, 105, 114, 101, 99, 116, 111, 114, 121, 32, 110, 111, 116, 32, 101, 109, 112, 116, 121, 0, 67, 111, 110, 110, 101, 99, 116, 105, 111, 110, 32, 114, 101, 115, 101, 116, 32, 98, 121, 32, 112, 101, 101, 114, 0, 79, 112, 101, 114, 97, 116, 105, 111, 110, 32, 116, 105, 109, 101, 100, 32, 111, 117, 116, 0, 67, 111, 110, 110, 101, 99, 116, 105, 111, 110, 32, 114, 101, 102, 117, 115, 101, 100, 0, 72, 111, 115, 116, 32, 105, 115, 32, 100, 111, 119, 110, 0, 72, 111, 115, 116, 32, 105, 115, 32, 117, 110, 114, 101, 97, 99, 104, 97, 98, 108, 101, 0, 65, 100, 100, 114, 101, 115, 115, 32, 105, 110, 32, 117, 115, 101, 0, 66, 114, 111, 107, 101, 110, 32, 112, 105, 112, 101, 0, 73, 47, 79, 32, 101, 114, 114, 111, 114, 0, 78, 111, 32, 115, 117, 99, 104, 32, 100, 101, 118, 105, 99, 101, 32, 111, 114, 32, 97, 100, 100, 114, 101, 115, 115, 0, 66, 108, 111, 99, 107, 32, 100, 101, 118, 105, 99, 101, 32, 114, 101, 113, 117, 105, 114, 101, 100, 0, 78, 111, 32, 115, 117, 99, 104, 32, 100, 101, 118, 105, 99, 101, 0, 78, 111, 116, 32, 97, 32, 100, 105, 114, 101, 99, 116, 111, 114, 121, 0, 73, 115, 32, 97, 32, 100, 105, 114, 101, 99, 116, 111, 114, 121, 0, 84, 101, 120, 116, 32, 102, 105, 108, 101, 32, 98, 117, 115, 121, 0, 69, 120, 101, 99, 32, 102, 111, 114, 109, 97, 116, 32, 101, 114, 114, 111, 114, 0, 73, 110, 118, 97, 108, 105, 100, 32, 97, 114, 103, 117, 109, 101, 110, 116, 0, 65, 114, 103, 117, 109, 101, 110, 116, 32, 108, 105, 115, 116, 32, 116, 111, 111, 32, 108, 111, 110, 103, 0, 83, 121, 109, 98, 111, 108, 105, 99, 32, 108, 105, 110, 107, 32, 108, 111, 111, 112, 0, 70, 105, 108, 101, 110, 97, 109, 101, 32, 116, 111, 111, 32, 108, 111, 110, 103, 0, 84, 111, 111, 32, 109, 97, 110, 121, 32, 111, 112, 101, 110, 32, 102, 105, 108, 101, 115, 32, 105, 110, 32, 115, 121, 115, 116, 101, 109, 0, 78, 111, 32, 102, 105, 108, 101, 32, 100, 101, 115, 99, 114, 105, 112, 116, 111, 114, 115, 32, 97, 118, 97, 105, 108, 97, 98, 108, 101, 0, 66, 97, 100, 32, 102, 105, 108, 101, 32, 100, 101, 115, 99, 114, 105, 112, 116, 111, 114, 0, 78, 111, 32, 99, 104, 105, 108, 100, 32, 112, 114, 111, 99, 101, 115, 115, 0, 66, 97, 100, 32, 97, 100, 100, 114, 101, 115, 115, 0, 70, 105, 108, 101, 32, 116, 111, 111, 32, 108, 97, 114, 103, 101, 0, 84, 111, 111, 32, 109, 97, 110, 121, 32, 108, 105, 110, 107, 115, 0, 78, 111, 32, 108, 111, 99, 107, 115, 32, 97, 118, 97, 105, 108, 97, 98, 108, 101, 0, 82, 101, 115, 111, 117, 114, 99, 101, 32, 100, 101, 97, 100, 108, 111, 99, 107, 32, 119, 111, 117, 108, 100, 32, 111, 99, 99, 117, 114, 0, 83, 116, 97, 116, 101, 32, 110, 111, 116, 32, 114, 101, 99, 111, 118, 101, 114, 97, 98, 108, 101, 0, 80, 114, 101, 118, 105, 111, 117, 115, 32, 111, 119, 110, 101, 114, 32, 100, 105, 101, 100, 0, 79, 112, 101, 114, 97, 116, 105, 111, 110, 32, 99, 97, 110, 99, 101, 108, 101, 100, 0, 70, 117, 110, 99, 116, 105, 111, 110, 32, 110, 111, 116, 32, 105, 109, 112, 108, 101, 109, 101, 110, 116, 101, 100, 0, 78, 111, 32, 109, 101, 115, 115, 97, 103, 101, 32, 111, 102, 32, 100, 101, 115, 105, 114, 101, 100, 32, 116, 121, 112, 101, 0, 73, 100, 101, 110, 116, 105, 102, 105, 101, 114, 32, 114, 101, 109, 111, 118, 101, 100, 0, 68, 101, 118, 105, 99, 101, 32, 110, 111, 116, 32, 97, 32, 115, 116, 114, 101, 97, 109, 0, 78, 111, 32, 100, 97, 116, 97, 32, 97, 118, 97, 105, 108, 97, 98, 108, 101, 0, 68, 101, 118, 105, 99, 101, 32, 116, 105, 109, 101, 111, 117, 116, 0, 79, 117, 116, 32, 111, 102, 32, 115, 116, 114, 101, 97, 109, 115, 32, 114, 101, 115, 111, 117, 114, 99, 101, 115, 0, 76, 105, 110, 107, 32, 104, 97, 115, 32, 98, 101, 101, 110, 32, 115, 101, 118, 101, 114, 101, 100, 0, 80, 114, 111, 116, 111, 99, 111, 108, 32, 101, 114, 114, 111, 114, 0, 66, 97, 100, 32, 109, 101, 115, 115, 97, 103, 101, 0, 70, 105, 108, 101, 32, 100, 101, 115, 99, 114, 105, 112, 116, 111, 114, 32, 105, 110, 32, 98, 97, 100, 32, 115, 116, 97, 116, 101, 0, 78, 111, 116, 32, 97, 32, 115, 111, 99, 107, 101, 116, 0, 68, 101, 115, 116, 105, 110, 97, 116, 105, 111, 110, 32, 97, 100, 100, 114, 101, 115, 115, 32, 114, 101, 113, 117, 105, 114, 101, 100, 0, 77, 101, 115, 115, 97, 103, 101, 32, 116, 111, 111, 32, 108, 97, 114, 103, 101, 0, 80, 114, 111, 116, 111, 99, 111, 108, 32, 119, 114, 111, 110, 103, 32, 116, 121, 112, 101, 32, 102, 111, 114, 32, 115, 111, 99, 107, 101, 116, 0, 80, 114, 111, 116, 111, 99, 111, 108, 32, 110, 111, 116, 32, 97, 118, 97, 105, 108, 97, 98, 108, 101, 0, 80, 114, 111, 116, 111, 99, 111, 108, 32, 110, 111, 116, 32, 115, 117, 112, 112, 111, 114, 116, 101, 100, 0, 83, 111, 99, 107, 101, 116, 32, 116, 121, 112, 101, 32, 110, 111, 116, 32, 115, 117, 112, 112, 111, 114, 116, 101, 100, 0, 78, 111, 116, 32, 115, 117, 112, 112, 111, 114, 116, 101, 100, 0, 80, 114, 111, 116, 111, 99, 111, 108, 32, 102, 97, 109, 105, 108, 121, 32, 110, 111, 116, 32, 115, 117, 112, 112, 111, 114, 116, 101, 100, 0, 65, 100, 100, 114, 101, 115, 115, 32, 102, 97, 109, 105, 108, 121, 32, 110, 111, 116, 32, 115, 117, 112, 112, 111, 114, 116, 101, 100, 32, 98, 121, 32, 112, 114, 111, 116, 111, 99, 111, 108, 0, 65, 100, 100, 114, 101, 115, 115, 32, 110, 111, 116, 32, 97, 118, 97, 105, 108, 97, 98, 108, 101, 0, 78, 101, 116, 119, 111, 114, 107, 32, 105, 115, 32, 100, 111, 119, 110, 0, 78, 101, 116, 119, 111, 114, 107, 32, 117, 110, 114, 101, 97, 99, 104, 97, 98, 108, 101, 0, 67, 111, 110, 110, 101, 99, 116, 105, 111, 110, 32, 114, 101, 115, 101, 116, 32, 98, 121, 32, 110, 101, 116, 119, 111, 114, 107, 0, 67, 111, 110, 110, 101, 99, 116, 105, 111, 110, 32, 97, 98, 111, 114, 116, 101, 100, 0, 78, 111, 32, 98, 117, 102, 102, 101, 114, 32, 115, 112, 97, 99, 101, 32, 97, 118, 97, 105, 108, 97, 98, 108, 101, 0, 83, 111, 99, 107, 101, 116, 32, 105, 115, 32, 99, 111, 110, 110, 101, 99, 116, 101, 100, 0, 83, 111, 99, 107, 101, 116, 32, 110, 111, 116, 32, 99, 111, 110, 110, 101, 99, 116, 101, 100, 0, 67, 97, 110, 110, 111, 116, 32, 115, 101, 110, 100, 32, 97, 102, 116, 101, 114, 32, 115, 111, 99, 107, 101, 116, 32, 115, 104, 117, 116, 100, 111, 119, 110, 0, 79, 112, 101, 114, 97, 116, 105, 111, 110, 32, 97, 108, 114, 101, 97, 100, 121, 32, 105, 110, 32, 112, 114, 111, 103, 114, 101, 115, 115, 0, 79, 112, 101, 114, 97, 116, 105, 111, 110, 32, 105, 110, 32, 112, 114, 111, 103, 114, 101, 115, 115, 0, 83, 116, 97, 108, 101, 32, 102, 105, 108, 101, 32, 104, 97, 110, 100, 108, 101, 0, 82, 101, 109, 111, 116, 101, 32, 73, 47, 79, 32, 101, 114, 114, 111, 114, 0, 81, 117, 111, 116, 97, 32, 101, 120, 99, 101, 101, 100, 101, 100, 0, 78, 111, 32, 109, 101, 100, 105, 117, 109, 32, 102, 111, 117, 110, 100, 0, 87, 114, 111, 110, 103, 32, 109, 101, 100, 105, 117, 109, 32, 116, 121, 112, 101, 0, 78, 111, 32, 101, 114, 114, 111, 114, 32, 105, 110, 102, 111, 114, 109, 97, 116, 105, 111, 110, 0, 0], "i8", ALLOC_NONE, Runtime.GLOBAL_BASE);var tempDoublePtr = STATICTOP;STATICTOP += 16;function _atexit(func, arg) { + __ATEXIT__.unshift({ func: func, arg: arg }); + }function ___cxa_atexit() { + return _atexit.apply(null, arguments); + }function _abort() { + Module["abort"](); + }function __ZN8facebook4yoga14YGNodeToStringEPNSt3__212basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEP6YGNode14YGPrintOptionsj() { + Module["printErr"]("missing function: _ZN8facebook4yoga14YGNodeToStringEPNSt3__212basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEP6YGNode14YGPrintOptionsj");abort(-1); + }function __decorate(decorators, target, key, desc) { + var c = arguments.length, + r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, + d;if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;return c > 3 && r && Object.defineProperty(target, key, r), r; + }function _defineHidden(value) { + return function (target, key) { + Object.defineProperty(target, key, { configurable: false, enumerable: false, value: value, writable: true }); + }; + }var _nbind = {};function __nbind_free_external(num) { + _nbind.externalList[num].dereference(num); + }function __nbind_reference_external(num) { + _nbind.externalList[num].reference(); + }function _llvm_stackrestore(p) { + var self = _llvm_stacksave;var ret = self.LLVM_SAVEDSTACKS[p];self.LLVM_SAVEDSTACKS.splice(p, 1);Runtime.stackRestore(ret); + }function __nbind_register_pool(pageSize, usedPtr, rootPtr, pagePtr) { + _nbind.Pool.pageSize = pageSize;_nbind.Pool.usedPtr = usedPtr / 4;_nbind.Pool.rootPtr = rootPtr;_nbind.Pool.pagePtr = pagePtr / 4;HEAP32[usedPtr / 4] = 16909060;if (HEAP8[usedPtr] == 1) _nbind.bigEndian = true;HEAP32[usedPtr / 4] = 0;_nbind.makeTypeKindTbl = (_a = {}, _a[1024] = _nbind.PrimitiveType, _a[64] = _nbind.Int64Type, _a[2048] = _nbind.BindClass, _a[3072] = _nbind.BindClassPtr, _a[4096] = _nbind.SharedClassPtr, _a[5120] = _nbind.ArrayType, _a[6144] = _nbind.ArrayType, _a[7168] = _nbind.CStringType, _a[9216] = _nbind.CallbackType, _a[10240] = _nbind.BindType, _a);_nbind.makeTypeNameTbl = { "Buffer": _nbind.BufferType, "External": _nbind.ExternalType, "Int64": _nbind.Int64Type, "_nbind_new": _nbind.CreateValueType, "bool": _nbind.BooleanType, "cbFunction &": _nbind.CallbackType, "const cbFunction &": _nbind.CallbackType, "const std::string &": _nbind.StringType, "std::string": _nbind.StringType };Module["toggleLightGC"] = _nbind.toggleLightGC;_nbind.callUpcast = Module["dynCall_ii"];var globalScope = _nbind.makeType(_nbind.constructType, { flags: 2048, id: 0, name: "" });globalScope.proto = Module;_nbind.BindClass.list.push(globalScope);var _a; + }function _emscripten_set_main_loop_timing(mode, value) { + Browser.mainLoop.timingMode = mode;Browser.mainLoop.timingValue = value;if (!Browser.mainLoop.func) { + return 1; + }if (mode == 0) { + Browser.mainLoop.scheduler = function Browser_mainLoop_scheduler_setTimeout() { + var timeUntilNextTick = Math.max(0, Browser.mainLoop.tickStartTime + value - _emscripten_get_now()) | 0;setTimeout(Browser.mainLoop.runner, timeUntilNextTick); + };Browser.mainLoop.method = "timeout"; + } else if (mode == 1) { + Browser.mainLoop.scheduler = function Browser_mainLoop_scheduler_rAF() { + Browser.requestAnimationFrame(Browser.mainLoop.runner); + };Browser.mainLoop.method = "rAF"; + } else if (mode == 2) { + if (!window["setImmediate"]) { + var setImmediates = [];var emscriptenMainLoopMessageId = "setimmediate";function Browser_setImmediate_messageHandler(event) { + if (event.source === window && event.data === emscriptenMainLoopMessageId) { + event.stopPropagation();setImmediates.shift()(); + } + }window.addEventListener("message", Browser_setImmediate_messageHandler, true);window["setImmediate"] = function Browser_emulated_setImmediate(func) { + setImmediates.push(func);if (ENVIRONMENT_IS_WORKER) { + if (Module["setImmediates"] === undefined) Module["setImmediates"] = [];Module["setImmediates"].push(func);window.postMessage({ target: emscriptenMainLoopMessageId }); + } else window.postMessage(emscriptenMainLoopMessageId, "*"); + }; + }Browser.mainLoop.scheduler = function Browser_mainLoop_scheduler_setImmediate() { + window["setImmediate"](Browser.mainLoop.runner); + };Browser.mainLoop.method = "immediate"; + }return 0; + }function _emscripten_get_now() { + abort(); + }function _emscripten_set_main_loop(func, fps, simulateInfiniteLoop, arg, noSetTiming) { + Module["noExitRuntime"] = true;assert(!Browser.mainLoop.func, "emscripten_set_main_loop: there can only be one main loop function at once: call emscripten_cancel_main_loop to cancel the previous one before setting a new one with different parameters.");Browser.mainLoop.func = func;Browser.mainLoop.arg = arg;var browserIterationFunc;if (typeof arg !== "undefined") { + browserIterationFunc = function () { + Module["dynCall_vi"](func, arg); + }; + } else { + browserIterationFunc = function () { + Module["dynCall_v"](func); + }; + }var thisMainLoopId = Browser.mainLoop.currentlyRunningMainloop;Browser.mainLoop.runner = function Browser_mainLoop_runner() { + if (ABORT) return;if (Browser.mainLoop.queue.length > 0) { + var start = Date.now();var blocker = Browser.mainLoop.queue.shift();blocker.func(blocker.arg);if (Browser.mainLoop.remainingBlockers) { + var remaining = Browser.mainLoop.remainingBlockers;var next = remaining % 1 == 0 ? remaining - 1 : Math.floor(remaining);if (blocker.counted) { + Browser.mainLoop.remainingBlockers = next; + } else { + next = next + .5;Browser.mainLoop.remainingBlockers = (8 * remaining + next) / 9; + } + }console.log('main loop blocker "' + blocker.name + '" took ' + (Date.now() - start) + " ms");Browser.mainLoop.updateStatus();if (thisMainLoopId < Browser.mainLoop.currentlyRunningMainloop) return;setTimeout(Browser.mainLoop.runner, 0);return; + }if (thisMainLoopId < Browser.mainLoop.currentlyRunningMainloop) return;Browser.mainLoop.currentFrameNumber = Browser.mainLoop.currentFrameNumber + 1 | 0;if (Browser.mainLoop.timingMode == 1 && Browser.mainLoop.timingValue > 1 && Browser.mainLoop.currentFrameNumber % Browser.mainLoop.timingValue != 0) { + Browser.mainLoop.scheduler();return; + } else if (Browser.mainLoop.timingMode == 0) { + Browser.mainLoop.tickStartTime = _emscripten_get_now(); + }if (Browser.mainLoop.method === "timeout" && Module.ctx) { + Module.printErr("Looks like you are rendering without using requestAnimationFrame for the main loop. You should use 0 for the frame rate in emscripten_set_main_loop in order to use requestAnimationFrame, as that can greatly improve your frame rates!");Browser.mainLoop.method = ""; + }Browser.mainLoop.runIter(browserIterationFunc);if (thisMainLoopId < Browser.mainLoop.currentlyRunningMainloop) return;if (typeof SDL === "object" && SDL.audio && SDL.audio.queueNewAudioData) SDL.audio.queueNewAudioData();Browser.mainLoop.scheduler(); + };if (!noSetTiming) { + if (fps && fps > 0) _emscripten_set_main_loop_timing(0, 1e3 / fps);else _emscripten_set_main_loop_timing(1, 1);Browser.mainLoop.scheduler(); + }if (simulateInfiniteLoop) { + throw "SimulateInfiniteLoop"; + } + }var Browser = { mainLoop: { scheduler: null, method: "", currentlyRunningMainloop: 0, func: null, arg: 0, timingMode: 0, timingValue: 0, currentFrameNumber: 0, queue: [], pause: function () { + Browser.mainLoop.scheduler = null;Browser.mainLoop.currentlyRunningMainloop++; + }, resume: function () { + Browser.mainLoop.currentlyRunningMainloop++;var timingMode = Browser.mainLoop.timingMode;var timingValue = Browser.mainLoop.timingValue;var func = Browser.mainLoop.func;Browser.mainLoop.func = null;_emscripten_set_main_loop(func, 0, false, Browser.mainLoop.arg, true);_emscripten_set_main_loop_timing(timingMode, timingValue);Browser.mainLoop.scheduler(); + }, updateStatus: function () { + if (Module["setStatus"]) { + var message = Module["statusMessage"] || "Please wait...";var remaining = Browser.mainLoop.remainingBlockers;var expected = Browser.mainLoop.expectedBlockers;if (remaining) { + if (remaining < expected) { + Module["setStatus"](message + " (" + (expected - remaining) + "/" + expected + ")"); + } else { + Module["setStatus"](message); + } + } else { + Module["setStatus"](""); + } + } + }, runIter: function (func) { + if (ABORT) return;if (Module["preMainLoop"]) { + var preRet = Module["preMainLoop"]();if (preRet === false) { + return; + } + }try { + func(); + } catch (e) { + if (e instanceof ExitStatus) { + return; + } else { + if (e && typeof e === "object" && e.stack) Module.printErr("exception thrown: " + [e, e.stack]);throw e; + } + }if (Module["postMainLoop"]) Module["postMainLoop"](); + } }, isFullscreen: false, pointerLock: false, moduleContextCreatedCallbacks: [], workers: [], init: function () { + if (!Module["preloadPlugins"]) Module["preloadPlugins"] = [];if (Browser.initted) return;Browser.initted = true;try { + new Blob();Browser.hasBlobConstructor = true; + } catch (e) { + Browser.hasBlobConstructor = false;console.log("warning: no blob constructor, cannot create blobs with mimetypes"); + }Browser.BlobBuilder = typeof MozBlobBuilder != "undefined" ? MozBlobBuilder : typeof WebKitBlobBuilder != "undefined" ? WebKitBlobBuilder : !Browser.hasBlobConstructor ? console.log("warning: no BlobBuilder") : null;Browser.URLObject = typeof window != "undefined" ? window.URL ? window.URL : window.webkitURL : undefined;if (!Module.noImageDecoding && typeof Browser.URLObject === "undefined") { + console.log("warning: Browser does not support creating object URLs. Built-in browser image decoding will not be available.");Module.noImageDecoding = true; + }var imagePlugin = {};imagePlugin["canHandle"] = function imagePlugin_canHandle(name) { + return !Module.noImageDecoding && /\.(jpg|jpeg|png|bmp)$/i.test(name); + };imagePlugin["handle"] = function imagePlugin_handle(byteArray, name, onload, onerror) { + var b = null;if (Browser.hasBlobConstructor) { + try { + b = new Blob([byteArray], { type: Browser.getMimetype(name) });if (b.size !== byteArray.length) { + b = new Blob([new Uint8Array(byteArray).buffer], { type: Browser.getMimetype(name) }); + } + } catch (e) { + Runtime.warnOnce("Blob constructor present but fails: " + e + "; falling back to blob builder"); + } + }if (!b) { + var bb = new Browser.BlobBuilder();bb.append(new Uint8Array(byteArray).buffer);b = bb.getBlob(); + }var url = Browser.URLObject.createObjectURL(b);var img = new Image();img.onload = function img_onload() { + assert(img.complete, "Image " + name + " could not be decoded");var canvas = document.createElement("canvas");canvas.width = img.width;canvas.height = img.height;var ctx = canvas.getContext("2d");ctx.drawImage(img, 0, 0);Module["preloadedImages"][name] = canvas;Browser.URLObject.revokeObjectURL(url);if (onload) onload(byteArray); + };img.onerror = function img_onerror(event) { + console.log("Image " + url + " could not be decoded");if (onerror) onerror(); + };img.src = url; + };Module["preloadPlugins"].push(imagePlugin);var audioPlugin = {};audioPlugin["canHandle"] = function audioPlugin_canHandle(name) { + return !Module.noAudioDecoding && name.substr(-4) in { ".ogg": 1, ".wav": 1, ".mp3": 1 }; + };audioPlugin["handle"] = function audioPlugin_handle(byteArray, name, onload, onerror) { + var done = false;function finish(audio) { + if (done) return;done = true;Module["preloadedAudios"][name] = audio;if (onload) onload(byteArray); + }function fail() { + if (done) return;done = true;Module["preloadedAudios"][name] = new Audio();if (onerror) onerror(); + }if (Browser.hasBlobConstructor) { + try { + var b = new Blob([byteArray], { type: Browser.getMimetype(name) }); + } catch (e) { + return fail(); + }var url = Browser.URLObject.createObjectURL(b);var audio = new Audio();audio.addEventListener("canplaythrough", function () { + finish(audio); + }, false);audio.onerror = function audio_onerror(event) { + if (done) return;console.log("warning: browser could not fully decode audio " + name + ", trying slower base64 approach");function encode64(data) { + var BASE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";var PAD = "=";var ret = "";var leftchar = 0;var leftbits = 0;for (var i = 0; i < data.length; i++) { + leftchar = leftchar << 8 | data[i];leftbits += 8;while (leftbits >= 6) { + var curr = leftchar >> leftbits - 6 & 63;leftbits -= 6;ret += BASE[curr]; + } + }if (leftbits == 2) { + ret += BASE[(leftchar & 3) << 4];ret += PAD + PAD; + } else if (leftbits == 4) { + ret += BASE[(leftchar & 15) << 2];ret += PAD; + }return ret; + }audio.src = "data:audio/x-" + name.substr(-3) + ";base64," + encode64(byteArray);finish(audio); + };audio.src = url;Browser.safeSetTimeout(function () { + finish(audio); + }, 1e4); + } else { + return fail(); + } + };Module["preloadPlugins"].push(audioPlugin);function pointerLockChange() { + Browser.pointerLock = document["pointerLockElement"] === Module["canvas"] || document["mozPointerLockElement"] === Module["canvas"] || document["webkitPointerLockElement"] === Module["canvas"] || document["msPointerLockElement"] === Module["canvas"]; + }var canvas = Module["canvas"];if (canvas) { + canvas.requestPointerLock = canvas["requestPointerLock"] || canvas["mozRequestPointerLock"] || canvas["webkitRequestPointerLock"] || canvas["msRequestPointerLock"] || function () {};canvas.exitPointerLock = document["exitPointerLock"] || document["mozExitPointerLock"] || document["webkitExitPointerLock"] || document["msExitPointerLock"] || function () {};canvas.exitPointerLock = canvas.exitPointerLock.bind(document);document.addEventListener("pointerlockchange", pointerLockChange, false);document.addEventListener("mozpointerlockchange", pointerLockChange, false);document.addEventListener("webkitpointerlockchange", pointerLockChange, false);document.addEventListener("mspointerlockchange", pointerLockChange, false);if (Module["elementPointerLock"]) { + canvas.addEventListener("click", function (ev) { + if (!Browser.pointerLock && Module["canvas"].requestPointerLock) { + Module["canvas"].requestPointerLock();ev.preventDefault(); + } + }, false); + } + } + }, createContext: function (canvas, useWebGL, setInModule, webGLContextAttributes) { + if (useWebGL && Module.ctx && canvas == Module.canvas) return Module.ctx;var ctx;var contextHandle;if (useWebGL) { + var contextAttributes = { antialias: false, alpha: false };if (webGLContextAttributes) { + for (var attribute in webGLContextAttributes) { + contextAttributes[attribute] = webGLContextAttributes[attribute]; + } + }contextHandle = GL.createContext(canvas, contextAttributes);if (contextHandle) { + ctx = GL.getContext(contextHandle).GLctx; + } + } else { + ctx = canvas.getContext("2d"); + }if (!ctx) return null;if (setInModule) { + if (!useWebGL) assert(typeof GLctx === "undefined", "cannot set in module if GLctx is used, but we are a non-GL context that would replace it");Module.ctx = ctx;if (useWebGL) GL.makeContextCurrent(contextHandle);Module.useWebGL = useWebGL;Browser.moduleContextCreatedCallbacks.forEach(function (callback) { + callback(); + });Browser.init(); + }return ctx; + }, destroyContext: function (canvas, useWebGL, setInModule) {}, fullscreenHandlersInstalled: false, lockPointer: undefined, resizeCanvas: undefined, requestFullscreen: function (lockPointer, resizeCanvas, vrDevice) { + Browser.lockPointer = lockPointer;Browser.resizeCanvas = resizeCanvas;Browser.vrDevice = vrDevice;if (typeof Browser.lockPointer === "undefined") Browser.lockPointer = true;if (typeof Browser.resizeCanvas === "undefined") Browser.resizeCanvas = false;if (typeof Browser.vrDevice === "undefined") Browser.vrDevice = null;var canvas = Module["canvas"];function fullscreenChange() { + Browser.isFullscreen = false;var canvasContainer = canvas.parentNode;if ((document["fullscreenElement"] || document["mozFullScreenElement"] || document["msFullscreenElement"] || document["webkitFullscreenElement"] || document["webkitCurrentFullScreenElement"]) === canvasContainer) { + canvas.exitFullscreen = document["exitFullscreen"] || document["cancelFullScreen"] || document["mozCancelFullScreen"] || document["msExitFullscreen"] || document["webkitCancelFullScreen"] || function () {};canvas.exitFullscreen = canvas.exitFullscreen.bind(document);if (Browser.lockPointer) canvas.requestPointerLock();Browser.isFullscreen = true;if (Browser.resizeCanvas) Browser.setFullscreenCanvasSize(); + } else { + canvasContainer.parentNode.insertBefore(canvas, canvasContainer);canvasContainer.parentNode.removeChild(canvasContainer);if (Browser.resizeCanvas) Browser.setWindowedCanvasSize(); + }if (Module["onFullScreen"]) Module["onFullScreen"](Browser.isFullscreen);if (Module["onFullscreen"]) Module["onFullscreen"](Browser.isFullscreen);Browser.updateCanvasDimensions(canvas); + }if (!Browser.fullscreenHandlersInstalled) { + Browser.fullscreenHandlersInstalled = true;document.addEventListener("fullscreenchange", fullscreenChange, false);document.addEventListener("mozfullscreenchange", fullscreenChange, false);document.addEventListener("webkitfullscreenchange", fullscreenChange, false);document.addEventListener("MSFullscreenChange", fullscreenChange, false); + }var canvasContainer = document.createElement("div");canvas.parentNode.insertBefore(canvasContainer, canvas);canvasContainer.appendChild(canvas);canvasContainer.requestFullscreen = canvasContainer["requestFullscreen"] || canvasContainer["mozRequestFullScreen"] || canvasContainer["msRequestFullscreen"] || (canvasContainer["webkitRequestFullscreen"] ? function () { + canvasContainer["webkitRequestFullscreen"](Element["ALLOW_KEYBOARD_INPUT"]); + } : null) || (canvasContainer["webkitRequestFullScreen"] ? function () { + canvasContainer["webkitRequestFullScreen"](Element["ALLOW_KEYBOARD_INPUT"]); + } : null);if (vrDevice) { + canvasContainer.requestFullscreen({ vrDisplay: vrDevice }); + } else { + canvasContainer.requestFullscreen(); + } + }, requestFullScreen: function (lockPointer, resizeCanvas, vrDevice) { + Module.printErr("Browser.requestFullScreen() is deprecated. Please call Browser.requestFullscreen instead.");Browser.requestFullScreen = function (lockPointer, resizeCanvas, vrDevice) { + return Browser.requestFullscreen(lockPointer, resizeCanvas, vrDevice); + };return Browser.requestFullscreen(lockPointer, resizeCanvas, vrDevice); + }, nextRAF: 0, fakeRequestAnimationFrame: function (func) { + var now = Date.now();if (Browser.nextRAF === 0) { + Browser.nextRAF = now + 1e3 / 60; + } else { + while (now + 2 >= Browser.nextRAF) { + Browser.nextRAF += 1e3 / 60; + } + }var delay = Math.max(Browser.nextRAF - now, 0);setTimeout(func, delay); + }, requestAnimationFrame: function requestAnimationFrame(func) { + if (typeof window === "undefined") { + Browser.fakeRequestAnimationFrame(func); + } else { + if (!window.requestAnimationFrame) { + window.requestAnimationFrame = window["requestAnimationFrame"] || window["mozRequestAnimationFrame"] || window["webkitRequestAnimationFrame"] || window["msRequestAnimationFrame"] || window["oRequestAnimationFrame"] || Browser.fakeRequestAnimationFrame; + }window.requestAnimationFrame(func); + } + }, safeCallback: function (func) { + return function () { + if (!ABORT) return func.apply(null, arguments); + }; + }, allowAsyncCallbacks: true, queuedAsyncCallbacks: [], pauseAsyncCallbacks: function () { + Browser.allowAsyncCallbacks = false; + }, resumeAsyncCallbacks: function () { + Browser.allowAsyncCallbacks = true;if (Browser.queuedAsyncCallbacks.length > 0) { + var callbacks = Browser.queuedAsyncCallbacks;Browser.queuedAsyncCallbacks = [];callbacks.forEach(function (func) { + func(); + }); + } + }, safeRequestAnimationFrame: function (func) { + return Browser.requestAnimationFrame(function () { + if (ABORT) return;if (Browser.allowAsyncCallbacks) { + func(); + } else { + Browser.queuedAsyncCallbacks.push(func); + } + }); + }, safeSetTimeout: function (func, timeout) { + Module["noExitRuntime"] = true;return setTimeout(function () { + if (ABORT) return;if (Browser.allowAsyncCallbacks) { + func(); + } else { + Browser.queuedAsyncCallbacks.push(func); + } + }, timeout); + }, safeSetInterval: function (func, timeout) { + Module["noExitRuntime"] = true;return setInterval(function () { + if (ABORT) return;if (Browser.allowAsyncCallbacks) { + func(); + } + }, timeout); + }, getMimetype: function (name) { + return { "jpg": "image/jpeg", "jpeg": "image/jpeg", "png": "image/png", "bmp": "image/bmp", "ogg": "audio/ogg", "wav": "audio/wav", "mp3": "audio/mpeg" }[name.substr(name.lastIndexOf(".") + 1)]; + }, getUserMedia: function (func) { + if (!window.getUserMedia) { + window.getUserMedia = navigator["getUserMedia"] || navigator["mozGetUserMedia"]; + }window.getUserMedia(func); + }, getMovementX: function (event) { + return event["movementX"] || event["mozMovementX"] || event["webkitMovementX"] || 0; + }, getMovementY: function (event) { + return event["movementY"] || event["mozMovementY"] || event["webkitMovementY"] || 0; + }, getMouseWheelDelta: function (event) { + var delta = 0;switch (event.type) {case "DOMMouseScroll": + delta = event.detail;break;case "mousewheel": + delta = event.wheelDelta;break;case "wheel": + delta = event["deltaY"];break;default: + throw "unrecognized mouse wheel event: " + event.type;}return delta; + }, mouseX: 0, mouseY: 0, mouseMovementX: 0, mouseMovementY: 0, touches: {}, lastTouches: {}, calculateMouseEvent: function (event) { + if (Browser.pointerLock) { + if (event.type != "mousemove" && "mozMovementX" in event) { + Browser.mouseMovementX = Browser.mouseMovementY = 0; + } else { + Browser.mouseMovementX = Browser.getMovementX(event);Browser.mouseMovementY = Browser.getMovementY(event); + }if (typeof SDL != "undefined") { + Browser.mouseX = SDL.mouseX + Browser.mouseMovementX;Browser.mouseY = SDL.mouseY + Browser.mouseMovementY; + } else { + Browser.mouseX += Browser.mouseMovementX;Browser.mouseY += Browser.mouseMovementY; + } + } else { + var rect = Module["canvas"].getBoundingClientRect();var cw = Module["canvas"].width;var ch = Module["canvas"].height;var scrollX = typeof window.scrollX !== "undefined" ? window.scrollX : window.pageXOffset;var scrollY = typeof window.scrollY !== "undefined" ? window.scrollY : window.pageYOffset;if (event.type === "touchstart" || event.type === "touchend" || event.type === "touchmove") { + var touch = event.touch;if (touch === undefined) { + return; + }var adjustedX = touch.pageX - (scrollX + rect.left);var adjustedY = touch.pageY - (scrollY + rect.top);adjustedX = adjustedX * (cw / rect.width);adjustedY = adjustedY * (ch / rect.height);var coords = { x: adjustedX, y: adjustedY };if (event.type === "touchstart") { + Browser.lastTouches[touch.identifier] = coords;Browser.touches[touch.identifier] = coords; + } else if (event.type === "touchend" || event.type === "touchmove") { + var last = Browser.touches[touch.identifier];if (!last) last = coords;Browser.lastTouches[touch.identifier] = last;Browser.touches[touch.identifier] = coords; + }return; + }var x = event.pageX - (scrollX + rect.left);var y = event.pageY - (scrollY + rect.top);x = x * (cw / rect.width);y = y * (ch / rect.height);Browser.mouseMovementX = x - Browser.mouseX;Browser.mouseMovementY = y - Browser.mouseY;Browser.mouseX = x;Browser.mouseY = y; + } + }, asyncLoad: function (url, onload, onerror, noRunDep) { + var dep = !noRunDep ? getUniqueRunDependency("al " + url) : "";Module["readAsync"](url, function (arrayBuffer) { + assert(arrayBuffer, 'Loading data file "' + url + '" failed (no arrayBuffer).');onload(new Uint8Array(arrayBuffer));if (dep) removeRunDependency(dep); + }, function (event) { + if (onerror) { + onerror(); + } else { + throw 'Loading data file "' + url + '" failed.'; + } + });if (dep) addRunDependency(dep); + }, resizeListeners: [], updateResizeListeners: function () { + var canvas = Module["canvas"];Browser.resizeListeners.forEach(function (listener) { + listener(canvas.width, canvas.height); + }); + }, setCanvasSize: function (width, height, noUpdates) { + var canvas = Module["canvas"];Browser.updateCanvasDimensions(canvas, width, height);if (!noUpdates) Browser.updateResizeListeners(); + }, windowedWidth: 0, windowedHeight: 0, setFullscreenCanvasSize: function () { + if (typeof SDL != "undefined") { + var flags = HEAPU32[SDL.screen + Runtime.QUANTUM_SIZE * 0 >> 2];flags = flags | 8388608;HEAP32[SDL.screen + Runtime.QUANTUM_SIZE * 0 >> 2] = flags; + }Browser.updateResizeListeners(); + }, setWindowedCanvasSize: function () { + if (typeof SDL != "undefined") { + var flags = HEAPU32[SDL.screen + Runtime.QUANTUM_SIZE * 0 >> 2];flags = flags & ~8388608;HEAP32[SDL.screen + Runtime.QUANTUM_SIZE * 0 >> 2] = flags; + }Browser.updateResizeListeners(); + }, updateCanvasDimensions: function (canvas, wNative, hNative) { + if (wNative && hNative) { + canvas.widthNative = wNative;canvas.heightNative = hNative; + } else { + wNative = canvas.widthNative;hNative = canvas.heightNative; + }var w = wNative;var h = hNative;if (Module["forcedAspectRatio"] && Module["forcedAspectRatio"] > 0) { + if (w / h < Module["forcedAspectRatio"]) { + w = Math.round(h * Module["forcedAspectRatio"]); + } else { + h = Math.round(w / Module["forcedAspectRatio"]); + } + }if ((document["fullscreenElement"] || document["mozFullScreenElement"] || document["msFullscreenElement"] || document["webkitFullscreenElement"] || document["webkitCurrentFullScreenElement"]) === canvas.parentNode && typeof screen != "undefined") { + var factor = Math.min(screen.width / w, screen.height / h);w = Math.round(w * factor);h = Math.round(h * factor); + }if (Browser.resizeCanvas) { + if (canvas.width != w) canvas.width = w;if (canvas.height != h) canvas.height = h;if (typeof canvas.style != "undefined") { + canvas.style.removeProperty("width");canvas.style.removeProperty("height"); + } + } else { + if (canvas.width != wNative) canvas.width = wNative;if (canvas.height != hNative) canvas.height = hNative;if (typeof canvas.style != "undefined") { + if (w != wNative || h != hNative) { + canvas.style.setProperty("width", w + "px", "important");canvas.style.setProperty("height", h + "px", "important"); + } else { + canvas.style.removeProperty("width");canvas.style.removeProperty("height"); + } + } + } + }, wgetRequests: {}, nextWgetRequestHandle: 0, getNextWgetRequestHandle: function () { + var handle = Browser.nextWgetRequestHandle;Browser.nextWgetRequestHandle++;return handle; + } };var SYSCALLS = { varargs: 0, get: function (varargs) { + SYSCALLS.varargs += 4;var ret = HEAP32[SYSCALLS.varargs - 4 >> 2];return ret; + }, getStr: function () { + var ret = Pointer_stringify(SYSCALLS.get());return ret; + }, get64: function () { + var low = SYSCALLS.get(), + high = SYSCALLS.get();if (low >= 0) assert(high === 0);else assert(high === -1);return low; + }, getZero: function () { + assert(SYSCALLS.get() === 0); + } };function ___syscall6(which, varargs) { + SYSCALLS.varargs = varargs;try { + var stream = SYSCALLS.getStreamFromFD();FS.close(stream);return 0; + } catch (e) { + if (typeof FS === "undefined" || !(e instanceof FS.ErrnoError)) abort(e);return -e.errno; + } + }function ___syscall54(which, varargs) { + SYSCALLS.varargs = varargs;try { + return 0; + } catch (e) { + if (typeof FS === "undefined" || !(e instanceof FS.ErrnoError)) abort(e);return -e.errno; + } + }function _typeModule(self) { + var structureList = [[0, 1, "X"], [1, 1, "const X"], [128, 1, "X *"], [256, 1, "X &"], [384, 1, "X &&"], [512, 1, "std::shared_ptr<X>"], [640, 1, "std::unique_ptr<X>"], [5120, 1, "std::vector<X>"], [6144, 2, "std::array<X, Y>"], [9216, -1, "std::function<X (Y)>"]];function applyStructure(outerName, outerFlags, innerName, innerFlags, param, flip) { + if (outerFlags == 1) { + var ref = innerFlags & 896;if (ref == 128 || ref == 256 || ref == 384) outerName = "X const"; + }var name;if (flip) { + name = innerName.replace("X", outerName).replace("Y", param); + } else { + name = outerName.replace("X", innerName).replace("Y", param); + }return name.replace(/([*&]) (?=[*&])/g, "$1"); + }function reportProblem(problem, id, kind, structureType, place) { + throw new Error(problem + " type " + kind.replace("X", id + "?") + (structureType ? " with flag " + structureType : "") + " in " + place); + }function getComplexType(id, constructType, getType, queryType, place, kind, prevStructure, depth) { + if (kind === void 0) { + kind = "X"; + }if (depth === void 0) { + depth = 1; + }var result = getType(id);if (result) return result;var query = queryType(id);var structureType = query.placeholderFlag;var structure = structureList[structureType];if (prevStructure && structure) { + kind = applyStructure(prevStructure[2], prevStructure[0], kind, structure[0], "?", true); + }var problem;if (structureType == 0) problem = "Unbound";if (structureType >= 10) problem = "Corrupt";if (depth > 20) problem = "Deeply nested";if (problem) reportProblem(problem, id, kind, structureType, place || "?");var subId = query.paramList[0];var subType = getComplexType(subId, constructType, getType, queryType, place, kind, structure, depth + 1);var srcSpec;var spec = { flags: structure[0], id: id, name: "", paramList: [subType] };var argList = [];var structureParam = "?";switch (query.placeholderFlag) {case 1: + srcSpec = subType.spec;break;case 2: + if ((subType.flags & 15360) == 1024 && subType.spec.ptrSize == 1) { + spec.flags = 7168;break; + }case 3:case 6:case 5: + srcSpec = subType.spec;if ((subType.flags & 15360) != 2048) {}break;case 8: + structureParam = "" + query.paramList[1];spec.paramList.push(query.paramList[1]);break;case 9: + for (var _i = 0, _a = query.paramList[1]; _i < _a.length; _i++) { + var paramId = _a[_i];var paramType = getComplexType(paramId, constructType, getType, queryType, place, kind, structure, depth + 1);argList.push(paramType.name);spec.paramList.push(paramType); + }structureParam = argList.join(", ");break;default: + break;}spec.name = applyStructure(structure[2], structure[0], subType.name, subType.flags, structureParam);if (srcSpec) { + for (var _b = 0, _c = Object.keys(srcSpec); _b < _c.length; _b++) { + var key = _c[_b];spec[key] = spec[key] || srcSpec[key]; + }spec.flags |= srcSpec.flags; + }return makeType(constructType, spec); + }function makeType(constructType, spec) { + var flags = spec.flags;var refKind = flags & 896;var kind = flags & 15360;if (!spec.name && kind == 1024) { + if (spec.ptrSize == 1) { + spec.name = (flags & 16 ? "" : (flags & 8 ? "un" : "") + "signed ") + "char"; + } else { + spec.name = (flags & 8 ? "u" : "") + (flags & 32 ? "float" : "int") + (spec.ptrSize * 8 + "_t"); + } + }if (spec.ptrSize == 8 && !(flags & 32)) kind = 64;if (kind == 2048) { + if (refKind == 512 || refKind == 640) { + kind = 4096; + } else if (refKind) kind = 3072; + }return constructType(kind, spec); + }var Type = function () { + function Type(spec) { + this.id = spec.id;this.name = spec.name;this.flags = spec.flags;this.spec = spec; + }Type.prototype.toString = function () { + return this.name; + };return Type; + }();var output = { Type: Type, getComplexType: getComplexType, makeType: makeType, structureList: structureList };self.output = output;return self.output || output; + }function __nbind_register_type(id, namePtr) { + var name = _nbind.readAsciiString(namePtr);var spec = { flags: 10240, id: id, name: name };_nbind.makeType(_nbind.constructType, spec); + }function __nbind_register_callback_signature(typeListPtr, typeCount) { + var typeList = _nbind.readTypeIdList(typeListPtr, typeCount);var num = _nbind.callbackSignatureList.length;_nbind.callbackSignatureList[num] = _nbind.makeJSCaller(typeList);return num; + }function __extends(Class, Parent) { + for (var key in Parent) if (Parent.hasOwnProperty(key)) Class[key] = Parent[key];function Base() { + this.constructor = Class; + }Base.prototype = Parent.prototype;Class.prototype = new Base(); + }function __nbind_register_class(idListPtr, policyListPtr, superListPtr, upcastListPtr, superCount, destructorPtr, namePtr) { + var name = _nbind.readAsciiString(namePtr);var policyTbl = _nbind.readPolicyList(policyListPtr);var idList = HEAPU32.subarray(idListPtr / 4, idListPtr / 4 + 2);var spec = { flags: 2048 | (policyTbl["Value"] ? 2 : 0), id: idList[0], name: name };var bindClass = _nbind.makeType(_nbind.constructType, spec);bindClass.ptrType = _nbind.getComplexType(idList[1], _nbind.constructType, _nbind.getType, _nbind.queryType);bindClass.destroy = _nbind.makeMethodCaller(bindClass.ptrType, { boundID: spec.id, flags: 0, name: "destroy", num: 0, ptr: destructorPtr, title: bindClass.name + ".free", typeList: ["void", "uint32_t", "uint32_t"] });if (superCount) { + bindClass.superIdList = Array.prototype.slice.call(HEAPU32.subarray(superListPtr / 4, superListPtr / 4 + superCount));bindClass.upcastList = Array.prototype.slice.call(HEAPU32.subarray(upcastListPtr / 4, upcastListPtr / 4 + superCount)); + }Module[bindClass.name] = bindClass.makeBound(policyTbl);_nbind.BindClass.list.push(bindClass); + }function _removeAccessorPrefix(name) { + var prefixMatcher = /^[Gg]et_?([A-Z]?([A-Z]?))/;return name.replace(prefixMatcher, function (match, initial, second) { + return second ? initial : initial.toLowerCase(); + }); + }function __nbind_register_function(boundID, policyListPtr, typeListPtr, typeCount, ptr, direct, signatureType, namePtr, num, flags) { + var bindClass = _nbind.getType(boundID);var policyTbl = _nbind.readPolicyList(policyListPtr);var typeList = _nbind.readTypeIdList(typeListPtr, typeCount);var specList;if (signatureType == 5) { + specList = [{ direct: ptr, name: "__nbindConstructor", ptr: 0, title: bindClass.name + " constructor", typeList: ["uint32_t"].concat(typeList.slice(1)) }, { direct: direct, name: "__nbindValueConstructor", ptr: 0, title: bindClass.name + " value constructor", typeList: ["void", "uint32_t"].concat(typeList.slice(1)) }]; + } else { + var name_1 = _nbind.readAsciiString(namePtr);var title = (bindClass.name && bindClass.name + ".") + name_1;if (signatureType == 3 || signatureType == 4) { + name_1 = _removeAccessorPrefix(name_1); + }specList = [{ boundID: boundID, direct: direct, name: name_1, ptr: ptr, title: title, typeList: typeList }]; + }for (var _i = 0, specList_1 = specList; _i < specList_1.length; _i++) { + var spec = specList_1[_i];spec.signatureType = signatureType;spec.policyTbl = policyTbl;spec.num = num;spec.flags = flags;bindClass.addMethod(spec); + } + }function _nbind_value(name, proto) { + if (!_nbind.typeNameTbl[name]) _nbind.throwError("Unknown value type " + name);Module["NBind"].bind_value(name, proto);_defineHidden(_nbind.typeNameTbl[name].proto.prototype.__nbindValueConstructor)(proto.prototype, "__nbindValueConstructor"); + }Module["_nbind_value"] = _nbind_value;function __nbind_get_value_object(num, ptr) { + var obj = _nbind.popValue(num);if (!obj.fromJS) { + throw new Error("Object " + obj + " has no fromJS function"); + }obj.fromJS(function () { + obj.__nbindValueConstructor.apply(this, Array.prototype.concat.apply([ptr], arguments)); + }); + }function _emscripten_memcpy_big(dest, src, num) { + HEAPU8.set(HEAPU8.subarray(src, src + num), dest);return dest; + }function __nbind_register_primitive(id, size, flags) { + var spec = { flags: 1024 | flags, id: id, ptrSize: size };_nbind.makeType(_nbind.constructType, spec); + }var cttz_i8 = allocate([8, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0], "i8", ALLOC_STATIC);function ___setErrNo(value) { + if (Module["___errno_location"]) HEAP32[Module["___errno_location"]() >> 2] = value;return value; + }function _llvm_stacksave() { + var self = _llvm_stacksave;if (!self.LLVM_SAVEDSTACKS) { + self.LLVM_SAVEDSTACKS = []; + }self.LLVM_SAVEDSTACKS.push(Runtime.stackSave());return self.LLVM_SAVEDSTACKS.length - 1; + }function ___syscall140(which, varargs) { + SYSCALLS.varargs = varargs;try { + var stream = SYSCALLS.getStreamFromFD(), + offset_high = SYSCALLS.get(), + offset_low = SYSCALLS.get(), + result = SYSCALLS.get(), + whence = SYSCALLS.get();var offset = offset_low;FS.llseek(stream, offset, whence);HEAP32[result >> 2] = stream.position;if (stream.getdents && offset === 0 && whence === 0) stream.getdents = null;return 0; + } catch (e) { + if (typeof FS === "undefined" || !(e instanceof FS.ErrnoError)) abort(e);return -e.errno; + } + }function ___syscall146(which, varargs) { + SYSCALLS.varargs = varargs;try { + var stream = SYSCALLS.get(), + iov = SYSCALLS.get(), + iovcnt = SYSCALLS.get();var ret = 0;if (!___syscall146.buffer) { + ___syscall146.buffers = [null, [], []];___syscall146.printChar = function (stream, curr) { + var buffer = ___syscall146.buffers[stream];assert(buffer);if (curr === 0 || curr === 10) { + (stream === 1 ? Module["print"] : Module["printErr"])(UTF8ArrayToString(buffer, 0));buffer.length = 0; + } else { + buffer.push(curr); + } + }; + }for (var i = 0; i < iovcnt; i++) { + var ptr = HEAP32[iov + i * 8 >> 2];var len = HEAP32[iov + (i * 8 + 4) >> 2];for (var j = 0; j < len; j++) { + ___syscall146.printChar(stream, HEAPU8[ptr + j]); + }ret += len; + }return ret; + } catch (e) { + if (typeof FS === "undefined" || !(e instanceof FS.ErrnoError)) abort(e);return -e.errno; + } + }function __nbind_finish() { + for (var _i = 0, _a = _nbind.BindClass.list; _i < _a.length; _i++) { + var bindClass = _a[_i];bindClass.finish(); + } + }var ___dso_handle = STATICTOP;STATICTOP += 16;(function (_nbind) { + var typeIdTbl = {};_nbind.typeNameTbl = {};var Pool = function () { + function Pool() {}Pool.lalloc = function (size) { + size = size + 7 & ~7;var used = HEAPU32[Pool.usedPtr];if (size > Pool.pageSize / 2 || size > Pool.pageSize - used) { + var NBind = _nbind.typeNameTbl["NBind"].proto;return NBind.lalloc(size); + } else { + HEAPU32[Pool.usedPtr] = used + size;return Pool.rootPtr + used; + } + };Pool.lreset = function (used, page) { + var topPage = HEAPU32[Pool.pagePtr];if (topPage) { + var NBind = _nbind.typeNameTbl["NBind"].proto;NBind.lreset(used, page); + } else { + HEAPU32[Pool.usedPtr] = used; + } + };return Pool; + }();_nbind.Pool = Pool;function constructType(kind, spec) { + var construct = kind == 10240 ? _nbind.makeTypeNameTbl[spec.name] || _nbind.BindType : _nbind.makeTypeKindTbl[kind];var bindType = new construct(spec);typeIdTbl[spec.id] = bindType;_nbind.typeNameTbl[spec.name] = bindType;return bindType; + }_nbind.constructType = constructType;function getType(id) { + return typeIdTbl[id]; + }_nbind.getType = getType;function queryType(id) { + var placeholderFlag = HEAPU8[id];var paramCount = _nbind.structureList[placeholderFlag][1];id /= 4;if (paramCount < 0) { + ++id;paramCount = HEAPU32[id] + 1; + }var paramList = Array.prototype.slice.call(HEAPU32.subarray(id + 1, id + 1 + paramCount));if (placeholderFlag == 9) { + paramList = [paramList[0], paramList.slice(1)]; + }return { paramList: paramList, placeholderFlag: placeholderFlag }; + }_nbind.queryType = queryType;function getTypes(idList, place) { + return idList.map(function (id) { + return typeof id == "number" ? _nbind.getComplexType(id, constructType, getType, queryType, place) : _nbind.typeNameTbl[id]; + }); + }_nbind.getTypes = getTypes;function readTypeIdList(typeListPtr, typeCount) { + return Array.prototype.slice.call(HEAPU32, typeListPtr / 4, typeListPtr / 4 + typeCount); + }_nbind.readTypeIdList = readTypeIdList;function readAsciiString(ptr) { + var endPtr = ptr;while (HEAPU8[endPtr++]);return String.fromCharCode.apply("", HEAPU8.subarray(ptr, endPtr - 1)); + }_nbind.readAsciiString = readAsciiString;function readPolicyList(policyListPtr) { + var policyTbl = {};if (policyListPtr) { + while (1) { + var namePtr = HEAPU32[policyListPtr / 4];if (!namePtr) break;policyTbl[readAsciiString(namePtr)] = true;policyListPtr += 4; + } + }return policyTbl; + }_nbind.readPolicyList = readPolicyList;function getDynCall(typeList, name) { + var mangleMap = { float32_t: "d", float64_t: "d", int64_t: "d", uint64_t: "d", "void": "v" };var signature = typeList.map(function (type) { + return mangleMap[type.name] || "i"; + }).join("");var dynCall = Module["dynCall_" + signature];if (!dynCall) { + throw new Error("dynCall_" + signature + " not found for " + name + "(" + typeList.map(function (type) { + return type.name; + }).join(", ") + ")"); + }return dynCall; + }_nbind.getDynCall = getDynCall;function addMethod(obj, name, func, arity) { + var overload = obj[name];if (obj.hasOwnProperty(name) && overload) { + if (overload.arity || overload.arity === 0) { + overload = _nbind.makeOverloader(overload, overload.arity);obj[name] = overload; + }overload.addMethod(func, arity); + } else { + func.arity = arity;obj[name] = func; + } + }_nbind.addMethod = addMethod;function throwError(message) { + throw new Error(message); + }_nbind.throwError = throwError;_nbind.bigEndian = false;var _a = _typeModule(_typeModule); _nbind.Type = _a.Type, _nbind.makeType = _a.makeType, _nbind.getComplexType = _a.getComplexType, _nbind.structureList = _a.structureList;var BindType = function (_super) { + __extends(BindType, _super);function BindType() { + var _this = _super !== null && _super.apply(this, arguments) || this;_this.heap = HEAPU32;_this.ptrSize = 4;return _this; + }BindType.prototype.needsWireRead = function (policyTbl) { + return !!this.wireRead || !!this.makeWireRead; + };BindType.prototype.needsWireWrite = function (policyTbl) { + return !!this.wireWrite || !!this.makeWireWrite; + };return BindType; + }(_nbind.Type);_nbind.BindType = BindType;var PrimitiveType = function (_super) { + __extends(PrimitiveType, _super);function PrimitiveType(spec) { + var _this = _super.call(this, spec) || this;var heapTbl = spec.flags & 32 ? { 32: HEAPF32, 64: HEAPF64 } : spec.flags & 8 ? { 8: HEAPU8, 16: HEAPU16, 32: HEAPU32 } : { 8: HEAP8, 16: HEAP16, 32: HEAP32 };_this.heap = heapTbl[spec.ptrSize * 8];_this.ptrSize = spec.ptrSize;return _this; + }PrimitiveType.prototype.needsWireWrite = function (policyTbl) { + return !!policyTbl && !!policyTbl["Strict"]; + };PrimitiveType.prototype.makeWireWrite = function (expr, policyTbl) { + return policyTbl && policyTbl["Strict"] && function (arg) { + if (typeof arg == "number") return arg;throw new Error("Type mismatch"); + }; + };return PrimitiveType; + }(BindType);_nbind.PrimitiveType = PrimitiveType;function pushCString(str, policyTbl) { + if (str === null || str === undefined) { + if (policyTbl && policyTbl["Nullable"]) { + return 0; + } else throw new Error("Type mismatch"); + }if (policyTbl && policyTbl["Strict"]) { + if (typeof str != "string") throw new Error("Type mismatch"); + } else str = str.toString();var length = Module.lengthBytesUTF8(str) + 1;var result = _nbind.Pool.lalloc(length);Module.stringToUTF8Array(str, HEAPU8, result, length);return result; + }_nbind.pushCString = pushCString;function popCString(ptr) { + if (ptr === 0) return null;return Module.Pointer_stringify(ptr); + }_nbind.popCString = popCString;var CStringType = function (_super) { + __extends(CStringType, _super);function CStringType() { + var _this = _super !== null && _super.apply(this, arguments) || this;_this.wireRead = popCString;_this.wireWrite = pushCString;_this.readResources = [_nbind.resources.pool];_this.writeResources = [_nbind.resources.pool];return _this; + }CStringType.prototype.makeWireWrite = function (expr, policyTbl) { + return function (arg) { + return pushCString(arg, policyTbl); + }; + };return CStringType; + }(BindType);_nbind.CStringType = CStringType;var BooleanType = function (_super) { + __extends(BooleanType, _super);function BooleanType() { + var _this = _super !== null && _super.apply(this, arguments) || this;_this.wireRead = function (arg) { + return !!arg; + };return _this; + }BooleanType.prototype.needsWireWrite = function (policyTbl) { + return !!policyTbl && !!policyTbl["Strict"]; + };BooleanType.prototype.makeWireRead = function (expr) { + return "!!(" + expr + ")"; + };BooleanType.prototype.makeWireWrite = function (expr, policyTbl) { + return policyTbl && policyTbl["Strict"] && function (arg) { + if (typeof arg == "boolean") return arg;throw new Error("Type mismatch"); + } || expr; + };return BooleanType; + }(BindType);_nbind.BooleanType = BooleanType;var Wrapper = function () { + function Wrapper() {}Wrapper.prototype.persist = function () { + this.__nbindState |= 1; + };return Wrapper; + }();_nbind.Wrapper = Wrapper;function makeBound(policyTbl, bindClass) { + var Bound = function (_super) { + __extends(Bound, _super);function Bound(marker, flags, ptr, shared) { + var _this = _super.call(this) || this;if (!(_this instanceof Bound)) { + return new (Function.prototype.bind.apply(Bound, Array.prototype.concat.apply([null], arguments)))(); + }var nbindFlags = flags;var nbindPtr = ptr;var nbindShared = shared;if (marker !== _nbind.ptrMarker) { + var wirePtr = _this.__nbindConstructor.apply(_this, arguments);nbindFlags = 4096 | 512;nbindShared = HEAPU32[wirePtr / 4];nbindPtr = HEAPU32[wirePtr / 4 + 1]; + }var spec = { configurable: true, enumerable: false, value: null, writable: false };var propTbl = { "__nbindFlags": nbindFlags, "__nbindPtr": nbindPtr };if (nbindShared) { + propTbl["__nbindShared"] = nbindShared;_nbind.mark(_this); + }for (var _i = 0, _a = Object.keys(propTbl); _i < _a.length; _i++) { + var key = _a[_i];spec.value = propTbl[key];Object.defineProperty(_this, key, spec); + }_defineHidden(0)(_this, "__nbindState");return _this; + }Bound.prototype.free = function () { + bindClass.destroy.call(this, this.__nbindShared, this.__nbindFlags);this.__nbindState |= 2;disableMember(this, "__nbindShared");disableMember(this, "__nbindPtr"); + };return Bound; + }(Wrapper);__decorate([_defineHidden()], Bound.prototype, "__nbindConstructor", void 0);__decorate([_defineHidden()], Bound.prototype, "__nbindValueConstructor", void 0);__decorate([_defineHidden(policyTbl)], Bound.prototype, "__nbindPolicies", void 0);return Bound; + }_nbind.makeBound = makeBound;function disableMember(obj, name) { + function die() { + throw new Error("Accessing deleted object"); + }Object.defineProperty(obj, name, { configurable: false, enumerable: false, get: die, set: die }); + }_nbind.ptrMarker = {};var BindClass = function (_super) { + __extends(BindClass, _super);function BindClass(spec) { + var _this = _super.call(this, spec) || this;_this.wireRead = function (arg) { + return _nbind.popValue(arg, _this.ptrType); + };_this.wireWrite = function (arg) { + return pushPointer(arg, _this.ptrType, true); + };_this.pendingSuperCount = 0;_this.ready = false;_this.methodTbl = {};if (spec.paramList) { + _this.classType = spec.paramList[0].classType;_this.proto = _this.classType.proto; + } else _this.classType = _this;return _this; + }BindClass.prototype.makeBound = function (policyTbl) { + var Bound = _nbind.makeBound(policyTbl, this);this.proto = Bound;this.ptrType.proto = Bound;return Bound; + };BindClass.prototype.addMethod = function (spec) { + var overloadList = this.methodTbl[spec.name] || [];overloadList.push(spec);this.methodTbl[spec.name] = overloadList; + };BindClass.prototype.registerMethods = function (src, staticOnly) { + var setter;for (var _i = 0, _a = Object.keys(src.methodTbl); _i < _a.length; _i++) { + var name_1 = _a[_i];var overloadList = src.methodTbl[name_1];for (var _b = 0, overloadList_1 = overloadList; _b < overloadList_1.length; _b++) { + var spec = overloadList_1[_b];var target = void 0;var caller = void 0;target = this.proto.prototype;if (staticOnly && spec.signatureType != 1) continue;switch (spec.signatureType) {case 1: + target = this.proto;case 5: + caller = _nbind.makeCaller(spec);_nbind.addMethod(target, spec.name, caller, spec.typeList.length - 1);break;case 4: + setter = _nbind.makeMethodCaller(src.ptrType, spec);break;case 3: + Object.defineProperty(target, spec.name, { configurable: true, enumerable: false, get: _nbind.makeMethodCaller(src.ptrType, spec), set: setter });break;case 2: + caller = _nbind.makeMethodCaller(src.ptrType, spec);_nbind.addMethod(target, spec.name, caller, spec.typeList.length - 1);break;default: + break;} + } + } + };BindClass.prototype.registerSuperMethods = function (src, firstSuper, visitTbl) { + if (visitTbl[src.name]) return;visitTbl[src.name] = true;var superNum = 0;var nextFirst;for (var _i = 0, _a = src.superIdList || []; _i < _a.length; _i++) { + var superId = _a[_i];var superClass = _nbind.getType(superId);if (superNum++ < firstSuper || firstSuper < 0) { + nextFirst = -1; + } else { + nextFirst = 0; + }this.registerSuperMethods(superClass, nextFirst, visitTbl); + }this.registerMethods(src, firstSuper < 0); + };BindClass.prototype.finish = function () { + if (this.ready) return this;this.ready = true;this.superList = (this.superIdList || []).map(function (superId) { + return _nbind.getType(superId).finish(); + });var Bound = this.proto;if (this.superList.length) { + var Proto = function () { + this.constructor = Bound; + };Proto.prototype = this.superList[0].proto.prototype;Bound.prototype = new Proto(); + }if (Bound != Module) Bound.prototype.__nbindType = this;this.registerSuperMethods(this, 1, {});return this; + };BindClass.prototype.upcastStep = function (dst, ptr) { + if (dst == this) return ptr;for (var i = 0; i < this.superList.length; ++i) { + var superPtr = this.superList[i].upcastStep(dst, _nbind.callUpcast(this.upcastList[i], ptr));if (superPtr) return superPtr; + }return 0; + };return BindClass; + }(_nbind.BindType);BindClass.list = [];_nbind.BindClass = BindClass;function popPointer(ptr, type) { + return ptr ? new type.proto(_nbind.ptrMarker, type.flags, ptr) : null; + }_nbind.popPointer = popPointer;function pushPointer(obj, type, tryValue) { + if (!(obj instanceof _nbind.Wrapper)) { + if (tryValue) { + return _nbind.pushValue(obj); + } else throw new Error("Type mismatch"); + }var ptr = obj.__nbindPtr;var objType = obj.__nbindType.classType;var classType = type.classType;if (obj instanceof type.proto) { + while (objType != classType) { + ptr = _nbind.callUpcast(objType.upcastList[0], ptr);objType = objType.superList[0]; + } + } else { + ptr = objType.upcastStep(classType, ptr);if (!ptr) throw new Error("Type mismatch"); + }return ptr; + }_nbind.pushPointer = pushPointer;function pushMutablePointer(obj, type) { + var ptr = pushPointer(obj, type);if (obj.__nbindFlags & 1) { + throw new Error("Passing a const value as a non-const argument"); + }return ptr; + }var BindClassPtr = function (_super) { + __extends(BindClassPtr, _super);function BindClassPtr(spec) { + var _this = _super.call(this, spec) || this;_this.classType = spec.paramList[0].classType;_this.proto = _this.classType.proto;var isConst = spec.flags & 1;var isValue = (_this.flags & 896) == 256 && spec.flags & 2;var push = isConst ? pushPointer : pushMutablePointer;var pop = isValue ? _nbind.popValue : popPointer;_this.makeWireWrite = function (expr, policyTbl) { + return policyTbl["Nullable"] ? function (arg) { + return arg ? push(arg, _this) : 0; + } : function (arg) { + return push(arg, _this); + }; + };_this.wireRead = function (arg) { + return pop(arg, _this); + };_this.wireWrite = function (arg) { + return push(arg, _this); + };return _this; + }return BindClassPtr; + }(_nbind.BindType);_nbind.BindClassPtr = BindClassPtr;function popShared(ptr, type) { + var shared = HEAPU32[ptr / 4];var unsafe = HEAPU32[ptr / 4 + 1];return unsafe ? new type.proto(_nbind.ptrMarker, type.flags, unsafe, shared) : null; + }_nbind.popShared = popShared;function pushShared(obj, type) { + if (!(obj instanceof type.proto)) throw new Error("Type mismatch");return obj.__nbindShared; + }function pushMutableShared(obj, type) { + if (!(obj instanceof type.proto)) throw new Error("Type mismatch");if (obj.__nbindFlags & 1) { + throw new Error("Passing a const value as a non-const argument"); + }return obj.__nbindShared; + }var SharedClassPtr = function (_super) { + __extends(SharedClassPtr, _super);function SharedClassPtr(spec) { + var _this = _super.call(this, spec) || this;_this.readResources = [_nbind.resources.pool];_this.classType = spec.paramList[0].classType;_this.proto = _this.classType.proto;var isConst = spec.flags & 1;var push = isConst ? pushShared : pushMutableShared;_this.wireRead = function (arg) { + return popShared(arg, _this); + };_this.wireWrite = function (arg) { + return push(arg, _this); + };return _this; + }return SharedClassPtr; + }(_nbind.BindType);_nbind.SharedClassPtr = SharedClassPtr;_nbind.externalList = [0];var firstFreeExternal = 0;var External = function () { + function External(data) { + this.refCount = 1;this.data = data; + }External.prototype.register = function () { + var num = firstFreeExternal;if (num) { + firstFreeExternal = _nbind.externalList[num]; + } else num = _nbind.externalList.length;_nbind.externalList[num] = this;return num; + };External.prototype.reference = function () { + ++this.refCount; + };External.prototype.dereference = function (num) { + if (--this.refCount == 0) { + if (this.free) this.free();_nbind.externalList[num] = firstFreeExternal;firstFreeExternal = num; + } + };return External; + }();_nbind.External = External;function popExternal(num) { + var obj = _nbind.externalList[num];obj.dereference(num);return obj.data; + }function pushExternal(obj) { + var external = new External(obj);external.reference();return external.register(); + }var ExternalType = function (_super) { + __extends(ExternalType, _super);function ExternalType() { + var _this = _super !== null && _super.apply(this, arguments) || this;_this.wireRead = popExternal;_this.wireWrite = pushExternal;return _this; + }return ExternalType; + }(_nbind.BindType);_nbind.ExternalType = ExternalType;_nbind.callbackSignatureList = [];var CallbackType = function (_super) { + __extends(CallbackType, _super);function CallbackType() { + var _this = _super !== null && _super.apply(this, arguments) || this;_this.wireWrite = function (func) { + if (typeof func != "function") _nbind.throwError("Type mismatch");return new _nbind.External(func).register(); + };return _this; + }return CallbackType; + }(_nbind.BindType);_nbind.CallbackType = CallbackType;_nbind.valueList = [0];var firstFreeValue = 0;function pushValue(value) { + var num = firstFreeValue;if (num) { + firstFreeValue = _nbind.valueList[num]; + } else num = _nbind.valueList.length;_nbind.valueList[num] = value;return num * 2 + 1; + }_nbind.pushValue = pushValue;function popValue(num, type) { + if (!num) _nbind.throwError("Value type JavaScript class is missing or not registered");if (num & 1) { + num >>= 1;var obj = _nbind.valueList[num];_nbind.valueList[num] = firstFreeValue;firstFreeValue = num;return obj; + } else if (type) { + return _nbind.popShared(num, type); + } else throw new Error("Invalid value slot " + num); + }_nbind.popValue = popValue;var valueBase = 0x10000000000000000;function push64(num) { + if (typeof num == "number") return num;return pushValue(num) * 4096 + valueBase; + }function pop64(num) { + if (num < valueBase) return num;return popValue((num - valueBase) / 4096); + }var CreateValueType = function (_super) { + __extends(CreateValueType, _super);function CreateValueType() { + return _super !== null && _super.apply(this, arguments) || this; + }CreateValueType.prototype.makeWireWrite = function (expr) { + return "(_nbind.pushValue(new " + expr + "))"; + };return CreateValueType; + }(_nbind.BindType);_nbind.CreateValueType = CreateValueType;var Int64Type = function (_super) { + __extends(Int64Type, _super);function Int64Type() { + var _this = _super !== null && _super.apply(this, arguments) || this;_this.wireWrite = push64;_this.wireRead = pop64;return _this; + }return Int64Type; + }(_nbind.BindType);_nbind.Int64Type = Int64Type;function pushArray(arr, type) { + if (!arr) return 0;var length = arr.length;if ((type.size || type.size === 0) && length < type.size) { + throw new Error("Type mismatch"); + }var ptrSize = type.memberType.ptrSize;var result = _nbind.Pool.lalloc(4 + length * ptrSize);HEAPU32[result / 4] = length;var heap = type.memberType.heap;var ptr = (result + 4) / ptrSize;var wireWrite = type.memberType.wireWrite;var num = 0;if (wireWrite) { + while (num < length) { + heap[ptr++] = wireWrite(arr[num++]); + } + } else { + while (num < length) { + heap[ptr++] = arr[num++]; + } + }return result; + }_nbind.pushArray = pushArray;function popArray(ptr, type) { + if (ptr === 0) return null;var length = HEAPU32[ptr / 4];var arr = new Array(length);var heap = type.memberType.heap;ptr = (ptr + 4) / type.memberType.ptrSize;var wireRead = type.memberType.wireRead;var num = 0;if (wireRead) { + while (num < length) { + arr[num++] = wireRead(heap[ptr++]); + } + } else { + while (num < length) { + arr[num++] = heap[ptr++]; + } + }return arr; + }_nbind.popArray = popArray;var ArrayType = function (_super) { + __extends(ArrayType, _super);function ArrayType(spec) { + var _this = _super.call(this, spec) || this;_this.wireRead = function (arg) { + return popArray(arg, _this); + };_this.wireWrite = function (arg) { + return pushArray(arg, _this); + };_this.readResources = [_nbind.resources.pool];_this.writeResources = [_nbind.resources.pool];_this.memberType = spec.paramList[0];if (spec.paramList[1]) _this.size = spec.paramList[1];return _this; + }return ArrayType; + }(_nbind.BindType);_nbind.ArrayType = ArrayType;function pushString(str, policyTbl) { + if (str === null || str === undefined) { + if (policyTbl && policyTbl["Nullable"]) { + str = ""; + } else throw new Error("Type mismatch"); + }if (policyTbl && policyTbl["Strict"]) { + if (typeof str != "string") throw new Error("Type mismatch"); + } else str = str.toString();var length = Module.lengthBytesUTF8(str);var result = _nbind.Pool.lalloc(4 + length + 1);HEAPU32[result / 4] = length;Module.stringToUTF8Array(str, HEAPU8, result + 4, length + 1);return result; + }_nbind.pushString = pushString;function popString(ptr) { + if (ptr === 0) return null;var length = HEAPU32[ptr / 4];return Module.Pointer_stringify(ptr + 4, length); + }_nbind.popString = popString;var StringType = function (_super) { + __extends(StringType, _super);function StringType() { + var _this = _super !== null && _super.apply(this, arguments) || this;_this.wireRead = popString;_this.wireWrite = pushString;_this.readResources = [_nbind.resources.pool];_this.writeResources = [_nbind.resources.pool];return _this; + }StringType.prototype.makeWireWrite = function (expr, policyTbl) { + return function (arg) { + return pushString(arg, policyTbl); + }; + };return StringType; + }(_nbind.BindType);_nbind.StringType = StringType;function makeArgList(argCount) { + return Array.apply(null, Array(argCount)).map(function (dummy, num) { + return "a" + (num + 1); + }); + }function anyNeedsWireWrite(typeList, policyTbl) { + return typeList.reduce(function (result, type) { + return result || type.needsWireWrite(policyTbl); + }, false); + }function anyNeedsWireRead(typeList, policyTbl) { + return typeList.reduce(function (result, type) { + return result || !!type.needsWireRead(policyTbl); + }, false); + }function makeWireRead(convertParamList, policyTbl, type, expr) { + var paramNum = convertParamList.length;if (type.makeWireRead) { + return type.makeWireRead(expr, convertParamList, paramNum); + } else if (type.wireRead) { + convertParamList[paramNum] = type.wireRead;return "(convertParamList[" + paramNum + "](" + expr + "))"; + } else return expr; + }function makeWireWrite(convertParamList, policyTbl, type, expr) { + var wireWrite;var paramNum = convertParamList.length;if (type.makeWireWrite) { + wireWrite = type.makeWireWrite(expr, policyTbl, convertParamList, paramNum); + } else wireWrite = type.wireWrite;if (wireWrite) { + if (typeof wireWrite == "string") { + return wireWrite; + } else { + convertParamList[paramNum] = wireWrite;return "(convertParamList[" + paramNum + "](" + expr + "))"; + } + } else return expr; + }function buildCallerFunction(dynCall, ptrType, ptr, num, policyTbl, needsWireWrite, prefix, returnType, argTypeList, mask, err) { + var argList = makeArgList(argTypeList.length);var convertParamList = [];var callExpression = makeWireRead(convertParamList, policyTbl, returnType, "dynCall(" + [prefix].concat(argList.map(function (name, index) { + return makeWireWrite(convertParamList, policyTbl, argTypeList[index], name); + })).join(",") + ")");var resourceSet = _nbind.listResources([returnType], argTypeList);var sourceCode = "function(" + argList.join(",") + "){" + (mask ? "this.__nbindFlags&mask&&err();" : "") + resourceSet.makeOpen() + "var r=" + callExpression + ";" + resourceSet.makeClose() + "return r;" + "}";return eval("(" + sourceCode + ")"); + }function buildJSCallerFunction(returnType, argTypeList) { + var argList = makeArgList(argTypeList.length);var convertParamList = [];var callExpression = makeWireWrite(convertParamList, null, returnType, "_nbind.externalList[num].data(" + argList.map(function (name, index) { + return makeWireRead(convertParamList, null, argTypeList[index], name); + }).join(",") + ")");var resourceSet = _nbind.listResources(argTypeList, [returnType]);resourceSet.remove(_nbind.resources.pool);var sourceCode = "function(" + ["dummy", "num"].concat(argList).join(",") + "){" + resourceSet.makeOpen() + "var r=" + callExpression + ";" + resourceSet.makeClose() + "return r;" + "}";return eval("(" + sourceCode + ")"); + }_nbind.buildJSCallerFunction = buildJSCallerFunction;function makeJSCaller(idList) { + var argCount = idList.length - 1;var typeList = _nbind.getTypes(idList, "callback");var returnType = typeList[0];var argTypeList = typeList.slice(1);var needsWireRead = anyNeedsWireRead(argTypeList, null);var needsWireWrite = returnType.needsWireWrite(null);if (!needsWireWrite && !needsWireRead) { + switch (argCount) {case 0: + return function (dummy, num) { + return _nbind.externalList[num].data(); + };case 1: + return function (dummy, num, a1) { + return _nbind.externalList[num].data(a1); + };case 2: + return function (dummy, num, a1, a2) { + return _nbind.externalList[num].data(a1, a2); + };case 3: + return function (dummy, num, a1, a2, a3) { + return _nbind.externalList[num].data(a1, a2, a3); + };default: + break;} + }return buildJSCallerFunction(returnType, argTypeList); + }_nbind.makeJSCaller = makeJSCaller;function makeMethodCaller(ptrType, spec) { + var argCount = spec.typeList.length - 1;var typeIdList = spec.typeList.slice(0);typeIdList.splice(1, 0, "uint32_t", spec.boundID);var typeList = _nbind.getTypes(typeIdList, spec.title);var returnType = typeList[0];var argTypeList = typeList.slice(3);var needsWireRead = returnType.needsWireRead(spec.policyTbl);var needsWireWrite = anyNeedsWireWrite(argTypeList, spec.policyTbl);var ptr = spec.ptr;var num = spec.num;var dynCall = _nbind.getDynCall(typeList, spec.title);var mask = ~spec.flags & 1;function err() { + throw new Error("Calling a non-const method on a const object"); + }if (!needsWireRead && !needsWireWrite) { + switch (argCount) {case 0: + return function () { + return this.__nbindFlags & mask ? err() : dynCall(ptr, num, _nbind.pushPointer(this, ptrType)); + };case 1: + return function (a1) { + return this.__nbindFlags & mask ? err() : dynCall(ptr, num, _nbind.pushPointer(this, ptrType), a1); + };case 2: + return function (a1, a2) { + return this.__nbindFlags & mask ? err() : dynCall(ptr, num, _nbind.pushPointer(this, ptrType), a1, a2); + };case 3: + return function (a1, a2, a3) { + return this.__nbindFlags & mask ? err() : dynCall(ptr, num, _nbind.pushPointer(this, ptrType), a1, a2, a3); + };default: + break;} + }return buildCallerFunction(dynCall, ptrType, ptr, num, spec.policyTbl, needsWireWrite, "ptr,num,pushPointer(this,ptrType)", returnType, argTypeList, mask, err); + }_nbind.makeMethodCaller = makeMethodCaller;function makeCaller(spec) { + var argCount = spec.typeList.length - 1;var typeList = _nbind.getTypes(spec.typeList, spec.title);var returnType = typeList[0];var argTypeList = typeList.slice(1);var needsWireRead = returnType.needsWireRead(spec.policyTbl);var needsWireWrite = anyNeedsWireWrite(argTypeList, spec.policyTbl);var direct = spec.direct;var ptr = spec.ptr;if (spec.direct && !needsWireRead && !needsWireWrite) { + var dynCall_1 = _nbind.getDynCall(typeList, spec.title);switch (argCount) {case 0: + return function () { + return dynCall_1(direct); + };case 1: + return function (a1) { + return dynCall_1(direct, a1); + };case 2: + return function (a1, a2) { + return dynCall_1(direct, a1, a2); + };case 3: + return function (a1, a2, a3) { + return dynCall_1(direct, a1, a2, a3); + };default: + break;}ptr = 0; + }var prefix;if (ptr) { + var typeIdList = spec.typeList.slice(0);typeIdList.splice(1, 0, "uint32_t");typeList = _nbind.getTypes(typeIdList, spec.title);prefix = "ptr,num"; + } else { + ptr = direct;prefix = "ptr"; + }var dynCall = _nbind.getDynCall(typeList, spec.title);return buildCallerFunction(dynCall, null, ptr, spec.num, spec.policyTbl, needsWireWrite, prefix, returnType, argTypeList); + }_nbind.makeCaller = makeCaller;function makeOverloader(func, arity) { + var callerList = [];function call() { + return callerList[arguments.length].apply(this, arguments); + }call.addMethod = function (_func, _arity) { + callerList[_arity] = _func; + };call.addMethod(func, arity);return call; + }_nbind.makeOverloader = makeOverloader;var Resource = function () { + function Resource(open, close) { + var _this = this;this.makeOpen = function () { + return Object.keys(_this.openTbl).join(""); + };this.makeClose = function () { + return Object.keys(_this.closeTbl).join(""); + };this.openTbl = {};this.closeTbl = {};if (open) this.openTbl[open] = true;if (close) this.closeTbl[close] = true; + }Resource.prototype.add = function (other) { + for (var _i = 0, _a = Object.keys(other.openTbl); _i < _a.length; _i++) { + var key = _a[_i];this.openTbl[key] = true; + }for (var _b = 0, _c = Object.keys(other.closeTbl); _b < _c.length; _b++) { + var key = _c[_b];this.closeTbl[key] = true; + } + };Resource.prototype.remove = function (other) { + for (var _i = 0, _a = Object.keys(other.openTbl); _i < _a.length; _i++) { + var key = _a[_i];delete this.openTbl[key]; + }for (var _b = 0, _c = Object.keys(other.closeTbl); _b < _c.length; _b++) { + var key = _c[_b];delete this.closeTbl[key]; + } + };return Resource; + }();_nbind.Resource = Resource;function listResources(readList, writeList) { + var result = new Resource();for (var _i = 0, readList_1 = readList; _i < readList_1.length; _i++) { + var bindType = readList_1[_i];for (var _a = 0, _b = bindType.readResources || []; _a < _b.length; _a++) { + var resource = _b[_a];result.add(resource); + } + }for (var _c = 0, writeList_1 = writeList; _c < writeList_1.length; _c++) { + var bindType = writeList_1[_c];for (var _d = 0, _e = bindType.writeResources || []; _d < _e.length; _d++) { + var resource = _e[_d];result.add(resource); + } + }return result; + }_nbind.listResources = listResources;_nbind.resources = { pool: new Resource("var used=HEAPU32[_nbind.Pool.usedPtr],page=HEAPU32[_nbind.Pool.pagePtr];", "_nbind.Pool.lreset(used,page);") };var ExternalBuffer = function (_super) { + __extends(ExternalBuffer, _super);function ExternalBuffer(buf, ptr) { + var _this = _super.call(this, buf) || this;_this.ptr = ptr;return _this; + }ExternalBuffer.prototype.free = function () { + _free(this.ptr); + };return ExternalBuffer; + }(_nbind.External);function getBuffer(buf) { + if (buf instanceof ArrayBuffer) { + return new Uint8Array(buf); + } else if (buf instanceof DataView) { + return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength); + } else return buf; + }function pushBuffer(buf, policyTbl) { + if (buf === null || buf === undefined) { + if (policyTbl && policyTbl["Nullable"]) buf = []; + }if (typeof buf != "object") throw new Error("Type mismatch");var b = buf;var length = b.byteLength || b.length;if (!length && length !== 0 && b.byteLength !== 0) throw new Error("Type mismatch");var result = _nbind.Pool.lalloc(8);var data = _malloc(length);var ptr = result / 4;HEAPU32[ptr++] = length;HEAPU32[ptr++] = data;HEAPU32[ptr++] = new ExternalBuffer(buf, data).register();HEAPU8.set(getBuffer(buf), data);return result; + }var BufferType = function (_super) { + __extends(BufferType, _super);function BufferType() { + var _this = _super !== null && _super.apply(this, arguments) || this;_this.wireWrite = pushBuffer;_this.readResources = [_nbind.resources.pool];_this.writeResources = [_nbind.resources.pool];return _this; + }BufferType.prototype.makeWireWrite = function (expr, policyTbl) { + return function (arg) { + return pushBuffer(arg, policyTbl); + }; + };return BufferType; + }(_nbind.BindType);_nbind.BufferType = BufferType;function commitBuffer(num, data, length) { + var buf = _nbind.externalList[num].data;var NodeBuffer = Buffer;if (typeof Buffer != "function") NodeBuffer = function () {};if (buf instanceof Array) {} else { + var src = HEAPU8.subarray(data, data + length);if (buf instanceof NodeBuffer) { + var srcBuf = void 0;if (typeof Buffer.from == "function" && Buffer.from.length >= 3) { + srcBuf = Buffer.from(src); + } else srcBuf = new Buffer(src);srcBuf.copy(buf); + } else getBuffer(buf).set(src); + } + }_nbind.commitBuffer = commitBuffer;var dirtyList = [];var gcTimer = 0;function sweep() { + for (var _i = 0, dirtyList_1 = dirtyList; _i < dirtyList_1.length; _i++) { + var obj = dirtyList_1[_i];if (!(obj.__nbindState & (1 | 2))) { + obj.free(); + } + }dirtyList = [];gcTimer = 0; + }_nbind.mark = function (obj) {};function toggleLightGC(enable) { + if (enable) { + _nbind.mark = function (obj) { + dirtyList.push(obj);if (!gcTimer) gcTimer = setTimeout(sweep, 0); + }; + } else { + _nbind.mark = function (obj) {}; + } + }_nbind.toggleLightGC = toggleLightGC; + })(_nbind);Module["requestFullScreen"] = function Module_requestFullScreen(lockPointer, resizeCanvas, vrDevice) { + Module.printErr("Module.requestFullScreen is deprecated. Please call Module.requestFullscreen instead.");Module["requestFullScreen"] = Module["requestFullscreen"];Browser.requestFullScreen(lockPointer, resizeCanvas, vrDevice); + };Module["requestFullscreen"] = function Module_requestFullscreen(lockPointer, resizeCanvas, vrDevice) { + Browser.requestFullscreen(lockPointer, resizeCanvas, vrDevice); + };Module["requestAnimationFrame"] = function Module_requestAnimationFrame(func) { + Browser.requestAnimationFrame(func); + };Module["setCanvasSize"] = function Module_setCanvasSize(width, height, noUpdates) { + Browser.setCanvasSize(width, height, noUpdates); + };Module["pauseMainLoop"] = function Module_pauseMainLoop() { + Browser.mainLoop.pause(); + };Module["resumeMainLoop"] = function Module_resumeMainLoop() { + Browser.mainLoop.resume(); + };Module["getUserMedia"] = function Module_getUserMedia() { + Browser.getUserMedia(); + };Module["createContext"] = function Module_createContext(canvas, useWebGL, setInModule, webGLContextAttributes) { + return Browser.createContext(canvas, useWebGL, setInModule, webGLContextAttributes); + };if (ENVIRONMENT_IS_NODE) { + _emscripten_get_now = function _emscripten_get_now_actual() { + var t = process["hrtime"]();return t[0] * 1e3 + t[1] / 1e6; + }; + } else if (typeof dateNow !== "undefined") { + _emscripten_get_now = dateNow; + } else if (typeof self === "object" && self["performance"] && typeof self["performance"]["now"] === "function") { + _emscripten_get_now = function () { + return self["performance"]["now"](); + }; + } else if (typeof performance === "object" && typeof performance["now"] === "function") { + _emscripten_get_now = function () { + return performance["now"](); + }; + } else { + _emscripten_get_now = Date.now; + }__ATEXIT__.push(function () { + var fflush = Module["_fflush"];if (fflush) fflush(0);var printChar = ___syscall146.printChar;if (!printChar) return;var buffers = ___syscall146.buffers;if (buffers[1].length) printChar(1, 10);if (buffers[2].length) printChar(2, 10); + });DYNAMICTOP_PTR = allocate(1, "i32", ALLOC_STATIC);STACK_BASE = STACKTOP = Runtime.alignMemory(STATICTOP);STACK_MAX = STACK_BASE + TOTAL_STACK;DYNAMIC_BASE = Runtime.alignMemory(STACK_MAX);HEAP32[DYNAMICTOP_PTR >> 2] = DYNAMIC_BASE;staticSealed = true;function invoke_viiiii(index, a1, a2, a3, a4, a5) { + try { + Module["dynCall_viiiii"](index, a1, a2, a3, a4, a5); + } catch (e) { + if (typeof e !== "number" && e !== "longjmp") throw e;Module["setThrew"](1, 0); + } + }function invoke_vif(index, a1, a2) { + try { + Module["dynCall_vif"](index, a1, a2); + } catch (e) { + if (typeof e !== "number" && e !== "longjmp") throw e;Module["setThrew"](1, 0); + } + }function invoke_vid(index, a1, a2) { + try { + Module["dynCall_vid"](index, a1, a2); + } catch (e) { + if (typeof e !== "number" && e !== "longjmp") throw e;Module["setThrew"](1, 0); + } + }function invoke_fiff(index, a1, a2, a3) { + try { + return Module["dynCall_fiff"](index, a1, a2, a3); + } catch (e) { + if (typeof e !== "number" && e !== "longjmp") throw e;Module["setThrew"](1, 0); + } + }function invoke_vi(index, a1) { + try { + Module["dynCall_vi"](index, a1); + } catch (e) { + if (typeof e !== "number" && e !== "longjmp") throw e;Module["setThrew"](1, 0); + } + }function invoke_vii(index, a1, a2) { + try { + Module["dynCall_vii"](index, a1, a2); + } catch (e) { + if (typeof e !== "number" && e !== "longjmp") throw e;Module["setThrew"](1, 0); + } + }function invoke_ii(index, a1) { + try { + return Module["dynCall_ii"](index, a1); + } catch (e) { + if (typeof e !== "number" && e !== "longjmp") throw e;Module["setThrew"](1, 0); + } + }function invoke_viddi(index, a1, a2, a3, a4) { + try { + Module["dynCall_viddi"](index, a1, a2, a3, a4); + } catch (e) { + if (typeof e !== "number" && e !== "longjmp") throw e;Module["setThrew"](1, 0); + } + }function invoke_vidd(index, a1, a2, a3) { + try { + Module["dynCall_vidd"](index, a1, a2, a3); + } catch (e) { + if (typeof e !== "number" && e !== "longjmp") throw e;Module["setThrew"](1, 0); + } + }function invoke_iiii(index, a1, a2, a3) { + try { + return Module["dynCall_iiii"](index, a1, a2, a3); + } catch (e) { + if (typeof e !== "number" && e !== "longjmp") throw e;Module["setThrew"](1, 0); + } + }function invoke_diii(index, a1, a2, a3) { + try { + return Module["dynCall_diii"](index, a1, a2, a3); + } catch (e) { + if (typeof e !== "number" && e !== "longjmp") throw e;Module["setThrew"](1, 0); + } + }function invoke_di(index, a1) { + try { + return Module["dynCall_di"](index, a1); + } catch (e) { + if (typeof e !== "number" && e !== "longjmp") throw e;Module["setThrew"](1, 0); + } + }function invoke_iid(index, a1, a2) { + try { + return Module["dynCall_iid"](index, a1, a2); + } catch (e) { + if (typeof e !== "number" && e !== "longjmp") throw e;Module["setThrew"](1, 0); + } + }function invoke_iii(index, a1, a2) { + try { + return Module["dynCall_iii"](index, a1, a2); + } catch (e) { + if (typeof e !== "number" && e !== "longjmp") throw e;Module["setThrew"](1, 0); + } + }function invoke_viiddi(index, a1, a2, a3, a4, a5) { + try { + Module["dynCall_viiddi"](index, a1, a2, a3, a4, a5); + } catch (e) { + if (typeof e !== "number" && e !== "longjmp") throw e;Module["setThrew"](1, 0); + } + }function invoke_viiiiii(index, a1, a2, a3, a4, a5, a6) { + try { + Module["dynCall_viiiiii"](index, a1, a2, a3, a4, a5, a6); + } catch (e) { + if (typeof e !== "number" && e !== "longjmp") throw e;Module["setThrew"](1, 0); + } + }function invoke_dii(index, a1, a2) { + try { + return Module["dynCall_dii"](index, a1, a2); + } catch (e) { + if (typeof e !== "number" && e !== "longjmp") throw e;Module["setThrew"](1, 0); + } + }function invoke_i(index) { + try { + return Module["dynCall_i"](index); + } catch (e) { + if (typeof e !== "number" && e !== "longjmp") throw e;Module["setThrew"](1, 0); + } + }function invoke_iiiiii(index, a1, a2, a3, a4, a5) { + try { + return Module["dynCall_iiiiii"](index, a1, a2, a3, a4, a5); + } catch (e) { + if (typeof e !== "number" && e !== "longjmp") throw e;Module["setThrew"](1, 0); + } + }function invoke_viiid(index, a1, a2, a3, a4) { + try { + Module["dynCall_viiid"](index, a1, a2, a3, a4); + } catch (e) { + if (typeof e !== "number" && e !== "longjmp") throw e;Module["setThrew"](1, 0); + } + }function invoke_viififi(index, a1, a2, a3, a4, a5, a6) { + try { + Module["dynCall_viififi"](index, a1, a2, a3, a4, a5, a6); + } catch (e) { + if (typeof e !== "number" && e !== "longjmp") throw e;Module["setThrew"](1, 0); + } + }function invoke_viii(index, a1, a2, a3) { + try { + Module["dynCall_viii"](index, a1, a2, a3); + } catch (e) { + if (typeof e !== "number" && e !== "longjmp") throw e;Module["setThrew"](1, 0); + } + }function invoke_v(index) { + try { + Module["dynCall_v"](index); + } catch (e) { + if (typeof e !== "number" && e !== "longjmp") throw e;Module["setThrew"](1, 0); + } + }function invoke_viid(index, a1, a2, a3) { + try { + Module["dynCall_viid"](index, a1, a2, a3); + } catch (e) { + if (typeof e !== "number" && e !== "longjmp") throw e;Module["setThrew"](1, 0); + } + }function invoke_idd(index, a1, a2) { + try { + return Module["dynCall_idd"](index, a1, a2); + } catch (e) { + if (typeof e !== "number" && e !== "longjmp") throw e;Module["setThrew"](1, 0); + } + }function invoke_viiii(index, a1, a2, a3, a4) { + try { + Module["dynCall_viiii"](index, a1, a2, a3, a4); + } catch (e) { + if (typeof e !== "number" && e !== "longjmp") throw e;Module["setThrew"](1, 0); + } + }Module.asmGlobalArg = { "Math": Math, "Int8Array": Int8Array, "Int16Array": Int16Array, "Int32Array": Int32Array, "Uint8Array": Uint8Array, "Uint16Array": Uint16Array, "Uint32Array": Uint32Array, "Float32Array": Float32Array, "Float64Array": Float64Array, "NaN": NaN, "Infinity": Infinity };Module.asmLibraryArg = { "abort": abort, "assert": assert, "enlargeMemory": enlargeMemory, "getTotalMemory": getTotalMemory, "abortOnCannotGrowMemory": abortOnCannotGrowMemory, "invoke_viiiii": invoke_viiiii, "invoke_vif": invoke_vif, "invoke_vid": invoke_vid, "invoke_fiff": invoke_fiff, "invoke_vi": invoke_vi, "invoke_vii": invoke_vii, "invoke_ii": invoke_ii, "invoke_viddi": invoke_viddi, "invoke_vidd": invoke_vidd, "invoke_iiii": invoke_iiii, "invoke_diii": invoke_diii, "invoke_di": invoke_di, "invoke_iid": invoke_iid, "invoke_iii": invoke_iii, "invoke_viiddi": invoke_viiddi, "invoke_viiiiii": invoke_viiiiii, "invoke_dii": invoke_dii, "invoke_i": invoke_i, "invoke_iiiiii": invoke_iiiiii, "invoke_viiid": invoke_viiid, "invoke_viififi": invoke_viififi, "invoke_viii": invoke_viii, "invoke_v": invoke_v, "invoke_viid": invoke_viid, "invoke_idd": invoke_idd, "invoke_viiii": invoke_viiii, "_emscripten_asm_const_iiiii": _emscripten_asm_const_iiiii, "_emscripten_asm_const_iiidddddd": _emscripten_asm_const_iiidddddd, "_emscripten_asm_const_iiiid": _emscripten_asm_const_iiiid, "__nbind_reference_external": __nbind_reference_external, "_emscripten_asm_const_iiiiiiii": _emscripten_asm_const_iiiiiiii, "_removeAccessorPrefix": _removeAccessorPrefix, "_typeModule": _typeModule, "__nbind_register_pool": __nbind_register_pool, "__decorate": __decorate, "_llvm_stackrestore": _llvm_stackrestore, "___cxa_atexit": ___cxa_atexit, "__extends": __extends, "__nbind_get_value_object": __nbind_get_value_object, "__ZN8facebook4yoga14YGNodeToStringEPNSt3__212basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEP6YGNode14YGPrintOptionsj": __ZN8facebook4yoga14YGNodeToStringEPNSt3__212basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEP6YGNode14YGPrintOptionsj, "_emscripten_set_main_loop_timing": _emscripten_set_main_loop_timing, "__nbind_register_primitive": __nbind_register_primitive, "__nbind_register_type": __nbind_register_type, "_emscripten_memcpy_big": _emscripten_memcpy_big, "__nbind_register_function": __nbind_register_function, "___setErrNo": ___setErrNo, "__nbind_register_class": __nbind_register_class, "__nbind_finish": __nbind_finish, "_abort": _abort, "_nbind_value": _nbind_value, "_llvm_stacksave": _llvm_stacksave, "___syscall54": ___syscall54, "_defineHidden": _defineHidden, "_emscripten_set_main_loop": _emscripten_set_main_loop, "_emscripten_get_now": _emscripten_get_now, "__nbind_register_callback_signature": __nbind_register_callback_signature, "_emscripten_asm_const_iiiiii": _emscripten_asm_const_iiiiii, "__nbind_free_external": __nbind_free_external, "_emscripten_asm_const_iiii": _emscripten_asm_const_iiii, "_emscripten_asm_const_iiididi": _emscripten_asm_const_iiididi, "___syscall6": ___syscall6, "_atexit": _atexit, "___syscall140": ___syscall140, "___syscall146": ___syscall146, "DYNAMICTOP_PTR": DYNAMICTOP_PTR, "tempDoublePtr": tempDoublePtr, "ABORT": ABORT, "STACKTOP": STACKTOP, "STACK_MAX": STACK_MAX, "cttz_i8": cttz_i8, "___dso_handle": ___dso_handle }; // EMSCRIPTEN_START_ASM + var asm = function (global, env, buffer) { + "use asm"; + var a = new global.Int8Array(buffer);var b = new global.Int16Array(buffer);var c = new global.Int32Array(buffer);var d = new global.Uint8Array(buffer);var e = new global.Uint16Array(buffer);var f = new global.Uint32Array(buffer);var g = new global.Float32Array(buffer);var h = new global.Float64Array(buffer);var i = env.DYNAMICTOP_PTR | 0;var j = env.tempDoublePtr | 0;var k = env.ABORT | 0;var l = env.STACKTOP | 0;var m = env.STACK_MAX | 0;var n = env.cttz_i8 | 0;var o = env.___dso_handle | 0;var t = global.NaN, + u = global.Infinity;var A = 0;var B = global.Math.floor;var C = global.Math.abs;var D = global.Math.sqrt;var E = global.Math.pow;var F = global.Math.cos;var G = global.Math.sin;var H = global.Math.tan;var I = global.Math.acos;var J = global.Math.asin;var K = global.Math.atan;var L = global.Math.atan2;var M = global.Math.exp;var N = global.Math.log;var O = global.Math.ceil;var P = global.Math.imul;var Q = global.Math.min;var R = global.Math.max;var S = global.Math.clz32;var T = global.Math.fround;var U = env.abort;var V = env.assert;var W = env.enlargeMemory;var X = env.getTotalMemory;var Y = env.abortOnCannotGrowMemory;var Z = env.invoke_viiiii;var _ = env.invoke_vif;var $ = env.invoke_vid;var aa = env.invoke_fiff;var ba = env.invoke_vi;var ca = env.invoke_vii;var da = env.invoke_ii;var ea = env.invoke_viddi;var fa = env.invoke_vidd;var ga = env.invoke_iiii;var ha = env.invoke_diii;var ia = env.invoke_di;var ja = env.invoke_iid;var ka = env.invoke_iii;var la = env.invoke_viiddi;var ma = env.invoke_viiiiii;var na = env.invoke_dii;var oa = env.invoke_i;var pa = env.invoke_iiiiii;var qa = env.invoke_viiid;var ra = env.invoke_viififi;var sa = env.invoke_viii;var ta = env.invoke_v;var ua = env.invoke_viid;var va = env.invoke_idd;var wa = env.invoke_viiii;var xa = env._emscripten_asm_const_iiiii;var ya = env._emscripten_asm_const_iiidddddd;var za = env._emscripten_asm_const_iiiid;var Aa = env.__nbind_reference_external;var Ba = env._emscripten_asm_const_iiiiiiii;var Ca = env._removeAccessorPrefix;var Da = env._typeModule;var Ea = env.__nbind_register_pool;var Fa = env.__decorate;var Ga = env._llvm_stackrestore;var Ha = env.___cxa_atexit;var Ia = env.__extends;var Ja = env.__nbind_get_value_object;var Ka = env.__ZN8facebook4yoga14YGNodeToStringEPNSt3__212basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEP6YGNode14YGPrintOptionsj;var La = env._emscripten_set_main_loop_timing;var Ma = env.__nbind_register_primitive;var Na = env.__nbind_register_type;var Oa = env._emscripten_memcpy_big;var Pa = env.__nbind_register_function;var Qa = env.___setErrNo;var Ra = env.__nbind_register_class;var Sa = env.__nbind_finish;var Ta = env._abort;var Ua = env._nbind_value;var Va = env._llvm_stacksave;var Wa = env.___syscall54;var Xa = env._defineHidden;var Ya = env._emscripten_set_main_loop;var Za = env._emscripten_get_now;var _a = env.__nbind_register_callback_signature;var $a = env._emscripten_asm_const_iiiiii;var ab = env.__nbind_free_external;var bb = env._emscripten_asm_const_iiii;var cb = env._emscripten_asm_const_iiididi;var db = env.___syscall6;var eb = env._atexit;var fb = env.___syscall140;var gb = env.___syscall146;var hb = T(0);const ib = T(0); + // EMSCRIPTEN_START_FUNCS + function Jb(a) { + a = a | 0;var b = 0;b = l;l = l + a | 0;l = l + 15 & -16;return b | 0; + }function Kb() { + return l | 0; + }function Lb(a) { + a = a | 0;l = a; + }function Mb(a, b) { + a = a | 0;b = b | 0;l = a;m = b; + }function Nb(a, b) { + a = a | 0;b = b | 0; }function Ob(a) { + a = a | 0;A = a; + }function Pb() { + return A | 0; + }function Qb() { + var b = 0, + d = 0;BC(8104, 8, 400) | 0;BC(8504, 408, 540) | 0;b = 9044;d = b + 44 | 0;do { + c[b >> 2] = 0;b = b + 4 | 0; + } while ((b | 0) < (d | 0));a[9088] = 0;a[9089] = 1;c[2273] = 0;c[2274] = 948;c[2275] = 948;Ha(17, 8104, o | 0) | 0;return; + }function Rb(a) { + a = a | 0;oc(a + 948 | 0);return; + }function Sb(a) { + a = T(a);return ((af(a) | 0) & 2147483647) >>> 0 > 2139095040 | 0; + }function Tb(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;a: do if (!(c[a + (b << 3) + 4 >> 2] | 0)) { + if ((b | 2 | 0) == 3 ? c[a + 60 >> 2] | 0 : 0) { + a = a + 56 | 0;break; + }switch (b | 0) {case 0:case 2:case 4:case 5: + { + if (c[a + 52 >> 2] | 0) { + a = a + 48 | 0;break a; + }break; + }default: +}if (!(c[a + 68 >> 2] | 0)) { + a = (b | 1 | 0) == 5 ? 948 : d;break; + } else { + a = a + 64 | 0;break; + } + } else a = a + (b << 3) | 0; while (0);return a | 0; + }function Ub(b) { + b = b | 0;var d = 0;d = oB(1e3) | 0;Vb(b, (d | 0) != 0, 2456);c[2276] = (c[2276] | 0) + 1;BC(d | 0, 8104, 1e3) | 0;if (a[b + 2 >> 0] | 0) { + c[d + 4 >> 2] = 2;c[d + 12 >> 2] = 4; + }c[d + 976 >> 2] = b;return d | 0; + }function Vb(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0;f = l;l = l + 16 | 0;e = f;if (!b) { + c[e >> 2] = d;fe(a, 5, 3197, e); + }l = f;return; + }function Wb() { + return Ub(956) | 0; + }function Xb(a) { + a = a | 0;var b = 0;b = qC(1e3) | 0;Yb(b, a);Vb(c[a + 976 >> 2] | 0, 1, 2456);c[2276] = (c[2276] | 0) + 1;c[b + 944 >> 2] = 0;return b | 0; + }function Yb(a, b) { + a = a | 0;b = b | 0;var d = 0;BC(a | 0, b | 0, 948) | 0;ie(a + 948 | 0, b + 948 | 0);d = a + 960 | 0;a = b + 960 | 0;b = d + 40 | 0;do { + c[d >> 2] = c[a >> 2];d = d + 4 | 0;a = a + 4 | 0; + } while ((d | 0) < (b | 0));return; + }function Zb(a) { + a = a | 0;var b = 0, + d = 0, + e = 0, + f = 0;b = a + 944 | 0;d = c[b >> 2] | 0;if (d | 0) { + _b(d + 948 | 0, a) | 0;c[b >> 2] = 0; + }d = $b(a) | 0;if (d | 0) { + b = 0;do { + c[(ac(a, b) | 0) + 944 >> 2] = 0;b = b + 1 | 0; + } while ((b | 0) != (d | 0)); + }d = a + 948 | 0;e = c[d >> 2] | 0;f = a + 952 | 0;b = c[f >> 2] | 0;if ((b | 0) != (e | 0)) c[f >> 2] = b + (~((b + -4 - e | 0) >>> 2) << 2);bc(d);pB(a);c[2276] = (c[2276] | 0) + -1;return; + }function _b(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0;e = c[a >> 2] | 0;i = a + 4 | 0;d = c[i >> 2] | 0;g = d;a: do if ((e | 0) == (d | 0)) { + f = e;h = 4; + } else { + a = e;while (1) { + if ((c[a >> 2] | 0) == (b | 0)) { + f = a;h = 4;break a; + }a = a + 4 | 0;if ((a | 0) == (d | 0)) { + a = 0;break; + } + } + } while (0);if ((h | 0) == 4) if ((f | 0) != (d | 0)) { + e = f + 4 | 0;a = g - e | 0;b = a >> 2;if (b) { + GC(f | 0, e | 0, a | 0) | 0;d = c[i >> 2] | 0; + }a = f + (b << 2) | 0;if ((d | 0) == (a | 0)) a = 1;else { + c[i >> 2] = d + (~((d + -4 - a | 0) >>> 2) << 2);a = 1; + } + } else a = 0;return a | 0; + }function $b(a) { + a = a | 0;return (c[a + 952 >> 2] | 0) - (c[a + 948 >> 2] | 0) >> 2 | 0; + }function ac(a, b) { + a = a | 0;b = b | 0;var d = 0;d = c[a + 948 >> 2] | 0;if ((c[a + 952 >> 2] | 0) - d >> 2 >>> 0 > b >>> 0) a = c[d + (b << 2) >> 2] | 0;else a = 0;return a | 0; + }function bc(a) { + a = a | 0;var b = 0, + d = 0, + e = 0, + f = 0;e = l;l = l + 32 | 0;b = e;f = c[a >> 2] | 0;d = (c[a + 4 >> 2] | 0) - f | 0;if (((c[a + 8 >> 2] | 0) - f | 0) >>> 0 > d >>> 0) { + f = d >> 2;bf(b, f, f, a + 8 | 0);cf(a, b);df(b); + }l = e;return; + }function cc(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0;k = $b(a) | 0;do if (k | 0) { + if ((c[(ac(a, 0) | 0) + 944 >> 2] | 0) == (a | 0)) { + if (!(_b(a + 948 | 0, b) | 0)) break;BC(b + 400 | 0, 8504, 540) | 0;c[b + 944 >> 2] = 0;nc(a);break; + }h = c[(c[a + 976 >> 2] | 0) + 12 >> 2] | 0;i = a + 948 | 0;j = (h | 0) == 0;d = 0;g = 0;do { + e = c[(c[i >> 2] | 0) + (g << 2) >> 2] | 0;if ((e | 0) == (b | 0)) nc(a);else { + f = Xb(e) | 0;c[(c[i >> 2] | 0) + (d << 2) >> 2] = f;c[f + 944 >> 2] = a;if (!j) Ib[h & 15](e, f, a, d);d = d + 1 | 0; + }g = g + 1 | 0; + } while ((g | 0) != (k | 0));if (d >>> 0 < k >>> 0) { + j = a + 948 | 0;i = a + 952 | 0;h = d;d = c[i >> 2] | 0;do { + g = (c[j >> 2] | 0) + (h << 2) | 0;e = g + 4 | 0;f = d - e | 0;b = f >> 2;if (!b) f = d;else { + GC(g | 0, e | 0, f | 0) | 0;d = c[i >> 2] | 0;f = d; + }e = g + (b << 2) | 0;if ((f | 0) != (e | 0)) { + d = f + (~((f + -4 - e | 0) >>> 2) << 2) | 0;c[i >> 2] = d; + }h = h + 1 | 0; + } while ((h | 0) != (k | 0)); + } + } while (0);return; + }function dc(b) { + b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0;ec(b, ($b(b) | 0) == 0, 2491);ec(b, (c[b + 944 >> 2] | 0) == 0, 2545);d = b + 948 | 0;e = c[d >> 2] | 0;f = b + 952 | 0;g = c[f >> 2] | 0;if ((g | 0) != (e | 0)) c[f >> 2] = g + (~((g + -4 - e | 0) >>> 2) << 2);bc(d);d = b + 976 | 0;e = c[d >> 2] | 0;BC(b | 0, 8104, 1e3) | 0;if (a[e + 2 >> 0] | 0) { + c[b + 4 >> 2] = 2;c[b + 12 >> 2] = 4; + }c[d >> 2] = e;return; + }function ec(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0;f = l;l = l + 16 | 0;e = f;if (!b) { + c[e >> 2] = d;Vd(a, 5, 3197, e); + }l = f;return; + }function fc() { + return c[2276] | 0; + }function gc() { + var a = 0;a = oB(20) | 0;hc((a | 0) != 0, 2592);c[2277] = (c[2277] | 0) + 1;c[a >> 2] = c[239];c[a + 4 >> 2] = c[240];c[a + 8 >> 2] = c[241];c[a + 12 >> 2] = c[242];c[a + 16 >> 2] = c[243];return a | 0; + }function hc(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0;e = l;l = l + 16 | 0;d = e;if (!a) { + c[d >> 2] = b;Vd(0, 5, 3197, d); + }l = e;return; + }function ic(a) { + a = a | 0;pB(a);c[2277] = (c[2277] | 0) + -1;return; + }function jc(a, b) { + a = a | 0;b = b | 0;var d = 0;if (!b) { + d = 0;b = 0; + } else { + ec(a, ($b(a) | 0) == 0, 2629);d = 1; + }c[a + 964 >> 2] = b;c[a + 988 >> 2] = d;return; + }function kc(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0;e = l;l = l + 16 | 0;g = e + 8 | 0;f = e + 4 | 0;h = e;c[f >> 2] = b;ec(a, (c[b + 944 >> 2] | 0) == 0, 2709);ec(a, (c[a + 964 >> 2] | 0) == 0, 2763);lc(a);b = a + 948 | 0;c[h >> 2] = (c[b >> 2] | 0) + (d << 2);c[g >> 2] = c[h >> 2];mc(b, g, f) | 0;c[(c[f >> 2] | 0) + 944 >> 2] = a;nc(a);l = e;return; + }function lc(a) { + a = a | 0;var b = 0, + d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0;d = $b(a) | 0;if (d | 0 ? (c[(ac(a, 0) | 0) + 944 >> 2] | 0) != (a | 0) : 0) { + e = c[(c[a + 976 >> 2] | 0) + 12 >> 2] | 0;f = a + 948 | 0;g = (e | 0) == 0;b = 0;do { + h = c[(c[f >> 2] | 0) + (b << 2) >> 2] | 0;i = Xb(h) | 0;c[(c[f >> 2] | 0) + (b << 2) >> 2] = i;c[i + 944 >> 2] = a;if (!g) Ib[e & 15](h, i, a, b);b = b + 1 | 0; + } while ((b | 0) != (d | 0)); + }return; + }function mc(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0, + o = 0, + p = 0, + q = 0, + r = 0, + s = 0, + t = 0;s = l;l = l + 64 | 0;n = s + 52 | 0;i = s + 48 | 0;o = s + 28 | 0;p = s + 24 | 0;q = s + 20 | 0;r = s;e = c[a >> 2] | 0;g = e;b = e + ((c[b >> 2] | 0) - g >> 2 << 2) | 0;e = a + 4 | 0;f = c[e >> 2] | 0;h = a + 8 | 0;do if (f >>> 0 < (c[h >> 2] | 0) >>> 0) { + if ((b | 0) == (f | 0)) { + c[b >> 2] = c[d >> 2];c[e >> 2] = (c[e >> 2] | 0) + 4;break; + }ef(a, b, f, b + 4 | 0);if (b >>> 0 <= d >>> 0) d = (c[e >> 2] | 0) >>> 0 > d >>> 0 ? d + 4 | 0 : d;c[b >> 2] = c[d >> 2]; + } else { + e = (f - g >> 2) + 1 | 0;f = le(a) | 0;if (f >>> 0 < e >>> 0) jC(a);m = c[a >> 2] | 0;k = (c[h >> 2] | 0) - m | 0;g = k >> 1;bf(r, k >> 2 >>> 0 < f >>> 1 >>> 0 ? g >>> 0 < e >>> 0 ? e : g : f, b - m >> 2, a + 8 | 0);m = r + 8 | 0;e = c[m >> 2] | 0;g = r + 12 | 0;k = c[g >> 2] | 0;h = k;j = e;do if ((e | 0) == (k | 0)) { + k = r + 4 | 0;e = c[k >> 2] | 0;t = c[r >> 2] | 0;f = t;if (e >>> 0 <= t >>> 0) { + e = h - f >> 1;e = (e | 0) == 0 ? 1 : e;bf(o, e, e >>> 2, c[r + 16 >> 2] | 0);c[p >> 2] = c[k >> 2];c[q >> 2] = c[m >> 2];c[i >> 2] = c[p >> 2];c[n >> 2] = c[q >> 2];gf(o, i, n);e = c[r >> 2] | 0;c[r >> 2] = c[o >> 2];c[o >> 2] = e;e = o + 4 | 0;t = c[k >> 2] | 0;c[k >> 2] = c[e >> 2];c[e >> 2] = t;e = o + 8 | 0;t = c[m >> 2] | 0;c[m >> 2] = c[e >> 2];c[e >> 2] = t;e = o + 12 | 0;t = c[g >> 2] | 0;c[g >> 2] = c[e >> 2];c[e >> 2] = t;df(o);e = c[m >> 2] | 0;break; + }g = e;h = ((g - f >> 2) + 1 | 0) / -2 | 0;i = e + (h << 2) | 0;f = j - g | 0;g = f >> 2;if (g) { + GC(i | 0, e | 0, f | 0) | 0;e = c[k >> 2] | 0; + }t = i + (g << 2) | 0;c[m >> 2] = t;c[k >> 2] = e + (h << 2);e = t; + } while (0);c[e >> 2] = c[d >> 2];c[m >> 2] = (c[m >> 2] | 0) + 4;b = ff(a, r, b) | 0;df(r); + } while (0);l = s;return b | 0; + }function nc(b) { + b = b | 0;var d = 0;do { + d = b + 984 | 0;if (a[d >> 0] | 0) break;a[d >> 0] = 1;g[b + 504 >> 2] = T(t);b = c[b + 944 >> 2] | 0; + } while ((b | 0) != 0);return; + }function oc(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;d = c[a >> 2] | 0;e = d;if (d | 0) { + a = a + 4 | 0;b = c[a >> 2] | 0;if ((b | 0) != (d | 0)) c[a >> 2] = b + (~((b + -4 - e | 0) >>> 2) << 2);sC(d); + }return; + }function pc(a) { + a = a | 0;return c[a + 944 >> 2] | 0; + }function qc(a) { + a = a | 0;ec(a, (c[a + 964 >> 2] | 0) != 0, 2832);nc(a);return; + }function rc(b) { + b = b | 0;return (a[b + 984 >> 0] | 0) != 0 | 0; + }function sc(a, b) { + a = a | 0;b = b | 0;if (BB(a, b, 400) | 0) { + BC(a | 0, b | 0, 400) | 0;nc(a); + }return; + }function tc(a) { + a = a | 0;var b = ib;b = T(g[a + 44 >> 2]);a = Sb(b) | 0;return T(a ? T(0.0) : b); + }function uc(b) { + b = b | 0;var d = ib;d = T(g[b + 48 >> 2]);if (Sb(d) | 0) d = a[(c[b + 976 >> 2] | 0) + 2 >> 0] | 0 ? T(1.0) : T(0.0);return T(d); + }function vc(a, b) { + a = a | 0;b = b | 0;c[a + 980 >> 2] = b;return; + }function wc(a) { + a = a | 0;return c[a + 980 >> 2] | 0; + }function xc(a, b) { + a = a | 0;b = b | 0;var d = 0;d = a + 4 | 0;if ((c[d >> 2] | 0) != (b | 0)) { + c[d >> 2] = b;nc(a); + }return; + }function yc(a) { + a = a | 0;return c[a + 4 >> 2] | 0; + }function zc(a, b) { + a = a | 0;b = b | 0;var d = 0;d = a + 8 | 0;if ((c[d >> 2] | 0) != (b | 0)) { + c[d >> 2] = b;nc(a); + }return; + }function Ac(a) { + a = a | 0;return c[a + 8 >> 2] | 0; + }function Bc(a, b) { + a = a | 0;b = b | 0;var d = 0;d = a + 12 | 0;if ((c[d >> 2] | 0) != (b | 0)) { + c[d >> 2] = b;nc(a); + }return; + }function Cc(a) { + a = a | 0;return c[a + 12 >> 2] | 0; + }function Dc(a, b) { + a = a | 0;b = b | 0;var d = 0;d = a + 16 | 0;if ((c[d >> 2] | 0) != (b | 0)) { + c[d >> 2] = b;nc(a); + }return; + }function Ec(a) { + a = a | 0;return c[a + 16 >> 2] | 0; + }function Fc(a, b) { + a = a | 0;b = b | 0;var d = 0;d = a + 20 | 0;if ((c[d >> 2] | 0) != (b | 0)) { + c[d >> 2] = b;nc(a); + }return; + }function Gc(a) { + a = a | 0;return c[a + 20 >> 2] | 0; + }function Hc(a, b) { + a = a | 0;b = b | 0;var d = 0;d = a + 24 | 0;if ((c[d >> 2] | 0) != (b | 0)) { + c[d >> 2] = b;nc(a); + }return; + }function Ic(a) { + a = a | 0;return c[a + 24 >> 2] | 0; + }function Jc(a, b) { + a = a | 0;b = b | 0;var d = 0;d = a + 28 | 0;if ((c[d >> 2] | 0) != (b | 0)) { + c[d >> 2] = b;nc(a); + }return; + }function Kc(a) { + a = a | 0;return c[a + 28 >> 2] | 0; + }function Lc(a, b) { + a = a | 0;b = b | 0;var d = 0;d = a + 32 | 0;if ((c[d >> 2] | 0) != (b | 0)) { + c[d >> 2] = b;nc(a); + }return; + }function Mc(a) { + a = a | 0;return c[a + 32 >> 2] | 0; + }function Nc(a, b) { + a = a | 0;b = b | 0;var d = 0;d = a + 36 | 0;if ((c[d >> 2] | 0) != (b | 0)) { + c[d >> 2] = b;nc(a); + }return; + }function Oc(a) { + a = a | 0;return c[a + 36 >> 2] | 0; + }function Pc(a, b) { + a = a | 0;b = T(b);var c = 0;c = a + 40 | 0;if (T(g[c >> 2]) != b) { + g[c >> 2] = b;nc(a); + }return; + }function Qc(a, b) { + a = a | 0;b = T(b);var c = 0;c = a + 44 | 0;if (T(g[c >> 2]) != b) { + g[c >> 2] = b;nc(a); + }return; + }function Rc(a, b) { + a = a | 0;b = T(b);var c = 0;c = a + 48 | 0;if (T(g[c >> 2]) != b) { + g[c >> 2] = b;nc(a); + }return; + }function Sc(a, b) { + a = a | 0;b = T(b);var d = 0, + e = 0, + f = 0, + h = 0;h = Sb(b) | 0;d = (h ^ 1) & 1;e = a + 52 | 0;f = a + 56 | 0;if (!(h | T(g[e >> 2]) == b ? (c[f >> 2] | 0) == (d | 0) : 0)) { + g[e >> 2] = b;c[f >> 2] = d;nc(a); + }return; + }function Tc(a, b) { + a = a | 0;b = T(b);var d = 0, + e = 0;e = a + 52 | 0;d = a + 56 | 0;if (!(!(T(g[e >> 2]) != b) ? (c[d >> 2] | 0) == 2 : 0)) { + g[e >> 2] = b;e = Sb(b) | 0;c[d >> 2] = e ? 3 : 2;nc(a); + }return; + }function Uc(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0;e = b + 52 | 0;d = c[e + 4 >> 2] | 0;b = a;c[b >> 2] = c[e >> 2];c[b + 4 >> 2] = d;return; + }function Vc(a, b, d) { + a = a | 0;b = b | 0;d = T(d);var e = 0, + f = 0, + h = 0;h = Sb(d) | 0;e = (h ^ 1) & 1;f = a + 132 + (b << 3) | 0;b = a + 132 + (b << 3) + 4 | 0;if (!(h | T(g[f >> 2]) == d ? (c[b >> 2] | 0) == (e | 0) : 0)) { + g[f >> 2] = d;c[b >> 2] = e;nc(a); + }return; + }function Wc(a, b, d) { + a = a | 0;b = b | 0;d = T(d);var e = 0, + f = 0, + h = 0;h = Sb(d) | 0;e = h ? 0 : 2;f = a + 132 + (b << 3) | 0;b = a + 132 + (b << 3) + 4 | 0;if (!(h | T(g[f >> 2]) == d ? (c[b >> 2] | 0) == (e | 0) : 0)) { + g[f >> 2] = d;c[b >> 2] = e;nc(a); + }return; + }function Xc(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0;e = b + 132 + (d << 3) | 0;b = c[e + 4 >> 2] | 0;d = a;c[d >> 2] = c[e >> 2];c[d + 4 >> 2] = b;return; + }function Yc(a, b, d) { + a = a | 0;b = b | 0;d = T(d);var e = 0, + f = 0, + h = 0;h = Sb(d) | 0;e = (h ^ 1) & 1;f = a + 60 + (b << 3) | 0;b = a + 60 + (b << 3) + 4 | 0;if (!(h | T(g[f >> 2]) == d ? (c[b >> 2] | 0) == (e | 0) : 0)) { + g[f >> 2] = d;c[b >> 2] = e;nc(a); + }return; + }function Zc(a, b, d) { + a = a | 0;b = b | 0;d = T(d);var e = 0, + f = 0, + h = 0;h = Sb(d) | 0;e = h ? 0 : 2;f = a + 60 + (b << 3) | 0;b = a + 60 + (b << 3) + 4 | 0;if (!(h | T(g[f >> 2]) == d ? (c[b >> 2] | 0) == (e | 0) : 0)) { + g[f >> 2] = d;c[b >> 2] = e;nc(a); + }return; + }function _c(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0;e = b + 60 + (d << 3) | 0;b = c[e + 4 >> 2] | 0;d = a;c[d >> 2] = c[e >> 2];c[d + 4 >> 2] = b;return; + }function $c(a, b) { + a = a | 0;b = b | 0;var d = 0;d = a + 60 + (b << 3) + 4 | 0;if ((c[d >> 2] | 0) != 3) { + g[a + 60 + (b << 3) >> 2] = T(t);c[d >> 2] = 3;nc(a); + }return; + }function ad(a, b, d) { + a = a | 0;b = b | 0;d = T(d);var e = 0, + f = 0, + h = 0;h = Sb(d) | 0;e = (h ^ 1) & 1;f = a + 204 + (b << 3) | 0;b = a + 204 + (b << 3) + 4 | 0;if (!(h | T(g[f >> 2]) == d ? (c[b >> 2] | 0) == (e | 0) : 0)) { + g[f >> 2] = d;c[b >> 2] = e;nc(a); + }return; + }function bd(a, b, d) { + a = a | 0;b = b | 0;d = T(d);var e = 0, + f = 0, + h = 0;h = Sb(d) | 0;e = h ? 0 : 2;f = a + 204 + (b << 3) | 0;b = a + 204 + (b << 3) + 4 | 0;if (!(h | T(g[f >> 2]) == d ? (c[b >> 2] | 0) == (e | 0) : 0)) { + g[f >> 2] = d;c[b >> 2] = e;nc(a); + }return; + }function cd(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0;e = b + 204 + (d << 3) | 0;b = c[e + 4 >> 2] | 0;d = a;c[d >> 2] = c[e >> 2];c[d + 4 >> 2] = b;return; + }function dd(a, b, d) { + a = a | 0;b = b | 0;d = T(d);var e = 0, + f = 0, + h = 0;h = Sb(d) | 0;e = (h ^ 1) & 1;f = a + 276 + (b << 3) | 0;b = a + 276 + (b << 3) + 4 | 0;if (!(h | T(g[f >> 2]) == d ? (c[b >> 2] | 0) == (e | 0) : 0)) { + g[f >> 2] = d;c[b >> 2] = e;nc(a); + }return; + }function ed(a, b) { + a = a | 0;b = b | 0;return T(g[a + 276 + (b << 3) >> 2]); + }function fd(a, b) { + a = a | 0;b = T(b);var d = 0, + e = 0, + f = 0, + h = 0;h = Sb(b) | 0;d = (h ^ 1) & 1;e = a + 348 | 0;f = a + 352 | 0;if (!(h | T(g[e >> 2]) == b ? (c[f >> 2] | 0) == (d | 0) : 0)) { + g[e >> 2] = b;c[f >> 2] = d;nc(a); + }return; + }function gd(a, b) { + a = a | 0;b = T(b);var d = 0, + e = 0;e = a + 348 | 0;d = a + 352 | 0;if (!(!(T(g[e >> 2]) != b) ? (c[d >> 2] | 0) == 2 : 0)) { + g[e >> 2] = b;e = Sb(b) | 0;c[d >> 2] = e ? 3 : 2;nc(a); + }return; + }function hd(a) { + a = a | 0;var b = 0;b = a + 352 | 0;if ((c[b >> 2] | 0) != 3) { + g[a + 348 >> 2] = T(t);c[b >> 2] = 3;nc(a); + }return; + }function id(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0;e = b + 348 | 0;d = c[e + 4 >> 2] | 0;b = a;c[b >> 2] = c[e >> 2];c[b + 4 >> 2] = d;return; + }function jd(a, b) { + a = a | 0;b = T(b);var d = 0, + e = 0, + f = 0, + h = 0;h = Sb(b) | 0;d = (h ^ 1) & 1;e = a + 356 | 0;f = a + 360 | 0;if (!(h | T(g[e >> 2]) == b ? (c[f >> 2] | 0) == (d | 0) : 0)) { + g[e >> 2] = b;c[f >> 2] = d;nc(a); + }return; + }function kd(a, b) { + a = a | 0;b = T(b);var d = 0, + e = 0;e = a + 356 | 0;d = a + 360 | 0;if (!(!(T(g[e >> 2]) != b) ? (c[d >> 2] | 0) == 2 : 0)) { + g[e >> 2] = b;e = Sb(b) | 0;c[d >> 2] = e ? 3 : 2;nc(a); + }return; + }function ld(a) { + a = a | 0;var b = 0;b = a + 360 | 0;if ((c[b >> 2] | 0) != 3) { + g[a + 356 >> 2] = T(t);c[b >> 2] = 3;nc(a); + }return; + }function md(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0;e = b + 356 | 0;d = c[e + 4 >> 2] | 0;b = a;c[b >> 2] = c[e >> 2];c[b + 4 >> 2] = d;return; + }function nd(a, b) { + a = a | 0;b = T(b);var d = 0, + e = 0, + f = 0, + h = 0;h = Sb(b) | 0;d = (h ^ 1) & 1;e = a + 364 | 0;f = a + 368 | 0;if (!(h | T(g[e >> 2]) == b ? (c[f >> 2] | 0) == (d | 0) : 0)) { + g[e >> 2] = b;c[f >> 2] = d;nc(a); + }return; + }function od(a, b) { + a = a | 0;b = T(b);var d = 0, + e = 0, + f = 0, + h = 0;h = Sb(b) | 0;d = h ? 0 : 2;e = a + 364 | 0;f = a + 368 | 0;if (!(h | T(g[e >> 2]) == b ? (c[f >> 2] | 0) == (d | 0) : 0)) { + g[e >> 2] = b;c[f >> 2] = d;nc(a); + }return; + }function pd(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0;e = b + 364 | 0;d = c[e + 4 >> 2] | 0;b = a;c[b >> 2] = c[e >> 2];c[b + 4 >> 2] = d;return; + }function qd(a, b) { + a = a | 0;b = T(b);var d = 0, + e = 0, + f = 0, + h = 0;h = Sb(b) | 0;d = (h ^ 1) & 1;e = a + 372 | 0;f = a + 376 | 0;if (!(h | T(g[e >> 2]) == b ? (c[f >> 2] | 0) == (d | 0) : 0)) { + g[e >> 2] = b;c[f >> 2] = d;nc(a); + }return; + }function rd(a, b) { + a = a | 0;b = T(b);var d = 0, + e = 0, + f = 0, + h = 0;h = Sb(b) | 0;d = h ? 0 : 2;e = a + 372 | 0;f = a + 376 | 0;if (!(h | T(g[e >> 2]) == b ? (c[f >> 2] | 0) == (d | 0) : 0)) { + g[e >> 2] = b;c[f >> 2] = d;nc(a); + }return; + }function sd(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0;e = b + 372 | 0;d = c[e + 4 >> 2] | 0;b = a;c[b >> 2] = c[e >> 2];c[b + 4 >> 2] = d;return; + }function td(a, b) { + a = a | 0;b = T(b);var d = 0, + e = 0, + f = 0, + h = 0;h = Sb(b) | 0;d = (h ^ 1) & 1;e = a + 380 | 0;f = a + 384 | 0;if (!(h | T(g[e >> 2]) == b ? (c[f >> 2] | 0) == (d | 0) : 0)) { + g[e >> 2] = b;c[f >> 2] = d;nc(a); + }return; + }function ud(a, b) { + a = a | 0;b = T(b);var d = 0, + e = 0, + f = 0, + h = 0;h = Sb(b) | 0;d = h ? 0 : 2;e = a + 380 | 0;f = a + 384 | 0;if (!(h | T(g[e >> 2]) == b ? (c[f >> 2] | 0) == (d | 0) : 0)) { + g[e >> 2] = b;c[f >> 2] = d;nc(a); + }return; + }function vd(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0;e = b + 380 | 0;d = c[e + 4 >> 2] | 0;b = a;c[b >> 2] = c[e >> 2];c[b + 4 >> 2] = d;return; + }function wd(a, b) { + a = a | 0;b = T(b);var d = 0, + e = 0, + f = 0, + h = 0;h = Sb(b) | 0;d = (h ^ 1) & 1;e = a + 388 | 0;f = a + 392 | 0;if (!(h | T(g[e >> 2]) == b ? (c[f >> 2] | 0) == (d | 0) : 0)) { + g[e >> 2] = b;c[f >> 2] = d;nc(a); + }return; + }function xd(a, b) { + a = a | 0;b = T(b);var d = 0, + e = 0, + f = 0, + h = 0;h = Sb(b) | 0;d = h ? 0 : 2;e = a + 388 | 0;f = a + 392 | 0;if (!(h | T(g[e >> 2]) == b ? (c[f >> 2] | 0) == (d | 0) : 0)) { + g[e >> 2] = b;c[f >> 2] = d;nc(a); + }return; + }function yd(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0;e = b + 388 | 0;d = c[e + 4 >> 2] | 0;b = a;c[b >> 2] = c[e >> 2];c[b + 4 >> 2] = d;return; + }function zd(a, b) { + a = a | 0;b = T(b);var c = 0;c = a + 396 | 0;if (T(g[c >> 2]) != b) { + g[c >> 2] = b;nc(a); + }return; + }function Ad(a) { + a = a | 0;return T(g[a + 396 >> 2]); + }function Bd(a) { + a = a | 0;return T(g[a + 400 >> 2]); + }function Cd(a) { + a = a | 0;return T(g[a + 404 >> 2]); + }function Dd(a) { + a = a | 0;return T(g[a + 408 >> 2]); + }function Ed(a) { + a = a | 0;return T(g[a + 412 >> 2]); + }function Fd(a) { + a = a | 0;return T(g[a + 416 >> 2]); + }function Gd(a) { + a = a | 0;return T(g[a + 420 >> 2]); + }function Hd(a, b) { + a = a | 0;b = b | 0;ec(a, (b | 0) < 6, 2918);switch (b | 0) {case 0: + { + b = (c[a + 496 >> 2] | 0) == 2 ? 5 : 4;break; + }case 2: + { + b = (c[a + 496 >> 2] | 0) == 2 ? 4 : 5;break; + }default: +}return T(g[a + 424 + (b << 2) >> 2]); + }function Id(a, b) { + a = a | 0;b = b | 0;ec(a, (b | 0) < 6, 2918);switch (b | 0) {case 0: + { + b = (c[a + 496 >> 2] | 0) == 2 ? 5 : 4;break; + }case 2: + { + b = (c[a + 496 >> 2] | 0) == 2 ? 4 : 5;break; + }default: +}return T(g[a + 448 + (b << 2) >> 2]); + }function Jd(a, b) { + a = a | 0;b = b | 0;ec(a, (b | 0) < 6, 2918);switch (b | 0) {case 0: + { + b = (c[a + 496 >> 2] | 0) == 2 ? 5 : 4;break; + }case 2: + { + b = (c[a + 496 >> 2] | 0) == 2 ? 4 : 5;break; + }default: +}return T(g[a + 472 + (b << 2) >> 2]); + }function Kd(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = ib;d = c[a + 4 >> 2] | 0;if ((d | 0) == (c[b + 4 >> 2] | 0)) { + if (!d) a = 1;else { + e = T(g[a >> 2]);a = T(C(T(e - T(g[b >> 2])))) < T(.0000999999974); + } + } else a = 0;return a | 0; + }function Ld(a, b) { + a = T(a);b = T(b);var c = 0;if (Sb(a) | 0) c = Sb(b) | 0;else c = T(C(T(a - b))) < T(.0000999999974);return c | 0; + }function Md(a, b) { + a = a | 0;b = b | 0;Nd(a, b);return; + }function Nd(b, d) { + b = b | 0;d = d | 0;var e = 0, + f = 0;e = l;l = l + 16 | 0;f = e + 4 | 0;c[f >> 2] = 0;c[f + 4 >> 2] = 0;c[f + 8 >> 2] = 0;Ka(f | 0, b | 0, d | 0, 0);Vd(b, 3, (a[f + 11 >> 0] | 0) < 0 ? c[f >> 2] | 0 : f, e);tC(f);l = e;return; + }function Od(a, b, c, d) { + a = T(a);b = T(b);c = c | 0;d = d | 0;var e = ib;a = T(a * b);e = T(gC(a, T(1.0)));do if (!(Ld(e, T(0.0)) | 0)) { + a = T(a - e);if (Ld(e, T(1.0)) | 0) { + a = T(a + T(1.0));break; + }if (c) { + a = T(a + T(1.0));break; + }if (!d) { + if (e > T(.5)) e = T(1.0);else { + d = Ld(e, T(.5)) | 0;e = d ? T(1.0) : T(0.0); + }a = T(a + e); + } + } else a = T(a - e); while (0);return T(a / b); + }function Pd(a, b, c, d, e, f, h, i, j, k, l, m, n) { + a = a | 0;b = T(b);c = c | 0;d = T(d);e = e | 0;f = T(f);h = h | 0;i = T(i);j = T(j);k = T(k);l = T(l);m = T(m);n = n | 0;var o = 0, + p = ib, + q = ib, + r = ib, + s = ib, + t = ib, + u = ib;if (j < T(0.0) | k < T(0.0)) n = 0;else { + if ((n | 0) != 0 ? (p = T(g[n + 4 >> 2]), p != T(0.0)) : 0) { + r = T(Od(b, p, 0, 0));s = T(Od(d, p, 0, 0));q = T(Od(f, p, 0, 0));p = T(Od(i, p, 0, 0)); + } else { + q = f;r = b;p = i;s = d; + }if ((e | 0) == (a | 0)) o = Ld(q, r) | 0;else o = 0;if ((h | 0) == (c | 0)) n = Ld(p, s) | 0;else n = 0;if ((!o ? (t = T(b - l), !(Qd(a, t, j) | 0)) : 0) ? !(Rd(a, t, e, j) | 0) : 0) o = Sd(a, t, e, f, j) | 0;else o = 1;if ((!n ? (u = T(d - m), !(Qd(c, u, k) | 0)) : 0) ? !(Rd(c, u, h, k) | 0) : 0) n = Sd(c, u, h, i, k) | 0;else n = 1;n = o & n; + }return n | 0; + }function Qd(a, b, c) { + a = a | 0;b = T(b);c = T(c);if ((a | 0) == 1) a = Ld(b, c) | 0;else a = 0;return a | 0; + }function Rd(a, b, c, d) { + a = a | 0;b = T(b);c = c | 0;d = T(d);if ((a | 0) == 2 & (c | 0) == 0) { + if (!(b >= d)) a = Ld(b, d) | 0;else a = 1; + } else a = 0;return a | 0; + }function Sd(a, b, c, d, e) { + a = a | 0;b = T(b);c = c | 0;d = T(d);e = T(e);if ((a | 0) == 2 & (c | 0) == 2 & d > b) { + if (!(e <= b)) a = Ld(b, e) | 0;else a = 1; + } else a = 0;return a | 0; + }function Td(b, d, e, f, i, j, k, m, n, o, p) { + b = b | 0;d = T(d);e = T(e);f = f | 0;i = i | 0;j = j | 0;k = T(k);m = T(m);n = n | 0;o = o | 0;p = p | 0;var q = 0, + r = 0, + s = 0, + t = 0, + u = ib, + v = ib, + w = 0, + x = 0, + y = 0, + z = 0, + A = 0, + B = 0, + C = 0, + D = 0, + E = 0, + F = 0, + G = 0, + H = ib, + I = ib, + J = ib, + K = 0.0, + L = 0.0;G = l;l = l + 160 | 0;D = G + 152 | 0;C = G + 120 | 0;B = G + 104 | 0;y = G + 72 | 0;t = G + 56 | 0;A = G + 8 | 0;x = G;z = (c[2279] | 0) + 1 | 0;c[2279] = z;E = b + 984 | 0;if ((a[E >> 0] | 0) != 0 ? (c[b + 512 >> 2] | 0) != (c[2278] | 0) : 0) w = 4;else if ((c[b + 516 >> 2] | 0) == (f | 0)) F = 0;else w = 4;if ((w | 0) == 4) { + c[b + 520 >> 2] = 0;c[b + 924 >> 2] = -1;c[b + 928 >> 2] = -1;g[b + 932 >> 2] = T(-1.0);g[b + 936 >> 2] = T(-1.0);F = 1; + }a: do if (!(c[b + 964 >> 2] | 0)) { + if (n) { + q = b + 916 | 0;if (!(Ld(T(g[q >> 2]), d) | 0)) { + w = 21;break; + }if (!(Ld(T(g[b + 920 >> 2]), e) | 0)) { + w = 21;break; + }if ((c[b + 924 >> 2] | 0) != (i | 0)) { + w = 21;break; + }q = (c[b + 928 >> 2] | 0) == (j | 0) ? q : 0;w = 22;break; + }s = c[b + 520 >> 2] | 0;if (!s) w = 21;else { + r = 0;while (1) { + q = b + 524 + (r * 24 | 0) | 0;if (((Ld(T(g[q >> 2]), d) | 0 ? Ld(T(g[b + 524 + (r * 24 | 0) + 4 >> 2]), e) | 0 : 0) ? (c[b + 524 + (r * 24 | 0) + 8 >> 2] | 0) == (i | 0) : 0) ? (c[b + 524 + (r * 24 | 0) + 12 >> 2] | 0) == (j | 0) : 0) { + w = 22;break a; + }r = r + 1 | 0;if (r >>> 0 >= s >>> 0) { + w = 21;break; + } + } + } + } else { + u = T(Ud(b, 2, k));v = T(Ud(b, 0, k));q = b + 916 | 0;J = T(g[q >> 2]);I = T(g[b + 920 >> 2]);H = T(g[b + 932 >> 2]);if (!(Pd(i, d, j, e, c[b + 924 >> 2] | 0, J, c[b + 928 >> 2] | 0, I, H, T(g[b + 936 >> 2]), u, v, p) | 0)) { + s = c[b + 520 >> 2] | 0;if (!s) w = 21;else { + r = 0;while (1) { + q = b + 524 + (r * 24 | 0) | 0;H = T(g[q >> 2]);I = T(g[b + 524 + (r * 24 | 0) + 4 >> 2]);J = T(g[b + 524 + (r * 24 | 0) + 16 >> 2]);if (Pd(i, d, j, e, c[b + 524 + (r * 24 | 0) + 8 >> 2] | 0, H, c[b + 524 + (r * 24 | 0) + 12 >> 2] | 0, I, J, T(g[b + 524 + (r * 24 | 0) + 20 >> 2]), u, v, p) | 0) { + w = 22;break a; + }r = r + 1 | 0;if (r >>> 0 >= s >>> 0) { + w = 21;break; + } + } + } + } else w = 22; + } while (0);do if ((w | 0) == 21) { + if (!(a[11697] | 0)) { + q = 0;w = 31; + } else { + q = 0;w = 28; + } + } else if ((w | 0) == 22) { + r = (a[11697] | 0) != 0;if (!((q | 0) != 0 & (F ^ 1))) if (r) { + w = 28;break; + } else { + w = 31;break; + }t = q + 16 | 0;c[b + 908 >> 2] = c[t >> 2];s = q + 20 | 0;c[b + 912 >> 2] = c[s >> 2];if (!((a[11698] | 0) == 0 | r ^ 1)) { + c[x >> 2] = Wd(z) | 0;c[x + 4 >> 2] = z;Vd(b, 4, 2972, x);r = c[b + 972 >> 2] | 0;if (r | 0) nb[r & 127](b);i = Xd(i, n) | 0;j = Xd(j, n) | 0;L = +T(g[t >> 2]);K = +T(g[s >> 2]);c[A >> 2] = i;c[A + 4 >> 2] = j;h[A + 8 >> 3] = +d;h[A + 16 >> 3] = +e;h[A + 24 >> 3] = L;h[A + 32 >> 3] = K;c[A + 40 >> 2] = o;Vd(b, 4, 2989, A); + } + } while (0);if ((w | 0) == 28) { + r = Wd(z) | 0;c[t >> 2] = r;c[t + 4 >> 2] = z;c[t + 8 >> 2] = F ? 3047 : 11699;Vd(b, 4, 3038, t);r = c[b + 972 >> 2] | 0;if (r | 0) nb[r & 127](b);A = Xd(i, n) | 0;w = Xd(j, n) | 0;c[y >> 2] = A;c[y + 4 >> 2] = w;h[y + 8 >> 3] = +d;h[y + 16 >> 3] = +e;c[y + 24 >> 2] = o;Vd(b, 4, 3049, y);w = 31; + }if ((w | 0) == 31) { + Yd(b, d, e, f, i, j, k, m, n, p);if (a[11697] | 0) { + r = c[2279] | 0;A = Wd(r) | 0;c[B >> 2] = A;c[B + 4 >> 2] = r;c[B + 8 >> 2] = F ? 3047 : 11699;Vd(b, 4, 3083, B);r = c[b + 972 >> 2] | 0;if (r | 0) nb[r & 127](b);A = Xd(i, n) | 0;B = Xd(j, n) | 0;K = +T(g[b + 908 >> 2]);L = +T(g[b + 912 >> 2]);c[C >> 2] = A;c[C + 4 >> 2] = B;h[C + 8 >> 3] = K;h[C + 16 >> 3] = L;c[C + 24 >> 2] = o;Vd(b, 4, 3092, C); + }c[b + 516 >> 2] = f;if (!q) { + r = b + 520 | 0;q = c[r >> 2] | 0;if ((q | 0) == 16) { + if (a[11697] | 0) Vd(b, 4, 3124, D);c[r >> 2] = 0;q = 0; + }if (n) q = b + 916 | 0;else { + c[r >> 2] = q + 1;q = b + 524 + (q * 24 | 0) | 0; + }g[q >> 2] = d;g[q + 4 >> 2] = e;c[q + 8 >> 2] = i;c[q + 12 >> 2] = j;c[q + 16 >> 2] = c[b + 908 >> 2];c[q + 20 >> 2] = c[b + 912 >> 2];q = 0; + } + }if (n) { + c[b + 416 >> 2] = c[b + 908 >> 2];c[b + 420 >> 2] = c[b + 912 >> 2];a[b + 985 >> 0] = 1;a[E >> 0] = 0; + }c[2279] = (c[2279] | 0) + -1;c[b + 512 >> 2] = c[2278];l = G;return F | (q | 0) == 0 | 0; + }function Ud(a, b, c) { + a = a | 0;b = b | 0;c = T(c);var d = ib;d = T(me(a, b, c));return T(d + T(ne(a, b, c))); + }function Vd(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0;g = l;l = l + 16 | 0;f = g;c[f >> 2] = e;if (!a) e = 0;else e = c[a + 976 >> 2] | 0;ge(e, a, b, d, f);l = g;return; + }function Wd(a) { + a = a | 0;return (a >>> 0 > 60 ? 3201 : 3201 + (60 - a) | 0) | 0; + }function Xd(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0;f = l;l = l + 32 | 0;d = f + 12 | 0;e = f;c[d >> 2] = c[254];c[d + 4 >> 2] = c[255];c[d + 8 >> 2] = c[256];c[e >> 2] = c[257];c[e + 4 >> 2] = c[258];c[e + 8 >> 2] = c[259];if ((a | 0) > 2) a = 11699;else a = c[(b ? e : d) + (a << 2) >> 2] | 0;l = f;return a | 0; + }function Yd(b, e, f, h, i, k, m, n, o, p) { + b = b | 0;e = T(e);f = T(f);h = h | 0;i = i | 0;k = k | 0;m = T(m);n = T(n);o = o | 0;p = p | 0;var q = 0, + r = 0, + s = 0, + t = 0, + u = ib, + v = ib, + w = ib, + x = ib, + y = ib, + z = ib, + A = ib, + B = 0, + C = 0, + D = 0, + E = ib, + F = ib, + G = 0, + H = ib, + I = 0, + J = 0, + K = 0, + L = 0, + M = 0, + N = 0, + O = 0, + P = 0, + Q = 0, + R = 0, + S = 0, + U = 0, + V = 0, + W = 0, + X = 0, + Y = 0, + Z = 0, + _ = 0, + $ = ib, + aa = ib, + ba = ib, + ca = ib, + da = ib, + ea = 0, + fa = 0, + ga = 0, + ha = 0, + ia = 0, + ja = ib, + ka = ib, + la = ib, + ma = ib, + na = ib, + oa = ib, + pa = 0, + qa = ib, + ra = ib, + sa = ib, + ta = ib, + ua = ib, + va = ib, + wa = 0, + xa = 0, + ya = ib, + za = ib, + Aa = 0, + Ba = 0, + Ca = 0, + Da = 0, + Ea = ib, + Fa = 0, + Ga = 0, + Ha = 0, + Ia = 0, + Ja = 0, + Ka = 0, + La = 0, + Ma = ib, + Na = 0, + Oa = 0;La = l;l = l + 16 | 0;ea = La + 12 | 0;fa = La + 8 | 0;ga = La + 4 | 0;ha = La;ec(b, (i | 0) == 0 | (Sb(e) | 0) ^ 1, 3326);ec(b, (k | 0) == 0 | (Sb(f) | 0) ^ 1, 3406);Ga = qe(b, h) | 0;c[b + 496 >> 2] = Ga;Ja = re(2, Ga) | 0;Ka = re(0, Ga) | 0;g[b + 440 >> 2] = T(me(b, Ja, m));g[b + 444 >> 2] = T(ne(b, Ja, m));g[b + 428 >> 2] = T(me(b, Ka, m));g[b + 436 >> 2] = T(ne(b, Ka, m));g[b + 464 >> 2] = T(se(b, Ja));g[b + 468 >> 2] = T(te(b, Ja));g[b + 452 >> 2] = T(se(b, Ka));g[b + 460 >> 2] = T(te(b, Ka));g[b + 488 >> 2] = T(ue(b, Ja, m));g[b + 492 >> 2] = T(ve(b, Ja, m));g[b + 476 >> 2] = T(ue(b, Ka, m));g[b + 484 >> 2] = T(ve(b, Ka, m));do if (!(c[b + 964 >> 2] | 0)) { + Ha = b + 948 | 0;Ia = (c[b + 952 >> 2] | 0) - (c[Ha >> 2] | 0) >> 2;if (!Ia) { + xe(b, e, f, i, k, m, n);break; + }if (!o ? ye(b, e, f, i, k, m, n) | 0 : 0) break;lc(b);Y = b + 508 | 0;a[Y >> 0] = 0;Ja = re(c[b + 4 >> 2] | 0, Ga) | 0;Ka = ze(Ja, Ga) | 0;Fa = oe(Ja) | 0;Z = c[b + 8 >> 2] | 0;Ba = b + 28 | 0;_ = (c[Ba >> 2] | 0) != 0;ua = Fa ? m : n;ya = Fa ? n : m;$ = T(Ae(b, Ja, m));aa = T(Be(b, Ja, m));u = T(Ae(b, Ka, m));va = T(Ce(b, Ja, m));za = T(Ce(b, Ka, m));D = Fa ? i : k;Aa = Fa ? k : i;Ea = Fa ? va : za;y = Fa ? za : va;ta = T(Ud(b, 2, m));x = T(Ud(b, 0, m));v = T(T(be(b + 364 | 0, m)) - Ea);w = T(T(be(b + 380 | 0, m)) - Ea);z = T(T(be(b + 372 | 0, n)) - y);A = T(T(be(b + 388 | 0, n)) - y);ba = Fa ? v : z;ca = Fa ? w : A;ta = T(e - ta);e = T(ta - Ea);if (Sb(e) | 0) Ea = e;else Ea = T(cC(T(eC(e, w)), v));ra = T(f - x);e = T(ra - y);if (Sb(e) | 0) sa = e;else sa = T(cC(T(eC(e, A)), z));v = Fa ? Ea : sa;qa = Fa ? sa : Ea;a: do if ((D | 0) == 1) { + h = 0;r = 0;while (1) { + q = ac(b, r) | 0;if (!h) { + if (T(Ee(q)) > T(0.0) ? T(Fe(q)) > T(0.0) : 0) h = q;else h = 0; + } else if (De(q) | 0) { + t = 0;break a; + }r = r + 1 | 0;if (r >>> 0 >= Ia >>> 0) { + t = h;break; + } + } + } else t = 0; while (0);B = t + 500 | 0;C = t + 504 | 0;h = 0;q = 0;e = T(0.0);s = 0;do { + r = c[(c[Ha >> 2] | 0) + (s << 2) >> 2] | 0;if ((c[r + 36 >> 2] | 0) == 1) { + Ge(r);a[r + 985 >> 0] = 1;a[r + 984 >> 0] = 0; + } else { + $d(r);if (o) ce(r, qe(r, Ga) | 0, v, qa, Ea);do if ((c[r + 24 >> 2] | 0) != 1) { + if ((r | 0) == (t | 0)) { + c[B >> 2] = c[2278];g[C >> 2] = T(0.0);break; + } else { + He(b, r, Ea, i, sa, Ea, sa, k, Ga, p);break; + } + } else { + if (q | 0) c[q + 960 >> 2] = r;c[r + 960 >> 2] = 0;q = r;h = (h | 0) == 0 ? r : h; + } while (0);oa = T(g[r + 504 >> 2]);e = T(e + T(oa + T(Ud(r, Ja, Ea)))); + }s = s + 1 | 0; + } while ((s | 0) != (Ia | 0));K = e > v;pa = _ & ((D | 0) == 2 & K) ? 1 : D;I = (Aa | 0) == 1;M = I & (o ^ 1);N = (pa | 0) == 1;O = (pa | 0) == 2;P = 976 + (Ja << 2) | 0;Q = (Aa | 2 | 0) == 2;W = I & (_ ^ 1);R = 1040 + (Ka << 2) | 0;S = 1040 + (Ja << 2) | 0;U = 976 + (Ka << 2) | 0;V = (Aa | 0) != 1;K = _ & ((D | 0) != 0 & K);J = b + 976 | 0;I = I ^ 1;e = v;G = 0;L = 0;oa = T(0.0);da = T(0.0);while (1) { + b: do if (G >>> 0 < Ia >>> 0) { + C = c[Ha >> 2] | 0;s = 0;A = T(0.0);z = T(0.0);w = T(0.0);v = T(0.0);r = 0;q = 0;t = G;while (1) { + B = c[C + (t << 2) >> 2] | 0;if ((c[B + 36 >> 2] | 0) != 1 ? (c[B + 940 >> 2] = L, (c[B + 24 >> 2] | 0) != 1) : 0) { + x = T(Ud(B, Ja, Ea));X = c[P >> 2] | 0;f = T(be(B + 380 + (X << 3) | 0, ua));y = T(g[B + 504 >> 2]);f = T(eC(f, y));f = T(cC(T(be(B + 364 + (X << 3) | 0, ua)), f));if (_ & (s | 0) != 0 & T(x + T(z + f)) > e) { + k = s;x = A;D = t;break b; + }x = T(x + f);f = T(z + x);x = T(A + x);if (De(B) | 0) { + w = T(w + T(Ee(B)));v = T(v - T(y * T(Fe(B)))); + }if (q | 0) c[q + 960 >> 2] = B;c[B + 960 >> 2] = 0;s = s + 1 | 0;q = B;r = (r | 0) == 0 ? B : r; + } else { + x = A;f = z; + }t = t + 1 | 0;if (t >>> 0 < Ia >>> 0) { + A = x;z = f; + } else { + k = s;D = t;break; + } + } + } else { + k = 0;x = T(0.0);w = T(0.0);v = T(0.0);r = 0;D = G; + } while (0);X = w > T(0.0) & w < T(1.0);E = X ? T(1.0) : w;X = v > T(0.0) & v < T(1.0);A = X ? T(1.0) : v;do if (!N) { + if (!(x < ba & ((Sb(ba) | 0) ^ 1))) { + if (!(x > ca & ((Sb(ca) | 0) ^ 1))) { + if (!(a[(c[J >> 2] | 0) + 3 >> 0] | 0)) { + if (!(E == T(0.0)) ? !(T(Ee(b)) == T(0.0)) : 0) { + X = 53;break; + }e = x;X = 53; + } else X = 51; + } else { + e = ca;X = 51; + } + } else { + e = ba;X = 51; + } + } else X = 51; while (0);if ((X | 0) == 51) { + X = 0;if (Sb(e) | 0) X = 53;else { + F = T(e - x);H = e; + } + }if ((X | 0) == 53) { + X = 0;if (x < T(0.0)) { + F = T(-x);H = e; + } else { + F = T(0.0);H = e; + } + }if (!M ? (ia = (r | 0) == 0, !ia) : 0) { + s = c[P >> 2] | 0;t = F < T(0.0);y = T(F / A);B = F > T(0.0);z = T(F / E);w = T(0.0);x = T(0.0);e = T(0.0);q = r;do { + f = T(be(q + 380 + (s << 3) | 0, ua));v = T(be(q + 364 + (s << 3) | 0, ua));v = T(eC(f, T(cC(v, T(g[q + 504 >> 2])))));if (t) { + f = T(v * T(Fe(q)));if (f != T(-0.0) ? (Ma = T(v - T(y * f)), ja = T(Ie(q, Ja, Ma, H, Ea)), Ma != ja) : 0) { + w = T(w - T(ja - v));e = T(e + f); + } + } else if ((B ? (ka = T(Ee(q)), ka != T(0.0)) : 0) ? (Ma = T(v + T(z * ka)), la = T(Ie(q, Ja, Ma, H, Ea)), Ma != la) : 0) { + w = T(w - T(la - v));x = T(x - ka); + }q = c[q + 960 >> 2] | 0; + } while ((q | 0) != 0);e = T(A + e);v = T(F + w);if (!ia) { + y = T(E + x);t = c[P >> 2] | 0;B = v < T(0.0);C = e == T(0.0);z = T(v / e);s = v > T(0.0);y = T(v / y);e = T(0.0);do { + Ma = T(be(r + 380 + (t << 3) | 0, ua));w = T(be(r + 364 + (t << 3) | 0, ua));w = T(eC(Ma, T(cC(w, T(g[r + 504 >> 2])))));if (B) { + Ma = T(w * T(Fe(r)));v = T(-Ma);if (Ma != T(-0.0)) { + Ma = T(z * v);v = T(Ie(r, Ja, T(w + (C ? v : Ma)), H, Ea)); + } else v = w; + } else if (s ? (ma = T(Ee(r)), ma != T(0.0)) : 0) v = T(Ie(r, Ja, T(w + T(y * ma)), H, Ea));else v = w;e = T(e - T(v - w));x = T(Ud(r, Ja, Ea));f = T(Ud(r, Ka, Ea));v = T(v + x);g[fa >> 2] = v;c[ha >> 2] = 1;w = T(g[r + 396 >> 2]);c: do if (Sb(w) | 0) { + q = Sb(qa) | 0;do if (!q) { + if (K | (ae(r, Ka, qa) | 0 | I)) break;if ((Je(b, r) | 0) != 4) break;if ((c[(Ke(r, Ka) | 0) + 4 >> 2] | 0) == 3) break;if ((c[(Le(r, Ka) | 0) + 4 >> 2] | 0) == 3) break;g[ea >> 2] = qa;c[ga >> 2] = 1;break c; + } while (0);if (ae(r, Ka, qa) | 0) { + q = c[r + 992 + (c[U >> 2] << 2) >> 2] | 0;Ma = T(f + T(be(q, qa)));g[ea >> 2] = Ma;q = V & (c[q + 4 >> 2] | 0) == 2;c[ga >> 2] = ((Sb(Ma) | 0 | q) ^ 1) & 1;break; + } else { + g[ea >> 2] = qa;c[ga >> 2] = q ? 0 : 2;break; + } + } else { + Ma = T(v - x);E = T(Ma / w);Ma = T(w * Ma);c[ga >> 2] = 1;g[ea >> 2] = T(f + (Fa ? E : Ma)); + } while (0);Me(r, Ja, H, Ea, ha, fa);Me(r, Ka, qa, Ea, ga, ea);do if (!(ae(r, Ka, qa) | 0) ? (Je(b, r) | 0) == 4 : 0) { + if ((c[(Ke(r, Ka) | 0) + 4 >> 2] | 0) == 3) { + q = 0;break; + }q = (c[(Le(r, Ka) | 0) + 4 >> 2] | 0) != 3; + } else q = 0; while (0);Ma = T(g[fa >> 2]);E = T(g[ea >> 2]);Na = c[ha >> 2] | 0;Oa = c[ga >> 2] | 0;Td(r, Fa ? Ma : E, Fa ? E : Ma, Ga, Fa ? Na : Oa, Fa ? Oa : Na, Ea, sa, o & (q ^ 1), 3488, p) | 0;a[Y >> 0] = a[Y >> 0] | a[r + 508 >> 0];r = c[r + 960 >> 2] | 0; + } while ((r | 0) != 0); + } else e = T(0.0); + } else e = T(0.0);e = T(F + e);Oa = e < T(0.0) & 1;a[Y >> 0] = Oa | d[Y >> 0];if (O & e > T(0.0)) { + q = c[P >> 2] | 0;if ((c[b + 364 + (q << 3) + 4 >> 2] | 0) != 0 ? (na = T(be(b + 364 + (q << 3) | 0, ua)), na >= T(0.0)) : 0) v = T(cC(T(0.0), T(na - T(H - e))));else v = T(0.0); + } else v = e;B = G >>> 0 < D >>> 0;if (B) { + t = c[Ha >> 2] | 0;s = G;q = 0;do { + r = c[t + (s << 2) >> 2] | 0;if (!(c[r + 24 >> 2] | 0)) { + q = ((c[(Ke(r, Ja) | 0) + 4 >> 2] | 0) == 3 & 1) + q | 0;q = q + ((c[(Le(r, Ja) | 0) + 4 >> 2] | 0) == 3 & 1) | 0; + }s = s + 1 | 0; + } while ((s | 0) != (D | 0));if (q) { + x = T(0.0);f = T(0.0); + } else X = 101; + } else X = 101;d: do if ((X | 0) == 101) { + X = 0;switch (Z | 0) {case 1: + { + q = 0;x = T(v * T(.5));f = T(0.0);break d; + }case 2: + { + q = 0;x = v;f = T(0.0);break d; + }case 3: + { + if (k >>> 0 <= 1) { + q = 0;x = T(0.0);f = T(0.0);break d; + }f = T((k + -1 | 0) >>> 0);q = 0;x = T(0.0);f = T(T(cC(v, T(0.0))) / f);break d; + }case 5: + { + f = T(v / T((k + 1 | 0) >>> 0));q = 0;x = f;break d; + }case 4: + { + f = T(v / T(k >>> 0));q = 0;x = T(f * T(.5));break d; + }default: + { + q = 0;x = T(0.0);f = T(0.0);break d; + }} + } while (0);e = T($ + x);if (B) { + w = T(v / T(q | 0));s = c[Ha >> 2] | 0;r = G;v = T(0.0);do { + q = c[s + (r << 2) >> 2] | 0;e: do if ((c[q + 36 >> 2] | 0) != 1) { + switch (c[q + 24 >> 2] | 0) {case 1: + { + if (Ne(q, Ja) | 0) { + if (!o) break e;Ma = T(Oe(q, Ja, H));Ma = T(Ma + T(se(b, Ja)));Ma = T(Ma + T(me(q, Ja, Ea)));g[q + 400 + (c[S >> 2] << 2) >> 2] = Ma;break e; + }break; + }case 0: + { + Oa = (c[(Ke(q, Ja) | 0) + 4 >> 2] | 0) == 3;Ma = T(w + e);e = Oa ? Ma : e;if (o) { + Oa = q + 400 + (c[S >> 2] << 2) | 0;g[Oa >> 2] = T(e + T(g[Oa >> 2])); + }Oa = (c[(Le(q, Ja) | 0) + 4 >> 2] | 0) == 3;Ma = T(w + e);e = Oa ? Ma : e;if (M) { + Ma = T(f + T(Ud(q, Ja, Ea)));v = qa;e = T(e + T(Ma + T(g[q + 504 >> 2])));break e; + } else { + e = T(e + T(f + T(Pe(q, Ja, Ea))));v = T(cC(v, T(Pe(q, Ka, Ea))));break e; + } + }default: +}if (o) { + Ma = T(x + T(se(b, Ja)));Oa = q + 400 + (c[S >> 2] << 2) | 0;g[Oa >> 2] = T(Ma + T(g[Oa >> 2])); + } + } while (0);r = r + 1 | 0; + } while ((r | 0) != (D | 0)); + } else v = T(0.0);f = T(aa + e);if (Q) x = T(T(Ie(b, Ka, T(za + v), ya, m)) - za);else x = qa;w = T(T(Ie(b, Ka, T(za + (W ? qa : v)), ya, m)) - za);if (B & o) { + r = G;do { + s = c[(c[Ha >> 2] | 0) + (r << 2) >> 2] | 0;do if ((c[s + 36 >> 2] | 0) != 1) { + if ((c[s + 24 >> 2] | 0) == 1) { + if (Ne(s, Ka) | 0) { + Ma = T(Oe(s, Ka, qa));Ma = T(Ma + T(se(b, Ka)));Ma = T(Ma + T(me(s, Ka, Ea)));q = c[R >> 2] | 0;g[s + 400 + (q << 2) >> 2] = Ma;if (!(Sb(Ma) | 0)) break; + } else q = c[R >> 2] | 0;Ma = T(se(b, Ka));g[s + 400 + (q << 2) >> 2] = T(Ma + T(me(s, Ka, Ea)));break; + }q = Je(b, s) | 0;do if ((q | 0) == 4) { + if ((c[(Ke(s, Ka) | 0) + 4 >> 2] | 0) == 3) { + X = 139;break; + }if ((c[(Le(s, Ka) | 0) + 4 >> 2] | 0) == 3) { + X = 139;break; + }if (ae(s, Ka, qa) | 0) { + e = u;break; + }Na = c[s + 908 + (c[P >> 2] << 2) >> 2] | 0;c[ea >> 2] = Na;e = T(g[s + 396 >> 2]);Oa = Sb(e) | 0;v = (c[j >> 2] = Na, T(g[j >> 2]));if (Oa) e = w;else { + F = T(Ud(s, Ka, Ea));Ma = T(v / e);e = T(e * v);e = T(F + (Fa ? Ma : e)); + }g[fa >> 2] = e;g[ea >> 2] = T(T(Ud(s, Ja, Ea)) + v);c[ga >> 2] = 1;c[ha >> 2] = 1;Me(s, Ja, H, Ea, ga, ea);Me(s, Ka, qa, Ea, ha, fa);e = T(g[ea >> 2]);F = T(g[fa >> 2]);Ma = Fa ? e : F;e = Fa ? F : e;Oa = ((Sb(Ma) | 0) ^ 1) & 1;Td(s, Ma, e, Ga, Oa, ((Sb(e) | 0) ^ 1) & 1, Ea, sa, 1, 3493, p) | 0;e = u; + } else X = 139; while (0);f: do if ((X | 0) == 139) { + X = 0;e = T(x - T(Pe(s, Ka, Ea)));do if ((c[(Ke(s, Ka) | 0) + 4 >> 2] | 0) == 3) { + if ((c[(Le(s, Ka) | 0) + 4 >> 2] | 0) != 3) break;e = T(u + T(cC(T(0.0), T(e * T(.5)))));break f; + } while (0);if ((c[(Le(s, Ka) | 0) + 4 >> 2] | 0) == 3) { + e = u;break; + }if ((c[(Ke(s, Ka) | 0) + 4 >> 2] | 0) == 3) { + e = T(u + T(cC(T(0.0), e)));break; + }switch (q | 0) {case 1: + { + e = u;break f; + }case 2: + { + e = T(u + T(e * T(.5)));break f; + }default: + { + e = T(u + e);break f; + }} + } while (0);Ma = T(oa + e);Oa = s + 400 + (c[R >> 2] << 2) | 0;g[Oa >> 2] = T(Ma + T(g[Oa >> 2])); + } while (0);r = r + 1 | 0; + } while ((r | 0) != (D | 0)); + }oa = T(oa + w);da = T(cC(da, f));k = L + 1 | 0;if (D >>> 0 >= Ia >>> 0) break;else { + e = H;G = D;L = k; + } + }do if (o) { + q = k >>> 0 > 1;if (!q ? !(Qe(b) | 0) : 0) break;if (!(Sb(qa) | 0)) { + e = T(qa - oa);g: do switch (c[b + 12 >> 2] | 0) {case 3: + { + u = T(u + e);z = T(0.0);break; + }case 2: + { + u = T(u + T(e * T(.5)));z = T(0.0);break; + }case 4: + { + if (qa > oa) z = T(e / T(k >>> 0));else z = T(0.0);break; + }case 7: + if (qa > oa) { + u = T(u + T(e / T(k << 1 >>> 0)));z = T(e / T(k >>> 0));z = q ? z : T(0.0);break g; + } else { + u = T(u + T(e * T(.5)));z = T(0.0);break g; + }case 6: + { + z = T(e / T(L >>> 0));z = qa > oa & q ? z : T(0.0);break; + }default: + z = T(0.0);} while (0);if (k | 0) { + B = 1040 + (Ka << 2) | 0;C = 976 + (Ka << 2) | 0;t = 0;r = 0;while (1) { + h: do if (r >>> 0 < Ia >>> 0) { + v = T(0.0);w = T(0.0);e = T(0.0);s = r;while (1) { + q = c[(c[Ha >> 2] | 0) + (s << 2) >> 2] | 0;do if ((c[q + 36 >> 2] | 0) != 1 ? (c[q + 24 >> 2] | 0) == 0 : 0) { + if ((c[q + 940 >> 2] | 0) != (t | 0)) break h;if (Re(q, Ka) | 0) { + Ma = T(g[q + 908 + (c[C >> 2] << 2) >> 2]);e = T(cC(e, T(Ma + T(Ud(q, Ka, Ea))))); + }if ((Je(b, q) | 0) != 5) break;na = T(Se(q));na = T(na + T(me(q, 0, Ea)));Ma = T(g[q + 912 >> 2]);Ma = T(T(Ma + T(Ud(q, 0, Ea))) - na);na = T(cC(w, na));Ma = T(cC(v, Ma));v = Ma;w = na;e = T(cC(e, T(na + Ma))); + } while (0);q = s + 1 | 0;if (q >>> 0 < Ia >>> 0) s = q;else { + s = q;break; + } + } + } else { + w = T(0.0);e = T(0.0);s = r; + } while (0);y = T(z + e);f = u;u = T(u + y);if (r >>> 0 < s >>> 0) { + x = T(f + w);q = r;do { + r = c[(c[Ha >> 2] | 0) + (q << 2) >> 2] | 0;i: do if ((c[r + 36 >> 2] | 0) != 1 ? (c[r + 24 >> 2] | 0) == 0 : 0) switch (Je(b, r) | 0) {case 1: + { + Ma = T(f + T(me(r, Ka, Ea)));g[r + 400 + (c[B >> 2] << 2) >> 2] = Ma;break i; + }case 3: + { + Ma = T(T(u - T(ne(r, Ka, Ea))) - T(g[r + 908 + (c[C >> 2] << 2) >> 2]));g[r + 400 + (c[B >> 2] << 2) >> 2] = Ma;break i; + }case 2: + { + Ma = T(f + T(T(y - T(g[r + 908 + (c[C >> 2] << 2) >> 2])) * T(.5)));g[r + 400 + (c[B >> 2] << 2) >> 2] = Ma;break i; + }case 4: + { + Ma = T(f + T(me(r, Ka, Ea)));g[r + 400 + (c[B >> 2] << 2) >> 2] = Ma;if (ae(r, Ka, qa) | 0) break i;if (Fa) { + v = T(g[r + 908 >> 2]);e = T(v + T(Ud(r, Ja, Ea)));w = y; + } else { + w = T(g[r + 912 >> 2]);w = T(w + T(Ud(r, Ka, Ea)));e = y;v = T(g[r + 908 >> 2]); + }if (Ld(e, v) | 0 ? Ld(w, T(g[r + 912 >> 2])) | 0 : 0) break i;Td(r, e, w, Ga, 1, 1, Ea, sa, 1, 3501, p) | 0;break i; + }case 5: + { + g[r + 404 >> 2] = T(T(x - T(Se(r))) + T(Oe(r, 0, qa)));break i; + }default: + break i;} while (0);q = q + 1 | 0; + } while ((q | 0) != (s | 0)); + }t = t + 1 | 0;if ((t | 0) == (k | 0)) break;else r = s; + } + } + } + } while (0);g[b + 908 >> 2] = T(Ie(b, 2, ta, m, m));g[b + 912 >> 2] = T(Ie(b, 0, ra, n, m));if ((pa | 0) != 0 ? (wa = c[b + 32 >> 2] | 0, xa = (pa | 0) == 2, !(xa & (wa | 0) != 2)) : 0) { + if (xa & (wa | 0) == 2) { + e = T(va + H);e = T(cC(T(eC(e, T(Te(b, Ja, da, ua)))), va));X = 198; + } + } else { + e = T(Ie(b, Ja, da, ua, m));X = 198; + }if ((X | 0) == 198) g[b + 908 + (c[976 + (Ja << 2) >> 2] << 2) >> 2] = e;if ((Aa | 0) != 0 ? (Ca = c[b + 32 >> 2] | 0, Da = (Aa | 0) == 2, !(Da & (Ca | 0) != 2)) : 0) { + if (Da & (Ca | 0) == 2) { + e = T(za + qa);e = T(cC(T(eC(e, T(Te(b, Ka, T(za + oa), ya)))), za));X = 204; + } + } else { + e = T(Ie(b, Ka, T(za + oa), ya, m));X = 204; + }if ((X | 0) == 204) g[b + 908 + (c[976 + (Ka << 2) >> 2] << 2) >> 2] = e;if (o) { + if ((c[Ba >> 2] | 0) == 2) { + r = 976 + (Ka << 2) | 0;s = 1040 + (Ka << 2) | 0;q = 0;do { + t = ac(b, q) | 0;if (!(c[t + 24 >> 2] | 0)) { + Na = c[r >> 2] | 0;Ma = T(g[b + 908 + (Na << 2) >> 2]);Oa = t + 400 + (c[s >> 2] << 2) | 0;Ma = T(Ma - T(g[Oa >> 2]));g[Oa >> 2] = T(Ma - T(g[t + 908 + (Na << 2) >> 2])); + }q = q + 1 | 0; + } while ((q | 0) != (Ia | 0)); + }if (h | 0) { + q = Fa ? pa : i;do { + Ue(b, h, Ea, q, sa, Ga, p);h = c[h + 960 >> 2] | 0; + } while ((h | 0) != 0); + }q = (Ja | 2 | 0) == 3;r = (Ka | 2 | 0) == 3;if (q | r) { + h = 0;do { + s = c[(c[Ha >> 2] | 0) + (h << 2) >> 2] | 0;if ((c[s + 36 >> 2] | 0) != 1) { + if (q) Ve(b, s, Ja);if (r) Ve(b, s, Ka); + }h = h + 1 | 0; + } while ((h | 0) != (Ia | 0)); + } + } + } else we(b, e, f, i, k, m, n); while (0);l = La;return; + }function Zd(a, b) { + a = a | 0;b = T(b);var c = 0;Vb(a, b >= T(0.0), 3147);c = b == T(0.0);g[a + 4 >> 2] = c ? T(0.0) : b;return; + }function _d(b, d, e, f) { + b = b | 0;d = T(d);e = T(e);f = f | 0;var h = ib, + i = ib, + j = 0, + k = 0, + l = 0;c[2278] = (c[2278] | 0) + 1;$d(b);if (!(ae(b, 2, d) | 0)) { + h = T(be(b + 380 | 0, d));if (!(h >= T(0.0))) { + l = ((Sb(d) | 0) ^ 1) & 1;h = d; + } else l = 2; + } else { + h = T(be(c[b + 992 >> 2] | 0, d));l = 1;h = T(h + T(Ud(b, 2, d))); + }if (!(ae(b, 0, e) | 0)) { + i = T(be(b + 388 | 0, e));if (!(i >= T(0.0))) { + k = ((Sb(e) | 0) ^ 1) & 1;i = e; + } else k = 2; + } else { + i = T(be(c[b + 996 >> 2] | 0, e));k = 1;i = T(i + T(Ud(b, 0, d))); + }j = b + 976 | 0;if (Td(b, h, i, f, l, k, d, e, 1, 3189, c[j >> 2] | 0) | 0 ? (ce(b, c[b + 496 >> 2] | 0, d, e, d), de(b, T(g[(c[j >> 2] | 0) + 4 >> 2]), T(0.0), T(0.0)), a[11696] | 0) : 0) Md(b, 7);return; + }function $d(a) { + a = a | 0;var b = 0, + d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0;i = l;l = l + 32 | 0;h = i + 24 | 0;g = i + 16 | 0;e = i + 8 | 0;f = i;d = 0;do { + b = a + 380 + (d << 3) | 0;if (!((c[a + 380 + (d << 3) + 4 >> 2] | 0) != 0 ? (j = b, k = c[j + 4 >> 2] | 0, m = e, c[m >> 2] = c[j >> 2], c[m + 4 >> 2] = k, m = a + 364 + (d << 3) | 0, k = c[m + 4 >> 2] | 0, j = f, c[j >> 2] = c[m >> 2], c[j + 4 >> 2] = k, c[g >> 2] = c[e >> 2], c[g + 4 >> 2] = c[e + 4 >> 2], c[h >> 2] = c[f >> 2], c[h + 4 >> 2] = c[f + 4 >> 2], Kd(g, h) | 0) : 0)) b = a + 348 + (d << 3) | 0;c[a + 992 + (d << 2) >> 2] = b;d = d + 1 | 0; + } while ((d | 0) != 2);l = i;return; + }function ae(a, b, d) { + a = a | 0;b = b | 0;d = T(d);var e = 0;a = c[a + 992 + (c[976 + (b << 2) >> 2] << 2) >> 2] | 0;switch (c[a + 4 >> 2] | 0) {case 0:case 3: + { + a = 0;break; + }case 1: + { + if (T(g[a >> 2]) < T(0.0)) a = 0;else e = 5;break; + }case 2: + { + if (T(g[a >> 2]) < T(0.0)) a = 0;else a = (Sb(d) | 0) ^ 1;break; + }default: + e = 5;}if ((e | 0) == 5) a = 1;return a | 0; + }function be(a, b) { + a = a | 0;b = T(b);switch (c[a + 4 >> 2] | 0) {case 2: + { + b = T(T(T(g[a >> 2]) * b) / T(100.0));break; + }case 1: + { + b = T(g[a >> 2]);break; + }default: + b = T(t);}return T(b); + }function ce(a, b, d, e, f) { + a = a | 0;b = b | 0;d = T(d);e = T(e);f = T(f);var h = 0, + i = ib;b = c[a + 944 >> 2] | 0 ? b : 1;h = re(c[a + 4 >> 2] | 0, b) | 0;b = ze(h, b) | 0;d = T($e(a, h, d));e = T($e(a, b, e));i = T(d + T(me(a, h, f)));g[a + 400 + (c[1040 + (h << 2) >> 2] << 2) >> 2] = i;d = T(d + T(ne(a, h, f)));g[a + 400 + (c[1e3 + (h << 2) >> 2] << 2) >> 2] = d;d = T(e + T(me(a, b, f)));g[a + 400 + (c[1040 + (b << 2) >> 2] << 2) >> 2] = d;f = T(e + T(ne(a, b, f)));g[a + 400 + (c[1e3 + (b << 2) >> 2] << 2) >> 2] = f;return; + }function de(a, b, d, e) { + a = a | 0;b = T(b);d = T(d);e = T(e);var f = 0, + h = 0, + i = ib, + j = ib, + k = 0, + l = 0, + m = ib, + n = 0, + o = ib, + p = ib, + q = ib, + r = ib;if (!(b == T(0.0))) { + f = a + 400 | 0;r = T(g[f >> 2]);h = a + 404 | 0;q = T(g[h >> 2]);n = a + 416 | 0;p = T(g[n >> 2]);l = a + 420 | 0;i = T(g[l >> 2]);o = T(r + d);m = T(q + e);e = T(o + p);j = T(m + i);k = (c[a + 988 >> 2] | 0) == 1;g[f >> 2] = T(Od(r, b, 0, k));g[h >> 2] = T(Od(q, b, 0, k));d = T(gC(T(p * b), T(1.0)));if (Ld(d, T(0.0)) | 0) h = 0;else h = (Ld(d, T(1.0)) | 0) ^ 1;d = T(gC(T(i * b), T(1.0)));if (Ld(d, T(0.0)) | 0) f = 0;else f = (Ld(d, T(1.0)) | 0) ^ 1;r = T(Od(e, b, k & h, k & (h ^ 1)));g[n >> 2] = T(r - T(Od(o, b, 0, k)));r = T(Od(j, b, k & f, k & (f ^ 1)));g[l >> 2] = T(r - T(Od(m, b, 0, k)));h = (c[a + 952 >> 2] | 0) - (c[a + 948 >> 2] | 0) >> 2;if (h | 0) { + f = 0;do { + de(ac(a, f) | 0, b, o, m);f = f + 1 | 0; + } while ((f | 0) != (h | 0)); + } + }return; + }function ee(a, b, d, e, f) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;f = f | 0;switch (d | 0) {case 5:case 0: + { + a = CB(c[489] | 0, e, f) | 0;break; + }default: + a = iC(e, f) | 0;}return a | 0; + }function fe(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0;f = l;l = l + 16 | 0;g = f;c[g >> 2] = e;ge(a, 0, b, d, g);l = f;return; + }function ge(a, b, d, e, f) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;f = f | 0;a = a | 0 ? a : 956;Bb[c[a + 8 >> 2] & 1](a, b, d, e, f) | 0;if ((d | 0) == 5) Ta();else return; + }function he(b, c, d) { + b = b | 0;c = c | 0;d = d | 0;a[b + c >> 0] = d & 1;return; + }function ie(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0;c[a >> 2] = 0;c[a + 4 >> 2] = 0;c[a + 8 >> 2] = 0;d = b + 4 | 0;e = (c[d >> 2] | 0) - (c[b >> 2] | 0) >> 2;if (e | 0) { + je(a, e);ke(a, c[b >> 2] | 0, c[d >> 2] | 0, e); + }return; + }function je(a, b) { + a = a | 0;b = b | 0;var d = 0;if ((le(a) | 0) >>> 0 < b >>> 0) jC(a);if (b >>> 0 > 1073741823) Ta();else { + d = qC(b << 2) | 0;c[a + 4 >> 2] = d;c[a >> 2] = d;c[a + 8 >> 2] = d + (b << 2);return; + } + }function ke(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;e = a + 4 | 0;a = d - b | 0;if ((a | 0) > 0) { + BC(c[e >> 2] | 0, b | 0, a | 0) | 0;c[e >> 2] = (c[e >> 2] | 0) + (a >>> 2 << 2); + }return; + }function le(a) { + a = a | 0;return 1073741823; + }function me(a, b, d) { + a = a | 0;b = b | 0;d = T(d);if (oe(b) | 0 ? (c[a + 96 >> 2] | 0) != 0 : 0) a = a + 92 | 0;else a = Tb(a + 60 | 0, c[1040 + (b << 2) >> 2] | 0, 992) | 0;return T(pe(a, d)); + }function ne(a, b, d) { + a = a | 0;b = b | 0;d = T(d);if (oe(b) | 0 ? (c[a + 104 >> 2] | 0) != 0 : 0) a = a + 100 | 0;else a = Tb(a + 60 | 0, c[1e3 + (b << 2) >> 2] | 0, 992) | 0;return T(pe(a, d)); + }function oe(a) { + a = a | 0;return (a | 1 | 0) == 3 | 0; + }function pe(a, b) { + a = a | 0;b = T(b);if ((c[a + 4 >> 2] | 0) == 3) b = T(0.0);else b = T(be(a, b));return T(b); + }function qe(a, b) { + a = a | 0;b = b | 0;a = c[a >> 2] | 0;return ((a | 0) == 0 ? (b | 0) > 1 ? b : 1 : a) | 0; + }function re(a, b) { + a = a | 0;b = b | 0;var c = 0;a: do if ((b | 0) == 2) { + switch (a | 0) {case 2: + { + a = 3;break a; + }case 3: + break;default: + { + c = 4;break a; + }}a = 2; + } else c = 4; while (0);return a | 0; + }function se(a, b) { + a = a | 0;b = b | 0;var d = ib;if (!((oe(b) | 0 ? (c[a + 312 >> 2] | 0) != 0 : 0) ? (d = T(g[a + 308 >> 2]), d >= T(0.0)) : 0)) d = T(cC(T(g[(Tb(a + 276 | 0, c[1040 + (b << 2) >> 2] | 0, 992) | 0) >> 2]), T(0.0)));return T(d); + }function te(a, b) { + a = a | 0;b = b | 0;var d = ib;if (!((oe(b) | 0 ? (c[a + 320 >> 2] | 0) != 0 : 0) ? (d = T(g[a + 316 >> 2]), d >= T(0.0)) : 0)) d = T(cC(T(g[(Tb(a + 276 | 0, c[1e3 + (b << 2) >> 2] | 0, 992) | 0) >> 2]), T(0.0)));return T(d); + }function ue(a, b, d) { + a = a | 0;b = b | 0;d = T(d);var e = ib;if (!((oe(b) | 0 ? (c[a + 240 >> 2] | 0) != 0 : 0) ? (e = T(be(a + 236 | 0, d)), e >= T(0.0)) : 0)) e = T(cC(T(be(Tb(a + 204 | 0, c[1040 + (b << 2) >> 2] | 0, 992) | 0, d)), T(0.0)));return T(e); + }function ve(a, b, d) { + a = a | 0;b = b | 0;d = T(d);var e = ib;if (!((oe(b) | 0 ? (c[a + 248 >> 2] | 0) != 0 : 0) ? (e = T(be(a + 244 | 0, d)), e >= T(0.0)) : 0)) e = T(cC(T(be(Tb(a + 204 | 0, c[1e3 + (b << 2) >> 2] | 0, 992) | 0, d)), T(0.0)));return T(e); + }function we(a, b, d, e, f, h, i) { + a = a | 0;b = T(b);d = T(d);e = e | 0;f = f | 0;h = T(h);i = T(i);var j = ib, + k = ib, + m = ib, + n = ib, + o = ib, + p = ib, + q = 0, + r = 0, + s = 0;s = l;l = l + 16 | 0;q = s;r = a + 964 | 0;ec(a, (c[r >> 2] | 0) != 0, 3519);j = T(Ce(a, 2, b));k = T(Ce(a, 0, b));m = T(Ud(a, 2, b));n = T(Ud(a, 0, b));if (Sb(b) | 0) o = b;else o = T(cC(T(0.0), T(T(b - m) - j)));if (Sb(d) | 0) p = d;else p = T(cC(T(0.0), T(T(d - n) - k)));if ((e | 0) == 1 & (f | 0) == 1) { + g[a + 908 >> 2] = T(Ie(a, 2, T(b - m), h, h));b = T(Ie(a, 0, T(d - n), i, h)); + } else { + Db[c[r >> 2] & 1](q, a, o, e, p, f);o = T(j + T(g[q >> 2]));p = T(b - m);g[a + 908 >> 2] = T(Ie(a, 2, (e | 2 | 0) == 2 ? o : p, h, h));p = T(k + T(g[q + 4 >> 2]));b = T(d - n);b = T(Ie(a, 0, (f | 2 | 0) == 2 ? p : b, i, h)); + }g[a + 912 >> 2] = b;l = s;return; + }function xe(a, b, c, d, e, f, h) { + a = a | 0;b = T(b);c = T(c);d = d | 0;e = e | 0;f = T(f);h = T(h);var i = ib, + j = ib, + k = ib, + l = ib;k = T(Ce(a, 2, f));i = T(Ce(a, 0, f));l = T(Ud(a, 2, f));j = T(Ud(a, 0, f));b = T(b - l);g[a + 908 >> 2] = T(Ie(a, 2, (d | 2 | 0) == 2 ? k : b, f, f));c = T(c - j);g[a + 912 >> 2] = T(Ie(a, 0, (e | 2 | 0) == 2 ? i : c, h, f));return; + }function ye(a, b, c, d, e, f, h) { + a = a | 0;b = T(b);c = T(c);d = d | 0;e = e | 0;f = T(f);h = T(h);var i = 0, + j = ib, + k = ib;i = (d | 0) == 2;if ((!(b <= T(0.0) & i) ? !(c <= T(0.0) & (e | 0) == 2) : 0) ? !((d | 0) == 1 & (e | 0) == 1) : 0) a = 0;else { + j = T(Ud(a, 0, f));k = T(Ud(a, 2, f));i = b < T(0.0) & i | (Sb(b) | 0);b = T(b - k);g[a + 908 >> 2] = T(Ie(a, 2, i ? T(0.0) : b, f, f));b = T(c - j);i = c < T(0.0) & (e | 0) == 2 | (Sb(c) | 0);g[a + 912 >> 2] = T(Ie(a, 0, i ? T(0.0) : b, h, f));a = 1; + }return a | 0; + }function ze(a, b) { + a = a | 0;b = b | 0;if (We(a) | 0) a = re(2, b) | 0;else a = 0;return a | 0; + }function Ae(a, b, c) { + a = a | 0;b = b | 0;c = T(c);c = T(ue(a, b, c));return T(c + T(se(a, b))); + }function Be(a, b, c) { + a = a | 0;b = b | 0;c = T(c);c = T(ve(a, b, c));return T(c + T(te(a, b))); + }function Ce(a, b, c) { + a = a | 0;b = b | 0;c = T(c);var d = ib;d = T(Ae(a, b, c));return T(d + T(Be(a, b, c))); + }function De(a) { + a = a | 0;if (!(c[a + 24 >> 2] | 0)) { + if (T(Ee(a)) != T(0.0)) a = 1;else a = T(Fe(a)) != T(0.0); + } else a = 0;return a | 0; + }function Ee(a) { + a = a | 0;var b = ib;if (c[a + 944 >> 2] | 0) { + b = T(g[a + 44 >> 2]);if (Sb(b) | 0) { + b = T(g[a + 40 >> 2]);a = b > T(0.0) & ((Sb(b) | 0) ^ 1);return T(a ? b : T(0.0)); + } + } else b = T(0.0);return T(b); + }function Fe(b) { + b = b | 0;var d = ib, + e = 0, + f = ib;do if (c[b + 944 >> 2] | 0) { + d = T(g[b + 48 >> 2]);if (Sb(d) | 0) { + e = a[(c[b + 976 >> 2] | 0) + 2 >> 0] | 0;if (e << 24 >> 24 == 0 ? (f = T(g[b + 40 >> 2]), f < T(0.0) & ((Sb(f) | 0) ^ 1)) : 0) { + d = T(-f);break; + }d = e << 24 >> 24 ? T(1.0) : T(0.0); + } + } else d = T(0.0); while (0);return T(d); + }function Ge(b) { + b = b | 0;var d = 0, + e = 0;yC(b + 400 | 0, 0, 540) | 0;a[b + 985 >> 0] = 1;lc(b);e = $b(b) | 0;if (e | 0) { + d = b + 948 | 0;b = 0;do { + Ge(c[(c[d >> 2] | 0) + (b << 2) >> 2] | 0);b = b + 1 | 0; + } while ((b | 0) != (e | 0)); + }return; + }function He(a, b, d, e, f, h, i, j, k, m) { + a = a | 0;b = b | 0;d = T(d);e = e | 0;f = T(f);h = T(h);i = T(i);j = j | 0;k = k | 0;m = m | 0;var n = 0, + o = ib, + p = 0, + q = 0, + r = ib, + s = ib, + u = 0, + v = ib, + w = 0, + x = ib, + y = 0, + z = 0, + A = 0, + B = 0, + C = 0, + D = 0, + E = 0, + F = 0, + G = 0, + H = 0;G = l;l = l + 16 | 0;A = G + 12 | 0;B = G + 8 | 0;C = G + 4 | 0;D = G;F = re(c[a + 4 >> 2] | 0, k) | 0;y = oe(F) | 0;o = T(be(Xe(b) | 0, y ? h : i));z = ae(b, 2, h) | 0;E = ae(b, 0, i) | 0;do if (!(Sb(o) | 0) ? !(Sb(y ? d : f) | 0) : 0) { + n = b + 504 | 0;if (!(Sb(T(g[n >> 2])) | 0)) { + if (!(Ye(c[b + 976 >> 2] | 0, 0) | 0)) break;if ((c[b + 500 >> 2] | 0) == (c[2278] | 0)) break; + }g[n >> 2] = T(cC(o, T(Ce(b, F, h)))); + } else p = 7; while (0);do if ((p | 0) == 7) { + w = y ^ 1;if (!(w | z ^ 1)) { + i = T(be(c[b + 992 >> 2] | 0, h));g[b + 504 >> 2] = T(cC(i, T(Ce(b, 2, h))));break; + }if (!(y | E ^ 1)) { + i = T(be(c[b + 996 >> 2] | 0, i));g[b + 504 >> 2] = T(cC(i, T(Ce(b, 0, h))));break; + }g[A >> 2] = T(t);g[B >> 2] = T(t);c[C >> 2] = 0;c[D >> 2] = 0;v = T(Ud(b, 2, h));x = T(Ud(b, 0, h));if (z) { + r = T(v + T(be(c[b + 992 >> 2] | 0, h)));g[A >> 2] = r;c[C >> 2] = 1;q = 1; + } else { + q = 0;r = T(t); + }if (E) { + o = T(x + T(be(c[b + 996 >> 2] | 0, i)));g[B >> 2] = o;c[D >> 2] = 1;n = 1; + } else { + n = 0;o = T(t); + }p = c[a + 32 >> 2] | 0;if (!(y & (p | 0) == 2)) { + if (Sb(r) | 0 ? !(Sb(d) | 0) : 0) { + g[A >> 2] = d;c[C >> 2] = 2;q = 2;r = d; + } + } else p = 2;if ((!((p | 0) == 2 & w) ? Sb(o) | 0 : 0) ? !(Sb(f) | 0) : 0) { + g[B >> 2] = f;c[D >> 2] = 2;n = 2;o = f; + }s = T(g[b + 396 >> 2]);u = Sb(s) | 0;do if (!u) { + if ((q | 0) == 1 & w) { + g[B >> 2] = T(T(r - v) / s);c[D >> 2] = 1;n = 1;p = 1;break; + }if (y & (n | 0) == 1) { + g[A >> 2] = T(s * T(o - x));c[C >> 2] = 1;n = 1;p = 1; + } else p = q; + } else p = q; while (0);H = Sb(d) | 0;q = (Je(a, b) | 0) != 4;if (!(y | z | ((e | 0) != 1 | H) | (q | (p | 0) == 1)) ? (g[A >> 2] = d, c[C >> 2] = 1, !u) : 0) { + g[B >> 2] = T(T(d - v) / s);c[D >> 2] = 1;n = 1; + }if (!(E | w | ((j | 0) != 1 | (Sb(f) | 0)) | (q | (n | 0) == 1)) ? (g[B >> 2] = f, c[D >> 2] = 1, !u) : 0) { + g[A >> 2] = T(s * T(f - x));c[C >> 2] = 1; + }Me(b, 2, h, h, C, A);Me(b, 0, i, h, D, B);d = T(g[A >> 2]);f = T(g[B >> 2]);Td(b, d, f, k, c[C >> 2] | 0, c[D >> 2] | 0, h, i, 0, 3565, m) | 0;i = T(g[b + 908 + (c[976 + (F << 2) >> 2] << 2) >> 2]);g[b + 504 >> 2] = T(cC(i, T(Ce(b, F, h)))); + } while (0);c[b + 500 >> 2] = c[2278];l = G;return; + }function Ie(a, b, c, d, e) { + a = a | 0;b = b | 0;c = T(c);d = T(d);e = T(e);d = T(Te(a, b, c, d));return T(cC(d, T(Ce(a, b, e)))); + }function Je(a, b) { + a = a | 0;b = b | 0;b = b + 20 | 0;b = c[((c[b >> 2] | 0) == 0 ? a + 16 | 0 : b) >> 2] | 0;if ((b | 0) == 5 ? We(c[a + 4 >> 2] | 0) | 0 : 0) b = 1;return b | 0; + }function Ke(a, b) { + a = a | 0;b = b | 0;if (oe(b) | 0 ? (c[a + 96 >> 2] | 0) != 0 : 0) b = 4;else b = c[1040 + (b << 2) >> 2] | 0;return a + 60 + (b << 3) | 0; + }function Le(a, b) { + a = a | 0;b = b | 0;if (oe(b) | 0 ? (c[a + 104 >> 2] | 0) != 0 : 0) b = 5;else b = c[1e3 + (b << 2) >> 2] | 0;return a + 60 + (b << 3) | 0; + }function Me(a, b, d, e, f, h) { + a = a | 0;b = b | 0;d = T(d);e = T(e);f = f | 0;h = h | 0;d = T(be(a + 380 + (c[976 + (b << 2) >> 2] << 3) | 0, d));d = T(d + T(Ud(a, b, e)));switch (c[f >> 2] | 0) {case 2:case 1: + { + f = Sb(d) | 0;e = T(g[h >> 2]);g[h >> 2] = f | e < d ? e : d;break; + }case 0: + { + if (!(Sb(d) | 0)) { + c[f >> 2] = 2;g[h >> 2] = d; + }break; + }default: +}return; + }function Ne(a, b) { + a = a | 0;b = b | 0;a = a + 132 | 0;if (oe(b) | 0 ? (c[(Tb(a, 4, 948) | 0) + 4 >> 2] | 0) != 0 : 0) a = 1;else a = (c[(Tb(a, c[1040 + (b << 2) >> 2] | 0, 948) | 0) + 4 >> 2] | 0) != 0;return a | 0; + }function Oe(a, b, d) { + a = a | 0;b = b | 0;d = T(d);var e = 0, + f = 0;a = a + 132 | 0;if (oe(b) | 0 ? (e = Tb(a, 4, 948) | 0, (c[e + 4 >> 2] | 0) != 0) : 0) f = 4;else { + e = Tb(a, c[1040 + (b << 2) >> 2] | 0, 948) | 0;if (!(c[e + 4 >> 2] | 0)) d = T(0.0);else f = 4; + }if ((f | 0) == 4) d = T(be(e, d));return T(d); + }function Pe(a, b, d) { + a = a | 0;b = b | 0;d = T(d);var e = ib;e = T(g[a + 908 + (c[976 + (b << 2) >> 2] << 2) >> 2]);e = T(e + T(me(a, b, d)));return T(e + T(ne(a, b, d))); + }function Qe(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;a: do if (!(We(c[a + 4 >> 2] | 0) | 0)) { + if ((c[a + 16 >> 2] | 0) != 5) { + d = $b(a) | 0;if (!d) b = 0;else { + b = 0;while (1) { + e = ac(a, b) | 0;if ((c[e + 24 >> 2] | 0) == 0 ? (c[e + 20 >> 2] | 0) == 5 : 0) { + b = 1;break a; + }b = b + 1 | 0;if (b >>> 0 >= d >>> 0) { + b = 0;break; + } + } + } + } else b = 1; + } else b = 0; while (0);return b | 0; + }function Re(a, b) { + a = a | 0;b = b | 0;var d = ib;d = T(g[a + 908 + (c[976 + (b << 2) >> 2] << 2) >> 2]);return d >= T(0.0) & ((Sb(d) | 0) ^ 1) | 0; + }function Se(a) { + a = a | 0;var b = ib, + d = 0, + e = 0, + f = 0, + h = 0, + i = 0, + j = 0, + k = ib;d = c[a + 968 >> 2] | 0;if (!d) { + h = $b(a) | 0;do if (h | 0) { + d = 0;f = 0;while (1) { + e = ac(a, f) | 0;if (c[e + 940 >> 2] | 0) { + i = 8;break; + }if ((c[e + 24 >> 2] | 0) != 1) { + j = (Je(a, e) | 0) == 5;if (j) { + d = e;break; + } else d = (d | 0) == 0 ? e : d; + }f = f + 1 | 0;if (f >>> 0 >= h >>> 0) { + i = 8;break; + } + }if ((i | 0) == 8) if (!d) break;b = T(Se(d));return T(b + T(g[d + 404 >> 2])); + } while (0);b = T(g[a + 912 >> 2]); + } else { + k = T(g[a + 908 >> 2]);b = T(g[a + 912 >> 2]);b = T(mb[d & 0](a, k, b));ec(a, (Sb(b) | 0) ^ 1, 3573); + }return T(b); + }function Te(a, b, c, d) { + a = a | 0;b = b | 0;c = T(c);d = T(d);var e = ib, + f = 0;if (!(We(b) | 0)) { + if (oe(b) | 0) { + b = 0;f = 3; + } else { + d = T(t);e = T(t); + } + } else { + b = 1;f = 3; + }if ((f | 0) == 3) { + e = T(be(a + 364 + (b << 3) | 0, d));d = T(be(a + 380 + (b << 3) | 0, d)); + }f = d < c & (d >= T(0.0) & ((Sb(d) | 0) ^ 1));c = f ? d : c;f = e >= T(0.0) & ((Sb(e) | 0) ^ 1) & c < e;return T(f ? e : c); + }function Ue(a, b, d, e, f, h, i) { + a = a | 0;b = b | 0;d = T(d);e = e | 0;f = T(f);h = h | 0;i = i | 0;var j = ib, + k = ib, + l = 0, + m = 0, + n = ib, + o = ib, + p = ib, + q = 0, + r = 0, + s = 0, + u = 0, + v = ib, + w = 0;s = re(c[a + 4 >> 2] | 0, h) | 0;q = ze(s, h) | 0;r = oe(s) | 0;n = T(Ud(b, 2, d));o = T(Ud(b, 0, d));if (!(ae(b, 2, d) | 0)) { + if (Ne(b, 2) | 0 ? Ze(b, 2) | 0 : 0) { + j = T(g[a + 908 >> 2]);k = T(se(a, 2));k = T(j - T(k + T(te(a, 2))));j = T(Oe(b, 2, d));j = T(Ie(b, 2, T(k - T(j + T(_e(b, 2, d)))), d, d)); + } else j = T(t); + } else j = T(n + T(be(c[b + 992 >> 2] | 0, d)));if (!(ae(b, 0, f) | 0)) { + if (Ne(b, 0) | 0 ? Ze(b, 0) | 0 : 0) { + k = T(g[a + 912 >> 2]);v = T(se(a, 0));v = T(k - T(v + T(te(a, 0))));k = T(Oe(b, 0, f));k = T(Ie(b, 0, T(v - T(k + T(_e(b, 0, f)))), f, d)); + } else k = T(t); + } else k = T(o + T(be(c[b + 996 >> 2] | 0, f)));l = Sb(j) | 0;m = Sb(k) | 0;do if (l ^ m ? (p = T(g[b + 396 >> 2]), !(Sb(p) | 0)) : 0) if (l) { + j = T(n + T(T(k - o) * p));break; + } else { + v = T(o + T(T(j - n) / p));k = m ? v : k;break; + } while (0);m = Sb(j) | 0;l = Sb(k) | 0;if (m | l) { + w = (m ^ 1) & 1;e = d > T(0.0) & ((e | 0) != 0 & m);j = r ? j : e ? d : j;Td(b, j, k, h, r ? w : e ? 2 : w, m & (l ^ 1) & 1, j, k, 0, 3623, i) | 0;j = T(g[b + 908 >> 2]);j = T(j + T(Ud(b, 2, d)));k = T(g[b + 912 >> 2]);k = T(k + T(Ud(b, 0, d))); + }Td(b, j, k, h, 1, 1, j, k, 1, 3635, i) | 0;if (Ze(b, s) | 0 ? !(Ne(b, s) | 0) : 0) { + w = c[976 + (s << 2) >> 2] | 0;v = T(g[a + 908 + (w << 2) >> 2]);v = T(v - T(g[b + 908 + (w << 2) >> 2]));v = T(v - T(te(a, s)));v = T(v - T(ne(b, s, d)));v = T(v - T(_e(b, s, r ? d : f)));g[b + 400 + (c[1040 + (s << 2) >> 2] << 2) >> 2] = v; + } else u = 21;do if ((u | 0) == 21) { + if (!(Ne(b, s) | 0) ? (c[a + 8 >> 2] | 0) == 1 : 0) { + w = c[976 + (s << 2) >> 2] | 0;v = T(g[a + 908 + (w << 2) >> 2]);v = T(T(v - T(g[b + 908 + (w << 2) >> 2])) * T(.5));g[b + 400 + (c[1040 + (s << 2) >> 2] << 2) >> 2] = v;break; + }if (!(Ne(b, s) | 0) ? (c[a + 8 >> 2] | 0) == 2 : 0) { + w = c[976 + (s << 2) >> 2] | 0;v = T(g[a + 908 + (w << 2) >> 2]);v = T(v - T(g[b + 908 + (w << 2) >> 2]));g[b + 400 + (c[1040 + (s << 2) >> 2] << 2) >> 2] = v; + } + } while (0);if (Ze(b, q) | 0 ? !(Ne(b, q) | 0) : 0) { + w = c[976 + (q << 2) >> 2] | 0;v = T(g[a + 908 + (w << 2) >> 2]);v = T(v - T(g[b + 908 + (w << 2) >> 2]));v = T(v - T(te(a, q)));v = T(v - T(ne(b, q, d)));v = T(v - T(_e(b, q, r ? f : d)));g[b + 400 + (c[1040 + (q << 2) >> 2] << 2) >> 2] = v; + } else u = 30;do if ((u | 0) == 30 ? !(Ne(b, q) | 0) : 0) { + if ((Je(a, b) | 0) == 2) { + w = c[976 + (q << 2) >> 2] | 0;v = T(g[a + 908 + (w << 2) >> 2]);v = T(T(v - T(g[b + 908 + (w << 2) >> 2])) * T(.5));g[b + 400 + (c[1040 + (q << 2) >> 2] << 2) >> 2] = v;break; + }w = (Je(a, b) | 0) == 3;if (w ^ (c[a + 28 >> 2] | 0) == 2) { + w = c[976 + (q << 2) >> 2] | 0;v = T(g[a + 908 + (w << 2) >> 2]);v = T(v - T(g[b + 908 + (w << 2) >> 2]));g[b + 400 + (c[1040 + (q << 2) >> 2] << 2) >> 2] = v; + } + } while (0);return; + }function Ve(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = ib, + f = 0;f = c[976 + (d << 2) >> 2] | 0;e = T(g[b + 908 + (f << 2) >> 2]);e = T(T(g[a + 908 + (f << 2) >> 2]) - e);e = T(e - T(g[b + 400 + (c[1040 + (d << 2) >> 2] << 2) >> 2]));g[b + 400 + (c[1e3 + (d << 2) >> 2] << 2) >> 2] = e;return; + }function We(a) { + a = a | 0;return (a | 1 | 0) == 1 | 0; + }function Xe(b) { + b = b | 0;var d = ib;switch (c[b + 56 >> 2] | 0) {case 0:case 3: + { + d = T(g[b + 40 >> 2]);if (d > T(0.0) & ((Sb(d) | 0) ^ 1)) b = a[(c[b + 976 >> 2] | 0) + 2 >> 0] | 0 ? 1056 : 992;else b = 1056;break; + }default: + b = b + 52 | 0;}return b | 0; + }function Ye(b, c) { + b = b | 0;c = c | 0;return (a[b + c >> 0] | 0) != 0 | 0; + }function Ze(a, b) { + a = a | 0;b = b | 0;a = a + 132 | 0;if (oe(b) | 0 ? (c[(Tb(a, 5, 948) | 0) + 4 >> 2] | 0) != 0 : 0) a = 1;else a = (c[(Tb(a, c[1e3 + (b << 2) >> 2] | 0, 948) | 0) + 4 >> 2] | 0) != 0;return a | 0; + }function _e(a, b, d) { + a = a | 0;b = b | 0;d = T(d);var e = 0, + f = 0;a = a + 132 | 0;if (oe(b) | 0 ? (e = Tb(a, 5, 948) | 0, (c[e + 4 >> 2] | 0) != 0) : 0) f = 4;else { + e = Tb(a, c[1e3 + (b << 2) >> 2] | 0, 948) | 0;if (!(c[e + 4 >> 2] | 0)) d = T(0.0);else f = 4; + }if ((f | 0) == 4) d = T(be(e, d));return T(d); + }function $e(a, b, c) { + a = a | 0;b = b | 0;c = T(c);if (Ne(a, b) | 0) c = T(Oe(a, b, c));else c = T(-T(_e(a, b, c)));return T(c); + }function af(a) { + a = T(a);return (g[j >> 2] = a, c[j >> 2] | 0) | 0; + }function bf(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0;c[a + 12 >> 2] = 0;c[a + 16 >> 2] = e;do if (b) { + if (b >>> 0 > 1073741823) Ta();else { + f = qC(b << 2) | 0;break; + } + } else f = 0; while (0);c[a >> 2] = f;e = f + (d << 2) | 0;c[a + 8 >> 2] = e;c[a + 4 >> 2] = e;c[a + 12 >> 2] = f + (b << 2);return; + }function cf(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0;e = c[a >> 2] | 0;h = a + 4 | 0;g = b + 4 | 0;f = (c[h >> 2] | 0) - e | 0;d = (c[g >> 2] | 0) + (0 - (f >> 2) << 2) | 0;c[g >> 2] = d;if ((f | 0) > 0) { + BC(d | 0, e | 0, f | 0) | 0;e = g;d = c[g >> 2] | 0; + } else e = g;g = c[a >> 2] | 0;c[a >> 2] = d;c[e >> 2] = g;g = b + 8 | 0;f = c[h >> 2] | 0;c[h >> 2] = c[g >> 2];c[g >> 2] = f;g = a + 8 | 0;h = b + 12 | 0;a = c[g >> 2] | 0;c[g >> 2] = c[h >> 2];c[h >> 2] = a;c[b >> 2] = c[e >> 2];return; + }function df(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;b = c[a + 4 >> 2] | 0;d = a + 8 | 0;e = c[d >> 2] | 0;if ((e | 0) != (b | 0)) c[d >> 2] = e + (~((e + -4 - b | 0) >>> 2) << 2);a = c[a >> 2] | 0;if (a | 0) sC(a);return; + }function ef(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0, + h = 0, + i = 0;h = a + 4 | 0;i = c[h >> 2] | 0;f = i - e | 0;g = f >> 2;a = b + (g << 2) | 0;if (a >>> 0 < d >>> 0) { + e = i;do { + c[e >> 2] = c[a >> 2];a = a + 4 | 0;e = (c[h >> 2] | 0) + 4 | 0;c[h >> 2] = e; + } while (a >>> 0 < d >>> 0); + }if (g | 0) GC(i + (0 - g << 2) | 0, b | 0, f | 0) | 0;return; + }function ff(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0;i = b + 4 | 0;j = c[i >> 2] | 0;f = c[a >> 2] | 0;h = d;g = h - f | 0;e = j + (0 - (g >> 2) << 2) | 0;c[i >> 2] = e;if ((g | 0) > 0) BC(e | 0, f | 0, g | 0) | 0;f = a + 4 | 0;g = b + 8 | 0;e = (c[f >> 2] | 0) - h | 0;if ((e | 0) > 0) { + BC(c[g >> 2] | 0, d | 0, e | 0) | 0;c[g >> 2] = (c[g >> 2] | 0) + (e >>> 2 << 2); + }h = c[a >> 2] | 0;c[a >> 2] = c[i >> 2];c[i >> 2] = h;h = c[f >> 2] | 0;c[f >> 2] = c[g >> 2];c[g >> 2] = h;h = a + 8 | 0;d = b + 12 | 0;a = c[h >> 2] | 0;c[h >> 2] = c[d >> 2];c[d >> 2] = a;c[b >> 2] = c[i >> 2];return j | 0; + }function gf(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0;h = c[b >> 2] | 0;g = c[d >> 2] | 0;if ((h | 0) != (g | 0)) { + f = a + 8 | 0;d = ((g + -4 - h | 0) >>> 2) + 1 | 0;a = h;e = c[f >> 2] | 0;do { + c[e >> 2] = c[a >> 2];e = (c[f >> 2] | 0) + 4 | 0;c[f >> 2] = e;a = a + 4 | 0; + } while ((a | 0) != (g | 0));c[b >> 2] = h + (d << 2); + }return; + }function hf() { + Qb();return; + }function jf() { + var a = 0;a = qC(4) | 0;kf(a);return a | 0; + }function kf(a) { + a = a | 0;c[a >> 2] = gc() | 0;return; + }function lf(a) { + a = a | 0;if (a | 0) { + mf(a);sC(a); + }return; + }function mf(a) { + a = a | 0;ic(c[a >> 2] | 0);return; + }function nf(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;he(c[a >> 2] | 0, b, d);return; + }function of(a, b) { + a = a | 0;b = T(b);Zd(c[a >> 2] | 0, b);return; + }function pf(a, b) { + a = a | 0;b = b | 0;return Ye(c[a >> 2] | 0, b) | 0; + }function qf() { + var a = 0;a = qC(8) | 0;rf(a, 0);return a | 0; + }function rf(a, b) { + a = a | 0;b = b | 0;if (!b) b = Wb() | 0;else b = Ub(c[b >> 2] | 0) | 0;c[a >> 2] = b;c[a + 4 >> 2] = 0;vc(b, a);return; + }function sf(a) { + a = a | 0;var b = 0;b = qC(8) | 0;rf(b, a);return b | 0; + }function tf(a) { + a = a | 0;if (a | 0) { + uf(a);sC(a); + }return; + }function uf(a) { + a = a | 0;var b = 0;Zb(c[a >> 2] | 0);b = a + 4 | 0;a = c[b >> 2] | 0;c[b >> 2] = 0;if (a | 0) { + vf(a);sC(a); + }return; + }function vf(a) { + a = a | 0;wf(a);return; + }function wf(a) { + a = a | 0;a = c[a >> 2] | 0;if (a | 0) ab(a | 0);return; + }function xf(a) { + a = a | 0;return wc(a) | 0; + }function yf(a) { + a = a | 0;var b = 0, + d = 0;d = a + 4 | 0;b = c[d >> 2] | 0;c[d >> 2] = 0;if (b | 0) { + vf(b);sC(b); + }dc(c[a >> 2] | 0);return; + }function zf(a, b) { + a = a | 0;b = b | 0;sc(c[a >> 2] | 0, c[b >> 2] | 0);return; + }function Af(a, b) { + a = a | 0;b = b | 0;Hc(c[a >> 2] | 0, b);return; + }function Bf(a, b, d) { + a = a | 0;b = b | 0;d = +d;Vc(c[a >> 2] | 0, b, T(d));return; + }function Cf(a, b, d) { + a = a | 0;b = b | 0;d = +d;Wc(c[a >> 2] | 0, b, T(d));return; + }function Df(a, b) { + a = a | 0;b = b | 0;Bc(c[a >> 2] | 0, b);return; + }function Ef(a, b) { + a = a | 0;b = b | 0;Dc(c[a >> 2] | 0, b);return; + }function Ff(a, b) { + a = a | 0;b = b | 0;Fc(c[a >> 2] | 0, b);return; + }function Gf(a, b) { + a = a | 0;b = b | 0;xc(c[a >> 2] | 0, b);return; + }function Hf(a, b) { + a = a | 0;b = b | 0;Jc(c[a >> 2] | 0, b);return; + }function If(a, b) { + a = a | 0;b = b | 0;zc(c[a >> 2] | 0, b);return; + }function Jf(a, b, d) { + a = a | 0;b = b | 0;d = +d;Yc(c[a >> 2] | 0, b, T(d));return; + }function Kf(a, b, d) { + a = a | 0;b = b | 0;d = +d;Zc(c[a >> 2] | 0, b, T(d));return; + }function Lf(a, b) { + a = a | 0;b = b | 0;$c(c[a >> 2] | 0, b);return; + }function Mf(a, b) { + a = a | 0;b = b | 0;Lc(c[a >> 2] | 0, b);return; + }function Nf(a, b) { + a = a | 0;b = b | 0;Nc(c[a >> 2] | 0, b);return; + }function Of(a, b) { + a = a | 0;b = +b;Pc(c[a >> 2] | 0, T(b));return; + }function Pf(a, b) { + a = a | 0;b = +b;Sc(c[a >> 2] | 0, T(b));return; + }function Qf(a, b) { + a = a | 0;b = +b;Tc(c[a >> 2] | 0, T(b));return; + }function Rf(a, b) { + a = a | 0;b = +b;Qc(c[a >> 2] | 0, T(b));return; + }function Sf(a, b) { + a = a | 0;b = +b;Rc(c[a >> 2] | 0, T(b));return; + }function Tf(a, b) { + a = a | 0;b = +b;fd(c[a >> 2] | 0, T(b));return; + }function Uf(a, b) { + a = a | 0;b = +b;gd(c[a >> 2] | 0, T(b));return; + }function Vf(a) { + a = a | 0;hd(c[a >> 2] | 0);return; + }function Wf(a, b) { + a = a | 0;b = +b;jd(c[a >> 2] | 0, T(b));return; + }function Xf(a, b) { + a = a | 0;b = +b;kd(c[a >> 2] | 0, T(b));return; + }function Yf(a) { + a = a | 0;ld(c[a >> 2] | 0);return; + }function Zf(a, b) { + a = a | 0;b = +b;nd(c[a >> 2] | 0, T(b));return; + }function _f(a, b) { + a = a | 0;b = +b;od(c[a >> 2] | 0, T(b));return; + }function $f(a, b) { + a = a | 0;b = +b;qd(c[a >> 2] | 0, T(b));return; + }function ag(a, b) { + a = a | 0;b = +b;rd(c[a >> 2] | 0, T(b));return; + }function bg(a, b) { + a = a | 0;b = +b;td(c[a >> 2] | 0, T(b));return; + }function cg(a, b) { + a = a | 0;b = +b;ud(c[a >> 2] | 0, T(b));return; + }function dg(a, b) { + a = a | 0;b = +b;wd(c[a >> 2] | 0, T(b));return; + }function eg(a, b) { + a = a | 0;b = +b;xd(c[a >> 2] | 0, T(b));return; + }function fg(a, b) { + a = a | 0;b = +b;zd(c[a >> 2] | 0, T(b));return; + }function gg(a, b, d) { + a = a | 0;b = b | 0;d = +d;dd(c[a >> 2] | 0, b, T(d));return; + }function hg(a, b, d) { + a = a | 0;b = b | 0;d = +d;ad(c[a >> 2] | 0, b, T(d));return; + }function ig(a, b, d) { + a = a | 0;b = b | 0;d = +d;bd(c[a >> 2] | 0, b, T(d));return; + }function jg(a) { + a = a | 0;return Ic(c[a >> 2] | 0) | 0; + }function kg(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0;e = l;l = l + 16 | 0;f = e;Xc(f, c[b >> 2] | 0, d);lg(a, f);l = e;return; + }function lg(a, b) { + a = a | 0;b = b | 0;mg(a, c[b + 4 >> 2] | 0, +T(g[b >> 2]));return; + }function mg(a, b, d) { + a = a | 0;b = b | 0;d = +d;c[a >> 2] = b;h[a + 8 >> 3] = d;return; + }function ng(a) { + a = a | 0;return Cc(c[a >> 2] | 0) | 0; + }function og(a) { + a = a | 0;return Ec(c[a >> 2] | 0) | 0; + }function pg(a) { + a = a | 0;return Gc(c[a >> 2] | 0) | 0; + }function qg(a) { + a = a | 0;return yc(c[a >> 2] | 0) | 0; + }function rg(a) { + a = a | 0;return Kc(c[a >> 2] | 0) | 0; + }function sg(a) { + a = a | 0;return Ac(c[a >> 2] | 0) | 0; + }function tg(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0;e = l;l = l + 16 | 0;f = e;_c(f, c[b >> 2] | 0, d);lg(a, f);l = e;return; + }function ug(a) { + a = a | 0;return Mc(c[a >> 2] | 0) | 0; + }function vg(a) { + a = a | 0;return Oc(c[a >> 2] | 0) | 0; + }function wg(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0;d = l;l = l + 16 | 0;e = d;Uc(e, c[b >> 2] | 0);lg(a, e);l = d;return; + }function xg(a) { + a = a | 0;return + +T(tc(c[a >> 2] | 0)); + }function yg(a) { + a = a | 0;return + +T(uc(c[a >> 2] | 0)); + }function zg(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0;d = l;l = l + 16 | 0;e = d;id(e, c[b >> 2] | 0);lg(a, e);l = d;return; + }function Ag(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0;d = l;l = l + 16 | 0;e = d;md(e, c[b >> 2] | 0);lg(a, e);l = d;return; + }function Bg(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0;d = l;l = l + 16 | 0;e = d;pd(e, c[b >> 2] | 0);lg(a, e);l = d;return; + }function Cg(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0;d = l;l = l + 16 | 0;e = d;sd(e, c[b >> 2] | 0);lg(a, e);l = d;return; + }function Dg(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0;d = l;l = l + 16 | 0;e = d;vd(e, c[b >> 2] | 0);lg(a, e);l = d;return; + }function Eg(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0;d = l;l = l + 16 | 0;e = d;yd(e, c[b >> 2] | 0);lg(a, e);l = d;return; + }function Fg(a) { + a = a | 0;return + +T(Ad(c[a >> 2] | 0)); + }function Gg(a, b) { + a = a | 0;b = b | 0;return + +T(ed(c[a >> 2] | 0, b)); + }function Hg(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0;e = l;l = l + 16 | 0;f = e;cd(f, c[b >> 2] | 0, d);lg(a, f);l = e;return; + }function Ig(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;kc(c[a >> 2] | 0, c[b >> 2] | 0, d);return; + }function Jg(a, b) { + a = a | 0;b = b | 0;cc(c[a >> 2] | 0, c[b >> 2] | 0);return; + }function Kg(a) { + a = a | 0;return $b(c[a >> 2] | 0) | 0; + }function Lg(a) { + a = a | 0;a = pc(c[a >> 2] | 0) | 0;if (!a) a = 0;else a = xf(a) | 0;return a | 0; + }function Mg(a, b) { + a = a | 0;b = b | 0;a = ac(c[a >> 2] | 0, b) | 0;if (!a) a = 0;else a = xf(a) | 0;return a | 0; + }function Ng(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0;e = qC(4) | 0;Og(e, b);d = a + 4 | 0;b = c[d >> 2] | 0;c[d >> 2] = e;if (b | 0) { + vf(b);sC(b); + }jc(c[a >> 2] | 0, 1);return; + }function Og(a, b) { + a = a | 0;b = b | 0;gh(a, b);return; + }function Pg(a, b, c, d, e, f) { + a = a | 0;b = b | 0;c = T(c);d = d | 0;e = T(e);f = f | 0;var i = 0, + j = 0;i = l;l = l + 16 | 0;j = i;Qg(j, wc(b) | 0, +c, d, +e, f);g[a >> 2] = T(+h[j >> 3]);g[a + 4 >> 2] = T(+h[j + 8 >> 3]);l = i;return; + }function Qg(a, b, d, e, f, g) { + a = a | 0;b = b | 0;d = +d;e = e | 0;f = +f;g = g | 0;var i = 0, + j = 0, + k = 0, + m = 0, + n = 0;i = l;l = l + 32 | 0;n = i + 8 | 0;m = i + 20 | 0;k = i;j = i + 16 | 0;h[n >> 3] = d;c[m >> 2] = e;h[k >> 3] = f;c[j >> 2] = g;Rg(a, c[b + 4 >> 2] | 0, n, m, k, j);l = i;return; + }function Rg(a, b, d, e, f, g) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;f = f | 0;g = g | 0;var i = 0, + j = 0;i = l;l = l + 16 | 0;j = i;UA(j);b = Sg(b) | 0;Tg(a, b, +h[d >> 3], c[e >> 2] | 0, +h[f >> 3], c[g >> 2] | 0);WA(j);l = i;return; + }function Sg(a) { + a = a | 0;return c[a >> 2] | 0; + }function Tg(a, b, c, d, e, f) { + a = a | 0;b = b | 0;c = +c;d = d | 0;e = +e;f = f | 0;var g = 0;g = Vg(Ug() | 0) | 0;c = +Wg(c);d = Xg(d) | 0;e = +Wg(e);Yg(a, cb(0, g | 0, b | 0, +c, d | 0, +e, Xg(f) | 0) | 0);return; + }function Ug() { + var b = 0;if (!(a[7608] | 0)) { + dh(9120);b = 7608;c[b >> 2] = 1;c[b + 4 >> 2] = 0; + }return 9120; + }function Vg(a) { + a = a | 0;return c[a + 8 >> 2] | 0; + }function Wg(a) { + a = +a;return + +ch(a); + }function Xg(a) { + a = a | 0;return bh(a) | 0; + }function Yg(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0;f = l;l = l + 32 | 0;d = f;e = b;if (!(e & 1)) { + c[a >> 2] = c[b >> 2];c[a + 4 >> 2] = c[b + 4 >> 2];c[a + 8 >> 2] = c[b + 8 >> 2];c[a + 12 >> 2] = c[b + 12 >> 2]; + } else { + Zg(d, 0);Ja(e | 0, d | 0) | 0;_g(a, d);$g(d); + }l = f;return; + }function Zg(b, d) { + b = b | 0;d = d | 0;ah(b, d);c[b + 8 >> 2] = 0;a[b + 24 >> 0] = 0;return; + }function _g(a, b) { + a = a | 0;b = b | 0;b = b + 8 | 0;c[a >> 2] = c[b >> 2];c[a + 4 >> 2] = c[b + 4 >> 2];c[a + 8 >> 2] = c[b + 8 >> 2];c[a + 12 >> 2] = c[b + 12 >> 2];return; + }function $g(b) { + b = b | 0;a[b + 24 >> 0] = 0;return; + }function ah(a, b) { + a = a | 0;b = b | 0;c[a >> 2] = b;return; + }function bh(a) { + a = a | 0;return a | 0; + }function ch(a) { + a = +a;return +a; + }function dh(a) { + a = a | 0;fh(a, eh() | 0, 4);return; + }function eh() { + return 1064; + }function fh(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;c[a >> 2] = b;c[a + 4 >> 2] = d;c[a + 8 >> 2] = _a(b | 0, d + 1 | 0) | 0;return; + }function gh(a, b) { + a = a | 0;b = b | 0;b = c[b >> 2] | 0;c[a >> 2] = b;Aa(b | 0);return; + }function hh(a) { + a = a | 0;var b = 0, + d = 0;d = a + 4 | 0;b = c[d >> 2] | 0;c[d >> 2] = 0;if (b | 0) { + vf(b);sC(b); + }jc(c[a >> 2] | 0, 0);return; + }function ih(a) { + a = a | 0;qc(c[a >> 2] | 0);return; + }function jh(a) { + a = a | 0;return rc(c[a >> 2] | 0) | 0; + }function kh(a, b, d, e) { + a = a | 0;b = +b;d = +d;e = e | 0;_d(c[a >> 2] | 0, T(b), T(d), e);return; + }function lh(a) { + a = a | 0;return + +T(Bd(c[a >> 2] | 0)); + }function mh(a) { + a = a | 0;return + +T(Dd(c[a >> 2] | 0)); + }function nh(a) { + a = a | 0;return + +T(Cd(c[a >> 2] | 0)); + }function oh(a) { + a = a | 0;return + +T(Ed(c[a >> 2] | 0)); + }function ph(a) { + a = a | 0;return + +T(Fd(c[a >> 2] | 0)); + }function qh(a) { + a = a | 0;return + +T(Gd(c[a >> 2] | 0)); + }function rh(a, b) { + a = a | 0;b = b | 0;h[a >> 3] = +T(Bd(c[b >> 2] | 0));h[a + 8 >> 3] = +T(Dd(c[b >> 2] | 0));h[a + 16 >> 3] = +T(Cd(c[b >> 2] | 0));h[a + 24 >> 3] = +T(Ed(c[b >> 2] | 0));h[a + 32 >> 3] = +T(Fd(c[b >> 2] | 0));h[a + 40 >> 3] = +T(Gd(c[b >> 2] | 0));return; + }function sh(a, b) { + a = a | 0;b = b | 0;return + +T(Hd(c[a >> 2] | 0, b)); + }function th(a, b) { + a = a | 0;b = b | 0;return + +T(Id(c[a >> 2] | 0, b)); + }function uh(a, b) { + a = a | 0;b = b | 0;return + +T(Jd(c[a >> 2] | 0, b)); + }function vh() { + return fc() | 0; + }function wh() { + xh();yh();zh();Ah();Bh();Ch();return; + }function xh() { + kv(11713, 4938, 1);return; + }function yh() { + yu(10448);return; + }function zh() { + eu(10408);return; + }function Ah() { + vt(10324);return; + }function Bh() { + or(10096);return; + }function Ch() { + Dh(9132);return; + }function Dh(a) { + a = a | 0;var b = 0, + d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0, + o = 0, + p = 0, + q = 0, + r = 0, + s = 0, + t = 0, + u = 0, + v = 0, + w = 0, + x = 0, + y = 0, + z = 0, + A = 0, + B = 0, + C = 0, + D = 0, + E = 0, + F = 0, + G = 0, + H = 0, + I = 0, + J = 0, + K = 0, + L = 0, + M = 0, + N = 0, + O = 0, + P = 0, + Q = 0, + R = 0, + S = 0, + T = 0, + U = 0, + V = 0, + W = 0, + X = 0, + Y = 0, + Z = 0, + _ = 0, + $ = 0, + aa = 0, + ba = 0, + ca = 0, + da = 0, + ea = 0, + fa = 0, + ga = 0, + ha = 0, + ia = 0, + ja = 0, + ka = 0, + la = 0, + ma = 0, + na = 0, + oa = 0, + pa = 0, + qa = 0, + ra = 0, + sa = 0, + ta = 0, + ua = 0, + va = 0, + wa = 0, + xa = 0, + ya = 0, + za = 0, + Aa = 0, + Ba = 0, + Ca = 0, + Da = 0, + Ea = 0, + Fa = 0, + Ga = 0;b = l;l = l + 672 | 0;d = b + 656 | 0;Ga = b + 648 | 0;Fa = b + 640 | 0;Ea = b + 632 | 0;Da = b + 624 | 0;Ca = b + 616 | 0;Ba = b + 608 | 0;Aa = b + 600 | 0;za = b + 592 | 0;ya = b + 584 | 0;xa = b + 576 | 0;wa = b + 568 | 0;va = b + 560 | 0;ua = b + 552 | 0;ta = b + 544 | 0;sa = b + 536 | 0;ra = b + 528 | 0;qa = b + 520 | 0;pa = b + 512 | 0;oa = b + 504 | 0;na = b + 496 | 0;ma = b + 488 | 0;la = b + 480 | 0;ka = b + 472 | 0;ja = b + 464 | 0;ia = b + 456 | 0;ha = b + 448 | 0;ga = b + 440 | 0;fa = b + 432 | 0;ea = b + 424 | 0;da = b + 416 | 0;ca = b + 408 | 0;ba = b + 400 | 0;aa = b + 392 | 0;$ = b + 384 | 0;_ = b + 376 | 0;Z = b + 368 | 0;Y = b + 360 | 0;X = b + 352 | 0;W = b + 344 | 0;V = b + 336 | 0;U = b + 328 | 0;T = b + 320 | 0;S = b + 312 | 0;R = b + 304 | 0;Q = b + 296 | 0;P = b + 288 | 0;O = b + 280 | 0;N = b + 272 | 0;M = b + 264 | 0;L = b + 256 | 0;K = b + 248 | 0;J = b + 240 | 0;I = b + 232 | 0;H = b + 224 | 0;G = b + 216 | 0;F = b + 208 | 0;E = b + 200 | 0;D = b + 192 | 0;C = b + 184 | 0;B = b + 176 | 0;A = b + 168 | 0;z = b + 160 | 0;y = b + 152 | 0;x = b + 144 | 0;w = b + 136 | 0;v = b + 128 | 0;u = b + 120 | 0;t = b + 112 | 0;s = b + 104 | 0;r = b + 96 | 0;q = b + 88 | 0;p = b + 80 | 0;o = b + 72 | 0;n = b + 64 | 0;m = b + 56 | 0;k = b + 48 | 0;j = b + 40 | 0;i = b + 32 | 0;h = b + 24 | 0;g = b + 16 | 0;f = b + 8 | 0;e = b;Eh(a, 3646);Fh(a, 3651, 2) | 0;Gh(a, 3665, 2) | 0;Hh(a, 3682, 18) | 0;c[Ga >> 2] = 19;c[Ga + 4 >> 2] = 0;c[d >> 2] = c[Ga >> 2];c[d + 4 >> 2] = c[Ga + 4 >> 2];Ih(a, 3690, d) | 0;c[Fa >> 2] = 1;c[Fa + 4 >> 2] = 0;c[d >> 2] = c[Fa >> 2];c[d + 4 >> 2] = c[Fa + 4 >> 2];Jh(a, 3696, d) | 0;c[Ea >> 2] = 2;c[Ea + 4 >> 2] = 0;c[d >> 2] = c[Ea >> 2];c[d + 4 >> 2] = c[Ea + 4 >> 2];Kh(a, 3706, d) | 0;c[Da >> 2] = 1;c[Da + 4 >> 2] = 0;c[d >> 2] = c[Da >> 2];c[d + 4 >> 2] = c[Da + 4 >> 2];Lh(a, 3722, d) | 0;c[Ca >> 2] = 2;c[Ca + 4 >> 2] = 0;c[d >> 2] = c[Ca >> 2];c[d + 4 >> 2] = c[Ca + 4 >> 2];Lh(a, 3734, d) | 0;c[Ba >> 2] = 3;c[Ba + 4 >> 2] = 0;c[d >> 2] = c[Ba >> 2];c[d + 4 >> 2] = c[Ba + 4 >> 2];Kh(a, 3753, d) | 0;c[Aa >> 2] = 4;c[Aa + 4 >> 2] = 0;c[d >> 2] = c[Aa >> 2];c[d + 4 >> 2] = c[Aa + 4 >> 2];Kh(a, 3769, d) | 0;c[za >> 2] = 5;c[za + 4 >> 2] = 0;c[d >> 2] = c[za >> 2];c[d + 4 >> 2] = c[za + 4 >> 2];Kh(a, 3783, d) | 0;c[ya >> 2] = 6;c[ya + 4 >> 2] = 0;c[d >> 2] = c[ya >> 2];c[d + 4 >> 2] = c[ya + 4 >> 2];Kh(a, 3796, d) | 0;c[xa >> 2] = 7;c[xa + 4 >> 2] = 0;c[d >> 2] = c[xa >> 2];c[d + 4 >> 2] = c[xa + 4 >> 2];Kh(a, 3813, d) | 0;c[wa >> 2] = 8;c[wa + 4 >> 2] = 0;c[d >> 2] = c[wa >> 2];c[d + 4 >> 2] = c[wa + 4 >> 2];Kh(a, 3825, d) | 0;c[va >> 2] = 3;c[va + 4 >> 2] = 0;c[d >> 2] = c[va >> 2];c[d + 4 >> 2] = c[va + 4 >> 2];Lh(a, 3843, d) | 0;c[ua >> 2] = 4;c[ua + 4 >> 2] = 0;c[d >> 2] = c[ua >> 2];c[d + 4 >> 2] = c[ua + 4 >> 2];Lh(a, 3853, d) | 0;c[ta >> 2] = 9;c[ta + 4 >> 2] = 0;c[d >> 2] = c[ta >> 2];c[d + 4 >> 2] = c[ta + 4 >> 2];Kh(a, 3870, d) | 0;c[sa >> 2] = 10;c[sa + 4 >> 2] = 0;c[d >> 2] = c[sa >> 2];c[d + 4 >> 2] = c[sa + 4 >> 2];Kh(a, 3884, d) | 0;c[ra >> 2] = 11;c[ra + 4 >> 2] = 0;c[d >> 2] = c[ra >> 2];c[d + 4 >> 2] = c[ra + 4 >> 2];Kh(a, 3896, d) | 0;c[qa >> 2] = 1;c[qa + 4 >> 2] = 0;c[d >> 2] = c[qa >> 2];c[d + 4 >> 2] = c[qa + 4 >> 2];Mh(a, 3907, d) | 0;c[pa >> 2] = 2;c[pa + 4 >> 2] = 0;c[d >> 2] = c[pa >> 2];c[d + 4 >> 2] = c[pa + 4 >> 2];Mh(a, 3915, d) | 0;c[oa >> 2] = 3;c[oa + 4 >> 2] = 0;c[d >> 2] = c[oa >> 2];c[d + 4 >> 2] = c[oa + 4 >> 2];Mh(a, 3928, d) | 0;c[na >> 2] = 4;c[na + 4 >> 2] = 0;c[d >> 2] = c[na >> 2];c[d + 4 >> 2] = c[na + 4 >> 2];Mh(a, 3948, d) | 0;c[ma >> 2] = 5;c[ma + 4 >> 2] = 0;c[d >> 2] = c[ma >> 2];c[d + 4 >> 2] = c[ma + 4 >> 2];Mh(a, 3960, d) | 0;c[la >> 2] = 6;c[la + 4 >> 2] = 0;c[d >> 2] = c[la >> 2];c[d + 4 >> 2] = c[la + 4 >> 2];Mh(a, 3974, d) | 0;c[ka >> 2] = 7;c[ka + 4 >> 2] = 0;c[d >> 2] = c[ka >> 2];c[d + 4 >> 2] = c[ka + 4 >> 2];Mh(a, 3983, d) | 0;c[ja >> 2] = 20;c[ja + 4 >> 2] = 0;c[d >> 2] = c[ja >> 2];c[d + 4 >> 2] = c[ja + 4 >> 2];Ih(a, 3999, d) | 0;c[ia >> 2] = 8;c[ia + 4 >> 2] = 0;c[d >> 2] = c[ia >> 2];c[d + 4 >> 2] = c[ia + 4 >> 2];Mh(a, 4012, d) | 0;c[ha >> 2] = 9;c[ha + 4 >> 2] = 0;c[d >> 2] = c[ha >> 2];c[d + 4 >> 2] = c[ha + 4 >> 2];Mh(a, 4022, d) | 0;c[ga >> 2] = 21;c[ga + 4 >> 2] = 0;c[d >> 2] = c[ga >> 2];c[d + 4 >> 2] = c[ga + 4 >> 2];Ih(a, 4039, d) | 0;c[fa >> 2] = 10;c[fa + 4 >> 2] = 0;c[d >> 2] = c[fa >> 2];c[d + 4 >> 2] = c[fa + 4 >> 2];Mh(a, 4053, d) | 0;c[ea >> 2] = 11;c[ea + 4 >> 2] = 0;c[d >> 2] = c[ea >> 2];c[d + 4 >> 2] = c[ea + 4 >> 2];Mh(a, 4065, d) | 0;c[da >> 2] = 12;c[da + 4 >> 2] = 0;c[d >> 2] = c[da >> 2];c[d + 4 >> 2] = c[da + 4 >> 2];Mh(a, 4084, d) | 0;c[ca >> 2] = 13;c[ca + 4 >> 2] = 0;c[d >> 2] = c[ca >> 2];c[d + 4 >> 2] = c[ca + 4 >> 2];Mh(a, 4097, d) | 0;c[ba >> 2] = 14;c[ba + 4 >> 2] = 0;c[d >> 2] = c[ba >> 2];c[d + 4 >> 2] = c[ba + 4 >> 2];Mh(a, 4117, d) | 0;c[aa >> 2] = 15;c[aa + 4 >> 2] = 0;c[d >> 2] = c[aa >> 2];c[d + 4 >> 2] = c[aa + 4 >> 2];Mh(a, 4129, d) | 0;c[$ >> 2] = 16;c[$ + 4 >> 2] = 0;c[d >> 2] = c[$ >> 2];c[d + 4 >> 2] = c[$ + 4 >> 2];Mh(a, 4148, d) | 0;c[_ >> 2] = 17;c[_ + 4 >> 2] = 0;c[d >> 2] = c[_ >> 2];c[d + 4 >> 2] = c[_ + 4 >> 2];Mh(a, 4161, d) | 0;c[Z >> 2] = 18;c[Z + 4 >> 2] = 0;c[d >> 2] = c[Z >> 2];c[d + 4 >> 2] = c[Z + 4 >> 2];Mh(a, 4181, d) | 0;c[Y >> 2] = 5;c[Y + 4 >> 2] = 0;c[d >> 2] = c[Y >> 2];c[d + 4 >> 2] = c[Y + 4 >> 2];Lh(a, 4196, d) | 0;c[X >> 2] = 6;c[X + 4 >> 2] = 0;c[d >> 2] = c[X >> 2];c[d + 4 >> 2] = c[X + 4 >> 2];Lh(a, 4206, d) | 0;c[W >> 2] = 7;c[W + 4 >> 2] = 0;c[d >> 2] = c[W >> 2];c[d + 4 >> 2] = c[W + 4 >> 2];Lh(a, 4217, d) | 0;c[V >> 2] = 3;c[V + 4 >> 2] = 0;c[d >> 2] = c[V >> 2];c[d + 4 >> 2] = c[V + 4 >> 2];Nh(a, 4235, d) | 0;c[U >> 2] = 1;c[U + 4 >> 2] = 0;c[d >> 2] = c[U >> 2];c[d + 4 >> 2] = c[U + 4 >> 2];Oh(a, 4251, d) | 0;c[T >> 2] = 4;c[T + 4 >> 2] = 0;c[d >> 2] = c[T >> 2];c[d + 4 >> 2] = c[T + 4 >> 2];Nh(a, 4263, d) | 0;c[S >> 2] = 5;c[S + 4 >> 2] = 0;c[d >> 2] = c[S >> 2];c[d + 4 >> 2] = c[S + 4 >> 2];Nh(a, 4279, d) | 0;c[R >> 2] = 6;c[R + 4 >> 2] = 0;c[d >> 2] = c[R >> 2];c[d + 4 >> 2] = c[R + 4 >> 2];Nh(a, 4293, d) | 0;c[Q >> 2] = 7;c[Q + 4 >> 2] = 0;c[d >> 2] = c[Q >> 2];c[d + 4 >> 2] = c[Q + 4 >> 2];Nh(a, 4306, d) | 0;c[P >> 2] = 8;c[P + 4 >> 2] = 0;c[d >> 2] = c[P >> 2];c[d + 4 >> 2] = c[P + 4 >> 2];Nh(a, 4323, d) | 0;c[O >> 2] = 9;c[O + 4 >> 2] = 0;c[d >> 2] = c[O >> 2];c[d + 4 >> 2] = c[O + 4 >> 2];Nh(a, 4335, d) | 0;c[N >> 2] = 2;c[N + 4 >> 2] = 0;c[d >> 2] = c[N >> 2];c[d + 4 >> 2] = c[N + 4 >> 2];Oh(a, 4353, d) | 0;c[M >> 2] = 12;c[M + 4 >> 2] = 0;c[d >> 2] = c[M >> 2];c[d + 4 >> 2] = c[M + 4 >> 2];Ph(a, 4363, d) | 0;c[L >> 2] = 1;c[L + 4 >> 2] = 0;c[d >> 2] = c[L >> 2];c[d + 4 >> 2] = c[L + 4 >> 2];Qh(a, 4376, d) | 0;c[K >> 2] = 2;c[K + 4 >> 2] = 0;c[d >> 2] = c[K >> 2];c[d + 4 >> 2] = c[K + 4 >> 2];Qh(a, 4388, d) | 0;c[J >> 2] = 13;c[J + 4 >> 2] = 0;c[d >> 2] = c[J >> 2];c[d + 4 >> 2] = c[J + 4 >> 2];Ph(a, 4402, d) | 0;c[I >> 2] = 14;c[I + 4 >> 2] = 0;c[d >> 2] = c[I >> 2];c[d + 4 >> 2] = c[I + 4 >> 2];Ph(a, 4411, d) | 0;c[H >> 2] = 15;c[H + 4 >> 2] = 0;c[d >> 2] = c[H >> 2];c[d + 4 >> 2] = c[H + 4 >> 2];Ph(a, 4421, d) | 0;c[G >> 2] = 16;c[G + 4 >> 2] = 0;c[d >> 2] = c[G >> 2];c[d + 4 >> 2] = c[G + 4 >> 2];Ph(a, 4433, d) | 0;c[F >> 2] = 17;c[F + 4 >> 2] = 0;c[d >> 2] = c[F >> 2];c[d + 4 >> 2] = c[F + 4 >> 2];Ph(a, 4446, d) | 0;c[E >> 2] = 18;c[E + 4 >> 2] = 0;c[d >> 2] = c[E >> 2];c[d + 4 >> 2] = c[E + 4 >> 2];Ph(a, 4458, d) | 0;c[D >> 2] = 3;c[D + 4 >> 2] = 0;c[d >> 2] = c[D >> 2];c[d + 4 >> 2] = c[D + 4 >> 2];Qh(a, 4471, d) | 0;c[C >> 2] = 1;c[C + 4 >> 2] = 0;c[d >> 2] = c[C >> 2];c[d + 4 >> 2] = c[C + 4 >> 2];Rh(a, 4486, d) | 0;c[B >> 2] = 10;c[B + 4 >> 2] = 0;c[d >> 2] = c[B >> 2];c[d + 4 >> 2] = c[B + 4 >> 2];Nh(a, 4496, d) | 0;c[A >> 2] = 11;c[A + 4 >> 2] = 0;c[d >> 2] = c[A >> 2];c[d + 4 >> 2] = c[A + 4 >> 2];Nh(a, 4508, d) | 0;c[z >> 2] = 3;c[z + 4 >> 2] = 0;c[d >> 2] = c[z >> 2];c[d + 4 >> 2] = c[z + 4 >> 2];Oh(a, 4519, d) | 0;c[y >> 2] = 4;c[y + 4 >> 2] = 0;c[d >> 2] = c[y >> 2];c[d + 4 >> 2] = c[y + 4 >> 2];Sh(a, 4530, d) | 0;c[x >> 2] = 19;c[x + 4 >> 2] = 0;c[d >> 2] = c[x >> 2];c[d + 4 >> 2] = c[x + 4 >> 2];Th(a, 4542, d) | 0;c[w >> 2] = 12;c[w + 4 >> 2] = 0;c[d >> 2] = c[w >> 2];c[d + 4 >> 2] = c[w + 4 >> 2];Uh(a, 4554, d) | 0;c[v >> 2] = 13;c[v + 4 >> 2] = 0;c[d >> 2] = c[v >> 2];c[d + 4 >> 2] = c[v + 4 >> 2];Vh(a, 4568, d) | 0;c[u >> 2] = 2;c[u + 4 >> 2] = 0;c[d >> 2] = c[u >> 2];c[d + 4 >> 2] = c[u + 4 >> 2];Wh(a, 4578, d) | 0;c[t >> 2] = 20;c[t + 4 >> 2] = 0;c[d >> 2] = c[t >> 2];c[d + 4 >> 2] = c[t + 4 >> 2];Xh(a, 4587, d) | 0;c[s >> 2] = 22;c[s + 4 >> 2] = 0;c[d >> 2] = c[s >> 2];c[d + 4 >> 2] = c[s + 4 >> 2];Ih(a, 4602, d) | 0;c[r >> 2] = 23;c[r + 4 >> 2] = 0;c[d >> 2] = c[r >> 2];c[d + 4 >> 2] = c[r + 4 >> 2];Ih(a, 4619, d) | 0;c[q >> 2] = 14;c[q + 4 >> 2] = 0;c[d >> 2] = c[q >> 2];c[d + 4 >> 2] = c[q + 4 >> 2];Yh(a, 4629, d) | 0;c[p >> 2] = 1;c[p + 4 >> 2] = 0;c[d >> 2] = c[p >> 2];c[d + 4 >> 2] = c[p + 4 >> 2];Zh(a, 4637, d) | 0;c[o >> 2] = 4;c[o + 4 >> 2] = 0;c[d >> 2] = c[o >> 2];c[d + 4 >> 2] = c[o + 4 >> 2];Qh(a, 4653, d) | 0;c[n >> 2] = 5;c[n + 4 >> 2] = 0;c[d >> 2] = c[n >> 2];c[d + 4 >> 2] = c[n + 4 >> 2];Qh(a, 4669, d) | 0;c[m >> 2] = 6;c[m + 4 >> 2] = 0;c[d >> 2] = c[m >> 2];c[d + 4 >> 2] = c[m + 4 >> 2];Qh(a, 4686, d) | 0;c[k >> 2] = 7;c[k + 4 >> 2] = 0;c[d >> 2] = c[k >> 2];c[d + 4 >> 2] = c[k + 4 >> 2];Qh(a, 4701, d) | 0;c[j >> 2] = 8;c[j + 4 >> 2] = 0;c[d >> 2] = c[j >> 2];c[d + 4 >> 2] = c[j + 4 >> 2];Qh(a, 4719, d) | 0;c[i >> 2] = 9;c[i + 4 >> 2] = 0;c[d >> 2] = c[i >> 2];c[d + 4 >> 2] = c[i + 4 >> 2];Qh(a, 4736, d) | 0;c[h >> 2] = 21;c[h + 4 >> 2] = 0;c[d >> 2] = c[h >> 2];c[d + 4 >> 2] = c[h + 4 >> 2];_h(a, 4754, d) | 0;c[g >> 2] = 2;c[g + 4 >> 2] = 0;c[d >> 2] = c[g >> 2];c[d + 4 >> 2] = c[g + 4 >> 2];Rh(a, 4772, d) | 0;c[f >> 2] = 3;c[f + 4 >> 2] = 0;c[d >> 2] = c[f >> 2];c[d + 4 >> 2] = c[f + 4 >> 2];Rh(a, 4790, d) | 0;c[e >> 2] = 4;c[e + 4 >> 2] = 0;c[d >> 2] = c[e >> 2];c[d + 4 >> 2] = c[e + 4 >> 2];Rh(a, 4808, d) | 0;l = b;return; + }function Eh(a, b) { + a = a | 0;b = b | 0;var d = 0;d = dr() | 0;c[a >> 2] = d;er(d, b);Hv(c[a >> 2] | 0);return; + }function Fh(a, b, c) { + a = a | 0;b = b | 0;c = c | 0;Oq(a, ai(b) | 0, c, 0);return a | 0; + }function Gh(a, b, c) { + a = a | 0;b = b | 0;c = c | 0;tq(a, ai(b) | 0, c, 0);return a | 0; + }function Hh(a, b, c) { + a = a | 0;b = b | 0;c = c | 0;cq(a, ai(b) | 0, c, 0);return a | 0; + }function Ih(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;h = c[d + 4 >> 2] | 0;c[g >> 2] = c[d >> 2];c[g + 4 >> 2] = h;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];Lp(a, b, f);l = e;return a | 0; + }function Jh(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;h = c[d + 4 >> 2] | 0;c[g >> 2] = c[d >> 2];c[g + 4 >> 2] = h;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];pp(a, b, f);l = e;return a | 0; + }function Kh(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;h = c[d + 4 >> 2] | 0;c[g >> 2] = c[d >> 2];c[g + 4 >> 2] = h;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];Yo(a, b, f);l = e;return a | 0; + }function Lh(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;h = c[d + 4 >> 2] | 0;c[g >> 2] = c[d >> 2];c[g + 4 >> 2] = h;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];Fo(a, b, f);l = e;return a | 0; + }function Mh(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;h = c[d + 4 >> 2] | 0;c[g >> 2] = c[d >> 2];c[g + 4 >> 2] = h;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];mo(a, b, f);l = e;return a | 0; + }function Nh(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;h = c[d + 4 >> 2] | 0;c[g >> 2] = c[d >> 2];c[g + 4 >> 2] = h;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];Un(a, b, f);l = e;return a | 0; + }function Oh(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;h = c[d + 4 >> 2] | 0;c[g >> 2] = c[d >> 2];c[g + 4 >> 2] = h;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];Bn(a, b, f);l = e;return a | 0; + }function Ph(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;h = c[d + 4 >> 2] | 0;c[g >> 2] = c[d >> 2];c[g + 4 >> 2] = h;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];Um(a, b, f);l = e;return a | 0; + }function Qh(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;h = c[d + 4 >> 2] | 0;c[g >> 2] = c[d >> 2];c[g + 4 >> 2] = h;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];Bm(a, b, f);l = e;return a | 0; + }function Rh(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;h = c[d + 4 >> 2] | 0;c[g >> 2] = c[d >> 2];c[g + 4 >> 2] = h;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];im(a, b, f);l = e;return a | 0; + }function Sh(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;h = c[d + 4 >> 2] | 0;c[g >> 2] = c[d >> 2];c[g + 4 >> 2] = h;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];Rl(a, b, f);l = e;return a | 0; + }function Th(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;h = c[d + 4 >> 2] | 0;c[g >> 2] = c[d >> 2];c[g + 4 >> 2] = h;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];vl(a, b, f);l = e;return a | 0; + }function Uh(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;h = c[d + 4 >> 2] | 0;c[g >> 2] = c[d >> 2];c[g + 4 >> 2] = h;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];bl(a, b, f);l = e;return a | 0; + }function Vh(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;h = c[d + 4 >> 2] | 0;c[g >> 2] = c[d >> 2];c[g + 4 >> 2] = h;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];Kk(a, b, f);l = e;return a | 0; + }function Wh(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;h = c[d + 4 >> 2] | 0;c[g >> 2] = c[d >> 2];c[g + 4 >> 2] = h;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];nk(a, b, f);l = e;return a | 0; + }function Xh(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;h = c[d + 4 >> 2] | 0;c[g >> 2] = c[d >> 2];c[g + 4 >> 2] = h;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];Rj(a, b, f);l = e;return a | 0; + }function Yh(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;h = c[d + 4 >> 2] | 0;c[g >> 2] = c[d >> 2];c[g + 4 >> 2] = h;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];xj(a, b, f);l = e;return a | 0; + }function Zh(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;h = c[d + 4 >> 2] | 0;c[g >> 2] = c[d >> 2];c[g + 4 >> 2] = h;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];_i(a, b, f);l = e;return a | 0; + }function _h(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;h = c[d + 4 >> 2] | 0;c[g >> 2] = c[d >> 2];c[g + 4 >> 2] = h;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];$h(a, b, f);l = e;return a | 0; + }function $h(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;i = c[d >> 2] | 0;h = c[d + 4 >> 2] | 0;d = ai(b) | 0;c[g >> 2] = i;c[g + 4 >> 2] = h;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];bi(a, d, f, 1);l = e;return; + }function ai(a) { + a = a | 0;return a | 0; + }function bi(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0;f = l;l = l + 32 | 0;g = f + 16 | 0;m = f + 8 | 0;i = f;k = c[d >> 2] | 0;j = c[d + 4 >> 2] | 0;h = c[a >> 2] | 0;a = ci() | 0;c[m >> 2] = k;c[m + 4 >> 2] = j;c[g >> 2] = c[m >> 2];c[g + 4 >> 2] = c[m + 4 >> 2];d = di(g) | 0;c[i >> 2] = k;c[i + 4 >> 2] = j;c[g >> 2] = c[i >> 2];c[g + 4 >> 2] = c[i + 4 >> 2];fi(h, b, a, d, ei(g, e) | 0, e);l = f;return; + }function ci() { + var b = 0, + d = 0;if (!(a[7616] | 0)) { + qi(9136);Ha(24, 9136, o | 0) | 0;d = 7616;c[d >> 2] = 1;c[d + 4 >> 2] = 0; + }if (!(si(9136) | 0)) { + b = 9136;d = b + 36 | 0;do { + c[b >> 2] = 0;b = b + 4 | 0; + } while ((b | 0) < (d | 0));qi(9136); + }return 9136; + }function di(a) { + a = a | 0;return 0; + }function ei(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0;m = l;l = l + 32 | 0;f = m + 24 | 0;h = m + 16 | 0;i = m;j = m + 8 | 0;g = c[a >> 2] | 0;e = c[a + 4 >> 2] | 0;c[i >> 2] = g;c[i + 4 >> 2] = e;n = ci() | 0;k = n + 24 | 0;a = ji(b, 4) | 0;c[j >> 2] = a;b = n + 28 | 0;d = c[b >> 2] | 0;if (d >>> 0 < (c[n + 32 >> 2] | 0) >>> 0) { + c[h >> 2] = g;c[h + 4 >> 2] = e;c[f >> 2] = c[h >> 2];c[f + 4 >> 2] = c[h + 4 >> 2];ki(d, f, a);a = (c[b >> 2] | 0) + 12 | 0;c[b >> 2] = a; + } else { + li(k, i, j);a = c[b >> 2] | 0; + }l = m;return ((a - (c[k >> 2] | 0) | 0) / 12 | 0) + -1 | 0; + }function fi(a, b, d, e, f, g) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;f = f | 0;g = g | 0;var h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0, + o = 0, + p = 0;h = l;l = l + 32 | 0;o = h + 24 | 0;n = h + 20 | 0;j = h + 16 | 0;m = h + 12 | 0;k = h + 8 | 0;i = h + 4 | 0;p = h;c[n >> 2] = b;c[j >> 2] = d;c[m >> 2] = e;c[k >> 2] = f;c[i >> 2] = g;g = a + 28 | 0;c[p >> 2] = c[g >> 2];c[o >> 2] = c[p >> 2];gi(a + 24 | 0, o, n, m, k, j, i) | 0;c[g >> 2] = c[c[g >> 2] >> 2];l = h;return; + }function gi(a, b, d, e, f, g, h) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;f = f | 0;g = g | 0;h = h | 0;a = hi(b) | 0;b = qC(24) | 0;ii(b + 4 | 0, c[d >> 2] | 0, c[e >> 2] | 0, c[f >> 2] | 0, c[g >> 2] | 0, c[h >> 2] | 0);c[b >> 2] = c[a >> 2];c[a >> 2] = b;return b | 0; + }function hi(a) { + a = a | 0;return c[a >> 2] | 0; + }function ii(a, b, d, e, f, g) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;f = f | 0;g = g | 0;c[a >> 2] = b;c[a + 4 >> 2] = d;c[a + 8 >> 2] = e;c[a + 12 >> 2] = f;c[a + 16 >> 2] = g;return; + }function ji(a, b) { + a = a | 0;b = b | 0;return b | a | 0; + }function ki(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0;e = c[b + 4 >> 2] | 0;c[a >> 2] = c[b >> 2];c[a + 4 >> 2] = e;c[a + 8 >> 2] = d;return; + }function li(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0, + o = 0;k = l;l = l + 48 | 0;e = k + 32 | 0;h = k + 24 | 0;i = k;j = a + 4 | 0;f = (((c[j >> 2] | 0) - (c[a >> 2] | 0) | 0) / 12 | 0) + 1 | 0;g = mi(a) | 0;if (g >>> 0 < f >>> 0) jC(a);else { + m = c[a >> 2] | 0;o = ((c[a + 8 >> 2] | 0) - m | 0) / 12 | 0;n = o << 1;ni(i, o >>> 0 < g >>> 1 >>> 0 ? n >>> 0 < f >>> 0 ? f : n : g, ((c[j >> 2] | 0) - m | 0) / 12 | 0, a + 8 | 0);j = i + 8 | 0;g = c[j >> 2] | 0;f = c[b + 4 >> 2] | 0;d = c[d >> 2] | 0;c[h >> 2] = c[b >> 2];c[h + 4 >> 2] = f;c[e >> 2] = c[h >> 2];c[e + 4 >> 2] = c[h + 4 >> 2];ki(g, e, d);c[j >> 2] = (c[j >> 2] | 0) + 12;oi(a, i);pi(i);l = k;return; + } + }function mi(a) { + a = a | 0;return 357913941; + }function ni(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0;c[a + 12 >> 2] = 0;c[a + 16 >> 2] = e;do if (b) { + if (b >>> 0 > 357913941) Ta();else { + f = qC(b * 12 | 0) | 0;break; + } + } else f = 0; while (0);c[a >> 2] = f;e = f + (d * 12 | 0) | 0;c[a + 8 >> 2] = e;c[a + 4 >> 2] = e;c[a + 12 >> 2] = f + (b * 12 | 0);return; + }function oi(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0;e = c[a >> 2] | 0;h = a + 4 | 0;g = b + 4 | 0;f = (c[h >> 2] | 0) - e | 0;d = (c[g >> 2] | 0) + (((f | 0) / -12 | 0) * 12 | 0) | 0;c[g >> 2] = d;if ((f | 0) > 0) { + BC(d | 0, e | 0, f | 0) | 0;e = g;d = c[g >> 2] | 0; + } else e = g;g = c[a >> 2] | 0;c[a >> 2] = d;c[e >> 2] = g;g = b + 8 | 0;f = c[h >> 2] | 0;c[h >> 2] = c[g >> 2];c[g >> 2] = f;g = a + 8 | 0;h = b + 12 | 0;a = c[g >> 2] | 0;c[g >> 2] = c[h >> 2];c[h >> 2] = a;c[b >> 2] = c[e >> 2];return; + }function pi(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;b = c[a + 4 >> 2] | 0;d = a + 8 | 0;e = c[d >> 2] | 0;if ((e | 0) != (b | 0)) c[d >> 2] = e + (~(((e + -12 - b | 0) >>> 0) / 12 | 0) * 12 | 0);a = c[a >> 2] | 0;if (a | 0) sC(a);return; + }function qi(a) { + a = a | 0;ui(a);return; + }function ri(a) { + a = a | 0;ti(a + 24 | 0);return; + }function si(a) { + a = a | 0;return c[a >> 2] | 0; + }function ti(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;d = c[a >> 2] | 0;e = d;if (d | 0) { + a = a + 4 | 0;b = c[a >> 2] | 0;if ((b | 0) != (d | 0)) c[a >> 2] = b + (~(((b + -12 - e | 0) >>> 0) / 12 | 0) * 12 | 0);sC(d); + }return; + }function ui(a) { + a = a | 0;var b = 0;b = vi() | 0;yi(a, 2, 3, b, wi() | 0, 0);c[a + 24 >> 2] = 0;c[a + 28 >> 2] = 0;c[a + 32 >> 2] = 0;return; + }function vi() { + return 9228; + }function wi() { + return 1140; + }function xi(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0;d = l;l = l + 16 | 0;e = d + 8 | 0;f = d;g = zi(a) | 0;a = c[g + 4 >> 2] | 0;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = a;c[e >> 2] = c[f >> 2];c[e + 4 >> 2] = c[f + 4 >> 2];b = Ai(b, e) | 0;l = d;return b | 0; + }function yi(a, b, d, e, f, g) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;f = f | 0;g = g | 0;c[a >> 2] = b;c[a + 4 >> 2] = d;c[a + 8 >> 2] = e;c[a + 12 >> 2] = f;c[a + 16 >> 2] = g;return; + }function zi(a) { + a = a | 0;return (c[(ci() | 0) + 24 >> 2] | 0) + (a * 12 | 0) | 0; + }function Ai(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0;f = l;l = l + 48 | 0;e = f;d = c[b >> 2] | 0;b = c[b + 4 >> 2] | 0;a = a + (b >> 1) | 0;if (b & 1) d = c[(c[a >> 2] | 0) + d >> 2] | 0;ob[d & 31](e, a);e = Bi(e) | 0;l = f;return e | 0; + }function Bi(a) { + a = a | 0;var b = 0, + c = 0, + d = 0, + e = 0;e = l;l = l + 32 | 0;b = e + 12 | 0;c = e;d = Di(Ci() | 0) | 0;if (!d) a = Ii(a) | 0;else { + Ei(b, d);Fi(c, b);Gi(a, c);a = Hi(b) | 0; + }l = e;return a | 0; + }function Ci() { + var b = 0;if (!(a[7632] | 0)) { + Ti(9184);Ha(25, 9184, o | 0) | 0;b = 7632;c[b >> 2] = 1;c[b + 4 >> 2] = 0; + }return 9184; + }function Di(a) { + a = a | 0;return c[a + 36 >> 2] | 0; + }function Ei(a, b) { + a = a | 0;b = b | 0;c[a >> 2] = b;c[a + 4 >> 2] = a;c[a + 8 >> 2] = 0;return; + }function Fi(a, b) { + a = a | 0;b = b | 0;c[a >> 2] = c[b >> 2];c[a + 4 >> 2] = c[b + 4 >> 2];c[a + 8 >> 2] = 0;return; + }function Gi(a, b) { + a = a | 0;b = b | 0;Ni(b, a, a + 8 | 0, a + 16 | 0, a + 24 | 0, a + 32 | 0, a + 40 | 0) | 0;return; + }function Hi(a) { + a = a | 0;return c[(c[a + 4 >> 2] | 0) + 8 >> 2] | 0; + }function Ii(a) { + a = a | 0;var b = 0, + d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0;j = l;l = l + 16 | 0;d = j + 4 | 0;e = j;f = jy(8) | 0;g = f;h = qC(48) | 0;i = h;b = i + 48 | 0;do { + c[i >> 2] = c[a >> 2];i = i + 4 | 0;a = a + 4 | 0; + } while ((i | 0) < (b | 0));b = g + 4 | 0;c[b >> 2] = h;i = qC(8) | 0;h = c[b >> 2] | 0;c[e >> 2] = 0;c[d >> 2] = c[e >> 2];Ji(i, h, d);c[f >> 2] = i;l = j;return g | 0; + }function Ji(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;c[a >> 2] = b;d = qC(16) | 0;c[d + 4 >> 2] = 0;c[d + 8 >> 2] = 0;c[d >> 2] = 1092;c[d + 12 >> 2] = b;c[a + 4 >> 2] = d;return; + }function Ki(a) { + a = a | 0;kC(a);sC(a);return; + }function Li(a) { + a = a | 0;a = c[a + 12 >> 2] | 0;if (a | 0) sC(a);return; + }function Mi(a) { + a = a | 0;sC(a);return; + }function Ni(a, b, d, e, f, g, h) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;f = f | 0;g = g | 0;h = h | 0;g = Oi(c[a >> 2] | 0, b, d, e, f, g, h) | 0;h = a + 4 | 0;c[(c[h >> 2] | 0) + 8 >> 2] = g;return c[(c[h >> 2] | 0) + 8 >> 2] | 0; + }function Oi(a, b, c, d, e, f, g) { + a = a | 0;b = b | 0;c = c | 0;d = d | 0;e = e | 0;f = f | 0;g = g | 0;var i = 0, + j = 0;i = l;l = l + 16 | 0;j = i;UA(j);a = Sg(a) | 0;g = Pi(a, +h[b >> 3], +h[c >> 3], +h[d >> 3], +h[e >> 3], +h[f >> 3], +h[g >> 3]) | 0;WA(j);l = i;return g | 0; + }function Pi(a, b, c, d, e, f, g) { + a = a | 0;b = +b;c = +c;d = +d;e = +e;f = +f;g = +g;var h = 0;h = Vg(Qi() | 0) | 0;b = +Wg(b);c = +Wg(c);d = +Wg(d);e = +Wg(e);f = +Wg(f);return ya(0, h | 0, a | 0, +b, +c, +d, +e, +f, + +Wg(g)) | 0; + }function Qi() { + var b = 0;if (!(a[7624] | 0)) { + Ri(9172);b = 7624;c[b >> 2] = 1;c[b + 4 >> 2] = 0; + }return 9172; + }function Ri(a) { + a = a | 0;fh(a, Si() | 0, 6);return; + }function Si() { + return 1112; + }function Ti(a) { + a = a | 0;Zi(a);return; + }function Ui(a) { + a = a | 0;Vi(a + 24 | 0);Wi(a + 16 | 0);return; + }function Vi(a) { + a = a | 0;Yi(a);return; + }function Wi(a) { + a = a | 0;Xi(a);return; + }function Xi(a) { + a = a | 0;var b = 0, + d = 0;b = c[a >> 2] | 0;if (b | 0) do { + d = b;b = c[b >> 2] | 0;sC(d); + } while ((b | 0) != 0);c[a >> 2] = 0;return; + }function Yi(a) { + a = a | 0;var b = 0, + d = 0;b = c[a >> 2] | 0;if (b | 0) do { + d = b;b = c[b >> 2] | 0;sC(d); + } while ((b | 0) != 0);c[a >> 2] = 0;return; + }function Zi(b) { + b = b | 0;var d = 0;c[b + 16 >> 2] = 0;c[b + 20 >> 2] = 0;d = b + 24 | 0;c[d >> 2] = 0;c[b + 28 >> 2] = d;c[b + 36 >> 2] = 0;a[b + 40 >> 0] = 0;a[b + 41 >> 0] = 0;return; + }function _i(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;i = c[d >> 2] | 0;h = c[d + 4 >> 2] | 0;d = ai(b) | 0;c[g >> 2] = i;c[g + 4 >> 2] = h;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];$i(a, d, f, 0);l = e;return; + }function $i(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0;f = l;l = l + 32 | 0;g = f + 16 | 0;m = f + 8 | 0;i = f;k = c[d >> 2] | 0;j = c[d + 4 >> 2] | 0;h = c[a >> 2] | 0;a = aj() | 0;c[m >> 2] = k;c[m + 4 >> 2] = j;c[g >> 2] = c[m >> 2];c[g + 4 >> 2] = c[m + 4 >> 2];d = bj(g) | 0;c[i >> 2] = k;c[i + 4 >> 2] = j;c[g >> 2] = c[i >> 2];c[g + 4 >> 2] = c[i + 4 >> 2];fi(h, b, a, d, cj(g, e) | 0, e);l = f;return; + }function aj() { + var b = 0, + d = 0;if (!(a[7640] | 0)) { + jj(9232);Ha(26, 9232, o | 0) | 0;d = 7640;c[d >> 2] = 1;c[d + 4 >> 2] = 0; + }if (!(si(9232) | 0)) { + b = 9232;d = b + 36 | 0;do { + c[b >> 2] = 0;b = b + 4 | 0; + } while ((b | 0) < (d | 0));jj(9232); + }return 9232; + }function bj(a) { + a = a | 0;return 0; + }function cj(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0;m = l;l = l + 32 | 0;f = m + 24 | 0;h = m + 16 | 0;i = m;j = m + 8 | 0;g = c[a >> 2] | 0;e = c[a + 4 >> 2] | 0;c[i >> 2] = g;c[i + 4 >> 2] = e;n = aj() | 0;k = n + 24 | 0;a = ji(b, 4) | 0;c[j >> 2] = a;b = n + 28 | 0;d = c[b >> 2] | 0;if (d >>> 0 < (c[n + 32 >> 2] | 0) >>> 0) { + c[h >> 2] = g;c[h + 4 >> 2] = e;c[f >> 2] = c[h >> 2];c[f + 4 >> 2] = c[h + 4 >> 2];dj(d, f, a);a = (c[b >> 2] | 0) + 12 | 0;c[b >> 2] = a; + } else { + ej(k, i, j);a = c[b >> 2] | 0; + }l = m;return ((a - (c[k >> 2] | 0) | 0) / 12 | 0) + -1 | 0; + }function dj(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0;e = c[b + 4 >> 2] | 0;c[a >> 2] = c[b >> 2];c[a + 4 >> 2] = e;c[a + 8 >> 2] = d;return; + }function ej(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0, + o = 0;k = l;l = l + 48 | 0;e = k + 32 | 0;h = k + 24 | 0;i = k;j = a + 4 | 0;f = (((c[j >> 2] | 0) - (c[a >> 2] | 0) | 0) / 12 | 0) + 1 | 0;g = fj(a) | 0;if (g >>> 0 < f >>> 0) jC(a);else { + m = c[a >> 2] | 0;o = ((c[a + 8 >> 2] | 0) - m | 0) / 12 | 0;n = o << 1;gj(i, o >>> 0 < g >>> 1 >>> 0 ? n >>> 0 < f >>> 0 ? f : n : g, ((c[j >> 2] | 0) - m | 0) / 12 | 0, a + 8 | 0);j = i + 8 | 0;g = c[j >> 2] | 0;f = c[b + 4 >> 2] | 0;d = c[d >> 2] | 0;c[h >> 2] = c[b >> 2];c[h + 4 >> 2] = f;c[e >> 2] = c[h >> 2];c[e + 4 >> 2] = c[h + 4 >> 2];dj(g, e, d);c[j >> 2] = (c[j >> 2] | 0) + 12;hj(a, i);ij(i);l = k;return; + } + }function fj(a) { + a = a | 0;return 357913941; + }function gj(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0;c[a + 12 >> 2] = 0;c[a + 16 >> 2] = e;do if (b) { + if (b >>> 0 > 357913941) Ta();else { + f = qC(b * 12 | 0) | 0;break; + } + } else f = 0; while (0);c[a >> 2] = f;e = f + (d * 12 | 0) | 0;c[a + 8 >> 2] = e;c[a + 4 >> 2] = e;c[a + 12 >> 2] = f + (b * 12 | 0);return; + }function hj(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0;e = c[a >> 2] | 0;h = a + 4 | 0;g = b + 4 | 0;f = (c[h >> 2] | 0) - e | 0;d = (c[g >> 2] | 0) + (((f | 0) / -12 | 0) * 12 | 0) | 0;c[g >> 2] = d;if ((f | 0) > 0) { + BC(d | 0, e | 0, f | 0) | 0;e = g;d = c[g >> 2] | 0; + } else e = g;g = c[a >> 2] | 0;c[a >> 2] = d;c[e >> 2] = g;g = b + 8 | 0;f = c[h >> 2] | 0;c[h >> 2] = c[g >> 2];c[g >> 2] = f;g = a + 8 | 0;h = b + 12 | 0;a = c[g >> 2] | 0;c[g >> 2] = c[h >> 2];c[h >> 2] = a;c[b >> 2] = c[e >> 2];return; + }function ij(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;b = c[a + 4 >> 2] | 0;d = a + 8 | 0;e = c[d >> 2] | 0;if ((e | 0) != (b | 0)) c[d >> 2] = e + (~(((e + -12 - b | 0) >>> 0) / 12 | 0) * 12 | 0);a = c[a >> 2] | 0;if (a | 0) sC(a);return; + }function jj(a) { + a = a | 0;mj(a);return; + }function kj(a) { + a = a | 0;lj(a + 24 | 0);return; + }function lj(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;d = c[a >> 2] | 0;e = d;if (d | 0) { + a = a + 4 | 0;b = c[a >> 2] | 0;if ((b | 0) != (d | 0)) c[a >> 2] = b + (~(((b + -12 - e | 0) >>> 0) / 12 | 0) * 12 | 0);sC(d); + }return; + }function mj(a) { + a = a | 0;var b = 0;b = vi() | 0;yi(a, 2, 1, b, nj() | 0, 3);c[a + 24 >> 2] = 0;c[a + 28 >> 2] = 0;c[a + 32 >> 2] = 0;return; + }function nj() { + return 1144; + }function oj(a, b, d, e, f) { + a = a | 0;b = b | 0;d = +d;e = +e;f = f | 0;var g = 0, + h = 0, + i = 0, + j = 0;g = l;l = l + 16 | 0;h = g + 8 | 0;i = g;j = pj(a) | 0;a = c[j + 4 >> 2] | 0;c[i >> 2] = c[j >> 2];c[i + 4 >> 2] = a;c[h >> 2] = c[i >> 2];c[h + 4 >> 2] = c[i + 4 >> 2];qj(b, h, d, e, f);l = g;return; + }function pj(a) { + a = a | 0;return (c[(aj() | 0) + 24 >> 2] | 0) + (a * 12 | 0) | 0; + }function qj(a, b, d, e, f) { + a = a | 0;b = b | 0;d = +d;e = +e;f = f | 0;var g = 0, + h = 0, + i = 0, + j = 0, + k = 0;k = l;l = l + 16 | 0;h = k + 2 | 0;i = k + 1 | 0;j = k;g = c[b >> 2] | 0;b = c[b + 4 >> 2] | 0;a = a + (b >> 1) | 0;if (b & 1) g = c[(c[a >> 2] | 0) + g >> 2] | 0;d = +sj(h, d);e = +sj(i, e);j = uj(j, f) | 0;qb[g & 1](a, d, e, j);l = k;return; + }function sj(a, b) { + a = a | 0;b = +b;return + +wj(b); + }function uj(a, b) { + a = a | 0;b = b | 0;return vj(b) | 0; + }function vj(a) { + a = a | 0;return a | 0; + }function wj(a) { + a = +a;return +a; + }function xj(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;i = c[d >> 2] | 0;h = c[d + 4 >> 2] | 0;d = ai(b) | 0;c[g >> 2] = i;c[g + 4 >> 2] = h;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];yj(a, d, f, 1);l = e;return; + }function yj(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0;f = l;l = l + 32 | 0;g = f + 16 | 0;m = f + 8 | 0;i = f;k = c[d >> 2] | 0;j = c[d + 4 >> 2] | 0;h = c[a >> 2] | 0;a = zj() | 0;c[m >> 2] = k;c[m + 4 >> 2] = j;c[g >> 2] = c[m >> 2];c[g + 4 >> 2] = c[m + 4 >> 2];d = Aj(g) | 0;c[i >> 2] = k;c[i + 4 >> 2] = j;c[g >> 2] = c[i >> 2];c[g + 4 >> 2] = c[i + 4 >> 2];fi(h, b, a, d, Bj(g, e) | 0, e);l = f;return; + }function zj() { + var b = 0, + d = 0;if (!(a[7648] | 0)) { + Ij(9268);Ha(27, 9268, o | 0) | 0;d = 7648;c[d >> 2] = 1;c[d + 4 >> 2] = 0; + }if (!(si(9268) | 0)) { + b = 9268;d = b + 36 | 0;do { + c[b >> 2] = 0;b = b + 4 | 0; + } while ((b | 0) < (d | 0));Ij(9268); + }return 9268; + }function Aj(a) { + a = a | 0;return 0; + }function Bj(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0;m = l;l = l + 32 | 0;f = m + 24 | 0;h = m + 16 | 0;i = m;j = m + 8 | 0;g = c[a >> 2] | 0;e = c[a + 4 >> 2] | 0;c[i >> 2] = g;c[i + 4 >> 2] = e;n = zj() | 0;k = n + 24 | 0;a = ji(b, 4) | 0;c[j >> 2] = a;b = n + 28 | 0;d = c[b >> 2] | 0;if (d >>> 0 < (c[n + 32 >> 2] | 0) >>> 0) { + c[h >> 2] = g;c[h + 4 >> 2] = e;c[f >> 2] = c[h >> 2];c[f + 4 >> 2] = c[h + 4 >> 2];Cj(d, f, a);a = (c[b >> 2] | 0) + 12 | 0;c[b >> 2] = a; + } else { + Dj(k, i, j);a = c[b >> 2] | 0; + }l = m;return ((a - (c[k >> 2] | 0) | 0) / 12 | 0) + -1 | 0; + }function Cj(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0;e = c[b + 4 >> 2] | 0;c[a >> 2] = c[b >> 2];c[a + 4 >> 2] = e;c[a + 8 >> 2] = d;return; + }function Dj(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0, + o = 0;k = l;l = l + 48 | 0;e = k + 32 | 0;h = k + 24 | 0;i = k;j = a + 4 | 0;f = (((c[j >> 2] | 0) - (c[a >> 2] | 0) | 0) / 12 | 0) + 1 | 0;g = Ej(a) | 0;if (g >>> 0 < f >>> 0) jC(a);else { + m = c[a >> 2] | 0;o = ((c[a + 8 >> 2] | 0) - m | 0) / 12 | 0;n = o << 1;Fj(i, o >>> 0 < g >>> 1 >>> 0 ? n >>> 0 < f >>> 0 ? f : n : g, ((c[j >> 2] | 0) - m | 0) / 12 | 0, a + 8 | 0);j = i + 8 | 0;g = c[j >> 2] | 0;f = c[b + 4 >> 2] | 0;d = c[d >> 2] | 0;c[h >> 2] = c[b >> 2];c[h + 4 >> 2] = f;c[e >> 2] = c[h >> 2];c[e + 4 >> 2] = c[h + 4 >> 2];Cj(g, e, d);c[j >> 2] = (c[j >> 2] | 0) + 12;Gj(a, i);Hj(i);l = k;return; + } + }function Ej(a) { + a = a | 0;return 357913941; + }function Fj(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0;c[a + 12 >> 2] = 0;c[a + 16 >> 2] = e;do if (b) { + if (b >>> 0 > 357913941) Ta();else { + f = qC(b * 12 | 0) | 0;break; + } + } else f = 0; while (0);c[a >> 2] = f;e = f + (d * 12 | 0) | 0;c[a + 8 >> 2] = e;c[a + 4 >> 2] = e;c[a + 12 >> 2] = f + (b * 12 | 0);return; + }function Gj(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0;e = c[a >> 2] | 0;h = a + 4 | 0;g = b + 4 | 0;f = (c[h >> 2] | 0) - e | 0;d = (c[g >> 2] | 0) + (((f | 0) / -12 | 0) * 12 | 0) | 0;c[g >> 2] = d;if ((f | 0) > 0) { + BC(d | 0, e | 0, f | 0) | 0;e = g;d = c[g >> 2] | 0; + } else e = g;g = c[a >> 2] | 0;c[a >> 2] = d;c[e >> 2] = g;g = b + 8 | 0;f = c[h >> 2] | 0;c[h >> 2] = c[g >> 2];c[g >> 2] = f;g = a + 8 | 0;h = b + 12 | 0;a = c[g >> 2] | 0;c[g >> 2] = c[h >> 2];c[h >> 2] = a;c[b >> 2] = c[e >> 2];return; + }function Hj(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;b = c[a + 4 >> 2] | 0;d = a + 8 | 0;e = c[d >> 2] | 0;if ((e | 0) != (b | 0)) c[d >> 2] = e + (~(((e + -12 - b | 0) >>> 0) / 12 | 0) * 12 | 0);a = c[a >> 2] | 0;if (a | 0) sC(a);return; + }function Ij(a) { + a = a | 0;Lj(a);return; + }function Jj(a) { + a = a | 0;Kj(a + 24 | 0);return; + }function Kj(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;d = c[a >> 2] | 0;e = d;if (d | 0) { + a = a + 4 | 0;b = c[a >> 2] | 0;if ((b | 0) != (d | 0)) c[a >> 2] = b + (~(((b + -12 - e | 0) >>> 0) / 12 | 0) * 12 | 0);sC(d); + }return; + }function Lj(a) { + a = a | 0;var b = 0;b = vi() | 0;yi(a, 2, 4, b, Mj() | 0, 0);c[a + 24 >> 2] = 0;c[a + 28 >> 2] = 0;c[a + 32 >> 2] = 0;return; + }function Mj() { + return 1160; + }function Nj(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0;d = l;l = l + 16 | 0;e = d + 8 | 0;f = d;g = Oj(a) | 0;a = c[g + 4 >> 2] | 0;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = a;c[e >> 2] = c[f >> 2];c[e + 4 >> 2] = c[f + 4 >> 2];b = Pj(b, e) | 0;l = d;return b | 0; + }function Oj(a) { + a = a | 0;return (c[(zj() | 0) + 24 >> 2] | 0) + (a * 12 | 0) | 0; + }function Pj(a, b) { + a = a | 0;b = b | 0;var d = 0;d = c[b >> 2] | 0;b = c[b + 4 >> 2] | 0;a = a + (b >> 1) | 0;if (b & 1) d = c[(c[a >> 2] | 0) + d >> 2] | 0;return Qj(pb[d & 31](a) | 0) | 0; + }function Qj(a) { + a = a | 0;return a & 1 | 0; + }function Rj(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;i = c[d >> 2] | 0;h = c[d + 4 >> 2] | 0;d = ai(b) | 0;c[g >> 2] = i;c[g + 4 >> 2] = h;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];Sj(a, d, f, 0);l = e;return; + }function Sj(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0;f = l;l = l + 32 | 0;g = f + 16 | 0;m = f + 8 | 0;i = f;k = c[d >> 2] | 0;j = c[d + 4 >> 2] | 0;h = c[a >> 2] | 0;a = Tj() | 0;c[m >> 2] = k;c[m + 4 >> 2] = j;c[g >> 2] = c[m >> 2];c[g + 4 >> 2] = c[m + 4 >> 2];d = Uj(g) | 0;c[i >> 2] = k;c[i + 4 >> 2] = j;c[g >> 2] = c[i >> 2];c[g + 4 >> 2] = c[i + 4 >> 2];fi(h, b, a, d, Vj(g, e) | 0, e);l = f;return; + }function Tj() { + var b = 0, + d = 0;if (!(a[7656] | 0)) { + ak(9304);Ha(28, 9304, o | 0) | 0;d = 7656;c[d >> 2] = 1;c[d + 4 >> 2] = 0; + }if (!(si(9304) | 0)) { + b = 9304;d = b + 36 | 0;do { + c[b >> 2] = 0;b = b + 4 | 0; + } while ((b | 0) < (d | 0));ak(9304); + }return 9304; + }function Uj(a) { + a = a | 0;return 0; + }function Vj(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0;m = l;l = l + 32 | 0;f = m + 24 | 0;h = m + 16 | 0;i = m;j = m + 8 | 0;g = c[a >> 2] | 0;e = c[a + 4 >> 2] | 0;c[i >> 2] = g;c[i + 4 >> 2] = e;n = Tj() | 0;k = n + 24 | 0;a = ji(b, 4) | 0;c[j >> 2] = a;b = n + 28 | 0;d = c[b >> 2] | 0;if (d >>> 0 < (c[n + 32 >> 2] | 0) >>> 0) { + c[h >> 2] = g;c[h + 4 >> 2] = e;c[f >> 2] = c[h >> 2];c[f + 4 >> 2] = c[h + 4 >> 2];Wj(d, f, a);a = (c[b >> 2] | 0) + 12 | 0;c[b >> 2] = a; + } else { + Xj(k, i, j);a = c[b >> 2] | 0; + }l = m;return ((a - (c[k >> 2] | 0) | 0) / 12 | 0) + -1 | 0; + }function Wj(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0;e = c[b + 4 >> 2] | 0;c[a >> 2] = c[b >> 2];c[a + 4 >> 2] = e;c[a + 8 >> 2] = d;return; + }function Xj(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0, + o = 0;k = l;l = l + 48 | 0;e = k + 32 | 0;h = k + 24 | 0;i = k;j = a + 4 | 0;f = (((c[j >> 2] | 0) - (c[a >> 2] | 0) | 0) / 12 | 0) + 1 | 0;g = Yj(a) | 0;if (g >>> 0 < f >>> 0) jC(a);else { + m = c[a >> 2] | 0;o = ((c[a + 8 >> 2] | 0) - m | 0) / 12 | 0;n = o << 1;Zj(i, o >>> 0 < g >>> 1 >>> 0 ? n >>> 0 < f >>> 0 ? f : n : g, ((c[j >> 2] | 0) - m | 0) / 12 | 0, a + 8 | 0);j = i + 8 | 0;g = c[j >> 2] | 0;f = c[b + 4 >> 2] | 0;d = c[d >> 2] | 0;c[h >> 2] = c[b >> 2];c[h + 4 >> 2] = f;c[e >> 2] = c[h >> 2];c[e + 4 >> 2] = c[h + 4 >> 2];Wj(g, e, d);c[j >> 2] = (c[j >> 2] | 0) + 12;_j(a, i);$j(i);l = k;return; + } + }function Yj(a) { + a = a | 0;return 357913941; + }function Zj(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0;c[a + 12 >> 2] = 0;c[a + 16 >> 2] = e;do if (b) { + if (b >>> 0 > 357913941) Ta();else { + f = qC(b * 12 | 0) | 0;break; + } + } else f = 0; while (0);c[a >> 2] = f;e = f + (d * 12 | 0) | 0;c[a + 8 >> 2] = e;c[a + 4 >> 2] = e;c[a + 12 >> 2] = f + (b * 12 | 0);return; + }function _j(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0;e = c[a >> 2] | 0;h = a + 4 | 0;g = b + 4 | 0;f = (c[h >> 2] | 0) - e | 0;d = (c[g >> 2] | 0) + (((f | 0) / -12 | 0) * 12 | 0) | 0;c[g >> 2] = d;if ((f | 0) > 0) { + BC(d | 0, e | 0, f | 0) | 0;e = g;d = c[g >> 2] | 0; + } else e = g;g = c[a >> 2] | 0;c[a >> 2] = d;c[e >> 2] = g;g = b + 8 | 0;f = c[h >> 2] | 0;c[h >> 2] = c[g >> 2];c[g >> 2] = f;g = a + 8 | 0;h = b + 12 | 0;a = c[g >> 2] | 0;c[g >> 2] = c[h >> 2];c[h >> 2] = a;c[b >> 2] = c[e >> 2];return; + }function $j(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;b = c[a + 4 >> 2] | 0;d = a + 8 | 0;e = c[d >> 2] | 0;if ((e | 0) != (b | 0)) c[d >> 2] = e + (~(((e + -12 - b | 0) >>> 0) / 12 | 0) * 12 | 0);a = c[a >> 2] | 0;if (a | 0) sC(a);return; + }function ak(a) { + a = a | 0;dk(a);return; + }function bk(a) { + a = a | 0;ck(a + 24 | 0);return; + }function ck(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;d = c[a >> 2] | 0;e = d;if (d | 0) { + a = a + 4 | 0;b = c[a >> 2] | 0;if ((b | 0) != (d | 0)) c[a >> 2] = b + (~(((b + -12 - e | 0) >>> 0) / 12 | 0) * 12 | 0);sC(d); + }return; + }function dk(a) { + a = a | 0;var b = 0;b = vi() | 0;yi(a, 2, 5, b, ek() | 0, 1);c[a + 24 >> 2] = 0;c[a + 28 >> 2] = 0;c[a + 32 >> 2] = 0;return; + }function ek() { + return 1164; + }function fk(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;h = gk(a) | 0;a = c[h + 4 >> 2] | 0;c[g >> 2] = c[h >> 2];c[g + 4 >> 2] = a;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];hk(b, f, d);l = e;return; + }function gk(a) { + a = a | 0;return (c[(Tj() | 0) + 24 >> 2] | 0) + (a * 12 | 0) | 0; + }function hk(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0;g = l;l = l + 16 | 0;f = g;e = c[b >> 2] | 0;b = c[b + 4 >> 2] | 0;a = a + (b >> 1) | 0;if (b & 1) e = c[(c[a >> 2] | 0) + e >> 2] | 0;ik(f, d);d = jk(f, d) | 0;ob[e & 31](a, d);kk(f);l = g;return; + }function ik(a, b) { + a = a | 0;b = b | 0;lk(a, b);return; + }function jk(a, b) { + a = a | 0;b = b | 0;return a | 0; + }function kk(a) { + a = a | 0;vf(a);return; + }function lk(a, b) { + a = a | 0;b = b | 0;mk(a, b);return; + }function mk(a, b) { + a = a | 0;b = b | 0;c[a >> 2] = b;return; + }function nk(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;i = c[d >> 2] | 0;h = c[d + 4 >> 2] | 0;d = ai(b) | 0;c[g >> 2] = i;c[g + 4 >> 2] = h;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];ok(a, d, f, 0);l = e;return; + }function ok(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0;f = l;l = l + 32 | 0;g = f + 16 | 0;m = f + 8 | 0;i = f;k = c[d >> 2] | 0;j = c[d + 4 >> 2] | 0;h = c[a >> 2] | 0;a = pk() | 0;c[m >> 2] = k;c[m + 4 >> 2] = j;c[g >> 2] = c[m >> 2];c[g + 4 >> 2] = c[m + 4 >> 2];d = qk(g) | 0;c[i >> 2] = k;c[i + 4 >> 2] = j;c[g >> 2] = c[i >> 2];c[g + 4 >> 2] = c[i + 4 >> 2];fi(h, b, a, d, rk(g, e) | 0, e);l = f;return; + }function pk() { + var b = 0, + d = 0;if (!(a[7664] | 0)) { + yk(9340);Ha(29, 9340, o | 0) | 0;d = 7664;c[d >> 2] = 1;c[d + 4 >> 2] = 0; + }if (!(si(9340) | 0)) { + b = 9340;d = b + 36 | 0;do { + c[b >> 2] = 0;b = b + 4 | 0; + } while ((b | 0) < (d | 0));yk(9340); + }return 9340; + }function qk(a) { + a = a | 0;return 0; + }function rk(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0;m = l;l = l + 32 | 0;f = m + 24 | 0;h = m + 16 | 0;i = m;j = m + 8 | 0;g = c[a >> 2] | 0;e = c[a + 4 >> 2] | 0;c[i >> 2] = g;c[i + 4 >> 2] = e;n = pk() | 0;k = n + 24 | 0;a = ji(b, 4) | 0;c[j >> 2] = a;b = n + 28 | 0;d = c[b >> 2] | 0;if (d >>> 0 < (c[n + 32 >> 2] | 0) >>> 0) { + c[h >> 2] = g;c[h + 4 >> 2] = e;c[f >> 2] = c[h >> 2];c[f + 4 >> 2] = c[h + 4 >> 2];sk(d, f, a);a = (c[b >> 2] | 0) + 12 | 0;c[b >> 2] = a; + } else { + tk(k, i, j);a = c[b >> 2] | 0; + }l = m;return ((a - (c[k >> 2] | 0) | 0) / 12 | 0) + -1 | 0; + }function sk(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0;e = c[b + 4 >> 2] | 0;c[a >> 2] = c[b >> 2];c[a + 4 >> 2] = e;c[a + 8 >> 2] = d;return; + }function tk(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0, + o = 0;k = l;l = l + 48 | 0;e = k + 32 | 0;h = k + 24 | 0;i = k;j = a + 4 | 0;f = (((c[j >> 2] | 0) - (c[a >> 2] | 0) | 0) / 12 | 0) + 1 | 0;g = uk(a) | 0;if (g >>> 0 < f >>> 0) jC(a);else { + m = c[a >> 2] | 0;o = ((c[a + 8 >> 2] | 0) - m | 0) / 12 | 0;n = o << 1;vk(i, o >>> 0 < g >>> 1 >>> 0 ? n >>> 0 < f >>> 0 ? f : n : g, ((c[j >> 2] | 0) - m | 0) / 12 | 0, a + 8 | 0);j = i + 8 | 0;g = c[j >> 2] | 0;f = c[b + 4 >> 2] | 0;d = c[d >> 2] | 0;c[h >> 2] = c[b >> 2];c[h + 4 >> 2] = f;c[e >> 2] = c[h >> 2];c[e + 4 >> 2] = c[h + 4 >> 2];sk(g, e, d);c[j >> 2] = (c[j >> 2] | 0) + 12;wk(a, i);xk(i);l = k;return; + } + }function uk(a) { + a = a | 0;return 357913941; + }function vk(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0;c[a + 12 >> 2] = 0;c[a + 16 >> 2] = e;do if (b) { + if (b >>> 0 > 357913941) Ta();else { + f = qC(b * 12 | 0) | 0;break; + } + } else f = 0; while (0);c[a >> 2] = f;e = f + (d * 12 | 0) | 0;c[a + 8 >> 2] = e;c[a + 4 >> 2] = e;c[a + 12 >> 2] = f + (b * 12 | 0);return; + }function wk(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0;e = c[a >> 2] | 0;h = a + 4 | 0;g = b + 4 | 0;f = (c[h >> 2] | 0) - e | 0;d = (c[g >> 2] | 0) + (((f | 0) / -12 | 0) * 12 | 0) | 0;c[g >> 2] = d;if ((f | 0) > 0) { + BC(d | 0, e | 0, f | 0) | 0;e = g;d = c[g >> 2] | 0; + } else e = g;g = c[a >> 2] | 0;c[a >> 2] = d;c[e >> 2] = g;g = b + 8 | 0;f = c[h >> 2] | 0;c[h >> 2] = c[g >> 2];c[g >> 2] = f;g = a + 8 | 0;h = b + 12 | 0;a = c[g >> 2] | 0;c[g >> 2] = c[h >> 2];c[h >> 2] = a;c[b >> 2] = c[e >> 2];return; + }function xk(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;b = c[a + 4 >> 2] | 0;d = a + 8 | 0;e = c[d >> 2] | 0;if ((e | 0) != (b | 0)) c[d >> 2] = e + (~(((e + -12 - b | 0) >>> 0) / 12 | 0) * 12 | 0);a = c[a >> 2] | 0;if (a | 0) sC(a);return; + }function yk(a) { + a = a | 0;Bk(a);return; + }function zk(a) { + a = a | 0;Ak(a + 24 | 0);return; + }function Ak(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;d = c[a >> 2] | 0;e = d;if (d | 0) { + a = a + 4 | 0;b = c[a >> 2] | 0;if ((b | 0) != (d | 0)) c[a >> 2] = b + (~(((b + -12 - e | 0) >>> 0) / 12 | 0) * 12 | 0);sC(d); + }return; + }function Bk(a) { + a = a | 0;var b = 0;b = vi() | 0;yi(a, 2, 4, b, Ck() | 0, 1);c[a + 24 >> 2] = 0;c[a + 28 >> 2] = 0;c[a + 32 >> 2] = 0;return; + }function Ck() { + return 1180; + }function Dk(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;h = Ek(a) | 0;a = c[h + 4 >> 2] | 0;c[g >> 2] = c[h >> 2];c[g + 4 >> 2] = a;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];d = Fk(b, f, d) | 0;l = e;return d | 0; + }function Ek(a) { + a = a | 0;return (c[(pk() | 0) + 24 >> 2] | 0) + (a * 12 | 0) | 0; + }function Fk(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0;g = l;l = l + 16 | 0;f = g;e = c[b >> 2] | 0;b = c[b + 4 >> 2] | 0;a = a + (b >> 1) | 0;if (b & 1) e = c[(c[a >> 2] | 0) + e >> 2] | 0;f = Hk(f, d) | 0;f = Ik(wb[e & 15](a, f) | 0) | 0;l = g;return f | 0; + }function Hk(a, b) { + a = a | 0;b = b | 0;return Jk(b) | 0; + }function Ik(a) { + a = a | 0;return a | 0; + }function Jk(a) { + a = a | 0;return a | 0; + }function Kk(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;i = c[d >> 2] | 0;h = c[d + 4 >> 2] | 0;d = ai(b) | 0;c[g >> 2] = i;c[g + 4 >> 2] = h;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];Lk(a, d, f, 0);l = e;return; + }function Lk(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0;f = l;l = l + 32 | 0;g = f + 16 | 0;m = f + 8 | 0;i = f;k = c[d >> 2] | 0;j = c[d + 4 >> 2] | 0;h = c[a >> 2] | 0;a = Mk() | 0;c[m >> 2] = k;c[m + 4 >> 2] = j;c[g >> 2] = c[m >> 2];c[g + 4 >> 2] = c[m + 4 >> 2];d = Nk(g) | 0;c[i >> 2] = k;c[i + 4 >> 2] = j;c[g >> 2] = c[i >> 2];c[g + 4 >> 2] = c[i + 4 >> 2];fi(h, b, a, d, Ok(g, e) | 0, e);l = f;return; + }function Mk() { + var b = 0, + d = 0;if (!(a[7672] | 0)) { + Vk(9376);Ha(30, 9376, o | 0) | 0;d = 7672;c[d >> 2] = 1;c[d + 4 >> 2] = 0; + }if (!(si(9376) | 0)) { + b = 9376;d = b + 36 | 0;do { + c[b >> 2] = 0;b = b + 4 | 0; + } while ((b | 0) < (d | 0));Vk(9376); + }return 9376; + }function Nk(a) { + a = a | 0;return 0; + }function Ok(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0;m = l;l = l + 32 | 0;f = m + 24 | 0;h = m + 16 | 0;i = m;j = m + 8 | 0;g = c[a >> 2] | 0;e = c[a + 4 >> 2] | 0;c[i >> 2] = g;c[i + 4 >> 2] = e;n = Mk() | 0;k = n + 24 | 0;a = ji(b, 4) | 0;c[j >> 2] = a;b = n + 28 | 0;d = c[b >> 2] | 0;if (d >>> 0 < (c[n + 32 >> 2] | 0) >>> 0) { + c[h >> 2] = g;c[h + 4 >> 2] = e;c[f >> 2] = c[h >> 2];c[f + 4 >> 2] = c[h + 4 >> 2];Pk(d, f, a);a = (c[b >> 2] | 0) + 12 | 0;c[b >> 2] = a; + } else { + Qk(k, i, j);a = c[b >> 2] | 0; + }l = m;return ((a - (c[k >> 2] | 0) | 0) / 12 | 0) + -1 | 0; + }function Pk(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0;e = c[b + 4 >> 2] | 0;c[a >> 2] = c[b >> 2];c[a + 4 >> 2] = e;c[a + 8 >> 2] = d;return; + }function Qk(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0, + o = 0;k = l;l = l + 48 | 0;e = k + 32 | 0;h = k + 24 | 0;i = k;j = a + 4 | 0;f = (((c[j >> 2] | 0) - (c[a >> 2] | 0) | 0) / 12 | 0) + 1 | 0;g = Rk(a) | 0;if (g >>> 0 < f >>> 0) jC(a);else { + m = c[a >> 2] | 0;o = ((c[a + 8 >> 2] | 0) - m | 0) / 12 | 0;n = o << 1;Sk(i, o >>> 0 < g >>> 1 >>> 0 ? n >>> 0 < f >>> 0 ? f : n : g, ((c[j >> 2] | 0) - m | 0) / 12 | 0, a + 8 | 0);j = i + 8 | 0;g = c[j >> 2] | 0;f = c[b + 4 >> 2] | 0;d = c[d >> 2] | 0;c[h >> 2] = c[b >> 2];c[h + 4 >> 2] = f;c[e >> 2] = c[h >> 2];c[e + 4 >> 2] = c[h + 4 >> 2];Pk(g, e, d);c[j >> 2] = (c[j >> 2] | 0) + 12;Tk(a, i);Uk(i);l = k;return; + } + }function Rk(a) { + a = a | 0;return 357913941; + }function Sk(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0;c[a + 12 >> 2] = 0;c[a + 16 >> 2] = e;do if (b) { + if (b >>> 0 > 357913941) Ta();else { + f = qC(b * 12 | 0) | 0;break; + } + } else f = 0; while (0);c[a >> 2] = f;e = f + (d * 12 | 0) | 0;c[a + 8 >> 2] = e;c[a + 4 >> 2] = e;c[a + 12 >> 2] = f + (b * 12 | 0);return; + }function Tk(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0;e = c[a >> 2] | 0;h = a + 4 | 0;g = b + 4 | 0;f = (c[h >> 2] | 0) - e | 0;d = (c[g >> 2] | 0) + (((f | 0) / -12 | 0) * 12 | 0) | 0;c[g >> 2] = d;if ((f | 0) > 0) { + BC(d | 0, e | 0, f | 0) | 0;e = g;d = c[g >> 2] | 0; + } else e = g;g = c[a >> 2] | 0;c[a >> 2] = d;c[e >> 2] = g;g = b + 8 | 0;f = c[h >> 2] | 0;c[h >> 2] = c[g >> 2];c[g >> 2] = f;g = a + 8 | 0;h = b + 12 | 0;a = c[g >> 2] | 0;c[g >> 2] = c[h >> 2];c[h >> 2] = a;c[b >> 2] = c[e >> 2];return; + }function Uk(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;b = c[a + 4 >> 2] | 0;d = a + 8 | 0;e = c[d >> 2] | 0;if ((e | 0) != (b | 0)) c[d >> 2] = e + (~(((e + -12 - b | 0) >>> 0) / 12 | 0) * 12 | 0);a = c[a >> 2] | 0;if (a | 0) sC(a);return; + }function Vk(a) { + a = a | 0;Yk(a);return; + }function Wk(a) { + a = a | 0;Xk(a + 24 | 0);return; + }function Xk(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;d = c[a >> 2] | 0;e = d;if (d | 0) { + a = a + 4 | 0;b = c[a >> 2] | 0;if ((b | 0) != (d | 0)) c[a >> 2] = b + (~(((b + -12 - e | 0) >>> 0) / 12 | 0) * 12 | 0);sC(d); + }return; + }function Yk(a) { + a = a | 0;var b = 0;b = vi() | 0;yi(a, 2, 5, b, Zk() | 0, 0);c[a + 24 >> 2] = 0;c[a + 28 >> 2] = 0;c[a + 32 >> 2] = 0;return; + }function Zk() { + return 1196; + }function _k(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0;d = l;l = l + 16 | 0;e = d + 8 | 0;f = d;g = $k(a) | 0;a = c[g + 4 >> 2] | 0;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = a;c[e >> 2] = c[f >> 2];c[e + 4 >> 2] = c[f + 4 >> 2];b = al(b, e) | 0;l = d;return b | 0; + }function $k(a) { + a = a | 0;return (c[(Mk() | 0) + 24 >> 2] | 0) + (a * 12 | 0) | 0; + }function al(a, b) { + a = a | 0;b = b | 0;var d = 0;d = c[b >> 2] | 0;b = c[b + 4 >> 2] | 0;a = a + (b >> 1) | 0;if (b & 1) d = c[(c[a >> 2] | 0) + d >> 2] | 0;return Ik(pb[d & 31](a) | 0) | 0; + }function bl(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;i = c[d >> 2] | 0;h = c[d + 4 >> 2] | 0;d = ai(b) | 0;c[g >> 2] = i;c[g + 4 >> 2] = h;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];cl(a, d, f, 1);l = e;return; + }function cl(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0;f = l;l = l + 32 | 0;g = f + 16 | 0;m = f + 8 | 0;i = f;k = c[d >> 2] | 0;j = c[d + 4 >> 2] | 0;h = c[a >> 2] | 0;a = dl() | 0;c[m >> 2] = k;c[m + 4 >> 2] = j;c[g >> 2] = c[m >> 2];c[g + 4 >> 2] = c[m + 4 >> 2];d = el(g) | 0;c[i >> 2] = k;c[i + 4 >> 2] = j;c[g >> 2] = c[i >> 2];c[g + 4 >> 2] = c[i + 4 >> 2];fi(h, b, a, d, fl(g, e) | 0, e);l = f;return; + }function dl() { + var b = 0, + d = 0;if (!(a[7680] | 0)) { + ml(9412);Ha(31, 9412, o | 0) | 0;d = 7680;c[d >> 2] = 1;c[d + 4 >> 2] = 0; + }if (!(si(9412) | 0)) { + b = 9412;d = b + 36 | 0;do { + c[b >> 2] = 0;b = b + 4 | 0; + } while ((b | 0) < (d | 0));ml(9412); + }return 9412; + }function el(a) { + a = a | 0;return 0; + }function fl(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0;m = l;l = l + 32 | 0;f = m + 24 | 0;h = m + 16 | 0;i = m;j = m + 8 | 0;g = c[a >> 2] | 0;e = c[a + 4 >> 2] | 0;c[i >> 2] = g;c[i + 4 >> 2] = e;n = dl() | 0;k = n + 24 | 0;a = ji(b, 4) | 0;c[j >> 2] = a;b = n + 28 | 0;d = c[b >> 2] | 0;if (d >>> 0 < (c[n + 32 >> 2] | 0) >>> 0) { + c[h >> 2] = g;c[h + 4 >> 2] = e;c[f >> 2] = c[h >> 2];c[f + 4 >> 2] = c[h + 4 >> 2];gl(d, f, a);a = (c[b >> 2] | 0) + 12 | 0;c[b >> 2] = a; + } else { + hl(k, i, j);a = c[b >> 2] | 0; + }l = m;return ((a - (c[k >> 2] | 0) | 0) / 12 | 0) + -1 | 0; + }function gl(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0;e = c[b + 4 >> 2] | 0;c[a >> 2] = c[b >> 2];c[a + 4 >> 2] = e;c[a + 8 >> 2] = d;return; + }function hl(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0, + o = 0;k = l;l = l + 48 | 0;e = k + 32 | 0;h = k + 24 | 0;i = k;j = a + 4 | 0;f = (((c[j >> 2] | 0) - (c[a >> 2] | 0) | 0) / 12 | 0) + 1 | 0;g = il(a) | 0;if (g >>> 0 < f >>> 0) jC(a);else { + m = c[a >> 2] | 0;o = ((c[a + 8 >> 2] | 0) - m | 0) / 12 | 0;n = o << 1;jl(i, o >>> 0 < g >>> 1 >>> 0 ? n >>> 0 < f >>> 0 ? f : n : g, ((c[j >> 2] | 0) - m | 0) / 12 | 0, a + 8 | 0);j = i + 8 | 0;g = c[j >> 2] | 0;f = c[b + 4 >> 2] | 0;d = c[d >> 2] | 0;c[h >> 2] = c[b >> 2];c[h + 4 >> 2] = f;c[e >> 2] = c[h >> 2];c[e + 4 >> 2] = c[h + 4 >> 2];gl(g, e, d);c[j >> 2] = (c[j >> 2] | 0) + 12;kl(a, i);ll(i);l = k;return; + } + }function il(a) { + a = a | 0;return 357913941; + }function jl(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0;c[a + 12 >> 2] = 0;c[a + 16 >> 2] = e;do if (b) { + if (b >>> 0 > 357913941) Ta();else { + f = qC(b * 12 | 0) | 0;break; + } + } else f = 0; while (0);c[a >> 2] = f;e = f + (d * 12 | 0) | 0;c[a + 8 >> 2] = e;c[a + 4 >> 2] = e;c[a + 12 >> 2] = f + (b * 12 | 0);return; + }function kl(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0;e = c[a >> 2] | 0;h = a + 4 | 0;g = b + 4 | 0;f = (c[h >> 2] | 0) - e | 0;d = (c[g >> 2] | 0) + (((f | 0) / -12 | 0) * 12 | 0) | 0;c[g >> 2] = d;if ((f | 0) > 0) { + BC(d | 0, e | 0, f | 0) | 0;e = g;d = c[g >> 2] | 0; + } else e = g;g = c[a >> 2] | 0;c[a >> 2] = d;c[e >> 2] = g;g = b + 8 | 0;f = c[h >> 2] | 0;c[h >> 2] = c[g >> 2];c[g >> 2] = f;g = a + 8 | 0;h = b + 12 | 0;a = c[g >> 2] | 0;c[g >> 2] = c[h >> 2];c[h >> 2] = a;c[b >> 2] = c[e >> 2];return; + }function ll(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;b = c[a + 4 >> 2] | 0;d = a + 8 | 0;e = c[d >> 2] | 0;if ((e | 0) != (b | 0)) c[d >> 2] = e + (~(((e + -12 - b | 0) >>> 0) / 12 | 0) * 12 | 0);a = c[a >> 2] | 0;if (a | 0) sC(a);return; + }function ml(a) { + a = a | 0;pl(a);return; + }function nl(a) { + a = a | 0;ol(a + 24 | 0);return; + }function ol(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;d = c[a >> 2] | 0;e = d;if (d | 0) { + a = a + 4 | 0;b = c[a >> 2] | 0;if ((b | 0) != (d | 0)) c[a >> 2] = b + (~(((b + -12 - e | 0) >>> 0) / 12 | 0) * 12 | 0);sC(d); + }return; + }function pl(a) { + a = a | 0;var b = 0;b = vi() | 0;yi(a, 2, 6, b, ql() | 0, 0);c[a + 24 >> 2] = 0;c[a + 28 >> 2] = 0;c[a + 32 >> 2] = 0;return; + }function ql() { + return 1200; + }function rl(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0;d = l;l = l + 16 | 0;e = d + 8 | 0;f = d;g = sl(a) | 0;a = c[g + 4 >> 2] | 0;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = a;c[e >> 2] = c[f >> 2];c[e + 4 >> 2] = c[f + 4 >> 2];b = tl(b, e) | 0;l = d;return b | 0; + }function sl(a) { + a = a | 0;return (c[(dl() | 0) + 24 >> 2] | 0) + (a * 12 | 0) | 0; + }function tl(a, b) { + a = a | 0;b = b | 0;var d = 0;d = c[b >> 2] | 0;b = c[b + 4 >> 2] | 0;a = a + (b >> 1) | 0;if (b & 1) d = c[(c[a >> 2] | 0) + d >> 2] | 0;return ul(pb[d & 31](a) | 0) | 0; + }function ul(a) { + a = a | 0;return a | 0; + }function vl(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;i = c[d >> 2] | 0;h = c[d + 4 >> 2] | 0;d = ai(b) | 0;c[g >> 2] = i;c[g + 4 >> 2] = h;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];wl(a, d, f, 0);l = e;return; + }function wl(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0;f = l;l = l + 32 | 0;g = f + 16 | 0;m = f + 8 | 0;i = f;k = c[d >> 2] | 0;j = c[d + 4 >> 2] | 0;h = c[a >> 2] | 0;a = xl() | 0;c[m >> 2] = k;c[m + 4 >> 2] = j;c[g >> 2] = c[m >> 2];c[g + 4 >> 2] = c[m + 4 >> 2];d = yl(g) | 0;c[i >> 2] = k;c[i + 4 >> 2] = j;c[g >> 2] = c[i >> 2];c[g + 4 >> 2] = c[i + 4 >> 2];fi(h, b, a, d, zl(g, e) | 0, e);l = f;return; + }function xl() { + var b = 0, + d = 0;if (!(a[7688] | 0)) { + Gl(9448);Ha(32, 9448, o | 0) | 0;d = 7688;c[d >> 2] = 1;c[d + 4 >> 2] = 0; + }if (!(si(9448) | 0)) { + b = 9448;d = b + 36 | 0;do { + c[b >> 2] = 0;b = b + 4 | 0; + } while ((b | 0) < (d | 0));Gl(9448); + }return 9448; + }function yl(a) { + a = a | 0;return 0; + }function zl(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0;m = l;l = l + 32 | 0;f = m + 24 | 0;h = m + 16 | 0;i = m;j = m + 8 | 0;g = c[a >> 2] | 0;e = c[a + 4 >> 2] | 0;c[i >> 2] = g;c[i + 4 >> 2] = e;n = xl() | 0;k = n + 24 | 0;a = ji(b, 4) | 0;c[j >> 2] = a;b = n + 28 | 0;d = c[b >> 2] | 0;if (d >>> 0 < (c[n + 32 >> 2] | 0) >>> 0) { + c[h >> 2] = g;c[h + 4 >> 2] = e;c[f >> 2] = c[h >> 2];c[f + 4 >> 2] = c[h + 4 >> 2];Al(d, f, a);a = (c[b >> 2] | 0) + 12 | 0;c[b >> 2] = a; + } else { + Bl(k, i, j);a = c[b >> 2] | 0; + }l = m;return ((a - (c[k >> 2] | 0) | 0) / 12 | 0) + -1 | 0; + }function Al(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0;e = c[b + 4 >> 2] | 0;c[a >> 2] = c[b >> 2];c[a + 4 >> 2] = e;c[a + 8 >> 2] = d;return; + }function Bl(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0, + o = 0;k = l;l = l + 48 | 0;e = k + 32 | 0;h = k + 24 | 0;i = k;j = a + 4 | 0;f = (((c[j >> 2] | 0) - (c[a >> 2] | 0) | 0) / 12 | 0) + 1 | 0;g = Cl(a) | 0;if (g >>> 0 < f >>> 0) jC(a);else { + m = c[a >> 2] | 0;o = ((c[a + 8 >> 2] | 0) - m | 0) / 12 | 0;n = o << 1;Dl(i, o >>> 0 < g >>> 1 >>> 0 ? n >>> 0 < f >>> 0 ? f : n : g, ((c[j >> 2] | 0) - m | 0) / 12 | 0, a + 8 | 0);j = i + 8 | 0;g = c[j >> 2] | 0;f = c[b + 4 >> 2] | 0;d = c[d >> 2] | 0;c[h >> 2] = c[b >> 2];c[h + 4 >> 2] = f;c[e >> 2] = c[h >> 2];c[e + 4 >> 2] = c[h + 4 >> 2];Al(g, e, d);c[j >> 2] = (c[j >> 2] | 0) + 12;El(a, i);Fl(i);l = k;return; + } + }function Cl(a) { + a = a | 0;return 357913941; + }function Dl(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0;c[a + 12 >> 2] = 0;c[a + 16 >> 2] = e;do if (b) { + if (b >>> 0 > 357913941) Ta();else { + f = qC(b * 12 | 0) | 0;break; + } + } else f = 0; while (0);c[a >> 2] = f;e = f + (d * 12 | 0) | 0;c[a + 8 >> 2] = e;c[a + 4 >> 2] = e;c[a + 12 >> 2] = f + (b * 12 | 0);return; + }function El(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0;e = c[a >> 2] | 0;h = a + 4 | 0;g = b + 4 | 0;f = (c[h >> 2] | 0) - e | 0;d = (c[g >> 2] | 0) + (((f | 0) / -12 | 0) * 12 | 0) | 0;c[g >> 2] = d;if ((f | 0) > 0) { + BC(d | 0, e | 0, f | 0) | 0;e = g;d = c[g >> 2] | 0; + } else e = g;g = c[a >> 2] | 0;c[a >> 2] = d;c[e >> 2] = g;g = b + 8 | 0;f = c[h >> 2] | 0;c[h >> 2] = c[g >> 2];c[g >> 2] = f;g = a + 8 | 0;h = b + 12 | 0;a = c[g >> 2] | 0;c[g >> 2] = c[h >> 2];c[h >> 2] = a;c[b >> 2] = c[e >> 2];return; + }function Fl(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;b = c[a + 4 >> 2] | 0;d = a + 8 | 0;e = c[d >> 2] | 0;if ((e | 0) != (b | 0)) c[d >> 2] = e + (~(((e + -12 - b | 0) >>> 0) / 12 | 0) * 12 | 0);a = c[a >> 2] | 0;if (a | 0) sC(a);return; + }function Gl(a) { + a = a | 0;Jl(a);return; + }function Hl(a) { + a = a | 0;Il(a + 24 | 0);return; + }function Il(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;d = c[a >> 2] | 0;e = d;if (d | 0) { + a = a + 4 | 0;b = c[a >> 2] | 0;if ((b | 0) != (d | 0)) c[a >> 2] = b + (~(((b + -12 - e | 0) >>> 0) / 12 | 0) * 12 | 0);sC(d); + }return; + }function Jl(a) { + a = a | 0;var b = 0;b = vi() | 0;yi(a, 2, 6, b, Kl() | 0, 1);c[a + 24 >> 2] = 0;c[a + 28 >> 2] = 0;c[a + 32 >> 2] = 0;return; + }function Kl() { + return 1204; + }function Ll(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;h = Ml(a) | 0;a = c[h + 4 >> 2] | 0;c[g >> 2] = c[h >> 2];c[g + 4 >> 2] = a;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];Nl(b, f, d);l = e;return; + }function Ml(a) { + a = a | 0;return (c[(xl() | 0) + 24 >> 2] | 0) + (a * 12 | 0) | 0; + }function Nl(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0;g = l;l = l + 16 | 0;f = g;e = c[b >> 2] | 0;b = c[b + 4 >> 2] | 0;a = a + (b >> 1) | 0;if (b & 1) e = c[(c[a >> 2] | 0) + e >> 2] | 0;f = Pl(f, d) | 0;ob[e & 31](a, f);l = g;return; + }function Pl(a, b) { + a = a | 0;b = b | 0;return Ql(b) | 0; + }function Ql(a) { + a = a | 0;return a | 0; + }function Rl(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;i = c[d >> 2] | 0;h = c[d + 4 >> 2] | 0;d = ai(b) | 0;c[g >> 2] = i;c[g + 4 >> 2] = h;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];Sl(a, d, f, 0);l = e;return; + }function Sl(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0;f = l;l = l + 32 | 0;g = f + 16 | 0;m = f + 8 | 0;i = f;k = c[d >> 2] | 0;j = c[d + 4 >> 2] | 0;h = c[a >> 2] | 0;a = Tl() | 0;c[m >> 2] = k;c[m + 4 >> 2] = j;c[g >> 2] = c[m >> 2];c[g + 4 >> 2] = c[m + 4 >> 2];d = Ul(g) | 0;c[i >> 2] = k;c[i + 4 >> 2] = j;c[g >> 2] = c[i >> 2];c[g + 4 >> 2] = c[i + 4 >> 2];fi(h, b, a, d, Vl(g, e) | 0, e);l = f;return; + }function Tl() { + var b = 0, + d = 0;if (!(a[7696] | 0)) { + am(9484);Ha(33, 9484, o | 0) | 0;d = 7696;c[d >> 2] = 1;c[d + 4 >> 2] = 0; + }if (!(si(9484) | 0)) { + b = 9484;d = b + 36 | 0;do { + c[b >> 2] = 0;b = b + 4 | 0; + } while ((b | 0) < (d | 0));am(9484); + }return 9484; + }function Ul(a) { + a = a | 0;return 0; + }function Vl(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0;m = l;l = l + 32 | 0;f = m + 24 | 0;h = m + 16 | 0;i = m;j = m + 8 | 0;g = c[a >> 2] | 0;e = c[a + 4 >> 2] | 0;c[i >> 2] = g;c[i + 4 >> 2] = e;n = Tl() | 0;k = n + 24 | 0;a = ji(b, 4) | 0;c[j >> 2] = a;b = n + 28 | 0;d = c[b >> 2] | 0;if (d >>> 0 < (c[n + 32 >> 2] | 0) >>> 0) { + c[h >> 2] = g;c[h + 4 >> 2] = e;c[f >> 2] = c[h >> 2];c[f + 4 >> 2] = c[h + 4 >> 2];Wl(d, f, a);a = (c[b >> 2] | 0) + 12 | 0;c[b >> 2] = a; + } else { + Xl(k, i, j);a = c[b >> 2] | 0; + }l = m;return ((a - (c[k >> 2] | 0) | 0) / 12 | 0) + -1 | 0; + }function Wl(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0;e = c[b + 4 >> 2] | 0;c[a >> 2] = c[b >> 2];c[a + 4 >> 2] = e;c[a + 8 >> 2] = d;return; + }function Xl(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0, + o = 0;k = l;l = l + 48 | 0;e = k + 32 | 0;h = k + 24 | 0;i = k;j = a + 4 | 0;f = (((c[j >> 2] | 0) - (c[a >> 2] | 0) | 0) / 12 | 0) + 1 | 0;g = Yl(a) | 0;if (g >>> 0 < f >>> 0) jC(a);else { + m = c[a >> 2] | 0;o = ((c[a + 8 >> 2] | 0) - m | 0) / 12 | 0;n = o << 1;Zl(i, o >>> 0 < g >>> 1 >>> 0 ? n >>> 0 < f >>> 0 ? f : n : g, ((c[j >> 2] | 0) - m | 0) / 12 | 0, a + 8 | 0);j = i + 8 | 0;g = c[j >> 2] | 0;f = c[b + 4 >> 2] | 0;d = c[d >> 2] | 0;c[h >> 2] = c[b >> 2];c[h + 4 >> 2] = f;c[e >> 2] = c[h >> 2];c[e + 4 >> 2] = c[h + 4 >> 2];Wl(g, e, d);c[j >> 2] = (c[j >> 2] | 0) + 12;_l(a, i);$l(i);l = k;return; + } + }function Yl(a) { + a = a | 0;return 357913941; + }function Zl(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0;c[a + 12 >> 2] = 0;c[a + 16 >> 2] = e;do if (b) { + if (b >>> 0 > 357913941) Ta();else { + f = qC(b * 12 | 0) | 0;break; + } + } else f = 0; while (0);c[a >> 2] = f;e = f + (d * 12 | 0) | 0;c[a + 8 >> 2] = e;c[a + 4 >> 2] = e;c[a + 12 >> 2] = f + (b * 12 | 0);return; + }function _l(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0;e = c[a >> 2] | 0;h = a + 4 | 0;g = b + 4 | 0;f = (c[h >> 2] | 0) - e | 0;d = (c[g >> 2] | 0) + (((f | 0) / -12 | 0) * 12 | 0) | 0;c[g >> 2] = d;if ((f | 0) > 0) { + BC(d | 0, e | 0, f | 0) | 0;e = g;d = c[g >> 2] | 0; + } else e = g;g = c[a >> 2] | 0;c[a >> 2] = d;c[e >> 2] = g;g = b + 8 | 0;f = c[h >> 2] | 0;c[h >> 2] = c[g >> 2];c[g >> 2] = f;g = a + 8 | 0;h = b + 12 | 0;a = c[g >> 2] | 0;c[g >> 2] = c[h >> 2];c[h >> 2] = a;c[b >> 2] = c[e >> 2];return; + }function $l(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;b = c[a + 4 >> 2] | 0;d = a + 8 | 0;e = c[d >> 2] | 0;if ((e | 0) != (b | 0)) c[d >> 2] = e + (~(((e + -12 - b | 0) >>> 0) / 12 | 0) * 12 | 0);a = c[a >> 2] | 0;if (a | 0) sC(a);return; + }function am(a) { + a = a | 0;dm(a);return; + }function bm(a) { + a = a | 0;cm(a + 24 | 0);return; + }function cm(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;d = c[a >> 2] | 0;e = d;if (d | 0) { + a = a + 4 | 0;b = c[a >> 2] | 0;if ((b | 0) != (d | 0)) c[a >> 2] = b + (~(((b + -12 - e | 0) >>> 0) / 12 | 0) * 12 | 0);sC(d); + }return; + }function dm(a) { + a = a | 0;var b = 0;b = vi() | 0;yi(a, 2, 1, b, em() | 0, 2);c[a + 24 >> 2] = 0;c[a + 28 >> 2] = 0;c[a + 32 >> 2] = 0;return; + }function em() { + return 1212; + }function fm(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0, + h = 0, + i = 0;f = l;l = l + 16 | 0;g = f + 8 | 0;h = f;i = gm(a) | 0;a = c[i + 4 >> 2] | 0;c[h >> 2] = c[i >> 2];c[h + 4 >> 2] = a;c[g >> 2] = c[h >> 2];c[g + 4 >> 2] = c[h + 4 >> 2];hm(b, g, d, e);l = f;return; + }function gm(a) { + a = a | 0;return (c[(Tl() | 0) + 24 >> 2] | 0) + (a * 12 | 0) | 0; + }function hm(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0, + h = 0, + i = 0;i = l;l = l + 16 | 0;g = i + 1 | 0;h = i;f = c[b >> 2] | 0;b = c[b + 4 >> 2] | 0;a = a + (b >> 1) | 0;if (b & 1) f = c[(c[a >> 2] | 0) + f >> 2] | 0;g = Pl(g, d) | 0;h = Hk(h, e) | 0;Eb[f & 15](a, g, h);l = i;return; + }function im(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;i = c[d >> 2] | 0;h = c[d + 4 >> 2] | 0;d = ai(b) | 0;c[g >> 2] = i;c[g + 4 >> 2] = h;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];jm(a, d, f, 1);l = e;return; + }function jm(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0;f = l;l = l + 32 | 0;g = f + 16 | 0;m = f + 8 | 0;i = f;k = c[d >> 2] | 0;j = c[d + 4 >> 2] | 0;h = c[a >> 2] | 0;a = km() | 0;c[m >> 2] = k;c[m + 4 >> 2] = j;c[g >> 2] = c[m >> 2];c[g + 4 >> 2] = c[m + 4 >> 2];d = lm(g) | 0;c[i >> 2] = k;c[i + 4 >> 2] = j;c[g >> 2] = c[i >> 2];c[g + 4 >> 2] = c[i + 4 >> 2];fi(h, b, a, d, mm(g, e) | 0, e);l = f;return; + }function km() { + var b = 0, + d = 0;if (!(a[7704] | 0)) { + tm(9520);Ha(34, 9520, o | 0) | 0;d = 7704;c[d >> 2] = 1;c[d + 4 >> 2] = 0; + }if (!(si(9520) | 0)) { + b = 9520;d = b + 36 | 0;do { + c[b >> 2] = 0;b = b + 4 | 0; + } while ((b | 0) < (d | 0));tm(9520); + }return 9520; + }function lm(a) { + a = a | 0;return 0; + }function mm(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0;m = l;l = l + 32 | 0;f = m + 24 | 0;h = m + 16 | 0;i = m;j = m + 8 | 0;g = c[a >> 2] | 0;e = c[a + 4 >> 2] | 0;c[i >> 2] = g;c[i + 4 >> 2] = e;n = km() | 0;k = n + 24 | 0;a = ji(b, 4) | 0;c[j >> 2] = a;b = n + 28 | 0;d = c[b >> 2] | 0;if (d >>> 0 < (c[n + 32 >> 2] | 0) >>> 0) { + c[h >> 2] = g;c[h + 4 >> 2] = e;c[f >> 2] = c[h >> 2];c[f + 4 >> 2] = c[h + 4 >> 2];nm(d, f, a);a = (c[b >> 2] | 0) + 12 | 0;c[b >> 2] = a; + } else { + om(k, i, j);a = c[b >> 2] | 0; + }l = m;return ((a - (c[k >> 2] | 0) | 0) / 12 | 0) + -1 | 0; + }function nm(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0;e = c[b + 4 >> 2] | 0;c[a >> 2] = c[b >> 2];c[a + 4 >> 2] = e;c[a + 8 >> 2] = d;return; + }function om(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0, + o = 0;k = l;l = l + 48 | 0;e = k + 32 | 0;h = k + 24 | 0;i = k;j = a + 4 | 0;f = (((c[j >> 2] | 0) - (c[a >> 2] | 0) | 0) / 12 | 0) + 1 | 0;g = pm(a) | 0;if (g >>> 0 < f >>> 0) jC(a);else { + m = c[a >> 2] | 0;o = ((c[a + 8 >> 2] | 0) - m | 0) / 12 | 0;n = o << 1;qm(i, o >>> 0 < g >>> 1 >>> 0 ? n >>> 0 < f >>> 0 ? f : n : g, ((c[j >> 2] | 0) - m | 0) / 12 | 0, a + 8 | 0);j = i + 8 | 0;g = c[j >> 2] | 0;f = c[b + 4 >> 2] | 0;d = c[d >> 2] | 0;c[h >> 2] = c[b >> 2];c[h + 4 >> 2] = f;c[e >> 2] = c[h >> 2];c[e + 4 >> 2] = c[h + 4 >> 2];nm(g, e, d);c[j >> 2] = (c[j >> 2] | 0) + 12;rm(a, i);sm(i);l = k;return; + } + }function pm(a) { + a = a | 0;return 357913941; + }function qm(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0;c[a + 12 >> 2] = 0;c[a + 16 >> 2] = e;do if (b) { + if (b >>> 0 > 357913941) Ta();else { + f = qC(b * 12 | 0) | 0;break; + } + } else f = 0; while (0);c[a >> 2] = f;e = f + (d * 12 | 0) | 0;c[a + 8 >> 2] = e;c[a + 4 >> 2] = e;c[a + 12 >> 2] = f + (b * 12 | 0);return; + }function rm(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0;e = c[a >> 2] | 0;h = a + 4 | 0;g = b + 4 | 0;f = (c[h >> 2] | 0) - e | 0;d = (c[g >> 2] | 0) + (((f | 0) / -12 | 0) * 12 | 0) | 0;c[g >> 2] = d;if ((f | 0) > 0) { + BC(d | 0, e | 0, f | 0) | 0;e = g;d = c[g >> 2] | 0; + } else e = g;g = c[a >> 2] | 0;c[a >> 2] = d;c[e >> 2] = g;g = b + 8 | 0;f = c[h >> 2] | 0;c[h >> 2] = c[g >> 2];c[g >> 2] = f;g = a + 8 | 0;h = b + 12 | 0;a = c[g >> 2] | 0;c[g >> 2] = c[h >> 2];c[h >> 2] = a;c[b >> 2] = c[e >> 2];return; + }function sm(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;b = c[a + 4 >> 2] | 0;d = a + 8 | 0;e = c[d >> 2] | 0;if ((e | 0) != (b | 0)) c[d >> 2] = e + (~(((e + -12 - b | 0) >>> 0) / 12 | 0) * 12 | 0);a = c[a >> 2] | 0;if (a | 0) sC(a);return; + }function tm(a) { + a = a | 0;wm(a);return; + }function um(a) { + a = a | 0;vm(a + 24 | 0);return; + }function vm(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;d = c[a >> 2] | 0;e = d;if (d | 0) { + a = a + 4 | 0;b = c[a >> 2] | 0;if ((b | 0) != (d | 0)) c[a >> 2] = b + (~(((b + -12 - e | 0) >>> 0) / 12 | 0) * 12 | 0);sC(d); + }return; + }function wm(a) { + a = a | 0;var b = 0;b = vi() | 0;yi(a, 2, 1, b, xm() | 0, 1);c[a + 24 >> 2] = 0;c[a + 28 >> 2] = 0;c[a + 32 >> 2] = 0;return; + }function xm() { + return 1224; + }function ym(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0.0, + f = 0, + g = 0, + h = 0, + i = 0;f = l;l = l + 16 | 0;g = f + 8 | 0;h = f;i = zm(a) | 0;a = c[i + 4 >> 2] | 0;c[h >> 2] = c[i >> 2];c[h + 4 >> 2] = a;c[g >> 2] = c[h >> 2];c[g + 4 >> 2] = c[h + 4 >> 2];e = +Am(b, g, d);l = f;return +e; + }function zm(a) { + a = a | 0;return (c[(km() | 0) + 24 >> 2] | 0) + (a * 12 | 0) | 0; + }function Am(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0.0;g = l;l = l + 16 | 0;f = g;e = c[b >> 2] | 0;b = c[b + 4 >> 2] | 0;a = a + (b >> 1) | 0;if (b & 1) e = c[(c[a >> 2] | 0) + e >> 2] | 0;f = uj(f, d) | 0;h = +ch(+zb[e & 7](a, f));l = g;return +h; + }function Bm(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;i = c[d >> 2] | 0;h = c[d + 4 >> 2] | 0;d = ai(b) | 0;c[g >> 2] = i;c[g + 4 >> 2] = h;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];Cm(a, d, f, 1);l = e;return; + }function Cm(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0;f = l;l = l + 32 | 0;g = f + 16 | 0;m = f + 8 | 0;i = f;k = c[d >> 2] | 0;j = c[d + 4 >> 2] | 0;h = c[a >> 2] | 0;a = Dm() | 0;c[m >> 2] = k;c[m + 4 >> 2] = j;c[g >> 2] = c[m >> 2];c[g + 4 >> 2] = c[m + 4 >> 2];d = Em(g) | 0;c[i >> 2] = k;c[i + 4 >> 2] = j;c[g >> 2] = c[i >> 2];c[g + 4 >> 2] = c[i + 4 >> 2];fi(h, b, a, d, Fm(g, e) | 0, e);l = f;return; + }function Dm() { + var b = 0, + d = 0;if (!(a[7712] | 0)) { + Mm(9556);Ha(35, 9556, o | 0) | 0;d = 7712;c[d >> 2] = 1;c[d + 4 >> 2] = 0; + }if (!(si(9556) | 0)) { + b = 9556;d = b + 36 | 0;do { + c[b >> 2] = 0;b = b + 4 | 0; + } while ((b | 0) < (d | 0));Mm(9556); + }return 9556; + }function Em(a) { + a = a | 0;return 0; + }function Fm(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0;m = l;l = l + 32 | 0;f = m + 24 | 0;h = m + 16 | 0;i = m;j = m + 8 | 0;g = c[a >> 2] | 0;e = c[a + 4 >> 2] | 0;c[i >> 2] = g;c[i + 4 >> 2] = e;n = Dm() | 0;k = n + 24 | 0;a = ji(b, 4) | 0;c[j >> 2] = a;b = n + 28 | 0;d = c[b >> 2] | 0;if (d >>> 0 < (c[n + 32 >> 2] | 0) >>> 0) { + c[h >> 2] = g;c[h + 4 >> 2] = e;c[f >> 2] = c[h >> 2];c[f + 4 >> 2] = c[h + 4 >> 2];Gm(d, f, a);a = (c[b >> 2] | 0) + 12 | 0;c[b >> 2] = a; + } else { + Hm(k, i, j);a = c[b >> 2] | 0; + }l = m;return ((a - (c[k >> 2] | 0) | 0) / 12 | 0) + -1 | 0; + }function Gm(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0;e = c[b + 4 >> 2] | 0;c[a >> 2] = c[b >> 2];c[a + 4 >> 2] = e;c[a + 8 >> 2] = d;return; + }function Hm(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0, + o = 0;k = l;l = l + 48 | 0;e = k + 32 | 0;h = k + 24 | 0;i = k;j = a + 4 | 0;f = (((c[j >> 2] | 0) - (c[a >> 2] | 0) | 0) / 12 | 0) + 1 | 0;g = Im(a) | 0;if (g >>> 0 < f >>> 0) jC(a);else { + m = c[a >> 2] | 0;o = ((c[a + 8 >> 2] | 0) - m | 0) / 12 | 0;n = o << 1;Jm(i, o >>> 0 < g >>> 1 >>> 0 ? n >>> 0 < f >>> 0 ? f : n : g, ((c[j >> 2] | 0) - m | 0) / 12 | 0, a + 8 | 0);j = i + 8 | 0;g = c[j >> 2] | 0;f = c[b + 4 >> 2] | 0;d = c[d >> 2] | 0;c[h >> 2] = c[b >> 2];c[h + 4 >> 2] = f;c[e >> 2] = c[h >> 2];c[e + 4 >> 2] = c[h + 4 >> 2];Gm(g, e, d);c[j >> 2] = (c[j >> 2] | 0) + 12;Km(a, i);Lm(i);l = k;return; + } + }function Im(a) { + a = a | 0;return 357913941; + }function Jm(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0;c[a + 12 >> 2] = 0;c[a + 16 >> 2] = e;do if (b) { + if (b >>> 0 > 357913941) Ta();else { + f = qC(b * 12 | 0) | 0;break; + } + } else f = 0; while (0);c[a >> 2] = f;e = f + (d * 12 | 0) | 0;c[a + 8 >> 2] = e;c[a + 4 >> 2] = e;c[a + 12 >> 2] = f + (b * 12 | 0);return; + }function Km(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0;e = c[a >> 2] | 0;h = a + 4 | 0;g = b + 4 | 0;f = (c[h >> 2] | 0) - e | 0;d = (c[g >> 2] | 0) + (((f | 0) / -12 | 0) * 12 | 0) | 0;c[g >> 2] = d;if ((f | 0) > 0) { + BC(d | 0, e | 0, f | 0) | 0;e = g;d = c[g >> 2] | 0; + } else e = g;g = c[a >> 2] | 0;c[a >> 2] = d;c[e >> 2] = g;g = b + 8 | 0;f = c[h >> 2] | 0;c[h >> 2] = c[g >> 2];c[g >> 2] = f;g = a + 8 | 0;h = b + 12 | 0;a = c[g >> 2] | 0;c[g >> 2] = c[h >> 2];c[h >> 2] = a;c[b >> 2] = c[e >> 2];return; + }function Lm(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;b = c[a + 4 >> 2] | 0;d = a + 8 | 0;e = c[d >> 2] | 0;if ((e | 0) != (b | 0)) c[d >> 2] = e + (~(((e + -12 - b | 0) >>> 0) / 12 | 0) * 12 | 0);a = c[a >> 2] | 0;if (a | 0) sC(a);return; + }function Mm(a) { + a = a | 0;Pm(a);return; + }function Nm(a) { + a = a | 0;Om(a + 24 | 0);return; + }function Om(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;d = c[a >> 2] | 0;e = d;if (d | 0) { + a = a + 4 | 0;b = c[a >> 2] | 0;if ((b | 0) != (d | 0)) c[a >> 2] = b + (~(((b + -12 - e | 0) >>> 0) / 12 | 0) * 12 | 0);sC(d); + }return; + }function Pm(a) { + a = a | 0;var b = 0;b = vi() | 0;yi(a, 2, 5, b, Qm() | 0, 0);c[a + 24 >> 2] = 0;c[a + 28 >> 2] = 0;c[a + 32 >> 2] = 0;return; + }function Qm() { + return 1232; + }function Rm(a, b) { + a = a | 0;b = b | 0;var d = 0.0, + e = 0, + f = 0, + g = 0, + h = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;h = Sm(a) | 0;a = c[h + 4 >> 2] | 0;c[g >> 2] = c[h >> 2];c[g + 4 >> 2] = a;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];d = +Tm(b, f);l = e;return +d; + }function Sm(a) { + a = a | 0;return (c[(Dm() | 0) + 24 >> 2] | 0) + (a * 12 | 0) | 0; + }function Tm(a, b) { + a = a | 0;b = b | 0;var d = 0;d = c[b >> 2] | 0;b = c[b + 4 >> 2] | 0;a = a + (b >> 1) | 0;if (b & 1) d = c[(c[a >> 2] | 0) + d >> 2] | 0;return + +ch(+ub[d & 15](a)); + }function Um(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;i = c[d >> 2] | 0;h = c[d + 4 >> 2] | 0;d = ai(b) | 0;c[g >> 2] = i;c[g + 4 >> 2] = h;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];Vm(a, d, f, 1);l = e;return; + }function Vm(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0;f = l;l = l + 32 | 0;g = f + 16 | 0;m = f + 8 | 0;i = f;k = c[d >> 2] | 0;j = c[d + 4 >> 2] | 0;h = c[a >> 2] | 0;a = Wm() | 0;c[m >> 2] = k;c[m + 4 >> 2] = j;c[g >> 2] = c[m >> 2];c[g + 4 >> 2] = c[m + 4 >> 2];d = Xm(g) | 0;c[i >> 2] = k;c[i + 4 >> 2] = j;c[g >> 2] = c[i >> 2];c[g + 4 >> 2] = c[i + 4 >> 2];fi(h, b, a, d, Ym(g, e) | 0, e);l = f;return; + }function Wm() { + var b = 0, + d = 0;if (!(a[7720] | 0)) { + dn(9592);Ha(36, 9592, o | 0) | 0;d = 7720;c[d >> 2] = 1;c[d + 4 >> 2] = 0; + }if (!(si(9592) | 0)) { + b = 9592;d = b + 36 | 0;do { + c[b >> 2] = 0;b = b + 4 | 0; + } while ((b | 0) < (d | 0));dn(9592); + }return 9592; + }function Xm(a) { + a = a | 0;return 0; + }function Ym(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0;m = l;l = l + 32 | 0;f = m + 24 | 0;h = m + 16 | 0;i = m;j = m + 8 | 0;g = c[a >> 2] | 0;e = c[a + 4 >> 2] | 0;c[i >> 2] = g;c[i + 4 >> 2] = e;n = Wm() | 0;k = n + 24 | 0;a = ji(b, 4) | 0;c[j >> 2] = a;b = n + 28 | 0;d = c[b >> 2] | 0;if (d >>> 0 < (c[n + 32 >> 2] | 0) >>> 0) { + c[h >> 2] = g;c[h + 4 >> 2] = e;c[f >> 2] = c[h >> 2];c[f + 4 >> 2] = c[h + 4 >> 2];Zm(d, f, a);a = (c[b >> 2] | 0) + 12 | 0;c[b >> 2] = a; + } else { + _m(k, i, j);a = c[b >> 2] | 0; + }l = m;return ((a - (c[k >> 2] | 0) | 0) / 12 | 0) + -1 | 0; + }function Zm(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0;e = c[b + 4 >> 2] | 0;c[a >> 2] = c[b >> 2];c[a + 4 >> 2] = e;c[a + 8 >> 2] = d;return; + }function _m(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0, + o = 0;k = l;l = l + 48 | 0;e = k + 32 | 0;h = k + 24 | 0;i = k;j = a + 4 | 0;f = (((c[j >> 2] | 0) - (c[a >> 2] | 0) | 0) / 12 | 0) + 1 | 0;g = $m(a) | 0;if (g >>> 0 < f >>> 0) jC(a);else { + m = c[a >> 2] | 0;o = ((c[a + 8 >> 2] | 0) - m | 0) / 12 | 0;n = o << 1;an(i, o >>> 0 < g >>> 1 >>> 0 ? n >>> 0 < f >>> 0 ? f : n : g, ((c[j >> 2] | 0) - m | 0) / 12 | 0, a + 8 | 0);j = i + 8 | 0;g = c[j >> 2] | 0;f = c[b + 4 >> 2] | 0;d = c[d >> 2] | 0;c[h >> 2] = c[b >> 2];c[h + 4 >> 2] = f;c[e >> 2] = c[h >> 2];c[e + 4 >> 2] = c[h + 4 >> 2];Zm(g, e, d);c[j >> 2] = (c[j >> 2] | 0) + 12;bn(a, i);cn(i);l = k;return; + } + }function $m(a) { + a = a | 0;return 357913941; + }function an(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0;c[a + 12 >> 2] = 0;c[a + 16 >> 2] = e;do if (b) { + if (b >>> 0 > 357913941) Ta();else { + f = qC(b * 12 | 0) | 0;break; + } + } else f = 0; while (0);c[a >> 2] = f;e = f + (d * 12 | 0) | 0;c[a + 8 >> 2] = e;c[a + 4 >> 2] = e;c[a + 12 >> 2] = f + (b * 12 | 0);return; + }function bn(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0;e = c[a >> 2] | 0;h = a + 4 | 0;g = b + 4 | 0;f = (c[h >> 2] | 0) - e | 0;d = (c[g >> 2] | 0) + (((f | 0) / -12 | 0) * 12 | 0) | 0;c[g >> 2] = d;if ((f | 0) > 0) { + BC(d | 0, e | 0, f | 0) | 0;e = g;d = c[g >> 2] | 0; + } else e = g;g = c[a >> 2] | 0;c[a >> 2] = d;c[e >> 2] = g;g = b + 8 | 0;f = c[h >> 2] | 0;c[h >> 2] = c[g >> 2];c[g >> 2] = f;g = a + 8 | 0;h = b + 12 | 0;a = c[g >> 2] | 0;c[g >> 2] = c[h >> 2];c[h >> 2] = a;c[b >> 2] = c[e >> 2];return; + }function cn(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;b = c[a + 4 >> 2] | 0;d = a + 8 | 0;e = c[d >> 2] | 0;if ((e | 0) != (b | 0)) c[d >> 2] = e + (~(((e + -12 - b | 0) >>> 0) / 12 | 0) * 12 | 0);a = c[a >> 2] | 0;if (a | 0) sC(a);return; + }function dn(a) { + a = a | 0;gn(a);return; + }function en(a) { + a = a | 0;fn(a + 24 | 0);return; + }function fn(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;d = c[a >> 2] | 0;e = d;if (d | 0) { + a = a + 4 | 0;b = c[a >> 2] | 0;if ((b | 0) != (d | 0)) c[a >> 2] = b + (~(((b + -12 - e | 0) >>> 0) / 12 | 0) * 12 | 0);sC(d); + }return; + }function gn(a) { + a = a | 0;var b = 0;b = vi() | 0;yi(a, 2, 7, b, hn() | 0, 0);c[a + 24 >> 2] = 0;c[a + 28 >> 2] = 0;c[a + 32 >> 2] = 0;return; + }function hn() { + return 1276; + }function jn(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0;d = l;l = l + 16 | 0;e = d + 8 | 0;f = d;g = kn(a) | 0;a = c[g + 4 >> 2] | 0;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = a;c[e >> 2] = c[f >> 2];c[e + 4 >> 2] = c[f + 4 >> 2];b = ln(b, e) | 0;l = d;return b | 0; + }function kn(a) { + a = a | 0;return (c[(Wm() | 0) + 24 >> 2] | 0) + (a * 12 | 0) | 0; + }function ln(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0;f = l;l = l + 16 | 0;e = f;d = c[b >> 2] | 0;b = c[b + 4 >> 2] | 0;a = a + (b >> 1) | 0;if (b & 1) d = c[(c[a >> 2] | 0) + d >> 2] | 0;ob[d & 31](e, a);e = mn(e) | 0;l = f;return e | 0; + }function mn(a) { + a = a | 0;var b = 0, + c = 0, + d = 0, + e = 0;e = l;l = l + 32 | 0;b = e + 12 | 0;c = e;d = Di(nn() | 0) | 0;if (!d) a = pn(a) | 0;else { + Ei(b, d);Fi(c, b);on(a, c);a = Hi(b) | 0; + }l = e;return a | 0; + }function nn() { + var b = 0;if (!(a[7736] | 0)) { + An(9640);Ha(25, 9640, o | 0) | 0;b = 7736;c[b >> 2] = 1;c[b + 4 >> 2] = 0; + }return 9640; + }function on(a, b) { + a = a | 0;b = b | 0;un(b, a, a + 8 | 0) | 0;return; + } + function pn(a) { + a = a | 0;var b = 0, + d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0;d = l;l = l + 16 | 0;f = d + 4 | 0;h = d;e = jy(8) | 0;b = e;i = qC(16) | 0;c[i >> 2] = c[a >> 2];c[i + 4 >> 2] = c[a + 4 >> 2];c[i + 8 >> 2] = c[a + 8 >> 2];c[i + 12 >> 2] = c[a + 12 >> 2];g = b + 4 | 0;c[g >> 2] = i;a = qC(8) | 0;g = c[g >> 2] | 0;c[h >> 2] = 0;c[f >> 2] = c[h >> 2];qn(a, g, f);c[e >> 2] = a;l = d;return b | 0; + }function qn(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;c[a >> 2] = b;d = qC(16) | 0;c[d + 4 >> 2] = 0;c[d + 8 >> 2] = 0;c[d >> 2] = 1244;c[d + 12 >> 2] = b;c[a + 4 >> 2] = d;return; + }function rn(a) { + a = a | 0;kC(a);sC(a);return; + }function sn(a) { + a = a | 0;a = c[a + 12 >> 2] | 0;if (a | 0) sC(a);return; + }function tn(a) { + a = a | 0;sC(a);return; + }function un(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;b = vn(c[a >> 2] | 0, b, d) | 0;d = a + 4 | 0;c[(c[d >> 2] | 0) + 8 >> 2] = b;return c[(c[d >> 2] | 0) + 8 >> 2] | 0; + }function vn(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0;e = l;l = l + 16 | 0;f = e;UA(f);a = Sg(a) | 0;d = wn(a, c[b >> 2] | 0, +h[d >> 3]) | 0;WA(f);l = e;return d | 0; + }function wn(a, b, c) { + a = a | 0;b = b | 0;c = +c;var d = 0;d = Vg(xn() | 0) | 0;b = Xg(b) | 0;return za(0, d | 0, a | 0, b | 0, + +Wg(c)) | 0; + }function xn() { + var b = 0;if (!(a[7728] | 0)) { + yn(9628);b = 7728;c[b >> 2] = 1;c[b + 4 >> 2] = 0; + }return 9628; + }function yn(a) { + a = a | 0;fh(a, zn() | 0, 2);return; + }function zn() { + return 1264; + }function An(a) { + a = a | 0;Zi(a);return; + }function Bn(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;i = c[d >> 2] | 0;h = c[d + 4 >> 2] | 0;d = ai(b) | 0;c[g >> 2] = i;c[g + 4 >> 2] = h;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];Cn(a, d, f, 1);l = e;return; + }function Cn(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0;f = l;l = l + 32 | 0;g = f + 16 | 0;m = f + 8 | 0;i = f;k = c[d >> 2] | 0;j = c[d + 4 >> 2] | 0;h = c[a >> 2] | 0;a = Dn() | 0;c[m >> 2] = k;c[m + 4 >> 2] = j;c[g >> 2] = c[m >> 2];c[g + 4 >> 2] = c[m + 4 >> 2];d = En(g) | 0;c[i >> 2] = k;c[i + 4 >> 2] = j;c[g >> 2] = c[i >> 2];c[g + 4 >> 2] = c[i + 4 >> 2];fi(h, b, a, d, Fn(g, e) | 0, e);l = f;return; + }function Dn() { + var b = 0, + d = 0;if (!(a[7744] | 0)) { + Mn(9684);Ha(37, 9684, o | 0) | 0;d = 7744;c[d >> 2] = 1;c[d + 4 >> 2] = 0; + }if (!(si(9684) | 0)) { + b = 9684;d = b + 36 | 0;do { + c[b >> 2] = 0;b = b + 4 | 0; + } while ((b | 0) < (d | 0));Mn(9684); + }return 9684; + }function En(a) { + a = a | 0;return 0; + }function Fn(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0;m = l;l = l + 32 | 0;f = m + 24 | 0;h = m + 16 | 0;i = m;j = m + 8 | 0;g = c[a >> 2] | 0;e = c[a + 4 >> 2] | 0;c[i >> 2] = g;c[i + 4 >> 2] = e;n = Dn() | 0;k = n + 24 | 0;a = ji(b, 4) | 0;c[j >> 2] = a;b = n + 28 | 0;d = c[b >> 2] | 0;if (d >>> 0 < (c[n + 32 >> 2] | 0) >>> 0) { + c[h >> 2] = g;c[h + 4 >> 2] = e;c[f >> 2] = c[h >> 2];c[f + 4 >> 2] = c[h + 4 >> 2];Gn(d, f, a);a = (c[b >> 2] | 0) + 12 | 0;c[b >> 2] = a; + } else { + Hn(k, i, j);a = c[b >> 2] | 0; + }l = m;return ((a - (c[k >> 2] | 0) | 0) / 12 | 0) + -1 | 0; + }function Gn(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0;e = c[b + 4 >> 2] | 0;c[a >> 2] = c[b >> 2];c[a + 4 >> 2] = e;c[a + 8 >> 2] = d;return; + }function Hn(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0, + o = 0;k = l;l = l + 48 | 0;e = k + 32 | 0;h = k + 24 | 0;i = k;j = a + 4 | 0;f = (((c[j >> 2] | 0) - (c[a >> 2] | 0) | 0) / 12 | 0) + 1 | 0;g = In(a) | 0;if (g >>> 0 < f >>> 0) jC(a);else { + m = c[a >> 2] | 0;o = ((c[a + 8 >> 2] | 0) - m | 0) / 12 | 0;n = o << 1;Jn(i, o >>> 0 < g >>> 1 >>> 0 ? n >>> 0 < f >>> 0 ? f : n : g, ((c[j >> 2] | 0) - m | 0) / 12 | 0, a + 8 | 0);j = i + 8 | 0;g = c[j >> 2] | 0;f = c[b + 4 >> 2] | 0;d = c[d >> 2] | 0;c[h >> 2] = c[b >> 2];c[h + 4 >> 2] = f;c[e >> 2] = c[h >> 2];c[e + 4 >> 2] = c[h + 4 >> 2];Gn(g, e, d);c[j >> 2] = (c[j >> 2] | 0) + 12;Kn(a, i);Ln(i);l = k;return; + } + }function In(a) { + a = a | 0;return 357913941; + }function Jn(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0;c[a + 12 >> 2] = 0;c[a + 16 >> 2] = e;do if (b) { + if (b >>> 0 > 357913941) Ta();else { + f = qC(b * 12 | 0) | 0;break; + } + } else f = 0; while (0);c[a >> 2] = f;e = f + (d * 12 | 0) | 0;c[a + 8 >> 2] = e;c[a + 4 >> 2] = e;c[a + 12 >> 2] = f + (b * 12 | 0);return; + }function Kn(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0;e = c[a >> 2] | 0;h = a + 4 | 0;g = b + 4 | 0;f = (c[h >> 2] | 0) - e | 0;d = (c[g >> 2] | 0) + (((f | 0) / -12 | 0) * 12 | 0) | 0;c[g >> 2] = d;if ((f | 0) > 0) { + BC(d | 0, e | 0, f | 0) | 0;e = g;d = c[g >> 2] | 0; + } else e = g;g = c[a >> 2] | 0;c[a >> 2] = d;c[e >> 2] = g;g = b + 8 | 0;f = c[h >> 2] | 0;c[h >> 2] = c[g >> 2];c[g >> 2] = f;g = a + 8 | 0;h = b + 12 | 0;a = c[g >> 2] | 0;c[g >> 2] = c[h >> 2];c[h >> 2] = a;c[b >> 2] = c[e >> 2];return; + }function Ln(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;b = c[a + 4 >> 2] | 0;d = a + 8 | 0;e = c[d >> 2] | 0;if ((e | 0) != (b | 0)) c[d >> 2] = e + (~(((e + -12 - b | 0) >>> 0) / 12 | 0) * 12 | 0);a = c[a >> 2] | 0;if (a | 0) sC(a);return; + }function Mn(a) { + a = a | 0;Pn(a);return; + }function Nn(a) { + a = a | 0;On(a + 24 | 0);return; + }function On(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;d = c[a >> 2] | 0;e = d;if (d | 0) { + a = a + 4 | 0;b = c[a >> 2] | 0;if ((b | 0) != (d | 0)) c[a >> 2] = b + (~(((b + -12 - e | 0) >>> 0) / 12 | 0) * 12 | 0);sC(d); + }return; + }function Pn(a) { + a = a | 0;var b = 0;b = vi() | 0;yi(a, 2, 5, b, Qn() | 0, 1);c[a + 24 >> 2] = 0;c[a + 28 >> 2] = 0;c[a + 32 >> 2] = 0;return; + }function Qn() { + return 1280; + }function Rn(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;h = Sn(a) | 0;a = c[h + 4 >> 2] | 0;c[g >> 2] = c[h >> 2];c[g + 4 >> 2] = a;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];d = Tn(b, f, d) | 0;l = e;return d | 0; + }function Sn(a) { + a = a | 0;return (c[(Dn() | 0) + 24 >> 2] | 0) + (a * 12 | 0) | 0; + }function Tn(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0;h = l;l = l + 32 | 0;f = h;g = h + 16 | 0;e = c[b >> 2] | 0;b = c[b + 4 >> 2] | 0;a = a + (b >> 1) | 0;if (b & 1) e = c[(c[a >> 2] | 0) + e >> 2] | 0;g = uj(g, d) | 0;Eb[e & 15](f, a, g);g = mn(f) | 0;l = h;return g | 0; + }function Un(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;i = c[d >> 2] | 0;h = c[d + 4 >> 2] | 0;d = ai(b) | 0;c[g >> 2] = i;c[g + 4 >> 2] = h;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];Vn(a, d, f, 1);l = e;return; + }function Vn(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0;f = l;l = l + 32 | 0;g = f + 16 | 0;m = f + 8 | 0;i = f;k = c[d >> 2] | 0;j = c[d + 4 >> 2] | 0;h = c[a >> 2] | 0;a = Wn() | 0;c[m >> 2] = k;c[m + 4 >> 2] = j;c[g >> 2] = c[m >> 2];c[g + 4 >> 2] = c[m + 4 >> 2];d = Xn(g) | 0;c[i >> 2] = k;c[i + 4 >> 2] = j;c[g >> 2] = c[i >> 2];c[g + 4 >> 2] = c[i + 4 >> 2];fi(h, b, a, d, Yn(g, e) | 0, e);l = f;return; + }function Wn() { + var b = 0, + d = 0;if (!(a[7752] | 0)) { + eo(9720);Ha(38, 9720, o | 0) | 0;d = 7752;c[d >> 2] = 1;c[d + 4 >> 2] = 0; + }if (!(si(9720) | 0)) { + b = 9720;d = b + 36 | 0;do { + c[b >> 2] = 0;b = b + 4 | 0; + } while ((b | 0) < (d | 0));eo(9720); + }return 9720; + }function Xn(a) { + a = a | 0;return 0; + }function Yn(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0;m = l;l = l + 32 | 0;f = m + 24 | 0;h = m + 16 | 0;i = m;j = m + 8 | 0;g = c[a >> 2] | 0;e = c[a + 4 >> 2] | 0;c[i >> 2] = g;c[i + 4 >> 2] = e;n = Wn() | 0;k = n + 24 | 0;a = ji(b, 4) | 0;c[j >> 2] = a;b = n + 28 | 0;d = c[b >> 2] | 0;if (d >>> 0 < (c[n + 32 >> 2] | 0) >>> 0) { + c[h >> 2] = g;c[h + 4 >> 2] = e;c[f >> 2] = c[h >> 2];c[f + 4 >> 2] = c[h + 4 >> 2];Zn(d, f, a);a = (c[b >> 2] | 0) + 12 | 0;c[b >> 2] = a; + } else { + _n(k, i, j);a = c[b >> 2] | 0; + }l = m;return ((a - (c[k >> 2] | 0) | 0) / 12 | 0) + -1 | 0; + }function Zn(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0;e = c[b + 4 >> 2] | 0;c[a >> 2] = c[b >> 2];c[a + 4 >> 2] = e;c[a + 8 >> 2] = d;return; + }function _n(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0, + o = 0;k = l;l = l + 48 | 0;e = k + 32 | 0;h = k + 24 | 0;i = k;j = a + 4 | 0;f = (((c[j >> 2] | 0) - (c[a >> 2] | 0) | 0) / 12 | 0) + 1 | 0;g = $n(a) | 0;if (g >>> 0 < f >>> 0) jC(a);else { + m = c[a >> 2] | 0;o = ((c[a + 8 >> 2] | 0) - m | 0) / 12 | 0;n = o << 1;ao(i, o >>> 0 < g >>> 1 >>> 0 ? n >>> 0 < f >>> 0 ? f : n : g, ((c[j >> 2] | 0) - m | 0) / 12 | 0, a + 8 | 0);j = i + 8 | 0;g = c[j >> 2] | 0;f = c[b + 4 >> 2] | 0;d = c[d >> 2] | 0;c[h >> 2] = c[b >> 2];c[h + 4 >> 2] = f;c[e >> 2] = c[h >> 2];c[e + 4 >> 2] = c[h + 4 >> 2];Zn(g, e, d);c[j >> 2] = (c[j >> 2] | 0) + 12;bo(a, i);co(i);l = k;return; + } + }function $n(a) { + a = a | 0;return 357913941; + }function ao(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0;c[a + 12 >> 2] = 0;c[a + 16 >> 2] = e;do if (b) { + if (b >>> 0 > 357913941) Ta();else { + f = qC(b * 12 | 0) | 0;break; + } + } else f = 0; while (0);c[a >> 2] = f;e = f + (d * 12 | 0) | 0;c[a + 8 >> 2] = e;c[a + 4 >> 2] = e;c[a + 12 >> 2] = f + (b * 12 | 0);return; + }function bo(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0;e = c[a >> 2] | 0;h = a + 4 | 0;g = b + 4 | 0;f = (c[h >> 2] | 0) - e | 0;d = (c[g >> 2] | 0) + (((f | 0) / -12 | 0) * 12 | 0) | 0;c[g >> 2] = d;if ((f | 0) > 0) { + BC(d | 0, e | 0, f | 0) | 0;e = g;d = c[g >> 2] | 0; + } else e = g;g = c[a >> 2] | 0;c[a >> 2] = d;c[e >> 2] = g;g = b + 8 | 0;f = c[h >> 2] | 0;c[h >> 2] = c[g >> 2];c[g >> 2] = f;g = a + 8 | 0;h = b + 12 | 0;a = c[g >> 2] | 0;c[g >> 2] = c[h >> 2];c[h >> 2] = a;c[b >> 2] = c[e >> 2];return; + }function co(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;b = c[a + 4 >> 2] | 0;d = a + 8 | 0;e = c[d >> 2] | 0;if ((e | 0) != (b | 0)) c[d >> 2] = e + (~(((e + -12 - b | 0) >>> 0) / 12 | 0) * 12 | 0);a = c[a >> 2] | 0;if (a | 0) sC(a);return; + }function eo(a) { + a = a | 0;ho(a);return; + }function fo(a) { + a = a | 0;go(a + 24 | 0);return; + }function go(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;d = c[a >> 2] | 0;e = d;if (d | 0) { + a = a + 4 | 0;b = c[a >> 2] | 0;if ((b | 0) != (d | 0)) c[a >> 2] = b + (~(((b + -12 - e | 0) >>> 0) / 12 | 0) * 12 | 0);sC(d); + }return; + }function ho(a) { + a = a | 0;var b = 0;b = vi() | 0;yi(a, 2, 8, b, io() | 0, 0);c[a + 24 >> 2] = 0;c[a + 28 >> 2] = 0;c[a + 32 >> 2] = 0;return; + }function io() { + return 1288; + }function jo(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0;d = l;l = l + 16 | 0;e = d + 8 | 0;f = d;g = ko(a) | 0;a = c[g + 4 >> 2] | 0;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = a;c[e >> 2] = c[f >> 2];c[e + 4 >> 2] = c[f + 4 >> 2];b = lo(b, e) | 0;l = d;return b | 0; + }function ko(a) { + a = a | 0;return (c[(Wn() | 0) + 24 >> 2] | 0) + (a * 12 | 0) | 0; + }function lo(a, b) { + a = a | 0;b = b | 0;var d = 0;d = c[b >> 2] | 0;b = c[b + 4 >> 2] | 0;a = a + (b >> 1) | 0;if (b & 1) d = c[(c[a >> 2] | 0) + d >> 2] | 0;return bh(pb[d & 31](a) | 0) | 0; + }function mo(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;i = c[d >> 2] | 0;h = c[d + 4 >> 2] | 0;d = ai(b) | 0;c[g >> 2] = i;c[g + 4 >> 2] = h;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];no(a, d, f, 0);l = e;return; + }function no(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0;f = l;l = l + 32 | 0;g = f + 16 | 0;m = f + 8 | 0;i = f;k = c[d >> 2] | 0;j = c[d + 4 >> 2] | 0;h = c[a >> 2] | 0;a = oo() | 0;c[m >> 2] = k;c[m + 4 >> 2] = j;c[g >> 2] = c[m >> 2];c[g + 4 >> 2] = c[m + 4 >> 2];d = po(g) | 0;c[i >> 2] = k;c[i + 4 >> 2] = j;c[g >> 2] = c[i >> 2];c[g + 4 >> 2] = c[i + 4 >> 2];fi(h, b, a, d, qo(g, e) | 0, e);l = f;return; + }function oo() { + var b = 0, + d = 0;if (!(a[7760] | 0)) { + xo(9756);Ha(39, 9756, o | 0) | 0;d = 7760;c[d >> 2] = 1;c[d + 4 >> 2] = 0; + }if (!(si(9756) | 0)) { + b = 9756;d = b + 36 | 0;do { + c[b >> 2] = 0;b = b + 4 | 0; + } while ((b | 0) < (d | 0));xo(9756); + }return 9756; + }function po(a) { + a = a | 0;return 0; + }function qo(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0;m = l;l = l + 32 | 0;f = m + 24 | 0;h = m + 16 | 0;i = m;j = m + 8 | 0;g = c[a >> 2] | 0;e = c[a + 4 >> 2] | 0;c[i >> 2] = g;c[i + 4 >> 2] = e;n = oo() | 0;k = n + 24 | 0;a = ji(b, 4) | 0;c[j >> 2] = a;b = n + 28 | 0;d = c[b >> 2] | 0;if (d >>> 0 < (c[n + 32 >> 2] | 0) >>> 0) { + c[h >> 2] = g;c[h + 4 >> 2] = e;c[f >> 2] = c[h >> 2];c[f + 4 >> 2] = c[h + 4 >> 2];ro(d, f, a);a = (c[b >> 2] | 0) + 12 | 0;c[b >> 2] = a; + } else { + so(k, i, j);a = c[b >> 2] | 0; + }l = m;return ((a - (c[k >> 2] | 0) | 0) / 12 | 0) + -1 | 0; + }function ro(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0;e = c[b + 4 >> 2] | 0;c[a >> 2] = c[b >> 2];c[a + 4 >> 2] = e;c[a + 8 >> 2] = d;return; + }function so(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0, + o = 0;k = l;l = l + 48 | 0;e = k + 32 | 0;h = k + 24 | 0;i = k;j = a + 4 | 0;f = (((c[j >> 2] | 0) - (c[a >> 2] | 0) | 0) / 12 | 0) + 1 | 0;g = to(a) | 0;if (g >>> 0 < f >>> 0) jC(a);else { + m = c[a >> 2] | 0;o = ((c[a + 8 >> 2] | 0) - m | 0) / 12 | 0;n = o << 1;uo(i, o >>> 0 < g >>> 1 >>> 0 ? n >>> 0 < f >>> 0 ? f : n : g, ((c[j >> 2] | 0) - m | 0) / 12 | 0, a + 8 | 0);j = i + 8 | 0;g = c[j >> 2] | 0;f = c[b + 4 >> 2] | 0;d = c[d >> 2] | 0;c[h >> 2] = c[b >> 2];c[h + 4 >> 2] = f;c[e >> 2] = c[h >> 2];c[e + 4 >> 2] = c[h + 4 >> 2];ro(g, e, d);c[j >> 2] = (c[j >> 2] | 0) + 12;vo(a, i);wo(i);l = k;return; + } + }function to(a) { + a = a | 0;return 357913941; + }function uo(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0;c[a + 12 >> 2] = 0;c[a + 16 >> 2] = e;do if (b) { + if (b >>> 0 > 357913941) Ta();else { + f = qC(b * 12 | 0) | 0;break; + } + } else f = 0; while (0);c[a >> 2] = f;e = f + (d * 12 | 0) | 0;c[a + 8 >> 2] = e;c[a + 4 >> 2] = e;c[a + 12 >> 2] = f + (b * 12 | 0);return; + }function vo(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0;e = c[a >> 2] | 0;h = a + 4 | 0;g = b + 4 | 0;f = (c[h >> 2] | 0) - e | 0;d = (c[g >> 2] | 0) + (((f | 0) / -12 | 0) * 12 | 0) | 0;c[g >> 2] = d;if ((f | 0) > 0) { + BC(d | 0, e | 0, f | 0) | 0;e = g;d = c[g >> 2] | 0; + } else e = g;g = c[a >> 2] | 0;c[a >> 2] = d;c[e >> 2] = g;g = b + 8 | 0;f = c[h >> 2] | 0;c[h >> 2] = c[g >> 2];c[g >> 2] = f;g = a + 8 | 0;h = b + 12 | 0;a = c[g >> 2] | 0;c[g >> 2] = c[h >> 2];c[h >> 2] = a;c[b >> 2] = c[e >> 2];return; + }function wo(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;b = c[a + 4 >> 2] | 0;d = a + 8 | 0;e = c[d >> 2] | 0;if ((e | 0) != (b | 0)) c[d >> 2] = e + (~(((e + -12 - b | 0) >>> 0) / 12 | 0) * 12 | 0);a = c[a >> 2] | 0;if (a | 0) sC(a);return; + }function xo(a) { + a = a | 0;Ao(a);return; + }function yo(a) { + a = a | 0;zo(a + 24 | 0);return; + }function zo(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;d = c[a >> 2] | 0;e = d;if (d | 0) { + a = a + 4 | 0;b = c[a >> 2] | 0;if ((b | 0) != (d | 0)) c[a >> 2] = b + (~(((b + -12 - e | 0) >>> 0) / 12 | 0) * 12 | 0);sC(d); + }return; + }function Ao(a) { + a = a | 0;var b = 0;b = vi() | 0;yi(a, 2, 8, b, Bo() | 0, 1);c[a + 24 >> 2] = 0;c[a + 28 >> 2] = 0;c[a + 32 >> 2] = 0;return; + }function Bo() { + return 1292; + }function Co(a, b, d) { + a = a | 0;b = b | 0;d = +d;var e = 0, + f = 0, + g = 0, + h = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;h = Do(a) | 0;a = c[h + 4 >> 2] | 0;c[g >> 2] = c[h >> 2];c[g + 4 >> 2] = a;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];Eo(b, f, d);l = e;return; + }function Do(a) { + a = a | 0;return (c[(oo() | 0) + 24 >> 2] | 0) + (a * 12 | 0) | 0; + }function Eo(a, b, d) { + a = a | 0;b = b | 0;d = +d;var e = 0, + f = 0, + g = 0;g = l;l = l + 16 | 0;f = g;e = c[b >> 2] | 0;b = c[b + 4 >> 2] | 0;a = a + (b >> 1) | 0;if (b & 1) e = c[(c[a >> 2] | 0) + e >> 2] | 0;d = +sj(f, d);lb[e & 31](a, d);l = g;return; + }function Fo(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;i = c[d >> 2] | 0;h = c[d + 4 >> 2] | 0;d = ai(b) | 0;c[g >> 2] = i;c[g + 4 >> 2] = h;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];Go(a, d, f, 0);l = e;return; + }function Go(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0;f = l;l = l + 32 | 0;g = f + 16 | 0;m = f + 8 | 0;i = f;k = c[d >> 2] | 0;j = c[d + 4 >> 2] | 0;h = c[a >> 2] | 0;a = Ho() | 0;c[m >> 2] = k;c[m + 4 >> 2] = j;c[g >> 2] = c[m >> 2];c[g + 4 >> 2] = c[m + 4 >> 2];d = Io(g) | 0;c[i >> 2] = k;c[i + 4 >> 2] = j;c[g >> 2] = c[i >> 2];c[g + 4 >> 2] = c[i + 4 >> 2];fi(h, b, a, d, Jo(g, e) | 0, e);l = f;return; + }function Ho() { + var b = 0, + d = 0;if (!(a[7768] | 0)) { + Qo(9792);Ha(40, 9792, o | 0) | 0;d = 7768;c[d >> 2] = 1;c[d + 4 >> 2] = 0; + }if (!(si(9792) | 0)) { + b = 9792;d = b + 36 | 0;do { + c[b >> 2] = 0;b = b + 4 | 0; + } while ((b | 0) < (d | 0));Qo(9792); + }return 9792; + }function Io(a) { + a = a | 0;return 0; + }function Jo(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0;m = l;l = l + 32 | 0;f = m + 24 | 0;h = m + 16 | 0;i = m;j = m + 8 | 0;g = c[a >> 2] | 0;e = c[a + 4 >> 2] | 0;c[i >> 2] = g;c[i + 4 >> 2] = e;n = Ho() | 0;k = n + 24 | 0;a = ji(b, 4) | 0;c[j >> 2] = a;b = n + 28 | 0;d = c[b >> 2] | 0;if (d >>> 0 < (c[n + 32 >> 2] | 0) >>> 0) { + c[h >> 2] = g;c[h + 4 >> 2] = e;c[f >> 2] = c[h >> 2];c[f + 4 >> 2] = c[h + 4 >> 2];Ko(d, f, a);a = (c[b >> 2] | 0) + 12 | 0;c[b >> 2] = a; + } else { + Lo(k, i, j);a = c[b >> 2] | 0; + }l = m;return ((a - (c[k >> 2] | 0) | 0) / 12 | 0) + -1 | 0; + }function Ko(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0;e = c[b + 4 >> 2] | 0;c[a >> 2] = c[b >> 2];c[a + 4 >> 2] = e;c[a + 8 >> 2] = d;return; + }function Lo(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0, + o = 0;k = l;l = l + 48 | 0;e = k + 32 | 0;h = k + 24 | 0;i = k;j = a + 4 | 0;f = (((c[j >> 2] | 0) - (c[a >> 2] | 0) | 0) / 12 | 0) + 1 | 0;g = Mo(a) | 0;if (g >>> 0 < f >>> 0) jC(a);else { + m = c[a >> 2] | 0;o = ((c[a + 8 >> 2] | 0) - m | 0) / 12 | 0;n = o << 1;No(i, o >>> 0 < g >>> 1 >>> 0 ? n >>> 0 < f >>> 0 ? f : n : g, ((c[j >> 2] | 0) - m | 0) / 12 | 0, a + 8 | 0);j = i + 8 | 0;g = c[j >> 2] | 0;f = c[b + 4 >> 2] | 0;d = c[d >> 2] | 0;c[h >> 2] = c[b >> 2];c[h + 4 >> 2] = f;c[e >> 2] = c[h >> 2];c[e + 4 >> 2] = c[h + 4 >> 2];Ko(g, e, d);c[j >> 2] = (c[j >> 2] | 0) + 12;Oo(a, i);Po(i);l = k;return; + } + }function Mo(a) { + a = a | 0;return 357913941; + }function No(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0;c[a + 12 >> 2] = 0;c[a + 16 >> 2] = e;do if (b) { + if (b >>> 0 > 357913941) Ta();else { + f = qC(b * 12 | 0) | 0;break; + } + } else f = 0; while (0);c[a >> 2] = f;e = f + (d * 12 | 0) | 0;c[a + 8 >> 2] = e;c[a + 4 >> 2] = e;c[a + 12 >> 2] = f + (b * 12 | 0);return; + }function Oo(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0;e = c[a >> 2] | 0;h = a + 4 | 0;g = b + 4 | 0;f = (c[h >> 2] | 0) - e | 0;d = (c[g >> 2] | 0) + (((f | 0) / -12 | 0) * 12 | 0) | 0;c[g >> 2] = d;if ((f | 0) > 0) { + BC(d | 0, e | 0, f | 0) | 0;e = g;d = c[g >> 2] | 0; + } else e = g;g = c[a >> 2] | 0;c[a >> 2] = d;c[e >> 2] = g;g = b + 8 | 0;f = c[h >> 2] | 0;c[h >> 2] = c[g >> 2];c[g >> 2] = f;g = a + 8 | 0;h = b + 12 | 0;a = c[g >> 2] | 0;c[g >> 2] = c[h >> 2];c[h >> 2] = a;c[b >> 2] = c[e >> 2];return; + }function Po(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;b = c[a + 4 >> 2] | 0;d = a + 8 | 0;e = c[d >> 2] | 0;if ((e | 0) != (b | 0)) c[d >> 2] = e + (~(((e + -12 - b | 0) >>> 0) / 12 | 0) * 12 | 0);a = c[a >> 2] | 0;if (a | 0) sC(a);return; + }function Qo(a) { + a = a | 0;To(a);return; + }function Ro(a) { + a = a | 0;So(a + 24 | 0);return; + }function So(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;d = c[a >> 2] | 0;e = d;if (d | 0) { + a = a + 4 | 0;b = c[a >> 2] | 0;if ((b | 0) != (d | 0)) c[a >> 2] = b + (~(((b + -12 - e | 0) >>> 0) / 12 | 0) * 12 | 0);sC(d); + }return; + }function To(a) { + a = a | 0;var b = 0;b = vi() | 0;yi(a, 2, 1, b, Uo() | 0, 2);c[a + 24 >> 2] = 0;c[a + 28 >> 2] = 0;c[a + 32 >> 2] = 0;return; + }function Uo() { + return 1300; + }function Vo(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = +e;var f = 0, + g = 0, + h = 0, + i = 0;f = l;l = l + 16 | 0;g = f + 8 | 0;h = f;i = Wo(a) | 0;a = c[i + 4 >> 2] | 0;c[h >> 2] = c[i >> 2];c[h + 4 >> 2] = a;c[g >> 2] = c[h >> 2];c[g + 4 >> 2] = c[h + 4 >> 2];Xo(b, g, d, e);l = f;return; + }function Wo(a) { + a = a | 0;return (c[(Ho() | 0) + 24 >> 2] | 0) + (a * 12 | 0) | 0; + }function Xo(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = +e;var f = 0, + g = 0, + h = 0, + i = 0;i = l;l = l + 16 | 0;g = i + 1 | 0;h = i;f = c[b >> 2] | 0;b = c[b + 4 >> 2] | 0;a = a + (b >> 1) | 0;if (b & 1) f = c[(c[a >> 2] | 0) + f >> 2] | 0;g = uj(g, d) | 0;e = +sj(h, e);Gb[f & 15](a, g, e);l = i;return; + }function Yo(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;i = c[d >> 2] | 0;h = c[d + 4 >> 2] | 0;d = ai(b) | 0;c[g >> 2] = i;c[g + 4 >> 2] = h;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];Zo(a, d, f, 0);l = e;return; + }function Zo(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0;f = l;l = l + 32 | 0;g = f + 16 | 0;m = f + 8 | 0;i = f;k = c[d >> 2] | 0;j = c[d + 4 >> 2] | 0;h = c[a >> 2] | 0;a = _o() | 0;c[m >> 2] = k;c[m + 4 >> 2] = j;c[g >> 2] = c[m >> 2];c[g + 4 >> 2] = c[m + 4 >> 2];d = $o(g) | 0;c[i >> 2] = k;c[i + 4 >> 2] = j;c[g >> 2] = c[i >> 2];c[g + 4 >> 2] = c[i + 4 >> 2];fi(h, b, a, d, ap(g, e) | 0, e);l = f;return; + }function _o() { + var b = 0, + d = 0;if (!(a[7776] | 0)) { + hp(9828);Ha(41, 9828, o | 0) | 0;d = 7776;c[d >> 2] = 1;c[d + 4 >> 2] = 0; + }if (!(si(9828) | 0)) { + b = 9828;d = b + 36 | 0;do { + c[b >> 2] = 0;b = b + 4 | 0; + } while ((b | 0) < (d | 0));hp(9828); + }return 9828; + }function $o(a) { + a = a | 0;return 0; + }function ap(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0;m = l;l = l + 32 | 0;f = m + 24 | 0;h = m + 16 | 0;i = m;j = m + 8 | 0;g = c[a >> 2] | 0;e = c[a + 4 >> 2] | 0;c[i >> 2] = g;c[i + 4 >> 2] = e;n = _o() | 0;k = n + 24 | 0;a = ji(b, 4) | 0;c[j >> 2] = a;b = n + 28 | 0;d = c[b >> 2] | 0;if (d >>> 0 < (c[n + 32 >> 2] | 0) >>> 0) { + c[h >> 2] = g;c[h + 4 >> 2] = e;c[f >> 2] = c[h >> 2];c[f + 4 >> 2] = c[h + 4 >> 2];bp(d, f, a);a = (c[b >> 2] | 0) + 12 | 0;c[b >> 2] = a; + } else { + cp(k, i, j);a = c[b >> 2] | 0; + }l = m;return ((a - (c[k >> 2] | 0) | 0) / 12 | 0) + -1 | 0; + }function bp(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0;e = c[b + 4 >> 2] | 0;c[a >> 2] = c[b >> 2];c[a + 4 >> 2] = e;c[a + 8 >> 2] = d;return; + }function cp(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0, + o = 0;k = l;l = l + 48 | 0;e = k + 32 | 0;h = k + 24 | 0;i = k;j = a + 4 | 0;f = (((c[j >> 2] | 0) - (c[a >> 2] | 0) | 0) / 12 | 0) + 1 | 0;g = dp(a) | 0;if (g >>> 0 < f >>> 0) jC(a);else { + m = c[a >> 2] | 0;o = ((c[a + 8 >> 2] | 0) - m | 0) / 12 | 0;n = o << 1;ep(i, o >>> 0 < g >>> 1 >>> 0 ? n >>> 0 < f >>> 0 ? f : n : g, ((c[j >> 2] | 0) - m | 0) / 12 | 0, a + 8 | 0);j = i + 8 | 0;g = c[j >> 2] | 0;f = c[b + 4 >> 2] | 0;d = c[d >> 2] | 0;c[h >> 2] = c[b >> 2];c[h + 4 >> 2] = f;c[e >> 2] = c[h >> 2];c[e + 4 >> 2] = c[h + 4 >> 2];bp(g, e, d);c[j >> 2] = (c[j >> 2] | 0) + 12;fp(a, i);gp(i);l = k;return; + } + }function dp(a) { + a = a | 0;return 357913941; + }function ep(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0;c[a + 12 >> 2] = 0;c[a + 16 >> 2] = e;do if (b) { + if (b >>> 0 > 357913941) Ta();else { + f = qC(b * 12 | 0) | 0;break; + } + } else f = 0; while (0);c[a >> 2] = f;e = f + (d * 12 | 0) | 0;c[a + 8 >> 2] = e;c[a + 4 >> 2] = e;c[a + 12 >> 2] = f + (b * 12 | 0);return; + }function fp(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0;e = c[a >> 2] | 0;h = a + 4 | 0;g = b + 4 | 0;f = (c[h >> 2] | 0) - e | 0;d = (c[g >> 2] | 0) + (((f | 0) / -12 | 0) * 12 | 0) | 0;c[g >> 2] = d;if ((f | 0) > 0) { + BC(d | 0, e | 0, f | 0) | 0;e = g;d = c[g >> 2] | 0; + } else e = g;g = c[a >> 2] | 0;c[a >> 2] = d;c[e >> 2] = g;g = b + 8 | 0;f = c[h >> 2] | 0;c[h >> 2] = c[g >> 2];c[g >> 2] = f;g = a + 8 | 0;h = b + 12 | 0;a = c[g >> 2] | 0;c[g >> 2] = c[h >> 2];c[h >> 2] = a;c[b >> 2] = c[e >> 2];return; + }function gp(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;b = c[a + 4 >> 2] | 0;d = a + 8 | 0;e = c[d >> 2] | 0;if ((e | 0) != (b | 0)) c[d >> 2] = e + (~(((e + -12 - b | 0) >>> 0) / 12 | 0) * 12 | 0);a = c[a >> 2] | 0;if (a | 0) sC(a);return; + }function hp(a) { + a = a | 0;kp(a);return; + }function ip(a) { + a = a | 0;jp(a + 24 | 0);return; + }function jp(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;d = c[a >> 2] | 0;e = d;if (d | 0) { + a = a + 4 | 0;b = c[a >> 2] | 0;if ((b | 0) != (d | 0)) c[a >> 2] = b + (~(((b + -12 - e | 0) >>> 0) / 12 | 0) * 12 | 0);sC(d); + }return; + }function kp(a) { + a = a | 0;var b = 0;b = vi() | 0;yi(a, 2, 7, b, lp() | 0, 1);c[a + 24 >> 2] = 0;c[a + 28 >> 2] = 0;c[a + 32 >> 2] = 0;return; + }function lp() { + return 1312; + }function mp(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;h = np(a) | 0;a = c[h + 4 >> 2] | 0;c[g >> 2] = c[h >> 2];c[g + 4 >> 2] = a;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];op(b, f, d);l = e;return; + }function np(a) { + a = a | 0;return (c[(_o() | 0) + 24 >> 2] | 0) + (a * 12 | 0) | 0; + }function op(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0;g = l;l = l + 16 | 0;f = g;e = c[b >> 2] | 0;b = c[b + 4 >> 2] | 0;a = a + (b >> 1) | 0;if (b & 1) e = c[(c[a >> 2] | 0) + e >> 2] | 0;f = uj(f, d) | 0;ob[e & 31](a, f);l = g;return; + }function pp(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;i = c[d >> 2] | 0;h = c[d + 4 >> 2] | 0;d = ai(b) | 0;c[g >> 2] = i;c[g + 4 >> 2] = h;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];qp(a, d, f, 0);l = e;return; + }function qp(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0;f = l;l = l + 32 | 0;g = f + 16 | 0;m = f + 8 | 0;i = f;k = c[d >> 2] | 0;j = c[d + 4 >> 2] | 0;h = c[a >> 2] | 0;a = rp() | 0;c[m >> 2] = k;c[m + 4 >> 2] = j;c[g >> 2] = c[m >> 2];c[g + 4 >> 2] = c[m + 4 >> 2];d = sp(g) | 0;c[i >> 2] = k;c[i + 4 >> 2] = j;c[g >> 2] = c[i >> 2];c[g + 4 >> 2] = c[i + 4 >> 2];fi(h, b, a, d, tp(g, e) | 0, e);l = f;return; + }function rp() { + var b = 0, + d = 0;if (!(a[7784] | 0)) { + Ap(9864);Ha(42, 9864, o | 0) | 0;d = 7784;c[d >> 2] = 1;c[d + 4 >> 2] = 0; + }if (!(si(9864) | 0)) { + b = 9864;d = b + 36 | 0;do { + c[b >> 2] = 0;b = b + 4 | 0; + } while ((b | 0) < (d | 0));Ap(9864); + }return 9864; + }function sp(a) { + a = a | 0;return 0; + }function tp(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0;m = l;l = l + 32 | 0;f = m + 24 | 0;h = m + 16 | 0;i = m;j = m + 8 | 0;g = c[a >> 2] | 0;e = c[a + 4 >> 2] | 0;c[i >> 2] = g;c[i + 4 >> 2] = e;n = rp() | 0;k = n + 24 | 0;a = ji(b, 4) | 0;c[j >> 2] = a;b = n + 28 | 0;d = c[b >> 2] | 0;if (d >>> 0 < (c[n + 32 >> 2] | 0) >>> 0) { + c[h >> 2] = g;c[h + 4 >> 2] = e;c[f >> 2] = c[h >> 2];c[f + 4 >> 2] = c[h + 4 >> 2];up(d, f, a);a = (c[b >> 2] | 0) + 12 | 0;c[b >> 2] = a; + } else { + vp(k, i, j);a = c[b >> 2] | 0; + }l = m;return ((a - (c[k >> 2] | 0) | 0) / 12 | 0) + -1 | 0; + }function up(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0;e = c[b + 4 >> 2] | 0;c[a >> 2] = c[b >> 2];c[a + 4 >> 2] = e;c[a + 8 >> 2] = d;return; + }function vp(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0, + o = 0;k = l;l = l + 48 | 0;e = k + 32 | 0;h = k + 24 | 0;i = k;j = a + 4 | 0;f = (((c[j >> 2] | 0) - (c[a >> 2] | 0) | 0) / 12 | 0) + 1 | 0;g = wp(a) | 0;if (g >>> 0 < f >>> 0) jC(a);else { + m = c[a >> 2] | 0;o = ((c[a + 8 >> 2] | 0) - m | 0) / 12 | 0;n = o << 1;xp(i, o >>> 0 < g >>> 1 >>> 0 ? n >>> 0 < f >>> 0 ? f : n : g, ((c[j >> 2] | 0) - m | 0) / 12 | 0, a + 8 | 0);j = i + 8 | 0;g = c[j >> 2] | 0;f = c[b + 4 >> 2] | 0;d = c[d >> 2] | 0;c[h >> 2] = c[b >> 2];c[h + 4 >> 2] = f;c[e >> 2] = c[h >> 2];c[e + 4 >> 2] = c[h + 4 >> 2];up(g, e, d);c[j >> 2] = (c[j >> 2] | 0) + 12;yp(a, i);zp(i);l = k;return; + } + }function wp(a) { + a = a | 0;return 357913941; + }function xp(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0;c[a + 12 >> 2] = 0;c[a + 16 >> 2] = e;do if (b) { + if (b >>> 0 > 357913941) Ta();else { + f = qC(b * 12 | 0) | 0;break; + } + } else f = 0; while (0);c[a >> 2] = f;e = f + (d * 12 | 0) | 0;c[a + 8 >> 2] = e;c[a + 4 >> 2] = e;c[a + 12 >> 2] = f + (b * 12 | 0);return; + }function yp(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0;e = c[a >> 2] | 0;h = a + 4 | 0;g = b + 4 | 0;f = (c[h >> 2] | 0) - e | 0;d = (c[g >> 2] | 0) + (((f | 0) / -12 | 0) * 12 | 0) | 0;c[g >> 2] = d;if ((f | 0) > 0) { + BC(d | 0, e | 0, f | 0) | 0;e = g;d = c[g >> 2] | 0; + } else e = g;g = c[a >> 2] | 0;c[a >> 2] = d;c[e >> 2] = g;g = b + 8 | 0;f = c[h >> 2] | 0;c[h >> 2] = c[g >> 2];c[g >> 2] = f;g = a + 8 | 0;h = b + 12 | 0;a = c[g >> 2] | 0;c[g >> 2] = c[h >> 2];c[h >> 2] = a;c[b >> 2] = c[e >> 2];return; + }function zp(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;b = c[a + 4 >> 2] | 0;d = a + 8 | 0;e = c[d >> 2] | 0;if ((e | 0) != (b | 0)) c[d >> 2] = e + (~(((e + -12 - b | 0) >>> 0) / 12 | 0) * 12 | 0);a = c[a >> 2] | 0;if (a | 0) sC(a);return; + }function Ap(a) { + a = a | 0;Dp(a);return; + }function Bp(a) { + a = a | 0;Cp(a + 24 | 0);return; + }function Cp(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;d = c[a >> 2] | 0;e = d;if (d | 0) { + a = a + 4 | 0;b = c[a >> 2] | 0;if ((b | 0) != (d | 0)) c[a >> 2] = b + (~(((b + -12 - e | 0) >>> 0) / 12 | 0) * 12 | 0);sC(d); + }return; + }function Dp(a) { + a = a | 0;var b = 0;b = vi() | 0;yi(a, 2, 8, b, Ep() | 0, 1);c[a + 24 >> 2] = 0;c[a + 28 >> 2] = 0;c[a + 32 >> 2] = 0;return; + }function Ep() { + return 1320; + }function Fp(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;h = Gp(a) | 0;a = c[h + 4 >> 2] | 0;c[g >> 2] = c[h >> 2];c[g + 4 >> 2] = a;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];Hp(b, f, d);l = e;return; + }function Gp(a) { + a = a | 0;return (c[(rp() | 0) + 24 >> 2] | 0) + (a * 12 | 0) | 0; + }function Hp(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0;g = l;l = l + 16 | 0;f = g;e = c[b >> 2] | 0;b = c[b + 4 >> 2] | 0;a = a + (b >> 1) | 0;if (b & 1) e = c[(c[a >> 2] | 0) + e >> 2] | 0;f = Jp(f, d) | 0;ob[e & 31](a, f);l = g;return; + }function Jp(a, b) { + a = a | 0;b = b | 0;return Kp(b) | 0; + }function Kp(a) { + a = a | 0;return a | 0; + }function Lp(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;i = c[d >> 2] | 0;h = c[d + 4 >> 2] | 0;d = ai(b) | 0;c[g >> 2] = i;c[g + 4 >> 2] = h;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];Mp(a, d, f, 0);l = e;return; + }function Mp(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0;f = l;l = l + 32 | 0;g = f + 16 | 0;m = f + 8 | 0;i = f;k = c[d >> 2] | 0;j = c[d + 4 >> 2] | 0;h = c[a >> 2] | 0;a = Np() | 0;c[m >> 2] = k;c[m + 4 >> 2] = j;c[g >> 2] = c[m >> 2];c[g + 4 >> 2] = c[m + 4 >> 2];d = Op(g) | 0;c[i >> 2] = k;c[i + 4 >> 2] = j;c[g >> 2] = c[i >> 2];c[g + 4 >> 2] = c[i + 4 >> 2];fi(h, b, a, d, Pp(g, e) | 0, e);l = f;return; + }function Np() { + var b = 0, + d = 0;if (!(a[7792] | 0)) { + Wp(9900);Ha(43, 9900, o | 0) | 0;d = 7792;c[d >> 2] = 1;c[d + 4 >> 2] = 0; + }if (!(si(9900) | 0)) { + b = 9900;d = b + 36 | 0;do { + c[b >> 2] = 0;b = b + 4 | 0; + } while ((b | 0) < (d | 0));Wp(9900); + }return 9900; + }function Op(a) { + a = a | 0;return 0; + }function Pp(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0;m = l;l = l + 32 | 0;f = m + 24 | 0;h = m + 16 | 0;i = m;j = m + 8 | 0;g = c[a >> 2] | 0;e = c[a + 4 >> 2] | 0;c[i >> 2] = g;c[i + 4 >> 2] = e;n = Np() | 0;k = n + 24 | 0;a = ji(b, 4) | 0;c[j >> 2] = a;b = n + 28 | 0;d = c[b >> 2] | 0;if (d >>> 0 < (c[n + 32 >> 2] | 0) >>> 0) { + c[h >> 2] = g;c[h + 4 >> 2] = e;c[f >> 2] = c[h >> 2];c[f + 4 >> 2] = c[h + 4 >> 2];Qp(d, f, a);a = (c[b >> 2] | 0) + 12 | 0;c[b >> 2] = a; + } else { + Rp(k, i, j);a = c[b >> 2] | 0; + }l = m;return ((a - (c[k >> 2] | 0) | 0) / 12 | 0) + -1 | 0; + }function Qp(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0;e = c[b + 4 >> 2] | 0;c[a >> 2] = c[b >> 2];c[a + 4 >> 2] = e;c[a + 8 >> 2] = d;return; + }function Rp(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0, + o = 0;k = l;l = l + 48 | 0;e = k + 32 | 0;h = k + 24 | 0;i = k;j = a + 4 | 0;f = (((c[j >> 2] | 0) - (c[a >> 2] | 0) | 0) / 12 | 0) + 1 | 0;g = Sp(a) | 0;if (g >>> 0 < f >>> 0) jC(a);else { + m = c[a >> 2] | 0;o = ((c[a + 8 >> 2] | 0) - m | 0) / 12 | 0;n = o << 1;Tp(i, o >>> 0 < g >>> 1 >>> 0 ? n >>> 0 < f >>> 0 ? f : n : g, ((c[j >> 2] | 0) - m | 0) / 12 | 0, a + 8 | 0);j = i + 8 | 0;g = c[j >> 2] | 0;f = c[b + 4 >> 2] | 0;d = c[d >> 2] | 0;c[h >> 2] = c[b >> 2];c[h + 4 >> 2] = f;c[e >> 2] = c[h >> 2];c[e + 4 >> 2] = c[h + 4 >> 2];Qp(g, e, d);c[j >> 2] = (c[j >> 2] | 0) + 12;Up(a, i);Vp(i);l = k;return; + } + }function Sp(a) { + a = a | 0;return 357913941; + }function Tp(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0;c[a + 12 >> 2] = 0;c[a + 16 >> 2] = e;do if (b) { + if (b >>> 0 > 357913941) Ta();else { + f = qC(b * 12 | 0) | 0;break; + } + } else f = 0; while (0);c[a >> 2] = f;e = f + (d * 12 | 0) | 0;c[a + 8 >> 2] = e;c[a + 4 >> 2] = e;c[a + 12 >> 2] = f + (b * 12 | 0);return; + }function Up(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0;e = c[a >> 2] | 0;h = a + 4 | 0;g = b + 4 | 0;f = (c[h >> 2] | 0) - e | 0;d = (c[g >> 2] | 0) + (((f | 0) / -12 | 0) * 12 | 0) | 0;c[g >> 2] = d;if ((f | 0) > 0) { + BC(d | 0, e | 0, f | 0) | 0;e = g;d = c[g >> 2] | 0; + } else e = g;g = c[a >> 2] | 0;c[a >> 2] = d;c[e >> 2] = g;g = b + 8 | 0;f = c[h >> 2] | 0;c[h >> 2] = c[g >> 2];c[g >> 2] = f;g = a + 8 | 0;h = b + 12 | 0;a = c[g >> 2] | 0;c[g >> 2] = c[h >> 2];c[h >> 2] = a;c[b >> 2] = c[e >> 2];return; + }function Vp(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;b = c[a + 4 >> 2] | 0;d = a + 8 | 0;e = c[d >> 2] | 0;if ((e | 0) != (b | 0)) c[d >> 2] = e + (~(((e + -12 - b | 0) >>> 0) / 12 | 0) * 12 | 0);a = c[a >> 2] | 0;if (a | 0) sC(a);return; + }function Wp(a) { + a = a | 0;Zp(a);return; + }function Xp(a) { + a = a | 0;Yp(a + 24 | 0);return; + }function Yp(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;d = c[a >> 2] | 0;e = d;if (d | 0) { + a = a + 4 | 0;b = c[a >> 2] | 0;if ((b | 0) != (d | 0)) c[a >> 2] = b + (~(((b + -12 - e | 0) >>> 0) / 12 | 0) * 12 | 0);sC(d); + }return; + }function Zp(a) { + a = a | 0;var b = 0;b = vi() | 0;yi(a, 2, 22, b, _p() | 0, 0);c[a + 24 >> 2] = 0;c[a + 28 >> 2] = 0;c[a + 32 >> 2] = 0;return; + }function _p() { + return 1344; + }function $p(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0;d = l;l = l + 16 | 0;e = d + 8 | 0;f = d;g = aq(a) | 0;a = c[g + 4 >> 2] | 0;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = a;c[e >> 2] = c[f >> 2];c[e + 4 >> 2] = c[f + 4 >> 2];bq(b, e);l = d;return; + }function aq(a) { + a = a | 0;return (c[(Np() | 0) + 24 >> 2] | 0) + (a * 12 | 0) | 0; + }function bq(a, b) { + a = a | 0;b = b | 0;var d = 0;d = c[b >> 2] | 0;b = c[b + 4 >> 2] | 0;a = a + (b >> 1) | 0;if (b & 1) d = c[(c[a >> 2] | 0) + d >> 2] | 0;nb[d & 127](a);return; + }function cq(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0;g = c[a >> 2] | 0;f = dq() | 0;a = eq(d) | 0;fi(g, b, f, a, fq(d, e) | 0, e);return; + }function dq() { + var b = 0, + d = 0;if (!(a[7800] | 0)) { + mq(9936);Ha(44, 9936, o | 0) | 0;d = 7800;c[d >> 2] = 1;c[d + 4 >> 2] = 0; + }if (!(si(9936) | 0)) { + b = 9936;d = b + 36 | 0;do { + c[b >> 2] = 0;b = b + 4 | 0; + } while ((b | 0) < (d | 0));mq(9936); + }return 9936; + }function eq(a) { + a = a | 0;return a | 0; + }function fq(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0;i = l;l = l + 16 | 0;f = i;g = i + 4 | 0;c[f >> 2] = a;j = dq() | 0;h = j + 24 | 0;b = ji(b, 4) | 0;c[g >> 2] = b;d = j + 28 | 0;e = c[d >> 2] | 0;if (e >>> 0 < (c[j + 32 >> 2] | 0) >>> 0) { + gq(e, a, b);b = (c[d >> 2] | 0) + 8 | 0;c[d >> 2] = b; + } else { + hq(h, f, g);b = c[d >> 2] | 0; + }l = i;return (b - (c[h >> 2] | 0) >> 3) + -1 | 0; + }function gq(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;c[a >> 2] = b;c[a + 4 >> 2] = d;return; + }function hq(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0;i = l;l = l + 32 | 0;f = i;g = a + 4 | 0;h = ((c[g >> 2] | 0) - (c[a >> 2] | 0) >> 3) + 1 | 0;e = iq(a) | 0;if (e >>> 0 < h >>> 0) jC(a);else { + j = c[a >> 2] | 0;m = (c[a + 8 >> 2] | 0) - j | 0;k = m >> 2;jq(f, m >> 3 >>> 0 < e >>> 1 >>> 0 ? k >>> 0 < h >>> 0 ? h : k : e, (c[g >> 2] | 0) - j >> 3, a + 8 | 0);h = f + 8 | 0;gq(c[h >> 2] | 0, c[b >> 2] | 0, c[d >> 2] | 0);c[h >> 2] = (c[h >> 2] | 0) + 8;kq(a, f);lq(f);l = i;return; + } + }function iq(a) { + a = a | 0;return 536870911; + }function jq(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0;c[a + 12 >> 2] = 0;c[a + 16 >> 2] = e;do if (b) { + if (b >>> 0 > 536870911) Ta();else { + f = qC(b << 3) | 0;break; + } + } else f = 0; while (0);c[a >> 2] = f;e = f + (d << 3) | 0;c[a + 8 >> 2] = e;c[a + 4 >> 2] = e;c[a + 12 >> 2] = f + (b << 3);return; + }function kq(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0;e = c[a >> 2] | 0;h = a + 4 | 0;g = b + 4 | 0;f = (c[h >> 2] | 0) - e | 0;d = (c[g >> 2] | 0) + (0 - (f >> 3) << 3) | 0;c[g >> 2] = d;if ((f | 0) > 0) { + BC(d | 0, e | 0, f | 0) | 0;e = g;d = c[g >> 2] | 0; + } else e = g;g = c[a >> 2] | 0;c[a >> 2] = d;c[e >> 2] = g;g = b + 8 | 0;f = c[h >> 2] | 0;c[h >> 2] = c[g >> 2];c[g >> 2] = f;g = a + 8 | 0;h = b + 12 | 0;a = c[g >> 2] | 0;c[g >> 2] = c[h >> 2];c[h >> 2] = a;c[b >> 2] = c[e >> 2];return; + }function lq(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;b = c[a + 4 >> 2] | 0;d = a + 8 | 0;e = c[d >> 2] | 0;if ((e | 0) != (b | 0)) c[d >> 2] = e + (~((e + -8 - b | 0) >>> 3) << 3);a = c[a >> 2] | 0;if (a | 0) sC(a);return; + }function mq(a) { + a = a | 0;pq(a);return; + }function nq(a) { + a = a | 0;oq(a + 24 | 0);return; + }function oq(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;d = c[a >> 2] | 0;e = d;if (d | 0) { + a = a + 4 | 0;b = c[a >> 2] | 0;if ((b | 0) != (d | 0)) c[a >> 2] = b + (~((b + -8 - e | 0) >>> 3) << 3);sC(d); + }return; + }function pq(a) { + a = a | 0;var b = 0;b = vi() | 0;yi(a, 1, 23, b, Kl() | 0, 1);c[a + 24 >> 2] = 0;c[a + 28 >> 2] = 0;c[a + 32 >> 2] = 0;return; + }function qq(a, b) { + a = a | 0;b = b | 0;sq(c[(rq(a) | 0) >> 2] | 0, b);return; + }function rq(a) { + a = a | 0;return (c[(dq() | 0) + 24 >> 2] | 0) + (a << 3) | 0; + }function sq(a, b) { + a = a | 0;b = b | 0;var c = 0, + d = 0;c = l;l = l + 16 | 0;d = c;b = Pl(d, b) | 0;nb[a & 127](b);l = c;return; + }function tq(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0;g = c[a >> 2] | 0;f = uq() | 0;a = vq(d) | 0;fi(g, b, f, a, wq(d, e) | 0, e);return; + }function uq() { + var b = 0, + d = 0;if (!(a[7808] | 0)) { + Dq(9972);Ha(45, 9972, o | 0) | 0;d = 7808;c[d >> 2] = 1;c[d + 4 >> 2] = 0; + }if (!(si(9972) | 0)) { + b = 9972;d = b + 36 | 0;do { + c[b >> 2] = 0;b = b + 4 | 0; + } while ((b | 0) < (d | 0));Dq(9972); + }return 9972; + }function vq(a) { + a = a | 0;return a | 0; + }function wq(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0;i = l;l = l + 16 | 0;f = i;g = i + 4 | 0;c[f >> 2] = a;j = uq() | 0;h = j + 24 | 0;b = ji(b, 4) | 0;c[g >> 2] = b;d = j + 28 | 0;e = c[d >> 2] | 0;if (e >>> 0 < (c[j + 32 >> 2] | 0) >>> 0) { + xq(e, a, b);b = (c[d >> 2] | 0) + 8 | 0;c[d >> 2] = b; + } else { + yq(h, f, g);b = c[d >> 2] | 0; + }l = i;return (b - (c[h >> 2] | 0) >> 3) + -1 | 0; + }function xq(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;c[a >> 2] = b;c[a + 4 >> 2] = d;return; + }function yq(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0;i = l;l = l + 32 | 0;f = i;g = a + 4 | 0;h = ((c[g >> 2] | 0) - (c[a >> 2] | 0) >> 3) + 1 | 0;e = zq(a) | 0;if (e >>> 0 < h >>> 0) jC(a);else { + j = c[a >> 2] | 0;m = (c[a + 8 >> 2] | 0) - j | 0;k = m >> 2;Aq(f, m >> 3 >>> 0 < e >>> 1 >>> 0 ? k >>> 0 < h >>> 0 ? h : k : e, (c[g >> 2] | 0) - j >> 3, a + 8 | 0);h = f + 8 | 0;xq(c[h >> 2] | 0, c[b >> 2] | 0, c[d >> 2] | 0);c[h >> 2] = (c[h >> 2] | 0) + 8;Bq(a, f);Cq(f);l = i;return; + } + }function zq(a) { + a = a | 0;return 536870911; + }function Aq(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0;c[a + 12 >> 2] = 0;c[a + 16 >> 2] = e;do if (b) { + if (b >>> 0 > 536870911) Ta();else { + f = qC(b << 3) | 0;break; + } + } else f = 0; while (0);c[a >> 2] = f;e = f + (d << 3) | 0;c[a + 8 >> 2] = e;c[a + 4 >> 2] = e;c[a + 12 >> 2] = f + (b << 3);return; + }function Bq(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0;e = c[a >> 2] | 0;h = a + 4 | 0;g = b + 4 | 0;f = (c[h >> 2] | 0) - e | 0;d = (c[g >> 2] | 0) + (0 - (f >> 3) << 3) | 0;c[g >> 2] = d;if ((f | 0) > 0) { + BC(d | 0, e | 0, f | 0) | 0;e = g;d = c[g >> 2] | 0; + } else e = g;g = c[a >> 2] | 0;c[a >> 2] = d;c[e >> 2] = g;g = b + 8 | 0;f = c[h >> 2] | 0;c[h >> 2] = c[g >> 2];c[g >> 2] = f;g = a + 8 | 0;h = b + 12 | 0;a = c[g >> 2] | 0;c[g >> 2] = c[h >> 2];c[h >> 2] = a;c[b >> 2] = c[e >> 2];return; + }function Cq(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;b = c[a + 4 >> 2] | 0;d = a + 8 | 0;e = c[d >> 2] | 0;if ((e | 0) != (b | 0)) c[d >> 2] = e + (~((e + -8 - b | 0) >>> 3) << 3);a = c[a >> 2] | 0;if (a | 0) sC(a);return; + }function Dq(a) { + a = a | 0;Gq(a);return; + }function Eq(a) { + a = a | 0;Fq(a + 24 | 0);return; + }function Fq(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;d = c[a >> 2] | 0;e = d;if (d | 0) { + a = a + 4 | 0;b = c[a >> 2] | 0;if ((b | 0) != (d | 0)) c[a >> 2] = b + (~((b + -8 - e | 0) >>> 3) << 3);sC(d); + }return; + }function Gq(a) { + a = a | 0;var b = 0;b = vi() | 0;yi(a, 1, 9, b, Hq() | 0, 1);c[a + 24 >> 2] = 0;c[a + 28 >> 2] = 0;c[a + 32 >> 2] = 0;return; + }function Hq() { + return 1348; + }function Iq(a, b) { + a = a | 0;b = b | 0;return Kq(c[(Jq(a) | 0) >> 2] | 0, b) | 0; + }function Jq(a) { + a = a | 0;return (c[(uq() | 0) + 24 >> 2] | 0) + (a << 3) | 0; + }function Kq(a, b) { + a = a | 0;b = b | 0;var c = 0, + d = 0;c = l;l = l + 16 | 0;d = c;b = Mq(d, b) | 0;b = Ik(pb[a & 31](b) | 0) | 0;l = c;return b | 0; + }function Mq(a, b) { + a = a | 0;b = b | 0;return Nq(b) | 0; + }function Nq(a) { + a = a | 0;return a | 0; + }function Oq(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0;g = c[a >> 2] | 0;f = Pq() | 0;a = Qq(d) | 0;fi(g, b, f, a, Rq(d, e) | 0, e);return; + }function Pq() { + var b = 0, + d = 0;if (!(a[7816] | 0)) { + Yq(10008);Ha(46, 10008, o | 0) | 0;d = 7816;c[d >> 2] = 1;c[d + 4 >> 2] = 0; + }if (!(si(10008) | 0)) { + b = 10008;d = b + 36 | 0;do { + c[b >> 2] = 0;b = b + 4 | 0; + } while ((b | 0) < (d | 0));Yq(10008); + }return 10008; + }function Qq(a) { + a = a | 0;return a | 0; + }function Rq(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0;i = l;l = l + 16 | 0;f = i;g = i + 4 | 0;c[f >> 2] = a;j = Pq() | 0;h = j + 24 | 0;b = ji(b, 4) | 0;c[g >> 2] = b;d = j + 28 | 0;e = c[d >> 2] | 0;if (e >>> 0 < (c[j + 32 >> 2] | 0) >>> 0) { + Sq(e, a, b);b = (c[d >> 2] | 0) + 8 | 0;c[d >> 2] = b; + } else { + Tq(h, f, g);b = c[d >> 2] | 0; + }l = i;return (b - (c[h >> 2] | 0) >> 3) + -1 | 0; + }function Sq(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;c[a >> 2] = b;c[a + 4 >> 2] = d;return; + }function Tq(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0;i = l;l = l + 32 | 0;f = i;g = a + 4 | 0;h = ((c[g >> 2] | 0) - (c[a >> 2] | 0) >> 3) + 1 | 0;e = Uq(a) | 0;if (e >>> 0 < h >>> 0) jC(a);else { + j = c[a >> 2] | 0;m = (c[a + 8 >> 2] | 0) - j | 0;k = m >> 2;Vq(f, m >> 3 >>> 0 < e >>> 1 >>> 0 ? k >>> 0 < h >>> 0 ? h : k : e, (c[g >> 2] | 0) - j >> 3, a + 8 | 0);h = f + 8 | 0;Sq(c[h >> 2] | 0, c[b >> 2] | 0, c[d >> 2] | 0);c[h >> 2] = (c[h >> 2] | 0) + 8;Wq(a, f);Xq(f);l = i;return; + } + }function Uq(a) { + a = a | 0;return 536870911; + }function Vq(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0;c[a + 12 >> 2] = 0;c[a + 16 >> 2] = e;do if (b) { + if (b >>> 0 > 536870911) Ta();else { + f = qC(b << 3) | 0;break; + } + } else f = 0; while (0);c[a >> 2] = f;e = f + (d << 3) | 0;c[a + 8 >> 2] = e;c[a + 4 >> 2] = e;c[a + 12 >> 2] = f + (b << 3);return; + }function Wq(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0;e = c[a >> 2] | 0;h = a + 4 | 0;g = b + 4 | 0;f = (c[h >> 2] | 0) - e | 0;d = (c[g >> 2] | 0) + (0 - (f >> 3) << 3) | 0;c[g >> 2] = d;if ((f | 0) > 0) { + BC(d | 0, e | 0, f | 0) | 0;e = g;d = c[g >> 2] | 0; + } else e = g;g = c[a >> 2] | 0;c[a >> 2] = d;c[e >> 2] = g;g = b + 8 | 0;f = c[h >> 2] | 0;c[h >> 2] = c[g >> 2];c[g >> 2] = f;g = a + 8 | 0;h = b + 12 | 0;a = c[g >> 2] | 0;c[g >> 2] = c[h >> 2];c[h >> 2] = a;c[b >> 2] = c[e >> 2];return; + }function Xq(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;b = c[a + 4 >> 2] | 0;d = a + 8 | 0;e = c[d >> 2] | 0;if ((e | 0) != (b | 0)) c[d >> 2] = e + (~((e + -8 - b | 0) >>> 3) << 3);a = c[a >> 2] | 0;if (a | 0) sC(a);return; + }function Yq(a) { + a = a | 0;$q(a);return; + }function Zq(a) { + a = a | 0;_q(a + 24 | 0);return; + }function _q(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;d = c[a >> 2] | 0;e = d;if (d | 0) { + a = a + 4 | 0;b = c[a >> 2] | 0;if ((b | 0) != (d | 0)) c[a >> 2] = b + (~((b + -8 - e | 0) >>> 3) << 3);sC(d); + }return; + }function $q(a) { + a = a | 0;var b = 0;b = vi() | 0;yi(a, 1, 15, b, Zk() | 0, 0);c[a + 24 >> 2] = 0;c[a + 28 >> 2] = 0;c[a + 32 >> 2] = 0;return; + }function ar(a) { + a = a | 0;return cr(c[(br(a) | 0) >> 2] | 0) | 0; + }function br(a) { + a = a | 0;return (c[(Pq() | 0) + 24 >> 2] | 0) + (a << 3) | 0; + }function cr(a) { + a = a | 0;return Ik(Ab[a & 7]() | 0) | 0; + }function dr() { + var b = 0;if (!(a[7832] | 0)) { + nr(10052);Ha(25, 10052, o | 0) | 0;b = 7832;c[b >> 2] = 1;c[b + 4 >> 2] = 0; + }return 10052; + }function er(a, b) { + a = a | 0;b = b | 0;c[a >> 2] = fr() | 0;c[a + 4 >> 2] = gr() | 0;c[a + 12 >> 2] = b;c[a + 8 >> 2] = hr() | 0;c[a + 32 >> 2] = 2;return; + }function fr() { + return 11709; + }function gr() { + return 1188; + }function hr() { + return lr() | 0; + }function ir(a, b, c, d) { + a = a | 0;b = b | 0;c = c | 0;d = d | 0;if ((jr(d, 896) | 0) == 512) { + if (c | 0) { + kr(c);sC(c); + } + } else if (b | 0) { + uf(b);sC(b); + }return; + }function jr(a, b) { + a = a | 0;b = b | 0;return b & a | 0; + }function kr(a) { + a = a | 0;a = c[a + 4 >> 2] | 0;if (a | 0) oC(a);return; + }function lr() { + var b = 0;if (!(a[7824] | 0)) { + c[2511] = mr() | 0;c[2512] = 0;b = 7824;c[b >> 2] = 1;c[b + 4 >> 2] = 0; + }return 10044; + }function mr() { + return 0; + }function nr(a) { + a = a | 0;Zi(a);return; + }function or(a) { + a = a | 0;var b = 0, + d = 0, + e = 0, + f = 0, + g = 0;b = l;l = l + 32 | 0;d = b + 24 | 0;g = b + 16 | 0;f = b + 8 | 0;e = b;pr(a, 4827);qr(a, 4834, 3) | 0;rr(a, 3682, 47) | 0;c[g >> 2] = 9;c[g + 4 >> 2] = 0;c[d >> 2] = c[g >> 2];c[d + 4 >> 2] = c[g + 4 >> 2];sr(a, 4841, d) | 0;c[f >> 2] = 1;c[f + 4 >> 2] = 0;c[d >> 2] = c[f >> 2];c[d + 4 >> 2] = c[f + 4 >> 2];tr(a, 4871, d) | 0;c[e >> 2] = 10;c[e + 4 >> 2] = 0;c[d >> 2] = c[e >> 2];c[d + 4 >> 2] = c[e + 4 >> 2];ur(a, 4891, d) | 0;l = b;return; + }function pr(a, b) { + a = a | 0;b = b | 0;var d = 0;d = nt() | 0;c[a >> 2] = d;ot(d, b);Hv(c[a >> 2] | 0);return; + }function qr(a, b, c) { + a = a | 0;b = b | 0;c = c | 0;Ws(a, ai(b) | 0, c, 0);return a | 0; + }function rr(a, b, c) { + a = a | 0;b = b | 0;c = c | 0;Es(a, ai(b) | 0, c, 0);return a | 0; + }function sr(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;h = c[d + 4 >> 2] | 0;c[g >> 2] = c[d >> 2];c[g + 4 >> 2] = h;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];is(a, b, f);l = e;return a | 0; + }function tr(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;h = c[d + 4 >> 2] | 0;c[g >> 2] = c[d >> 2];c[g + 4 >> 2] = h;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];Or(a, b, f);l = e;return a | 0; + }function ur(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;h = c[d + 4 >> 2] | 0;c[g >> 2] = c[d >> 2];c[g + 4 >> 2] = h;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];vr(a, b, f);l = e;return a | 0; + }function vr(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;i = c[d >> 2] | 0;h = c[d + 4 >> 2] | 0;d = ai(b) | 0;c[g >> 2] = i;c[g + 4 >> 2] = h;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];wr(a, d, f, 1);l = e;return; + }function wr(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0;f = l;l = l + 32 | 0;g = f + 16 | 0;m = f + 8 | 0;i = f;k = c[d >> 2] | 0;j = c[d + 4 >> 2] | 0;h = c[a >> 2] | 0;a = xr() | 0;c[m >> 2] = k;c[m + 4 >> 2] = j;c[g >> 2] = c[m >> 2];c[g + 4 >> 2] = c[m + 4 >> 2];d = yr(g) | 0;c[i >> 2] = k;c[i + 4 >> 2] = j;c[g >> 2] = c[i >> 2];c[g + 4 >> 2] = c[i + 4 >> 2];fi(h, b, a, d, zr(g, e) | 0, e);l = f;return; + }function xr() { + var b = 0, + d = 0;if (!(a[7840] | 0)) { + Gr(10100);Ha(48, 10100, o | 0) | 0;d = 7840;c[d >> 2] = 1;c[d + 4 >> 2] = 0; + }if (!(si(10100) | 0)) { + b = 10100;d = b + 36 | 0;do { + c[b >> 2] = 0;b = b + 4 | 0; + } while ((b | 0) < (d | 0));Gr(10100); + }return 10100; + }function yr(a) { + a = a | 0;return 0; + }function zr(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0;m = l;l = l + 32 | 0;f = m + 24 | 0;h = m + 16 | 0;i = m;j = m + 8 | 0;g = c[a >> 2] | 0;e = c[a + 4 >> 2] | 0;c[i >> 2] = g;c[i + 4 >> 2] = e;n = xr() | 0;k = n + 24 | 0;a = ji(b, 4) | 0;c[j >> 2] = a;b = n + 28 | 0;d = c[b >> 2] | 0;if (d >>> 0 < (c[n + 32 >> 2] | 0) >>> 0) { + c[h >> 2] = g;c[h + 4 >> 2] = e;c[f >> 2] = c[h >> 2];c[f + 4 >> 2] = c[h + 4 >> 2];Ar(d, f, a);a = (c[b >> 2] | 0) + 12 | 0;c[b >> 2] = a; + } else { + Br(k, i, j);a = c[b >> 2] | 0; + }l = m;return ((a - (c[k >> 2] | 0) | 0) / 12 | 0) + -1 | 0; + }function Ar(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0;e = c[b + 4 >> 2] | 0;c[a >> 2] = c[b >> 2];c[a + 4 >> 2] = e;c[a + 8 >> 2] = d;return; + }function Br(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0, + o = 0;k = l;l = l + 48 | 0;e = k + 32 | 0;h = k + 24 | 0;i = k;j = a + 4 | 0;f = (((c[j >> 2] | 0) - (c[a >> 2] | 0) | 0) / 12 | 0) + 1 | 0;g = Cr(a) | 0;if (g >>> 0 < f >>> 0) jC(a);else { + m = c[a >> 2] | 0;o = ((c[a + 8 >> 2] | 0) - m | 0) / 12 | 0;n = o << 1;Dr(i, o >>> 0 < g >>> 1 >>> 0 ? n >>> 0 < f >>> 0 ? f : n : g, ((c[j >> 2] | 0) - m | 0) / 12 | 0, a + 8 | 0);j = i + 8 | 0;g = c[j >> 2] | 0;f = c[b + 4 >> 2] | 0;d = c[d >> 2] | 0;c[h >> 2] = c[b >> 2];c[h + 4 >> 2] = f;c[e >> 2] = c[h >> 2];c[e + 4 >> 2] = c[h + 4 >> 2];Ar(g, e, d);c[j >> 2] = (c[j >> 2] | 0) + 12;Er(a, i);Fr(i);l = k;return; + } + }function Cr(a) { + a = a | 0;return 357913941; + }function Dr(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0;c[a + 12 >> 2] = 0;c[a + 16 >> 2] = e;do if (b) { + if (b >>> 0 > 357913941) Ta();else { + f = qC(b * 12 | 0) | 0;break; + } + } else f = 0; while (0);c[a >> 2] = f;e = f + (d * 12 | 0) | 0;c[a + 8 >> 2] = e;c[a + 4 >> 2] = e;c[a + 12 >> 2] = f + (b * 12 | 0);return; + }function Er(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0;e = c[a >> 2] | 0;h = a + 4 | 0;g = b + 4 | 0;f = (c[h >> 2] | 0) - e | 0;d = (c[g >> 2] | 0) + (((f | 0) / -12 | 0) * 12 | 0) | 0;c[g >> 2] = d;if ((f | 0) > 0) { + BC(d | 0, e | 0, f | 0) | 0;e = g;d = c[g >> 2] | 0; + } else e = g;g = c[a >> 2] | 0;c[a >> 2] = d;c[e >> 2] = g;g = b + 8 | 0;f = c[h >> 2] | 0;c[h >> 2] = c[g >> 2];c[g >> 2] = f;g = a + 8 | 0;h = b + 12 | 0;a = c[g >> 2] | 0;c[g >> 2] = c[h >> 2];c[h >> 2] = a;c[b >> 2] = c[e >> 2];return; + }function Fr(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;b = c[a + 4 >> 2] | 0;d = a + 8 | 0;e = c[d >> 2] | 0;if ((e | 0) != (b | 0)) c[d >> 2] = e + (~(((e + -12 - b | 0) >>> 0) / 12 | 0) * 12 | 0);a = c[a >> 2] | 0;if (a | 0) sC(a);return; + }function Gr(a) { + a = a | 0;Jr(a);return; + }function Hr(a) { + a = a | 0;Ir(a + 24 | 0);return; + }function Ir(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;d = c[a >> 2] | 0;e = d;if (d | 0) { + a = a + 4 | 0;b = c[a >> 2] | 0;if ((b | 0) != (d | 0)) c[a >> 2] = b + (~(((b + -12 - e | 0) >>> 0) / 12 | 0) * 12 | 0);sC(d); + }return; + }function Jr(a) { + a = a | 0;var b = 0;b = vi() | 0;yi(a, 2, 6, b, Kr() | 0, 1);c[a + 24 >> 2] = 0;c[a + 28 >> 2] = 0;c[a + 32 >> 2] = 0;return; + }function Kr() { + return 1364; + }function Lr(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;h = Mr(a) | 0;a = c[h + 4 >> 2] | 0;c[g >> 2] = c[h >> 2];c[g + 4 >> 2] = a;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];d = Nr(b, f, d) | 0;l = e;return d | 0; + }function Mr(a) { + a = a | 0;return (c[(xr() | 0) + 24 >> 2] | 0) + (a * 12 | 0) | 0; + }function Nr(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0;g = l;l = l + 16 | 0;f = g;e = c[b >> 2] | 0;b = c[b + 4 >> 2] | 0;a = a + (b >> 1) | 0;if (b & 1) e = c[(c[a >> 2] | 0) + e >> 2] | 0;f = uj(f, d) | 0;f = Qj(wb[e & 15](a, f) | 0) | 0;l = g;return f | 0; + }function Or(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;i = c[d >> 2] | 0;h = c[d + 4 >> 2] | 0;d = ai(b) | 0;c[g >> 2] = i;c[g + 4 >> 2] = h;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];Pr(a, d, f, 0);l = e;return; + }function Pr(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0;f = l;l = l + 32 | 0;g = f + 16 | 0;m = f + 8 | 0;i = f;k = c[d >> 2] | 0;j = c[d + 4 >> 2] | 0;h = c[a >> 2] | 0;a = Qr() | 0;c[m >> 2] = k;c[m + 4 >> 2] = j;c[g >> 2] = c[m >> 2];c[g + 4 >> 2] = c[m + 4 >> 2];d = Rr(g) | 0;c[i >> 2] = k;c[i + 4 >> 2] = j;c[g >> 2] = c[i >> 2];c[g + 4 >> 2] = c[i + 4 >> 2];fi(h, b, a, d, Sr(g, e) | 0, e);l = f;return; + }function Qr() { + var b = 0, + d = 0;if (!(a[7848] | 0)) { + Zr(10136);Ha(49, 10136, o | 0) | 0;d = 7848;c[d >> 2] = 1;c[d + 4 >> 2] = 0; + }if (!(si(10136) | 0)) { + b = 10136;d = b + 36 | 0;do { + c[b >> 2] = 0;b = b + 4 | 0; + } while ((b | 0) < (d | 0));Zr(10136); + }return 10136; + }function Rr(a) { + a = a | 0;return 0; + }function Sr(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0;m = l;l = l + 32 | 0;f = m + 24 | 0;h = m + 16 | 0;i = m;j = m + 8 | 0;g = c[a >> 2] | 0;e = c[a + 4 >> 2] | 0;c[i >> 2] = g;c[i + 4 >> 2] = e;n = Qr() | 0;k = n + 24 | 0;a = ji(b, 4) | 0;c[j >> 2] = a;b = n + 28 | 0;d = c[b >> 2] | 0;if (d >>> 0 < (c[n + 32 >> 2] | 0) >>> 0) { + c[h >> 2] = g;c[h + 4 >> 2] = e;c[f >> 2] = c[h >> 2];c[f + 4 >> 2] = c[h + 4 >> 2];Tr(d, f, a);a = (c[b >> 2] | 0) + 12 | 0;c[b >> 2] = a; + } else { + Ur(k, i, j);a = c[b >> 2] | 0; + }l = m;return ((a - (c[k >> 2] | 0) | 0) / 12 | 0) + -1 | 0; + }function Tr(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0;e = c[b + 4 >> 2] | 0;c[a >> 2] = c[b >> 2];c[a + 4 >> 2] = e;c[a + 8 >> 2] = d;return; + }function Ur(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0, + o = 0;k = l;l = l + 48 | 0;e = k + 32 | 0;h = k + 24 | 0;i = k;j = a + 4 | 0;f = (((c[j >> 2] | 0) - (c[a >> 2] | 0) | 0) / 12 | 0) + 1 | 0;g = Vr(a) | 0;if (g >>> 0 < f >>> 0) jC(a);else { + m = c[a >> 2] | 0;o = ((c[a + 8 >> 2] | 0) - m | 0) / 12 | 0;n = o << 1;Wr(i, o >>> 0 < g >>> 1 >>> 0 ? n >>> 0 < f >>> 0 ? f : n : g, ((c[j >> 2] | 0) - m | 0) / 12 | 0, a + 8 | 0);j = i + 8 | 0;g = c[j >> 2] | 0;f = c[b + 4 >> 2] | 0;d = c[d >> 2] | 0;c[h >> 2] = c[b >> 2];c[h + 4 >> 2] = f;c[e >> 2] = c[h >> 2];c[e + 4 >> 2] = c[h + 4 >> 2];Tr(g, e, d);c[j >> 2] = (c[j >> 2] | 0) + 12;Xr(a, i);Yr(i);l = k;return; + } + }function Vr(a) { + a = a | 0;return 357913941; + }function Wr(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0;c[a + 12 >> 2] = 0;c[a + 16 >> 2] = e;do if (b) { + if (b >>> 0 > 357913941) Ta();else { + f = qC(b * 12 | 0) | 0;break; + } + } else f = 0; while (0);c[a >> 2] = f;e = f + (d * 12 | 0) | 0;c[a + 8 >> 2] = e;c[a + 4 >> 2] = e;c[a + 12 >> 2] = f + (b * 12 | 0);return; + }function Xr(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0;e = c[a >> 2] | 0;h = a + 4 | 0;g = b + 4 | 0;f = (c[h >> 2] | 0) - e | 0;d = (c[g >> 2] | 0) + (((f | 0) / -12 | 0) * 12 | 0) | 0;c[g >> 2] = d;if ((f | 0) > 0) { + BC(d | 0, e | 0, f | 0) | 0;e = g;d = c[g >> 2] | 0; + } else e = g;g = c[a >> 2] | 0;c[a >> 2] = d;c[e >> 2] = g;g = b + 8 | 0;f = c[h >> 2] | 0;c[h >> 2] = c[g >> 2];c[g >> 2] = f;g = a + 8 | 0;h = b + 12 | 0;a = c[g >> 2] | 0;c[g >> 2] = c[h >> 2];c[h >> 2] = a;c[b >> 2] = c[e >> 2];return; + }function Yr(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;b = c[a + 4 >> 2] | 0;d = a + 8 | 0;e = c[d >> 2] | 0;if ((e | 0) != (b | 0)) c[d >> 2] = e + (~(((e + -12 - b | 0) >>> 0) / 12 | 0) * 12 | 0);a = c[a >> 2] | 0;if (a | 0) sC(a);return; + }function Zr(a) { + a = a | 0;as(a);return; + }function _r(a) { + a = a | 0;$r(a + 24 | 0);return; + }function $r(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;d = c[a >> 2] | 0;e = d;if (d | 0) { + a = a + 4 | 0;b = c[a >> 2] | 0;if ((b | 0) != (d | 0)) c[a >> 2] = b + (~(((b + -12 - e | 0) >>> 0) / 12 | 0) * 12 | 0);sC(d); + }return; + }function as(a) { + a = a | 0;var b = 0;b = vi() | 0;yi(a, 2, 9, b, bs() | 0, 1);c[a + 24 >> 2] = 0;c[a + 28 >> 2] = 0;c[a + 32 >> 2] = 0;return; + }function bs() { + return 1372; + }function cs(a, b, d) { + a = a | 0;b = b | 0;d = +d;var e = 0, + f = 0, + g = 0, + h = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;h = ds(a) | 0;a = c[h + 4 >> 2] | 0;c[g >> 2] = c[h >> 2];c[g + 4 >> 2] = a;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];es(b, f, d);l = e;return; + }function ds(a) { + a = a | 0;return (c[(Qr() | 0) + 24 >> 2] | 0) + (a * 12 | 0) | 0; + }function es(a, b, d) { + a = a | 0;b = b | 0;d = +d;var e = 0, + f = 0, + g = 0, + h = ib;g = l;l = l + 16 | 0;f = g;e = c[b >> 2] | 0;b = c[b + 4 >> 2] | 0;a = a + (b >> 1) | 0;if (b & 1) e = c[(c[a >> 2] | 0) + e >> 2] | 0;h = T(gs(f, d));kb[e & 1](a, h);l = g;return; + }function gs(a, b) { + a = a | 0;b = +b;return T(hs(b)); + }function hs(a) { + a = +a;return T(a); + }function is(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0;e = l;l = l + 16 | 0;f = e + 8 | 0;g = e;i = c[d >> 2] | 0;h = c[d + 4 >> 2] | 0;d = ai(b) | 0;c[g >> 2] = i;c[g + 4 >> 2] = h;c[f >> 2] = c[g >> 2];c[f + 4 >> 2] = c[g + 4 >> 2];js(a, d, f, 0);l = e;return; + }function js(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0;f = l;l = l + 32 | 0;g = f + 16 | 0;m = f + 8 | 0;i = f;k = c[d >> 2] | 0;j = c[d + 4 >> 2] | 0;h = c[a >> 2] | 0;a = ks() | 0;c[m >> 2] = k;c[m + 4 >> 2] = j;c[g >> 2] = c[m >> 2];c[g + 4 >> 2] = c[m + 4 >> 2];d = ls(g) | 0;c[i >> 2] = k;c[i + 4 >> 2] = j;c[g >> 2] = c[i >> 2];c[g + 4 >> 2] = c[i + 4 >> 2];fi(h, b, a, d, ms(g, e) | 0, e);l = f;return; + }function ks() { + var b = 0, + d = 0;if (!(a[7856] | 0)) { + ts(10172);Ha(50, 10172, o | 0) | 0;d = 7856;c[d >> 2] = 1;c[d + 4 >> 2] = 0; + }if (!(si(10172) | 0)) { + b = 10172;d = b + 36 | 0;do { + c[b >> 2] = 0;b = b + 4 | 0; + } while ((b | 0) < (d | 0));ts(10172); + }return 10172; + }function ls(a) { + a = a | 0;return 0; + }function ms(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0;m = l;l = l + 32 | 0;f = m + 24 | 0;h = m + 16 | 0;i = m;j = m + 8 | 0;g = c[a >> 2] | 0;e = c[a + 4 >> 2] | 0;c[i >> 2] = g;c[i + 4 >> 2] = e;n = ks() | 0;k = n + 24 | 0;a = ji(b, 4) | 0;c[j >> 2] = a;b = n + 28 | 0;d = c[b >> 2] | 0;if (d >>> 0 < (c[n + 32 >> 2] | 0) >>> 0) { + c[h >> 2] = g;c[h + 4 >> 2] = e;c[f >> 2] = c[h >> 2];c[f + 4 >> 2] = c[h + 4 >> 2];ns(d, f, a);a = (c[b >> 2] | 0) + 12 | 0;c[b >> 2] = a; + } else { + os(k, i, j);a = c[b >> 2] | 0; + }l = m;return ((a - (c[k >> 2] | 0) | 0) / 12 | 0) + -1 | 0; + }function ns(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0;e = c[b + 4 >> 2] | 0;c[a >> 2] = c[b >> 2];c[a + 4 >> 2] = e;c[a + 8 >> 2] = d;return; + }function os(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0, + o = 0;k = l;l = l + 48 | 0;e = k + 32 | 0;h = k + 24 | 0;i = k;j = a + 4 | 0;f = (((c[j >> 2] | 0) - (c[a >> 2] | 0) | 0) / 12 | 0) + 1 | 0;g = ps(a) | 0;if (g >>> 0 < f >>> 0) jC(a);else { + m = c[a >> 2] | 0;o = ((c[a + 8 >> 2] | 0) - m | 0) / 12 | 0;n = o << 1;qs(i, o >>> 0 < g >>> 1 >>> 0 ? n >>> 0 < f >>> 0 ? f : n : g, ((c[j >> 2] | 0) - m | 0) / 12 | 0, a + 8 | 0);j = i + 8 | 0;g = c[j >> 2] | 0;f = c[b + 4 >> 2] | 0;d = c[d >> 2] | 0;c[h >> 2] = c[b >> 2];c[h + 4 >> 2] = f;c[e >> 2] = c[h >> 2];c[e + 4 >> 2] = c[h + 4 >> 2];ns(g, e, d);c[j >> 2] = (c[j >> 2] | 0) + 12;rs(a, i);ss(i);l = k;return; + } + }function ps(a) { + a = a | 0;return 357913941; + }function qs(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0;c[a + 12 >> 2] = 0;c[a + 16 >> 2] = e;do if (b) { + if (b >>> 0 > 357913941) Ta();else { + f = qC(b * 12 | 0) | 0;break; + } + } else f = 0; while (0);c[a >> 2] = f;e = f + (d * 12 | 0) | 0;c[a + 8 >> 2] = e;c[a + 4 >> 2] = e;c[a + 12 >> 2] = f + (b * 12 | 0);return; + }function rs(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0;e = c[a >> 2] | 0;h = a + 4 | 0;g = b + 4 | 0;f = (c[h >> 2] | 0) - e | 0;d = (c[g >> 2] | 0) + (((f | 0) / -12 | 0) * 12 | 0) | 0;c[g >> 2] = d;if ((f | 0) > 0) { + BC(d | 0, e | 0, f | 0) | 0;e = g;d = c[g >> 2] | 0; + } else e = g;g = c[a >> 2] | 0;c[a >> 2] = d;c[e >> 2] = g;g = b + 8 | 0;f = c[h >> 2] | 0;c[h >> 2] = c[g >> 2];c[g >> 2] = f;g = a + 8 | 0;h = b + 12 | 0;a = c[g >> 2] | 0;c[g >> 2] = c[h >> 2];c[h >> 2] = a;c[b >> 2] = c[e >> 2];return; + }function ss(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;b = c[a + 4 >> 2] | 0;d = a + 8 | 0;e = c[d >> 2] | 0;if ((e | 0) != (b | 0)) c[d >> 2] = e + (~(((e + -12 - b | 0) >>> 0) / 12 | 0) * 12 | 0);a = c[a >> 2] | 0;if (a | 0) sC(a);return; + }function ts(a) { + a = a | 0;ws(a);return; + }function us(a) { + a = a | 0;vs(a + 24 | 0);return; + }function vs(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;d = c[a >> 2] | 0;e = d;if (d | 0) { + a = a + 4 | 0;b = c[a >> 2] | 0;if ((b | 0) != (d | 0)) c[a >> 2] = b + (~(((b + -12 - e | 0) >>> 0) / 12 | 0) * 12 | 0);sC(d); + }return; + }function ws(a) { + a = a | 0;var b = 0;b = vi() | 0;yi(a, 2, 3, b, xs() | 0, 2);c[a + 24 >> 2] = 0;c[a + 28 >> 2] = 0;c[a + 32 >> 2] = 0;return; + }function xs() { + return 1380; + }function ys(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0, + h = 0, + i = 0;f = l;l = l + 16 | 0;g = f + 8 | 0;h = f;i = zs(a) | 0;a = c[i + 4 >> 2] | 0;c[h >> 2] = c[i >> 2];c[h + 4 >> 2] = a;c[g >> 2] = c[h >> 2];c[g + 4 >> 2] = c[h + 4 >> 2];As(b, g, d, e);l = f;return; + }function zs(a) { + a = a | 0;return (c[(ks() | 0) + 24 >> 2] | 0) + (a * 12 | 0) | 0; + }function As(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0, + h = 0, + i = 0;i = l;l = l + 16 | 0;g = i + 1 | 0;h = i;f = c[b >> 2] | 0;b = c[b + 4 >> 2] | 0;a = a + (b >> 1) | 0;if (b & 1) f = c[(c[a >> 2] | 0) + f >> 2] | 0;g = uj(g, d) | 0;h = Cs(h, e) | 0;Eb[f & 15](a, g, h);l = i;return; + }function Cs(a, b) { + a = a | 0;b = b | 0;return Ds(b) | 0; + }function Ds(a) { + a = a | 0;return (a | 0) != 0 | 0; + }function Es(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0;g = c[a >> 2] | 0;f = Fs() | 0;a = Gs(d) | 0;fi(g, b, f, a, Hs(d, e) | 0, e);return; + }function Fs() { + var b = 0, + d = 0;if (!(a[7864] | 0)) { + Os(10208);Ha(51, 10208, o | 0) | 0;d = 7864;c[d >> 2] = 1;c[d + 4 >> 2] = 0; + }if (!(si(10208) | 0)) { + b = 10208;d = b + 36 | 0;do { + c[b >> 2] = 0;b = b + 4 | 0; + } while ((b | 0) < (d | 0));Os(10208); + }return 10208; + }function Gs(a) { + a = a | 0;return a | 0; + }function Hs(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0;i = l;l = l + 16 | 0;f = i;g = i + 4 | 0;c[f >> 2] = a;j = Fs() | 0;h = j + 24 | 0;b = ji(b, 4) | 0;c[g >> 2] = b;d = j + 28 | 0;e = c[d >> 2] | 0;if (e >>> 0 < (c[j + 32 >> 2] | 0) >>> 0) { + Is(e, a, b);b = (c[d >> 2] | 0) + 8 | 0;c[d >> 2] = b; + } else { + Js(h, f, g);b = c[d >> 2] | 0; + }l = i;return (b - (c[h >> 2] | 0) >> 3) + -1 | 0; + }function Is(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;c[a >> 2] = b;c[a + 4 >> 2] = d;return; + }function Js(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0;i = l;l = l + 32 | 0;f = i;g = a + 4 | 0;h = ((c[g >> 2] | 0) - (c[a >> 2] | 0) >> 3) + 1 | 0;e = Ks(a) | 0;if (e >>> 0 < h >>> 0) jC(a);else { + j = c[a >> 2] | 0;m = (c[a + 8 >> 2] | 0) - j | 0;k = m >> 2;Ls(f, m >> 3 >>> 0 < e >>> 1 >>> 0 ? k >>> 0 < h >>> 0 ? h : k : e, (c[g >> 2] | 0) - j >> 3, a + 8 | 0);h = f + 8 | 0;Is(c[h >> 2] | 0, c[b >> 2] | 0, c[d >> 2] | 0);c[h >> 2] = (c[h >> 2] | 0) + 8;Ms(a, f);Ns(f);l = i;return; + } + }function Ks(a) { + a = a | 0;return 536870911; + }function Ls(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0;c[a + 12 >> 2] = 0;c[a + 16 >> 2] = e;do if (b) { + if (b >>> 0 > 536870911) Ta();else { + f = qC(b << 3) | 0;break; + } + } else f = 0; while (0);c[a >> 2] = f;e = f + (d << 3) | 0;c[a + 8 >> 2] = e;c[a + 4 >> 2] = e;c[a + 12 >> 2] = f + (b << 3);return; + }function Ms(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0;e = c[a >> 2] | 0;h = a + 4 | 0;g = b + 4 | 0;f = (c[h >> 2] | 0) - e | 0;d = (c[g >> 2] | 0) + (0 - (f >> 3) << 3) | 0;c[g >> 2] = d;if ((f | 0) > 0) { + BC(d | 0, e | 0, f | 0) | 0;e = g;d = c[g >> 2] | 0; + } else e = g;g = c[a >> 2] | 0;c[a >> 2] = d;c[e >> 2] = g;g = b + 8 | 0;f = c[h >> 2] | 0;c[h >> 2] = c[g >> 2];c[g >> 2] = f;g = a + 8 | 0;h = b + 12 | 0;a = c[g >> 2] | 0;c[g >> 2] = c[h >> 2];c[h >> 2] = a;c[b >> 2] = c[e >> 2];return; + }function Ns(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;b = c[a + 4 >> 2] | 0;d = a + 8 | 0;e = c[d >> 2] | 0;if ((e | 0) != (b | 0)) c[d >> 2] = e + (~((e + -8 - b | 0) >>> 3) << 3);a = c[a >> 2] | 0;if (a | 0) sC(a);return; + }function Os(a) { + a = a | 0;Rs(a);return; + }function Ps(a) { + a = a | 0;Qs(a + 24 | 0);return; + }function Qs(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;d = c[a >> 2] | 0;e = d;if (d | 0) { + a = a + 4 | 0;b = c[a >> 2] | 0;if ((b | 0) != (d | 0)) c[a >> 2] = b + (~((b + -8 - e | 0) >>> 3) << 3);sC(d); + }return; + }function Rs(a) { + a = a | 0;var b = 0;b = vi() | 0;yi(a, 1, 24, b, Ss() | 0, 1);c[a + 24 >> 2] = 0;c[a + 28 >> 2] = 0;c[a + 32 >> 2] = 0;return; + }function Ss() { + return 1392; + }function Ts(a, b) { + a = a | 0;b = b | 0;Vs(c[(Us(a) | 0) >> 2] | 0, b);return; + }function Us(a) { + a = a | 0;return (c[(Fs() | 0) + 24 >> 2] | 0) + (a << 3) | 0; + }function Vs(a, b) { + a = a | 0;b = b | 0;var c = 0, + d = 0;c = l;l = l + 16 | 0;d = c;b = Mq(d, b) | 0;nb[a & 127](b);l = c;return; + }function Ws(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0;g = c[a >> 2] | 0;f = Xs() | 0;a = Ys(d) | 0;fi(g, b, f, a, Zs(d, e) | 0, e);return; + }function Xs() { + var b = 0, + d = 0;if (!(a[7872] | 0)) { + et(10244);Ha(52, 10244, o | 0) | 0;d = 7872;c[d >> 2] = 1;c[d + 4 >> 2] = 0; + }if (!(si(10244) | 0)) { + b = 10244;d = b + 36 | 0;do { + c[b >> 2] = 0;b = b + 4 | 0; + } while ((b | 0) < (d | 0));et(10244); + }return 10244; + }function Ys(a) { + a = a | 0;return a | 0; + }function Zs(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0;i = l;l = l + 16 | 0;f = i;g = i + 4 | 0;c[f >> 2] = a;j = Xs() | 0;h = j + 24 | 0;b = ji(b, 4) | 0;c[g >> 2] = b;d = j + 28 | 0;e = c[d >> 2] | 0;if (e >>> 0 < (c[j + 32 >> 2] | 0) >>> 0) { + _s(e, a, b);b = (c[d >> 2] | 0) + 8 | 0;c[d >> 2] = b; + } else { + $s(h, f, g);b = c[d >> 2] | 0; + }l = i;return (b - (c[h >> 2] | 0) >> 3) + -1 | 0; + }function _s(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;c[a >> 2] = b;c[a + 4 >> 2] = d;return; + }function $s(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0;i = l;l = l + 32 | 0;f = i;g = a + 4 | 0;h = ((c[g >> 2] | 0) - (c[a >> 2] | 0) >> 3) + 1 | 0;e = at(a) | 0;if (e >>> 0 < h >>> 0) jC(a);else { + j = c[a >> 2] | 0;m = (c[a + 8 >> 2] | 0) - j | 0;k = m >> 2;bt(f, m >> 3 >>> 0 < e >>> 1 >>> 0 ? k >>> 0 < h >>> 0 ? h : k : e, (c[g >> 2] | 0) - j >> 3, a + 8 | 0);h = f + 8 | 0;_s(c[h >> 2] | 0, c[b >> 2] | 0, c[d >> 2] | 0);c[h >> 2] = (c[h >> 2] | 0) + 8;ct(a, f);dt(f);l = i;return; + } + }function at(a) { + a = a | 0;return 536870911; + }function bt(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0;c[a + 12 >> 2] = 0;c[a + 16 >> 2] = e;do if (b) { + if (b >>> 0 > 536870911) Ta();else { + f = qC(b << 3) | 0;break; + } + } else f = 0; while (0);c[a >> 2] = f;e = f + (d << 3) | 0;c[a + 8 >> 2] = e;c[a + 4 >> 2] = e;c[a + 12 >> 2] = f + (b << 3);return; + }function ct(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0;e = c[a >> 2] | 0;h = a + 4 | 0;g = b + 4 | 0;f = (c[h >> 2] | 0) - e | 0;d = (c[g >> 2] | 0) + (0 - (f >> 3) << 3) | 0;c[g >> 2] = d;if ((f | 0) > 0) { + BC(d | 0, e | 0, f | 0) | 0;e = g;d = c[g >> 2] | 0; + } else e = g;g = c[a >> 2] | 0;c[a >> 2] = d;c[e >> 2] = g;g = b + 8 | 0;f = c[h >> 2] | 0;c[h >> 2] = c[g >> 2];c[g >> 2] = f;g = a + 8 | 0;h = b + 12 | 0;a = c[g >> 2] | 0;c[g >> 2] = c[h >> 2];c[h >> 2] = a;c[b >> 2] = c[e >> 2];return; + }function dt(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;b = c[a + 4 >> 2] | 0;d = a + 8 | 0;e = c[d >> 2] | 0;if ((e | 0) != (b | 0)) c[d >> 2] = e + (~((e + -8 - b | 0) >>> 3) << 3);a = c[a >> 2] | 0;if (a | 0) sC(a);return; + }function et(a) { + a = a | 0;ht(a);return; + }function ft(a) { + a = a | 0;gt(a + 24 | 0);return; + }function gt(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;d = c[a >> 2] | 0;e = d;if (d | 0) { + a = a + 4 | 0;b = c[a >> 2] | 0;if ((b | 0) != (d | 0)) c[a >> 2] = b + (~((b + -8 - e | 0) >>> 3) << 3);sC(d); + }return; + }function ht(a) { + a = a | 0;var b = 0;b = vi() | 0;yi(a, 1, 16, b, it() | 0, 0);c[a + 24 >> 2] = 0;c[a + 28 >> 2] = 0;c[a + 32 >> 2] = 0;return; + }function it() { + return 1400; + }function jt(a) { + a = a | 0;return lt(c[(kt(a) | 0) >> 2] | 0) | 0; + }function kt(a) { + a = a | 0;return (c[(Xs() | 0) + 24 >> 2] | 0) + (a << 3) | 0; + }function lt(a) { + a = a | 0;return mt(Ab[a & 7]() | 0) | 0; + }function mt(a) { + a = a | 0;return a | 0; + }function nt() { + var b = 0;if (!(a[7880] | 0)) { + ut(10280);Ha(25, 10280, o | 0) | 0;b = 7880;c[b >> 2] = 1;c[b + 4 >> 2] = 0; + }return 10280; + }function ot(a, b) { + a = a | 0;b = b | 0;c[a >> 2] = pt() | 0;c[a + 4 >> 2] = qt() | 0;c[a + 12 >> 2] = b;c[a + 8 >> 2] = rt() | 0;c[a + 32 >> 2] = 4;return; + }function pt() { + return 11711; + }function qt() { + return 1356; + }function rt() { + return lr() | 0; + }function st(a, b, c, d) { + a = a | 0;b = b | 0;c = c | 0;d = d | 0;if ((jr(d, 896) | 0) == 512) { + if (c | 0) { + tt(c);sC(c); + } + } else if (b | 0) { + mf(b);sC(b); + }return; + }function tt(a) { + a = a | 0;a = c[a + 4 >> 2] | 0;if (a | 0) oC(a);return; + }function ut(a) { + a = a | 0;Zi(a);return; + }function vt(a) { + a = a | 0;wt(a, 4920);xt(a) | 0;yt(a) | 0;return; + }function wt(a, b) { + a = a | 0;b = b | 0;var d = 0;d = nn() | 0;c[a >> 2] = d;Yt(d, b);Hv(c[a >> 2] | 0);return; + }function xt(a) { + a = a | 0;var b = 0;b = c[a >> 2] | 0;At(b, Mt() | 0);return a | 0; + }function yt(a) { + a = a | 0;var b = 0;b = c[a >> 2] | 0;At(b, zt() | 0);return a | 0; + }function zt() { + var b = 0;if (!(a[7888] | 0)) { + Bt(10328);Ha(53, 10328, o | 0) | 0;b = 7888;c[b >> 2] = 1;c[b + 4 >> 2] = 0; + }if (!(si(10328) | 0)) Bt(10328);return 10328; + }function At(a, b) { + a = a | 0;b = b | 0;fi(a, 0, b, 0, 0, 0);return; + }function Bt(a) { + a = a | 0;Et(a);Gt(a, 10);return; + }function Ct(a) { + a = a | 0;Dt(a + 24 | 0);return; + }function Dt(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;d = c[a >> 2] | 0;e = d;if (d | 0) { + a = a + 4 | 0;b = c[a >> 2] | 0;if ((b | 0) != (d | 0)) c[a >> 2] = b + (~((b + -8 - e | 0) >>> 3) << 3);sC(d); + }return; + }function Et(a) { + a = a | 0;var b = 0;b = vi() | 0;yi(a, 5, 1, b, Jt() | 0, 2);c[a + 24 >> 2] = 0;c[a + 28 >> 2] = 0;c[a + 32 >> 2] = 0;return; + }function Ft(a, b, c) { + a = a | 0;b = b | 0;c = +c;Ht(a, b, c);return; + }function Gt(a, b) { + a = a | 0;b = b | 0;c[a + 20 >> 2] = b;return; + }function Ht(a, b, d) { + a = a | 0;b = b | 0;d = +d;var e = 0, + f = 0, + g = 0, + i = 0, + j = 0;e = l;l = l + 16 | 0;g = e + 8 | 0;j = e + 13 | 0;f = e;i = e + 12 | 0;c[g >> 2] = uj(j, b) | 0;h[f >> 3] = +sj(i, d);It(a, g, f);l = e;return; + }function It(b, d, e) { + b = b | 0;d = d | 0;e = e | 0;mg(b + 8 | 0, c[d >> 2] | 0, +h[e >> 3]);a[b + 24 >> 0] = 1;return; + }function Jt() { + return 1404; + }function Kt(a, b) { + a = a | 0;b = +b;return Lt(a, b) | 0; + }function Lt(a, b) { + a = a | 0;b = +b;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0;e = l;l = l + 16 | 0;g = e + 4 | 0;h = e + 8 | 0;i = e;f = jy(8) | 0;d = f;j = qC(16) | 0;a = uj(g, a) | 0;mg(j, a, +sj(h, b));h = d + 4 | 0;c[h >> 2] = j;a = qC(8) | 0;h = c[h >> 2] | 0;c[i >> 2] = 0;c[g >> 2] = c[i >> 2];qn(a, h, g);c[f >> 2] = a;l = e;return d | 0; + }function Mt() { + var b = 0;if (!(a[7896] | 0)) { + Nt(10364);Ha(54, 10364, o | 0) | 0;b = 7896;c[b >> 2] = 1;c[b + 4 >> 2] = 0; + }if (!(si(10364) | 0)) Nt(10364);return 10364; + }function Nt(a) { + a = a | 0;Qt(a);Gt(a, 55);return; + }function Ot(a) { + a = a | 0;Pt(a + 24 | 0);return; + }function Pt(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;d = c[a >> 2] | 0;e = d;if (d | 0) { + a = a + 4 | 0;b = c[a >> 2] | 0;if ((b | 0) != (d | 0)) c[a >> 2] = b + (~((b + -8 - e | 0) >>> 3) << 3);sC(d); + }return; + }function Qt(a) { + a = a | 0;var b = 0;b = vi() | 0;yi(a, 5, 4, b, Vt() | 0, 0);c[a + 24 >> 2] = 0;c[a + 28 >> 2] = 0;c[a + 32 >> 2] = 0;return; + }function Rt(a) { + a = a | 0;St(a);return; + }function St(a) { + a = a | 0;Tt(a);return; + }function Tt(b) { + b = b | 0;Ut(b + 8 | 0);a[b + 24 >> 0] = 1;return; + }function Ut(a) { + a = a | 0;c[a >> 2] = 0;h[a + 8 >> 3] = 0.0;return; + }function Vt() { + return 1424; + }function Wt() { + return Xt() | 0; + }function Xt() { + var a = 0, + b = 0, + d = 0, + e = 0, + f = 0, + g = 0, + h = 0;b = l;l = l + 16 | 0;f = b + 4 | 0;h = b;d = jy(8) | 0;a = d;e = qC(16) | 0;Ut(e);g = a + 4 | 0;c[g >> 2] = e;e = qC(8) | 0;g = c[g >> 2] | 0;c[h >> 2] = 0;c[f >> 2] = c[h >> 2];qn(e, g, f);c[d >> 2] = e;l = b;return a | 0; + }function Yt(a, b) { + a = a | 0;b = b | 0;c[a >> 2] = Zt() | 0;c[a + 4 >> 2] = _t() | 0;c[a + 12 >> 2] = b;c[a + 8 >> 2] = $t() | 0;c[a + 32 >> 2] = 5;return; + }function Zt() { + return 11710; + }function _t() { + return 1416; + }function $t() { + return cu() | 0; + }function au(a, b, c, d) { + a = a | 0;b = b | 0;c = c | 0;d = d | 0;if ((jr(d, 896) | 0) == 512) { + if (c | 0) { + bu(c);sC(c); + } + } else if (b | 0) sC(b);return; + }function bu(a) { + a = a | 0;a = c[a + 4 >> 2] | 0;if (a | 0) oC(a);return; + }function cu() { + var b = 0;if (!(a[7904] | 0)) { + c[2600] = du() | 0;c[2601] = 0;b = 7904;c[b >> 2] = 1;c[b + 4 >> 2] = 0; + }return 10400; + }function du() { + return c[357] | 0; + }function eu(a) { + a = a | 0;fu(a, 4926);gu(a) | 0;return; + }function fu(a, b) { + a = a | 0;b = b | 0;var d = 0;d = Ci() | 0;c[a >> 2] = d;su(d, b);Hv(c[a >> 2] | 0);return; + }function gu(a) { + a = a | 0;var b = 0;b = c[a >> 2] | 0;At(b, hu() | 0);return a | 0; + }function hu() { + var b = 0;if (!(a[7912] | 0)) { + iu(10412);Ha(56, 10412, o | 0) | 0;b = 7912;c[b >> 2] = 1;c[b + 4 >> 2] = 0; + }if (!(si(10412) | 0)) iu(10412);return 10412; + }function iu(a) { + a = a | 0;lu(a);Gt(a, 57);return; + }function ju(a) { + a = a | 0;ku(a + 24 | 0);return; + }function ku(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;d = c[a >> 2] | 0;e = d;if (d | 0) { + a = a + 4 | 0;b = c[a >> 2] | 0;if ((b | 0) != (d | 0)) c[a >> 2] = b + (~((b + -8 - e | 0) >>> 3) << 3);sC(d); + }return; + }function lu(a) { + a = a | 0;var b = 0;b = vi() | 0;yi(a, 5, 5, b, pu() | 0, 0);c[a + 24 >> 2] = 0;c[a + 28 >> 2] = 0;c[a + 32 >> 2] = 0;return; + }function mu(a) { + a = a | 0;nu(a);return; + }function nu(a) { + a = a | 0;ou(a);return; + }function ou(b) { + b = b | 0;var d = 0, + e = 0;d = b + 8 | 0;e = d + 48 | 0;do { + c[d >> 2] = 0;d = d + 4 | 0; + } while ((d | 0) < (e | 0));a[b + 56 >> 0] = 1;return; + }function pu() { + return 1432; + }function qu() { + return ru() | 0; + }function ru() { + var a = 0, + b = 0, + d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0;h = l;l = l + 16 | 0;a = h + 4 | 0;b = h;d = jy(8) | 0;e = d;f = qC(48) | 0;g = f;i = g + 48 | 0;do { + c[g >> 2] = 0;g = g + 4 | 0; + } while ((g | 0) < (i | 0));g = e + 4 | 0;c[g >> 2] = f;i = qC(8) | 0;g = c[g >> 2] | 0;c[b >> 2] = 0;c[a >> 2] = c[b >> 2];Ji(i, g, a);c[d >> 2] = i;l = h;return e | 0; + }function su(a, b) { + a = a | 0;b = b | 0;c[a >> 2] = tu() | 0;c[a + 4 >> 2] = uu() | 0;c[a + 12 >> 2] = b;c[a + 8 >> 2] = vu() | 0;c[a + 32 >> 2] = 6;return; + }function tu() { + return 11704; + }function uu() { + return 1436; + }function vu() { + return cu() | 0; + }function wu(a, b, c, d) { + a = a | 0;b = b | 0;c = c | 0;d = d | 0;if ((jr(d, 896) | 0) == 512) { + if (c | 0) { + xu(c);sC(c); + } + } else if (b | 0) sC(b);return; + }function xu(a) { + a = a | 0;a = c[a + 4 >> 2] | 0;if (a | 0) oC(a);return; + }function yu(a) { + a = a | 0;zu(a, 4933);Au(a) | 0;Bu(a) | 0;return; + }function zu(a, b) { + a = a | 0;b = b | 0;var d = 0;d = cv() | 0;c[a >> 2] = d;dv(d, b);Hv(c[a >> 2] | 0);return; + }function Au(a) { + a = a | 0;var b = 0;b = c[a >> 2] | 0;At(b, Su() | 0);return a | 0; + }function Bu(a) { + a = a | 0;var b = 0;b = c[a >> 2] | 0;At(b, Cu() | 0);return a | 0; + }function Cu() { + var b = 0;if (!(a[7920] | 0)) { + Du(10452);Ha(58, 10452, o | 0) | 0;b = 7920;c[b >> 2] = 1;c[b + 4 >> 2] = 0; + }if (!(si(10452) | 0)) Du(10452);return 10452; + }function Du(a) { + a = a | 0;Gu(a);Gt(a, 1);return; + }function Eu(a) { + a = a | 0;Fu(a + 24 | 0);return; + }function Fu(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;d = c[a >> 2] | 0;e = d;if (d | 0) { + a = a + 4 | 0;b = c[a >> 2] | 0;if ((b | 0) != (d | 0)) c[a >> 2] = b + (~((b + -8 - e | 0) >>> 3) << 3);sC(d); + }return; + }function Gu(a) { + a = a | 0;var b = 0;b = vi() | 0;yi(a, 5, 1, b, Lu() | 0, 2);c[a + 24 >> 2] = 0;c[a + 28 >> 2] = 0;c[a + 32 >> 2] = 0;return; + }function Hu(a, b, c) { + a = a | 0;b = +b;c = +c;Iu(a, b, c);return; + }function Iu(a, b, c) { + a = a | 0;b = +b;c = +c;var d = 0, + e = 0, + f = 0, + g = 0, + i = 0;d = l;l = l + 32 | 0;f = d + 8 | 0;i = d + 17 | 0;e = d;g = d + 16 | 0;h[f >> 3] = +sj(i, b);h[e >> 3] = +sj(g, c);Ju(a, f, e);l = d;return; + }function Ju(b, c, d) { + b = b | 0;c = c | 0;d = d | 0;Ku(b + 8 | 0, +h[c >> 3], +h[d >> 3]);a[b + 24 >> 0] = 1;return; + }function Ku(a, b, c) { + a = a | 0;b = +b;c = +c;h[a >> 3] = b;h[a + 8 >> 3] = c;return; + }function Lu() { + return 1472; + }function Mu(a, b) { + a = +a;b = +b;return Nu(a, b) | 0; + }function Nu(a, b) { + a = +a;b = +b;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0;e = l;l = l + 16 | 0;h = e + 4 | 0;i = e + 8 | 0;j = e;f = jy(8) | 0;d = f;g = qC(16) | 0;a = +sj(h, a);Ku(g, a, +sj(i, b));i = d + 4 | 0;c[i >> 2] = g;g = qC(8) | 0;i = c[i >> 2] | 0;c[j >> 2] = 0;c[h >> 2] = c[j >> 2];Ou(g, i, h);c[f >> 2] = g;l = e;return d | 0; + }function Ou(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;c[a >> 2] = b;d = qC(16) | 0;c[d + 4 >> 2] = 0;c[d + 8 >> 2] = 0;c[d >> 2] = 1452;c[d + 12 >> 2] = b;c[a + 4 >> 2] = d;return; + }function Pu(a) { + a = a | 0;kC(a);sC(a);return; + }function Qu(a) { + a = a | 0;a = c[a + 12 >> 2] | 0;if (a | 0) sC(a);return; + }function Ru(a) { + a = a | 0;sC(a);return; + }function Su() { + var b = 0;if (!(a[7928] | 0)) { + Tu(10488);Ha(59, 10488, o | 0) | 0;b = 7928;c[b >> 2] = 1;c[b + 4 >> 2] = 0; + }if (!(si(10488) | 0)) Tu(10488);return 10488; + }function Tu(a) { + a = a | 0;Wu(a);Gt(a, 60);return; + }function Uu(a) { + a = a | 0;Vu(a + 24 | 0);return; + }function Vu(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;d = c[a >> 2] | 0;e = d;if (d | 0) { + a = a + 4 | 0;b = c[a >> 2] | 0;if ((b | 0) != (d | 0)) c[a >> 2] = b + (~((b + -8 - e | 0) >>> 3) << 3);sC(d); + }return; + }function Wu(a) { + a = a | 0;var b = 0;b = vi() | 0;yi(a, 5, 6, b, $u() | 0, 0);c[a + 24 >> 2] = 0;c[a + 28 >> 2] = 0;c[a + 32 >> 2] = 0;return; + }function Xu(a) { + a = a | 0;Yu(a);return; + }function Yu(a) { + a = a | 0;Zu(a);return; + }function Zu(b) { + b = b | 0;_u(b + 8 | 0);a[b + 24 >> 0] = 1;return; + }function _u(a) { + a = a | 0;c[a >> 2] = 0;c[a + 4 >> 2] = 0;c[a + 8 >> 2] = 0;c[a + 12 >> 2] = 0;return; + }function $u() { + return 1492; + }function av() { + return bv() | 0; + }function bv() { + var a = 0, + b = 0, + d = 0, + e = 0, + f = 0, + g = 0, + h = 0;b = l;l = l + 16 | 0;f = b + 4 | 0;h = b;d = jy(8) | 0;a = d;e = qC(16) | 0;_u(e);g = a + 4 | 0;c[g >> 2] = e;e = qC(8) | 0;g = c[g >> 2] | 0;c[h >> 2] = 0;c[f >> 2] = c[h >> 2];Ou(e, g, f);c[d >> 2] = e;l = b;return a | 0; + }function cv() { + var b = 0;if (!(a[7936] | 0)) { + jv(10524);Ha(25, 10524, o | 0) | 0;b = 7936;c[b >> 2] = 1;c[b + 4 >> 2] = 0; + }return 10524; + }function dv(a, b) { + a = a | 0;b = b | 0;c[a >> 2] = ev() | 0;c[a + 4 >> 2] = fv() | 0;c[a + 12 >> 2] = b;c[a + 8 >> 2] = gv() | 0;c[a + 32 >> 2] = 7;return; + }function ev() { + return 11700; + }function fv() { + return 1484; + }function gv() { + return cu() | 0; + }function hv(a, b, c, d) { + a = a | 0;b = b | 0;c = c | 0;d = d | 0;if ((jr(d, 896) | 0) == 512) { + if (c | 0) { + iv(c);sC(c); + } + } else if (b | 0) sC(b);return; + }function iv(a) { + a = a | 0;a = c[a + 4 >> 2] | 0;if (a | 0) oC(a);return; + }function jv(a) { + a = a | 0;Zi(a);return; + }function kv(a, b, c) { + a = a | 0;b = b | 0;c = c | 0;a = ai(b) | 0;b = lv(c) | 0;c = mv(c, 0) | 0;Zv(a, b, c, nv() | 0, 0);return; + }function lv(a) { + a = a | 0;return a | 0; + }function mv(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0;i = l;l = l + 16 | 0;f = i;g = i + 4 | 0;c[f >> 2] = a;j = nv() | 0;h = j + 24 | 0;b = ji(b, 4) | 0;c[g >> 2] = b;d = j + 28 | 0;e = c[d >> 2] | 0;if (e >>> 0 < (c[j + 32 >> 2] | 0) >>> 0) { + vv(e, a, b);b = (c[d >> 2] | 0) + 8 | 0;c[d >> 2] = b; + } else { + wv(h, f, g);b = c[d >> 2] | 0; + }l = i;return (b - (c[h >> 2] | 0) >> 3) + -1 | 0; + }function nv() { + var b = 0, + d = 0;if (!(a[7944] | 0)) { + ov(10568);Ha(61, 10568, o | 0) | 0;d = 7944;c[d >> 2] = 1;c[d + 4 >> 2] = 0; + }if (!(si(10568) | 0)) { + b = 10568;d = b + 36 | 0;do { + c[b >> 2] = 0;b = b + 4 | 0; + } while ((b | 0) < (d | 0));ov(10568); + }return 10568; + }function ov(a) { + a = a | 0;rv(a);return; + }function pv(a) { + a = a | 0;qv(a + 24 | 0);return; + }function qv(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;d = c[a >> 2] | 0;e = d;if (d | 0) { + a = a + 4 | 0;b = c[a >> 2] | 0;if ((b | 0) != (d | 0)) c[a >> 2] = b + (~((b + -8 - e | 0) >>> 3) << 3);sC(d); + }return; + }function rv(a) { + a = a | 0;var b = 0;b = vi() | 0;yi(a, 1, 17, b, ql() | 0, 0);c[a + 24 >> 2] = 0;c[a + 28 >> 2] = 0;c[a + 32 >> 2] = 0;return; + }function sv(a) { + a = a | 0;return uv(c[(tv(a) | 0) >> 2] | 0) | 0; + }function tv(a) { + a = a | 0;return (c[(nv() | 0) + 24 >> 2] | 0) + (a << 3) | 0; + }function uv(a) { + a = a | 0;return ul(Ab[a & 7]() | 0) | 0; + }function vv(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;c[a >> 2] = b;c[a + 4 >> 2] = d;return; + }function wv(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0;i = l;l = l + 32 | 0;f = i;g = a + 4 | 0;h = ((c[g >> 2] | 0) - (c[a >> 2] | 0) >> 3) + 1 | 0;e = xv(a) | 0;if (e >>> 0 < h >>> 0) jC(a);else { + j = c[a >> 2] | 0;m = (c[a + 8 >> 2] | 0) - j | 0;k = m >> 2;yv(f, m >> 3 >>> 0 < e >>> 1 >>> 0 ? k >>> 0 < h >>> 0 ? h : k : e, (c[g >> 2] | 0) - j >> 3, a + 8 | 0);h = f + 8 | 0;vv(c[h >> 2] | 0, c[b >> 2] | 0, c[d >> 2] | 0);c[h >> 2] = (c[h >> 2] | 0) + 8;zv(a, f);Av(f);l = i;return; + } + }function xv(a) { + a = a | 0;return 536870911; + }function yv(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0;c[a + 12 >> 2] = 0;c[a + 16 >> 2] = e;do if (b) { + if (b >>> 0 > 536870911) Ta();else { + f = qC(b << 3) | 0;break; + } + } else f = 0; while (0);c[a >> 2] = f;e = f + (d << 3) | 0;c[a + 8 >> 2] = e;c[a + 4 >> 2] = e;c[a + 12 >> 2] = f + (b << 3);return; + }function zv(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0;e = c[a >> 2] | 0;h = a + 4 | 0;g = b + 4 | 0;f = (c[h >> 2] | 0) - e | 0;d = (c[g >> 2] | 0) + (0 - (f >> 3) << 3) | 0;c[g >> 2] = d;if ((f | 0) > 0) { + BC(d | 0, e | 0, f | 0) | 0;e = g;d = c[g >> 2] | 0; + } else e = g;g = c[a >> 2] | 0;c[a >> 2] = d;c[e >> 2] = g;g = b + 8 | 0;f = c[h >> 2] | 0;c[h >> 2] = c[g >> 2];c[g >> 2] = f;g = a + 8 | 0;h = b + 12 | 0;a = c[g >> 2] | 0;c[g >> 2] = c[h >> 2];c[h >> 2] = a;c[b >> 2] = c[e >> 2];return; + }function Av(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;b = c[a + 4 >> 2] | 0;d = a + 8 | 0;e = c[d >> 2] | 0;if ((e | 0) != (b | 0)) c[d >> 2] = e + (~((e + -8 - b | 0) >>> 3) << 3);a = c[a >> 2] | 0;if (a | 0) sC(a);return; + }function Bv() { + Cv();return; + }function Cv() { + Dv(10604);return; + }function Dv(a) { + a = a | 0;Ev(a, 4955);return; + }function Ev(a, b) { + a = a | 0;b = b | 0;var d = 0;d = Fv() | 0;c[a >> 2] = d;Gv(d, b);Hv(c[a >> 2] | 0);return; + }function Fv() { + var b = 0;if (!(a[7952] | 0)) { + Rv(10612);Ha(25, 10612, o | 0) | 0;b = 7952;c[b >> 2] = 1;c[b + 4 >> 2] = 0; + }return 10612; + }function Gv(a, b) { + a = a | 0;b = b | 0;c[a >> 2] = Mv() | 0;c[a + 4 >> 2] = Nv() | 0;c[a + 12 >> 2] = b;c[a + 8 >> 2] = Ov() | 0;c[a + 32 >> 2] = 8;return; + }function Hv(a) { + a = a | 0;var b = 0, + d = 0;b = l;l = l + 16 | 0;d = b;Iv() | 0;c[d >> 2] = a;Jv(10608, d);l = b;return; + }function Iv() { + if (!(a[11714] | 0)) { + c[2652] = 0;Ha(62, 10608, o | 0) | 0;a[11714] = 1; + }return 10608; + }function Jv(a, b) { + a = a | 0;b = b | 0;var d = 0;d = qC(8) | 0;c[d + 4 >> 2] = c[b >> 2];c[d >> 2] = c[a >> 2];c[a >> 2] = d;return; + }function Kv(a) { + a = a | 0;Lv(a);return; + }function Lv(a) { + a = a | 0;var b = 0, + d = 0;b = c[a >> 2] | 0;if (b | 0) do { + d = b;b = c[b >> 2] | 0;sC(d); + } while ((b | 0) != 0);c[a >> 2] = 0;return; + }function Mv() { + return 11715; + }function Nv() { + return 1496; + }function Ov() { + return lr() | 0; + }function Pv(a, b, c, d) { + a = a | 0;b = b | 0;c = c | 0;d = d | 0;if ((jr(d, 896) | 0) == 512) { + if (c | 0) { + Qv(c);sC(c); + } + } else if (b | 0) sC(b);return; + }function Qv(a) { + a = a | 0;a = c[a + 4 >> 2] | 0;if (a | 0) oC(a);return; + }function Rv(a) { + a = a | 0;Zi(a);return; + }function Sv(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0;Iv() | 0;d = c[2652] | 0;a: do if (d | 0) { + while (1) { + e = c[d + 4 >> 2] | 0;if (e | 0 ? (AB(Tv(e) | 0, a) | 0) == 0 : 0) break;d = c[d >> 2] | 0;if (!d) break a; + }Uv(e, b); + } while (0);return; + }function Tv(a) { + a = a | 0;return c[a + 12 >> 2] | 0; + }function Uv(a, b) { + a = a | 0;b = b | 0;var d = 0;a = a + 36 | 0;d = c[a >> 2] | 0;if (d | 0) { + vf(d);sC(d); + }d = qC(4) | 0;Og(d, b);c[a >> 2] = d;return; + }function Vv() { + if (!(a[11716] | 0)) { + c[2664] = 0;Ha(63, 10656, o | 0) | 0;a[11716] = 1; + }return 10656; + }function Wv() { + var b = 0;if (!(a[11717] | 0)) { + Xv();c[2665] = 1504;a[11717] = 1;b = 1504; + } else b = c[2665] | 0;return b | 0; + }function Xv() { + if (!(a[11740] | 0)) { + a[11718] = ji(ji(8, 0) | 0, 0) | 0;a[11719] = ji(ji(0, 0) | 0, 0) | 0;a[11720] = ji(ji(0, 16) | 0, 0) | 0;a[11721] = ji(ji(8, 0) | 0, 0) | 0;a[11722] = ji(ji(0, 0) | 0, 0) | 0;a[11723] = ji(ji(8, 0) | 0, 0) | 0;a[11724] = ji(ji(0, 0) | 0, 0) | 0;a[11725] = ji(ji(8, 0) | 0, 0) | 0;a[11726] = ji(ji(0, 0) | 0, 0) | 0;a[11727] = ji(ji(8, 0) | 0, 0) | 0;a[11728] = ji(ji(0, 0) | 0, 0) | 0;a[11729] = ji(ji(0, 0) | 0, 32) | 0;a[11730] = ji(ji(0, 0) | 0, 32) | 0;a[11740] = 1; + }return; + }function Yv() { + return 1572; + }function Zv(a, b, d, e, f) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;f = f | 0;var g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0;g = l;l = l + 32 | 0;m = g + 16 | 0;k = g + 12 | 0;j = g + 8 | 0;i = g + 4 | 0;h = g;c[m >> 2] = a;c[k >> 2] = b;c[j >> 2] = d;c[i >> 2] = e;c[h >> 2] = f;Vv() | 0;_v(10656, m, k, j, i, h);l = g;return; + }function _v(a, b, d, e, f, g) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;f = f | 0;g = g | 0;var h = 0;h = qC(24) | 0;ii(h + 4 | 0, c[b >> 2] | 0, c[d >> 2] | 0, c[e >> 2] | 0, c[f >> 2] | 0, c[g >> 2] | 0);c[h >> 2] = c[a >> 2];c[a >> 2] = h;return; + }function $v(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0, + o = 0, + p = 0, + q = 0, + r = 0, + s = 0, + t = 0, + u = 0;u = l;l = l + 32 | 0;q = u + 20 | 0;r = u + 8 | 0;s = u + 4 | 0;t = u;b = c[b >> 2] | 0;if (b | 0) { + p = q + 4 | 0;j = q + 8 | 0;k = r + 4 | 0;m = r + 8 | 0;n = r + 8 | 0;o = q + 8 | 0;do { + h = b + 4 | 0;i = aw(h) | 0;if (i | 0) { + f = bw(i) | 0;c[q >> 2] = 0;c[p >> 2] = 0;c[j >> 2] = 0;e = (cw(i) | 0) + 1 | 0;dw(q, e);if (e | 0) while (1) { + e = e + -1 | 0;OA(r, c[f >> 2] | 0);g = c[p >> 2] | 0;if (g >>> 0 < (c[o >> 2] | 0) >>> 0) { + c[g >> 2] = c[r >> 2];c[p >> 2] = (c[p >> 2] | 0) + 4; + } else ew(q, r);if (!e) break;else f = f + 4 | 0; + }e = fw(i) | 0;c[r >> 2] = 0;c[k >> 2] = 0;c[m >> 2] = 0;a: do if (c[e >> 2] | 0) { + f = 0;g = 0;while (1) { + if ((f | 0) == (g | 0)) gw(r, e);else { + c[f >> 2] = c[e >> 2];c[k >> 2] = (c[k >> 2] | 0) + 4; + }e = e + 4 | 0;if (!(c[e >> 2] | 0)) break a;f = c[k >> 2] | 0;g = c[n >> 2] | 0; + } + } while (0);c[s >> 2] = hw(h) | 0;c[t >> 2] = si(i) | 0;iw(d, a, s, t, q, r);jw(r);kw(q); + }b = c[b >> 2] | 0; + } while ((b | 0) != 0); + }l = u;return; + }function aw(a) { + a = a | 0;return c[a + 12 >> 2] | 0; + }function bw(a) { + a = a | 0;return c[a + 12 >> 2] | 0; + }function cw(a) { + a = a | 0;return c[a + 16 >> 2] | 0; + }function dw(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0;f = l;l = l + 32 | 0;d = f;e = c[a >> 2] | 0;if ((c[a + 8 >> 2] | 0) - e >> 2 >>> 0 < b >>> 0) { + Rw(d, b, (c[a + 4 >> 2] | 0) - e >> 2, a + 8 | 0);Sw(a, d);Tw(d); + }l = f;return; + }function ew(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0;h = l;l = l + 32 | 0;d = h;e = a + 4 | 0;f = ((c[e >> 2] | 0) - (c[a >> 2] | 0) >> 2) + 1 | 0;g = Nw(a) | 0;if (g >>> 0 < f >>> 0) jC(a);else { + i = c[a >> 2] | 0;k = (c[a + 8 >> 2] | 0) - i | 0;j = k >> 1;Rw(d, k >> 2 >>> 0 < g >>> 1 >>> 0 ? j >>> 0 < f >>> 0 ? f : j : g, (c[e >> 2] | 0) - i >> 2, a + 8 | 0);g = d + 8 | 0;c[c[g >> 2] >> 2] = c[b >> 2];c[g >> 2] = (c[g >> 2] | 0) + 4;Sw(a, d);Tw(d);l = h;return; + } + }function fw(a) { + a = a | 0;return c[a + 8 >> 2] | 0; + }function gw(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0;h = l;l = l + 32 | 0;d = h;e = a + 4 | 0;f = ((c[e >> 2] | 0) - (c[a >> 2] | 0) >> 2) + 1 | 0;g = Kw(a) | 0;if (g >>> 0 < f >>> 0) jC(a);else { + i = c[a >> 2] | 0;k = (c[a + 8 >> 2] | 0) - i | 0;j = k >> 1;Ow(d, k >> 2 >>> 0 < g >>> 1 >>> 0 ? j >>> 0 < f >>> 0 ? f : j : g, (c[e >> 2] | 0) - i >> 2, a + 8 | 0);g = d + 8 | 0;c[c[g >> 2] >> 2] = c[b >> 2];c[g >> 2] = (c[g >> 2] | 0) + 4;Pw(a, d);Qw(d);l = h;return; + } + }function hw(a) { + a = a | 0;return c[a >> 2] | 0; + }function iw(a, b, c, d, e, f) { + a = a | 0;b = b | 0;c = c | 0;d = d | 0;e = e | 0;f = f | 0;lw(a, b, c, d, e, f);return; + }function jw(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;d = c[a >> 2] | 0;e = d;if (d | 0) { + a = a + 4 | 0;b = c[a >> 2] | 0;if ((b | 0) != (d | 0)) c[a >> 2] = b + (~((b + -4 - e | 0) >>> 2) << 2);sC(d); + }return; + }function kw(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;d = c[a >> 2] | 0;e = d;if (d | 0) { + a = a + 4 | 0;b = c[a >> 2] | 0;if ((b | 0) != (d | 0)) c[a >> 2] = b + (~((b + -4 - e | 0) >>> 2) << 2);sC(d); + }return; + }function lw(a, b, d, e, f, g) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;f = f | 0;g = g | 0;var h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0;h = l;l = l + 48 | 0;m = h + 40 | 0;i = h + 32 | 0;n = h + 24 | 0;j = h + 12 | 0;k = h;UA(i);a = Sg(a) | 0;c[n >> 2] = c[b >> 2];d = c[d >> 2] | 0;e = c[e >> 2] | 0;mw(j, f);nw(k, g);c[m >> 2] = c[n >> 2];ow(a, m, d, e, j, k);jw(k);kw(j);WA(i);l = h;return; + }function mw(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0;c[a >> 2] = 0;c[a + 4 >> 2] = 0;c[a + 8 >> 2] = 0;d = b + 4 | 0;e = (c[d >> 2] | 0) - (c[b >> 2] | 0) >> 2;if (e | 0) { + Lw(a, e);Mw(a, c[b >> 2] | 0, c[d >> 2] | 0, e); + }return; + }function nw(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0;c[a >> 2] = 0;c[a + 4 >> 2] = 0;c[a + 8 >> 2] = 0;d = b + 4 | 0;e = (c[d >> 2] | 0) - (c[b >> 2] | 0) >> 2;if (e | 0) { + Iw(a, e);Jw(a, c[b >> 2] | 0, c[d >> 2] | 0, e); + }return; + }function ow(a, b, d, e, f, g) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;f = f | 0;g = g | 0;var h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0;h = l;l = l + 32 | 0;m = h + 28 | 0;n = h + 24 | 0;i = h + 12 | 0;j = h;k = Vg(pw() | 0) | 0;c[n >> 2] = c[b >> 2];c[m >> 2] = c[n >> 2];b = qw(m) | 0;d = rw(d) | 0;e = sw(e) | 0;c[i >> 2] = c[f >> 2];m = f + 4 | 0;c[i + 4 >> 2] = c[m >> 2];n = f + 8 | 0;c[i + 8 >> 2] = c[n >> 2];c[n >> 2] = 0;c[m >> 2] = 0;c[f >> 2] = 0;f = tw(i) | 0;c[j >> 2] = c[g >> 2];m = g + 4 | 0;c[j + 4 >> 2] = c[m >> 2];n = g + 8 | 0;c[j + 8 >> 2] = c[n >> 2];c[n >> 2] = 0;c[m >> 2] = 0;c[g >> 2] = 0;Ba(0, k | 0, a | 0, b | 0, d | 0, e | 0, f | 0, uw(j) | 0) | 0;jw(j);kw(i);l = h;return; + }function pw() { + var b = 0;if (!(a[7968] | 0)) { + Gw(10708);b = 7968;c[b >> 2] = 1;c[b + 4 >> 2] = 0; + }return 10708; + }function qw(a) { + a = a | 0;return yw(a) | 0; + }function rw(a) { + a = a | 0;return ww(a) | 0; + }function sw(a) { + a = a | 0;return ul(a) | 0; + }function tw(a) { + a = a | 0;return xw(a) | 0; + }function uw(a) { + a = a | 0;return vw(a) | 0; + }function vw(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;e = (c[a + 4 >> 2] | 0) - (c[a >> 2] | 0) | 0;d = e >> 2;e = jy(e + 4 | 0) | 0;c[e >> 2] = d;if (d | 0) { + b = 0;do { + c[e + 4 + (b << 2) >> 2] = ww(c[(c[a >> 2] | 0) + (b << 2) >> 2] | 0) | 0;b = b + 1 | 0; + } while ((b | 0) != (d | 0)); + }return e | 0; + }function ww(a) { + a = a | 0;return a | 0; + }function xw(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;e = (c[a + 4 >> 2] | 0) - (c[a >> 2] | 0) | 0;d = e >> 2;e = jy(e + 4 | 0) | 0;c[e >> 2] = d;if (d | 0) { + b = 0;do { + c[e + 4 + (b << 2) >> 2] = yw((c[a >> 2] | 0) + (b << 2) | 0) | 0;b = b + 1 | 0; + } while ((b | 0) != (d | 0)); + }return e | 0; + }function yw(a) { + a = a | 0;var b = 0, + c = 0, + d = 0, + e = 0;e = l;l = l + 32 | 0;b = e + 12 | 0;c = e;d = Di(zw() | 0) | 0;if (!d) a = Aw(a) | 0;else { + Ei(b, d);Fi(c, b);RA(a, c);a = Hi(b) | 0; + }l = e;return a | 0; + }function zw() { + var b = 0;if (!(a[7960] | 0)) { + Fw(10664);Ha(25, 10664, o | 0) | 0;b = 7960;c[b >> 2] = 1;c[b + 4 >> 2] = 0; + }return 10664; + }function Aw(a) { + a = a | 0;var b = 0, + d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0;d = l;l = l + 16 | 0;f = d + 4 | 0;h = d;e = jy(8) | 0;b = e;i = qC(4) | 0;c[i >> 2] = c[a >> 2];g = b + 4 | 0;c[g >> 2] = i;a = qC(8) | 0;g = c[g >> 2] | 0;c[h >> 2] = 0;c[f >> 2] = c[h >> 2];Bw(a, g, f);c[e >> 2] = a;l = d;return b | 0; + }function Bw(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;c[a >> 2] = b;d = qC(16) | 0;c[d + 4 >> 2] = 0;c[d + 8 >> 2] = 0;c[d >> 2] = 1656;c[d + 12 >> 2] = b;c[a + 4 >> 2] = d;return; + }function Cw(a) { + a = a | 0;kC(a);sC(a);return; + }function Dw(a) { + a = a | 0;a = c[a + 12 >> 2] | 0;if (a | 0) sC(a);return; + }function Ew(a) { + a = a | 0;sC(a);return; + }function Fw(a) { + a = a | 0;Zi(a);return; + }function Gw(a) { + a = a | 0;fh(a, Hw() | 0, 5);return; + }function Hw() { + return 1676; + }function Iw(a, b) { + a = a | 0;b = b | 0;var d = 0;if ((Kw(a) | 0) >>> 0 < b >>> 0) jC(a);if (b >>> 0 > 1073741823) Ta();else { + d = qC(b << 2) | 0;c[a + 4 >> 2] = d;c[a >> 2] = d;c[a + 8 >> 2] = d + (b << 2);return; + } + }function Jw(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;e = a + 4 | 0;a = d - b | 0;if ((a | 0) > 0) { + BC(c[e >> 2] | 0, b | 0, a | 0) | 0;c[e >> 2] = (c[e >> 2] | 0) + (a >>> 2 << 2); + }return; + }function Kw(a) { + a = a | 0;return 1073741823; + }function Lw(a, b) { + a = a | 0;b = b | 0;var d = 0;if ((Nw(a) | 0) >>> 0 < b >>> 0) jC(a);if (b >>> 0 > 1073741823) Ta();else { + d = qC(b << 2) | 0;c[a + 4 >> 2] = d;c[a >> 2] = d;c[a + 8 >> 2] = d + (b << 2);return; + } + }function Mw(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;e = a + 4 | 0;a = d - b | 0;if ((a | 0) > 0) { + BC(c[e >> 2] | 0, b | 0, a | 0) | 0;c[e >> 2] = (c[e >> 2] | 0) + (a >>> 2 << 2); + }return; + }function Nw(a) { + a = a | 0;return 1073741823; + }function Ow(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0;c[a + 12 >> 2] = 0;c[a + 16 >> 2] = e;do if (b) { + if (b >>> 0 > 1073741823) Ta();else { + f = qC(b << 2) | 0;break; + } + } else f = 0; while (0);c[a >> 2] = f;e = f + (d << 2) | 0;c[a + 8 >> 2] = e;c[a + 4 >> 2] = e;c[a + 12 >> 2] = f + (b << 2);return; + }function Pw(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0;e = c[a >> 2] | 0;h = a + 4 | 0;g = b + 4 | 0;f = (c[h >> 2] | 0) - e | 0;d = (c[g >> 2] | 0) + (0 - (f >> 2) << 2) | 0;c[g >> 2] = d;if ((f | 0) > 0) { + BC(d | 0, e | 0, f | 0) | 0;e = g;d = c[g >> 2] | 0; + } else e = g;g = c[a >> 2] | 0;c[a >> 2] = d;c[e >> 2] = g;g = b + 8 | 0;f = c[h >> 2] | 0;c[h >> 2] = c[g >> 2];c[g >> 2] = f;g = a + 8 | 0;h = b + 12 | 0;a = c[g >> 2] | 0;c[g >> 2] = c[h >> 2];c[h >> 2] = a;c[b >> 2] = c[e >> 2];return; + }function Qw(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;b = c[a + 4 >> 2] | 0;d = a + 8 | 0;e = c[d >> 2] | 0;if ((e | 0) != (b | 0)) c[d >> 2] = e + (~((e + -4 - b | 0) >>> 2) << 2);a = c[a >> 2] | 0;if (a | 0) sC(a);return; + }function Rw(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0;c[a + 12 >> 2] = 0;c[a + 16 >> 2] = e;do if (b) { + if (b >>> 0 > 1073741823) Ta();else { + f = qC(b << 2) | 0;break; + } + } else f = 0; while (0);c[a >> 2] = f;e = f + (d << 2) | 0;c[a + 8 >> 2] = e;c[a + 4 >> 2] = e;c[a + 12 >> 2] = f + (b << 2);return; + }function Sw(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0;e = c[a >> 2] | 0;h = a + 4 | 0;g = b + 4 | 0;f = (c[h >> 2] | 0) - e | 0;d = (c[g >> 2] | 0) + (0 - (f >> 2) << 2) | 0;c[g >> 2] = d;if ((f | 0) > 0) { + BC(d | 0, e | 0, f | 0) | 0;e = g;d = c[g >> 2] | 0; + } else e = g;g = c[a >> 2] | 0;c[a >> 2] = d;c[e >> 2] = g;g = b + 8 | 0;f = c[h >> 2] | 0;c[h >> 2] = c[g >> 2];c[g >> 2] = f;g = a + 8 | 0;h = b + 12 | 0;a = c[g >> 2] | 0;c[g >> 2] = c[h >> 2];c[h >> 2] = a;c[b >> 2] = c[e >> 2];return; + }function Tw(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;b = c[a + 4 >> 2] | 0;d = a + 8 | 0;e = c[d >> 2] | 0;if ((e | 0) != (b | 0)) c[d >> 2] = e + (~((e + -4 - b | 0) >>> 2) << 2);a = c[a >> 2] | 0;if (a | 0) sC(a);return; + }function Uw(a, b, d, e, f) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;f = f | 0;var g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0, + o = 0, + p = 0, + q = 0, + r = 0;r = l;l = l + 32 | 0;m = r + 20 | 0;n = r + 12 | 0;k = r + 16 | 0;o = r + 4 | 0;p = r;q = r + 8 | 0;i = Wv() | 0;g = c[i >> 2] | 0;h = c[g >> 2] | 0;if (h | 0) { + j = c[i + 8 >> 2] | 0;i = c[i + 4 >> 2] | 0;while (1) { + OA(m, h);Vw(a, m, i, j);g = g + 4 | 0;h = c[g >> 2] | 0;if (!h) break;else { + j = j + 1 | 0;i = i + 1 | 0; + } + } + }g = Yv() | 0;h = c[g >> 2] | 0;if (h | 0) do { + OA(m, h);c[n >> 2] = c[g + 4 >> 2];Ww(b, m, n);g = g + 8 | 0;h = c[g >> 2] | 0; + } while ((h | 0) != 0);g = c[(Iv() | 0) >> 2] | 0;if (g | 0) do { + b = c[g + 4 >> 2] | 0;OA(m, c[(Xw(b) | 0) >> 2] | 0);c[n >> 2] = Tv(b) | 0;Yw(d, m, n);g = c[g >> 2] | 0; + } while ((g | 0) != 0);OA(k, 0);g = Vv() | 0;c[m >> 2] = c[k >> 2];$v(m, g, f);g = c[(Iv() | 0) >> 2] | 0;if (g | 0) { + a = m + 4 | 0;b = m + 8 | 0;d = m + 8 | 0;do { + j = c[g + 4 >> 2] | 0;OA(n, c[(Xw(j) | 0) >> 2] | 0);_w(o, Zw(j) | 0);h = c[o >> 2] | 0;if (h | 0) { + c[m >> 2] = 0;c[a >> 2] = 0;c[b >> 2] = 0;do { + OA(p, c[(Xw(c[h + 4 >> 2] | 0) | 0) >> 2] | 0);i = c[a >> 2] | 0;if (i >>> 0 < (c[d >> 2] | 0) >>> 0) { + c[i >> 2] = c[p >> 2];c[a >> 2] = (c[a >> 2] | 0) + 4; + } else ew(m, p);h = c[h >> 2] | 0; + } while ((h | 0) != 0);$w(e, n, m);kw(m); + }c[q >> 2] = c[n >> 2];k = ax(j) | 0;c[m >> 2] = c[q >> 2];$v(m, k, f);Wi(o);g = c[g >> 2] | 0; + } while ((g | 0) != 0); + }l = r;return; + }function Vw(a, b, c, d) { + a = a | 0;b = b | 0;c = c | 0;d = d | 0;ox(a, b, c, d);return; + }function Ww(a, b, c) { + a = a | 0;b = b | 0;c = c | 0;nx(a, b, c);return; + }function Xw(a) { + a = a | 0;return a | 0; + }function Yw(a, b, c) { + a = a | 0;b = b | 0;c = c | 0;ix(a, b, c);return; + }function Zw(a) { + a = a | 0;return a + 16 | 0; + }function _w(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0;g = l;l = l + 16 | 0;f = g + 8 | 0;d = g;c[a >> 2] = 0;e = c[b >> 2] | 0;c[f >> 2] = e;c[d >> 2] = a;d = gx(d) | 0;if (e | 0) { + e = qC(12) | 0;h = (hx(f) | 0) + 4 | 0;a = c[h + 4 >> 2] | 0;b = e + 4 | 0;c[b >> 2] = c[h >> 2];c[b + 4 >> 2] = a;b = c[c[f >> 2] >> 2] | 0;c[f >> 2] = b;if (!b) a = e;else { + b = e;while (1) { + a = qC(12) | 0;j = (hx(f) | 0) + 4 | 0;i = c[j + 4 >> 2] | 0;h = a + 4 | 0;c[h >> 2] = c[j >> 2];c[h + 4 >> 2] = i;c[b >> 2] = a;h = c[c[f >> 2] >> 2] | 0;c[f >> 2] = h;if (!h) break;else b = a; + } + }c[a >> 2] = c[d >> 2];c[d >> 2] = e; + }l = g;return; + }function $w(a, b, c) { + a = a | 0;b = b | 0;c = c | 0;bx(a, b, c);return; + }function ax(a) { + a = a | 0;return a + 24 | 0; + }function bx(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0;e = l;l = l + 32 | 0;h = e + 24 | 0;f = e + 16 | 0;i = e + 12 | 0;g = e;UA(f);a = Sg(a) | 0;c[i >> 2] = c[b >> 2];mw(g, d);c[h >> 2] = c[i >> 2];cx(a, h, g);kw(g);WA(f);l = e;return; + }function cx(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0;e = l;l = l + 32 | 0;h = e + 16 | 0;i = e + 12 | 0;f = e;g = Vg(dx() | 0) | 0;c[i >> 2] = c[b >> 2];c[h >> 2] = c[i >> 2];b = qw(h) | 0;c[f >> 2] = c[d >> 2];h = d + 4 | 0;c[f + 4 >> 2] = c[h >> 2];i = d + 8 | 0;c[f + 8 >> 2] = c[i >> 2];c[i >> 2] = 0;c[h >> 2] = 0;c[d >> 2] = 0;xa(0, g | 0, a | 0, b | 0, tw(f) | 0) | 0;kw(f);l = e;return; + }function dx() { + var b = 0;if (!(a[7976] | 0)) { + ex(10720);b = 7976;c[b >> 2] = 1;c[b + 4 >> 2] = 0; + }return 10720; + }function ex(a) { + a = a | 0;fh(a, fx() | 0, 2);return; + }function fx() { + return 1732; + }function gx(a) { + a = a | 0;return c[a >> 2] | 0; + }function hx(a) { + a = a | 0;return c[a >> 2] | 0; + }function ix(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0;e = l;l = l + 32 | 0;g = e + 16 | 0;f = e + 8 | 0;h = e;UA(f);a = Sg(a) | 0;c[h >> 2] = c[b >> 2];d = c[d >> 2] | 0;c[g >> 2] = c[h >> 2];jx(a, g, d);WA(f);l = e;return; + }function jx(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0;e = l;l = l + 16 | 0;g = e + 4 | 0;h = e;f = Vg(kx() | 0) | 0;c[h >> 2] = c[b >> 2];c[g >> 2] = c[h >> 2];b = qw(g) | 0;xa(0, f | 0, a | 0, b | 0, rw(d) | 0) | 0;l = e;return; + }function kx() { + var b = 0;if (!(a[7984] | 0)) { + lx(10732);b = 7984;c[b >> 2] = 1;c[b + 4 >> 2] = 0; + }return 10732; + }function lx(a) { + a = a | 0;fh(a, mx() | 0, 2);return; + }function mx() { + return 1744; + }function nx(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0;e = l;l = l + 32 | 0;g = e + 16 | 0;f = e + 8 | 0;h = e;UA(f);a = Sg(a) | 0;c[h >> 2] = c[b >> 2];d = c[d >> 2] | 0;c[g >> 2] = c[h >> 2];jx(a, g, d);WA(f);l = e;return; + }function ox(b, d, e, f) { + b = b | 0;d = d | 0;e = e | 0;f = f | 0;var g = 0, + h = 0, + i = 0, + j = 0;g = l;l = l + 32 | 0;i = g + 16 | 0;h = g + 8 | 0;j = g;UA(h);b = Sg(b) | 0;c[j >> 2] = c[d >> 2];e = a[e >> 0] | 0;f = a[f >> 0] | 0;c[i >> 2] = c[j >> 2];px(b, i, e, f);WA(h);l = g;return; + }function px(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0, + h = 0, + i = 0;f = l;l = l + 16 | 0;h = f + 4 | 0;i = f;g = Vg(qx() | 0) | 0;c[i >> 2] = c[b >> 2];c[h >> 2] = c[i >> 2];b = qw(h) | 0;d = rx(d) | 0;$a(0, g | 0, a | 0, b | 0, d | 0, rx(e) | 0) | 0;l = f;return; + }function qx() { + var b = 0;if (!(a[7992] | 0)) { + tx(10744);b = 7992;c[b >> 2] = 1;c[b + 4 >> 2] = 0; + }return 10744; + }function rx(a) { + a = a | 0;return sx(a) | 0; + }function sx(a) { + a = a | 0;return a & 255 | 0; + }function tx(a) { + a = a | 0;fh(a, ux() | 0, 3);return; + }function ux() { + return 1756; + }function vx(b, d, e) { + b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0, + o = 0, + p = 0;p = l;l = l + 32 | 0;j = p + 8 | 0;k = p + 4 | 0;m = p + 20 | 0;n = p;mk(b, 0);f = QA(d) | 0;c[j >> 2] = 0;o = j + 4 | 0;c[o >> 2] = 0;c[j + 8 >> 2] = 0;switch (f << 24 >> 24) {case 0: + { + a[m >> 0] = 0;wx(k, e, m);xx(b, k) | 0;wf(k);break; + }case 8: + { + o = PA(d) | 0;a[m >> 0] = 8;OA(n, c[o + 4 >> 2] | 0);yx(k, e, m, n, o + 8 | 0);xx(b, k) | 0;wf(k);break; + }case 9: + { + h = PA(d) | 0;d = c[h + 4 >> 2] | 0;if (d | 0) { + i = j + 8 | 0;g = h + 12 | 0;while (1) { + d = d + -1 | 0;OA(k, c[g >> 2] | 0);f = c[o >> 2] | 0;if (f >>> 0 < (c[i >> 2] | 0) >>> 0) { + c[f >> 2] = c[k >> 2];c[o >> 2] = (c[o >> 2] | 0) + 4; + } else ew(j, k);if (!d) break;else g = g + 4 | 0; + } + }a[m >> 0] = 9;OA(n, c[h + 8 >> 2] | 0);zx(k, e, m, n, j);xx(b, k) | 0;wf(k);break; + }default: + { + o = PA(d) | 0;a[m >> 0] = f;OA(n, c[o + 4 >> 2] | 0);Ax(k, e, m, n);xx(b, k) | 0;wf(k); + }}kw(j);l = p;return; + }function wx(b, c, d) { + b = b | 0;c = c | 0;d = d | 0;var e = 0, + f = 0;e = l;l = l + 16 | 0;f = e;UA(f);c = Sg(c) | 0;Ox(b, c, a[d >> 0] | 0);WA(f);l = e;return; + }function xx(a, b) { + a = a | 0;b = b | 0;var d = 0;d = c[a >> 2] | 0;if (d | 0) ab(d | 0);c[a >> 2] = c[b >> 2];c[b >> 2] = 0;return a | 0; + }function yx(b, d, e, f, g) { + b = b | 0;d = d | 0;e = e | 0;f = f | 0;g = g | 0;var h = 0, + i = 0, + j = 0, + k = 0;h = l;l = l + 32 | 0;j = h + 16 | 0;i = h + 8 | 0;k = h;UA(i);d = Sg(d) | 0;e = a[e >> 0] | 0;c[k >> 2] = c[f >> 2];g = c[g >> 2] | 0;c[j >> 2] = c[k >> 2];Kx(b, d, e, j, g);WA(i);l = h;return; + }function zx(b, d, e, f, g) { + b = b | 0;d = d | 0;e = e | 0;f = f | 0;g = g | 0;var h = 0, + i = 0, + j = 0, + k = 0, + m = 0;h = l;l = l + 32 | 0;k = h + 24 | 0;i = h + 16 | 0;m = h + 12 | 0;j = h;UA(i);d = Sg(d) | 0;e = a[e >> 0] | 0;c[m >> 2] = c[f >> 2];mw(j, g);c[k >> 2] = c[m >> 2];Gx(b, d, e, k, j);kw(j);WA(i);l = h;return; + }function Ax(b, d, e, f) { + b = b | 0;d = d | 0;e = e | 0;f = f | 0;var g = 0, + h = 0, + i = 0, + j = 0;g = l;l = l + 32 | 0;i = g + 16 | 0;h = g + 8 | 0;j = g;UA(h);d = Sg(d) | 0;e = a[e >> 0] | 0;c[j >> 2] = c[f >> 2];c[i >> 2] = c[j >> 2];Bx(b, d, e, i);WA(h);l = g;return; + }function Bx(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0, + h = 0, + i = 0;f = l;l = l + 16 | 0;g = f + 4 | 0;i = f;h = Vg(Cx() | 0) | 0;d = rx(d) | 0;c[i >> 2] = c[e >> 2];c[g >> 2] = c[i >> 2];Dx(a, xa(0, h | 0, b | 0, d | 0, qw(g) | 0) | 0);l = f;return; + }function Cx() { + var b = 0;if (!(a[8e3] | 0)) { + Ex(10756);b = 8e3;c[b >> 2] = 1;c[b + 4 >> 2] = 0; + }return 10756; + }function Dx(a, b) { + a = a | 0;b = b | 0;mk(a, b);return; + }function Ex(a) { + a = a | 0;fh(a, Fx() | 0, 2);return; + }function Fx() { + return 1772; + }function Gx(a, b, d, e, f) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;f = f | 0;var g = 0, + h = 0, + i = 0, + j = 0, + k = 0;g = l;l = l + 32 | 0;j = g + 16 | 0;k = g + 12 | 0;h = g;i = Vg(Hx() | 0) | 0;d = rx(d) | 0;c[k >> 2] = c[e >> 2];c[j >> 2] = c[k >> 2];e = qw(j) | 0;c[h >> 2] = c[f >> 2];j = f + 4 | 0;c[h + 4 >> 2] = c[j >> 2];k = f + 8 | 0;c[h + 8 >> 2] = c[k >> 2];c[k >> 2] = 0;c[j >> 2] = 0;c[f >> 2] = 0;Dx(a, $a(0, i | 0, b | 0, d | 0, e | 0, tw(h) | 0) | 0);kw(h);l = g;return; + }function Hx() { + var b = 0;if (!(a[8008] | 0)) { + Ix(10768);b = 8008;c[b >> 2] = 1;c[b + 4 >> 2] = 0; + }return 10768; + }function Ix(a) { + a = a | 0;fh(a, Jx() | 0, 3);return; + }function Jx() { + return 1784; + }function Kx(a, b, d, e, f) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;f = f | 0;var g = 0, + h = 0, + i = 0, + j = 0;g = l;l = l + 16 | 0;i = g + 4 | 0;j = g;h = Vg(Lx() | 0) | 0;d = rx(d) | 0;c[j >> 2] = c[e >> 2];c[i >> 2] = c[j >> 2];e = qw(i) | 0;Dx(a, $a(0, h | 0, b | 0, d | 0, e | 0, sw(f) | 0) | 0);l = g;return; + }function Lx() { + var b = 0;if (!(a[8016] | 0)) { + Mx(10780);b = 8016;c[b >> 2] = 1;c[b + 4 >> 2] = 0; + }return 10780; + }function Mx(a) { + a = a | 0;fh(a, Nx() | 0, 3);return; + }function Nx() { + return 1800; + }function Ox(a, b, c) { + a = a | 0;b = b | 0;c = c | 0;var d = 0;d = Vg(Px() | 0) | 0;Dx(a, bb(0, d | 0, b | 0, rx(c) | 0) | 0);return; + }function Px() { + var b = 0;if (!(a[8024] | 0)) { + Qx(10792);b = 8024;c[b >> 2] = 1;c[b + 4 >> 2] = 0; + }return 10792; + }function Qx(a) { + a = a | 0;fh(a, Rx() | 0, 1);return; + }function Rx() { + return 1816; + }function Sx() { + Tx();Ux();Vx();return; + }function Tx() { + c[2702] = rC(65536) | 0;return; + }function Ux() { + qy(10856);return; + }function Vx() { + Wx(10816);return; + }function Wx(a) { + a = a | 0;Xx(a, 5044);Yx(a) | 0;return; + }function Xx(a, b) { + a = a | 0;b = b | 0;var d = 0;d = zw() | 0;c[a >> 2] = d;ky(d, b);Hv(c[a >> 2] | 0);return; + }function Yx(a) { + a = a | 0;var b = 0;b = c[a >> 2] | 0;At(b, Zx() | 0);return a | 0; + }function Zx() { + var b = 0;if (!(a[8032] | 0)) { + _x(10820);Ha(64, 10820, o | 0) | 0;b = 8032;c[b >> 2] = 1;c[b + 4 >> 2] = 0; + }if (!(si(10820) | 0)) _x(10820);return 10820; + }function _x(a) { + a = a | 0;by(a);Gt(a, 25);return; + }function $x(a) { + a = a | 0;ay(a + 24 | 0);return; + }function ay(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;d = c[a >> 2] | 0;e = d;if (d | 0) { + a = a + 4 | 0;b = c[a >> 2] | 0;if ((b | 0) != (d | 0)) c[a >> 2] = b + (~((b + -8 - e | 0) >>> 3) << 3);sC(d); + }return; + }function by(a) { + a = a | 0;var b = 0;b = vi() | 0;yi(a, 5, 18, b, gy() | 0, 1);c[a + 24 >> 2] = 0;c[a + 28 >> 2] = 0;c[a + 32 >> 2] = 0;return; + }function cy(a, b) { + a = a | 0;b = b | 0;dy(a, b);return; + }function dy(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0;d = l;l = l + 16 | 0;e = d;f = d + 4 | 0;c[e >> 2] = Hk(f, b) | 0;ey(a, e);l = d;return; + }function ey(b, d) { + b = b | 0;d = d | 0;fy(b + 4 | 0, c[d >> 2] | 0);a[b + 8 >> 0] = 1;return; + }function fy(a, b) { + a = a | 0;b = b | 0;c[a >> 2] = b;return; + }function gy() { + return 1824; + }function hy(a) { + a = a | 0;return iy(a) | 0; + }function iy(a) { + a = a | 0;var b = 0, + d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0;d = l;l = l + 16 | 0;f = d + 4 | 0;h = d;e = jy(8) | 0;b = e;i = qC(4) | 0;fy(i, Hk(f, a) | 0);g = b + 4 | 0;c[g >> 2] = i;a = qC(8) | 0;g = c[g >> 2] | 0;c[h >> 2] = 0;c[f >> 2] = c[h >> 2];Bw(a, g, f);c[e >> 2] = a;l = d;return b | 0; + }function jy(a) { + a = a | 0;var b = 0, + d = 0;a = a + 7 & -8;if (a >>> 0 <= 32768 ? (b = c[2701] | 0, a >>> 0 <= (65536 - b | 0) >>> 0) : 0) { + d = (c[2702] | 0) + b | 0;c[2701] = b + a;a = d; + } else { + a = rC(a + 8 | 0) | 0;c[a >> 2] = c[2703];c[2703] = a;a = a + 8 | 0; + }return a | 0; + }function ky(a, b) { + a = a | 0;b = b | 0;c[a >> 2] = ly() | 0;c[a + 4 >> 2] = my() | 0;c[a + 12 >> 2] = b;c[a + 8 >> 2] = ny() | 0;c[a + 32 >> 2] = 9;return; + }function ly() { + return 11744; + }function my() { + return 1832; + }function ny() { + return cu() | 0; + }function oy(a, b, c, d) { + a = a | 0;b = b | 0;c = c | 0;d = d | 0;if ((jr(d, 896) | 0) == 512) { + if (c | 0) { + py(c);sC(c); + } + } else if (b | 0) sC(b);return; + }function py(a) { + a = a | 0;a = c[a + 4 >> 2] | 0;if (a | 0) oC(a);return; + }function qy(a) { + a = a | 0;ry(a, 5052);sy(a) | 0;ty(a, 5058, 26) | 0;uy(a, 5069, 1) | 0;vy(a, 5077, 10) | 0;wy(a, 5087, 19) | 0;yy(a, 5094, 27) | 0;return; + }function ry(a, b) { + a = a | 0;b = b | 0;var d = 0;d = GA() | 0;c[a >> 2] = d;HA(d, b);Hv(c[a >> 2] | 0);return; + }function sy(a) { + a = a | 0;var b = 0;b = c[a >> 2] | 0;At(b, rA() | 0);return a | 0; + }function ty(a, b, c) { + a = a | 0;b = b | 0;c = c | 0;Yz(a, ai(b) | 0, c, 0);return a | 0; + }function uy(a, b, c) { + a = a | 0;b = b | 0;c = c | 0;Gz(a, ai(b) | 0, c, 0);return a | 0; + }function vy(a, b, c) { + a = a | 0;b = b | 0;c = c | 0;hz(a, ai(b) | 0, c, 0);return a | 0; + }function wy(a, b, c) { + a = a | 0;b = b | 0;c = c | 0;Ry(a, ai(b) | 0, c, 0);return a | 0; + }function xy(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0;a: while (1) { + d = c[2703] | 0;while (1) { + if ((d | 0) == (b | 0)) break a;e = c[d >> 2] | 0;c[2703] = e;if (!d) d = e;else break; + }sC(d); + }c[2701] = a;return; + }function yy(a, b, c) { + a = a | 0;b = b | 0;c = c | 0;zy(a, ai(b) | 0, c, 0);return a | 0; + }function zy(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0;g = c[a >> 2] | 0;f = Ay() | 0;a = By(d) | 0;fi(g, b, f, a, Cy(d, e) | 0, e);return; + }function Ay() { + var b = 0, + d = 0;if (!(a[8040] | 0)) { + Jy(10860);Ha(65, 10860, o | 0) | 0;d = 8040;c[d >> 2] = 1;c[d + 4 >> 2] = 0; + }if (!(si(10860) | 0)) { + b = 10860;d = b + 36 | 0;do { + c[b >> 2] = 0;b = b + 4 | 0; + } while ((b | 0) < (d | 0));Jy(10860); + }return 10860; + }function By(a) { + a = a | 0;return a | 0; + }function Cy(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0;i = l;l = l + 16 | 0;f = i;g = i + 4 | 0;c[f >> 2] = a;j = Ay() | 0;h = j + 24 | 0;b = ji(b, 4) | 0;c[g >> 2] = b;d = j + 28 | 0;e = c[d >> 2] | 0;if (e >>> 0 < (c[j + 32 >> 2] | 0) >>> 0) { + Dy(e, a, b);b = (c[d >> 2] | 0) + 8 | 0;c[d >> 2] = b; + } else { + Ey(h, f, g);b = c[d >> 2] | 0; + }l = i;return (b - (c[h >> 2] | 0) >> 3) + -1 | 0; + }function Dy(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;c[a >> 2] = b;c[a + 4 >> 2] = d;return; + }function Ey(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0;i = l;l = l + 32 | 0;f = i;g = a + 4 | 0;h = ((c[g >> 2] | 0) - (c[a >> 2] | 0) >> 3) + 1 | 0;e = Fy(a) | 0;if (e >>> 0 < h >>> 0) jC(a);else { + j = c[a >> 2] | 0;m = (c[a + 8 >> 2] | 0) - j | 0;k = m >> 2;Gy(f, m >> 3 >>> 0 < e >>> 1 >>> 0 ? k >>> 0 < h >>> 0 ? h : k : e, (c[g >> 2] | 0) - j >> 3, a + 8 | 0);h = f + 8 | 0;Dy(c[h >> 2] | 0, c[b >> 2] | 0, c[d >> 2] | 0);c[h >> 2] = (c[h >> 2] | 0) + 8;Hy(a, f);Iy(f);l = i;return; + } + }function Fy(a) { + a = a | 0;return 536870911; + }function Gy(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0;c[a + 12 >> 2] = 0;c[a + 16 >> 2] = e;do if (b) { + if (b >>> 0 > 536870911) Ta();else { + f = qC(b << 3) | 0;break; + } + } else f = 0; while (0);c[a >> 2] = f;e = f + (d << 3) | 0;c[a + 8 >> 2] = e;c[a + 4 >> 2] = e;c[a + 12 >> 2] = f + (b << 3);return; + }function Hy(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0;e = c[a >> 2] | 0;h = a + 4 | 0;g = b + 4 | 0;f = (c[h >> 2] | 0) - e | 0;d = (c[g >> 2] | 0) + (0 - (f >> 3) << 3) | 0;c[g >> 2] = d;if ((f | 0) > 0) { + BC(d | 0, e | 0, f | 0) | 0;e = g;d = c[g >> 2] | 0; + } else e = g;g = c[a >> 2] | 0;c[a >> 2] = d;c[e >> 2] = g;g = b + 8 | 0;f = c[h >> 2] | 0;c[h >> 2] = c[g >> 2];c[g >> 2] = f;g = a + 8 | 0;h = b + 12 | 0;a = c[g >> 2] | 0;c[g >> 2] = c[h >> 2];c[h >> 2] = a;c[b >> 2] = c[e >> 2];return; + }function Iy(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;b = c[a + 4 >> 2] | 0;d = a + 8 | 0;e = c[d >> 2] | 0;if ((e | 0) != (b | 0)) c[d >> 2] = e + (~((e + -8 - b | 0) >>> 3) << 3);a = c[a >> 2] | 0;if (a | 0) sC(a);return; + }function Jy(a) { + a = a | 0;My(a);return; + }function Ky(a) { + a = a | 0;Ly(a + 24 | 0);return; + }function Ly(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;d = c[a >> 2] | 0;e = d;if (d | 0) { + a = a + 4 | 0;b = c[a >> 2] | 0;if ((b | 0) != (d | 0)) c[a >> 2] = b + (~((b + -8 - e | 0) >>> 3) << 3);sC(d); + }return; + }function My(a) { + a = a | 0;var b = 0;b = vi() | 0;yi(a, 1, 11, b, Ny() | 0, 2);c[a + 24 >> 2] = 0;c[a + 28 >> 2] = 0;c[a + 32 >> 2] = 0;return; + }function Ny() { + return 1840; + }function Oy(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;Qy(c[(Py(a) | 0) >> 2] | 0, b, d);return; + }function Py(a) { + a = a | 0;return (c[(Ay() | 0) + 24 >> 2] | 0) + (a << 3) | 0; + }function Qy(a, b, c) { + a = a | 0;b = b | 0;c = c | 0;var d = 0, + e = 0, + f = 0;d = l;l = l + 16 | 0;f = d + 1 | 0;e = d;b = Hk(f, b) | 0;c = Hk(e, c) | 0;ob[a & 31](b, c);l = d;return; + }function Ry(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0;g = c[a >> 2] | 0;f = Sy() | 0;a = Ty(d) | 0;fi(g, b, f, a, Uy(d, e) | 0, e);return; + }function Sy() { + var b = 0, + d = 0;if (!(a[8048] | 0)) { + $y(10896);Ha(66, 10896, o | 0) | 0;d = 8048;c[d >> 2] = 1;c[d + 4 >> 2] = 0; + }if (!(si(10896) | 0)) { + b = 10896;d = b + 36 | 0;do { + c[b >> 2] = 0;b = b + 4 | 0; + } while ((b | 0) < (d | 0));$y(10896); + }return 10896; + }function Ty(a) { + a = a | 0;return a | 0; + }function Uy(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0;i = l;l = l + 16 | 0;f = i;g = i + 4 | 0;c[f >> 2] = a;j = Sy() | 0;h = j + 24 | 0;b = ji(b, 4) | 0;c[g >> 2] = b;d = j + 28 | 0;e = c[d >> 2] | 0;if (e >>> 0 < (c[j + 32 >> 2] | 0) >>> 0) { + Vy(e, a, b);b = (c[d >> 2] | 0) + 8 | 0;c[d >> 2] = b; + } else { + Wy(h, f, g);b = c[d >> 2] | 0; + }l = i;return (b - (c[h >> 2] | 0) >> 3) + -1 | 0; + }function Vy(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;c[a >> 2] = b;c[a + 4 >> 2] = d;return; + }function Wy(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0;i = l;l = l + 32 | 0;f = i;g = a + 4 | 0;h = ((c[g >> 2] | 0) - (c[a >> 2] | 0) >> 3) + 1 | 0;e = Xy(a) | 0;if (e >>> 0 < h >>> 0) jC(a);else { + j = c[a >> 2] | 0;m = (c[a + 8 >> 2] | 0) - j | 0;k = m >> 2;Yy(f, m >> 3 >>> 0 < e >>> 1 >>> 0 ? k >>> 0 < h >>> 0 ? h : k : e, (c[g >> 2] | 0) - j >> 3, a + 8 | 0);h = f + 8 | 0;Vy(c[h >> 2] | 0, c[b >> 2] | 0, c[d >> 2] | 0);c[h >> 2] = (c[h >> 2] | 0) + 8;Zy(a, f);_y(f);l = i;return; + } + }function Xy(a) { + a = a | 0;return 536870911; + }function Yy(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0;c[a + 12 >> 2] = 0;c[a + 16 >> 2] = e;do if (b) { + if (b >>> 0 > 536870911) Ta();else { + f = qC(b << 3) | 0;break; + } + } else f = 0; while (0);c[a >> 2] = f;e = f + (d << 3) | 0;c[a + 8 >> 2] = e;c[a + 4 >> 2] = e;c[a + 12 >> 2] = f + (b << 3);return; + }function Zy(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0;e = c[a >> 2] | 0;h = a + 4 | 0;g = b + 4 | 0;f = (c[h >> 2] | 0) - e | 0;d = (c[g >> 2] | 0) + (0 - (f >> 3) << 3) | 0;c[g >> 2] = d;if ((f | 0) > 0) { + BC(d | 0, e | 0, f | 0) | 0;e = g;d = c[g >> 2] | 0; + } else e = g;g = c[a >> 2] | 0;c[a >> 2] = d;c[e >> 2] = g;g = b + 8 | 0;f = c[h >> 2] | 0;c[h >> 2] = c[g >> 2];c[g >> 2] = f;g = a + 8 | 0;h = b + 12 | 0;a = c[g >> 2] | 0;c[g >> 2] = c[h >> 2];c[h >> 2] = a;c[b >> 2] = c[e >> 2];return; + }function _y(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;b = c[a + 4 >> 2] | 0;d = a + 8 | 0;e = c[d >> 2] | 0;if ((e | 0) != (b | 0)) c[d >> 2] = e + (~((e + -8 - b | 0) >>> 3) << 3);a = c[a >> 2] | 0;if (a | 0) sC(a);return; + }function $y(a) { + a = a | 0;cz(a);return; + }function az(a) { + a = a | 0;bz(a + 24 | 0);return; + }function bz(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;d = c[a >> 2] | 0;e = d;if (d | 0) { + a = a + 4 | 0;b = c[a >> 2] | 0;if ((b | 0) != (d | 0)) c[a >> 2] = b + (~((b + -8 - e | 0) >>> 3) << 3);sC(d); + }return; + }function cz(a) { + a = a | 0;var b = 0;b = vi() | 0;yi(a, 1, 11, b, dz() | 0, 1);c[a + 24 >> 2] = 0;c[a + 28 >> 2] = 0;c[a + 32 >> 2] = 0;return; + }function dz() { + return 1852; + }function ez(a, b) { + a = a | 0;b = b | 0;return gz(c[(fz(a) | 0) >> 2] | 0, b) | 0; + }function fz(a) { + a = a | 0;return (c[(Sy() | 0) + 24 >> 2] | 0) + (a << 3) | 0; + }function gz(a, b) { + a = a | 0;b = b | 0;var c = 0, + d = 0;c = l;l = l + 16 | 0;d = c;b = Hk(d, b) | 0;b = ul(pb[a & 31](b) | 0) | 0;l = c;return b | 0; + }function hz(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0;g = c[a >> 2] | 0;f = iz() | 0;a = jz(d) | 0;fi(g, b, f, a, kz(d, e) | 0, e);return; + }function iz() { + var b = 0, + d = 0;if (!(a[8056] | 0)) { + rz(10932);Ha(67, 10932, o | 0) | 0;d = 8056;c[d >> 2] = 1;c[d + 4 >> 2] = 0; + }if (!(si(10932) | 0)) { + b = 10932;d = b + 36 | 0;do { + c[b >> 2] = 0;b = b + 4 | 0; + } while ((b | 0) < (d | 0));rz(10932); + }return 10932; + }function jz(a) { + a = a | 0;return a | 0; + }function kz(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0;i = l;l = l + 16 | 0;f = i;g = i + 4 | 0;c[f >> 2] = a;j = iz() | 0;h = j + 24 | 0;b = ji(b, 4) | 0;c[g >> 2] = b;d = j + 28 | 0;e = c[d >> 2] | 0;if (e >>> 0 < (c[j + 32 >> 2] | 0) >>> 0) { + lz(e, a, b);b = (c[d >> 2] | 0) + 8 | 0;c[d >> 2] = b; + } else { + mz(h, f, g);b = c[d >> 2] | 0; + }l = i;return (b - (c[h >> 2] | 0) >> 3) + -1 | 0; + }function lz(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;c[a >> 2] = b;c[a + 4 >> 2] = d;return; + }function mz(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0;i = l;l = l + 32 | 0;f = i;g = a + 4 | 0;h = ((c[g >> 2] | 0) - (c[a >> 2] | 0) >> 3) + 1 | 0;e = nz(a) | 0;if (e >>> 0 < h >>> 0) jC(a);else { + j = c[a >> 2] | 0;m = (c[a + 8 >> 2] | 0) - j | 0;k = m >> 2;oz(f, m >> 3 >>> 0 < e >>> 1 >>> 0 ? k >>> 0 < h >>> 0 ? h : k : e, (c[g >> 2] | 0) - j >> 3, a + 8 | 0);h = f + 8 | 0;lz(c[h >> 2] | 0, c[b >> 2] | 0, c[d >> 2] | 0);c[h >> 2] = (c[h >> 2] | 0) + 8;pz(a, f);qz(f);l = i;return; + } + }function nz(a) { + a = a | 0;return 536870911; + }function oz(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0;c[a + 12 >> 2] = 0;c[a + 16 >> 2] = e;do if (b) { + if (b >>> 0 > 536870911) Ta();else { + f = qC(b << 3) | 0;break; + } + } else f = 0; while (0);c[a >> 2] = f;e = f + (d << 3) | 0;c[a + 8 >> 2] = e;c[a + 4 >> 2] = e;c[a + 12 >> 2] = f + (b << 3);return; + }function pz(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0;e = c[a >> 2] | 0;h = a + 4 | 0;g = b + 4 | 0;f = (c[h >> 2] | 0) - e | 0;d = (c[g >> 2] | 0) + (0 - (f >> 3) << 3) | 0;c[g >> 2] = d;if ((f | 0) > 0) { + BC(d | 0, e | 0, f | 0) | 0;e = g;d = c[g >> 2] | 0; + } else e = g;g = c[a >> 2] | 0;c[a >> 2] = d;c[e >> 2] = g;g = b + 8 | 0;f = c[h >> 2] | 0;c[h >> 2] = c[g >> 2];c[g >> 2] = f;g = a + 8 | 0;h = b + 12 | 0;a = c[g >> 2] | 0;c[g >> 2] = c[h >> 2];c[h >> 2] = a;c[b >> 2] = c[e >> 2];return; + }function qz(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;b = c[a + 4 >> 2] | 0;d = a + 8 | 0;e = c[d >> 2] | 0;if ((e | 0) != (b | 0)) c[d >> 2] = e + (~((e + -8 - b | 0) >>> 3) << 3);a = c[a >> 2] | 0;if (a | 0) sC(a);return; + }function rz(a) { + a = a | 0;uz(a);return; + }function sz(a) { + a = a | 0;tz(a + 24 | 0);return; + }function tz(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;d = c[a >> 2] | 0;e = d;if (d | 0) { + a = a + 4 | 0;b = c[a >> 2] | 0;if ((b | 0) != (d | 0)) c[a >> 2] = b + (~((b + -8 - e | 0) >>> 3) << 3);sC(d); + }return; + }function uz(a) { + a = a | 0;var b = 0;b = vi() | 0;yi(a, 1, 7, b, vz() | 0, 2);c[a + 24 >> 2] = 0;c[a + 28 >> 2] = 0;c[a + 32 >> 2] = 0;return; + }function vz() { + return 1860; + }function wz(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;return yz(c[(xz(a) | 0) >> 2] | 0, b, d) | 0; + }function xz(a) { + a = a | 0;return (c[(iz() | 0) + 24 >> 2] | 0) + (a << 3) | 0; + }function yz(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0;e = l;l = l + 32 | 0;h = e + 12 | 0;g = e + 8 | 0;i = e;j = e + 16 | 0;f = e + 4 | 0;Az(i, j, b);ik(f, d);d = jk(f, d) | 0;c[h >> 2] = c[i >> 2];Eb[a & 15](g, h, d);d = Bz(g) | 0;wf(g);kk(f);l = e;return d | 0; + }function Az(a, b, c) { + a = a | 0;b = b | 0;c = c | 0;Cz(a, c);return; + }function Bz(a) { + a = a | 0;return Sg(a) | 0; + }function Cz(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0;f = l;l = l + 16 | 0;d = f;e = b;if (!(e & 1)) c[a >> 2] = c[b >> 2];else { + Dz(d, 0);Ja(e | 0, d | 0) | 0;Ez(a, d);Fz(d); + }l = f;return; + }function Dz(b, d) { + b = b | 0;d = d | 0;ah(b, d);c[b + 4 >> 2] = 0;a[b + 8 >> 0] = 0;return; + }function Ez(a, b) { + a = a | 0;b = b | 0;c[a >> 2] = c[b + 4 >> 2];return; + }function Fz(b) { + b = b | 0;a[b + 8 >> 0] = 0;return; + }function Gz(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0;g = c[a >> 2] | 0;f = Hz() | 0;a = Iz(d) | 0;fi(g, b, f, a, Jz(d, e) | 0, e);return; + }function Hz() { + var b = 0, + d = 0;if (!(a[8064] | 0)) { + Qz(10968);Ha(68, 10968, o | 0) | 0;d = 8064;c[d >> 2] = 1;c[d + 4 >> 2] = 0; + }if (!(si(10968) | 0)) { + b = 10968;d = b + 36 | 0;do { + c[b >> 2] = 0;b = b + 4 | 0; + } while ((b | 0) < (d | 0));Qz(10968); + }return 10968; + }function Iz(a) { + a = a | 0;return a | 0; + }function Jz(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0;i = l;l = l + 16 | 0;f = i;g = i + 4 | 0;c[f >> 2] = a;j = Hz() | 0;h = j + 24 | 0;b = ji(b, 4) | 0;c[g >> 2] = b;d = j + 28 | 0;e = c[d >> 2] | 0;if (e >>> 0 < (c[j + 32 >> 2] | 0) >>> 0) { + Kz(e, a, b);b = (c[d >> 2] | 0) + 8 | 0;c[d >> 2] = b; + } else { + Lz(h, f, g);b = c[d >> 2] | 0; + }l = i;return (b - (c[h >> 2] | 0) >> 3) + -1 | 0; + }function Kz(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;c[a >> 2] = b;c[a + 4 >> 2] = d;return; + }function Lz(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0;i = l;l = l + 32 | 0;f = i;g = a + 4 | 0;h = ((c[g >> 2] | 0) - (c[a >> 2] | 0) >> 3) + 1 | 0;e = Mz(a) | 0;if (e >>> 0 < h >>> 0) jC(a);else { + j = c[a >> 2] | 0;m = (c[a + 8 >> 2] | 0) - j | 0;k = m >> 2;Nz(f, m >> 3 >>> 0 < e >>> 1 >>> 0 ? k >>> 0 < h >>> 0 ? h : k : e, (c[g >> 2] | 0) - j >> 3, a + 8 | 0);h = f + 8 | 0;Kz(c[h >> 2] | 0, c[b >> 2] | 0, c[d >> 2] | 0);c[h >> 2] = (c[h >> 2] | 0) + 8;Oz(a, f);Pz(f);l = i;return; + } + }function Mz(a) { + a = a | 0;return 536870911; + }function Nz(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0;c[a + 12 >> 2] = 0;c[a + 16 >> 2] = e;do if (b) { + if (b >>> 0 > 536870911) Ta();else { + f = qC(b << 3) | 0;break; + } + } else f = 0; while (0);c[a >> 2] = f;e = f + (d << 3) | 0;c[a + 8 >> 2] = e;c[a + 4 >> 2] = e;c[a + 12 >> 2] = f + (b << 3);return; + }function Oz(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0;e = c[a >> 2] | 0;h = a + 4 | 0;g = b + 4 | 0;f = (c[h >> 2] | 0) - e | 0;d = (c[g >> 2] | 0) + (0 - (f >> 3) << 3) | 0;c[g >> 2] = d;if ((f | 0) > 0) { + BC(d | 0, e | 0, f | 0) | 0;e = g;d = c[g >> 2] | 0; + } else e = g;g = c[a >> 2] | 0;c[a >> 2] = d;c[e >> 2] = g;g = b + 8 | 0;f = c[h >> 2] | 0;c[h >> 2] = c[g >> 2];c[g >> 2] = f;g = a + 8 | 0;h = b + 12 | 0;a = c[g >> 2] | 0;c[g >> 2] = c[h >> 2];c[h >> 2] = a;c[b >> 2] = c[e >> 2];return; + }function Pz(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;b = c[a + 4 >> 2] | 0;d = a + 8 | 0;e = c[d >> 2] | 0;if ((e | 0) != (b | 0)) c[d >> 2] = e + (~((e + -8 - b | 0) >>> 3) << 3);a = c[a >> 2] | 0;if (a | 0) sC(a);return; + }function Qz(a) { + a = a | 0;Tz(a);return; + }function Rz(a) { + a = a | 0;Sz(a + 24 | 0);return; + }function Sz(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;d = c[a >> 2] | 0;e = d;if (d | 0) { + a = a + 4 | 0;b = c[a >> 2] | 0;if ((b | 0) != (d | 0)) c[a >> 2] = b + (~((b + -8 - e | 0) >>> 3) << 3);sC(d); + }return; + }function Tz(a) { + a = a | 0;var b = 0;b = vi() | 0;yi(a, 1, 1, b, Uz() | 0, 5);c[a + 24 >> 2] = 0;c[a + 28 >> 2] = 0;c[a + 32 >> 2] = 0;return; + }function Uz() { + return 1872; + }function Vz(a, b, d, e, f, g) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;f = f | 0;g = g | 0;Xz(c[(Wz(a) | 0) >> 2] | 0, b, d, e, f, g);return; + }function Wz(a) { + a = a | 0;return (c[(Hz() | 0) + 24 >> 2] | 0) + (a << 3) | 0; + }function Xz(a, b, c, d, e, f) { + a = a | 0;b = b | 0;c = c | 0;d = d | 0;e = e | 0;f = f | 0;var g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0;g = l;l = l + 32 | 0;h = g + 16 | 0;i = g + 12 | 0;j = g + 8 | 0;k = g + 4 | 0;m = g;ik(h, b);b = jk(h, b) | 0;ik(i, c);c = jk(i, c) | 0;ik(j, d);d = jk(j, d) | 0;ik(k, e);e = jk(k, e) | 0;ik(m, f);f = jk(m, f) | 0;jb[a & 1](b, c, d, e, f);kk(m);kk(k);kk(j);kk(i);kk(h);l = g;return; + }function Yz(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0;g = c[a >> 2] | 0;f = Zz() | 0;a = _z(d) | 0;fi(g, b, f, a, $z(d, e) | 0, e);return; + }function Zz() { + var b = 0, + d = 0;if (!(a[8072] | 0)) { + gA(11004);Ha(69, 11004, o | 0) | 0;d = 8072;c[d >> 2] = 1;c[d + 4 >> 2] = 0; + }if (!(si(11004) | 0)) { + b = 11004;d = b + 36 | 0;do { + c[b >> 2] = 0;b = b + 4 | 0; + } while ((b | 0) < (d | 0));gA(11004); + }return 11004; + }function _z(a) { + a = a | 0;return a | 0; + }function $z(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0;i = l;l = l + 16 | 0;f = i;g = i + 4 | 0;c[f >> 2] = a;j = Zz() | 0;h = j + 24 | 0;b = ji(b, 4) | 0;c[g >> 2] = b;d = j + 28 | 0;e = c[d >> 2] | 0;if (e >>> 0 < (c[j + 32 >> 2] | 0) >>> 0) { + aA(e, a, b);b = (c[d >> 2] | 0) + 8 | 0;c[d >> 2] = b; + } else { + bA(h, f, g);b = c[d >> 2] | 0; + }l = i;return (b - (c[h >> 2] | 0) >> 3) + -1 | 0; + }function aA(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;c[a >> 2] = b;c[a + 4 >> 2] = d;return; + }function bA(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0;i = l;l = l + 32 | 0;f = i;g = a + 4 | 0;h = ((c[g >> 2] | 0) - (c[a >> 2] | 0) >> 3) + 1 | 0;e = cA(a) | 0;if (e >>> 0 < h >>> 0) jC(a);else { + j = c[a >> 2] | 0;m = (c[a + 8 >> 2] | 0) - j | 0;k = m >> 2;dA(f, m >> 3 >>> 0 < e >>> 1 >>> 0 ? k >>> 0 < h >>> 0 ? h : k : e, (c[g >> 2] | 0) - j >> 3, a + 8 | 0);h = f + 8 | 0;aA(c[h >> 2] | 0, c[b >> 2] | 0, c[d >> 2] | 0);c[h >> 2] = (c[h >> 2] | 0) + 8;eA(a, f);fA(f);l = i;return; + } + }function cA(a) { + a = a | 0;return 536870911; + }function dA(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0;c[a + 12 >> 2] = 0;c[a + 16 >> 2] = e;do if (b) { + if (b >>> 0 > 536870911) Ta();else { + f = qC(b << 3) | 0;break; + } + } else f = 0; while (0);c[a >> 2] = f;e = f + (d << 3) | 0;c[a + 8 >> 2] = e;c[a + 4 >> 2] = e;c[a + 12 >> 2] = f + (b << 3);return; + }function eA(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0, + f = 0, + g = 0, + h = 0;e = c[a >> 2] | 0;h = a + 4 | 0;g = b + 4 | 0;f = (c[h >> 2] | 0) - e | 0;d = (c[g >> 2] | 0) + (0 - (f >> 3) << 3) | 0;c[g >> 2] = d;if ((f | 0) > 0) { + BC(d | 0, e | 0, f | 0) | 0;e = g;d = c[g >> 2] | 0; + } else e = g;g = c[a >> 2] | 0;c[a >> 2] = d;c[e >> 2] = g;g = b + 8 | 0;f = c[h >> 2] | 0;c[h >> 2] = c[g >> 2];c[g >> 2] = f;g = a + 8 | 0;h = b + 12 | 0;a = c[g >> 2] | 0;c[g >> 2] = c[h >> 2];c[h >> 2] = a;c[b >> 2] = c[e >> 2];return; + }function fA(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;b = c[a + 4 >> 2] | 0;d = a + 8 | 0;e = c[d >> 2] | 0;if ((e | 0) != (b | 0)) c[d >> 2] = e + (~((e + -8 - b | 0) >>> 3) << 3);a = c[a >> 2] | 0;if (a | 0) sC(a);return; + }function gA(a) { + a = a | 0;jA(a);return; + }function hA(a) { + a = a | 0;iA(a + 24 | 0);return; + }function iA(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;d = c[a >> 2] | 0;e = d;if (d | 0) { + a = a + 4 | 0;b = c[a >> 2] | 0;if ((b | 0) != (d | 0)) c[a >> 2] = b + (~((b + -8 - e | 0) >>> 3) << 3);sC(d); + }return; + }function jA(a) { + a = a | 0;var b = 0;b = vi() | 0;yi(a, 1, 12, b, kA() | 0, 2);c[a + 24 >> 2] = 0;c[a + 28 >> 2] = 0;c[a + 32 >> 2] = 0;return; + }function kA() { + return 1896; + }function lA(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;nA(c[(mA(a) | 0) >> 2] | 0, b, d);return; + }function mA(a) { + a = a | 0;return (c[(Zz() | 0) + 24 >> 2] | 0) + (a << 3) | 0; + }function nA(a, b, c) { + a = a | 0;b = b | 0;c = c | 0;var d = 0, + e = 0, + f = 0;d = l;l = l + 16 | 0;f = d + 4 | 0;e = d;b = pA(f, b) | 0;ik(e, c);c = jk(e, c) | 0;ob[a & 31](b, c);kk(e);l = d;return; + }function pA(a, b) { + a = a | 0;b = b | 0;return qA(b) | 0; + }function qA(a) { + a = a | 0;return a | 0; + }function rA() { + var b = 0;if (!(a[8080] | 0)) { + sA(11040);Ha(70, 11040, o | 0) | 0;b = 8080;c[b >> 2] = 1;c[b + 4 >> 2] = 0; + }if (!(si(11040) | 0)) sA(11040);return 11040; + }function sA(a) { + a = a | 0;vA(a);Gt(a, 71);return; + }function tA(a) { + a = a | 0;uA(a + 24 | 0);return; + }function uA(a) { + a = a | 0;var b = 0, + d = 0, + e = 0;d = c[a >> 2] | 0;e = d;if (d | 0) { + a = a + 4 | 0;b = c[a >> 2] | 0;if ((b | 0) != (d | 0)) c[a >> 2] = b + (~((b + -8 - e | 0) >>> 3) << 3);sC(d); + }return; + }function vA(a) { + a = a | 0;var b = 0;b = vi() | 0;yi(a, 5, 7, b, zA() | 0, 0);c[a + 24 >> 2] = 0;c[a + 28 >> 2] = 0;c[a + 32 >> 2] = 0;return; + }function wA(a) { + a = a | 0;xA(a);return; + }function xA(a) { + a = a | 0;yA(a);return; + }function yA(b) { + b = b | 0;a[b + 8 >> 0] = 1;return; + }function zA() { + return 1936; + }function AA() { + return BA() | 0; + }function BA() { + var a = 0, + b = 0, + d = 0, + e = 0, + f = 0, + g = 0, + h = 0;b = l;l = l + 16 | 0;f = b + 4 | 0;h = b;d = jy(8) | 0;a = d;g = a + 4 | 0;c[g >> 2] = qC(1) | 0;e = qC(8) | 0;g = c[g >> 2] | 0;c[h >> 2] = 0;c[f >> 2] = c[h >> 2];CA(e, g, f);c[d >> 2] = e;l = b;return a | 0; + }function CA(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;c[a >> 2] = b;d = qC(16) | 0;c[d + 4 >> 2] = 0;c[d + 8 >> 2] = 0;c[d >> 2] = 1916;c[d + 12 >> 2] = b;c[a + 4 >> 2] = d;return; + }function DA(a) { + a = a | 0;kC(a);sC(a);return; + }function EA(a) { + a = a | 0;a = c[a + 12 >> 2] | 0;if (a | 0) sC(a);return; + }function FA(a) { + a = a | 0;sC(a);return; + }function GA() { + var b = 0;if (!(a[8088] | 0)) { + NA(11076);Ha(25, 11076, o | 0) | 0;b = 8088;c[b >> 2] = 1;c[b + 4 >> 2] = 0; + }return 11076; + }function HA(a, b) { + a = a | 0;b = b | 0;c[a >> 2] = IA() | 0;c[a + 4 >> 2] = JA() | 0;c[a + 12 >> 2] = b;c[a + 8 >> 2] = KA() | 0;c[a + 32 >> 2] = 10;return; + }function IA() { + return 11745; + }function JA() { + return 1940; + }function KA() { + return lr() | 0; + }function LA(a, b, c, d) { + a = a | 0;b = b | 0;c = c | 0;d = d | 0;if ((jr(d, 896) | 0) == 512) { + if (c | 0) { + MA(c);sC(c); + } + } else if (b | 0) sC(b);return; + }function MA(a) { + a = a | 0;a = c[a + 4 >> 2] | 0;if (a | 0) oC(a);return; + }function NA(a) { + a = a | 0;Zi(a);return; + }function OA(a, b) { + a = a | 0;b = b | 0;c[a >> 2] = b;return; + }function PA(a) { + a = a | 0;return c[a >> 2] | 0; + }function QA(b) { + b = b | 0;return a[c[b >> 2] >> 0] | 0; + }function RA(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0;d = l;l = l + 16 | 0;e = d;c[e >> 2] = c[a >> 2];SA(b, e) | 0;l = d;return; + }function SA(a, b) { + a = a | 0;b = b | 0;var d = 0;d = TA(c[a >> 2] | 0, b) | 0;b = a + 4 | 0;c[(c[b >> 2] | 0) + 8 >> 2] = d;return c[(c[b >> 2] | 0) + 8 >> 2] | 0; + }function TA(a, b) { + a = a | 0;b = b | 0;var d = 0, + e = 0;d = l;l = l + 16 | 0;e = d;UA(e);a = Sg(a) | 0;b = VA(a, c[b >> 2] | 0) | 0;WA(e);l = d;return b | 0; + }function UA(a) { + a = a | 0;c[a >> 2] = c[2701];c[a + 4 >> 2] = c[2703];return; + }function VA(a, b) { + a = a | 0;b = b | 0;var c = 0;c = Vg(XA() | 0) | 0;return bb(0, c | 0, a | 0, sw(b) | 0) | 0; + }function WA(a) { + a = a | 0;xy(c[a >> 2] | 0, c[a + 4 >> 2] | 0);return; + }function XA() { + var b = 0;if (!(a[8096] | 0)) { + YA(11120);b = 8096;c[b >> 2] = 1;c[b + 4 >> 2] = 0; + }return 11120; + }function YA(a) { + a = a | 0;fh(a, ZA() | 0, 1);return; + }function ZA() { + return 1948; + }function _A() { + $A();return; + }function $A() { + var b = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0, + o = 0, + p = 0, + q = 0, + r = 0, + s = 0, + t = 0;s = l;l = l + 16 | 0;o = s + 4 | 0;p = s;Ea(65536, 10804, c[2702] | 0, 10812);f = Wv() | 0;e = c[f >> 2] | 0;b = c[e >> 2] | 0;if (b | 0) { + g = c[f + 8 >> 2] | 0;f = c[f + 4 >> 2] | 0;while (1) { + Ma(b | 0, d[f >> 0] | 0 | 0, a[g >> 0] | 0);e = e + 4 | 0;b = c[e >> 2] | 0;if (!b) break;else { + g = g + 1 | 0;f = f + 1 | 0; + } + } + }b = Yv() | 0;e = c[b >> 2] | 0;if (e | 0) do { + Na(e | 0, c[b + 4 >> 2] | 0);b = b + 8 | 0;e = c[b >> 2] | 0; + } while ((e | 0) != 0);Na(aB() | 0, 5167);n = Iv() | 0;b = c[n >> 2] | 0;a: do if (b | 0) { + do { + bB(c[b + 4 >> 2] | 0);b = c[b >> 2] | 0; + } while ((b | 0) != 0);b = c[n >> 2] | 0;if (b | 0) { + m = n;do { + while (1) { + h = b;b = c[b >> 2] | 0;h = c[h + 4 >> 2] | 0;if (!(cB(h) | 0)) break;c[p >> 2] = m;c[o >> 2] = c[p >> 2];dB(n, o) | 0;if (!b) break a; + }eB(h);m = c[m >> 2] | 0;e = fB(h) | 0;i = Va() | 0;j = l;l = l + ((1 * (e << 2) | 0) + 15 & -16) | 0;k = l;l = l + ((1 * (e << 2) | 0) + 15 & -16) | 0;e = c[(Zw(h) | 0) >> 2] | 0;if (e | 0) { + f = j;g = k;while (1) { + c[f >> 2] = c[(Xw(c[e + 4 >> 2] | 0) | 0) >> 2];c[g >> 2] = c[e + 8 >> 2];e = c[e >> 2] | 0;if (!e) break;else { + f = f + 4 | 0;g = g + 4 | 0; + } + } + }t = Xw(h) | 0;e = gB(h) | 0;f = fB(h) | 0;g = hB(h) | 0;Ra(t | 0, e | 0, j | 0, k | 0, f | 0, g | 0, Tv(h) | 0);Ga(i | 0); + } while ((b | 0) != 0); + } + } while (0);b = c[(Vv() | 0) >> 2] | 0;if (b | 0) do { + t = b + 4 | 0;n = aw(t) | 0;h = fw(n) | 0;i = bw(n) | 0;j = (cw(n) | 0) + 1 | 0;k = iB(n) | 0;m = jB(t) | 0;n = si(n) | 0;o = hw(t) | 0;p = kB(t) | 0;Pa(0, h | 0, i | 0, j | 0, k | 0, m | 0, n | 0, o | 0, p | 0, lB(t) | 0);b = c[b >> 2] | 0; + } while ((b | 0) != 0);b = c[(Iv() | 0) >> 2] | 0;b: do if (b | 0) { + c: while (1) { + e = c[b + 4 >> 2] | 0;if (e | 0 ? (q = c[(Xw(e) | 0) >> 2] | 0, r = c[(ax(e) | 0) >> 2] | 0, r | 0) : 0) { + f = r;do { + e = f + 4 | 0;g = aw(e) | 0;d: do if (g | 0) switch (si(g) | 0) {case 0: + break c;case 4:case 3:case 2: + { + k = fw(g) | 0;m = bw(g) | 0;n = (cw(g) | 0) + 1 | 0;o = iB(g) | 0;p = si(g) | 0;t = hw(e) | 0;Pa(q | 0, k | 0, m | 0, n | 0, o | 0, 0, p | 0, t | 0, kB(e) | 0, lB(e) | 0);break d; + }case 1: + { + j = fw(g) | 0;k = bw(g) | 0;m = (cw(g) | 0) + 1 | 0;n = iB(g) | 0;o = jB(e) | 0;p = si(g) | 0;t = hw(e) | 0;Pa(q | 0, j | 0, k | 0, m | 0, n | 0, o | 0, p | 0, t | 0, kB(e) | 0, lB(e) | 0);break d; + }case 5: + { + n = fw(g) | 0;o = bw(g) | 0;p = (cw(g) | 0) + 1 | 0;t = iB(g) | 0;Pa(q | 0, n | 0, o | 0, p | 0, t | 0, mB(g) | 0, si(g) | 0, 0, 0, 0);break d; + }default: + break d;} while (0);f = c[f >> 2] | 0; + } while ((f | 0) != 0); + }b = c[b >> 2] | 0;if (!b) break b; + }Ta(); + } while (0);Sa();l = s;return; + }function aB() { + return 11703; + }function bB(b) { + b = b | 0;a[b + 40 >> 0] = 0;return; + }function cB(b) { + b = b | 0;return (a[b + 40 >> 0] | 0) != 0 | 0; + }function dB(a, b) { + a = a | 0;b = b | 0;b = nB(b) | 0;a = c[b >> 2] | 0;c[b >> 2] = c[a >> 2];sC(a);return c[b >> 2] | 0; + }function eB(b) { + b = b | 0;a[b + 40 >> 0] = 1;return; + }function fB(a) { + a = a | 0;return c[a + 20 >> 2] | 0; + }function gB(a) { + a = a | 0;return c[a + 8 >> 2] | 0; + }function hB(a) { + a = a | 0;return c[a + 32 >> 2] | 0; + }function iB(a) { + a = a | 0;return c[a + 4 >> 2] | 0; + }function jB(a) { + a = a | 0;return c[a + 4 >> 2] | 0; + }function kB(a) { + a = a | 0;return c[a + 8 >> 2] | 0; + }function lB(a) { + a = a | 0;return c[a + 16 >> 2] | 0; + }function mB(a) { + a = a | 0;return c[a + 20 >> 2] | 0; + }function nB(a) { + a = a | 0;return c[a >> 2] | 0; + } + function oB(a) { + a = a | 0;var b = 0, + d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0, + o = 0, + p = 0, + q = 0, + r = 0, + s = 0, + t = 0, + u = 0, + v = 0, + w = 0, + x = 0;x = l;l = l + 16 | 0;o = x;do if (a >>> 0 < 245) { + k = a >>> 0 < 11 ? 16 : a + 11 & -8;a = k >>> 3;n = c[2783] | 0;d = n >>> a;if (d & 3 | 0) { + b = (d & 1 ^ 1) + a | 0;a = 11172 + (b << 1 << 2) | 0;d = a + 8 | 0;e = c[d >> 2] | 0;f = e + 8 | 0;g = c[f >> 2] | 0;if ((a | 0) == (g | 0)) c[2783] = n & ~(1 << b);else { + c[g + 12 >> 2] = a;c[d >> 2] = g; + }w = b << 3;c[e + 4 >> 2] = w | 3;w = e + w + 4 | 0;c[w >> 2] = c[w >> 2] | 1;w = f;l = x;return w | 0; + }m = c[2785] | 0;if (k >>> 0 > m >>> 0) { + if (d | 0) { + b = 2 << a;b = d << a & (b | 0 - b);b = (b & 0 - b) + -1 | 0;h = b >>> 12 & 16;b = b >>> h;d = b >>> 5 & 8;b = b >>> d;f = b >>> 2 & 4;b = b >>> f;a = b >>> 1 & 2;b = b >>> a;e = b >>> 1 & 1;e = (d | h | f | a | e) + (b >>> e) | 0;b = 11172 + (e << 1 << 2) | 0;a = b + 8 | 0;f = c[a >> 2] | 0;h = f + 8 | 0;d = c[h >> 2] | 0;if ((b | 0) == (d | 0)) { + a = n & ~(1 << e);c[2783] = a; + } else { + c[d + 12 >> 2] = b;c[a >> 2] = d;a = n; + }g = (e << 3) - k | 0;c[f + 4 >> 2] = k | 3;e = f + k | 0;c[e + 4 >> 2] = g | 1;c[e + g >> 2] = g;if (m | 0) { + f = c[2788] | 0;b = m >>> 3;d = 11172 + (b << 1 << 2) | 0;b = 1 << b;if (!(a & b)) { + c[2783] = a | b;b = d;a = d + 8 | 0; + } else { + a = d + 8 | 0;b = c[a >> 2] | 0; + }c[a >> 2] = f;c[b + 12 >> 2] = f;c[f + 8 >> 2] = b;c[f + 12 >> 2] = d; + }c[2785] = g;c[2788] = e;w = h;l = x;return w | 0; + }i = c[2784] | 0;if (i) { + d = (i & 0 - i) + -1 | 0;h = d >>> 12 & 16;d = d >>> h;g = d >>> 5 & 8;d = d >>> g;j = d >>> 2 & 4;d = d >>> j;e = d >>> 1 & 2;d = d >>> e;a = d >>> 1 & 1;a = c[11436 + ((g | h | j | e | a) + (d >>> a) << 2) >> 2] | 0;d = (c[a + 4 >> 2] & -8) - k | 0;e = c[a + 16 + (((c[a + 16 >> 2] | 0) == 0 & 1) << 2) >> 2] | 0;if (!e) { + j = a;g = d; + } else { + do { + h = (c[e + 4 >> 2] & -8) - k | 0;j = h >>> 0 < d >>> 0;d = j ? h : d;a = j ? e : a;e = c[e + 16 + (((c[e + 16 >> 2] | 0) == 0 & 1) << 2) >> 2] | 0; + } while ((e | 0) != 0);j = a;g = d; + }h = j + k | 0;if (j >>> 0 < h >>> 0) { + f = c[j + 24 >> 2] | 0;b = c[j + 12 >> 2] | 0;do if ((b | 0) == (j | 0)) { + a = j + 20 | 0;b = c[a >> 2] | 0;if (!b) { + a = j + 16 | 0;b = c[a >> 2] | 0;if (!b) { + d = 0;break; + } + }while (1) { + d = b + 20 | 0;e = c[d >> 2] | 0;if (e | 0) { + b = e;a = d;continue; + }d = b + 16 | 0;e = c[d >> 2] | 0;if (!e) break;else { + b = e;a = d; + } + }c[a >> 2] = 0;d = b; + } else { + d = c[j + 8 >> 2] | 0;c[d + 12 >> 2] = b;c[b + 8 >> 2] = d;d = b; + } while (0);do if (f | 0) { + b = c[j + 28 >> 2] | 0;a = 11436 + (b << 2) | 0;if ((j | 0) == (c[a >> 2] | 0)) { + c[a >> 2] = d;if (!d) { + c[2784] = i & ~(1 << b);break; + } + } else { + c[f + 16 + (((c[f + 16 >> 2] | 0) != (j | 0) & 1) << 2) >> 2] = d;if (!d) break; + }c[d + 24 >> 2] = f;b = c[j + 16 >> 2] | 0;if (b | 0) { + c[d + 16 >> 2] = b;c[b + 24 >> 2] = d; + }b = c[j + 20 >> 2] | 0;if (b | 0) { + c[d + 20 >> 2] = b;c[b + 24 >> 2] = d; + } + } while (0);if (g >>> 0 < 16) { + w = g + k | 0;c[j + 4 >> 2] = w | 3;w = j + w + 4 | 0;c[w >> 2] = c[w >> 2] | 1; + } else { + c[j + 4 >> 2] = k | 3;c[h + 4 >> 2] = g | 1;c[h + g >> 2] = g;if (m | 0) { + e = c[2788] | 0;b = m >>> 3;d = 11172 + (b << 1 << 2) | 0;b = 1 << b;if (!(n & b)) { + c[2783] = n | b;b = d;a = d + 8 | 0; + } else { + a = d + 8 | 0;b = c[a >> 2] | 0; + }c[a >> 2] = e;c[b + 12 >> 2] = e;c[e + 8 >> 2] = b;c[e + 12 >> 2] = d; + }c[2785] = g;c[2788] = h; + }w = j + 8 | 0;l = x;return w | 0; + } else n = k; + } else n = k; + } else n = k; + } else if (a >>> 0 <= 4294967231) { + a = a + 11 | 0;k = a & -8;j = c[2784] | 0;if (j) { + e = 0 - k | 0;a = a >>> 8;if (a) { + if (k >>> 0 > 16777215) i = 31;else { + n = (a + 1048320 | 0) >>> 16 & 8;v = a << n;m = (v + 520192 | 0) >>> 16 & 4;v = v << m;i = (v + 245760 | 0) >>> 16 & 2;i = 14 - (m | n | i) + (v << i >>> 15) | 0;i = k >>> (i + 7 | 0) & 1 | i << 1; + } + } else i = 0;d = c[11436 + (i << 2) >> 2] | 0;a: do if (!d) { + d = 0;a = 0;v = 57; + } else { + a = 0;h = k << ((i | 0) == 31 ? 0 : 25 - (i >>> 1) | 0);g = 0;while (1) { + f = (c[d + 4 >> 2] & -8) - k | 0;if (f >>> 0 < e >>> 0) if (!f) { + a = d;e = 0;f = d;v = 61;break a; + } else { + a = d;e = f; + }f = c[d + 20 >> 2] | 0;d = c[d + 16 + (h >>> 31 << 2) >> 2] | 0;g = (f | 0) == 0 | (f | 0) == (d | 0) ? g : f;f = (d | 0) == 0;if (f) { + d = g;v = 57;break; + } else h = h << ((f ^ 1) & 1); + } + } while (0);if ((v | 0) == 57) { + if ((d | 0) == 0 & (a | 0) == 0) { + a = 2 << i;a = j & (a | 0 - a);if (!a) { + n = k;break; + }n = (a & 0 - a) + -1 | 0;h = n >>> 12 & 16;n = n >>> h;g = n >>> 5 & 8;n = n >>> g;i = n >>> 2 & 4;n = n >>> i;m = n >>> 1 & 2;n = n >>> m;d = n >>> 1 & 1;a = 0;d = c[11436 + ((g | h | i | m | d) + (n >>> d) << 2) >> 2] | 0; + }if (!d) { + i = a;h = e; + } else { + f = d;v = 61; + } + }if ((v | 0) == 61) while (1) { + v = 0;d = (c[f + 4 >> 2] & -8) - k | 0;n = d >>> 0 < e >>> 0;d = n ? d : e;a = n ? f : a;f = c[f + 16 + (((c[f + 16 >> 2] | 0) == 0 & 1) << 2) >> 2] | 0;if (!f) { + i = a;h = d;break; + } else { + e = d;v = 61; + } + }if ((i | 0) != 0 ? h >>> 0 < ((c[2785] | 0) - k | 0) >>> 0 : 0) { + g = i + k | 0;if (i >>> 0 >= g >>> 0) { + w = 0;l = x;return w | 0; + }f = c[i + 24 >> 2] | 0;b = c[i + 12 >> 2] | 0;do if ((b | 0) == (i | 0)) { + a = i + 20 | 0;b = c[a >> 2] | 0;if (!b) { + a = i + 16 | 0;b = c[a >> 2] | 0;if (!b) { + b = 0;break; + } + }while (1) { + d = b + 20 | 0;e = c[d >> 2] | 0;if (e | 0) { + b = e;a = d;continue; + }d = b + 16 | 0;e = c[d >> 2] | 0;if (!e) break;else { + b = e;a = d; + } + }c[a >> 2] = 0; + } else { + w = c[i + 8 >> 2] | 0;c[w + 12 >> 2] = b;c[b + 8 >> 2] = w; + } while (0);do if (f) { + a = c[i + 28 >> 2] | 0;d = 11436 + (a << 2) | 0;if ((i | 0) == (c[d >> 2] | 0)) { + c[d >> 2] = b;if (!b) { + e = j & ~(1 << a);c[2784] = e;break; + } + } else { + c[f + 16 + (((c[f + 16 >> 2] | 0) != (i | 0) & 1) << 2) >> 2] = b;if (!b) { + e = j;break; + } + }c[b + 24 >> 2] = f;a = c[i + 16 >> 2] | 0;if (a | 0) { + c[b + 16 >> 2] = a;c[a + 24 >> 2] = b; + }a = c[i + 20 >> 2] | 0;if (a) { + c[b + 20 >> 2] = a;c[a + 24 >> 2] = b;e = j; + } else e = j; + } else e = j; while (0);do if (h >>> 0 >= 16) { + c[i + 4 >> 2] = k | 3;c[g + 4 >> 2] = h | 1;c[g + h >> 2] = h;b = h >>> 3;if (h >>> 0 < 256) { + d = 11172 + (b << 1 << 2) | 0;a = c[2783] | 0;b = 1 << b;if (!(a & b)) { + c[2783] = a | b;b = d;a = d + 8 | 0; + } else { + a = d + 8 | 0;b = c[a >> 2] | 0; + }c[a >> 2] = g;c[b + 12 >> 2] = g;c[g + 8 >> 2] = b;c[g + 12 >> 2] = d;break; + }b = h >>> 8;if (b) { + if (h >>> 0 > 16777215) b = 31;else { + v = (b + 1048320 | 0) >>> 16 & 8;w = b << v;u = (w + 520192 | 0) >>> 16 & 4;w = w << u;b = (w + 245760 | 0) >>> 16 & 2;b = 14 - (u | v | b) + (w << b >>> 15) | 0;b = h >>> (b + 7 | 0) & 1 | b << 1; + } + } else b = 0;d = 11436 + (b << 2) | 0;c[g + 28 >> 2] = b;a = g + 16 | 0;c[a + 4 >> 2] = 0;c[a >> 2] = 0;a = 1 << b;if (!(e & a)) { + c[2784] = e | a;c[d >> 2] = g;c[g + 24 >> 2] = d;c[g + 12 >> 2] = g;c[g + 8 >> 2] = g;break; + }a = h << ((b | 0) == 31 ? 0 : 25 - (b >>> 1) | 0);d = c[d >> 2] | 0;while (1) { + if ((c[d + 4 >> 2] & -8 | 0) == (h | 0)) { + v = 97;break; + }e = d + 16 + (a >>> 31 << 2) | 0;b = c[e >> 2] | 0;if (!b) { + v = 96;break; + } else { + a = a << 1;d = b; + } + }if ((v | 0) == 96) { + c[e >> 2] = g;c[g + 24 >> 2] = d;c[g + 12 >> 2] = g;c[g + 8 >> 2] = g;break; + } else if ((v | 0) == 97) { + v = d + 8 | 0;w = c[v >> 2] | 0;c[w + 12 >> 2] = g;c[v >> 2] = g;c[g + 8 >> 2] = w;c[g + 12 >> 2] = d;c[g + 24 >> 2] = 0;break; + } + } else { + w = h + k | 0;c[i + 4 >> 2] = w | 3;w = i + w + 4 | 0;c[w >> 2] = c[w >> 2] | 1; + } while (0);w = i + 8 | 0;l = x;return w | 0; + } else n = k; + } else n = k; + } else n = -1; while (0);d = c[2785] | 0;if (d >>> 0 >= n >>> 0) { + b = d - n | 0;a = c[2788] | 0;if (b >>> 0 > 15) { + w = a + n | 0;c[2788] = w;c[2785] = b;c[w + 4 >> 2] = b | 1;c[w + b >> 2] = b;c[a + 4 >> 2] = n | 3; + } else { + c[2785] = 0;c[2788] = 0;c[a + 4 >> 2] = d | 3;w = a + d + 4 | 0;c[w >> 2] = c[w >> 2] | 1; + }w = a + 8 | 0;l = x;return w | 0; + }h = c[2786] | 0;if (h >>> 0 > n >>> 0) { + u = h - n | 0;c[2786] = u;w = c[2789] | 0;v = w + n | 0;c[2789] = v;c[v + 4 >> 2] = u | 1;c[w + 4 >> 2] = n | 3;w = w + 8 | 0;l = x;return w | 0; + }if (!(c[2901] | 0)) { + c[2903] = 4096;c[2902] = 4096;c[2904] = -1;c[2905] = -1;c[2906] = 0;c[2894] = 0;a = o & -16 ^ 1431655768;c[o >> 2] = a;c[2901] = a;a = 4096; + } else a = c[2903] | 0;i = n + 48 | 0;j = n + 47 | 0;g = a + j | 0;f = 0 - a | 0;k = g & f;if (k >>> 0 <= n >>> 0) { + w = 0;l = x;return w | 0; + }a = c[2893] | 0;if (a | 0 ? (m = c[2891] | 0, o = m + k | 0, o >>> 0 <= m >>> 0 | o >>> 0 > a >>> 0) : 0) { + w = 0;l = x;return w | 0; + }b: do if (!(c[2894] & 4)) { + d = c[2789] | 0;c: do if (d) { + e = 11580;while (1) { + a = c[e >> 2] | 0;if (a >>> 0 <= d >>> 0 ? (r = e + 4 | 0, (a + (c[r >> 2] | 0) | 0) >>> 0 > d >>> 0) : 0) break;a = c[e + 8 >> 2] | 0;if (!a) { + v = 118;break c; + } else e = a; + }b = g - h & f;if (b >>> 0 < 2147483647) { + a = FC(b | 0) | 0;if ((a | 0) == ((c[e >> 2] | 0) + (c[r >> 2] | 0) | 0)) { + if ((a | 0) != (-1 | 0)) { + h = b;g = a;v = 135;break b; + } + } else { + e = a;v = 126; + } + } else b = 0; + } else v = 118; while (0);do if ((v | 0) == 118) { + d = FC(0) | 0;if ((d | 0) != (-1 | 0) ? (b = d, p = c[2902] | 0, q = p + -1 | 0, b = ((q & b | 0) == 0 ? 0 : (q + b & 0 - p) - b | 0) + k | 0, p = c[2891] | 0, q = b + p | 0, b >>> 0 > n >>> 0 & b >>> 0 < 2147483647) : 0) { + r = c[2893] | 0;if (r | 0 ? q >>> 0 <= p >>> 0 | q >>> 0 > r >>> 0 : 0) { + b = 0;break; + }a = FC(b | 0) | 0;if ((a | 0) == (d | 0)) { + h = b;g = d;v = 135;break b; + } else { + e = a;v = 126; + } + } else b = 0; + } while (0);do if ((v | 0) == 126) { + d = 0 - b | 0;if (!(i >>> 0 > b >>> 0 & (b >>> 0 < 2147483647 & (e | 0) != (-1 | 0)))) if ((e | 0) == (-1 | 0)) { + b = 0;break; + } else { + h = b;g = e;v = 135;break b; + }a = c[2903] | 0;a = j - b + a & 0 - a;if (a >>> 0 >= 2147483647) { + h = b;g = e;v = 135;break b; + }if ((FC(a | 0) | 0) == (-1 | 0)) { + FC(d | 0) | 0;b = 0;break; + } else { + h = a + b | 0;g = e;v = 135;break b; + } + } while (0);c[2894] = c[2894] | 4;v = 133; + } else { + b = 0;v = 133; + } while (0);if (((v | 0) == 133 ? k >>> 0 < 2147483647 : 0) ? (u = FC(k | 0) | 0, r = FC(0) | 0, s = r - u | 0, t = s >>> 0 > (n + 40 | 0) >>> 0, !((u | 0) == (-1 | 0) | t ^ 1 | u >>> 0 < r >>> 0 & ((u | 0) != (-1 | 0) & (r | 0) != (-1 | 0)) ^ 1)) : 0) { + h = t ? s : b;g = u;v = 135; + }if ((v | 0) == 135) { + b = (c[2891] | 0) + h | 0;c[2891] = b;if (b >>> 0 > (c[2892] | 0) >>> 0) c[2892] = b;j = c[2789] | 0;do if (j) { + b = 11580;while (1) { + a = c[b >> 2] | 0;d = b + 4 | 0;e = c[d >> 2] | 0;if ((g | 0) == (a + e | 0)) { + v = 145;break; + }f = c[b + 8 >> 2] | 0;if (!f) break;else b = f; + }if (((v | 0) == 145 ? (c[b + 12 >> 2] & 8 | 0) == 0 : 0) ? j >>> 0 < g >>> 0 & j >>> 0 >= a >>> 0 : 0) { + c[d >> 2] = e + h;w = j + 8 | 0;w = (w & 7 | 0) == 0 ? 0 : 0 - w & 7;v = j + w | 0;w = (c[2786] | 0) + (h - w) | 0;c[2789] = v;c[2786] = w;c[v + 4 >> 2] = w | 1;c[v + w + 4 >> 2] = 40;c[2790] = c[2905];break; + }if (g >>> 0 < (c[2787] | 0) >>> 0) c[2787] = g;d = g + h | 0;b = 11580;while (1) { + if ((c[b >> 2] | 0) == (d | 0)) { + v = 153;break; + }a = c[b + 8 >> 2] | 0;if (!a) break;else b = a; + }if ((v | 0) == 153 ? (c[b + 12 >> 2] & 8 | 0) == 0 : 0) { + c[b >> 2] = g;m = b + 4 | 0;c[m >> 2] = (c[m >> 2] | 0) + h;m = g + 8 | 0;m = g + ((m & 7 | 0) == 0 ? 0 : 0 - m & 7) | 0;b = d + 8 | 0;b = d + ((b & 7 | 0) == 0 ? 0 : 0 - b & 7) | 0;k = m + n | 0;i = b - m - n | 0;c[m + 4 >> 2] = n | 3;do if ((b | 0) != (j | 0)) { + if ((b | 0) == (c[2788] | 0)) { + w = (c[2785] | 0) + i | 0;c[2785] = w;c[2788] = k;c[k + 4 >> 2] = w | 1;c[k + w >> 2] = w;break; + }a = c[b + 4 >> 2] | 0;if ((a & 3 | 0) == 1) { + h = a & -8;e = a >>> 3;d: do if (a >>> 0 < 256) { + a = c[b + 8 >> 2] | 0;d = c[b + 12 >> 2] | 0;if ((d | 0) == (a | 0)) { + c[2783] = c[2783] & ~(1 << e);break; + } else { + c[a + 12 >> 2] = d;c[d + 8 >> 2] = a;break; + } + } else { + g = c[b + 24 >> 2] | 0;a = c[b + 12 >> 2] | 0;do if ((a | 0) == (b | 0)) { + e = b + 16 | 0;d = e + 4 | 0;a = c[d >> 2] | 0;if (!a) { + a = c[e >> 2] | 0;if (!a) { + a = 0;break; + } else d = e; + }while (1) { + e = a + 20 | 0;f = c[e >> 2] | 0;if (f | 0) { + a = f;d = e;continue; + }e = a + 16 | 0;f = c[e >> 2] | 0;if (!f) break;else { + a = f;d = e; + } + }c[d >> 2] = 0; + } else { + w = c[b + 8 >> 2] | 0;c[w + 12 >> 2] = a;c[a + 8 >> 2] = w; + } while (0);if (!g) break;d = c[b + 28 >> 2] | 0;e = 11436 + (d << 2) | 0;do if ((b | 0) != (c[e >> 2] | 0)) { + c[g + 16 + (((c[g + 16 >> 2] | 0) != (b | 0) & 1) << 2) >> 2] = a;if (!a) break d; + } else { + c[e >> 2] = a;if (a | 0) break;c[2784] = c[2784] & ~(1 << d);break d; + } while (0);c[a + 24 >> 2] = g;d = b + 16 | 0;e = c[d >> 2] | 0;if (e | 0) { + c[a + 16 >> 2] = e;c[e + 24 >> 2] = a; + }d = c[d + 4 >> 2] | 0;if (!d) break;c[a + 20 >> 2] = d;c[d + 24 >> 2] = a; + } while (0);b = b + h | 0;f = h + i | 0; + } else f = i;b = b + 4 | 0;c[b >> 2] = c[b >> 2] & -2;c[k + 4 >> 2] = f | 1;c[k + f >> 2] = f;b = f >>> 3;if (f >>> 0 < 256) { + d = 11172 + (b << 1 << 2) | 0;a = c[2783] | 0;b = 1 << b;if (!(a & b)) { + c[2783] = a | b;b = d;a = d + 8 | 0; + } else { + a = d + 8 | 0;b = c[a >> 2] | 0; + }c[a >> 2] = k;c[b + 12 >> 2] = k;c[k + 8 >> 2] = b;c[k + 12 >> 2] = d;break; + }b = f >>> 8;do if (!b) b = 0;else { + if (f >>> 0 > 16777215) { + b = 31;break; + }v = (b + 1048320 | 0) >>> 16 & 8;w = b << v;u = (w + 520192 | 0) >>> 16 & 4;w = w << u;b = (w + 245760 | 0) >>> 16 & 2;b = 14 - (u | v | b) + (w << b >>> 15) | 0;b = f >>> (b + 7 | 0) & 1 | b << 1; + } while (0);e = 11436 + (b << 2) | 0;c[k + 28 >> 2] = b;a = k + 16 | 0;c[a + 4 >> 2] = 0;c[a >> 2] = 0;a = c[2784] | 0;d = 1 << b;if (!(a & d)) { + c[2784] = a | d;c[e >> 2] = k;c[k + 24 >> 2] = e;c[k + 12 >> 2] = k;c[k + 8 >> 2] = k;break; + }a = f << ((b | 0) == 31 ? 0 : 25 - (b >>> 1) | 0);d = c[e >> 2] | 0;while (1) { + if ((c[d + 4 >> 2] & -8 | 0) == (f | 0)) { + v = 194;break; + }e = d + 16 + (a >>> 31 << 2) | 0;b = c[e >> 2] | 0;if (!b) { + v = 193;break; + } else { + a = a << 1;d = b; + } + }if ((v | 0) == 193) { + c[e >> 2] = k;c[k + 24 >> 2] = d;c[k + 12 >> 2] = k;c[k + 8 >> 2] = k;break; + } else if ((v | 0) == 194) { + v = d + 8 | 0;w = c[v >> 2] | 0;c[w + 12 >> 2] = k;c[v >> 2] = k;c[k + 8 >> 2] = w;c[k + 12 >> 2] = d;c[k + 24 >> 2] = 0;break; + } + } else { + w = (c[2786] | 0) + i | 0;c[2786] = w;c[2789] = k;c[k + 4 >> 2] = w | 1; + } while (0);w = m + 8 | 0;l = x;return w | 0; + }b = 11580;while (1) { + a = c[b >> 2] | 0;if (a >>> 0 <= j >>> 0 ? (w = a + (c[b + 4 >> 2] | 0) | 0, w >>> 0 > j >>> 0) : 0) break;b = c[b + 8 >> 2] | 0; + }f = w + -47 | 0;a = f + 8 | 0;a = f + ((a & 7 | 0) == 0 ? 0 : 0 - a & 7) | 0;f = j + 16 | 0;a = a >>> 0 < f >>> 0 ? j : a;b = a + 8 | 0;d = g + 8 | 0;d = (d & 7 | 0) == 0 ? 0 : 0 - d & 7;v = g + d | 0;d = h + -40 - d | 0;c[2789] = v;c[2786] = d;c[v + 4 >> 2] = d | 1;c[v + d + 4 >> 2] = 40;c[2790] = c[2905];d = a + 4 | 0;c[d >> 2] = 27;c[b >> 2] = c[2895];c[b + 4 >> 2] = c[2896];c[b + 8 >> 2] = c[2897];c[b + 12 >> 2] = c[2898];c[2895] = g;c[2896] = h;c[2898] = 0;c[2897] = b;b = a + 24 | 0;do { + v = b;b = b + 4 | 0;c[b >> 2] = 7; + } while ((v + 8 | 0) >>> 0 < w >>> 0);if ((a | 0) != (j | 0)) { + g = a - j | 0;c[d >> 2] = c[d >> 2] & -2;c[j + 4 >> 2] = g | 1;c[a >> 2] = g;b = g >>> 3;if (g >>> 0 < 256) { + d = 11172 + (b << 1 << 2) | 0;a = c[2783] | 0;b = 1 << b;if (!(a & b)) { + c[2783] = a | b;b = d;a = d + 8 | 0; + } else { + a = d + 8 | 0;b = c[a >> 2] | 0; + }c[a >> 2] = j;c[b + 12 >> 2] = j;c[j + 8 >> 2] = b;c[j + 12 >> 2] = d;break; + }b = g >>> 8;if (b) { + if (g >>> 0 > 16777215) d = 31;else { + v = (b + 1048320 | 0) >>> 16 & 8;w = b << v;u = (w + 520192 | 0) >>> 16 & 4;w = w << u;d = (w + 245760 | 0) >>> 16 & 2;d = 14 - (u | v | d) + (w << d >>> 15) | 0;d = g >>> (d + 7 | 0) & 1 | d << 1; + } + } else d = 0;e = 11436 + (d << 2) | 0;c[j + 28 >> 2] = d;c[j + 20 >> 2] = 0;c[f >> 2] = 0;b = c[2784] | 0;a = 1 << d;if (!(b & a)) { + c[2784] = b | a;c[e >> 2] = j;c[j + 24 >> 2] = e;c[j + 12 >> 2] = j;c[j + 8 >> 2] = j;break; + }a = g << ((d | 0) == 31 ? 0 : 25 - (d >>> 1) | 0);d = c[e >> 2] | 0;while (1) { + if ((c[d + 4 >> 2] & -8 | 0) == (g | 0)) { + v = 216;break; + }e = d + 16 + (a >>> 31 << 2) | 0;b = c[e >> 2] | 0;if (!b) { + v = 215;break; + } else { + a = a << 1;d = b; + } + }if ((v | 0) == 215) { + c[e >> 2] = j;c[j + 24 >> 2] = d;c[j + 12 >> 2] = j;c[j + 8 >> 2] = j;break; + } else if ((v | 0) == 216) { + v = d + 8 | 0;w = c[v >> 2] | 0;c[w + 12 >> 2] = j;c[v >> 2] = j;c[j + 8 >> 2] = w;c[j + 12 >> 2] = d;c[j + 24 >> 2] = 0;break; + } + } + } else { + w = c[2787] | 0;if ((w | 0) == 0 | g >>> 0 < w >>> 0) c[2787] = g;c[2895] = g;c[2896] = h;c[2898] = 0;c[2792] = c[2901];c[2791] = -1;b = 0;do { + w = 11172 + (b << 1 << 2) | 0;c[w + 12 >> 2] = w;c[w + 8 >> 2] = w;b = b + 1 | 0; + } while ((b | 0) != 32);w = g + 8 | 0;w = (w & 7 | 0) == 0 ? 0 : 0 - w & 7;v = g + w | 0;w = h + -40 - w | 0;c[2789] = v;c[2786] = w;c[v + 4 >> 2] = w | 1;c[v + w + 4 >> 2] = 40;c[2790] = c[2905]; + } while (0);b = c[2786] | 0;if (b >>> 0 > n >>> 0) { + u = b - n | 0;c[2786] = u;w = c[2789] | 0;v = w + n | 0;c[2789] = v;c[v + 4 >> 2] = u | 1;c[w + 4 >> 2] = n | 3;w = w + 8 | 0;l = x;return w | 0; + } + }c[(vB() | 0) >> 2] = 12;w = 0;l = x;return w | 0; + }function pB(a) { + a = a | 0;var b = 0, + d = 0, + e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0;if (!a) return;d = a + -8 | 0;f = c[2787] | 0;a = c[a + -4 >> 2] | 0;b = a & -8;j = d + b | 0;do if (!(a & 1)) { + e = c[d >> 2] | 0;if (!(a & 3)) return;h = d + (0 - e) | 0;g = e + b | 0;if (h >>> 0 < f >>> 0) return;if ((h | 0) == (c[2788] | 0)) { + a = j + 4 | 0;b = c[a >> 2] | 0;if ((b & 3 | 0) != 3) { + i = h;b = g;break; + }c[2785] = g;c[a >> 2] = b & -2;c[h + 4 >> 2] = g | 1;c[h + g >> 2] = g;return; + }d = e >>> 3;if (e >>> 0 < 256) { + a = c[h + 8 >> 2] | 0;b = c[h + 12 >> 2] | 0;if ((b | 0) == (a | 0)) { + c[2783] = c[2783] & ~(1 << d);i = h;b = g;break; + } else { + c[a + 12 >> 2] = b;c[b + 8 >> 2] = a;i = h;b = g;break; + } + }f = c[h + 24 >> 2] | 0;a = c[h + 12 >> 2] | 0;do if ((a | 0) == (h | 0)) { + d = h + 16 | 0;b = d + 4 | 0;a = c[b >> 2] | 0;if (!a) { + a = c[d >> 2] | 0;if (!a) { + a = 0;break; + } else b = d; + }while (1) { + d = a + 20 | 0;e = c[d >> 2] | 0;if (e | 0) { + a = e;b = d;continue; + }d = a + 16 | 0;e = c[d >> 2] | 0;if (!e) break;else { + a = e;b = d; + } + }c[b >> 2] = 0; + } else { + i = c[h + 8 >> 2] | 0;c[i + 12 >> 2] = a;c[a + 8 >> 2] = i; + } while (0);if (f) { + b = c[h + 28 >> 2] | 0;d = 11436 + (b << 2) | 0;if ((h | 0) == (c[d >> 2] | 0)) { + c[d >> 2] = a;if (!a) { + c[2784] = c[2784] & ~(1 << b);i = h;b = g;break; + } + } else { + c[f + 16 + (((c[f + 16 >> 2] | 0) != (h | 0) & 1) << 2) >> 2] = a;if (!a) { + i = h;b = g;break; + } + }c[a + 24 >> 2] = f;b = h + 16 | 0;d = c[b >> 2] | 0;if (d | 0) { + c[a + 16 >> 2] = d;c[d + 24 >> 2] = a; + }b = c[b + 4 >> 2] | 0;if (b) { + c[a + 20 >> 2] = b;c[b + 24 >> 2] = a;i = h;b = g; + } else { + i = h;b = g; + } + } else { + i = h;b = g; + } + } else { + i = d;h = d; + } while (0);if (h >>> 0 >= j >>> 0) return;a = j + 4 | 0;e = c[a >> 2] | 0;if (!(e & 1)) return;if (!(e & 2)) { + a = c[2788] | 0;if ((j | 0) == (c[2789] | 0)) { + j = (c[2786] | 0) + b | 0;c[2786] = j;c[2789] = i;c[i + 4 >> 2] = j | 1;if ((i | 0) != (a | 0)) return;c[2788] = 0;c[2785] = 0;return; + }if ((j | 0) == (a | 0)) { + j = (c[2785] | 0) + b | 0;c[2785] = j;c[2788] = h;c[i + 4 >> 2] = j | 1;c[h + j >> 2] = j;return; + }f = (e & -8) + b | 0;d = e >>> 3;do if (e >>> 0 < 256) { + b = c[j + 8 >> 2] | 0;a = c[j + 12 >> 2] | 0;if ((a | 0) == (b | 0)) { + c[2783] = c[2783] & ~(1 << d);break; + } else { + c[b + 12 >> 2] = a;c[a + 8 >> 2] = b;break; + } + } else { + g = c[j + 24 >> 2] | 0;a = c[j + 12 >> 2] | 0;do if ((a | 0) == (j | 0)) { + d = j + 16 | 0;b = d + 4 | 0;a = c[b >> 2] | 0;if (!a) { + a = c[d >> 2] | 0;if (!a) { + d = 0;break; + } else b = d; + }while (1) { + d = a + 20 | 0;e = c[d >> 2] | 0;if (e | 0) { + a = e;b = d;continue; + }d = a + 16 | 0;e = c[d >> 2] | 0;if (!e) break;else { + a = e;b = d; + } + }c[b >> 2] = 0;d = a; + } else { + d = c[j + 8 >> 2] | 0;c[d + 12 >> 2] = a;c[a + 8 >> 2] = d;d = a; + } while (0);if (g | 0) { + a = c[j + 28 >> 2] | 0;b = 11436 + (a << 2) | 0;if ((j | 0) == (c[b >> 2] | 0)) { + c[b >> 2] = d;if (!d) { + c[2784] = c[2784] & ~(1 << a);break; + } + } else { + c[g + 16 + (((c[g + 16 >> 2] | 0) != (j | 0) & 1) << 2) >> 2] = d;if (!d) break; + }c[d + 24 >> 2] = g;a = j + 16 | 0;b = c[a >> 2] | 0;if (b | 0) { + c[d + 16 >> 2] = b;c[b + 24 >> 2] = d; + }a = c[a + 4 >> 2] | 0;if (a | 0) { + c[d + 20 >> 2] = a;c[a + 24 >> 2] = d; + } + } + } while (0);c[i + 4 >> 2] = f | 1;c[h + f >> 2] = f;if ((i | 0) == (c[2788] | 0)) { + c[2785] = f;return; + } + } else { + c[a >> 2] = e & -2;c[i + 4 >> 2] = b | 1;c[h + b >> 2] = b;f = b; + }a = f >>> 3;if (f >>> 0 < 256) { + d = 11172 + (a << 1 << 2) | 0;b = c[2783] | 0;a = 1 << a;if (!(b & a)) { + c[2783] = b | a;a = d;b = d + 8 | 0; + } else { + b = d + 8 | 0;a = c[b >> 2] | 0; + }c[b >> 2] = i;c[a + 12 >> 2] = i;c[i + 8 >> 2] = a;c[i + 12 >> 2] = d;return; + }a = f >>> 8;if (a) { + if (f >>> 0 > 16777215) a = 31;else { + h = (a + 1048320 | 0) >>> 16 & 8;j = a << h;g = (j + 520192 | 0) >>> 16 & 4;j = j << g;a = (j + 245760 | 0) >>> 16 & 2;a = 14 - (g | h | a) + (j << a >>> 15) | 0;a = f >>> (a + 7 | 0) & 1 | a << 1; + } + } else a = 0;e = 11436 + (a << 2) | 0;c[i + 28 >> 2] = a;c[i + 20 >> 2] = 0;c[i + 16 >> 2] = 0;b = c[2784] | 0;d = 1 << a;do if (b & d) { + b = f << ((a | 0) == 31 ? 0 : 25 - (a >>> 1) | 0);d = c[e >> 2] | 0;while (1) { + if ((c[d + 4 >> 2] & -8 | 0) == (f | 0)) { + a = 73;break; + }e = d + 16 + (b >>> 31 << 2) | 0;a = c[e >> 2] | 0;if (!a) { + a = 72;break; + } else { + b = b << 1;d = a; + } + }if ((a | 0) == 72) { + c[e >> 2] = i;c[i + 24 >> 2] = d;c[i + 12 >> 2] = i;c[i + 8 >> 2] = i;break; + } else if ((a | 0) == 73) { + h = d + 8 | 0;j = c[h >> 2] | 0;c[j + 12 >> 2] = i;c[h >> 2] = i;c[i + 8 >> 2] = j;c[i + 12 >> 2] = d;c[i + 24 >> 2] = 0;break; + } + } else { + c[2784] = b | d;c[e >> 2] = i;c[i + 24 >> 2] = e;c[i + 12 >> 2] = i;c[i + 8 >> 2] = i; + } while (0);j = (c[2791] | 0) + -1 | 0;c[2791] = j;if (!j) a = 11588;else return;while (1) { + a = c[a >> 2] | 0;if (!a) break;else a = a + 8 | 0; + }c[2791] = -1;return; + }function qB() { + return 11628; + }function rB(a) { + a = a | 0;var b = 0, + d = 0;b = l;l = l + 16 | 0;d = b;c[d >> 2] = yB(c[a + 60 >> 2] | 0) | 0;a = uB(db(6, d | 0) | 0) | 0;l = b;return a | 0; + }function sB(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0, + o = 0, + p = 0;n = l;l = l + 48 | 0;k = n + 16 | 0;g = n;f = n + 32 | 0;i = a + 28 | 0;e = c[i >> 2] | 0;c[f >> 2] = e;j = a + 20 | 0;e = (c[j >> 2] | 0) - e | 0;c[f + 4 >> 2] = e;c[f + 8 >> 2] = b;c[f + 12 >> 2] = d;e = e + d | 0;h = a + 60 | 0;c[g >> 2] = c[h >> 2];c[g + 4 >> 2] = f;c[g + 8 >> 2] = 2;g = uB(gb(146, g | 0) | 0) | 0;a: do if ((e | 0) != (g | 0)) { + b = 2;while (1) { + if ((g | 0) < 0) break;e = e - g | 0;p = c[f + 4 >> 2] | 0;o = g >>> 0 > p >>> 0;f = o ? f + 8 | 0 : f;b = (o << 31 >> 31) + b | 0;p = g - (o ? p : 0) | 0;c[f >> 2] = (c[f >> 2] | 0) + p;o = f + 4 | 0;c[o >> 2] = (c[o >> 2] | 0) - p;c[k >> 2] = c[h >> 2];c[k + 4 >> 2] = f;c[k + 8 >> 2] = b;g = uB(gb(146, k | 0) | 0) | 0;if ((e | 0) == (g | 0)) { + m = 3;break a; + } + }c[a + 16 >> 2] = 0;c[i >> 2] = 0;c[j >> 2] = 0;c[a >> 2] = c[a >> 2] | 32;if ((b | 0) == 2) d = 0;else d = d - (c[f + 4 >> 2] | 0) | 0; + } else m = 3; while (0);if ((m | 0) == 3) { + p = c[a + 44 >> 2] | 0;c[a + 16 >> 2] = p + (c[a + 48 >> 2] | 0);c[i >> 2] = p;c[j >> 2] = p; + }l = n;return d | 0; + }function tB(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0;f = l;l = l + 32 | 0;g = f;e = f + 20 | 0;c[g >> 2] = c[a + 60 >> 2];c[g + 4 >> 2] = 0;c[g + 8 >> 2] = b;c[g + 12 >> 2] = e;c[g + 16 >> 2] = d;if ((uB(fb(140, g | 0) | 0) | 0) < 0) { + c[e >> 2] = -1;a = -1; + } else a = c[e >> 2] | 0;l = f;return a | 0; + }function uB(a) { + a = a | 0;if (a >>> 0 > 4294963200) { + c[(vB() | 0) >> 2] = 0 - a;a = -1; + }return a | 0; + }function vB() { + return (wB() | 0) + 64 | 0; + }function wB() { + return xB() | 0; + }function xB() { + return 2084; + }function yB(a) { + a = a | 0;return a | 0; + }function zB(b, d, e) { + b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0;g = l;l = l + 32 | 0;f = g;c[b + 36 >> 2] = 1;if ((c[b >> 2] & 64 | 0) == 0 ? (c[f >> 2] = c[b + 60 >> 2], c[f + 4 >> 2] = 21523, c[f + 8 >> 2] = g + 16, Wa(54, f | 0) | 0) : 0) a[b + 75 >> 0] = -1;f = sB(b, d, e) | 0;l = g;return f | 0; + }function AB(b, c) { + b = b | 0;c = c | 0;var d = 0, + e = 0;d = a[b >> 0] | 0;e = a[c >> 0] | 0;if (d << 24 >> 24 == 0 ? 1 : d << 24 >> 24 != e << 24 >> 24) b = e;else { + do { + b = b + 1 | 0;c = c + 1 | 0;d = a[b >> 0] | 0;e = a[c >> 0] | 0; + } while (!(d << 24 >> 24 == 0 ? 1 : d << 24 >> 24 != e << 24 >> 24));b = e; + }return (d & 255) - (b & 255) | 0; + }function BB(b, c, d) { + b = b | 0;c = c | 0;d = d | 0;var e = 0, + f = 0;a: do if (!d) b = 0;else { + while (1) { + e = a[b >> 0] | 0;f = a[c >> 0] | 0;if (e << 24 >> 24 != f << 24 >> 24) break;d = d + -1 | 0;if (!d) { + b = 0;break a; + } else { + b = b + 1 | 0;c = c + 1 | 0; + } + }b = (e & 255) - (f & 255) | 0; + } while (0);return b | 0; + }function CB(b, d, e) { + b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + m = 0, + n = 0, + o = 0, + q = 0, + r = 0, + s = 0;s = l;l = l + 224 | 0;n = s + 120 | 0;o = s + 80 | 0;q = s;r = s + 136 | 0;f = o;g = f + 40 | 0;do { + c[f >> 2] = 0;f = f + 4 | 0; + } while ((f | 0) < (g | 0));c[n >> 2] = c[e >> 2];if ((DB(0, d, n, q, o) | 0) < 0) e = -1;else { +e = c[b >> 2] | 0;m = e & 32;if ((a[b + 74 >> 0] | 0) < 1) c[b >> 2] = e & -33;f = b + 48 | 0;if (!(c[f >> 2] | 0)) { + g = b + 44 | 0;h = c[g >> 2] | 0;c[g >> 2] = r;i = b + 28 | 0;c[i >> 2] = r;j = b + 20 | 0;c[j >> 2] = r;c[f >> 2] = 80;k = b + 16 | 0;c[k >> 2] = r + 80;e = DB(b, d, n, q, o) | 0;if (h) { + sb[c[b + 36 >> 2] & 7](b, 0, 0) | 0;e = (c[j >> 2] | 0) == 0 ? -1 : e;c[g >> 2] = h;c[f >> 2] = 0;c[k >> 2] = 0;c[i >> 2] = 0;c[j >> 2] = 0; + } + } else e = DB(b, d, n, q, o) | 0;f = c[b >> 2] | 0;c[b >> 2] = f | m;e = (f & 32 | 0) == 0 ? e : -1; + }l = s;return e | 0; + }function DB(d, e, f, g, i) { + d = d | 0;e = e | 0;f = f | 0;g = g | 0;i = i | 0;var j = 0, + k = 0, + m = 0, + n = 0, + o = 0, + p = 0, + q = 0, + r = 0, + s = 0, + t = 0, + u = 0, + v = 0, + w = 0, + x = 0, + y = 0, + z = 0, + B = 0, + C = 0, + D = 0, + E = 0, + F = 0, + G = 0, + H = 0;H = l;l = l + 64 | 0;D = H + 16 | 0;E = H;B = H + 24 | 0;F = H + 8 | 0;G = H + 20 | 0;c[D >> 2] = e;x = (d | 0) != 0;y = B + 40 | 0;z = y;B = B + 39 | 0;C = F + 4 | 0;k = 0;j = 0;p = 0;a: while (1) { + do if ((j | 0) > -1) if ((k | 0) > (2147483647 - j | 0)) { + c[(vB() | 0) >> 2] = 75;j = -1;break; + } else { + j = k + j | 0;break; + } while (0);k = a[e >> 0] | 0;if (!(k << 24 >> 24)) { + w = 87;break; + } else m = e;b: while (1) { + switch (k << 24 >> 24) {case 37: + { + k = m;w = 9;break b; + }case 0: + { + k = m;break b; + }default: +}v = m + 1 | 0;c[D >> 2] = v;k = a[v >> 0] | 0;m = v; + }c: do if ((w | 0) == 9) while (1) { + w = 0;if ((a[m + 1 >> 0] | 0) != 37) break c;k = k + 1 | 0;m = m + 2 | 0;c[D >> 2] = m;if ((a[m >> 0] | 0) == 37) w = 9;else break; + } while (0);k = k - e | 0;if (x) GB(d, e, k);if (k | 0) { + e = m;continue; + }n = m + 1 | 0;k = (a[n >> 0] | 0) + -48 | 0;if (k >>> 0 < 10) { + v = (a[m + 2 >> 0] | 0) == 36;u = v ? k : -1;p = v ? 1 : p;n = v ? m + 3 | 0 : n; + } else u = -1;c[D >> 2] = n;k = a[n >> 0] | 0;m = (k << 24 >> 24) + -32 | 0;d: do if (m >>> 0 < 32) { + o = 0;q = k;while (1) { + k = 1 << m;if (!(k & 75913)) { + k = q;break d; + }o = k | o;n = n + 1 | 0;c[D >> 2] = n;k = a[n >> 0] | 0;m = (k << 24 >> 24) + -32 | 0;if (m >>> 0 >= 32) break;else q = k; + } + } else o = 0; while (0);if (k << 24 >> 24 == 42) { + m = n + 1 | 0;k = (a[m >> 0] | 0) + -48 | 0;if (k >>> 0 < 10 ? (a[n + 2 >> 0] | 0) == 36 : 0) { + c[i + (k << 2) >> 2] = 10;k = c[g + ((a[m >> 0] | 0) + -48 << 3) >> 2] | 0;p = 1;n = n + 3 | 0; + } else { + if (p | 0) { + j = -1;break; + }if (x) { + p = (c[f >> 2] | 0) + (4 - 1) & ~(4 - 1);k = c[p >> 2] | 0;c[f >> 2] = p + 4;p = 0;n = m; + } else { + k = 0;p = 0;n = m; + } + }c[D >> 2] = n;v = (k | 0) < 0;k = v ? 0 - k | 0 : k;o = v ? o | 8192 : o; + } else { + k = HB(D) | 0;if ((k | 0) < 0) { + j = -1;break; + }n = c[D >> 2] | 0; + }do if ((a[n >> 0] | 0) == 46) { + if ((a[n + 1 >> 0] | 0) != 42) { + c[D >> 2] = n + 1;m = HB(D) | 0;n = c[D >> 2] | 0;break; + }q = n + 2 | 0;m = (a[q >> 0] | 0) + -48 | 0;if (m >>> 0 < 10 ? (a[n + 3 >> 0] | 0) == 36 : 0) { + c[i + (m << 2) >> 2] = 10;m = c[g + ((a[q >> 0] | 0) + -48 << 3) >> 2] | 0;n = n + 4 | 0;c[D >> 2] = n;break; + }if (p | 0) { + j = -1;break a; + }if (x) { + v = (c[f >> 2] | 0) + (4 - 1) & ~(4 - 1);m = c[v >> 2] | 0;c[f >> 2] = v + 4; + } else m = 0;c[D >> 2] = q;n = q; + } else m = -1; while (0);t = 0;while (1) { + if (((a[n >> 0] | 0) + -65 | 0) >>> 0 > 57) { + j = -1;break a; + }v = n + 1 | 0;c[D >> 2] = v;q = a[(a[n >> 0] | 0) + -65 + (5178 + (t * 58 | 0)) >> 0] | 0;r = q & 255;if ((r + -1 | 0) >>> 0 < 8) { + t = r;n = v; + } else break; + }if (!(q << 24 >> 24)) { + j = -1;break; + }s = (u | 0) > -1;do if (q << 24 >> 24 == 19) { + if (s) { + j = -1;break a; + } else w = 49; + } else { + if (s) { + c[i + (u << 2) >> 2] = r;s = g + (u << 3) | 0;u = c[s + 4 >> 2] | 0;w = E;c[w >> 2] = c[s >> 2];c[w + 4 >> 2] = u;w = 49;break; + }if (!x) { + j = 0;break a; + }IB(E, r, f); + } while (0);if ((w | 0) == 49 ? (w = 0, !x) : 0) { + k = 0;e = v;continue; + }n = a[n >> 0] | 0;n = (t | 0) != 0 & (n & 15 | 0) == 3 ? n & -33 : n;s = o & -65537;u = (o & 8192 | 0) == 0 ? o : s;e: do switch (n | 0) {case 110: + switch ((t & 255) << 24 >> 24) {case 0: + { + c[c[E >> 2] >> 2] = j;k = 0;e = v;continue a; + }case 1: + { + c[c[E >> 2] >> 2] = j;k = 0;e = v;continue a; + }case 2: + { + k = c[E >> 2] | 0;c[k >> 2] = j;c[k + 4 >> 2] = ((j | 0) < 0) << 31 >> 31;k = 0;e = v;continue a; + }case 3: + { + b[c[E >> 2] >> 1] = j;k = 0;e = v;continue a; + }case 4: + { + a[c[E >> 2] >> 0] = j;k = 0;e = v;continue a; + }case 6: + { + c[c[E >> 2] >> 2] = j;k = 0;e = v;continue a; + }case 7: + { + k = c[E >> 2] | 0;c[k >> 2] = j;c[k + 4 >> 2] = ((j | 0) < 0) << 31 >> 31;k = 0;e = v;continue a; + }default: + { + k = 0;e = v;continue a; + }}case 112: + { + n = 120;m = m >>> 0 > 8 ? m : 8;e = u | 8;w = 61;break; + }case 88:case 120: + { + e = u;w = 61;break; + }case 111: + { + n = E;e = c[n >> 2] | 0;n = c[n + 4 >> 2] | 0;r = KB(e, n, y) | 0;s = z - r | 0;o = 0;q = 5642;m = (u & 8 | 0) == 0 | (m | 0) > (s | 0) ? m : s + 1 | 0;s = u;w = 67;break; + }case 105:case 100: + { + n = E;e = c[n >> 2] | 0;n = c[n + 4 >> 2] | 0;if ((n | 0) < 0) { + e = wC(0, 0, e | 0, n | 0) | 0;n = A;o = E;c[o >> 2] = e;c[o + 4 >> 2] = n;o = 1;q = 5642;w = 66;break e; + } else { + o = (u & 2049 | 0) != 0 & 1;q = (u & 2048 | 0) == 0 ? (u & 1 | 0) == 0 ? 5642 : 5644 : 5643;w = 66;break e; + } + }case 117: + { + n = E;o = 0;q = 5642;e = c[n >> 2] | 0;n = c[n + 4 >> 2] | 0;w = 66;break; + }case 99: + { + a[B >> 0] = c[E >> 2];e = B;o = 0;q = 5642;r = y;n = 1;m = s;break; + }case 109: + { + n = MB(c[(vB() | 0) >> 2] | 0) | 0;w = 71;break; + }case 115: + { + n = c[E >> 2] | 0;n = n | 0 ? n : 5652;w = 71;break; + }case 67: + { + c[F >> 2] = c[E >> 2];c[C >> 2] = 0;c[E >> 2] = F;r = -1;n = F;w = 75;break; + }case 83: + { + e = c[E >> 2] | 0;if (!m) { + OB(d, 32, k, 0, u);e = 0;w = 84; + } else { + r = m;n = e;w = 75; + }break; + }case 65:case 71:case 70:case 69:case 97:case 103:case 102:case 101: + { + k = QB(d, +h[E >> 3], k, m, u, n) | 0;e = v;continue a; + }default: + { + o = 0;q = 5642;r = y;n = m;m = u; + }} while (0);f: do if ((w | 0) == 61) { + u = E;t = c[u >> 2] | 0;u = c[u + 4 >> 2] | 0;r = JB(t, u, y, n & 32) | 0;q = (e & 8 | 0) == 0 | (t | 0) == 0 & (u | 0) == 0;o = q ? 0 : 2;q = q ? 5642 : 5642 + (n >> 4) | 0;s = e;e = t;n = u;w = 67; + } else if ((w | 0) == 66) { + r = LB(e, n, y) | 0;s = u;w = 67; + } else if ((w | 0) == 71) { + w = 0;u = NB(n, 0, m) | 0;t = (u | 0) == 0;e = n;o = 0;q = 5642;r = t ? n + m | 0 : u;n = t ? m : u - n | 0;m = s; + } else if ((w | 0) == 75) { + w = 0;q = n;e = 0;m = 0;while (1) { + o = c[q >> 2] | 0;if (!o) break;m = PB(G, o) | 0;if ((m | 0) < 0 | m >>> 0 > (r - e | 0) >>> 0) break;e = m + e | 0;if (r >>> 0 > e >>> 0) q = q + 4 | 0;else break; + }if ((m | 0) < 0) { + j = -1;break a; + }OB(d, 32, k, e, u);if (!e) { + e = 0;w = 84; + } else { + o = 0;while (1) { + m = c[n >> 2] | 0;if (!m) { + w = 84;break f; + }m = PB(G, m) | 0;o = m + o | 0;if ((o | 0) > (e | 0)) { + w = 84;break f; + }GB(d, G, m);if (o >>> 0 >= e >>> 0) { + w = 84;break; + } else n = n + 4 | 0; + } + } + } while (0);if ((w | 0) == 67) { + w = 0;n = (e | 0) != 0 | (n | 0) != 0;u = (m | 0) != 0 | n;n = ((n ^ 1) & 1) + (z - r) | 0;e = u ? r : y;r = y;n = u ? (m | 0) > (n | 0) ? m : n : m;m = (m | 0) > -1 ? s & -65537 : s; + } else if ((w | 0) == 84) { + w = 0;OB(d, 32, k, e, u ^ 8192);k = (k | 0) > (e | 0) ? k : e;e = v;continue; + }t = r - e | 0;s = (n | 0) < (t | 0) ? t : n;u = s + o | 0;k = (k | 0) < (u | 0) ? u : k;OB(d, 32, k, u, m);GB(d, q, o);OB(d, 48, k, u, m ^ 65536);OB(d, 48, s, t, 0);GB(d, e, t);OB(d, 32, k, u, m ^ 8192);e = v; + }g: do if ((w | 0) == 87) if (!d) if (!p) j = 0;else { + j = 1;while (1) { + e = c[i + (j << 2) >> 2] | 0;if (!e) break;IB(g + (j << 3) | 0, e, f);j = j + 1 | 0;if ((j | 0) >= 10) { + j = 1;break g; + } + }while (1) { + if (c[i + (j << 2) >> 2] | 0) { + j = -1;break g; + }j = j + 1 | 0;if ((j | 0) >= 10) { + j = 1;break; + } + } + } while (0);l = H;return j | 0; + }function GB(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;if (!(c[a >> 2] & 32)) aC(b, d, a) | 0;return; + }function HB(b) { + b = b | 0;var d = 0, + e = 0, + f = 0;e = c[b >> 2] | 0;f = (a[e >> 0] | 0) + -48 | 0;if (f >>> 0 < 10) { + d = 0;do { + d = f + (d * 10 | 0) | 0;e = e + 1 | 0;c[b >> 2] = e;f = (a[e >> 0] | 0) + -48 | 0; + } while (f >>> 0 < 10); + } else d = 0;return d | 0; + }function IB(a, b, d) { + a = a | 0;b = b | 0;d = d | 0;var e = 0, + f = 0, + g = 0.0;a: do if (b >>> 0 <= 20) do switch (b | 0) {case 9: + { + e = (c[d >> 2] | 0) + (4 - 1) & ~(4 - 1);b = c[e >> 2] | 0;c[d >> 2] = e + 4;c[a >> 2] = b;break a; + }case 10: + { + e = (c[d >> 2] | 0) + (4 - 1) & ~(4 - 1);b = c[e >> 2] | 0;c[d >> 2] = e + 4;e = a;c[e >> 2] = b;c[e + 4 >> 2] = ((b | 0) < 0) << 31 >> 31;break a; + }case 11: + { + e = (c[d >> 2] | 0) + (4 - 1) & ~(4 - 1);b = c[e >> 2] | 0;c[d >> 2] = e + 4;e = a;c[e >> 2] = b;c[e + 4 >> 2] = 0;break a; + }case 12: + { + e = (c[d >> 2] | 0) + (8 - 1) & ~(8 - 1);b = e;f = c[b >> 2] | 0;b = c[b + 4 >> 2] | 0;c[d >> 2] = e + 8;e = a;c[e >> 2] = f;c[e + 4 >> 2] = b;break a; + }case 13: + { + f = (c[d >> 2] | 0) + (4 - 1) & ~(4 - 1);e = c[f >> 2] | 0;c[d >> 2] = f + 4;e = (e & 65535) << 16 >> 16;f = a;c[f >> 2] = e;c[f + 4 >> 2] = ((e | 0) < 0) << 31 >> 31;break a; + }case 14: + { + f = (c[d >> 2] | 0) + (4 - 1) & ~(4 - 1);e = c[f >> 2] | 0;c[d >> 2] = f + 4;f = a;c[f >> 2] = e & 65535;c[f + 4 >> 2] = 0;break a; + }case 15: + { + f = (c[d >> 2] | 0) + (4 - 1) & ~(4 - 1);e = c[f >> 2] | 0;c[d >> 2] = f + 4;e = (e & 255) << 24 >> 24;f = a;c[f >> 2] = e;c[f + 4 >> 2] = ((e | 0) < 0) << 31 >> 31;break a; + }case 16: + { + f = (c[d >> 2] | 0) + (4 - 1) & ~(4 - 1);e = c[f >> 2] | 0;c[d >> 2] = f + 4;f = a;c[f >> 2] = e & 255;c[f + 4 >> 2] = 0;break a; + }case 17: + { + f = (c[d >> 2] | 0) + (8 - 1) & ~(8 - 1);g = +h[f >> 3];c[d >> 2] = f + 8;h[a >> 3] = g;break a; + }case 18: + { + f = (c[d >> 2] | 0) + (8 - 1) & ~(8 - 1);g = +h[f >> 3];c[d >> 2] = f + 8;h[a >> 3] = g;break a; + }default: + break a;} while (0); while (0);return; + }function JB(b, c, e, f) { + b = b | 0;c = c | 0;e = e | 0;f = f | 0;if (!((b | 0) == 0 & (c | 0) == 0)) do { + e = e + -1 | 0;a[e >> 0] = d[5694 + (b & 15) >> 0] | 0 | f;b = AC(b | 0, c | 0, 4) | 0;c = A; + } while (!((b | 0) == 0 & (c | 0) == 0));return e | 0; + }function KB(b, c, d) { + b = b | 0;c = c | 0;d = d | 0;if (!((b | 0) == 0 & (c | 0) == 0)) do { + d = d + -1 | 0;a[d >> 0] = b & 7 | 48;b = AC(b | 0, c | 0, 3) | 0;c = A; + } while (!((b | 0) == 0 & (c | 0) == 0));return d | 0; + }function LB(b, c, d) { + b = b | 0;c = c | 0;d = d | 0;var e = 0;if (c >>> 0 > 0 | (c | 0) == 0 & b >>> 0 > 4294967295) { + while (1) { + e = HC(b | 0, c | 0, 10, 0) | 0;d = d + -1 | 0;a[d >> 0] = e & 255 | 48;e = b;b = EC(b | 0, c | 0, 10, 0) | 0;if (!(c >>> 0 > 9 | (c | 0) == 9 & e >>> 0 > 4294967295)) break;else c = A; + }c = b; + } else c = b;if (c) while (1) { + d = d + -1 | 0;a[d >> 0] = (c >>> 0) % 10 | 0 | 48;if (c >>> 0 < 10) break;else c = (c >>> 0) / 10 | 0; + }return d | 0; + }function MB(a) { + a = a | 0;return XB(a, c[(WB() | 0) + 188 >> 2] | 0) | 0; + }function NB(b, d, e) { + b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0, + h = 0, + i = 0;h = d & 255;f = (e | 0) != 0;a: do if (f & (b & 3 | 0) != 0) { + g = d & 255;while (1) { + if ((a[b >> 0] | 0) == g << 24 >> 24) { + i = 6;break a; + }b = b + 1 | 0;e = e + -1 | 0;f = (e | 0) != 0;if (!(f & (b & 3 | 0) != 0)) { + i = 5;break; + } + } + } else i = 5; while (0);if ((i | 0) == 5) if (f) i = 6;else e = 0;b: do if ((i | 0) == 6) { + g = d & 255;if ((a[b >> 0] | 0) != g << 24 >> 24) { + f = P(h, 16843009) | 0;c: do if (e >>> 0 > 3) while (1) { + h = c[b >> 2] ^ f;if ((h & -2139062144 ^ -2139062144) & h + -16843009 | 0) break;b = b + 4 | 0;e = e + -4 | 0;if (e >>> 0 <= 3) { + i = 11;break c; + } + } else i = 11; while (0);if ((i | 0) == 11) if (!e) { + e = 0;break; + }while (1) { + if ((a[b >> 0] | 0) == g << 24 >> 24) break b;b = b + 1 | 0;e = e + -1 | 0;if (!e) { + e = 0;break; + } + } + } + } while (0);return (e | 0 ? b : 0) | 0; + }function OB(a, b, c, d, e) { + a = a | 0;b = b | 0;c = c | 0;d = d | 0;e = e | 0;var f = 0, + g = 0;g = l;l = l + 256 | 0;f = g;if ((c | 0) > (d | 0) & (e & 73728 | 0) == 0) { + e = c - d | 0;yC(f | 0, b | 0, (e >>> 0 < 256 ? e : 256) | 0) | 0;if (e >>> 0 > 255) { + b = c - d | 0;do { + GB(a, f, 256);e = e + -256 | 0; + } while (e >>> 0 > 255);e = b & 255; + }GB(a, f, e); + }l = g;return; + }function PB(a, b) { + a = a | 0;b = b | 0;if (!a) a = 0;else a = UB(a, b, 0) | 0;return a | 0; + }function QB(b, e, f, g, h, i) { + b = b | 0;e = +e;f = f | 0;g = g | 0;h = h | 0;i = i | 0;var j = 0, + k = 0, + m = 0, + n = 0, + o = 0, + p = 0, + q = 0, + r = 0.0, + s = 0, + t = 0, + u = 0, + v = 0, + w = 0, + x = 0, + y = 0, + z = 0, + B = 0, + C = 0, + D = 0, + E = 0, + F = 0, + G = 0, + H = 0;H = l;l = l + 560 | 0;m = H + 8 | 0;u = H;G = H + 524 | 0;F = G;n = H + 512 | 0;c[u >> 2] = 0;E = n + 12 | 0;RB(e) | 0;if ((A | 0) < 0) { + e = -e;C = 1;B = 5659; + } else { + C = (h & 2049 | 0) != 0 & 1;B = (h & 2048 | 0) == 0 ? (h & 1 | 0) == 0 ? 5660 : 5665 : 5662; + }RB(e) | 0;D = A & 2146435072;do if (D >>> 0 < 2146435072 | (D | 0) == 2146435072 & 0 < 0) { + r = +SB(e, u) * 2.0;j = r != 0.0;if (j) c[u >> 2] = (c[u >> 2] | 0) + -1;w = i | 32;if ((w | 0) == 97) { + s = i & 32;q = (s | 0) == 0 ? B : B + 9 | 0;p = C | 2;j = 12 - g | 0;do if (!(g >>> 0 > 11 | (j | 0) == 0)) { + e = 8.0;do { + j = j + -1 | 0;e = e * 16.0; + } while ((j | 0) != 0);if ((a[q >> 0] | 0) == 45) { + e = -(e + (-r - e));break; + } else { + e = r + e - e;break; + } + } else e = r; while (0);k = c[u >> 2] | 0;j = (k | 0) < 0 ? 0 - k | 0 : k;j = LB(j, ((j | 0) < 0) << 31 >> 31, E) | 0;if ((j | 0) == (E | 0)) { + j = n + 11 | 0;a[j >> 0] = 48; + }a[j + -1 >> 0] = (k >> 31 & 2) + 43;o = j + -2 | 0;a[o >> 0] = i + 15;n = (g | 0) < 1;m = (h & 8 | 0) == 0;j = G;do { + D = ~~e;k = j + 1 | 0;a[j >> 0] = d[5694 + D >> 0] | s;e = (e - +(D | 0)) * 16.0;if ((k - F | 0) == 1 ? !(m & (n & e == 0.0)) : 0) { + a[k >> 0] = 46;j = j + 2 | 0; + } else j = k; + } while (e != 0.0);D = j - F | 0;F = E - o | 0;E = (g | 0) != 0 & (D + -2 | 0) < (g | 0) ? g + 2 | 0 : D;j = F + p + E | 0;OB(b, 32, f, j, h);GB(b, q, p);OB(b, 48, f, j, h ^ 65536);GB(b, G, D);OB(b, 48, E - D | 0, 0, 0);GB(b, o, F);OB(b, 32, f, j, h ^ 8192);break; + }k = (g | 0) < 0 ? 6 : g;if (j) { + j = (c[u >> 2] | 0) + -28 | 0;c[u >> 2] = j;e = r * 268435456.0; + } else { + e = r;j = c[u >> 2] | 0; + }D = (j | 0) < 0 ? m : m + 288 | 0;m = D;do { + y = ~~e >>> 0;c[m >> 2] = y;m = m + 4 | 0;e = (e - +(y >>> 0)) * 1.0e9; + } while (e != 0.0);if ((j | 0) > 0) { + n = D;p = m;while (1) { + o = (j | 0) < 29 ? j : 29;j = p + -4 | 0;if (j >>> 0 >= n >>> 0) { + m = 0;do { + x = zC(c[j >> 2] | 0, 0, o | 0) | 0;x = xC(x | 0, A | 0, m | 0, 0) | 0;y = A;v = HC(x | 0, y | 0, 1e9, 0) | 0;c[j >> 2] = v;m = EC(x | 0, y | 0, 1e9, 0) | 0;j = j + -4 | 0; + } while (j >>> 0 >= n >>> 0);if (m) { + n = n + -4 | 0;c[n >> 2] = m; + } + }m = p;while (1) { + if (m >>> 0 <= n >>> 0) break;j = m + -4 | 0;if (!(c[j >> 2] | 0)) m = j;else break; + }j = (c[u >> 2] | 0) - o | 0;c[u >> 2] = j;if ((j | 0) > 0) p = m;else break; + } + } else n = D;if ((j | 0) < 0) { + g = ((k + 25 | 0) / 9 | 0) + 1 | 0;t = (w | 0) == 102;do { + s = 0 - j | 0;s = (s | 0) < 9 ? s : 9;if (n >>> 0 < m >>> 0) { + o = (1 << s) + -1 | 0;p = 1e9 >>> s;q = 0;j = n;do { + y = c[j >> 2] | 0;c[j >> 2] = (y >>> s) + q;q = P(y & o, p) | 0;j = j + 4 | 0; + } while (j >>> 0 < m >>> 0);j = (c[n >> 2] | 0) == 0 ? n + 4 | 0 : n;if (!q) { + n = j;j = m; + } else { + c[m >> 2] = q;n = j;j = m + 4 | 0; + } + } else { + n = (c[n >> 2] | 0) == 0 ? n + 4 | 0 : n;j = m; + }m = t ? D : n;m = (j - m >> 2 | 0) > (g | 0) ? m + (g << 2) | 0 : j;j = (c[u >> 2] | 0) + s | 0;c[u >> 2] = j; + } while ((j | 0) < 0);j = n;g = m; + } else { + j = n;g = m; + }y = D;if (j >>> 0 < g >>> 0) { + m = (y - j >> 2) * 9 | 0;o = c[j >> 2] | 0;if (o >>> 0 >= 10) { + n = 10;do { + n = n * 10 | 0;m = m + 1 | 0; + } while (o >>> 0 >= n >>> 0); + } + } else m = 0;t = (w | 0) == 103;v = (k | 0) != 0;n = k - ((w | 0) != 102 ? m : 0) + ((v & t) << 31 >> 31) | 0;if ((n | 0) < (((g - y >> 2) * 9 | 0) + -9 | 0)) { + n = n + 9216 | 0;s = D + 4 + (((n | 0) / 9 | 0) + -1024 << 2) | 0;n = ((n | 0) % 9 | 0) + 1 | 0;if ((n | 0) < 9) { + o = 10;do { + o = o * 10 | 0;n = n + 1 | 0; + } while ((n | 0) != 9); + } else o = 10;p = c[s >> 2] | 0;q = (p >>> 0) % (o >>> 0) | 0;n = (s + 4 | 0) == (g | 0);if (!(n & (q | 0) == 0)) { + r = (((p >>> 0) / (o >>> 0) | 0) & 1 | 0) == 0 ? 9007199254740992.0 : 9007199254740994.0;x = (o | 0) / 2 | 0;e = q >>> 0 < x >>> 0 ? .5 : n & (q | 0) == (x | 0) ? 1.0 : 1.5;if (C) { + x = (a[B >> 0] | 0) == 45;e = x ? -e : e;r = x ? -r : r; + }n = p - q | 0;c[s >> 2] = n;if (r + e != r) { + x = n + o | 0;c[s >> 2] = x;if (x >>> 0 > 999999999) { + m = s;while (1) { + n = m + -4 | 0;c[m >> 2] = 0;if (n >>> 0 < j >>> 0) { + j = j + -4 | 0;c[j >> 2] = 0; + }x = (c[n >> 2] | 0) + 1 | 0;c[n >> 2] = x;if (x >>> 0 > 999999999) m = n;else break; + } + } else n = s;m = (y - j >> 2) * 9 | 0;p = c[j >> 2] | 0;if (p >>> 0 >= 10) { + o = 10;do { + o = o * 10 | 0;m = m + 1 | 0; + } while (p >>> 0 >= o >>> 0); + } + } else n = s; + } else n = s;n = n + 4 | 0;n = g >>> 0 > n >>> 0 ? n : g;x = j; + } else { + n = g;x = j; + }w = n;while (1) { + if (w >>> 0 <= x >>> 0) { + u = 0;break; + }j = w + -4 | 0;if (!(c[j >> 2] | 0)) w = j;else { + u = 1;break; + } + }g = 0 - m | 0;do if (t) { + j = ((v ^ 1) & 1) + k | 0;if ((j | 0) > (m | 0) & (m | 0) > -5) { + o = i + -1 | 0;k = j + -1 - m | 0; + } else { + o = i + -2 | 0;k = j + -1 | 0; + }j = h & 8;if (!j) { + if (u ? (z = c[w + -4 >> 2] | 0, (z | 0) != 0) : 0) { + if (!((z >>> 0) % 10 | 0)) { + n = 0;j = 10;do { + j = j * 10 | 0;n = n + 1 | 0; + } while (!((z >>> 0) % (j >>> 0) | 0 | 0)); + } else n = 0; + } else n = 9;j = ((w - y >> 2) * 9 | 0) + -9 | 0;if ((o | 32 | 0) == 102) { + s = j - n | 0;s = (s | 0) > 0 ? s : 0;k = (k | 0) < (s | 0) ? k : s;s = 0;break; + } else { + s = j + m - n | 0;s = (s | 0) > 0 ? s : 0;k = (k | 0) < (s | 0) ? k : s;s = 0;break; + } + } else s = j; + } else { + o = i;s = h & 8; + } while (0);t = k | s;p = (t | 0) != 0 & 1;q = (o | 32 | 0) == 102;if (q) { + v = 0;j = (m | 0) > 0 ? m : 0; + } else { + j = (m | 0) < 0 ? g : m;j = LB(j, ((j | 0) < 0) << 31 >> 31, E) | 0;n = E;if ((n - j | 0) < 2) do { + j = j + -1 | 0;a[j >> 0] = 48; + } while ((n - j | 0) < 2);a[j + -1 >> 0] = (m >> 31 & 2) + 43;j = j + -2 | 0;a[j >> 0] = o;v = j;j = n - j | 0; + }j = C + 1 + k + p + j | 0;OB(b, 32, f, j, h);GB(b, B, C);OB(b, 48, f, j, h ^ 65536);if (q) { + o = x >>> 0 > D >>> 0 ? D : x;s = G + 9 | 0;p = s;q = G + 8 | 0;n = o;do { + m = LB(c[n >> 2] | 0, 0, s) | 0;if ((n | 0) == (o | 0)) { + if ((m | 0) == (s | 0)) { + a[q >> 0] = 48;m = q; + } + } else if (m >>> 0 > G >>> 0) { + yC(G | 0, 48, m - F | 0) | 0;do m = m + -1 | 0; while (m >>> 0 > G >>> 0); + }GB(b, m, p - m | 0);n = n + 4 | 0; + } while (n >>> 0 <= D >>> 0);if (t | 0) GB(b, 5710, 1);if (n >>> 0 < w >>> 0 & (k | 0) > 0) while (1) { + m = LB(c[n >> 2] | 0, 0, s) | 0;if (m >>> 0 > G >>> 0) { + yC(G | 0, 48, m - F | 0) | 0;do m = m + -1 | 0; while (m >>> 0 > G >>> 0); + }GB(b, m, (k | 0) < 9 ? k : 9);n = n + 4 | 0;m = k + -9 | 0;if (!(n >>> 0 < w >>> 0 & (k | 0) > 9)) { + k = m;break; + } else k = m; + }OB(b, 48, k + 9 | 0, 9, 0); + } else { + t = u ? w : x + 4 | 0;if ((k | 0) > -1) { + u = G + 9 | 0;s = (s | 0) == 0;g = u;p = 0 - F | 0;q = G + 8 | 0;o = x;do { + m = LB(c[o >> 2] | 0, 0, u) | 0;if ((m | 0) == (u | 0)) { + a[q >> 0] = 48;m = q; + }do if ((o | 0) == (x | 0)) { + n = m + 1 | 0;GB(b, m, 1);if (s & (k | 0) < 1) { + m = n;break; + }GB(b, 5710, 1);m = n; + } else { + if (m >>> 0 <= G >>> 0) break;yC(G | 0, 48, m + p | 0) | 0;do m = m + -1 | 0; while (m >>> 0 > G >>> 0); + } while (0);F = g - m | 0;GB(b, m, (k | 0) > (F | 0) ? F : k);k = k - F | 0;o = o + 4 | 0; + } while (o >>> 0 < t >>> 0 & (k | 0) > -1); + }OB(b, 48, k + 18 | 0, 18, 0);GB(b, v, E - v | 0); + }OB(b, 32, f, j, h ^ 8192); + } else { + G = (i & 32 | 0) != 0;j = C + 3 | 0;OB(b, 32, f, j, h & -65537);GB(b, B, C);GB(b, e != e | 0.0 != 0.0 ? G ? 5686 : 5690 : G ? 5678 : 5682, 3);OB(b, 32, f, j, h ^ 8192); + } while (0);l = H;return ((j | 0) < (f | 0) ? f : j) | 0; + }function RB(a) { + a = +a;var b = 0;h[j >> 3] = a;b = c[j >> 2] | 0;A = c[j + 4 >> 2] | 0;return b | 0; + }function SB(a, b) { + a = +a;b = b | 0;return + +TB(a, b); + }function TB(a, b) { + a = +a;b = b | 0;var d = 0, + e = 0, + f = 0;h[j >> 3] = a;d = c[j >> 2] | 0;e = c[j + 4 >> 2] | 0;f = AC(d | 0, e | 0, 52) | 0;switch (f & 2047) {case 0: + { + if (a != 0.0) { + a = +TB(a * 18446744073709551616.0, b);d = (c[b >> 2] | 0) + -64 | 0; + } else d = 0;c[b >> 2] = d;break; + }case 2047: + break;default: + { + c[b >> 2] = (f & 2047) + -1022;c[j >> 2] = d;c[j + 4 >> 2] = e & -2146435073 | 1071644672;a = +h[j >> 3]; + }}return +a; + }function UB(b, d, e) { + b = b | 0;d = d | 0;e = e | 0;do if (b) { + if (d >>> 0 < 128) { + a[b >> 0] = d;b = 1;break; + }if (!(c[c[(VB() | 0) + 188 >> 2] >> 2] | 0)) if ((d & -128 | 0) == 57216) { + a[b >> 0] = d;b = 1;break; + } else { + c[(vB() | 0) >> 2] = 84;b = -1;break; + }if (d >>> 0 < 2048) { + a[b >> 0] = d >>> 6 | 192;a[b + 1 >> 0] = d & 63 | 128;b = 2;break; + }if (d >>> 0 < 55296 | (d & -8192 | 0) == 57344) { + a[b >> 0] = d >>> 12 | 224;a[b + 1 >> 0] = d >>> 6 & 63 | 128;a[b + 2 >> 0] = d & 63 | 128;b = 3;break; + }if ((d + -65536 | 0) >>> 0 < 1048576) { + a[b >> 0] = d >>> 18 | 240;a[b + 1 >> 0] = d >>> 12 & 63 | 128;a[b + 2 >> 0] = d >>> 6 & 63 | 128;a[b + 3 >> 0] = d & 63 | 128;b = 4;break; + } else { + c[(vB() | 0) >> 2] = 84;b = -1;break; + } + } else b = 1; while (0);return b | 0; + }function VB() { + return xB() | 0; + }function WB() { + return xB() | 0; + }function XB(b, e) { + b = b | 0;e = e | 0;var f = 0, + g = 0;g = 0;while (1) { + if ((d[5712 + g >> 0] | 0) == (b | 0)) { + b = 2;break; + }f = g + 1 | 0;if ((f | 0) == 87) { + f = 5800;g = 87;b = 5;break; + } else g = f; + }if ((b | 0) == 2) if (!g) f = 5800;else { + f = 5800;b = 5; + }if ((b | 0) == 5) while (1) { + do { + b = f;f = f + 1 | 0; + } while ((a[b >> 0] | 0) != 0);g = g + -1 | 0;if (!g) break;else b = 5; + }return YB(f, c[e + 20 >> 2] | 0) | 0; + }function YB(a, b) { + a = a | 0;b = b | 0;return ZB(a, b) | 0; + }function ZB(a, b) { + a = a | 0;b = b | 0;if (!b) b = 0;else b = _B(c[b >> 2] | 0, c[b + 4 >> 2] | 0, a) | 0;return (b | 0 ? b : a) | 0; + }function _B(b, d, e) { + b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + l = 0, + m = 0, + n = 0, + o = 0;o = (c[b >> 2] | 0) + 1794895138 | 0;h = $B(c[b + 8 >> 2] | 0, o) | 0;f = $B(c[b + 12 >> 2] | 0, o) | 0;g = $B(c[b + 16 >> 2] | 0, o) | 0;a: do if ((h >>> 0 < d >>> 2 >>> 0 ? (n = d - (h << 2) | 0, f >>> 0 < n >>> 0 & g >>> 0 < n >>> 0) : 0) ? ((g | f) & 3 | 0) == 0 : 0) { + n = f >>> 2;m = g >>> 2;l = 0;while (1) { + j = h >>> 1;k = l + j | 0;i = k << 1;g = i + n | 0;f = $B(c[b + (g << 2) >> 2] | 0, o) | 0;g = $B(c[b + (g + 1 << 2) >> 2] | 0, o) | 0;if (!(g >>> 0 < d >>> 0 & f >>> 0 < (d - g | 0) >>> 0)) { + f = 0;break a; + }if (a[b + (g + f) >> 0] | 0) { + f = 0;break a; + }f = AB(e, b + g | 0) | 0;if (!f) break;f = (f | 0) < 0;if ((h | 0) == 1) { + f = 0;break a; + } else { + l = f ? l : k;h = f ? j : h - j | 0; + } + }f = i + m | 0;g = $B(c[b + (f << 2) >> 2] | 0, o) | 0;f = $B(c[b + (f + 1 << 2) >> 2] | 0, o) | 0;if (f >>> 0 < d >>> 0 & g >>> 0 < (d - f | 0) >>> 0) f = (a[b + (f + g) >> 0] | 0) == 0 ? b + f | 0 : 0;else f = 0; + } else f = 0; while (0);return f | 0; + }function $B(a, b) { + a = a | 0;b = b | 0;var c = 0;c = IC(a | 0) | 0;return ((b | 0) == 0 ? a : c) | 0; + }function aC(b, d, e) { + b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0, + h = 0, + i = 0, + j = 0;f = e + 16 | 0;g = c[f >> 2] | 0;if (!g) { + if (!(bC(e) | 0)) { + g = c[f >> 2] | 0;h = 5; + } else f = 0; + } else h = 5;a: do if ((h | 0) == 5) { + j = e + 20 | 0;i = c[j >> 2] | 0;f = i;if ((g - i | 0) >>> 0 < d >>> 0) { + f = sb[c[e + 36 >> 2] & 7](e, b, d) | 0;break; + }b: do if ((a[e + 75 >> 0] | 0) > -1) { + i = d;while (1) { + if (!i) { + h = 0;g = b;break b; + }g = i + -1 | 0;if ((a[b + g >> 0] | 0) == 10) break;else i = g; + }f = sb[c[e + 36 >> 2] & 7](e, b, i) | 0;if (f >>> 0 < i >>> 0) break a;h = i;g = b + i | 0;d = d - i | 0;f = c[j >> 2] | 0; + } else { + h = 0;g = b; + } while (0);BC(f | 0, g | 0, d | 0) | 0;c[j >> 2] = (c[j >> 2] | 0) + d;f = h + d | 0; + } while (0);return f | 0; + }function bC(b) { + b = b | 0;var d = 0, + e = 0;d = b + 74 | 0;e = a[d >> 0] | 0;a[d >> 0] = e + 255 | e;d = c[b >> 2] | 0;if (!(d & 8)) { + c[b + 8 >> 2] = 0;c[b + 4 >> 2] = 0;e = c[b + 44 >> 2] | 0;c[b + 28 >> 2] = e;c[b + 20 >> 2] = e;c[b + 16 >> 2] = e + (c[b + 48 >> 2] | 0);b = 0; + } else { + c[b >> 2] = d | 32;b = -1; + }return b | 0; + }function cC(a, b) { + a = T(a);b = T(b);var c = 0, + d = 0;c = dC(a) | 0;do if ((c & 2147483647) >>> 0 <= 2139095040) { + d = dC(b) | 0;if ((d & 2147483647) >>> 0 <= 2139095040) if ((d ^ c | 0) < 0) { + a = (c | 0) < 0 ? b : a;break; + } else { + a = a < b ? b : a;break; + } + } else a = b; while (0);return T(a); + }function dC(a) { + a = T(a);return (g[j >> 2] = a, c[j >> 2] | 0) | 0; + }function eC(a, b) { + a = T(a);b = T(b);var c = 0, + d = 0;c = fC(a) | 0;do if ((c & 2147483647) >>> 0 <= 2139095040) { + d = fC(b) | 0;if ((d & 2147483647) >>> 0 <= 2139095040) if ((d ^ c | 0) < 0) { + a = (c | 0) < 0 ? a : b;break; + } else { + a = a < b ? a : b;break; + } + } else a = b; while (0);return T(a); + }function fC(a) { + a = T(a);return (g[j >> 2] = a, c[j >> 2] | 0) | 0; + }function gC(a, b) { + a = T(a);b = T(b);var d = 0, + e = 0, + f = 0, + h = 0, + i = 0, + k = 0, + l = 0, + m = 0;h = (g[j >> 2] = a, c[j >> 2] | 0);k = (g[j >> 2] = b, c[j >> 2] | 0);d = h >>> 23 & 255;i = k >>> 23 & 255;l = h & -2147483648;f = k << 1;a: do if ((f | 0) != 0 ? !((d | 0) == 255 | ((hC(b) | 0) & 2147483647) >>> 0 > 2139095040) : 0) { + e = h << 1;if (e >>> 0 <= f >>> 0) { + b = T(a * T(0.0));return T((e | 0) == (f | 0) ? b : a); + }if (!d) { + d = h << 9;if ((d | 0) > -1) { + e = d;d = 0;do { + d = d + -1 | 0;e = e << 1; + } while ((e | 0) > -1); + } else d = 0;e = h << 1 - d; + } else e = h & 8388607 | 8388608;if (!i) { + h = k << 9;if ((h | 0) > -1) { + f = 0;do { + f = f + -1 | 0;h = h << 1; + } while ((h | 0) > -1); + } else f = 0;i = f;k = k << 1 - f; + } else k = k & 8388607 | 8388608;f = e - k | 0;h = (f | 0) > -1;b: do if ((d | 0) > (i | 0)) { + while (1) { + if (h) if (!f) break;else e = f;e = e << 1;d = d + -1 | 0;f = e - k | 0;h = (f | 0) > -1;if ((d | 0) <= (i | 0)) break b; + }b = T(a * T(0.0));break a; + } while (0);if (h) if (!f) { + b = T(a * T(0.0));break; + } else e = f;if (e >>> 0 < 8388608) do { + e = e << 1;d = d + -1 | 0; + } while (e >>> 0 < 8388608);if ((d | 0) > 0) d = e + -8388608 | d << 23;else d = e >>> (1 - d | 0);b = (c[j >> 2] = d | l, T(g[j >> 2])); + } else m = 3; while (0);if ((m | 0) == 3) { + b = T(a * b);b = T(b / b); + }return T(b); + }function hC(a) { + a = T(a);return (g[j >> 2] = a, c[j >> 2] | 0) | 0; + }function iC(a, b) { + a = a | 0;b = b | 0;return CB(c[582] | 0, a, b) | 0; + }function jC(a) { + a = a | 0;Ta(); + }function kC(a) { + a = a | 0;return; + }function lC(a, b) { + a = a | 0;b = b | 0;return 0; + }function mC(a) { + a = a | 0;if ((nC(a + 4 | 0) | 0) == -1) { + nb[c[(c[a >> 2] | 0) + 8 >> 2] & 127](a);a = 1; + } else a = 0;return a | 0; + }function nC(a) { + a = a | 0;var b = 0;b = c[a >> 2] | 0;c[a >> 2] = b + -1;return b + -1 | 0; + }function oC(a) { + a = a | 0;if (mC(a) | 0) pC(a);return; + }function pC(a) { + a = a | 0;var b = 0;b = a + 8 | 0;if (!((c[b >> 2] | 0) != 0 ? (nC(b) | 0) != -1 : 0)) nb[c[(c[a >> 2] | 0) + 16 >> 2] & 127](a);return; + }function qC(a) { + a = a | 0;var b = 0;b = (a | 0) == 0 ? 1 : a;while (1) { + a = oB(b) | 0;if (a | 0) break;a = uC() | 0;if (!a) { + a = 0;break; + }Fb[a & 0](); + }return a | 0; + }function rC(a) { + a = a | 0;return qC(a) | 0; + }function sC(a) { + a = a | 0;pB(a);return; + }function tC(b) { + b = b | 0;if ((a[b + 11 >> 0] | 0) < 0) sC(c[b >> 2] | 0);return; + }function uC() { + var a = 0;a = c[2923] | 0;c[2923] = a + 0;return a | 0; + }function vC() {}function wC(a, b, c, d) { + a = a | 0;b = b | 0;c = c | 0;d = d | 0;d = b - d - (c >>> 0 > a >>> 0 | 0) >>> 0;return (A = d, a - c >>> 0 | 0) | 0; + }function xC(a, b, c, d) { + a = a | 0;b = b | 0;c = c | 0;d = d | 0;c = a + c >>> 0;return (A = b + d + (c >>> 0 < a >>> 0 | 0) >>> 0, c | 0) | 0; + }function yC(b, d, e) { + b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0, + h = 0, + i = 0;h = b + e | 0;d = d & 255;if ((e | 0) >= 67) { + while (b & 3) { + a[b >> 0] = d;b = b + 1 | 0; + }f = h & -4 | 0;g = f - 64 | 0;i = d | d << 8 | d << 16 | d << 24;while ((b | 0) <= (g | 0)) { + c[b >> 2] = i;c[b + 4 >> 2] = i;c[b + 8 >> 2] = i;c[b + 12 >> 2] = i;c[b + 16 >> 2] = i;c[b + 20 >> 2] = i;c[b + 24 >> 2] = i;c[b + 28 >> 2] = i;c[b + 32 >> 2] = i;c[b + 36 >> 2] = i;c[b + 40 >> 2] = i;c[b + 44 >> 2] = i;c[b + 48 >> 2] = i;c[b + 52 >> 2] = i;c[b + 56 >> 2] = i;c[b + 60 >> 2] = i;b = b + 64 | 0; + }while ((b | 0) < (f | 0)) { + c[b >> 2] = i;b = b + 4 | 0; + } + }while ((b | 0) < (h | 0)) { + a[b >> 0] = d;b = b + 1 | 0; + }return h - e | 0; + }function zC(a, b, c) { + a = a | 0;b = b | 0;c = c | 0;if ((c | 0) < 32) { + A = b << c | (a & (1 << c) - 1 << 32 - c) >>> 32 - c;return a << c; + }A = a << c - 32;return 0; + }function AC(a, b, c) { + a = a | 0;b = b | 0;c = c | 0;if ((c | 0) < 32) { + A = b >>> c;return a >>> c | (b & (1 << c) - 1) << 32 - c; + }A = 0;return b >>> c - 32 | 0; + }function BC(b, d, e) { + b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0, + h = 0;if ((e | 0) >= 8192) return Oa(b | 0, d | 0, e | 0) | 0;h = b | 0;g = b + e | 0;if ((b & 3) == (d & 3)) { + while (b & 3) { + if (!e) return h | 0;a[b >> 0] = a[d >> 0] | 0;b = b + 1 | 0;d = d + 1 | 0;e = e - 1 | 0; + }e = g & -4 | 0;f = e - 64 | 0;while ((b | 0) <= (f | 0)) { + c[b >> 2] = c[d >> 2];c[b + 4 >> 2] = c[d + 4 >> 2];c[b + 8 >> 2] = c[d + 8 >> 2];c[b + 12 >> 2] = c[d + 12 >> 2];c[b + 16 >> 2] = c[d + 16 >> 2];c[b + 20 >> 2] = c[d + 20 >> 2];c[b + 24 >> 2] = c[d + 24 >> 2];c[b + 28 >> 2] = c[d + 28 >> 2];c[b + 32 >> 2] = c[d + 32 >> 2];c[b + 36 >> 2] = c[d + 36 >> 2];c[b + 40 >> 2] = c[d + 40 >> 2];c[b + 44 >> 2] = c[d + 44 >> 2];c[b + 48 >> 2] = c[d + 48 >> 2];c[b + 52 >> 2] = c[d + 52 >> 2];c[b + 56 >> 2] = c[d + 56 >> 2];c[b + 60 >> 2] = c[d + 60 >> 2];b = b + 64 | 0;d = d + 64 | 0; + }while ((b | 0) < (e | 0)) { + c[b >> 2] = c[d >> 2];b = b + 4 | 0;d = d + 4 | 0; + } + } else { + e = g - 4 | 0;while ((b | 0) < (e | 0)) { + a[b >> 0] = a[d >> 0] | 0;a[b + 1 >> 0] = a[d + 1 >> 0] | 0;a[b + 2 >> 0] = a[d + 2 >> 0] | 0;a[b + 3 >> 0] = a[d + 3 >> 0] | 0;b = b + 4 | 0;d = d + 4 | 0; + } + }while ((b | 0) < (g | 0)) { + a[b >> 0] = a[d >> 0] | 0;b = b + 1 | 0;d = d + 1 | 0; + }return h | 0; + }function CC(b) { + b = b | 0;var c = 0;c = a[n + (b & 255) >> 0] | 0;if ((c | 0) < 8) return c | 0;c = a[n + (b >> 8 & 255) >> 0] | 0;if ((c | 0) < 8) return c + 8 | 0;c = a[n + (b >> 16 & 255) >> 0] | 0;if ((c | 0) < 8) return c + 16 | 0;return (a[n + (b >>> 24) >> 0] | 0) + 24 | 0; + }function DC(a, b, d, e, f) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;f = f | 0;var g = 0, + h = 0, + i = 0, + j = 0, + k = 0, + l = 0, + m = 0, + n = 0, + o = 0, + p = 0;l = a;j = b;k = j;h = d;n = e;i = n;if (!k) { + g = (f | 0) != 0;if (!i) { + if (g) { + c[f >> 2] = (l >>> 0) % (h >>> 0);c[f + 4 >> 2] = 0; + }n = 0;f = (l >>> 0) / (h >>> 0) >>> 0;return (A = n, f) | 0; + } else { + if (!g) { + n = 0;f = 0;return (A = n, f) | 0; + }c[f >> 2] = a | 0;c[f + 4 >> 2] = b & 0;n = 0;f = 0;return (A = n, f) | 0; + } + }g = (i | 0) == 0;do if (h) { + if (!g) { + g = (S(i | 0) | 0) - (S(k | 0) | 0) | 0;if (g >>> 0 <= 31) { + m = g + 1 | 0;i = 31 - g | 0;b = g - 31 >> 31;h = m;a = l >>> (m >>> 0) & b | k << i;b = k >>> (m >>> 0) & b;g = 0;i = l << i;break; + }if (!f) { + n = 0;f = 0;return (A = n, f) | 0; + }c[f >> 2] = a | 0;c[f + 4 >> 2] = j | b & 0;n = 0;f = 0;return (A = n, f) | 0; + }g = h - 1 | 0;if (g & h | 0) { + i = (S(h | 0) | 0) + 33 - (S(k | 0) | 0) | 0;p = 64 - i | 0;m = 32 - i | 0;j = m >> 31;o = i - 32 | 0;b = o >> 31;h = i;a = m - 1 >> 31 & k >>> (o >>> 0) | (k << m | l >>> (i >>> 0)) & b;b = b & k >>> (i >>> 0);g = l << p & j;i = (k << p | l >>> (o >>> 0)) & j | l << m & i - 33 >> 31;break; + }if (f | 0) { + c[f >> 2] = g & l;c[f + 4 >> 2] = 0; + }if ((h | 0) == 1) { + o = j | b & 0;p = a | 0 | 0;return (A = o, p) | 0; + } else { + p = CC(h | 0) | 0;o = k >>> (p >>> 0) | 0;p = k << 32 - p | l >>> (p >>> 0) | 0;return (A = o, p) | 0; + } + } else { + if (g) { + if (f | 0) { + c[f >> 2] = (k >>> 0) % (h >>> 0);c[f + 4 >> 2] = 0; + }o = 0;p = (k >>> 0) / (h >>> 0) >>> 0;return (A = o, p) | 0; + }if (!l) { + if (f | 0) { + c[f >> 2] = 0;c[f + 4 >> 2] = (k >>> 0) % (i >>> 0); + }o = 0;p = (k >>> 0) / (i >>> 0) >>> 0;return (A = o, p) | 0; + }g = i - 1 | 0;if (!(g & i)) { + if (f | 0) { + c[f >> 2] = a | 0;c[f + 4 >> 2] = g & k | b & 0; + }o = 0;p = k >>> ((CC(i | 0) | 0) >>> 0);return (A = o, p) | 0; + }g = (S(i | 0) | 0) - (S(k | 0) | 0) | 0;if (g >>> 0 <= 30) { + b = g + 1 | 0;i = 31 - g | 0;h = b;a = k << i | l >>> (b >>> 0);b = k >>> (b >>> 0);g = 0;i = l << i;break; + }if (!f) { + o = 0;p = 0;return (A = o, p) | 0; + }c[f >> 2] = a | 0;c[f + 4 >> 2] = j | b & 0;o = 0;p = 0;return (A = o, p) | 0; + } while (0);if (!h) { + k = i;j = 0;i = 0; + } else { + m = d | 0 | 0;l = n | e & 0;k = xC(m | 0, l | 0, -1, -1) | 0;d = A;j = i;i = 0;do { + e = j;j = g >>> 31 | j << 1;g = i | g << 1;e = a << 1 | e >>> 31 | 0;n = a >>> 31 | b << 1 | 0;wC(k | 0, d | 0, e | 0, n | 0) | 0;p = A;o = p >> 31 | ((p | 0) < 0 ? -1 : 0) << 1;i = o & 1;a = wC(e | 0, n | 0, o & m | 0, (((p | 0) < 0 ? -1 : 0) >> 31 | ((p | 0) < 0 ? -1 : 0) << 1) & l | 0) | 0;b = A;h = h - 1 | 0; + } while ((h | 0) != 0);k = j;j = 0; + }h = 0;if (f | 0) { + c[f >> 2] = a;c[f + 4 >> 2] = b; + }o = (g | 0) >>> 31 | (k | h) << 1 | (h << 1 | g >>> 31) & 0 | j;p = (g << 1 | 0 >>> 31) & -2 | i;return (A = o, p) | 0; + }function EC(a, b, c, d) { + a = a | 0;b = b | 0;c = c | 0;d = d | 0;return DC(a, b, c, d, 0) | 0; + }function FC(a) { + a = a | 0;var b = 0, + d = 0;d = a + 15 & -16 | 0;b = c[i >> 2] | 0;a = b + d | 0;if ((d | 0) > 0 & (a | 0) < (b | 0) | (a | 0) < 0) { + Y() | 0;Qa(12);return -1; + }c[i >> 2] = a;if ((a | 0) > (X() | 0) ? (W() | 0) == 0 : 0) { + c[i >> 2] = b;Qa(12);return -1; + }return b | 0; + }function GC(b, c, d) { + b = b | 0;c = c | 0;d = d | 0;var e = 0;if ((c | 0) < (b | 0) & (b | 0) < (c + d | 0)) { + e = b;c = c + d | 0;b = b + d | 0;while ((d | 0) > 0) { + b = b - 1 | 0;c = c - 1 | 0;d = d - 1 | 0;a[b >> 0] = a[c >> 0] | 0; + }b = e; + } else BC(b, c, d) | 0;return b | 0; + }function HC(a, b, d, e) { + a = a | 0;b = b | 0;d = d | 0;e = e | 0;var f = 0, + g = 0;g = l;l = l + 16 | 0;f = g | 0;DC(a, b, d, e, f) | 0;l = g;return (A = c[f + 4 >> 2] | 0, c[f >> 2] | 0) | 0; + }function IC(a) { + a = a | 0;return (a & 255) << 24 | (a >> 8 & 255) << 16 | (a >> 16 & 255) << 8 | a >>> 24 | 0; + }function JC(a, b, c, d, e, f) { + a = a | 0;b = b | 0;c = c | 0;d = d | 0;e = e | 0;f = f | 0;jb[a & 1](b | 0, c | 0, d | 0, e | 0, f | 0); + }function KC(a, b, c) { + a = a | 0;b = b | 0;c = T(c);kb[a & 1](b | 0, T(c)); + }function LC(a, b, c) { + a = a | 0;b = b | 0;c = +c;lb[a & 31](b | 0, +c); + }function MC(a, b, c, d) { + a = a | 0;b = b | 0;c = T(c);d = T(d);return T(mb[a & 0](b | 0, T(c), T(d))); + }function NC(a, b) { + a = a | 0;b = b | 0;nb[a & 127](b | 0); + }function OC(a, b, c) { + a = a | 0;b = b | 0;c = c | 0;ob[a & 31](b | 0, c | 0); + }function PC(a, b) { + a = a | 0;b = b | 0;return pb[a & 31](b | 0) | 0; + }function QC(a, b, c, d, e) { + a = a | 0;b = b | 0;c = +c;d = +d;e = e | 0;qb[a & 1](b | 0, +c, +d, e | 0); + }function RC(a, b, c, d) { + a = a | 0;b = b | 0;c = +c;d = +d;rb[a & 1](b | 0, +c, +d); + }function SC(a, b, c, d) { + a = a | 0;b = b | 0;c = c | 0;d = d | 0;return sb[a & 7](b | 0, c | 0, d | 0) | 0; + }function TC(a, b, c, d) { + a = a | 0;b = b | 0;c = c | 0;d = d | 0;return +tb[a & 1](b | 0, c | 0, d | 0); + }function UC(a, b) { + a = a | 0;b = b | 0;return +ub[a & 15](b | 0); + }function VC(a, b, c) { + a = a | 0;b = b | 0;c = +c;return vb[a & 1](b | 0, +c) | 0; + }function WC(a, b, c) { + a = a | 0;b = b | 0;c = c | 0;return wb[a & 15](b | 0, c | 0) | 0; + }function XC(a, b, c, d, e, f) { + a = a | 0;b = b | 0;c = c | 0;d = +d;e = +e;f = f | 0;xb[a & 1](b | 0, c | 0, +d, +e, f | 0); + }function YC(a, b, c, d, e, f, g) { + a = a | 0;b = b | 0;c = c | 0;d = d | 0;e = e | 0;f = f | 0;g = g | 0;yb[a & 1](b | 0, c | 0, d | 0, e | 0, f | 0, g | 0); + }function ZC(a, b, c) { + a = a | 0;b = b | 0;c = c | 0;return +zb[a & 7](b | 0, c | 0); + }function _C(a) { + a = a | 0;return Ab[a & 7]() | 0; + }function $C(a, b, c, d, e, f) { + a = a | 0;b = b | 0;c = c | 0;d = d | 0;e = e | 0;f = f | 0;return Bb[a & 1](b | 0, c | 0, d | 0, e | 0, f | 0) | 0; + }function aD(a, b, c, d, e) { + a = a | 0;b = b | 0;c = c | 0;d = d | 0;e = +e;Cb[a & 1](b | 0, c | 0, d | 0, +e); + }function bD(a, b, c, d, e, f, g) { + a = a | 0;b = b | 0;c = c | 0;d = T(d);e = e | 0;f = T(f);g = g | 0;Db[a & 1](b | 0, c | 0, T(d), e | 0, T(f), g | 0); + }function cD(a, b, c, d) { + a = a | 0;b = b | 0;c = c | 0;d = d | 0;Eb[a & 15](b | 0, c | 0, d | 0); + }function dD(a) { + a = a | 0;Fb[a & 0](); + }function eD(a, b, c, d) { + a = a | 0;b = b | 0;c = c | 0;d = +d;Gb[a & 15](b | 0, c | 0, +d); + }function fD(a, b, c) { + a = a | 0;b = +b;c = +c;return Hb[a & 1](+b, +c) | 0; + }function gD(a, b, c, d, e) { + a = a | 0;b = b | 0;c = c | 0;d = d | 0;e = e | 0;Ib[a & 15](b | 0, c | 0, d | 0, e | 0); + }function hD(a, b, c, d, e) { + a = a | 0;b = b | 0;c = c | 0;d = d | 0;e = e | 0;U(0); + }function iD(a, b) { + a = a | 0;b = T(b);U(1); + }function jD(a, b) { + a = a | 0;b = +b;U(2); + }function kD(a, b, c) { + a = a | 0;b = T(b);c = T(c);U(3);return ib; + }function lD(a) { + a = a | 0;U(4); + }function mD(a, b) { + a = a | 0;b = b | 0;U(5); + }function nD(a) { + a = a | 0;U(6);return 0; + }function oD(a, b, c, d) { + a = a | 0;b = +b;c = +c;d = d | 0;U(7); + }function pD(a, b, c) { + a = a | 0;b = +b;c = +c;U(8); + }function qD(a, b, c) { + a = a | 0;b = b | 0;c = c | 0;U(9);return 0; + }function rD(a, b, c) { + a = a | 0;b = b | 0;c = c | 0;U(10);return 0.0; + }function sD(a) { + a = a | 0;U(11);return 0.0; + }function tD(a, b) { + a = a | 0;b = +b;U(12);return 0; + }function uD(a, b) { + a = a | 0;b = b | 0;U(13);return 0; + }function vD(a, b, c, d, e) { + a = a | 0;b = b | 0;c = +c;d = +d;e = e | 0;U(14); + }function wD(a, b, c, d, e, f) { + a = a | 0;b = b | 0;c = c | 0;d = d | 0;e = e | 0;f = f | 0;U(15); + }function xD(a, b) { + a = a | 0;b = b | 0;U(16);return 0.0; + }function yD() { + U(17);return 0; + }function zD(a, b, c, d, e) { + a = a | 0;b = b | 0;c = c | 0;d = d | 0;e = e | 0;U(18);return 0; + }function AD(a, b, c, d) { + a = a | 0;b = b | 0;c = c | 0;d = +d;U(19); + }function BD(a, b, c, d, e, f) { + a = a | 0;b = b | 0;c = T(c);d = d | 0;e = T(e);f = f | 0;U(20); + }function CD(a, b, c) { + a = a | 0;b = b | 0;c = c | 0;U(21); + }function DD() { + U(22); + }function ED(a, b, c) { + a = a | 0;b = b | 0;c = +c;U(23); + }function FD(a, b) { + a = +a;b = +b;U(24);return 0; + }function GD(a, b, c, d) { + a = a | 0;b = b | 0;c = c | 0;d = d | 0;U(25); + } + + // EMSCRIPTEN_END_FUNCS + var jb = [hD, Uw];var kb = [iD, of];var lb = [jD, Of, Pf, Qf, Rf, Sf, Tf, Uf, Wf, Xf, Zf, _f, $f, ag, bg, cg, dg, eg, fg, jD, jD, jD, jD, jD, jD, jD, jD, jD, jD, jD, jD, jD];var mb = [kD];var nb = [lD, kC, Ki, Li, Mi, rn, sn, tn, Pu, Qu, Ru, Cw, Dw, Ew, DA, EA, FA, Rb, tf, yf, Vf, Yf, hh, ih, ri, Ui, kj, Jj, bk, zk, Wk, nl, Hl, bm, um, Nm, en, Nn, fo, yo, Ro, ip, Bp, Xp, nq, Eq, Zq, lf, Hr, _r, us, Ps, ft, Ct, Ot, Rt, ju, mu, Eu, Uu, Xu, pv, Kv, Vi, $x, Ky, az, sz, Rz, hA, tA, wA, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD, lD];var ob = [mD, zf, Af, Df, Ef, Ff, Gf, Hf, If, Lf, Mf, Nf, wg, zg, Ag, Bg, Cg, Dg, Eg, Jg, Ng, rh, $p, qq, Ts, cy, Sv, xy, mD, mD, mD, mD];var pb = [nD, rB, sf, jg, ng, og, pg, qg, rg, sg, ug, vg, Kg, Lg, jh, ar, jt, sv, hy, jy, nD, nD, nD, nD, nD, nD, nD, nD, nD, nD, nD, nD];var qb = [oD, kh];var rb = [pD, Hu];var sb = [qD, sB, tB, zB, Dk, Rn, Lr, wz];var tb = [rD, ym];var ub = [sD, xg, yg, Fg, lh, mh, nh, oh, ph, qh, sD, sD, sD, sD, sD, sD];var vb = [tD, Kt];var wb = [uD, lC, Mg, xi, Nj, _k, rl, jn, jo, Iq, pf, ez, uD, uD, uD, uD];var xb = [vD, oj];var yb = [wD, Vz];var zb = [xD, Gg, sh, th, uh, Rm, xD, xD];var Ab = [yD, vh, qf, jf, Wt, qu, av, AA];var Bb = [zD, ee];var Cb = [AD, Vo];var Db = [BD, Pg];var Eb = [CD, kg, tg, Hg, Ig, fk, Ll, mp, Fp, nf, vx, Oy, lA, CD, CD, CD];var Fb = [DD];var Gb = [ED, Bf, Cf, Jf, Kf, gg, hg, ig, Co, cs, Ft, ED, ED, ED, ED, ED];var Hb = [FD, Mu];var Ib = [GD, fm, ir, ys, st, au, wu, hv, Pv, oy, LA, GD, GD, GD, GD, GD];return { _llvm_bswap_i32: IC, dynCall_idd: fD, dynCall_i: _C, _i64Subtract: wC, ___udivdi3: EC, dynCall_vif: KC, setThrew: Nb, dynCall_viii: cD, _bitshift64Lshr: AC, _bitshift64Shl: zC, dynCall_vi: NC, dynCall_viiddi: XC, dynCall_diii: TC, dynCall_iii: WC, _memset: yC, _sbrk: FC, _memcpy: BC, __GLOBAL__sub_I_Yoga_cpp: hf, dynCall_vii: OC, ___uremdi3: HC, dynCall_vid: LC, stackAlloc: Jb, _nbind_init: _A, getTempRet0: Pb, dynCall_di: UC, dynCall_iid: VC, setTempRet0: Ob, _i64Add: xC, dynCall_fiff: MC, dynCall_iiii: SC, _emscripten_get_global_libc: qB, dynCall_viid: eD, dynCall_viiid: aD, dynCall_viififi: bD, dynCall_ii: PC, __GLOBAL__sub_I_Binding_cc: Sx, dynCall_viiii: gD, dynCall_iiiiii: $C, stackSave: Kb, dynCall_viiiii: JC, __GLOBAL__sub_I_nbind_cc: wh, dynCall_vidd: RC, _free: pB, runPostSets: vC, dynCall_viiiiii: YC, establishStackSpace: Mb, _memmove: GC, stackRestore: Lb, _malloc: oB, __GLOBAL__sub_I_common_cc: Bv, dynCall_viddi: QC, dynCall_dii: ZC, dynCall_v: dD }; + }( + + // EMSCRIPTEN_END_ASM + Module.asmGlobalArg, Module.asmLibraryArg, buffer);var _llvm_bswap_i32 = Module["_llvm_bswap_i32"] = asm["_llvm_bswap_i32"];var getTempRet0 = Module["getTempRet0"] = asm["getTempRet0"];var ___udivdi3 = Module["___udivdi3"] = asm["___udivdi3"];var setThrew = Module["setThrew"] = asm["setThrew"];var _bitshift64Lshr = Module["_bitshift64Lshr"] = asm["_bitshift64Lshr"];var _bitshift64Shl = Module["_bitshift64Shl"] = asm["_bitshift64Shl"];var _memset = Module["_memset"] = asm["_memset"];var _sbrk = Module["_sbrk"] = asm["_sbrk"];var _memcpy = Module["_memcpy"] = asm["_memcpy"];var stackAlloc = Module["stackAlloc"] = asm["stackAlloc"];var ___uremdi3 = Module["___uremdi3"] = asm["___uremdi3"];var _nbind_init = Module["_nbind_init"] = asm["_nbind_init"];var _i64Subtract = Module["_i64Subtract"] = asm["_i64Subtract"];var setTempRet0 = Module["setTempRet0"] = asm["setTempRet0"];var _i64Add = Module["_i64Add"] = asm["_i64Add"];var _emscripten_get_global_libc = Module["_emscripten_get_global_libc"] = asm["_emscripten_get_global_libc"];var __GLOBAL__sub_I_Yoga_cpp = Module["__GLOBAL__sub_I_Yoga_cpp"] = asm["__GLOBAL__sub_I_Yoga_cpp"];var __GLOBAL__sub_I_Binding_cc = Module["__GLOBAL__sub_I_Binding_cc"] = asm["__GLOBAL__sub_I_Binding_cc"];var stackSave = Module["stackSave"] = asm["stackSave"];var __GLOBAL__sub_I_nbind_cc = Module["__GLOBAL__sub_I_nbind_cc"] = asm["__GLOBAL__sub_I_nbind_cc"];var _free = Module["_free"] = asm["_free"];var runPostSets = Module["runPostSets"] = asm["runPostSets"];var establishStackSpace = Module["establishStackSpace"] = asm["establishStackSpace"];var _memmove = Module["_memmove"] = asm["_memmove"];var stackRestore = Module["stackRestore"] = asm["stackRestore"];var _malloc = Module["_malloc"] = asm["_malloc"];var __GLOBAL__sub_I_common_cc = Module["__GLOBAL__sub_I_common_cc"] = asm["__GLOBAL__sub_I_common_cc"];var dynCall_viiiii = Module["dynCall_viiiii"] = asm["dynCall_viiiii"];var dynCall_vif = Module["dynCall_vif"] = asm["dynCall_vif"];var dynCall_vid = Module["dynCall_vid"] = asm["dynCall_vid"];var dynCall_fiff = Module["dynCall_fiff"] = asm["dynCall_fiff"];var dynCall_vi = Module["dynCall_vi"] = asm["dynCall_vi"];var dynCall_vii = Module["dynCall_vii"] = asm["dynCall_vii"];var dynCall_ii = Module["dynCall_ii"] = asm["dynCall_ii"];var dynCall_viddi = Module["dynCall_viddi"] = asm["dynCall_viddi"];var dynCall_vidd = Module["dynCall_vidd"] = asm["dynCall_vidd"];var dynCall_iiii = Module["dynCall_iiii"] = asm["dynCall_iiii"];var dynCall_diii = Module["dynCall_diii"] = asm["dynCall_diii"];var dynCall_di = Module["dynCall_di"] = asm["dynCall_di"];var dynCall_iid = Module["dynCall_iid"] = asm["dynCall_iid"];var dynCall_iii = Module["dynCall_iii"] = asm["dynCall_iii"];var dynCall_viiddi = Module["dynCall_viiddi"] = asm["dynCall_viiddi"];var dynCall_viiiiii = Module["dynCall_viiiiii"] = asm["dynCall_viiiiii"];var dynCall_dii = Module["dynCall_dii"] = asm["dynCall_dii"];var dynCall_i = Module["dynCall_i"] = asm["dynCall_i"];var dynCall_iiiiii = Module["dynCall_iiiiii"] = asm["dynCall_iiiiii"];var dynCall_viiid = Module["dynCall_viiid"] = asm["dynCall_viiid"];var dynCall_viififi = Module["dynCall_viififi"] = asm["dynCall_viififi"];var dynCall_viii = Module["dynCall_viii"] = asm["dynCall_viii"];var dynCall_v = Module["dynCall_v"] = asm["dynCall_v"];var dynCall_viid = Module["dynCall_viid"] = asm["dynCall_viid"];var dynCall_idd = Module["dynCall_idd"] = asm["dynCall_idd"];var dynCall_viiii = Module["dynCall_viiii"] = asm["dynCall_viiii"];Runtime.stackAlloc = Module["stackAlloc"];Runtime.stackSave = Module["stackSave"];Runtime.stackRestore = Module["stackRestore"];Runtime.establishStackSpace = Module["establishStackSpace"];Runtime.setTempRet0 = Module["setTempRet0"];Runtime.getTempRet0 = Module["getTempRet0"];Module["asm"] = asm;function ExitStatus(status) { + this.name = "ExitStatus";this.message = "Program terminated with exit(" + status + ")";this.status = status; + }ExitStatus.prototype = new Error();ExitStatus.prototype.constructor = ExitStatus;var initialStackTop;dependenciesFulfilled = function runCaller() { + if (!Module["calledRun"]) run();if (!Module["calledRun"]) dependenciesFulfilled = runCaller; + };Module["callMain"] = Module.callMain = function callMain(args) { + args = args || [];ensureInitRuntime();var argc = args.length + 1;function pad() { + for (var i = 0; i < 4 - 1; i++) { + argv.push(0); + } + }var argv = [allocate(intArrayFromString(Module["thisProgram"]), "i8", ALLOC_NORMAL)];pad();for (var i = 0; i < argc - 1; i = i + 1) { + argv.push(allocate(intArrayFromString(args[i]), "i8", ALLOC_NORMAL));pad(); + }argv.push(0);argv = allocate(argv, "i32", ALLOC_NORMAL);try { + var ret = Module["_main"](argc, argv, 0);exit(ret, true); + } catch (e) { + if (e instanceof ExitStatus) { + return; + } else if (e == "SimulateInfiniteLoop") { + Module["noExitRuntime"] = true;return; + } else { + var toLog = e;if (e && typeof e === "object" && e.stack) { + toLog = [e, e.stack]; + }Module.printErr("exception thrown: " + toLog);Module["quit"](1, e); + } + } finally { + } + };function run(args) { + args = args || Module["arguments"];if (runDependencies > 0) { + return; + }preRun();if (runDependencies > 0) return;if (Module["calledRun"]) return;function doRun() { + if (Module["calledRun"]) return;Module["calledRun"] = true;if (ABORT) return;ensureInitRuntime();preMain();if (Module["onRuntimeInitialized"]) Module["onRuntimeInitialized"]();if (Module["_main"] && shouldRunNow) Module["callMain"](args);postRun(); + }if (Module["setStatus"]) { + Module["setStatus"]("Running...");setTimeout(function () { + setTimeout(function () { + Module["setStatus"](""); + }, 1);doRun(); + }, 1); + } else { + doRun(); + } + }Module["run"] = Module.run = run;function exit(status, implicit) { + if (implicit && Module["noExitRuntime"]) { + return; + }if (Module["noExitRuntime"]) {} else { + ABORT = true;STACKTOP = initialStackTop;exitRuntime();if (Module["onExit"]) Module["onExit"](status); + }if (ENVIRONMENT_IS_NODE) { + process["exit"](status); + }Module["quit"](status, new ExitStatus(status)); + }Module["exit"] = Module.exit = exit;var abortDecorators = [];function abort(what) { + if (Module["onAbort"]) { + Module["onAbort"](what); + }if (what !== undefined) { + Module.print(what);Module.printErr(what);what = JSON.stringify(what); + } else { + what = ""; + }ABORT = true;var extra = "\nIf this abort() is unexpected, build with -s ASSERTIONS=1 which can give more information.";var output = "abort(" + what + ") at " + stackTrace() + extra;if (abortDecorators) { + abortDecorators.forEach(function (decorator) { + output = decorator(output, what); + }); + }throw output; + }Module["abort"] = Module.abort = abort;if (Module["preInit"]) { + if (typeof Module["preInit"] == "function") Module["preInit"] = [Module["preInit"]];while (Module["preInit"].length > 0) { + Module["preInit"].pop()(); + } + }var shouldRunNow = true;if (Module["noInitialRun"]) { + shouldRunNow = false; + }run(); +}); +}); + +/** + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + * @format + */ + + + + +var ran = false; +var ret = null; + +nbind({}, function (err, result) { + if (ran) { + return; + } + + ran = true; + + if (err) { + throw err; + } + + ret = result; +}); + +if (!ran) { + throw new Error("Failed to load the yoga module - it needed to be loaded synchronously, but didn't"); +} + +// $FlowFixMe ret will not be null here +var entryBrowser = entryCommon(ret.bind, ret.lib); + +return entryBrowser; + +}))); \ No newline at end of file -- GitLab