~andreserl/maas/packaging_precise_rebase

« back to all changes in this revision

Viewing changes to debian/extras/jslibs/yui/editor-base/editor-base-debug.js

  • Committer: Andres Rodriguez
  • Date: 2013-03-20 18:12:30 UTC
  • mfrom: (145.2.22 precise.sru)
  • Revision ID: andreserl@ubuntu.com-20130320181230-6l5guc0nhlv2z4p7
Re-base againts latest quantal released branch towards SRU

Show diffs side-by-side

added added

removed removed

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