Verzeichnisstruktur phpBB-3.3.16


Veröffentlicht
27.04.2026

So funktioniert es


Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück

Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

      (Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

render.js

Zuletzt modifiziert: 01.05.2026, 11:26 - Dateigröße: 9.30 KiB


 001  var MSXML = (typeof DOMParser === 'undefined' || typeof XSLTProcessor === 'undefined');
 002  var xslt = {
 003      /**
 004      * @param {string} xsl
 005      */
 006      init: function(xsl)
 007      {
 008          var stylesheet = xslt.loadXML(xsl);
 009          if (MSXML)
 010          {
 011              var generator = new ActiveXObject('MSXML2.XSLTemplate.6.0');
 012              generator['stylesheet'] = stylesheet;
 013              xslt.proc = generator['createProcessor']();
 014          }
 015          else
 016          {
 017              xslt.proc = new XSLTProcessor;
 018              xslt.proc['importStylesheet'](stylesheet);
 019          }
 020      },
 021   
 022      /**
 023      * @param  {string} xml
 024      * @return {!Document}
 025      */
 026      loadXML: function(xml)
 027      {
 028          var dom;
 029          if (MSXML)
 030          {
 031              dom = new ActiveXObject('MSXML2.FreeThreadedDOMDocument.6.0');
 032              dom['async'] = false;
 033              dom['validateOnParse'] = false;
 034              dom['loadXML'](xml);
 035          }
 036          else
 037          {
 038              dom = (new DOMParser).parseFromString(xml, 'text/xml');
 039          }
 040   
 041          if (!dom)
 042          {
 043              throw 'Cannot parse ' + xml;
 044          }
 045   
 046          return dom;
 047      },
 048   
 049      /**
 050      * @param {string} paramName  Parameter name
 051      * @param {string} paramValue Parameter's value
 052      */
 053      setParameter: function(paramName, paramValue)
 054      {
 055          if (MSXML)
 056          {
 057              xslt.proc['addParameter'](paramName, paramValue, '');
 058          }
 059          else
 060          {
 061              xslt.proc['setParameter'](null, paramName, paramValue);
 062          }
 063      },
 064   
 065      /**
 066      * @param  {string}    xml
 067      * @param  {!Document} targetDoc
 068      * @return {!DocumentFragment}
 069      */
 070      transformToFragment: function(xml, targetDoc)
 071      {
 072          if (MSXML)
 073          {
 074              var div = targetDoc.createElement('div'),
 075                  fragment = targetDoc.createDocumentFragment();
 076   
 077              xslt.proc['input'] = xslt.loadXML(xml);
 078              xslt.proc['transform']();
 079              div.innerHTML = xslt.proc['output'];
 080              while (div.firstChild)
 081              {
 082                  fragment.appendChild(div.firstChild);
 083              }
 084   
 085              return fragment;
 086          }
 087   
 088          return xslt.proc['transformToFragment'](xslt.loadXML(xml), targetDoc);
 089      }
 090  };
 091  xslt.init(xsl);
 092   
 093  /**
 094  * Parse a given text and render it into given HTML element
 095  *
 096  * @param  {string} text
 097  * @param  {!HTMLElement} target
 098  * @return {!Node}
 099  */
 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  }