[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/editor/atto/yui/src/rangy/js/ -> rangy-selectionsaverestore.js (source)

   1  /**

   2   * Selection save and restore module for Rangy.

   3   * Saves and restores user selections using marker invisible elements in the DOM.

   4   *

   5   * Part of Rangy, a cross-browser JavaScript range and selection library

   6   * https://github.com/timdown/rangy

   7   *

   8   * Depends on Rangy core.

   9   *

  10   * Copyright 2015, Tim Down

  11   * Licensed under the MIT license.

  12   * Version: 1.3.0

  13   * Build date: 10 May 2015

  14   */
  15  (function(factory, root) {
  16      // No AMD or CommonJS support so we use the rangy property of root (probably the global variable)
  17      factory(root.rangy);
  18  })(function(rangy) {
  19      rangy.createModule("SaveRestore", ["WrappedRange"], function(api, module) {
  20          var dom = api.dom;
  21          var removeNode = dom.removeNode;
  22          var isDirectionBackward = api.Selection.isDirectionBackward;
  23          var markerTextChar = "\ufeff";
  24  
  25          function gEBI(id, doc) {
  26              return (doc || document).getElementById(id);
  27          }
  28  
  29          function insertRangeBoundaryMarker(range, atStart) {
  30              var markerId = "selectionBoundary_" + (+new Date()) + "_" + ("" + Math.random()).slice(2);
  31              var markerEl;
  32              var doc = dom.getDocument(range.startContainer);
  33  
  34              // Clone the Range and collapse to the appropriate boundary point
  35              var boundaryRange = range.cloneRange();
  36              boundaryRange.collapse(atStart);
  37  
  38              // Create the marker element containing a single invisible character using DOM methods and insert it
  39              markerEl = doc.createElement("span");
  40              markerEl.id = markerId;
  41              markerEl.style.lineHeight = "0";
  42              markerEl.style.display = "none";
  43              markerEl.className = "rangySelectionBoundary";
  44              markerEl.appendChild(doc.createTextNode(markerTextChar));
  45  
  46              boundaryRange.insertNode(markerEl);
  47              return markerEl;
  48          }
  49  
  50          function setRangeBoundary(doc, range, markerId, atStart) {
  51              var markerEl = gEBI(markerId, doc);
  52              if (markerEl) {
  53                  range[atStart ? "setStartBefore" : "setEndBefore"](markerEl);
  54                  removeNode(markerEl);
  55              } else {
  56                  module.warn("Marker element has been removed. Cannot restore selection.");
  57              }
  58          }
  59  
  60          function compareRanges(r1, r2) {
  61              return r2.compareBoundaryPoints(r1.START_TO_START, r1);
  62          }
  63  
  64          function saveRange(range, direction) {
  65              var startEl, endEl, doc = api.DomRange.getRangeDocument(range), text = range.toString();
  66              var backward = isDirectionBackward(direction);
  67  
  68              if (range.collapsed) {
  69                  endEl = insertRangeBoundaryMarker(range, false);
  70                  return {
  71                      document: doc,
  72                      markerId: endEl.id,
  73                      collapsed: true
  74                  };
  75              } else {
  76                  endEl = insertRangeBoundaryMarker(range, false);
  77                  startEl = insertRangeBoundaryMarker(range, true);
  78  
  79                  return {
  80                      document: doc,
  81                      startMarkerId: startEl.id,
  82                      endMarkerId: endEl.id,
  83                      collapsed: false,
  84                      backward: backward,
  85                      toString: function() {
  86                          return "original text: '" + text + "', new text: '" + range.toString() + "'";
  87                      }
  88                  };
  89              }
  90          }
  91  
  92          function restoreRange(rangeInfo, normalize) {
  93              var doc = rangeInfo.document;
  94              if (typeof normalize == "undefined") {
  95                  normalize = true;
  96              }
  97              var range = api.createRange(doc);
  98              if (rangeInfo.collapsed) {
  99                  var markerEl = gEBI(rangeInfo.markerId, doc);
 100                  if (markerEl) {
 101                      markerEl.style.display = "inline";
 102                      var previousNode = markerEl.previousSibling;
 103  
 104                      // Workaround for issue 17
 105                      if (previousNode && previousNode.nodeType == 3) {
 106                          removeNode(markerEl);
 107                          range.collapseToPoint(previousNode, previousNode.length);
 108                      } else {
 109                          range.collapseBefore(markerEl);
 110                          removeNode(markerEl);
 111                      }
 112                  } else {
 113                      module.warn("Marker element has been removed. Cannot restore selection.");
 114                  }
 115              } else {
 116                  setRangeBoundary(doc, range, rangeInfo.startMarkerId, true);
 117                  setRangeBoundary(doc, range, rangeInfo.endMarkerId, false);
 118              }
 119  
 120              if (normalize) {
 121                  range.normalizeBoundaries();
 122              }
 123  
 124              return range;
 125          }
 126  
 127          function saveRanges(ranges, direction) {
 128              var rangeInfos = [], range, doc;
 129              var backward = isDirectionBackward(direction);
 130  
 131              // Order the ranges by position within the DOM, latest first, cloning the array to leave the original untouched
 132              ranges = ranges.slice(0);
 133              ranges.sort(compareRanges);
 134  
 135              for (var i = 0, len = ranges.length; i < len; ++i) {
 136                  rangeInfos[i] = saveRange(ranges[i], backward);
 137              }
 138  
 139              // Now that all the markers are in place and DOM manipulation over, adjust each range's boundaries to lie
 140              // between its markers
 141              for (i = len - 1; i >= 0; --i) {
 142                  range = ranges[i];
 143                  doc = api.DomRange.getRangeDocument(range);
 144                  if (range.collapsed) {
 145                      range.collapseAfter(gEBI(rangeInfos[i].markerId, doc));
 146                  } else {
 147                      range.setEndBefore(gEBI(rangeInfos[i].endMarkerId, doc));
 148                      range.setStartAfter(gEBI(rangeInfos[i].startMarkerId, doc));
 149                  }
 150              }
 151  
 152              return rangeInfos;
 153          }
 154  
 155          function saveSelection(win) {
 156              if (!api.isSelectionValid(win)) {
 157                  module.warn("Cannot save selection. This usually happens when the selection is collapsed and the selection document has lost focus.");
 158                  return null;
 159              }
 160              var sel = api.getSelection(win);
 161              var ranges = sel.getAllRanges();
 162              var backward = (ranges.length == 1 && sel.isBackward());
 163  
 164              var rangeInfos = saveRanges(ranges, backward);
 165  
 166              // Ensure current selection is unaffected
 167              if (backward) {
 168                  sel.setSingleRange(ranges[0], backward);
 169              } else {
 170                  sel.setRanges(ranges);
 171              }
 172  
 173              return {
 174                  win: win,
 175                  rangeInfos: rangeInfos,
 176                  restored: false
 177              };
 178          }
 179  
 180          function restoreRanges(rangeInfos) {
 181              var ranges = [];
 182  
 183              // Ranges are in reverse order of appearance in the DOM. We want to restore earliest first to avoid
 184              // normalization affecting previously restored ranges.
 185              var rangeCount = rangeInfos.length;
 186  
 187              for (var i = rangeCount - 1; i >= 0; i--) {
 188                  ranges[i] = restoreRange(rangeInfos[i], true);
 189              }
 190  
 191              return ranges;
 192          }
 193  
 194          function restoreSelection(savedSelection, preserveDirection) {
 195              if (!savedSelection.restored) {
 196                  var rangeInfos = savedSelection.rangeInfos;
 197                  var sel = api.getSelection(savedSelection.win);
 198                  var ranges = restoreRanges(rangeInfos), rangeCount = rangeInfos.length;
 199  
 200                  if (rangeCount == 1 && preserveDirection && api.features.selectionHasExtend && rangeInfos[0].backward) {
 201                      sel.removeAllRanges();
 202                      sel.addRange(ranges[0], true);
 203                  } else {
 204                      sel.setRanges(ranges);
 205                  }
 206  
 207                  savedSelection.restored = true;
 208              }
 209          }
 210  
 211          function removeMarkerElement(doc, markerId) {
 212              var markerEl = gEBI(markerId, doc);
 213              if (markerEl) {
 214                  removeNode(markerEl);
 215              }
 216          }
 217  
 218          function removeMarkers(savedSelection) {
 219              var rangeInfos = savedSelection.rangeInfos;
 220              for (var i = 0, len = rangeInfos.length, rangeInfo; i < len; ++i) {
 221                  rangeInfo = rangeInfos[i];
 222                  if (rangeInfo.collapsed) {
 223                      removeMarkerElement(savedSelection.doc, rangeInfo.markerId);
 224                  } else {
 225                      removeMarkerElement(savedSelection.doc, rangeInfo.startMarkerId);
 226                      removeMarkerElement(savedSelection.doc, rangeInfo.endMarkerId);
 227                  }
 228              }
 229          }
 230  
 231          api.util.extend(api, {
 232              saveRange: saveRange,
 233              restoreRange: restoreRange,
 234              saveRanges: saveRanges,
 235              restoreRanges: restoreRanges,
 236              saveSelection: saveSelection,
 237              restoreSelection: restoreSelection,
 238              removeMarkerElement: removeMarkerElement,
 239              removeMarkers: removeMarkers
 240          });
 241      });
 242      
 243      return rangy;
 244  }, this);


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