Skip to content

Instantly share code, notes, and snippets.

@kohlikohl
Last active November 10, 2016 01:41
Show Gist options
  • Save kohlikohl/7ee8d1d280c3d70ea35583bf950fe553 to your computer and use it in GitHub Desktop.
Save kohlikohl/7ee8d1d280c3d70ea35583bf950fe553 to your computer and use it in GitHub Desktop.
var win = window;
a();
function a(exports) {
console.log('Loading');
exports = win;
console.log('exports',exports);
//
// Array utilities
//
/* eslint no-cond-assign: 0 */
function arrayForEach(array, action) {
for (var i = 0, j = array.length; i < j; i++)
action(array[i], i);
}
//
// This becomes ko.options
// --
//
// This is the root 'options', which must be extended by others.
var options = {
deferUpdates: false,
useOnlyNativeEvents: false,
protoProperty: '__ko_proto__',
// Modify the default attribute from `data-bind`.
defaultBindingAttribute: 'data-bind',
// Enable/disable <!-- ko binding: ... -> style bindings
allowVirtualElements: true,
// Global variables that can be accessed from bindings.
bindingGlobals: window,
// An instance of the binding provider.
bindingProviderInstance: null,
// jQuery will be automatically set to window.jQuery in applyBindings
// if it is (strictly equal to) undefined. Set it to false or null to
// disable automatically setting jQuery.
jQuery: window && window.jQuery,
taskScheduler: null,
debug: false,
// Filters for bindings
// data-bind="expression | filter_1 | filter_2"
filters: {},
onError: function (e) { throw e; },
set: function (name, value) {
options[name] = value;
}
};
Object.defineProperty(options, '$', {
get: function () { return options.jQuery; }
});
function catchFunctionErrors(delegate) {
return options.onError ? function () {
try {
return delegate.apply(this, arguments);
} catch (e) {
options.onError(e);
}
} : delegate;
}
function deferError(error) {
safeSetTimeout(function () { options.onError(error); }, 0);
}
function safeSetTimeout(handler, timeout) {
return setTimeout(catchFunctionErrors(handler), timeout);
}
//
// Detection and Workarounds for Internet Explorer
//
/* eslint no-empty: 0 */
// Detect IE versions for bug workarounds (uses IE conditionals, not UA string, for robustness)
// Note that, since IE 10 does not support conditional comments, the following logic only detects IE < 10.
// Currently this is by design, since IE 10+ behaves correctly when treated as a standard browser.
// If there is a future need to detect specific versions of IE10+, we will amend this.
var ieVersion = document && (function() {
var version = 3, div = document.createElement('div'), iElems = div.getElementsByTagName('i');
// Keep constructing conditional HTML blocks until we hit one that resolves to an empty fragment
while (
div.innerHTML = '<!--[if gt IE ' + (++version) + ']><i></i><![endif]-->',
iElems[0]
) {}
return version > 4 ? version : undefined;
}());
var isIe6 = ieVersion === 6;
var isIe7 = ieVersion === 7;
//
// Object functions
//
function extend(target, source) {
if (source) {
for(var prop in source) {
if(source.hasOwnProperty(prop)) {
target[prop] = source[prop];
}
}
}
return target;
}
function objectForEach(obj, action) {
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
action(prop, obj[prop]);
}
}
}
function clonePlainObjectDeep(obj, seen) {
if (!seen) { seen = []; }
if (!obj || typeof obj !== 'object'
|| obj.constructor !== Object
|| seen.indexOf(obj) !== -1) {
return obj;
}
// Anything that makes it below is a plain object that has not yet
// been seen/cloned.
seen.push(obj);
var result = {};
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
result[prop] = clonePlainObjectDeep(obj[prop], seen);
}
}
return result;
}
var protoProperty = options.protoProperty;
//
// String (and JSON)
//
function stringTrim (string) {
return string === null || string === undefined ? '' :
string.trim ?
string.trim() :
string.toString().replace(/^[\s\xa0]+|[\s\xa0]+$/g, '');
}
//
// ES6 Symbols
//
var useSymbols = typeof Symbol === 'function';
function createSymbolOrString(identifier) {
return useSymbols ? Symbol(identifier) : identifier;
}
//
// jQuery
//
// TODO: deprecate in favour of options.$
var jQueryInstance = window && window.jQuery;
//
// DOM node data
//
// import {createSymbolOrString} from '../symbol.js'
var uniqueId = 0;
var dataStoreKeyExpandoPropertyName = "__ko__data" + new Date();
function nextKey() {
return (uniqueId++) + dataStoreKeyExpandoPropertyName;
}
var domDataKey = nextKey();
// Expose supplemental node cleaning functions.
var otherNodeCleanerFunctions = [];
// Special support for jQuery here because it's so commonly used.
// Many jQuery plugins (including jquery.tmpl) store data using jQuery's equivalent of domData
// so notify it to tear down any resources associated with the node & descendants here.
function cleanjQueryData(node) {
var jQueryCleanNodeFn = jQueryInstance
? jQueryInstance.cleanData : null;
if (jQueryCleanNodeFn) {
jQueryCleanNodeFn([node]);
}
}
otherNodeCleanerFunctions.push(cleanjQueryData);
var knownEvents = {};
var knownEventTypesByEventName = {};
var keyEventTypeName = (navigator && /Firefox\/2/i.test(navigator.userAgent)) ? 'KeyboardEvent' : 'UIEvents';
knownEvents[keyEventTypeName] = ['keyup', 'keydown', 'keypress'];
knownEvents['MouseEvents'] = [
'click', 'dblclick', 'mousedown', 'mouseup', 'mousemove', 'mouseover',
'mouseout', 'mouseenter', 'mouseleave'];
objectForEach(knownEvents, function(eventType, knownEventsForType) {
if (knownEventsForType.length) {
for (var i = 0, j = knownEventsForType.length; i < j; i++)
knownEventTypesByEventName[knownEventsForType[i]] = eventType;
}
});
var commentNodesHaveTextProperty = document && document.createComment("test").text === "<!--test-->";
var startCommentRegex = commentNodesHaveTextProperty ? /^<!--\s*ko(?:\s+([\s\S]+))?\s*-->$/ : /^\s*ko(?:\s+([\s\S]+))?\s*$/;
function isStartComment(node) {
return (node.nodeType == 8) && startCommentRegex.test(commentNodesHaveTextProperty ? node.text : node.nodeValue);
}
function virtualNodeBindingValue(node) {
var regexMatch = (commentNodesHaveTextProperty ? node.text : node.nodeValue).match(startCommentRegex);
return regexMatch ? regexMatch[1] : null;
}
var supportsTemplateTag = 'content' in document.createElement('template');
var taskQueue = [];
var taskQueueLength = 0;
var nextIndexToProcess = 0;
if (window.MutationObserver && !(window.navigator && window.navigator.standalone)) {
// Chrome 27+, Firefox 14+, IE 11+, Opera 15+, Safari 6.1+
// From https://github.com/petkaantonov/bluebird * Copyright (c) 2014 Petka Antonov * License: MIT
options.taskScheduler = (function (callback) {
var div = document.createElement("div");
new MutationObserver(callback).observe(div, {attributes: true});
return function () { div.classList.toggle("foo"); };
})(scheduledProcess);
} else if (document && "onreadystatechange" in document.createElement("script")) {
// IE 6-10
// From https://github.com/YuzuJS/setImmediate * Copyright (c) 2012 Barnesandnoble.com, llc, Donavon West, and Domenic Denicola * License: MIT
options.taskScheduler = function (callback) {
var script = document.createElement("script");
script.onreadystatechange = function () {
script.onreadystatechange = null;
document.documentElement.removeChild(script);
script = null;
callback();
};
document.documentElement.appendChild(script);
};
} else {
options.taskScheduler = function (callback) {
setTimeout(callback, 0);
};
}
function processTasks() {
if (taskQueueLength) {
// Each mark represents the end of a logical group of tasks and the number of these groups is
// limited to prevent unchecked recursion.
var mark = taskQueueLength, countMarks = 0;
// nextIndexToProcess keeps track of where we are in the queue; processTasks can be called recursively without issue
for (var task; nextIndexToProcess < taskQueueLength; ) {
if (task = taskQueue[nextIndexToProcess++]) {
if (nextIndexToProcess > mark) {
if (++countMarks >= 5000) {
nextIndexToProcess = taskQueueLength; // skip all tasks remaining in the queue since any of them could be causing the recursion
deferError(Error("'Too much recursion' after processing " + countMarks + " task groups."));
break;
}
mark = taskQueueLength;
}
try {
task();
} catch (ex) {
deferError(ex);
}
}
}
}
}
function scheduledProcess() {
processTasks();
// Reset the queue
nextIndexToProcess = taskQueueLength = taskQueue.length = 0;
}
var stringDouble = '"(?:[^"\\\\]|\\\\.)*"';
var stringSingle = "'(?:[^'\\\\]|\\\\.)*'";
var stringRegexp = '/(?:[^/\\\\]|\\\\.)*/\w*';
var specials = ',"\'{}()/:[\\]';
var everyThingElse = '[^\\s:,/][^' + specials + ']*[^\\s' + specials + ']';
var oneNotSpace = '[^\\s]';
var bindingToken = RegExp(stringDouble + '|' + stringSingle + '|' + stringRegexp + '|' + everyThingElse + '|' + oneNotSpace, 'g');
var divisionLookBehind = /[\])"'A-Za-z0-9_$]+$/;
var keywordRegexLookBehind = {
'in': 1,
'return': 1,
'typeof': 1
};
/**
* Break a binding string (data-bind='x: val, y: ..') into a stable array
* of {key: value}.
*/
function parseObjectLiteral(objectLiteralString) {
// Trim leading and trailing spaces from the string
var str = stringTrim(objectLiteralString);
// Trim braces '{' surrounding the whole object literal
if (str.charCodeAt(0) === 123) str = str.slice(1, -1);
// Split into tokens
var result = [],
toks = str.match(bindingToken),
key, values = [],
depth = 0;
if (!toks) { return [] }
// Append a comma so that we don't need a separate code block to deal with the last item
toks.push(',');
for (var i = 0, tok; tok = toks[i]; ++i) {
var c = tok.charCodeAt(0);
// A comma signals the end of a key/value pair if depth is zero
if (c === 44) { // ","
if (depth <= 0) {
result.push((key && values.length) ? {
key: key,
value: values.join('')
} : {
'unknown': key || values.join('')
});
key = depth = 0;
values = [];
continue;
}
// Simply skip the colon that separates the name and value
} else if (c === 58) { // ":"
if (!depth && !key && values.length === 1) {
key = values.pop();
continue;
}
// A set of slashes is initially matched as a regular expression, but could be division
} else if (c === 47 && i && tok.length > 1) { // "/"
// Look at the end of the previous token to determine if the slash is actually division
var match = toks[i - 1].match(divisionLookBehind);
if (match && !keywordRegexLookBehind[match[0]]) {
// The slash is actually a division punctuator; re-parse the remainder of the string (not including the slash)
str = str.substr(str.indexOf(tok) + 1);
toks = str.match(bindingToken);
toks.push(',');
i = -1;
// Continue with just the slash
tok = '/';
}
// Increment depth for parentheses, braces, and brackets so that interior commas are ignored
} else if (c === 40 || c === 123 || c === 91) { // '(', '{', '['
++depth;
} else if (c === 41 || c === 125 || c === 93) { // ')', '}', ']'
--depth;
// The key will be the first token; if it's a string, trim the quotes
} else if (!key && !values.length && (c === 34 || c === 39)) { // '"', "'"
tok = tok.slice(1, -1);
}
values.push(tok);
}
return result;
}
//
// Array utilities
//
/* eslint no-cond-assign: 0 */
function arrayForEach$1(array, action) {
for (var i = 0, j = array.length; i < j; i++)
action(array[i], i);
}
function arrayIndexOf$1(array, item) {
// IE9
if (typeof Array.prototype.indexOf == "function")
return Array.prototype.indexOf.call(array, item);
for (var i = 0, j = array.length; i < j; i++)
if (array[i] === item)
return i;
return -1;
}
function arrayRemoveItem$1(array, itemToRemove) {
var index = arrayIndexOf$1(array, itemToRemove);
if (index > 0) {
array.splice(index, 1);
}
else if (index === 0) {
array.shift();
}
}
// Go through the items that have been added and deleted and try to find matches between them.
function findMovesInArrayComparison$1(left, right, limitFailedCompares) {
if (left.length && right.length) {
var failedCompares, l, r, leftItem, rightItem;
for (failedCompares = l = 0; (!limitFailedCompares || failedCompares < limitFailedCompares) && (leftItem = left[l]); ++l) {
for (r = 0; rightItem = right[r]; ++r) {
if (leftItem['value'] === rightItem['value']) {
leftItem['moved'] = rightItem['index'];
rightItem['moved'] = leftItem['index'];
right.splice(r, 1); // This item is marked as moved; so remove it from right list
failedCompares = r = 0; // Reset failed compares count because we're checking for consecutive failures
break;
}
}
failedCompares += r;
}
}
}
var statusNotInOld$1 = 'added';
var statusNotInNew$1 = 'deleted';
// Simple calculation based on Levenshtein distance.
function compareArrays$1(oldArray, newArray, options) {
// For backward compatibility, if the third arg is actually a bool, interpret
// it as the old parameter 'dontLimitMoves'. Newer code should use { dontLimitMoves: true }.
options = (typeof options === 'boolean') ? { 'dontLimitMoves': options } : (options || {});
oldArray = oldArray || [];
newArray = newArray || [];
if (oldArray.length < newArray.length)
return compareSmallArrayToBigArray$1(oldArray, newArray, statusNotInOld$1, statusNotInNew$1, options);
else
return compareSmallArrayToBigArray$1(newArray, oldArray, statusNotInNew$1, statusNotInOld$1, options);
}
function compareSmallArrayToBigArray$1(smlArray, bigArray, statusNotInSml, statusNotInBig, options) {
var myMin = Math.min,
myMax = Math.max,
editDistanceMatrix = [],
smlIndex, smlIndexMax = smlArray.length,
bigIndex, bigIndexMax = bigArray.length,
compareRange = (bigIndexMax - smlIndexMax) || 1,
maxDistance = smlIndexMax + bigIndexMax + 1,
thisRow, lastRow,
bigIndexMaxForRow, bigIndexMinForRow;
for (smlIndex = 0; smlIndex <= smlIndexMax; smlIndex++) {
lastRow = thisRow;
editDistanceMatrix.push(thisRow = []);
bigIndexMaxForRow = myMin(bigIndexMax, smlIndex + compareRange);
bigIndexMinForRow = myMax(0, smlIndex - 1);
for (bigIndex = bigIndexMinForRow; bigIndex <= bigIndexMaxForRow; bigIndex++) {
if (!bigIndex)
thisRow[bigIndex] = smlIndex + 1;
else if (!smlIndex) // Top row - transform empty array into new array via additions
thisRow[bigIndex] = bigIndex + 1;
else if (smlArray[smlIndex - 1] === bigArray[bigIndex - 1])
thisRow[bigIndex] = lastRow[bigIndex - 1]; // copy value (no edit)
else {
var northDistance = lastRow[bigIndex] || maxDistance; // not in big (deletion)
var westDistance = thisRow[bigIndex - 1] || maxDistance; // not in small (addition)
thisRow[bigIndex] = myMin(northDistance, westDistance) + 1;
}
}
}
var editScript = [], meMinusOne, notInSml = [], notInBig = [];
for (smlIndex = smlIndexMax, bigIndex = bigIndexMax; smlIndex || bigIndex;) {
meMinusOne = editDistanceMatrix[smlIndex][bigIndex] - 1;
if (bigIndex && meMinusOne === editDistanceMatrix[smlIndex][bigIndex-1]) {
notInSml.push(editScript[editScript.length] = { // added
'status': statusNotInSml,
'value': bigArray[--bigIndex],
'index': bigIndex });
} else if (smlIndex && meMinusOne === editDistanceMatrix[smlIndex - 1][bigIndex]) {
notInBig.push(editScript[editScript.length] = { // deleted
'status': statusNotInBig,
'value': smlArray[--smlIndex],
'index': smlIndex });
} else {
--bigIndex;
--smlIndex;
if (!options['sparse']) {
editScript.push({
'status': "retained",
'value': bigArray[bigIndex] });
}
}
}
// Set a limit on the number of consecutive non-matching comparisons; having it a multiple of
// smlIndexMax keeps the time complexity of this algorithm linear.
findMovesInArrayComparison$1(notInBig, notInSml, !options['dontLimitMoves'] && smlIndexMax * 10);
return editScript.reverse();
}
//
// This becomes ko.options
// --
//
// This is the root 'options', which must be extended by others.
var options$1 = {
deferUpdates: false,
useOnlyNativeEvents: false,
protoProperty: '__ko_proto__',
// Modify the default attribute from `data-bind`.
defaultBindingAttribute: 'data-bind',
// Enable/disable <!-- ko binding: ... -> style bindings
allowVirtualElements: true,
// Global variables that can be accessed from bindings.
bindingGlobals: window,
// An instance of the binding provider.
bindingProviderInstance: null,
// jQuery will be automatically set to window.jQuery in applyBindings
// if it is (strictly equal to) undefined. Set it to false or null to
// disable automatically setting jQuery.
jQuery: window && window.jQuery,
taskScheduler: null,
debug: false,
// Filters for bindings
// data-bind="expression | filter_1 | filter_2"
filters: {},
onError: function (e) { throw e; },
set: function (name, value) {
options$1[name] = value;
}
};
Object.defineProperty(options$1, '$', {
get: function () { return options$1.jQuery; }
});
function catchFunctionErrors$1(delegate) {
return options$1.onError ? function () {
try {
return delegate.apply(this, arguments);
} catch (e) {
options$1.onError(e);
}
} : delegate;
}
function deferError$1(error) {
safeSetTimeout$1(function () { options$1.onError(error); }, 0);
}
function safeSetTimeout$1(handler, timeout) {
return setTimeout(catchFunctionErrors$1(handler), timeout);
}
function throttleFn(callback, timeout) {
var timeoutInstance;
return function () {
if (!timeoutInstance) {
timeoutInstance = safeSetTimeout$1(function () {
timeoutInstance = undefined;
callback();
}, timeout);
}
};
}
function debounceFn(callback, timeout) {
var timeoutInstance;
return function () {
clearTimeout(timeoutInstance);
timeoutInstance = safeSetTimeout$1(callback, timeout);
};
}
//
// Detection and Workarounds for Internet Explorer
//
/* eslint no-empty: 0 */
// Detect IE versions for bug workarounds (uses IE conditionals, not UA string, for robustness)
// Note that, since IE 10 does not support conditional comments, the following logic only detects IE < 10.
// Currently this is by design, since IE 10+ behaves correctly when treated as a standard browser.
// If there is a future need to detect specific versions of IE10+, we will amend this.
var ieVersion$1 = document && (function() {
var version = 3, div = document.createElement('div'), iElems = div.getElementsByTagName('i');
// Keep constructing conditional HTML blocks until we hit one that resolves to an empty fragment
while (
div.innerHTML = '<!--[if gt IE ' + (++version) + ']><i></i><![endif]-->',
iElems[0]
) {}
return version > 4 ? version : undefined;
}());
var isIe6$1 = ieVersion$1 === 6;
var isIe7$1 = ieVersion$1 === 7;
//
// Object functions
//
function extend$1(target, source) {
if (source) {
for(var prop in source) {
if(source.hasOwnProperty(prop)) {
target[prop] = source[prop];
}
}
}
return target;
}
function objectForEach$1(obj, action) {
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
action(prop, obj[prop]);
}
}
}
var protoProperty$2 = options$1.protoProperty;
var canSetPrototype$1 = ({ __proto__: [] } instanceof Array);
function setPrototypeOf$1(obj, proto) {
obj.__proto__ = proto;
return obj;
}
var setPrototypeOfOrExtend$1 = canSetPrototype$1 ? setPrototypeOf$1 : extend$1;
function hasPrototype$1(instance, prototype) {
if ((instance === null) || (instance === undefined) || (instance[protoProperty$2] === undefined)) return false;
if (instance[protoProperty$2] === prototype) return true;
return hasPrototype$1(instance[protoProperty$2], prototype); // Walk the prototype chain
}
//
// ES6 Symbols
//
var useSymbols$1 = typeof Symbol === 'function';
function createSymbolOrString$1(identifier) {
return useSymbols$1 ? Symbol(identifier) : identifier;
}
//
// jQuery
//
// TODO: deprecate in favour of options.$
var jQueryInstance$1 = window && window.jQuery;
//
// DOM node data
//
// import {createSymbolOrString} from '../symbol.js'
var uniqueId$1 = 0;
var dataStoreKeyExpandoPropertyName$1 = "__ko__data" + new Date();
function nextKey$1() {
return (uniqueId$1++) + dataStoreKeyExpandoPropertyName$1;
}
var domDataKey$1 = nextKey$1();
// Expose supplemental node cleaning functions.
var otherNodeCleanerFunctions$1 = [];
// Special support for jQuery here because it's so commonly used.
// Many jQuery plugins (including jquery.tmpl) store data using jQuery's equivalent of domData
// so notify it to tear down any resources associated with the node & descendants here.
function cleanjQueryData$1(node) {
var jQueryCleanNodeFn = jQueryInstance$1
? jQueryInstance$1.cleanData : null;
if (jQueryCleanNodeFn) {
jQueryCleanNodeFn([node]);
}
}
otherNodeCleanerFunctions$1.push(cleanjQueryData$1);
var knownEvents$1 = {};
var knownEventTypesByEventName$1 = {};
var keyEventTypeName$1 = (navigator && /Firefox\/2/i.test(navigator.userAgent)) ? 'KeyboardEvent' : 'UIEvents';
knownEvents$1[keyEventTypeName$1] = ['keyup', 'keydown', 'keypress'];
knownEvents$1['MouseEvents'] = [
'click', 'dblclick', 'mousedown', 'mouseup', 'mousemove', 'mouseover',
'mouseout', 'mouseenter', 'mouseleave'];
objectForEach$1(knownEvents$1, function(eventType, knownEventsForType) {
if (knownEventsForType.length) {
for (var i = 0, j = knownEventsForType.length; i < j; i++)
knownEventTypesByEventName$1[knownEventsForType[i]] = eventType;
}
});
var commentNodesHaveTextProperty$1 = document && document.createComment("test").text === "<!--test-->";
var supportsTemplateTag$1 = 'content' in document.createElement('template');
var taskQueue$1 = [];
var taskQueueLength$1 = 0;
var nextHandle$1 = 1;
var nextIndexToProcess$1 = 0;
if (window.MutationObserver && !(window.navigator && window.navigator.standalone)) {
// Chrome 27+, Firefox 14+, IE 11+, Opera 15+, Safari 6.1+
// From https://github.com/petkaantonov/bluebird * Copyright (c) 2014 Petka Antonov * License: MIT
options$1.taskScheduler = (function (callback) {
var div = document.createElement("div");
new MutationObserver(callback).observe(div, {attributes: true});
return function () { div.classList.toggle("foo"); };
})(scheduledProcess$1);
} else if (document && "onreadystatechange" in document.createElement("script")) {
// IE 6-10
// From https://github.com/YuzuJS/setImmediate * Copyright (c) 2012 Barnesandnoble.com, llc, Donavon West, and Domenic Denicola * License: MIT
options$1.taskScheduler = function (callback) {
var script = document.createElement("script");
script.onreadystatechange = function () {
script.onreadystatechange = null;
document.documentElement.removeChild(script);
script = null;
callback();
};
document.documentElement.appendChild(script);
};
} else {
options$1.taskScheduler = function (callback) {
setTimeout(callback, 0);
};
}
function processTasks$1() {
if (taskQueueLength$1) {
// Each mark represents the end of a logical group of tasks and the number of these groups is
// limited to prevent unchecked recursion.
var mark = taskQueueLength$1, countMarks = 0;
// nextIndexToProcess keeps track of where we are in the queue; processTasks can be called recursively without issue
for (var task; nextIndexToProcess$1 < taskQueueLength$1; ) {
if (task = taskQueue$1[nextIndexToProcess$1++]) {
if (nextIndexToProcess$1 > mark) {
if (++countMarks >= 5000) {
nextIndexToProcess$1 = taskQueueLength$1; // skip all tasks remaining in the queue since any of them could be causing the recursion
deferError$1(Error("'Too much recursion' after processing " + countMarks + " task groups."));
break;
}
mark = taskQueueLength$1;
}
try {
task();
} catch (ex) {
deferError$1(ex);
}
}
}
}
}
function scheduledProcess$1() {
processTasks$1();
// Reset the queue
nextIndexToProcess$1 = taskQueueLength$1 = taskQueue$1.length = 0;
}
function scheduleTaskProcessing$1() {
options$1.taskScheduler(scheduledProcess$1);
}
function schedule$1(func) {
if (!taskQueueLength$1) {
scheduleTaskProcessing$1();
}
taskQueue$1[taskQueueLength$1++] = func;
return nextHandle$1++;
}
function cancel$1(handle) {
var index = handle - (nextHandle$1 - taskQueueLength$1);
if (index >= nextIndexToProcess$1 && index < taskQueueLength$1) {
taskQueue$1[index] = null;
}
}
function deferUpdates(target) {
if (!target._deferUpdates) {
target._deferUpdates = true;
target.limit(function (callback) {
var handle;
return function () {
cancel$1(handle);
handle = schedule$1(callback);
target.notifySubscribers(undefined, 'dirty');
};
});
}
}
var primitiveTypes = {
'undefined': 1, 'boolean': 1, 'number': 1, 'string': 1
};
function valuesArePrimitiveAndEqual(a, b) {
var oldValueIsPrimitive = (a === null) || (typeof(a) in primitiveTypes);
return oldValueIsPrimitive ? (a === b) : false;
}
function applyExtenders(requestedExtenders) {
var target = this;
if (requestedExtenders) {
objectForEach$1(requestedExtenders, function(key, value) {
var extenderHandler = extenders[key];
if (typeof extenderHandler == 'function') {
target = extenderHandler(target, value) || target;
} else {
options$1.onError(new Error("Extender not found: " + key));
}
});
}
return target;
}
/*
--- DEFAULT EXTENDERS ---
*/
// Change when notifications are published.
function notify(target, notifyWhen) {
target.equalityComparer = notifyWhen == "always" ?
null : // null equalityComparer means to always notify
valuesArePrimitiveAndEqual;
}
function deferred(target, option) {
if (option !== true) {
throw new Error('The \'deferred\' extender only accepts the value \'true\', because it is not supported to turn deferral off once enabled.');
}
deferUpdates(target);
}
function rateLimit(target, options) {
var timeout, method, limitFunction;
if (typeof options == 'number') {
timeout = options;
} else {
timeout = options.timeout;
method = options.method;
}
// rateLimit supersedes deferred updates
target._deferUpdates = false;
limitFunction = method == 'notifyWhenChangesStop' ? debounceFn : throttleFn;
target.limit(function(callback) {
return limitFunction(callback, timeout);
});
}
var extenders = {
notify: notify,
deferred: deferred,
rateLimit: rateLimit
};
function subscription(target, callback, disposeCallback) {
this._target = target;
this.callback = callback;
this.disposeCallback = disposeCallback;
this.isDisposed = false;
}
subscription.prototype.dispose = function () {
this.isDisposed = true;
this.disposeCallback();
};
function subscribable() {
setPrototypeOfOrExtend$1(this, ko_subscribable_fn);
ko_subscribable_fn.init(this);
}
var defaultEvent = "change";
var ko_subscribable_fn = {
init: function(instance) {
instance._subscriptions = {};
instance._versionNumber = 1;
},
subscribe: function (callback, callbackTarget, event) {
var self = this;
event = event || defaultEvent;
var boundCallback = callbackTarget ? callback.bind(callbackTarget) : callback;
var subscriptionInstance = new subscription(self, boundCallback, function () {
arrayRemoveItem$1(self._subscriptions[event], subscriptionInstance);
if (self.afterSubscriptionRemove)
self.afterSubscriptionRemove(event);
});
if (self.beforeSubscriptionAdd)
self.beforeSubscriptionAdd(event);
if (!self._subscriptions[event])
self._subscriptions[event] = [];
self._subscriptions[event].push(subscriptionInstance);
return subscriptionInstance;
},
notifySubscribers: function (valueToNotify, event) {
event = event || defaultEvent;
if (event === defaultEvent) {
this.updateVersion();
}
if (this.hasSubscriptionsForEvent(event)) {
try {
begin(); // Begin suppressing dependency detection (by setting the top frame to undefined)
for (var a = this._subscriptions[event].slice(0), i = 0, subscriptionInstance; subscriptionInstance = a[i]; ++i) {
// In case a subscription was disposed during the arrayForEach cycle, check
// for isDisposed on each subscription before invoking its callback
if (!subscriptionInstance.isDisposed)
subscriptionInstance.callback(valueToNotify);
}
} finally {
end(); // End suppressing dependency detection
}
}
},
getVersion: function () {
return this._versionNumber;
},
hasChanged: function (versionToCheck) {
return this.getVersion() !== versionToCheck;
},
updateVersion: function () {
++this._versionNumber;
},
hasSubscriptionsForEvent: function(event) {
return this._subscriptions[event] && this._subscriptions[event].length;
},
getSubscriptionsCount: function (event) {
if (event) {
return this._subscriptions[event] && this._subscriptions[event].length || 0;
} else {
var total = 0;
objectForEach$1(this._subscriptions, function(eventName, subscriptions) {
if (eventName !== 'dirty')
total += subscriptions.length;
});
return total;
}
},
isDifferent: function(oldValue, newValue) {
return !this.equalityComparer ||
!this.equalityComparer(oldValue, newValue);
},
extend: applyExtenders
};
// For browsers that support proto assignment, we overwrite the prototype of each
// observable instance. Since observables are functions, we need Function.prototype
// to still be in the prototype chain.
if (canSetPrototype$1) {
setPrototypeOf$1(ko_subscribable_fn, Function.prototype);
}
subscribable.fn = ko_subscribable_fn;
function isSubscribable(instance) {
return instance != null && typeof instance.subscribe == "function" && typeof instance.notifySubscribers == "function";
}
var outerFrames = [];
var currentFrame;
var lastId = 0;
// Return a unique ID that can be assigned to an observable for dependency tracking.
// Theoretically, you could eventually overflow the number storage size, resulting
// in duplicate IDs. But in JavaScript, the largest exact integral value is 2^53
// or 9,007,199,254,740,992. If you created 1,000,000 IDs per second, it would
// take over 285 years to reach that number.
// Reference http://blog.vjeux.com/2010/javascript/javascript-max_int-number-limits.html
function getId() {
return ++lastId;
}
function begin(options) {
outerFrames.push(currentFrame);
currentFrame = options;
}
function end() {
currentFrame = outerFrames.pop();
}
function registerDependency(subscribable) {
if (currentFrame) {
if (!isSubscribable(subscribable))
throw new Error("Only subscribable things can act as dependencies");
currentFrame.callback.call(currentFrame.callbackTarget, subscribable, subscribable._id || (subscribable._id = getId()));
}
}
var observableLatestValue = createSymbolOrString$1('_latestValue');
function observable(initialValue) {
function Observable() {
if (arguments.length > 0) {
// Write
// Ignore writes if the value hasn't changed
if (Observable.isDifferent(Observable[observableLatestValue], arguments[0])) {
Observable.valueWillMutate();
Observable[observableLatestValue] = arguments[0];
Observable.valueHasMutated();
}
return this; // Permits chained assignments
}
else {
// Read
registerDependency(Observable); // The caller only needs to be notified of changes if they did a "read" operation
return Observable[observableLatestValue];
}
}
Observable[observableLatestValue] = initialValue;
// Inherit from 'subscribable'
if (!canSetPrototype$1) {
// 'subscribable' won't be on the prototype chain unless we put it there directly
extend$1(Observable, subscribable.fn);
}
subscribable.fn.init(Observable);
// Inherit from 'observable'
setPrototypeOfOrExtend$1(Observable, observable.fn);
if (options$1.deferUpdates) {
deferUpdates(Observable);
}
return Observable;
}
// Define prototype for observables
observable.fn = {
equalityComparer: valuesArePrimitiveAndEqual,
peek: function() { return this[observableLatestValue]; },
valueHasMutated: function () { this.notifySubscribers(this[observableLatestValue]); },
valueWillMutate: function () {
this.notifySubscribers(this[observableLatestValue], 'beforeChange');
}
};
// Moved out of "limit" to avoid the extra closure
function limitNotifySubscribers(value, event) {
if (!event || event === defaultEvent) {
this._limitChange(value);
} else if (event === 'beforeChange') {
this._limitBeforeChange(value);
} else {
this._origNotifySubscribers(value, event);
}
}
// Add `limit` function to the subscribable prototype
subscribable.fn.limit = function limit(limitFunction) {
var self = this, selfIsObservable = isObservable(self),
ignoreBeforeChange, previousValue, pendingValue, beforeChange = 'beforeChange';
if (!self._origNotifySubscribers) {
self._origNotifySubscribers = self.notifySubscribers;
self.notifySubscribers = limitNotifySubscribers;
}
var finish = limitFunction(function() {
self._notificationIsPending = false;
// If an observable provided a reference to itself, access it to get the latest value.
// This allows computed observables to delay calculating their value until needed.
if (selfIsObservable && pendingValue === self) {
pendingValue = self();
}
ignoreBeforeChange = false;
if (self.isDifferent(previousValue, pendingValue)) {
self._origNotifySubscribers(previousValue = pendingValue);
}
});
self._limitChange = function(value) {
self._notificationIsPending = ignoreBeforeChange = true;
pendingValue = value;
finish();
};
self._limitBeforeChange = function(value) {
if (!ignoreBeforeChange) {
previousValue = value;
self._origNotifySubscribers(value, beforeChange);
}
};
};
// Note that for browsers that don't support proto assignment, the
// inheritance chain is created manually in the observable constructor
if (canSetPrototype$1) {
setPrototypeOf$1(observable.fn, subscribable.fn);
}
var protoProperty$1 = observable.protoProperty = options$1.protoProperty;
observable.fn[protoProperty$1] = observable;
function isObservable(instance) {
return hasPrototype$1(instance, observable);
}
function unwrap(value) {
return isObservable(value) ? value() : value;
}
function isWriteableObservable(instance) {
// Observable
if ((typeof instance == 'function') && instance[protoProperty$1] === observable)
return true;
// Writeable dependent observable
if ((typeof instance == 'function') /* && (instance[protoProperty] === ko.dependentObservable)*/ && (instance.hasWriteFunction))
return true;
// Anything else
return false;
}
var arrayChangeEventName = 'arrayChange';
function trackArrayChanges(target, options) {
// Use the provided options--each call to trackArrayChanges overwrites the previously set options
target.compareArrayOptions = {};
if (options && typeof options == "object") {
extend$1(target.compareArrayOptions, options);
}
target.compareArrayOptions.sparse = true;
// Only modify the target observable once
if (target.cacheDiffForKnownOperation) {
return;
}
var trackingChanges = false,
cachedDiff = null,
arrayChangeSubscription,
pendingNotifications = 0,
underlyingBeforeSubscriptionAddFunction = target.beforeSubscriptionAdd,
underlyingAfterSubscriptionRemoveFunction = target.afterSubscriptionRemove;
// Watch "subscribe" calls, and for array change events, ensure change tracking is enabled
target.beforeSubscriptionAdd = function (event) {
if (underlyingBeforeSubscriptionAddFunction)
underlyingBeforeSubscriptionAddFunction.call(target, event);
if (event === arrayChangeEventName) {
trackChanges();
}
};
// Watch "dispose" calls, and for array change events, ensure change tracking is disabled when all are disposed
target.afterSubscriptionRemove = function (event) {
if (underlyingAfterSubscriptionRemoveFunction)
underlyingAfterSubscriptionRemoveFunction.call(target, event);
if (event === arrayChangeEventName && !target.hasSubscriptionsForEvent(arrayChangeEventName)) {
arrayChangeSubscription.dispose();
trackingChanges = false;
}
};
function trackChanges() {
// Calling 'trackChanges' multiple times is the same as calling it once
if (trackingChanges) {
return;
}
trackingChanges = true;
// Intercept "notifySubscribers" to track how many times it was called.
var underlyingNotifySubscribersFunction = target['notifySubscribers'];
target['notifySubscribers'] = function(valueToNotify, event) {
if (!event || event === defaultEvent) {
++pendingNotifications;
}
return underlyingNotifySubscribersFunction.apply(this, arguments);
};
// Each time the array changes value, capture a clone so that on the next
// change it's possible to produce a diff
var previousContents = [].concat(target.peek() || []);
cachedDiff = null;
arrayChangeSubscription = target.subscribe(function(currentContents) {
// Make a copy of the current contents and ensure it's an array
currentContents = [].concat(currentContents || []);
// Compute the diff and issue notifications, but only if someone is listening
if (target.hasSubscriptionsForEvent(arrayChangeEventName)) {
var changes = getChanges(previousContents, currentContents);
}
// Eliminate references to the old, removed items, so they can be GCed
previousContents = currentContents;
cachedDiff = null;
pendingNotifications = 0;
if (changes && changes.length) {
target['notifySubscribers'](changes, arrayChangeEventName);
}
});
}
function getChanges(previousContents, currentContents) {
// We try to re-use cached diffs.
// The scenarios where pendingNotifications > 1 are when using rate-limiting or the Deferred Updates
// plugin, which without this check would not be compatible with arrayChange notifications. Normally,
// notifications are issued immediately so we wouldn't be queueing up more than one.
if (!cachedDiff || pendingNotifications > 1) {
cachedDiff = trackArrayChanges.compareArrays(previousContents, currentContents, target.compareArrayOptions);
}
return cachedDiff;
}
target.cacheDiffForKnownOperation = function(rawArray, operationName, args) {
var index, argsIndex;
// Only run if we're currently tracking changes for this observable array
// and there aren't any pending deferred notifications.
if (!trackingChanges || pendingNotifications) {
return;
}
var diff = [],
arrayLength = rawArray.length,
argsLength = args.length,
offset = 0;
function pushDiff(status, value, index) {
return diff[diff.length] = { 'status': status, 'value': value, 'index': index };
}
switch (operationName) {
case 'push':
offset = arrayLength;
case 'unshift':
for (index = 0; index < argsLength; index++) {
pushDiff('added', args[index], offset + index);
}
break;
case 'pop':
offset = arrayLength - 1;
case 'shift':
if (arrayLength) {
pushDiff('deleted', rawArray[offset], offset);
}
break;
case 'splice':
// Negative start index means 'from end of array'. After that we clamp to [0...arrayLength].
// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice
var startIndex = Math.min(Math.max(0, args[0] < 0 ? arrayLength + args[0] : args[0]), arrayLength),
endDeleteIndex = argsLength === 1 ? arrayLength : Math.min(startIndex + (args[1] || 0), arrayLength),
endAddIndex = startIndex + argsLength - 2,
endIndex = Math.max(endDeleteIndex, endAddIndex),
additions = [], deletions = [];
for (index = startIndex, argsIndex = 2; index < endIndex; ++index, ++argsIndex) {
if (index < endDeleteIndex)
deletions.push(pushDiff('deleted', rawArray[index], index));
if (index < endAddIndex)
additions.push(pushDiff('added', args[argsIndex], index));
}
findMovesInArrayComparison$1(deletions, additions);
break;
default:
return;
}
cachedDiff = diff;
};
}
// Expose compareArrays for testing.
trackArrayChanges.compareArrays = compareArrays$1;
// Add the trackArrayChanges extender so we can use
// obs.extend({ trackArrayChanges: true })
extenders.trackArrayChanges = trackArrayChanges;
function observableArray(initialValues) {
initialValues = initialValues || [];
if (typeof initialValues != 'object' || !('length' in initialValues))
throw new Error("The argument passed when initializing an observable array must be an array, or null, or undefined.");
var result = observable(initialValues);
setPrototypeOfOrExtend$1(result, observableArray.fn);
trackArrayChanges(result);
// ^== result.extend({ trackArrayChanges: true })
return result;
}
observableArray.fn = {
remove: function (valueOrPredicate) {
var underlyingArray = this.peek();
var removedValues = [];
var predicate = typeof valueOrPredicate == "function" && !isObservable(valueOrPredicate) ? valueOrPredicate : function (value) { return value === valueOrPredicate; };
for (var i = 0; i < underlyingArray.length; i++) {
var value = underlyingArray[i];
if (predicate(value)) {
if (removedValues.length === 0) {
this.valueWillMutate();
}
removedValues.push(value);
underlyingArray.splice(i, 1);
i--;
}
}
if (removedValues.length) {
this.valueHasMutated();
}
return removedValues;
},
removeAll: function (arrayOfValues) {
// If you passed zero args, we remove everything
if (arrayOfValues === undefined) {
var underlyingArray = this.peek();
var allValues = underlyingArray.slice(0);
this.valueWillMutate();
underlyingArray.splice(0, underlyingArray.length);
this.valueHasMutated();
return allValues;
}
// If you passed an arg, we interpret it as an array of entries to remove
if (!arrayOfValues)
return [];
return this['remove'](function (value) {
return arrayIndexOf$1(arrayOfValues, value) >= 0;
});
},
destroy: function (valueOrPredicate) {
var underlyingArray = this.peek();
var predicate = typeof valueOrPredicate == "function" && !isObservable(valueOrPredicate) ? valueOrPredicate : function (value) { return value === valueOrPredicate; };
this.valueWillMutate();
for (var i = underlyingArray.length - 1; i >= 0; i--) {
var value = underlyingArray[i];
if (predicate(value))
underlyingArray[i]["_destroy"] = true;
}
this.valueHasMutated();
},
destroyAll: function (arrayOfValues) {
// If you passed zero args, we destroy everything
if (arrayOfValues === undefined)
return this.destroy(function() { return true; });
// If you passed an arg, we interpret it as an array of entries to destroy
if (!arrayOfValues)
return [];
return this.destroy(function (value) {
return arrayIndexOf$1(arrayOfValues, value) >= 0;
});
},
indexOf: function (item) {
var underlyingArray = this();
return arrayIndexOf$1(underlyingArray, item);
},
replace: function(oldItem, newItem) {
var index = this.indexOf(oldItem);
if (index >= 0) {
this.valueWillMutate();
this.peek()[index] = newItem;
this.valueHasMutated();
}
}
};
// Note that for browsers that don't support proto assignment, the
// inheritance chain is created manually in the ko.observableArray constructor
if (canSetPrototype$1) {
setPrototypeOf$1(observableArray.fn, observable.fn);
}
// Populate ko.observableArray.fn with read/write functions from native arrays
// Important: Do not add any additional functions here that may reasonably be used to *read* data from the array
// because we'll eval them without causing subscriptions, so ko.computed output could end up getting stale
arrayForEach$1(["pop", "push", "reverse", "shift", "sort", "splice", "unshift"], function (methodName) {
observableArray.fn[methodName] = function () {
// Use "peek" to avoid creating a subscription in any computed that we're executing in the context of
// (for consistency with mutating regular observables)
var underlyingArray = this.peek();
this.valueWillMutate();
this.cacheDiffForKnownOperation(underlyingArray, methodName, arguments);
var methodCallResult = underlyingArray[methodName].apply(underlyingArray, arguments);
this.valueHasMutated();
// The native sort and reverse methods return a reference to the array, but it makes more sense to return the observable array instead.
return methodCallResult === underlyingArray ? this : methodCallResult;
};
});
// Populate ko.observableArray.fn with read-only functions from native arrays
arrayForEach$1(["slice"], function (methodName) {
observableArray.fn[methodName] = function () {
var underlyingArray = this();
return underlyingArray[methodName].apply(underlyingArray, arguments);
};
});
function Node(lhs, op, rhs) {
this.lhs = lhs;
this.op = op;
this.rhs = rhs;
}
/* Just a placeholder */
function LAMBDA() {}
/**
* @ operator - recursively call the identifier if it's a function
* @param {operand} a ignored
* @param {operand} b The variable to be called (if a function) and unwrapped
* @return {value} The result.
*/
function unwrapOrCall(a, b) {
while (typeof b === 'function') { b = b(); }
return b;
}
var operators$1 = {
// unary
'@': unwrapOrCall,
'=>': LAMBDA,
'!': function not(a, b) { return !b; },
'!!': function notnot(a, b) { return !!b; },
'++': function preinc(a, b) { return ++b; },
'--': function preinc(a, b) { return --b; },
// mul/div
'*': function mul(a, b) { return a * b; },
'/': function div(a, b) { return a / b; },
'%': function mod(a, b) { return a % b; },
// sub/add
'+': function add(a, b) { return a + b; },
'-': function sub(a, b) { return a - b; },
// relational
'<': function lt(a, b) { return a < b; },
'<=': function le(a, b) { return a <= b; },
'>': function gt(a, b) { return a > b; },
'>=': function ge(a, b) { return a >= b; },
// TODO: 'in': function (a, b) { return a in b; },
// TODO: 'instanceof': function (a, b) { return a instanceof b; },
// equality
'==': function equal(a, b) { return a === b; },
'!=': function ne(a, b) { return a !== b; },
'===': function sequal(a, b) { return a === b; },
'!==': function sne(a, b) { return a !== b; },
// Fuzzy (bad) equality
'~==': function equal(a, b) { return a == b; },
'~!=': function ne(a, b) { return a != b; },
// bitwise
'&': function bit_and(a, b) { return a & b; },
'^': function xor(a, b) { return a ^ b; },
'|': function bit_or(a, b) { return a | b; },
// logic
'&&': function logic_and(a, b) { return a && b; },
'||': function logic_or(a, b) { return a || b; },
// conditional/ternary
'?': function ternary(a, b) { return Node.value_of(a ? b.yes : b.no); }
};
/* Order of precedence from:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence#Table
*/
// Our operator - unwrap/call
operators$1['@'].precedence = 21;
// lambda
operators$1['=>'].precedence = 20;
// Logical not
operators$1['!'].precedence = 16;
operators$1['!!'].precedence = 16; // explicit double-negative
// Prefix inc/dec
operators$1['++'].precedence = 16;
operators$1['--'].precedence = 16;
// logic
operators$1['||'].precedence = 14;
operators$1['&&'].precedence = 13;
// mul/div/remainder
operators$1['%'].precedence = 14;
operators$1['*'].precedence = 14;
operators$1['/'].precedence = 14;
// add/sub
operators$1['+'].precedence = 13;
operators$1['-'].precedence = 13;
// bitwise
operators$1['|'].precedence = 12;
operators$1['^'].precedence = 11;
operators$1['&'].precedence = 10;
// comparison
operators$1['<'].precedence = 11;
operators$1['<='].precedence = 11;
operators$1['>'].precedence = 11;
operators$1['>='].precedence = 11;
// operators['in'].precedence = 8;
// operators['instanceof'].precedence = 8;
// equality
operators$1['=='].precedence = 10;
operators$1['!='].precedence = 10;
operators$1['==='].precedence = 10;
operators$1['!=='].precedence = 10;
// Fuzzy operators for backwards compat with the "evil twins"
// http://stackoverflow.com/questions/359494
operators$1['~=='].precedence = 10;
operators$1['~!='].precedence = 10;
// Conditional/ternary
operators$1['?'].precedence = 4;
Node.operators = operators$1;
Node.prototype.get_leaf_value = function (leaf, member_of) {
if (typeof(leaf) === 'function') {
// Expressions on observables are nonsensical, so we unwrap any
// function values (e.g. identifiers).
return unwrap(leaf());
}
// primitives
if (typeof(leaf) !== 'object') {
return member_of ? member_of[leaf] : leaf;
}
if (leaf === null) { return leaf; }
// Identifiers and Expressions
if (leaf[Node.isExpressionOrIdentifierSymbol]) {
// lhs is passed in as the parent of the leaf. It will be defined in
// cases like a.b.c as 'a' for 'b' then as 'b' for 'c'.
return unwrap(leaf.get_value(member_of));
}
if (leaf instanceof Node) {
return leaf.get_node_value(member_of);
}
throw new Error("Invalid type of leaf node: " + leaf);
};
/**
* Return a function that calculates and returns an expression's value
* when called.
* @param {array} ops The operations to perform
* @return {function} The function that calculates the expression.
*
* Note that for a lambda, we do not evaluate the RHS expression until
* the lambda is called.
*/
Node.prototype.get_node_value = function () {
var node = this;
if (node.op === LAMBDA) {
return function () { return node.get_leaf_value(node.rhs); };
}
return node.op(node.get_leaf_value(node.lhs),
node.get_leaf_value(node.rhs));
};
//
// Class variables.
//
Node.isExpressionOrIdentifierSymbol = createSymbolOrString("isExpressionOrIdentifierSymbol");
Node.value_of = function value_of(item) {
if (item && item[Node.isExpressionOrIdentifierSymbol]) {
return item.get_value();
}
return item;
};
/**
* Convert an array of nodes to an executable tree.
* @return {object} An object with a `lhs`, `rhs` and `op` key, corresponding
* to the left hand side, right hand side, and
* operation function.
*/
Node.create_root = function create_root(nodes) {
var root, leaf, op, value;
// Prime the leaf = root node.
leaf = root = new Node(nodes.shift(), nodes.shift(), nodes.shift());
while (nodes) {
op = nodes.shift();
value = nodes.shift();
if (!op) {
break;
}
if (op.precedence < root.op.precedence) {
// rebase
root = new Node(root, op, value);
leaf = root;
} else {
leaf.rhs = new Node(leaf.rhs, op, value);
leaf = leaf.rhs;
}
}
// console.log("tree", root)
return root;
};
function Expression(nodes) {
this.nodes = nodes;
this.root = Node.create_root(nodes);
}
// Exports for testing.
Expression.operators = Node.operators;
Expression.Node = Node;
/**
* Return the value of `this` Expression instance.
*
*/
Expression.prototype.get_value = function () {
if (!this.root) {
this.root = Node.create_root(this.nodes);
}
return this.root.get_node_value();
};
Expression.prototype[Node.isExpressionOrIdentifierSymbol] = true;
function Arguments(parser, args) {
this.parser = parser;
this.args = args;
}
Arguments.prototype.get_value = function get_value(/* parent */) {
var dereffed_args = [];
for (var i = 0, j = this.args.length; i < j; ++i) {
dereffed_args.push(Node.value_of(this.args[i]));
}
return dereffed_args;
};
Arguments.prototype[Node.isExpressionOrIdentifierSymbol] = true;
function Identifier(parser, token, dereferences) {
this.token = token;
this.dereferences = dereferences;
this.parser = parser;
}
/**
* Return the value of the given
*
* @param {Object} parent (optional) source of the identifier e.g. for
* membership. e.g. `a.b`, one would pass `a` in as
* the parent when calling lookup_value for `b`.
* @return {Mixed} The value of the token for this Identifier.
*/
Identifier.prototype.lookup_value = function (parent) {
var token = this.token,
parser = this.parser,
$context = parser.context,
$data = $context.$data || {},
globals = parser.globals || {};
if (parent) {
return Node.value_of(parent)[token];
}
// short circuits
switch (token) {
case '$element': return parser.node;
case '$context': return $context;
case '$data': return $context.$data;
default:
}
// instanceof Object covers 1. {}, 2. [], 3. function() {}, 4. new *; it excludes undefined, null, primitives.
if ($data instanceof Object && token in $data) { return $data[token]; }
if (token in $context) { return $context[token]; }
if (token in globals) { return globals[token]; }
throw new Error("The variable \"" + token + "\" was not found on $data, $context, or knockout options.bindingGlobals.");
};
/**
* Apply all () and [] functions on the identifier to the lhs value e.g.
* a()[3] has deref functions that are essentially this:
* [_deref_call, _deref_this where this=3]
*
* @param {mixed} value Should be an object.
* @return {mixed} The dereferenced value.
*/
Identifier.prototype.dereference = function (value) {
var member,
refs = this.dereferences || [],
parser = this.parser,
$context = parser.context || {},
$data = $context.$data || {},
self = { // top-level `this` in function calls
$context: $context,
$data: $data,
globals: parser.globals || {},
$element: parser.node
},
last_value, // becomes `this` in function calls to object properties.
i, n;
for (i = 0, n = refs.length; i < n; ++i) {
member = Node.value_of(refs[i]);
if (typeof value === 'function' && refs[i] instanceof Arguments) {
// fn(args)
value = value.apply(last_value || self, member);
last_value = value;
} else {
// obj[x] or obj.x dereference. Note that obj may be a function.
last_value = value;
value = Node.value_of(value[member]);
}
}
// With obj.x, make `obj = this`
if (typeof value === 'function' && n > 0 && last_value !== value) {
return value.bind(last_value);
}
return value;
};
/**
* Return the value as one would get it from the top-level i.e.
* $data.token/$context.token/globals.token; this does not return intermediate
* values on a chain of members i.e. $data.hello.there -- requesting the
* Identifier('there').value will return $data/$context/globals.there.
*
* This will dereference using () or [arg] member.
* @param {object | Identifier | Expression} parent
* @return {mixed} Return the primitive or an accessor.
*/
Identifier.prototype.get_value = function (parent) {
return this.dereference(this.lookup_value(parent));
};
Identifier.prototype.assign = function assign(object, property, value) {
if (isWriteableObservable(object[property])) {
object[property](value);
} else if (!isObservable(object[property])) {
object[property] = value;
}
};
/**
* Set the value of the Identifier.
*
* @param {Mixed} new_value The value that Identifier is to be set to.
*/
Identifier.prototype.set_value = function (new_value) {
var parser = this.parser,
$context = parser.context,
$data = $context.$data || {},
globals = parser.globals || {},
refs = this.dereferences || [],
leaf = this.token,
i, n, root;
if (Object.hasOwnProperty.call($data, leaf)) {
root = $data;
} else if (Object.hasOwnProperty.call($context, leaf)) {
root = $context;
} else if (Object.hasOwnProperty.call(globals, leaf)) {
root = globals;
} else {
throw new Error("Identifier::set_value -- " +
"The property '" + leaf + "' does not exist " +
"on the $data, $context, or globals.");
}
// Degenerate case. {$data|$context|global}[leaf] = something;
n = refs.length;
if (n === 0) {
this.assign(root, leaf, new_value);
return;
}
// First dereference is {$data|$context|global}[token].
root = root[leaf];
// We cannot use this.dereference because that gives the leaf; to evoke
// the ES5 setter we have to call `obj[leaf] = new_value`
for (i = 0; i < n - 1; ++i) {
leaf = refs[i];
if (leaf instanceof Arguments) {
root = root();
} else {
root = root[Node.value_of(leaf)];
}
}
// We indicate that a dereference is a function when it is `true`.
if (refs[i] === true) {
throw new Error("Cannot assign a value to a function.");
}
// Call the setter for the leaf.
if (refs[i]) {
this.assign(root, Node.value_of(refs[i]), new_value);
}
};
Identifier.prototype[Node.isExpressionOrIdentifierSymbol] = true;
function Ternary(yes, no) {
this.yes = yes;
this.no = no;
}
Ternary.prototype[Node.isExpressionOrIdentifierSymbol] = true;
Ternary.prototype.get_value = function () { return this; };
var escapee = {
"'": "'",
'"': '"',
"`": "`",
'\\': '\\',
'/': '/',
'$': '$',
b: '\b',
f: '\f',
n: '\n',
r: '\r',
t: '\t'
};
var operators = Node.operators;
/**
* Construct a new Parser instance with new Parser(node, context)
* @param {Node} node The DOM element from which we parsed the
* content.
* @param {object} context The Knockout context.
* @param {object} globals An object containing any desired globals.
*/
function Parser(node, context, globals) {
this.node = node;
this.context = context;
this.globals = globals || {};
}
// Exposed for testing.
Parser.Expression = Expression;
Parser.Identifier = Identifier;
Parser.Arguments = Arguments;
Parser.Node = Node;
Parser.prototype.white = function () {
var ch = this.ch;
while (ch && ch <= ' ') {
ch = this.next();
}
return ch;
};
Parser.prototype.next = function (c) {
if (c && c !== this.ch) {
this.error("Expected '" + c + "' but got '" + this.ch + "'");
}
this.ch = this.text.charAt(this.at);
this.at += 1;
return this.ch;
};
Parser.prototype.lookahead = function() {
return this.text[this.at];
};
Parser.prototype.error = function (m) {
throw {
name: 'SyntaxError',
message: m,
at: this.at,
text: this.text
};
};
Parser.prototype.name = function () {
// A name of a binding
var name = '',
enclosed_by;
this.white();
var ch = this.ch;
if (ch === "'" || ch === '"') {
enclosed_by = ch;
ch = this.next();
}
while (ch) {
if (enclosed_by && ch === enclosed_by) {
this.white();
ch = this.next();
if (ch !== ':' && ch !== ',') {
this.error(
"Object name: " + name + " missing closing " + enclosed_by
);
}
return name;
} else if (ch === ':' || ch <= ' ' || ch === ',') {
return name;
}
name += ch;
ch = this.next();
}
return name;
};
Parser.prototype.number = function () {
var number,
string = '',
ch = this.ch;
if (ch === '-') {
string = '-';
ch = this.next('-');
}
while (ch >= '0' && ch <= '9') {
string += ch;
ch = this.next();
}
if (ch === '.') {
string += '.';
ch = this.next();
while (ch && ch >= '0' && ch <= '9') {
string += ch;
ch = this.next();
}
}
if (ch === 'e' || ch === 'E') {
string += ch;
ch = this.next();
if (ch === '-' || ch === '+') {
string += ch;
ch = this.next();
}
while (ch >= '0' && ch <= '9') {
string += ch;
ch = this.next();
}
}
number = +string;
if (!isFinite(number)) {
options.onError(new Error("Bad number: " + number + " in " + string));
} else {
return number;
}
};
/**
* Add a property to 'object' that equals the given value.
* @param {Object} object The object to add the value to.
* @param {String} key object[key] is set to the given value.
* @param {mixed} value The value, may be a primitive or a function. If a
* function it is unwrapped as a property.
*/
Parser.prototype.object_add_value = function (object, key, value) {
if (value && value[Node.isExpressionOrIdentifierSymbol]) {
Object.defineProperty(object, key, {
get: function () {
return value.get_value();
},
enumerable: true
});
} else {
// primitives
object[key] = value;
}
};
Parser.prototype.object = function () {
var key,
object = {},
ch = this.ch;
if (ch === '{') {
this.next('{');
ch = this.white();
if (ch === '}') {
ch = this.next('}');
return object;
}
while (ch) {
if (ch === '"' || ch === "'" || ch === "`") {
key = this.string();
} else {
key = this.name();
}
this.white();
ch = this.next(':');
if (Object.hasOwnProperty.call(object, key)) {
this.error('Duplicate key "' + key + '"');
}
this.object_add_value(object, key, this.expression());
ch = this.white();
if (ch === '}') {
ch = this.next('}');
return object;
}
this.next(',');
ch = this.white();
}
}
this.error("Bad object");
};
/**
* Read up to delim and return the string
* @param {string} delim The delimiter, either ' or "
* @return {string} The string read.
*/
Parser.prototype.read_string = function (delim) {
var string = '',
nodes = [''],
plus_op = operators['+'],
hex,
i,
uffff,
interpolate = delim === "`",
ch = this.next();
while (ch) {
if (ch === delim) {
ch = this.next();
if (interpolate) { nodes.push(plus_op); }
nodes.push(string);
return nodes;
}
if (ch === '\\') {
ch = this.next();
if (ch === 'u') {
uffff = 0;
for (i = 0; i < 4; i += 1) {
hex = parseInt(ch = this.next(), 16);
if (!isFinite(hex)) {
break;
}
uffff = uffff * 16 + hex;
}
string += String.fromCharCode(uffff);
} else if (typeof escapee[ch] === 'string') {
string += escapee[ch];
} else {
break;
}
} else if (interpolate && ch === "$") {
ch = this.next();
if (ch === '{') {
this.next('{');
nodes.push(plus_op);
nodes.push(string);
nodes.push(plus_op);
nodes.push(this.expression());
string = '';
// this.next('}');
} else {
string += "$" + ch;
}
} else {
string += ch;
}
ch = this.next();
}
this.error("Bad string");
};
Parser.prototype.string = function () {
var ch = this.ch;
if (ch === '"') {
return this.read_string('"').join('');
} else if (ch === "'") {
return this.read_string("'").join('');
} else if (ch === "`") {
return Node.create_root(this.read_string("`"));
}
this.error("Bad string");
};
Parser.prototype.array = function () {
var array = [],
ch = this.ch;
if (ch === '[') {
ch = this.next('[');
this.white();
if (ch === ']') {
ch = this.next(']');
return array;
}
while (ch) {
array.push(this.expression());
ch = this.white();
if (ch === ']') {
ch = this.next(']');
return array;
}
this.next(',');
ch = this.white();
}
}
this.error("Bad array");
};
Parser.prototype.value = function () {
var ch;
this.white();
ch = this.ch;
switch (ch) {
case '{': return this.object();
case '[': return this.array();
case '"': case "'": case "`": return this.string();
case '-': return this.number();
default:
return ch >= '0' && ch <= '9' ? this.number() : this.identifier();
}
};
/**
* Get the function for the given operator.
* A `.precedence` value is added to the function, with increasing
* precedence having a higher number.
* @return {function} The function that performs the infix operation
*/
Parser.prototype.operator = function () {
var op = '',
op_fn,
ch = this.white();
while (ch) {
if (is_identifier_char(ch) || ch <= ' ' || ch === '' ||
ch === '"' || ch === "'" || ch === '{' || ch === '[' ||
ch === '(' || ch === "`") {
break;
}
op += ch;
ch = this.next();
// An infix followed by the prefix e.g. a + @b
// TODO: other prefix unary operators
if (ch === '@') {
break;
}
}
if (op !== '') {
op_fn = operators[op];
if (!op_fn) {
this.error("Bad operator: '" + op + "'.");
}
}
return op_fn;
};
/**
* Filters
* Returns what the Node interprets as an "operator".
* e.g.
* <span data-bind="text: name | fit:20 | uppercase"></span>
*/
Parser.prototype.filter = function() {
var ch = this.next(),
args = [],
next_filter = function(v) { return v; },
name = this.name();
if (!options.filters[name]) {
options.onError("Cannot find filter by the name of: " + name);
}
while (ch) {
if (ch === ':') {
this.next();
args.push(this.expression());
}
if (ch === '|') {
next_filter = this.filter();
break;
}
ch = this.white();
}
var filter = function filter(value) {
var arg_values = [value];
for (var i = 0, j = args.length; i < j; ++i) {
arg_values.push(Node.value_of(args[i]));
}
return next_filter(options.filters[name].apply(null, arg_values));
};
// Lowest precedence.
filter.precedence = 1;
return filter;
};
/**
* Parse an expression – builds an operator tree, in something like
* Shunting-Yard.
* See: http://en.wikipedia.org/wiki/Shunting-yard_algorithm
*
* @return {function} A function that computes the value of the expression
* when called or a primitive.
*/
Parser.prototype.expression = function (filterable) {
var op,
nodes = [],
filters = [],
ch = this.white();
while (ch) {
// unary prefix operators
op = this.operator();
if (op) {
nodes.push(undefined); // LHS Tree node.
nodes.push(op);
ch = this.white();
}
if (ch === '(') {
this.next();
nodes.push(this.expression());
this.next(')');
} else {
nodes.push(this.value());
}
ch = this.white();
if (ch === ':' || ch === '}' || ch === ',' || ch === ']' ||
ch === ')' || ch === '' || ch === '`') {
break;
}
// filters
if (ch === '|' && this.lookahead() !== '|' && filterable) {
nodes.push(this.filter());
nodes.push(undefined);
break;
}
// infix operators
op = this.operator();
if (op === operators['?']) {
this.ternary(nodes);
break;
} else if (op) {
nodes.push(op);
}
ch = this.white();
}
if (nodes.length === 0) {
return undefined;
}
if (nodes.length === 1) {
return nodes[0];
}
return new Expression(nodes, filters);
};
Parser.prototype.ternary = function(nodes) {
var ternary = new Ternary();
ternary.yes = this.expression();
this.next(":");
ternary.no = this.expression();
nodes.push(operators['?']);
nodes.push(ternary);
};
/**
* Parse the arguments to a function, returning an Array.
*
*/
Parser.prototype.func_arguments = function () {
var args = [],
ch = this.next('(');
while(ch) {
ch = this.white();
if (ch === ')') {
this.next(')');
return new Arguments(this, args);
} else {
args.push(this.expression());
ch = this.white();
}
if (ch !== ')') { this.next(','); }
}
this.error("Bad arguments to function");
};
/**
* A dereference applies to an identifer, being either a function
* call "()" or a membership lookup with square brackets "[member]".
* @return {fn or undefined} Dereference function to be applied to the
* Identifier
*/
Parser.prototype.dereference = function () {
var member,
ch = this.white();
while (ch) {
if (ch === '(') {
// a(...) function call
return this.func_arguments();
} else if (ch === '[') {
// a[x] membership
this.next('[');
member = this.expression();
this.white();
this.next(']');
return member;
} else if (ch === '.') {
// a.x membership
member = '';
this.next('.');
ch = this.white();
while (ch) {
if (!is_identifier_char(ch)) {
break;
}
member += ch;
ch = this.next();
}
return member;
} else {
break;
}
}
return;
};
Parser.prototype.identifier = function () {
var token = '', ch, deref, dereferences = [];
ch = this.white();
while (ch) {
if (!is_identifier_char(ch)) {
break;
}
token += ch;
ch = this.next();
}
switch (token) {
case 'true': return true;
case 'false': return false;
case 'null': return null;
case 'undefined': return void 0;
default:
}
while (ch) {
deref = this.dereference();
if (deref !== undefined) {
dereferences.push(deref);
} else {
break;
}
}
return new Identifier(this, token, dereferences);
};
Parser.prototype.read_bindings = function () {
var key,
bindings = {},
sep,
expr,
ch = this.ch;
while (ch) {
key = this.name();
sep = this.white();
if (!sep || sep === ',') {
if (sep) {
ch = this.next(',');
} else {
ch = '';
}
// A "bare" binding e.g. "text"; substitute value of 'null'
// so it becomes "text: null".
bindings[key] = null;
} else {
if (key.indexOf('.') !== -1) {
// Namespaced – i.e.
// `attr.css: x` becomes `attr: { css: x }`
// ^^^ - key
key = key.split('.');
bindings[key[0]] = bindings[key[0]] || {};
if (key.length !== 2) {
options.onError("Binding " + key + " should have two parts (a.b).");
} else if (bindings[key[0]].constructor !== Object) {
options.onError("Binding " + key[0] + "." + key[1] + " paired with a non-object.");
}
ch = this.next(':');
this.object_add_value(bindings[key[0]], key[1], this.expression(true));
} else {
ch = this.next(':');
if (bindings[key] && typeof bindings[key] === 'object' && bindings[key].constructor === Object) {
// Extend a namespaced bindings e.g. we've previously seen
// on.x, now we're seeing on: { 'abc' }.
expr = this.expression(true);
if (typeof expr !== 'object' || expr.constructor !== Object) {
options.onError("Expected plain object for " + key + " value.");
} else {
extend(bindings[key], expr);
}
} else {
bindings[key] = this.expression(true);
}
}
this.white();
if (this.ch) {
ch = this.next(',');
} else {
ch = '';
}
}
}
return bindings;
};
/**
* Convert result[name] from a value to a function (i.e. `valueAccessor()`)
* @param {object} result [Map of top-level names to values]
* @return {object} [Map of top-level names to functions]
*
* Accessors may be one of (below) constAccessor, identifierAccessor,
* expressionAccessor, or nodeAccessor.
*/
Parser.prototype.convert_to_accessors = function (result) {
objectForEach(result, function (name, value) {
if (value instanceof Identifier) {
// Return a function that, with no arguments returns
// the value of the identifier, otherwise sets the
// value of the identifier to the first given argument.
Object.defineProperty(result, name, {
value: function (optionalValue, options) {
if (arguments.length === 0) {
return value.get_value();
}
if (options && options.onlyIfChanged && optionalValue === value.get_value()) {
return;
}
return value.set_value(optionalValue);
}
});
} else if (value instanceof Expression) {
result[name] = function expressionAccessor() {
return value.get_value();
};
} else if (value instanceof Node) {
result[name] = function nodeAccessor() {
return value.get_node_value();
};
} else if (typeof(value) !== 'function') {
result[name] = function constAccessor() {
return clonePlainObjectDeep(value);
};
} else if (value === 'function') {
result[name] = function functionAccessor() {
return value();
};
}
});
return result;
};
/**
* Get the bindings as name: accessor()
* @param {string} source The binding string to parse.
* @return {object} Map of name to accessor function.
*/
Parser.prototype.parse = function (source) {
this.text = (source || '').trim();
this.at = 0;
this.ch = ' ';
if (!this.text) {
return null;
}
try {
var result = this.read_bindings();
} catch (e) {
// `e` may be 1.) a proper Error; 2.) a parsing error; or 3.) a string.
var emsg = typeof e === Error ?
"\nMessage: <" + e.name + "> " + e.message :
typeof e === 'object' && 'at' in e ?
"\n" + e.name + " " + e.message + " of \n"
+ " " + e.text + "\n"
+ Array(e.at).join(" ") + "_/ 🔥 \\_\n"
: e;
options.onError(new Error(emsg));
}
this.white();
if (this.ch) {
this.error("Syntax Error");
}
return this.convert_to_accessors(result);
};
/**
* Determine if a character is a valid item in an identifier.
* Note that we do not check whether the first item is a number, nor do we
* support unicode identifiers here.
*
* See: http://docstore.mik.ua/orelly/webprog/jscript/ch02_07.htm
* @param {String} ch The character
* @return {Boolean} True if [A-Za-z0-9_]
*/
function is_identifier_char(ch) {
return (ch >= 'A' && ch <= 'Z') ||
(ch >= 'a' && ch <= 'z') ||
(ch >= '0' && ch <= 9) ||
ch === '_' || ch === '$';
}
function Provider(options) {
options = options || {};
this.otherProviders = options.otherProviders || [];
this.bindingPreProcessors = options.bindingPreProcessors || [];
this.nodePreProcessors = options.preprocessors || [];
// the binding classes -- defaults to the bind's
// bindingsHandlers
var bindingHandlers = this.bindingHandlers = {};
addGetterSetter(bindingHandlers);
// Cache the result of parsing binding strings.
// TODO
// this.cache = {};
}
/** Add non-enumerable `get` and `set` properties.
*/
// bindingHandlers.set(nameOrObject, value)
// ---
// Examples:
//
// bindingHandlers.set('name', bindingDefinition)
// bindingHandlers.set({ text: textBinding, input: inputBinding })
function addGetterSetter(bindingHandlersObject) {
Object.defineProperties(bindingHandlersObject, {
'set': {
configurable: true,
value: function setBindingHandler(nameOrObject, value) {
if (typeof nameOrObject === 'string') {
bindingHandlersObject[nameOrObject] = value;
} else if (typeof nameOrObject === 'object') {
if (value !== undefined) {
options.onError(
new Error("Given extraneous `value` parameter (first param should be a string, but it was an object)." + nameOrObject));
}
extend(bindingHandlersObject, nameOrObject);
} else {
options.onError(
new Error("Given a bad binding handler type: " + nameOrObject));
}
}
},
'get': {
configurable: true,
value: function getBindingHandler(name) {
// NOTE: Strict binding checking ought to occur here.
return bindingHandlersObject[name];
}
}
});
}
function nodeHasBindings(node) {
if (node.nodeType === node.ELEMENT_NODE) {
if (node.getAttribute(options.defaultBindingAttribute)) { return true; }
} else if (node.nodeType === node.COMMENT_NODE) {
if (options.allowVirtualElements &&
isStartComment(node)) {
return true;
}
}
for (var i = 0, j = this.otherProviders.length; i < j; i++) {
if (this.otherProviders[i].nodeHasBindings(node)) { return true; }
}
return false;
}
function getBindingsString(node) {
switch (node.nodeType) {
case node.ELEMENT_NODE:
return node.getAttribute(options.defaultBindingAttribute);
case node.COMMENT_NODE:
return virtualNodeBindingValue(node);
default:
return null;
}
}
// Note we do not seem to need both getBindings and getBindingAccessors; just
// the latter appears to suffice.
//
// Return the name/valueAccessor pairs.
// (undocumented replacement for getBindings)
// see https://github.com/knockout/knockout/pull/742
function getBindingAccessors(node, context) {
var bindings = {},
parser = new Parser(node, context, options.bindingGlobals),
binding_string = this.getBindingsString(node);
if (binding_string) {
binding_string = this.preProcessBindings(binding_string);
bindings = parser.parse(binding_string || '');
}
arrayForEach(this.otherProviders, function(p) {
extend(bindings, p.getBindingAccessors(node, context, parser, bindings));
});
objectForEach(bindings, this.preProcessBindings.bind(this));
return bindings;
}
/** Call bindingHandler.preprocess on each respective binding string.
*
* The `preprocess` property of bindingHandler must be a static
* function (i.e. on the object or constructor).
*/
function preProcessBindings(bindingString) {
var results = [];
var bindingHandlers = this.bindingHandlers;
var preprocessed;
// Check for a Provider.preprocessNode property
if (typeof this.preprocessNode === 'function') {
preprocessed = this.preprocessNode(bindingString, this);
if (preprocessed) { bindingString = preprocessed; }
}
for (var i = 0, j = this.bindingPreProcessors.length; i < j; ++i) {
preprocessed = this.bindingPreProcessors[i](bindingString, this);
if (preprocessed) { bindingString = preprocessed; }
}
function addBinding(name, value) {
results.push("'" + name + "':" + value);
}
function processBinding(key, value) {
// Get the "on" binding from "on.click"
var handler = bindingHandlers.get(key.split('.')[0]);
if (handler && typeof handler.preprocess === 'function') {
value = handler.preprocess(value, key, processBinding);
}
addBinding(key, value);
}
arrayForEach(parseObjectLiteral(bindingString), function(keyValueItem) {
processBinding(
keyValueItem.key || keyValueItem.unknown,
keyValueItem.value
);
});
return results.join(',');
}
/**
* Run the preprocessors on a given node
* @param {HTMLElement} node The node to be modified/preprocessed.
* @return {Array<HTMLElement>} An array of nodes.
*
* FIXME: This only lets one node preprocessor modify the nodes; more
* generically we want to be able to have a nested node->nodes for each
* preprocessor, eventually flattening a tree-like result.
*/
function preprocessNode(node, startingPreprocessorIndex) {
var newNodes;
for (var i = startingPreprocessorIndex || 0, j = this.nodePreProcessors.length; i < j; i++) {
newNodes = this.nodePreProcessors[i].call(this, node, this);
if (newNodes) { return newNodes; }
}
return;
}
// addProvider(provider instance)
// ---
//
// Other providers (such as ko.components) can be added with the `addProvider`
// call. Each provider is expected to have a `nodeHasBindings` and a
// `getBindingAccessors` function.
//
function addProvider(p) { this.otherProviders.push(p); }
function clearProviders() { this.otherProviders.length = 0; }
function addBindingPreprocessor(fn) { this.bindingPreProcessors.push(fn); }
function clearBindingPreprocessors() { this.bindingPreProcessors.length = 0; }
function addNodePreprocessor(fn) { this.nodePreProcessors.push(fn); }
function clearNodePreprocessors() { this.nodePreProcessors.length = 0; }
extend(Provider.prototype, {
nodeHasBindings: nodeHasBindings,
getBindingAccessors: getBindingAccessors,
getBindingsString: getBindingsString,
Parser: Parser,
preProcessBindings: preProcessBindings,
preprocessNode: preprocessNode,
addProvider: addProvider,
addBindingPreprocessor: addBindingPreprocessor,
clearProviders: clearProviders,
clearBindingPreprocessors: clearBindingPreprocessors,
addNodePreprocessor: addNodePreprocessor,
clearNodePreprocessors: clearNodePreprocessors,
});
exports.parseObjectLiteral = parseObjectLiteral;
exports.Provider = Provider;
Object.defineProperty(exports, '__esModule', { value: true });
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment