Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
YUI.add('editor-base', function (Y, NAME) {
2
 
3
 
4
    /**
5
     * Base class for Editor. Handles the business logic of Editor, no GUI involved only utility methods and events.
6
     *
7
     *      var editor = new Y.EditorBase({
8
     *          content: 'Foo'
9
     *      });
10
     *      editor.render('#demo');
11
     *
12
     * @class EditorBase
13
     * @extends Base
14
     * @module editor
15
     * @main editor
16
     * @submodule editor-base
17
     * @constructor
18
     */
19
 
20
    var Lang = Y.Lang,
21
 
22
    EditorBase = function() {
23
        EditorBase.superclass.constructor.apply(this, arguments);
24
    }, LAST_CHILD = ':last-child';
25
 
26
    Y.extend(EditorBase, Y.Base, {
27
        /**
28
        * Internal reference to the Y.ContentEditable instance
29
        * @property frame
30
        */
31
        frame: null,
32
 
33
        initializer: function() {
34
            this.publish('nodeChange', {
35
                emitFacade: true,
36
                bubbles: true,
37
                defaultFn: this._defNodeChangeFn
38
            });
39
 
40
            //this.plug(Y.Plugin.EditorPara);
41
        },
42
        destructor: function() {
43
            this.detachAll();
44
        },
45
        /**
46
        * Copy certain styles from one node instance to another (used for new paragraph creation mainly)
47
        * @method copyStyles
48
        * @param {Node} from The Node instance to copy the styles from
49
        * @param {Node} to The Node instance to copy the styles to
50
        */
51
        copyStyles: function(from, to) {
52
            if (from.test('a')) {
53
                //Don't carry the A styles
54
                return;
55
            }
56
            var styles = ['color', 'fontSize', 'fontFamily', 'backgroundColor', 'fontStyle' ],
57
                newStyles = {};
58
 
59
            Y.each(styles, function(v) {
60
                newStyles[v] = from.getStyle(v);
61
            });
62
            if (from.ancestor('b,strong')) {
63
                newStyles.fontWeight = 'bold';
64
            }
65
            if (from.ancestor('u')) {
66
                if (!newStyles.textDecoration) {
67
                    newStyles.textDecoration = 'underline';
68
                }
69
            }
70
            to.setStyles(newStyles);
71
        },
72
        /**
73
        * Holder for the selection bookmark in IE.
74
        * @property _lastBookmark
75
        * @private
76
        */
77
        _lastBookmark: null,
78
        /**
79
        * Resolves the e.changedNode in the nodeChange event if it comes from the document. If
80
        * the event came from the document, it will get the last child of the last child of the document
81
        * and return that instead.
82
        * @method _resolveChangedNode
83
        * @param {Node} n The node to resolve
84
        * @private
85
        */
86
        _resolveChangedNode: function(n) {
87
            var inst = this.getInstance(), lc, lc2, found, root = this._getRoot(), sel;
88
 
89
            if (n && n.compareTo(root)) {
90
                sel = new inst.EditorSelection();
91
                if (sel && sel.anchorNode) {
92
                    n = sel.anchorNode;
93
                }
94
            }
95
            if (inst && n && n.test('html')) {
96
                lc = root.one(LAST_CHILD);
97
                while (!found) {
98
                    if (lc) {
99
                        lc2 = lc.one(LAST_CHILD);
100
                        if (lc2) {
101
                            lc = lc2;
102
                        } else {
103
                            found = true;
104
                        }
105
                    } else {
106
                        found = true;
107
                    }
108
                }
109
                if (lc) {
110
                    if (lc.test('br')) {
111
                        if (lc.previous()) {
112
                            lc = lc.previous();
113
                        } else {
114
                            lc = lc.get('parentNode');
115
                        }
116
                    }
117
                    if (lc) {
118
                        n = lc;
119
                    }
120
                }
121
            }
122
            if (!n) {
123
                //Fallback to make sure a node is attached to the event
124
                n = root;
125
            }
126
            return n;
127
        },
128
        /**
129
        * Resolves the ROOT editor element.
130
        * @method _getRoot
131
        * @private
132
        */
133
        _getRoot: function() {
134
            return this.getInstance().EditorSelection.ROOT;
135
        },
136
        /**
137
        * The default handler for the nodeChange event.
138
        * @method _defNodeChangeFn
139
        * @param {Event} e The event
140
        * @private
141
        */
142
        _defNodeChangeFn: function(e) {
143
            var startTime = (new Date()).getTime(),
144
                inst = this.getInstance(), sel,
145
                changed, endTime,
146
                cmds = {}, family, fsize, classes = [],
147
                fColor = '', bColor = '', bq,
148
                normal = false,
149
                root = this._getRoot();
150
 
151
            if (Y.UA.ie && Y.UA.ie < 11) {
152
                try {
153
                    sel = inst.config.doc.selection.createRange();
154
                    if (sel.getBookmark) {
155
                        this._lastBookmark = sel.getBookmark();
156
                    }
157
                } catch (ie) {}
158
            }
159
 
160
            e.changedNode = this._resolveChangedNode(e.changedNode);
161
 
162
 
163
            /*
164
            * @TODO
165
            * This whole method needs to be fixed and made more dynamic.
166
            * Maybe static functions for the e.changeType and an object bag
167
            * to walk through and filter to pass off the event to before firing..
168
            */
169
 
170
            if (e.changedType === 'tab') {
171
                if (!e.changedNode.test('li, li *') && !e.changedEvent.shiftKey) {
172
                    e.changedEvent.frameEvent.preventDefault();
173
                    if (Y.UA.webkit) {
174
                        this.execCommand('inserttext', '\t');
175
                    } else if (Y.UA.gecko) {
176
                        this.frame.exec._command('inserthtml', EditorBase.TABKEY);
177
                    } else if (Y.UA.ie) {
178
                        this.execCommand('inserthtml', EditorBase.TABKEY);
179
                    }
180
                }
181
            }
182
 
183
            if (Y.UA.webkit && e.commands && (e.commands.indent || e.commands.outdent)) {
184
                /*
185
                * When executing execCommand 'indent or 'outdent' Webkit applies
186
                * a class to the BLOCKQUOTE that adds left/right margin to it
187
                * This strips that style so it is just a normal BLOCKQUOTE
188
                */
189
                bq = root.all('.webkit-indent-blockquote, blockquote');
190
                if (bq.size()) {
191
                    bq.setStyle('margin', '');
192
                }
193
            }
194
 
195
            changed = this.getDomPath(e.changedNode, false);
196
 
197
            if (e.commands) {
198
                cmds = e.commands;
199
            }
200
 
201
 
202
            Y.each(changed, function(el) {
203
                var tag = el.tagName.toLowerCase(),
204
                    cmd = EditorBase.TAG2CMD[tag], s,
205
                    n, family2, cls, bColor2;
206
 
207
                if (cmd) {
208
                    cmds[cmd] = 1;
209
                }
210
 
211
                //Bold and Italic styles
212
                s = el.currentStyle || el.style;
213
 
214
                if ((''+s.fontWeight) === 'normal') {
215
                    normal = true;
216
                }
217
                if ((''+s.fontWeight) === 'bold') { //Cast this to a string
218
                    cmds.bold = 1;
219
                }
220
                if (Y.UA.ie) {
221
                    if (s.fontWeight > 400) {
222
                        cmds.bold = 1;
223
                    }
224
                }
225
                if (s.fontStyle === 'italic') {
226
                    cmds.italic = 1;
227
                }
228
 
229
                if (s.textDecoration.indexOf('underline') > -1) {
230
                    cmds.underline = 1;
231
                }
232
                if (s.textDecoration.indexOf('line-through') > -1) {
233
                    cmds.strikethrough = 1;
234
                }
235
 
236
                n = inst.one(el);
237
                if (n.getStyle('fontFamily')) {
238
                    family2 = n.getStyle('fontFamily').split(',')[0].toLowerCase();
239
                    if (family2) {
240
                        family = family2;
241
                    }
242
                    if (family) {
243
                        family = family.replace(/'/g, '').replace(/"/g, '');
244
                    }
245
                }
246
 
247
                fsize = EditorBase.NORMALIZE_FONTSIZE(n);
248
 
249
 
250
                cls = el.className.split(' ');
251
                Y.each(cls, function(v) {
252
                    if (v !== '' && (v.substr(0, 4) !== 'yui_')) {
253
                        classes.push(v);
254
                    }
255
                });
256
 
257
                fColor = EditorBase.FILTER_RGB(n.getStyle('color'));
258
                bColor2 = EditorBase.FILTER_RGB(s.backgroundColor);
259
                if (bColor2 !== 'transparent') {
260
                    if (bColor2 !== '') {
261
                        bColor = bColor2;
262
                    }
263
                }
264
 
265
            });
266
 
267
            if (normal) {
268
                delete cmds.bold;
269
                delete cmds.italic;
270
            }
271
 
272
            e.dompath = inst.all(changed);
273
            e.classNames = classes;
274
            e.commands = cmds;
275
 
276
            //TODO Dont' like this, not dynamic enough..
277
            if (!e.fontFamily) {
278
                e.fontFamily = family;
279
            }
280
            if (!e.fontSize) {
281
                e.fontSize = fsize;
282
            }
283
            if (!e.fontColor) {
284
                e.fontColor = fColor;
285
            }
286
            if (!e.backgroundColor) {
287
                e.backgroundColor = bColor;
288
            }
289
 
290
            endTime = (new Date()).getTime();
291
        },
292
        /**
293
        * Walk the dom tree from this node up to body, returning a reversed array of parents.
294
        * @method getDomPath
295
        * @param {Node} node The Node to start from
296
        */
297
        getDomPath: function(node, nodeList) {
298
            var domPath = [], domNode, rootNode,
299
                root = this._getRoot(),
300
                inst = this.frame.getInstance();
301
 
302
            domNode = inst.Node.getDOMNode(node);
303
            rootNode = inst.Node.getDOMNode(root);
304
            //return inst.all(domNode);
305
 
306
            while (domNode !== null) {
307
 
308
                if ((domNode === inst.config.doc.documentElement) || (domNode === inst.config.doc) || !domNode.tagName) {
309
                    domNode = null;
310
                    break;
311
                }
312
 
313
                if (!inst.DOM.inDoc(domNode)) {
314
                    domNode = null;
315
                    break;
316
                }
317
 
318
                //Check to see if we get el.nodeName and nodeType
319
                if (domNode.nodeName && domNode.nodeType && (domNode.nodeType === 1)) {
320
                    domPath.push(domNode);
321
                }
322
 
323
                if (domNode === rootNode) {
324
                    domNode = null;
325
                    break;
326
                }
327
 
328
                domNode = domNode.parentNode;
329
            }
330
 
331
            /*{{{ Using Node
332
            while (node !== null) {
333
                if (node.test('html') || node.test('doc') || !node.get('tagName')) {
334
                    node = null;
335
                    break;
336
                }
337
                if (!node.inDoc()) {
338
                    node = null;
339
                    break;
340
                }
341
                //Check to see if we get el.nodeName and nodeType
342
                if (node.get('nodeName') && node.get('nodeType') && (node.get('nodeType') == 1)) {
343
                    domPath.push(inst.Node.getDOMNode(node));
344
                }
345
 
346
                if (node.test('body')) {
347
                    node = null;
348
                    break;
349
                }
350
 
351
                node = node.get('parentNode');
352
            }
353
            }}}*/
354
 
355
            if (domPath.length === 0) {
356
                domPath[0] = inst.config.doc.body;
357
            }
358
 
359
            if (nodeList) {
360
                return inst.all(domPath.reverse());
361
            } else {
362
                return domPath.reverse();
363
            }
364
 
365
        },
366
        /**
367
        * After frame ready, bind mousedown & keyup listeners
368
        * @method _afterFrameReady
369
        * @private
370
        */
371
        _afterFrameReady: function() {
372
            var inst = this.frame.getInstance();
373
 
374
            this.frame.on('dom:mouseup', Y.bind(this._onFrameMouseUp, this));
375
            this.frame.on('dom:mousedown', Y.bind(this._onFrameMouseDown, this));
376
            this.frame.on('dom:keydown', Y.bind(this._onFrameKeyDown, this));
377
 
378
            if (Y.UA.ie && Y.UA.ie < 11) {
379
                this.frame.on('dom:activate', Y.bind(this._onFrameActivate, this));
380
                this.frame.on('dom:beforedeactivate', Y.bind(this._beforeFrameDeactivate, this));
381
            }
382
            this.frame.on('dom:keyup', Y.bind(this._onFrameKeyUp, this));
383
            this.frame.on('dom:keypress', Y.bind(this._onFrameKeyPress, this));
384
            this.frame.on('dom:paste', Y.bind(this._onPaste, this));
385
 
386
            inst.EditorSelection.filter();
387
            this.fire('ready');
388
        },
389
        /**
390
        * Caches the current cursor position in IE.
391
        * @method _beforeFrameDeactivate
392
        * @private
393
        */
394
        _beforeFrameDeactivate: function(e) {
395
            if (e.frameTarget.test('html')) { //Means it came from a scrollbar
396
                return;
397
            }
398
            var inst = this.getInstance(),
399
                sel = inst.config.doc.selection.createRange();
400
 
401
            if (sel.compareEndPoints && !sel.compareEndPoints('StartToEnd', sel)) {
402
                sel.pasteHTML('<var id="yui-ie-cursor">');
403
            }
404
        },
405
        /**
406
        * Moves the cached selection bookmark back so IE can place the cursor in the right place.
407
        * @method _onFrameActivate
408
        * @private
409
        */
410
        _onFrameActivate: function(e) {
411
            if (e.frameTarget.test('html')) { //Means it came from a scrollbar
412
                return;
413
            }
414
            var inst = this.getInstance(),
415
                sel = new inst.EditorSelection(),
416
                range = sel.createRange(),
417
                root = this._getRoot(),
418
                cur = root.all('#yui-ie-cursor');
419
 
420
            if (cur.size()) {
421
                cur.each(function(n) {
422
                    n.set('id', '');
423
                    if (range.moveToElementText) {
424
                        try {
425
                            range.moveToElementText(n._node);
426
                            var moved = range.move('character', -1);
427
                            if (moved === -1) { //Only move up if we actually moved back.
428
                                range.move('character', 1);
429
                            }
430
                            range.select();
431
                            range.text = '';
432
                        } catch (e) {}
433
                    }
434
                    n.remove();
435
                });
436
            }
437
        },
438
        /**
439
        * Fires nodeChange event
440
        * @method _onPaste
441
        * @private
442
        */
443
        _onPaste: function(e) {
444
            this.fire('nodeChange', { changedNode: e.frameTarget, changedType: 'paste', changedEvent: e.frameEvent });
445
        },
446
        /**
447
        * Fires nodeChange event
448
        * @method _onFrameMouseUp
449
        * @private
450
        */
451
        _onFrameMouseUp: function(e) {
452
            this.fire('nodeChange', { changedNode: e.frameTarget, changedType: 'mouseup', changedEvent: e.frameEvent  });
453
        },
454
        /**
455
        * Fires nodeChange event
456
        * @method _onFrameMouseDown
457
        * @private
458
        */
459
        _onFrameMouseDown: function(e) {
460
            this.fire('nodeChange', { changedNode: e.frameTarget, changedType: 'mousedown', changedEvent: e.frameEvent  });
461
        },
462
        /**
463
        * Caches a copy of the selection for key events. Only creating the selection on keydown
464
        * @property _currentSelection
465
        * @private
466
        */
467
        _currentSelection: null,
468
        /**
469
        * Holds the timer for selection clearing
470
        * @property _currentSelectionTimer
471
        * @private
472
        */
473
        _currentSelectionTimer: null,
474
        /**
475
        * Flag to determine if we can clear the selection or not.
476
        * @property _currentSelectionClear
477
        * @private
478
        */
479
        _currentSelectionClear: null,
480
        /**
481
        * Fires nodeChange event
482
        * @method _onFrameKeyDown
483
        * @private
484
        */
485
        _onFrameKeyDown: function(e) {
486
            var inst, sel;
487
            if (!this._currentSelection) {
488
                if (this._currentSelectionTimer) {
489
                    this._currentSelectionTimer.cancel();
490
                }
491
                this._currentSelectionTimer = Y.later(850, this, function() {
492
                    this._currentSelectionClear = true;
493
                });
494
 
495
                inst = this.frame.getInstance();
496
                sel = new inst.EditorSelection(e);
497
 
498
                this._currentSelection = sel;
499
            } else {
500
                sel = this._currentSelection;
501
            }
502
 
503
            inst = this.frame.getInstance();
504
            sel = new inst.EditorSelection();
505
 
506
            this._currentSelection = sel;
507
 
508
            if (sel && sel.anchorNode) {
509
                this.fire('nodeChange', { changedNode: sel.anchorNode, changedType: 'keydown', changedEvent: e.frameEvent });
510
                if (EditorBase.NC_KEYS[e.keyCode]) {
511
                    this.fire('nodeChange', {
512
                        changedNode: sel.anchorNode,
513
                        changedType: EditorBase.NC_KEYS[e.keyCode],
514
                        changedEvent: e.frameEvent
515
                    });
516
                    this.fire('nodeChange', {
517
                        changedNode: sel.anchorNode,
518
                        changedType: EditorBase.NC_KEYS[e.keyCode] + '-down',
519
                        changedEvent: e.frameEvent
520
                    });
521
                }
522
            }
523
        },
524
        /**
525
        * Fires nodeChange event
526
        * @method _onFrameKeyPress
527
        * @private
528
        */
529
        _onFrameKeyPress: function(e) {
530
            var sel = this._currentSelection;
531
 
532
            if (sel && sel.anchorNode) {
533
                this.fire('nodeChange', { changedNode: sel.anchorNode, changedType: 'keypress', changedEvent: e.frameEvent });
534
                if (EditorBase.NC_KEYS[e.keyCode]) {
535
                    this.fire('nodeChange', {
536
                        changedNode: sel.anchorNode,
537
                        changedType: EditorBase.NC_KEYS[e.keyCode] + '-press',
538
                        changedEvent: e.frameEvent
539
                    });
540
                }
541
            }
542
        },
543
        /**
544
        * Fires nodeChange event for keyup on specific keys
545
        * @method _onFrameKeyUp
546
        * @private
547
        */
548
        _onFrameKeyUp: function(e) {
549
            var inst = this.frame.getInstance(),
550
                sel = new inst.EditorSelection(e);
551
 
552
            if (sel && sel.anchorNode) {
553
                this.fire('nodeChange', { changedNode: sel.anchorNode, changedType: 'keyup', selection: sel, changedEvent: e.frameEvent  });
554
                if (EditorBase.NC_KEYS[e.keyCode]) {
555
                    this.fire('nodeChange', {
556
                        changedNode: sel.anchorNode,
557
                        changedType: EditorBase.NC_KEYS[e.keyCode] + '-up',
558
                        selection: sel,
559
                        changedEvent: e.frameEvent
560
                    });
561
                }
562
            }
563
            if (this._currentSelectionClear) {
564
                this._currentSelectionClear = this._currentSelection = null;
565
            }
566
        },
567
        /**
568
        * Validates linkedcss property
569
        *
570
        * @method _validateLinkedCSS
571
        * @private
572
        */
573
        _validateLinkedCSS: function(value) {
574
            return Lang.isString(value) || Lang.isArray(value);
575
        },
576
        /**
577
        * Pass through to the frame.execCommand method
578
        * @method execCommand
579
        * @param {String} cmd The command to pass: inserthtml, insertimage, bold
580
        * @param {String} val The optional value of the command: Helvetica
581
        * @return {Node/NodeList} The Node or Nodelist affected by the command. Only returns on override commands, not browser defined commands.
582
        */
583
        execCommand: function(cmd, val) {
584
            var ret = this.frame.execCommand(cmd, val),
585
                inst = this.frame.getInstance(),
586
                sel = new inst.EditorSelection(), cmds = {},
587
                e = { changedNode: sel.anchorNode, changedType: 'execcommand', nodes: ret };
588
 
589
            switch (cmd) {
590
                case 'forecolor':
591
                    e.fontColor = val;
592
                    break;
593
                case 'backcolor':
594
                    e.backgroundColor = val;
595
                    break;
596
                case 'fontsize':
597
                    e.fontSize = val;
598
                    break;
599
                case 'fontname':
600
                    e.fontFamily = val;
601
                    break;
602
            }
603
 
604
            cmds[cmd] = 1;
605
            e.commands = cmds;
606
 
607
            this.fire('nodeChange', e);
608
 
609
            return ret;
610
        },
611
        /**
612
        * Get the YUI instance of the frame
613
        * @method getInstance
614
        * @return {YUI} The YUI instance bound to the frame.
615
        */
616
        getInstance: function() {
617
            return this.frame.getInstance();
618
        },
619
        /**
620
        * Renders the Y.ContentEditable to the passed node.
621
        * @method render
622
        * @param {Selector/HTMLElement/Node} node The node to append the Editor to
623
        * @return {EditorBase}
624
        * @chainable
625
        */
626
        render: function(node) {
627
            var frame = this.frame;
628
 
629
            if (!frame) {
630
                this.plug(Y.Plugin.Frame, {
631
                    designMode: true,
632
                    title: EditorBase.STRINGS.title,
633
                    use: EditorBase.USE,
634
                    dir: this.get('dir'),
635
                    extracss: this.get('extracss'),
636
                    linkedcss: this.get('linkedcss'),
637
                    defaultblock: this.get('defaultblock')
638
                });
639
 
640
                frame = this.frame;
641
            }
642
 
643
            if (!frame.hasPlugin('exec')) {
644
                frame.plug(Y.Plugin.ExecCommand);
645
            }
646
 
647
            frame.after('ready', Y.bind(this._afterFrameReady, this));
648
 
649
            frame.addTarget(this);
650
 
651
            frame.set('content', this.get('content'));
652
 
653
            frame.render(node);
654
 
655
            return this;
656
        },
657
        /**
658
        * Focus the contentWindow of the iframe
659
        * @method focus
660
        * @param {Function} fn Callback function to execute after focus happens
661
        * @return {EditorBase}
662
        * @chainable
663
        */
664
        focus: function(fn) {
665
            this.frame.focus(fn);
666
            return this;
667
        },
668
        /**
669
        * Handles the showing of the Editor instance. Currently only handles the iframe
670
        * @method show
671
        * @return {EditorBase}
672
        * @chainable
673
        */
674
        show: function() {
675
            this.frame.show();
676
            return this;
677
        },
678
        /**
679
        * Handles the hiding of the Editor instance. Currently only handles the iframe
680
        * @method hide
681
        * @return {EditorBase}
682
        * @chainable
683
        */
684
        hide: function() {
685
            this.frame.hide();
686
            return this;
687
        },
688
        /**
689
        * (Un)Filters the content of the Editor, cleaning YUI related code. //TODO better filtering
690
        * @method getContent
691
        * @return {String} The filtered content of the Editor
692
        */
693
        getContent: function() {
694
            var html = '', inst = this.getInstance();
695
            if (inst && inst.EditorSelection) {
696
                html = inst.EditorSelection.unfilter();
697
            }
698
            //Removing the _yuid from the objects in IE
699
            html = html.replace(/ _yuid="([^>]*)"/g, '');
700
            return html;
701
        }
702
    }, {
703
        /**
704
        * @static
705
        * @method NORMALIZE_FONTSIZE
706
        * @description Pulls the fontSize from a node, then checks for string values (x-large, x-small)
707
        * and converts them to pixel sizes. If the parsed size is different from the original, it calls
708
        * node.setStyle to update the node with a pixel size for normalization.
709
        */
710
        NORMALIZE_FONTSIZE: function(n) {
711
            var size = n.getStyle('fontSize'), oSize = size;
712
 
713
            switch (size) {
714
                case '-webkit-xxx-large':
715
                    size = '48px';
716
                    break;
717
                case 'xx-large':
718
                    size = '32px';
719
                    break;
720
                case 'x-large':
721
                    size = '24px';
722
                    break;
723
                case 'large':
724
                    size = '18px';
725
                    break;
726
                case 'medium':
727
                    size = '16px';
728
                    break;
729
                case 'small':
730
                    size = '13px';
731
                    break;
732
                case 'x-small':
733
                    size = '10px';
734
                    break;
735
            }
736
            if (oSize !== size) {
737
                n.setStyle('fontSize', size);
738
            }
739
            return size;
740
        },
741
        /**
742
        * @static
743
        * @property TABKEY
744
        * @description The HTML markup to use for the tabkey
745
        */
746
        TABKEY: '<span class="tab">&nbsp;&nbsp;&nbsp;&nbsp;</span>',
747
        /**
748
        * @static
749
        * @method FILTER_RGB
750
        * @param String css The CSS string containing rgb(#,#,#);
751
        * @description Converts an RGB color string to a hex color, example: rgb(0, 255, 0) converts to #00ff00
752
        * @return String
753
        */
754
        FILTER_RGB: function(css) {
755
            if (css.toLowerCase().indexOf('rgb') !== -1) {
756
                var exp = new RegExp("(.*?)rgb\\s*?\\(\\s*?([0-9]+).*?,\\s*?([0-9]+).*?,\\s*?([0-9]+).*?\\)(.*?)", "gi"),
757
                    rgb = css.replace(exp, "$1,$2,$3,$4,$5").split(','),
758
                    r, g, b;
759
 
760
                if (rgb.length === 5) {
761
                    r = parseInt(rgb[1], 10).toString(16);
762
                    g = parseInt(rgb[2], 10).toString(16);
763
                    b = parseInt(rgb[3], 10).toString(16);
764
 
765
                    r = r.length === 1 ? '0' + r : r;
766
                    g = g.length === 1 ? '0' + g : g;
767
                    b = b.length === 1 ? '0' + b : b;
768
 
769
                    css = "#" + r + g + b;
770
                }
771
            }
772
            return css;
773
        },
774
        /**
775
        * @static
776
        * @property TAG2CMD
777
        * @description A hash table of tags to their execcomand's
778
        */
779
        TAG2CMD: {
780
            'b': 'bold',
781
            'strong': 'bold',
782
            'i': 'italic',
783
            'em': 'italic',
784
            'u': 'underline',
785
            'sup': 'superscript',
786
            'sub': 'subscript',
787
            'img': 'insertimage',
788
            'a' : 'createlink',
789
            'ul' : 'insertunorderedlist',
790
            'ol' : 'insertorderedlist'
791
        },
792
        /**
793
        * Hash table of keys to fire a nodeChange event for.
794
        * @static
795
        * @property NC_KEYS
796
        * @type Object
797
        */
798
        NC_KEYS: {
799
            8: 'backspace',
800
            9: 'tab',
801
            13: 'enter',
802
            32: 'space',
803
            33: 'pageup',
804
            34: 'pagedown',
805
            35: 'end',
806
            36: 'home',
807
            37: 'left',
808
            38: 'up',
809
            39: 'right',
810
            40: 'down',
811
            46: 'delete'
812
        },
813
        /**
814
        * The default modules to use inside the Frame
815
        * @static
816
        * @property USE
817
        * @type Array
818
        */
819
        USE: ['node', 'selector-css3', 'editor-selection', 'stylesheet'],
820
        /**
821
        * The Class Name: editorBase
822
        * @static
823
        * @property NAME
824
        */
825
        NAME: 'editorBase',
826
        /**
827
        * Editor Strings.  By default contains only the `title` property for the
828
        * Title of frame document (default "Rich Text Editor").
829
        *
830
        * @static
831
        * @property STRINGS
832
        */
833
        STRINGS: {
834
            title: 'Rich Text Editor'
835
        },
836
        ATTRS: {
837
            /**
838
            * The content to load into the Editor Frame
839
            * @attribute content
840
            */
841
            content: {
842
                validator: Lang.isString,
843
                value: '<br class="yui-cursor">',
844
                setter: function(str) {
845
                    if (str.substr(0, 1) === "\n") {
846
                        str = str.substr(1);
847
                    }
848
                    if (str === '') {
849
                        str = '<br class="yui-cursor">';
850
                    }
851
                    if (str === ' ') {
852
                        if (Y.UA.gecko) {
853
                            str = '<br class="yui-cursor">';
854
                        }
855
                    }
856
                    return this.frame.set('content', str);
857
                },
858
                getter: function() {
859
                    return this.frame.get('content');
860
                }
861
            },
862
            /**
863
            * The value of the dir attribute on the HTML element of the frame. Default: ltr
864
            * @attribute dir
865
            */
866
            dir: {
867
                validator: Lang.isString,
868
                writeOnce: true,
869
                value: 'ltr'
870
            },
871
            /**
872
            * @attribute linkedcss
873
            * @description An array of url's to external linked style sheets
874
            * @type String|Array
875
            */
876
            linkedcss: {
877
                validator: '_validateLinkedCSS',
878
                value: '',
879
                setter: function(css) {
880
                    if (this.frame) {
881
                        this.frame.set('linkedcss', css);
882
                    }
883
                    return css;
884
                }
885
            },
886
            /**
887
            * @attribute extracss
888
            * @description A string of CSS to add to the Head of the Editor
889
            * @type String
890
            */
891
            extracss: {
892
                validator: Lang.isString,
893
                value: '',
894
                setter: function(css) {
895
                    if (this.frame) {
896
                        this.frame.set('extracss', css);
897
                    }
898
                    return css;
899
                }
900
            },
901
            /**
902
            * @attribute defaultblock
903
            * @description The default tag to use for block level items, defaults to: p
904
            * @type String
905
            */
906
            defaultblock: {
907
                validator: Lang.isString,
908
                value: 'p'
909
            }
910
        }
911
    });
912
 
913
    Y.EditorBase = EditorBase;
914
 
915
    /**
916
    * @event nodeChange
917
    * @description Fired from several mouse/key/paste event points.
918
    * @param {EventFacade} event An Event Facade object with the following specific properties added:
919
    * <dl>
920
    *   <dt>changedEvent</dt><dd>The event that caused the nodeChange</dd>
921
    *   <dt>changedNode</dt><dd>The node that was interacted with</dd>
922
    *   <dt>changedType</dt><dd>The type of change: mousedown, mouseup, right, left, backspace, tab, enter, etc..</dd>
923
    *   <dt>commands</dt><dd>The list of execCommands that belong to this change and the dompath that's associated with the changedNode</dd>
924
    *   <dt>classNames</dt><dd>An array of classNames that are applied to the changedNode and all of it's parents</dd>
925
    *   <dt>dompath</dt><dd>A sorted array of node instances that make up the DOM path from the changedNode to body.</dd>
926
    *   <dt>backgroundColor</dt><dd>The cascaded backgroundColor of the changedNode</dd>
927
    *   <dt>fontColor</dt><dd>The cascaded fontColor of the changedNode</dd>
928
    *   <dt>fontFamily</dt><dd>The cascaded fontFamily of the changedNode</dd>
929
    *   <dt>fontSize</dt><dd>The cascaded fontSize of the changedNode</dd>
930
    * </dl>
931
    */
932
 
933
    /**
934
    * @event ready
935
    * @description Fired after the frame is ready.
936
    * @param {EventFacade} event An Event Facade object.
937
    */
938
 
939
 
940
 
941
 
942
 
943
}, '3.18.1', {"requires": ["base", "frame", "node", "exec-command", "editor-selection"]});