summaryrefslogtreecommitdiff
path: root/frontend/delta/js/Clipperz/YUI/DomHelper.js
Unidiff
Diffstat (limited to 'frontend/delta/js/Clipperz/YUI/DomHelper.js') (more/less context) (ignore whitespace changes)
-rw-r--r--frontend/delta/js/Clipperz/YUI/DomHelper.js471
1 files changed, 471 insertions, 0 deletions
diff --git a/frontend/delta/js/Clipperz/YUI/DomHelper.js b/frontend/delta/js/Clipperz/YUI/DomHelper.js
new file mode 100644
index 0000000..0a1f9fe
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/YUI/DomHelper.js
@@ -0,0 +1,471 @@
1/*
2
3Copyright 2008-2013 Clipperz Srl
4
5This file is part of Clipperz, the online password manager.
6For further information about its features and functionalities please
7refer to http://www.clipperz.com.
8
9* Clipperz is free software: you can redistribute it and/or modify it
10 under the terms of the GNU Affero General Public License as published
11 by the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14* Clipperz is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 See the GNU Affero General Public License for more details.
18
19* You should have received a copy of the GNU Affero General Public
20 License along with Clipperz. If not, see http://www.gnu.org/licenses/.
21
22*/
23
24if (typeof(Clipperz) == 'undefined') { Clipperz = {}; }
25if (typeof(Clipperz.YUI) == 'undefined') { Clipperz.YUI = {}; }
26
27
28/**
29 * @class Clipperz.ext.DomHelper
30 * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
31 * For more information see <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
32 * @singleton
33 */
34Clipperz.YUI.DomHelper = new function(){
35 /**@private*/
36 var d = document;
37 var tempTableEl = null;
38 /** True to force the use of DOM instead of html fragments @type Boolean */
39 this.useDom = false;
40 var emptyTags = /^(?:base|basefont|br|frame|hr|img|input|isindex|link|meta|nextid|range|spacer|wbr|audioscope|area|param|keygen|col|limittext|spot|tab|over|right|left|choose|atop|of)$/i;
41 /**
42 * Applies a style specification to an element
43 * @param {String/HTMLElement} el The element to apply styles to
44 * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
45 * a function which returns such a specification.
46 */
47 this.applyStyles = function(el, styles){
48 if(styles){
49 var D = YAHOO.util.Dom;
50 if (typeof styles == "string"){
51 var re = /\s?([a-z\-]*)\:([^;]*);?/gi;
52 var matches;
53 while ((matches = re.exec(styles)) != null){
54 D.setStyle(el, matches[1], matches[2]);
55 }
56 }else if (typeof styles == "object"){
57 for (var style in styles){
58 D.setStyle(el, style, styles[style]);
59 }
60 }else if (typeof styles == "function"){
61 Clipperz.YUI.DomHelper.applyStyles(el, styles.call());
62 }
63 }
64 };
65
66 // build as innerHTML where available
67 /** @ignore */
68 var createHtml = function(o){
69 var b = '';
70
71 if(typeof(o['html']) != 'undefined') {
72 o['html'] = Clipperz.Base.sanitizeString(o['html']);
73 } else if (typeof(o['htmlString']) != 'undefined') {
74 o['html'] = o['htmlString'];
75 delete o.htmlString;
76 }
77
78 if (MochiKit.Base.isArrayLike(o)) {
79 for (var i = 0, l = o.length; i < l; i++) {
80 b += createHtml(o[i]);
81 }
82 return b;
83 }
84
85 b += '<' + o.tag;
86 for(var attr in o){
87 if(attr == 'tag' || attr == 'children' || attr == 'html' || typeof o[attr] == 'function') continue;
88 if(attr == 'style'){
89 var s = o['style'];
90 if(typeof s == 'function'){
91 s = s.call();
92 }
93 if(typeof s == 'string'){
94 b += ' style="' + s + '"';
95 }else if(typeof s == 'object'){
96 b += ' style="';
97 for(var key in s){
98 if(typeof s[key] != 'function'){
99 b += key + ':' + s[key] + ';';
100 }
101 }
102 b += '"';
103 }
104 }else{
105 if(attr == 'cls'){
106 b += ' class="' + o['cls'] + '"';
107 }else if(attr == 'htmlFor'){
108 b += ' for="' + o['htmlFor'] + '"';
109 }else{
110 b += ' ' + attr + '="' + o[attr] + '"';
111 }
112 }
113 }
114 if(emptyTags.test(o.tag)){
115 b += ' />';
116 }else{
117 b += '>';
118 if(o.children){
119 for(var i = 0, len = o.children.length; i < len; i++) {
120 b += createHtml(o.children[i], b);
121 }
122 }
123 if(o.html){
124 b += o.html;
125 }
126 b += '</' + o.tag + '>';
127 }
128 return b;
129 }
130
131 // build as dom
132 /** @ignore */
133 var createDom = function(o, parentNode){
134 var el = d.createElement(o.tag);
135 var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
136 for(var attr in o){
137 if(attr == 'tag' || attr == 'children' || attr == 'html' || attr == 'style' || typeof o[attr] == 'function') continue;
138 if(attr=='cls'){
139 el.className = o['cls'];
140 }else{
141 if(useSet) el.setAttribute(attr, o[attr]);
142 else el[attr] = o[attr];
143 }
144 }
145 Clipperz.YUI.DomHelper.applyStyles(el, o.style);
146 if(o.children){
147 for(var i = 0, len = o.children.length; i < len; i++) {
148 createDom(o.children[i], el);
149 }
150 }
151 if(o.html){
152 el.innerHTML = o.html;
153 }
154 if(parentNode){
155 parentNode.appendChild(el);
156 }
157 return el;
158 };
159
160 /**
161 * @ignore
162 * Nasty code for IE's broken table implementation
163 */
164 var insertIntoTable = function(tag, where, el, html){
165 if(!tempTableEl){
166 tempTableEl = document.createElement('div');
167 }
168 var nodes;
169 if(tag == 'table' || tag == 'tbody'){
170 tempTableEl.innerHTML = '<table><tbody>'+html+'</tbody></table>';
171 nodes = tempTableEl.firstChild.firstChild.childNodes;
172 }else{
173 tempTableEl.innerHTML = '<table><tbody><tr>'+html+'</tr></tbody></table>';
174 nodes = tempTableEl.firstChild.firstChild.firstChild.childNodes;
175 }
176 if (where == 'beforebegin') {
177 nodes.reverse();
178 // el.parentNode.insertBefore(node, el);
179 MochiKit.Base.map(function(aNode) {el.parentNode.insertBefore(aNode, el)}, nodes);
180 } else if (where == 'afterbegin') {
181 nodes.reverse();
182 // el.insertBefore(node, el.firstChild);
183 MochiKit.Base.map(function(aNode) {el.insertBefore(aNode, el.firstChild)}, nodes);
184 } else if (where == 'beforeend') {
185 // el.appendChild(node);
186 MochiKit.Base.map(function(aNode) {el.appendChild(aNode)}, nodes);
187 } else if (where == 'afterend') {
188 // el.parentNode.insertBefore(node, el.nextSibling);
189 MochiKit.Base.map(function(aNode) {el.parentNode.insertBefore(aNode, el.nextSibling)}, nodes);
190 }
191
192 return nodes;
193 }
194
195 /**
196 * Inserts an HTML fragment into the Dom
197 * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
198 * @param {HTMLElement} el The context element
199 * @param {String} html The HTML fragmenet
200 * @return {HTMLElement} The new node
201 */
202 this.insertHtml = function(where, el, html){
203 where = where.toLowerCase();
204 // if(el.insertAdjacentHTML){
205 if(Clipperz_IEisBroken){
206 var tag = el.tagName.toLowerCase();
207 if(tag == 'table' || tag == 'tbody' || tag == 'tr'){
208 return insertIntoTable(tag, where, el, html);
209 }
210 switch(where){
211 case 'beforebegin':
212 el.insertAdjacentHTML(where, html);
213 return el.previousSibling;
214 case 'afterbegin':
215 el.insertAdjacentHTML(where, html);
216 return el.firstChild;
217 case 'beforeend':
218 el.insertAdjacentHTML(where, html);
219 return el.lastChild;
220 case 'afterend':
221 el.insertAdjacentHTML(where, html);
222 return el.nextSibling;
223 }
224 throw 'Illegal insertion point -> "' + where + '"';
225 }
226 var range = el.ownerDocument.createRange();
227 var frag;
228 switch(where){
229 case 'beforebegin':
230 range.setStartBefore(el);
231 frag = range.createContextualFragment(html);
232 el.parentNode.insertBefore(frag, el);
233 return el.previousSibling;
234 case 'afterbegin':
235 if(el.firstChild){ // faster
236 range.setStartBefore(el.firstChild);
237 }else{
238 range.selectNodeContents(el);
239 range.collapse(true);
240 }
241 frag = range.createContextualFragment(html);
242 el.insertBefore(frag, el.firstChild);
243 return el.firstChild;
244 case 'beforeend':
245 if(el.lastChild){
246 range.setStartAfter(el.lastChild); // faster
247 }else{
248 range.selectNodeContents(el);
249 range.collapse(false);
250 }
251 frag = range.createContextualFragment(html);
252 el.appendChild(frag);
253 return el.lastChild;
254 case 'afterend':
255 range.setStartAfter(el);
256 frag = range.createContextualFragment(html);
257 el.parentNode.insertBefore(frag, el.nextSibling);
258 return el.nextSibling;
259 }
260 throw 'Illegal insertion point -> "' + where + '"';
261 };
262
263 /**
264 * Creates new Dom element(s) and inserts them before el
265 * @param {String/HTMLElement/Element} el The context element
266 * @param {Object} o The Dom object spec (and children)
267 * @param {<i>Boolean</i>} returnElement (optional) true to return a YAHOO.Element
268 * @return {HTMLElement} The new node
269 */
270 this.insertBefore = function(el, o, returnElement){
271 el = el.dom ? el.dom : YAHOO.util.Dom.get(el);
272 var newNode;
273 if(this.useDom){
274 newNode = createDom(o, null);
275 el.parentNode.insertBefore(newNode, el);
276 }else{
277 var html = createHtml(o);
278 newNode = this.insertHtml('beforeBegin', el, html);
279 }
280 return returnElement ? YAHOO.Element.get(newNode, true) : newNode;
281 };
282
283 /**
284 * Creates new Dom element(s) and inserts them after el
285 * @param {String/HTMLElement/Element} el The context element
286 * @param {Object} o The Dom object spec (and children)
287 * @param {<i>Boolean</i>} returnElement (optional) true to return a YAHOO.Element
288 * @return {HTMLElement} The new node
289 */
290 this.insertAfter = function(el, o, returnElement){
291 el = el.dom ? el.dom : YAHOO.util.Dom.get(el);
292 var newNode;
293 if(this.useDom){
294 newNode = createDom(o, null);
295 el.parentNode.insertBefore(newNode, el.nextSibling);
296 }else{
297 var html = createHtml(o);
298 newNode = this.insertHtml('afterEnd', el, html);
299 }
300 return returnElement ? YAHOO.Element.get(newNode, true) : newNode;
301 };
302
303 /**
304 * Creates new Dom element(s) and appends them to el
305 * @param {String/HTMLElement/Element} el The context element
306 * @param {Object} o The Dom object spec (and children)
307 * @param {<i>Boolean</i>} returnElement (optional) true to return a YAHOO.Element
308 * @return {HTMLElement} The new node
309 */
310 this.append = function(el, o, returnElement){
311 el = el.dom ? el.dom : YAHOO.util.Dom.get(el);
312 var newNode;
313 if(this.useDom){
314 newNode = createDom(o, null);
315 el.appendChild(newNode);
316 }else{
317 var html = createHtml(o);
318 newNode = this.insertHtml('beforeEnd', el, html);
319 }
320 return returnElement ? YAHOO.Element.get(newNode, true) : newNode;
321 };
322
323 /**
324 * Creates new Dom element(s) and overwrites the contents of el with them
325 * @param {String/HTMLElement/Element} el The context element
326 * @param {Object} o The Dom object spec (and children)
327 * @param {<i>Boolean</i>} returnElement (optional) true to return a YAHOO.Element
328 * @return {HTMLElement} The new node
329 */
330 this.overwrite = function(el, o, returnElement){
331 el = el.dom ? el.dom : YAHOO.util.Dom.get(el);
332 el.innerHTML = createHtml(o);
333 return returnElement ? YAHOO.Element.get(el.firstChild, true) : el.firstChild;
334 };
335
336 /**
337 * Creates a new Clipperz.YUI.DomHelper.Template from the Dom object spec
338 * @param {Object} o The Dom object spec (and children)
339 * @return {Clipperz.YUI.DomHelper.Template} The new template
340 */
341 this.createTemplate = function(o){
342 var html = createHtml(o);
343 return new Clipperz.YUI.DomHelper.Template(html);
344 };
345}();
346
347/**
348* @class Clipperz.YUI.DomHelper.Template
349* Represents an HTML fragment template.
350* For more information see <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
351* <br>
352* <b>This class is also available as Clipperz.YUI.Template</b>.
353* @constructor
354* @param {String/Array} html The HTML fragment or an array of fragments to join('') or multiple arguments to join('')
355*/
356Clipperz.YUI.DomHelper.Template = function(html){
357 if(html instanceof Array){
358 html = html.join('');
359 }else if(arguments.length > 1){
360 html = Array.prototype.join.call(arguments, '');
361 }
362 /**@private*/
363 this.html = html;
364};
365Clipperz.YUI.DomHelper.Template.prototype = {
366 /**
367 * Returns an HTML fragment of this template with the specified values applied
368 * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
369 * @return {String}
370 */
371 applyTemplate : function(values){
372 if(this.compiled){
373 return this.compiled(values);
374 }
375 var empty = '';
376 var fn = function(match, index){
377 if(typeof values[index] != 'undefined'){
378 return values[index];
379 }else{
380 return empty;
381 }
382 }
383 return this.html.replace(this.re, fn);
384 },
385
386 /**
387 * The regular expression used to match template variables
388 * @type RegExp
389 * @property
390 */
391 re : /\{([\w|-]+)\}/g,
392
393 /**
394 * Compiles the template into an internal function, eliminating the RegEx overhead
395 */
396 compile : function(){
397 var body = ["this.compiled = function(values){ return ['"];
398 body.push(this.html.replace(this.re, "', values['$1'], '"));
399 body.push("'].join('');};");
400 eval(body.join(''));
401 return this;
402 },
403
404 /**
405 * Applies the supplied values to the template and inserts the new node(s) before el
406 * @param {String/HTMLElement/Element} el The context element
407 * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
408 * @param {<i>Boolean</i>} returnElement (optional) true to return a YAHOO.Element
409 * @return {HTMLElement} The new node
410 */
411 insertBefore: function(el, values, returnElement){
412 el = el.dom ? el.dom : YAHOO.util.Dom.get(el);
413 var newNode = Clipperz.YUI.DomHelper.insertHtml('beforeBegin', el, this.applyTemplate(values));
414 return returnElement ? YAHOO.Element.get(newNode, true) : newNode;
415 },
416
417 /**
418 * Applies the supplied values to the template and inserts the new node(s) after el
419 * @param {String/HTMLElement/Element} el The context element
420 * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
421 * @param {<i>Boolean</i>} returnElement (optional) true to return a YAHOO.Element
422 * @return {HTMLElement} The new node
423 */
424 insertAfter : function(el, values, returnElement){
425 el = el.dom ? el.dom : YAHOO.util.Dom.get(el);
426 var newNode = Clipperz.YUI.DomHelper.insertHtml('afterEnd', el, this.applyTemplate(values));
427 return returnElement ? YAHOO.Element.get(newNode, true) : newNode;
428 },
429
430 /**
431 * Applies the supplied values to the template and append the new node(s) to el
432 * @param {String/HTMLElement/Element} el The context element
433 * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
434 * @param {<i>Boolean</i>} returnElement (optional) true to return a YAHOO.Element
435 * @return {HTMLElement} The new node
436 */
437 append : function(el, values, returnElement){
438 var sanitizedValues;
439 var key;
440
441 sanitizedValues = {};
442 for (key in values) {
443 sanitizedValues[key] = Clipperz.Base.sanitizeString(values[key]);
444 }
445 el = (typeof el == 'string') ? YAHOO.util.Dom.get(el) : el;
446 var newNode = Clipperz.YUI.DomHelper.insertHtml('beforeEnd', el, this.applyTemplate(sanitizedValues));
447
448 return newNode;
449 },
450
451 /**
452 * Applies the supplied values to the template and overwrites the content of el with the new node(s)
453 * @param {String/HTMLElement/Element} el The context element
454 * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
455 * @param {<i>Boolean</i>} returnElement (optional) true to return a YAHOO.Element
456 * @return {HTMLElement} The new node
457 */
458 overwrite : function(el, values, returnElement){
459 el = el.dom ? el.dom : YAHOO.util.Dom.get(el);
460 el.innerHTML = '';
461 var newNode = Clipperz.YUI.DomHelper.insertHtml('beforeEnd', el, this.applyTemplate(values));
462 return returnElement ? YAHOO.Element.get(newNode, true) : newNode;
463 }
464};
465/**
466 * Alias for applyTemplate
467 * @method
468 */
469Clipperz.YUI.DomHelper.Template.prototype.apply = Clipperz.YUI.DomHelper.Template.prototype.applyTemplate;
470
471Clipperz.YUI.Template = Clipperz.YUI.DomHelper.Template;