Verzeichnisstruktur phpBB-3.3.15


Veröffentlicht
28.08.2024

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

Blocks.js

Zuletzt modifiziert: 02.04.2025, 15:04 - Dateigröße: 12.08 KiB


001  var setextLines = {};
002   
003  function parse()
004  {
005      matchSetextLines();
006   
007      var blocks       = [],
008          blocksCnt    = 0,
009          codeFence,
010          codeIndent   = 4,
011          codeTag,
012          lineIsEmpty  = true,
013          lists        = [],
014          listsCnt     = 0,
015          newContext   = false,
016          textBoundary = 0,
017          breakParagraph,
018          continuation,
019          ignoreLen,
020          indentStr,
021          indentLen,
022          lfPos,
023          listIndex,
024          maxIndent,
025          minIndent,
026          blockDepth,
027          tagPos,
028          tagLen;
029   
030      // Capture all the lines at once so that we can overwrite newlines safely, without preventing
031      // further matches
032      var matches = [],
033          m,
034          regexp = /^(?:(?=[-*+\d \t>`~#_])((?: {0,3}>(?:(?!!)|!(?![^\n>]*?!<)) ?)+)?([ \t]+)?(\* *\* *\*[* ]*$|- *- *-[- ]*$|_ *_ *_[_ ]*$)?((?:[-*+]|\d+\.)[ \t]+(?=\S))?[ \t]*(#{1,6}[ \t]+|```+[^`\n]*$|~~~+[^~\n]*$)?)?/gm;
035      while (m = regexp.exec(text))
036      {
037          matches.push(m);
038   
039          // Move regexp.lastIndex if the current match is empty
040          if (m.index === regexp.lastIndex)
041          {
042              ++regexp.lastIndex;
043          }
044      }
045   
046      matches.forEach(function(m)
047      {
048          var blockMarks = [],
049              matchPos   = m.index,
050              matchLen   = m[0].length,
051              startPos,
052              startLen,
053              endPos,
054              endLen;
055   
056          ignoreLen  = 0;
057          blockDepth = 0;
058   
059          // If the last line was empty then this is not a continuation, and vice-versa
060          continuation = !lineIsEmpty;
061   
062          // Capture the position of the end of the line and determine whether the line is empty
063          lfPos       = text.indexOf("\n", matchPos);
064          lineIsEmpty = (lfPos === matchPos + matchLen && !m[3] && !m[4] && !m[5]);
065   
066          // If the match is empty we need to move the cursor manually
067          if (!matchLen)
068          {
069              ++regexp.lastIndex;
070          }
071   
072          // If the line is empty and it's the first empty line then we break current paragraph.
073          breakParagraph = (lineIsEmpty && continuation);
074   
075          // Count block marks
076          if (m[1])
077          {
078              blockMarks = getBlockMarks(m[1]);
079              blockDepth = blockMarks.length;
080              ignoreLen  = m[1].length;
081              if (codeTag && codeTag.hasAttribute('blockDepth'))
082              {
083                  blockDepth = Math.min(blockDepth, codeTag.getAttribute('blockDepth'));
084                  ignoreLen  = computeBlockIgnoreLen(m[1], blockDepth);
085              }
086   
087              // Overwrite block markup
088              overwrite(matchPos, ignoreLen);
089          }
090   
091          // Close supernumerary blocks
092          if (blockDepth < blocksCnt && !continuation)
093          {
094              newContext = true;
095              do
096              {
097                  var startTag = blocks.pop();
098                  addEndTag(startTag.getName(), textBoundary, 0).pairWith(startTag);
099              }
100              while (blockDepth < --blocksCnt);
101          }
102   
103          // Open new blocks
104          if (blockDepth > blocksCnt && !lineIsEmpty)
105          {
106              newContext = true;
107              do
108              {
109                  var tagName = (blockMarks[blocksCnt] === '>!') ? 'SPOILER' : 'QUOTE';
110                  blocks.push(addStartTag(tagName, matchPos, 0, -999));
111              }
112              while (blockDepth > ++blocksCnt);
113          }
114   
115          // Compute the width of the indentation
116          var indentWidth = 0,
117              indentPos   = 0;
118          if (m[2] && !codeFence)
119          {
120              indentStr = m[2];
121              indentLen = indentStr.length;
122   
123              do
124              {
125                  if (indentStr[indentPos] === ' ')
126                  {
127                      ++indentWidth;
128                  }
129                  else
130                  {
131                      indentWidth = (indentWidth + 4) & ~3;
132                  }
133              }
134              while (++indentPos < indentLen && indentWidth < codeIndent);
135          }
136   
137          // Test whether we're out of a code block
138          if (codeTag && !codeFence && indentWidth < codeIndent && !lineIsEmpty)
139          {
140              newContext = true;
141          }
142   
143          if (newContext)
144          {
145              newContext = false;
146   
147              // Close the code block if applicable
148              if (codeTag)
149              {
150                  if (textBoundary > codeTag.getPos())
151                  {
152                      // Overwrite the whole block
153                      overwrite(codeTag.getPos(), textBoundary - codeTag.getPos());
154                      codeTag.pairWith(addEndTag('CODE', textBoundary, 0, -1));
155                  }
156                  else
157                  {
158                      // The code block is empty
159                      codeTag.invalidate();
160                  }
161                  codeTag = null;
162                  codeFence = null;
163              }
164   
165              // Close all the lists
166              lists.forEach(function(list)
167              {
168                  closeList(list, textBoundary);
169              });
170              lists    = [];
171              listsCnt = 0;
172   
173              // Mark the block boundary
174              if (matchPos)
175              {
176                  markBoundary(matchPos - 1);
177              }
178          }
179   
180          if (indentWidth >= codeIndent)
181          {
182              if (codeTag || !continuation)
183              {
184                  // Adjust the amount of text being ignored
185                  ignoreLen = (m[1] || '').length + indentPos;
186   
187                  if (!codeTag)
188                  {
189                      // Create code block
190                      codeTag = addStartTag('CODE', matchPos + ignoreLen, 0, -999);
191                  }
192   
193                  // Clear the captures to prevent any further processing
194                  m = {};
195              }
196          }
197          else if (!codeTag)
198          {
199              var hasListItem = !!m[4];
200   
201              if (!indentWidth && !continuation && !hasListItem)
202              {
203                  // Start of a new context
204                  listIndex = -1;
205              }
206              else if (continuation && !hasListItem)
207              {
208                  // Continuation of current list item or paragraph
209                  listIndex = listsCnt - 1;
210              }
211              else if (!listsCnt)
212              {
213                  // We're not inside of a list already, we can start one if there's a list item
214                  listIndex = (hasListItem) ? 0 : -1;
215              }
216              else
217              {
218                  // We're inside of a list but we need to compute the depth
219                  listIndex = 0;
220                  while (listIndex < listsCnt && indentWidth > lists[listIndex].maxIndent)
221                  {
222                      ++listIndex;
223                  }
224              }
225   
226              // Close deeper lists
227              while (listIndex < listsCnt - 1)
228              {
229                  closeList(lists.pop(), textBoundary);
230                  --listsCnt;
231              }
232   
233              // If there's no list item at current index, we'll need to either create one or
234              // drop down to previous index, in which case we have to adjust maxIndent
235              if (listIndex === listsCnt && !hasListItem)
236              {
237                  --listIndex;
238              }
239   
240              if (hasListItem && listIndex >= 0)
241              {
242                  breakParagraph = true;
243   
244                  // Compute the position and amount of text consumed by the item tag
245                  tagPos = matchPos + ignoreLen + indentPos;
246                  tagLen = m[4].length;
247   
248                  // Create a LI tag that consumes its markup
249                  var itemTag = addStartTag('LI', tagPos, tagLen);
250   
251                  // Overwrite the markup
252                  overwrite(tagPos, tagLen);
253   
254                  // If the list index is within current lists count it means this is not a new
255                  // list and we have to close the last item. Otherwise, it's a new list that we
256                  // have to create
257                  if (listIndex < listsCnt)
258                  {
259                      addEndTag('LI', textBoundary, 0).pairWith(lists[listIndex].itemTag);
260   
261                      // Record the item in the list
262                      lists[listIndex].itemTag = itemTag;
263                      lists[listIndex].itemTags.push(itemTag);
264                  }
265                  else
266                  {
267                      ++listsCnt;
268   
269                      if (listIndex)
270                      {
271                          minIndent = lists[listIndex - 1].maxIndent + 1;
272                          maxIndent = Math.max(minIndent, listIndex * 4);
273                      }
274                      else
275                      {
276                          minIndent = 0;
277                          maxIndent = indentWidth;
278                      }
279   
280                      // Create a 0-width LIST tag right before the item tag LI
281                      var listTag = addStartTag('LIST', tagPos, 0);
282   
283                      // Test whether the list item ends with a dot, as in "1."
284                      if (m[4].indexOf('.') > -1)
285                      {
286                          listTag.setAttribute('type', 'decimal');
287   
288                          var start = +m[4];
289                          if (start !== 1)
290                          {
291                              listTag.setAttribute('start', start);
292                          }
293                      }
294   
295                      // Record the new list depth
296                      lists.push({
297                          listTag   : listTag,
298                          itemTag   : itemTag,
299                          itemTags  : [itemTag],
300                          minIndent : minIndent,
301                          maxIndent : maxIndent,
302                          tight     : true
303                      });
304                  }
305              }
306   
307              // If we're in a list, on a non-empty line preceded with a blank line...
308              if (listsCnt && !continuation && !lineIsEmpty)
309              {
310                  // ...and this is not the first item of the list...
311                  if (lists[0].itemTags.length > 1 || !hasListItem)
312                  {
313                      // ...every list that is currently open becomes loose
314                      lists.forEach(function(list)
315                      {
316                          list.tight = false;
317                      });
318                  }
319              }
320   
321              codeIndent = (listsCnt + 1) * 4;
322          }
323   
324          if (m[5])
325          {
326              // Headers
327              if (m[5][0] === '#')
328              {
329                  startLen = m[5].length;
330                  startPos = matchPos + matchLen - startLen;
331                  endLen   = getAtxHeaderEndTagLen(matchPos + matchLen, lfPos);
332                  endPos   = lfPos - endLen;
333   
334                  addTagPair('H' + /#{1,6}/.exec(m[5])[0].length, startPos, startLen, endPos, endLen);
335   
336                  // Mark the start and the end of the header as boundaries
337                  markBoundary(startPos);
338                  markBoundary(lfPos);
339   
340                  if (continuation)
341                  {
342                      breakParagraph = true;
343                  }
344              }
345              // Code fence
346              else if (m[5][0] === '`' || m[5][0] === '~')
347              {
348                  tagPos = matchPos + ignoreLen;
349                  tagLen = lfPos - tagPos;
350   
351                  if (codeTag && m[5] === codeFence)
352                  {
353                      codeTag.pairWith(addEndTag('CODE', tagPos, tagLen, -1));
354                      addIgnoreTag(textBoundary, tagPos - textBoundary);
355   
356                      // Overwrite the whole block
357                      overwrite(codeTag.getPos(), tagPos + tagLen - codeTag.getPos());
358                      codeTag = null;
359                      codeFence = null;
360                  }
361                  else if (!codeTag)
362                  {
363                      // Create code block
364                      codeTag   = addStartTag('CODE', tagPos, tagLen);
365                      codeFence = m[5].replace(/[^`~]+/, '');
366                      codeTag.setAttribute('blockDepth', blockDepth);
367   
368                      // Ignore the next character, which should be a newline
369                      addIgnoreTag(tagPos + tagLen, 1);
370   
371                      // Add the language if present, e.g. ```php
372                      var lang = m[5].replace(/^[`~\s]*/, '').replace(/\s+$/, '');
373                      if (lang !== '')
374                      {
375                          codeTag.setAttribute('lang', lang);
376                      }
377                  }
378              }
379          }
380          else if (m[3] && !listsCnt && text[matchPos + matchLen] !== "\x17")
381          {
382              // Horizontal rule
383              addSelfClosingTag('HR', matchPos + ignoreLen, matchLen - ignoreLen);
384              breakParagraph = true;
385   
386              // Mark the end of the line as a boundary
387              markBoundary(lfPos);
388          }
389          else if (setextLines[lfPos] && setextLines[lfPos].blockDepth === blockDepth && !lineIsEmpty && !listsCnt && !codeTag)
390          {
391              // Setext-style header
392              addTagPair(
393                  setextLines[lfPos].tagName,
394                  matchPos + ignoreLen,
395                  0,
396                  setextLines[lfPos].endPos,
397                  setextLines[lfPos].endLen
398              );
399   
400              // Mark the end of the Setext line
401              markBoundary(setextLines[lfPos].endPos + setextLines[lfPos].endLen);
402          }
403   
404          if (breakParagraph)
405          {
406              addParagraphBreak(textBoundary);
407              markBoundary(textBoundary);
408          }
409   
410          if (!lineIsEmpty)
411          {
412              textBoundary = lfPos;
413          }
414   
415          if (ignoreLen)
416          {
417              addIgnoreTag(matchPos, ignoreLen, 1000);
418          }
419      });
420  }
421   
422  /**
423  * Close a list at given offset
424  *
425  * @param {!Object} list
426  * @param {number}  textBoundary
427  */
428  function closeList(list, textBoundary)
429  {
430      addEndTag('LIST', textBoundary, 0).pairWith(list.listTag);
431      addEndTag('LI',   textBoundary, 0).pairWith(list.itemTag);
432   
433      if (list.tight)
434      {
435          list.itemTags.forEach(function(itemTag)
436          {
437              itemTag.removeFlags(RULE_CREATE_PARAGRAPHS);
438          });
439      }
440  }
441   
442  /**
443  * Compute the amount of text to ignore at the start of a block line
444  *
445  * @param  {string} str           Original block markup
446  * @param  {number} maxBlockDepth Maximum block depth
447  * @return {number}               Number of characters to ignore
448  */
449  function computeBlockIgnoreLen(str, maxBlockDepth)
450  {
451      var remaining = str;
452      while (--maxBlockDepth >= 0)
453      {
454          remaining = remaining.replace(/^ *>!? ?/, '');
455      }
456   
457      return str.length - remaining.length;
458  }
459   
460  /**
461  * Return the length of the markup at the end of an ATX header
462  *
463  * @param  {number} startPos Start of the header's text
464  * @param  {number} endPos   End of the header's text
465  * @return {number}
466  */
467  function getAtxHeaderEndTagLen(startPos, endPos)
468  {
469      var content = text.substring(startPos, endPos),
470          m = /[ \t]*#*[ \t]*$/.exec(content);
471   
472      return m[0].length;
473  }
474   
475  /**
476  * Capture and return block marks from given string
477  *
478  * @param  {string} str Block markup, composed of ">", "!" and whitespace
479  * @return {!Array<string>}
480  */
481  function getBlockMarks(str)
482  {
483      var blockMarks = [],
484          regexp     = />!?/g,
485          m;
486      while (m = regexp.exec(str))
487      {
488          blockMarks.push(m[0]);
489      }
490   
491      return blockMarks;
492  }
493   
494  /**
495  * Capture and store lines that contain a Setext-tyle header
496  */
497  function matchSetextLines()
498  {
499      // Capture the underlines used for Setext-style headers
500      if (text.indexOf('-') === -1 && text.indexOf('=') === -1)
501      {
502          return;
503      }
504   
505      // Capture the any series of - or = alone on a line, optionally preceded with the
506      // angle brackets notation used in block markup
507      var m, regexp = /^(?=[-=>])(?:>!? ?)*(?=[-=])(?:-+|=+) *$/gm;
508   
509      while (m = regexp.exec(text))
510      {
511          var match    = m[0],
512              matchPos = m.index;
513   
514          // Compute the position of the end tag. We start on the LF character before the
515          // match and keep rewinding until we find a non-space character
516          var endPos = matchPos - 1;
517          while (endPos > 0 && text[endPos - 1] === ' ')
518          {
519              --endPos;
520          }
521   
522          // Store at the offset of the LF character
523          setextLines[matchPos - 1] = {
524              endLen     : matchPos + match.length - endPos,
525              endPos     : endPos,
526              blockDepth : match.length - match.replace(/>/g, '').length,
527              tagName    : (match[0] === '=') ? 'H1' : 'H2'
528          };
529      }
530  }