devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
467 lines (395 loc) • 14.6 kB
JavaScript
"use strict";
var $ = require("../core/renderer"),
eventsEngine = require("../events/core/events_engine"),
windowUtils = require("../core/utils/window"),
extend = require("./utils/extend").extend,
config = require("./config"),
errors = require("./errors"),
getPublicElement = require("../core/utils/dom").getPublicElement,
windowResizeCallbacks = require("../core/utils/resize_callbacks"),
commonUtils = require("./utils/common"),
each = require("./utils/iterator").each,
typeUtils = require("./utils/type"),
inArray = require("./utils/array").inArray,
publicComponentUtils = require("./utils/public_component"),
dataUtils = require("./element_data"),
Component = require("./component"),
abstract = Component.abstract;
var RTL_DIRECTION_CLASS = "dx-rtl",
VISIBILITY_CHANGE_CLASS = "dx-visibility-change-handler",
VISIBILITY_CHANGE_EVENTNAMESPACE = "VisibilityChange";
/**
* @name domcomponent
* @section uiWidgets
* @publicName DOMComponent
* @type object
* @inherits Component
* @namespace DevExpress
* @module core/dom_component
* @export default
* @hidden
*/
var DOMComponent = Component.inherit({
_getDefaultOptions: function _getDefaultOptions() {
return extend(this.callBase(), {
/**
* @name domcomponentoptions.onOptionChanged
* @publicName onOptionChanged
* @type function
* @type_function_param1 e:object
* @type_function_param1_field4 name:string
* @type_function_param1_field5 fullName:string
* @type_function_param1_field6 value:any
* @action
* @extends Action
* @inheritdoc
*/
/**
* @name domcomponentoptions.onDisposing
* @publicName onDisposing
* @action
* @extends Action
* @inheritdoc
*/
/**
* @name domcomponentoptions.width
* @publicName width
* @type number|string|function
* @default undefined
* @type_function_return number|string
*/
width: undefined,
/**
* @name domcomponentoptions.height
* @publicName height
* @type number|string|function
* @default undefined
* @type_function_return number|string
*/
height: undefined,
/**
* @name domcomponentoptions.rtlEnabled
* @publicName rtlEnabled
* @type boolean
* @default false
*/
rtlEnabled: config().rtlEnabled,
/**
* @name domcomponentoptions.elementAttr
* @publicName elementAttr
* @type object
* @default {}
*/
elementAttr: {},
disabled: false,
integrationOptions: {}
});
},
/**
* @name DOMComponentMethods.ctor
* @publicName ctor(element,options)
* @param1 element:Node|JQuery
* @param2 options:DOMComponentOptions|undefined
* @hidden
*/
ctor: function ctor(element, options) {
this._$element = $(element);
publicComponentUtils.attachInstanceToElement(this._$element, this, this._dispose);
this.callBase(options);
},
_visibilityChanged: abstract,
_dimensionChanged: abstract,
_init: function _init() {
this.callBase();
this._attachWindowResizeCallback();
},
_setOptionsByDevice: function _setOptionsByDevice(instanceCustomRules) {
this.callBase([].concat(this.constructor._classCustomRules || [], instanceCustomRules || []));
},
_isInitialOptionValue: function _isInitialOptionValue(name) {
var isCustomOption = this.constructor._classCustomRules && this._convertRulesToOptions(this.constructor._classCustomRules).hasOwnProperty(name);
return !isCustomOption && this.callBase(name);
},
_attachWindowResizeCallback: function _attachWindowResizeCallback() {
if (this._isDimensionChangeSupported()) {
var windowResizeCallBack = this._windowResizeCallBack = this._dimensionChanged.bind(this);
windowResizeCallbacks.add(windowResizeCallBack);
}
},
_isDimensionChangeSupported: function _isDimensionChangeSupported() {
return this._dimensionChanged !== abstract;
},
_renderComponent: function _renderComponent() {
this._initMarkup();
if (windowUtils.hasWindow()) {
this._render();
}
},
_initMarkup: function _initMarkup() {
this._renderElementAttributes();
this._toggleRTLDirection(this.option("rtlEnabled"));
this._renderVisibilityChange();
this._renderDimensions();
},
_render: function _render() {
this._attachVisibilityChangeHandlers();
},
_renderElementAttributes: function _renderElementAttributes() {
var attributes = extend({}, this.option("elementAttr")),
classNames = attributes.class;
delete attributes.class;
this.$element().attr(attributes).addClass(classNames);
},
_renderVisibilityChange: function _renderVisibilityChange() {
if (this._isDimensionChangeSupported()) {
this._attachDimensionChangeHandlers();
}
if (!this._isVisibilityChangeSupported()) {
return;
}
this.$element().addClass(VISIBILITY_CHANGE_CLASS);
},
_renderDimensions: function _renderDimensions() {
var $element = this.$element();
var element = $element.get(0);
var width = this._getOptionValue("width", element);
var height = this._getOptionValue("height", element);
if (this._isCssUpdateRequired(element, height, width)) {
$element.css({
width: width,
height: height
});
}
},
_isCssUpdateRequired: function _isCssUpdateRequired(element, height, width) {
return !!(width || height || element.style.width || element.style.height);
},
_attachDimensionChangeHandlers: function _attachDimensionChangeHandlers() {
var that = this;
var resizeEventName = "dxresize." + this.NAME + VISIBILITY_CHANGE_EVENTNAMESPACE;
eventsEngine.off(that.$element(), resizeEventName);
eventsEngine.on(that.$element(), resizeEventName, function () {
that._dimensionChanged();
});
},
_attachVisibilityChangeHandlers: function _attachVisibilityChangeHandlers() {
if (!this._isVisibilityChangeSupported()) {
return;
}
var that = this;
var hidingEventName = "dxhiding." + this.NAME + VISIBILITY_CHANGE_EVENTNAMESPACE;
var shownEventName = "dxshown." + this.NAME + VISIBILITY_CHANGE_EVENTNAMESPACE;
that._isHidden = !that._isVisible();
eventsEngine.off(that.$element(), hidingEventName);
eventsEngine.on(that.$element(), hidingEventName, function () {
that._checkVisibilityChanged("hiding");
});
eventsEngine.off(that.$element(), shownEventName);
eventsEngine.on(that.$element(), shownEventName, function () {
that._checkVisibilityChanged("shown");
});
},
_isVisible: function _isVisible() {
return this.$element().is(":visible");
},
_checkVisibilityChanged: function _checkVisibilityChanged(event) {
if (event === "hiding" && this._isVisible() && !this._isHidden) {
this._visibilityChanged(false);
this._isHidden = true;
} else if (event === "shown" && this._isVisible() && this._isHidden) {
this._isHidden = false;
this._visibilityChanged(true);
}
},
_isVisibilityChangeSupported: function _isVisibilityChangeSupported() {
return this._visibilityChanged !== abstract && windowUtils.hasWindow();
},
_clean: commonUtils.noop,
_modelByElement: function _modelByElement() {
var modelByElement = this.option("modelByElement") || commonUtils.noop;
return modelByElement(this.$element());
},
_invalidate: function _invalidate() {
if (!this._updateLockCount) {
throw errors.Error("E0007");
}
this._requireRefresh = true;
},
_refresh: function _refresh() {
this._clean();
this._renderComponent();
},
_dispose: function _dispose() {
this.callBase();
this._clean();
this._detachWindowResizeCallback();
},
_detachWindowResizeCallback: function _detachWindowResizeCallback() {
if (this._isDimensionChangeSupported()) {
windowResizeCallbacks.remove(this._windowResizeCallBack);
}
},
_toggleRTLDirection: function _toggleRTLDirection(rtl) {
this.$element().toggleClass(RTL_DIRECTION_CLASS, rtl);
},
_createComponent: function _createComponent(element, component, config) {
var that = this;
config = config || {};
var synchronizableOptions = commonUtils.grep(["rtlEnabled", "disabled"], function (value) {
return !(value in config);
});
var nestedComponentOptions = that.option("nestedComponentOptions") || commonUtils.noop;
that._extendConfig(config, extend({
integrationOptions: this.option("integrationOptions"),
rtlEnabled: this.option("rtlEnabled"),
disabled: this.option("disabled")
}, nestedComponentOptions(this)));
var instance;
if (typeUtils.isString(component)) {
var $element = $(element)[component](config);
instance = $element[component]("instance");
} else if (element) {
instance = component.getInstance(element);
if (instance) {
instance.option(config);
} else {
instance = new component(element, config);
}
}
if (instance) {
var optionChangedHandler = function optionChangedHandler(args) {
if (inArray(args.name, synchronizableOptions) >= 0) {
instance.option(args.name, args.value);
}
};
that.on("optionChanged", optionChangedHandler);
instance.on("disposing", function () {
that.off("optionChanged", optionChangedHandler);
});
}
return instance;
},
_extendConfig: function _extendConfig(config, extendConfig) {
each(extendConfig, function (key, value) {
config[key] = config.hasOwnProperty(key) ? config[key] : value;
});
},
_defaultActionConfig: function _defaultActionConfig() {
return extend(this.callBase(), {
context: this._modelByElement(this.$element())
});
},
/**
* @pseudo Action
* @section Utils
* @type function
* @default null
* @type_function_param1 e:object
* @type_function_param1_field1 component:DOMComponent
* @type_function_param1_field2 element:dxElement
* @type_function_param1_field3 model:object
**/
_defaultActionArgs: function _defaultActionArgs() {
var model = this._modelByElement(this.$element());
return extend(this.callBase(), {
element: this.element(),
model: model
});
},
_optionChanged: function _optionChanged(args) {
switch (args.name) {
case "width":
case "height":
this._renderDimensions();
break;
case "rtlEnabled":
case "elementAttr":
this._invalidate();
break;
case "disabled":
case "integrationOptions":
break;
default:
this.callBase(args);
break;
}
},
_removeAttributes: function _removeAttributes(element) {
var i = element.attributes.length - 1;
for (; i >= 0; i--) {
var attribute = element.attributes[i];
if (!attribute) {
return;
}
var attributeName = attribute.name;
if (attributeName.indexOf("aria-") === 0 || attributeName.indexOf("dx-") !== -1 || attributeName === "role" || attributeName === "style" || attributeName === "tabindex") {
element.removeAttribute(attributeName);
}
}
},
_removeClasses: function _removeClasses(element) {
var classes = element.className.split(" ").filter(function (cssClass) {
return cssClass.lastIndexOf("dx-", 0) !== 0;
});
element.className = classes.join(" ");
},
endUpdate: function endUpdate() {
var requireRender = !this._initializing && !this._initialized;
this.callBase.apply(this, arguments);
if (!this._updateLockCount) {
if (requireRender) {
this._renderComponent();
} else if (this._requireRefresh) {
this._requireRefresh = false;
this._refresh();
}
}
},
$element: function $element() {
return this._$element;
},
/**
* @name domcomponentmethods.element
* @publicName element()
* @return dxElement
*/
element: function element() {
return getPublicElement(this.$element());
},
/**
* @name domcomponentmethods.dispose
* @publicName dispose()
*/
dispose: function dispose() {
var element = this.$element().get(0);
dataUtils.cleanDataRecursive(element, true);
element.textContent = "";
this._removeAttributes(element);
this._removeClasses(element);
}
});
/**
* @name domcomponentmethods.getInstance
* @static
* @section uiWidgets
* @publicName getInstance(element)
* @param1 element:Node|JQuery
* @return DOMComponent
*/
DOMComponent.getInstance = function (element) {
return publicComponentUtils.getInstanceByElement($(element), this);
};
/**
* @name domcomponentmethods.defaultOptions
* @static
* @section uiWidgets
* @publicName defaultOptions(rule)
* @param1 rule:Object
* @param1_field1 device:Device|Array<Device>|function
* @param1_field2 options:Object
*/
DOMComponent.defaultOptions = function (rule) {
this._classCustomRules = this._classCustomRules || [];
this._classCustomRules.push(rule);
};
module.exports = DOMComponent;