devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
663 lines (548 loc) • 21.8 kB
JavaScript
"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; };
var Config = require("./config"),
domAdapter = require("./dom_adapter"),
extend = require("./utils/extend").extend,
Class = require("./class"),
Action = require("./action"),
errors = require("./errors"),
coreDataUtils = require("./utils/data"),
commonUtils = require("./utils/common"),
typeUtils = require("./utils/type"),
map = require("../core/utils/iterator").map,
Callbacks = require("./utils/callbacks"),
EventsMixin = require("./events_mixin"),
publicComponentUtils = require("./utils/public_component"),
devices = require("./devices"),
isFunction = typeUtils.isFunction,
noop = commonUtils.noop;
var cachedGetters = {};
var cachedSetters = {};
/**
* @name component
* @publicName Component
* @type object
* @inherits EventsMixin
* @module core/component
* @export default
* @namespace DevExpress
* @hidden
*/
var Component = Class.inherit({
_setDeprecatedOptions: function _setDeprecatedOptions() {
this._deprecatedOptions = {};
},
_getDeprecatedOptions: function _getDeprecatedOptions() {
return this._deprecatedOptions;
},
_getOptionAliasesByName: function _getOptionAliasesByName(optionName) {
return map(this._deprecatedOptions, function (deprecate, aliasName) {
return optionName === deprecate.alias ? aliasName : undefined;
});
},
_getDefaultOptions: function _getDefaultOptions() {
return {
/**
* @name componentOptions.onInitialized
* @publicName onInitialized
* @type function
* @type_function_param1 e:object
* @type_function_param1_field1 component:Component
* @type_function_param1_field2 element:dxElement
* @default null
* @action
*/
onInitialized: null,
/**
* @name componentOptions.onOptionChanged
* @publicName onOptionChanged
* @type function
* @type_function_param1 e:object
* @type_function_param1_field1 component:Component
* @type_function_param1_field4 name:string
* @type_function_param1_field5 fullName:string
* @type_function_param1_field6 value:any
* @default null
* @action
*/
onOptionChanged: null,
/**
* @name componentOptions.onDisposing
* @publicName onDisposing
* @type function
* @type_function_param1 e:object
* @type_function_param1_field1 component:Component
* @default null
* @action
*/
onDisposing: null,
defaultOptionsRules: null
};
},
_setDefaultOptions: function _setDefaultOptions() {
this._options = this._getDefaultOptions();
},
_defaultOptionsRules: function _defaultOptionsRules() {
return [];
},
_setOptionsByDevice: function _setOptionsByDevice(customRules) {
var rules = this._defaultOptionsRules();
if (Array.isArray(customRules)) {
rules = rules.concat(customRules);
}
var rulesOptions = this._convertRulesToOptions(rules);
extend(true, this._options, rulesOptions);
for (var fieldName in this._optionsByReference) {
if (rulesOptions.hasOwnProperty(fieldName)) {
this._options[fieldName] = rulesOptions[fieldName];
}
}
},
_convertRulesToOptions: function _convertRulesToOptions(rules) {
var options = {};
var currentDevice = devices.current();
var deviceMatch = function deviceMatch(device, filter) {
var filterArray = [];
Array.prototype.push.call(filterArray, filter);
return filterArray.length === 1 && typeUtils.isEmptyObject(filterArray[0]) || commonUtils.findBestMatches(device, filterArray).length > 0;
};
for (var i = 0; i < rules.length; i++) {
var rule = rules[i],
deviceFilter = rule.device || {},
match;
if (isFunction(deviceFilter)) {
match = deviceFilter(currentDevice);
} else {
match = deviceMatch(currentDevice, deviceFilter);
}
if (match) {
extend(options, rule.options);
}
}
return options;
},
_isInitialOptionValue: function _isInitialOptionValue(name) {
var optionValue = this.option(name),
initialOptionValue = this.initialOption(name),
isInitialOption = isFunction(optionValue) && isFunction(initialOptionValue) ? optionValue.toString() === initialOptionValue.toString() : commonUtils.equalByValue(optionValue, initialOptionValue);
return isInitialOption;
},
_setOptionsByReference: function _setOptionsByReference() {
this._optionsByReference = {};
},
_getOptionsByReference: function _getOptionsByReference() {
return this._optionsByReference;
},
/**
* @name ComponentMethods.ctor
* @publicName ctor(options)
* @param1 options:ComponentOptions|undefined
* @hidden
*/
ctor: function ctor(options) {
this.NAME = publicComponentUtils.name(this.constructor);
options = options || {};
if (options.eventsStrategy) {
this.setEventsStrategy(options.eventsStrategy);
}
this._options = {};
this._updateLockCount = 0;
this._optionChangedCallbacks = options._optionChangedCallbacks || Callbacks();
this._disposingCallbacks = options._disposingCallbacks || Callbacks();
this.beginUpdate();
try {
this._suppressDeprecatedWarnings();
this._setOptionsByReference();
this._setDeprecatedOptions();
this._setDefaultOptions();
if (options && options.onInitializing) {
options.onInitializing.apply(this, [options]);
}
this._setOptionsByDevice(options.defaultOptionsRules);
this._resumeDeprecatedWarnings();
this._initOptions(options);
} finally {
this.endUpdate();
}
},
_initOptions: function _initOptions(options) {
this.option(options);
},
_optionValuesEqual: function _optionValuesEqual(name, oldValue, newValue) {
oldValue = coreDataUtils.toComparable(oldValue, true);
newValue = coreDataUtils.toComparable(newValue, true);
if (oldValue && newValue && typeUtils.isRenderer(oldValue) && typeUtils.isRenderer(newValue)) {
return newValue.is(oldValue);
}
var oldValueIsNaN = oldValue !== oldValue,
newValueIsNaN = newValue !== newValue;
if (oldValueIsNaN && newValueIsNaN) {
return true;
}
if (oldValue === null || (typeof oldValue === "undefined" ? "undefined" : _typeof(oldValue)) !== "object" || domAdapter.isElementNode(oldValue)) {
return oldValue === newValue;
}
return false;
},
_init: function _init() {
this._createOptionChangedAction();
this.on("disposing", function (args) {
this._disposingCallbacks.fireWith(this, [args]);
}.bind(this));
},
_createOptionChangedAction: function _createOptionChangedAction() {
this._optionChangedAction = this._createActionByOption("onOptionChanged", { excludeValidators: ["disabled", "readOnly", "designMode"] });
},
_createDisposingAction: function _createDisposingAction() {
this._disposingAction = this._createActionByOption("onDisposing", { excludeValidators: ["disabled", "readOnly", "designMode"] });
},
_optionChanged: function _optionChanged(args) {
switch (args.name) {
case "onDisposing":
case "onInitialized":
break;
case "onOptionChanged":
this._createOptionChangedAction();
break;
case "defaultOptionsRules":
break;
}
},
_dispose: function _dispose() {
this._optionChangedCallbacks.empty();
this._createDisposingAction();
this._disposingAction();
this._disposeEvents();
this._disposed = true;
},
/**
* @name componentmethods.instance
* @publicName instance()
* @return Component
*/
instance: function instance() {
return this;
},
/**
* @name componentmethods.beginupdate
* @publicName beginUpdate()
*/
beginUpdate: function beginUpdate() {
this._updateLockCount++;
},
/**
* @name componentmethods.endupdate
* @publicName endUpdate()
*/
endUpdate: function endUpdate() {
this._updateLockCount = Math.max(this._updateLockCount - 1, 0);
if (!this._updateLockCount) {
if (!this._initializing && !this._initialized) {
this._initializing = true;
try {
this._init();
} finally {
this._initializing = false;
this._updateLockCount++;
this._createActionByOption("onInitialized", { excludeValidators: ["disabled", "readOnly", "designMode"] })();
this._updateLockCount--;
this._initialized = true;
}
}
}
},
_logWarningIfDeprecated: function _logWarningIfDeprecated(option) {
var info = this._deprecatedOptions[option];
if (info && !this._deprecatedOptionsSuppressed) {
this._logDeprecatedWarning(option, info);
}
},
_logDeprecatedWarningCount: 0,
_logDeprecatedWarning: function _logDeprecatedWarning(option, info) {
var message = info.message || "Use the '" + info.alias + "' option instead";
errors.log("W0001", this.NAME, option, info.since, message);
++this._logDeprecatedWarningCount;
},
_suppressDeprecatedWarnings: function _suppressDeprecatedWarnings() {
this._deprecatedOptionsSuppressed = true;
},
_resumeDeprecatedWarnings: function _resumeDeprecatedWarnings() {
this._deprecatedOptionsSuppressed = false;
},
_optionChanging: noop,
_notifyOptionChanged: function _notifyOptionChanged(option, value, previousValue) {
var that = this;
if (this._initialized) {
var optionNames = [option].concat(that._getOptionAliasesByName(option));
for (var i = 0; i < optionNames.length; i++) {
var name = optionNames[i],
args = {
name: name.split(/[.\[]/)[0],
fullName: name,
value: value,
previousValue: previousValue
};
that._optionChangedCallbacks.fireWith(that, [extend(that._defaultActionArgs(), args)]);
that._optionChangedAction(extend({}, args));
if (!that._disposed) {
that._optionChanged(args);
}
}
}
},
initialOption: function initialOption(optionName) {
var currentOptions,
currentInitialized = this._initialized;
if (!this._initialOptions) {
currentOptions = this._options;
this._options = {};
this._initialized = false;
this._setDefaultOptions();
this._setOptionsByDevice(currentOptions.defaultOptionsRules);
this._initialOptions = this._options;
this._options = currentOptions;
this._initialized = currentInitialized;
}
return this._initialOptions[optionName];
},
_defaultActionConfig: function _defaultActionConfig() {
return {
context: this,
component: this
};
},
_defaultActionArgs: function _defaultActionArgs() {
return {
component: this
};
},
_createAction: function _createAction(actionSource, config) {
var that = this,
action;
return function (e) {
if (!arguments.length) {
e = {};
}
if (!typeUtils.isPlainObject(e)) {
e = { actionValue: e };
}
action = action || new Action(actionSource, extend(config, that._defaultActionConfig()));
return action.execute.call(action, extend(e, that._defaultActionArgs()));
};
},
_createActionByOption: function _createActionByOption(optionName, config) {
var that = this,
action,
eventName,
actionFunc;
var result = function result() {
if (!eventName) {
config = config || {};
if (typeof optionName !== "string") {
throw errors.Error("E0008");
}
if (optionName.indexOf("on") === 0) {
eventName = that._getEventName(optionName);
}
///#DEBUG
if (optionName.indexOf("on") !== 0) {
throw Error("The '" + optionName + "' option name should start with 'on' prefix");
}
///#ENDDEBUG
actionFunc = that.option(optionName);
}
if (!action && !actionFunc && !config.beforeExecute && !config.afterExecute && !that.hasEvent(eventName)) {
return;
}
if (!action) {
var beforeExecute = config.beforeExecute;
config.beforeExecute = function (args) {
beforeExecute && beforeExecute.apply(that, arguments);
that.fireEvent(eventName, args.args);
};
that._suppressDeprecatedWarnings();
action = that._createAction(actionFunc, config);
that._resumeDeprecatedWarnings();
}
if (Config().wrapActionsBeforeExecute) {
var beforeActionExecute = that.option("beforeActionExecute") || noop;
var wrappedAction = beforeActionExecute(that, action, config) || action;
return wrappedAction.apply(that, arguments);
}
return action.apply(that, arguments);
};
if (!Config().wrapActionsBeforeExecute) {
var onActionCreated = that.option("onActionCreated") || noop;
result = onActionCreated(that, result, config) || result;
}
return result;
},
_getEventName: function _getEventName(actionName) {
return actionName.charAt(2).toLowerCase() + actionName.substr(3);
},
hasActionSubscription: function hasActionSubscription(actionName) {
return !!this.option(actionName) || this.hasEvent(this._getEventName(actionName));
},
isOptionDeprecated: function isOptionDeprecated(name) {
var deprecatedOptions = this._getDeprecatedOptions();
return deprecatedOptions.hasOwnProperty(name);
},
/**
* @name componentmethods.option
* @publicName option()
* @return object
*/
/**
* @name componentmethods.option
* @publicName option(optionName)
* @param1 optionName:string
* @return any
*/
/**
* @name componentmethods.option
* @publicName option(optionName, optionValue)
* @param1 optionName:string
* @param2 optionValue:any
*/
/**
* @name componentmethods.option
* @publicName option(options)
* @param1 options:object
*/
option: function () {
var normalizeOptionName = function normalizeOptionName(that, name) {
var deprecate;
if (name) {
if (!that._cachedDeprecateNames) {
that._cachedDeprecateNames = [];
for (var optionName in that._deprecatedOptions) {
that._cachedDeprecateNames.push(optionName);
}
}
for (var i = 0; i < that._cachedDeprecateNames.length; i++) {
if (that._cachedDeprecateNames[i] === name) {
deprecate = that._deprecatedOptions[name];
break;
}
}
if (deprecate) {
that._logWarningIfDeprecated(name);
var alias = deprecate.alias;
if (alias) {
name = alias;
}
}
}
return name;
};
var getPreviousName = function getPreviousName(fullName) {
var splitNames = fullName.split('.');
splitNames.pop();
return splitNames.join('.');
};
var getFieldName = function getFieldName(fullName) {
var splitNames = fullName.split('.');
return splitNames[splitNames.length - 1];
};
var getOptionValue = function getOptionValue(options, name, unwrapObservables) {
var getter = cachedGetters[name];
if (!getter) {
getter = cachedGetters[name] = coreDataUtils.compileGetter(name);
}
return getter(options, { functionsAsIs: true, unwrapObservables: unwrapObservables });
};
var clearOptionsField = function clearOptionsField(options, name) {
delete options[name];
var previousFieldName = getPreviousName(name),
fieldName = getFieldName(name),
fieldObject = previousFieldName ? getOptionValue(options, previousFieldName, false) : options;
if (fieldObject) {
delete fieldObject[fieldName];
}
};
var setOptionsField = function setOptionsField(options, fullName, value) {
var fieldName = "",
fieldObject;
do {
if (fieldName) {
fieldName = "." + fieldName;
}
fieldName = getFieldName(fullName) + fieldName;
fullName = getPreviousName(fullName);
fieldObject = fullName ? getOptionValue(options, fullName, false) : options;
} while (!fieldObject);
fieldObject[fieldName] = value;
};
var normalizeOptionValue = function normalizeOptionValue(that, options, name, value) {
if (name) {
var alias = normalizeOptionName(that, name);
if (alias && alias !== name) {
setOptionsField(options, alias, value);
clearOptionsField(options, name);
}
}
};
var prepareOption = function prepareOption(that, options, name, value) {
if (typeUtils.isPlainObject(value)) {
for (var valueName in value) {
prepareOption(that, options, name + "." + valueName, value[valueName]);
}
}
normalizeOptionValue(that, options, name, value);
};
var setOptionValue = function setOptionValue(that, name, value) {
if (!cachedSetters[name]) {
cachedSetters[name] = coreDataUtils.compileSetter(name);
}
var path = name.split(/[.\[]/);
cachedSetters[name](that._options, value, {
functionsAsIs: true,
merge: !that._getOptionsByReference()[name],
unwrapObservables: path.length > 1 && !!that._getOptionsByReference()[path[0]]
});
};
var setOption = function setOption(that, name, value) {
var previousValue = getOptionValue(that._options, name, false);
if (that._optionValuesEqual(name, previousValue, value)) {
return;
}
if (that._initialized) {
that._optionChanging(name, previousValue, value);
}
setOptionValue(that, name, value);
that._notifyOptionChanged(name, value, previousValue);
};
return function (options, value) {
var that = this,
name = options;
if (arguments.length < 2 && typeUtils.type(name) !== "object") {
name = normalizeOptionName(that, name);
return getOptionValue(that._options, name);
}
if (typeof name === "string") {
options = {};
options[name] = value;
}
that.beginUpdate();
try {
var optionName;
for (optionName in options) {
prepareOption(that, options, optionName, options[optionName]);
}
for (optionName in options) {
setOption(that, optionName, options[optionName]);
}
} finally {
that.endUpdate();
}
};
}(),
_getOptionValue: function _getOptionValue(name, context) {
var value = this.option(name);
if (isFunction(value)) {
return value.bind(context)();
}
return value;
}
}).include(EventsMixin);
module.exports = Component;