UNPKG

@danielkalen/simplybind

Version:

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

1,097 lines (1,094 loc) 37.4 kB
// Generated by CoffeeScript 1.10.0 (function() { var Binding, BindingInterface, BindingInterfacePrivate, BindingInterfacePublic, BindingMulti, SimplyBind, applyPlaceholders, arrayIncludes, arrayMutatorMethods, boundInstances, cache, checkIf, computeProxied, dummyPropertyDescriptor, errors, escapeRegEx, extendState, genID, genObj, getErrSource, globalOptions, handleUpdateFromEvent, i, len, maybeUpdateDep, methodName, methodNames, pholderRegEx, pholderRegExSplit, proto, setOptionsForBinding, setPholderRegEx, throwError, throwErrorBadArg, throwErrorUnavail, throwWarning; arrayMutatorMethods = ['push', 'pop', 'shift', 'unshift', 'splice', 'reverse', 'sort']; dummyPropertyDescriptor = {}; 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 subject !== void 0; }, 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; } }; setOptionsForBinding = function(binding, newOptions) { var option, value; for (option in newOptions) { value = newOptions[option]; if (checkIf.isDefined(globalOptions[option])) { binding.options[option] = value; } } binding.makePropertyLive(); }; extendState = function(base, stateToInherit) { var i, key, len, stateMapping; stateMapping = Object.keys(stateToInherit); for (i = 0, len = stateMapping.length; i < len; i++) { key = stateMapping[i]; base[key] = stateToInherit[key]; } }; cache = { get: function(object, isSimpleObject, selector, isMultiChoice) { if (isSimpleObject) { return boundInstances[object._sb_ID]; } else { if (object._sb_map && object._sb_map[selector]) { return boundInstances[object._sb_map[selector]]; } } }, set: function(B, isSimpleObject) { var propsMap, selector; if (isSimpleObject) { Object.defineProperty(B.object, '_sb_ID', { 'configurable': true, 'value': B.ID }); } else { selector = B.selector; if (B.object._sb_map) { B.object._sb_map[selector] = B.ID; } else { propsMap = {}; propsMap[selector] = B.ID; Object.defineProperty(B.object, '_sb_map', { 'configurable': true, 'value': propsMap }); } } } }; escapeRegEx = /[.*+?^${}()|[\]\\]/g; pholderRegEx = 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'); 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; console.warn('SimplyBind: ' + warn); } }; throwErrorUnavail = function(methodName) { throwError("You can't use/invoke ." + methodName + "() at this stage", true); }; throwErrorBadArg = function(methodName, arg) { 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()" }; boundInstances = {}; globalOptions = { silent: false, liveProps: true, dispatchEvents: false, updateEvenIfSame: false, updateOnBind: true, mutateInherited: false, trackArrayChildren: false, simpleSelector: false, promiseTransforms: false, placeholder: ['{{', '}}'] }; setPholderRegEx(); SimplyBind = function(subject, options, isProxiedFunc) { 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, isProxiedFunc, options); } }; Binding = function(object, type, state, isProxiedFunc) { var arrayBinding, subjectValue; extendState(this, state); this.type = type; this.object = object; this.ID = genID(); this.deps = []; this.depsMap = { 1: genObj(), 2: genObj() }; this.depsPholders = genObj(); this.myPholders = genObj(); this.transforms = genObj(); this.conditions = genObj(); this.attachedEvents = []; /* ========================================================================== */ if (this.type === 'Event' || this.type === 'Func') { this.options.updateOnBind = false; this.options.updateEvenIfSame = true; } if (!(this.type === 'Event' || isProxiedFunc)) { this.value = this.valueOriginal = subjectValue = this.fetchDirectValue(); if (this.type === 'ObjectProp' && !checkIf.isDefined(subjectValue)) { this.object[this.property] = 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, isProxiedFunc, options) { var key, newObjectType, 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, isProxiedFunc); } 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) { newObjectType = (function() { switch (false) { case !this.state.hasEventName: return 'Event'; 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 _, opts, propertyDescriptor, shouldWriteLiveProp; if (this.options.liveProps) { _ = this; if (this.type === 'ObjectProp') { propertyDescriptor = Object.getOwnPropertyDescriptor(this.object, this.property) || dummyPropertyDescriptor; shouldWriteLiveProp = force || !this.isLiveProp && (propertyDescriptor.configurable || this.options.mutateInherited); if (shouldWriteLiveProp) { this.isLiveProp = true; Object.defineProperty(this.object, this.property, { configurable: true, enumerable: propertyDescriptor.enumerable, get: function() { return _.value; }, set: propertyDescriptor.set ? function(newValue) { propertyDescriptor.set(newValue); _.setValue(newValue); } : function(newValue) { _.setValue(newValue); } }); } } else if (this.type === 'Array') { if (!this.isLiveProp) { this.isLiveProp = true; arrayMutatorMethods.forEach(function(method) { return Object.defineProperty(_.value, method, { configurable: true, value: function() { var result; result = Array.prototype[method].apply(_.value, arguments); _.updateAllDeps(_); return result; } }); }); } if (this.options.trackArrayChildren && !this.trackedChildren) { this.trackedChildren = []; this.updateSelf = function() { return _.updateAllDeps(_); }; opts = { updateOnBind: false }; this.value.forEach(function(item, index) { _.trackedChildren.push('' + index); return SimplyBind(index, opts).of(_.value).to(_.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) { this.destroy(); } }, destroy: function() { var event, i, j, len, len1, method, ref; delete boundInstances[this.ID]; if (this.type === 'ObjectProp') { 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() { var type; type = this.type; switch (false) { case type !== 'Func': return this.object(); case type !== 'Array': return this.object; default: return this.object[this.property]; } }, setValue: function(newValue, specificPlaceholder, updater, fromSelf) { var isNewValue, prevValue; if (updater == null) { updater = this; } prevValue = specificPlaceholder ? this.pholderValues[specificPlaceholder] : this.value; if (this.selfTransform) { newValue = this.selfTransform(newValue); } isNewValue = newValue !== prevValue || this.options.updateEvenIfSame; if (isNewValue && this.type !== 'Array') { if (specificPlaceholder) { this.pholderValues[specificPlaceholder] = newValue; newValue = applyPlaceholders(this.pholderContexts, this.pholderValues, this.pholderIndexMap); } switch (this.type) { case 'ObjectProp': if (!this.isLiveProp) { this.object[this.property] = newValue; } break; case 'Func': prevValue = this.valuePassed; if (updater.type === 'Array' && newValue === updater.value) { newValue = newValue.slice(); } this.valuePassed = newValue; newValue = this.object(newValue, prevValue); break; case 'Event': if (!fromSelf) { this.isEmitter = true; this.emitEvent(newValue); this.isEmitter = false; } } this.value = newValue; this.updateAllDeps(updater); } }, updateAllDeps: function(updater) { var currentTime, dep, i, len, ref, timePassed; if (this.deps.length) { if (this.throttleRate) { currentTime = +(new Date); timePassed = currentTime - this.lastUpdate; if (timePassed < this.throttleRate) { clearTimeout(this.throttleTimeout); return this.throttleTimeout = setTimeout(((function(_this) { return function() { return _this.updateAllDeps(updater); }; })(this)), this.throttleRate - timePassed); } else { this.lastUpdate = currentTime; } } 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, myPlaceholder, newValue; if ((updater === dep) || (updater !== this && updater.depsMap[1][dep.ID])) { return; } myPlaceholder = this.myPholders[dep.ID]; depPlaceholder = this.depsPholders[dep.ID]; currentValue = myPlaceholder ? this.pholderValues[myPlaceholder] : this.value; depValue = depPlaceholder ? dep.pholderValues[depPlaceholder] : dep.value; newValue = !this.hasTransforms ? currentValue : this.applyTransform(dep, depPlaceholder, currentValue, depValue); if (this.hasConditions && !this.checkCondition(dep, depPlaceholder, currentValue, depValue)) { return; } if (this.options.promiseTransforms && newValue && checkIf.isFunction(newValue.then)) { newValue.then(function(newValue) { dep.setValue(newValue, depPlaceholder, updater); }); } else { dep.setValue(newValue, depPlaceholder, updater); } }, processTransform: function(transformFn, subjects) { var i, len, prox; 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 { this.addTransform(prox.ID, transformFn); if (this.depsMap[2][prox.ID]) { prox.addTransform(this.ID, transformFn); } if (this.options.updateOnBind || this.type === 'Func') { this.updateDep(prox, this); } } } return true; } }, applyTransform: function(dep, placeholder, value, depValue) { if (this.transforms[dep.ID]) { return this.transforms[dep.ID](value, depValue); } else { return value; } }, addTransform: function(ID, transformFn) { this.hasTransforms = true; this.transforms[ID] = transformFn; }, processCondition: function(conditionFn, subjects) { var i, len, prox; 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 { this.addCondition(prox.ID, conditionFn); if (this.depsMap[2][prox.ID]) { prox.addCondition(this.ID, conditionFn); } } } return true; } }, checkCondition: function(dep, placeholder, value, depValue) { if (this.conditions[dep.ID]) { return this.conditions[dep.ID](value, depValue); } else { return true; } }, addCondition: function(ID, conditionFn) { this.hasConditions = true; 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)); } }, 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; }, attachEvents: function() { if (this.eventName) { this.registerEvent(this.eventName, this.customEventMethod["in"]); } }, registerEvent: function(eventName, customInMethod) { var attachmentMethod, defaultInMethod; if (!arrayIncludes(this.attachedEvents, eventName)) { defaultInMethod = 'on'; this.attachedEvents.push(eventName); attachmentMethod = customInMethod || defaultInMethod; this.invokeEventMethod(eventName, attachmentMethod, defaultInMethod); } }, unRegisterEvent: function(eventName, customMethod) { var defaultRemoveMethod, indexOfEvent, removalMethod; indexOfEvent = this.attachedEvents.indexOf(eventName); if (indexOfEvent === -1) { return; } defaultRemoveMethod = 'removeListener'; this.attachedEvents.splice(indexOfEvent, 1); removalMethod = customMethod || defaultRemoveMethod; this.invokeEventMethod(eventName, removalMethod, defaultRemoveMethod); }, invokeEventMethod: function(eventName, eventMethod, backupMethod) { var subject; subject = this.object; if (!subject[eventMethod]) { eventMethod = backupMethod; } if (!this.eventHandler) { this.eventHandler = handleUpdateFromEvent.bind(this); } if (typeof subject[eventMethod] === "function") { subject[eventMethod](eventName, this.eventHandler); } }, emitEvent: function(extraData) { var defaultOutMethod, emitMethod, subject; subject = this.object; defaultOutMethod = 'emit'; emitMethod = this.customEventMethod.out || defaultOutMethod; if (!subject[emitMethod]) { emitMethod = defaultOutMethod; } subject[emitMethod](this.eventName, extraData); } }; handleUpdateFromEvent = function() { var fetchedValue; if (!this.isEmitter) { fetchedValue = this.type === 'Event' ? arguments[this.property] : this.fetchDirectValue(); this.setValue(fetchedValue, null, null, 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, isProxiedFunc, 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, isProxiedFunc); 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, instance); } }; 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 }, 'updateDep': { value: function(dep) { return bindings.forEach(function(binding) { return binding.updateDep(dep, binding); }); } }, '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; ref = this.bindings; for (j = 0, len1 = ref.length; j < len1; j++) { binding = ref[j]; binding[methodName](a, b); } }; }); Object.defineProperties(SimplyBind, { 'version': { value: '1.5.4' }, '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') { 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))) { propMap = object._sb_map; if (object._sb_ID) { boundInstances[object._sb_ID].removeAllDeps(bothWays); } if (propMap) { for (prop in propMap) { boundID = propMap[prop]; boundInstances[boundID].removeAllDeps(bothWays); } } } }; return module.exports = SimplyBind; })();