| var zrUtil = require("../core/util"); | 
|   | 
| var Style = require("./Style"); | 
|   | 
| var _vector = require("../core/vector"); | 
|   | 
| var vec2Copy = _vector.copy; | 
|   | 
| /** | 
|  * States machine for managing graphic states | 
|  */ | 
|   | 
| /** | 
|  * @typedef {Object} IGraphicState | 
|  * @property {number} [zlevel] | 
|  * @property {number} [z] | 
|  * @property {Array.<number>} {position} | 
|  * @property {Array.<number>|number} {rotation} | 
|  * @property {Array.<number>} {scale} | 
|  * @property {Object} style | 
|  * | 
|  * @property {Function} onenter | 
|  * @property {Function} onleave | 
|  * @property {Function} ontransition | 
|  * @property {Array.<IGraphicStateTransition|string>} transition | 
|  *           Transition object or a string descriptor like '* 30 0 Linear' | 
|  */ | 
| var transitionProperties = ['position', 'rotation', 'scale', 'style', 'shape']; | 
| /** | 
|  * @module zrender/graphic/States~TransitionObject | 
|  */ | 
|   | 
| var TransitionObject = function (opts) { | 
|   if (typeof opts === 'string') { | 
|     this._fromStr(opts); | 
|   } else if (opts) { | 
|     opts.property && (this.property = opts.property); | 
|     opts.duration != null && (this.duration = opts.duration); | 
|     opts.easing && (this.easing = opts.easing); | 
|     opts.delay && (this.delay = opts.delay); | 
|   } | 
|   | 
|   if (this.property !== '*') { | 
|     this.property = this.property.split(','); | 
|   } else { | 
|     this.property = transitionProperties; | 
|   } | 
| }; | 
|   | 
| TransitionObject.prototype = { | 
|   constructor: TransitionObject, | 
|   | 
|   /** | 
|    * List of all transition properties. Splitted by comma. Must not have spaces in the string. | 
|    * e.g. 'position,style.color'. '*' will match all the valid properties. | 
|    * @type {string} | 
|    * @default * | 
|    */ | 
|   property: '*', | 
|   | 
|   /** | 
|    * @type {string} | 
|    * @default 'Linear' | 
|    */ | 
|   easing: 'Linear', | 
|   | 
|   /** | 
|    * @type {number} | 
|    * @default 'number' | 
|    */ | 
|   duration: 500, | 
|   | 
|   /** | 
|    * @type {number} | 
|    */ | 
|   delay: 0, | 
|   _fromStr: function (str) { | 
|     var arr = str.split(/\s+/g); | 
|     this.property = arr[0]; | 
|     this.duration = +arr[1]; | 
|     this.delay = +arr[2]; | 
|     this.easing = arr[3]; | 
|   } | 
| }; | 
| /** | 
|  * @alias module:zrender/graphic/States | 
|  */ | 
|   | 
| var GraphicStates = function (opts) { | 
|   opts = opts || {}; | 
|   this._states = {}; | 
|   /** | 
|    * Target element | 
|    * @type {zrender/graphic/Displayable|zrender/container/Group} | 
|    */ | 
|   | 
|   this._el = opts.el; | 
|   this._subStates = []; | 
|   this._transitionAnimators = []; | 
|   | 
|   if (opts.initialState) { | 
|     this._initialState = opts.initialState; | 
|   } | 
|   | 
|   var optsStates = opts.states; | 
|   | 
|   if (optsStates) { | 
|     for (var name in optsStates) { | 
|       if (optsStates.hasOwnProperty(name)) { | 
|         var state = optsStates[name]; | 
|   | 
|         this._addState(name, state); | 
|       } | 
|     } | 
|   } | 
|   | 
|   this.setState(this._initialState); | 
| }; | 
|   | 
| GraphicStates.prototype = { | 
|   constructor: GraphicStates, | 
|   | 
|   /** | 
|    * All other state will be extended from initial state | 
|    * @type {string} | 
|    * @private | 
|    */ | 
|   _initialState: 'normal', | 
|   | 
|   /** | 
|    * Current state | 
|    * @type {string} | 
|    * @private | 
|    */ | 
|   _currentState: '', | 
|   el: function () { | 
|     return this._el; | 
|   }, | 
|   _addState: function (name, state) { | 
|     this._states[name] = state; | 
|   | 
|     if (state.transition) { | 
|       state.transition = new TransitionObject(state.transition); | 
|     } // Extend from initial state | 
|   | 
|   | 
|     if (name !== this._initialState) { | 
|       this._extendFromInitial(state); | 
|     } else { | 
|       var el = this._el; // setState 的时候自带的 style 和 shape 都会被直接覆盖 | 
|       // 所以这边先把自带的 style 和 shape 扩展到初始状态中 | 
|   | 
|       zrUtil.merge(state.style, el.style, false, false); | 
|   | 
|       if (state.shape) { | 
|         zrUtil.merge(state.shape, el.shape, false, true); | 
|       } else { | 
|         state.shape = zrUtil.clone(el.shape, true); | 
|       } | 
|   | 
|       for (var name in this._states) { | 
|         if (this._states.hasOwnProperty(name)) { | 
|           this._extendFromInitial(this._states[name]); | 
|         } | 
|       } | 
|     } | 
|   }, | 
|   _extendFromInitial: function (state) { | 
|     var initialState = this._states[this._initialState]; | 
|   | 
|     if (initialState && state !== initialState) { | 
|       zrUtil.merge(state, initialState, false, true); | 
|     } | 
|   }, | 
|   setState: function (name, silent) { | 
|     if (name === this._currentState && !this.transiting()) { | 
|       return; | 
|     } | 
|   | 
|     var state = this._states[name]; | 
|   | 
|     if (state) { | 
|       this._stopTransition(); | 
|   | 
|       if (!silent) { | 
|         var prevState = this._states[this._currentState]; | 
|   | 
|         if (prevState) { | 
|           prevState.onleave && prevState.onleave.call(this); | 
|         } | 
|   | 
|         state.onenter && state.onenter.call(this); | 
|       } | 
|   | 
|       this._currentState = name; | 
|   | 
|       if (this._el) { | 
|         var el = this._el; // Setting attributes | 
|   | 
|         if (state.zlevel != null) { | 
|           el.zlevel = state.zlevel; | 
|         } | 
|   | 
|         if (state.z != null) { | 
|           el.z = state.z; | 
|         } // SRT | 
|   | 
|   | 
|         state.position && vec2Copy(el.position, state.position); | 
|         state.scale && vec2Copy(el.scale, state.scale); | 
|   | 
|         if (state.rotation != null) { | 
|           el.rotation = state.rotation; | 
|         } // Style | 
|   | 
|   | 
|         if (state.style) { | 
|           var initialState = this._states[this._initialState]; | 
|           el.style = new Style(); | 
|   | 
|           if (initialState) { | 
|             el.style.extendFrom(initialState.style, false); | 
|           } | 
|   | 
|           if ( // Not initial state | 
|           name !== this._initialState // Not copied from initial state in _extendFromInitial method | 
|           && initialState.style !== state.style) { | 
|             el.style.extendFrom(state.style, true); | 
|           } | 
|         } | 
|   | 
|         if (state.shape) { | 
|           el.shape = zrUtil.clone(state.shape, true); | 
|         } | 
|   | 
|         el.dirty(); | 
|       } | 
|     } | 
|   | 
|     for (var i = 0; i < this._subStates.length; i++) { | 
|       this._subStates.setState(name); | 
|     } | 
|   }, | 
|   getState: function () { | 
|     return this._currentState; | 
|   }, | 
|   transitionState: function (target, done) { | 
|     if (target === this._currentState && !this.transiting()) { | 
|       return; | 
|     } | 
|   | 
|     var state = this._states[target]; | 
|     var styleShapeReg = /$[style|shape]\./; | 
|     var self = this; // Animation 去重 | 
|   | 
|     var propPathMap = {}; | 
|   | 
|     if (state) { | 
|       self._stopTransition(); | 
|   | 
|       var el = self._el; | 
|   | 
|       if (state.transition && el && el.__zr) { | 
|         // El can be animated | 
|         var transitionCfg = state.transition; | 
|         var property = transitionCfg.property; | 
|         var animatingCount = 0; | 
|   | 
|         var animationDone = function () { | 
|           animatingCount--; | 
|   | 
|           if (animatingCount === 0) { | 
|             self.setState(target); | 
|             done && done(); | 
|           } | 
|         }; | 
|   | 
|         for (var i = 0; i < property.length; i++) { | 
|           var propName = property[i]; // Animating all the properties in style or shape | 
|   | 
|           if (propName === 'style' || propName === 'shape') { | 
|             if (state[propName]) { | 
|               for (var key in state[propName]) { | 
|                 /* eslint-disable max-depth */ | 
|                 if (!state[propName].hasOwnProperty(key)) { | 
|                   continue; | 
|                 } | 
|   | 
|                 var path = propName + '.' + key; | 
|   | 
|                 if (propPathMap[path]) { | 
|                   continue; | 
|                 } | 
|                 /* eslint-enable max-depth */ | 
|   | 
|   | 
|                 propPathMap[path] = 1; | 
|                 animatingCount += self._animProp(state, propName, key, transitionCfg, animationDone); | 
|               } | 
|             } | 
|           } else { | 
|             if (propPathMap[propName]) { | 
|               continue; | 
|             } | 
|   | 
|             propPathMap[propName] = 1; // Animating particular property in style or style | 
|   | 
|             if (propName.match(styleShapeReg)) { | 
|               // remove 'style.', 'shape.' prefix | 
|               var subProp = propName.slice(0, 5); | 
|               propName = propName.slice(6); | 
|               animatingCount += self._animProp(state, subProp, propName, transitionCfg, animationDone); | 
|             } else { | 
|               animatingCount += self._animProp(state, '', propName, transitionCfg, animationDone); | 
|             } | 
|           } | 
|         } // No transition properties | 
|   | 
|   | 
|         if (animatingCount === 0) { | 
|           self.setState(target); | 
|           done && done(); | 
|         } | 
|       } else { | 
|         self.setState(target); | 
|         done && done(); | 
|       } | 
|     } | 
|   | 
|     var subStates = self._subStates; | 
|   | 
|     for (var i = 0; i < subStates.length; i++) { | 
|       subStates.transitionState(target); | 
|     } | 
|   }, | 
|   | 
|   /** | 
|    * Do transition animation of particular property | 
|    * @param {Object} state | 
|    * @param {string} subPropKey | 
|    * @param {string} key | 
|    * @param {Object} transitionCfg | 
|    * @param {Function} done | 
|    * @private | 
|    */ | 
|   _animProp: function (state, subPropKey, key, transitionCfg, done) { | 
|     var el = this._el; | 
|     var stateObj = subPropKey ? state[subPropKey] : state; | 
|     var elObj = subPropKey ? el[subPropKey] : el; | 
|     var availableProp = stateObj && key in stateObj && elObj && key in elObj; | 
|     var transitionAnimators = this._transitionAnimators; | 
|   | 
|     if (availableProp) { | 
|       var obj = {}; | 
|   | 
|       if (stateObj[key] === elObj[key]) { | 
|         return 0; | 
|       } | 
|   | 
|       obj[key] = stateObj[key]; | 
|       var animator = el.animate(subPropKey).when(transitionCfg.duration, obj).delay(transitionCfg.dealy).done(function () { | 
|         var idx = zrUtil.indexOf(transitionAnimators, 1); | 
|   | 
|         if (idx > 0) { | 
|           transitionAnimators.splice(idx, 1); | 
|         } | 
|   | 
|         done(); | 
|       }).start(transitionCfg.easing); | 
|       transitionAnimators.push(animator); | 
|       return 1; | 
|     } | 
|   | 
|     return 0; | 
|   }, | 
|   _stopTransition: function () { | 
|     var transitionAnimators = this._transitionAnimators; | 
|   | 
|     for (var i = 0; i < transitionAnimators.length; i++) { | 
|       transitionAnimators[i].stop(); | 
|     } | 
|   | 
|     transitionAnimators.length = 0; | 
|   }, | 
|   transiting: function () { | 
|     return this._transitionAnimators.length > 0; | 
|   }, | 
|   addSubStates: function (states) { | 
|     this._subStates.push(states); | 
|   }, | 
|   removeSubStates: function (states) { | 
|     var idx = zrUtil.indexOf(this._subStates, states); | 
|   | 
|     if (idx >= 0) { | 
|       this._subStates.splice(states, 1); | 
|     } | 
|   } | 
| }; | 
| var _default = GraphicStates; | 
| module.exports = _default; |