您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
inheritance, mixins and other stuff, mainly to encapsulate objects
当前为
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://updategreasyfork.deno.dev/scripts/22752/145825/object-utils.js
// ==UserScript== // @name object-utils // @name:de object-utils // @namespace dannysaurus.camamba // @version 0.1 // @license MIT License // @description inheritance, mixins and other stuff, mainly to encapsulate objects // @description:de inheritance, mixins and other stuff, mainly to encapsulate objects // ==/UserScript== var LIB = LIB || {}; /** * * @type {{mixin, extend, TruthyFalsy, Truthy, Falsy, Keeper, BackingData, emptyFunction, emptyDate}} */ LIB.objectUtils = (function() { 'use strict'; /** * Extends an object from another object through the prototype chain * @param {Object} superObj - The object to be extended * @param {Object} obj - The object extending the superObj * @return {boolean} true, if executed successfully */ function _extend(superObj, obj){ if (typeof superObj === 'undefined') return false; if (typeof obj === 'undefined') return false; // save properties of prototype of the obj var descriptors = {}; Object.getOwnPropertyNames(obj.prototype).forEach(function(propName) { descriptors[propName] = Object.getOwnPropertyDescriptor(obj.prototype, propName); }); // create a new extended prototype obj.prototype = Object.create(superObj.prototype, descriptors); obj.prototype.constructor = obj; return true; } /** * Clones properties from one object to another. * A property only gets cloned if it does not yet exist in the target object. * @param {Object} receiver - the target object receiver the properties * @param {Object} supplier - the source object supplying the properties * @param {Array} [props] - names of the relevant properties * @return {boolean} true, if executed successfully */ function _mixin(receiver, supplier, props) { if (typeof supplier !== 'object' || typeof receiver !== 'object') { return false; } var propNames = Array.isArray(props) ? props : Object.getOwnPropertyNames(supplier); propNames.forEach(function(propName) { if (!Object.prototype.hasOwnProperty.call(receiver, propName)) { var desc = Object.getOwnPropertyDescriptor(supplier, propName); if (typeof desc !== 'undefined') { Object.defineProperty(receiver, propName, desc); } } }); return true; } /** * Has an object extending a super object by their prototype objects. * @param {Object} obj - The extending object * @return {{from: extendFrom, mixWith: mixWith}} */ var extend = function(obj) { /** * Clones the prototype properties of the super object in the target object if the property does not yet exist there. * @param {Object} superObj - the source object supplying the properties * @param {Array} [props] - names of the relevant properties * @return {{thenWith: mixWith}} */ var mixWith = function(superObj, props) { if (_mixin(obj.prototype, superObj.prototype, props)){ return { thenWith : mixWith }; } }; /** * Extends the super object from the prototype chain * @param superObj The super object to be extended * @return {{mixWith: mixWith}} */ var extendFrom = function (superObj) { if (_extend(superObj, obj)) { return { mixWith : mixWith }; } }; return { from : extendFrom, mixWith : mixWith }; }; /** * Defines an object that holds a value and specific values. * In case the value is a Truthy, Falsy or undefined it is overwritten with specific values. * @param value The common value. * @param [ifTruthy] if not undefined it overwrites value when it is a Truthy * @param [ifFalsy] if not undefined it overwrites value when it is a Falsy * @param [ifUndefined] if not undefined it overwrites value when it is undefined * @returns {TruthyFalsy} * @constructor */ function TruthyFalsy(value, ifTruthy, ifFalsy, ifUndefined) { if (!this instanceof TruthyFalsy) { return new TruthyFalsy(value, ifTruthy, ifFalsy, ifUndefined); } this.value = value; if (ifTruthy !== 'undefined') { this.ifTruthy = ifTruthy; } if (ifFalsy !== 'undefined') { this.ifFalsy = ifFalsy; } if (ifUndefined !== 'undefined') { this.ifUndefined = ifUndefined; } } TruthyFalsy.prototype = { constructor : TruthyFalsy, valueOf: function() { var result = this.value; if (typeof this.ifUndefined !== "undefined" && typeof result === "undefined") { result = this.ifUndefined; } else if (typeof this.ifTruthy !== "undefined" && result) { result = this.ifTruthy; } else if (typeof this.ifFalsy !== "undefined" && result) { result = this.ifFalsy; } return result; }, toString: function() { return String(TruthyFalsy.prototype.valueOf.call(this)); } }; function Falsy(value, ifFalsy, ifUndefined) { if (!(this instanceof Falsy)) { return new Falsy(value, ifFalsy, ifUndefined); } TruthyFalsy.call(this, value, undefined, ifFalsy, ifUndefined); } extend(Falsy).from(TruthyFalsy); function Truthy(value, ifTruthy, ifUndefined) { if (!(this instanceof Truthy)) { return new Truthy(value, ifTruthy, ifUndefined); } TruthyFalsy.call(this, value, ifTruthy, undefined, ifUndefined); } extend(Truthy).from(TruthyFalsy); /** * Keeps elements assigned by an index * @param {number} [initialCapacity] - initial size of the array used to store the elements * @constructor */ function Keeper(initialCapacity) { if (!(this instanceof Keeper)) { return new Keeper(initialCapacity); } var _store = new Array(initialCapacity || 16); var _pointer = 0; Object.defineProperties(this, { store: { get: function() { return _store }, configurable: false, enumerable: false }, pointer: { get: function() { return _pointer }, configurable: false, enumerable: false } }) } Keeper.prototype = { constructor: Keeper, /** * Adds a new element * @param {*|Object} item - The element to keep. Must not be type of <code>undefined</code>. * @return {number} The index (key) of the added element.<br> * <code>-1</code> if the element could not be added. */ push: function(item) { if (typeof item !== 'undefined') { var index = this.pointer; this.store[index] = item; for (this.pointer = index + 1; this.pointer <= this.store.length; this.pointer++) { if (typeof this.store[pointer] === 'undefined') { break; } } if (this.pointer === this.store.length) { this.store.length *= 2; } return index; } return -1; }, /** * Removes an element. * @param index The index (key) of the element to be removed. * @return {boolean} <code>true</code> if the element could be removed successfully */ remove: function(index) { if (index + 1 >= this.store.length) { this.store[index] = undefined; this.pointer = index; return true; } return false; }, /** * Gets the element with the specified index * @param {number} index - Index (key) of the element * @return {*|Object} the element with the specified index or <code>undefined</code> */ get: function (index) { if (index + 1 <= this.store.length) { return this.store[index]; } return undefined; } }; /** * This callback is displayed as part of the Requester class. * @callback OnSetCallback * @param {*} oldValue - old value * @param {*} newValue - new value to be set */ /** * Defines an accessor properties * @param {Object} sourceNode - the source object with the defining property * @param {Object} targetNode - the target object to define the accessor property * @param {string} propertyName - the name of the property which must exist on the <code>sourceNode</code> * @param {OnSetCallback} [onSetCallback] - called when the property gets assigned a value on the <code>targetNode</code> */ var definePropRecursive = function(sourceNode, targetNode, propertyName, onSetCallback) { if (typeof onSetCallback !== "function") { onSetCallback = emptyFunction; } var srcProperty = sourceNode[propertyName]; var isNode = (srcProperty !== null && typeof srcProperty === 'object' && Object.getPrototypeOf(srcProperty) === Object.prototype); if (isNode) { var childrenPropertyNames = Object.keys(srcProperty); var targetProperty = targetNode[propertyName] = {}; childrenPropertyNames.forEach(function(childPropName) { definePropRecursive(srcProperty, targetProperty, childPropName, onSetCallback); }); } else { Object.defineProperty(targetNode, propertyName, { enumerable: true, configurable: true, get: function() { return sourceNode[propertyName]; }, set: function(value) { onSetCallback(sourceNode[propertyName], value); sourceNode[propertyName] = value; } }); } }; /** * Backing data that can be stored in the userscript database. * intially defines getter and setter on the front * @param {Object} frontObject - the object to define the properties on according to <code>dataObject</code> * @param {Object} dataObject - the Object defining the backing data structure and initial values * @param {string} dbKey - key for storing the data in the userscript db * @param {OnSetCallback} [onSet] - function called when a property of <code>dataObject</code> gets assigned a value through the <code>frontObject</code> * @returns {BackingData} * @constructor */ function BackingData(frontObject, dataObject, dbKey, onSet) { if (!(this instanceof BackingData)) { return new BackingData(frontObject, dataObject, dbKey, onSet); } _mixin(this, dataObject); Object.defineProperty(this, "dbKey", { enumerable: false, configurable: true, writable: true, value: dbKey }); Object.keys(this).forEach(function(propName) { definePropRecursive(this, frontObject, propName, onSet); }, this); } BackingData.prototype = { constructor: BackingData, load: function() { var loadedObjSerialized = GM_getValue(this.dbKey); if (typeof loadedObjSerialized === "undefined") { return false; } var loadedObj = JSON.parse(loadedObjSerialized); Object.keys(this).forEach(function(propName) { var prop = loadedObj[propName]; if (typeof prop !== "undefined") { this[propName] = prop; } }, this); return true; }, save: function() { var savingObj = {}; _mixin(savingObj, this); var savingObjSerialized = JSON.stringify(savingObj); GM_setValue(this.dbKey, savingObjSerialized); }, remove: function() { GM_deleteValue(this.dbKey); } }; /** placeholder for empty callbacks */ function emptyFunction() {} /** placeholder for an undefined date */ var emptyDate = new Date('invalid'); return { mixin: _mixin, extend: extend, TruthyFalsy: TruthyFalsy, Truthy: Truthy, Falsy: Falsy, Keeper: Keeper, BackingData: BackingData, get emptyFunction() { return emptyFunction }, get emptyDate() { return emptyDate } }; })();