UNPKG

kmap-ui

Version:

A components of zmap base on vue2.X

344 lines (311 loc) 9.66 kB
import ol_ext_inherits from '../util/ext' import ol_interaction_Interaction from 'ol/interaction/Interaction' import ol_layer_Vector from 'ol/layer/Vector' import {unByKey as ol_Observable_unByKey} from 'ol/Observable' import '../source/Vector' /** Undo/redo interaction * @constructor * @extends {ol_interaction_Interaction} * @fires undo * @fires redo * @param {*} options */ var ol_interaction_UndoRedo = function(options) { if (!options) options = {}; ol_interaction_Interaction.call(this, { handleEvent: function() { return true; } }); this._undoStack = []; this._redoStack = []; // Block counter this._block = 0; // Start recording this._record = true; // Custom definitions this._defs = {}; }; ol_ext_inherits(ol_interaction_UndoRedo, ol_interaction_Interaction); /** Add a custom undo/redo * @param {string} action the action key name * @param {function} undoFn function called when undoing * @param {function} redoFn function called when redoing * @api */ ol_interaction_UndoRedo.prototype.define = function(action, undoFn, redoFn) { this._defs['_'+action] = { undo: undoFn, redo: redoFn }; }; /** Set a custom undo/redo * @param {string} action the action key name * @param {any} prop an object that will be passed in the undo/redo fucntions of the action * @return {boolean} true if the action is defined */ ol_interaction_UndoRedo.prototype.push = function(action, prop) { if (this._defs['_'+action]) { this._undoStack.push({type: '_'+action, prop: prop }); return true; } else { return false; } }; /** Activate or deactivate the interaction, ie. records or not events on the map. * @param {boolean} active * @api stable */ ol_interaction_UndoRedo.prototype.setActive = function(active) { ol_interaction_Interaction.prototype.setActive.call (this, active); this._record = active; }; /** * Remove the interaction from its current map, if any, and attach it to a new * map, if any. Pass `null` to just remove the interaction from the current map. * @param {ol.Map} map Map. * @api stable */ ol_interaction_UndoRedo.prototype.setMap = function(map) { if (this._mapListener) { this._mapListener.forEach(function(l) { ol_Observable_unByKey(l); }) } this._mapListener = []; ol_interaction_Interaction.prototype.setMap.call (this, map); // Watch blocks if (map) { this._mapListener.push(map.on('undoblockstart', this.blockStart.bind(this))); this._mapListener.push(map.on('undoblockend', this.blockEnd.bind(this))); } // Watch sources this._watchSources(); this._watchInteractions(); }; /** Watch for changes in the map sources * @private */ ol_interaction_UndoRedo.prototype._watchSources = function() { var map = this.getMap(); // Clear listeners if (this._sourceListener) { this._sourceListener.forEach(function(l) { ol_Observable_unByKey(l); }) } this._sourceListener = []; // Ges vector layers function getVectorLayers(layers, init) { if (!init) init = []; layers.forEach(function(l) { if (l instanceof ol_layer_Vector) { init.push(l); } else if (l.getLayers) { getVectorLayers(l.getLayers(), init); } }); return init; } if (map) { // Watch the vector sources in the map var vectors = getVectorLayers(map.getLayers()); vectors.forEach((function(l) { var s = l.getSource(); this._sourceListener.push( s.on(['addfeature', 'removefeature'], this._onAddRemove.bind(this)) ); this._sourceListener.push( s.on('clearstart', this.blockStart.bind(this)) ); this._sourceListener.push( s.on('clearend', this.blockEnd.bind(this)) ); }).bind(this)); // Watch new inserted/removed this._sourceListener.push( map.getLayers().on(['add', 'remove'], this._watchSources.bind(this) ) ); } }; /** Watch for interactions * @private */ ol_interaction_UndoRedo.prototype._watchInteractions = function() { var map = this.getMap(); // Clear listeners if (this._interactionListener) { this._interactionListener.forEach(function(l) { ol_Observable_unByKey(l); }) } this._interactionListener = []; if (map) { // Watch the interactions in the map map.getInteractions().forEach((function(i) { this._interactionListener.push(i.on( ['setattributestart', 'modifystart', 'rotatestart', 'translatestart', 'scalestart', 'deletestart', 'deleteend', 'beforesplit', 'aftersplit'], this._onInteraction.bind(this) )); }).bind(this)); // Watch new inserted / unwatch removed this._interactionListener.push( map.getInteractions().on( ['add', 'remove'], this._watchInteractions.bind(this) )); } }; /** A feature is added / removed */ ol_interaction_UndoRedo.prototype._onAddRemove = function(e) { if (this._record) { this._undoStack.push({type: e.type, source: e.target, feature: e.feature }); this._redoStack = []; } }; ol_interaction_UndoRedo.prototype._onInteraction = function(e) { var fn = this._onInteraction[e.type]; if (fn) fn.call(this,e); }; /** Set attribute * @private */ ol_interaction_UndoRedo.prototype._onInteraction.setattributestart = function(e) { this.blockStart(); var newp = Object.assign({}, e.properties); e.features.forEach(function(f) { var oldp = {}; for (var p in newp) { oldp[p] = f.get(p); } this._undoStack.push({ type: 'changeattribute', feature: f, newProperties: newp, oldProperties: oldp }); }.bind(this)); this.blockEnd(); }; ol_interaction_UndoRedo.prototype._onInteraction.rotatestart = ol_interaction_UndoRedo.prototype._onInteraction.translatestart = ol_interaction_UndoRedo.prototype._onInteraction.scalestart = ol_interaction_UndoRedo.prototype._onInteraction.modifystart = function (e) { this.blockStart(); e.features.forEach(function(m) { this._undoStack.push({type: 'changefeature', feature: m, oldFeature: m.clone() }); }.bind(this)); this.blockEnd(); }; /** Start an undo block * @api */ ol_interaction_UndoRedo.prototype.blockStart = function () { this._undoStack.push({ type: 'blockstart' }); this._redoStack = []; }; /** @private */ ol_interaction_UndoRedo.prototype._onInteraction.beforesplit = ol_interaction_UndoRedo.prototype._onInteraction.deletestart = ol_interaction_UndoRedo.prototype.blockStart; /** End an undo block * @api */ ol_interaction_UndoRedo.prototype.blockEnd = function () { this._undoStack.push({ type: 'blockend' }); }; /** @private */ ol_interaction_UndoRedo.prototype._onInteraction.aftersplit = ol_interaction_UndoRedo.prototype._onInteraction.deleteend = ol_interaction_UndoRedo.prototype.blockEnd; /** handle undo/redo * @private */ ol_interaction_UndoRedo.prototype._handleDo = function(e, undo) { // Not active if (!this.getActive()) return; // Stop recording while undoing this._record = false; switch (e.type) { case 'addfeature': { if (undo) e.source.removeFeature(e.feature); else e.source.addFeature(e.feature); break; } case 'removefeature': { if (undo) e.source.addFeature(e.feature); else e.source.removeFeature(e.feature); break; } case 'changefeature': { var geom = e.feature.getGeometry(); e.feature.setGeometry(e.oldFeature.getGeometry()); e.oldFeature.setGeometry(geom); break; } case 'changeattribute': { var newp = e.newProperties; var oldp = e.oldProperties; for (var p in oldp) { if (oldp === undefined) e.feature.unset(p); else e.feature.set(p, oldp[p]); } e.oldProperties = newp; e.newProperties = oldp; break; } case 'blockstart': { this._block += undo ? -1 : 1; break; } case 'blockend': { this._block += undo ? 1 : -1; break; } default: { if (this._defs[e.type]) { if (undo) this._defs[e.type].undo(e.prop); else this._defs[e.type].redo(e.prop); } else { console.warn('[UndoRedoInteraction]: "'+e.type.substr(1)+'" is not defined.'); } } } // Handle block if (this._block<0) this._block = 0; if (this._block) { if (undo) this.undo(); else this.redo(); } this._record = true; // Dispatch event this.dispatchEvent( { type: undo ? 'undo' : 'redo', action: e }); }; /** Undo last operation * @api */ ol_interaction_UndoRedo.prototype.undo = function() { var e = this._undoStack.pop(); if (!e) return; this._redoStack.push(e); this._handleDo(e, true); }; /** Redo last operation * @api */ ol_interaction_UndoRedo.prototype.redo = function() { var e = this._redoStack.pop(); if (!e) return; this._undoStack.push(e); this._handleDo(e, false); }; /** Clear undo stack * @api */ ol_interaction_UndoRedo.prototype.clear = function() { this._undoStack = []; this._redoStack = []; }; /** Check if undo is avaliable * @return {number} the number of undo * @api */ ol_interaction_UndoRedo.prototype.hasUndo = function() { return this._undoStack.length; }; /** Check if redo is avaliable * @return {number} the number of redo * @api */ ol_interaction_UndoRedo.prototype.hasRedo = function() { return this._redoStack.length; }; export default ol_interaction_UndoRedo