diff --git a/dist/hyperdis.js b/dist/hyperdis.js index 7936b12..f54c12e 100644 --- a/dist/hyperdis.js +++ b/dist/hyperdis.js @@ -1,2 +1,2 @@ -!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define("hyperdis",[],t):"object"==typeof exports?exports.hyperdis=t():e.hyperdis=t()}(window,function(){return function(e){var t={};function r(n){if(t[n])return t[n].exports;var i=t[n]={i:n,l:!1,exports:{}};return e[n].call(i.exports,i,i.exports,r),i.l=!0,i.exports}return r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var i in e)r.d(n,i,function(t){return e[t]}.bind(null,i));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=0)}([function(e,t,r){"use strict";r.r(t);var n=function(){function e(e,t){for(var r=0;r2?(i=arguments.length<=0?void 0:arguments[0],n=arguments.length<=1?void 0:arguments[1],o=arguments.length<=2?void 0:arguments[2]):(i=null,n=arguments.length<=0?void 0:arguments[0],o=arguments.length<=1?void 0:arguments[1]),t=o(y),(e=r=new _(t.fn)).addDependencies.apply(e,P(t.dependencies)),this._addPropInModel(i,function(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}({},n,r)),this}},{key:"_addPropInModel",value:function(e,t){return this._graph.createNodesFrom(t,e),this}},{key:"on",value:function(e,t,r){var n,i=e instanceof Array?e:[e],o=this._graph.createElectricNodeOf(i,{type:"CurrFrame",fn:t});r&&(n=this._graph.stopPropagation().setPropagationOverride("nextFrame")).resetNodeValue.apply(n,P(e));return o}},{key:"next",value:function(e,t,r){var n,i=e instanceof Array?e:[e],o=this._graph.createElectricNodeOf(i,{type:"NextFrame",fn:t});r&&(n=this._graph.stopPropagation().setPropagationOverride("currentFrame")).resetNodeValue.apply(n,P(e));return o}},{key:"lock",value:function(){return this._lockFlag=!0,this._reqQ.length=0,this}},{key:"unlock",value:function(){return this._lockFlag=!1,this.setProp.apply(this,P(this._reqQ)),this._reqQ.length=0,this}},{key:"prop",value:function(){var e,t=void 0,r=void 0;switch(e=arguments.length){case 1:t=arguments.length<=0?void 0:arguments[0];break;case 2:t=arguments.length<=0?void 0:arguments[0],r=arguments.length<=1?void 0:arguments[1];break;default:return this}return 2===e?(this._lockFlag?this._reqQ.push([t,r]):this.setProp([t,r]),this):this._graph.getNodeValue(t)}},{key:"setProp",value:function(){for(var e,t=this,r=arguments.length,n=Array(r),i=0;i2?(i=arguments.length<=0?void 0:arguments[0],n=arguments.length<=1?void 0:arguments[1],o=arguments.length<=2?void 0:arguments[2]):(i=null,n=arguments.length<=0?void 0:arguments[0],o=arguments.length<=1?void 0:arguments[1]),t=o(y),(e=r=new _(t.fn)).addDependencies.apply(e,P(t.dependencies)),this._addPropInModel(i,function(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}({},n,r)),this}},{key:"_addPropInModel",value:function(e,t){return this._graph.createNodesFrom(t,e),this}},{key:"on",value:function(e,t,r){var n,i=e instanceof Array?e:[e],o=this._graph.createElectricNodeOf(i,{type:"CurrFrame",fn:t});r&&(n=this._graph.stopPropagation().setPropagationOverride("nextFrame")).resetNodeValue.apply(n,P(e));return o}},{key:"next",value:function(e,t,r){var n,i=e instanceof Array?e:[e],o=this._graph.createElectricNodeOf(i,{type:"NextFrame",fn:t});r&&(n=this._graph.stopPropagation().setPropagationOverride("currentFrame")).resetNodeValue.apply(n,P(e));return o}},{key:"lock",value:function(){return this._lockFlag=!0,this._reqQ.length=0,this}},{key:"unlock",value:function(){return this._lockFlag=!1,this.setProp.apply(this,P(this._reqQ)),this._reqQ.length=0,this}},{key:"prop",value:function(){var e,t=void 0,r=void 0;switch(e=arguments.length){case 1:t=arguments.length<=0?void 0:arguments[0];break;case 2:t=arguments.length<=0?void 0:arguments[0],r=arguments.length<=1?void 0:arguments[1];break;default:return this}return 2===e?(this._lockFlag?this._reqQ.push([t,r]):this.setProp([t,r]),this):this._graph.getNodeValue(t)}},{key:"setProp",value:function(){for(var e,t=this,r=arguments.length,n=Array(r),i=0;i {\n arr.forEach((elem) => {\n if (elem === undefined || elem === null) {\n return;\n }\n sCon[elem] = 1;\n });\n};\n\nexport default class ForeignSet {\n constructor (arr) {\n this._set = {};\n makeEntry(arr, this._set);\n }\n\n append (arr) {\n makeEntry(arr, this._set);\n return this;\n }\n\n toArray () {\n return Object.keys(this._set);\n }\n\n static difference (set1, set2) {\n let key;\n const filteredKeys = [],\n s1 = set1._set,\n s2 = set2._set;\n for (key in s1) {\n if (!({}).hasOwnProperty.call(s1, key)) {\n continue;\n }\n if (key in s2) {\n continue;\n }\n filteredKeys.push(key);\n }\n return new ForeignSet(filteredKeys);\n }\n}\n","/* global window */\n\nimport ForeignSet from './set';\n\nconst\n isSimpleObject = (obj) => {\n let token;\n if (typeof obj === 'object') {\n if (obj === null) { return false; }\n token = Object.prototype.toString.call(obj);\n if (token === '[object Object]') {\n return (obj.constructor.toString().match(/^function (.*)\\(\\)/m) || [])[1] === 'Object';\n }\n }\n return false;\n },\n minMsThreshold = 16,\n /* istanbul ignore next */win = typeof window === 'undefined' ? (this || {}) : window,\n /* istanbul ignore next */ reqAnimFrame = win.requestAnimationFrame || win.webkitRequestAnimationFrame ||\n win.mozRequestAnimationFrame || win.oRequestAnimationFrame ||\n win.msRequestAnimationFrame ||\n function (callback) {\n setTimeout(callback, minMsThreshold);\n },\n\n getTimeBasedId = () => {\n if (getTimeBasedId.__lastTime === new Date().getTime()) {\n return (getTimeBasedId.__lastTime).toString() + (getTimeBasedId.__id++).toString();\n }\n\n getTimeBasedId.__id = 0;\n getTimeBasedId.__lastTime = new Date().getTime();\n return (getTimeBasedId.__lastTime).toString() + (getTimeBasedId.__id++).toString();\n },\n pullableRecent = (nodes, fn) => {\n let nFn = () => {\n fn(...nodes.map((node) => {\n const\n hist = node.history,\n l = hist.length - 1;\n return [hist[l - 1 < 0 ? 0 : l - 1], hist[l]];\n }));\n };\n nFn.__id = getTimeBasedId();\n return nFn;\n },\n pullableEnd = (nodes, fn) => {\n let nFn = () => {\n fn(...nodes.map((node) => {\n const hist = node.history;\n return [hist[0], hist[hist.length - 1]];\n }));\n };\n nFn.__id = getTimeBasedId();\n return nFn;\n },\n unique = fns => fns\n .reduce((store, fn) => {\n // @warn function with side effect, it mutates the store passed during initialization\n if (fn.__id in store.map) {\n return store;\n }\n\n store.map[fn.__id] = 1;\n store.unique.push(fn);\n\n return store;\n }, { map: {}, unique: [] })\n .unique,\n compose = fns => () => {\n fns.forEach(fn => fn());\n },\n flat = (...params) => {\n const res = [];\n params.forEach(param => res.push(...param));\n return res;\n },\n identityMap = arrays => arrays,\n splitPathProp = (path) => {\n const pathArr = path.split('.'),\n len = pathArr.length;\n return [pathArr.slice(0, len - 1), pathArr[len - 1]];\n },\n scheduler = (onFinishCallback) => {\n let queue = [],\n animationFrame = null;\n\n onFinishCallback = onFinishCallback &&\n typeof onFinishCallback === 'function' && onFinishCallback || (() => { });\n\n return (listeners) => {\n [].push.apply(queue, listeners);\n if (animationFrame === null) {\n animationFrame = reqAnimFrame(() => {\n unique(queue).forEach(fn => fn());\n onFinishCallback();\n animationFrame = null;\n queue.length = 0;\n });\n }\n };\n },\n fetch = namedNode => (...params) => params.map((param) => {\n const node = namedNode[param];\n return {\n name: node.name,\n qualifiedName: node.qualifiedName,\n value: node.seed\n };\n }),\n fetchAggregator = (...params) => ({\n dependencies: params.slice(0, params.length - 1),\n fn: params[params.length - 1]\n }),\n getUpstreamNodes = (list) => {\n let res = [];\n const map = {};\n list.forEach(node => map[node.qualifiedName] = -1);\n function rec (arr) {\n arr.forEach((node) => {\n let qname,\n placedIndex,\n preArr,\n postArr;\n if (!((qname = node.qualifiedName) in map)) {\n map[qname] = res.push(node) - 1;\n } else {\n placedIndex = map[qname];\n if (placedIndex !== -1 && placedIndex !== res.length - 1) {\n // If not the last element, readjust the array so that the later dependency position is saved\n preArr = res.slice(0, placedIndex);\n postArr = res.slice(placedIndex + 1);\n res = preArr.concat(postArr);\n\n // reset the index in map\n postArr.forEach((elem, i) => map[elem.qualifiedName] = i + preArr.length);\n map[qname] = res.push(node) - 1;\n }\n }\n rec(node.outgoingEdges);\n });\n }\n rec(list);\n return res;\n },\n resolver = {\n accumulate: (...params) => {\n const resp = {};\n params.forEach((nodeDetails) => {\n Object.assign(resp, { [nodeDetails.name]: nodeDetails.value });\n });\n return resp;\n },\n identity: nodeDetails => nodeDetails.value\n };\n\nfunction resolveDependencyOrder (node, resolved, resolveMap) {\n let qname;\n node.edges.forEach((neighbour) => {\n resolveDependencyOrder(neighbour, resolved, resolveMap);\n });\n\n if (node.isRoot() || (qname = node.qualifiedName) in resolveMap) {\n return;\n }\n resolved.push(node);\n resolveMap[qname] = 1;\n}\n\n// function getUpstreamNodes (node, list) {\n// if (node.isRoot()) {\n// return;\n// }\n// node.outgoingEdges.forEach((_node) => {\n// list.push(_node);\n// getUpstreamNodes(_node, list);\n// });\n// }\n\nclass CustomResolver {\n constructor (resolver) {\n this.fn = resolver;\n this.dep = [];\n }\n\n addDependencies(...dep) {\n this.dep.push(...dep);\n return this;\n }\n\n getDependencies () {\n return this.dep;\n }\n\n get () {\n return this.fn;\n }\n}\n\nexport {\n isSimpleObject,\n scheduler,\n compose,\n flat,\n identityMap,\n pullableEnd,\n pullableRecent,\n unique,\n splitPathProp,\n resolver,\n ForeignSet,\n resolveDependencyOrder,\n fetch,\n // upstreamNodes,\n fetchAggregator,\n CustomResolver,\n getUpstreamNodes\n};\n","export default class GraphNode {\n constructor (name, qualifiedName, options) {\n /* istanbul ignore next */options = options || {};\n this.name = name;\n this.qualifiedName = qualifiedName;\n this.edges = [];\n this.outgoingEdges = [];\n this._seed = null;\n this.requireResolve = true;\n this.retriever = options.retriever;\n this.history = [];\n this.resolver = null;\n this.electricEdges = [];\n }\n\n addDependencies (...dep) {\n this.edges.push(...dep);\n dep.forEach(entry => entry.outgoingEdges.push(this));\n return this;\n }\n\n addElectricNode (node) {\n this.electricEdges.push(node);\n return this;\n }\n\n get seed () {\n return this._seed;\n }\n\n set seed (value) {\n this._seed = value;\n this.requireResolve = true;\n return this;\n }\n\n resolve () {\n this.seed = this.resolver(...this.retrieveDetails());\n this.history.push(this.seed);\n this.requireResolve = false;\n return this;\n }\n\n retrieveDetails () {\n if (this.edges.length === 0) {\n return [{\n name: this.name,\n qualifiedName: this.qualifiedName,\n value: this.seed\n }];\n }\n return this.retriever(...this.edges.map(edge => edge.qualifiedName));\n }\n\n repeatHead () {\n const\n history = this.history,\n head = history[history.length - 1];\n // @todo- Akash, write test case if history.length === 0 to pass code coverage\n if (history.length === 0) {\n return this;\n }\n history.push(head);\n return this;\n }\n\n flush () {\n const hist = this.history,\n head = hist[hist.length - 1];\n hist.length = 0;\n hist.push(head);\n return this;\n }\n\n isRoot () {\n return this.name === null;\n }\n}\n","import {\n pullableRecent,\n pullableEnd\n} from './utils';\n\nexport default class ElectricNode {\n constructor () {\n this.edges = [];\n this.listeners = {\n nextFrame: [],\n currentFrame: []\n };\n }\n\n addEdges (...nodes) {\n this.edges.push(...nodes);\n return this;\n }\n\n regListenerForCurrFrame (fn) {\n const lstnrs = this.listeners.currentFrame;\n let index = lstnrs.push(pullableRecent(this.edges, fn)) - 1;\n\n return () => {\n this.listeners.currentFrame = lstnrs.filter((fn, i) => i !== index);\n };\n }\n\n regListenerForNextFrame (fn) {\n const lstnrs = this.listeners.nextFrame;\n let index = lstnrs.push(pullableEnd(this.edges, fn)) - 1;\n\n return () => {\n this.listeners.nextFrame = lstnrs.filter((fn, i) => i !== index);\n };\n }\n\n hasNextFrameListener () {\n return !!this.listeners.nextFrame.length;\n }\n}\n","import {\n isSimpleObject,\n resolver,\n // upstreamNodes,\n flat,\n resolveDependencyOrder,\n getUpstreamNodes,\n ForeignSet,\n fetch,\n CustomResolver,\n scheduler\n} from './utils';\nimport Node from './graph-node';\nimport ElectricNode from './electric-node';\n\nexport default class Graph {\n constructor () {\n this.qualifiedNodeMap = {};\n this.retriever = fetch(this.qualifiedNodeMap);\n this.root = new Node(null, null, { retriever: this.retriever });\n this.root.resolver = resolver.accumulate;\n\n this._wholeSet = null;\n this._propagate = true;\n this.propagationOverride = {\n currentFrameListeners: false,\n nextFrameListeners: false\n };\n\n this._schedule = scheduler(() => {\n let qname;\n for (qname in this.qualifiedNodeMap) {\n if (!({}).hasOwnProperty.call(this.qualifiedNodeMap, qname)) {\n return;\n }\n this.qualifiedNodeMap[qname].flush();\n }\n });\n }\n\n createNodesFrom (obj, mount) {\n let val,\n resolveReqList;\n const qualifiedNodeMap = this.qualifiedNodeMap,\n root = this.root,\n retriever = this.retriever;\n\n (function rec (objn, qualifiedName, history) {\n let key,\n qname,\n perv,\n node;\n\n for (key in objn) {\n if (!({}).hasOwnProperty.call(objn, key)) {\n continue;\n }\n qname = `${qualifiedName}${key}`;\n node = new Node(key, qname, { retriever });\n qualifiedNodeMap[qname] = node;\n\n if ((perv = history.perv) !== undefined) {\n perv.addDependencies(node);\n } else {\n // top most level entries\n root.addDependencies(node);\n }\n\n if (isSimpleObject(val = objn[key])) {\n rec(val, `${qualifiedName}${key}.`, { perv: node });\n node.resolver = resolver.accumulate;\n } else if (val instanceof CustomResolver) {\n node.resolver = val.get();\n node.addDependencies(...val.getDependencies().map(qname => qualifiedNodeMap[qname]));\n } else {\n node.resolver = resolver.identity;\n node.seed = val;\n // node.resolve();\n }\n }\n }(obj, mount === null ? '' : `${mount}.`, {\n perv: qualifiedNodeMap[mount]\n }));\n\n this._wholeSet = new ForeignSet(Object.keys(this.qualifiedNodeMap));\n\n // Recalculate the model without firing the listeners\n // @todo selective branch resolve. Currently resolve gets called even for a branch which was updated\n resolveReqList = this.constructor.getResolvedList(root).concat(root).filter(node => node.requireResolve);\n resolveReqList.forEach(node => node.resolve());\n getUpstreamNodes(resolveReqList).forEach(node => node.resolve());\n return this;\n }\n\n getNodeValue (prop) {\n if (prop in this.qualifiedNodeMap) {\n return this.qualifiedNodeMap[prop].seed;\n }\n return undefined;\n }\n\n createElectricNodeOf (props, fnSpec) {\n const nodes = props.map(prop => this.qualifiedNodeMap[prop]),\n eNode = new ElectricNode().addEdges(...nodes);\n\n nodes.forEach(node => node.addElectricNode(eNode));\n return eNode[`regListenerFor${fnSpec.type}`](fnSpec.fn);\n }\n\n update (...params) {\n let changedSet,\n upstreamNodes;\n const\n electricEdges = [],\n nodes = params.map((entry) => {\n entry[0].seed = entry[1];\n return entry[0];\n });\n nodes.forEach(node => node.resolve());\n electricEdges.push(...flat(...nodes.map(node => node.electricEdges)));\n changedSet = new ForeignSet(nodes.map(node => node.qualifiedName));\n\n if (!this._propagate) {\n this.__execUniqueElectricEdges(electricEdges);\n this._propagate = true;\n return this;\n }\n\n upstreamNodes = getUpstreamNodes(nodes);\n upstreamNodes.forEach(upstreamNode => upstreamNode.resolve());\n changedSet.append(upstreamNodes.map(node => node.qualifiedName));\n electricEdges.push(...flat(...upstreamNodes.map(node => node.electricEdges)));\n\n this.__execUniqueElectricEdges(Array.from(new Set(electricEdges)), changedSet);\n return this;\n }\n\n __execUniqueElectricEdges (electricEdges, changedSet) {\n const\n cfLstnrs = [], // current frame listeners\n nfLstnrs = []; // next frame listeners\n if (changedSet) {\n const\n differenceSet = ForeignSet.difference(this._wholeSet, changedSet),\n entries = differenceSet.toArray();\n\n entries.forEach(entry => this.qualifiedNodeMap[entry].repeatHead());\n }\n\n electricEdges.forEach((e) => {\n cfLstnrs.push(...e.listeners.currentFrame);\n });\n electricEdges.forEach((e) => {\n nfLstnrs.push(...e.listeners.nextFrame);\n });\n\n !this.propagationOverride.currentFrameListeners && cfLstnrs.forEach(fn => fn());\n !this.propagationOverride.nextFrameListeners && this._schedule(nfLstnrs);\n this.resetPropagationOverride();\n return this;\n }\n\n resetNodeValue (...qnames) {\n const nodes = qnames.map(qname => this.qualifiedNodeMap[qname]),\n args = nodes.map(node => [node, node.seed]);\n this.update(...args);\n return this;\n }\n\n static getResolvedList (node) {\n const resolved = [];\n resolveDependencyOrder(node, resolved, {});\n return resolved;\n }\n\n stopPropagation () {\n this._propagate = false;\n return this;\n }\n\n resetPropagationOverride () {\n this.propagationOverride.currentFrameListeners = false;\n this.propagationOverride.nextFrameListeners = false;\n return this;\n }\n\n setPropagationOverride (key) {\n this.propagationOverride[`${key}Listeners`] = true;\n return this;\n }\n\n getNodeFromQualifiedName (qname) {\n return this.qualifiedNodeMap[qname];\n }\n}\n","import Graph from './graph';\nimport { CustomResolver, fetchAggregator } from './utils';\n\n/**\n * The container class for Hyperdis. Hyperdis is an enabler for observable object with few interesting features like,\n * calculated property, next frame and same frame listeners, multiple listeners etc with a dependency resolving system.\n * It internally uses a graph to hold the hierarchial relationship of a object. Model is merely a container which\n * ties all the components together.\n *\n * @todo Circular dependency detection is not present\n *\n * @example check src/index.spec.js\n * @class\n */\nclass Model {\n constructor () {\n this._graph = new Graph();\n this._lockFlag = false;\n this._reqQ = [];\n }\n\n /**\n * Static method to create and init the model with an observable seed\n *\n * @param {Object} obj The target object which is required to be made observable\n * @return {Model} instance of the observable object model\n */\n static create (obj) {\n return new Model()._addPropInModel(null, obj);\n }\n\n /**\n * Appends more observable property on the already observable instance. This mutates the original model.\n *\n * This function works in two mode. One being\n * @param {String} mountPoint the property on which the new set of properties will be mounted. If its a nested\n * property then the mountPoint has to be written such a way so it feels like you are\n * accessing the object. If the mount point is not found then he obeservables are added in\n * the root.\n * @param {Object} The target object which is required to be made observable\n *\n * Another being\n * @param {Object} The target object which is required to be made observable\n *\n * @return {Model} instance of the observable object model\n */\n append (...params) {\n let mountPoint,\n obj;\n\n if (params.length === 1) {\n mountPoint = null;\n obj = params[0];\n } else {\n mountPoint = params[0];\n obj = params[1];\n }\n\n this._addPropInModel(mountPoint, obj);\n return this;\n }\n\n /**\n * Creates a calculated variable from existing variable. This variable can't be updated from outside.\n * @param {string} mountpoint property path on which the new variable will be placed\n * @param {string} name name of the variable. If the variable could have hierarchy like `limits.start`\n * @param {Function} fn funtion where the dependent variables are injected based on the dependency requirement\n */\n calculatedProp (...params) {\n let calculationConfig,\n customResolver,\n varName,\n mount,\n fetchFn;\n\n if (params.length > 2) {\n mount = params[0];\n varName = params[1];\n fetchFn = params[2];\n } else {\n mount = null;\n varName = params[0];\n fetchFn = params[1];\n }\n\n calculationConfig = fetchFn(fetchAggregator);\n customResolver = new CustomResolver(calculationConfig.fn);\n customResolver.addDependencies(...calculationConfig.dependencies);\n\n this._addPropInModel(mount, { [varName]: customResolver });\n return this;\n }\n\n // eslint-disable-next-line require-jsdoc\n _addPropInModel (mountPoint, obj) {\n this._graph.createNodesFrom(obj, mountPoint);\n return this;\n }\n\n /**\n * Register a listener in the current frame when a property or group of properties is changed.\n *\n * @example\n * This function takes a single or group of property and handler which is called when any of the properties are\n * changed.\n * When a single property is changed the handler is called with two parameter, what was the old value of the state\n * property and what is the new value.\n * myState.on('range.start', (oldValue, newValue) => {\n * console.log('Value before prop change', oldValue);\n * console.log('Value after prop change', newValue);\n * });\n *\n * myState.prop('range.start', 9);\n * // Output\n * Value before prop change 1\n * Value after prop change 9\n *\n * If a handler is registered on change of a property which has another state property as value, then the handler\n * gets called whenever any state property connected to it gets changed\n *\n * myState.on('range', (oldValue, newValue) => {\n * console.log('Value before prop change', oldValue);\n * console.log('Value after prop change', newValue);\n * });\n *\n * myState.prop('range.start', 10);\n * myState.prop('range.type.absolute', false);\n *\n * // Output\n * Value before prop change\n * range {\n * start: 9,\n * end: 5,\n * type: {\n * absolute: true\n * }\n * }\n * Value after prop change\n * range: {\n * start: 10,\n * end: 5,\n * type: {\n * absolute: false\n * }\n * }\n * If a handler is registered with more than one property change then, the handler is called when any of the\n * properties gets changed. In this cast the handler is called with more than one parameter: each for one state\n * property which is registered for listening. Each parameter is of type array containing [oldValue, newValue]\n *\n * myState.on('range.start', 'range.end', (start, end) => {\n * console.log('Start', start);\n * console.log('End', end);\n * });\n *\n * myState.prop('range.start', 12);\n *\n * // Output\n * Start [10, 12]\n * End [5, 5]\n *\n * myState.prop('range.end', 7);\n *\n * // Output\n * Start [12, 12]\n * End [5, 7]\n *\n * The on returns a function which is when called the listener registered gets unregistered\n *\n * let unsub = myState.on(['range.start', 'range.end'], (start, end) => {\n * console.log('Start', start);\n * console.log('End', end);\n * });\n *\n * // Unsubscribe\n * unsub()\n *\n * On takes an optional boolean value as the last parameter, which if passed as a true value the handler gets called\n * during registration itself.\n *\n * @param {Array.} props List of properties which is of interest\n * @param {Function} fn Listener to be executed when any of them is changed. The listener is called with the old\n * value and new value of the properties\n * @param {*} instantCall When registered if the function is to be triggered with the value of the property\n *\n * @return {Function} function to unsubscribe from the listeners registry\n */\n on (props, fn, instantCall) {\n const\n propsArr = props instanceof Array ? props : [props],\n // All there listeners will be executed in the current stack frame\n unsub = this._graph.createElectricNodeOf(propsArr, {\n type: 'CurrFrame',\n fn\n });\n\n if (instantCall) {\n // Bar current next frame listeners from getting fired\n this._graph.stopPropagation().setPropagationOverride('nextFrame').resetNodeValue(...props);\n }\n return unsub;\n }\n\n /**\n * Register a listener for the next frame when a property or group of properties is changed.\n *\n * @example\n * See the examples for the on listener\n *\n * @param {Array.} props List of properties which is of interest\n * @param {Function} fn Listener to be executed when any of them is changed. The listener is called with the old\n * value and new value of the properties. Here the oldvalue is last value of the last frame\n * @param {*} instantCall When registered if the function is to be triggered with the value of the property\n *\n * @return {Function} function to unsubscribe from the listeners registry\n */\n next (props, fn, instantCall) {\n const\n propsArr = props instanceof Array ? props : [props],\n // All there listeners will be executed at the tick of next animation frame\n unsub = this._graph.createElectricNodeOf(propsArr, {\n type: 'NextFrame',\n fn\n });\n\n // @todo check support for this from the graph side\n if (instantCall) {\n // Bar current frame listeners from getting fired\n this._graph.stopPropagation().setPropagationOverride('currentFrame').resetNodeValue(...props);\n }\n\n return unsub;\n }\n\n /**\n * Lock queues the request of property change and releases the change when unlock is called. This is helpful when\n * multiple property is getting called and the model listeners are to be fired once at the end of update.\n *\n * @return {Model} instance of the model\n */\n lock () {\n this._lockFlag = true;\n this._reqQ.length = 0;\n return this;\n }\n\n /**\n * Unlock unleashes the change done after the lock was called.\n\n * @return {Model} instance of the model\n */\n unlock () {\n this._lockFlag = false;\n this.setProp(...this._reqQ);\n this._reqQ.length = 0;\n return this;\n }\n\n /**\n * This acts as getter and setter. If the function is called by passing only one argument, it retrieve the value\n * associated with the property. If the same function is called using two parameters, first one being the property\n * and second one being the value, then the value is set for the property and the handlers are called (if any)\n * which got registered using the on function\n *\n * Getter\n * @param {string} prop property path whose value to be retrieved\n * @return {Object} value of the property at the time of call\n *\n * Setter\n * @param {string} property property path whose value to be ser\n * @return {Model} instance of the model\n */\n prop (...params) {\n let prop,\n val,\n len;\n\n switch (len = params.length) {\n case 1:\n prop = params[0];\n break;\n\n case 2:\n prop = params[0];\n val = params[1];\n break;\n\n default:\n return this;\n }\n\n if (len === 2) {\n this._lockFlag ? this._reqQ.push([prop, val]) : this.setProp([prop, val]);\n return this;\n }\n\n return this._graph.getNodeValue(prop);\n }\n\n // eslint-disable-next-line require-jsdoc\n setProp (...props) {\n // Filter out the calculated variables, so that it cant be changed from outside\n // @todo if a node is not leafValue, and change is called, ignore it too\n // props = props.filter(prop => !(VirtualObj.walkTill(prop[0].split('.'), this._vObj).leafValue()\n // instanceof CalculatedVar));\n\n if (props.length === 0) {\n return this;\n }\n\n this._graph.update(...props.map(prop => [this._graph.getNodeFromQualifiedName(prop[0]), prop[1]]));\n return this;\n }\n\n /**\n * Retrieves the graph representation of the object\n * @return {Graph} instance of the graph associated to the model\n */\n graph () {\n return this._graph;\n }\n\n /**\n * Get serialized data from the model\n *\n * @return {Object} Serialized data\n */\n serialize () {\n return this._graph.root.seed;\n }\n}\n\nexport default Model;\n","import Model from './model';\n\nexport default Model;\n"],"sourceRoot":""} \ No newline at end of file +{"version":3,"sources":["webpack://hyperdis/webpack/universalModuleDefinition","webpack://hyperdis/webpack/bootstrap","webpack://hyperdis/./src/utils/set.js","webpack://hyperdis/./src/utils/index.js","webpack://hyperdis/./src/graph-node.js","webpack://hyperdis/./src/electric-node.js","webpack://hyperdis/./src/graph.js","webpack://hyperdis/./src/model.js","webpack://hyperdis/./src/index.js"],"names":["root","factory","exports","module","define","amd","window","installedModules","__webpack_require__","moduleId","i","l","modules","call","m","c","d","name","getter","o","Object","defineProperty","enumerable","get","r","Symbol","toStringTag","value","t","mode","__esModule","ns","create","key","bind","n","object","property","prototype","hasOwnProperty","p","s","makeEntry","arr","sCon","forEach","elem","undefined","ForeignSet","_classCallCheck","this","_set","keys","set1","set2","filteredKeys","s1","s2","push","isSimpleObject","obj","_typeof","toString","constructor","match","win","reqAnimFrame","requestAnimationFrame","webkitRequestAnimationFrame","mozRequestAnimationFrame","oRequestAnimationFrame","msRequestAnimationFrame","callback","setTimeout","getTimeBasedId","__lastTime","Date","getTime","__id","flat","_len","arguments","length","params","Array","_key","res","param","apply","_toConsumableArray","scheduler","onFinishCallback","queue","animationFrame","listeners","payload","tempQ","slice","fns","reduce","store","fn","map","unique","fetch","namedNode","_len2","_key2","node","qualifiedName","seed","fetchAggregator","_len3","_key3","dependencies","getUpstreamNodes","list","rec","qname","placedIndex","preArr","postArr","concat","outgoingEdges","resolver","accumulate","_len4","_key4","resp","nodeDetails","assign","_defineProperty","identity","CustomResolver","utils_classCallCheck","dep","_dep","GraphNode","options","graph_node_classCallCheck","edges","_seed","requireResolve","retriever","history","electricEdges","_edges","_this","entry","graph_node_toConsumableArray","retrieveDetails","edge","head","hist","ElectricNode","electric_node_classCallCheck","nextFrame","currentFrame","lstnrs","index","nodes","nFn","pullableRecent","filter","_this2","pullableEnd","Graph","graph_classCallCheck","qualifiedNodeMap","graph_node","_wholeSet","_propagate","propagationOverride","currentFrameListeners","nextFrameListeners","_schedule","flushTarget","flush","mount","val","resolveReqList","objn","perv","addDependencies","_node","graph_toConsumableArray","getDependencies","set","getResolvedList","resolve","prop","props","fnSpec","_ref","eNode","electric_node","addEdges","addElectricNode","type","changedSet","upstreamNodes","upstreamNode","append","__execUniqueElectricEdges","from","Set","_this3","cfLstnrs","nfLstnrs","difference","toArray","repeatHead","e","resetPropagationOverride","_this4","qnames","args","update","resolved","resolveDependencyOrder","resolveMap","neighbour","isRoot","model","Model","model_classCallCheck","_graph","graph","_lockFlag","_reqQ","mountPoint","_addPropInModel","_customResolver","calculationConfig","customResolver","varName","fetchFn","model_toConsumableArray","model_defineProperty","createNodesFrom","instantCall","_graph$stopPropagatio","propsArr","unsub","createElectricNodeOf","stopPropagation","setPropagationOverride","resetNodeValue","_graph$stopPropagatio2","setProp","len","getNodeValue","getNodeFromQualifiedName","__webpack_exports__"],"mappings":"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","file":"hyperdis.js","sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine(\"hyperdis\", [], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"hyperdis\"] = factory();\n\telse\n\t\troot[\"hyperdis\"] = factory();\n})(window, function() {\nreturn "," \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 0);\n","const makeEntry = (arr, sCon) => {\n arr.forEach((elem) => {\n if (elem === undefined || elem === null) {\n return;\n }\n sCon[elem] = 1;\n });\n};\n\nexport default class ForeignSet {\n constructor (arr) {\n this._set = {};\n makeEntry(arr, this._set);\n }\n\n append (arr) {\n makeEntry(arr, this._set);\n return this;\n }\n\n toArray () {\n return Object.keys(this._set);\n }\n\n static difference (set1, set2) {\n let key;\n const filteredKeys = [],\n s1 = set1._set,\n s2 = set2._set;\n for (key in s1) {\n if (!({}).hasOwnProperty.call(s1, key)) {\n continue;\n }\n if (key in s2) {\n continue;\n }\n filteredKeys.push(key);\n }\n return new ForeignSet(filteredKeys);\n }\n}\n","/* global window */\n\nimport ForeignSet from './set';\n\nconst\n isSimpleObject = (obj) => {\n let token;\n if (typeof obj === 'object') {\n if (obj === null) { return false; }\n token = Object.prototype.toString.call(obj);\n if (token === '[object Object]') {\n return (obj.constructor.toString().match(/^function (.*)\\(\\)/m) || [])[1] === 'Object';\n }\n }\n return false;\n },\n minMsThreshold = 16,\n /* istanbul ignore next */win = typeof window === 'undefined' ? (this || {}) : window,\n /* istanbul ignore next */ reqAnimFrame = win.requestAnimationFrame || win.webkitRequestAnimationFrame ||\n win.mozRequestAnimationFrame || win.oRequestAnimationFrame ||\n win.msRequestAnimationFrame ||\n function (callback) {\n setTimeout(callback, minMsThreshold);\n },\n\n getTimeBasedId = () => {\n if (getTimeBasedId.__lastTime === new Date().getTime()) {\n return (getTimeBasedId.__lastTime).toString() + (getTimeBasedId.__id++).toString();\n }\n\n getTimeBasedId.__id = 0;\n getTimeBasedId.__lastTime = new Date().getTime();\n return (getTimeBasedId.__lastTime).toString() + (getTimeBasedId.__id++).toString();\n },\n pullableRecent = (nodes, fn) => {\n let nFn = () => {\n fn(...nodes.map((node) => {\n const\n hist = node.history,\n l = hist.length - 1;\n return [hist[l - 1 < 0 ? 0 : l - 1], hist[l]];\n }));\n };\n nFn.__id = getTimeBasedId();\n return nFn;\n },\n pullableEnd = (nodes, fn) => {\n let nFn = () => {\n fn(...nodes.map((node) => {\n const hist = node.history;\n return [hist[0], hist[hist.length - 1]];\n }));\n };\n nFn.__id = getTimeBasedId();\n return nFn;\n },\n unique = fns => fns\n .reduce((store, fn) => {\n // @warn function with side effect, it mutates the store passed during initialization\n if (fn.__id in store.map) {\n return store;\n }\n\n store.map[fn.__id] = 1;\n store.unique.push(fn);\n\n return store;\n }, { map: {}, unique: [] })\n .unique,\n compose = fns => () => {\n fns.forEach(fn => fn());\n },\n flat = (...params) => {\n const res = [];\n params.forEach(param => res.push(...param));\n return res;\n },\n identityMap = arrays => arrays,\n splitPathProp = (path) => {\n const pathArr = path.split('.'),\n len = pathArr.length;\n return [pathArr.slice(0, len - 1), pathArr[len - 1]];\n },\n scheduler = (onFinishCallback) => {\n let queue = [],\n animationFrame = null;\n\n onFinishCallback = onFinishCallback &&\n typeof onFinishCallback === 'function' && onFinishCallback || (() => { });\n\n return (listeners, payload) => {\n [].push.apply(queue, listeners);\n if (animationFrame === null) {\n animationFrame = reqAnimFrame(() => {\n const tempQ = queue.slice(0);\n queue.length = 0;\n animationFrame = null;\n\n unique(tempQ).forEach(fn => fn());\n onFinishCallback(payload);\n });\n }\n };\n },\n fetch = namedNode => (...params) => params.map((param) => {\n const node = namedNode[param];\n return {\n name: node.name,\n qualifiedName: node.qualifiedName,\n value: node.seed\n };\n }),\n fetchAggregator = (...params) => ({\n dependencies: params.slice(0, params.length - 1),\n fn: params[params.length - 1]\n }),\n getUpstreamNodes = (list) => {\n let res = [];\n const map = {};\n list.forEach(node => map[node.qualifiedName] = -1);\n function rec (arr) {\n arr.forEach((node) => {\n let qname,\n placedIndex,\n preArr,\n postArr;\n if (!((qname = node.qualifiedName) in map)) {\n map[qname] = res.push(node) - 1;\n } else {\n placedIndex = map[qname];\n if (placedIndex !== -1 && placedIndex !== res.length - 1) {\n // If not the last element, readjust the array so that the later dependency position is saved\n preArr = res.slice(0, placedIndex);\n postArr = res.slice(placedIndex + 1);\n res = preArr.concat(postArr);\n\n // reset the index in map\n postArr.forEach((elem, i) => map[elem.qualifiedName] = i + preArr.length);\n map[qname] = res.push(node) - 1;\n }\n }\n rec(node.outgoingEdges);\n });\n }\n rec(list);\n return res;\n },\n resolver = {\n accumulate: (...params) => {\n const resp = {};\n params.forEach((nodeDetails) => {\n Object.assign(resp, { [nodeDetails.name]: nodeDetails.value });\n });\n return resp;\n },\n identity: nodeDetails => nodeDetails.value\n };\n\nfunction resolveDependencyOrder (node, resolved, resolveMap) {\n let qname;\n node.edges.forEach((neighbour) => {\n resolveDependencyOrder(neighbour, resolved, resolveMap);\n });\n\n if (node.isRoot() || (qname = node.qualifiedName) in resolveMap) {\n return;\n }\n resolved.push(node);\n resolveMap[qname] = 1;\n}\n\nclass CustomResolver {\n constructor (resolver) {\n this.fn = resolver;\n this.dep = [];\n }\n\n addDependencies(...dep) {\n this.dep.push(...dep);\n return this;\n }\n\n getDependencies () {\n return this.dep;\n }\n\n get () {\n return this.fn;\n }\n}\n\nexport {\n isSimpleObject,\n scheduler,\n compose,\n flat,\n identityMap,\n pullableEnd,\n pullableRecent,\n unique,\n splitPathProp,\n resolver,\n ForeignSet,\n resolveDependencyOrder,\n fetch,\n fetchAggregator,\n CustomResolver,\n getUpstreamNodes\n};\n","export default class GraphNode {\n constructor (name, qualifiedName, options) {\n /* istanbul ignore next */options = options || {};\n this.name = name;\n this.qualifiedName = qualifiedName;\n this.edges = [];\n this.outgoingEdges = [];\n this._seed = null;\n this.requireResolve = true;\n this.retriever = options.retriever;\n this.history = [];\n this.resolver = null;\n this.electricEdges = [];\n }\n\n addDependencies (...dep) {\n this.edges.push(...dep);\n dep.forEach(entry => entry.outgoingEdges.push(this));\n return this;\n }\n\n addElectricNode (node) {\n this.electricEdges.push(node);\n return this;\n }\n\n get seed () {\n return this._seed;\n }\n\n set seed (value) {\n this._seed = value;\n this.requireResolve = true;\n return this;\n }\n\n resolve () {\n this.seed = this.resolver(...this.retrieveDetails());\n this.history.push(this.seed);\n this.requireResolve = false;\n return this;\n }\n\n retrieveDetails () {\n if (this.edges.length === 0) {\n return [{\n name: this.name,\n qualifiedName: this.qualifiedName,\n value: this.seed\n }];\n }\n return this.retriever(...this.edges.map(edge => edge.qualifiedName));\n }\n\n repeatHead () {\n const\n history = this.history,\n head = history[history.length - 1];\n // @todo- Akash, write test case if history.length === 0 to pass code coverage\n if (history.length === 0) {\n return this;\n }\n history.push(head);\n return this;\n }\n\n flush () {\n const hist = this.history,\n head = hist[hist.length - 1];\n hist.length = 0;\n hist.push(head);\n return this;\n }\n\n isRoot () {\n return this.name === null;\n }\n}\n","import {\n pullableRecent,\n pullableEnd\n} from './utils';\n\nexport default class ElectricNode {\n constructor () {\n this.edges = [];\n this.listeners = {\n nextFrame: [],\n currentFrame: []\n };\n }\n\n addEdges (...nodes) {\n this.edges.push(...nodes);\n return this;\n }\n\n regListenerForCurrFrame (fn) {\n const lstnrs = this.listeners.currentFrame;\n let index = lstnrs.push(pullableRecent(this.edges, fn)) - 1;\n\n return () => {\n this.listeners.currentFrame = lstnrs.filter((fn, i) => i !== index);\n };\n }\n\n regListenerForNextFrame (fn) {\n const lstnrs = this.listeners.nextFrame;\n let index = lstnrs.push(pullableEnd(this.edges, fn)) - 1;\n\n return () => {\n this.listeners.nextFrame = lstnrs.filter((fn, i) => i !== index);\n };\n }\n\n hasNextFrameListener () {\n return !!this.listeners.nextFrame.length;\n }\n}\n","import {\n isSimpleObject,\n resolver,\n // upstreamNodes,\n flat,\n resolveDependencyOrder,\n getUpstreamNodes,\n ForeignSet,\n fetch,\n CustomResolver,\n scheduler\n} from './utils';\nimport Node from './graph-node';\nimport ElectricNode from './electric-node';\n\nexport default class Graph {\n constructor () {\n this.qualifiedNodeMap = {};\n this.retriever = fetch(this.qualifiedNodeMap);\n this.root = new Node(null, null, { retriever: this.retriever });\n this.root.resolver = resolver.accumulate;\n\n this._wholeSet = null;\n this._propagate = true;\n this.propagationOverride = {\n currentFrameListeners: false,\n nextFrameListeners: false\n };\n\n this._schedule = scheduler((payload) => {\n let qname;\n for (qname in payload.flushTarget) {\n if (!({}).hasOwnProperty.call(this.qualifiedNodeMap, qname)) {\n return;\n }\n this.qualifiedNodeMap[qname].flush();\n }\n });\n }\n\n createNodesFrom (obj, mount) {\n let val,\n resolveReqList;\n const qualifiedNodeMap = this.qualifiedNodeMap,\n root = this.root,\n retriever = this.retriever;\n\n (function rec (objn, qualifiedName, history) {\n let key,\n qname,\n perv,\n node;\n\n for (key in objn) {\n if (!({}).hasOwnProperty.call(objn, key)) {\n continue;\n }\n qname = `${qualifiedName}${key}`;\n node = new Node(key, qname, { retriever });\n qualifiedNodeMap[qname] = node;\n\n if ((perv = history.perv) !== undefined) {\n perv.addDependencies(node);\n } else {\n // top most level entries\n root.addDependencies(node);\n }\n\n if (isSimpleObject(val = objn[key])) {\n rec(val, `${qualifiedName}${key}.`, { perv: node });\n node.resolver = resolver.accumulate;\n } else if (val instanceof CustomResolver) {\n node.resolver = val.get();\n node.addDependencies(...val.getDependencies().map(qname => qualifiedNodeMap[qname]));\n } else {\n node.resolver = resolver.identity;\n node.seed = val;\n // node.resolve();\n }\n }\n }(obj, mount === null ? '' : `${mount}.`, {\n perv: qualifiedNodeMap[mount]\n }));\n\n this._wholeSet = new ForeignSet(Object.keys(this.qualifiedNodeMap));\n\n // Recalculate the model without firing the listeners\n // @todo selective branch resolve. Currently resolve gets called even for a branch which was updated\n resolveReqList = this.constructor.getResolvedList(root).concat(root).filter(node => node.requireResolve);\n resolveReqList.forEach(node => node.resolve());\n getUpstreamNodes(resolveReqList).forEach(node => node.resolve());\n return this;\n }\n\n getNodeValue (prop) {\n if (prop in this.qualifiedNodeMap) {\n return this.qualifiedNodeMap[prop].seed;\n }\n return undefined;\n }\n\n createElectricNodeOf (props, fnSpec) {\n const nodes = props.map(prop => this.qualifiedNodeMap[prop]),\n eNode = new ElectricNode().addEdges(...nodes);\n\n nodes.forEach(node => node.addElectricNode(eNode));\n return eNode[`regListenerFor${fnSpec.type}`](fnSpec.fn);\n }\n\n update (...params) {\n let changedSet,\n upstreamNodes;\n const\n electricEdges = [],\n nodes = params.map((entry) => {\n entry[0].seed = entry[1];\n return entry[0];\n });\n nodes.forEach(node => node.resolve());\n electricEdges.push(...flat(...nodes.map(node => node.electricEdges)));\n changedSet = new ForeignSet(nodes.map(node => node.qualifiedName));\n\n if (!this._propagate) {\n this.__execUniqueElectricEdges(electricEdges);\n this._propagate = true;\n return this;\n }\n\n upstreamNodes = getUpstreamNodes(nodes);\n upstreamNodes.forEach(upstreamNode => upstreamNode.resolve());\n changedSet.append(upstreamNodes.map(node => node.qualifiedName));\n electricEdges.push(...flat(...upstreamNodes.map(node => node.electricEdges)));\n\n this.__execUniqueElectricEdges(Array.from(new Set(electricEdges)), changedSet);\n return this;\n }\n\n __execUniqueElectricEdges (electricEdges, changedSet) {\n const\n cfLstnrs = [], // current frame listeners\n nfLstnrs = []; // next frame listeners\n if (changedSet) {\n const\n differenceSet = ForeignSet.difference(this._wholeSet, changedSet),\n entries = differenceSet.toArray();\n\n entries.forEach(entry => this.qualifiedNodeMap[entry].repeatHead());\n }\n\n electricEdges.forEach((e) => {\n cfLstnrs.push(...e.listeners.currentFrame);\n });\n electricEdges.forEach((e) => {\n nfLstnrs.push(...e.listeners.nextFrame);\n });\n\n !this.propagationOverride.currentFrameListeners && cfLstnrs.forEach(fn => fn());\n !this.propagationOverride.nextFrameListeners && this._schedule(nfLstnrs, { flushTarget: changedSet });\n this.resetPropagationOverride();\n return this;\n }\n\n resetNodeValue (...qnames) {\n const nodes = qnames.map(qname => this.qualifiedNodeMap[qname]),\n args = nodes.map(node => [node, node.seed]);\n this.update(...args);\n return this;\n }\n\n static getResolvedList (node) {\n const resolved = [];\n resolveDependencyOrder(node, resolved, {});\n return resolved;\n }\n\n stopPropagation () {\n this._propagate = false;\n return this;\n }\n\n resetPropagationOverride () {\n this.propagationOverride.currentFrameListeners = false;\n this.propagationOverride.nextFrameListeners = false;\n return this;\n }\n\n setPropagationOverride (key) {\n this.propagationOverride[`${key}Listeners`] = true;\n return this;\n }\n\n getNodeFromQualifiedName (qname) {\n return this.qualifiedNodeMap[qname];\n }\n}\n","import Graph from './graph';\nimport { CustomResolver, fetchAggregator } from './utils';\n\n/**\n * The container class for Hyperdis. Hyperdis is an enabler for observable object with few interesting features like,\n * calculated property, next frame and same frame listeners, multiple listeners etc with a dependency resolving system.\n * It internally uses a graph to hold the hierarchial relationship of a object. Model is merely a container which\n * ties all the components together.\n *\n * @todo Circular dependency detection is not present\n *\n * @example check src/index.spec.js\n * @class\n */\nclass Model {\n constructor () {\n this._graph = new Graph();\n this._lockFlag = false;\n this._reqQ = [];\n }\n\n /**\n * Static method to create and init the model with an observable seed\n *\n * @param {Object} obj The target object which is required to be made observable\n * @return {Model} instance of the observable object model\n */\n static create (obj) {\n return new Model()._addPropInModel(null, obj);\n }\n\n /**\n * Appends more observable property on the already observable instance. This mutates the original model.\n *\n * This function works in two mode. One being\n * @param {String} mountPoint the property on which the new set of properties will be mounted. If its a nested\n * property then the mountPoint has to be written such a way so it feels like you are\n * accessing the object. If the mount point is not found then he obeservables are added in\n * the root.\n * @param {Object} The target object which is required to be made observable\n *\n * Another being\n * @param {Object} The target object which is required to be made observable\n *\n * @return {Model} instance of the observable object model\n */\n append (...params) {\n let mountPoint,\n obj;\n\n if (params.length === 1) {\n mountPoint = null;\n obj = params[0];\n } else {\n mountPoint = params[0];\n obj = params[1];\n }\n\n this._addPropInModel(mountPoint, obj);\n return this;\n }\n\n /**\n * Creates a calculated variable from existing variable. This variable can't be updated from outside.\n * @param {string} mountpoint property path on which the new variable will be placed\n * @param {string} name name of the variable. If the variable could have hierarchy like `limits.start`\n * @param {Function} fn funtion where the dependent variables are injected based on the dependency requirement\n */\n calculatedProp (...params) {\n let calculationConfig,\n customResolver,\n varName,\n mount,\n fetchFn;\n\n if (params.length > 2) {\n mount = params[0];\n varName = params[1];\n fetchFn = params[2];\n } else {\n mount = null;\n varName = params[0];\n fetchFn = params[1];\n }\n\n calculationConfig = fetchFn(fetchAggregator);\n customResolver = new CustomResolver(calculationConfig.fn);\n customResolver.addDependencies(...calculationConfig.dependencies);\n\n this._addPropInModel(mount, { [varName]: customResolver });\n return this;\n }\n\n // eslint-disable-next-line require-jsdoc\n _addPropInModel (mountPoint, obj) {\n this._graph.createNodesFrom(obj, mountPoint);\n return this;\n }\n\n /**\n * Register a listener in the current frame when a property or group of properties is changed.\n *\n * @example\n * This function takes a single or group of property and handler which is called when any of the properties are\n * changed.\n * When a single property is changed the handler is called with two parameter, what was the old value of the state\n * property and what is the new value.\n * myState.on('range.start', (oldValue, newValue) => {\n * console.log('Value before prop change', oldValue);\n * console.log('Value after prop change', newValue);\n * });\n *\n * myState.prop('range.start', 9);\n * // Output\n * Value before prop change 1\n * Value after prop change 9\n *\n * If a handler is registered on change of a property which has another state property as value, then the handler\n * gets called whenever any state property connected to it gets changed\n *\n * myState.on('range', (oldValue, newValue) => {\n * console.log('Value before prop change', oldValue);\n * console.log('Value after prop change', newValue);\n * });\n *\n * myState.prop('range.start', 10);\n * myState.prop('range.type.absolute', false);\n *\n * // Output\n * Value before prop change\n * range {\n * start: 9,\n * end: 5,\n * type: {\n * absolute: true\n * }\n * }\n * Value after prop change\n * range: {\n * start: 10,\n * end: 5,\n * type: {\n * absolute: false\n * }\n * }\n * If a handler is registered with more than one property change then, the handler is called when any of the\n * properties gets changed. In this cast the handler is called with more than one parameter: each for one state\n * property which is registered for listening. Each parameter is of type array containing [oldValue, newValue]\n *\n * myState.on('range.start', 'range.end', (start, end) => {\n * console.log('Start', start);\n * console.log('End', end);\n * });\n *\n * myState.prop('range.start', 12);\n *\n * // Output\n * Start [10, 12]\n * End [5, 5]\n *\n * myState.prop('range.end', 7);\n *\n * // Output\n * Start [12, 12]\n * End [5, 7]\n *\n * The on returns a function which is when called the listener registered gets unregistered\n *\n * let unsub = myState.on(['range.start', 'range.end'], (start, end) => {\n * console.log('Start', start);\n * console.log('End', end);\n * });\n *\n * // Unsubscribe\n * unsub()\n *\n * On takes an optional boolean value as the last parameter, which if passed as a true value the handler gets called\n * during registration itself.\n *\n * @param {Array.} props List of properties which is of interest\n * @param {Function} fn Listener to be executed when any of them is changed. The listener is called with the old\n * value and new value of the properties\n * @param {*} instantCall When registered if the function is to be triggered with the value of the property\n *\n * @return {Function} function to unsubscribe from the listeners registry\n */\n on (props, fn, instantCall) {\n const\n propsArr = props instanceof Array ? props : [props],\n // All there listeners will be executed in the current stack frame\n unsub = this._graph.createElectricNodeOf(propsArr, {\n type: 'CurrFrame',\n fn\n });\n\n if (instantCall) {\n // Bar current next frame listeners from getting fired\n this._graph.stopPropagation().setPropagationOverride('nextFrame').resetNodeValue(...props);\n }\n return unsub;\n }\n\n /**\n * Register a listener for the next frame when a property or group of properties is changed.\n *\n * @example\n * See the examples for the on listener\n *\n * @param {Array.} props List of properties which is of interest\n * @param {Function} fn Listener to be executed when any of them is changed. The listener is called with the old\n * value and new value of the properties. Here the oldvalue is last value of the last frame\n * @param {*} instantCall When registered if the function is to be triggered with the value of the property\n *\n * @return {Function} function to unsubscribe from the listeners registry\n */\n next (props, fn, instantCall) {\n const\n propsArr = props instanceof Array ? props : [props],\n // All there listeners will be executed at the tick of next animation frame\n unsub = this._graph.createElectricNodeOf(propsArr, {\n type: 'NextFrame',\n fn\n });\n\n // @todo check support for this from the graph side\n if (instantCall) {\n // Bar current frame listeners from getting fired\n this._graph.stopPropagation().setPropagationOverride('currentFrame').resetNodeValue(...props);\n }\n\n return unsub;\n }\n\n /**\n * Lock queues the request of property change and releases the change when unlock is called. This is helpful when\n * multiple property is getting called and the model listeners are to be fired once at the end of update.\n *\n * @return {Model} instance of the model\n */\n lock () {\n this._lockFlag = true;\n this._reqQ.length = 0;\n return this;\n }\n\n /**\n * Unlock unleashes the change done after the lock was called.\n\n * @return {Model} instance of the model\n */\n unlock () {\n this._lockFlag = false;\n this.setProp(...this._reqQ);\n this._reqQ.length = 0;\n return this;\n }\n\n /**\n * This acts as getter and setter. If the function is called by passing only one argument, it retrieve the value\n * associated with the property. If the same function is called using two parameters, first one being the property\n * and second one being the value, then the value is set for the property and the handlers are called (if any)\n * which got registered using the on function\n *\n * Getter\n * @param {string} prop property path whose value to be retrieved\n * @return {Object} value of the property at the time of call\n *\n * Setter\n * @param {string} property property path whose value to be ser\n * @return {Model} instance of the model\n */\n prop (...params) {\n let prop,\n val,\n len;\n\n switch (len = params.length) {\n case 1:\n prop = params[0];\n break;\n\n case 2:\n prop = params[0];\n val = params[1];\n break;\n\n default:\n return this;\n }\n\n if (len === 2) {\n this._lockFlag ? this._reqQ.push([prop, val]) : this.setProp([prop, val]);\n return this;\n }\n\n return this._graph.getNodeValue(prop);\n }\n\n // eslint-disable-next-line require-jsdoc\n setProp (...props) {\n // Filter out the calculated variables, so that it cant be changed from outside\n // @todo if a node is not leafValue, and change is called, ignore it too\n // props = props.filter(prop => !(VirtualObj.walkTill(prop[0].split('.'), this._vObj).leafValue()\n // instanceof CalculatedVar));\n\n if (props.length === 0) {\n return this;\n }\n\n this._graph.update(...props.map(prop => [this._graph.getNodeFromQualifiedName(prop[0]), prop[1]]));\n return this;\n }\n\n /**\n * Retrieves the graph representation of the object\n * @return {Graph} instance of the graph associated to the model\n */\n graph () {\n return this._graph;\n }\n\n /**\n * Get serialized data from the model\n *\n * @return {Object} Serialized data\n */\n serialize () {\n return this._graph.root.seed;\n }\n}\n\nexport default Model;\n","import Model from './model';\n\nexport default Model;\n"],"sourceRoot":""} \ No newline at end of file diff --git a/example/index.js b/example/index.js index 9928881..48f6f27 100644 --- a/example/index.js +++ b/example/index.js @@ -1,158 +1,184 @@ /* global hyperdis */ -const Model = hyperdis.default, - model = Model.create({ - x: 10, - y: 11, - fact: { - x: 2, - y: 3, - z: 4, - complex: { - i1: -9, - i2: -4 - } - } - }), - model1 = Model.create({ range: { start: 1, end: 5 }, visible: true, focus: 1 }); - -model1.append('range', { type: { absolute: true } }); -console.log('R', model1.serialize()); -console.log(model.serialize()); -model.prop('fact.y', -3); -console.log(model.serialize()); - -model.calculatedProp('metrix', fetch => fetch('x', 'y', (x, y) => { - console.log(1); - return x.value + y.value; -})); -model.calculatedProp('sqx', fetch => fetch('x', (x) => { - console.log(2); - return x.value * x.value; -})); - - -model1 - .lock() - .prop('focus', 0) - .prop('range.end', 111) - .unlock(); - -const unsub = model.next(['x', 'fact'], function () { - console.log(arguments); + +const Hyperdis = hyperdis.default; +const store = Hyperdis.create({ range: { start: 1, end: 5 }, visible: true }); + +store.next(['range.end'], (end) => { + console.log('range.end', end); }); +store.next(['range.start'], (start) => { + console.log('range.start', start); + store.prop('range.end', 2000); +}); -let unsubscribe = model1.next(['range.end', 'focus'], - (sRange, focus) => { - console.log(sRange, focus); - }); -model1 - .prop('range.end', 100) - .prop('range.type.absolute', true) - .prop('focus', 7); +// const model = Hyperdis.create({ range: { start: 1, end: 5 }, visible: true }); -function update () { - model1.lock().prop('range.end', 10).prop('range.end', 999).prop('range.end', 555).unlock(); -} +// model.next(['range.start'], (start) => { +// console.log('===========================================', start); +// model.prop('range.end', 200); +// }); +// model.next(['range.end'], (end) => { +// console.log('===========================================', end); +// }); -// model.append('fact.complex', { -// i9: -8 +// model.prop('range.start', 50); +// const Model = hyperdis.default, +// model = Model.create({ +// x: 10, +// y: 11, +// fact: { +// x: 2, +// y: 3, +// z: 4, +// complex: { +// i1: -9, +// i2: -4 +// } +// } +// }), +// model1 = Model.create({ range: { start: 1, end: 5 }, visible: true, focus: 1 }); + +// model1.append('range', { type: { absolute: true } }); +// console.log('R', model1.serialize()); +// console.log(model.serialize()); +// model.prop('fact.y', -3); +// console.log(model.serialize()); + +// model.calculatedProp('metrix', fetch => fetch('x', 'y', (x, y) => { +// console.log(1); +// return x.value + y.value; +// })); +// model.calculatedProp('sqx', fetch => fetch('x', (x) => { +// console.log(2); +// return x.value * x.value; +// })); + + +// model1 +// .lock() +// .prop('focus', 0) +// .prop('range.end', 111) +// .unlock(); + +// const unsub = model.next(['x', 'fact'], function () { +// console.log(arguments); // }); -// model.calcVar('sum', require => require('x', 'y', (x, y) => x + y)); -// console.log(model.prop('sum')); - - -// Render graph here -function createData (mod) { - const graph = mod.graph(), - network = graph.root, - hash = {}; - let data = { - nodes: [], - edges: [], - electricEdges: [] - }; - - (function rec (node, incoming) { - let i; - if (node.qualifiedName in hash) { - i = hash[node.qualifiedName]; - } else { - i = data.nodes.push({ label: node.name === null ? '$' : node.name, r: 20 }) - 1; - } - if (incoming !== undefined) { - data.edges.push({ source: incoming, target: i }); - } - hash[node.qualifiedName] = i; - node.edges.forEach((element) => { - rec(element, i); - }); - }(network)); - - return data; -} - -function renderGraph (data) { - const svg = d3.select('#graph').append('svg'), - chartLayer = svg.append('g').classed('chartLayer', true), - width = document.querySelector('#graph').clientWidth, - height = document.querySelector('#graph').clientHeight, - margin = { top: 0, left: 0, bottom: 0, right: 0 }, - chartWidth = width - (margin.left + margin.right), - chartHeight = height - (margin.top + margin.bottom); - - svg.attr('width', width).attr('height', height); - - chartLayer - .attr('width', chartWidth) - .attr('height', chartHeight) - .attr('transform', `translate(${[margin.left, margin.top]})`); - - let simulation = d3.forceSimulation() - .force('link', d3.forceLink().id(d => d.index)) - .force('collide', d3.forceCollide(d => d.r + 8).iterations(16)) - .force('charge', d3.forceManyBody()) - .force('center', d3.forceCenter(chartWidth / 2, chartHeight / 2)) - .force('y', d3.forceY(0)) - .force('x', d3.forceX(0)); - - let link = svg.append('g') - .attr('class', 'links') - .selectAll('line') - .data(data.edges) - .enter() - .append('line') - .attr('stroke', 'black'); - - let node = svg.append('g') - .attr('class', 'nodes') - .selectAll('text') - .data(data.nodes) - .enter().append('text') - // .attr('r', d => d.r) - .text(d => d.label); - - let ticked = function() { - link - .attr('x1', d => d.source.x) - .attr('y1', d => d.source.y) - .attr('x2', d => d.target.x) - .attr('y2', d => d.target.y); - - node - .attr('x', d => d.x) - .attr('y', d => d.y); - }; - - simulation - .nodes(data.nodes) - .on('tick', ticked); - - simulation.force('link') - .links(data.edges); -} - -const fdgData = createData(model); -renderGraph(fdgData); + + +// let unsubscribe = model1.next(['range.end', 'focus'], +// (sRange, focus) => { +// console.log(sRange, focus); +// }); + +// model1 +// .prop('range.end', 100) +// .prop('range.type.absolute', true) +// .prop('focus', 7); + +// function update () { +// model1.lock().prop('range.end', 10).prop('range.end', 999).prop('range.end', 555).unlock(); +// } + + +// // model.append('fact.complex', { +// // i9: -8 +// // }); +// // model.calcVar('sum', require => require('x', 'y', (x, y) => x + y)); +// // console.log(model.prop('sum')); + + +// // Render graph here +// function createData (mod) { +// const graph = mod.graph(), +// network = graph.root, +// hash = {}; +// let data = { +// nodes: [], +// edges: [], +// electricEdges: [] +// }; + +// (function rec (node, incoming) { +// let i; +// if (node.qualifiedName in hash) { +// i = hash[node.qualifiedName]; +// } else { +// i = data.nodes.push({ label: node.name === null ? '$' : node.name, r: 20 }) - 1; +// } +// if (incoming !== undefined) { +// data.edges.push({ source: incoming, target: i }); +// } +// hash[node.qualifiedName] = i; +// node.edges.forEach((element) => { +// rec(element, i); +// }); +// }(network)); + +// return data; +// } + +// function renderGraph (data) { +// const svg = d3.select('#graph').append('svg'), +// chartLayer = svg.append('g').classed('chartLayer', true), +// width = document.querySelector('#graph').clientWidth, +// height = document.querySelector('#graph').clientHeight, +// margin = { top: 0, left: 0, bottom: 0, right: 0 }, +// chartWidth = width - (margin.left + margin.right), +// chartHeight = height - (margin.top + margin.bottom); + +// svg.attr('width', width).attr('height', height); + +// chartLayer +// .attr('width', chartWidth) +// .attr('height', chartHeight) +// .attr('transform', `translate(${[margin.left, margin.top]})`); + +// let simulation = d3.forceSimulation() +// .force('link', d3.forceLink().id(d => d.index)) +// .force('collide', d3.forceCollide(d => d.r + 8).iterations(16)) +// .force('charge', d3.forceManyBody()) +// .force('center', d3.forceCenter(chartWidth / 2, chartHeight / 2)) +// .force('y', d3.forceY(0)) +// .force('x', d3.forceX(0)); + +// let link = svg.append('g') +// .attr('class', 'links') +// .selectAll('line') +// .data(data.edges) +// .enter() +// .append('line') +// .attr('stroke', 'black'); + +// let node = svg.append('g') +// .attr('class', 'nodes') +// .selectAll('text') +// .data(data.nodes) +// .enter().append('text') +// // .attr('r', d => d.r) +// .text(d => d.label); + +// let ticked = function() { +// link +// .attr('x1', d => d.source.x) +// .attr('y1', d => d.source.y) +// .attr('x2', d => d.target.x) +// .attr('y2', d => d.target.y); + +// node +// .attr('x', d => d.x) +// .attr('y', d => d.y); +// }; + +// simulation +// .nodes(data.nodes) +// .on('tick', ticked); + +// simulation.force('link') +// .links(data.edges); +// } + +// const fdgData = createData(model); +// renderGraph(fdgData); diff --git a/karma.conf.js b/karma.conf.js index b9075be..4117deb 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -1,56 +1,62 @@ -const isDebug = arg => arg === '--debug'; -let coverageRules = [ - { - test: /\.js$|\.jsx$/, - use: { - loader: 'istanbul-instrumenter-loader', - options: { esModules: true }, - }, - enforce: 'pre', - exclude: /node_modules|src\/renderer\/|\.spec\.js$/, - } - ], - preprocessors = ['webpack']; - -if (process.argv.some(isDebug)) { - coverageRules = []; - preprocessors.push('sourcemap'); -} - module.exports = function (config) { config.set({ basePath: './', frameworks: ['mocha', 'chai'], files: [ - 'test.webpack.js', + 'test.webpack.js' ], webpack: { + devtool: 'inline-source-map', module: { rules: [ + // instrument only testing sources with Istanbul { test: /\.js$/, use: { loader: 'babel-loader', query: { - presets: ['es2015'], - }, + presets: ['env'] + } }, - exclude: /node_modules/, + exclude: /node_modules/ }, - ...coverageRules - ], - }, - devtool: 'inline-source-map', + { + test: /\.(s*)css$/, + use: [ + { + loader: 'style-loader', + options: { singleton: true } + }, + { loader: 'css-loader' }, + { loader: 'sass-loader' } + ], + exclude: /node_modules/ + }, + { + test: /\.js$|\.jsx$/, + use: { + loader: 'istanbul-instrumenter-loader', + options: { esModules: true } + }, + enforce: 'pre', + exclude: /node_modules|src\/renderer\/|\.spec\.js$/ + } + ] + } }, webpackMiddleware: { - stats: 'errors-only' + noInfo: true, + stats: { + chunks: false + } }, preprocessors: { - 'test.webpack.js': preprocessors + 'test.webpack.js': ['webpack', 'sourcemap'] }, exclude: [ - '**/*.swp', + '**/*.swp' ], + coverageIstanbulReporter: { dir: 'coverage/', thresholds: { @@ -59,7 +65,7 @@ module.exports = function (config) { statements: 80, lines: 80, branches: 80, - functions: 80, + functions: 80 }, each: { // thresholds per file statements: 80, @@ -68,20 +74,19 @@ module.exports = function (config) { functions: 80, overrides: { 'baz/component/**/*.js': { - statements: 80, - }, - }, - }, + statements: 80 + } + } + } }, reports: ['html', 'lcov', 'text-summary'], fixWebpackSourcePaths: true, reporters: [ { type: 'text' }, { type: 'html', subdir: 'report-html', file: 'report.html' }, - { type: 'lcov', subdir: 'report-lcov', file: 'report.txt' }, - ], + { type: 'lcov', subdir: 'report-lcov', file: 'report.txt' } + ] }, - reporters: ['spec', 'coverage-istanbul'], specReporter: { maxLogLines: 5, // limit number of lines logged per test @@ -89,14 +94,20 @@ module.exports = function (config) { suppressFailed: false, // do not print information about failed tests suppressPassed: false, // do not print information about passed tests suppressSkipped: true, // do not print information about skipped tests - showSpecTiming: false, // print the time elapsed for each spec + showSpecTiming: false // print the time elapsed for each spec }, port: 9876, colors: true, - logLevel: config.LOG_WARN, + logLevel: config.LOG_INFO, autoWatch: false, - browsers: ['ChromeHeadless'], + browsers: ['ChromeHeadlessNoSandbox'], + customLaunchers: { + ChromeHeadlessNoSandbox: { + base: 'ChromeHeadless', + flags: ['--no-sandbox'] + } + }, singleRun: true, - concurrency: Infinity, + concurrency: Infinity }); }; diff --git a/package.json b/package.json index d186bb9..262fb00 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "scripts": { "test": "npm run lint && npm run ut", "ut": "karma start karma.conf.js", - "utd": "karma start karma.conf.js --browsers=Chrome --single-run=false --debug", + "utd": "karma start karma.conf.js --browsers=Chrome --single-run=false --debug --auto-watch", "build": "webpack --mode production", "start": "webpack-dev-server --open --mode development", "lint": "eslint ./src", diff --git a/src/graph.js b/src/graph.js index 5170514..cadff79 100644 --- a/src/graph.js +++ b/src/graph.js @@ -27,9 +27,9 @@ export default class Graph { nextFrameListeners: false }; - this._schedule = scheduler(() => { + this._schedule = scheduler((payload) => { let qname; - for (qname in this.qualifiedNodeMap) { + for (qname in payload.flushTarget) { if (!({}).hasOwnProperty.call(this.qualifiedNodeMap, qname)) { return; } @@ -155,7 +155,7 @@ export default class Graph { }); !this.propagationOverride.currentFrameListeners && cfLstnrs.forEach(fn => fn()); - !this.propagationOverride.nextFrameListeners && this._schedule(nfLstnrs); + !this.propagationOverride.nextFrameListeners && this._schedule(nfLstnrs, { flushTarget: changedSet }); this.resetPropagationOverride(); return this; } diff --git a/src/index.spec.js b/src/index.spec.js index 3dd0d2e..947baf2 100644 --- a/src/index.spec.js +++ b/src/index.spec.js @@ -1,10 +1,9 @@ /* global describe, it, before */ import { expect } from 'chai'; -import Model from '../'; +import Model from './'; describe('ReactiveModel', () => { let model; - // calcVarModel; it('should have the instance api methods intact', () => { let methods = [ @@ -44,14 +43,6 @@ describe('ReactiveModel', () => { before(() => { model = Model.create({ range: { start: 1, end: 5 }, visible: true }); - // calcVarModel = Model.create({ - // x: 10, - // y: 11, - // fact: { - // x: 2, - // y: 5 - // } - // }); }); describe('##create', () => { @@ -59,7 +50,6 @@ describe('ReactiveModel', () => { expect(model).to.be.an.instanceof(Model); }); - it('should return the root of graph once the model is created', () => { expect(model.graph().root.isRoot()).to.be.true; }); @@ -95,23 +85,17 @@ describe('ReactiveModel', () => { }); }); - // describe('#calcVar', () => { - // it('should add a new variable which is calculated from other variables', () => { - // calcVarModel.calcVar('sum', - // fetch => fetch('x', 'y', 'fact', (x, y, fact) => (x * fact.x) + (y * fact.y))); - // expect(calcVarModel.prop('sum')).to.equal(75); - // }); - // }); - describe('#prop', () => { it('should work as a getter when only one argument is passed', () => { let val = model.prop('range.start'); expect(val).to.be.equal(1); }); + it('should return undefined if the prop is not present', () => { let val = model.prop('range.isPresent'); expect(val).to.not.be.defined; }); + it('should current instance if no argument is provided', () => { let val = model.prop(); expect(val).to.deep.equal(model); @@ -126,11 +110,6 @@ describe('ReactiveModel', () => { let val = model.prop('focus', 10); expect(val.prop('focus')).to.equal(10); }); - - // it('should not update calculated variable', () => { - // calcVarModel.prop('sum', 10); - // expect(calcVarModel.prop('sum')).to.equal(75); - // }); }); describe('#lock, #unlock', () => { @@ -171,38 +150,15 @@ describe('ReactiveModel', () => { unsubscribe(); }); + it('should subscribe to a change or registration of a property and executor gets called when property change', - () => { - let unsubscribe = model.on(['range.start'], - (rangeStart) => { - expect(rangeStart[0]).to.deep.equal(rangeStart[1]); - }, true); - unsubscribe(); - }); - - // it('should not call the callback of a calculatedVariable when the variable is directly set', () => { - // let exeFlag = null, - // uns; - // uns = calcVarModel.on('sum', (oldVal, newVal) => { - // exeFlag = oldVal === newVal; - // }); - - // calcVarModel.prop('sum', 10); - // uns(); - // expect(exeFlag).to.equal(null); - // }); - - // it('should get called once any of the two variables are changed of a calculated variable', () => { - // let val = null, - // uns; - // uns = calcVarModel.on('sum', (oldVal, newVal) => { - // val = [oldVal, newVal]; - // }); - - // calcVarModel.prop('x', 20); - // uns(); - // expect(val).to.deep.equal([75, 95]); - // }); + () => { + let unsubscribe = model.on(['range.start'], + (rangeStart) => { + expect(rangeStart[0]).to.deep.equal(rangeStart[1]); + }, true); + unsubscribe(); + }); }); describe('#next', () => { @@ -221,6 +177,22 @@ describe('ReactiveModel', () => { unsubscribe(); }); + + it('should consecutively fire next listeners', (done) => { + const unsub2 = model.next(['range.end'], (end) => { + expect(end).to.deep.equal([5, 200]); + done(); + }); + + const unsub1 = model.next(['range.start'], () => { + model.prop('range.end', 200); + + unsub1(); + unsub2(); + }); + + model.prop('range.start', 50); + }); }); describe('#calculated Property', () => { @@ -240,6 +212,7 @@ describe('ReactiveModel', () => { .unlock(); expect(model.prop('newCalculatedProperty')).to.deep.equal(100); }); + it('should be able to generate a calculated property on a property path', () => { model.calculatedProp('range', 'newRangePropery', fetch => fetch('range.start', 'range.end', diff --git a/src/utils/index.js b/src/utils/index.js index 7437a92..8cf7335 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -88,14 +88,16 @@ const onFinishCallback = onFinishCallback && typeof onFinishCallback === 'function' && onFinishCallback || (() => { }); - return (listeners) => { + return (listeners, payload) => { [].push.apply(queue, listeners); if (animationFrame === null) { animationFrame = reqAnimFrame(() => { - unique(queue).forEach(fn => fn()); - onFinishCallback(); - animationFrame = null; + const tempQ = queue.slice(0); queue.length = 0; + animationFrame = null; + + unique(tempQ).forEach(fn => fn()); + onFinishCallback(payload); }); } }; @@ -167,16 +169,6 @@ function resolveDependencyOrder (node, resolved, resolveMap) { resolveMap[qname] = 1; } -// function getUpstreamNodes (node, list) { -// if (node.isRoot()) { -// return; -// } -// node.outgoingEdges.forEach((_node) => { -// list.push(_node); -// getUpstreamNodes(_node, list); -// }); -// } - class CustomResolver { constructor (resolver) { this.fn = resolver; @@ -211,7 +203,6 @@ export { ForeignSet, resolveDependencyOrder, fetch, - // upstreamNodes, fetchAggregator, CustomResolver, getUpstreamNodes diff --git a/webpack.config.js b/webpack.config.js index 0a632f2..81bb5b7 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -25,4 +25,4 @@ module.exports = { inline: true, contentBase: './example', } -}; \ No newline at end of file +};