- /* eslint-disable no-multi-spaces */
- /* eslint-disable no-implicit-globals */
- /* eslint-disable userscripts/no-invalid-headers */
- /* eslint-disable userscripts/no-invalid-grant */
-
- // ==UserScript==
- // @name SettingPanel
- // @displayname SettingPanel
- // @namespace Wenku8++
- // @version 0.3.9
- // @description SettingPanel for wenku8++
- // @author PY-DNG
- // @license GPL-v3
- // @regurl NONE
- // @require https://gf.qytechs.cn/scripts/449412-basic-functions/code/Basic%20Functions.js?version=1085783
- // @require https://gf.qytechs.cn/scripts/449583-configmanager/code/ConfigManager.js?version=1085836
- // @grant none
- // ==/UserScript==
-
- /*
- 计划任务:
- [x] 表格线换成蓝色的
- [x] 允许使用不同的alertify对话框
- [x] 点击按钮的时候要有GUI反馈
- [ ] 未保存内容,关闭窗口前要有提示
- [ ] 提供注册(不可用)SettingOptions组件的接口
- */
-
- (function __MAIN__() {
- 'use strict';
-
- const ASSETS = require('assets');
- const alertify = require('alertify');
- const CONST = {
- Text: {
- Saved: '已保存',
- Reset: '已恢复到修改前'
- },
- Manager_Config_Ruleset: {
- 'version-key': 'config-version',
- 'ignores': ["LOCAL-CDN"],
- 'defaultValues': {
- //'config-key': {},
- }
- }
- };
- const SettingOptionElements = {
- 'string': {
- createElement: function() {const e = $CrE('input'); e.style.width = '90%'; return e;},
- setValue: function(val) {this.element.value = val;},
- getValue: function() {return this.element.value;},
- },
- 'number': {
- createElement: function() {const e = $CrE('input'); e.type = 'number'; e.style.width = '90%'; return e;},
- setValue: function (val) {this.element.value = val;},
- getValue: function() {return this.element.value;},
- },
- 'boolean': {
- createElement: function() {const e = $CrE('input'); e.type = 'checkbox'; return e;},
- setValue: function(val) {this.element.checked = val;},
- getValue: function(data) {return this.element.checked ? (this.hasOwnProperty('data') ? this.data.checked : true) : (data ? this.data.unchecked : false);},
- },
- 'select': {
- createElement: (() => {const e = $CrE('select'); this.hasOwnProperty('data') && this.data.forEach((d) => {const o = $CrE('option'); o.innerText = d; e.appendChild(o)}); return e;}),
- setValue: (val) => (Array.from(this.element.children).find((opt) => (opt.value === val)).selected = true),
- getValue: function() {return this.element.value;},
- }
- }
-
- // initialize
- alertify.dialog('setpanel', function factory(){
- return {
- // The dialog startup function
- // This will be called each time the dialog is invoked
- // For example: alertify.myDialog( data );
- main:function(){
- // Split arguments
- let content, header, buttons, onsave, onreset, onclose;
- switch (arguments.length) {
- case 1:
- switch (typeof arguments[0]) {
- case 'string':
- content = arguments[0];
- break;
- case 'object':
- arguments[0].hasOwnProperty('content') && (content = arguments[0].content);
- arguments[0].hasOwnProperty('header') && (header = arguments[0].header);
- arguments[0].hasOwnProperty('buttons') && (buttons = arguments[0].buttons);
- arguments[0].hasOwnProperty('onsave') && (onsave = arguments[0].onsave);
- arguments[0].hasOwnProperty('onreset') && (onreset = arguments[0].onreset);
- arguments[0].hasOwnProperty('onclose') && (buttons = arguments[0].onclose);
- break;
- default:
- Err('Arguments invalid', 1);
- }
- break;
- case 2:
- content = arguments[0];
- header = arguments[1];
- break;
- case 3:
- content = arguments[0];
- header = arguments[1];
- buttons = buttons[2];
- break;
- }
-
- // Prepare dialog
- this.resizeTo('80%', '80%');
- content && this.setContent(content);
- header && this.setHeader(header);
- onsave && this.set('onsave', onsave);
- onreset && this.set('onreset', onreset);
- onclose && this.set('onclose', onclose);
-
- // Choose & show selected button groups
- const btnGroups = {
- // Close button only
- basic: [[1, 0]],
-
- // Save & reset button
- saver: [[0, 0], [1, 1]]
- };
- const group = btnGroups[buttons || 'basic'];
- const divs = ['auxiliary', 'primary'];
- divs.forEach((div) => {
- Array.from(this.elements.buttons[div].children).forEach((btn) => {
- btn.style.display = 'none';
- });
- });
- group.forEach((button) => {
- this.elements.buttons[divs[button[0]]].children[button[1]].style.display = '';
- });
-
- return this;
- },
- // The dialog setup function
- // This should return the dialog setup object ( buttons, focus and options overrides ).
- setup:function(){
- return {
- /* buttons collection */
- buttons:[{
- /* button label */
- text: '恢复到修改前',
-
- /*bind a keyboard key to the button */
- key: undefined,
-
- /* indicate if closing the dialog should trigger this button action */
- invokeOnClose: false,
-
- /* custom button class name */
- className: alertify.defaults.theme.cancel,
-
- /* custom button attributes */
- attrs: {},
-
- /* Defines the button scope, either primary (default) or auxiliary */
- scope:'auxiliary',
-
- /* The will conatin the button DOMElement once buttons are created */
- element: undefined
- },{
- /* button label */
- text: '关闭',
-
- /*bind a keyboard key to the button */
- key: undefined,
-
- /* indicate if closing the dialog should trigger this button action */
- invokeOnClose: false,
-
- /* custom button class name */
- className: alertify.defaults.theme.ok,
-
- /* custom button attributes */
- attrs: {},
-
- /* Defines the button scope, either primary (default) or auxiliary */
- scope:'primary',
-
- /* The will conatin the button DOMElement once buttons are created */
- element: undefined
- },{
- /* button label */
- text: '保存',
-
- /*bind a keyboard key to the button */
- key: undefined,
-
- /* indicate if closing the dialog should trigger this button action */
- invokeOnClose: false,
-
- /* custom button class name */
- className: alertify.defaults.theme.ok,
-
- /* custom button attributes */
- attrs: {},
-
- /* Defines the button scope, either primary (default) or auxiliary */
- scope:'primary',
-
- /* The will conatin the button DOMElement once buttons are created */
- element: undefined
- }],
-
- /* default focus */
- focus:{
- /* the element to receive default focus, has differnt meaning based on value type:
- number: action button index.
- string: querySelector to select from dialog body contents.
- function: when invoked, should return the focus element.
- DOMElement: the focus element.
- object: an object that implements .focus() and .select() functions.
- */
- element: 0,
-
- /* indicates if the element should be selected on focus or not*/
- select: true
-
- },
- /* dialog options, these override the defaults */
- options: {
- title: 'Setting Panel',
- modal: true,
- basic: false,
- frameless: false,
- pinned: false,
- movable: true,
- moveBounded: false,
- resizable: true,
- autoReset: false,
- closable: true,
- closableByDimmer: true,
- maximizable: false,
- startMaximized: false,
- pinnable: false,
- transition: 'fade',
- padding: true,
- overflow: true,
- /*
- onshow:...,
- onclose:...,
- onfocus:...,
- onmove:...,
- onmoved:...,
- onresize:...,
- onresized:...,
- onmaximize:...,
- onmaximized:...,
- onrestore:...,
- onrestored:...
- */
- }
- };
- },
- // This will be called once the dialog DOM has been created, just before its added to the document.
- // Its invoked only once.
- build:function(){
-
- // Do custom DOM manipulation here, accessible via this.elements
-
- // this.elements.root ==> Root div
- // this.elements.dimmer ==> Modal dimmer div
- // this.elements.modal ==> Modal div (dialog wrapper)
- // this.elements.dialog ==> Dialog div
- // this.elements.reset ==> Array containing the tab reset anchor links
- // this.elements.reset[0] ==> First reset element (button).
- // this.elements.reset[1] ==> Second reset element (button).
- // this.elements.header ==> Dialog header div
- // this.elements.body ==> Dialog body div
- // this.elements.content ==> Dialog body content div
- // this.elements.footer ==> Dialog footer div
- // this.elements.resizeHandle ==> Dialog resize handle div
-
- // Dialog commands (Pin/Maximize/Close)
- // this.elements.commands ==> Object containing dialog command buttons references
- // this.elements.commands.container ==> Root commands div
- // this.elements.commands.pin ==> Pin command button
- // this.elements.commands.maximize ==> Maximize command button
- // this.elements.commands.close ==> Close command button
-
- // Dialog action buttons (Ok, cancel ... etc)
- // this.elements.buttons ==> Object containing dialog action buttons references
- // this.elements.buttons.primary ==> Primary buttons div
- // this.elements.buttons.auxiliary ==> Auxiliary buttons div
-
- // Each created button will be saved with the button definition inside buttons collection
- // this.__internal.buttons[x].element
-
- },
- // This will be called each time the dialog is shown
- prepare:function(){
- // Do stuff that should be done every time the dialog is shown.
- },
- // This will be called each time an action button is clicked.
- callback:function(closeEvent){
- //The closeEvent has the following properties
- //
- // index: The index of the button triggering the event.
- // button: The button definition object.
- // cancel: When set true, prevent the dialog from closing.
- const myEvent = deepClone(closeEvent);
- switch (closeEvent.index) {
- case 0: {
- // Rests button
- closeEvent.cancel = myEvent.cancel = true;
- myEvent.save = false;
- myEvent.reset = true;
- const onreset = this.get('onreset');
- typeof onreset === 'function' && onreset(myEvent);
- break;
- }
- case 1: {
- // Close button
- // Do something here if need
- break;
- }
- case 2: {
- // Save button
- closeEvent.cancel = myEvent.cancel = true;
- myEvent.save = true;
- myEvent.reset = false;
- const onsave = this.get('onsave');
- typeof onsave === 'function' && onsave(myEvent);
- }
- }
- this.get(myEvent.save ? 'saver' : 'reseter').call(this);
- closeEvent.cancel = myEvent.cancel;
- },
- // To make use of AlertifyJS settings API, group your custom settings into a settings object.
- settings:{
- onsave: function() {},
- onreset: function() {},
- options: [], // SettingOption array
- saver: function() {
- this.get('options').forEach((o) => (o.save()));
- },
- reseter: function() {
- this.get('options').forEach((o) => (o.reset()));
- }
- },
- // AlertifyJS will invoke this each time a settings value gets updated.
- settingUpdated:function(key, oldValue, newValue){
- // Use this to respond to specific setting updates.
- const _this = this;
- ['onsave', 'onreset', 'saver', 'reseter'].includes(key) && check('function');
- ['options'].includes(key) && check(Array);
-
- function rollback() {
- _this.set(key, oldValue);
- }
-
- function check(type) {
- valid(oldValue, type) && !valid(newValue, type) && rollback();
- }
-
- function valid(value, type) {
- return ({
- 'string': () => (typeof value === type),
- 'function': () => (value instanceof type)
- })[typeof type]();
- }
- },
- // listen to internal dialog events.
- hooks:{
- // triggered when the dialog is shown, this is seperate from user defined onshow
- onshow: function() {
- this.resizeTo('80%', '80%');
- },
- // triggered when the dialog is closed, this is seperate from user defined onclose
- onclose: function() {
- const onclose = this.get('onclose');
- typeof onclose === 'function' && onclose();
- },
- // triggered when a dialog option gets updated.
- // IMPORTANT: This will not be triggered for dialog custom settings updates ( use settingUpdated instead).
- onupdate: function() {
- }
- }
- }
- }, true);
-
- exports = {
- SettingPanel: SettingPanel,
- SettingOption: SettingOption,
- optionAvailable: optionAvailable,
- isOption: isOption,
- registerElement: registerElement,
- };
-
- // A table-based setting panel using alertify-js
- // For wenku8++ only version
- // Use 'new' keyword
- // Usage:
- /*
- var panel = new SettingPanel({
- buttons: 0,
- header: '',
- className: '',
- id: '',
- name: '',
- tables: [
- {
- className: '',
- id: '',
- name: '',
- rows: [
- {
- className: '',
- id: '',
- name: '',
- blocks: [
- {
- isHeader: false,
- width: '',
- height: '',
- innerHTML / innerText: ''
- colSpan: 1,
- rowSpan: 1,
- className: '',
- id: '',
- name: '',
- options: [SettingOption, ...]
- children: [HTMLElement, ...]
- },
- ...
- ]
- },
- ...
- ]
- },
- ...
- ]
- });
- */
- function SettingPanel(details={}, storage) {
- const SP = this;
- SP.insertTable = insertTable;
- SP.appendTable = appendTable;
- SP.removeTable = removeTable;
- SP.remove = remove;
- SP.PanelTable = PanelTable;
- SP.PanelRow = PanelRow;
- SP.PanelBlock = PanelBlock;
-
- // <div> element
- const elm = $CrE('div');
- copyProps(details, elm, ['id', 'name', 'className']);
- elm.classList.add('settingpanel-container');
-
- // Configure object
- let css='', usercss='';
- SP.element = elm;
- SP.elements = {};
- SP.children = {};
- SP.tables = [];
- SP.length = 0;
- details.id !== undefined && (SP.elements[details.id] = elm);
- copyProps(details, SP, ['id', 'name']);
- Object.defineProperty(SP, 'css', {
- configurable: false,
- enumerable: true,
- get: function() {
- return css;
- },
- set: function(_css) {
- addStyle(_css, 'settingpanel-css');
- css = _css;
- }
- });
- Object.defineProperty(SP, 'usercss', {
- configurable: false,
- enumerable: true,
- get: function() {
- return usercss;
- },
- set: function(_usercss) {
- addStyle(_usercss, 'settingpanel-usercss');
- usercss = _usercss;
- }
- });
- SP.css = `.settingpanel-table {border-spacing: 0px; border-collapse: collapse; width: 100%; margin: 2em 0;} .settingpanel-block {border: 1px solid ${ASSETS.Color.Text}; text-align: center; vertical-align: middle; padding: 3px; text-align: left;} .settingpanel-header {font-weight: bold;}`
-
- // Make alerity box
- const box = SP.alertifyBox = alertify.setpanel({
- onsave: function() {
- alertify.notify(CONST.Text.Saved);
- },
- onreset: function() {
- alertify.notify(CONST.Text.Reset);
- },
- buttons: details.hasOwnProperty('buttons') ? details.buttons : 'basic'
- });
- clearChildNodes(box.elements.content);
- box.elements.content.appendChild(elm);
- box.elements.content.style.overflow = 'auto';
- box.setHeader(details.header);
- box.setting({
- maximizable: true,
- overflow: true
- });
- !box.isOpen() && box.show();
-
- // Create tables
- if (details.tables) {
- for (const table of details.tables) {
- if (table instanceof PanelTable) {
- appendTable(table);
- } else {
- appendTable(new PanelTable(table));
- }
- }
- }
-
- // Insert a Panel-Row
- // Returns Panel object
- function insertTable(table, index) {
- // Insert table
- !(table instanceof PanelTable) && (table = new PanelTable(table));
- index < SP.length ? elm.insertBefore(table.element, elm.children[index]) : elm.appendChild(table.element);
- insertItem(SP.tables, table, index);
- table.id !== undefined && (SP.children[table.id] = table);
- SP.length++;
-
- // Set parent
- table.parent = SP;
-
- // Inherit elements
- for (const [id, subelm] of Object.entries(table.elements)) {
- SP.elements[id] = subelm;
- }
-
- // Inherit children
- for (const [id, child] of Object.entries(table.children)) {
- SP.children[id] = child;
- }
- return SP;
- }
-
- // Append a Panel-Row
- // Returns Panel object
- function appendTable(table) {
- return insertTable(table, SP.length);
- }
-
- // Remove a Panel-Row
- // Returns Panel object
- function removeTable(index) {
- const table = SP.tables[index];
- SP.element.removeChild(table.element);
- removeItem(SP.rows, index);
- return SP;
- }
-
- // Remove itself from parentElement
- // Returns Panel object
- function remove() {
- SP.element.parentElement && SP.parentElement.removeChild(SP.element);
- return SP;
- }
-
- // Panel-Table object
- // Use 'new' keyword
- function PanelTable(details={}) {
- const PT = this;
- PT.insertRow = insertRow;
- PT.appendRow = appendRow;
- PT.removeRow = removeRow;
- PT.remove = remove
-
- // <table> element
- const elm = $CrE('table');
- copyProps(details, elm, ['id', 'name', 'className']);
- elm.classList.add('settingpanel-table');
-
- // Configure
- PT.element = elm;
- PT.elements = {};
- PT.children = {};
- PT.rows = [];
- PT.length = 0;
- details.id !== undefined && (PT.elements[details.id] = elm);
- copyProps(details, PT, ['id', 'name']);
-
- // Append rows
- if (details.rows) {
- for (const row of details.rows) {
- if (row instanceof PanelRow) {
- insertRow(row);
- } else {
- insertRow(new PanelRow(row));
- }
- }
- }
-
- // Insert a Panel-Row
- // Returns Panel-Table object
- function insertRow(row, index) {
- // Insert row
- !(row instanceof PanelRow) && (row = new PanelRow(row));
- index < PT.length ? elm.insertBefore(row.element, elm.children[index]) : elm.appendChild(row.element);
- insertItem(PT.rows, row, index);
- row.id !== undefined && (PT.children[row.id] = row);
- PT.length++;
-
- // Set parent
- row.parent = PT;
-
- // Inherit elements
- for (const [id, subelm] of Object.entries(row.elements)) {
- PT.elements[id] = subelm;
- }
-
- // Inherit children
- for (const [id, child] of Object.entries(row.children)) {
- PT.children[id] = child;
- }
- return PT;
- }
-
- // Append a Panel-Row
- // Returns Panel-Table object
- function appendRow(row) {
- return insertRow(row, PT.length);
- }
-
- // Remove a Panel-Row
- // Returns Panel-Table object
- function removeRow(index) {
- const row = PT.rows[index];
- PT.element.removeChild(row.element);
- removeItem(PT.rows, index);
- return PT;
- }
-
- // Remove itself from parentElement
- // Returns Panel-Table object
- function remove() {
- PT.parent instanceof SettingPanel && PT.parent.removeTable(PT.tables.indexOf(PT));
- return PT;
- }
- }
-
- // Panel-Row object
- // Use 'new' keyword
- function PanelRow(details={}) {
- const PR = this;
- PR.insertBlock = insertBlock;
- PR.appendBlock = appendBlock;
- PR.removeBlock = removeBlock;
- PR.remove = remove;
-
- // <tr> element
- const elm = $CrE('tr');
- copyProps(details, elm, ['id', 'name', 'className']);
- elm.classList.add('settingpanel-row');
-
- // Configure object
- PR.element = elm;
- PR.elements = {};
- PR.children = {};
- PR.blocks = [];
- PR.length = 0;
- details.id !== undefined && (PR.elements[details.id] = elm);
- copyProps(details, PR, ['id', 'name']);
-
- // Append blocks
- if (details.blocks) {
- for (const block of details.blocks) {
- if (block instanceof PanelBlock) {
- appendBlock(block);
- } else {
- appendBlock(new PanelBlock(block));
- }
- }
- }
-
- // Insert a Panel-Block
- // Returns Panel-Row object
- function insertBlock(block, index) {
- // Insert block
- !(block instanceof PanelBlock) && (block = new PanelBlock(block));
- index < PR.length ? elm.insertBefore(block.element, elm.children[index]) : elm.appendChild(block.element);
- insertItem(PR.blocks, block, index);
- block.id !== undefined && (PR.children[block.id] = block);
- PR.length++;
-
- // Set parent
- block.parent = PR;
-
- // Inherit elements
- for (const [id, subelm] of Object.entries(block.elements)) {
- PR.elements[id] = subelm;
- }
-
- // Inherit children
- for (const [id, child] of Object.entries(block.children)) {
- PR.children[id] = child;
- }
- return PR;
- };
-
- // Append a Panel-Block
- // Returns Panel-Row object
- function appendBlock(block) {
- return insertBlock(block, PR.length);
- }
-
- // Remove a Panel-Block
- // Returns Panel-Row object
- function removeBlock(index) {
- const block = PR.blocks[index];
- PR.element.removeChild(block.element);
- removeItem(PR.blocks, index);
- return PR;
- }
-
- // Remove itself from parent
- // Returns Panel-Row object
- function remove() {
- PR.parent instanceof PanelTable && PR.parent.removeRow(PR.parent.rows.indexOf(PR));
- return PR;
- }
- }
-
- // Panel-Block object
- // Use 'new' keyword
- function PanelBlock(details={}) {
- const PB = this;
- PB.remove = remove;
-
- // <td> element
- const elm = $CrE(details.isHeader ? 'th' : 'td');
- copyProps(details, elm, ['innerText', 'innerHTML', 'colSpan', 'rowSpan', 'id', 'name', 'className']);
- copyProps(details, elm.style, ['width', 'height']);
- elm.classList.add('settingpanel-block');
- details.isHeader && elm.classList.add('settingpanel-header');
-
- // Configure object
- PB.element = elm;
- PB.elements = {};
- PB.children = {};
- details.id !== undefined && (PB.elements[details.id] = elm);
- copyProps(details, PB, ['id', 'name']);
-
- // Append to parent if need
- details.parent instanceof PanelRow && (PB.parent = details.parent.appendBlock(PB));
-
- // Append SettingOptions if exist
- if (details.options) {
- details.options.filter(storage ? () => (true) : isOption).map((o) => (isOption(o) ? o : new SettingOption(storage, o))).forEach(function(option) {
- SP.alertifyBox.get('options').push(option);
- elm.appendChild(option.element);
- });
- }
-
- // Append child elements if exist
- if (details.children) {
- for (const child of details.children) {
- elm.appendChild(child);
- }
- }
-
- // Remove itself from parent
- // Returns Panel-Block object
- function remove() {
- PB.parent instanceof PanelRow && PB.parent.removeBlock(PB.parent.blocks.indexOf(PB));
- return PB;
- }
- }
-
- function $R(e) {return $(e) && $(e).parentElement.removeChild($(e));}
- function insertItem(arr, item, index) {
- for (let i = arr.length; i > index ; i--) {
- arr[i] = arr[i-1];
- }
- arr[index] = item;
- return arr;
- }
- function removeItem(arr, index) {
- for (let i = index; i < arr.length-1; i++) {
- arr[i] = arr[i+1];
- }
- delete arr[arr.length-1];
- return arr;
- }
- function MakeReadonlyObj(val) {
- return isObject(val) ? new Proxy(val, {
- get: function(target, property, receiver) {
- return MakeReadonlyObj(target[property]);
- },
- set: function(target, property, value, receiver) {},
- has: function(target, prop) {}
- }) : val;
-
- function isObject(value) {
- return ['object', 'function'].includes(typeof value) && value !== null;
- }
- }
- }
-
- // details = {path='config path', type='config type', data='option data'}
- function SettingOption(storage, details={}) {
- const SO = this;
- SO.save = save;
- SO.reset = reset;
-
- // Initialize ConfigManager
- !storage && Err('SettingOption requires GM_storage functions');
- const CM = new ConfigManager(CONST.Manager_Config_Ruleset, storage);
- const CONFIG = CM.Config;
-
- // Get args
- const options = ['path', 'type', 'checker', 'data', 'autoSave'];
- copyProps(details, SO, options);
-
- // Get first available type if multiple types provided
- Array.isArray(SO.type) && (SO.type = SO.type.find((t) => (optionAvailable(t))));
- !optionAvailable(SO.type) && Err('Unsupported Panel-Option type: ' + details.type);
-
- // Create element
- const original_value = CM.getConfig(SO.path);
- const SOE = {
- create: SettingOptionElements[SO.type].createElement.bind(SO),
- get: SettingOptionElements[SO.type].getValue.bind(SO),
- set: SettingOptionElements[SO.type].setValue.bind(SO),
- }
- SO.element = SOE.create();
- SOE.set(original_value);
-
- // Bind change-checker-saver
- SO.element.addEventListener('change', function(e) {
- if (SO.checker) {
- if (SO.checker(e, SOE.get())) {
- SO.autoSave && save();
- } else {
- // Reset value
- reset();
-
- // Do some value-invalid reminding here
- }
- } else {
- SO.autoSave && save();
- }
- });
-
- function save() {
- CM.setConfig(SO.path, SOE.get());
- }
-
- function reset(save=false) {
- SOE.set(original_value);
- save && CM.setConfig(SO.path, original_value);
- }
- }
-
- // Check if an settingoption type available
- function optionAvailable(type) {
- return Object.keys(SettingOptionElements).includes(type);
- }
-
- // Register SettingOption element
- function registerElement(name, obj) {
- const formatOkay = typeof obj.createElement === 'function' && typeof obj.setValue === 'function' && typeof obj.getValue === 'function';
- const noConflict = !SettingOptionElements.hasOwnProperty(name);
- const okay = formatOkay && noConflict;
- okay && (SettingOptionElements[name] = obj);
- return okay;
- }
-
- function isOption(obj) {
- return obj instanceof SettingOption;
- }
-
- // Deep copy an object
- function deepClone(obj) {
- let newObj = Array.isArray(obj) ? [] : {};
- if (obj && typeof obj === "object") {
- for (let key in obj) {
- if (obj.hasOwnProperty(key)) {
- newObj[key] = (obj && typeof obj[key] === 'object') ? deepClone(obj[key]) : obj[key];
- }
- }
- }
- return newObj;
- }
- })();