/* * The dirty implementation in this class is quite naive. The reasoning for this is that the dirty * state will only be used in very specific circumstances, specifically, after the render process * has begun but the component is not yet rendered to the DOM. As such, we want it to perform * as quickly as possible so it's not as fully featured as you may expect. */ /** * Manages certain element-like data prior to rendering. These values are passed * on to the render process. This is currently used to manage the "class" and "style" attributes * of a component's primary el as well as the bodyEl of panels. This allows things like * addBodyCls in Panel to share logic with addCls in Component. * @private */Ext.define('Ext.util.ProtoElement', function() { var splitWords = Ext.String.splitWords, toMap = Ext.Array.toMap; return { isProtoEl: true, /** * The property name for the className on the data object passed to {@link #writeTo}. */ clsProp: 'cls', /** * The property name for the style on the data object passed to {@link #writeTo}. */ styleProp: 'style', /** * The property name for the removed classes on the data object passed to {@link #writeTo}. */ removedProp: 'removed', /** * True if the style must be converted to text during {@link #writeTo}. When used to * populate tpl data, this will be true. When used to populate {@link Ext.dom.Helper} * specs, this will be false (the default). */ styleIsText: false, constructor: function(config) { var me = this, cls, style; if (config) { Ext.apply(me, config); cls = me.cls; style = me.style; delete me.cls; } me.classList = cls ? splitWords(cls) : []; me.classMap = cls ? toMap(me.classList) : {}; if (style) { if (typeof style === 'string') { me.style = Ext.Element.parseStyles(style); } else if (Ext.isFunction(style)) { me.styleFn = style; delete me.style; } else { me.style = Ext.apply({}, style); // don't edit the given object } } }, /** * Indicates that the current state of the object has been flushed to the DOM, so we need * to track any subsequent changes */ flush: function() { this.flushClassList = []; this.removedClasses = {}; // clear the style, it will be recreated if we add anything new delete this.style; delete this.unselectableAttr; }, /** * Adds class to the element. * @param {String} cls One or more classnames separated with spaces. * @return {Ext.util.ProtoElement} this */ addCls: function(cls) { if (!cls) { return this; } // eslint-disable-next-line vars-on-top var me = this, add = (typeof cls === 'string') ? splitWords(cls) : cls, length = add.length, list = me.classList, map = me.classMap, flushList = me.flushClassList, i = 0, c; for (; i < length; ++i) { c = add[i]; if (!map[c]) { map[c] = true; list.push(c); if (flushList) { flushList.push(c); delete me.removedClasses[c]; } } } return me; }, /** * True if the element has given class. * @param {String} cls * @return {Boolean} */ hasCls: function(cls) { return cls in this.classMap; }, /** * Removes class from the element. * @param {String} cls One or more classnames separated with spaces. * @return {Ext.util.ProtoElement} this */ removeCls: function(cls) { var me = this, list = me.classList, newList = (me.classList = []), remove = toMap(splitWords(cls)), length = list.length, map = me.classMap, removedClasses = me.removedClasses, i, c; for (i = 0; i < length; ++i) { c = list[i]; if (remove[c]) { if (removedClasses) { if (map[c]) { removedClasses[c] = true; Ext.Array.remove(me.flushClassList, c); } } delete map[c]; } else { newList.push(c); } } return me; }, /** * @method setStyle * @inheritdoc Ext.dom.Element#method-setStyle * @return {Ext.util.ProtoElement} this */ setStyle: function(prop, value) { var me = this, style = me.style || (me.style = {}); if (typeof prop === 'string') { if (arguments.length === 1) { me.setStyle(Ext.Element.parseStyles(prop)); } else { style[prop] = value; } } else { Ext.apply(style, prop); } return me; }, unselectable: function() { // See Ext.dom.Element.unselectable for an explanation of what is required // to make an element unselectable this.addCls(Ext.dom.Element.unselectableCls); if (Ext.isOpera) { this.unselectableAttr = true; } }, /** * Writes style and class properties to given object. * Styles will be written to {@link #styleProp} and class names to {@link #clsProp}. * @param {Object} to * @return {Object} to */ writeTo: function(to) { var me = this, classList = me.flushClassList || me.classList, removedClasses = me.removedClasses, style; if (me.styleFn) { style = Ext.apply({}, me.styleFn()); Ext.apply(style, me.style); } else { style = me.style; } to[me.clsProp] = classList.join(' '); if (style) { to[me.styleProp] = me.styleIsText ? Ext.DomHelper.generateStyles(style, null, true) : style; } if (removedClasses) { removedClasses = Ext.Object.getKeys(removedClasses); if (removedClasses.length) { to[me.removedProp] = removedClasses.join(' '); } } if (me.unselectableAttr) { to.unselectable = 'on'; } return to; } };});