//@tag dom,core //@define Ext.Element-all //@define Ext.Element-style //@require Ext.Element-position /** * @class Ext.dom.Element */ Ext.dom.Element.addMembers({ WIDTH: 'width', HEIGHT: 'height', MIN_WIDTH: 'min-width', MIN_HEIGHT: 'min-height', MAX_WIDTH: 'max-width', MAX_HEIGHT: 'max-height', TOP: 'top', RIGHT: 'right', BOTTOM: 'bottom', LEFT: 'left', /** * @property VISIBILITY * {@link #setVisibilityMode}で使用する可視モード定数。要素を非表示にするために`visibility`を使用します。 */ VISIBILITY: 1, /** * @property DISPLAY * {@link #setVisibilityMode}で使用する可視モード定数。要素を非表示にするために`display`を使用します。 */ DISPLAY: 2, /** * @property OFFSETS * {@link #setVisibilityMode}で使用する可視モード定数。要素を非表示にするためにオフセットを使用します。 */ OFFSETS: 3, SEPARATOR: '-', trimRe: /^\s+|\s+$/g, wordsRe: /\w/g, spacesRe: /\s+/, styleSplitRe: /\s*(?::|;)\s*/, transparentRe: /^(?:transparent|(?:rgba[(](?:\s*\d+\s*[,]){3}\s*0\s*[)]))$/i, classNameSplitRegex: /[\s]+/, borders: { t: 'border-top-width', r: 'border-right-width', b: 'border-bottom-width', l: 'border-left-width' }, paddings: { t: 'padding-top', r: 'padding-right', b: 'padding-bottom', l: 'padding-left' }, margins: { t: 'margin-top', r: 'margin-right', b: 'margin-bottom', l: 'margin-left' }, /** * @property {String} defaultUnit * 単位が指定されていない場合、CSS値に適用するデフォルトの単位。 */ defaultUnit: "px", isSynchronized: false, /** * @private */ synchronize: function() { var dom = this.dom, hasClassMap = {}, className = dom.className, classList, i, ln, name; if (className.length > 0) { classList = dom.className.split(this.classNameSplitRegex); for (i = 0, ln = classList.length; i < ln; i++) { name = classList[i]; hasClassMap[name] = true; } } else { classList = []; } this.classList = classList; this.hasClassMap = hasClassMap; this.isSynchronized = true; return this; }, /** * この要素に指定されたCSSクラスを追加します。 * @param {String} names この要素に追加するCSSクラス。 * @param {String} [prefix] (optional) 各クラスの前に付加する接頭辞。 * @param {String} [suffix] (optional) 各クラスの後に付加する接尾辞。 */ addCls: function(names, prefix, suffix) { if (!names) { return this; } if (!this.isSynchronized) { this.synchronize(); } var dom = this.dom, map = this.hasClassMap, classList = this.classList, SEPARATOR = this.SEPARATOR, i, ln, name; prefix = prefix ? prefix + SEPARATOR : ''; suffix = suffix ? SEPARATOR + suffix : ''; if (typeof names == 'string') { names = names.split(this.spacesRe); } for (i = 0, ln = names.length; i < ln; i++) { name = prefix + names[i] + suffix; if (!map[name]) { map[name] = true; classList.push(name); } } dom.className = classList.join(' '); return this; }, /** * この要素から指定されたCSSクラスを除去します。 * @param {String} names この要素から除去するCSSクラス。 * @param {String} [prefix=''] 除去する各クラスに追加するオプションのプレフィックス。 * @param {String} [suffix=''] 除去する各クラスに追加するオプションのサフィックス。 */ removeCls: function(names, prefix, suffix) { if (!names) { return this; } if (!this.isSynchronized) { this.synchronize(); } if (!suffix) { suffix = ''; } var dom = this.dom, map = this.hasClassMap, classList = this.classList, SEPARATOR = this.SEPARATOR, i, ln, name; prefix = prefix ? prefix + SEPARATOR : ''; suffix = suffix ? SEPARATOR + suffix : ''; if (typeof names == 'string') { names = names.split(this.spacesRe); } for (i = 0, ln = names.length; i < ln; i++) { name = prefix + names[i] + suffix; if (map[name]) { delete map[name]; Ext.Array.remove(classList, name); } } dom.className = classList.join(' '); return this; }, /** * 要素のCSSクラスを他のクラスに置換します。もし置換対象のクラスが存在しない場合、単純に新しいクラスが追加されます。 * @param {String} oldName 置換されるCSSクラス。 * @param {String} newName 置換された新しいCSSクラス。 * @param {String} [prefix=''] 置換する各クラスに追加するオプションのプレフィックス。 * @param {String} [suffix=''] 置換する各クラスに追加するオプションのサフィックス。 * @return {Ext.dom.Element} this */ replaceCls: function(oldName, newName, prefix, suffix) { if (!oldName && !newName) { return this; } oldName = oldName || []; newName = newName || []; if (!this.isSynchronized) { this.synchronize(); } if (!suffix) { suffix = ''; } var dom = this.dom, map = this.hasClassMap, classList = this.classList, SEPARATOR = this.SEPARATOR, i, ln, name; prefix = prefix ? prefix + SEPARATOR : ''; suffix = suffix ? SEPARATOR + suffix : ''; if (typeof oldName == 'string') { oldName = oldName.split(this.spacesRe); } if (typeof newName == 'string') { newName = newName.split(this.spacesRe); } for (i = 0, ln = oldName.length; i < ln; i++) { name = prefix + oldName[i] + suffix; if (map[name]) { delete map[name]; Ext.Array.remove(classList, name); } } for (i = 0, ln = newName.length; i < ln; i++) { name = prefix + newName[i] + suffix; if (!map[name]) { map[name] = true; classList.push(name); } } dom.className = classList.join(' '); return this; }, /** * 指定されたCSSクラスがこの要素のDOMノードに存在するかどうかチェックします。 * @param {String} name チェックするCSSクラス。 * @return {Boolean} クラスが存在すれば`true`、存在しなけれは`false`を返します。 */ hasCls: function(name) { if (!this.isSynchronized) { this.synchronize(); } return this.hasClassMap.hasOwnProperty(name); }, /** * 指定されたCSSクラスをこの要素のDOMノードに設定します。 * @param {String/Array} className この要素に設定するCSSクラス。 */ setCls: function(className) { var map = this.hasClassMap, i, ln, name; if (typeof className == 'string') { className = className.split(this.spacesRe); } for (i = 0, ln = className.length; i < ln; i++) { name = className[i]; if (!map[name]) { map[name] = true; } } this.classList = className.slice(); this.dom.className = className.join(' '); }, /** * この要素で指定されたCSSクラスを切り替えます(既に存在している場合は削除し、存在しない場合は追加します)。 * @param {String} className 切り替えるCSSクラス。 * @return {Ext.dom.Element} this */ toggleCls: function(className, force){ if (typeof force !== 'boolean') { force = !this.hasCls(className); } return (force) ? this.addCls(className) : this.removeCls(className); }, /** * @private * @param {String} firstClass * @param {String} secondClass * @param {Boolean} flag * @param {String} prefix * @return {Mixed} */ swapCls: function(firstClass, secondClass, flag, prefix) { if (flag === undefined) { flag = true; } var addedClass = flag ? firstClass : secondClass, removedClass = flag ? secondClass : firstClass; if (removedClass) { this.removeCls(prefix ? prefix + '-' + removedClass : removedClass); } if (addedClass) { this.addCls(prefix ? prefix + '-' + addedClass : addedClass); } return this; }, /** * この要素の幅を設定します。 * @param {Number/String} width 新しい幅。 * @return {Ext.dom.Element} this */ setWidth: function(width) { return this.setLengthValue(this.WIDTH, width); }, /** * この要素の高さを設定します。 * @param {Number/String} height 新しい高さ。 * @return {Ext.dom.Element} this */ setHeight: function(height) { return this.setLengthValue(this.HEIGHT, height); }, /** * この要素のサイズを設定します。 * * @param {Number/String} width 新しい幅。取り得る値は次の通りです。 * * - この要素の{@link #defaultUnit}の新しい幅を指定する数字(デフォルトではピクセル)。 * - CSSの幅スタイル設定するために使用する文字列。この場合、アニメーションは使用**できません**。 * - `{width: widthValue, height: heightValue}`というフォーマットのサイズオブジェクト。 * * @param {Number/String} height 新しい高さ。取り得る値は次の通りです。 * * - この要素の{@link #defaultUnit}の新しい高さを指定する数字(デフォルトではピクセル)。 * - CSSの高さを設定する際に使用する文字列。この場合、アニメーションは使用**できません**。 * @return {Ext.dom.Element} this */ setSize: function(width, height) { if (Ext.isObject(width)) { // in case of object from getSize() height = width.height; width = width.width; } this.setWidth(width); this.setHeight(height); return this; }, /** * この要素の最小の幅を設定します。 * @param {Number/String} width 新しい最小の幅。 * @return {Ext.dom.Element} this */ setMinWidth: function(width) { return this.setLengthValue(this.MIN_WIDTH, width); }, /** * この要素の最小の高さを設定します。 * @param {Number/String} height 新しい最大の高さ。 * @return {Ext.dom.Element} this */ setMinHeight: function(height) { return this.setLengthValue(this.MIN_HEIGHT, height); }, /** * この要素の最大の幅を設定します。 * @param {Number/String} width 新しい最大の幅。 * @return {Ext.dom.Element} this */ setMaxWidth: function(width) { return this.setLengthValue(this.MAX_WIDTH, width); }, /** * この要素の最大の高さを設定します。 * @param {Number/String} height 新しい最大の高さ。 * @return {Ext.dom.Element} this */ setMaxHeight: function(height) { return this.setLengthValue(this.MAX_HEIGHT, height); }, /** * CSSスタイルを直接使用して({@link #setY}の代わりに)要素の上端の位置を設定します。 * @param {String} top CSSのtopプロパティの値です。 * @return {Ext.dom.Element} this */ setTop: function(top) { return this.setLengthValue(this.TOP, top); }, /** * 要素のCSSスタイルのrightを設定します。 * @param {String} right CSSのrightプロパティの値。 * @return {Ext.dom.Element} this */ setRight: function(right) { return this.setLengthValue(this.RIGHT, right); }, /** * 要素のCSSスタイルのbottomを設定します。 * @param {String} bottom CSSのbottomプロパティの値。 * @return {Ext.dom.Element} this */ setBottom: function(bottom) { return this.setLengthValue(this.BOTTOM, bottom); }, /** * CSSスタイルを直接使用して({@link #setX}の代わりに)要素の左端の位置を設定します。 * @param {String} left CSSのleftプロパティの値。 * @return {Ext.dom.Element} this */ setLeft: function(left) { return this.setLengthValue(this.LEFT, left); }, setMargin: function(margin) { var domStyle = this.dom.style; if (margin || margin === 0) { margin = this.self.unitizeBox((margin === true) ? 5 : margin); domStyle.setProperty('margin', margin, 'important'); } else { domStyle.removeProperty('margin-top'); domStyle.removeProperty('margin-right'); domStyle.removeProperty('margin-bottom'); domStyle.removeProperty('margin-left'); } }, setPadding: function(padding) { var domStyle = this.dom.style; if (padding || padding === 0) { padding = this.self.unitizeBox((padding === true) ? 5 : padding); domStyle.setProperty('padding', padding, 'important'); } else { domStyle.removeProperty('padding-top'); domStyle.removeProperty('padding-right'); domStyle.removeProperty('padding-bottom'); domStyle.removeProperty('padding-left'); } }, setBorder: function(border) { var domStyle = this.dom.style; if (border || border === 0) { border = this.self.unitizeBox((border === true) ? 1 : border); domStyle.setProperty('border-width', border, 'important'); } else { domStyle.removeProperty('border-top-width'); domStyle.removeProperty('border-right-width'); domStyle.removeProperty('border-bottom-width'); domStyle.removeProperty('border-left-width'); } }, setLengthValue: function(name, value) { var domStyle = this.dom.style; if (value === null) { domStyle.removeProperty(name); return this; } if (typeof value == 'number') { value = value + 'px'; } domStyle.setProperty(name, value, 'important'); return this; }, /** * 要素の可視状態を設定します(詳細を参照してください)。`visibilityMode`が`Element.DISPLAY`に設定されている場合、要素を非表示にするにはdisplayプロパティを使用し、そうでない場合はvisibilityを使用します。デフォルトでは`visibility`プロパティを使用して表示、非表示を行います。 * @param {Boolean} visible 要素を可視状態にするかどうか。 * @return {Ext.Element} this */ setVisible: function(visible) { var mode = this.getVisibilityMode(), method = visible ? 'removeCls' : 'addCls'; switch (mode) { case this.VISIBILITY: this.removeCls(['x-hidden-display', 'x-hidden-offsets']); this[method]('x-hidden-visibility'); break; case this.DISPLAY: this.removeCls(['x-hidden-visibility', 'x-hidden-offsets']); this[method]('x-hidden-display'); break; case this.OFFSETS: this.removeCls(['x-hidden-visibility', 'x-hidden-display']); this[method]('x-hidden-offsets'); break; } return this; }, getVisibilityMode: function() { var dom = this.dom, mode = Ext.dom.Element.data(dom, 'visibilityMode'); if (mode === undefined) { Ext.dom.Element.data(dom, 'visibilityMode', mode = this.DISPLAY); } return mode; }, /** * {@link #VISIBILITY}、{@link #DISPLAY}、{@link #OFFSETS}の間でvisibilityモードを変更するために使用します。 */ setVisibilityMode: function(mode) { this.self.data(this.dom, 'visibilityMode', mode); return this; }, /** * この要素を表示させます。"display"か"visibility"のどちらを使用するかを決定するためにdisplay modeを使用します。{@link #setVisible}を参照してください。 */ show: function() { var dom = this.dom; if (dom) { dom.style.removeProperty('display'); } }, /** * この要素を非表示にします。"display"か"visibility"のどちらを使用するかを決定するためにdisplay modeを使用します。{@link #setVisible}を参照してください。 */ hide: function() { this.dom.style.setProperty('display', 'none', 'important'); }, setVisibility: function(isVisible) { var domStyle = this.dom.style; if (isVisible) { domStyle.removeProperty('visibility'); } else { domStyle.setProperty('visibility', 'hidden', 'important'); } }, /** * この共有されたオブジェクトはスタイル名をキーとします(例えば、'margin-left'または'marginLeft')。この値は次のプロパティを持つオブジェクトです: * * * `name` (文字列):DOMに提示される実際の名前。これは一般的に{@link #normalize}により返される値です。 * * `get` (関数):このスタイルにgetを実行するフック関数。これらの関数は引数"(dom, el)"を受け取ります。`dom`パラメータはこのスタイルを取得するDOM要素です。引数`el`(`null`である場合もあります)はExt.Elementです。 * * `set` (関数) :このスタイルにsetを実行するフック関数。これらの関数は引数"(dom, value, el)"を受け取ります。`dom`パラメータはこのスタイルを取得するDOM要素です。`value`パラメータはスタイルのための新しい値です。引数`el`(`null`である場合もあります)はExt.Elementです。 * * `this`ポインタは`get`または`set`を含むオブジェクトです。つまり、必要な場合、`this.name`にアクセスできます。フック関数はどちらもオプションです。 * @private */ styleHooks: {}, // @private addStyles: function(sides, styles) { var totalSize = 0, sidesArr = sides.match(this.wordsRe), i = 0, len = sidesArr.length, side, size; for (; i < len; i++) { side = sidesArr[i]; size = side && parseInt(this.getStyle(styles[side]), 10); if (size) { totalSize += Math.abs(size); } } return totalSize; }, /** * スタイルの現在の値が与えられた値と等しいかどうかチェックします。 * @param {String} style 値が返されるプロパティ。 * @param {String} value チェックする値。 * @return {Boolean} 現在の値が与えられた値と等しいとき`true`を返します。 */ isStyle: function(style, val) { return this.getStyle(style) == val; }, getStyleValue: function(name) { return this.dom.style.getPropertyValue(name); }, /** * `currentStyle`と`computedStyle`を正規化します。 * @param {String} prop 値が返されるstyleプロパティ。 * @return {String} この要素のstyleプロパティの現在の値。 */ getStyle: function(prop) { var me = this, dom = me.dom, hook = me.styleHooks[prop], cs, result; if (dom == document) { return null; } if (!hook) { me.styleHooks[prop] = hook = { name: Ext.dom.Element.normalize(prop) }; } if (hook.get) { return hook.get(dom, me); } cs = window.getComputedStyle(dom, ''); // why the dom.style lookup? It is not true that "style == computedStyle" as // well as the fact that 0/false are valid answers... result = (cs && cs[hook.name]); // || dom.style[hook.name]; // WebKit returns rgb values for transparent, how does this work n IE9+ // if (!supportsTransparentColor && result == 'rgba(0, 0, 0, 0)') { // result = 'transparent'; // } return result; }, /** * styleプロパティを設定するラッパーは、複数のスタイルから1つのオブジェクトパラメータをとります。 * @param {String/Object} property 設定されるstyleプロパティ、または複数のスタイルのオブジェクト。 * @param {String} [value] 設定したプロパティへ適用する値、またはオブジェクトが渡された場合は`null`。 * @return {Ext.dom.Element} this */ setStyle: function(prop, value) { var me = this, dom = me.dom, hooks = me.styleHooks, style = dom.style, valueFrom = Ext.valueFrom, name, hook; // we don't promote the 2-arg form to object-form to avoid the overhead... if (typeof prop == 'string') { hook = hooks[prop]; if (!hook) { hooks[prop] = hook = { name: Ext.dom.Element.normalize(prop) }; } value = valueFrom(value, ''); if (hook.set) { hook.set(dom, value, me); } else { style[hook.name] = value; } } else { for (name in prop) { if (prop.hasOwnProperty(name)) { hook = hooks[name]; if (!hook) { hooks[name] = hook = { name: Ext.dom.Element.normalize(name) }; } value = valueFrom(prop[name], ''); if (hook.set) { hook.set(dom, value, me); } else { style[hook.name] = value; } } } } return me; }, /** * 要素のオフセットされた高さを返します。 * @param {Boolean} [contentHeight] `true`に設定するとボーダーとパディングを含まない高さを取得します。 * @return {Number} 要素の高さ。 */ getHeight: function(contentHeight) { var dom = this.dom, height = contentHeight ? (dom.clientHeight - this.getPadding("tb")) : dom.offsetHeight; return height > 0 ? height : 0; }, /** * 要素のオフセットされた幅を返します。 * @param {Boolean} [contentWidth] `true`に設定するとボーダーとパディングを含まない幅を取得します。 * @return {Number} 要素の幅。 */ getWidth: function(contentWidth) { var dom = this.dom, width = contentWidth ? (dom.clientWidth - this.getPadding("lr")) : dom.offsetWidth; return width > 0 ? width : 0; }, /** * 指定した(両)サイドのボーダーの幅を取得します。 * @param {String} side 複数の値を追加するために、t、l、r、bもしくはこれらの任意の組み合わせを設定できます。例えば、`'lr'`を設定した場合は、ボーダーの**l**eft widthと**r**ight widthを取得します。 * @return {Number} 渡されたサイドを合計した幅の値。 */ getBorderWidth: function(side) { return this.addStyles(side, this.borders); }, /** * 指定された(両)サイドのパディング幅を取得します。 * @param {String} side 複数の値を追加するために、t、l、r、bもしくはこれらの任意の組み合わせを設定できます。例えば、`'lr'`を設定するとパディングの**l**left+**r**ightを取得します。 * @return {Number} 渡されたサイドを追加したパディングの値。 */ getPadding: function(side) { return this.addStyles(side, this.paddings); }, /** * styleプロパティを設定するための{@link #setStyle}のより柔軟なバージョンです。 * @param {String/Object/Function} styles スタイル指定文字列。 例えば、"width:100px"、または`{width:"100px"}`の形式のオブジェクト、またはそのような指定を返す関数です。 * @return {Ext.dom.Element} this */ applyStyles: function(styles) { if (styles) { var dom = this.dom, styleType, i, len; if (typeof styles == 'function') { styles = styles.call(); } styleType = typeof styles; if (styleType == 'string') { styles = Ext.util.Format.trim(styles).split(this.styleSplitRe); for (i = 0, len = styles.length; i < len;) { dom.style[Ext.dom.Element.normalize(styles[i++])] = styles[i++]; } } else if (styleType == 'object') { this.setStyle(styles); } } return this; }, /** * 要素のサイズを返します。 * @param {Boolean} [contentSize] `true` にするとボーダーとパディングを差し引いた幅/サイズを取得します。 * @return {Object} 要素のサイズを含むオブジェクト: * @return {Number} return.width * @return {Number} return.height */ getSize: function(contentSize) { var dom = this.dom; return { width: Math.max(0, contentSize ? (dom.clientWidth - this.getPadding("lr")) : dom.offsetWidth), height: Math.max(0, contentSize ? (dom.clientHeight - this.getPadding("tb")) : dom.offsetHeight) }; }, /** * ブラウザにこの要素を強制的に再描画させます。 * @return {Ext.dom.Element} this */ repaint: function() { var dom = this.dom; this.addCls(Ext.baseCSSPrefix + 'repaint'); setTimeout(function() { Ext.fly(dom).removeCls(Ext.baseCSSPrefix + 'repaint'); }, 1); return this; }, /** * サイドが指定されないとき、この要素のマージンを表すtop、left、right、bottomプロパティを含むオブジェクトを返します。 それから、計算されたサイドの幅を返します。 {@link #getPadding}を参照してください。 * @param {String} [sides] サイドを取得するためのl、r、t、bの組み合わせ。 * @return {Object/Number} */ getMargin: function(side) { var me = this, hash = {t: "top", l: "left", r: "right", b: "bottom"}, o = {}, key; if (!side) { for (key in me.margins) { o[hash[key]] = parseFloat(me.getStyle(me.margins[key])) || 0; } return o; } else { return me.addStyles.call(me, side, me.margins); } }, translate: function() { var transformStyleName = 'webkitTransform' in document.createElement('div').style ? 'webkitTransform' : 'transform'; return function(x, y, z) { this.dom.style[transformStyleName] = 'translate3d(' + (x || 0) + 'px, ' + (y || 0) + 'px, ' + (z || 0) + 'px)'; } }() }); //<deprecated product=touch since=2.0> Ext.dom.Element.addMembers({ /** * 内部にコンテンツを配置できるような要素のサイズを返します。 * * 要素(もしくは先祖と成る要素)にCSSで`display: none`が設定されている場合、このサイズは0になります。 * * 例: * * var vpSize = Ext.getBody().getViewSize(); * * // all Windows created afterwards will have a default value of 90% height and 95% width * Ext.Window.override({ * width: vpSize.width * 0.9, * height: vpSize.height * 0.95 * }); * // To handle window resizing you would have to hook onto onWindowResize. * * @deprecated 2.0.0 * @return {Object} `width`と`height`を記述するオブジェクト。 * @return {Number} return.width * @return {Number} return.height */ getViewSize: function() { //<debug warn> Ext.Logger.deprecate("Ext.dom.Element.getViewSize() is deprecated", this); //</debug> var doc = document, dom = this.dom; if (dom == doc || dom == doc.body) { return { width: Element.getViewportWidth(), height: Element.getViewportHeight() }; } else { return { width: dom.clientWidth, height: dom.clientHeight }; } }, /** * 与えられたプロパティの値が視覚的に透明の場合は`true`を返します。これは'transparent'スタイル値、またはalphaコンポーネントが0のrgba値に起因します。 * @deprecated 2.0.0 * @param {String} prop 値がテストされるstyleプロパティ。 * @return {Boolean} styleプロパティが視覚的に透明の場合は`true`を返します。 */ isTransparent: function(prop) { //<debug warn> Ext.Logger.deprecate("Ext.dom.Element.isTransparent() is deprecated", this); //</debug> var value = this.getStyle(prop); return value ? this.transparentRe.test(value) : false; }, /** * この要素に1つ以上のCSSクラスを追加し、そのCSSクラスを全ての兄弟要素から削除します。 * @deprecated 2.0.0 * @param {String/String[]} className 追加するCSSクラス、またはクラスの配列。 * @return {Ext.dom.Element} this */ radioCls: function(className) { //<debug warn> Ext.Logger.deprecate("Ext.dom.Element.radioCls() is deprecated", this); //</debug> var cn = this.dom.parentNode.childNodes, v; className = Ext.isArray(className) ? className : [className]; for (var i = 0, len = cn.length; i < len; i++) { v = cn[i]; if (v && v.nodeType == 1) { Ext.fly(v, '_internal').removeCls(className); } } return this.addCls(className); } }); //</deprecated>