UNPKG

@danielkalen/simplybind

Version:

Magically simple, framework-less one-way/two-way data binding for frontend/backend in ~5kb.

1,409 lines (1,405 loc) 50.9 kB
// Generated by CoffeeScript 1.10.0 (function() { var Binding, BindingInterface, BindingInterfacePrivate, BindingInterfacePublic, BindingMulti, SimplyBind, applyPlaceholders, arrayIncludes, arrayMutatorMethods, boundInstances, cache, changeEvent, checkIf, computeProxied, errors, escapeRegEx, extendState, genID, genObj, getErrSource, globalOptions, handleUpdateFromEvent, i, len, maybeUpdateDep, methodName, methodNames, pholderRegEx, pholderRegExSplit, proto, setOptionsForBinding, setPholderRegEx, throwError, throwErrorBadArg, throwErrorUnavail, throwWarning; changeEvent = function() { var event; event = document.createEvent('Event'); event.initEvent('change', true, false); event._sb = true; return event; }; arrayMutatorMethods = ['push', 'pop', 'shift', 'unshift', 'splice', 'reverse', 'sort']; genID = function() { return 'sb_' + (Math.floor((1 + Math.random()) * 1000000000000).toString(16)); }; genObj = function() { return Object.create(null); }; arrayIncludes = function(arr, item) { return arr.indexOf(item) !== -1; }; checkIf = { isDefined: function(subject) { return typeof subject !== 'undefined'; }, isObject: function(subject) { return typeof subject === 'object' && subject; }, isString: function(subject) { return typeof subject === 'string'; }, isNumber: function(subject) { return typeof subject === 'number'; }, isFunction: function(subject) { return typeof subject === 'function'; }, isBindingInterface: function(subject) { return subject instanceof BindingInterface; }, isBinding: function(subject) { return subject instanceof Binding; }, isIterable: function(subject) { return checkIf.isObject(subject) && checkIf.isNumber(subject.length); }, isDom: function(subject) { return subject.nodeName && subject.nodeType === 1; }, isDomInput: function(subject) { var nodeName; nodeName = subject.nodeName.toUpperCase(); return nodeName === 'INPUT' || nodeName === 'TEXTAREA' || nodeName === 'SELECT'; }, isDomRadio: function(subject) { return subject.type === 'radio'; }, isDomCheckbox: function(subject) { return subject.type === 'checkbox'; }, isElCollection: function(subject) { return (subject instanceof NodeList) || (subject instanceof HTMLCollection) || (checkIf.isDefined(jQuery) && subject instanceof jQuery); }, domElsAreSame: function(iterable) { var itemsWithSameType, type; type = iterable[0].type; itemsWithSameType = [].filter.call(iterable, function(item) { return item.type === type; }); return itemsWithSameType.length === iterable.length; } }; setOptionsForBinding = function(binding, newOptions) { var option, value; for (option in newOptions) { value = newOptions[option]; if (checkIf.isDefined(globalOptions[option])) { binding.options[option] = value; } } return binding.makePropertyLive(); }; extendState = function(base, stateToInherit) { var i, key, len, results1, stateMapping; stateMapping = Object.keys(stateToInherit); results1 = []; for (i = 0, len = stateMapping.length; i < len; i++) { key = stateMapping[i]; results1.push(base[key] = stateToInherit[key]); } return results1; }; cache = { textNodes: {}, set: function(B, isSimpleObject) { var propsMap, selector; if (isSimpleObject) { return Object.defineProperty(B.object, '_sb_ID', { 'configurable': true, 'value': B.ID }); } else { selector = B.selector; if (B.object._sb_map) { return B.object._sb_map[selector] = B.ID; } else { propsMap = {}; propsMap[selector] = B.ID; return Object.defineProperty(B.object, '_sb_map', { 'configurable': true, 'value': propsMap }); } } }, get: function(object, isSimpleObject, selector, isMultiChoice) { var sampleItem; if (isSimpleObject) { return boundInstances[object._sb_ID]; } else { if (isMultiChoice && object[0]._sb_map) { sampleItem = boundInstances[object[0]._sb_map[selector]]; if (sampleItem.groupBinding) { return sampleItem.groupBinding; } } if (object._sb_map && object._sb_map[selector]) { return boundInstances[object._sb_map[selector]]; } } } }; escapeRegEx = /[.*+?^${}()|[\]\\]/g; pholderRegEx = null; pholderRegExSplit = null; setPholderRegEx = function() { var end, middle, start; start = globalOptions.placeholder[0].replace(escapeRegEx, '\\$&'); end = globalOptions.placeholder[1].replace(escapeRegEx, '\\$&'); middle = "[^" + end + "]+"; pholderRegEx = new RegExp(start + "(" + middle + ")" + end, 'g'); return pholderRegExSplit = new RegExp("" + start + middle + end, 'g'); }; applyPlaceholders = function(contexts, values, indexMap) { var contextPart, i, index, len, output; output = ''; for (index = i = 0, len = contexts.length; i < len; index = ++i) { contextPart = contexts[index]; output += contextPart; if (indexMap[index]) { output += values[indexMap[index]]; } } return output; }; throwError = function(errorName) { throw new Error('SimplyBind: ' + (errors[errorName] || errorName)); }; throwWarning = function(warningName, depth) { var errSource, warn; if (!globalOptions.silent) { errSource = getErrSource(depth); warn = errors[warningName]; warn += "\n\n" + errSource; return console.warn('SimplyBind: ' + warn); } }; throwErrorUnavail = function(methodName) { return throwError("You can't use/invoke ." + methodName + "() at this stage", true); }; throwErrorBadArg = function(methodName, arg) { return throwError("Invalid argument/s (" + arg + ") passed to ." + methodName + "()", true); }; getErrSource = function(depth) { return ((new Error).stack || '').split('\n').slice(depth + 3).join('\n'); }; errors = { invalidParamName: "SimplyBind() and .to() only accept a function, an array, a bound object, a string, or a number.", fnOnly: "Only functions are allowed for .transform/.condition/All()", badEventArg: "Invalid argument number in .ofEvent()", onlyOneDOMElement: "You can only pass a single DOM element to a binding", mixedElList: "'checked' of Mixed list of element cannot be bound", emptyElList: "Empty element lists cannot be bound" }; boundInstances = {}; globalOptions = { silent: false, liveProps: true, dispatchEvents: false, updateEvenIfUndefined: false, updateEvenIfSame: false, updateOnBind: true, mutateInherited: false, trackArrayChildren: false, simpleSelector: false, promiseTransforms: false, placeholder: ['{{', '}}'] }; setPholderRegEx(); SimplyBind = function(subject, options, isBeingProxied) { if ((!subject && subject !== 0) || (!checkIf.isString(subject) && !checkIf.isNumber(subject) && !checkIf.isFunction(subject) && !(subject instanceof Array))) { if (!checkIf.isBindingInterface(subject)) { throwError('invalidParamName'); } } if (checkIf.isObject(subject) && !(subject instanceof Array)) { return new BindingInterface(subject._, 1); } else { return new BindingInterface(null, 0, null, subject, isBeingProxied, options); } }; Binding = function(object, type, state, isBeingProxied) { var arrayBinding, subjectValue; extendState(this, state); this.type = type; this.object = object; this.ID = genID(); this.valueOriginal = null; this.value = null; this.deps = []; this.depsMap = { 1: genObj(), 2: genObj() }; this.depsPholders = genObj(); this.myPholders = genObj(); this.transforms = genObj(); this.conditions = genObj(); this.attachedEvents = []; /* ========================================================================== */ if (this.isMultiChoice) { this.choices = genObj(); this.object.forEach((function(_this) { return function(choiceEl) { var choiceBinding; choiceBinding = _this.choices[choiceEl.value] = SimplyBind('checked').of(choiceEl)._; choiceBinding.addDep(_this).addTransform(_this.ID, function() { return choiceBinding; }); return choiceBinding.groupBinding = _this; }; })(this)); } if (this.type !== 'Event') { subjectValue = this.fetchDirectValue(isBeingProxied); if (!checkIf.isDefined(subjectValue) && this.type === 'ObjectProp') { this.object[this.property] = subjectValue = null; } this.value = this.valueOriginal = subjectValue; if (this.placeholder && !this.pholderValues) { this.scanForPholders(); } } this.makePropertyLive(); this.attachEvents(); if (this.object instanceof Array && this.type !== 'Array') { this.arrayBinding = arrayBinding = cache.get(this.object, true); if (arrayBinding && arrayBinding.options.trackArrayChildren && !arrayIncludes(arrayBinding.trackedChildren, this.property)) { arrayBinding.trackedChildren.push(this.property); SimplyBind(this.property).of(this.object).to(arrayBinding.updateSelf); } } return boundInstances[this.ID] = this; }; /** * Stage definitions: * * 0: Selection: Got selector, awaiting object. * 1: Indication: Got object, awaiting proxied property / function / Binding-object. * 2: Binding Selection: Got proxied selector, awaiting proxied object. * 3: Binding Complete: Complete, awaiting additional (optional) bindings/mutations. */ BindingInterface = function(binding, stage, inheritedState, subject, isBeingProxied, options) { var isDomCheckbox, isDomInput, isDomRadio, isIterable, key, newObjectType, sampleItem, split; if (inheritedState) { extendState(this, inheritedState); } this.stage = stage || 0; if (this.proxies == null) { this.proxies = []; } if (this.state == null) { this.state = {}; } switch (this.stage) { case 0: this.optionsPassed = options || (options = {}); this.options = {}; for (key in globalOptions) { this.options[key] = options[key] != null ? options[key] : globalOptions[key]; } if (checkIf.isFunction(subject)) { this.stage = 1; binding = this.createBinding(subject, 'Func', true, isBeingProxied); } else if (subject instanceof Array) { this.stage = 1; binding = this.createBinding(subject, 'Array', true); } else { if (checkIf.isNumber(subject)) { subject = subject.toString(); } this.selector = this.property = subject; if (!this.options.simpleSelector) { if (arrayIncludes(this.selector, ':')) { split = this.property.split(':'); this.descriptor = split[0]; this.property = split[1]; } if (arrayIncludes(this.selector, '.')) { split = this.property.split('.'); this.property = split[0]; this.placeholder = split.slice(1).join('.'); } this.selector = this.property; } } break; case 1: if (!binding) { isIterable = subject !== window && checkIf.isIterable(subject); sampleItem = isIterable ? subject[0] : subject; if (!sampleItem) { if (isIterable && checkIf.isElCollection(subject)) { throwWarning('emptyElList', 3); } } else if (this.isDom = checkIf.isDom(sampleItem)) { if (this.property === 'checked') { isDomRadio = sampleItem && checkIf.isDomRadio(sampleItem); isDomCheckbox = !isDomRadio && sampleItem && checkIf.isDomCheckbox(sampleItem); } else if (this.property === 'value') { isDomInput = checkIf.isDomInput(sampleItem); } if (isIterable && this.descriptor !== 'multi') { if (subject.length === 1) { subject = subject[0]; } else { if ((isDomRadio || isDomCheckbox) && !checkIf.domElsAreSame(subject)) { return throwWarning('mixedElList', 3); } else { if (isDomRadio || isDomCheckbox) { this.isMultiChoice = true; subject = [].slice.call(subject); } else { subject = subject[0]; throwWarning('onlyOneDOMElement', 3); } } } } } newObjectType = (function() { switch (false) { case !this.state.hasEventName: return 'Event'; case !isDomInput: return 'DOMValue'; case !isDomRadio: return 'DOMRadio'; case !isDomCheckbox: return 'DOMCheckbox'; case !(this.isDom && this.property === 'textContent'): return 'DOMText'; case this.descriptor !== 'attr': return 'DOMAttr'; default: return 'ObjectProp'; } }).call(this); if (this.descriptor === 'multi') { binding = new BindingMulti(subject, newObjectType, this); } else { binding = this.createBinding(subject, newObjectType); } } } return this.defineMainProps(binding); }; Binding.prototype = { makePropertyLive: function(force) { var _this, getter, opts, propertyDescriptor, setter, shouldWriteLiveProp; if (this.options.liveProps) { _this = this; if (this.type === 'ObjectProp') { propertyDescriptor = Object.getOwnPropertyDescriptor(this.object, this.property) || {}; shouldWriteLiveProp = this.options.mutateInherited || (propertyDescriptor && propertyDescriptor.configurable); shouldWriteLiveProp = shouldWriteLiveProp && this.object.constructor !== CSSStyleDeclaration; if ((shouldWriteLiveProp && !this.isLiveProp) || force) { this.isLiveProp = true; getter = propertyDescriptor.get ? propertyDescriptor.get : function() { return _this.value; }; if (propertyDescriptor.set) { setter = function(newValue) { _this.setValue(newValue); return propertyDescriptor.set(newValue); }; } else { setter = function(newValue) { return _this.setValue(newValue); }; } return Object.defineProperty(this.object, this.property, { configurable: true, enumerable: propertyDescriptor.enumerable, get: getter, set: setter }); } } else if (this.type === 'Array') { if (!this.isLiveProp) { this.isLiveProp = true; arrayMutatorMethods.forEach(function(method) { return Object.defineProperty(_this.value, method, { configurable: true, value: function() { var result; result = Array.prototype[method].apply(_this.value, arguments); _this.updateAllDeps(); return result; } }); }); } if (this.options.trackArrayChildren && !this.trackedChildren) { this.trackedChildren = []; this.updateSelf = function() { return _this.updateAllDeps(); }; opts = { updateOnBind: false }; return this.value.forEach(function(item, index) { _this.trackedChildren.push('' + index); return SimplyBind(index, opts).of(_this.value).to(_this.updateSelf); }); } } } }, addDep: function(dep, bothWays) { var depItem, i, len, ref; if (dep.isMulti) { ref = dep.bindings; for (i = 0, len = ref.length; i < len; i++) { depItem = ref[i]; this.addDep(depItem); } } else { if (!this.depsMap[1][dep.ID]) { this.depsMap[1][dep.ID] = dep; this.deps.push(dep); } if (this.placeholder) { this.myPholders[dep.ID] = this.placeholder; } else if (this.myPholders[dep.ID]) { delete this.myPholders[dep.ID]; } if (dep.placeholder) { this.depsPholders[dep.ID] = dep.placeholder; } if (bothWays) { this.depsMap[2][dep.ID] = dep; } else if (dep.depsMap[1][this.ID]) { dep.addDep(this, true); this.addDep(dep, true); } } return this; }, removeDep: function(dep, bothWays) { var depItem, i, len, ref; if (dep.isMulti) { ref = dep.bindings; for (i = 0, len = ref.length; i < len; i++) { depItem = ref[i]; this.removeDep(depItem, bothWays); } } else { if (this.depsMap[1][dep.ID]) { this.deps.splice(this.deps.indexOf(dep), 1); delete this.depsMap[1][dep.ID]; delete this.depsPholders[dep.ID]; } if (bothWays) { dep.removeDep(this); delete this.depsMap[2][dep.ID]; } } }, removeAllDeps: function(bothWays) { var dep, i, len, ref; ref = this.deps.slice(); for (i = 0, len = ref.length; i < len; i++) { dep = ref[i]; this.removeDep(dep, bothWays); } if (bothWays || Object.keys(this.depsMap[2]).length === 0) { return this.destroy(); } }, destroy: function() { var event, i, j, len, len1, method, ref; delete boundInstances[this.ID]; if (this.type === 'ObjectProp' || this.type === 'DOMText') { Object.defineProperty(this.object, this.property, { 'value': this.value, 'writable': true }); delete this.object._sb_map; delete this.object._sb_ID; } else if (this.type === 'Event') { ref = this.attachedEvents; for (i = 0, len = ref.length; i < len; i++) { event = ref[i]; this.unRegisterEvent(event, this.customEventMethod.remove); } delete this.object._sb_map; } else if (this.type === 'Array') { delete this.object._sb_ID; for (j = 0, len1 = arrayMutatorMethods.length; j < len1; j++) { method = arrayMutatorMethods[j]; delete this.object[method]; } } else if (this.type === 'Func') { delete this.object._sb_ID; } }, fetchDirectValue: function(isBeingProxied) { var choiceEl, choiceName, ref, results, type; type = this.type; switch (false) { case type !== 'Func': if (!isBeingProxied) { return this.object(); } break; case type !== 'Array': return this.object; case type !== 'DOMAttr': return this.object.getAttribute(this.property) || ''; case !this.isMultiChoice: results = []; ref = this.choices; for (choiceName in ref) { choiceEl = ref[choiceName]; if (choiceEl.object.checked) { if (type === 'DOMRadio') { return choiceName; } else { results.push(choiceName); } } } return results; default: return this.object[this.property]; } }, setValue: function(newValue, specificPlaceholder, updater, fromSelf) { var choiceBinding, choiceName, currentValue, i, index, isNewValue, isValidValue, len, n, newChoiceValue, newChoices, newValueArray, overwritePrevious, placeholderValue, prevValue, ref, ref1, targetChoiceBinding, value; if (updater == null) { updater = this; } currentValue = specificPlaceholder ? this.pholderValues[specificPlaceholder] : this.value; if (this.selfTransform) { newValue = this.selfTransform(newValue); } isNewValue = newValue !== currentValue || this.options.updateEvenIfSame; isValidValue = typeof newValue !== 'undefined' || this.options.updateEvenIfUndefined; if ((isNewValue && isValidValue || this.eventName) && this.type !== 'Array') { prevValue = this.value; if (specificPlaceholder) { this.pholderValues[specificPlaceholder] = placeholderValue = newValue; this.value = newValue = applyPlaceholders(this.pholderContexts, this.pholderValues, this.pholderIndexMap); } else { this.value = newValue; } switch (this.type) { case 'ObjectProp': if (!this.isLiveProp) { this.object[this.property] = newValue; } break; case 'Func': prevValue = this.valuePassed; this.valuePassed = newValue; this.value = this.object(newValue, prevValue); break; case 'Event': if (!fromSelf) { this.emitCauser = updater; this.emitEvent(newValue); this.emitCauser = null; } break; case 'DOMValue': if (this.object.value !== this.value) { this.object.value = this.value; this.emitChangeEvent(); } break; case 'DOMRadio': if (this.isMultiChoice) { targetChoiceBinding = checkIf.isBinding(newValue) ? newValue : this.choices[newValue]; if (targetChoiceBinding) { this.value = targetChoiceBinding.object.value; ref = this.choices; for (n in ref) { choiceBinding = ref[n]; choiceBinding.setValue(choiceBinding.ID === targetChoiceBinding.ID, null, updater); } } else { this.value = prevValue; } } else { this.value = !!this.value; if (this.object.checked !== this.value) { this.object.checked = this.value; } if (this.value) { this.emitChangeEvent(); } } break; case 'DOMCheckbox': if (this.isMultiChoice) { overwritePrevious = !checkIf.isBinding(newValue); newChoices = [].concat(newValue); for (index = i = 0, len = newChoices.length; i < len; index = ++i) { value = newChoices[index]; newChoices[index] = checkIf.isBinding(value) ? value : this.choices[value]; } newValueArray = []; ref1 = this.choices; for (choiceName in ref1) { choiceBinding = ref1[choiceName]; if (overwritePrevious) { newChoiceValue = arrayIncludes(newChoices, choiceBinding); } else { newChoiceValue = choiceBinding.value; } choiceBinding.setValue(newChoiceValue, null, updater); if (newChoiceValue) { newValueArray.push(choiceName); } } this.value = newValueArray; } else { this.value = !!this.value; if (this.object.checked !== this.value) { this.object.checked = this.value; this.emitChangeEvent(); } } break; case 'DOMText': this.applyTextContentReplace(this.object, specificPlaceholder, placeholderValue); break; case 'DOMAttr': this.object.setAttribute(this.property, this.value); } if (!(fromSelf && this.emitCauser)) { this.updateAllDeps(updater); } } }, updateAllDeps: function(updater) { var dep, i, len, ref; if (this.deps.length) { if (this.throttleRate) { if (+(new Date) - this.lastUpdate < this.throttleRate) { clearTimeout(this.throttleTimeout); return this.throttleTimeout = setTimeout(((function(_this) { return function() { return _this.updateAllDeps(updater); }; })(this)), this.throttleRate); } else { this.lastUpdate = +(new Date); } } ref = this.deps; for (i = 0, len = ref.length; i < len; i++) { dep = ref[i]; this.updateDep(dep, updater); } } }, updateDep: function(dep, updater) { var currentValue, depPlaceholder, depValue, isInfiniteLoop, isNewEventBinding, myPlaceholder, newValue; isNewEventBinding = this.value === null && this.type === 'Event'; isInfiniteLoop = updater && ((updater === dep) || (updater !== this && updater.depsMap[1][dep.ID])); if (isInfiniteLoop || isNewEventBinding) { return; } updater || (updater = this); myPlaceholder = this.myPholders[dep.ID]; currentValue = myPlaceholder ? this.pholderValues[myPlaceholder] : this.value; depPlaceholder = this.depsPholders[dep.ID]; depValue = depPlaceholder ? dep.pholderValues[depPlaceholder] : dep.value; newValue = currentValue; newValue = this.applyTransform(dep, depPlaceholder, currentValue, depValue); if (!this.checkCondition(dep, depPlaceholder, currentValue, depValue)) { return; } if (this.options.promiseTransforms && (newValue != null) && checkIf.isFunction(newValue.then)) { return newValue.then((function(_this) { return function(newValue) { return dep.setValue(newValue, depPlaceholder, updater); }; })(this)); } else { return dep.setValue(newValue, depPlaceholder, updater); } }, processTransform: function(transformFn, subjects) { var i, len, prox, proxID; if (!checkIf.isFunction(transformFn)) { return throwWarning('fnOnly', 2); } else { for (i = 0, len = subjects.length; i < len; i++) { prox = subjects[i]; prox = prox._ || prox; if (prox.isMulti) { this.processTransform(transformFn, prox.bindings); } else { proxID = prox.ID; proxID += prox.placeholder ? '.' + prox.placeholder : ''; this.addTransform(proxID, transformFn); if (this.depsMap[2][prox.ID]) { prox.addTransform(this.ID, transformFn); } if (this.options.updateOnBind || this.type === 'Func') { this.updateDep(prox); } } } return true; } }, applyTransform: function(dep, placeholder, value, depValue) { var depID; if (!this.hasTransforms) { return value; } else { depID = dep.ID; if (placeholder) { depID += '.' + placeholder; } if (this.transforms[depID]) { return this.transforms[depID](value, depValue); } else { return value; } } }, addTransform: function(ID, transformFn) { this.hasTransforms = true; return this.transforms[ID] = transformFn; }, processCondition: function(conditionFn, subjects) { var i, len, prox, proxID; if (!checkIf.isFunction(conditionFn)) { return throwWarning('fnOnly', 2); } else { for (i = 0, len = subjects.length; i < len; i++) { prox = subjects[i]; prox = prox._ || prox; if (prox.isMulti) { this.processCondition(conditionFn, prox.bindings); } else { proxID = prox.ID; proxID += prox.placeholder ? '.' + prox.placeholder : ''; this.addCondition(proxID, conditionFn); if (this.depsMap[2][prox.ID]) { prox.addCondition(this.ID, conditionFn); } } } return true; } }, checkCondition: function(dep, placeholder, value, depValue) { var depID; if (!this.hasConditions) { return true; } else { depID = dep.ID; if (placeholder) { depID += '.' + placeholder; } if (this.conditions[depID]) { return this.conditions[depID](value, depValue); } else { return true; } } }, addCondition: function(ID, conditionFn) { this.hasConditions = true; return this.conditions[ID] = conditionFn; }, scanForPholders: function() { var index; this.pholderValues = genObj(); this.pholderIndexMap = genObj(); this.pholderContexts = []; if (checkIf.isString(this.valueOriginal)) { this.pholderContexts = this.valueOriginal.split(pholderRegExSplit); index = 0; this.value = this.valueOriginal.replace(pholderRegEx, (function(_this) { return function(e, pholder) { _this.pholderIndexMap[index++] = pholder; return _this.pholderValues[pholder] = pholder; }; })(this)); } if (this.isDom && this.property === 'textContent') { return this.scanTextNodesPlaceholders(); } }, scanTextNodesPlaceholders: function(element) { var contexts, hasPlaceholders, i, index, indexMap, len, node, nodeID, nodeTextContent, ref, values; if (element == null) { element = this.object; } ref = element.childNodes; for (i = 0, len = ref.length; i < len; i++) { node = ref[i]; if (node.nodeType !== 3) { this.scanTextNodesPlaceholders(node); } else { nodeID = genID(); nodeTextContent = node.textContent; hasPlaceholders = false; values = genObj(); indexMap = genObj(); contexts = nodeTextContent.split(pholderRegExSplit); index = 0; nodeTextContent.replace(pholderRegEx, (function(_this) { return function(e, pholder) { hasPlaceholders = true; indexMap[index++] = pholder; return values[pholder] = _this.pholderValues[pholder]; }; })(this)); if (!hasPlaceholders) { values = null; } Object.defineProperty(node, '_sb_ID', { 'configurable': true, 'value': nodeID }); cache.textNodes[nodeID] = [nodeTextContent, contexts, values, indexMap]; } } }, getNodeFullValue: function(node, placeholderToUpdate, newPlaceholderValue) { var nodeProps, pholderValues; nodeProps = cache.textNodes[node._sb_ID]; if (!nodeProps) { return false; } else { pholderValues = nodeProps[2]; if (pholderValues) { pholderValues[placeholderToUpdate] = newPlaceholderValue; return applyPlaceholders(nodeProps[1], nodeProps[2], nodeProps[3]); } else { return nodeProps[0]; } } }, applyTextContentReplace: function(subject, placeholderToUpdate, newPlaceholderValue) { var i, len, newFullValue, node, ref; if (!this.placeholder) { subject.textContent = this.value; } else { ref = subject.childNodes; for (i = 0, len = ref.length; i < len; i++) { node = ref[i]; if (node.nodeType !== 3) { this.applyTextContentReplace(node, placeholderToUpdate, newPlaceholderValue); } else { newFullValue = this.getNodeFullValue(node, placeholderToUpdate, newPlaceholderValue); if (newFullValue) { node.textContent = newFullValue; } } } } }, addPollInterval: function(time) { this.removePollInterval(); return this.pollInterval = setInterval((function(_this) { return function() { var polledValue; polledValue = _this.fetchDirectValue(); return _this.setValue(polledValue); }; })(this), time); }, removePollInterval: function() { clearInterval(this.pollInterval); return this.pollInterval = null; }, addUpdateListener: function(eventName, targetProperty) { return this.object.addEventListener(eventName, (function(_this) { return function(event) { if (!event._sb) { return _this.setValue(event.target[targetProperty]); } }; })(this), false); }, emitChangeEvent: function() { if (this.options.dispatchEvents) { return this.object.dispatchEvent(changeEvent()); } }, attachEvents: function() { if (this.eventName) { return this.registerEvent(this.eventName, this.customEventMethod["in"]); } else if (this.type === 'DOMValue') { this.addUpdateListener('input', 'value'); return this.addUpdateListener('change', 'value'); } else if (!this.isMultiChoice && (this.type === 'DOMRadio' || this.type === 'DOMCheckbox')) { return this.addUpdateListener('change', 'checked'); } }, registerEvent: function(eventName, customInMethod) { var attachmentMethod, defaultInMethod; if (arrayIncludes(this.attachedEvents, eventName)) { return; } defaultInMethod = 'addEventListener'; this.attachedEvents.push(eventName); attachmentMethod = customInMethod || defaultInMethod; return this.invokeEventMethod(eventName, attachmentMethod, defaultInMethod); }, unRegisterEvent: function(eventName, customMethod) { var defaultRemoveMethod, indexOfEvent, removalMethod; indexOfEvent = this.attachedEvents.indexOf(eventName); if (indexOfEvent === -1) { return; } defaultRemoveMethod = 'removeEventListener'; this.attachedEvents.splice(indexOfEvent, 1); removalMethod = customMethod || defaultRemoveMethod; return this.invokeEventMethod(eventName, removalMethod, defaultRemoveMethod); }, invokeEventMethod: function(eventName, eventMethod, backupMethod) { var subject; subject = this.object; if (this.isDom && checkIf.isDefined(jQuery) && eventMethod === 'on' || eventMethod === 'off') { subject = jQuery(this.object); } if (!subject[eventMethod]) { eventMethod = backupMethod; } if (!this.eventHandler) { this.eventHandler = handleUpdateFromEvent.bind(this); } return typeof subject[eventMethod] === "function" ? subject[eventMethod](eventName, this.eventHandler) : void 0; }, emitEvent: function(extraData) { var defaultOutMethod, emitMethod, subject; subject = this.object; defaultOutMethod = 'dispatchEvent'; emitMethod = this.customEventMethod.out || defaultOutMethod; if (this.isDom && checkIf.isDefined(jQuery) && emitMethod === 'trigger') { subject = jQuery(this.object); } if (!subject[emitMethod]) { emitMethod = defaultOutMethod; } if (emitMethod === defaultOutMethod) { if (!this.eventObject) { this.eventObject = document.createEvent('Event'); this.eventObject.initEvent(this.eventName, true, true); } this.eventObject.boundData = extraData; return subject[emitMethod](this.eventObject); } return subject[emitMethod](this.eventName, extraData); } }; handleUpdateFromEvent = function() { var fetchedValue; fetchedValue = this.type === 'Event' ? arguments[this.property] : this.fetchDirectValue(); return this.setValue(fetchedValue, null, this.emitCauser, true); }; BindingInterfacePrivate = { "new": function(stage, object) { return new BindingInterface(this._, stage, this, object); }, defineMainProps: function(binding) { this._ = binding; return Object.defineProperties(this, { 'ID': { get: function() { return binding.ID; } }, 'value': { get: function() { return binding.value; } }, 'original': { get: function() { return binding.objects || binding.object; } }, 'dependents': { get: function() { return binding.deps.slice().map(function(dep) { return dep.object; }); } }, 'lastProxied': { get: function() { return this.proxies[this.proxies.length - 1]; } } }); }, createBinding: function(subject, newObjectType, isSimpleObject, isBeingProxied, bindingInterface) { var cachedBinding, newBinding; this.object = subject; cachedBinding = cache.get(subject, isSimpleObject, this.selector, this.isMultiChoice); if (cachedBinding) { return this.patchCachedBinding(cachedBinding); } else { newBinding = new Binding(subject, newObjectType, bindingInterface || this, isBeingProxied); cache.set(newBinding, isSimpleObject); return newBinding; } }, patchCachedBinding: function(cachedBinding) { cachedBinding.placeholder = this.placeholder; if (this.placeholder && !cachedBinding.pholderValues) { cachedBinding.valueOriginal = cachedBinding.fetchDirectValue(); cachedBinding.scanForPholders(); } if (cachedBinding.type === 'ObjectProp' && !(this.property in this.object)) { cachedBinding.makePropertyLive(true); } setOptionsForBinding(cachedBinding, this.optionsPassed); return cachedBinding; } }; BindingInterface.prototype = Object.create(BindingInterfacePrivate); BindingInterfacePublic = { of: function(object) { var proxied; if (this.stage !== 0 && this.stage !== 2) { throwErrorUnavail(methodNames[0]); } if (!(checkIf.isObject(object) || checkIf.isFunction(object))) { throwErrorBadArg(methodNames[0], object); } if (checkIf.isBindingInterface(object)) { object = object.object; } if (this.stage === 2) { this.proxies[this.proxies.length - 1] = proxied = this.lastProxied.of(object); this.state.hasInitialBinding = true; this.state.hasTransform = false; this._.addDep(proxied._); maybeUpdateDep(this._, proxied._); } return this["new"](this.stage + 1, object); }, ofEvent: function(eventName, customInMethod, customOutMethod) { if (this.stage !== 0 || this.state.hasEventName) { throwErrorUnavail(methodNames[1]); } else if (!eventName || !checkIf.isString(eventName)) { throwErrorBadArg(methodNames[1], eventName); } else if (isNaN(parseInt(this.property))) { throwWarning('badEventArg', 1); } this.state.hasEventName = true; this.eventName = eventName; this.selector = this.property + '#' + this.eventName; this.customEventMethod = { 'in': customInMethod, 'out': customOutMethod }; return this; }, to: function(subject, specificOptions) { var newStage, proxied; if (this.stage !== 1 || this.state.hasInitialBinding) { throwErrorUnavail(methodNames[2]); } this.proxies.push(proxied = computeProxied(this, subject, specificOptions)); if (proxied.stage === 0) { newStage = 2; } else { newStage = 3; this.state.hasInitialBinding = true; } return this["new"](newStage); }, and: function(subject, specificOptions) { var newStage, proxied; if (this.stage !== 3 || !this.state.hasInitialBinding || this.state.hasMultiTransform) { throwErrorUnavail(methodNames[3]); } this.proxies.push(proxied = computeProxied(this, subject, specificOptions)); if (proxied.stage === 0) { newStage = 2; } else { newStage = 3; this.state.hasTransform = false; } return this["new"](newStage); }, toEvent: function(eventName, customOutMethod, customInMethod, specificOptions) { if (this.stage !== 1) { throwErrorUnavail(methodNames[4]); } this.proxies.push(SimplyBind(0, specificOptions).ofEvent(eventName, customInMethod, customOutMethod)); return this["new"](2); }, chainTo: function(subject, specificOptions) { if (this.stage !== 3) { throwErrorUnavail(methodNames[5]); } return SimplyBind(this.lastProxied).to(subject, specificOptions); }, set: function(newValue) { if (this.stage === 0 || this.stage === 2) { throwErrorUnavail(methodNames[6]); } this._.setValue(newValue, this.placeholder); return this; }, get: function() { if (this.stage === 0 || this.stage === 2) { throwErrorUnavail(methodNames[7]); } if (this.placeholder) { return this._.pholderValues[this.placeholder]; } else { return this._.value; } }, transformSelf: function(transformFn) { var currentValue; if (this.stage !== 1 || this.stage === 1 && this._.type === 'Array') { throwErrorUnavail(methodNames[8]); } if (!checkIf.isFunction(transformFn)) { throwWarning('fnOnly', 1); } else { this._.selfTransform = transformFn; if (this._.options.updateOnBind) { currentValue = this._.isMulti ? this.value[0] : this.value; this._.setValue(currentValue); } } return this; }, transform: function(transformFn) { if (this.stage !== 3 || this.state.hasTransform || this.state.hasMultiTransform) { throwErrorUnavail(methodNames[9]); } this.state.hasTransform = this._.processTransform(transformFn, this.proxies.slice(-1)) || false; return this["new"](3); }, transformAll: function(transformFn) { if (this.stage !== 3 || this.state.hasTransform || this.state.hasMultiTransform) { throwErrorUnavail(methodNames[10]); } this.state.hasMultiTransform = this._.processTransform(transformFn, this.proxies) || false; return this["new"](3); }, condition: function(conditionFn) { if (this.stage !== 3) { throwErrorUnavail(methodNames[11]); } this._.processCondition(conditionFn, this.proxies.slice(-1)); return this["new"](3); }, conditionAll: function(conditionFn) { if (this.stage !== 3) { throwErrorUnavail(methodNames[12]); } this._.processCondition(conditionFn, this.proxies); return this["new"](3); }, bothWays: function(dontOrAltTransform) { var originCondition, originTransform, proxied, proxiedBinding, transformToUse; if (this.stage !== 3 || this.state.hasMultiTransform) { throwErrorUnavail(methodNames[13]); } proxied = this.lastProxied; proxiedBinding = proxied._.addDep(this._, true); originTransform = this._.transforms[proxied.ID]; originCondition = this._.conditions[proxied.ID]; if (originTransform || dontOrAltTransform) { transformToUse = checkIf.isFunction(dontOrAltTransform) ? dontOrAltTransform : originTransform; if (transformToUse && dontOrAltTransform !== false) { proxiedBinding.addTransform(this.ID, transformToUse); } } if (originCondition) { proxiedBinding.addCondition(this.ID, originCondition); } this._.addDep(proxied._, true); return this; }, unBind: function(bothWays) { var i, len, proxied, ref; if (this.stage !== 3) { throwErrorUnavail(methodNames[14]); } ref = this.proxies; for (i = 0, len = ref.length; i < len; i++) { proxied = ref[i]; this._.removeDep(proxied._, bothWays); } return this; }, pollEvery: function(time) { if (this.stage !== 3 || this._.type === 'Event') { throwErrorUnavail(methodNames[15]); } this._.addPollInterval(time); return this; }, stopPolling: function() { if (this.stage !== 3) { throwErrorUnavail(methodNames[16]); } this._.removePollInterval(); return this; }, updateDepsOnEvent: function(eventName, customMethod) { if (this.stage !== 3) { throwErrorUnavail(methodNames[17]); } this._.registerEvent(eventName, customMethod); return this; }, removeEvent: function(eventName, customMethod) { if (this.stage !== 3) { throwErrorUnavail(methodNames[18]); } this._.unRegisterEvent(eventName, customMethod); return this; }, throttle: function(delay) { if (this.stage !== 1 && this.stage !== 3) { throwErrorUnavail(methodNames[19]); } if (delay && checkIf.isNumber(delay)) { this._.throttleRate = delay; } else if (delay === false) { delete this._.throttleRate; } return this; }, setOption: function(optionName, newValue) { var i, len, newOptions, obj, proxied, ref; newOptions = ( obj = {}, obj["" + optionName] = newValue, obj ); setOptionsForBinding(this._, newOptions); ref = this.proxies; for (i = 0, len = ref.length; i < len; i++) { proxied = ref[i]; setOptionsForBinding(proxied._, newOptions); } return this; } }; BindingInterfacePublic.update = BindingInterfacePublic.set; BindingInterfacePublic.twoWay = BindingInterfacePublic.bothWays; BindingInterfacePublic.pipe = BindingInterfacePublic.chainTo; methodNames = Object.keys(BindingInterfacePublic); for (i = 0, len = methodNames.length; i < len; i++) { methodName = methodNames[i]; BindingInterface.prototype[methodName] = BindingInterfacePublic[methodName]; } computeProxied = function(instance, subject, specificOptions) { var proxied; if (specificOptions == null) { specificOptions = {}; } proxied = SimplyBind(subject, specificOptions, true); if (proxied.stage !== 0) { instance._.addDep(proxied._, instance); maybeUpdateDep(instance._, proxied._); } return proxied; }; maybeUpdateDep = function(instance, proxied) { if (instance.options.updateOnBind || instance.type === 'Func') { return instance.updateDep(proxied); } }; BindingMulti = function(objects, type, bindingInterface) { var bindings, j, len1, object; extendState(this, bindingInterface); this.isMulti = true; this.type = type; this.bindings = bindings = []; for (j = 0, len1 = objects.length; j < len1; j++) { object = objects[j]; bindings.push(this.createBinding(object, type, null, null, bindingInterface)); } return Object.defineProperties(this, { 'ID': { value: bindings[0].ID }, 'options': { value: bindings[0].options }, 'transforms': { value: bindings[0].transforms }, 'conditions': { value: bindings[0].conditions }, 'pholderValues': { get: function() { return bindings[0].pholderValues; } }, 'value': { get: function() { return bindings.map(function(binding) { return binding.value; }); } }, 'throttleRate': { set: function(newRate) { return bindings.forEach(function(binding) { return binding.throttleRate = newRate; }); } }, 'selfTransform': { set: function(fn) { return bindings.forEach(function(binding) { return binding.selfTransform = fn; }); } } }); }; proto = BindingMulti.prototype = Object.create(BindingInterfacePrivate); Object.keys(Binding.prototype).forEach(function(methodName) { return proto[methodName] = function(a, b) { var binding, j, len1, ref, results1; ref = this.bindings; results1 = []; for (j = 0, len1 = ref.length; j < len1; j++) { binding = ref[j]; results1.push(binding[methodName](a, b)); } return results1; }; }); Object.defineProperties(SimplyBind, { 'version': { value: '1.4.6' }, 'options': { get: function() { var clonedOptions, key, value; clonedOptions = {}; for (key in globalOptions) { value = globalOptions[key]; clonedOptions[key] = value; } return clonedOptions; } } }); SimplyBind.setOption = function(option, newValue) { if (checkIf.isDefined(globalOptions[option])) { globalOptions[option] = newValue; if (option === 'placeholder') { return setPholderRegEx(); } } }; SimplyBind.setOptions = function(newOptions) { var option, value; for (option in newOptions) { value = newOptions[option]; SimplyBind.setOption(option, value); } }; SimplyBind.unBindAll = function(object, bothWays) { var boundID, prop, propMap; if (object && (checkIf.isObject(object) || checkIf.isFunction(object))) { /** * Conditional Checks: