]>
git.proxmox.com Git - sencha-touch.git/blob - src/src/dom/Helper.js
2 //@define Ext.DomHelper
3 //@require Ext.dom.Query
7 * @alternateClassName Ext.dom.Helper
10 * The DomHelper class provides a layer of abstraction from DOM and transparently supports creating elements via DOM or
11 * using HTML fragments. It also has the ability to create HTML fragment templates from your DOM building code.
13 * ## DomHelper element specification object
15 * A specification object is used when creating elements. Attributes of this object are assumed to be element
16 * attributes, except for 4 special attributes:
18 * * **tag**: The tag name of the element
19 * * **children (or cn)**: An array of the same kind of element definition objects to be created and appended. These
20 * can be nested as deep as you want.
21 * * **cls**: The class attribute of the element. This will end up being either the "class" attribute on a HTML
22 * fragment or className for a DOM node, depending on whether DomHelper is using fragments or DOM.
23 * * **html**: The innerHTML for the element
25 * ## Insertion methods
27 * Commonly used insertion methods:
30 * * {@link #insertBefore}
31 * * {@link #insertAfter}
32 * * {@link #overwrite}
33 * * {@link #insertHtml}
37 * This is an example, where an unordered list with 3 children items is appended to an existing element with id
40 * var dh = Ext.DomHelper; // create shorthand alias
41 * // specification object
46 * // append children after creating
47 * children: [ // may also specify 'cn' instead of 'children'
48 * {tag: 'li', id: 'item0', html: 'List Item 0'},
49 * {tag: 'li', id: 'item1', html: 'List Item 1'},
50 * {tag: 'li', id: 'item2', html: 'List Item 2'}
53 * var list = dh.append(
54 * 'my-div', // the context element 'my-div' can either be the id or the actual node
55 * spec // the specification object
58 * Element creation specification parameters in this class may also be passed as an Array of specification objects.
59 * This can be used to insert multiple sibling nodes into an existing container very efficiently. For example, to add
60 * more list items to the example above:
62 * dh.append('my-ul', [
63 * {tag: 'li', id: 'item3', html: 'List Item 3'},
64 * {tag: 'li', id: 'item4', html: 'List Item 4'}
69 * The real power is in the built-in templating. Instead of creating or appending any elements, createTemplate returns
70 * a Template object which can be used over and over to insert new elements. Revisiting the example above, we could
71 * utilize templating this time:
74 * var list = dh.append('my-div', {tag: 'ul', cls: 'my-list'});
76 * var tpl = dh.createTemplate({tag: 'li', id: 'item{0}', html: 'List Item {0}'});
78 * for(var i = 0; i < 5; i++){
79 * tpl.append(list, i); // use template to append to the actual node
82 * An example using a template:
84 * var html = '"{0}" href="{1}" class="nav">{2}';
86 * var tpl = new Ext.DomHelper.createTemplate(html);
87 * tpl.append('blog-roll', ['link1', 'http://www.tommymaintz.com/', "Tommy's Site"]);
88 * tpl.append('blog-roll', ['link2', 'http://www.avins.org/', "Jamie's Site"]);
90 * The same example using named parameters:
92 * var html = '"{id}" href="{url}" class="nav">{text}';
94 * var tpl = new Ext.DomHelper.createTemplate(html);
95 * tpl.append('blog-roll', {
97 * url: 'http://www.tommymaintz.com/',
98 * text: "Tommy's Site"
100 * tpl.append('blog-roll', {
102 * url: 'http://www.avins.org/',
103 * text: "Jamie's Site"
106 * ## Compiling Templates
108 * Templates are applied using regular expressions. The performance is great, but if you are adding a bunch of DOM
109 * elements using the same template, you can increase performance even further by "compiling" the template. The way
110 * "compile()" works is the template is parsed and broken up at the different variable points and a dynamic function is
111 * created and eval'ed. The generated function performs string concatenation of these parts and the passed variables
112 * instead of using regular expressions.
114 * var html = '"{id}" href="{url}" class="nav">{text}';
116 * var tpl = new Ext.DomHelper.createTemplate(html);
119 * // ... use template like normal
121 * ## Performance Boost
123 * DomHelper will transparently create HTML fragments when it can. Using HTML fragments instead of DOM can
124 * significantly boost performance.
126 * Element creation specification parameters may also be strings. If useDom is false, then the string is used as
127 * innerHTML. If useDom is true, a string specification results in the creation of a text node. Usage:
129 * Ext.DomHelper.useDom = true; // force it to use DOM; reduces performance
132 Ext
.define('Ext.dom.Helper', {
133 emptyTags
: /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i,
134 confRe
: /tag|children|cn|html|tpl|tplData$/i,
137 attribXlat
: { cls
: 'class', htmlFor
: 'for' },
141 decamelizeName : function () {
142 var camelCaseRe
= /([a-z])([A-Z])/g,
145 function decamel (match
, p1
, p2
) {
146 return p1
+ '-' + p2
.toLowerCase();
149 return function (s
) {
150 return cache
[s
] || (cache
[s
] = s
.replace(camelCaseRe
, decamel
));
154 generateMarkup: function(spec
, buffer
) {
156 attr
, val
, tag
, i
, closeTags
;
158 if (typeof spec
== "string") {
160 } else if (Ext
.isArray(spec
)) {
161 for (i
= 0; i
< spec
.length
; i
++) {
163 me
.generateMarkup(spec
[i
], buffer
);
167 tag
= spec
.tag
|| 'div';
168 buffer
.push('<', tag
);
171 if (spec
.hasOwnProperty(attr
)) {
173 if (!me
.confRe
.test(attr
)) {
174 if (typeof val
== "object") {
175 buffer
.push(' ', attr
, '="');
176 me
.generateStyles(val
, buffer
).push('"');
178 buffer
.push(' ', me
.attribXlat
[attr
] || attr
, '="', val
, '"');
184 // Now either just close the tag or try to add children and close the tag.
185 if (me
.emptyTags
.test(tag
)) {
190 // Apply the tpl html, and cn specifications
191 if ((val
= spec
.tpl
)) {
192 val
.applyOut(spec
.tplData
, buffer
);
194 if ((val
= spec
.html
)) {
197 if ((val
= spec
.cn
|| spec
.children
)) {
198 me
.generateMarkup(val
, buffer
);
201 // we generate a lot of close tags, so cache them rather than push 3 parts
202 closeTags
= me
.closeTags
;
203 buffer
.push(closeTags
[tag
] || (closeTags
[tag
] = '</' + tag
+ '>'));
211 * Converts the styles from the given object to text. The styles are CSS style names
212 * with their associated value.
214 * The basic form of this method returns a string:
216 * var s = Ext.DomHelper.generateStyles({
217 * backgroundColor: 'red'
220 * // s = 'background-color:red;'
222 * Alternatively, this method can append to an output array.
228 * Ext.DomHelper.generateStyles({
229 * backgroundColor: 'red'
232 * In this case, the style text is pushed on to the array and the array is returned.
234 * @param {Object} styles The object describing the styles.
235 * @param {String[]} [buffer] The output buffer.
236 * @return {String/String[]} If buffer is passed, it is returned. Otherwise the style
237 * string is returned.
239 generateStyles: function (styles
, buffer
) {
240 var a
= buffer
|| [],
243 for (name
in styles
) {
244 if (styles
.hasOwnProperty(name
)) {
245 a
.push(this.decamelizeName(name
), ':', styles
[name
], ';');
249 return buffer
|| a
.join('');
253 * Returns the markup for the passed Element(s) config.
254 * @param {Object} spec The DOM object spec (and children).
257 markup: function(spec
) {
258 if (typeof spec
== "string") {
262 var buf
= this.generateMarkup(spec
, []);
267 * Applies a style specification to an element.
268 * @param {String/HTMLElement} el The element to apply styles to
269 * @param {String/Object/Function} styles A style specification string e.g. 'width:100px', or object in the form {width:'100px'}, or
270 * a function which returns such a specification.
272 applyStyles: function(el
, styles
) {
273 Ext
.fly(el
).applyStyles(styles
);
278 * Fix for browsers which no longer support createContextualFragment
280 createContextualFragment: function(html
){
281 var div
= document
.createElement("div"),
282 fragment
= document
.createDocumentFragment(),
286 div
.innerHTML
= html
;
287 childNodes
= div
.childNodes
;
288 length
= childNodes
.length
;
290 for (; i
< length
; i
++) {
291 fragment
.appendChild(childNodes
[i
].cloneNode(true));
298 * Inserts an HTML fragment into the DOM.
299 * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
301 * For example take the following HTML: `<div>Contents</div>`
303 * Using different `where` values inserts element to the following places:
305 * - beforeBegin: `<HERE><div>Contents</div>`
306 * - afterBegin: `<div><HERE>Contents</div>`
307 * - beforeEnd: `<div>Contents<HERE></div>`
308 * - afterEnd: `<div>Contents</div><HERE>`
310 * @param {HTMLElement/TextNode} el The context element
311 * @param {String} html The HTML fragment
312 * @return {HTMLElement} The new node
314 insertHtml: function(where
, el
, html
) {
315 var setStart
, range
, frag
, rangeEl
, isBeforeBegin
, isAfterBegin
;
317 where
= where
.toLowerCase();
319 if (Ext
.isTextNode(el
)) {
320 if (where
== 'afterbegin' ) {
321 where
= 'beforebegin';
323 else if (where
== 'beforeend') {
328 isBeforeBegin
= where
== 'beforebegin';
329 isAfterBegin
= where
== 'afterbegin';
331 range
= Ext
.feature
.has
.CreateContextualFragment
? el
.ownerDocument
.createRange() : undefined;
332 setStart
= 'setStart' + (this.endRe
.test(where
) ? 'After' : 'Before');
334 if (isBeforeBegin
|| where
== 'afterend') {
337 frag
= range
.createContextualFragment(html
);
340 frag
= this.createContextualFragment(html
);
342 el
.parentNode
.insertBefore(frag
, isBeforeBegin
? el
: el
.nextSibling
);
343 return el
[(isBeforeBegin
? 'previous' : 'next') + 'Sibling'];
346 rangeEl
= (isAfterBegin
? 'first' : 'last') + 'Child';
349 // Creating ranges on a hidden element throws an error, checking for the element being painted is
350 // VERY expensive, so we'll catch the error and fall back to using the full fragment
352 range
[setStart
](el
[rangeEl
]);
353 frag
= range
.createContextualFragment(html
);
356 frag
= this.createContextualFragment(html
);
359 frag
= this.createContextualFragment(html
);
363 el
.insertBefore(frag
, el
.firstChild
);
365 el
.appendChild(frag
);
375 * Creates new DOM element(s) and inserts them before el.
376 * @param {String/HTMLElement/Ext.Element} el The context element
377 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
378 * @param {Boolean} [returnElement] true to return a Ext.Element
379 * @return {HTMLElement/Ext.Element} The new node
381 insertBefore: function(el
, o
, returnElement
) {
382 return this.doInsert(el
, o
, returnElement
, 'beforebegin');
386 * Creates new DOM element(s) and inserts them after el.
387 * @param {String/HTMLElement/Ext.Element} el The context element
388 * @param {Object} o The DOM object spec (and children)
389 * @param {Boolean} [returnElement] true to return a Ext.Element
390 * @return {HTMLElement/Ext.Element} The new node
392 insertAfter: function(el
, o
, returnElement
) {
393 return this.doInsert(el
, o
, returnElement
, 'afterend');
397 * Creates new DOM element(s) and inserts them as the first child of el.
398 * @param {String/HTMLElement/Ext.Element} el The context element
399 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
400 * @param {Boolean} [returnElement] true to return a Ext.Element
401 * @return {HTMLElement/Ext.Element} The new node
403 insertFirst: function(el
, o
, returnElement
) {
404 return this.doInsert(el
, o
, returnElement
, 'afterbegin');
408 * Creates new DOM element(s) and appends them to el.
409 * @param {String/HTMLElement/Ext.Element} el The context element
410 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
411 * @param {Boolean} [returnElement] true to return a Ext.Element
412 * @return {HTMLElement/Ext.Element} The new node
414 append: function(el
, o
, returnElement
) {
415 return this.doInsert(el
, o
, returnElement
, 'beforeend');
419 * Creates new DOM element(s) and overwrites the contents of el with them.
420 * @param {String/HTMLElement/Ext.Element} el The context element
421 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
422 * @param {Boolean} [returnElement] true to return a Ext.Element
423 * @return {HTMLElement/Ext.Element} The new node
425 overwrite: function(el
, o
, returnElement
) {
427 el
.innerHTML
= this.markup(o
);
428 return returnElement
? Ext
.get(el
.firstChild
) : el
.firstChild
;
431 doInsert: function(el
, o
, returnElement
, pos
) {
432 var newNode
= this.insertHtml(pos
, Ext
.getDom(el
), this.markup(o
));
433 return returnElement
? Ext
.get(newNode
, true) : newNode
;
437 * Creates a new Ext.Template from the DOM object spec.
438 * @param {Object} o The DOM object spec (and children)
439 * @return {Ext.Template} The new template
441 createTemplate: function(o
) {
442 var html
= this.markup(o
);
443 return new Ext
.Template(html
);
447 Ext
.core
.DomHelper
= Ext
.DomHelper
= new this;