[ Index ] |
PHP Cross Reference of phpBB-3.2.11-deutsch |
[Summary view] [Print] [Text view]
1 var setextLines = {}; 2 3 function parse() 4 { 5 matchSetextLines(); 6 7 var codeFence, 8 codeIndent = 4, 9 codeTag, 10 lineIsEmpty = true, 11 lists = [], 12 listsCnt = 0, 13 newContext = false, 14 quotes = [], 15 quotesCnt = 0, 16 textBoundary = 0, 17 breakParagraph, 18 continuation, 19 endTag, 20 ignoreLen, 21 indentStr, 22 indentLen, 23 lfPos, 24 listIndex, 25 maxIndent, 26 minIndent, 27 quoteDepth, 28 tagPos, 29 tagLen; 30 31 // Capture all the lines at once so that we can overwrite newlines safely, without preventing 32 // further matches 33 var matches = [], 34 m, 35 regexp = /^(?:(?=[-*+\d \t>`~#_])((?: {0,3}> ?)+)?([ \t]+)?(\* *\* *\*[* ]*$|- *- *-[- ]*$|_ *_ *_[_ ]*$)?((?:[-*+]|\d+\.)[ \t]+(?=\S))?[ \t]*(#{1,6}[ \t]+|```+[^`\n]*$|~~~+[^~\n]*$)?)?/gm; 36 while (m = regexp.exec(text)) 37 { 38 matches.push(m); 39 40 // Move regexp.lastIndex if the current match is empty 41 if (m['index'] === regexp['lastIndex']) 42 { 43 ++regexp['lastIndex']; 44 } 45 } 46 47 matches.forEach(function(m) 48 { 49 var matchPos = +m['index'], 50 matchLen = m[0].length, 51 startPos, 52 startLen, 53 endPos, 54 endLen; 55 56 ignoreLen = 0; 57 quoteDepth = 0; 58 59 // If the last line was empty then this is not a continuation, and vice-versa 60 continuation = !lineIsEmpty; 61 62 // Capture the position of the end of the line and determine whether the line is empty 63 lfPos = text.indexOf("\n", matchPos); 64 lineIsEmpty = (lfPos === matchPos + matchLen && !m[3] && !m[4] && !m[5]); 65 66 // If the match is empty we need to move the cursor manually 67 if (!matchLen) 68 { 69 ++regexp.lastIndex; 70 } 71 72 // If the line is empty and it's the first empty line then we break current paragraph. 73 breakParagraph = (lineIsEmpty && continuation); 74 75 // Count quote marks 76 if (m[1]) 77 { 78 quoteDepth = m[1].length - m[1].replace(/>/g, '').length; 79 ignoreLen = m[1].length; 80 if (codeTag && codeTag.hasAttribute('quoteDepth')) 81 { 82 quoteDepth = Math.min(quoteDepth, codeTag.getAttribute('quoteDepth')); 83 ignoreLen = computeQuoteIgnoreLen(m[1], quoteDepth); 84 } 85 86 // Overwrite quote markup 87 overwrite(matchPos, ignoreLen); 88 } 89 90 // Close supernumerary quotes 91 if (quoteDepth < quotesCnt && !continuation) 92 { 93 newContext = true; 94 95 do 96 { 97 addEndTag('QUOTE', textBoundary, 0).pairWith(quotes.pop()); 98 } 99 while (quoteDepth < --quotesCnt); 100 } 101 102 // Open new quotes 103 if (quoteDepth > quotesCnt && !lineIsEmpty) 104 { 105 newContext = true; 106 107 do 108 { 109 var tag = addStartTag('QUOTE', matchPos, 0, quotesCnt - 999); 110 quotes.push(tag); 111 } 112 while (quoteDepth > ++quotesCnt); 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 155 endTag = addEndTag('CODE', textBoundary, 0, -1); 156 endTag.pairWith(codeTag); 157 } 158 else 159 { 160 // The code block is empty 161 codeTag.invalidate(); 162 } 163 codeTag = null; 164 codeFence = null; 165 } 166 167 // Close all the lists 168 lists.forEach(function(list) 169 { 170 closeList(list, textBoundary); 171 }); 172 lists = []; 173 listsCnt = 0; 174 175 // Mark the block boundary 176 if (matchPos) 177 { 178 markBoundary(matchPos - 1); 179 } 180 } 181 182 if (indentWidth >= codeIndent) 183 { 184 if (codeTag || !continuation) 185 { 186 // Adjust the amount of text being ignored 187 ignoreLen = (m[1] || '').length + indentPos; 188 189 if (!codeTag) 190 { 191 // Create code block 192 codeTag = addStartTag('CODE', matchPos + ignoreLen, 0, -999); 193 } 194 195 // Clear the captures to prevent any further processing 196 m = {}; 197 } 198 } 199 else 200 { 201 var hasListItem = !!m[4]; 202 203 if (!indentWidth && !continuation && !hasListItem) 204 { 205 // Start of a new context 206 listIndex = -1; 207 } 208 else if (continuation && !hasListItem) 209 { 210 // Continuation of current list item or paragraph 211 listIndex = listsCnt - 1; 212 } 213 else if (!listsCnt) 214 { 215 // We're not inside of a list already, we can start one if there's a list item 216 listIndex = (hasListItem) ? 0 : -1 217 } 218 else 219 { 220 // We're inside of a list but we need to compute the depth 221 listIndex = 0; 222 while (listIndex < listsCnt && indentWidth > lists[listIndex].maxIndent) 223 { 224 ++listIndex; 225 } 226 } 227 228 // Close deeper lists 229 while (listIndex < listsCnt - 1) 230 { 231 closeList(lists.pop(), textBoundary); 232 --listsCnt; 233 } 234 235 // If there's no list item at current index, we'll need to either create one or 236 // drop down to previous index, in which case we have to adjust maxIndent 237 if (listIndex === listsCnt && !hasListItem) 238 { 239 --listIndex; 240 } 241 242 if (hasListItem && listIndex >= 0) 243 { 244 breakParagraph = true; 245 246 // Compute the position and amount of text consumed by the item tag 247 tagPos = matchPos + ignoreLen + indentPos 248 tagLen = m[4].length; 249 250 // Create a LI tag that consumes its markup 251 var itemTag = addStartTag('LI', tagPos, tagLen); 252 253 // Overwrite the markup 254 overwrite(tagPos, tagLen); 255 256 // If the list index is within current lists count it means this is not a new 257 // list and we have to close the last item. Otherwise, it's a new list that we 258 // have to create 259 if (listIndex < listsCnt) 260 { 261 addEndTag('LI', textBoundary, 0).pairWith(lists[listIndex].itemTag); 262 263 // Record the item in the list 264 lists[listIndex].itemTag = itemTag; 265 lists[listIndex].itemTags.push(itemTag); 266 } 267 else 268 { 269 ++listsCnt; 270 271 if (listIndex) 272 { 273 minIndent = lists[listIndex - 1].maxIndent + 1; 274 maxIndent = Math.max(minIndent, listIndex * 4); 275 } 276 else 277 { 278 minIndent = 0; 279 maxIndent = indentWidth; 280 } 281 282 // Create a 0-width LIST tag right before the item tag LI 283 var listTag = addStartTag('LIST', tagPos, 0); 284 285 // Test whether the list item ends with a dot, as in "1." 286 if (m[4].indexOf('.') > -1) 287 { 288 listTag.setAttribute('type', 'decimal'); 289 290 var start = +m[4]; 291 if (start !== 1) 292 { 293 listTag.setAttribute('start', start); 294 } 295 } 296 297 // Record the new list depth 298 lists.push({ 299 listTag : listTag, 300 itemTag : itemTag, 301 itemTags : [itemTag], 302 minIndent : minIndent, 303 maxIndent : maxIndent, 304 tight : true 305 }); 306 } 307 } 308 309 // If we're in a list, on a non-empty line preceded with a blank line... 310 if (listsCnt && !continuation && !lineIsEmpty) 311 { 312 // ...and this is not the first item of the list... 313 if (lists[0].itemTags.length > 1 || !hasListItem) 314 { 315 // ...every list that is currently open becomes loose 316 lists.forEach(function(list) 317 { 318 list.tight = false; 319 }); 320 } 321 } 322 323 codeIndent = (listsCnt + 1) * 4; 324 } 325 326 if (m[5]) 327 { 328 // Headers 329 if (m[5][0] === '#') 330 { 331 startLen = m[5].length; 332 startPos = matchPos + matchLen - startLen; 333 endLen = getAtxHeaderEndTagLen(matchPos + matchLen, lfPos); 334 endPos = lfPos - endLen; 335 336 addTagPair('H' + /#{1,6}/.exec(m[5])[0].length, startPos, startLen, endPos, endLen); 337 338 // Mark the start and the end of the header as boundaries 339 markBoundary(startPos); 340 markBoundary(lfPos); 341 342 if (continuation) 343 { 344 breakParagraph = true; 345 } 346 } 347 // Code fence 348 else if (m[5][0] === '`' || m[5][0] === '~') 349 { 350 tagPos = matchPos + ignoreLen; 351 tagLen = lfPos - tagPos; 352 353 if (codeTag && m[5] === codeFence) 354 { 355 endTag = addEndTag('CODE', tagPos, tagLen, -1); 356 endTag.pairWith(codeTag); 357 358 addIgnoreTag(textBoundary, tagPos - textBoundary); 359 360 // Overwrite the whole block 361 overwrite(codeTag.getPos(), tagPos + tagLen - codeTag.getPos()); 362 codeTag = null; 363 codeFence = null; 364 } 365 else if (!codeTag) 366 { 367 // Create code block 368 codeTag = addStartTag('CODE', tagPos, tagLen); 369 codeFence = m[5].replace(/[^`~]+/, ''); 370 codeTag.setAttribute('quoteDepth', quoteDepth); 371 372 // Ignore the next character, which should be a newline 373 addIgnoreTag(tagPos + tagLen, 1); 374 375 // Add the language if present, e.g. ```php 376 var lang = m[5].replace(/^[`~\s]*/, '').replace(/\s+$/, ''); 377 if (lang !== '') 378 { 379 codeTag.setAttribute('lang', lang); 380 } 381 } 382 } 383 } 384 else if (m[3] && !listsCnt && text[matchPos + matchLen] !== "\x17") 385 { 386 // Horizontal rule 387 addSelfClosingTag('HR', matchPos + ignoreLen, matchLen - ignoreLen); 388 breakParagraph = true; 389 390 // Mark the end of the line as a boundary 391 markBoundary(lfPos); 392 } 393 else if (setextLines[lfPos] && setextLines[lfPos].quoteDepth === quoteDepth && !lineIsEmpty && !listsCnt && !codeTag) 394 { 395 // Setext-style header 396 addTagPair( 397 setextLines[lfPos].tagName, 398 matchPos + ignoreLen, 399 0, 400 setextLines[lfPos].endPos, 401 setextLines[lfPos].endLen 402 ); 403 404 // Mark the end of the Setext line 405 markBoundary(setextLines[lfPos].endPos + setextLines[lfPos].endLen); 406 } 407 408 if (breakParagraph) 409 { 410 addParagraphBreak(textBoundary); 411 markBoundary(textBoundary); 412 } 413 414 if (!lineIsEmpty) 415 { 416 textBoundary = lfPos; 417 } 418 419 if (ignoreLen) 420 { 421 addIgnoreTag(matchPos, ignoreLen, 1000); 422 } 423 }); 424 } 425 426 /** 427 * Close a list at given offset 428 * 429 * @param {!Array} list 430 * @param {number} textBoundary 431 */ 432 function closeList(list, textBoundary) 433 { 434 addEndTag('LIST', textBoundary, 0).pairWith(list.listTag); 435 addEndTag('LI', textBoundary, 0).pairWith(list.itemTag); 436 437 if (list.tight) 438 { 439 list.itemTags.forEach(function(itemTag) 440 { 441 itemTag.removeFlags(RULE_CREATE_PARAGRAPHS); 442 }); 443 } 444 } 445 446 /** 447 * Compute the amount of text to ignore at the start of a quote line 448 * 449 * @param {string} str Original quote markup 450 * @param {number} maxQuoteDepth Maximum quote depth 451 * @return {number} Number of characters to ignore 452 */ 453 function computeQuoteIgnoreLen(str, maxQuoteDepth) 454 { 455 var remaining = str; 456 while (--maxQuoteDepth >= 0) 457 { 458 remaining = remaining.replace(/^ *> ?/, ''); 459 } 460 461 return str.length - remaining.length; 462 } 463 464 /** 465 * Return the length of the markup at the end of an ATX header 466 * 467 * @param {number} startPos Start of the header's text 468 * @param {number} endPos End of the header's text 469 * @return {number} 470 */ 471 function getAtxHeaderEndTagLen(startPos, endPos) 472 { 473 var content = text.substr(startPos, endPos - startPos), 474 m = /[ \t]*#*[ \t]*$/.exec(content); 475 476 return m[0].length; 477 } 478 479 /** 480 * Capture and store lines that contain a Setext-tyle header 481 */ 482 function matchSetextLines() 483 { 484 // Capture the underlines used for Setext-style headers 485 if (text.indexOf('-') === -1 && text.indexOf('=') === -1) 486 { 487 return; 488 } 489 490 // Capture the any series of - or = alone on a line, optionally preceded with the 491 // angle brackets notation used in blockquotes 492 var m, regexp = /^(?=[-=>])(?:> ?)*(?=[-=])(?:-+|=+) *$/gm; 493 494 while (m = regexp.exec(text)) 495 { 496 var match = m[0], 497 matchPos = +m['index']; 498 499 // Compute the position of the end tag. We start on the LF character before the 500 // match and keep rewinding until we find a non-space character 501 var endPos = matchPos - 1; 502 while (endPos > 0 && text[endPos - 1] === ' ') 503 { 504 --endPos; 505 } 506 507 // Store at the offset of the LF character 508 setextLines[matchPos - 1] = { 509 endLen : matchPos + match.length - endPos, 510 endPos : endPos, 511 quoteDepth : match.length - match.replace(/>/g, '').length, 512 tagName : (match[0] === '=') ? 'H1' : 'H2' 513 }; 514 } 515 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Nov 11 20:33:01 2020 | Cross-referenced by PHPXref 0.7.1 |