UNPKG

@danielkalen/simplybind

Version:

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

1,179 lines (1,176 loc) 38.6 kB
// Generated by CoffeeScript 1.10.0 (function() { var Binding, BindingInterface, BindingInterfacePrivate, GroupBinding, METHOD_bothWays, METHOD_chainTo, METHOD_condition, METHOD_conditionAll, METHOD_get, METHOD_of, METHOD_ofEvent, METHOD_pollEvery, METHOD_removeEvent, METHOD_set, METHOD_setOption, METHOD_stopPolling, METHOD_throttle, METHOD_transform, METHOD_transformAll, METHOD_transformSelf, METHOD_unBind, METHOD_updateSubsOnEvent, OPTS_NOUPDATE, SimplyBind, applyPlaceholders, arrayMutatorMethods, boundInstances, cache, checkIf, cloneObject, currentID, defaultOptions, dummyPropertyDescriptor, errors, escapeRegEx, extendState, genID, genObj, genProxiedEventInterface, genProxiedInterface, getErrSource, handleUpdateFromEvent, noop, pholderRegEx, pholderRegExSplit, placeholder, proto, setPholderRegEx, settings, targetIncludes, throwError, throwErrorBadArg, throwWarning; OPTS_NOUPDATE = { updateOnBind: false }; currentID = 0; arrayMutatorMethods = ['push', 'pop', 'shift', 'unshift', 'splice', 'reverse', 'sort']; dummyPropertyDescriptor = {}; boundInstances = {}; placeholder = ['{{', '}}']; settings = Object.create({ silent: false, trackArrayChildren: false }, { placeholder: { get: function() { return placeholder; }, set: function(newPlaceholder) { if (checkIf.isArray(newPlaceholder) && newPlaceholder.length === 2) { placeholder = newPlaceholder; setPholderRegEx(); } } } }); defaultOptions = { simpleSelector: false, promiseTransforms: false, dispatchEvents: false, updateEvenIfSame: false, updateOnBind: true }; noop = function() {}; genID = function() { return '' + (++currentID); }; genObj = function() { return Object.create(null); }; genProxiedInterface = function(publisher, isSibling) { return function(subject, specificOptions, saveOptions) { return SimplyBind(subject, specificOptions, saveOptions, publisher, isSibling); }; }; genProxiedEventInterface = function(publisher, isSibling) { return function(eventName, customOutMethod, customInMethod) { return SimplyBind(0, null, false, publisher, isSibling).ofEvent(eventName, customInMethod, customOutMethod); }; }; targetIncludes = function(target, item) { return target.indexOf(item) !== -1; }; checkIf = { isDefined: function(subject) { return subject !== void 0; }, isArray: function(subject) { return subject instanceof Array; }, 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; }, isSimpleObject: function(subject) { return checkIf.isFunction(subject) || checkIf.isArray(subject); } }; cloneObject = function(object) { var clone, key; clone = genObj(); for (key in object) { clone[key] = object[key]; } return clone; }; extendState = function(base, stateToInherit) { var j, key, len, stateMapping; stateMapping = Object.keys(stateToInherit); for (j = 0, len = stateMapping.length; j < len; j++) { key = stateMapping[j]; 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 = settings.placeholder[0].replace(escapeRegEx, '\\$&'); end = settings.placeholder[1].replace(escapeRegEx, '\\$&'); middle = "[^" + end + "]+"; pholderRegEx = new RegExp(start + "(" + middle + ")" + end, 'g'); pholderRegExSplit = new RegExp("" + start + middle + end, 'g'); }; setPholderRegEx(); applyPlaceholders = function(contexts, values, indexMap) { var contextPart, index, j, len, output; output = ''; for (index = j = 0, len = contexts.length; j < len; index = ++j) { 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 (!settings.silent) { errSource = getErrSource(depth); warn = errors[warningName]; warn += "\n\n" + errSource; console.warn('SimplyBind: ' + warn); } }; throwErrorBadArg = function(arg) { throwError("Invalid argument/s (" + arg + ")", 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()", emptyList: "Empty collection provided" }; SimplyBind = function(subject, options, saveOptions, publisher, isSibling) { 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)) { if (publisher) { return subject.addToPublisher(publisher); } return subject.selfClone(1); } else { return new BindingInterface(null, 0, subject, options, publisher, isSibling, saveOptions); } }; SimplyBind.version = '1.9.0'; SimplyBind.settings = settings; SimplyBind.defaultOptions = defaultOptions; SimplyBind.func = function(funcName, customOptions) { var bindingInterface; bindingInterface = SimplyBind(funcName, customOptions); bindingInterface.isProxy = true; return bindingInterface; }; 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].removeAllSubs(bothWays); } if (propMap) { for (prop in propMap) { boundID = propMap[prop]; boundInstances[boundID].removeAllSubs(bothWays); } } } }; Binding = function(object, type, state) { var arrayBinding, subjectValue; extendState(this, state); this.optionsDefault = this.saveOptions ? this.options : defaultOptions; this.type = type; this.object = object; this.ID = genID(); this.subs = []; this.subOpts = genObj(); this.pubsMap = genObj(); this.subsPholders = genObj(); this.myPholders = genObj(); this.attachedEvents = []; if (this.type === 'Array' || this.type === 'Proxy') { this.setValue = noop; } /* ========================================================================== */ if (!(this.type === 'Event' || (this.type === 'Func' && this.parentInterface))) { 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.convertToLive(); } this.attachEvents(); if (this.object instanceof Array && this.type !== 'Array') { this.arrayBinding = arrayBinding = cache.get(this.object, true); if (arrayBinding && arrayBinding.trackedChildren && !targetIncludes(arrayBinding.trackedChildren, this.property)) { arrayBinding.trackedChildren.push(this.property); SimplyBind(this.property).of(this.object).to(arrayBinding.updateSelf); } } return boundInstances[this.ID] = this; }; Binding.prototype = { convertToLive: function(force) { var _, context, originalFn, propertyDescriptor, shouldWriteLiveProp, slice; _ = this; switch (this.type) { case 'ObjectProp': propertyDescriptor = Object.getOwnPropertyDescriptor(this.object, this.property) || dummyPropertyDescriptor; shouldWriteLiveProp = force || propertyDescriptor.configurable; 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); } }); } break; case 'Array': arrayMutatorMethods.forEach(function(method) { return Object.defineProperty(_.value, method, { configurable: true, value: function() { var result; result = Array.prototype[method].apply(_.value, arguments); _.updateAllSubs(_); return result; } }); }); if (settings.trackArrayChildren && !this.trackedChildren) { this.trackedChildren = []; this.updateSelf = function() { return _.updateAllSubs(_); }; this.value.forEach(function(item, index) { _.trackedChildren.push('' + index); return SimplyBind(index, OPTS_NOUPDATE).of(_.value).to(_.updateSelf); }); } break; case 'Proxy': originalFn = this.original = this.value; context = this.object; this.value = { result: null, args: null }; if (checkIf.isFunction(originalFn)) { slice = [].slice; this.object[this.property] = function() { var args, result; _.value.args = args = slice.call(arguments); _.value.result = result = originalFn.apply(context, args); _.updateAllSubs(_); return result; }; } } }, addSub: function(sub, options, updateOnce) { var alreadyHadSub, j, len, ref, subItem; if (options == null) { options = this.optionsDefault; } if (sub.isMulti) { ref = sub.bindings; for (j = 0, len = ref.length; j < len; j++) { subItem = ref[j]; this.addSub(subItem, options, updateOnce); } } else { if (this.subOpts[sub.ID]) { alreadyHadSub = !this.myPholders[sub.ID]; } else { this.subs.unshift(sub); this.subOpts[sub.ID] = cloneObject(options); if (sub.type === 'Func' || sub.type === 'Event') { this.subOpts[sub.ID].updateEvenIfSame = true; } sub.pubsMap[this.ID] = this; if (updateOnce) { SimplyBind('value', OPTS_NOUPDATE).of(sub).to((function(_this) { return function() { return _this.removeSub(sub); }; })(this)); } } if (this.placeholder) { this.myPholders[sub.ID] = this.placeholder; } else if (this.myPholders[sub.ID]) { delete this.myPholders[sub.ID]; } if (sub.placeholder) { this.subsPholders[sub.ID] = sub.placeholder; } } return alreadyHadSub; }, removeSub: function(sub, bothWays) { var j, len, ref, subItem; if (sub.isMulti) { ref = sub.bindings; for (j = 0, len = ref.length; j < len; j++) { subItem = ref[j]; this.removeSub(subItem, bothWays); } } else { if (this.subOpts[sub.ID]) { this.subs.splice(this.subs.indexOf(sub), 1); delete this.subsPholders[sub.ID]; delete this.subOpts[sub.ID]; delete sub.pubsMap[this.ID]; } if (bothWays) { sub.removeSub(this); delete this.pubsMap[sub.ID]; } } if (this.subs.length === 0 && Object.keys(this.pubsMap).length === 0) { this.destroy(); } }, removeAllSubs: function(bothWays) { var j, len, ref, sub; ref = this.subs.slice(); for (j = 0, len = ref.length; j < len; j++) { sub = ref[j]; this.removeSub(sub, bothWays); } }, destroy: function() { var event, j, k, len, len1, method, ref; delete boundInstances[this.ID]; this.removePollInterval(); if (this.type === 'Event') { ref = this.attachedEvents; for (j = 0, len = ref.length; j < len; j++) { event = ref[j]; this.unRegisterEvent(event, this.customEventMethod.remove); } delete this.object._sb_map; } else if (this.type === 'Array') { delete this.object._sb_ID; for (k = 0, len1 = arrayMutatorMethods.length; k < len1; k++) { method = arrayMutatorMethods[k]; delete this.object[method]; } } else if (this.type === 'Func') { delete this.object._sb_ID; } else { 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; } }, 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, publisher, fromSelf) { var prevValue; publisher || (publisher = this); if (this.selfTransform) { newValue = this.selfTransform(newValue); } if (specificPlaceholder) { prevValue = this.pholderValues[specificPlaceholder]; this.pholderValues[specificPlaceholder] = newValue; newValue = applyPlaceholders(this.pholderContexts, this.pholderValues, this.pholderIndexMap); } switch (this.type) { case 'ObjectProp': if (!this.isLiveProp && newValue !== this.value) { this.object[this.property] = newValue; } break; case 'Func': prevValue = this.valuePassed; if (publisher.type === 'Array' && newValue === publisher.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.updateAllSubs(publisher); }, updateAllSubs: function(publisher) { var arr, currentTime, i, timePassed; if (this.subs.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.updateAllSubs(publisher); }; })(this)), this.throttleRate - timePassed); } else { this.lastUpdate = currentTime; } } i = (arr = this.subs).length; while (i--) { this.updateSub(arr[i], publisher); } } }, updateSub: function(sub, publisher) { var currentValue, myPlaceholder, newValue, subOptions, subPlaceholder, subValue; if ((publisher === sub) || (publisher !== this && publisher.subOpts[sub.ID])) { return; } subOptions = this.subOpts[sub.ID]; myPlaceholder = this.placeholder && this.myPholders[sub.ID]; subPlaceholder = sub.placeholder && this.subsPholders[sub.ID]; currentValue = myPlaceholder ? this.pholderValues[myPlaceholder] : this.value; subValue = subPlaceholder ? sub.pholderValues[subPlaceholder] : sub.value; newValue = this.transforms && this.transforms[sub.ID] ? this.transforms[sub.ID](currentValue, subValue, sub.object) : currentValue; if (newValue === subValue && !subOptions.updateEvenIfSame || this.conditions && this.conditions[sub.ID] && !this.conditions[sub.ID](newValue, subValue, sub.object)) { return; } if (subOptions.promiseTransforms && newValue && checkIf.isFunction(newValue.then)) { newValue.then(function(newValue) { sub.setValue(newValue, subPlaceholder, publisher); }); } else { sub.setValue(newValue, subPlaceholder, publisher); } }, addModifierFn: function(targetCollection, subInterfaces, targetFn, updateOnBind) { var j, len, subInterface, subscriber; if (!checkIf.isFunction(targetFn)) { return throwWarning('fnOnly', 2); } else { for (j = 0, len = subInterfaces.length; j < len; j++) { subInterface = subInterfaces[j]; subscriber = subInterface._ || subInterface; if (subscriber.isMulti) { this.addModifierFn(targetCollection, subscriber.bindings, targetFn); } else { this.setModifierFn(subscriber.ID, targetCollection, targetFn); if (this.pubsMap[subscriber.ID]) { subscriber.setModifierFn(this.ID, targetCollection, targetFn); } if ((updateOnBind || this.type === 'Func') && targetCollection === 'transforms') { this.updateSub(subscriber, this); } } } return true; } }, setModifierFn: function(targetID, targetCollection, targetFn) { if (this[targetCollection] == null) { this[targetCollection] = genObj(); } this[targetCollection][targetID] = targetFn; }, setSelfTransform: function(transformFn, updateOnBind) { if (this.type !== 'Array') { this.selfTransform = transformFn; if (updateOnBind) { this.setValue(this.value); } } }, 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) { if (this.type !== 'Event') { 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 (!targetIncludes(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); } }; /** * Stage definitions: * * 0: Selection: Got selector, awaiting object. * 1: Indication: Got object, awaiting proxied property / function / Binding-object. * 2: Binding Complete: Complete, awaiting additional (optional) bindings/mutations. */ BindingInterface = function(inheritedState, stage, subject, options, parentInterface, isSibling, saveOptions) { var key, split; if (inheritedState) { extendState(this, inheritedState); } this.stage = stage || 0; if (this.subs == null) { this.subs = []; } if (saveOptions) { this.saveOptions = saveOptions; } if (isSibling) { this.isSibling = isSibling; } if (parentInterface) { this.parentInterface = parentInterface; } if (this.stage === 0) { this.optionsPassed = options || (options = {}); this.options = {}; for (key in defaultOptions) { this.options[key] = options[key] != null ? options[key] : defaultOptions[key]; } if (checkIf.isSimpleObject(subject)) { this.stage = 1; return this.setObject(subject, true); } else { if (checkIf.isNumber(subject)) { subject = subject.toString(); } this.selector = this.property = subject; if (!this.options.simpleSelector) { if (targetIncludes(this.selector, ':')) { split = this.property.split(':'); this.descriptor = split[0]; this.property = split[1]; } if (targetIncludes(this.selector, '.')) { split = this.property.split('.'); this.property = split[0]; this.placeholder = split.slice(1).join('.'); } this.selector = this.property; } } } return this; }; BindingInterfacePrivate = { selfClone: function(newStage) { return new BindingInterface(this, newStage); }, defineMainProps: function(binding) { this._ = binding; return Object.defineProperties(this, { 'value': { get: function() { return binding.value; } }, 'original': { get: function() { return binding.objects || binding.object; } }, 'subscribers': { get: function() { return binding.subs.slice().map(function(sub) { return sub.object; }); } } }); }, createBinding: function(subject, newObjectType, bindingInterface, isSimpleObject) { 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); cache.set(newBinding, isSimpleObject); return newBinding; } }, patchCachedBinding: function(cachedBinding) { var key, option, ref, ref1, value; 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.convertToLive(true); } if (this.saveOptions) { ref = this.optionsPassed; for (option in ref) { value = ref[option]; cachedBinding.optionsDefault[option] = value; } } ref1 = cachedBinding.optionsDefault; for (key in ref1) { value = ref1[key]; this.options[key] = checkIf.isDefined(this.optionsPassed[key]) ? this.optionsPassed[key] : value; } return cachedBinding; }, setObject: function(subject, isSimpleObject) { var newObjectType; switch (false) { case !isSimpleObject: newObjectType = checkIf.isFunction(subject) ? 'Func' : 'Array'; break; case !this.eventName: newObjectType = 'Event'; break; case !this.isProxy: newObjectType = 'Proxy'; break; default: newObjectType = 'ObjectProp'; } if (this.descriptor === 'multi') { if (!subject.length) { throwError('emptyList'); } this.defineMainProps(new GroupBinding(this, subject, newObjectType)); } else { this.defineMainProps(this.createBinding(subject, newObjectType, this, isSimpleObject)); } if (targetIncludes(this._.type, 'Event') || targetIncludes(this._.type, 'Proxy')) { this.options.updateOnBind = false; } else if (targetIncludes(this._.type, 'Func')) { this.options.updateOnBind = true; } if (this.parentInterface) { return this.addToPublisher(this.parentInterface); } else { return this; } }, addToPublisher: function(publisherInterface) { var alreadyHadSub, binding, j, len, ref; if (this.isSibling) { publisherInterface._.addBinding(this._); } else { publisherInterface.stage = 2; publisherInterface.subs.push(this); alreadyHadSub = publisherInterface._.addSub(this._, publisherInterface.options, publisherInterface.updateOnce); if (publisherInterface.updateOnce) { delete publisherInterface.updateOnce; } else if (publisherInterface.options.updateOnBind && !alreadyHadSub) { if (this._.isMulti) { ref = this._.bindings; for (j = 0, len = ref.length; j < len; j++) { binding = ref[j]; publisherInterface._.updateSub(binding, publisherInterface._); } } else { publisherInterface._.updateSub(this._, publisherInterface._); } } } return publisherInterface; } }; BindingInterface.prototype = Object.create(BindingInterfacePrivate, { of: { get: function() { if (!this.stage) { return METHOD_of; } } }, ofEvent: { get: function() { if (!this.stage) { return METHOD_ofEvent; } } }, chainTo: { get: function() { if (this.stage) { return METHOD_chainTo; } } }, set: { get: function() { if (this.stage) { return METHOD_set; } } }, get: { get: function() { if (this.stage) { return METHOD_get; } } }, transformSelf: { get: function() { if (this.stage === 1) { return METHOD_transformSelf; } } }, transform: { get: function() { if (this.stage === 2) { return METHOD_transform; } } }, transformAll: { get: function() { if (this.stage === 2) { return METHOD_transformAll; } } }, condition: { get: function() { if (this.stage === 2) { return METHOD_condition; } } }, conditionAll: { get: function() { if (this.stage === 2) { return METHOD_conditionAll; } } }, bothWays: { get: function() { if (this.stage === 2) { return METHOD_bothWays; } } }, unBind: { get: function() { if (this.stage === 2) { return METHOD_unBind; } } }, pollEvery: { get: function() { if (this.stage === 2) { return METHOD_pollEvery; } } }, stopPolling: { get: function() { if (this.stage === 2) { return METHOD_stopPolling; } } }, updateSubsOnEvent: { get: function() { if (this.stage === 2) { return METHOD_updateSubsOnEvent; } } }, removeEvent: { get: function() { if (this.stage === 2) { return METHOD_removeEvent; } } }, throttle: { get: function() { if (this.stage) { return METHOD_throttle; } } }, setOption: { get: function() { if (this.stage === 2) { return METHOD_setOption; } } }, to: { get: function() { if (this.stage === 1) { return genProxiedInterface(this); } } }, toEvent: { get: function() { if (this.stage === 1) { return genProxiedEventInterface(this); } } }, and: { get: function() { var cloneBinding, cloneInterface; cloneInterface = this.selfClone(1); if (this.stage === 2) { return cloneInterface; } else if (this.stage === 1) { if (!cloneInterface._.isMulti) { cloneBinding = cloneInterface._; cloneInterface._ = new GroupBinding(cloneInterface); cloneInterface._.addBinding(cloneBinding); } return genProxiedInterface(cloneInterface, true); } } }, once: { get: function() { var interfaceToReturn; if (this.stage === 1) { interfaceToReturn = this.selfClone(1); interfaceToReturn.updateOnce = true; return interfaceToReturn; } } }, update: { get: function() { return this.set; } }, twoWay: { get: function() { return this.bothWays; } }, pipe: { get: function() { return this.chainTo; } } }); METHOD_of = function(object) { if (!(checkIf.isObject(object) || checkIf.isFunction(object))) { throwErrorBadArg(object); } if (checkIf.isBindingInterface(object)) { object = object.object; } this.stage = 1; return this.setObject(object); }; METHOD_ofEvent = function(eventName, customInMethod, customOutMethod) { if (!eventName || !checkIf.isString(eventName)) { throwErrorBadArg(eventName); } else if (isNaN(parseInt(this.property))) { throwWarning('badEventArg', 1); } this.eventName = eventName; this.selector = this.property + '#' + this.eventName; this.customEventMethod = { 'in': customInMethod, 'out': customOutMethod }; return this; }; METHOD_chainTo = function(subject, specificOptions, saveOptions) { return SimplyBind(this.subs[this.subs.length - 1]).to(subject, specificOptions, saveOptions); }; METHOD_set = function(newValue) { this._.setValue(newValue, this.placeholder); return this; }; METHOD_get = function() { if (!this.placeholder || this._.isMulti) { return this._.value; } else { return this._.pholderValues[this.placeholder]; } }; METHOD_transformSelf = function(transformFn) { if (!checkIf.isFunction(transformFn)) { throwWarning('fnOnly', 1); } else { this._.setSelfTransform(transformFn, this.options.updateOnBind); } return this; }; METHOD_transform = function(transformFn) { this._.addModifierFn('transforms', this.subs.slice(-1), transformFn, this.options.updateOnBind); return this; }; METHOD_transformAll = function(transformFn) { this._.addModifierFn('transforms', this.subs, transformFn, this.options.updateOnBind); return this; }; METHOD_condition = function(conditionFn) { this._.addModifierFn('conditions', this.subs.slice(-1), conditionFn); return this; }; METHOD_conditionAll = function(conditionFn) { this._.addModifierFn('conditions', this.subs, conditionFn); return this; }; METHOD_bothWays = function(altTransform) { var binding, bindings, j, len, originCondition, originTransform, sub, subBinding, transformToUse; sub = this.subs[this.subs.length - 1]; subBinding = sub._; bindings = this._.isMulti ? this._.bindings : [this._]; subBinding.addSub(this._, sub.options); for (j = 0, len = bindings.length; j < len; j++) { binding = bindings[j]; originTransform = binding.transforms && binding.transforms[subBinding.ID]; originCondition = binding.conditions && binding.conditions[subBinding.ID]; if (originTransform || altTransform) { transformToUse = checkIf.isFunction(altTransform) ? altTransform : originTransform; if (transformToUse && altTransform !== false) { subBinding.setModifierFn(this._.ID, 'transforms', transformToUse); } } if (originCondition) { subBinding.setModifierFn(this._.ID, 'conditions', originCondition); } } return this; }; METHOD_unBind = function(bothWays) { var j, len, ref, sub; ref = this.subs; for (j = 0, len = ref.length; j < len; j++) { sub = ref[j]; this._.removeSub(sub._, bothWays); } return this; }; METHOD_pollEvery = function(time) { this._.addPollInterval(time); return this; }; METHOD_stopPolling = function() { this._.removePollInterval(); return this; }; METHOD_updateSubsOnEvent = function(eventName, customMethod) { this._.registerEvent(eventName, customMethod); return this; }; METHOD_removeEvent = function(eventName, customMethod) { this._.unRegisterEvent(eventName, customMethod); return this; }; METHOD_throttle = function(delay) { if (delay && checkIf.isNumber(delay)) { this._.throttleRate = delay; } else if (delay === false) { delete this._.throttleRate; } return this; }; METHOD_setOption = function(optionName, newValue) { this._.subOpts[this.subs[this.subs.length - 1]._.ID][optionName] = newValue; return this; }; GroupBinding = function(bindingInterface, objects, objectType) { var bindings, j, len, object; extendState(this, this["interface"] = bindingInterface); this.isMulti = true; this.bindings = bindings = []; if (objects) { for (j = 0, len = objects.length; j < len; j++) { object = objects[j]; this.addBinding(object, objectType); } } return Object.defineProperties(this, { 'type': { get: function() { return bindings.map(function(binding) { return binding.type; }); } }, 'value': { get: function() { return bindings.map(function(binding) { if (binding.placeholder) { return binding.pholderValues[binding.placeholder]; } else { return binding.value; } }); } }, 'throttleRate': { set: function(newRate) { return bindings.forEach(function(binding) { return binding.throttleRate = newRate; }); } } }); }; proto = GroupBinding.prototype = Object.create(BindingInterfacePrivate); Object.keys(Binding.prototype).forEach(function(methodName) { return proto[methodName] = function(a, b, c, d) { var binding, j, len, ref; ref = this.bindings; for (j = 0, len = ref.length; j < len; j++) { binding = ref[j]; if (methodName === 'updateSub') { b = binding; } binding[methodName](a, b, c, d); } }; }); proto.addBinding = function(object, objectType) { this.bindings.push(!objectType ? object : this.createBinding(object, objectType, this["interface"])); }; return module.exports = SimplyBind; })();