@danielkalen/simplybind
Version:
Magically simple, framework-less one-way/two-way data binding for frontend/backend in ~5kb.
1,048 lines (1,045 loc) • 36.3 kB
JavaScript
// Generated by CoffeeScript 1.10.0
(function() {
var Binding, BindingInterface, SimplyBind, applyPlaceholders, arrayMutatorMethods, boundInstances, cache, computeProxied, errRegEx, errors, escapeRegEx, genID, genObj, getErrSource, globalOptions, handleUpdateFromEvent, isBindingInterface, maybeUpdateDep, 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);
};
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();
}
};
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) {
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 (!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, selector, property, object, objects, options, extraState, isBeingProxied) {
var arrayBinding, key, subjectValue, value;
for (key in extraState) {
value = extraState[key];
this[key] = value;
}
this.options = options;
this.type = type;
this.ID = genID();
this.property = property;
this.selector = selector;
this.object = object;
this.objects = objects;
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, ref, split, value;
ref = inheritedState || {};
for (key in ref) {
value = ref[key];
this[key] = value;
}
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) {
break;
}
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.getState(), object, isBeingProxied);
}
}
});
return this;
};
Binding.prototype = {
makePropertyLive: function(force) {
var _this, opts, origGetFn, origSetFn, propertyDescriptor, ref, ref1, 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 = (propertyDescriptor != null ? (ref = propertyDescriptor.get) != null ? ref.bind(this.object) : void 0 : void 0) || function() {
return _this.value;
};
origSetFn = (propertyDescriptor != null ? (ref1 = propertyDescriptor.set) != null ? ref1.bind(this.object) : void 0 : void 0) || function() {};
return Object.defineProperty(this.object, this.property, {
configurable: true,
enumerable: propertyDescriptor != null ? propertyDescriptor.enumerable : void 0,
get: function() {
return origGetFn();
},
set: function(newValue) {
_this.setValue(newValue);
return origSetFn(newValue);
}
});
}
} 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 (type) {
case 'Func':
if (!isBeingProxied) {
return this.object();
}
break;
case '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.isEvent) && 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.type === 'Event') {
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);
};
BindingInterface.prototype = {
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 (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;
}
};
BindingInterface.prototype.update = BindingInterface.prototype.set;
BindingInterface.prototype.twoWay = BindingInterface.prototype.bothWays;
BindingInterface.prototype.pipe = BindingInterface.prototype.chainTo;
methodNames = Object.keys(BindingInterface.prototype);
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(BindingInterface.prototype, {
setObject: {
value: function(object, newObjectType, isSimpleObject, isBeingProxied) {
var cacheObjectType, extraState, fromCache, newPrivate;
this.object = object;
cacheObjectType = isSimpleObject ? 'simpleObject' : 'object';
fromCache = cache[cacheObjectType].get(object, this.selector);
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.options, true);
return fromCache;
} else {
extraState = {
isDom: this.isDom,
isDomInput: this.isDomInput,
placeholder: this.placeholder,
eventName: this.eventName,
customEventMethod: this.customEventMethod,
isEvent: this.state.hasEventName
};
newPrivate = new Binding(newObjectType, this.selector, this.property, this.object, this.objects, this.options, extraState, isBeingProxied);
cache[cacheObjectType].set(newPrivate);
return newPrivate;
}
}
},
getState: {
value: function() {
return {
options: this.options,
selector: this.selector,
object: this.object,
property: this.property,
descriptor: this.descriptor,
placeholder: this.placeholder,
eventName: this.eventName,
customEventMethod: this.customEventMethod,
proxied: this.proxied,
proxies: this.proxies,
state: this.state
};
}
}
});
Object.defineProperties(SimplyBind, {
'version': {
value: '1.2.1'
},
'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;
})();