~lutostag/ubuntu/utopic/maas/1.5.2

« back to all changes in this revision

Viewing changes to src/maasserver/static/jslibs/yui/3.4.1/build/selection/selection.js

  • Committer: Package Import Robot
  • Author(s): Andres Rodriguez
  • Date: 2012-03-15 18:14:08 UTC
  • mfrom: (1.1.3)
  • Revision ID: package-import@ubuntu.com-20120315181408-zgl94hzo0x4n99an
Tags: 0.1+bzr295+dfsg-0ubuntu2
* debian/patches:
  - 01-fix-database-settings.patch: Update to set PSERV_URL.
  - 02-pserv-config.patch: Set port to 8001.
* debian/maas.postinst: Run maas-import-isos on install.
* debian/control: Depends on rabbitmq-server.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
YUI 3.4.1 (build 4118)
 
3
Copyright 2011 Yahoo! Inc. All rights reserved.
 
4
Licensed under the BSD License.
 
5
http://yuilibrary.com/license/
 
6
*/
 
7
YUI.add('selection', function(Y) {
 
8
 
 
9
    /**
 
10
     * Wraps some common Selection/Range functionality into a simple object
 
11
     * @class Selection
 
12
     * @constructor
 
13
     * @module editor
 
14
     * @submodule selection
 
15
     */
 
16
    
 
17
    //TODO This shouldn't be there, Y.Node doesn't normalize getting textnode content.
 
18
    var textContent = 'textContent',
 
19
    INNER_HTML = 'innerHTML',
 
20
    FONT_FAMILY = 'fontFamily';
 
21
 
 
22
    if (Y.UA.ie) {
 
23
        textContent = 'nodeValue';
 
24
    }
 
25
 
 
26
    Y.Selection = function(domEvent) {
 
27
        var sel, par, ieNode, nodes, rng, i;
 
28
 
 
29
        if (Y.config.win.getSelection) {
 
30
                sel = Y.config.win.getSelection();
 
31
        } else if (Y.config.doc.selection) {
 
32
            sel = Y.config.doc.selection.createRange();
 
33
        }
 
34
        this._selection = sel;
 
35
 
 
36
        if (sel.pasteHTML) {
 
37
            this.isCollapsed = (sel.compareEndPoints('StartToEnd', sel)) ? false : true;
 
38
            if (this.isCollapsed) {
 
39
                this.anchorNode = this.focusNode = Y.one(sel.parentElement());
 
40
 
 
41
                if (domEvent) {
 
42
                    ieNode = Y.config.doc.elementFromPoint(domEvent.clientX, domEvent.clientY);
 
43
                }
 
44
                rng = sel.duplicate();
 
45
                if (!ieNode) {
 
46
                    par = sel.parentElement();
 
47
                    nodes = par.childNodes;
 
48
 
 
49
                    for (i = 0; i < nodes.length; i++) {
 
50
                        //This causes IE to not allow a selection on a doubleclick
 
51
                        //rng.select(nodes[i]);
 
52
                        if (rng.inRange(sel)) {
 
53
                            if (!ieNode) {
 
54
                                ieNode = nodes[i];
 
55
                            }
 
56
                        }
 
57
                    }
 
58
                }
 
59
 
 
60
                this.ieNode = ieNode;
 
61
                
 
62
                if (ieNode) {
 
63
                    if (ieNode.nodeType !== 3) {
 
64
                        if (ieNode.firstChild) {
 
65
                            ieNode = ieNode.firstChild;
 
66
                        }
 
67
                        if (ieNode && ieNode.tagName && ieNode.tagName.toLowerCase() === 'body') {
 
68
                            if (ieNode.firstChild) {
 
69
                                ieNode = ieNode.firstChild;
 
70
                            }
 
71
                        }
 
72
                    }
 
73
                    this.anchorNode = this.focusNode = Y.Selection.resolve(ieNode);
 
74
                    
 
75
                    rng.moveToElementText(sel.parentElement());
 
76
                    var comp = sel.compareEndPoints('StartToStart', rng),
 
77
                    moved = 0;
 
78
                    if (comp) {
 
79
                        //We are not at the beginning of the selection.
 
80
                        //Setting the move to something large, may need to increase it later
 
81
                        moved = Math.abs(sel.move('character', -9999));
 
82
                    }
 
83
                    
 
84
                    this.anchorOffset = this.focusOffset = moved;
 
85
                    
 
86
                    this.anchorTextNode = this.focusTextNode = Y.one(ieNode);
 
87
                }
 
88
                
 
89
                
 
90
            } else {
 
91
                //This helps IE deal with a selection and nodeChange events
 
92
                if (sel.htmlText && sel.htmlText !== '') {
 
93
                    var n = Y.Node.create(sel.htmlText);
 
94
                    if (n && n.get('id')) {
 
95
                        var id = n.get('id');
 
96
                        this.anchorNode = this.focusNode = Y.one('#' + id);
 
97
                    } else if (n) {
 
98
                        n = n.get('childNodes');
 
99
                        this.anchorNode = this.focusNode = n.item(0);
 
100
                    }
 
101
                }
 
102
            }
 
103
 
 
104
            //var self = this;
 
105
            //debugger;
 
106
        } else {
 
107
            this.isCollapsed = sel.isCollapsed;
 
108
            this.anchorNode = Y.Selection.resolve(sel.anchorNode);
 
109
            this.focusNode = Y.Selection.resolve(sel.focusNode);
 
110
            this.anchorOffset = sel.anchorOffset;
 
111
            this.focusOffset = sel.focusOffset;
 
112
            
 
113
            this.anchorTextNode = Y.one(sel.anchorNode);
 
114
            this.focusTextNode = Y.one(sel.focusNode);
 
115
        }
 
116
        if (Y.Lang.isString(sel.text)) {
 
117
            this.text = sel.text;
 
118
        } else {
 
119
            if (sel.toString) {
 
120
                this.text = sel.toString();
 
121
            } else {
 
122
                this.text = '';
 
123
            }
 
124
        }
 
125
    };
 
126
    
 
127
    /**
 
128
    * Utility method to remove dead font-family styles from an element.
 
129
    * @static
 
130
    * @method removeFontFamily
 
131
    */
 
132
    Y.Selection.removeFontFamily = function(n) {
 
133
        n.removeAttribute('face');
 
134
        var s = n.getAttribute('style').toLowerCase();
 
135
        if (s === '' || (s == 'font-family: ')) {
 
136
            n.removeAttribute('style');
 
137
        }
 
138
        if (s.match(Y.Selection.REG_FONTFAMILY)) {
 
139
            s = s.replace(Y.Selection.REG_FONTFAMILY, '');
 
140
            n.setAttribute('style', s);
 
141
        }
 
142
    };
 
143
 
 
144
    /**
 
145
    * Performs a prefilter on all nodes in the editor. Looks for nodes with a style: fontFamily or font face
 
146
    * It then creates a dynamic class assigns it and removed the property. This is so that we don't lose
 
147
    * the fontFamily when selecting nodes.
 
148
    * @static
 
149
    * @method filter
 
150
    */
 
151
    Y.Selection.filter = function(blocks) {
 
152
        var startTime = (new Date()).getTime();
 
153
 
 
154
        var nodes = Y.all(Y.Selection.ALL),
 
155
            baseNodes = Y.all('strong,em'),
 
156
            doc = Y.config.doc, hrs,
 
157
            classNames = {}, cssString = '',
 
158
            ls;
 
159
 
 
160
        var startTime1 = (new Date()).getTime();
 
161
        nodes.each(function(n) {
 
162
            var raw = Y.Node.getDOMNode(n);
 
163
            if (raw.style[FONT_FAMILY]) {
 
164
                classNames['.' + n._yuid] = raw.style[FONT_FAMILY];
 
165
                n.addClass(n._yuid);
 
166
 
 
167
                Y.Selection.removeFontFamily(raw);
 
168
            }
 
169
            /*
 
170
            if (n.getStyle(FONT_FAMILY)) {
 
171
                classNames['.' + n._yuid] = n.getStyle(FONT_FAMILY);
 
172
                n.addClass(n._yuid);
 
173
                n.removeAttribute('face');
 
174
                n.setStyle(FONT_FAMILY, '');
 
175
                if (n.getAttribute('style') === '') {
 
176
                    n.removeAttribute('style');
 
177
                }
 
178
                //This is for IE
 
179
                if (n.getAttribute('style').toLowerCase() === 'font-family: ') {
 
180
                    n.removeAttribute('style');
 
181
                }
 
182
            }
 
183
            */
 
184
        });
 
185
        var endTime1 = (new Date()).getTime();
 
186
 
 
187
        Y.all('.hr').addClass('yui-skip').addClass('yui-non');
 
188
        
 
189
        if (Y.UA.ie) {
 
190
            hrs = doc.getElementsByTagName('hr');
 
191
            Y.each(hrs, function(hr) {
 
192
                var el = doc.createElement('div');
 
193
                    el.className = 'hr yui-non yui-skip';
 
194
                    
 
195
                    el.setAttribute('readonly', true);
 
196
                    el.setAttribute('contenteditable', false); //Keep it from being Edited
 
197
                    if (hr.parentNode) {
 
198
                        hr.parentNode.replaceChild(el, hr);
 
199
                    }
 
200
                    //Had to move to inline style. writes for ie's < 8. They don't render el.setAttribute('style');
 
201
                    var s = el.style;
 
202
                    s.border = '1px solid #ccc';
 
203
                    s.lineHeight = '0';
 
204
                    s.height = '0';
 
205
                    s.fontSize = '0';
 
206
                    s.marginTop = '5px';
 
207
                    s.marginBottom = '5px';
 
208
                    s.marginLeft = '0px';
 
209
                    s.marginRight = '0px';
 
210
                    s.padding = '0';
 
211
            });
 
212
        }
 
213
        
 
214
 
 
215
        Y.each(classNames, function(v, k) {
 
216
            cssString += k + ' { font-family: ' + v.replace(/"/gi, '') + '; }';
 
217
        });
 
218
        Y.StyleSheet(cssString, 'editor');
 
219
 
 
220
        
 
221
        //Not sure about this one?
 
222
        baseNodes.each(function(n, k) {
 
223
            var t = n.get('tagName').toLowerCase(),
 
224
                newTag = 'i';
 
225
            if (t === 'strong') {
 
226
                newTag = 'b';
 
227
            }
 
228
            Y.Selection.prototype._swap(baseNodes.item(k), newTag);
 
229
        });
 
230
 
 
231
        //Filter out all the empty UL/OL's
 
232
        ls = Y.all('ol,ul');
 
233
        ls.each(function(v, k) {
 
234
            var lis = v.all('li');
 
235
            if (!lis.size()) {
 
236
                v.remove();
 
237
            }
 
238
        });
 
239
        
 
240
        if (blocks) {
 
241
            Y.Selection.filterBlocks();
 
242
        }
 
243
        var endTime = (new Date()).getTime();
 
244
    };
 
245
 
 
246
    /**
 
247
    * Method attempts to replace all "orphined" text nodes in the main body by wrapping them with a <p>. Called from filter.
 
248
    * @static
 
249
    * @method filterBlocks
 
250
    */
 
251
    Y.Selection.filterBlocks = function() {
 
252
        var startTime = (new Date()).getTime();
 
253
        var childs = Y.config.doc.body.childNodes, i, node, wrapped = false, doit = true,
 
254
            sel, single, br, divs, spans, c, s;
 
255
 
 
256
        if (childs) {
 
257
            for (i = 0; i < childs.length; i++) {
 
258
                node = Y.one(childs[i]);
 
259
                if (!node.test(Y.Selection.BLOCKS)) {
 
260
                    doit = true;
 
261
                    if (childs[i].nodeType == 3) {
 
262
                        c = childs[i][textContent].match(Y.Selection.REG_CHAR);
 
263
                        s = childs[i][textContent].match(Y.Selection.REG_NON);
 
264
                        if (c === null && s) {
 
265
                            doit = false;
 
266
                            
 
267
                        }
 
268
                    }
 
269
                    if (doit) {
 
270
                        if (!wrapped) {
 
271
                            wrapped = [];
 
272
                        }
 
273
                        wrapped.push(childs[i]);
 
274
                    }
 
275
                } else {
 
276
                    wrapped = Y.Selection._wrapBlock(wrapped);
 
277
                }
 
278
            }
 
279
            wrapped = Y.Selection._wrapBlock(wrapped);
 
280
        }
 
281
 
 
282
        single = Y.all(Y.Selection.DEFAULT_BLOCK_TAG);
 
283
        if (single.size() === 1) {
 
284
            br = single.item(0).all('br');
 
285
            if (br.size() === 1) {
 
286
                if (!br.item(0).test('.yui-cursor')) {
 
287
                    br.item(0).remove();
 
288
                }
 
289
                var html = single.item(0).get('innerHTML');
 
290
                if (html === '' || html === ' ') {
 
291
                    single.set('innerHTML', Y.Selection.CURSOR);
 
292
                    sel = new Y.Selection();
 
293
                    sel.focusCursor(true, true);
 
294
                }
 
295
                if (br.item(0).test('.yui-cursor') && Y.UA.ie) {
 
296
                    br.item(0).remove();
 
297
                }
 
298
            }
 
299
        } else {
 
300
            single.each(function(p) {
 
301
                var html = p.get('innerHTML');
 
302
                if (html === '') {
 
303
                    p.remove();
 
304
                }
 
305
            });
 
306
        }
 
307
        
 
308
        if (!Y.UA.ie) {
 
309
            /*
 
310
            divs = Y.all('div, p');
 
311
            divs.each(function(d) {
 
312
                if (d.hasClass('yui-non')) {
 
313
                    return;
 
314
                }
 
315
                var html = d.get('innerHTML');
 
316
                if (html === '') {
 
317
                    d.remove();
 
318
                } else {
 
319
                    if (d.get('childNodes').size() == 1) {
 
320
                        if (d.ancestor('p')) {
 
321
                            d.replace(d.get('firstChild'));
 
322
                        }
 
323
                    }
 
324
                }
 
325
            });*/
 
326
 
 
327
            /* Removed this, as it was causing Pasting to be funky in Safari
 
328
            spans = Y.all('.Apple-style-span, .apple-style-span');
 
329
            spans.each(function(s) {
 
330
                s.setAttribute('style', '');
 
331
            });
 
332
            */
 
333
        }
 
334
 
 
335
 
 
336
        var endTime = (new Date()).getTime();
 
337
    };
 
338
 
 
339
    /**
 
340
    * Regular Expression used to find dead font-family styles
 
341
    * @static
 
342
    * @property REG_FONTFAMILY
 
343
    */   
 
344
    Y.Selection.REG_FONTFAMILY = /font-family: ;/;
 
345
 
 
346
    /**
 
347
    * Regular Expression to determine if a string has a character in it
 
348
    * @static
 
349
    * @property REG_CHAR
 
350
    */   
 
351
    Y.Selection.REG_CHAR = /[a-zA-Z-0-9_!@#\$%\^&*\(\)-=_+\[\]\\{}|;':",.\/<>\?]/gi;
 
352
 
 
353
    /**
 
354
    * Regular Expression to determine if a string has a non-character in it
 
355
    * @static
 
356
    * @property REG_NON
 
357
    */
 
358
    Y.Selection.REG_NON = /[\s|\n|\t]/gi;
 
359
 
 
360
    /**
 
361
    * Regular Expression to remove all HTML from a string
 
362
    * @static
 
363
    * @property REG_NOHTML
 
364
    */
 
365
    Y.Selection.REG_NOHTML = /<\S[^><]*>/g;
 
366
 
 
367
 
 
368
    /**
 
369
    * Wraps an array of elements in a Block level tag
 
370
    * @static
 
371
    * @private
 
372
    * @method _wrapBlock
 
373
    */
 
374
    Y.Selection._wrapBlock = function(wrapped) {
 
375
        if (wrapped) {
 
376
            var newChild = Y.Node.create('<' + Y.Selection.DEFAULT_BLOCK_TAG + '></' + Y.Selection.DEFAULT_BLOCK_TAG + '>'),
 
377
                firstChild = Y.one(wrapped[0]), i;
 
378
 
 
379
            for (i = 1; i < wrapped.length; i++) {
 
380
                newChild.append(wrapped[i]);
 
381
            }
 
382
            firstChild.replace(newChild);
 
383
            newChild.prepend(firstChild);
 
384
        }
 
385
        return false;
 
386
    };
 
387
 
 
388
    /**
 
389
    * Undoes what filter does enough to return the HTML from the Editor, then re-applies the filter.
 
390
    * @static
 
391
    * @method unfilter
 
392
    * @return {String} The filtered HTML
 
393
    */
 
394
    Y.Selection.unfilter = function() {
 
395
        var nodes = Y.all('body [class]'),
 
396
            html = '', nons, ids,
 
397
            body = Y.one('body');
 
398
        
 
399
        
 
400
        nodes.each(function(n) {
 
401
            if (n.hasClass(n._yuid)) {
 
402
                //One of ours
 
403
                n.setStyle(FONT_FAMILY, n.getStyle(FONT_FAMILY));
 
404
                n.removeClass(n._yuid);
 
405
                if (n.getAttribute('class') === '') {
 
406
                    n.removeAttribute('class');
 
407
                }
 
408
            }
 
409
        });
 
410
 
 
411
        nons = Y.all('.yui-non');
 
412
        nons.each(function(n) {
 
413
            if (!n.hasClass('yui-skip') && n.get('innerHTML') === '') {
 
414
                n.remove();
 
415
            } else {
 
416
                n.removeClass('yui-non').removeClass('yui-skip');
 
417
            }
 
418
        });
 
419
 
 
420
        ids = Y.all('body [id]');
 
421
        ids.each(function(n) {
 
422
            if (n.get('id').indexOf('yui_3_') === 0) {
 
423
                n.removeAttribute('id');
 
424
                n.removeAttribute('_yuid');
 
425
            }
 
426
        });
 
427
        
 
428
        if (body) {
 
429
            html = body.get('innerHTML');
 
430
        }
 
431
        
 
432
        Y.all('.hr').addClass('yui-skip').addClass('yui-non');
 
433
        
 
434
        /*
 
435
        nodes.each(function(n) {
 
436
            n.addClass(n._yuid);
 
437
            n.setStyle(FONT_FAMILY, '');
 
438
            if (n.getAttribute('style') === '') {
 
439
                n.removeAttribute('style');
 
440
            }
 
441
        });
 
442
        */
 
443
        
 
444
        return html;
 
445
    };
 
446
    /**
 
447
    * Resolve a node from the selection object and return a Node instance
 
448
    * @static
 
449
    * @method resolve
 
450
    * @param {HTMLElement} n The HTMLElement to resolve. Might be a TextNode, gives parentNode.
 
451
    * @return {Node} The Resolved node
 
452
    */
 
453
    Y.Selection.resolve = function(n) {
 
454
        if (n && n.nodeType === 3) {
 
455
            //Adding a try/catch here because in rare occasions IE will
 
456
            //Throw a error accessing the parentNode of a stranded text node.
 
457
            //In the case of Ctrl+Z (Undo)
 
458
            try {
 
459
                n = n.parentNode;
 
460
            } catch (re) {
 
461
                n = 'body';
 
462
            }
 
463
        }
 
464
        return Y.one(n);
 
465
    };
 
466
 
 
467
    /**
 
468
    * Returns the innerHTML of a node with all HTML tags removed.
 
469
    * @static
 
470
    * @method getText
 
471
    * @param {Node} node The Node instance to remove the HTML from
 
472
    * @return {String} The string of text
 
473
    */
 
474
    Y.Selection.getText = function(node) {
 
475
        var txt = node.get('innerHTML').replace(Y.Selection.REG_NOHTML, '');
 
476
        //Clean out the cursor subs to see if the Node is empty
 
477
        txt = txt.replace('<span><br></span>', '').replace('<br>', '');
 
478
        return txt;
 
479
    };
 
480
 
 
481
    //Y.Selection.DEFAULT_BLOCK_TAG = 'div';
 
482
    Y.Selection.DEFAULT_BLOCK_TAG = 'p';
 
483
 
 
484
    /**
 
485
    * The selector to use when looking for Nodes to cache the value of: [style],font[face]
 
486
    * @static
 
487
    * @property ALL
 
488
    */
 
489
    Y.Selection.ALL = '[style],font[face]';
 
490
 
 
491
    /**
 
492
    * The selector to use when looking for block level items.
 
493
    * @static
 
494
    * @property BLOCKS
 
495
    */
 
496
    Y.Selection.BLOCKS = 'p,div,ul,ol,table,style';
 
497
    /**
 
498
    * The temporary fontname applied to a selection to retrieve their values: yui-tmp
 
499
    * @static
 
500
    * @property TMP
 
501
    */
 
502
    Y.Selection.TMP = 'yui-tmp';
 
503
    /**
 
504
    * The default tag to use when creating elements: span
 
505
    * @static
 
506
    * @property DEFAULT_TAG
 
507
    */
 
508
    Y.Selection.DEFAULT_TAG = 'span';
 
509
 
 
510
    /**
 
511
    * The id of the outer cursor wrapper
 
512
    * @static
 
513
    * @property DEFAULT_TAG
 
514
    */
 
515
    Y.Selection.CURID = 'yui-cursor';
 
516
 
 
517
    /**
 
518
    * The id used to wrap the inner space of the cursor position
 
519
    * @static
 
520
    * @property CUR_WRAPID
 
521
    */
 
522
    Y.Selection.CUR_WRAPID = 'yui-cursor-wrapper';
 
523
 
 
524
    /**
 
525
    * The default HTML used to focus the cursor..
 
526
    * @static
 
527
    * @property CURSOR
 
528
    */
 
529
    Y.Selection.CURSOR = '<span><br class="yui-cursor"></span>';
 
530
 
 
531
    Y.Selection.hasCursor = function() {
 
532
        var cur = Y.all('#' + Y.Selection.CUR_WRAPID);
 
533
        return cur.size();
 
534
    };
 
535
 
 
536
    /**
 
537
    * Called from Editor keydown to remove the "extra" space before the cursor.
 
538
    * @static
 
539
    * @method cleanCursor
 
540
    */
 
541
    Y.Selection.cleanCursor = function() {
 
542
        var cur, sel = 'br.yui-cursor';
 
543
        cur = Y.all(sel);
 
544
        if (cur.size()) {
 
545
            cur.each(function(b) {
 
546
                var c = b.get('parentNode.parentNode.childNodes'), html;
 
547
                if (c.size()) {
 
548
                    b.remove();
 
549
                } else {
 
550
                    html = Y.Selection.getText(c.item(0));
 
551
                    if (html !== '') {
 
552
                        b.remove();
 
553
                    }
 
554
                }
 
555
            });
 
556
        }
 
557
        /*
 
558
        var cur = Y.all('#' + Y.Selection.CUR_WRAPID);
 
559
        if (cur.size()) {
 
560
            cur.each(function(c) {
 
561
                var html = c.get('innerHTML');
 
562
                if (html == '&nbsp;' || html == '<br>') {
 
563
                    if (c.previous() || c.next()) {
 
564
                        c.remove();
 
565
                    }
 
566
                }
 
567
            });
 
568
        }
 
569
        */
 
570
    };
 
571
 
 
572
    Y.Selection.prototype = {
 
573
        /**
 
574
        * Range text value
 
575
        * @property text
 
576
        * @type String
 
577
        */
 
578
        text: null,
 
579
        /**
 
580
        * Flag to show if the range is collapsed or not
 
581
        * @property isCollapsed
 
582
        * @type Boolean
 
583
        */
 
584
        isCollapsed: null,
 
585
        /**
 
586
        * A Node instance of the parentNode of the anchorNode of the range
 
587
        * @property anchorNode
 
588
        * @type Node
 
589
        */
 
590
        anchorNode: null,
 
591
        /**
 
592
        * The offset from the range object
 
593
        * @property anchorOffset
 
594
        * @type Number
 
595
        */
 
596
        anchorOffset: null,
 
597
        /**
 
598
        * A Node instance of the actual textNode of the range.
 
599
        * @property anchorTextNode
 
600
        * @type Node
 
601
        */
 
602
        anchorTextNode: null,
 
603
        /**
 
604
        * A Node instance of the parentNode of the focusNode of the range
 
605
        * @property focusNode
 
606
        * @type Node
 
607
        */
 
608
        focusNode: null,
 
609
        /**
 
610
        * The offset from the range object
 
611
        * @property focusOffset
 
612
        * @type Number
 
613
        */
 
614
        focusOffset: null,
 
615
        /**
 
616
        * A Node instance of the actual textNode of the range.
 
617
        * @property focusTextNode
 
618
        * @type Node
 
619
        */
 
620
        focusTextNode: null,
 
621
        /**
 
622
        * The actual Selection/Range object
 
623
        * @property _selection
 
624
        * @private
 
625
        */
 
626
        _selection: null,
 
627
        /**
 
628
        * Wrap an element, with another element 
 
629
        * @private
 
630
        * @method _wrap
 
631
        * @param {HTMLElement} n The node to wrap 
 
632
        * @param {String} tag The tag to use when creating the new element.
 
633
        * @return {HTMLElement} The wrapped node
 
634
        */
 
635
        _wrap: function(n, tag) {
 
636
            var tmp = Y.Node.create('<' + tag + '></' + tag + '>');
 
637
            tmp.set(INNER_HTML, n.get(INNER_HTML));
 
638
            n.set(INNER_HTML, '');
 
639
            n.append(tmp);
 
640
            return Y.Node.getDOMNode(tmp);
 
641
        },
 
642
        /**
 
643
        * Swap an element, with another element 
 
644
        * @private
 
645
        * @method _swap
 
646
        * @param {HTMLElement} n The node to swap 
 
647
        * @param {String} tag The tag to use when creating the new element.
 
648
        * @return {HTMLElement} The new node
 
649
        */
 
650
        _swap: function(n, tag) {
 
651
            var tmp = Y.Node.create('<' + tag + '></' + tag + '>');
 
652
            tmp.set(INNER_HTML, n.get(INNER_HTML));
 
653
            n.replace(tmp, n);
 
654
            return Y.Node.getDOMNode(tmp);
 
655
        },
 
656
        /**
 
657
        * Get all the nodes in the current selection. This method will actually perform a filter first.
 
658
        * Then it calls doc.execCommand('fontname', null, 'yui-tmp') to touch all nodes in the selection.
 
659
        * The it compiles a list of all nodes affected by the execCommand and builds a NodeList to return.
 
660
        * @method getSelected
 
661
        * @return {NodeList} A NodeList of all items in the selection.
 
662
        */
 
663
        getSelected: function() {
 
664
            Y.Selection.filter();
 
665
            Y.config.doc.execCommand('fontname', null, Y.Selection.TMP);
 
666
            var nodes = Y.all(Y.Selection.ALL),
 
667
                items = [];
 
668
            
 
669
            nodes.each(function(n, k) {
 
670
                if (n.getStyle(FONT_FAMILY) ==  Y.Selection.TMP) {
 
671
                    n.setStyle(FONT_FAMILY, '');
 
672
                    Y.Selection.removeFontFamily(n);
 
673
                    if (!n.test('body')) {
 
674
                        items.push(Y.Node.getDOMNode(nodes.item(k)));
 
675
                    }
 
676
                }
 
677
            });
 
678
            return Y.all(items);
 
679
        },
 
680
        /**
 
681
        * Insert HTML at the current cursor position and return a Node instance of the newly inserted element.
 
682
        * @method insertContent
 
683
        * @param {String} html The HTML to insert.
 
684
        * @return {Node} The inserted Node.
 
685
        */
 
686
        insertContent: function(html) {
 
687
            return this.insertAtCursor(html, this.anchorTextNode, this.anchorOffset, true);
 
688
        },
 
689
        /**
 
690
        * Insert HTML at the current cursor position, this method gives you control over the text node to insert into and the offset where to put it.
 
691
        * @method insertAtCursor
 
692
        * @param {String} html The HTML to insert.
 
693
        * @param {Node} node The text node to break when inserting.
 
694
        * @param {Number} offset The left offset of the text node to break and insert the new content.
 
695
        * @param {Boolean} collapse Should the range be collapsed after insertion. default: false
 
696
        * @return {Node} The inserted Node.
 
697
        */
 
698
        insertAtCursor: function(html, node, offset, collapse) {
 
699
            var cur = Y.Node.create('<' + Y.Selection.DEFAULT_TAG + ' class="yui-non"></' + Y.Selection.DEFAULT_TAG + '>'),
 
700
                inHTML, txt, txt2, newNode, range = this.createRange(), b;
 
701
 
 
702
            if (node && node.test('body')) {
 
703
                b = Y.Node.create('<span></span>');
 
704
                node.append(b);
 
705
                node = b;
 
706
            }
 
707
 
 
708
            
 
709
            if (range.pasteHTML) {
 
710
                if (offset === 0 && node && !node.previous() && node.get('nodeType') === 3) {
 
711
                    /*
 
712
                    * For some strange reason, range.pasteHTML fails if the node is a textNode and
 
713
                    * the offset is 0. (The cursor is at the beginning of the line)
 
714
                    * It will always insert the new content at position 1 instead of 
 
715
                    * position 0. Here we test for that case and do it the hard way.
 
716
                    */
 
717
                    node.insert(html, 'before');
 
718
                    if (range.moveToElementText) {
 
719
                        range.moveToElementText(Y.Node.getDOMNode(node.previous()));
 
720
                    }
 
721
                    //Move the cursor after the new node
 
722
                    range.collapse(false);
 
723
                    range.select();
 
724
                    return node.previous();
 
725
                } else {
 
726
                    newNode = Y.Node.create(html);
 
727
                    try {
 
728
                        range.pasteHTML('<span id="rte-insert"></span>');
 
729
                    } catch (e) {}
 
730
                    inHTML = Y.one('#rte-insert');
 
731
                    if (inHTML) {
 
732
                        inHTML.set('id', '');
 
733
                        inHTML.replace(newNode);
 
734
                        if (range.moveToElementText) {
 
735
                            range.moveToElementText(Y.Node.getDOMNode(newNode));
 
736
                        }
 
737
                        range.collapse(false);
 
738
                        range.select();
 
739
                        return newNode;
 
740
                    } else {
 
741
                        Y.on('available', function() {
 
742
                            inHTML.set('id', '');
 
743
                            inHTML.replace(newNode);
 
744
                            if (range.moveToElementText) {
 
745
                                range.moveToElementText(Y.Node.getDOMNode(newNode));
 
746
                            }
 
747
                            range.collapse(false);
 
748
                            range.select();
 
749
                        }, '#rte-insert');
 
750
                    }
 
751
                }
 
752
            } else {
 
753
                //TODO using Y.Node.create here throws warnings & strips first white space character
 
754
                //txt = Y.one(Y.Node.create(inHTML.substr(0, offset)));
 
755
                //txt2 = Y.one(Y.Node.create(inHTML.substr(offset)));
 
756
                if (offset > 0) {
 
757
                    inHTML = node.get(textContent);
 
758
 
 
759
                    txt = Y.one(Y.config.doc.createTextNode(inHTML.substr(0, offset)));
 
760
                    txt2 = Y.one(Y.config.doc.createTextNode(inHTML.substr(offset)));
 
761
 
 
762
                    node.replace(txt, node);
 
763
                    newNode = Y.Node.create(html);
 
764
                    if (newNode.get('nodeType') === 11) {
 
765
                        b = Y.Node.create('<span></span>');
 
766
                        b.append(newNode);
 
767
                        newNode = b;
 
768
                    }
 
769
                    txt.insert(newNode, 'after');
 
770
                    //if (txt2 && txt2.get('length')) {
 
771
                    if (txt2) {
 
772
                        newNode.insert(cur, 'after');
 
773
                        cur.insert(txt2, 'after');
 
774
                        this.selectNode(cur, collapse);
 
775
                    }
 
776
                } else {
 
777
                    if (node.get('nodeType') === 3) {
 
778
                        node = node.get('parentNode');
 
779
                    }
 
780
                    newNode = Y.Node.create(html);
 
781
                    html = node.get('innerHTML').replace(/\n/gi, '');
 
782
                    if (html === '' || html === '<br>') {
 
783
                        node.append(newNode);
 
784
                    } else {
 
785
                        if (newNode.get('parentNode')) {
 
786
                            node.insert(newNode, 'before');
 
787
                        } else {
 
788
                            Y.one('body').prepend(newNode);
 
789
                        }
 
790
                    }
 
791
                    if (node.get('firstChild').test('br')) {
 
792
                        node.get('firstChild').remove();
 
793
                    }
 
794
                }
 
795
            }
 
796
            return newNode;
 
797
        },
 
798
        /**
 
799
        * Get all elements inside a selection and wrap them with a new element and return a NodeList of all elements touched.
 
800
        * @method wrapContent
 
801
        * @param {String} tag The tag to wrap all selected items with.
 
802
        * @return {NodeList} A NodeList of all items in the selection.
 
803
        */
 
804
        wrapContent: function(tag) {
 
805
            tag = (tag) ? tag : Y.Selection.DEFAULT_TAG;
 
806
 
 
807
            if (!this.isCollapsed) {
 
808
                var items = this.getSelected(),
 
809
                    changed = [], range, last, first, range2;
 
810
 
 
811
                items.each(function(n, k) {
 
812
                    var t = n.get('tagName').toLowerCase();
 
813
                    if (t === 'font') {
 
814
                        changed.push(this._swap(items.item(k), tag));
 
815
                    } else {
 
816
                        changed.push(this._wrap(items.item(k), tag));
 
817
                    }
 
818
                }, this);
 
819
                
 
820
                        range = this.createRange();
 
821
                first = changed[0];
 
822
                last = changed[changed.length - 1];
 
823
                if (this._selection.removeAllRanges) {
 
824
                    range.setStart(changed[0], 0);
 
825
                    range.setEnd(last, last.childNodes.length);
 
826
                    this._selection.removeAllRanges();
 
827
                    this._selection.addRange(range);
 
828
                } else {
 
829
                    if (range.moveToElementText) {
 
830
                        range.moveToElementText(Y.Node.getDOMNode(first));
 
831
                        range2 = this.createRange();
 
832
                        range2.moveToElementText(Y.Node.getDOMNode(last));
 
833
                        range.setEndPoint('EndToEnd', range2);
 
834
                    }
 
835
                    range.select();
 
836
                }
 
837
 
 
838
                changed = Y.all(changed);
 
839
                return changed;
 
840
 
 
841
 
 
842
            } else {
 
843
                return Y.all([]);
 
844
            }
 
845
        },
 
846
        /**
 
847
        * Find and replace a string inside a text node and replace it with HTML focusing the node after 
 
848
        * to allow you to continue to type.
 
849
        * @method replace
 
850
        * @param {String} se The string to search for.
 
851
        * @param {String} re The string of HTML to replace it with.
 
852
        * @return {Node} The node inserted.
 
853
        */
 
854
        replace: function(se,re) {
 
855
            var range = this.createRange(), node, txt, index, newNode;
 
856
 
 
857
            if (range.getBookmark) {
 
858
                index = range.getBookmark();
 
859
                txt = this.anchorNode.get('innerHTML').replace(se, re);
 
860
                this.anchorNode.set('innerHTML', txt);
 
861
                range.moveToBookmark(index);
 
862
                newNode = Y.one(range.parentElement());
 
863
            } else {
 
864
                node = this.anchorTextNode;
 
865
                txt = node.get(textContent);
 
866
                index = txt.indexOf(se);
 
867
 
 
868
                txt = txt.replace(se, '');
 
869
                node.set(textContent, txt);
 
870
                newNode = this.insertAtCursor(re, node, index, true);
 
871
            }
 
872
            return newNode;
 
873
        },
 
874
        /**
 
875
        * Destroy the range.
 
876
        * @method remove
 
877
        * @chainable
 
878
        * @return {Selection}
 
879
        */
 
880
        remove: function() {
 
881
            this._selection.removeAllRanges();
 
882
            return this;
 
883
        },
 
884
        /**
 
885
        * Wrapper for the different range creation methods.
 
886
        * @method createRange
 
887
        * @return {RangeObject}
 
888
        */
 
889
        createRange: function() {
 
890
            if (Y.config.doc.selection) {
 
891
                return Y.config.doc.selection.createRange();
 
892
            } else {
 
893
                        return Y.config.doc.createRange();
 
894
            }
 
895
        },
 
896
        /**
 
897
        * Select a Node (hilighting it).
 
898
        * @method selectNode
 
899
        * @param {Node} node The node to select
 
900
        * @param {Boolean} collapse Should the range be collapsed after insertion. default: false
 
901
        * @chainable
 
902
        * @return {Selection}
 
903
        */
 
904
        selectNode: function(node, collapse, end) {
 
905
            if (!node) {
 
906
                return;
 
907
            }
 
908
            end = end || 0;
 
909
            node = Y.Node.getDOMNode(node);
 
910
                    var range = this.createRange();
 
911
            if (range.selectNode) {
 
912
                range.selectNode(node);
 
913
                this._selection.removeAllRanges();
 
914
                this._selection.addRange(range);
 
915
                if (collapse) {
 
916
                    try {
 
917
                        this._selection.collapse(node, end);
 
918
                    } catch (err) {
 
919
                        this._selection.collapse(node, 0);
 
920
                    }
 
921
                }
 
922
            } else {
 
923
                if (node.nodeType === 3) {
 
924
                    node = node.parentNode;
 
925
                }
 
926
                try {
 
927
                    range.moveToElementText(node);
 
928
                } catch(e) {}
 
929
                if (collapse) {
 
930
                    range.collapse(((end) ? false : true));
 
931
                }
 
932
                range.select();
 
933
            }
 
934
            return this;
 
935
        },
 
936
        /**
 
937
        * Put a placeholder in the DOM at the current cursor position.
 
938
        * @method setCursor
 
939
        * @return {Node}
 
940
        */
 
941
        setCursor: function() {
 
942
            this.removeCursor(false);
 
943
            return this.insertContent(Y.Selection.CURSOR);
 
944
        },
 
945
        /**
 
946
        * Get the placeholder in the DOM at the current cursor position.
 
947
        * @method getCursor
 
948
        * @return {Node}
 
949
        */
 
950
        getCursor: function() {
 
951
            return Y.all('#' + Y.Selection.CURID);
 
952
        },
 
953
        /**
 
954
        * Remove the cursor placeholder from the DOM.
 
955
        * @method removeCursor
 
956
        * @param {Boolean} keep Setting this to true will keep the node, but remove the unique parts that make it the cursor.
 
957
        * @return {Node}
 
958
        */
 
959
        removeCursor: function(keep) {
 
960
            var cur = this.getCursor();
 
961
            if (cur) {
 
962
                if (keep) {
 
963
                    cur.removeAttribute('id');
 
964
                    cur.set('innerHTML', '<br class="yui-cursor">');
 
965
                } else {
 
966
                    cur.remove();
 
967
                }
 
968
            }
 
969
            return cur;
 
970
        },
 
971
        /**
 
972
        * Gets a stored cursor and focuses it for editing, must be called sometime after setCursor
 
973
        * @method focusCursor
 
974
        * @return {Node}
 
975
        */
 
976
        focusCursor: function(collapse, end) {
 
977
            if (collapse !== false) {
 
978
                collapse = true;
 
979
            }
 
980
            if (end !== false) {
 
981
                end = true;
 
982
            }
 
983
            var cur = this.removeCursor(true);
 
984
            if (cur) {
 
985
                cur.each(function(c) {
 
986
                    this.selectNode(c, collapse, end);
 
987
                }, this);
 
988
            }
 
989
        },
 
990
        /**
 
991
        * Generic toString for logging.
 
992
        * @method toString
 
993
        * @return {String}
 
994
        */
 
995
        toString: function() {
 
996
            return 'Selection Object';
 
997
        }
 
998
    };
 
999
 
 
1000
 
 
1001
}, '3.4.1' ,{skinnable:false, requires:['node']});