Comparing version 0.4.6 to 0.5.1
{ | ||
"name": "imvvm", | ||
"version": "0.4.6", | ||
"version": "0.5.1", | ||
"homepage": "https://github.com/entrendipity/imvvm", | ||
@@ -5,0 +5,0 @@ "authors": [ |
@@ -12,3 +12,3 @@ !function(e){if("object"==typeof exports)module.exports=e();else if("function"==typeof define&&define.amd)define(e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.IMVVM=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){ | ||
exports.getInitialState = function(appNamespace, domainModel, stateChangedHandler, disableUndo) { | ||
exports.getInitialState = function(appNamespace, domainModel, stateChangedHandler, enableUndo) { | ||
@@ -18,96 +18,123 @@ if(typeof stateChangedHandler !== 'function'){ | ||
} | ||
enableUndo === void(0) ? true : enableUndo; | ||
var ApplicationDataContext, | ||
thisAppState = {}, | ||
dependsOn, | ||
appState = {}, | ||
dataContexts = {}, | ||
watchedProps, | ||
watchedPropsLen, | ||
watchList = {}, | ||
dependents = [], | ||
domain, | ||
reprocessing = false; | ||
watchListPropA, | ||
watchListPropB; | ||
disableUndo === void(0) ? false : disableUndo; | ||
var transitionState = function(nextState, prevState, watchedDataContext){ | ||
var processed = false, | ||
dependencies, | ||
initialize; | ||
var depProp; | ||
prevState = prevState || {}; | ||
var configure = function(obj, propName){ | ||
var newObj = {}; | ||
for(var k in obj){ | ||
if(obj.hasOwnProperty(k)){ | ||
if(Object.prototype.toString.call(obj[k]) === '[object Object]'){ | ||
newObj[k] = obj[k]; | ||
} else { | ||
newObj[k] = {}; | ||
newObj[k][propName] = obj[k]; | ||
} | ||
} | ||
} | ||
return newObj; | ||
}; | ||
if(nextState === void(0)){ | ||
initialize = true; | ||
nextState = {}; | ||
var getDeps = function(nextState, dataContext){ | ||
var dependencies = {}, | ||
props, | ||
watchedValue; | ||
if(!dataContext){ | ||
return {}; | ||
} | ||
var getDependencies = function(dataContext){ | ||
var deps = {}, | ||
props, | ||
watchedValue; | ||
dataContext = ('dependsOn' in dataContext) ? dataContext : {dependsOn: dataContext}; | ||
if('dependsOn' in dataContext){ | ||
dataContext.dependsOn.forEach(function(dependency){ | ||
watchedValue = {}; | ||
props = dependency.property.split('.'); | ||
props.forEach(function(prop, idx){ | ||
if(idx === 0){ | ||
watchedValue = nextState[prop]; | ||
} else { | ||
watchedValue = watchedValue ? watchedValue[prop] : void(0); | ||
} | ||
}); | ||
if('alias' in dependency){ | ||
deps[dependency.alias] = watchedValue; | ||
for(var dependency in dataContext.dependsOn){ | ||
if(dataContext.dependsOn.hasOwnProperty(dependency)){ | ||
watchedValue = {}; | ||
props = dataContext.dependsOn[dependency].property.split('.'); | ||
props.forEach(function(prop, idx){ | ||
if(idx === 0){ | ||
watchedValue = nextState[prop]; | ||
} else { | ||
deps[props.join('$')] = watchedValue; | ||
watchedValue = watchedValue ? watchedValue[prop] : void(0); | ||
} | ||
}); | ||
dependencies[dependency] = watchedValue; | ||
} | ||
return deps; | ||
}; | ||
return dependencies; | ||
}; | ||
for(var dataContext in domain){ | ||
if(domain.hasOwnProperty(dataContext)){ | ||
dependencies = getDependencies(domain[dataContext]); | ||
//Need to inject dependencies so that the state for this object can change | ||
//if required. Can't use appState as that is provided after the object is created | ||
if(initialize){ | ||
nextState[dataContext] = new dataContexts[dataContext](nextState[dataContext], dependencies, | ||
prevState[dataContext]).getInitialState(); | ||
} else { | ||
nextState[dataContext] = new dataContexts[dataContext](nextState[dataContext], dependencies, | ||
prevState[dataContext]); | ||
var transitionState = function(caller, nextState, prevState, subscribers){ | ||
nextState = nextState || {}; | ||
prevState = prevState || {}; | ||
var processed = false, | ||
tempDeps, | ||
nextVal; | ||
if(caller !== appNamespace){ | ||
nextState[caller] = new dataContexts[caller](nextState[caller], | ||
getDeps(nextState, domain[caller]), prevState[caller]); | ||
} | ||
if(subscribers){ | ||
if(!!dependsOn){ | ||
tempDeps = getDeps(nextState, dependsOn); | ||
nextState = extend(nextState, tempDeps); | ||
for(var depKey in dependsOn){ | ||
if(dependsOn.hasOwnProperty(depKey) && ('onStateChange' in dependsOn[depKey])){ | ||
nextVal = {}; | ||
nextVal[depKey] = nextState[depKey]; | ||
nextState = extend(nextState, dependsOn[depKey].onStateChange(nextVal)); | ||
} | ||
} | ||
if(watchedDataContext){ | ||
if(processed && watchedDataContext.subscribers.indexOf(dataContext) !== -1){ | ||
dependencies = getDependencies(domain[watchedDataContext.name]); | ||
nextState[watchedDataContext.name] = new dataContexts[watchedDataContext.name](nextState[watchedDataContext.name], | ||
dependencies, prevState[watchedDataContext.name]); | ||
} | ||
processed = processed ? processed : dataContext === watchedDataContext.name; | ||
} | ||
} | ||
nextState = new ApplicationDataContext(extend(nextState, tempDeps), prevState, enableUndo); | ||
subscribers.forEach(function(subscriber){ | ||
if(subscriber !== appNamespace){ | ||
nextState[subscriber] = new dataContexts[subscriber](nextState[subscriber], | ||
getDeps(nextState, domain[subscriber]), prevState[subscriber]); | ||
} | ||
}); | ||
} | ||
return nextState; | ||
}; | ||
var appStateChangedHandler = function(caller, newState, callback, initialize) { | ||
var appStateChangedHandler = function(caller, newState, callback) { | ||
if((newState === void(0) || newState === null || Object.keys(newState).length === 0)){ | ||
return; | ||
} | ||
var nextState = {}, | ||
prevState = {}, | ||
watchedDataContext = void(0), | ||
subscribers = [], | ||
newStateKeys, | ||
newStateKeysLen, | ||
subscriberKeys, | ||
rollback = false; | ||
subscriberNames, | ||
idxKey, | ||
idxDepFld, | ||
tmpNextState = {}, | ||
changeState, | ||
dependsOnObj; | ||
initialize === void(0) ? false : initialize; | ||
if(!initialize && (newState === void(0) || newState === null || Object.keys(newState).length === 0)){ | ||
return; | ||
} | ||
var DomainModel = !!newState ? Object.getPrototypeOf(newState).constructor.classType === "DomainModel" : false; | ||
//Check to see if appState is a ready made state object. If so | ||
//pass it straight to the stateChangedHandler. If a callback was passed in | ||
//it would be assigned to newState | ||
if(DomainModel) { | ||
if(Object.getPrototypeOf(newState).constructor.classType === "DomainModel") { | ||
//This means previous state has been requested | ||
@@ -118,78 +145,120 @@ //so set nextState to the previous state | ||
prevState = newState.previousState; | ||
rollback = true; | ||
} else { | ||
if(caller in watchList){ | ||
newStateKeys = Object.keys(newState); | ||
newStateKeysLen = newStateKeys.length; | ||
subscriberKeys = {}; | ||
subscriberNames = {}; | ||
for(var i= 0; i < newStateKeysLen; i++){ | ||
for (var i = newStateKeysLen - 1; i >= 0; i--){ | ||
if(watchList[caller][newStateKeys[i]]){ | ||
subscriberKeys[watchList[caller][newStateKeys[i]]] = true; | ||
for(var j = watchList[caller][newStateKeys[i]].length - 1; j >= 0; j--){ | ||
subscriberNames[watchList[caller][newStateKeys[i]][j].dataContext] = true; | ||
} | ||
} | ||
} | ||
watchedDataContext = {}; | ||
watchedDataContext.name = caller; | ||
watchedDataContext.subscribers = Object.keys(subscriberKeys); | ||
//If there are no subscriber reset watchedDataContext | ||
watchedDataContext = !!watchedDataContext.subscribers.length ? watchedDataContext : void(0); | ||
subscribers = Object.keys(subscriberNames); | ||
hasSubscribers = !!subscribers.length; | ||
} | ||
if(caller !== appNamespace){ | ||
nextState[caller] = newState; | ||
nextState = extend(thisAppState.state, nextState); | ||
nextState = extend(appState.state, nextState); | ||
if(hasSubscribers){ | ||
subscribers.forEach(function(sub){ | ||
for(idxKey=newStateKeysLen-1; idxKey >= 0; idxKey--){ | ||
if(watchList[caller][newStateKeys[idxKey]]){ | ||
var depFldArr = watchList[caller][newStateKeys[idxKey]]; | ||
for(idxDepFld = depFldArr.length - 1; idxDepFld >= 0; idxDepFld--){ | ||
dependsOnObj = depFldArr[idxDepFld].dataContext === appNamespace ? dependsOn : | ||
domain[depFldArr[idxDepFld].dataContext].dependsOn; | ||
if(dependsOnObj[depFldArr[idxDepFld].alias].onStateChange){ | ||
tmpNextState[depFldArr[idxDepFld].alias] = nextState[caller][newStateKeys[idxKey]]; | ||
changeState = dependsOnObj[depFldArr[idxDepFld].alias]. | ||
onStateChange.call(appState.state[depFldArr[idxDepFld].dataContext], tmpNextState); | ||
if(Object.prototype.toString.call(changeState) === '[object Object]'){ | ||
if(depFldArr[idxDepFld].dataContext === appNamespace){ | ||
nextState = extend(nextState, changeState); | ||
} else { | ||
nextState[depFldArr[idxDepFld].dataContext] = | ||
extend(nextState[depFldArr[idxDepFld].dataContext], changeState); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
}); | ||
} | ||
} else { | ||
//appDataContext is calling function | ||
if(initialize) { | ||
nextState = extend(transitionState(), newState); | ||
} else { | ||
nextState = extend(thisAppState.state, newState); | ||
} | ||
nextState = extend(appState.state, newState); | ||
} | ||
prevState = reprocessing ? thisAppState.previousState : thisAppState; | ||
nextState = transitionState(nextState, thisAppState.state, watchedDataContext); | ||
prevState = appState; | ||
nextState = transitionState(caller, nextState, appState.state, hasSubscribers ? subscribers : false); | ||
} | ||
prevState = prevState || {}; | ||
Object.freeze(prevState); | ||
if(!!prevState){ | ||
Object.freeze(prevState); | ||
} | ||
//Create a new App state context. | ||
thisAppState = new ApplicationDataContext(nextState, prevState, disableUndo); | ||
if(!!thisAppState.getValidState && !rollback && !reprocessing) { | ||
var validationObj = thisAppState.getValidState(thisAppState.state, thisAppState.previousState); | ||
var validationKeys = Object.keys(validationObj); | ||
for (var keyIdx = validationKeys.length - 1; keyIdx >= 0; keyIdx--) { | ||
if(Object.prototype.toString.call(validationObj[validationKeys[keyIdx]]) !== '[object Object]' && | ||
Object.prototype.toString.call(validationObj[validationKeys[keyIdx]]) !== '[object Array]' && | ||
validationObj[validationKeys[keyIdx]] !== thisAppState.state[validationKeys[keyIdx]]){ | ||
reprocessing = true; | ||
thisAppState.setState(extend(thisAppState.state, validationObj)); | ||
reprocessing = false; | ||
break; | ||
} | ||
}; | ||
} | ||
appState = new ApplicationDataContext(nextState, prevState, enableUndo); | ||
//All the work is done! -> Notify the View | ||
//Provided for the main app to return from init() to the View | ||
if(!reprocessing){ | ||
Object.freeze(thisAppState); | ||
Object.freeze(thisAppState.state); | ||
stateChangedHandler(thisAppState, caller, callback); | ||
return thisAppState; | ||
} | ||
Object.freeze(appState); | ||
Object.freeze(appState.state); | ||
stateChangedHandler(appState, caller, callback); | ||
return appState; | ||
}; | ||
//Initialize Application Data Context | ||
ApplicationDataContext = domainModel.call(this, appStateChangedHandler.bind(this, appNamespace)); | ||
var applicationDataContext = new ApplicationDataContext({}, {}, disableUndo); | ||
domain = applicationDataContext.getDomainDataContext(); | ||
appState = new ApplicationDataContext({}, void(0), enableUndo, true); | ||
dependsOn = appState.getDependencies ? configure(appState.getDependencies(), 'property') : void(0); | ||
if(dependsOn){ | ||
dependents.push(appNamespace); | ||
for(depProp in dependsOn){ | ||
if(dependsOn.hasOwnProperty(depProp)){ | ||
watchedProps = dependsOn[depProp].property.split('.'); | ||
watchedPropsLen = watchedProps.length; | ||
watchListPropA = watchedPropsLen > 1 ? watchedProps[0] : appNamespace; | ||
watchListPropB = watchedPropsLen > 1 ? watchedProps[1] : watchedProps[0]; | ||
watchList[watchListPropA] = watchList[watchListPropA] || {}; | ||
watchList[watchListPropA][watchListPropB] = watchList[watchListPropA][watchListPropB] || []; | ||
if(watchList[watchListPropA][watchListPropB].indexOf(appNamespace) === -1){ | ||
watchList[watchListPropA][watchListPropB].push({dataContext:appNamespace, alias: depProp}); | ||
} | ||
} | ||
} | ||
} | ||
domain = configure(appState.getDomainDataContext(), 'viewModel'); | ||
for(var dataContext in domain){ | ||
if(domain.hasOwnProperty(dataContext)){ | ||
dataContexts[dataContext] = domain[dataContext].viewModel.call(this, appStateChangedHandler.bind(this, dataContext)); | ||
if('dependsOn' in domain[dataContext]){ | ||
for(var i = 0, len = domain[dataContext].dependsOn.length; i < len; i++){ | ||
watchedProps = domain[dataContext].dependsOn[i].property.split('.'); | ||
if(watchedProps.length > 1){ | ||
watchList[watchedProps[0]] = watchList[watchedProps[0]] || {}; | ||
watchList[watchedProps[0]][watchedProps[1]] = watchList[watchedProps[0]][watchedProps[1]] || []; | ||
if(watchList[watchedProps[0]][watchedProps[1]].indexOf(dataContext) === -1){ | ||
watchList[watchedProps[0]][watchedProps[1]].push(dataContext); | ||
appState[dataContext] = new dataContexts[dataContext]({}, {}, {}, true); | ||
if(appState[dataContext].getDependencies){ | ||
dependents.push(dataContext); | ||
domain[dataContext].dependsOn = configure(appState[dataContext].getDependencies(), 'property'); | ||
for(depProp in domain[dataContext].dependsOn){ | ||
if(domain[dataContext].dependsOn.hasOwnProperty(depProp)){ | ||
watchedProps = domain[dataContext].dependsOn[depProp].property.split('.'); | ||
watchedPropsLen = watchedProps.length; | ||
watchListPropA = watchedPropsLen > 1 ? watchedProps[0] : appNamespace; | ||
watchListPropB = watchedPropsLen > 1 ? watchedProps[1] : watchedProps[0]; | ||
watchList[watchListPropA] = watchList[watchListPropA] || {}; | ||
watchList[watchListPropA][watchListPropB] = watchList[watchListPropA][watchListPropB] || []; | ||
if(watchList[watchListPropA][watchListPropB].indexOf(dataContext) === -1){ | ||
watchList[watchListPropA][watchListPropB].push({dataContext:dataContext, alias: depProp}); | ||
} | ||
@@ -201,3 +270,12 @@ } | ||
} | ||
return applicationDataContext.getInitialState(); | ||
dependents.forEach(function(dependent){ | ||
if(dependent !== appNamespace){ | ||
appState[dependent] = new dataContexts[dependent](appState[dependent], | ||
getDeps(appState, domain[dependent]), {}); | ||
} | ||
}); | ||
appState = new ApplicationDataContext(extend(appState, getDeps(appState, dependsOn)), void(0), enableUndo); | ||
Object.freeze(appState.state); | ||
return Object.freeze(appState); | ||
}; | ||
@@ -232,3 +310,3 @@ },{"./utils":8}],3:[function(_dereq_,module,exports){ | ||
var ConvenienceConstructor = function(raiseStateChangeHandler) { | ||
var ConvenienceConstructor = function(stateChangedHandler) { | ||
var descriptor = new DescriptorConstructor(); | ||
@@ -291,30 +369,14 @@ return descriptor.construct.apply(ConvenienceConstructor, arguments); | ||
Mixin: { | ||
construct: function(raiseStateChangeHandler){ | ||
construct: function(stateChangedHandler){ | ||
var desc = getDescriptor.call(this); | ||
desc.proto.setState = raiseStateChangeHandler; | ||
desc.proto.setState = stateChangedHandler; | ||
var initialize = function(initState, callback){ | ||
return desc.proto.setState(initState, callback, true); | ||
} | ||
var dataContext = function(nextState, prevState, disableUndo) { | ||
var initFunc; | ||
var calcFld; | ||
nextState = nextState || {}; | ||
prevState = prevState || {}; | ||
if(!('getInitialState' in desc.proto)){ | ||
desc.proto.getInitialState = function(){ | ||
return initialize(); | ||
} | ||
} else { | ||
initFunc = desc.proto.getInitialState; | ||
desc.proto.getInitialState = function(){ | ||
return initialize(initFunc.call(this)); | ||
} | ||
} | ||
var model = Object.create(desc.proto, desc.descriptor); | ||
var dataContext = function(nextState, prevState, enableUndo, initialize) { | ||
Object.defineProperty(model, 'state', { | ||
var freezeFields = desc.freezeFields; | ||
var domainModel = Object.create(desc.proto, desc.descriptor); | ||
//Need to have 'state' prop in domainModel before can extend domainModel to get correct state | ||
Object.defineProperty(domainModel, 'state', { | ||
configurable: true, | ||
@@ -325,22 +387,5 @@ enumerable: false, | ||
}); | ||
//Need to have state prop in model before can extend model to get correct state | ||
nextState = extend(nextState, model); | ||
//runs everytime to initialize calculated state but will not run the calc func | ||
//if the prop has already been initialized | ||
if(!!desc.originalSpec.getInitialCalculatedState){ | ||
for (var i = desc.calculatedFields.length - 1; i >= 0; i--) { | ||
if(!(desc.calculatedFields[i] in nextState) || nextState[desc.calculatedFields[i]] === void(0)){ | ||
calcFld = {} | ||
calcFld[desc.calculatedFields[i]] = desc.originalSpec.getInitialCalculatedState. | ||
call(model, nextState, prevState)[desc.calculatedFields[i]]; | ||
if(calcFld[desc.calculatedFields[i]] !== void(0)){ | ||
nextState = extend(nextState,calcFld); | ||
} | ||
} | ||
}; | ||
} | ||
if(!disableUndo && !!Object.keys(prevState).length){ | ||
Object.defineProperty(nextState, 'previousState', { | ||
if(!!enableUndo && !!prevState){ | ||
Object.defineProperty(domainModel, 'previousState', { | ||
configurable: false, | ||
@@ -353,18 +398,32 @@ enumerable: false, | ||
Object.defineProperty(nextState, 'state', { | ||
prevState = prevState || {}; | ||
if(initialize && ('getInitialState' in domainModel)){ | ||
//Add state prop so that it can be referenced from within getInitialState | ||
nextState = extend(nextState, domainModel.getInitialState.call(domainModel)); | ||
} | ||
//attach the nextState props to domainModel if they don't exist | ||
var keys = Object.keys(nextState); | ||
for (var i = keys.length - 1; i >= 0; i--) { | ||
if(!(keys[i] in domainModel)){ | ||
domainModel[keys[i]] = nextState[keys[i]]; | ||
} | ||
}; | ||
Object.defineProperty(domainModel, 'state', { | ||
configurable: false, | ||
enumerable: false, | ||
writable: false, | ||
value: extend(nextState) | ||
value: nextState | ||
}); | ||
for(var k in desc.descriptor){ | ||
if(desc.descriptor.hasOwnProperty(k)){ | ||
Object.defineProperty(nextState, k, desc.descriptor[k]); | ||
} | ||
} | ||
//freeze arrays and domainModel instances | ||
for (var fld = freezeFields.length - 1; fld >= 0; fld--) { | ||
Object.freeze(domainModel[freezeFields[fld].fieldName]); | ||
}; | ||
nextState.__proto__ = model.__proto__; | ||
return nextState; | ||
return domainModel; | ||
}; | ||
return dataContext; | ||
@@ -385,9 +444,13 @@ } | ||
Mixin: { | ||
construct: function(raiseStateChangeHandler){ | ||
construct: function(stateChangedHandler){ | ||
var desc = getDescriptor.call(this); | ||
var dataContext = function(nextState, prevState, withContext) { | ||
var freezeFields = desc.freezeFields; | ||
var model = Object.create(desc.proto, desc.descriptor); | ||
var argCount = arguments.length; | ||
var lastArgIsBool = typeof Array.prototype.slice.call(arguments, -1)[0] === 'boolean'; | ||
var calcFld; | ||
var initialize = false; | ||
@@ -398,3 +461,3 @@ if(argCount === 0){ | ||
prevState = {}; | ||
withContext = true; | ||
withContext = false; | ||
} else if(argCount === 1){ | ||
@@ -406,14 +469,18 @@ if(lastArgIsBool){ | ||
} else { | ||
//assume prevState is same as nextState | ||
prevState = nextState; | ||
withContext = true; | ||
//assume this is a new Object and there is no prevState | ||
prevState = {}; | ||
withContext = false; | ||
initialize = true; | ||
} | ||
} else if(argCount === 2){ | ||
if(lastArgIsBool){ | ||
//assume this is a new Object and there is no prevState | ||
withContext = prevState; | ||
prevState = nextState; | ||
prevState = {}; | ||
initialize = true; | ||
} else { | ||
withContext = true; | ||
withContext = false; | ||
} | ||
} | ||
nextState = ('state' in nextState) ? nextState.state : nextState; | ||
@@ -428,24 +495,5 @@ prevState = ('state' in prevState) ? prevState.state : prevState; | ||
}); | ||
//Need to have state prop in model before can extend model to get correct state | ||
nextState = extend(nextState, model); | ||
//runs everytime to initialize calculated state but will not run the calc func | ||
//if the prop has already been initialized | ||
if(!!desc.originalSpec.getInitialCalculatedState){ | ||
for (var i = desc.calculatedFields.length - 1; i >= 0; i--) { | ||
if(!(desc.calculatedFields[i] in nextState) || nextState[desc.calculatedFields[i]] === void(0)){ | ||
calcFld = {} | ||
calcFld[desc.calculatedFields[i]] = desc.originalSpec.getInitialCalculatedState. | ||
call(model, nextState, prevState)[desc.calculatedFields[i]]; | ||
if(calcFld[desc.calculatedFields[i]] !== void(0)){ | ||
nextState = extend(nextState,calcFld); | ||
} | ||
} | ||
}; | ||
} | ||
//runs everytime | ||
if(desc.originalSpec.getValidState){ | ||
nextState = extend(nextState, | ||
desc.originalSpec.getValidState.call(model, nextState, prevState)); | ||
if(initialize && ('getInitialState' in model)){ | ||
nextState = extend(nextState, model.getInitialState.call(model)); | ||
} | ||
@@ -457,6 +505,6 @@ | ||
configurable: true, | ||
enumerable: true, | ||
enumerable: false, | ||
set: function(context){ | ||
this.setState = function(nextState, callback){ //callback may be useful for DB updates | ||
return raiseStateChangeHandler.bind(context) | ||
return stateChangedHandler.bind(context) | ||
.call(context, extend(this.state, nextState), this.state, callback); | ||
@@ -476,8 +524,6 @@ }.bind(this); | ||
Object.keys(model).forEach(function(key){ | ||
if(Object.prototype.toString.call(this[key]) === '[object Object]' || | ||
Object.prototype.toString.call(this[key]) === '[object Array]'){ | ||
Object.freeze(this[key]); | ||
} | ||
}.bind(model)); | ||
//freeze arrays and model instances | ||
for (var i = freezeFields.length - 1; i >= 0; i--) { | ||
Object.freeze(model[freezeFields[i].fieldName]); | ||
}; | ||
@@ -487,2 +533,3 @@ if(!withContext){ | ||
} | ||
return model; | ||
@@ -505,9 +552,9 @@ }; | ||
Mixin: { | ||
construct: function(raiseStateChangeHandler){ | ||
construct: function(stateChangedHandler){ | ||
var desc = getDescriptor.call(this); | ||
desc.proto.setState = raiseStateChangeHandler; | ||
var dataContext = function(nextState, dependencies, prevState) { | ||
var initFunc; | ||
var calcFld; | ||
desc.proto.setState = stateChangedHandler; | ||
var dataContext = function(nextState, dependencies, prevState, initialize) { | ||
//nextState has already been extended with prevState in core | ||
@@ -517,17 +564,7 @@ nextState = extend(nextState, dependencies); | ||
prevState = ('state' in prevState) ? prevState.state : prevState; | ||
if(!('getInitialState' in desc.proto)){ | ||
desc.proto.getInitialState = function(){ | ||
return dataContext(); | ||
} | ||
} else { | ||
initFunc = desc.proto.getInitialState; | ||
desc.proto.getInitialState = function(){ | ||
return dataContext(initFunc.call(this)); | ||
} | ||
} | ||
var model = Object.create(desc.proto, desc.descriptor); | ||
var freezeFields = desc.freezeFields; | ||
var viewModel = Object.create(desc.proto, desc.descriptor); | ||
Object.defineProperty(model, 'state', { | ||
Object.defineProperty(viewModel, 'state', { | ||
configurable: true, | ||
@@ -538,27 +575,8 @@ enumerable: false, | ||
}); | ||
//Need to have state prop in model before can extend model to get correct state | ||
nextState = extend(nextState, model); | ||
//runs everytime to initialize calculated state but will not run the calc func | ||
//if the prop has already been initialized | ||
if(!!desc.originalSpec.getInitialCalculatedState){ | ||
for (var i = desc.calculatedFields.length - 1; i >= 0; i--) { | ||
if(!(desc.calculatedFields[i] in nextState) || nextState[desc.calculatedFields[i]] === void(0)){ | ||
calcFld = {} | ||
calcFld[desc.calculatedFields[i]] = desc.originalSpec.getInitialCalculatedState. | ||
call(model, nextState, prevState)[desc.calculatedFields[i]]; | ||
if(calcFld[desc.calculatedFields[i]] !== void(0)){ | ||
nextState = extend(nextState,calcFld); | ||
} | ||
} | ||
}; | ||
if(initialize && ('getInitialState' in viewModel)){ | ||
nextState = extend(nextState, viewModel.getInitialState.call(viewModel)); | ||
} | ||
//runs everytime | ||
if(desc.originalSpec.getValidState){ | ||
nextState = extend(nextState, | ||
desc.originalSpec.getValidState.call(model, nextState, prevState)); | ||
} | ||
Object.defineProperty(model, 'state', { | ||
Object.defineProperty(viewModel, 'state', { | ||
configurable: false, | ||
@@ -570,16 +588,15 @@ enumerable: false, | ||
Object.keys(model).forEach(function(key){ | ||
if(Object.prototype.toString.call(this[key]) === '[object Object]' && | ||
('context' in this[key])){ | ||
this[key].context = this; | ||
Object.freeze(this[key]); | ||
} else if(Object.prototype.toString.call(this[key]) === '[object Array]'){ | ||
Object.freeze(this[key]); | ||
//freeze arrays and viewModel instances | ||
for (var i = freezeFields.length - 1; i >= 0; i--) { | ||
if(freezeFields[i].kind === 'instance' && | ||
('context' in viewModel[freezeFields[i].fieldName])){ | ||
viewModel[freezeFields[i].fieldName].context = viewModel; | ||
} | ||
}.bind(model)); | ||
Object.freeze(viewModel[freezeFields[i].fieldName]); | ||
}; | ||
//Add dependencies to model | ||
//Add dependencies to viewModel | ||
for(var dep in dependencies){ | ||
if(dependencies.hasOwnProperty(dep) && dep[0] !== '_'){ | ||
Object.defineProperty(model, dep, { | ||
Object.defineProperty(viewModel, dep, { | ||
configurable: false, | ||
@@ -594,3 +611,3 @@ enumerable: false, | ||
Object.freeze(nextState); | ||
return Object.freeze(model); | ||
return Object.freeze(viewModel); | ||
@@ -612,3 +629,3 @@ }; | ||
stateChangedHandler: function(dataContext, caller, callback){ | ||
this.setState({applicationDataContext: dataContext}, function(){ | ||
this.setState({domainDataContext: dataContext}, function(){ | ||
//send all state back to caller | ||
@@ -621,9 +638,9 @@ //useful if you need to know what other parts of the app | ||
if(typeof callback === 'function'){ | ||
if(this.state === null || !('applicationDataContext' in this.state)){ | ||
if(this.state === null || !('domainDataContext' in this.state)){ | ||
callback(void(0)); | ||
} else { | ||
if(caller in this.state.applicationDataContext){ | ||
callback(this.state.applicationDataContext[caller]); | ||
if(caller in this.state.domainDataContext){ | ||
callback(this.state.domainDataContext[caller]); | ||
} else if(caller === NAMESPACE) { | ||
callback(this.state.applicationDataContext); | ||
callback(this.state.domainDataContext); | ||
} else { | ||
@@ -638,5 +655,5 @@ callback(void(0)); | ||
getInitialState: function(){ | ||
var appDataContext = core.getInitialState(NAMESPACE, this.props.domainModel, | ||
this.stateChangedHandler, this.props.disableUndo); | ||
return {applicationDataContext: appDataContext}; | ||
var dataContext = core.getInitialState(NAMESPACE, this.props.domainModel, | ||
this.stateChangedHandler, this.props.enableUndo); | ||
return {domainDataContext: dataContext}; | ||
} | ||
@@ -650,8 +667,12 @@ | ||
var utils = { | ||
getDescriptor: function(){ | ||
var descriptor = {}; | ||
var proto = this.prototype; | ||
var calcFlds = []; | ||
var autoFreeze = []; | ||
//var originalSpec = this.originalSpec || {}; | ||
if('__processedObject__' in this.originalSpec){ | ||
return this.originalSpec.__processedObject__; | ||
} | ||
for(var key in this.originalSpec){ | ||
@@ -661,21 +682,12 @@ if(this.originalSpec.hasOwnProperty(key)){ | ||
//assume it is a descriptor | ||
if('calculated' in this.originalSpec[key]){ | ||
//We want to preserve the calculated flag on originalSpec | ||
descriptor[key] = utils.extend(this.originalSpec[key]); | ||
descriptor[key].enumerable = !this.originalSpec[key].calculated; | ||
delete descriptor[key].calculated; | ||
calcFlds.push(key); | ||
} else if(!('enumerable' in this.originalSpec[key])){ | ||
//No need to preserve the pseudo flag on originalSpec | ||
if('pseudo' in this.originalSpec[key]){ | ||
this.originalSpec[key].enumerable = !this.originalSpec[key].pseudo; | ||
delete this.originalSpec[key].pseudo; | ||
} else { | ||
//default enumerable to true | ||
this.originalSpec[key].enumerable = true; | ||
this.originalSpec[key].enumerable = true; | ||
if('kind' in this.originalSpec[key]){ | ||
if(this.originalSpec[key].kind === 'pseudo'){ | ||
this.originalSpec[key].enumerable = false; | ||
} else { //'instance' || 'array' | ||
autoFreeze.push({fieldName: key, kind: this.originalSpec[key].kind}); | ||
} | ||
descriptor[key] = this.originalSpec[key]; | ||
} else { | ||
descriptor[key] = this.originalSpec[key]; | ||
delete this.originalSpec[key].kind; | ||
} | ||
descriptor[key] = this.originalSpec[key]; | ||
} else { | ||
@@ -689,9 +701,13 @@ proto[key] = this.originalSpec[key]; | ||
} | ||
return { | ||
this.originalSpec.__processedObject__ = { | ||
descriptor: descriptor, | ||
proto: proto, | ||
originalSpec: this.originalSpec || {}, | ||
calculatedFields: calcFlds | ||
} | ||
freezeFields: autoFreeze | ||
}; | ||
return this.originalSpec.__processedObject__; | ||
}, | ||
extend: function () { | ||
@@ -709,2 +725,3 @@ var newObj = {}; | ||
}, | ||
mixInto: function(constructor, methodBag) { | ||
@@ -719,2 +736,3 @@ var methodName; | ||
} | ||
}; | ||
@@ -721,0 +739,0 @@ |
@@ -1,1 +0,1 @@ | ||
!function(b){if("object"==typeof exports){module.exports=b()}else{if("function"==typeof define&&define.amd){define(b)}else{var a;"undefined"!=typeof window?a=window:"undefined"!=typeof global?a=global:"undefined"!=typeof self&&(a=self),a.IMVVM=b()}}}(function(){var d,b,a;return(function c(f,k,h){function g(n,l){if(!k[n]){if(!f[n]){var i=typeof require=="function"&&require;if(!l&&i){return i(n,!0)}if(e){return e(n,!0)}throw new Error("Cannot find module '"+n+"'")}var m=k[n]={exports:{}};f[n][0].call(m.exports,function(o){var p=f[n][1][o];return g(p?p:o)},m,m.exports,c,f,k,h)}return k[n].exports}var e=typeof require=="function"&&require;for(var j=0;j<h.length;j++){g(h[j])}return g})({1:[function(h,g,f){var e=h("./src/imvvm.js");g.exports=e},{"./src/imvvm.js":3}],2:[function(h,g,f){var e=h("./utils");var i=e.extend;f.getInitialState=function(k,p,u,y){if(typeof u!=="function"){throw new TypeError()}var x,w={},j={},v,n={},q,t=false;y===void (0)?false:y;var o=function(C,G,E){var H=false,F,B;G=G||{};if(C===void (0)){B=true;C={}}var D=function(J){var L={},K,I;if("dependsOn" in J){J.dependsOn.forEach(function(M){I={};K=M.property.split(".");K.forEach(function(O,N){if(N===0){I=C[O]}else{I=I?I[O]:void (0)}});if("alias" in M){L[M.alias]=I}else{L[K.join("$")]=I}})}return L};for(var A in q){if(q.hasOwnProperty(A)){F=D(q[A]);if(B){C[A]=new j[A](C[A],F,G[A]).getInitialState()}else{C[A]=new j[A](C[A],F,G[A])}if(E){if(H&&E.subscribers.indexOf(A)!==-1){F=D(q[E.name]);C[E.name]=new j[E.name](C[E.name],F,G[E.name])}H=H?H:A===E.name}}}return C};var m=function(C,E,M,I){var P={},L={},O=void (0),J,H,N,K=false;I===void (0)?false:I;if(!I&&(E===void (0)||E===null||Object.keys(E).length===0)){return}var G=!!E?Object.getPrototypeOf(E).constructor.classType==="DomainModel":false;if(G){P=i(E);L=E.previousState;K=true}else{if(C in n){J=Object.keys(E);H=J.length;N={};for(var F=0;F<H;F++){if(n[C][J[F]]){N[n[C][J[F]]]=true}}O={};O.name=C;O.subscribers=Object.keys(N);O=!!O.subscribers.length?O:void (0)}if(C!==k){P[C]=E;P=i(w.state,P)}else{if(I){P=i(o(),E)}else{P=i(w.state,E)}}L=t?w.previousState:w;P=o(P,w.state,O)}L=L||{};Object.freeze(L);w=new x(P,L,y);if(!!w.getValidState&&!K&&!t){var B=w.getValidState(w.state,w.previousState);var A=Object.keys(B);for(var D=A.length-1;D>=0;D--){if(Object.prototype.toString.call(B[A[D]])!=="[object Object]"&&Object.prototype.toString.call(B[A[D]])!=="[object Array]"&&B[A[D]]!==w.state[A[D]]){t=true;w.setState(i(w.state,B));t=false;break}}}if(!t){Object.freeze(w);Object.freeze(w.state);u(w,C,M);return w}};x=p.call(this,m.bind(this,k));var z=new x({},{},y);q=z.getDomainDataContext();for(var l in q){if(q.hasOwnProperty(l)){j[l]=q[l].viewModel.call(this,m.bind(this,l));if("dependsOn" in q[l]){for(var r=0,s=q[l].dependsOn.length;r<s;r++){v=q[l].dependsOn[r].property.split(".");if(v.length>1){n[v[0]]=n[v[0]]||{};n[v[0]][v[1]]=n[v[0]][v[1]]||[];if(n[v[0]][v[1]].indexOf(l)===-1){n[v[0]][v[1]].push(l)}}}}}}return z.getInitialState()}},{"./utils":8}],3:[function(f,g,j){var k=f("./imvvmModel");var p=f("./imvvmViewModel");var i=f("./imvvmDomainModel");var r=f("./mixin");var q=f("./utils");var n=q.extend;var m=q.mixInto;var l=function(){};var h=function(){};var o=function(){};m(l,k.Mixin);m(h,p.Mixin);m(o,i.Mixin);var e={createClass:function(u,v,t){var y=function(){};y.prototype=new u();y.prototype.constructor=y;var x=y;var w=function(z){var A=new x();return A.construct.apply(w,arguments)};w.componentConstructor=y;y.ConvenienceConstructor=w;w.originalSpec=t;w.type=y;y.prototype.type=y;w.classType=v;y.prototype.classType=v;return w}};var s={createModel:e.createClass.bind(this,l,"Model"),createViewModel:e.createClass.bind(this,h,"ViewModel"),createDomainModel:e.createClass.bind(this,o,"DomainModel"),mixin:r};g.exports=s},{"./imvvmDomainModel":4,"./imvvmModel":5,"./imvvmViewModel":6,"./mixin":7,"./utils":8}],4:[function(j,h,f){var e=j("./utils");var k=e.extend;var g=e.getDescriptor;var i={Mixin:{construct:function(n){var o=g.call(this);o.proto.setState=n;var m=function(p,q){return o.proto.setState(p,q,true)};var l=function(t,v,p){var w;var u;t=t||{};v=v||{};if(!("getInitialState" in o.proto)){o.proto.getInitialState=function(){return m()}}else{w=o.proto.getInitialState;o.proto.getInitialState=function(){return m(w.call(this))}}var r=Object.create(o.proto,o.descriptor);Object.defineProperty(r,"state",{configurable:true,enumerable:false,writable:true,value:t});t=k(t,r);if(!!o.originalSpec.getInitialCalculatedState){for(var s=o.calculatedFields.length-1;s>=0;s--){if(!(o.calculatedFields[s] in t)||t[o.calculatedFields[s]]===void (0)){u={};u[o.calculatedFields[s]]=o.originalSpec.getInitialCalculatedState.call(r,t,v)[o.calculatedFields[s]];if(u[o.calculatedFields[s]]!==void (0)){t=k(t,u)}}}}if(!p&&!!Object.keys(v).length){Object.defineProperty(t,"previousState",{configurable:false,enumerable:false,writable:false,value:v})}Object.defineProperty(t,"state",{configurable:false,enumerable:false,writable:false,value:k(t)});for(var q in o.descriptor){if(o.descriptor.hasOwnProperty(q)){Object.defineProperty(t,q,o.descriptor[q])}}t.__proto__=r.__proto__;return t};return l}}};h.exports=i},{"./utils":8}],5:[function(i,h,f){var e=i("./utils");var k=e.extend;var g=e.getDescriptor;var j={Mixin:{construct:function(m){var n=g.call(this);var l=function(s,u,o){var p=Object.create(n.proto,n.descriptor);var v=arguments.length;var r=typeof Array.prototype.slice.call(arguments,-1)[0]==="boolean";var t;if(v===0){s={};u={};o=true}else{if(v===1){if(r){o=s;s={};u={}}else{u=s;o=true}}else{if(v===2){if(r){o=u;u=s}else{o=true}}}}s=("state" in s)?s.state:s;u=("state" in u)?u.state:u;Object.defineProperty(p,"state",{configurable:true,enumerable:false,writable:true,value:s});s=k(s,p);if(!!n.originalSpec.getInitialCalculatedState){for(var q=n.calculatedFields.length-1;q>=0;q--){if(!(n.calculatedFields[q] in s)||s[n.calculatedFields[q]]===void (0)){t={};t[n.calculatedFields[q]]=n.originalSpec.getInitialCalculatedState.call(p,s,u)[n.calculatedFields[q]];if(t[n.calculatedFields[q]]!==void (0)){s=k(s,t)}}}}if(n.originalSpec.getValidState){s=k(s,n.originalSpec.getValidState.call(p,s,u))}if(o){Object.defineProperty(p,"context",{configurable:true,enumerable:true,set:function(w){this.setState=function(x,y){return m.bind(w).call(w,k(this.state,x),this.state,y)}.bind(this);delete this.context}})}Object.defineProperty(p,"state",{configurable:false,enumerable:false,writable:false,value:s});Object.keys(p).forEach(function(w){if(Object.prototype.toString.call(this[w])==="[object Object]"||Object.prototype.toString.call(this[w])==="[object Array]"){Object.freeze(this[w])}}.bind(p));if(!o){Object.freeze(p)}return p};return l}}};h.exports=j},{"./utils":8}],6:[function(j,i,g){var f=j("./utils");var k=f.extend;var h=f.getDescriptor;var e={Mixin:{construct:function(m){var n=h.call(this);n.proto.setState=m;var l=function(q,r,u){var v;var t;q=k(q,r);u=u||{};u=("state" in u)?u.state:u;if(!("getInitialState" in n.proto)){n.proto.getInitialState=function(){return l()}}else{v=n.proto.getInitialState;n.proto.getInitialState=function(){return l(v.call(this))}}var o=Object.create(n.proto,n.descriptor);Object.defineProperty(o,"state",{configurable:true,enumerable:false,writable:true,value:q});q=k(q,o);if(!!n.originalSpec.getInitialCalculatedState){for(var p=n.calculatedFields.length-1;p>=0;p--){if(!(n.calculatedFields[p] in q)||q[n.calculatedFields[p]]===void (0)){t={};t[n.calculatedFields[p]]=n.originalSpec.getInitialCalculatedState.call(o,q,u)[n.calculatedFields[p]];if(t[n.calculatedFields[p]]!==void (0)){q=k(q,t)}}}}if(n.originalSpec.getValidState){q=k(q,n.originalSpec.getValidState.call(o,q,u))}Object.defineProperty(o,"state",{configurable:false,enumerable:false,writable:false,value:q});Object.keys(o).forEach(function(w){if(Object.prototype.toString.call(this[w])==="[object Object]"&&("context" in this[w])){this[w].context=this;Object.freeze(this[w])}else{if(Object.prototype.toString.call(this[w])==="[object Array]"){Object.freeze(this[w])}}}.bind(o));for(var s in r){if(r.hasOwnProperty(s)&&s[0]!=="_"){Object.defineProperty(o,s,{configurable:false,enumerable:false,writable:false,value:r[s]})}}Object.freeze(q);return Object.freeze(o)};return l}}};i.exports=e},{"./utils":8}],7:[function(j,i,h){var f=j("./core");var e="__IMVVM__";var g={stateChangedHandler:function(k,l,m){this.setState({applicationDataContext:k},function(){if(typeof m==="function"){if(this.state===null||!("applicationDataContext" in this.state)){m(void (0))}else{if(l in this.state.applicationDataContext){m(this.state.applicationDataContext[l])}else{if(l===e){m(this.state.applicationDataContext)}else{m(void (0))}}}}}.bind(this))},getInitialState:function(){var k=f.getInitialState(e,this.props.domainModel,this.stateChangedHandler,this.props.disableUndo);return{applicationDataContext:k}}};i.exports=g},{"./core":2}],8:[function(h,g,f){var e={getDescriptor:function(){var l={};var k=this.prototype;var i=[];for(var j in this.originalSpec){if(this.originalSpec.hasOwnProperty(j)){if("get" in this.originalSpec[j]||"set" in this.originalSpec[j]){if("calculated" in this.originalSpec[j]){l[j]=e.extend(this.originalSpec[j]);l[j].enumerable=!this.originalSpec[j].calculated;delete l[j].calculated;i.push(j)}else{if(!("enumerable" in this.originalSpec[j])){if("pseudo" in this.originalSpec[j]){this.originalSpec[j].enumerable=!this.originalSpec[j].pseudo;delete this.originalSpec[j].pseudo}else{this.originalSpec[j].enumerable=true}l[j]=this.originalSpec[j]}else{l[j]=this.originalSpec[j]}}}else{k[j]=this.originalSpec[j]}}}if(!("extend" in k)){k.extend=e.extend}return{descriptor:l,proto:k,originalSpec:this.originalSpec||{},calculatedFields:i}},extend:function(){var j={};for(var l=0;l<arguments.length;l++){var m=arguments[l];for(var k in m){if(m.hasOwnProperty(k)){j[k]=m[k]}}}return j},mixInto:function(j,k){var i;for(i in k){if(!k.hasOwnProperty(i)){continue}j.prototype[i]=k[i]}}};g.exports=e},{}]},{},[1])(1)}); | ||
!function(b){if("object"==typeof exports){module.exports=b()}else{if("function"==typeof define&&define.amd){define(b)}else{var a;"undefined"!=typeof window?a=window:"undefined"!=typeof global?a=global:"undefined"!=typeof self&&(a=self),a.IMVVM=b()}}}(function(){var d,b,a;return(function c(f,k,h){function g(n,l){if(!k[n]){if(!f[n]){var i=typeof require=="function"&&require;if(!l&&i){return i(n,!0)}if(e){return e(n,!0)}throw new Error("Cannot find module '"+n+"'")}var m=k[n]={exports:{}};f[n][0].call(m.exports,function(o){var p=f[n][1][o];return g(p?p:o)},m,m.exports,c,f,k,h)}return k[n].exports}var e=typeof require=="function"&&require;for(var j=0;j<h.length;j++){g(h[j])}return g})({1:[function(h,g,f){var e=h("./src/imvvm.js");g.exports=e},{"./src/imvvm.js":3}],2:[function(h,g,f){var e=h("./utils");var i=e.extend;f.getInitialState=function(l,m,u,k){if(typeof u!=="function"){throw new TypeError()}k===void (0)?true:k;var B,j,t={},r={},y,n,w={},v=[],D,q,p;var z;var A=function(H,G){var F={};for(var E in H){if(H.hasOwnProperty(E)){if(Object.prototype.toString.call(H[E])==="[object Object]"){F[E]=H[E]}else{F[E]={};F[E][G]=H[E]}}}return F};var C=function(H,F){var J={},I,E;if(!F){return{}}F=("dependsOn" in F)?F:{dependsOn:F};for(var G in F.dependsOn){if(F.dependsOn.hasOwnProperty(G)){E={};I=F.dependsOn[G].property.split(".");I.forEach(function(L,K){if(K===0){E=H[L]}else{E=E?E[L]:void (0)}});J[G]=E}}return J};var x=function(E,F,J,I){F=F||{};J=J||{};var L=false,K,G;if(E!==l){F[E]=new r[E](F[E],C(F,D[E]),J[E])}if(I){if(!!j){K=C(F,j);F=i(F,K);for(var H in j){if(j.hasOwnProperty(H)&&("onStateChange" in j[H])){G={};G[H]=F[H];F=i(F,j[H].onStateChange(G))}}}F=new B(i(F,K),J,k);I.forEach(function(M){if(M!==l){F[M]=new r[M](F[M],C(F,D[M]),J[M])}})}return F};var o=function(G,I,S){if((I===void (0)||I===null||Object.keys(I).length===0)){return}var T={},R={},E=[],P,K,N,F,O,L={},Q,M;if(Object.getPrototypeOf(I).constructor.classType==="DomainModel"){T=i(I);R=I.previousState}else{if(G in w){P=Object.keys(I);K=P.length;N={};for(var J=K-1;J>=0;J--){if(w[G][P[J]]){for(var H=w[G][P[J]].length-1;H>=0;H--){N[w[G][P[J]][H].dataContext]=true}}}E=Object.keys(N);hasSubscribers=!!E.length}if(G!==l){T[G]=I;T=i(t.state,T);if(hasSubscribers){E.forEach(function(V){for(F=K-1;F>=0;F--){if(w[G][P[F]]){var U=w[G][P[F]];for(O=U.length-1;O>=0;O--){M=U[O].dataContext===l?j:D[U[O].dataContext].dependsOn;if(M[U[O].alias].onStateChange){L[U[O].alias]=T[G][P[F]];Q=M[U[O].alias].onStateChange.call(t.state[U[O].dataContext],L);if(Object.prototype.toString.call(Q)==="[object Object]"){if(U[O].dataContext===l){T=i(T,Q)}else{T[U[O].dataContext]=i(T[U[O].dataContext],Q)}}}}}}})}}else{T=i(t.state,I)}R=t;T=x(G,T,t.state,hasSubscribers?E:false)}if(!!R){Object.freeze(R)}t=new B(T,R,k);Object.freeze(t);Object.freeze(t.state);u(t,G,S);return t};B=m.call(this,o.bind(this,l));t=new B({},void (0),k,true);j=t.getDependencies?A(t.getDependencies(),"property"):void (0);if(j){v.push(l);for(z in j){if(j.hasOwnProperty(z)){y=j[z].property.split(".");n=y.length;q=n>1?y[0]:l;p=n>1?y[1]:y[0];w[q]=w[q]||{};w[q][p]=w[q][p]||[];if(w[q][p].indexOf(l)===-1){w[q][p].push({dataContext:l,alias:z})}}}}D=A(t.getDomainDataContext(),"viewModel");for(var s in D){if(D.hasOwnProperty(s)){r[s]=D[s].viewModel.call(this,o.bind(this,s));t[s]=new r[s]({},{},{},true);if(t[s].getDependencies){v.push(s);D[s].dependsOn=A(t[s].getDependencies(),"property");for(z in D[s].dependsOn){if(D[s].dependsOn.hasOwnProperty(z)){y=D[s].dependsOn[z].property.split(".");n=y.length;q=n>1?y[0]:l;p=n>1?y[1]:y[0];w[q]=w[q]||{};w[q][p]=w[q][p]||[];if(w[q][p].indexOf(s)===-1){w[q][p].push({dataContext:s,alias:z})}}}}}}v.forEach(function(E){if(E!==l){t[E]=new r[E](t[E],C(t,D[E]),{})}});t=new B(i(t,C(t,j)),void (0),k);Object.freeze(t.state);return Object.freeze(t)}},{"./utils":8}],3:[function(f,g,j){var k=f("./imvvmModel");var p=f("./imvvmViewModel");var i=f("./imvvmDomainModel");var r=f("./mixin");var q=f("./utils");var n=q.extend;var m=q.mixInto;var l=function(){};var h=function(){};var o=function(){};m(l,k.Mixin);m(h,p.Mixin);m(o,i.Mixin);var e={createClass:function(u,v,t){var y=function(){};y.prototype=new u();y.prototype.constructor=y;var x=y;var w=function(z){var A=new x();return A.construct.apply(w,arguments)};w.componentConstructor=y;y.ConvenienceConstructor=w;w.originalSpec=t;w.type=y;y.prototype.type=y;w.classType=v;y.prototype.classType=v;return w}};var s={createModel:e.createClass.bind(this,l,"Model"),createViewModel:e.createClass.bind(this,h,"ViewModel"),createDomainModel:e.createClass.bind(this,o,"DomainModel"),mixin:r};g.exports=s},{"./imvvmDomainModel":4,"./imvvmModel":5,"./imvvmViewModel":6,"./mixin":7,"./utils":8}],4:[function(j,h,f){var e=j("./utils");var k=e.extend;var g=e.getDescriptor;var i={Mixin:{construct:function(m){var n=g.call(this);n.proto.setState=m;var l=function(w,u,o,t){var q=n.freezeFields;var r=Object.create(n.proto,n.descriptor);Object.defineProperty(r,"state",{configurable:true,enumerable:false,writable:true,value:w});if(!!o&&!!u){Object.defineProperty(r,"previousState",{configurable:false,enumerable:false,writable:false,value:u})}u=u||{};if(t&&("getInitialState" in r)){w=k(w,r.getInitialState.call(r))}var v=Object.keys(w);for(var s=v.length-1;s>=0;s--){if(!(v[s] in r)){r[v[s]]=w[v[s]]}}Object.defineProperty(r,"state",{configurable:false,enumerable:false,writable:false,value:w});for(var p=q.length-1;p>=0;p--){Object.freeze(r[q[p].fieldName])}return r};return l}}};h.exports=i},{"./utils":8}],5:[function(i,h,f){var e=i("./utils");var k=e.extend;var g=e.getDescriptor;var j={Mixin:{construct:function(m){var n=g.call(this);var l=function(w,v,o){var p=n.freezeFields;var t=Object.create(n.proto,n.descriptor);var r=arguments.length;var u=typeof Array.prototype.slice.call(arguments,-1)[0]==="boolean";var s=false;if(r===0){w={};v={};o=false}else{if(r===1){if(u){o=w;w={};v={}}else{v={};o=false;s=true}}else{if(r===2){if(u){o=v;v={};s=true}else{o=false}}}}w=("state" in w)?w.state:w;v=("state" in v)?v.state:v;Object.defineProperty(t,"state",{configurable:true,enumerable:false,writable:true,value:w});if(s&&("getInitialState" in t)){w=k(w,t.getInitialState.call(t))}if(o){Object.defineProperty(t,"context",{configurable:true,enumerable:false,set:function(x){this.setState=function(y,z){return m.bind(x).call(x,k(this.state,y),this.state,z)}.bind(this);delete this.context}})}Object.defineProperty(t,"state",{configurable:false,enumerable:false,writable:false,value:w});for(var q=p.length-1;q>=0;q--){Object.freeze(t[p[q].fieldName])}if(!o){Object.freeze(t)}return t};return l}}};h.exports=j},{"./utils":8}],6:[function(j,i,g){var f=j("./utils");var k=f.extend;var h=f.getDescriptor;var e={Mixin:{construct:function(m){var n=h.call(this);n.proto.setState=m;var l=function(r,t,v,o){r=k(r,t);v=v||{};v=("state" in v)?v.state:v;var s=n.freezeFields;var p=Object.create(n.proto,n.descriptor);Object.defineProperty(p,"state",{configurable:true,enumerable:false,writable:true,value:r});if(o&&("getInitialState" in p)){r=k(r,p.getInitialState.call(p))}Object.defineProperty(p,"state",{configurable:false,enumerable:false,writable:false,value:r});for(var q=s.length-1;q>=0;q--){if(s[q].kind==="instance"&&("context" in p[s[q].fieldName])){p[s[q].fieldName].context=p}Object.freeze(p[s[q].fieldName])}for(var u in t){if(t.hasOwnProperty(u)&&u[0]!=="_"){Object.defineProperty(p,u,{configurable:false,enumerable:false,writable:false,value:t[u]})}}Object.freeze(r);return Object.freeze(p)};return l}}};i.exports=e},{"./utils":8}],7:[function(j,i,h){var f=j("./core");var e="__IMVVM__";var g={stateChangedHandler:function(k,l,m){this.setState({domainDataContext:k},function(){if(typeof m==="function"){if(this.state===null||!("domainDataContext" in this.state)){m(void (0))}else{if(l in this.state.domainDataContext){m(this.state.domainDataContext[l])}else{if(l===e){m(this.state.domainDataContext)}else{m(void (0))}}}}}.bind(this))},getInitialState:function(){var k=f.getInitialState(e,this.props.domainModel,this.stateChangedHandler,this.props.enableUndo);return{domainDataContext:k}}};i.exports=g},{"./core":2}],8:[function(h,g,f){var e={getDescriptor:function(){var l={};var k=this.prototype;var i=[];if("__processedObject__" in this.originalSpec){return this.originalSpec.__processedObject__}for(var j in this.originalSpec){if(this.originalSpec.hasOwnProperty(j)){if("get" in this.originalSpec[j]||"set" in this.originalSpec[j]){this.originalSpec[j].enumerable=true;if("kind" in this.originalSpec[j]){if(this.originalSpec[j].kind==="pseudo"){this.originalSpec[j].enumerable=false}else{i.push({fieldName:j,kind:this.originalSpec[j].kind})}delete this.originalSpec[j].kind}l[j]=this.originalSpec[j]}else{k[j]=this.originalSpec[j]}}}if(!("extend" in k)){k.extend=e.extend}this.originalSpec.__processedObject__={descriptor:l,proto:k,originalSpec:this.originalSpec||{},freezeFields:i};return this.originalSpec.__processedObject__},extend:function(){var j={};for(var l=0;l<arguments.length;l++){var m=arguments[l];for(var k in m){if(m.hasOwnProperty(k)){j[k]=m[k]}}}return j},mixInto:function(j,k){var i;for(i in k){if(!k.hasOwnProperty(i)){continue}j.prototype[i]=k[i]}}};g.exports=e},{}]},{},[1])(1)}); |
@@ -9,11 +9,14 @@ /*jshint unused: vars */ | ||
//run once | ||
getInitialState: function(){ //optional | ||
//dataContext keys define the dataContext names that will appear in | ||
//the Viewand and associates a ViewModel. | ||
getDomainDataContext: function(){ | ||
return { | ||
online: true | ||
hobbies: HobbiesViewModel, | ||
persons: PersonsViewModel | ||
}; | ||
}, | ||
//runs to initialize calculated fields | ||
getInitialCalculatedState: function(nextState, prevState){ | ||
getInitialState: function(){ //optional | ||
return { | ||
online: true, | ||
busy: false | ||
@@ -23,5 +26,13 @@ }; | ||
//runs last every time transition to new State (after all ViewModels have been updated) | ||
getValidState: function(nextState, prevState){ | ||
if(!!nextState.hobbies.selected){ | ||
getDependencies: function(){ | ||
return { | ||
selectedHobby: { | ||
property: 'hobbies.selected', | ||
onStateChange: this.toggleBusyState | ||
} | ||
} | ||
}, | ||
toggleBusyState: function(nextState){ | ||
if(!!nextState.selectedHobby){ | ||
return {busy: true}; | ||
@@ -44,3 +55,2 @@ } else { | ||
busy: { | ||
calculated: true, | ||
get: function(){ | ||
@@ -59,23 +69,3 @@ return this.state.busy; | ||
}, | ||
//dataContext keys define the dataContext names that will appear in | ||
//the View. viewModel refer to the ViewModels and | ||
//dependsOn will enable props to be transfered to other ViewModels. | ||
//if alias is supplied that prop name will appear in ViewModel state | ||
//otherwise it will appear concatenated by '$' i.e. hobbies$selected | ||
//if you would like dependency prop to not appear in the View, supply | ||
//and alias with a preceding underscore i.e. alias: _busy | ||
getDomainDataContext: function(){ | ||
return { | ||
hobbies: { | ||
viewModel: HobbiesViewModel, | ||
dependsOn: [{property: 'persons.selected', alias: '_selectedPerson'}, | ||
{property: 'busy', alias: '_busy'}] | ||
}, | ||
persons: { | ||
viewModel: PersonsViewModel, | ||
dependsOn: [{property: 'hobbies.selected'}, | ||
{property: 'online', alias: 'imOnline'}] | ||
} | ||
}; | ||
} | ||
}); |
@@ -15,5 +15,5 @@ /** | ||
domainModel={DomainModel} | ||
disableUndo={false} />, | ||
enableUndo={true} />, | ||
document.getElementById('container')); | ||
@@ -8,41 +8,6 @@ /*jshint unused: false */ | ||
uuid: function () { | ||
/*jshint bitwise:false */ | ||
var i, random; | ||
var uuid = ''; | ||
for (i = 0; i < 32; i++) { | ||
random = Math.random() * 16 | 0; | ||
if (i === 8 || i === 12 || i === 16 || i === 20) { | ||
uuid += '-'; | ||
} | ||
uuid += (i === 12 ? 4 : (i === 16 ? (random & 3 | 8) : random)) | ||
.toString(16); | ||
} | ||
return uuid; | ||
}, | ||
addHobby: function(value){ | ||
var arr; | ||
if(this.hobbies.indexOf(value) === -1){ | ||
arr = this.hobbies.slice(0); | ||
this.hobbies = arr.concat(value); | ||
} | ||
}, | ||
deleteHobby: function(value){ | ||
this.hobbies = this.hobbies.filter(function(hobby){ | ||
return hobby !== value; | ||
}); | ||
}, | ||
calculateAge: function(dob){ // dob is a date | ||
var DOB = new Date(dob); | ||
var ageDate = new Date(Date.now() - DOB.getTime()); // miliseconds from | ||
var age = Math.abs(ageDate.getFullYear() - 1970); | ||
return Number.isNaN(age) ? 'Enter your Birthday' : age + ' years old'; | ||
}, | ||
getInitialCalculatedState: function(nextState, prevState){ | ||
getInitialState: function(/*nextState, prevState*/){ | ||
return { | ||
age: this.calculateAge(nextState.dob), | ||
age: this.calculateAge(this.dob), | ||
id: this.id ? this.id : this.uuid() | ||
}; | ||
@@ -53,3 +18,3 @@ }, | ||
get: function(){ | ||
return this.state.id ? this.state.id : this.uuid(); | ||
return this.state.id; | ||
} | ||
@@ -77,3 +42,3 @@ }, | ||
fullName: { | ||
pseudo: true, | ||
kind: 'pseudo', | ||
get: function(){ | ||
@@ -125,5 +90,4 @@ if(this.lastName === void(0)){ | ||
//Calculated field -> dob | ||
//Calculated field <- dob | ||
age: { | ||
calculated: true, | ||
get: function(){ | ||
@@ -134,2 +98,9 @@ return this.state.age; | ||
calculateAge: function(dob){ // dob is a date | ||
var DOB = new Date(dob); | ||
var ageDate = new Date(Date.now() - DOB.getTime()); // miliseconds from | ||
var age = Math.abs(ageDate.getFullYear() - 1970); | ||
return Number.isNaN(age) ? 'Enter your Birthday' : age + ' years old'; | ||
}, | ||
gender: { | ||
@@ -143,2 +114,3 @@ get: function(){ return this.state.gender; }, | ||
hobbies: { | ||
kind: 'array', | ||
get: function(){ return this.state.hobbies ? this.state.hobbies : []; }, | ||
@@ -149,3 +121,34 @@ set: function(newArray){ | ||
}, | ||
addHobby: function(value){ | ||
var arr; | ||
if(this.hobbies.indexOf(value) === -1){ | ||
arr = this.hobbies.slice(0); | ||
this.hobbies = arr.concat(value); | ||
} | ||
}, | ||
deleteHobby: function(value){ | ||
this.hobbies = this.hobbies.filter(function(hobby){ | ||
return hobby !== value; | ||
}); | ||
}, | ||
uuid: function () { | ||
/*jshint bitwise:false */ | ||
var i, random; | ||
var uuid = ''; | ||
for (i = 0; i < 32; i++) { | ||
random = Math.random() * 16 | 0; | ||
if (i === 8 || i === 12 || i === 16 || i === 20) { | ||
uuid += '-'; | ||
} | ||
uuid += (i === 12 ? 4 : (i === 16 ? (random & 3 | 8) : random)) | ||
.toString(16); | ||
} | ||
return uuid; | ||
}, | ||
}); | ||
@@ -7,2 +7,34 @@ /*jshint unused: false */ | ||
var HobbiesViewModel = IMVVM.createViewModel({ | ||
getDependencies: function(){ | ||
return { | ||
_selectedPerson: { | ||
property: 'persons.selected', | ||
onStateChange: this.resetSelected | ||
}, | ||
busy: 'busy' | ||
} | ||
}, | ||
hobbies: { | ||
kind: 'pseudo',//kind: 'pseudo' because get is supplied from other source | ||
//if referencing a dependency this kind = 'pseudo' | ||
get: function(){ | ||
return this.state._selectedPerson.hobbies; | ||
} | ||
}, | ||
busyText: { | ||
kind: 'pseudo', //kind: 'pseudo' because its not calculated but is supplied externally | ||
get: function(){ | ||
return this.state.busy ? 'Im Busy! Go away...' : 'Not doing too much.'; | ||
} | ||
}, | ||
selected: { | ||
get: function(){ | ||
return this.state.selected; | ||
} | ||
}, | ||
select: function(value){ | ||
@@ -25,38 +57,8 @@ var nextState = {}; | ||
//When a dependency changes reset the selected hobby to undefined | ||
resetSelected: function(nextState, prevState) { | ||
if(prevState._selectedPerson && nextState._selectedPerson){ | ||
if(nextState._selectedPerson.id !== prevState._selectedPerson.id && | ||
nextState.selected !== void(0)){ | ||
return void(0); | ||
} | ||
resetSelected: function(newValue) { | ||
if(this.selected !== void(0)){ | ||
return { selected: void(0) }; | ||
} | ||
return nextState.selected; | ||
}, | ||
hobbies: { | ||
pseudo: true, //true because its not calculated but is supplied externally | ||
get: function(){ | ||
return this.state._selectedPerson.hobbies; | ||
} | ||
}, | ||
//runs everytime transition to state occurs | ||
getValidState: function(nextState, prevState){ | ||
return { | ||
selected: this.resetSelected(nextState, prevState), | ||
}; | ||
}, | ||
busyText: { | ||
pseudo: true, //true because its not calculated but is supplied externally | ||
get: function(){ | ||
return this.state._busy ? 'Im Busy! Go away...' : 'Not doing too much.'; | ||
} | ||
}, | ||
selected: { | ||
get: function(){ | ||
return this.state.selected; | ||
} | ||
}, | ||
}); |
@@ -8,2 +8,49 @@ /*jshint unused: false */ | ||
getInitialState: function(){ | ||
var nextState = {}; | ||
nextState.collection = DataService.getData().map(function(person, idx){ | ||
if (idx === 0){ | ||
nextState.selected = this.Person(person, true); | ||
return nextState.selected; | ||
} | ||
return this.Person(person); | ||
}.bind(this)); | ||
return nextState; | ||
}, | ||
getDependencies: function(){ | ||
return { | ||
selectedHobby: 'hobbies.selected', | ||
imOnline: 'online' | ||
} | ||
}, | ||
Person: function(){ | ||
return new PersonModel(this.personStateChangedHandler).apply(this, arguments); | ||
}, | ||
personStateChangedHandler: function(nextState, prevState/*, callback*/){ | ||
var persons = {}; | ||
persons.collection = this.collection.map(function(person){ | ||
if(person.id === nextState.id){ | ||
persons.selected = this.Person(nextState, person, true); | ||
return persons.selected; | ||
} | ||
return person; | ||
}.bind(this)); | ||
this.setState(persons); | ||
}, | ||
selected: { | ||
kind: 'instance', | ||
get: function() { return this.state.selected; } | ||
}, | ||
collection: { | ||
kind: 'array', | ||
get: function(){ return this.state.collection; }, | ||
}, | ||
select: function(id){ | ||
@@ -13,3 +60,3 @@ var nextState = {}; | ||
if(person.id === id){ | ||
nextState.selected = this.Person(person); | ||
nextState.selected = this.Person(person, true); | ||
return nextState.selected; | ||
@@ -34,3 +81,3 @@ } | ||
lastName: name.slice(1).join(' ') | ||
}); | ||
}, true); | ||
nextState.collection = this.collection.slice(0); | ||
@@ -50,5 +97,5 @@ nextState.collection = nextState.collection.concat(nextState.selected); | ||
if (this.selected.id === uid){ | ||
nextState.selected = this.Person(nextState.collection[0]); | ||
nextState.selected = this.Person(nextState.collection[0], true); | ||
} else { | ||
nextState.selected = this.Person(this.selected); | ||
nextState.selected = this.Person(this.selected, true); | ||
} | ||
@@ -58,40 +105,3 @@ } | ||
}, | ||
//runs once to initialize | ||
getInitialState: function(){ | ||
var nextState = {}; | ||
nextState.collection = DataService.getData().map(function(person, idx){ | ||
if (idx === 0){ | ||
nextState.selected = this.Person(person); | ||
return nextState.selected; | ||
} | ||
return this.Person(person, false); | ||
}.bind(this)); | ||
return nextState; | ||
}, | ||
personStateChangeHandler: function(nextState, prevState/*, callback*/){ | ||
var persons = {}; | ||
persons.collection = this.collection.map(function(person){ | ||
if(person.id === nextState.id){ | ||
persons.selected = this.Person(nextState, person); | ||
return persons.selected; | ||
} | ||
return person; | ||
}.bind(this)); | ||
this.setState(persons); | ||
}, | ||
Person: function(){ | ||
return new PersonModel(this.personStateChangeHandler).apply(this, arguments); | ||
}, | ||
collection: { | ||
get: function(){ return this.state.collection; }, | ||
}, | ||
selected: { | ||
get: function() { return this.state.selected; } | ||
}, | ||
}); |
@@ -17,26 +17,27 @@ /** | ||
console.log('------------------------------------------ Current Application State ------------------------------------------') | ||
console.log(this.state.applicationDataContext); | ||
console.log(this.state.domainDataContext); | ||
/*console.log(Object.isFrozen(this.state.applicationDataContext.previousState || {})); | ||
console.log(Object.isFrozen(this.state.applicationDataContext)); | ||
console.log(Object.isFrozen(this.state.applicationDataContext.state)); | ||
console.log(Object.isFrozen(this.state.applicationDataContext.persons)); | ||
console.log(Object.isFrozen(this.state.applicationDataContext.persons.collection)); | ||
console.log(Object.isFrozen(this.state.applicationDataContext.persons.selected)); | ||
console.log(Object.isFrozen(this.state.applicationDataContext.persons.selected.hobbies)); | ||
console.log(Object.isFrozen(this.state.applicationDataContext.persons.state)); | ||
console.log(Object.isFrozen(this.state.applicationDataContext.persons.state.collection)); | ||
console.log(Object.isFrozen(this.state.applicationDataContext.persons.state.collection[1])); | ||
console.log(Object.isFrozen(this.state.applicationDataContext.persons.state.collection[1].hobbies)); | ||
console.log(Object.isFrozen(this.state.applicationDataContext.hobbies.state._selectedPerson));*/ | ||
/*console.log(Object.isFrozen(this.state.domainDataContext.previousState || {})); | ||
console.log(Object.isFrozen(this.state.domainDataContext)); | ||
console.log(Object.isFrozen(this.state.domainDataContext.state)); | ||
console.log(Object.isFrozen(this.state.domainDataContext.persons)); | ||
console.log(Object.isFrozen(this.state.domainDataContext.persons.collection)); | ||
console.log(Object.isFrozen(this.state.domainDataContext.persons.selected)); | ||
console.log(Object.isFrozen(this.state.domainDataContext.persons.selected.hobbies)); | ||
console.log(Object.isFrozen(this.state.domainDataContext.persons.state)); | ||
console.log(Object.isFrozen(this.state.domainDataContext.persons.state.collection)); | ||
console.log(Object.isFrozen(this.state.domainDataContext.persons.state.collection[1])); | ||
console.log(Object.isFrozen(this.state.domainDataContext.persons.state.collection[1].hobbies)); | ||
console.log(Object.isFrozen(this.state.domainDataContext.hobbies.state._selectedPerson)); | ||
console.log(Object.isFrozen(this.state.domainDataContext.hobbies.state._selectedPerson.hobbies));*/ | ||
return ( | ||
<div> | ||
<NavBarView appContext={this.state.applicationDataContext} /> | ||
<NavBarView appContext={this.state.domainDataContext} /> | ||
<div className="container"> | ||
<div className="row"> | ||
<div className="col-md-4"> | ||
<SideBarView appContext={this.state.applicationDataContext} /> | ||
<SideBarView appContext={this.state.domainDataContext} /> | ||
</div> | ||
<div className="col-md-8"> | ||
<DetailsView appContext={this.state.applicationDataContext} /> | ||
<DetailsView appContext={this.state.domainDataContext} /> | ||
</div> | ||
@@ -43,0 +44,0 @@ </div> |
@@ -22,3 +22,3 @@ /** | ||
var current = this.props.appContext.persons.selected; | ||
var selectedHobby = !!this.props.appContext.persons.hobbies$selected ? " is " + this.props.appContext.persons.hobbies$selected : ""; | ||
var selectedHobby = !!this.props.appContext.persons.selectedHobby ? " is " + this.props.appContext.persons.selectedHobby : ""; | ||
@@ -25,0 +25,0 @@ var list = collection.map(function(person){ |
{ | ||
"name": "imvvm", | ||
"description": "Immutable MVVM for React", | ||
"version": "0.4.6", | ||
"version": "0.5.1", | ||
"keywords": [ | ||
@@ -6,0 +6,0 @@ "imvvm", |
@@ -405,3 +405,3 @@ IMVVM | ||
_Available in:_ DomainModel, ViewModel, Model | ||
_Available in:_ ViewModel | ||
@@ -408,0 +408,0 @@ _Optional:_ true |
312
src/core.js
@@ -5,3 +5,3 @@ | ||
exports.getInitialState = function(appNamespace, domainModel, stateChangedHandler, disableUndo) { | ||
exports.getInitialState = function(appNamespace, domainModel, stateChangedHandler, enableUndo) { | ||
@@ -11,96 +11,123 @@ if(typeof stateChangedHandler !== 'function'){ | ||
} | ||
enableUndo === void(0) ? true : enableUndo; | ||
var ApplicationDataContext, | ||
thisAppState = {}, | ||
dependsOn, | ||
appState = {}, | ||
dataContexts = {}, | ||
watchedProps, | ||
watchedPropsLen, | ||
watchList = {}, | ||
dependents = [], | ||
domain, | ||
reprocessing = false; | ||
watchListPropA, | ||
watchListPropB; | ||
disableUndo === void(0) ? false : disableUndo; | ||
var transitionState = function(nextState, prevState, watchedDataContext){ | ||
var processed = false, | ||
dependencies, | ||
initialize; | ||
var depProp; | ||
prevState = prevState || {}; | ||
var configure = function(obj, propName){ | ||
var newObj = {}; | ||
for(var k in obj){ | ||
if(obj.hasOwnProperty(k)){ | ||
if(Object.prototype.toString.call(obj[k]) === '[object Object]'){ | ||
newObj[k] = obj[k]; | ||
} else { | ||
newObj[k] = {}; | ||
newObj[k][propName] = obj[k]; | ||
} | ||
} | ||
} | ||
return newObj; | ||
}; | ||
if(nextState === void(0)){ | ||
initialize = true; | ||
nextState = {}; | ||
var getDeps = function(nextState, dataContext){ | ||
var dependencies = {}, | ||
props, | ||
watchedValue; | ||
if(!dataContext){ | ||
return {}; | ||
} | ||
var getDependencies = function(dataContext){ | ||
var deps = {}, | ||
props, | ||
watchedValue; | ||
dataContext = ('dependsOn' in dataContext) ? dataContext : {dependsOn: dataContext}; | ||
if('dependsOn' in dataContext){ | ||
dataContext.dependsOn.forEach(function(dependency){ | ||
watchedValue = {}; | ||
props = dependency.property.split('.'); | ||
props.forEach(function(prop, idx){ | ||
if(idx === 0){ | ||
watchedValue = nextState[prop]; | ||
} else { | ||
watchedValue = watchedValue ? watchedValue[prop] : void(0); | ||
} | ||
}); | ||
if('alias' in dependency){ | ||
deps[dependency.alias] = watchedValue; | ||
for(var dependency in dataContext.dependsOn){ | ||
if(dataContext.dependsOn.hasOwnProperty(dependency)){ | ||
watchedValue = {}; | ||
props = dataContext.dependsOn[dependency].property.split('.'); | ||
props.forEach(function(prop, idx){ | ||
if(idx === 0){ | ||
watchedValue = nextState[prop]; | ||
} else { | ||
deps[props.join('$')] = watchedValue; | ||
watchedValue = watchedValue ? watchedValue[prop] : void(0); | ||
} | ||
}); | ||
dependencies[dependency] = watchedValue; | ||
} | ||
return deps; | ||
}; | ||
return dependencies; | ||
}; | ||
for(var dataContext in domain){ | ||
if(domain.hasOwnProperty(dataContext)){ | ||
dependencies = getDependencies(domain[dataContext]); | ||
//Need to inject dependencies so that the state for this object can change | ||
//if required. Can't use appState as that is provided after the object is created | ||
if(initialize){ | ||
nextState[dataContext] = new dataContexts[dataContext](nextState[dataContext], dependencies, | ||
prevState[dataContext]).getInitialState(); | ||
} else { | ||
nextState[dataContext] = new dataContexts[dataContext](nextState[dataContext], dependencies, | ||
prevState[dataContext]); | ||
var transitionState = function(caller, nextState, prevState, subscribers){ | ||
nextState = nextState || {}; | ||
prevState = prevState || {}; | ||
var processed = false, | ||
tempDeps, | ||
nextVal; | ||
if(caller !== appNamespace){ | ||
nextState[caller] = new dataContexts[caller](nextState[caller], | ||
getDeps(nextState, domain[caller]), prevState[caller]); | ||
} | ||
if(subscribers){ | ||
if(!!dependsOn){ | ||
tempDeps = getDeps(nextState, dependsOn); | ||
nextState = extend(nextState, tempDeps); | ||
for(var depKey in dependsOn){ | ||
if(dependsOn.hasOwnProperty(depKey) && ('onStateChange' in dependsOn[depKey])){ | ||
nextVal = {}; | ||
nextVal[depKey] = nextState[depKey]; | ||
nextState = extend(nextState, dependsOn[depKey].onStateChange(nextVal)); | ||
} | ||
} | ||
if(watchedDataContext){ | ||
if(processed && watchedDataContext.subscribers.indexOf(dataContext) !== -1){ | ||
dependencies = getDependencies(domain[watchedDataContext.name]); | ||
nextState[watchedDataContext.name] = new dataContexts[watchedDataContext.name](nextState[watchedDataContext.name], | ||
dependencies, prevState[watchedDataContext.name]); | ||
} | ||
processed = processed ? processed : dataContext === watchedDataContext.name; | ||
} | ||
} | ||
nextState = new ApplicationDataContext(extend(nextState, tempDeps), prevState, enableUndo); | ||
subscribers.forEach(function(subscriber){ | ||
if(subscriber !== appNamespace){ | ||
nextState[subscriber] = new dataContexts[subscriber](nextState[subscriber], | ||
getDeps(nextState, domain[subscriber]), prevState[subscriber]); | ||
} | ||
}); | ||
} | ||
return nextState; | ||
}; | ||
var appStateChangedHandler = function(caller, newState, callback, initialize) { | ||
var appStateChangedHandler = function(caller, newState, callback) { | ||
if((newState === void(0) || newState === null || Object.keys(newState).length === 0)){ | ||
return; | ||
} | ||
var nextState = {}, | ||
prevState = {}, | ||
watchedDataContext = void(0), | ||
subscribers = [], | ||
newStateKeys, | ||
newStateKeysLen, | ||
subscriberKeys, | ||
rollback = false; | ||
subscriberNames, | ||
idxKey, | ||
idxDepFld, | ||
tmpNextState = {}, | ||
changeState, | ||
dependsOnObj; | ||
initialize === void(0) ? false : initialize; | ||
if(!initialize && (newState === void(0) || newState === null || Object.keys(newState).length === 0)){ | ||
return; | ||
} | ||
var DomainModel = !!newState ? Object.getPrototypeOf(newState).constructor.classType === "DomainModel" : false; | ||
//Check to see if appState is a ready made state object. If so | ||
//pass it straight to the stateChangedHandler. If a callback was passed in | ||
//it would be assigned to newState | ||
if(DomainModel) { | ||
if(Object.getPrototypeOf(newState).constructor.classType === "DomainModel") { | ||
//This means previous state has been requested | ||
@@ -111,78 +138,120 @@ //so set nextState to the previous state | ||
prevState = newState.previousState; | ||
rollback = true; | ||
} else { | ||
if(caller in watchList){ | ||
newStateKeys = Object.keys(newState); | ||
newStateKeysLen = newStateKeys.length; | ||
subscriberKeys = {}; | ||
subscriberNames = {}; | ||
for(var i= 0; i < newStateKeysLen; i++){ | ||
for (var i = newStateKeysLen - 1; i >= 0; i--){ | ||
if(watchList[caller][newStateKeys[i]]){ | ||
subscriberKeys[watchList[caller][newStateKeys[i]]] = true; | ||
for(var j = watchList[caller][newStateKeys[i]].length - 1; j >= 0; j--){ | ||
subscriberNames[watchList[caller][newStateKeys[i]][j].dataContext] = true; | ||
} | ||
} | ||
} | ||
watchedDataContext = {}; | ||
watchedDataContext.name = caller; | ||
watchedDataContext.subscribers = Object.keys(subscriberKeys); | ||
//If there are no subscriber reset watchedDataContext | ||
watchedDataContext = !!watchedDataContext.subscribers.length ? watchedDataContext : void(0); | ||
subscribers = Object.keys(subscriberNames); | ||
hasSubscribers = !!subscribers.length; | ||
} | ||
if(caller !== appNamespace){ | ||
nextState[caller] = newState; | ||
nextState = extend(thisAppState.state, nextState); | ||
nextState = extend(appState.state, nextState); | ||
if(hasSubscribers){ | ||
subscribers.forEach(function(sub){ | ||
for(idxKey=newStateKeysLen-1; idxKey >= 0; idxKey--){ | ||
if(watchList[caller][newStateKeys[idxKey]]){ | ||
var depFldArr = watchList[caller][newStateKeys[idxKey]]; | ||
for(idxDepFld = depFldArr.length - 1; idxDepFld >= 0; idxDepFld--){ | ||
dependsOnObj = depFldArr[idxDepFld].dataContext === appNamespace ? dependsOn : | ||
domain[depFldArr[idxDepFld].dataContext].dependsOn; | ||
if(dependsOnObj[depFldArr[idxDepFld].alias].onStateChange){ | ||
tmpNextState[depFldArr[idxDepFld].alias] = nextState[caller][newStateKeys[idxKey]]; | ||
changeState = dependsOnObj[depFldArr[idxDepFld].alias]. | ||
onStateChange.call(appState.state[depFldArr[idxDepFld].dataContext], tmpNextState); | ||
if(Object.prototype.toString.call(changeState) === '[object Object]'){ | ||
if(depFldArr[idxDepFld].dataContext === appNamespace){ | ||
nextState = extend(nextState, changeState); | ||
} else { | ||
nextState[depFldArr[idxDepFld].dataContext] = | ||
extend(nextState[depFldArr[idxDepFld].dataContext], changeState); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
}); | ||
} | ||
} else { | ||
//appDataContext is calling function | ||
if(initialize) { | ||
nextState = extend(transitionState(), newState); | ||
} else { | ||
nextState = extend(thisAppState.state, newState); | ||
} | ||
nextState = extend(appState.state, newState); | ||
} | ||
prevState = reprocessing ? thisAppState.previousState : thisAppState; | ||
nextState = transitionState(nextState, thisAppState.state, watchedDataContext); | ||
prevState = appState; | ||
nextState = transitionState(caller, nextState, appState.state, hasSubscribers ? subscribers : false); | ||
} | ||
prevState = prevState || {}; | ||
Object.freeze(prevState); | ||
if(!!prevState){ | ||
Object.freeze(prevState); | ||
} | ||
//Create a new App state context. | ||
thisAppState = new ApplicationDataContext(nextState, prevState, disableUndo); | ||
if(!!thisAppState.getValidState && !rollback && !reprocessing) { | ||
var validationObj = thisAppState.getValidState(thisAppState.state, thisAppState.previousState); | ||
var validationKeys = Object.keys(validationObj); | ||
for (var keyIdx = validationKeys.length - 1; keyIdx >= 0; keyIdx--) { | ||
if(Object.prototype.toString.call(validationObj[validationKeys[keyIdx]]) !== '[object Object]' && | ||
Object.prototype.toString.call(validationObj[validationKeys[keyIdx]]) !== '[object Array]' && | ||
validationObj[validationKeys[keyIdx]] !== thisAppState.state[validationKeys[keyIdx]]){ | ||
reprocessing = true; | ||
thisAppState.setState(extend(thisAppState.state, validationObj)); | ||
reprocessing = false; | ||
break; | ||
} | ||
}; | ||
} | ||
appState = new ApplicationDataContext(nextState, prevState, enableUndo); | ||
//All the work is done! -> Notify the View | ||
//Provided for the main app to return from init() to the View | ||
if(!reprocessing){ | ||
Object.freeze(thisAppState); | ||
Object.freeze(thisAppState.state); | ||
stateChangedHandler(thisAppState, caller, callback); | ||
return thisAppState; | ||
} | ||
Object.freeze(appState); | ||
Object.freeze(appState.state); | ||
stateChangedHandler(appState, caller, callback); | ||
return appState; | ||
}; | ||
//Initialize Application Data Context | ||
ApplicationDataContext = domainModel.call(this, appStateChangedHandler.bind(this, appNamespace)); | ||
var applicationDataContext = new ApplicationDataContext({}, {}, disableUndo); | ||
domain = applicationDataContext.getDomainDataContext(); | ||
appState = new ApplicationDataContext({}, void(0), enableUndo, true); | ||
dependsOn = appState.getDependencies ? configure(appState.getDependencies(), 'property') : void(0); | ||
if(dependsOn){ | ||
dependents.push(appNamespace); | ||
for(depProp in dependsOn){ | ||
if(dependsOn.hasOwnProperty(depProp)){ | ||
watchedProps = dependsOn[depProp].property.split('.'); | ||
watchedPropsLen = watchedProps.length; | ||
watchListPropA = watchedPropsLen > 1 ? watchedProps[0] : appNamespace; | ||
watchListPropB = watchedPropsLen > 1 ? watchedProps[1] : watchedProps[0]; | ||
watchList[watchListPropA] = watchList[watchListPropA] || {}; | ||
watchList[watchListPropA][watchListPropB] = watchList[watchListPropA][watchListPropB] || []; | ||
if(watchList[watchListPropA][watchListPropB].indexOf(appNamespace) === -1){ | ||
watchList[watchListPropA][watchListPropB].push({dataContext:appNamespace, alias: depProp}); | ||
} | ||
} | ||
} | ||
} | ||
domain = configure(appState.getDomainDataContext(), 'viewModel'); | ||
for(var dataContext in domain){ | ||
if(domain.hasOwnProperty(dataContext)){ | ||
dataContexts[dataContext] = domain[dataContext].viewModel.call(this, appStateChangedHandler.bind(this, dataContext)); | ||
if('dependsOn' in domain[dataContext]){ | ||
for(var i = 0, len = domain[dataContext].dependsOn.length; i < len; i++){ | ||
watchedProps = domain[dataContext].dependsOn[i].property.split('.'); | ||
if(watchedProps.length > 1){ | ||
watchList[watchedProps[0]] = watchList[watchedProps[0]] || {}; | ||
watchList[watchedProps[0]][watchedProps[1]] = watchList[watchedProps[0]][watchedProps[1]] || []; | ||
if(watchList[watchedProps[0]][watchedProps[1]].indexOf(dataContext) === -1){ | ||
watchList[watchedProps[0]][watchedProps[1]].push(dataContext); | ||
appState[dataContext] = new dataContexts[dataContext]({}, {}, {}, true); | ||
if(appState[dataContext].getDependencies){ | ||
dependents.push(dataContext); | ||
domain[dataContext].dependsOn = configure(appState[dataContext].getDependencies(), 'property'); | ||
for(depProp in domain[dataContext].dependsOn){ | ||
if(domain[dataContext].dependsOn.hasOwnProperty(depProp)){ | ||
watchedProps = domain[dataContext].dependsOn[depProp].property.split('.'); | ||
watchedPropsLen = watchedProps.length; | ||
watchListPropA = watchedPropsLen > 1 ? watchedProps[0] : appNamespace; | ||
watchListPropB = watchedPropsLen > 1 ? watchedProps[1] : watchedProps[0]; | ||
watchList[watchListPropA] = watchList[watchListPropA] || {}; | ||
watchList[watchListPropA][watchListPropB] = watchList[watchListPropA][watchListPropB] || []; | ||
if(watchList[watchListPropA][watchListPropB].indexOf(dataContext) === -1){ | ||
watchList[watchListPropA][watchListPropB].push({dataContext:dataContext, alias: depProp}); | ||
} | ||
@@ -194,3 +263,12 @@ } | ||
} | ||
return applicationDataContext.getInitialState(); | ||
dependents.forEach(function(dependent){ | ||
if(dependent !== appNamespace){ | ||
appState[dependent] = new dataContexts[dependent](appState[dependent], | ||
getDeps(appState, domain[dependent]), {}); | ||
} | ||
}); | ||
appState = new ApplicationDataContext(extend(appState, getDeps(appState, dependsOn)), void(0), enableUndo); | ||
Object.freeze(appState.state); | ||
return Object.freeze(appState); | ||
}; |
@@ -28,3 +28,3 @@ | ||
var ConvenienceConstructor = function(raiseStateChangeHandler) { | ||
var ConvenienceConstructor = function(stateChangedHandler) { | ||
var descriptor = new DescriptorConstructor(); | ||
@@ -31,0 +31,0 @@ return descriptor.construct.apply(ConvenienceConstructor, arguments); |
@@ -8,30 +8,14 @@ | ||
Mixin: { | ||
construct: function(raiseStateChangeHandler){ | ||
construct: function(stateChangedHandler){ | ||
var desc = getDescriptor.call(this); | ||
desc.proto.setState = raiseStateChangeHandler; | ||
desc.proto.setState = stateChangedHandler; | ||
var initialize = function(initState, callback){ | ||
return desc.proto.setState(initState, callback, true); | ||
} | ||
var dataContext = function(nextState, prevState, disableUndo) { | ||
var initFunc; | ||
var calcFld; | ||
nextState = nextState || {}; | ||
prevState = prevState || {}; | ||
if(!('getInitialState' in desc.proto)){ | ||
desc.proto.getInitialState = function(){ | ||
return initialize(); | ||
} | ||
} else { | ||
initFunc = desc.proto.getInitialState; | ||
desc.proto.getInitialState = function(){ | ||
return initialize(initFunc.call(this)); | ||
} | ||
} | ||
var model = Object.create(desc.proto, desc.descriptor); | ||
var dataContext = function(nextState, prevState, enableUndo, initialize) { | ||
Object.defineProperty(model, 'state', { | ||
var freezeFields = desc.freezeFields; | ||
var domainModel = Object.create(desc.proto, desc.descriptor); | ||
//Need to have 'state' prop in domainModel before can extend domainModel to get correct state | ||
Object.defineProperty(domainModel, 'state', { | ||
configurable: true, | ||
@@ -42,22 +26,5 @@ enumerable: false, | ||
}); | ||
//Need to have state prop in model before can extend model to get correct state | ||
nextState = extend(nextState, model); | ||
//runs everytime to initialize calculated state but will not run the calc func | ||
//if the prop has already been initialized | ||
if(!!desc.originalSpec.getInitialCalculatedState){ | ||
for (var i = desc.calculatedFields.length - 1; i >= 0; i--) { | ||
if(!(desc.calculatedFields[i] in nextState) || nextState[desc.calculatedFields[i]] === void(0)){ | ||
calcFld = {} | ||
calcFld[desc.calculatedFields[i]] = desc.originalSpec.getInitialCalculatedState. | ||
call(model, nextState, prevState)[desc.calculatedFields[i]]; | ||
if(calcFld[desc.calculatedFields[i]] !== void(0)){ | ||
nextState = extend(nextState,calcFld); | ||
} | ||
} | ||
}; | ||
} | ||
if(!disableUndo && !!Object.keys(prevState).length){ | ||
Object.defineProperty(nextState, 'previousState', { | ||
if(!!enableUndo && !!prevState){ | ||
Object.defineProperty(domainModel, 'previousState', { | ||
configurable: false, | ||
@@ -70,18 +37,32 @@ enumerable: false, | ||
Object.defineProperty(nextState, 'state', { | ||
prevState = prevState || {}; | ||
if(initialize && ('getInitialState' in domainModel)){ | ||
//Add state prop so that it can be referenced from within getInitialState | ||
nextState = extend(nextState, domainModel.getInitialState.call(domainModel)); | ||
} | ||
//attach the nextState props to domainModel if they don't exist | ||
var keys = Object.keys(nextState); | ||
for (var i = keys.length - 1; i >= 0; i--) { | ||
if(!(keys[i] in domainModel)){ | ||
domainModel[keys[i]] = nextState[keys[i]]; | ||
} | ||
}; | ||
Object.defineProperty(domainModel, 'state', { | ||
configurable: false, | ||
enumerable: false, | ||
writable: false, | ||
value: extend(nextState) | ||
value: nextState | ||
}); | ||
for(var k in desc.descriptor){ | ||
if(desc.descriptor.hasOwnProperty(k)){ | ||
Object.defineProperty(nextState, k, desc.descriptor[k]); | ||
} | ||
} | ||
//freeze arrays and domainModel instances | ||
for (var fld = freezeFields.length - 1; fld >= 0; fld--) { | ||
Object.freeze(domainModel[freezeFields[fld].fieldName]); | ||
}; | ||
nextState.__proto__ = model.__proto__; | ||
return nextState; | ||
return domainModel; | ||
}; | ||
return dataContext; | ||
@@ -88,0 +69,0 @@ } |
@@ -8,9 +8,13 @@ | ||
Mixin: { | ||
construct: function(raiseStateChangeHandler){ | ||
construct: function(stateChangedHandler){ | ||
var desc = getDescriptor.call(this); | ||
var dataContext = function(nextState, prevState, withContext) { | ||
var freezeFields = desc.freezeFields; | ||
var model = Object.create(desc.proto, desc.descriptor); | ||
var argCount = arguments.length; | ||
var lastArgIsBool = typeof Array.prototype.slice.call(arguments, -1)[0] === 'boolean'; | ||
var calcFld; | ||
var initialize = false; | ||
@@ -21,3 +25,3 @@ if(argCount === 0){ | ||
prevState = {}; | ||
withContext = true; | ||
withContext = false; | ||
} else if(argCount === 1){ | ||
@@ -29,14 +33,18 @@ if(lastArgIsBool){ | ||
} else { | ||
//assume prevState is same as nextState | ||
prevState = nextState; | ||
withContext = true; | ||
//assume this is a new Object and there is no prevState | ||
prevState = {}; | ||
withContext = false; | ||
initialize = true; | ||
} | ||
} else if(argCount === 2){ | ||
if(lastArgIsBool){ | ||
//assume this is a new Object and there is no prevState | ||
withContext = prevState; | ||
prevState = nextState; | ||
prevState = {}; | ||
initialize = true; | ||
} else { | ||
withContext = true; | ||
withContext = false; | ||
} | ||
} | ||
nextState = ('state' in nextState) ? nextState.state : nextState; | ||
@@ -51,24 +59,5 @@ prevState = ('state' in prevState) ? prevState.state : prevState; | ||
}); | ||
//Need to have state prop in model before can extend model to get correct state | ||
nextState = extend(nextState, model); | ||
//runs everytime to initialize calculated state but will not run the calc func | ||
//if the prop has already been initialized | ||
if(!!desc.originalSpec.getInitialCalculatedState){ | ||
for (var i = desc.calculatedFields.length - 1; i >= 0; i--) { | ||
if(!(desc.calculatedFields[i] in nextState) || nextState[desc.calculatedFields[i]] === void(0)){ | ||
calcFld = {} | ||
calcFld[desc.calculatedFields[i]] = desc.originalSpec.getInitialCalculatedState. | ||
call(model, nextState, prevState)[desc.calculatedFields[i]]; | ||
if(calcFld[desc.calculatedFields[i]] !== void(0)){ | ||
nextState = extend(nextState,calcFld); | ||
} | ||
} | ||
}; | ||
} | ||
//runs everytime | ||
if(desc.originalSpec.getValidState){ | ||
nextState = extend(nextState, | ||
desc.originalSpec.getValidState.call(model, nextState, prevState)); | ||
if(initialize && ('getInitialState' in model)){ | ||
nextState = extend(nextState, model.getInitialState.call(model)); | ||
} | ||
@@ -80,6 +69,6 @@ | ||
configurable: true, | ||
enumerable: true, | ||
enumerable: false, | ||
set: function(context){ | ||
this.setState = function(nextState, callback){ //callback may be useful for DB updates | ||
return raiseStateChangeHandler.bind(context) | ||
return stateChangedHandler.bind(context) | ||
.call(context, extend(this.state, nextState), this.state, callback); | ||
@@ -99,8 +88,6 @@ }.bind(this); | ||
Object.keys(model).forEach(function(key){ | ||
if(Object.prototype.toString.call(this[key]) === '[object Object]' || | ||
Object.prototype.toString.call(this[key]) === '[object Array]'){ | ||
Object.freeze(this[key]); | ||
} | ||
}.bind(model)); | ||
//freeze arrays and model instances | ||
for (var i = freezeFields.length - 1; i >= 0; i--) { | ||
Object.freeze(model[freezeFields[i].fieldName]); | ||
}; | ||
@@ -110,2 +97,3 @@ if(!withContext){ | ||
} | ||
return model; | ||
@@ -112,0 +100,0 @@ }; |
@@ -8,9 +8,9 @@ | ||
Mixin: { | ||
construct: function(raiseStateChangeHandler){ | ||
construct: function(stateChangedHandler){ | ||
var desc = getDescriptor.call(this); | ||
desc.proto.setState = raiseStateChangeHandler; | ||
var dataContext = function(nextState, dependencies, prevState) { | ||
var initFunc; | ||
var calcFld; | ||
desc.proto.setState = stateChangedHandler; | ||
var dataContext = function(nextState, dependencies, prevState, initialize) { | ||
//nextState has already been extended with prevState in core | ||
@@ -20,17 +20,7 @@ nextState = extend(nextState, dependencies); | ||
prevState = ('state' in prevState) ? prevState.state : prevState; | ||
if(!('getInitialState' in desc.proto)){ | ||
desc.proto.getInitialState = function(){ | ||
return dataContext(); | ||
} | ||
} else { | ||
initFunc = desc.proto.getInitialState; | ||
desc.proto.getInitialState = function(){ | ||
return dataContext(initFunc.call(this)); | ||
} | ||
} | ||
var model = Object.create(desc.proto, desc.descriptor); | ||
var freezeFields = desc.freezeFields; | ||
var viewModel = Object.create(desc.proto, desc.descriptor); | ||
Object.defineProperty(model, 'state', { | ||
Object.defineProperty(viewModel, 'state', { | ||
configurable: true, | ||
@@ -41,27 +31,8 @@ enumerable: false, | ||
}); | ||
//Need to have state prop in model before can extend model to get correct state | ||
nextState = extend(nextState, model); | ||
//runs everytime to initialize calculated state but will not run the calc func | ||
//if the prop has already been initialized | ||
if(!!desc.originalSpec.getInitialCalculatedState){ | ||
for (var i = desc.calculatedFields.length - 1; i >= 0; i--) { | ||
if(!(desc.calculatedFields[i] in nextState) || nextState[desc.calculatedFields[i]] === void(0)){ | ||
calcFld = {} | ||
calcFld[desc.calculatedFields[i]] = desc.originalSpec.getInitialCalculatedState. | ||
call(model, nextState, prevState)[desc.calculatedFields[i]]; | ||
if(calcFld[desc.calculatedFields[i]] !== void(0)){ | ||
nextState = extend(nextState,calcFld); | ||
} | ||
} | ||
}; | ||
if(initialize && ('getInitialState' in viewModel)){ | ||
nextState = extend(nextState, viewModel.getInitialState.call(viewModel)); | ||
} | ||
//runs everytime | ||
if(desc.originalSpec.getValidState){ | ||
nextState = extend(nextState, | ||
desc.originalSpec.getValidState.call(model, nextState, prevState)); | ||
} | ||
Object.defineProperty(model, 'state', { | ||
Object.defineProperty(viewModel, 'state', { | ||
configurable: false, | ||
@@ -73,16 +44,15 @@ enumerable: false, | ||
Object.keys(model).forEach(function(key){ | ||
if(Object.prototype.toString.call(this[key]) === '[object Object]' && | ||
('context' in this[key])){ | ||
this[key].context = this; | ||
Object.freeze(this[key]); | ||
} else if(Object.prototype.toString.call(this[key]) === '[object Array]'){ | ||
Object.freeze(this[key]); | ||
//freeze arrays and viewModel instances | ||
for (var i = freezeFields.length - 1; i >= 0; i--) { | ||
if(freezeFields[i].kind === 'instance' && | ||
('context' in viewModel[freezeFields[i].fieldName])){ | ||
viewModel[freezeFields[i].fieldName].context = viewModel; | ||
} | ||
}.bind(model)); | ||
Object.freeze(viewModel[freezeFields[i].fieldName]); | ||
}; | ||
//Add dependencies to model | ||
//Add dependencies to viewModel | ||
for(var dep in dependencies){ | ||
if(dependencies.hasOwnProperty(dep) && dep[0] !== '_'){ | ||
Object.defineProperty(model, dep, { | ||
Object.defineProperty(viewModel, dep, { | ||
configurable: false, | ||
@@ -97,3 +67,3 @@ enumerable: false, | ||
Object.freeze(nextState); | ||
return Object.freeze(model); | ||
return Object.freeze(viewModel); | ||
@@ -100,0 +70,0 @@ }; |
@@ -7,3 +7,3 @@ | ||
stateChangedHandler: function(dataContext, caller, callback){ | ||
this.setState({applicationDataContext: dataContext}, function(){ | ||
this.setState({domainDataContext: dataContext}, function(){ | ||
//send all state back to caller | ||
@@ -16,9 +16,9 @@ //useful if you need to know what other parts of the app | ||
if(typeof callback === 'function'){ | ||
if(this.state === null || !('applicationDataContext' in this.state)){ | ||
if(this.state === null || !('domainDataContext' in this.state)){ | ||
callback(void(0)); | ||
} else { | ||
if(caller in this.state.applicationDataContext){ | ||
callback(this.state.applicationDataContext[caller]); | ||
if(caller in this.state.domainDataContext){ | ||
callback(this.state.domainDataContext[caller]); | ||
} else if(caller === NAMESPACE) { | ||
callback(this.state.applicationDataContext); | ||
callback(this.state.domainDataContext); | ||
} else { | ||
@@ -33,5 +33,5 @@ callback(void(0)); | ||
getInitialState: function(){ | ||
var appDataContext = core.getInitialState(NAMESPACE, this.props.domainModel, | ||
this.stateChangedHandler, this.props.disableUndo); | ||
return {applicationDataContext: appDataContext}; | ||
var dataContext = core.getInitialState(NAMESPACE, this.props.domainModel, | ||
this.stateChangedHandler, this.props.enableUndo); | ||
return {domainDataContext: dataContext}; | ||
} | ||
@@ -38,0 +38,0 @@ |
var utils = { | ||
getDescriptor: function(){ | ||
var descriptor = {}; | ||
var proto = this.prototype; | ||
var calcFlds = []; | ||
var autoFreeze = []; | ||
//var originalSpec = this.originalSpec || {}; | ||
if('__processedObject__' in this.originalSpec){ | ||
return this.originalSpec.__processedObject__; | ||
} | ||
for(var key in this.originalSpec){ | ||
@@ -13,21 +17,12 @@ if(this.originalSpec.hasOwnProperty(key)){ | ||
//assume it is a descriptor | ||
if('calculated' in this.originalSpec[key]){ | ||
//We want to preserve the calculated flag on originalSpec | ||
descriptor[key] = utils.extend(this.originalSpec[key]); | ||
descriptor[key].enumerable = !this.originalSpec[key].calculated; | ||
delete descriptor[key].calculated; | ||
calcFlds.push(key); | ||
} else if(!('enumerable' in this.originalSpec[key])){ | ||
//No need to preserve the pseudo flag on originalSpec | ||
if('pseudo' in this.originalSpec[key]){ | ||
this.originalSpec[key].enumerable = !this.originalSpec[key].pseudo; | ||
delete this.originalSpec[key].pseudo; | ||
} else { | ||
//default enumerable to true | ||
this.originalSpec[key].enumerable = true; | ||
this.originalSpec[key].enumerable = true; | ||
if('kind' in this.originalSpec[key]){ | ||
if(this.originalSpec[key].kind === 'pseudo'){ | ||
this.originalSpec[key].enumerable = false; | ||
} else { //'instance' || 'array' | ||
autoFreeze.push({fieldName: key, kind: this.originalSpec[key].kind}); | ||
} | ||
descriptor[key] = this.originalSpec[key]; | ||
} else { | ||
descriptor[key] = this.originalSpec[key]; | ||
delete this.originalSpec[key].kind; | ||
} | ||
descriptor[key] = this.originalSpec[key]; | ||
} else { | ||
@@ -41,9 +36,13 @@ proto[key] = this.originalSpec[key]; | ||
} | ||
return { | ||
this.originalSpec.__processedObject__ = { | ||
descriptor: descriptor, | ||
proto: proto, | ||
originalSpec: this.originalSpec || {}, | ||
calculatedFields: calcFlds | ||
} | ||
freezeFields: autoFreeze | ||
}; | ||
return this.originalSpec.__processedObject__; | ||
}, | ||
extend: function () { | ||
@@ -61,2 +60,3 @@ var newObj = {}; | ||
}, | ||
mixInto: function(constructor, methodBag) { | ||
@@ -71,4 +71,5 @@ var methodName; | ||
} | ||
}; | ||
module.exports = utils; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
144990
2330