~bjornt/lazr-js/prefetch-yui-3.5

« back to all changes in this revision

Viewing changes to src-js/lazrjs/yui/editor/editor.js

  • Committer: Launchpad Patch Queue Manager
  • Date: 2011-01-14 23:32:29 UTC
  • mfrom: (197.1.7 yui-3.3.0)
  • Revision ID: launchpad@pqm.canonical.com-20110114233229-r6i4cazdiiw18o7p
Upgrade to YUI 3.3.0 [r=mars]

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
Copyright (c) 2010, Yahoo! Inc. All rights reserved.
3
3
Code licensed under the BSD License:
4
4
http://developer.yahoo.com/yui/license.html
5
 
version: 3.2.0
6
 
build: 2676
 
5
version: 3.3.0
 
6
build: 3167
7
7
*/
8
8
YUI.add('frame', function(Y) {
9
9
 
69
69
            this._iframe.set('src', this.get('src'));
70
70
            this.get('container').append(this._iframe);
71
71
 
 
72
            this._iframe.set('height', '99%');
72
73
 
 
74
            
73
75
            var html = '',
74
76
                extra_css = ((this.get('extracss')) ? '<style id="extra_css">' + this.get('extracss') + '</style>' : '');
75
77
 
78
80
                LANG: this.get('lang'),
79
81
                TITLE: this.get('title'),
80
82
                META: Frame.META,
 
83
                LINKED_CSS: this.get('linkedcss'),
81
84
                CONTENT: this.get('content'),
82
85
                BASE_HREF: this.get('basehref'),
83
86
                DEFAULT_CSS: Frame.DEFAULT_CSS,
84
87
                EXTRA_CSS: extra_css
85
88
            });
86
89
            if (Y.config.doc.compatMode != 'BackCompat') {
87
 
                html = Frame.DOC_TYPE + "\n" + html;
 
90
                
 
91
                //html = Frame.DOC_TYPE + "\n" + html;
 
92
                html = Frame.getDocType() + "\n" + html;
88
93
            } else {
89
94
            }
90
95
 
