undoo
Version:
Undo/redo manager
838 lines (720 loc) • 26.2 kB
JavaScript
// [AIV] Undoo Build version: 0.5.0
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if(typeof define === 'function' && define.amd)
define("Undoo", [], factory);
else if(typeof exports === 'object')
exports["Undoo"] = factory();
else
root["Undoo"] = factory();
})(typeof self !== 'undefined' ? self : this, function() {
return /******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, {
/******/ configurable: false,
/******/ enumerable: true,
/******/ get: getter
/******/ });
/******/ }
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
module.exports = __webpack_require__(1);
/***/ }),
/* 1 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var extend = __webpack_require__(2);
var isEqual = __webpack_require__(4);
/**
* @class
*/
var Undoo = function () {
/**
* Create instance
* @param [opts] {Object} configuration object
* @param [opts.provider=null] {Function} optional function called on save that returns new state for history
* @param [opts.maxLength=20] {number} max length history
*/
function Undoo(opts) {
_classCallCheck(this, Undoo);
Object.defineProperties(this, {
_opts: {
writable: true
},
_history: {
writable: true
},
_position: {
writable: true
},
_initialState: {
writable: true
},
_onUpdate: {
writable: true,
value: function value() {}
},
_onBeforeSave: {
writable: true,
value: function value() {}
},
_onMaxLength: {
writable: true,
value: function value() {}
},
_isExceeded: {
writable: true,
value: false
},
_suspendSave: {
writable: true,
value: false
}
});
this._opts = extend.copy(opts, {
provider: null,
maxLength: 20
});
this._initiliaze();
}
/**
* @ignore
* @private
*/
_createClass(Undoo, [{
key: '_initiliaze',
value: function _initiliaze() {
this._initialState = undefined;
this._history = [];
this._isExceeded = false;
this._position = 0;
}
/**
* @ignore
* @private
*/
}, {
key: '_checkMaxLength',
value: function _checkMaxLength() {
if (this._history.length > this._opts.maxLength) {
this._history = this._history.slice(1, this._history.length);
if (!this._isExceeded) {
this._onMaxLength.call(null, this.current(), this.history(), this);
this._isExceeded = true;
}
} else {
this._isExceeded = false;
}
}
/**
*
* @param item
* @param beforeSave
* @returns {boolean|*}
* @private
* @ignore
*/
}, {
key: '_rejectSave',
value: function _rejectSave(item, beforeSave) {
return isEqual(item, this.current()) || beforeSave === false || this._suspendSave;
}
/**
* Check if undo is available
* @returns {boolean}
*/
}, {
key: 'canUndo',
value: function canUndo() {
return this._position > 1;
}
/**
* @Check if redo is available
* @returns {boolean}
*/
}, {
key: 'canRedo',
value: function canRedo() {
return this._position < this._history.length;
}
/**
* ignore
* @param callback
* @private
*/
}, {
key: 'import',
/**
* Import external history
* @param history {Array}
* @returns {Undoo}
*/
value: function _import() {
var history = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
if (!Array.isArray(history)) throw new TypeError('Items must be an array');
this._initiliaze();
this._history = history;
this._position = this._history.length;
this._initialState = history[0];
return this;
}
/**
* Get history
* @returns {Array}
*/
}, {
key: 'history',
value: function history() {
return this._history;
}
/**
* Save history
* @param [item] {*}
* @returns {Undoo}
*/
}, {
key: 'save',
value: function save(item) {
if (typeof item === 'undefined' && typeof this._opts.provider === 'function') item = this._opts.provider();
var beforeSave = this._onBeforeSave.call(null, item, this);
item = beforeSave || item;
if (this._rejectSave(item, beforeSave)) return this;
if (this._position < this._history.length) this._history = this._history.slice(0, this._position);
if (typeof item !== 'undefined') {
this._history.push(item);
if (this._initialState === undefined) this._initialState = item;
}
this._checkMaxLength();
this._position = this._history.length;
this._onUpdate.call(null, this.current(), 'save', this.history(), this);
return this;
}
/**
* Suspend save method
* @param [state=true] {boolean}
* @returns {Undoo}
*/
}, {
key: 'suspendSave',
value: function suspendSave() {
var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
this._suspendSave = state;
return this;
}
/**
* Check if save is allowed
* @returns {boolean}
*/
}, {
key: 'allowedSave',
value: function allowedSave() {
return !this._suspendSave;
}
/**
* Clear history
* @returns {Undoo}
*/
}, {
key: 'clear',
value: function clear() {
this._initiliaze();
this._onUpdate.call(null, null, 'clear', this.history(), this);
return this;
}
/**
* undo callback
* @callback Undoo~undoCallback
* @param item {*} current history item
*/
/**
* Undo
* @param [callback] {Undoo~undoCallback} callback function
* @returns {Undoo}
*/
}, {
key: 'undo',
value: function undo(callback) {
if (this.canUndo()) {
this._position--;
if (typeof callback === 'function') callback(this.current());
this._onUpdate.call(null, this.current(), 'undo', this.history(), this);
}
return this;
}
/**
* redo callback
* @callback Undoo~redoCallback
* @param item {*} current history item
*/
/**
* Redo
* @param [callback] {Undoo~redoCallback} callback function
* @returns {Undoo}
*/
}, {
key: 'redo',
value: function redo(callback) {
if (this.canRedo()) {
this._position++;
if (typeof callback === 'function') callback(this.current());
this._onUpdate.call(null, this.current(), 'redo', this.history(), this);
}
return this;
}
/**
* Get current item in history
* @returns {*}
*/
}, {
key: 'current',
value: function current() {
return this._history.length ? this._history[this._position - 1] : null;
}
/**
* Count history items, the first element is not considered
* @returns {number}
*/
}, {
key: 'count',
value: function count() {
return this._history.length ? this._history.length - 1 : 0;
}
/**
* Get initial state history
* @returns {*}
*/
}, {
key: 'initialState',
value: function initialState() {
return this._initialState;
}
/**
* onUpdate callback
* @callback Undoo~updateCallback
* @param item {*} current history item
* @param action {string} action that has called update event. Can be: redo, undo, save, clear
* @param history {Array} history array
* @param istance {Undoo}
*/
/**
* Triggered when history is updated
* @param callback {Undoo~updateCallback} callback function
* @returns {Undoo}
*/
}, {
key: 'onUpdate',
value: function onUpdate(callback) {
Undoo.callbackError(callback);
this._onUpdate = callback;
return this;
}
/**
* onMaxLength callback
* @callback Undoo~maxLengthCallback
* @param item {*} current history item
* @param history {Array} history array
* @param istance {Undoo}
*/
/**
* Triggered when maxLength is exceeded
* @param callback {Undoo~maxLengthCallback} callback function
* @returns {Undoo}
*/
}, {
key: 'onMaxLength',
value: function onMaxLength(callback) {
Undoo.callbackError(callback);
this._onMaxLength = callback;
return this;
}
/**
* onBeforeSave callback
* @callback Undoo~beforeSaveCallback
* @param item {*} current history item
* @param istance {Undoo}
*/
/**
* Triggered before save
* @param callback {Undoo~beforeSaveCallback} callback function
* @returns {Undoo}
* @example
* // If callback returns `false` the save command will not be executed
* myHistory.onBeforeSave(()=>false)
*
* // You can overwrite item before save
* myHistory.onBeforeSave((item)=>{
* return item.toUpperCase();
* })
*/
}, {
key: 'onBeforeSave',
value: function onBeforeSave(callback) {
Undoo.callbackError(callback);
this._onBeforeSave = callback;
return this;
}
}], [{
key: 'callbackError',
value: function callbackError(callback) {
if (typeof callback !== 'function') throw new TypeError('callback must be a function');
}
}]);
return Undoo;
}();
module.exports = Undoo;
/***/ }),
/* 2 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* WEBPACK VAR INJECTION */(function(module) {var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;
var _typeof2 = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
// [AIV] Defaulty Build version: 2.1.0
(function webpackUniversalModuleDefinition(root, factory) {
if (( false ? 'undefined' : _typeof2(exports)) === 'object' && ( false ? 'undefined' : _typeof2(module)) === 'object') module.exports = factory();else if (true) !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory),
__WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ?
(__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__),
__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));else if ((typeof exports === 'undefined' ? 'undefined' : _typeof2(exports)) === 'object') exports["defaulty"] = factory();else root["defaulty"] = factory();
})(typeof self !== 'undefined' ? self : undefined, function () {
return (/******/function (modules) {
// webpackBootstrap
/******/ // The module cache
/******/var installedModules = {};
/******/
/******/ // The require function
/******/function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/if (installedModules[moduleId]) {
/******/return installedModules[moduleId].exports;
/******/
}
/******/ // Create a new module (and put it into the cache)
/******/var module = installedModules[moduleId] = {
/******/i: moduleId,
/******/l: false,
/******/exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/module.l = true;
/******/
/******/ // Return the exports of the module
/******/return module.exports;
/******/
}
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/__webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/__webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/__webpack_require__.d = function (exports, name, getter) {
/******/if (!__webpack_require__.o(exports, name)) {
/******/Object.defineProperty(exports, name, {
/******/configurable: false,
/******/enumerable: true,
/******/get: getter
/******/ });
/******/
}
/******/
};
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/__webpack_require__.n = function (module) {
/******/var getter = module && module.__esModule ?
/******/function getDefault() {
return module['default'];
} :
/******/function getModuleExports() {
return module;
};
/******/__webpack_require__.d(getter, 'a', getter);
/******/return getter;
/******/
};
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/__webpack_require__.o = function (object, property) {
return Object.prototype.hasOwnProperty.call(object, property);
};
/******/
/******/ // __webpack_public_path__
/******/__webpack_require__.p = "";
/******/
/******/ // Load entry module and return exports
/******/return __webpack_require__(__webpack_require__.s = 0);
/******/
}(
/************************************************************************/
/******/[
/* 0 */
/***/function (module, exports, __webpack_require__) {
"use strict";
module.exports = __webpack_require__(1);
/***/
},
/* 1 */
/***/function (module, exports, __webpack_require__) {
"use strict";
var _typeof = typeof Symbol === "function" && _typeof2(Symbol.iterator) === "symbol" ? function (obj) {
return typeof obj === 'undefined' ? 'undefined' : _typeof2(obj);
} : function (obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj === 'undefined' ? 'undefined' : _typeof2(obj);
};
var deepCopy = __webpack_require__(2);
/**
* Copies deep missing properties to the target object
* @param targetObj {Object} target object
* @param defaultObj {Object} default object
* @param exclude {Array} exclude properties from copy
* @returns {*}
*/
var defaulty = function defaulty(targetObj, defaultObj) {
var exclude = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];
for (var i in defaultObj) {
/* istanbul ignore else */
if (defaultObj.hasOwnProperty(i) && exclude.indexOf(i) === -1) {
if (!targetObj.hasOwnProperty(i) || typeof targetObj[i] === 'undefined') {
targetObj[i] = defaultObj[i];
} else if (_typeof(targetObj[i]) === 'object') {
defaulty(targetObj[i], defaultObj[i]);
}
}
}
return targetObj;
};
/**
* Creates new target object and copies deep missing properties to the target object
* @param args[0] {Object} target object
* @param args[1] {Object} default object
* @param args[2] {Array} exclude properties from copy
* @returns {*}
*/
var copy = function copy() {
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
//args[0] = Object.assign({}, args[0]);
args[0] = deepCopy(args[0]);
return defaulty.apply(undefined, args);
};
module.exports = defaulty;
module.exports.copy = copy;
/***/
},
/* 2 */
/***/function (module, exports, __webpack_require__) {
"use strict";
var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_RESULT__;
var _typeof = typeof Symbol === "function" && _typeof2(Symbol.iterator) === "symbol" ? function (obj) {
return typeof obj === 'undefined' ? 'undefined' : _typeof2(obj);
} : function (obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj === 'undefined' ? 'undefined' : _typeof2(obj);
};
;(function (name, root, factory) {
if ((false ? 'undefined' : _typeof(exports)) === 'object') {
module.exports = factory();
}
/* istanbul ignore next */
else if (true) {
!(__WEBPACK_AMD_DEFINE_FACTORY__ = factory, __WEBPACK_AMD_DEFINE_RESULT__ = typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? __WEBPACK_AMD_DEFINE_FACTORY__.call(exports, __webpack_require__, exports, module) : __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
} else {
root[name] = factory();
}
})('dcopy', undefined, function () {
/**
* Deep copy objects and arrays
*
* @param {Object/Array} target
* @return {Object/Array} copy
* @api public
*/
return function (target) {
if (/number|string|boolean/.test(typeof target === 'undefined' ? 'undefined' : _typeof(target))) {
return target;
}
if (target instanceof Date) {
return new Date(target.getTime());
}
var copy = target instanceof Array ? [] : {};
walk(target, copy);
return copy;
function walk(target, copy) {
for (var key in target) {
var obj = target[key];
if (obj instanceof Date) {
var value = new Date(obj.getTime());
add(copy, key, value);
} else if (obj instanceof Function) {
var value = obj;
add(copy, key, value);
} else if (obj instanceof Array) {
var value = [];
var last = add(copy, key, value);
walk(obj, last);
} else if (obj instanceof Object) {
var value = {};
var last = add(copy, key, value);
walk(obj, last);
} else {
var value = obj;
add(copy, key, value);
}
}
}
};
/**
* Adds a value to the copy object based on its type
*
* @api private
*/
function add(copy, key, value) {
if (copy instanceof Array) {
copy.push(value);
return copy[copy.length - 1];
} else if (copy instanceof Object) {
copy[key] = value;
return copy[key];
}
}
});
/***/
}]
/******/)
);
});
/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(3)(module)))
/***/ }),
/* 3 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
module.exports = function (module) {
if (!module.webpackPolyfill) {
module.deprecate = function () {};
module.paths = [];
// module.parent = undefined by default
if (!module.children) module.children = [];
Object.defineProperty(module, "loaded", {
enumerable: true,
get: function get() {
return module.l;
}
});
Object.defineProperty(module, "id", {
enumerable: true,
get: function get() {
return module.i;
}
});
module.webpackPolyfill = 1;
}
return module;
};
/***/ }),
/* 4 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
module.exports = function equal(a, b) {
if (a === b) return true;
var arrA = Array.isArray(a),
arrB = Array.isArray(b),
i;
if (arrA && arrB) {
if (a.length != b.length) return false;
for (i = 0; i < a.length; i++) {
if (!equal(a[i], b[i])) return false;
}return true;
}
if (arrA != arrB) return false;
if (a && b && (typeof a === 'undefined' ? 'undefined' : _typeof(a)) === 'object' && (typeof b === 'undefined' ? 'undefined' : _typeof(b)) === 'object') {
var keys = Object.keys(a);
if (keys.length !== Object.keys(b).length) return false;
var dateA = a instanceof Date,
dateB = b instanceof Date;
if (dateA && dateB) return a.getTime() == b.getTime();
if (dateA != dateB) return false;
var regexpA = a instanceof RegExp,
regexpB = b instanceof RegExp;
if (regexpA && regexpB) return a.toString() == b.toString();
if (regexpA != regexpB) return false;
for (i = 0; i < keys.length; i++) {
if (!Object.prototype.hasOwnProperty.call(b, keys[i])) return false;
}for (i = 0; i < keys.length; i++) {
if (!equal(a[keys[i]], b[keys[i]])) return false;
}return true;
}
return false;
};
/***/ })
/******/ ]);
});