Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
YUI.add('dom-base', function (Y, NAME) {
2
 
3
/**
4
* @for DOM
5
* @module dom
6
*/
7
var documentElement = Y.config.doc.documentElement,
8
    Y_DOM = Y.DOM,
9
    TAG_NAME = 'tagName',
10
    OWNER_DOCUMENT = 'ownerDocument',
11
    EMPTY_STRING = '',
12
    addFeature = Y.Features.add,
13
    testFeature = Y.Features.test;
14
 
15
Y.mix(Y_DOM, {
16
    /**
17
     * Returns the text content of the HTMLElement.
18
     * @method getText
19
     * @param {HTMLElement} element The html element.
20
     * @return {String} The text content of the element (includes text of any descending elements).
21
     */
22
    getText: (documentElement.textContent !== undefined) ?
23
        function(element) {
24
            var ret = '';
25
            if (element) {
26
                ret = element.textContent;
27
            }
28
            return ret || '';
29
        } : function(element) {
30
            var ret = '';
31
            if (element) {
32
                ret = element.innerText || element.nodeValue; // might be a textNode
33
            }
34
            return ret || '';
35
        },
36
 
37
    /**
38
     * Sets the text content of the HTMLElement.
39
     * @method setText
40
     * @param {HTMLElement} element The html element.
41
     * @param {String} content The content to add.
42
     */
43
    setText: (documentElement.textContent !== undefined) ?
44
        function(element, content) {
45
            if (element) {
46
                element.textContent = content;
47
            }
48
        } : function(element, content) {
49
            if ('innerText' in element) {
50
                element.innerText = content;
51
            } else if ('nodeValue' in element) {
52
                element.nodeValue = content;
53
            }
54
    },
55
 
56
    CUSTOM_ATTRIBUTES: (!documentElement.hasAttribute) ? { // IE < 8
57
        'for': 'htmlFor',
58
        'class': 'className'
59
    } : { // w3c
60
        'htmlFor': 'for',
61
        'className': 'class'
62
    },
63
 
64
    /**
65
     * Provides a normalized attribute interface.
66
     * @method setAttribute
67
     * @param {HTMLElement} el The target element for the attribute.
68
     * @param {String} attr The attribute to set.
69
     * @param {String} val The value of the attribute.
70
     */
71
    setAttribute: function(el, attr, val, ieAttr) {
72
        if (el && attr && el.setAttribute) {
73
            attr = Y_DOM.CUSTOM_ATTRIBUTES[attr] || attr;
74
            el.setAttribute(attr, val, ieAttr);
75
        }
76
        else { Y.log('bad input to setAttribute', 'warn', 'dom'); }
77
    },
78
 
79
 
80
    /**
81
     * Provides a normalized attribute interface.
82
     * @method getAttribute
83
     * @param {HTMLElement} el The target element for the attribute.
84
     * @param {String} attr The attribute to get.
85
     * @return {String} The current value of the attribute.
86
     */
87
    getAttribute: function(el, attr, ieAttr) {
88
        ieAttr = (ieAttr !== undefined) ? ieAttr : 2;
89
        var ret = '';
90
        if (el && attr && el.getAttribute) {
91
            attr = Y_DOM.CUSTOM_ATTRIBUTES[attr] || attr;
92
            // BUTTON value issue for IE < 8
93
            ret = (el.tagName === "BUTTON" && attr === 'value') ? Y_DOM.getValue(el) : el.getAttribute(attr, ieAttr);
94
 
95
            if (ret === null) {
96
                ret = ''; // per DOM spec
97
            }
98
        }
99
        else { Y.log('bad input to getAttribute', 'warn', 'dom'); }
100
        return ret;
101
    },
102
 
103
    VALUE_SETTERS: {},
104
 
105
    VALUE_GETTERS: {},
106
 
107
    getValue: function(node) {
108
        var ret = '', // TODO: return null?
109
            getter;
110
 
111
        if (node && node[TAG_NAME]) {
112
            getter = Y_DOM.VALUE_GETTERS[node[TAG_NAME].toLowerCase()];
113
 
114
            if (getter) {
115
                ret = getter(node);
116
            } else {
117
                ret = node.value;
118
            }
119
        }
120
 
121
        // workaround for IE8 JSON stringify bug
122
        // which converts empty string values to null
123
        if (ret === EMPTY_STRING) {
124
            ret = EMPTY_STRING; // for real
125
        }
126
 
127
        return (typeof ret === 'string') ? ret : '';
128
    },
129
 
130
    setValue: function(node, val) {
131
        var setter;
132
 
133
        if (node && node[TAG_NAME]) {
134
            setter = Y_DOM.VALUE_SETTERS[node[TAG_NAME].toLowerCase()];
135
            val = (val === null) ? '' : val;
136
            if (setter) {
137
                setter(node, val);
138
            } else {
139
                node.value = val;
140
            }
141
        }
142
    },
143
 
144
    creators: {}
145
});
146
 
147
addFeature('value-set', 'select', {
148
    test: function() {
149
        var node = Y.config.doc.createElement('select');
150
        node.innerHTML = '<option>1</option><option>2</option>';
151
        node.value = '2';
152
        return (node.value && node.value === '2');
153
    }
154
});
155
 
156
if (!testFeature('value-set', 'select')) {
157
    Y_DOM.VALUE_SETTERS.select = function(node, val) {
158
        for (var i = 0, options = node.getElementsByTagName('option'), option;
159
                option = options[i++];) {
160
            if (Y_DOM.getValue(option) === val) {
161
                option.selected = true;
162
                //Y_DOM.setAttribute(option, 'selected', 'selected');
163
                break;
164
            }
165
        }
166
    };
167
}
168
 
169
Y.mix(Y_DOM.VALUE_GETTERS, {
170
    button: function(node) {
171
        return (node.attributes && node.attributes.value) ? node.attributes.value.value : '';
172
    }
173
});
174
 
175
Y.mix(Y_DOM.VALUE_SETTERS, {
176
    // IE: node.value changes the button text, which should be handled via innerHTML
177
    button: function(node, val) {
178
        var attr = node.attributes.value;
179
        if (!attr) {
180
            attr = node[OWNER_DOCUMENT].createAttribute('value');
181
            node.setAttributeNode(attr);
182
        }
183
 
184
        attr.value = val;
185
    }
186
});
187
 
188
 
189
Y.mix(Y_DOM.VALUE_GETTERS, {
190
    option: function(node) {
191
        var attrs = node.attributes;
192
        return (attrs.value && attrs.value.specified) ? node.value : node.text;
193
    },
194
 
195
    select: function(node) {
196
        var val = node.value,
197
            options = node.options;
198
 
199
        if (options && options.length) {
200
            // TODO: implement multipe select
201
            if (node.multiple) {
202
                Y.log('multiple select normalization not implemented', 'warn', 'DOM');
203
            } else if (node.selectedIndex > -1) {
204
                val = Y_DOM.getValue(options[node.selectedIndex]);
205
            }
206
        }
207
 
208
        return val;
209
    }
210
});
211
var addClass, hasClass, removeClass;
212
 
213
Y.mix(Y.DOM, {
214
    /**
215
     * Determines whether a DOM element has the given className.
216
     * @method hasClass
217
     * @for DOM
218
     * @param {HTMLElement} element The DOM element.
219
     * @param {String} className the class name to search for
220
     * @return {Boolean} Whether or not the element has the given class.
221
     */
222
    hasClass: function(node, className) {
223
        var re = Y.DOM._getRegExp('(?:^|\\s+)' + className + '(?:\\s+|$)');
224
        return re.test(node.className);
225
    },
226
 
227
    /**
228
     * Adds a class name to a given DOM element.
229
     * @method addClass
230
     * @for DOM
231
     * @param {HTMLElement} element The DOM element.
232
     * @param {String} className the class name to add to the class attribute
233
     */
234
    addClass: function(node, className) {
235
        if (!Y.DOM.hasClass(node, className)) { // skip if already present
236
            node.className = Y.Lang.trim([node.className, className].join(' '));
237
        }
238
    },
239
 
240
    /**
241
     * Removes a class name from a given element.
242
     * @method removeClass
243
     * @for DOM
244
     * @param {HTMLElement} element The DOM element.
245
     * @param {String} className the class name to remove from the class attribute
246
     */
247
    removeClass: function(node, className) {
248
        if (className && hasClass(node, className)) {
249
            node.className = Y.Lang.trim(node.className.replace(Y.DOM._getRegExp('(?:^|\\s+)' +
250
                            className + '(?:\\s+|$)'), ' '));
251
 
252
            if ( hasClass(node, className) ) { // in case of multiple adjacent
253
                removeClass(node, className);
254
            }
255
        }
256
    },
257
 
258
    /**
259
     * Replace a class with another class for a given element.
260
     * If no oldClassName is present, the newClassName is simply added.
261
     * @method replaceClass
262
     * @for DOM
263
     * @param {HTMLElement} element The DOM element
264
     * @param {String} oldClassName the class name to be replaced
265
     * @param {String} newClassName the class name that will be replacing the old class name
266
     */
267
    replaceClass: function(node, oldC, newC) {
268
        //Y.log('replaceClass replacing ' + oldC + ' with ' + newC, 'info', 'Node');
269
        removeClass(node, oldC); // remove first in case oldC === newC
270
        addClass(node, newC);
271
    },
272
 
273
    /**
274
     * If the className exists on the node it is removed, if it doesn't exist it is added.
275
     * @method toggleClass
276
     * @for DOM
277
     * @param {HTMLElement} element The DOM element
278
     * @param {String} className the class name to be toggled
279
     * @param {Boolean} addClass optional boolean to indicate whether class
280
     * should be added or removed regardless of current state
281
     */
282
    toggleClass: function(node, className, force) {
283
        var add = (force !== undefined) ? force :
284
                !(hasClass(node, className));
285
 
286
        if (add) {
287
            addClass(node, className);
288
        } else {
289
            removeClass(node, className);
290
        }
291
    }
292
});
293
 
294
hasClass = Y.DOM.hasClass;
295
removeClass = Y.DOM.removeClass;
296
addClass = Y.DOM.addClass;
297
 
298
var re_tag = /<([a-z]+)/i,
299
 
300
    Y_DOM = Y.DOM,
301
 
302
    addFeature = Y.Features.add,
303
    testFeature = Y.Features.test,
304
 
305
    creators = {},
306
 
307
    createFromDIV = function(html, tag) {
308
        var div = Y.config.doc.createElement('div'),
309
            ret = true;
310
 
311
        div.innerHTML = html;
312
        if (!div.firstChild || div.firstChild.tagName !== tag.toUpperCase()) {
313
            ret = false;
314
        }
315
 
316
        return ret;
317
    },
318
 
319
    re_tbody = /(?:\/(?:thead|tfoot|tbody|caption|col|colgroup)>)+\s*<tbody/,
320
 
321
    TABLE_OPEN = '<table>',
322
    TABLE_CLOSE = '</table>',
323
 
324
    selectedIndex;
325
 
326
Y.mix(Y.DOM, {
327
    _fragClones: {},
328
 
329
    _create: function(html, doc, tag) {
330
        tag = tag || 'div';
331
 
332
        var frag = Y_DOM._fragClones[tag];
333
        if (frag) {
334
            frag = frag.cloneNode(false);
335
        } else {
336
            frag = Y_DOM._fragClones[tag] = doc.createElement(tag);
337
        }
338
        frag.innerHTML = html;
339
        return frag;
340
    },
341
 
342
    _children: function(node, tag) {
343
            var i = 0,
344
            children = node.children,
345
            childNodes,
346
            hasComments,
347
            child;
348
 
349
        if (children && children.tags) { // use tags filter when possible
350
            if (tag) {
351
                children = node.children.tags(tag);
352
            } else { // IE leaks comments into children
353
                hasComments = children.tags('!').length;
354
            }
355
        }
356
 
357
        if (!children || (!children.tags && tag) || hasComments) {
358
            childNodes = children || node.childNodes;
359
            children = [];
360
            while ((child = childNodes[i++])) {
361
                if (child.nodeType === 1) {
362
                    if (!tag || tag === child.tagName) {
363
                        children.push(child);
364
                    }
365
                }
366
            }
367
        }
368
 
369
        return children || [];
370
    },
371
 
372
    /**
373
     * Creates a new dom node using the provided markup string.
374
     * @method create
375
     * @param {String} html The markup used to create the element
376
     * @param {HTMLDocument} doc An optional document context
377
     * @return {HTMLElement|DocumentFragment} returns a single HTMLElement
378
     * when creating one node, and a documentFragment when creating
379
     * multiple nodes.
380
     */
381
    create: function(html, doc) {
382
        if (typeof html === 'string') {
383
            html = Y.Lang.trim(html); // match IE which trims whitespace from innerHTML
384
 
385
        }
386
 
387
        doc = doc || Y.config.doc;
388
        var m = re_tag.exec(html),
389
            create = Y_DOM._create,
390
            custom = creators,
391
            ret = null,
392
            creator, tag, node, nodes;
393
 
394
        if (html != undefined) { // not undefined or null
395
            if (m && m[1]) {
396
                creator = custom[m[1].toLowerCase()];
397
                if (typeof creator === 'function') {
398
                    create = creator;
399
                } else {
400
                    tag = creator;
401
                }
402
            }
403
 
404
            node = create(html, doc, tag);
405
            nodes = node.childNodes;
406
 
407
            if (nodes.length === 1) { // return single node, breaking parentNode ref from "fragment"
408
                ret = node.removeChild(nodes[0]);
409
            } else if (nodes[0] && nodes[0].className === 'yui3-big-dummy') { // using dummy node to preserve some attributes (e.g. OPTION not selected)
410
                selectedIndex = node.selectedIndex;
411
 
412
                if (nodes.length === 2) {
413
                    ret = nodes[0].nextSibling;
414
                } else {
415
                    node.removeChild(nodes[0]);
416
                    ret = Y_DOM._nl2frag(nodes, doc);
417
                }
418
            } else { // return multiple nodes as a fragment
419
                 ret = Y_DOM._nl2frag(nodes, doc);
420
            }
421
 
422
        }
423
 
424
        return ret;
425
    },
426
 
427
    _nl2frag: function(nodes, doc) {
428
        var ret = null,
429
            i, len;
430
 
431
        if (nodes && (nodes.push || nodes.item) && nodes[0]) {
432
            doc = doc || nodes[0].ownerDocument;
433
            ret = doc.createDocumentFragment();
434
 
435
            if (nodes.item) { // convert live list to static array
436
                nodes = Y.Array(nodes, 0, true);
437
            }
438
 
439
            for (i = 0, len = nodes.length; i < len; i++) {
440
                ret.appendChild(nodes[i]);
441
            }
442
        } // else inline with log for minification
443
        return ret;
444
    },
445
 
446
    /**
447
     * Inserts content in a node at the given location
448
     * @method addHTML
449
     * @param {HTMLElement} node The node to insert into
450
     * @param {HTMLElement | Array | HTMLCollection} content The content to be inserted
451
     * @param {HTMLElement} where Where to insert the content
452
     * If no "where" is given, content is appended to the node
453
     * Possible values for "where"
454
     * <dl>
455
     * <dt>HTMLElement</dt>
456
     * <dd>The element to insert before</dd>
457
     * <dt>"replace"</dt>
458
     * <dd>Replaces the existing HTML</dd>
459
     * <dt>"before"</dt>
460
     * <dd>Inserts before the existing HTML</dd>
461
     * <dt>"before"</dt>
462
     * <dd>Inserts content before the node</dd>
463
     * <dt>"after"</dt>
464
     * <dd>Inserts content after the node</dd>
465
     * </dl>
466
     */
467
    addHTML: function(node, content, where) {
468
        var nodeParent = node.parentNode,
469
            i = 0,
470
            item,
471
            ret = content,
472
            newNode;
473
 
474
 
475
        if (content != undefined) { // not null or undefined (maybe 0)
476
            if (content.nodeType) { // DOM node, just add it
477
                newNode = content;
478
            } else if (typeof content == 'string' || typeof content == 'number') {
479
                ret = newNode = Y_DOM.create(content);
480
            } else if (content[0] && content[0].nodeType) { // array or collection
481
                newNode = Y.config.doc.createDocumentFragment();
482
                while ((item = content[i++])) {
483
                    newNode.appendChild(item); // append to fragment for insertion
484
                }
485
            }
486
        }
487
 
488
        if (where) {
489
            if (newNode && where.parentNode) { // insert regardless of relationship to node
490
                where.parentNode.insertBefore(newNode, where);
491
            } else {
492
                switch (where) {
493
                    case 'replace':
494
                        while (node.firstChild) {
495
                            node.removeChild(node.firstChild);
496
                        }
497
                        if (newNode) { // allow empty content to clear node
498
                            node.appendChild(newNode);
499
                        }
500
                        break;
501
                    case 'before':
502
                        if (newNode) {
503
                            nodeParent.insertBefore(newNode, node);
504
                        }
505
                        break;
506
                    case 'after':
507
                        if (newNode) {
508
                            if (node.nextSibling) { // IE errors if refNode is null
509
                                nodeParent.insertBefore(newNode, node.nextSibling);
510
                            } else {
511
                                nodeParent.appendChild(newNode);
512
                            }
513
                        }
514
                        break;
515
                    default:
516
                        if (newNode) {
517
                            node.appendChild(newNode);
518
                        }
519
                }
520
            }
521
        } else if (newNode) {
522
            node.appendChild(newNode);
523
        }
524
 
525
        // `select` elements are the only elements with `selectedIndex`.
526
        // Don't grab the dummy `option` element's `selectedIndex`.
527
        if (node.nodeName == "SELECT" && selectedIndex > 0) {
528
            node.selectedIndex = selectedIndex - 1;
529
        }
530
 
531
        return ret;
532
    },
533
 
534
    wrap: function(node, html) {
535
        var parent = (html && html.nodeType) ? html : Y.DOM.create(html),
536
            nodes = parent.getElementsByTagName('*');
537
 
538
        if (nodes.length) {
539
            parent = nodes[nodes.length - 1];
540
        }
541
 
542
        if (node.parentNode) {
543
            node.parentNode.replaceChild(parent, node);
544
        }
545
        parent.appendChild(node);
546
    },
547
 
548
    unwrap: function(node) {
549
        var parent = node.parentNode,
550
            lastChild = parent.lastChild,
551
            next = node,
552
            grandparent;
553
 
554
        if (parent) {
555
            grandparent = parent.parentNode;
556
            if (grandparent) {
557
                node = parent.firstChild;
558
                while (node !== lastChild) {
559
                    next = node.nextSibling;
560
                    grandparent.insertBefore(node, parent);
561
                    node = next;
562
                }
563
                grandparent.replaceChild(lastChild, parent);
564
            } else {
565
                parent.removeChild(node);
566
            }
567
        }
568
    }
569
});
570
 
571
addFeature('innerhtml', 'table', {
572
    test: function() {
573
        var node = Y.config.doc.createElement('table');
574
        try {
575
            node.innerHTML = '<tbody></tbody>';
576
        } catch(e) {
577
            return false;
578
        }
579
        return (node.firstChild && node.firstChild.nodeName === 'TBODY');
580
    }
581
});
582
 
583
addFeature('innerhtml-div', 'tr', {
584
    test: function() {
585
        return createFromDIV('<tr></tr>', 'tr');
586
    }
587
});
588
 
589
addFeature('innerhtml-div', 'script', {
590
    test: function() {
591
        return createFromDIV('<script></script>', 'script');
592
    }
593
});
594
 
595
if (!testFeature('innerhtml', 'table')) {
596
    // TODO: thead/tfoot with nested tbody
597
        // IE adds TBODY when creating TABLE elements (which may share this impl)
598
    creators.tbody = function(html, doc) {
599
        var frag = Y_DOM.create(TABLE_OPEN + html + TABLE_CLOSE, doc),
600
            tb = Y.DOM._children(frag, 'tbody')[0];
601
 
602
        if (frag.children.length > 1 && tb && !re_tbody.test(html)) {
603
            tb.parentNode.removeChild(tb); // strip extraneous tbody
604
        }
605
        return frag;
606
    };
607
}
608
 
609
if (!testFeature('innerhtml-div', 'script')) {
610
    creators.script = function(html, doc) {
611
        var frag = doc.createElement('div');
612
 
613
        frag.innerHTML = '-' + html;
614
        frag.removeChild(frag.firstChild);
615
        return frag;
616
    };
617
 
618
    creators.link = creators.style = creators.script;
619
}
620
 
621
if (!testFeature('innerhtml-div', 'tr')) {
622
    Y.mix(creators, {
623
        option: function(html, doc) {
624
            return Y_DOM.create('<select><option class="yui3-big-dummy" selected></option>' + html + '</select>', doc);
625
        },
626
 
627
        tr: function(html, doc) {
628
            return Y_DOM.create('<tbody>' + html + '</tbody>', doc);
629
        },
630
 
631
        td: function(html, doc) {
632
            return Y_DOM.create('<tr>' + html + '</tr>', doc);
633
        },
634
 
635
        col: function(html, doc) {
636
            return Y_DOM.create('<colgroup>' + html + '</colgroup>', doc);
637
        },
638
 
639
        tbody: 'table'
640
    });
641
 
642
    Y.mix(creators, {
643
        legend: 'fieldset',
644
        th: creators.td,
645
        thead: creators.tbody,
646
        tfoot: creators.tbody,
647
        caption: creators.tbody,
648
        colgroup: creators.tbody,
649
        optgroup: creators.option
650
    });
651
}
652
 
653
Y_DOM.creators = creators;
654
Y.mix(Y.DOM, {
655
    /**
656
     * Sets the width of the element to the given size, regardless
657
     * of box model, border, padding, etc.
658
     * @method setWidth
659
     * @param {HTMLElement} element The DOM element.
660
     * @param {String|Number} size The pixel height to size to
661
     */
662
 
663
    setWidth: function(node, size) {
664
        Y.DOM._setSize(node, 'width', size);
665
    },
666
 
667
    /**
668
     * Sets the height of the element to the given size, regardless
669
     * of box model, border, padding, etc.
670
     * @method setHeight
671
     * @param {HTMLElement} element The DOM element.
672
     * @param {String|Number} size The pixel height to size to
673
     */
674
 
675
    setHeight: function(node, size) {
676
        Y.DOM._setSize(node, 'height', size);
677
    },
678
 
679
    _setSize: function(node, prop, val) {
680
        val = (val > 0) ? val : 0;
681
        var size = 0;
682
 
683
        node.style[prop] = val + 'px';
684
        size = (prop === 'height') ? node.offsetHeight : node.offsetWidth;
685
 
686
        if (size > val) {
687
            val = val - (size - val);
688
 
689
            if (val < 0) {
690
                val = 0;
691
            }
692
 
693
            node.style[prop] = val + 'px';
694
        }
695
    }
696
});
697
 
698
 
699
}, '3.18.1', {"requires": ["dom-core"]});