block-serialization-default-parser.js (15053B)
1 this["wp"] = this["wp"] || {}; this["wp"]["blockSerializationDefaultParser"] = 2 /******/ (function(modules) { // webpackBootstrap 3 /******/ // The module cache 4 /******/ var installedModules = {}; 5 /******/ 6 /******/ // The require function 7 /******/ function __webpack_require__(moduleId) { 8 /******/ 9 /******/ // Check if module is in cache 10 /******/ if(installedModules[moduleId]) { 11 /******/ return installedModules[moduleId].exports; 12 /******/ } 13 /******/ // Create a new module (and put it into the cache) 14 /******/ var module = installedModules[moduleId] = { 15 /******/ i: moduleId, 16 /******/ l: false, 17 /******/ exports: {} 18 /******/ }; 19 /******/ 20 /******/ // Execute the module function 21 /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 22 /******/ 23 /******/ // Flag the module as loaded 24 /******/ module.l = true; 25 /******/ 26 /******/ // Return the exports of the module 27 /******/ return module.exports; 28 /******/ } 29 /******/ 30 /******/ 31 /******/ // expose the modules object (__webpack_modules__) 32 /******/ __webpack_require__.m = modules; 33 /******/ 34 /******/ // expose the module cache 35 /******/ __webpack_require__.c = installedModules; 36 /******/ 37 /******/ // define getter function for harmony exports 38 /******/ __webpack_require__.d = function(exports, name, getter) { 39 /******/ if(!__webpack_require__.o(exports, name)) { 40 /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); 41 /******/ } 42 /******/ }; 43 /******/ 44 /******/ // define __esModule on exports 45 /******/ __webpack_require__.r = function(exports) { 46 /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { 47 /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); 48 /******/ } 49 /******/ Object.defineProperty(exports, '__esModule', { value: true }); 50 /******/ }; 51 /******/ 52 /******/ // create a fake namespace object 53 /******/ // mode & 1: value is a module id, require it 54 /******/ // mode & 2: merge all properties of value into the ns 55 /******/ // mode & 4: return value when already ns object 56 /******/ // mode & 8|1: behave like require 57 /******/ __webpack_require__.t = function(value, mode) { 58 /******/ if(mode & 1) value = __webpack_require__(value); 59 /******/ if(mode & 8) return value; 60 /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; 61 /******/ var ns = Object.create(null); 62 /******/ __webpack_require__.r(ns); 63 /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); 64 /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); 65 /******/ return ns; 66 /******/ }; 67 /******/ 68 /******/ // getDefaultExport function for compatibility with non-harmony modules 69 /******/ __webpack_require__.n = function(module) { 70 /******/ var getter = module && module.__esModule ? 71 /******/ function getDefault() { return module['default']; } : 72 /******/ function getModuleExports() { return module; }; 73 /******/ __webpack_require__.d(getter, 'a', getter); 74 /******/ return getter; 75 /******/ }; 76 /******/ 77 /******/ // Object.prototype.hasOwnProperty.call 78 /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 79 /******/ 80 /******/ // __webpack_public_path__ 81 /******/ __webpack_require__.p = ""; 82 /******/ 83 /******/ 84 /******/ // Load entry module and return exports 85 /******/ return __webpack_require__(__webpack_require__.s = "SiJt"); 86 /******/ }) 87 /************************************************************************/ 88 /******/ ({ 89 90 /***/ "SiJt": 91 /***/ (function(module, __webpack_exports__, __webpack_require__) { 92 93 "use strict"; 94 __webpack_require__.r(__webpack_exports__); 95 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "parse", function() { return parse; }); 96 let document; 97 let offset; 98 let output; 99 let stack; 100 /** 101 * Matches block comment delimiters 102 * 103 * While most of this pattern is straightforward the attribute parsing 104 * incorporates a tricks to make sure we don't choke on specific input 105 * 106 * - since JavaScript has no possessive quantifier or atomic grouping 107 * we are emulating it with a trick 108 * 109 * we want a possessive quantifier or atomic group to prevent backtracking 110 * on the `}`s should we fail to match the remainder of the pattern 111 * 112 * we can emulate this with a positive lookahead and back reference 113 * (a++)*c === ((?=(a+))\1)*c 114 * 115 * let's examine an example: 116 * - /(a+)*c/.test('aaaaaaaaaaaaad') fails after over 49,000 steps 117 * - /(a++)*c/.test('aaaaaaaaaaaaad') fails after 85 steps 118 * - /(?>a+)*c/.test('aaaaaaaaaaaaad') fails after 126 steps 119 * 120 * this is because the possessive `++` and the atomic group `(?>)` 121 * tell the engine that all those `a`s belong together as a single group 122 * and so it won't split it up when stepping backwards to try and match 123 * 124 * if we use /((?=(a+))\1)*c/ then we get the same behavior as the atomic group 125 * or possessive and prevent the backtracking because the `a+` is matched but 126 * not captured. thus, we find the long string of `a`s and remember it, then 127 * reference it as a whole unit inside our pattern 128 * 129 * @see http://instanceof.me/post/52245507631/regex-emulate-atomic-grouping-with-lookahead 130 * @see http://blog.stevenlevithan.com/archives/mimic-atomic-groups 131 * @see https://javascript.info/regexp-infinite-backtracking-problem 132 * 133 * once browsers reliably support atomic grouping or possessive 134 * quantifiers natively we should remove this trick and simplify 135 * 136 * @type {RegExp} 137 * 138 * @since 3.8.0 139 * @since 4.6.1 added optimization to prevent backtracking on attribute parsing 140 */ 141 142 const tokenizer = /<!--\s+(\/)?wp:([a-z][a-z0-9_-]*\/)?([a-z][a-z0-9_-]*)\s+({(?:(?=([^}]+|}+(?=})|(?!}\s+\/?-->)[^])*)\5|[^]*?)}\s+)?(\/)?-->/g; 143 144 function Block(blockName, attrs, innerBlocks, innerHTML, innerContent) { 145 return { 146 blockName, 147 attrs, 148 innerBlocks, 149 innerHTML, 150 innerContent 151 }; 152 } 153 154 function Freeform(innerHTML) { 155 return Block(null, {}, [], innerHTML, [innerHTML]); 156 } 157 158 function Frame(block, tokenStart, tokenLength, prevOffset, leadingHtmlStart) { 159 return { 160 block, 161 tokenStart, 162 tokenLength, 163 prevOffset: prevOffset || tokenStart + tokenLength, 164 leadingHtmlStart 165 }; 166 } 167 /** 168 * Parser function, that converts input HTML into a block based structure. 169 * 170 * @param {string} doc The HTML document to parse. 171 * 172 * @example 173 * Input post: 174 * ```html 175 * <!-- wp:columns {"columns":3} --> 176 * <div class="wp-block-columns has-3-columns"><!-- wp:column --> 177 * <div class="wp-block-column"><!-- wp:paragraph --> 178 * <p>Left</p> 179 * <!-- /wp:paragraph --></div> 180 * <!-- /wp:column --> 181 * 182 * <!-- wp:column --> 183 * <div class="wp-block-column"><!-- wp:paragraph --> 184 * <p><strong>Middle</strong></p> 185 * <!-- /wp:paragraph --></div> 186 * <!-- /wp:column --> 187 * 188 * <!-- wp:column --> 189 * <div class="wp-block-column"></div> 190 * <!-- /wp:column --></div> 191 * <!-- /wp:columns --> 192 * ``` 193 * 194 * Parsing code: 195 * ```js 196 * import { parse } from '@wordpress/block-serialization-default-parser'; 197 * 198 * parse( post ) === [ 199 * { 200 * blockName: "core/columns", 201 * attrs: { 202 * columns: 3 203 * }, 204 * innerBlocks: [ 205 * { 206 * blockName: "core/column", 207 * attrs: null, 208 * innerBlocks: [ 209 * { 210 * blockName: "core/paragraph", 211 * attrs: null, 212 * innerBlocks: [], 213 * innerHTML: "\n<p>Left</p>\n" 214 * } 215 * ], 216 * innerHTML: '\n<div class="wp-block-column"></div>\n' 217 * }, 218 * { 219 * blockName: "core/column", 220 * attrs: null, 221 * innerBlocks: [ 222 * { 223 * blockName: "core/paragraph", 224 * attrs: null, 225 * innerBlocks: [], 226 * innerHTML: "\n<p><strong>Middle</strong></p>\n" 227 * } 228 * ], 229 * innerHTML: '\n<div class="wp-block-column"></div>\n' 230 * }, 231 * { 232 * blockName: "core/column", 233 * attrs: null, 234 * innerBlocks: [], 235 * innerHTML: '\n<div class="wp-block-column"></div>\n' 236 * } 237 * ], 238 * innerHTML: '\n<div class="wp-block-columns has-3-columns">\n\n\n\n</div>\n' 239 * } 240 * ]; 241 * ``` 242 * @return {Array} A block-based representation of the input HTML. 243 */ 244 245 246 const parse = doc => { 247 document = doc; 248 offset = 0; 249 output = []; 250 stack = []; 251 tokenizer.lastIndex = 0; 252 253 do {// twiddle our thumbs 254 } while (proceed()); 255 256 return output; 257 }; 258 259 function proceed() { 260 const next = nextToken(); 261 const [tokenType, blockName, attrs, startOffset, tokenLength] = next; 262 const stackDepth = stack.length; // we may have some HTML soup before the next block 263 264 const leadingHtmlStart = startOffset > offset ? offset : null; 265 266 switch (tokenType) { 267 case 'no-more-tokens': 268 // if not in a block then flush output 269 if (0 === stackDepth) { 270 addFreeform(); 271 return false; 272 } // Otherwise we have a problem 273 // This is an error 274 // we have options 275 // - treat it all as freeform text 276 // - assume an implicit closer (easiest when not nesting) 277 // for the easy case we'll assume an implicit closer 278 279 280 if (1 === stackDepth) { 281 addBlockFromStack(); 282 return false; 283 } // for the nested case where it's more difficult we'll 284 // have to assume that multiple closers are missing 285 // and so we'll collapse the whole stack piecewise 286 287 288 while (0 < stack.length) { 289 addBlockFromStack(); 290 } 291 292 return false; 293 294 case 'void-block': 295 // easy case is if we stumbled upon a void block 296 // in the top-level of the document 297 if (0 === stackDepth) { 298 if (null !== leadingHtmlStart) { 299 output.push(Freeform(document.substr(leadingHtmlStart, startOffset - leadingHtmlStart))); 300 } 301 302 output.push(Block(blockName, attrs, [], '', [])); 303 offset = startOffset + tokenLength; 304 return true; 305 } // otherwise we found an inner block 306 307 308 addInnerBlock(Block(blockName, attrs, [], '', []), startOffset, tokenLength); 309 offset = startOffset + tokenLength; 310 return true; 311 312 case 'block-opener': 313 // track all newly-opened blocks on the stack 314 stack.push(Frame(Block(blockName, attrs, [], '', []), startOffset, tokenLength, startOffset + tokenLength, leadingHtmlStart)); 315 offset = startOffset + tokenLength; 316 return true; 317 318 case 'block-closer': 319 // if we're missing an opener we're in trouble 320 // This is an error 321 if (0 === stackDepth) { 322 // we have options 323 // - assume an implicit opener 324 // - assume _this_ is the opener 325 // - give up and close out the document 326 addFreeform(); 327 return false; 328 } // if we're not nesting then this is easy - close the block 329 330 331 if (1 === stackDepth) { 332 addBlockFromStack(startOffset); 333 offset = startOffset + tokenLength; 334 return true; 335 } // otherwise we're nested and we have to close out the current 336 // block and add it as a innerBlock to the parent 337 338 339 const stackTop = stack.pop(); 340 const html = document.substr(stackTop.prevOffset, startOffset - stackTop.prevOffset); 341 stackTop.block.innerHTML += html; 342 stackTop.block.innerContent.push(html); 343 stackTop.prevOffset = startOffset + tokenLength; 344 addInnerBlock(stackTop.block, stackTop.tokenStart, stackTop.tokenLength, startOffset + tokenLength); 345 offset = startOffset + tokenLength; 346 return true; 347 348 default: 349 // This is an error 350 addFreeform(); 351 return false; 352 } 353 } 354 /** 355 * Parse JSON if valid, otherwise return null 356 * 357 * Note that JSON coming from the block comment 358 * delimiters is constrained to be an object 359 * and cannot be things like `true` or `null` 360 * 361 * @param {string} input JSON input string to parse 362 * @return {Object|null} parsed JSON if valid 363 */ 364 365 366 function parseJSON(input) { 367 try { 368 return JSON.parse(input); 369 } catch (e) { 370 return null; 371 } 372 } 373 374 function nextToken() { 375 // aye the magic 376 // we're using a single RegExp to tokenize the block comment delimiters 377 // we're also using a trick here because the only difference between a 378 // block opener and a block closer is the leading `/` before `wp:` (and 379 // a closer has no attributes). we can trap them both and process the 380 // match back in JavaScript to see which one it was. 381 const matches = tokenizer.exec(document); // we have no more tokens 382 383 if (null === matches) { 384 return ['no-more-tokens']; 385 } 386 387 const startedAt = matches.index; 388 const [match, closerMatch, namespaceMatch, nameMatch, attrsMatch 389 /* internal/unused */ 390 ,, voidMatch] = matches; 391 const length = match.length; 392 const isCloser = !!closerMatch; 393 const isVoid = !!voidMatch; 394 const namespace = namespaceMatch || 'core/'; 395 const name = namespace + nameMatch; 396 const hasAttrs = !!attrsMatch; 397 const attrs = hasAttrs ? parseJSON(attrsMatch) : {}; // This state isn't allowed 398 // This is an error 399 400 if (isCloser && (isVoid || hasAttrs)) {// we can ignore them since they don't hurt anything 401 // we may warn against this at some point or reject it 402 } 403 404 if (isVoid) { 405 return ['void-block', name, attrs, startedAt, length]; 406 } 407 408 if (isCloser) { 409 return ['block-closer', name, null, startedAt, length]; 410 } 411 412 return ['block-opener', name, attrs, startedAt, length]; 413 } 414 415 function addFreeform(rawLength) { 416 const length = rawLength ? rawLength : document.length - offset; 417 418 if (0 === length) { 419 return; 420 } 421 422 output.push(Freeform(document.substr(offset, length))); 423 } 424 425 function addInnerBlock(block, tokenStart, tokenLength, lastOffset) { 426 const parent = stack[stack.length - 1]; 427 parent.block.innerBlocks.push(block); 428 const html = document.substr(parent.prevOffset, tokenStart - parent.prevOffset); 429 430 if (html) { 431 parent.block.innerHTML += html; 432 parent.block.innerContent.push(html); 433 } 434 435 parent.block.innerContent.push(null); 436 parent.prevOffset = lastOffset ? lastOffset : tokenStart + tokenLength; 437 } 438 439 function addBlockFromStack(endOffset) { 440 const { 441 block, 442 leadingHtmlStart, 443 prevOffset, 444 tokenStart 445 } = stack.pop(); 446 const html = endOffset ? document.substr(prevOffset, endOffset - prevOffset) : document.substr(prevOffset); 447 448 if (html) { 449 block.innerHTML += html; 450 block.innerContent.push(html); 451 } 452 453 if (null !== leadingHtmlStart) { 454 output.push(Freeform(document.substr(leadingHtmlStart, tokenStart - leadingHtmlStart))); 455 } 456 457 output.push(block); 458 } 459 460 461 /***/ }) 462 463 /******/ });