@danielkalen/simplybind
Version:
Magically simple, framework-less one-way/two-way data binding for frontend/backend in ~5kb.
1,047 lines (1,044 loc) • 36.4 kB
JavaScript
// Generated by CoffeeScript 1.10.0
(function() {
var Binding, BindingInterface, BindingInterfacePrivate, BindingInterfacePublic, SimplyBind, applyPlaceholders, arrayIncludes, arrayMutatorMethods, boundInstances, cache, checkIf, computeProxied, errRegEx, errors, escapeRegEx, extendState, genID, genObj, getErrSource, globalOptions, handleUpdateFromEvent, i, len, maybeUpdateDep, methodName, methodNames, pholderRegEx, pholderRegExSplit, setOptionsForBinding, setPholderRegEx, throwError, throwErrorBadArg, throwErrorUnavail, throwWarning;
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';
},
isIterable: function(subject) {
return typeof subject === 'object' && typeof subject.length === 'number';
},
isBinding: function(subject) {
return subject instanceof Binding;
},
isBindingInterface: function(subject) {
return subject instanceof BindingInterface;
}
};
setOptionsForBinding = function(binding, newOptions, makeLive) {
var option, value;
for (option in newOptions) {
value = newOptions[option];
if (globalOptions[option] != null) {
binding.options[option] = value;
}
}
if (makeLive) {
return binding.makePropertyLive();
}
};
extendState = function(base, stateToInherit) {
var i, key, len, results, stateMapping;
stateMapping = Object.keys(stateToInherit);
results = [];
for (i = 0, len = stateMapping.length; i < len; i++) {
key = stateMapping[i];
results.push(base[key] = stateToInherit[key]);
}
return results;
};
cache = {
textNodes: {},
object: {
set: function(B) {
var propsMap, selector;
selector = B.selector || B.property;
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, selector, isMultiChoice) {
if (object._sb_map && object._sb_map[selector]) {
return boundInstances[object._sb_map[selector]];
}
}
},
simpleObject: {
set: function(B) {
return Object.defineProperty(B.object, '_sb_ID', {
'configurable': true,
'value': B.ID
});
},
get: function(object) {
return boundInstances[object._sb_ID];
}
}
};
escapeRegEx = /[.*+?^${}()|[\]\\]/g;
pholderRegEx = null;
pholderRegExSplit = null;
setPholderRegEx = function() {
var end, middle, start;
start = globalOptions.placeholderStart.replace(escapeRegEx, '\\$&');
end = globalOptions.placeholderEnd.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, nonTemplate) {
var err, errSource;
errSource = getErrSource();
err = nonTemplate ? errorName : errors[errorName];
err += "\n\nCall Source:\n" + errSource;
throw new Error('SimplyBind: ' + err);
};
throwWarning = function(warningName) {
var errSource, warn;
if (!globalOptions.silent) {
errSource = getErrSource();
warn = errors[warningName];
warn += "\n\nCall Source:\n" + errSource;
return console.log('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() {
return (new Error).stack.split('\n').slice(3, 10).map(function(line) {
return line.replace(errRegEx, function(all, extras, source) {
return "@ " + source;
});
}).join('\n');
};
errRegEx = /[@(?:\wat\s)](.*)\/(.+)$/;
Error.prototype.stack = '\n\n\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,
updateEvenIfUndefined: false,
updateEvenIfSame: false,
updateOnBind: true,
mutateInherited: false,
trackArrayChildren: false,
simpleSelector: false,
promiseTransforms: false,
placeholderStart: '{{',
placeholderEnd: '}}'
};
setPholderRegEx();
SimplyBind = function(subject, options, isBeingProxied) {
if ((!subject && subject !== 0) || (typeof subject !== 'string' && typeof subject !== 'number' && typeof subject !== 'function' && !(subject instanceof Array))) {
if (!checkIf.isBindingInterface(subject)) {
throwError('invalidParamName');
}
}
if (typeof subject === 'object' && !(subject instanceof Array)) {
return new BindingInterface(subject._, 1);
} else {
return new BindingInterface(null, 0, null, subject, isBeingProxied, options);
}
};
Binding = function(type, state, isBeingProxied) {
var arrayBinding, subjectValue;
extendState(this, state);
this.type = type;
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.type !== 'Event') {
subjectValue = this.fetchDirectValue(null, isBeingProxied);
if (typeof subjectValue === 'undefined' && 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.simpleObject.get(this.object);
if (arrayBinding && arrayBinding.options.trackArrayChildren && arrayBinding.trackedChildren.indexOf(this.property) === -1) {
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(Private, stage, inheritedState, subject, isBeingProxied, options) {
var key, newObjectType, object, 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 (typeof subject === 'function') {
Private = this.setObject(subject, 'Func', true, isBeingProxied);
this.stage = 1;
} else if (subject instanceof Array) {
Private = this.setObject(subject, 'Array', true);
this.stage = 1;
} else {
if (typeof subject === 'number') {
subject = subject.toString();
}
this.selector = this.property = subject;
if (!this.options.simpleSelector) {
if (this.selector.indexOf(':') !== -1) {
split = this.property.split(':');
this.descriptor = split[0];
this.property = split[1];
}
if (this.selector.indexOf('.') !== -1) {
split = this.property.split('.');
this.property = split[0];
this.placeholder = split.slice(1).join('.');
}
this.selector = this.property;
}
}
break;
case 1:
if (!Private) {
object = subject;
newObjectType = (function() {
switch (false) {
case !this.state.hasEventName:
return 'Event';
default:
return 'ObjectProp';
}
}).call(this);
Private = this.setObject(object, newObjectType);
}
}
Object.defineProperties(this, {
'_': {
value: Private
},
'ID': {
get: function() {
return Private.ID;
}
},
'value': {
get: function() {
return Private.value;
}
},
'original': {
get: function() {
return Private.objects || Private.object;
}
},
'dependents': {
get: function() {
return Private.deps.slice().map(function(dep) {
return dep.object;
});
}
},
'lastProxied': {
get: function() {
return this.proxies[this.proxies.length - 1];
}
},
'new': {
value: function(stage, object) {
return new BindingInterface(Private, stage, this, object, isBeingProxied);
}
}
});
return this;
};
Binding.prototype = {
makePropertyLive: function(force) {
var _this, getter, opts, origGetFn, origSetFn, propertyDescriptor, ref, ref1, 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);
if ((shouldWriteLiveProp && !this.isLiveProp) || force) {
this.isLiveProp = true;
origGetFn = ((ref = propertyDescriptor.get) != null ? ref.bind(this.object) : void 0) || function() {
return _this.value;
};
origSetFn = ((ref1 = propertyDescriptor.set) != null ? ref1.bind(this.object) : void 0) || function() {};
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 != null ? propertyDescriptor.enumerable : void 0,
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) {
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) {
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);
return 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(type, isBeingProxied) {
if (type == null) {
type = this.type;
}
switch (false) {
case type !== 'Func':
if (!isBeingProxied) {
return this.object();
}
break;
case type !== 'Array':
return this.object;
default:
return this.object[this.property];
}
},
setValue: function(newValue, specificPlaceholder, updater, fromSelf) {
var currentValue, isNewValue, isValidValue, placeholderValue, prevValue;
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;
}
}
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) && typeof newValue.then === 'function') {
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 (typeof transformFn !== 'function') {
return throwWarning('fnOnly');
} else {
for (i = 0, len = subjects.length; i < len; i++) {
prox = subjects[i];
proxID = prox.ID;
proxID += prox.placeholder ? '.' + prox.placeholder : '';
this.addTransform(proxID, transformFn);
prox = prox._;
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 (typeof conditionFn !== 'function') {
return throwWarning('fnOnly');
} else {
for (i = 0, len = subjects.length; i < len; i++) {
prox = subjects[i];
proxID = prox.ID;
proxID += prox.placeholder ? '.' + prox.placeholder : '';
this.addCondition(proxID, conditionFn);
prox = prox._;
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 (typeof this.valueOriginal === 'string') {
this.pholderContexts = this.valueOriginal.split(pholderRegExSplit);
index = 0;
return 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) {
return this.registerEvent(this.eventName, this.customEventMethod["in"]);
}
},
registerEvent: function(eventName, customInMethod) {
var attachmentMethod, defaultInMethod;
if (this.attachedEvents.indexOf(eventName) !== -1) {
return;
}
defaultInMethod = 'on';
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 = 'removeListener';
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 (!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 = 'emit';
emitMethod = this.customEventMethod.out || defaultOutMethod;
if (!subject[emitMethod]) {
emitMethod = defaultOutMethod;
}
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 = {
setObject: function(object, newObjectType, isSimpleObject, isBeingProxied) {
var cacheObjectType, fromCache, newPrivate;
this.object = object;
cacheObjectType = isSimpleObject ? 'simpleObject' : 'object';
fromCache = cache[cacheObjectType].get(object, this.selector, this.isMultiChoice);
if (fromCache) {
fromCache.placeholder = this.placeholder;
if (this.placeholder && !fromCache.pholderValues) {
fromCache.valueOriginal = fromCache.fetchDirectValue();
fromCache.scanForPholders();
}
if (fromCache.type === 'ObjectProp' && !(this.property in object)) {
fromCache.makePropertyLive(true);
}
setOptionsForBinding(fromCache, this.optionsPassed, true);
return fromCache;
} else {
newPrivate = new Binding(newObjectType, this, isBeingProxied);
cache[cacheObjectType].set(newPrivate);
return newPrivate;
}
}
};
BindingInterface.prototype = Object.create(BindingInterfacePrivate);
BindingInterfacePublic = {
of: function(object) {
var proxied;
if (this.stage !== 0 && this.stage !== 2) {
throwErrorUnavail(methodNames[0]);
}
if (!((typeof object === 'object') || (typeof object === 'function'))) {
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 || typeof eventName !== 'string') {
throwErrorBadArg(methodNames[1], eventName);
} else if (isNaN(parseInt(this.property))) {
throwWarning('badEventArg');
}
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) {
if (this.stage !== 1 || this.stage === 1 && this._.type === 'Array') {
throwErrorUnavail(methodNames[8]);
}
if (typeof transformFn !== 'function') {
throwWarning('fnOnly');
} else {
this._.selfTransform = transformFn;
if (this._.options.updateOnBind) {
this.set(this.value);
}
}
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 boundProxied, originCondition, originTransform, proxied, transformToUse;
if (this.stage !== 3 || this.state.hasMultiTransform) {
throwErrorUnavail(methodNames[13]);
}
proxied = this.lastProxied;
boundProxied = proxied._.addDep(this._, true);
originTransform = this._.transforms[proxied.ID];
originCondition = this._.conditions[proxied.ID];
if (originTransform || dontOrAltTransform) {
transformToUse = typeof dontOrAltTransform === 'function' ? dontOrAltTransform : originTransform;
if (transformToUse && dontOrAltTransform !== false) {
boundProxied.addTransform(this.ID, transformToUse);
}
}
if (originCondition) {
boundProxied.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 (typeof delay === 'number' && 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, true);
ref = this.proxies;
for (i = 0, len = ref.length; i < len; i++) {
proxied = ref[i];
setOptionsForBinding(proxied._, newOptions, true);
}
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);
}
};
Object.defineProperties(SimplyBind, {
'version': {
value: '1.3.2'
},
'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 (globalOptions[option] != null) {
globalOptions[option] = newValue;
if (option.indexOf('place') !== -1) {
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 && ((typeof object === 'object') || (typeof object === 'function'))) {
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;
})();