147
152
            if (e.pageX > 0 || e.pageY > 0) {
148
153
                if (e.type.substring(0, 3) !== 'key') {
149
154
                    node = this._instance.one('win');
150
 
                    xy = this._iframe.getXY()
 
155
                    xy = this._iframe.getXY();
151
156
                    e.frameX = xy[0] + e.pageX - node.get('scrollLeft');
152
157
                    e.frameY = xy[1] + e.pageY - node.get('scrollTop');
153
158
                }
191
196
            
192
197
            if (win.clipboardData) {
193
198
                data = win.clipboardData.getData('Text');
194
 
                if (data == '') { // Could be empty, or failed
 
199
                if (data === '') { // Could be empty, or failed
195
200
                    // Verify failure
196
201
                    if (!win.clipboardData.setData('Text', data)) {
197
202
                        data = null;
228
233
                kfn = ((Y.UA.ie) ? Y.throttle(fn, 200) : fn);
229
234
 
230
235
            inst.Node.DOM_EVENTS.activate = 1;
 
236
            inst.Node.DOM_EVENTS.beforedeactivate = 1;
231
237
            inst.Node.DOM_EVENTS.focusin = 1;
232
238
            inst.Node.DOM_EVENTS.deactivate = 1;
233
239
            inst.Node.DOM_EVENTS.focusout = 1;
237
243
                if (v === 1) {
238
244
                    if (k !== 'focus' && k !== 'blur' && k !== 'paste') {
239
245
                        if (k.substring(0, 3) === 'key') {
240
 
                            inst.on(k, kfn, inst.config.doc);
 
246
                            if (k === 'keydown') {
 
247
                                inst.on(k, fn, inst.config.doc);
 
248
                            } else {
 
249
                                inst.on(k, kfn, inst.config.doc);
 
250
                            }
241
251
                        } else {
242
252
                            inst.on(k, fn, inst.config.doc);
243
253
                        }
255
265
 
256
266
            inst._use = inst.use;
257
267
            inst.use = Y.bind(this.use, this);
258
 
 
259
268
            this._iframe.setStyles({
260
269
                visibility: 'inherit'
261
270
            });
262
271
            inst.one('body').setStyle('display', 'block');
 
272
            if (Y.UA.ie) {
 
273
                this._fixIECursors();
 
274
            }
 
275
        },
 
276
        /**
 
277
        * It appears that having a BR tag anywhere in the source "below" a table with a percentage width (in IE 7 & 8)
 
278
        * if there is any TEXTINPUT's outside the iframe, the cursor will rapidly flickr and the CPU would occasionally 
 
279
        * spike. This method finds all <BR>'s below the sourceIndex of the first table. Does some checks to see if they
 
280
        * can be modified and replaces then with a <WBR> so the layout will remain in tact, but the flickering will
 
281
        * no longer happen.
 
282
        * @method _fixIECursors
 
283
        * @private
 
284
        */
 
285
        _fixIECursors: function() {
 
286
            var inst = this.getInstance(),
 
287
                tables = inst.all('table'),
 
288
                brs = inst.all('br'), si;
 
289
 
 
290
            if (tables.size() && brs.size()) {
 
291
                //First Table
 
292
                si = tables.item(0).get('sourceIndex');
 
293
                brs.each(function(n) {
 
294
                    var p = n.get('parentNode'),
 
295
                        c = p.get('children'), b = p.all('>br');
 
296
                    
 
297
                    if (p.test('div')) {
 
298
                        if (c.size() > 2) {
 
299
                            n.replace(inst.Node.create('<wbr>'));
 
300
                        } else {
 
301
                            if (n.get('sourceIndex') > si) {
 
302
                                if (b.size()) {
 
303
                                    n.replace(inst.Node.create('<wbr>'));
 
304
                                }
 
305
                            } else {
 
306
                                if (b.size() > 1) {
 
307
                                    n.replace(inst.Node.create('<wbr>'));
 
308
                                }
 
309
                            }
 
310
                        }
 
311
                    }
 
312
                    
 
313
                });
 
314
            }
263
315
        },
264
316
        /**
265
317
        * @private
280
332
                }
281
333
                //TODO Circle around and deal with CSS loading...
282
334
                args.push(Y.bind(function() {
 
335
                    if (inst.Selection) {
 
336
                        inst.Selection.DEFAULT_BLOCK_TAG = this.get('defaultblock');
 
337
                    }
 
338
 
283
339
                    this.fire('ready');
284
340
                }, this));
285
341
                inst.use.apply(inst, args);
340
396
        },
341
397
        /**
342
398
        * @private
 
399
        * @method _setLinkedCSS
 
400
        * @description Set's the linked CSS on the instance..
 
401
        */
 
402
        _getLinkedCSS: function(urls) {
 
403
            if (!Y.Lang.isArray(urls)) {
 
404
                urls = [urls];
 
405
            }
 
406
            var str = '';
 
407
            if (!this._ready) {
 
408
                Y.each(urls, function(v) {
 
409
                    if (v !== '') {
 
410
                        str += '<link rel="stylesheet" href="' + v + '" type="text/css">';
 
411
                    }
 
412
                });
 
413
            } else {
 
414
                str = urls;
 
415
            }
 
416
            return str;
 
417
        },
 
418
        /**
 
419
        * @private
 
420
        * @method _setLinkedCSS
 
421
        * @description Set's the linked CSS on the instance..
 
422
        */
 
423
        _setLinkedCSS: function(css) {
 
424
            if (this._ready) {
 
425
                var inst = this.getInstance();
 
426
                inst.Get.css(css);
 
427
            }
 
428
            return css;
 
429
        },
 
430
        /**
 
431
        * @private
343
432
        * @method _setExtraCSS
344
433
        * @description Set's the extra CSS on the instance..
345
434
        */
362
451
        */
363
452
        _instanceLoaded: function(inst) {
364
453
            this._instance = inst;
365
 
 
366
454
            this._onContentReady();
367
455
            
368
456
            var doc = this._instance.config.doc;
444
532
            }
445
533
 
446
534
            this._create(Y.bind(function(res) {
 
535
 
447
536
                var inst, timer,
448
537
                    cb = Y.bind(function(i) {
449
538
                        this._instanceLoaded(i);
479
568
            return this;
480
569
        },
481
570
        /**
 
571
        * @private
 
572
        * @method _handleFocus
 
573
        * @description Does some tricks on focus to set the proper cursor position.
 
574
        */
 
575
        _handleFocus: function() {
 
576
            var inst = this.getInstance(),
 
577
                sel = new inst.Selection();
 
578
 
 
579
            if (sel.anchorNode) {
 
580
                var n = sel.anchorNode,
 
581
                    c = n.get('childNodes');
 
582
 
 
583
                if (c.size() == 1) {
 
584
                    if (c.item(0).test('br')) {
 
585
                        sel.selectNode(n, true, false);
 
586
                    }
 
587
                    if (c.item(0).test('p')) {
 
588
                        n = c.item(0).one('br.yui-cursor').get('parentNode');
 
589
                        sel.selectNode(n, true, false);
 
590
                    }
 
591
                }
 
592
            }
 
593
        },
 
594
        /**
482
595
        * @method focus
483
596
        * @description Set the focus to the iframe
484
597
        * @param {Function} fn Callback function to execute after focus happens        
487
600
        */
488
601
        focus: function(fn) {
489
602
            if (Y.UA.ie) {
490
 
                Y.one('win').focus();
491
 
                this.getInstance().one('win').focus();
 
603
                try {
 
604
                    Y.one('win').focus();
 
605
                    this.getInstance().one('win').focus();
 
606
                } catch (ierr) {
 
607
                }
 
608
                if (fn === true) {
 
609
                    this._handleFocus();
 
610
                }
492
611
                if (Y.Lang.isFunction(fn)) {
493
612
                    fn();
494
613
                }
497
616
                    Y.one('win').focus();
498
617
                    Y.later(100, this, function() {
499
618
                        this.getInstance().one('win').focus();
 
619
                        if (fn === true) {
 
620
                            this._handleFocus();
 
621
                        }
500
622
                        if (Y.Lang.isFunction(fn)) {
501
623
                            fn();
502
624
                        }
547
669
        * @type Object
548
670
        */
549
671
        DOM_EVENTS: {
 
672
            dblclick: 1,
 
673
            click: 1,
550
674
            paste: 1,
551
675
            mouseup: 1,
552
676
            mousedown: 1,
555
679
            keypress: 1,
556
680
            activate: 1,
557
681
            deactivate: 1,
 
682
            beforedeactivate: 1,
558
683
            focusin: 1,
559
684
            focusout: 1
560
685
        },
565
690
        * @description The default css used when creating the document.
566
691
        * @type String
567
692
        */
568
 
        DEFAULT_CSS: 'html { height: 95%; } body { padding: 7px; background-color: #fff; font: 13px/1.22 arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small; } a, a:visited, a:hover { color: blue !important; text-decoration: underline !important; cursor: text !important; } img { cursor: pointer !important; border: none; }',
569
 
        
570
 
        //DEFAULT_CSS: 'html { } body { margin: -15px 0 0 -15px; padding: 7px 0 0 15px; display: block; background-color: #fff; font: 13px/1.22 arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small; }',
571
 
        //DEFAULT_CSS: 'html { height: 95%; } body { height: 100%; padding: 7px; margin: 0 0 0 -7px; postion: relative; background-color: #fff; font: 13px/1.22 arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small; } a, a:visited, a:hover { color: blue !important; text-decoration: underline !important; cursor: text !important; } img { cursor: pointer !important; border: none; }',
572
 
        //DEFAULT_CSS: 'html { margin: 0; padding: 0; border: none; border-size: 0; } body { height: 97%; margin: 0; padding: 0; display: block; background-color: gray; font: 13px/1.22 arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small; }',
 
693
        //DEFAULT_CSS: 'html { height: 95%; } body { padding: 7px; background-color: #fff; font: 13px/1.22 arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small; } a, a:visited, a:hover { color: blue !important; text-decoration: underline !important; cursor: text !important; } img { cursor: pointer !important; border: none; }',
 
694
        DEFAULT_CSS: 'body { background-color: #fff; font: 13px/1.22 arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small; } a, a:visited, a:hover { color: blue !important; text-decoration: underline !important; cursor: text !important; } img { cursor: pointer !important; border: none; }',
573
695
        /**
574
696
        * @static
575
697
        * @property HTML
576
698
        * @description The template string used to create the iframe
577
699
        * @type String
578
700
        */
 
701
        //HTML: '<iframe border="0" frameBorder="0" marginWidth="0" marginHeight="0" leftMargin="0" topMargin="0" allowTransparency="true" width="100%" height="99%"></iframe>',
579
702
        HTML: '<iframe border="0" frameBorder="0" marginWidth="0" marginHeight="0" leftMargin="0" topMargin="0" allowTransparency="true" width="100%" height="99%"></iframe>',
580
 
        //HTML: '<iframe border="0" frameBorder="0" width="100%" height="99%"></iframe>',
581
703
        /**
582
704
        * @static
583
705
        * @property PAGE_HTML
584
706
        * @description The template used to create the page when created dynamically.
585
707
        * @type String
586
708
        */
587
 
        PAGE_HTML: '<html dir="{DIR}" lang="{LANG}"><head><title>{TITLE}</title>{META}<base href="{BASE_HREF}"/><style id="editor_css">{DEFAULT_CSS}</style>{EXTRA_CSS}</head><body>{CONTENT}</body></html>',
 
709
        PAGE_HTML: '<html dir="{DIR}" lang="{LANG}"><head><title>{TITLE}</title>{META}<base href="{BASE_HREF}"/>{LINKED_CSS}<style id="editor_css">{DEFAULT_CSS}</style>{EXTRA_CSS}</head><body>{CONTENT}</body></html>',
 
710
 
 
711
        /**
 
712
        * @static
 
713
        * @method getDocType
 
714
        * @description Parses document.doctype and generates a DocType to match the parent page, if supported.
 
715
        * For IE8, it grabs document.all[0].nodeValue and uses that. For IE < 8, it falls back to Frame.DOC_TYPE.
 
716
        * @returns {String} The normalized DocType to apply to the iframe
 
717
        */
 
718
        getDocType: function() {
 
719
            var dt = Y.config.doc.doctype,
 
720
                str = Frame.DOC_TYPE;
 
721
 
 
722
            if (dt) {
 
723
                str = '<!DOCTYPE ' + dt.name + ((dt.publicId) ? ' ' + dt.publicId : '') + ((dt.systemId) ? ' ' + dt.systemId : '') + '>';
 
724
            } else {
 
725
                if (Y.config.doc.all) {
 
726
                    dt = Y.config.doc.all[0];
 
727
                    if (dt.nodeType) {
 
728
                        if (dt.nodeType === 8) {
 
729
                            if (dt.nodeValue) {
 
730
                                if (dt.nodeValue.toLowerCase().indexOf('doctype') !== -1) {
 
731
                                    str = '<!' + dt.nodeValue + '>';
 
732
                                }
 
733
                            }
 
734
                        }
 
735
                    }
 
736
                }
 
737
            }
 
738
            return str;
 
739
        },
588
740
        /**
589
741
        * @static
590
742
        * @property DOC_TYPE
598
750
        * @description The meta-tag for Content-Type to add to the dynamic document
599
751
        * @type String
600
752
        */
601
 
        META: '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/><meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7">',
602
 
        //META: '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>',
 
753
        //META: '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/><meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7">',
 
754
        META: '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>',
603
755
        /**
604
756
        * @static
605
757
        * @property NAME
692
844
                }
693
845
            },
694
846
            /**
 
847
            * @attribute node
 
848
            * @description The Node instance of the iframe.
 
849
            * @type Node
 
850
            */
 
851
            node: {
 
852
                readOnly: true,
 
853
                value: null,
 
854
                getter: function() {
 
855
                    return this._iframe;
 
856
                }
 
857
            },
 
858
            /**
695
859
            * @attribute id
696
860
            * @description Set the id of the new Node. (optional)
697
861
            * @type String
707
871
                }
708
872
            },
709
873
            /**
 
874
            * @attribute linkedcss
 
875
            * @description An array of url's to external linked style sheets
 
876
            * @type String
 
877
            */
 
878
            linkedcss: {
 
879
                value: '',
 
880
                getter: '_getLinkedCSS',
 
881
                setter: '_setLinkedCSS'
 
882
            },
 
883
            /**
710
884
            * @attribute extracss
711
885
            * @description A string of CSS to add to the Head of the Editor
712
886
            * @type String
722
896
            */
723
897
            host: {
724
898
                value: false
 
899
            },
 
900
            /**
 
901
            * @attribute defaultblock
 
902
            * @description The default tag to use for block level items, defaults to: p
 
903
            * @type String
 
904
            */            
 
905
            defaultblock: {
 
906
                value: 'p'
725
907
            }
726
908
        }
727
909
    });
731
913
 
732
914
 
733
915
 
734
 
}, '3.2.0' ,{skinnable:false, requires:['base', 'node', 'selector-css3', 'substitute']});
 
916
}, '3.3.0' ,{requires:['base', 'node', 'selector-css3', 'substitute'], skinnable:false});
735
917
YUI.add('selection', function(Y) {
736
918
 
737
919
    /**
764
946
            sel = Y.config.doc.selection.createRange();
765
947
        }
766
948
        this._selection = sel;
767
 
        
 
949
 
768
950
        if (sel.pasteHTML) {
769
951
            this.isCollapsed = (sel.compareEndPoints('StartToEnd', sel)) ? false : true;
770
952
            if (this.isCollapsed) {
773
955
                if (domEvent) {
774
956
                    ieNode = Y.config.doc.elementFromPoint(domEvent.clientX, domEvent.clientY);
775
957
                }
776
 
                
777
958
                if (!ieNode) {
778
959
                    par = sel.parentElement();
779
960
                    nodes = par.childNodes;
783
964
                        //This causes IE to not allow a selection on a doubleclick
784
965
                        //rng.select(nodes[i]);
785
966
                        if (rng.inRange(sel)) {
786
 
                           ieNode = nodes[i]; 
 
967
                            if (!ieNode) {
 
968
                                ieNode = nodes[i];
 
969
                            }
787
970
                        }
788
971
                    }
789
972
                }
795
978
                        if (ieNode.firstChild) {
796
979
                            ieNode = ieNode.firstChild;
797
980
                        }
 
981
                        if (ieNode && ieNode.tagName && ieNode.tagName.toLowerCase() === 'body') {
 
982
                            if (ieNode.firstChild) {
 
983
                                ieNode = ieNode.firstChild;
 
984
                            }
 
985
                        }
798
986
                    }
799
987
                    this.anchorNode = this.focusNode = Y.Selection.resolve(ieNode);
800
988
                    
804
992
                }
805
993
                
806
994
                
 
995
            } else {
 
996
                //This helps IE deal with a selection and nodeChange events
 
997
                if (sel.htmlText) {
 
998
                    var n = Y.Node.create(sel.htmlText);
 
999
                    if (n.get('id')) {
 
1000
                        var id = n.get('id');
 
1001
                        this.anchorNode = this.focusNode = Y.one('#' + id);
 
1002
                    } else {
 
1003
                        n = n.get('childNodes');
 
1004
                        this.anchorNode = this.focusNode = n.item(0);
 
1005
                    }
 
1006
                }
807
1007
            }
808
1008
 
809
1009
            //var self = this;
883
1083
        });
884
1084
        var endTime1 = (new Date()).getTime();
885
1085
 
 
1086
        Y.all('.hr').addClass('yui-skip').addClass('yui-non');
 
1087
 
886
1088
        Y.each(hrs, function(hr) {
887
1089
            var el = doc.createElement('div');
888
 
                el.className = 'hr yui-non';
889
 
                el.setAttribute('style', 'border: 1px solid #ccc; line-height: 0; font-size: 0;margin-top: 5px; margin-bottom: 5px;');
 
1090
                el.className = 'hr yui-non yui-skip';
 
1091
                
890
1092
                el.setAttribute('readonly', true);
891
1093
                el.setAttribute('contenteditable', false); //Keep it from being Edited
892
1094
                if (hr.parentNode) {
893
1095
                    hr.parentNode.replaceChild(el, hr);
894
1096
                }
895
 
 
 
1097
                //Had to move to inline style. writes for ie's < 8. They don't render el.setAttribute('style');
 
1098
                var s = el.style;
 
1099
                s.border = '1px solid #ccc';
 
1100
                s.lineHeight = '0';
 
1101
                s.fontSize = '0';
 
1102
                s.marginTop = '5px';
 
1103
                s.marginBottom = '5px';
 
1104
                s.marginLeft = '0px';
 
1105
                s.marginRight = '0px';
 
1106
                s.padding = '0';
896
1107
        });
 
1108
        
897
1109
 
898
1110
        Y.each(classNames, function(v, k) {
899
1111
            cssString += k + ' { font-family: ' + v.replace(/"/gi, '') + '; }';
962
1174
            wrapped = Y.Selection._wrapBlock(wrapped);
963
1175
        }
964
1176
 
965
 
        single = Y.all('p');
 
1177
        single = Y.all(Y.Selection.DEFAULT_BLOCK_TAG);
966
1178
        if (single.size() === 1) {
967
1179
            br = single.item(0).all('br');
968
1180
            if (br.size() === 1) {
969
 
                br.item(0).remove();
 
1181
                if (!br.item(0).test('.yui-cursor')) {
 
1182
                    br.item(0).remove();
 
1183
                }
970
1184
                var html = single.item(0).get('innerHTML');
971
 
                if (html == '' || html == ' ') {
 
1185
                if (html === '' || html === ' ') {
972
1186
                    single.set('innerHTML', Y.Selection.CURSOR);
973
1187
                    sel = new Y.Selection();
974
1188
                    sel.focusCursor(true, true);
984
1198
        }
985
1199
        
986
1200
        if (!Y.UA.ie) {
 
1201
            /*
987
1202
            divs = Y.all('div, p');
988
1203
            divs.each(function(d) {
989
1204
                if (d.hasClass('yui-non')) {
999
1214
                        }
1000
1215
                    }
1001
1216
                }
1002
 
            });
 
1217
            });*/
1003
1218
 
 
1219
            /** Removed this, as it was causing Pasting to be funky in Safari
1004
1220
            spans = Y.all('.Apple-style-span, .apple-style-span');
1005
1221
            spans.each(function(s) {
1006
1222
                s.setAttribute('style', '');
1007
1223
            });
 
1224
            */
1008
1225
        }
1009
1226
 
1010
1227
 
1025
1242
    */
1026
1243
    Y.Selection.REG_NON = /[\s\S|\n|\t]/gi;
1027
1244
 
 
1245
    /**
 
1246
    * Regular Expression to remove all HTML from a string
 
1247
    * @static
 
1248
    * @property REG_NOHTML
 
1249
    */
 
1250
    Y.Selection.REG_NOHTML = /<\S[^><]*>/g;
 
1251
 
1028
1252
 
1029
1253
    /**
1030
1254
    * Wraps an array of elements in a Block level tag
1034
1258
    */
1035
1259
    Y.Selection._wrapBlock = function(wrapped) {
1036
1260
        if (wrapped) {
1037
 
            var newChild = Y.Node.create('<p></p>'),
 
1261
            var newChild = Y.Node.create('<' + Y.Selection.DEFAULT_BLOCK_TAG + '></' + Y.Selection.DEFAULT_BLOCK_TAG + '>'),
1038
1262
                firstChild = Y.one(wrapped[0]), i;
1039
1263
 
1040
1264
            for (i = 1; i < wrapped.length; i++) {
1054
1278
    */
1055
1279
    Y.Selection.unfilter = function() {
1056
1280
        var nodes = Y.all('body [class]'),
1057
 
            html = '', nons, ids;
 
1281
            html = '', nons, ids,
 
1282
            body = Y.one('body');
1058
1283
        
1059
1284
        
1060
1285
        nodes.each(function(n) {
1070
1295
 
1071
1296
        nons = Y.all('.yui-non');
1072
1297
        nons.each(function(n) {
1073
 
            if (n.get('innerHTML') === '') {
 
1298
            if (!n.hasClass('yui-skip') && n.get('innerHTML') === '') {
1074
1299
                n.remove();
1075
1300
            } else {
1076
 
                n.removeClass('yui-non');
 
1301
                n.removeClass('yui-non').removeClass('yui-skip');
1077
1302
            }
1078
1303
        });
1079
1304
 
1084
1309
                n.removeAttribute('_yuid');
1085
1310
            }
1086
1311
        });
1087
 
 
1088
 
        html = Y.one('body').get('innerHTML');
1089
 
        
 
1312
        
 
1313
        if (body) {
 
1314
            html = body.get('innerHTML');
 
1315
        }
 
1316
        
 
1317
        Y.all('.hr').addClass('yui-skip').addClass('yui-non');
 
1318
        
 
1319
        /*
1090
1320
        nodes.each(function(n) {
1091
1321
            n.addClass(n._yuid);
1092
1322
            n.setStyle(FONT_FAMILY, '');
1094
1324
                n.removeAttribute('style');
1095
1325
            }
1096
1326
        });
 
1327
        */
1097
1328
        
1098
1329
        return html;
1099
1330
    };
1106
1337
    */
1107
1338
    Y.Selection.resolve = function(n) {
1108
1339
        if (n && n.nodeType === 3) {
1109
 
            n = n.parentNode;
 
1340
            //Adding a try/catch here because in rare occasions IE will
 
1341
            //Throw a error accessing the parentNode of a stranded text node.
 
1342
            //In the case of Ctrl+Z (Undo)
 
1343
            try {
 
1344
                n = n.parentNode;
 
1345
            } catch (re) {
 
1346
                n = 'body';
 
1347
            }
1110
1348
        }
1111
1349
        return Y.one(n);
1112
1350
    };
1119
1357
    * @return {String} The string of text
1120
1358
    */
1121
1359
    Y.Selection.getText = function(node) {
1122
 
        var t = node.get('innerHTML').replace(Y.Selection.STRIP_HTML, ''),
1123
 
            c = t.match(Y.Selection.REG_CHAR),
1124
 
            s = t.match(Y.Selection.REG_NON);
1125
 
            if (c === null && s) {
1126
 
                t = '';
1127
 
            }
1128
 
        return t;
 
1360
        var txt = node.get('innerHTML').replace(Y.Selection.REG_NOHTML, '');
 
1361
        //Clean out the cursor subs to see if the Node is empty
 
1362
        txt = txt.replace('<span><br></span>', '').replace('<br>', '');
 
1363
        return txt;
1129
1364
    };
1130
1365
 
 
1366
    //Y.Selection.DEFAULT_BLOCK_TAG = 'div';
 
1367
    Y.Selection.DEFAULT_BLOCK_TAG = 'p';
 
1368
 
1131
1369
    /**
1132
1370
    * The selector to use when looking for Nodes to cache the value of: [style],font[face]
1133
1371
    * @static
1136
1374
    Y.Selection.ALL = '[style],font[face]';
1137
1375
 
1138
1376
    /**
1139
 
    * RegExp used to strip HTML tags from a string
1140
 
    * @static
1141
 
    * @property STRIP_HTML
1142
 
    */
1143
 
    Y.Selection.STRIP_HTML = /<\S[^><]*>/g;
1144
 
 
1145
 
    /**
1146
1377
    * The selector to use when looking for block level items.
1147
1378
    * @static
1148
1379
    * @property BLOCKS
1180
1411
    * @static
1181
1412
    * @property CURSOR
1182
1413
    */
1183
 
    Y.Selection.CURSOR = '<span id="' + Y.Selection.CURID + '"><span id="' + Y.Selection.CUR_WRAPID + '">&nbsp;</span></span>';
 
1414
    Y.Selection.CURSOR = '<span><br class="yui-cursor"></span>';
 
1415
 
 
1416
    Y.Selection.hasCursor = function() {
 
1417
        var cur = Y.all('#' + Y.Selection.CUR_WRAPID);
 
1418
        return cur.size();
 
1419
    };
1184
1420
 
1185
1421
    /**
1186
1422
    * Called from Editor keydown to remove the "extra" space before the cursor.
1188
1424
    * @method cleanCursor
1189
1425
    */
1190
1426
    Y.Selection.cleanCursor = function() {
1191
 
        /*
1192
 
        var cur = Y.config.doc.getElementById(Y.Selection.CUR_WRAPID);
1193
 
        if (cur) {
1194
 
            cur.id = '';
1195
 
            if (cur.innerHTML == '&nbsp;' || cur.innerHTML == '<br>') {
1196
 
                if (cur.parentNode) {
1197
 
                    cur.parentNode.removeChild(cur);
 
1427
        var cur, sel = 'br.yui-cursor';
 
1428
        cur = Y.all(sel);
 
1429
        if (cur.size()) {
 
1430
            cur.each(function(b) {
 
1431
                var c = b.get('parentNode.parentNode.childNodes'), html;
 
1432
                if (c.size() > 1) {
 
1433
                    b.remove();
 
1434
                } else {
 
1435
                    html = Y.Selection.getText(c.item(0));
 
1436
                    if (html !== '') {
 
1437
                        b.remove();
 
1438
                    }
1198
1439
                }
1199
 
            }
 
1440
            });
1200
1441
        }
1201
 
        */
1202
 
        
 
1442
        /*
1203
1443
        var cur = Y.all('#' + Y.Selection.CUR_WRAPID);
1204
 
        if (cur.size) {
 
1444
        if (cur.size()) {
1205
1445
            cur.each(function(c) {
1206
1446
                var html = c.get('innerHTML');
1207
 
                if (html == '&nbsp' || html == '<br>') {
1208
 
                    c.remove();
 
1447
                if (html == '&nbsp;' || html == '<br>') {
 
1448
                    if (c.previous() || c.next()) {
 
1449
                        c.remove();
 
1450
                    }
1209
1451
                }
1210
1452
            });
1211
1453
        }
1212
 
        
 
1454
        */
1213
1455
    };
1214
1456
 
1215
1457
    Y.Selection.prototype = {
1310
1552
                items = [];
1311
1553
            
1312
1554
            nodes.each(function(n, k) {
1313
 
                if (n.getStyle(FONT_FAMILY, Y.Selection.TMP)) {
 
1555
                if (n.getStyle(FONT_FAMILY) ==  Y.Selection.TMP) {
1314
1556
                    n.setStyle(FONT_FAMILY, '');
1315
1557
                    n.removeAttribute('face');
1316
1558
                    if (n.getAttribute('style') === '') {
1398
1640
                    }
1399
1641
                    newNode = Y.Node.create(html);
1400
1642
                    html = node.get('innerHTML').replace(/\n/gi, '');
1401
 
                    if (html == '' || html == '<br>') {
 
1643
                    if (html === '' || html === '<br>') {
1402
1644
                        node.append(newNode);
1403
1645
                    } else {
1404
 
                        node.insert(newNode, 'before');
 
1646
                        if (newNode.get('parentNode')) {
 
1647
                            node.insert(newNode, 'before');
 
1648
                        } else {
 
1649
                            Y.one('body').prepend(newNode);
 
1650
                        }
1405
1651
                    }
1406
1652
                    if (node.get('firstChild').test('br')) {
1407
1653
                        node.get('firstChild').remove();
1515
1761
        * @return {Y.Selection}
1516
1762
        */
1517
1763
        selectNode: function(node, collapse, end) {
 
1764
            if (!node) {
 
1765
                return;
 
1766
            }
1518
1767
            end = end || 0;
1519
1768
            node = Y.Node.getDOMNode(node);
1520
1769
                    var range = this.createRange();
1571
1820
            if (cur) {
1572
1821
                if (keep) {
1573
1822
                    cur.removeAttribute('id');
1574
 
                    cur.set('innerHTML', '<span id="' + Y.Selection.CUR_WRAPID + '">&nbsp;</span>');
 
1823
                    cur.set('innerHTML', '<br class="yui-cursor">');
1575
1824
                } else {
1576
1825
                    cur.remove();
1577
1826
                }
1608
1857
    };
1609
1858
 
1610
1859
 
1611
 
}, '3.2.0' ,{skinnable:false, requires:['node']});
 
1860
}, '3.3.0' ,{requires:['node'], skinnable:false});
1612
1861
YUI.add('exec-command', function(Y) {
1613
1862
 
1614
1863
 
1629
1878
 
1630
1879
        Y.extend(ExecCommand, Y.Base, {
1631
1880
            /**
 
1881
            * An internal reference to the keyCode of the last key that was pressed.
 
1882
            * @private
 
1883
            * @property _lastKey
 
1884
            */
 
1885
            _lastKey: null,
 
1886
            /**
1632
1887
            * An internal reference to the instance of the frame plugged into.
1633
1888
            * @private
1634
1889
            * @property _inst
1643
1898
            */
1644
1899
            command: function(action, value) {
1645
1900
                var fn = ExecCommand.COMMANDS[action];
 
1901
                
 
1902
                /*
 
1903
                if (action !== 'insertbr') {
 
1904
                    Y.later(0, this, function() {
 
1905
                        var inst = this.getInstance();
 
1906
                        if (inst && inst.Selection) {
 
1907
                            inst.Selection.cleanCursor();
 
1908
                        }
 
1909
                    });
 
1910
                }
 
1911
                */
1646
1912
 
1647
1913
                if (fn) {
1648
1914
                    return fn.call(this, action, value);
1660
1926
            _command: function(action, value) {
1661
1927
                var inst = this.getInstance();
1662
1928
                try {
1663
 
                    inst.config.doc.execCommand(action, false, value);
 
1929
                    try {
 
1930
                        inst.config.doc.execCommand('styleWithCSS', null, 1);
 
1931
                    } catch (e1) {
 
1932
                        try {
 
1933
                            inst.config.doc.execCommand('useCSS', null, 0);
 
1934
                        } catch (e2) {
 
1935
                        }
 
1936
                    }
 
1937
                    inst.config.doc.execCommand(action, null, value);
1664
1938
                } catch (e) {
1665
1939
                }
1666
1940
            },
1684
1958
                        return this.exec._command(action, value);
1685
1959
                    }
1686
1960
                });
 
1961
 
 
1962
                this.get('host').on('dom:keypress', Y.bind(function(e) {
 
1963
                    this._lastKey = e.keyCode;
 
1964
                }, this));
1687
1965
            }
1688
1966
        }, {
1689
1967
            /**
1731
2009
                */
1732
2010
                inserthtml: function(cmd, html) {
1733
2011
                    var inst = this.getInstance();
1734
 
                    return (new inst.Selection()).insertContent(html);
 
2012
                    if (inst.Selection.hasCursor() || Y.UA.ie) {
 
2013
                        return (new inst.Selection()).insertContent(html);
 
2014
                    } else {
 
2015
                        this._command('inserthtml', html);
 
2016
                    }
1735
2017
                },
1736
2018
                /**
1737
2019
                * Inserts the provided HTML at the cursor, and focuses the cursor afterwards.
1743
2025
                */
1744
2026
                insertandfocus: function(cmd, html) {
1745
2027
                    var inst = this.getInstance(), out, sel;
1746
 
                    html += inst.Selection.CURSOR;
1747
 
                    out = this.command('inserthtml', html);
1748
 
                    sel = new inst.Selection();
1749
 
                    sel.focusCursor(true, true);
 
2028
                    if (inst.Selection.hasCursor()) {
 
2029
                        html += inst.Selection.CURSOR;
 
2030
                        out = this.command('inserthtml', html);
 
2031
                        sel = new inst.Selection();
 
2032
                        sel.focusCursor(true, true);
 
2033
                    } else {
 
2034
                        this.command('inserthtml', html);
 
2035
                    }
1750
2036
                    return out;
1751
2037
                },
1752
2038
                /**
1763
2049
                    cur = sel.getCursor();
1764
2050
                    cur.insert('<br>', 'before');
1765
2051
                    sel.focusCursor(true, false);
1766
 
                    return cur.previous();
 
2052
                    return ((cur && cur.previous) ? cur.previous() : null);
1767
2053
                },
1768
2054
                /**
1769
2055
                * Inserts an image at the cursor position
1801
2087
                    return (new inst.Selection()).getSelected().removeClass(cls);
1802
2088
                },
1803
2089
                /**
 
2090
                * Adds a forecolor to the current selection, or creates a new element and applies it
 
2091
                * @method COMMANDS.forecolor
 
2092
                * @static
 
2093
                * @param {String} cmd The command executed: forecolor
 
2094
                * @param {String} val The color value to apply
 
2095
                * @return {NodeList} NodeList of the items touched by this command.
 
2096
                */
 
2097
                forecolor: function(cmd, val) {
 
2098
                    var inst = this.getInstance(),
 
2099
                        sel = new inst.Selection(), n;
 
2100
 
 
2101
                    if (!Y.UA.ie) {
 
2102
                        this._command('useCSS', false);
 
2103
                    }
 
2104
                    if (inst.Selection.hasCursor()) {
 
2105
                        if (sel.isCollapsed) {
 
2106
                            if (sel.anchorNode && (sel.anchorNode.get('innerHTML') === '&nbsp;')) {
 
2107
                                sel.anchorNode.setStyle('color', val);
 
2108
                                n = sel.anchorNode;
 
2109
                            } else {
 
2110
                                n = this.command('inserthtml', '<span style="color: ' + val + '">' + inst.Selection.CURSOR + '</span>');
 
2111
                                sel.focusCursor(true, true);
 
2112
                            }
 
2113
                            return n;
 
2114
                        } else {
 
2115
                            return this._command(cmd, val);
 
2116
                        }
 
2117
                    } else {
 
2118
                        this._command(cmd, val);
 
2119
                    }
 
2120
                },
 
2121
                /**
1804
2122
                * Adds a background color to the current selection, or creates a new element and applies it
1805
2123
                * @method COMMANDS.backcolor
1806
2124
                * @static
1808
2126
                * @param {String} val The color value to apply
1809
2127
                * @return {NodeList} NodeList of the items touched by this command.
1810
2128
                */
1811
 
                forecolor: function(cmd, val) {
1812
 
                    var inst = this.getInstance(),
1813
 
                        sel = new inst.Selection(), n;
1814
 
 
1815
 
                    if (!Y.UA.ie) {
1816
 
                        this._command('styleWithCSS', 'true');
1817
 
                    }
1818
 
                    if (sel.isCollapsed) {
1819
 
                        if (sel.anchorNode && (sel.anchorNode.get('innerHTML') === '&nbsp;')) {
1820
 
                            sel.anchorNode.setStyle('color', val);
1821
 
                            n = sel.anchorNode;
1822
 
                        } else {
1823
 
                            n = this.command('inserthtml', '<span style="color: ' + val + '">' + inst.Selection.CURSOR + '</span>');
1824
 
                            sel.focusCursor(true, true);
1825
 
                        }
1826
 
                        return n;
1827
 
                    } else {
1828
 
                        return this._command(cmd, val);
1829
 
                    }
1830
 
                    if (!Y.UA.ie) {
1831
 
                        this._command('styleWithCSS', false);
1832
 
                    }
1833
 
                },
1834
2129
                backcolor: function(cmd, val) {
1835
2130
                    var inst = this.getInstance(),
1836
2131
                        sel = new inst.Selection(), n;
1837
 
 
 
2132
                    
1838
2133
                    if (Y.UA.gecko || Y.UA.opera) {
1839
2134
                        cmd = 'hilitecolor';
1840
2135
                    }
1841
2136
                    if (!Y.UA.ie) {
1842
 
                        this._command('styleWithCSS', 'true');
 
2137
                        this._command('useCSS', false);
1843
2138
                    }
1844
 
                    if (sel.isCollapsed) {
1845
 
                        if (sel.anchorNode && (sel.anchorNode.get('innerHTML') === '&nbsp;')) {
1846
 
                            sel.anchorNode.setStyle('backgroundColor', val);
1847
 
                            n = sel.anchorNode;
 
2139
                    if (inst.Selection.hasCursor()) {
 
2140
                        if (sel.isCollapsed) {
 
2141
                            if (sel.anchorNode && (sel.anchorNode.get('innerHTML') === '&nbsp;')) {
 
2142
                                sel.anchorNode.setStyle('backgroundColor', val);
 
2143
                                n = sel.anchorNode;
 
2144
                            } else {
 
2145
                                n = this.command('inserthtml', '<span style="background-color: ' + val + '">' + inst.Selection.CURSOR + '</span>');
 
2146
                                sel.focusCursor(true, true);
 
2147
                            }
 
2148
                            return n;
1848
2149
                        } else {
1849
 
                            n = this.command('inserthtml', '<span style="background-color: ' + val + '">' + inst.Selection.CURSOR + '</span>');
1850
 
                            sel.focusCursor(true, true);
 
2150
                            return this._command(cmd, val);
1851
2151
                        }
1852
 
                        return n;
1853
2152
                    } else {
1854
 
                        return this._command(cmd, val);
1855
 
                    }
1856
 
                    if (!Y.UA.ie) {
1857
 
                        this._command('styleWithCSS', false);
 
2153
                        this._command(cmd, val);
1858
2154
                    }
1859
2155
                },
1860
2156
                /**
1877
2173
                * @return {NodeList} NodeList of the items touched by this command.
1878
2174
                */
1879
2175
                fontname: function(cmd, val) {
 
2176
                    this._command('fontname', val);
1880
2177
                    var inst = this.getInstance(),
1881
 
                        sel = new inst.Selection(), n;
1882
 
 
1883
 
                    if (sel.isCollapsed) {
1884
 
                        if (sel.anchorNode && (sel.anchorNode.get('innerHTML') === '&nbsp;')) {
1885
 
                            sel.anchorNode.setStyle('fontFamily', val);
1886
 
                            n = sel.anchorNode;
1887
 
                        } else {
1888
 
                            n = this.command('inserthtml', '<span style="font-family: ' + val + '">' + inst.Selection.CURSOR + '</span>');
1889
 
                            sel.focusCursor(true, true);
 
2178
                        sel = new inst.Selection();
 
2179
                    
 
2180
                    if (sel.isCollapsed && (this._lastKey != 32)) {
 
2181
                        if (sel.anchorNode.test('font')) {
 
2182
                            sel.anchorNode.set('face', val);
1890
2183
                        }
1891
 
                        return n;
1892
 
                    } else {
1893
 
                        return this._command('fontname', val);
1894
2184
                    }
1895
2185
                },
1896
2186
                /**
1902
2192
                * @return {NodeList} NodeList of the items touched by this command.
1903
2193
                */
1904
2194
                fontsize: function(cmd, val) {
 
2195
                    this._command('fontsize', val);
 
2196
 
1905
2197
                    var inst = this.getInstance(),
1906
 
                        sel = new inst.Selection(), n, prev;
1907
 
 
1908
 
                    if (sel.isCollapsed) {
1909
 
                        n = this.command('inserthtml', '<font size="' + val + '">&nbsp;</font>');
1910
 
                        prev = n.get('previousSibling');
1911
 
                        if (prev && prev.get('nodeType') === 3) {
1912
 
                            if (prev.get('length') < 2) {
1913
 
                                prev.remove();
1914
 
                            }
1915
 
                        }
1916
 
                        sel.selectNode(n.get('firstChild'), true, false);
1917
 
                        return n;
1918
 
                    } else {
1919
 
                        return this._command('fontsize', val);
 
2198
                        sel = new inst.Selection();
 
2199
                    
 
2200
                    if (sel.isCollapsed && sel.anchorNode && (this._lastKey != 32)) {
 
2201
                        if (Y.UA.webkit) {
 
2202
                            if (sel.anchorNode.getStyle('lineHeight')) {
 
2203
                                sel.anchorNode.setStyle('lineHeight', '');
 
2204
                            }
 
2205
                        }
 
2206
                        if (sel.anchorNode.test('font')) {
 
2207
                            sel.anchorNode.set('size', val);
 
2208
                        } else if (Y.UA.gecko) {
 
2209
                            var p = sel.anchorNode.ancestor(inst.Selection.DEFAULT_BLOCK_TAG);
 
2210
                            if (p) {
 
2211
                                p.setStyle('fontSize', '');
 
2212
                            }
 
2213
                        }
1920
2214
                    }
1921
2215
                }
1922
2216
            }
1923
2217
        });
 
2218
        
 
2219
        /**
 
2220
        * This method is meant to normalize IE's in ability to exec the proper command on elements with CSS styling.
 
2221
        * @method fixIETags
 
2222
        * @protected
 
2223
        * @param {String} cmd The command to execute
 
2224
        * @param {String} tag The tag to create
 
2225
        * @param {String} rule The rule that we are looking for.
 
2226
        */
 
2227
        var fixIETags = function(cmd, tag, rule) {
 
2228
            var inst = this.getInstance(),
 
2229
                doc = inst.config.doc,
 
2230
                sel = doc.selection.createRange(),
 
2231
                o = doc.queryCommandValue(cmd),
 
2232
                html, reg, m, p, d, s, c;
 
2233
 
 
2234
            if (o) {
 
2235
                html = sel.htmlText;
 
2236
                reg = new RegExp(rule, 'g');
 
2237
                m = html.match(reg);
 
2238
 
 
2239
                if (m) {
 
2240
                    html = html.replace(rule + ';', '').replace(rule, '');
 
2241
 
 
2242
                    sel.pasteHTML('<var id="yui-ie-bs">');
 
2243
 
 
2244
                    p = doc.getElementById('yui-ie-bs');
 
2245
                    d = doc.createElement('div');
 
2246
                    s = doc.createElement(tag);
 
2247
                    
 
2248
                    d.innerHTML = html;
 
2249
                    if (p.parentNode !== inst.config.doc.body) {
 
2250
                        p = p.parentNode;
 
2251
                    }
 
2252
 
 
2253
                    c = d.childNodes;
 
2254
 
 
2255
                    p.parentNode.replaceChild(s, p);
 
2256
 
 
2257
                    Y.each(c, function(f) {
 
2258
                        s.appendChild(f);
 
2259
                    });
 
2260
                    sel.collapse();
 
2261
                    sel.moveToElementText(s);
 
2262
                    sel.select();
 
2263
                }
 
2264
            }
 
2265
            this._command(cmd);
 
2266
        };
 
2267
 
 
2268
        if (Y.UA.ie) {
 
2269
            ExecCommand.COMMANDS.bold = function() {
 
2270
                fixIETags.call(this, 'bold', 'b', 'FONT-WEIGHT: bold');
 
2271
            }
 
2272
            ExecCommand.COMMANDS.italic = function() {
 
2273
                fixIETags.call(this, 'italic', 'i', 'FONT-STYLE: italic');
 
2274
            }
 
2275
            ExecCommand.COMMANDS.underline = function() {
 
2276
                fixIETags.call(this, 'underline', 'u', 'TEXT-DECORATION: underline');
 
2277
            }
 
2278
        }
1924
2279
 
1925
2280
        Y.namespace('Plugin');
1926
2281
        Y.Plugin.ExecCommand = ExecCommand;
1927
2282
 
1928
2283
 
1929
2284
 
1930
 
}, '3.2.0' ,{skinnable:false, requires:['frame']});
 
2285
}, '3.3.0' ,{requires:['frame'], skinnable:false});
1931
2286
YUI.add('editor-tab', function(Y) {
1932
2287
 
1933
2288
    /**
1997
2352
    Y.Plugin.EditorTab = EditorTab;
1998
2353
 
1999
2354
 
2000
 
}, '3.2.0' ,{skinnable:false, requires:['editor-base']});
 
2355
}, '3.3.0' ,{requires:['editor-base'], skinnable:false});
2001
2356
YUI.add('createlink-base', function(Y) {
2002
2357
 
2003
2358
    /**
2045
2400
        * @return {Node} Node instance of the item touched by this command.
2046
2401
        */
2047
2402
        createlink: function(cmd) {
2048
 
            var inst = this.get('host').getInstance(), out, a, sel,
 
2403
            var inst = this.get('host').getInstance(), out, a, sel, holder,
2049
2404
                url = prompt(CreateLinkBase.STRINGS.PROMPT, CreateLinkBase.STRINGS.DEFAULT);
2050
2405
 
2051
2406
            if (url) {
 
2407
                holder = inst.config.doc.createElement('div');
 
2408
                url = inst.config.doc.createTextNode(url);
 
2409
                holder.appendChild(url);
 
2410
                url = holder.innerHTML;
 
2411
 
2052
2412
 
2053
2413
                this.get('host')._execCommand(cmd, url);
2054
2414
                sel = new inst.Selection();
2059
2419
                    if (a) {
2060
2420
                        out.item(0).replace(a);
2061
2421
                    }
 
2422
                    if (Y.UA.gecko) {
 
2423
                        if (a.get('parentNode').test('span')) {
 
2424
                            if (a.get('parentNode').one('br.yui-cursor')) {
 
2425
                                a.get('parentNode').insert(a, 'before');
 
2426
                            }
 
2427
                        }
 
2428
                    }
2062
2429
                } else {
2063
2430
                    //No selection, insert a new node..
2064
2431
                    this.get('host').execCommand('inserthtml', '<a href="' + url + '">' + url + '</a>');
2070
2437
 
2071
2438
 
2072
2439
 
2073
 
}, '3.2.0' ,{skinnable:false, requires:['editor-base']});
 
2440
}, '3.3.0' ,{requires:['editor-base'], skinnable:false});
2074
2441
YUI.add('editor-base', function(Y) {
2075
2442
 
2076
2443
 
2089
2456
    
2090
2457
    var EditorBase = function() {
2091
2458
        EditorBase.superclass.constructor.apply(this, arguments);
2092
 
    };
 
2459
    }, LAST_CHILD = ':last-child', BODY = 'body';
2093
2460
 
2094
2461
    Y.extend(EditorBase, Y.Base, {
2095
2462
        /**
2104
2471
                use: EditorBase.USE,
2105
2472
                dir: this.get('dir'),
2106
2473
                extracss: this.get('extracss'),
 
2474
                linkedcss: this.get('linkedcss'),
 
2475
                defaultblock: this.get('defaultblock'),
2107
2476
                host: this
2108
2477
            }).plug(Y.Plugin.ExecCommand);
2109
2478
 
 
2479
 
2110
2480
            frame.after('ready', Y.bind(this._afterFrameReady, this));
2111
2481
            frame.addTarget(this);
2112
2482
 
2118
2488
                defaultFn: this._defNodeChangeFn
2119
2489
            });
2120
2490
            
2121
 
            this.plug(Y.Plugin.EditorPara);
 
2491
            //this.plug(Y.Plugin.EditorPara);
2122
2492
        },
2123
2493
        destructor: function() {
2124
2494
            this.frame.destroy();
2132
2502
        * @param {Node} to The Node instance to copy the styles to
2133
2503
        */
2134
2504
        copyStyles: function(from, to) {
 
2505
            if (from.test('a')) {
 
2506
                //Don't carry the A styles
 
2507
                return;
 
2508
            }
2135
2509
            var styles = ['color', 'fontSize', 'fontFamily', 'backgroundColor', 'fontStyle' ],
2136
2510
                newStyles = {};
2137
2511
 
2141
2515
            if (from.ancestor('b,strong')) {
2142
2516
                newStyles.fontWeight = 'bold';
2143
2517
            }
 
2518
            if (from.ancestor('u')) {
 
2519
                if (!newStyles.textDecoration) {
 
2520
                    newStyles.textDecoration = 'underline';
 
2521
                }
 
2522
            }
2144
2523
            to.setStyles(newStyles);
2145
2524
        },
2146
2525
        /**
2150
2529
        */
2151
2530
        _lastBookmark: null,
2152
2531
        /**
 
2532
        * Resolves the e.changedNode in the nodeChange event if it comes from the document. If
 
2533
        * the event came from the document, it will get the last child of the last child of the document
 
2534
        * and return that instead.
 
2535
        * @method _resolveChangedNode
 
2536
        * @param {Node} n The node to resolve
 
2537
        * @private
 
2538
        */
 
2539
        _resolveChangedNode: function(n) {
 
2540
            var inst = this.getInstance(), lc, lc2, found;
 
2541
            if (inst && n && n.test('html')) {
 
2542
                lc = inst.one(BODY).one(LAST_CHILD);
 
2543
                while (!found) {
 
2544
                    if (lc) {
 
2545
                        lc2 = lc.one(LAST_CHILD);
 
2546
                        if (lc2) {
 
2547
                            lc = lc2;
 
2548
                        } else {
 
2549
                            found = true;
 
2550
                        }
 
2551
                    } else {
 
2552
                        found = true;
 
2553
                    }
 
2554
                }
 
2555
                if (lc) {
 
2556
                    if (lc.test('br')) {
 
2557
                        if (lc.previous()) {
 
2558
                            lc = lc.previous();
 
2559
                        } else {
 
2560
                            lc = lc.get('parentNode');
 
2561
                        }
 
2562
                    }
 
2563
                    if (lc) {
 
2564
                        n = lc;
 
2565
                    }
 
2566
                }
 
2567
                
 
2568
            }
 
2569
            return n;
 
2570
        },
 
2571
        /**
2153
2572
        * The default handler for the nodeChange event.
2154
2573
        * @method _defNodeChangeFn
2155
2574
        * @param {Event} e The event
2157
2576
        */
2158
2577
        _defNodeChangeFn: function(e) {
2159
2578
            var startTime = (new Date()).getTime();
2160
 
            var inst = this.getInstance(), sel;
 
2579
            var inst = this.getInstance(), sel, cur,
 
2580
                btag = inst.Selection.DEFAULT_BLOCK_TAG;
2161
2581
 
2162
2582
            if (Y.UA.ie) {
2163
 
                sel = inst.config.doc.selection.createRange();
2164
 
                this._lastBookmark = sel.getBookmark();
 
2583
                try {
 
2584
                    sel = inst.config.doc.selection.createRange();
 
2585
                    if (sel.getBookmark) {
 
2586
                        this._lastBookmark = sel.getBookmark();
 
2587
                    }
 
2588
                } catch (ie) {}
2165
2589
            }
2166
2590
 
 
2591
            e.changedNode = this._resolveChangedNode(e.changedNode);
 
2592
 
2167
2593
            /*
2168
2594
            * @TODO
2169
2595
            * This whole method needs to be fixed and made more dynamic.
2173
2599
            
2174
2600
            switch (e.changedType) {
2175
2601
                case 'keydown':
2176
 
                    inst.Selection.cleanCursor();
2177
 
                    break;
2178
 
                case 'enter':
2179
 
                    if (Y.UA.webkit) {
2180
 
                        //Webkit doesn't support shift+enter as a BR, this fixes that.
2181
 
                        if (e.changedEvent.shiftKey) {
2182
 
                            this.execCommand('insertbr');
2183
 
                            e.changedEvent.preventDefault();
 
2602
                    if (!Y.UA.gecko) {
 
2603
                        if (!EditorBase.NC_KEYS[e.changedEvent.keyCode] && !e.changedEvent.shiftKey && !e.changedEvent.ctrlKey && (e.changedEvent.keyCode !== 13)) {
 
2604
                            //inst.later(100, inst, inst.Selection.cleanCursor);
2184
2605
                        }
2185
2606
                    }
2186
2607
                    break;
2187
2608
                case 'tab':
2188
2609
                    if (!e.changedNode.test('li, li *') && !e.changedEvent.shiftKey) {
2189
 
                        e.changedEvent.preventDefault();
2190
 
 
2191
 
                        var sel = new inst.Selection();
2192
 
                        sel.setCursor();
2193
 
                        var cur = sel.getCursor();
2194
 
                        cur.insert(EditorBase.TABKEY, 'before');
2195
 
                        sel.focusCursor();
2196
 
                    }
2197
 
                    break;
2198
 
                case 'enter-up':
2199
 
                    if (e.changedNode.test('p')) {
2200
 
                        var prev = e.changedNode.previous(), lc, lc2, found = false;
2201
 
                        if (prev) {
2202
 
                            lc = prev.one(':last-child');
2203
 
                            while (!found) {
2204
 
                                if (lc) {
2205
 
                                    lc2 = lc.one(':last-child');
2206
 
                                    if (lc2) {
2207
 
                                        lc = lc2;
2208
 
                                    } else {
2209
 
                                        found = true;
2210
 
                                    }
2211
 
                                } else {
2212
 
                                    found = true;
2213
 
                                }
2214
 
                            }
2215
 
                            if (lc) {
2216
 
                                this.copyStyles(lc, e.changedNode);
2217
 
                            }
 
2610
                        e.changedEvent.frameEvent.preventDefault();
 
2611
                        if (Y.UA.webkit) {
 
2612
                            this.execCommand('inserttext', '\t');
 
2613
                        } else if (Y.UA.gecko) {
 
2614
                            this.frame.exec._command('inserthtml', '<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>');
 
2615
                        } else if (Y.UA.ie) {
 
2616
                            sel = new inst.Selection();
 
2617
                            sel._selection.pasteHTML(EditorBase.TABKEY);
2218
2618
                        }
2219
2619
                    }
2220
2620
                    break;
2221
2621
            }
 
2622
            if (Y.UA.webkit && e.commands && (e.commands.indent || e.commands.outdent)) {
 
2623
                /**
 
2624
                * When executing execCommand 'indent or 'outdent' Webkit applies
 
2625
                * a class to the BLOCKQUOTE that adds left/right margin to it
 
2626
                * This strips that style so it is just a normal BLOCKQUOTE
 
2627
                */
 
2628
                var bq = inst.all('.webkit-indent-blockquote');
 
2629
                if (bq.size()) {
 
2630
                    bq.setStyle('margin', '');
 
2631
                }
 
2632
            }
2222
2633
 
2223
2634
            var changed = this.getDomPath(e.changedNode, false),
2224
2635
                cmds = {}, family, fsize, classes = [],
2238
2649
 
2239
2650
                //Bold and Italic styles
2240
2651
                var s = el.currentStyle || el.style;
2241
 
 
2242
2652
                if ((''+s.fontWeight) == 'bold') { //Cast this to a string
2243
2653
                    cmds.bold = 1;
2244
2654
                }
 
2655
                if (Y.UA.ie) {
 
2656
                    if (s.fontWeight > 400) {
 
2657
                        cmds.bold = 1;
 
2658
                    }
 
2659
                }
2245
2660
                if (s.fontStyle == 'italic') {
2246
2661
                    cmds.italic = 1;
2247
2662
                }
2263
2678
                    }
2264
2679
                }
2265
2680
 
2266
 
                fsize = n.getStyle('fontSize');
 
2681
                fsize = EditorBase.NORMALIZE_FONTSIZE(n);
 
2682
 
2267
2683
 
2268
2684
                var cls = el.className.split(' ');
2269
2685
 
2273
2689
                    }
2274
2690
                });
2275
2691
 
2276
 
                fColor = EditorBase.FILTER_RGB(s.color);
 
2692
                fColor = EditorBase.FILTER_RGB(n.getStyle('color'));
2277
2693
                var bColor2 = EditorBase.FILTER_RGB(s.backgroundColor);
2278
2694
                if (bColor2 !== 'transparent') {
2279
 
                    bColor = bColor2;
 
2695
                    if (bColor2 !== '') {
 
2696
                        bColor = bColor2;
 
2697
                    }
2280
2698
                }
2281
2699
                
2282
2700
            });
2383
2801
            
2384
2802
            this.frame.on('dom:mouseup', Y.bind(this._onFrameMouseUp, this));
2385
2803
            this.frame.on('dom:mousedown', Y.bind(this._onFrameMouseDown, this));
2386
 
            /*
 
2804
            this.frame.on('dom:keydown', Y.bind(this._onFrameKeyDown, this));
 
2805
 
 
2806
            if (Y.UA.ie) {
 
2807
                this.frame.on('dom:activate', Y.bind(this._onFrameActivate, this));
 
2808
                this.frame.on('dom:beforedeactivate', Y.bind(this._beforeFrameDeactivate, this));
 
2809
            }
2387
2810
            this.frame.on('dom:keyup', Y.bind(this._onFrameKeyUp, this));
2388
 
            this.frame.on('dom:keydown', Y.bind(this._onFrameKeyDown, this));
2389
2811
            this.frame.on('dom:keypress', Y.bind(this._onFrameKeyPress, this));
2390
 
            */
2391
 
            //this.frame.on('dom:keydown', Y.throttle(Y.bind(this._onFrameKeyDown, this), 500));
2392
 
 
2393
 
 
2394
 
            this.frame.on('dom:keydown', Y.bind(this._onFrameKeyDown, this));
2395
 
 
2396
 
            if (Y.UA.ie) {
2397
 
                this.frame.on('dom:activate', Y.bind(this._onFrameActivate, this));
2398
 
                this.frame.on('dom:keyup', Y.throttle(Y.bind(this._onFrameKeyUp, this), 800));
2399
 
                this.frame.on('dom:keypress', Y.throttle(Y.bind(this._onFrameKeyPress, this), 800));
2400
 
            } else {
2401
 
                this.frame.on('dom:keyup', Y.bind(this._onFrameKeyUp, this));
2402
 
                this.frame.on('dom:keypress', Y.bind(this._onFrameKeyPress, this));
2403
 
            }
2404
2812
 
2405
2813
            inst.Selection.filter();
2406
2814
            this.fire('ready');
2407
2815
        },
2408
2816
        /**
 
2817
        * Caches the current cursor position in IE.
 
2818
        * @method _beforeFrameDeactivate
 
2819
        * @private
 
2820
        */
 
2821
        _beforeFrameDeactivate: function() {
 
2822
            var inst = this.getInstance(),
 
2823
                sel = inst.config.doc.selection.createRange();
 
2824
            
 
2825
            if ((!sel.compareEndPoints('StartToEnd', sel))) {
 
2826
                sel.pasteHTML('<var id="yui-ie-cursor">');
 
2827
            }
 
2828
        },
 
2829
        /**
2409
2830
        * Moves the cached selection bookmark back so IE can place the cursor in the right place.
2410
2831
        * @method _onFrameActivate
2411
2832
        * @private
2412
2833
        */
2413
2834
        _onFrameActivate: function() {
2414
 
            if (this._lastBookmark) {
2415
 
                var inst = this.getInstance(),
2416
 
                    sel = inst.config.doc.selection.createRange(),
2417
 
                    bk = sel.moveToBookmark(this._lastBookmark);
 
2835
            var inst = this.getInstance(),
 
2836
                sel = new inst.Selection(),
 
2837
                range = sel.createRange(),
 
2838
                cur = inst.all('#yui-ie-cursor');
2418
2839
 
2419
 
                sel.collapse(true);
2420
 
                sel.select();
2421
 
                this._lastBookmark = null;
 
2840
            if (cur.size()) {
 
2841
                cur.each(function(n) {
 
2842
                    n.set('id', '');
 
2843
                    range.moveToElementText(n._node);
 
2844
                    range.move('character', -1);
 
2845
                    range.move('character', 1);
 
2846
                    range.select();
 
2847
                    range.text = '';
 
2848
                    n.remove();
 
2849
                });
2422
2850
            }
2423
2851
        },
2424
2852
        /**
2443
2871
        * @private
2444
2872
        */
2445
2873
        _currentSelection: null,
 
2874
        /**
 
2875
        * Holds the timer for selection clearing
 
2876
        * @property _currentSelectionTimer
 
2877
        * @private
 
2878
        */
2446
2879
        _currentSelectionTimer: null,
 
2880
        /**
 
2881
        * Flag to determine if we can clear the selection or not.
 
2882
        * @property _currentSelectionClear
 
2883
        * @private
 
2884
        */
2447
2885
        _currentSelectionClear: null,
2448
2886
        /**
2449
2887
        * Fires nodeChange event
2451
2889
        * @private
2452
2890
        */
2453
2891
        _onFrameKeyDown: function(e) {
 
2892
            var inst, sel;
2454
2893
            if (!this._currentSelection) {
2455
2894
                if (this._currentSelectionTimer) {
2456
2895
                    this._currentSelectionTimer.cancel();
2458
2897
                this._currentSelectionTimer = Y.later(850, this, function() {
2459
2898
                    this._currentSelectionClear = true;
2460
2899
                });
2461
 
                var inst = this.frame.getInstance(),
2462
 
                    sel = new inst.Selection(e);
 
2900
                
 
2901
                inst = this.frame.getInstance();
 
2902
                sel = new inst.Selection(e);
2463
2903
 
2464
2904
                this._currentSelection = sel;
2465
2905
            } else {
2466
 
                var sel = this._currentSelection;
 
2906
                sel = this._currentSelection;
2467
2907
            }
2468
 
                var inst = this.frame.getInstance(),
2469
 
                    sel = new inst.Selection();
2470
 
 
2471
 
                this._currentSelection = sel;
2472
 
 
 
2908
 
 
2909
            inst = this.frame.getInstance();
 
2910
            sel = new inst.Selection();
 
2911
 
 
2912
            this._currentSelection = sel;
 
2913
            
2473
2914
            if (sel && sel.anchorNode) {
2474
2915
                this.fire('nodeChange', { changedNode: sel.anchorNode, changedType: 'keydown', changedEvent: e.frameEvent });
2475
2916
                if (EditorBase.NC_KEYS[e.keyCode]) {
2614
3055
    }, {
2615
3056
        /**
2616
3057
        * @static
 
3058
        * @method NORMALIZE_FONTSIZE
 
3059
        * @description Pulls the fontSize from a node, then checks for string values (x-large, x-small)
 
3060
        * and converts them to pixel sizes. If the parsed size is different from the original, it calls
 
3061
        * node.setStyle to update the node with a pixel size for normalization.
 
3062
        */
 
3063
        NORMALIZE_FONTSIZE: function(n) {
 
3064
            var size = n.getStyle('fontSize'), oSize = size;
 
3065
            
 
3066
            switch (size) {
 
3067
                case '-webkit-xxx-large':
 
3068
                    size = '48px';
 
3069
                    break;
 
3070
                case 'xx-large':
 
3071
                    size = '32px';
 
3072
                    break;
 
3073
                case 'x-large':
 
3074
                    size = '24px';
 
3075
                    break;
 
3076
                case 'large':
 
3077
                    size = '18px';
 
3078
                    break;
 
3079
                case 'medium':
 
3080
                    size = '16px';
 
3081
                    break;
 
3082
                case 'small':
 
3083
                    size = '13px';
 
3084
                    break;
 
3085
                case 'x-small':
 
3086
                    size = '10px';
 
3087
                    break;
 
3088
            }
 
3089
            if (oSize !== size) {
 
3090
                n.setStyle('fontSize', size);
 
3091
            }
 
3092
            return size;
 
3093
        },
 
3094
        /**
 
3095
        * @static
2617
3096
        * @property TABKEY
2618
3097
        * @description The HTML markup to use for the tabkey
2619
3098
        */
2715
3194
            * @attribute content
2716
3195
            */
2717
3196
            content: {
2718
 
                value: '<br>',
 
3197
                value: '<br class="yui-cursor">',
2719
3198
                setter: function(str) {
2720
3199
                    if (str.substr(0, 1) === "\n") {
2721
3200
                        str = str.substr(1);
2722
3201
                    }
2723
3202
                    if (str === '') {
2724
 
                        str = '<br>';
 
3203
                        str = '<br class="yui-cursor">';
 
3204
                    }
 
3205
                    if (str === ' ') {
 
3206
                        if (Y.UA.gecko) {
 
3207
                            str = '<br class="yui-cursor">';
 
3208
                        }
2725
3209
                    }
2726
3210
                    return this.frame.set('content', str);
2727
3211
                },
2738
3222
                value: 'ltr'
2739
3223
            },
2740
3224
            /**
 
3225
            * @attribute linkedcss
 
3226
            * @description An array of url's to external linked style sheets
 
3227
            * @type String
 
3228
            */            
 
3229
            linkedcss: {
 
3230
                value: '',
 
3231
                setter: function(css) {
 
3232
                    if (this.frame) {
 
3233
                        this.frame.set('linkedcss', css);
 
3234
                    }
 
3235
                    return css;
 
3236
                }
 
3237
            },
 
3238
            /**
2741
3239
            * @attribute extracss
2742
3240
            * @description A string of CSS to add to the Head of the Editor
2743
3241
            * @type String
2750
3248
                    }
2751
3249
                    return css;
2752
3250
                }
 
3251
            },
 
3252
            /**
 
3253
            * @attribute defaultblock
 
3254
            * @description The default tag to use for block level items, defaults to: p
 
3255
            * @type String
 
3256
            */            
 
3257
            defaultblock: {
 
3258
                value: 'p'
2753
3259
            }
2754
3260
        }
2755
3261
    });
2786
3292
 
2787
3293
 
2788
3294
 
2789
 
}, '3.2.0' ,{skinnable:false, requires:['base', 'frame', 'node', 'exec-command']});
 
3295
}, '3.3.0' ,{requires:['base', 'frame', 'node', 'exec-command', 'selection', 'editor-para'], skinnable:false});
2790
3296
YUI.add('editor-lists', function(Y) {
2791
3297
 
2792
3298
    /**
2954
3460
 
2955
3461
 
2956
3462
 
2957
 
}, '3.2.0' ,{skinnable:false, requires:['editor-base']});
 
3463
}, '3.3.0' ,{requires:['editor-base'], skinnable:false});
2958
3464
YUI.add('editor-bidi', function(Y) {
2959
3465
 
2960
3466
 
3202
3708
        var inst = this.getInstance(),
3203
3709
            sel = new inst.Selection(),
3204
3710
            returnValue, block,
3205
 
            selected, selectedBlocks;
 
3711
            selected, selectedBlocks, dir;
3206
3712
 
3207
3713
        inst.Selection.filterBlocks();
3208
3714
        if (sel.isCollapsed) { // No selection
3209
3715
            block = EditorBidi.blockParent(sel.anchorNode);
 
3716
            if (!direction) {
 
3717
                //If no direction is set, auto-detect the proper setting to make it "toggle"
 
3718
                dir = block.getAttribute(DIR);
 
3719
                if (!dir || dir == 'ltr') {
 
3720
                    direction = 'rtl';
 
3721
                } else {
 
3722
                    direction = 'ltr';
 
3723
                }
 
3724
            }
3210
3725
            block.setAttribute(DIR, direction);
3211
3726
            returnValue = block;
3212
3727
        } else { // some text is selected
3213
3728
            selected = sel.getSelected();
3214
3729
            selectedBlocks = [];
3215
3730
            selected.each(function(node) {
3216
 
                if (!node.test(BODY)) { // workaround for a YUI bug
 
3731
                /*
 
3732
                * Temporarily removed this check, should already be fixed
 
3733
                * in Y.Selection.getSelected()
 
3734
                */
 
3735
                //if (!node.test(BODY)) { // workaround for a YUI bug
3217
3736
                   selectedBlocks.push(EditorBidi.blockParent(node));
3218
 
                }
 
3737
                //}
3219
3738
            });
3220
3739
            selectedBlocks = inst.all(EditorBidi.addParents(selectedBlocks));
3221
3740
            selectedBlocks.setAttribute(DIR, direction);
3222
3741
            returnValue = selectedBlocks;
3223
3742
        }
3224
3743
 
3225
 
        this.get(HOST).get(HOST).editorBidi.checkForChange();
 
3744
        this.get(HOST).get(HOST).editorBidi._checkForChange();
3226
3745
        return returnValue;
3227
3746
    };
3228
3747
 
3229
3748
 
3230
3749
 
3231
3750
 
3232
 
}, '3.2.0' ,{skinnable:false, requires:['editor-base', 'selection']});
 
3751
}, '3.3.0' ,{requires:['editor-base'], skinnable:false});
3233
3752
YUI.add('editor-para', function(Y) {
3234
3753
 
3235
3754
 
3249
3768
 
3250
3769
    var EditorPara = function() {
3251
3770
        EditorPara.superclass.constructor.apply(this, arguments);
3252
 
    }, HOST = 'host', BODY = 'body', NODE_CHANGE = 'nodeChange',
3253
 
    FIRST_P = BODY + ' > p';
 
3771
    }, HOST = 'host', BODY = 'body', NODE_CHANGE = 'nodeChange', PARENT_NODE = 'parentNode',
 
3772
    FIRST_P = BODY + ' > p', P = 'p', BR = '<br>', FC = 'firstChild', LI = 'li';
 
3773
 
3254
3774
 
3255
3775
    Y.extend(EditorPara, Y.Base, {
3256
3776
        /**
3260
3780
        */
3261
3781
        _fixFirstPara: function() {
3262
3782
            var host = this.get(HOST), inst = host.getInstance(), sel;
3263
 
            inst.one('body').setContent('<p>' + inst.Selection.CURSOR + '</p>');
 
3783
            inst.one('body').set('innerHTML', '<' + P + '>' + inst.Selection.CURSOR + '</' + P + '>');
 
3784
            var n = inst.one(FIRST_P);
3264
3785
            sel = new inst.Selection();
3265
 
            sel.focusCursor(true, false);
 
3786
            sel.selectNode(n, true, false);
 
3787
            
3266
3788
        },
3267
3789
        /**
3268
3790
        * nodeChange handler to handle fixing an empty document.
3270
3792
        * @method _onNodeChange
3271
3793
        */
3272
3794
        _onNodeChange: function(e) {
3273
 
            var host = this.get(HOST), inst = host.getInstance();
 
3795
            var host = this.get(HOST), inst = host.getInstance(),
 
3796
                html, txt, par , d, sel, btag = inst.Selection.DEFAULT_BLOCK_TAG,
 
3797
                inHTML, txt2, childs, aNode, index, node2, top, n, sib,
 
3798
                ps, br, item, p, imgs, t, LAST_CHILD = ':last-child';
3274
3799
 
3275
3800
            switch (e.changedType) {
 
3801
                case 'enter-up':
 
3802
                    var para = ((this._lastPara) ? this._lastPara : e.changedNode),
 
3803
                        b = para.one('br.yui-cursor');
 
3804
 
 
3805
                    if (this._lastPara) {
 
3806
                        delete this._lastPara;
 
3807
                    }
 
3808
 
 
3809
                    if (b) {
 
3810
                        if (b.previous() || b.next()) {
 
3811
                            b.remove();
 
3812
                        }
 
3813
                    }
 
3814
                    if (!para.test(btag)) {
 
3815
                        var para2 = para.ancestor(btag);
 
3816
                        if (para2) {
 
3817
                            para = para2;
 
3818
                            para2 = null;
 
3819
                        }
 
3820
                    }
 
3821
                    if (para.test(btag)) {
 
3822
                        var prev = para.previous(), lc, lc2, found = false;
 
3823
                        if (prev) {
 
3824
                            lc = prev.one(LAST_CHILD);
 
3825
                            while (!found) {
 
3826
                                if (lc) {
 
3827
                                    lc2 = lc.one(LAST_CHILD);
 
3828
                                    if (lc2) {
 
3829
                                        lc = lc2;
 
3830
                                    } else {
 
3831
                                        found = true;
 
3832
                                    }
 
3833
                                } else {
 
3834
                                    found = true;
 
3835
                                }
 
3836
                            }
 
3837
                            if (lc) {
 
3838
                                host.copyStyles(lc, para);
 
3839
                            }
 
3840
                        }
 
3841
                    }
 
3842
                    break;
 
3843
                case 'enter':
 
3844
                    if (Y.UA.webkit) {
 
3845
                        //Webkit doesn't support shift+enter as a BR, this fixes that.
 
3846
                        if (e.changedEvent.shiftKey) {
 
3847
                            host.execCommand('insertbr');
 
3848
                            e.changedEvent.preventDefault();
 
3849
                        }
 
3850
                    }
 
3851
                    //TODO Move this to a GECKO MODULE - Can't for the moment, requires no change to metadata (YMAIL)
 
3852
                    if (Y.UA.gecko && host.get('defaultblock') !== 'p') {
 
3853
                        par = e.changedNode;
 
3854
 
 
3855
                        if (!par.test(LI) && !par.ancestor(LI)) {
 
3856
                            if (!par.test(btag)) {
 
3857
                                par = par.ancestor(btag);
 
3858
                            }
 
3859
                            d = inst.Node.create('<' + btag + '></' + btag + '>');
 
3860
                            par.insert(d, 'after');
 
3861
                            sel = new inst.Selection();
 
3862
                            if (sel.anchorOffset) {
 
3863
                                inHTML = sel.anchorNode.get('textContent');
 
3864
 
 
3865
                                txt = inst.one(inst.config.doc.createTextNode(inHTML.substr(0, sel.anchorOffset)));
 
3866
                                txt2 = inst.one(inst.config.doc.createTextNode(inHTML.substr(sel.anchorOffset)));
 
3867
 
 
3868
                                aNode = sel.anchorNode;
 
3869
                                aNode.setContent(''); //I
 
3870
                                node2 = aNode.cloneNode(); //I
 
3871
                                node2.append(txt2); //text
 
3872
                                top = false;
 
3873
                                sib = aNode; //I
 
3874
                                while (!top) {
 
3875
                                    sib = sib.get(PARENT_NODE); //B
 
3876
                                    if (sib && !sib.test(btag)) {
 
3877
                                        n = sib.cloneNode();
 
3878
                                        n.set('innerHTML', '');
 
3879
                                        n.append(node2);
 
3880
                                        
 
3881
                                        //Get children..
 
3882
                                        childs = sib.get('childNodes');
 
3883
                                        var start = false;
 
3884
                                        childs.each(function(c) {
 
3885
                                            if (start) {
 
3886
                                                n.append(c);
 
3887
                                            }
 
3888
                                            if (c === aNode) {
 
3889
                                                start = true;
 
3890
                                            }
 
3891
                                        });
 
3892
 
 
3893
                                        aNode = sib; //Top sibling
 
3894
                                        node2 = n;
 
3895
                                    } else {
 
3896
                                        top = true;
 
3897
                                    }
 
3898
                                }
 
3899
                                txt2 = node2;
 
3900
                                sel.anchorNode.append(txt);
 
3901
 
 
3902
                                if (txt2) {
 
3903
                                    d.append(txt2);
 
3904
                                }
 
3905
                            }
 
3906
                            if (d.get(FC)) {
 
3907
                                d = d.get(FC);
 
3908
                            }
 
3909
                            d.prepend(inst.Selection.CURSOR);
 
3910
                            sel.focusCursor(true, true);
 
3911
                            html = inst.Selection.getText(d);
 
3912
                            if (html !== '') {
 
3913
                                inst.Selection.cleanCursor();
 
3914
                            }
 
3915
                            e.changedEvent.preventDefault();
 
3916
                        }
 
3917
                    }
 
3918
                    break;
3276
3919
                case 'keydown':
3277
3920
                    if (inst.config.doc.childNodes.length < 2) {
3278
3921
                        var cont = inst.config.doc.body.innerHTML;
3279
 
                        if (cont && cont.length < 5 && cont.toLowerCase() == '<br>') {
 
3922
                        if (cont && cont.length < 5 && cont.toLowerCase() == BR) {
3280
3923
                            this._fixFirstPara();
3281
3924
                        }
3282
3925
                    }
3283
3926
                    break;
3284
3927
                case 'backspace-up':
 
3928
                case 'backspace-down':
3285
3929
                case 'delete-up':
3286
 
                    var ps = inst.all(FIRST_P), br, item;
3287
 
                    if (ps.size() < 2) {
 
3930
                    if (!Y.UA.ie) {
 
3931
                        ps = inst.all(FIRST_P);
3288
3932
                        item = inst.one(BODY);
3289
3933
                        if (ps.item(0)) {
3290
3934
                            item = ps.item(0);
3291
3935
                        }
3292
 
                        if (inst.Selection.getText(item) === '' && !item.test('p')) {
3293
 
                            this._fixFirstPara();
3294
 
                        } else if (item.test('p') && item.get('innerHTML').length === 0) {
3295
 
                            e.changedEvent.halt();
3296
 
                        }
 
3936
                        br = item.one('br');
 
3937
                        if (br) {
 
3938
                            br.removeAttribute('id');
 
3939
                            br.removeAttribute('class');
 
3940
                        }
 
3941
 
 
3942
                        txt = inst.Selection.getText(item);
 
3943
                        txt = txt.replace(/ /g, '').replace(/\n/g, '');
 
3944
                        imgs = item.all('img');
 
3945
                        
 
3946
                        if (txt.length === 0 && !imgs.size()) {
 
3947
                            //God this is horrible..
 
3948
                            if (!item.test(P)) {
 
3949
                                this._fixFirstPara();
 
3950
                            }
 
3951
                            p = null;
 
3952
                            if (e.changedNode && e.changedNode.test(P)) {
 
3953
                                p = e.changedNode;
 
3954
                            }
 
3955
                            if (!p && host._lastPara && host._lastPara.inDoc()) {
 
3956
                                p = host._lastPara;
 
3957
                            }
 
3958
                            if (p && !p.test(P)) {
 
3959
                                p = p.ancestor(P);
 
3960
                            }
 
3961
                            if (p) {
 
3962
                                if (!p.previous() && p.get(PARENT_NODE) && p.get(PARENT_NODE).test(BODY)) {
 
3963
                                    e.changedEvent.frameEvent.halt();
 
3964
                                }
 
3965
                            }
 
3966
                        }
 
3967
                        if (Y.UA.webkit) {
 
3968
                            if (e.changedNode) {
 
3969
                                item = e.changedNode;
 
3970
                                if (item.test('li') && (!item.previous() && !item.next())) {
 
3971
                                    html = item.get('innerHTML').replace(BR, '');
 
3972
                                    if (html === '') {
 
3973
                                        if (item.get(PARENT_NODE)) {
 
3974
                                            item.get(PARENT_NODE).replace(inst.Node.create(BR));
 
3975
                                            e.changedEvent.frameEvent.halt();
 
3976
                                            e.preventDefault();
 
3977
                                            inst.Selection.filterBlocks();
 
3978
                                        }
 
3979
                                    }
 
3980
                                }
 
3981
                            }
 
3982
                        }
 
3983
                    }
 
3984
                    if (Y.UA.gecko) {
 
3985
                        /**
 
3986
                        * This forced FF to redraw the content on backspace.
 
3987
                        * On some occasions FF will leave a cursor residue after content has been deleted.
 
3988
                        * Dropping in the empty textnode and then removing it causes FF to redraw and
 
3989
                        * remove the "ghost cursors"
 
3990
                        */
 
3991
                        d = e.changedNode;
 
3992
                        t = inst.config.doc.createTextNode(' ');
 
3993
                        d.appendChild(t);
 
3994
                        d.removeChild(t);
3297
3995
                    }
3298
3996
                    break;
3299
3997
            }
 
3998
            if (Y.UA.gecko) {
 
3999
                if (e.changedNode && !e.changedNode.test(btag)) {
 
4000
                    var p = e.changedNode.ancestor(btag);
 
4001
                    if (p) {
 
4002
                        this._lastPara = p;
 
4003
                    }
 
4004
                }
 
4005
            }
3300
4006
            
3301
4007
        },
3302
4008
        /**
3305
4011
        * @method _afterEditorReady
3306
4012
        */
3307
4013
        _afterEditorReady: function() {
3308
 
            var host = this.get(HOST), inst = host.getInstance();
 
4014
            var host = this.get(HOST), inst = host.getInstance(), btag;
3309
4015
            if (inst) {
3310
4016
                inst.Selection.filterBlocks();
 
4017
                btag = inst.Selection.DEFAULT_BLOCK_TAG;
 
4018
                FIRST_P = BODY + ' > ' + btag;
 
4019
                P = btag;
3311
4020
            }
3312
4021
        },
3313
4022
        /**
3330
4039
            var host = this.get(HOST), inst = host.getInstance(),
3331
4040
                sel = new inst.Selection();
3332
4041
 
3333
 
            sel.setCursor();
3334
 
            
3335
4042
            Y.later(50, host, function() {
3336
4043
                inst.Selection.filterBlocks();
3337
 
                sel.focusCursor(true, true);
3338
4044
            });
3339
4045
            
3340
4046
        },
3341
4047
        initializer: function() {
3342
4048
            var host = this.get(HOST);
 
4049
            if (host.editorBR) {
 
4050
                Y.error('Can not plug EditorPara and EditorBR at the same time.');
 
4051
                return;
 
4052
            }
3343
4053
 
3344
4054
            host.on(NODE_CHANGE, Y.bind(this._onNodeChange, this));
3345
4055
            host.after('ready', Y.bind(this._afterEditorReady, this));
3346
4056
            host.after('contentChange', Y.bind(this._afterContentChange, this));
3347
 
            host.after('dom:paste', Y.bind(this._afterPaste, this));
 
4057
            if (Y.Env.webkit) {
 
4058
                host.after('dom:paste', Y.bind(this._afterPaste, this));
 
4059
            }
3348
4060
        }
3349
4061
    }, {
3350
4062
        /**
3351
 
        * editorBidi
 
4063
        * editorPara
3352
4064
        * @static
3353
4065
        * @property NAME
3354
4066
        */
3355
4067
        NAME: 'editorPara',
3356
4068
        /**
3357
 
        * editorBidi
 
4069
        * editorPara
3358
4070
        * @static
3359
4071
        * @property NS
3360
4072
        */
3372
4084
 
3373
4085
 
3374
4086
 
3375
 
}, '3.2.0' ,{skinnable:false, requires:['editor-base', 'selection']});
3376
 
 
3377
 
 
3378
 
YUI.add('editor', function(Y){}, '3.2.0' ,{use:['frame', 'selection', 'exec-command', 'editor-base'], skinnable:false});
 
4087
}, '3.3.0' ,{requires:['node'], skinnable:false});
 
4088
YUI.add('editor-br', function(Y) {
 
4089
 
 
4090
 
 
4091
 
 
4092
    /**
 
4093
     * Plugin for Editor to normalize BR's.
 
4094
     * @module editor
 
4095
     * @submodule editor-br
 
4096
     */     
 
4097
    /**
 
4098
     * Plugin for Editor to normalize BR's.
 
4099
     * @class Plugin.EditorBR
 
4100
     * @extends Base
 
4101
     * @constructor
 
4102
     */
 
4103
 
 
4104
 
 
4105
    var EditorBR = function() {
 
4106
        EditorBR.superclass.constructor.apply(this, arguments);
 
4107
    }, HOST = 'host', LI = 'li';
 
4108
 
 
4109
 
 
4110
    Y.extend(EditorBR, Y.Base, {
 
4111
        /**
 
4112
        * Frame keyDown handler that normalizes BR's when pressing ENTER.
 
4113
        * @private
 
4114
        * @method _onKeyDown
 
4115
        */
 
4116
        _onKeyDown: function(e) {
 
4117
            if (e.stopped) {
 
4118
                e.halt();
 
4119
                return;
 
4120
            }
 
4121
            if (e.keyCode == 13) {
 
4122
                var host = this.get(HOST), inst = host.getInstance(),
 
4123
                    sel = new inst.Selection(),
 
4124
                    last = '';
 
4125
 
 
4126
                if (sel) {
 
4127
                    if (Y.UA.ie) {
 
4128
                        if (!sel.anchorNode || (!sel.anchorNode.test(LI) && !sel.anchorNode.ancestor(LI))) {
 
4129
                            sel._selection.pasteHTML('<br>');
 
4130
                            sel._selection.collapse(false);
 
4131
                            sel._selection.select();
 
4132
                            e.halt();
 
4133
                        }
 
4134
                    }
 
4135
                    if (Y.UA.webkit) {
 
4136
                        if (!sel.anchorNode.test(LI) && !sel.anchorNode.ancestor(LI)) {
 
4137
                            host.frame._execCommand('insertlinebreak', null);
 
4138
                            e.halt();
 
4139
                        }
 
4140
                    }
 
4141
                }
 
4142
            }
 
4143
        },
 
4144
        /**
 
4145
        * Adds listeners for keydown in IE and Webkit. Also fires insertbeonreturn for supporting browsers.
 
4146
        * @private
 
4147
        * @method _afterEditorReady
 
4148
        */
 
4149
        _afterEditorReady: function() {
 
4150
            var inst = this.get(HOST).getInstance();
 
4151
            try {
 
4152
                inst.config.doc.execCommand('insertbronreturn', null, true);
 
4153
            } catch (bre) {};
 
4154
 
 
4155
            if (Y.UA.ie || Y.UA.webkit) {
 
4156
                inst.on('keydown', Y.bind(this._onKeyDown, this), inst.config.doc);
 
4157
            }
 
4158
        },
 
4159
        /**
 
4160
        * Adds a nodeChange listener only for FF, in the event of a backspace or delete, it creates an empy textNode
 
4161
        * inserts it into the DOM after the e.changedNode, then removes it. Causing FF to redraw the content.
 
4162
        * @private
 
4163
        * @method _onNodeChange
 
4164
        * @param {Event} e The nodeChange event.
 
4165
        */
 
4166
        _onNodeChange: function(e) {
 
4167
            switch (e.changedType) {
 
4168
                case 'backspace-up':
 
4169
                case 'backspace-down':
 
4170
                case 'delete-up':
 
4171
                    /**
 
4172
                    * This forced FF to redraw the content on backspace.
 
4173
                    * On some occasions FF will leave a cursor residue after content has been deleted.
 
4174
                    * Dropping in the empty textnode and then removing it causes FF to redraw and
 
4175
                    * remove the "ghost cursors"
 
4176
                    */
 
4177
                    var inst = this.get(HOST).getInstance();
 
4178
                    var d = e.changedNode;
 
4179
                    var t = inst.config.doc.createTextNode(' ');
 
4180
                    d.appendChild(t);
 
4181
                    d.removeChild(t);
 
4182
                    break;
 
4183
            }
 
4184
        },
 
4185
        initializer: function() {
 
4186
            var host = this.get(HOST);
 
4187
            if (host.editorPara) {
 
4188
                Y.error('Can not plug EditorBR and EditorPara at the same time.');
 
4189
                return;
 
4190
            }
 
4191
            host.after('ready', Y.bind(this._afterEditorReady, this));
 
4192
            if (Y.UA.gecko) {
 
4193
                host.on('nodeChange', Y.bind(this._onNodeChange, this));
 
4194
            }
 
4195
        }
 
4196
    }, {
 
4197
        /**
 
4198
        * editorBR
 
4199
        * @static
 
4200
        * @property NAME
 
4201
        */
 
4202
        NAME: 'editorBR',
 
4203
        /**
 
4204
        * editorBR
 
4205
        * @static
 
4206
        * @property NS
 
4207
        */
 
4208
        NS: 'editorBR',
 
4209
        ATTRS: {
 
4210
            host: {
 
4211
                value: false
 
4212
            }
 
4213
        }
 
4214
    });
 
4215
    
 
4216
    Y.namespace('Plugin');
 
4217
    
 
4218
    Y.Plugin.EditorBR = EditorBR;
 
4219
 
 
4220
 
 
4221
 
 
4222
}, '3.3.0' ,{requires:['node'], skinnable:false});
 
4223
 
 
4224
 
 
4225
YUI.add('editor', function(Y){}, '3.3.0' ,{skinnable:false, use:['frame', 'selection', 'exec-command', 'editor-base', 'editor-para', 'editor-br', 'editor-bidi', 'createlink-base']});
3379
4226