hyperflow
Version:
A javascript state flow and mutation management toolkit & library for developing universal app.
205 lines (185 loc) • 7.71 kB
JavaScript
/**
* Copyright 2015-present Tuan Le.
*
* Licensed under the MIT License.
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://opensource.org/licenses/mit-license.html
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*------------------------------------------------------------------------
*
* @module StateTimeTraversalComposite
* @description - A persistent state time traversal composite module.
*
* @author Tuan Le (tuan.t.lei@gmail.com)
*
* @flow
*/
; // eslint-disable-line
import CommonElement from '../../elements/common-element';
import CompositeElement from '../../elements/composite-element';
/* factory Ids */
import {
SERVICE_FACTORY_CODE,
STORE_FACTORY_CODE
} from '../factory-code';
const Hf = CommonElement();
export default CompositeElement({
template: {
/**
* @description - Initialized and check that factory is valid for this composite.
*
* @method $initStateTimeTraversalComposite
* @return void
*/
$initStateTimeTraversalComposite () {
const factory = this;
if (Hf.DEVELOPMENT) {
if (!Hf.isSchema({
fId: `string`,
name: `string`,
outgoing: `function`,
reconfigState: `function`,
getStateCursor: `function`,
getStateAsObject: `function`
}).of(factory) || !(factory.fId.substr(0, SERVICE_FACTORY_CODE.length) === SERVICE_FACTORY_CODE ||
factory.fId.substr(0, STORE_FACTORY_CODE.length) === STORE_FACTORY_CODE)) {
Hf.log(`error`, `StateTimeTraversalComposite.$init - Factory is invalid. Cannot apply composite.`);
} else {
const stateCursor = factory.getStateCursor();
if (!Hf.isSchema({
hasItem: `function`,
recallContentItem: `function`
}).of(stateCursor)) {
Hf.log(`error`, `StateTimeTraversalComposite.$init - Factory state curcor is invalid. Cannot apply composite.`);
}
}
}
},
/**
* @description - Clear all state mutation history.
*
* @method flush
* @param {object} option
* @return void
*/
flush (option = {}) {
const factory = this;
option = Hf.isObject(option) ? option : {};
factory.flushState(option);
},
/**
* @description - Time traverse to the previous state mutation from time history at path Id.
* Head state cursor changes back to the previous timeIndex.
*
* @method timeTraverse
* @param {string} key
* @param {number} timeIndexOffset
* @return {bool}
*/
timeTraverse (key, timeIndexOffset) {
const factory = this;
const stateCursor = factory.getStateCursor();
let reconfigurator = {};
if (Hf.DEVELOPMENT) {
if (!stateCursor.hasItem(key)) {
Hf.log(`error`, `StateTimeTraversalComposite.timeTraverse - Data item key:${key} is not defined.`);
} else if (!Hf.isInteger(timeIndexOffset) || timeIndexOffset >= 0) {
Hf.log(`error`, `StateTimeTraversalComposite.timeTraverse - Input time index offset must be non-zero and negative.`);
}
}
const {
timestamp,
content
} = stateCursor.recallContentItem(key, timeIndexOffset);
if (!(Hf.isDefined(content) && Hf.isNumeric(timestamp))) {
if (Hf.DEVELOPMENT) {
Hf.log(`warn1`, `StateTimeTraversalComposite.timeTraverse - Unable to time traverse to undefined state of key:${key} at time index.`);
}
return false;
}
reconfigurator[key] = content;
factory.reconfigState(reconfigurator);
const recalledState = Hf.mix(factory.getStateAsObject(), {
exclusion: {
keys: [
`name`,
`fId`
]
}
}).with({});
/* emitting a mutation event to interface */
factory.outgoing(
`as-state-mutated`,
`do-sync-reflected-state`
).emit(() => recalledState);
Hf.log(`info0`, `Time traversing to previous state at timestamp:${timestamp} of key:${key}.`);
return true;
},
/**
* @description - Traverse and recall the previous state mutation from time history.
* Head state cursor does not change.
*
* @method recall
* @param {string} key
* @param {number} timeIndexOffset
* @return {object|null}
*/
recall (key, timeIndexOffset) {
const factory = this;
const stateCursor = factory.getStateCursor();
if (Hf.DEVELOPMENT) {
if (!stateCursor.hasItem(key)) {
Hf.log(`error`, `StateTimeTraversalComposite.recall - Data item key:${key} is not defined.`);
} else if (!Hf.isInteger(timeIndexOffset) || timeIndexOffset >= 0) {
Hf.log(`error`, `StateTimeTraversalComposite.recall - Input time index offset must be non-zero and negative.`);
}
}
const {
timestamp,
content
} = stateCursor.recallContentItem(key, timeIndexOffset);
if (!(Hf.isDefined(content) && Hf.isNumeric(timestamp))) {
if (Hf.DEVELOPMENT) {
Hf.log(`warn1`, `StateTimeTraversalComposite.recall - Unable to recall an undefined state of key:${key} at time index.`);
}
} else {
Hf.log(`info0`, `Recalling previous state at timestamp:${timestamp} of key:${key}.`);
}
return content;
},
/**
* @description - Traverse and recall all the previous state mutation from time history.
* Head state cursor does not change.
*
* @method recallAll
* @param {string} key
* @return {array}
*/
recallAll (key) {
const factory = this;
const stateCursor = factory.getStateCursor();
if (Hf.DEVELOPMENT) {
if (!stateCursor.hasItem(key)) {
Hf.log(`error`, `StateTimeTraversalComposite.recallAll - Data item key:${key} is not defined.`);
}
}
const contentHistoryItems = stateCursor.recallAllContentItems(key);
if (!Hf.isArray(contentHistoryItems)) {
if (Hf.DEVELOPMENT) {
Hf.log(`error`, `StateTimeTraversalComposite.recallAll - Unable to recall all previous states of key:${key}.`);
}
} else {
Hf.log(`info0`, `Recalling all previous states of key:${key}.`);
}
return contentHistoryItems;
}
}
});