]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - jssource/src_files/include/javascript/yui3/build/editor/editor-bidi.js
Release 6.5.0
[Github/sugarcrm.git] / jssource / src_files / include / javascript / yui3 / build / editor / editor-bidi.js
1 /*
2 Copyright (c) 2010, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.com/yui/license.html
5 version: 3.3.0
6 build: 3167
7 */
8 YUI.add('editor-bidi', function(Y) {
9
10
11
12     /**
13      * Plugin for Editor to support BiDirectional (bidi) text operations.
14      * @module editor
15      * @submodule editor-bidi
16      */     
17     /**
18      * Plugin for Editor to support BiDirectional (bidi) text operations.
19      * @class Plugin.EditorBidi
20      * @extends Base
21      * @constructor
22      */
23
24
25     var EditorBidi = function() {
26         EditorBidi.superclass.constructor.apply(this, arguments);
27     }, HOST = 'host', DIR = 'dir', BODY = 'BODY', NODE_CHANGE = 'nodeChange',
28     B_C_CHANGE = 'bidiContextChange', FIRST_P = BODY + ' > p';
29
30     Y.extend(EditorBidi, Y.Base, {
31         /**
32         * Place holder for the last direction when checking for a switch
33         * @private
34         * @property lastDirection
35         */
36         lastDirection: null,
37         /**
38         * Tells us that an initial bidi check has already been performed
39         * @private
40         * @property firstEvent
41         */
42         firstEvent: null,
43
44         /**
45         * Method checks to see if the direction of the text has changed based on a nodeChange event.
46         * @private
47         * @method _checkForChange
48         */
49         _checkForChange: function() {
50             var host = this.get(HOST),
51                 inst = host.getInstance(),
52                 sel = new inst.Selection(),
53                 node, direction;
54             
55             if (sel.isCollapsed) {
56                 node = EditorBidi.blockParent(sel.focusNode);
57                 direction = node.getStyle('direction');
58                 if (direction !== this.lastDirection) {
59                     host.fire(B_C_CHANGE, { changedTo: direction });
60                     this.lastDirection = direction;
61                 }
62             } else {
63                 host.fire(B_C_CHANGE, { changedTo: 'select' });
64                 this.lastDirection = null;
65             }
66         },
67
68         /**
69         * Checked for a change after a specific nodeChange event has been fired.
70         * @private
71         * @method _afterNodeChange
72         */
73         _afterNodeChange: function(e) { 
74             // If this is the first event ever, or an event that can result in a context change
75             if (this.firstEvent || EditorBidi.EVENTS[e.changedType]) {
76                 this._checkForChange();
77                 this.firstEvent = false;
78             }
79         },
80
81         /**
82         * Checks for a direction change after a mouseup occurs.
83         * @private
84         * @method _afterMouseUp
85         */
86         _afterMouseUp: function(e) {
87             this._checkForChange();
88             this.firstEvent = false;
89         },
90         initializer: function() {
91             var host = this.get(HOST);
92
93             this.firstEvent = true;
94             
95             host.after(NODE_CHANGE, Y.bind(this._afterNodeChange, this));
96             host.after('dom:mouseup', Y.bind(this._afterMouseUp, this));
97         }
98     }, {
99         /**
100         * The events to check for a direction change on
101         * @property EVENTS
102         * @static
103         */
104         EVENTS: {
105             'backspace-up': true,
106             'pageup-up': true,
107             'pagedown-down': true,
108             'end-up': true,
109             'home-up': true,
110             'left-up': true,
111             'up-up': true,
112             'right-up': true,
113             'down-up': true,
114             'delete-up': true
115         },
116
117         /**
118         * More elements may be needed. BODY *must* be in the list to take care of the special case.
119         * 
120         * blockParent could be changed to use inst.Selection.BLOCKS
121         * instead, but that would make Y.Plugin.EditorBidi.blockParent
122         * unusable in non-RTE contexts (it being usable is a nice
123         * side-effect).
124         * @property BLOCKS
125         * @static
126         */
127         BLOCKS: Y.Selection.BLOCKS+',LI,HR,' + BODY,
128         /**
129         * Template for creating a block element
130         * @static
131         * @property DIV_WRAPPER
132         */
133         DIV_WRAPPER: '<DIV></DIV>',
134         /**
135         * Returns a block parent for a given element
136         * @static
137         * @method blockParent
138         */
139         blockParent: function(node, wrap) {
140             var parent = node, divNode, firstChild;
141             
142             if (!parent) {
143                 parent = Y.one(BODY);
144             }
145             
146             if (!parent.test(EditorBidi.BLOCKS)) {
147                 parent = parent.ancestor(EditorBidi.BLOCKS);
148             }
149             if (wrap && parent.test(BODY)) {
150                 // This shouldn't happen if the RTE handles everything
151                 // according to spec: we should get to a P before BODY. But
152                 // we don't want to set the direction of BODY even if that
153                 // happens, so we wrap everything in a DIV.
154                 
155                 // The code is based on YUI3's Y.Selection._wrapBlock function.
156                 divNode = Y.Node.create(EditorBidi.DIV_WRAPPER);
157                 parent.get('children').each(function(node, index) {
158                     if (index === 0) {
159                         firstChild = node;
160                     } else {
161                         divNode.append(node);
162                     }
163                 });
164                 firstChild.replace(divNode);
165                 divNode.prepend(firstChild);
166                 parent = divNode;
167             }
168             return parent;
169         },
170         /**
171         * The data key to store on the node.
172         * @static
173         * @property _NODE_SELECTED
174         */
175         _NODE_SELECTED: 'bidiSelected',
176         /**
177         * Generates a list of all the block parents of the current NodeList
178         * @static
179         * @method addParents
180         */
181         addParents: function(nodeArray) {
182             var i, parent, addParent;
183
184             for (i = 0; i < nodeArray.length; i += 1) {
185                 nodeArray[i].setData(EditorBidi._NODE_SELECTED, true);
186             }
187
188             // This works automagically, since new parents added get processed
189             // later themselves. So if there's a node early in the process that
190             // we haven't discovered some of its siblings yet, thus resulting in
191             // its parent not added, the parent will be added later, since those
192             // siblings will be added to the array and then get processed.
193             for (i = 0; i < nodeArray.length; i += 1) {
194                 parent = nodeArray[i].get('parentNode');
195
196                 // Don't add the parent if the parent is the BODY element.
197                 // We don't want to change the direction of BODY. Also don't
198                 // do it if the parent is already in the list.
199                 if (!parent.test(BODY) && !parent.getData(EditorBidi._NODE_SELECTED)) {
200                     addParent = true;
201                     parent.get('children').some(function(sibling) {
202                         if (!sibling.getData(EditorBidi._NODE_SELECTED)) {
203                             addParent = false;
204                             return true; // stop more processing
205                         }
206                     });
207                     if (addParent) {
208                         nodeArray.push(parent);
209                         parent.setData(EditorBidi._NODE_SELECTED, true);
210                     }
211                 }
212             }   
213
214             for (i = 0; i < nodeArray.length; i += 1) {
215                 nodeArray[i].clearData(EditorBidi._NODE_SELECTED);
216             }
217
218             return nodeArray;
219         },
220
221
222         /**
223         * editorBidi
224         * @static
225         * @property NAME
226         */
227         NAME: 'editorBidi',
228         /**
229         * editorBidi
230         * @static
231         * @property NS
232         */
233         NS: 'editorBidi',
234         ATTRS: {
235             host: {
236                 value: false
237             }
238         }
239     });
240     
241     Y.namespace('Plugin');
242     
243     Y.Plugin.EditorBidi = EditorBidi;
244
245     /**
246      * bidi execCommand override for setting the text direction of a node.
247      * @for Plugin.ExecCommand
248      * @property COMMANDS.bidi
249      */
250
251     Y.Plugin.ExecCommand.COMMANDS.bidi = function(cmd, direction) {
252         var inst = this.getInstance(),
253             sel = new inst.Selection(),
254             returnValue, block,
255             selected, selectedBlocks, dir;
256
257         inst.Selection.filterBlocks();
258         if (sel.isCollapsed) { // No selection
259             block = EditorBidi.blockParent(sel.anchorNode);
260             if (!direction) {
261                 //If no direction is set, auto-detect the proper setting to make it "toggle"
262                 dir = block.getAttribute(DIR);
263                 if (!dir || dir == 'ltr') {
264                     direction = 'rtl';
265                 } else {
266                     direction = 'ltr';
267                 }
268             }
269             block.setAttribute(DIR, direction);
270             returnValue = block;
271         } else { // some text is selected
272             selected = sel.getSelected();
273             selectedBlocks = [];
274             selected.each(function(node) {
275                 /*
276                 * Temporarily removed this check, should already be fixed
277                 * in Y.Selection.getSelected()
278                 */
279                 //if (!node.test(BODY)) { // workaround for a YUI bug
280                    selectedBlocks.push(EditorBidi.blockParent(node));
281                 //}
282             });
283             selectedBlocks = inst.all(EditorBidi.addParents(selectedBlocks));
284             selectedBlocks.setAttribute(DIR, direction);
285             returnValue = selectedBlocks;
286         }
287
288         this.get(HOST).get(HOST).editorBidi._checkForChange();
289         return returnValue;
290     };
291
292
293
294
295 }, '3.3.0' ,{requires:['editor-base'], skinnable:false});