[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 /* 2 YUI 3.17.2 (build 9c3c78e) 3 Copyright 2014 Yahoo! Inc. All rights reserved. 4 Licensed under the BSD License. 5 http://yuilibrary.com/license/ 6 */ 7 8 YUI.add('editor-bidi', function (Y, NAME) { 9 10 11 /** 12 * Plugin for Editor to support BiDirectional (bidi) text operations. 13 * @class Plugin.EditorBidi 14 * @extends Base 15 * @constructor 16 * @module editor 17 * @submodule editor-bidi 18 */ 19 20 21 var EditorBidi = function() { 22 EditorBidi.superclass.constructor.apply(this, arguments); 23 }, HOST = 'host', DIR = 'dir', NODE_CHANGE = 'nodeChange', 24 B_C_CHANGE = 'bidiContextChange', STYLE = 'style'; 25 26 Y.extend(EditorBidi, Y.Base, { 27 /** 28 * Place holder for the last direction when checking for a switch 29 * @private 30 * @property lastDirection 31 */ 32 lastDirection: null, 33 /** 34 * Tells us that an initial bidi check has already been performed 35 * @private 36 * @property firstEvent 37 */ 38 firstEvent: null, 39 40 /** 41 * Method checks to see if the direction of the text has changed based on a nodeChange event. 42 * @private 43 * @method _checkForChange 44 */ 45 _checkForChange: function() { 46 var host = this.get(HOST), 47 inst = host.getInstance(), 48 sel = new inst.EditorSelection(), 49 node, direction; 50 51 if (sel.isCollapsed) { 52 node = EditorBidi.blockParent(sel.focusNode, false, inst.EditorSelection.ROOT); 53 if (node) { 54 direction = node.getStyle('direction'); 55 if (direction !== this.lastDirection) { 56 host.fire(B_C_CHANGE, { changedTo: direction }); 57 this.lastDirection = direction; 58 } 59 } 60 } else { 61 host.fire(B_C_CHANGE, { changedTo: 'select' }); 62 this.lastDirection = null; 63 } 64 }, 65 66 /** 67 * Checked for a change after a specific nodeChange event has been fired. 68 * @private 69 * @method _afterNodeChange 70 */ 71 _afterNodeChange: function(e) { 72 // If this is the first event ever, or an event that can result in a context change 73 if (this.firstEvent || EditorBidi.EVENTS[e.changedType]) { 74 this._checkForChange(); 75 this.firstEvent = false; 76 } 77 }, 78 79 /** 80 * Checks for a direction change after a mouseup occurs. 81 * @private 82 * @method _afterMouseUp 83 */ 84 _afterMouseUp: function() { 85 this._checkForChange(); 86 this.firstEvent = false; 87 }, 88 initializer: function() { 89 var host = this.get(HOST); 90 91 this.firstEvent = true; 92 93 host.after(NODE_CHANGE, Y.bind(this._afterNodeChange, this)); 94 host.after('dom:mouseup', Y.bind(this._afterMouseUp, this)); 95 } 96 }, { 97 /** 98 * The events to check for a direction change on 99 * @property EVENTS 100 * @static 101 */ 102 EVENTS: { 103 'backspace-up': true, 104 'pageup-up': true, 105 'pagedown-down': true, 106 'end-up': true, 107 'home-up': true, 108 'left-up': true, 109 'up-up': true, 110 'right-up': true, 111 'down-up': true, 112 'delete-up': true 113 }, 114 115 /** 116 * More elements may be needed. BODY *must* be in the list to take care of the special case. 117 * 118 * blockParent could be changed to use inst.EditorSelection.BLOCKS 119 * instead, but that would make Y.Plugin.EditorBidi.blockParent 120 * unusable in non-RTE contexts (it being usable is a nice 121 * side-effect). 122 * @property BLOCKS 123 * @static 124 */ 125 //BLOCKS: Y.EditorSelection.BLOCKS+',LI,HR,' + BODY, 126 BLOCKS: Y.EditorSelection.BLOCKS, 127 /** 128 * Template for creating a block element 129 * @static 130 * @property DIV_WRAPPER 131 */ 132 DIV_WRAPPER: '<DIV></DIV>', 133 /** 134 * Returns a block parent for a given element 135 * @static 136 * @method blockParent 137 */ 138 blockParent: function(node, wrap, root) { 139 var parent = node, divNode, firstChild; 140 141 root = root || Y.EditorSelection.ROOT; 142 143 if (!parent) { 144 parent = root; 145 } 146 147 if (!parent.test(EditorBidi.BLOCKS)) { 148 parent = parent.ancestor(EditorBidi.BLOCKS); 149 } 150 if (wrap && parent.compareTo(root)) { 151 // This shouldn't happen if the RTE handles everything 152 // according to spec: we should get to a P before ROOT. But 153 // we don't want to set the direction of ROOT even if that 154 // happens, so we wrap everything in a DIV. 155 156 // The code is based on YUI3's Y.EditorSelection._wrapBlock function. 157 divNode = Y.Node.create(EditorBidi.DIV_WRAPPER); 158 parent.get('children').each(function(node, index) { 159 if (index === 0) { 160 firstChild = node; 161 } else { 162 divNode.append(node); 163 } 164 }); 165 firstChild.replace(divNode); 166 divNode.prepend(firstChild); 167 parent = divNode; 168 } 169 return parent; 170 }, 171 /** 172 * The data key to store on the node. 173 * @static 174 * @property _NODE_SELECTED 175 */ 176 _NODE_SELECTED: 'bidiSelected', 177 /** 178 * Generates a list of all the block parents of the current NodeList 179 * @static 180 * @method addParents 181 */ 182 addParents: function(nodeArray, root) { 183 var i, parent, addParent; 184 tester = function(sibling) { 185 if (!sibling.getData(EditorBidi._NODE_SELECTED)) { 186 addParent = false; 187 return true; // stop more processing 188 } 189 }; 190 191 root = root || Y.EditorSelection.ROOT; 192 193 for (i = 0; i < nodeArray.length; i += 1) { 194 nodeArray[i].setData(EditorBidi._NODE_SELECTED, true); 195 } 196 197 // This works automagically, since new parents added get processed 198 // later themselves. So if there's a node early in the process that 199 // we haven't discovered some of its siblings yet, thus resulting in 200 // its parent not added, the parent will be added later, since those 201 // siblings will be added to the array and then get processed. 202 for (i = 0; i < nodeArray.length; i += 1) { 203 parent = nodeArray[i].get('parentNode'); 204 205 // Don't add the parent if the parent is the ROOT element. 206 // We don't want to change the direction of ROOT. Also don't 207 // do it if the parent is already in the list. 208 if (!root.compareTo(parent) && !parent.getData(EditorBidi._NODE_SELECTED)) { 209 addParent = true; 210 parent.get('children').some(tester); 211 if (addParent) { 212 nodeArray.push(parent); 213 parent.setData(EditorBidi._NODE_SELECTED, true); 214 } 215 } 216 } 217 218 for (i = 0; i < nodeArray.length; i += 1) { 219 nodeArray[i].clearData(EditorBidi._NODE_SELECTED); 220 } 221 222 return nodeArray; 223 }, 224 225 226 /** 227 * editorBidi 228 * @static 229 * @property NAME 230 */ 231 NAME: 'editorBidi', 232 /** 233 * editorBidi 234 * @static 235 * @property NS 236 */ 237 NS: 'editorBidi', 238 ATTRS: { 239 host: { 240 value: false 241 } 242 }, 243 /** 244 * Regex for testing/removing text-align style from an element 245 * @static 246 * @property RE_TEXT_ALIGN 247 */ 248 RE_TEXT_ALIGN: /text-align:\s*\w*\s*;/, 249 /** 250 * Method to test a node's style attribute for text-align and removing it. 251 * @static 252 * @method removeTextAlign 253 */ 254 removeTextAlign: function(n) { 255 if (n) { 256 if (n.getAttribute(STYLE).match(EditorBidi.RE_TEXT_ALIGN)) { 257 n.setAttribute(STYLE, n.getAttribute(STYLE).replace(EditorBidi.RE_TEXT_ALIGN, '')); 258 } 259 if (n.hasAttribute('align')) { 260 n.removeAttribute('align'); 261 } 262 } 263 return n; 264 } 265 }); 266 267 Y.namespace('Plugin'); 268 269 Y.Plugin.EditorBidi = EditorBidi; 270 271 /** 272 * bidi execCommand override for setting the text direction of a node. 273 * This property is added to the `Y.Plugin.ExecCommands.COMMANDS` 274 * collection. 275 * 276 * @for Plugin.ExecCommand 277 * @property bidi 278 */ 279 //TODO -- This should not add this command unless the plugin is added to the instance.. 280 Y.Plugin.ExecCommand.COMMANDS.bidi = function(cmd, direction) { 281 var inst = this.getInstance(), 282 sel = new inst.EditorSelection(), 283 ns = this.get(HOST).get(HOST).editorBidi, 284 returnValue, block, b, 285 root = inst.EditorSelection.ROOT, 286 selected, selectedBlocks, dir; 287 288 if (!ns) { 289 Y.error('bidi execCommand is not available without the EditorBiDi plugin.'); 290 return; 291 } 292 293 inst.EditorSelection.filterBlocks(); 294 295 if (sel.isCollapsed) { // No selection 296 block = EditorBidi.blockParent(sel.anchorNode, false, root); 297 if (!block) { 298 block = root.one(inst.EditorSelection.BLOCKS); 299 } 300 //Remove text-align attribute if it exists 301 block = EditorBidi.removeTextAlign(block); 302 if (!direction) { 303 //If no direction is set, auto-detect the proper setting to make it "toggle" 304 dir = block.getAttribute(DIR); 305 if (!dir || dir === 'ltr') { 306 direction = 'rtl'; 307 } else { 308 direction = 'ltr'; 309 } 310 } 311 block.setAttribute(DIR, direction); 312 if (Y.UA.ie) { 313 b = block.all('br.yui-cursor'); 314 if (b.size() === 1 && block.get('childNodes').size() === 1) { 315 b.remove(); 316 } 317 } 318 returnValue = block; 319 } else { // some text is selected 320 selected = sel.getSelected(); 321 selectedBlocks = []; 322 selected.each(function(node) { 323 selectedBlocks.push(EditorBidi.blockParent(node, false, root)); 324 }); 325 selectedBlocks = inst.all(EditorBidi.addParents(selectedBlocks, root)); 326 selectedBlocks.each(function(n) { 327 var d = direction; 328 //Remove text-align attribute if it exists 329 n = EditorBidi.removeTextAlign(n); 330 if (!d) { 331 dir = n.getAttribute(DIR); 332 if (!dir || dir === 'ltr') { 333 d = 'rtl'; 334 } else { 335 d = 'ltr'; 336 } 337 } 338 n.setAttribute(DIR, d); 339 }); 340 returnValue = selectedBlocks; 341 } 342 ns._checkForChange(); 343 return returnValue; 344 }; 345 346 }, '3.17.2', {"requires": ["editor-base"]});
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |