[ Index ]

PHP Cross Reference of phpBB-3.3.14-deutsch

title

Body

[close]

/vendor/s9e/text-formatter/src/ -> render.js (source)

   1  var MSXML = (typeof DOMParser === 'undefined' || typeof XSLTProcessor === 'undefined');
   2  var xslt = {
   3      /**
   4      * @param {string} xsl
   5      */
   6      init: function(xsl)
   7      {
   8          var stylesheet = xslt.loadXML(xsl);
   9          if (MSXML)
  10          {
  11              var generator = new ActiveXObject('MSXML2.XSLTemplate.6.0');
  12              generator['stylesheet'] = stylesheet;
  13              xslt.proc = generator['createProcessor']();
  14          }
  15          else
  16          {
  17              xslt.proc = new XSLTProcessor;
  18              xslt.proc['importStylesheet'](stylesheet);
  19          }
  20      },
  21  
  22      /**
  23      * @param  {string} xml
  24      * @return {!Document}
  25      */
  26      loadXML: function(xml)
  27      {
  28          var dom;
  29          if (MSXML)
  30          {
  31              dom = new ActiveXObject('MSXML2.FreeThreadedDOMDocument.6.0');
  32              dom['async'] = false;
  33              dom['validateOnParse'] = false;
  34              dom['loadXML'](xml);
  35          }
  36          else
  37          {
  38              dom = (new DOMParser).parseFromString(xml, 'text/xml');
  39          }
  40  
  41          if (!dom)
  42          {
  43              throw 'Cannot parse ' + xml;
  44          }
  45  
  46          return dom;
  47      },
  48  
  49      /**
  50      * @param {string} paramName  Parameter name
  51      * @param {string} paramValue Parameter's value
  52      */
  53      setParameter: function(paramName, paramValue)
  54      {
  55          if (MSXML)
  56          {
  57              xslt.proc['addParameter'](paramName, paramValue, '');
  58          }
  59          else
  60          {
  61              xslt.proc['setParameter'](null, paramName, paramValue);
  62          }
  63      },
  64  
  65      /**
  66      * @param  {string}    xml
  67      * @param  {!Document} targetDoc
  68      * @return {!DocumentFragment}
  69      */
  70      transformToFragment: function(xml, targetDoc)
  71      {
  72          if (MSXML)
  73          {
  74              var div = targetDoc.createElement('div'),
  75                  fragment = targetDoc.createDocumentFragment();
  76  
  77              xslt.proc['input'] = xslt.loadXML(xml);
  78              xslt.proc['transform']();
  79              div.innerHTML = xslt.proc['output'];
  80              while (div.firstChild)
  81              {
  82                  fragment.appendChild(div.firstChild);
  83              }
  84  
  85              return fragment;
  86          }
  87  
  88          return xslt.proc['transformToFragment'](xslt.loadXML(xml), targetDoc);
  89      }
  90  };
  91  xslt.init(xsl);
  92  
  93  /**
  94  * Parse a given text and render it into given HTML element
  95  *
  96  * @param  {string} text
  97  * @param  {!HTMLElement} target
  98  * @return {!Node}
  99  */
 100  function preview(text, target)
 101  {
 102      var targetDoc = target.ownerDocument;
 103      if (!targetDoc)
 104      {
 105          throw 'Target does not have a ownerDocument';
 106      }
 107  
 108      var resultFragment = xslt.transformToFragment(parse(text).replace(/<[eis]>[^<]*<\/[eis]>/g, ''), targetDoc),
 109          lastUpdated    = target;
 110  
 111      // https://bugs.chromium.org/p/chromium/issues/detail?id=266305
 112      if (typeof window !== 'undefined' && 'chrome' in window)
 113      {
 114          resultFragment.querySelectorAll('script').forEach(
 115              function (oldScript)
 116              {
 117                  let newScript = document.createElement('script');
 118                  for (let attribute of oldScript['attributes'])
 119                  {
 120                      newScript['setAttribute'](attribute.name, attribute.value);
 121                  }
 122                  newScript.textContent = oldScript.textContent;
 123  
 124                  oldScript.parentNode.replaceChild(newScript, oldScript);
 125              }
 126          );
 127      }
 128  
 129      // Compute and refresh hashes
 130      if (HINT.hash)
 131      {
 132          computeHashes(resultFragment);
 133      }
 134  
 135      // Apply post-processing
 136      if (HINT.onRender)
 137      {
 138          executeEvents(resultFragment, 'render');
 139      }
 140  
 141      /**
 142      * Compute and set all hashes in given document fragment
 143      *
 144      * @param {!DocumentFragment} fragment
 145      */
 146  	function computeHashes(fragment)
 147      {
 148          var nodes = fragment.querySelectorAll('[data-s9e-livepreview-hash]'),
 149              i     = nodes.length;
 150          while (--i >= 0)
 151          {
 152              nodes[i]['setAttribute']('data-s9e-livepreview-hash', hash(nodes[i].outerHTML));
 153          }
 154      }
 155  
 156      /**
 157      * Execute an event's code on a given node
 158      *
 159      * @param {!Element} node
 160      * @param {string}   eventName
 161      */
 162  	function executeEvent(node, eventName)
 163      {
 164          /** @type {string} */
 165          var code = node.getAttribute('data-s9e-livepreview-on' + eventName);
 166          if (!functionCache[code])
 167          {
 168              functionCache[code] = new Function(code);
 169          }
 170  
 171          functionCache[code]['call'](node);
 172      }
 173  
 174      /**
 175      * Locate and execute an event on given document fragment or element
 176      *
 177      * @param {!DocumentFragment|!Element} root
 178      * @param {string}                     eventName
 179      */
 180  	function executeEvents(root, eventName)
 181      {
 182          // Execute the event on the root node, as there is no self-or-descendant selector in CSS
 183          if (root instanceof Element && root['hasAttribute']('data-s9e-livepreview-on' + eventName))
 184          {
 185              executeEvent(root, eventName);
 186          }
 187  
 188          var nodes = root.querySelectorAll('[data-s9e-livepreview-on' + eventName + ']'),
 189              i     = nodes.length;
 190          while (--i >= 0)
 191          {
 192              executeEvent(nodes[i], eventName);
 193          }
 194      }
 195  
 196      /**
 197      * Update the content of given node oldParent to match node newParent
 198      *
 199      * @param {!Node} oldParent
 200      * @param {!Node} newParent
 201      */
 202  	function refreshElementContent(oldParent, newParent)
 203      {
 204          var oldNodes = oldParent.childNodes,
 205              newNodes = newParent.childNodes,
 206              oldCnt   = oldNodes.length,
 207              newCnt   = newNodes.length,
 208              oldNode,
 209              newNode,
 210              left     = 0,
 211              right    = 0;
 212  
 213          // Skip the leftmost matching nodes
 214          while (left < oldCnt && left < newCnt)
 215          {
 216              oldNode = oldNodes[left];
 217              newNode = newNodes[left];
 218              if (!refreshNode(oldNode, newNode))
 219              {
 220                  break;
 221              }
 222  
 223              ++left;
 224          }
 225  
 226          // Skip the rightmost matching nodes
 227          var maxRight = Math.min(oldCnt - left, newCnt - left);
 228          while (right < maxRight)
 229          {
 230              oldNode = oldNodes[oldCnt - (right + 1)];
 231              newNode = newNodes[newCnt - (right + 1)];
 232              if (!refreshNode(oldNode, newNode))
 233              {
 234                  break;
 235              }
 236  
 237              ++right;
 238          }
 239  
 240          // Remove the old dirty nodes in the middle of the tree
 241          var i = oldCnt - right;
 242          while (--i >= left)
 243          {
 244              oldParent.removeChild(oldNodes[i]);
 245              lastUpdated = oldParent;
 246          }
 247  
 248          // Test whether there are any nodes in the new tree between the matching nodes at the left
 249          // and the matching nodes at the right
 250          var rightBoundary = newCnt - right;
 251          if (left >= rightBoundary)
 252          {
 253              return;
 254          }
 255  
 256          // Clone the new nodes
 257          var newNodesFragment = targetDoc.createDocumentFragment();
 258          i = left;
 259          do
 260          {
 261              newNode = newNodes[i];
 262              if (HINT.onUpdate && newNode instanceof Element)
 263              {
 264                  executeEvents(newNode, 'update');
 265              }
 266              lastUpdated = newNodesFragment.appendChild(newNode);
 267          }
 268          while (i < --rightBoundary);
 269  
 270          // If we haven't skipped any nodes to the right, we can just append the fragment
 271          if (!right)
 272          {
 273              oldParent.appendChild(newNodesFragment);
 274          }
 275          else
 276          {
 277              oldParent.insertBefore(newNodesFragment, oldParent.childNodes[left]);
 278          }
 279      }
 280  
 281      /**
 282      * Update given node oldNode to make it match newNode
 283      *
 284      * @param {!Node} oldNode
 285      * @param {!Node} newNode
 286      * @return {boolean} Whether the node can be skipped
 287      */
 288  	function refreshNode(oldNode, newNode)
 289      {
 290          if (oldNode.nodeName !== newNode.nodeName || oldNode.nodeType !== newNode.nodeType)
 291          {
 292              return false;
 293          }
 294  
 295          if (oldNode instanceof HTMLElement && newNode instanceof HTMLElement)
 296          {
 297              if (!oldNode.isEqualNode(newNode) && !elementHashesMatch(oldNode, newNode))
 298              {
 299                  if (HINT.onUpdate && newNode['hasAttribute']('data-s9e-livepreview-onupdate'))
 300                  {
 301                      executeEvent(newNode, 'update');
 302                  }
 303                  syncElementAttributes(oldNode, newNode);
 304                  refreshElementContent(oldNode, newNode);
 305              }
 306          }
 307          // Node.TEXT_NODE || Node.COMMENT_NODE
 308          else if (oldNode.nodeType === 3 || oldNode.nodeType === 8)
 309          {
 310              if (oldNode.nodeValue !== newNode.nodeValue)
 311              {
 312                  oldNode.nodeValue = newNode.nodeValue;
 313                  lastUpdated = oldNode;
 314              }
 315          }
 316  
 317          return true;
 318      }
 319  
 320      /**
 321      * Test whether both given elements have a hash value and both match
 322      *
 323      * @param  {!HTMLElement} oldEl
 324      * @param  {!HTMLElement} newEl
 325      * @return {boolean}
 326      */
 327  	function elementHashesMatch(oldEl, newEl)
 328      {
 329          if (!HINT.hash)
 330          {
 331              // Hashes can never match if there are no hashes in any template
 332              return false;
 333          }
 334          const attrName = 'data-s9e-livepreview-hash';
 335  
 336          return oldEl['hasAttribute'](attrName) && newEl['hasAttribute'](attrName) && oldEl['getAttribute'](attrName) === newEl['getAttribute'](attrName);
 337      }
 338  
 339      /**
 340      * Hash given string
 341      *
 342      * @param  {string} text
 343      * @return {number}
 344      */
 345  	function hash(text)
 346      {
 347          var pos = text.length, s1 = 0, s2 = 0;
 348          while (--pos >= 0)
 349          {
 350              s1 = (s1 + text.charCodeAt(pos)) % 0xFFFF;
 351              s2 = (s1 + s2) % 0xFFFF;
 352          }
 353  
 354          return (s2 << 16) | s1;
 355      }
 356  
 357      /**
 358      * Make the set of attributes of given element oldEl match newEl's
 359      *
 360      * @param {!HTMLElement} oldEl
 361      * @param {!HTMLElement} newEl
 362      */
 363  	function syncElementAttributes(oldEl, newEl)
 364      {
 365          var oldAttributes = oldEl['attributes'],
 366              newAttributes = newEl['attributes'],
 367              oldCnt        = oldAttributes.length,
 368              newCnt        = newAttributes.length,
 369              i             = oldCnt,
 370              ignoreAttrs   = ' ' + oldEl.getAttribute('data-s9e-livepreview-ignore-attrs') + ' ';
 371  
 372          while (--i >= 0)
 373          {
 374              var oldAttr      = oldAttributes[i],
 375                  namespaceURI = oldAttr['namespaceURI'],
 376                  attrName     = oldAttr['name'];
 377  
 378              if (HINT.ignoreAttrs && ignoreAttrs.indexOf(' ' + attrName + ' ') > -1)
 379              {
 380                  continue;
 381              }
 382              if (!newEl.hasAttributeNS(namespaceURI, attrName))
 383              {
 384                  oldEl.removeAttributeNS(namespaceURI, attrName);
 385                  lastUpdated = oldEl;
 386              }
 387          }
 388  
 389          i = newCnt;
 390          while (--i >= 0)
 391          {
 392              var newAttr      = newAttributes[i],
 393                  namespaceURI = newAttr['namespaceURI'],
 394                  attrName     = newAttr['name'],
 395                  attrValue    = newAttr['value'];
 396  
 397              if (HINT.ignoreAttrs && ignoreAttrs.indexOf(' ' + attrName + ' ') > -1)
 398              {
 399                  continue;
 400              }
 401              if (attrValue !== oldEl.getAttributeNS(namespaceURI, attrName))
 402              {
 403                  oldEl.setAttributeNS(namespaceURI, attrName, attrValue);
 404                  lastUpdated = oldEl;
 405              }
 406          }
 407      }
 408  
 409      refreshElementContent(target, resultFragment);
 410  
 411      return lastUpdated;
 412  }
 413  
 414  /**
 415  * Set the value of a stylesheet parameter
 416  *
 417  * @param {string} paramName  Parameter name
 418  * @param {string} paramValue Parameter's value
 419  */
 420  function setParameter(paramName, paramValue)
 421  {
 422      xslt.setParameter(paramName, paramValue);
 423  }


Generated: Mon Nov 25 19:05:08 2024 Cross-referenced by PHPXref 0.7.1