[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/yuilib/3.17.2/editor-bidi/ -> editor-bidi.js (source)

   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"]});


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1