UNPKG

@wiz-code/async-fsm

Version:

Finite StateMachine JavaScript Library

271 lines (201 loc) 7.1 kB
'use strict'; var _ = require('underscore'); var BaseState = require('./base-state'); var logger = require('./logger'); var util = require('./util'); var mixin = require('./mixin'); var max = Math.max; var State = function (name, options) { BaseState.call(this, name); options = _.defaults(options || {}, _.clone(State.options)); if (!_.isUndefined(options.data)) { this.set(options.data); } this.save(); if (!_.isUndefined(options.props)) { this.setProp(options.props); } if (!_.isUndefined(options.methods)) { this.setMethod(options.methods); } this.entryAction = options.entryAction; this.exitAction = options.exitAction; this.doActivity = options.doActivity; this.autoTransition = options.autoTransition; this._loop = options.loop; this._fps = options.fps; this._interval = 1000 / this._fps; this._useRAF = options.useRAF; this._timerId = 0; this._ticks = 0; this._elapsedTime = 0; this._lastTime = 0; this._setTimer = (this._useRAF && !_.isUndefined(util.global.requestAnimationFrame)) ? this._requestAnimFrame : this._setTimeout; this._clearTimer = (this._useRAF && !_.isUndefined(util.global.requestAnimationFrame)) ? this._cancelAnimFrame : this._clearTimeout; }; State.options = { entryAction: _.noop, exitAction: _.noop, doActivity: _.noop, autoTransition: false, loop: false, fps: 60, useRAF: false, }; State.prototype = _.create(BaseState.prototype, { constructor: State, _cname: 'State', getTicks: function () { if (!this._loop) { logger.error('Stateインスタンスのloopオプションが指定されてません。'); } return this._ticks; }, getElapsedTime: function () { if (!this._loop) { logger.error('Stateインスタンスのloopオプションが指定されてません。'); } return this._elapsedTime; }, completion: function () { var params = _.toArray(arguments); if (!this.isActive()) { logger.error(this._cname + 'インスタンス"' + this._name + '"はすでに非アクティブ化されています。'); } this._async(function () { var transit; if (!_.isNull(this.container)) { transit = util.findNextTransition(this.container, this); if (!_.isUndefined(transit)) { transit.trigger.apply(transit, params); } else { this._exit(); this.notify('container', 'completion'); } } }); }, update: function (event) { var params = _.toArray(arguments).slice(1); switch (event) { case 'entry': this._entry.apply(this, params); break; case 'exit': this._exit.apply(this, params); break; case 'refresh': this._refresh.apply(this, params); break; case 'completion': this.completion.apply(this, params); break; } }, _requestAnimFrame: function (callback) { var loop; callback = _.bind(callback, this); this.resetTimer(); loop = _.bind(function (currentTime) { var delta; this._timerId = requestAnimationFrame(loop); delta = this._lastTime !== 0 ? currentTime - this._lastTime : 0; this._elapsedTime += delta; this._lastTime = currentTime; this._ticks += 1; callback(delta); }, this); this._timerId = requestAnimationFrame(loop); }, _cancelAnimFrame: function () { cancelAnimationFrame(this._timerId); }, resetTimer: function () { this._lastTime = 0; this._elapsedTime = 0; this._ticks = 0; }, _setTimeout: function (callback) { var loop; callback = _.bind(callback, this); this.resetTimer(); loop = _.bind(function () { var timeToCall = this._tick(callback); this._timerId = setTimeout(loop, timeToCall); }, this); this._timerId = setTimeout(loop, 0); }, _clearTimeout: function () { clearTimeout(this._timerId); }, _tick: function (callback) { var currentTime, deltaTime, processingTime, timeToCall; currentTime = _.now(); deltaTime = this._lastTime === 0 ? 0 : currentTime - this._lastTime; this._elapsedTime += deltaTime; this._lastTime = currentTime; this._ticks += 1; callback(deltaTime); processingTime = _.now() - currentTime; timeToCall = max(this._interval - processingTime, 0); return timeToCall; }, _activate: function () { var transit = util.findRelatedTransition(this); this._status = 'active'; logger.info(this._cname + 'インスタンス"' + this._name + '"がアクティブ化されました。'); this.entryAction(transit); this.notify('root', 'prev-activity', this); this.notify('root', 'async-activity', _.bind(function () { if (this._loop) { this._setTimer(function (deltaTime) { this.doActivity(deltaTime, transit); if (this.autoTransition) { this.completion(); } }); } else { this.doActivity(transit); if (this.autoTransition) { this.completion(); } } }, this)); this.notify('root', 'after-activity', this); }, _deactivate: function () { var transit = util.findRelatedTransition(this); this._clearTimer(); if (!_.isNull(this.container)) { this.notify('container', 'set-previous-state', this); } this.exitAction(transit); this._status = 'inactive'; logger.info(this._cname + 'インスタンス"' + this._name + '"が非アクティブ化されました。'); }, }); var FinalState = function (name) { BaseState.call(this, name); }; FinalState.prototype = _.create(BaseState.prototype, _.extend({ constructor: FinalState, _cname: 'FinalState', _activate: function () { this._status = 'active'; logger.info('FinalStateインスタンス"' + this._name + '"がアクティブ化されました。'); this.completion(); }, _deactivate: function () { this._status = 'inactive'; if (!_.isNull(this.container)) { this.notify('container', 'set-previous-state', null); } logger.info('FinalStateインスタンス"' + this._name + '"が非アクティブ化されました。'); }, }, mixin.disable)); module.exports = { State: State, FinalState: FinalState, };