[{"data":1,"prerenderedAt":14292},["ShallowReactive",2],{"footer-articles":3,"/blog/building-a-platformer-game-with-vue-and-pixijs":8021,"related-/blog/building-a-platformer-game-with-vue-and-pixijs":10651},[4,3436,4750,6528],{"id":5,"title":6,"body":7,"category":3425,"date":3426,"description":3427,"draft":3428,"extension":3429,"image":3430,"meta":3431,"navigation":158,"path":3432,"seo":3433,"stem":3434,"tighten":3428,"__hash__":3435},"blog/blog/building-a-platformer-game-with-vue-and-pixijs.md","Building a Platformer Game with Vue and PixiJS",{"type":8,"value":9,"toc":3408},"minimark",[10,14,27,34,50,53,56,64,69,72,79,82,88,99,103,110,187,195,201,208,213,231,234,238,241,248,251,258,269,279,425,444,447,453,459,464,467,473,477,480,487,493,503,510,513,523,526,530,536,549,560,673,682,695,705,709,718,725,922,928,1221,1243,1246,1268,1277,1280,1285,1296,1301,1304,1308,1323,1338,1349,1431,1453,1460,1464,1471,1474,1507,1510,1628,1631,1639,1642,1645,1654,1668,1673,1683,1687,1698,1701,1898,1904,2069,2072,2079,2082,2170,2182,2189,2240,2250,2254,2257,2263,2270,2294,2305,2480,2500,2505,2512,2814,2820,2824,2827,2834,3044,3055,3058,3090,3093,3229,3245,3251,3270,3279,3288,3291,3295,3298,3367,3379,3383,3393,3401,3404],[11,12,13],"p",{},"Like many of you, I spend my days building web apps: forms, dashboards, a couple of animations here and there.",[11,15,16,17,21,22,26],{},"Then, a few days ago, I watched a YouTube video of someone building a ",[18,19,20],"strong",{},"browser game",": a tiny platformer. You control a blue box, jumping over boxes and avoiding cannon shots. A few minutes into the tutorial, I realized: ",[23,24,25],"em",{},"I have no idea how this works."," Loops, physics, collision math... it all sounded familiar, but very different from the reactive world I'm used to.",[11,28,29,30,33],{},"And a question popped up in my head: ",[18,31,32],{},"can I build a platformer game using Vue?"," Can my Vue skills help me in my game dev journey? Luckily, the answer to both questions is yes.",[11,35,36,37,40,41,44,45,49],{},"I built a platformer game with ",[18,38,39],{},"Vue 3"," and ",[18,42,43],{},"PixiJS",", and almost every piece of it mapped to something you already do in Vue: a game loop is a watcher, game state is a ",[46,47,48],"code",{},"ref",", the scene is a component tree, and so on.",[11,51,52],{},"So join me to discover how to do it! We'll walk through the seven ideas that make up every 2D game, each one paired with its Vue equivalent, with live demos you can poke at as we go.",[11,54,55],{},"Click this iframe to play: use the arrow keys to move and the spacebar to jump.",[57,58],"iframe",{"src":59,"title":60,"loading":61,"scrolling":62,"style":63},"https://demo-vue-platformer.netlify.app/","A little platformer","lazy","no","width:100%;aspect-ratio:960/640;border:0;border-radius:16px;margin:1.5rem 0;display:block",[65,66,68],"h2",{"id":67},"the-game-loop","The Game Loop",[11,70,71],{},"A single-page application re-renders only when the state changes. You want to minimize the number of times you repaint elements in the DOM. That's why we use the virtual DOM and other techniques to keep patching to a minimum. Touching the DOM is expensive, so we do it as little as possible.",[11,73,74,75,78],{},"In a game, that's totally different. Most likely you won't be dealing with DOM elements, but with a single ",[46,76,77],{},"\u003Ccanvas>",". The whole game takes place inside that canvas. The rendering model is completely different: the scene gets repainted continuously. At 60 frames per second, the canvas is redrawn 60 times every second. Each frame is a fresh snapshot, and when those snapshots come quickly enough, our eyes perceive motion.",[11,80,81],{},"But wait, isn't that... a lot? Recomputing everything, all the time, and repainting the whole scene many times per second? Well, yeah, that can sound like overkill in our DOM world, since we're used to waiting for specific events (a keystroke, a network response) and only re-rendering what we need.",[11,83,84,85,87],{},"But inside the ",[46,86,77],{},", things are different. It doesn't have a tree of expensive-to-patch elements. Instead, it's just a blank canvas that we can paint, clear, and repaint very quickly. This can still sound like a lot for a slow, turn-based game with only a few elements on screen. But once you introduce physics, collisions, or dozens of moving bullets, it makes sense to calculate where everything should be and paint it onto the canvas every frame.",[11,89,90,91,94,95,98],{},"And that's the game loop! The mindset shift is from ",[23,92,93],{},"event-driven"," to ",[23,96,97],{},"loop-driven",". Once that clicks, you're ready for game development.",[65,100,102],{"id":101},"games-in-plain-javascript","Games in Plain JavaScript",[11,104,105,106,109],{},"In the browser, that loop is ",[46,107,108],{},"requestAnimationFrame",":",[111,112,117],"pre",{"className":113,"code":114,"language":115,"meta":116,"style":116},"language-js shiki shiki-themes monokai","function loop() {\n  update()\n  render()\n\n  requestAnimationFrame(loop)\n}\n\nrequestAnimationFrame(loop)\n","js","",[46,118,119,136,145,153,160,169,175,180],{"__ignoreMap":116},[120,121,124,128,132],"span",{"class":122,"line":123},"line",1,[120,125,127],{"class":126},"sOx1s","function",[120,129,131],{"class":130},"sHkqI"," loop",[120,133,135],{"class":134},"sCdxs","() {\n",[120,137,139,142],{"class":122,"line":138},2,[120,140,141],{"class":130},"  update",[120,143,144],{"class":134},"()\n",[120,146,148,151],{"class":122,"line":147},3,[120,149,150],{"class":130},"  render",[120,152,144],{"class":134},[120,154,156],{"class":122,"line":155},4,[120,157,159],{"emptyLinePlaceholder":158},true,"\n",[120,161,163,166],{"class":122,"line":162},5,[120,164,165],{"class":130},"  requestAnimationFrame",[120,167,168],{"class":134},"(loop)\n",[120,170,172],{"class":122,"line":171},6,[120,173,174],{"class":134},"}\n",[120,176,178],{"class":122,"line":177},7,[120,179,159],{"emptyLinePlaceholder":158},[120,181,183,185],{"class":122,"line":182},8,[120,184,108],{"class":130},[120,186,168],{"class":134},[11,188,189,191,192],{},[46,190,108],{}," is a browser API specifically designed for animations. Instead of asking JavaScript to run your code as fast as possible, you're asking the browser: ",[23,193,194],{},"\"Call this function right before you paint the next frame.\"",[11,196,197,198,200],{},"This is an important distinction. Browsers already have their own rendering pipeline. Roughly 60 times per second (or more on high-refresh-rate displays), they calculate layouts, paint pixels, and display a new frame on the screen. ",[46,199,108],{}," lets your code hook directly into that process, so your updates happen at exactly the right moment.",[11,202,203,204,207],{},"You could imagine replacing it with a ",[46,205,206],{},"setInterval"," running every 16 milliseconds, but that has a few problems. Timers aren't synchronized with the browser's rendering cycle, so they can fire too early, too late, or even while the browser is still busy painting the previous frame. That can lead to choppy animations and unnecessary work.",[11,209,210,212],{},[46,211,108],{}," is also smarter than a timer. If the tab is hidden or minimized, the browser will automatically pause or greatly reduce how often your callback runs, saving CPU and battery life.",[11,214,215,216,218,219,222,223,226,227,230],{},"One thing to notice is that ",[46,217,108],{}," only schedules ",[18,220,221],{},"one"," callback. It doesn't automatically repeat. That's why the last line inside our ",[46,224,225],{},"loop()"," function is another call to ",[46,228,229],{},"requestAnimationFrame(loop)",". Each frame schedules the next one, creating an endless cycle that continues until we stop requesting new frames.",[11,232,233],{},"That's the foundation of nearly every browser game. Every frame, the browser says, \"It's time to draw again,\" your game updates its world, renders the result, and asks to be called back for the next frame.",[65,235,237],{"id":236},"time-vs-frames","Time vs. Frames",[11,239,240],{},"There's one subtle problem, though.",[11,242,243,244,247],{},"We just said the browser calls our loop before every frame. But what exactly is ",[23,245,246],{},"a frame","?",[11,249,250],{},"If every screen refreshed exactly 60 times per second, life would be easy. We could move the player 5 pixels every frame and know they'd travel 300 pixels every second. But... the real world is messier.",[11,252,253,254,257],{},"Some players have 144Hz monitors. Others are on older laptops that can barely manage 30 FPS. Sometimes the browser gets busy for a moment and skips a frame. If we move everything by a fixed amount ",[18,255,256],{},"per frame",", then the game's speed becomes tied to the machine running it. Faster computers make the game run faster, slower ones make it run in slow motion.",[11,259,260,261,264,265,268],{},"What we really care about isn't ",[18,262,263],{},"how many frames"," have passed. We care about ",[18,266,267],{},"how much time"," has passed.",[11,270,271,272,275,276,278],{},"And that's where ",[18,273,274],{},"delta time"," comes in. Delta time is simply the amount of time that elapsed since the previous frame. Luckily, ",[46,277,108],{}," already gives us the current timestamp every time it calls our callback. By comparing it to the previous timestamp, we can calculate exactly how much time has passed:",[111,280,282],{"className":113,"code":281,"language":115,"meta":116,"style":116},"let previousTime = 0\n\nfunction loop(currentTime) {\n  const elapsed = currentTime - previousTime\n  const deltaTime = elapsed / (1000 / 60)\n  previousTime = currentTime\n\n  update(deltaTime)\n  render()\n\n  requestAnimationFrame(loop)\n}\n\nrequestAnimationFrame(loop)\n",[46,283,284,300,304,320,339,368,378,382,389,396,401,408,413,418],{"__ignoreMap":116},[120,285,286,289,292,296],{"class":122,"line":123},[120,287,288],{"class":126},"let",[120,290,291],{"class":134}," previousTime ",[120,293,295],{"class":294},"s8I7P","=",[120,297,299],{"class":298},"s7s5_"," 0\n",[120,301,302],{"class":122,"line":138},[120,303,159],{"emptyLinePlaceholder":158},[120,305,306,308,310,313,317],{"class":122,"line":147},[120,307,127],{"class":126},[120,309,131],{"class":130},[120,311,312],{"class":134},"(",[120,314,316],{"class":315},"sW0Xf","currentTime",[120,318,319],{"class":134},") {\n",[120,321,322,325,328,330,333,336],{"class":122,"line":155},[120,323,324],{"class":126},"  const",[120,326,327],{"class":134}," elapsed ",[120,329,295],{"class":294},[120,331,332],{"class":134}," currentTime ",[120,334,335],{"class":294},"-",[120,337,338],{"class":134}," previousTime\n",[120,340,341,343,346,348,350,353,356,359,362,365],{"class":122,"line":162},[120,342,324],{"class":126},[120,344,345],{"class":134}," deltaTime ",[120,347,295],{"class":294},[120,349,327],{"class":134},[120,351,352],{"class":294},"/",[120,354,355],{"class":134}," (",[120,357,358],{"class":298},"1000",[120,360,361],{"class":294}," /",[120,363,364],{"class":298}," 60",[120,366,367],{"class":134},")\n",[120,369,370,373,375],{"class":122,"line":171},[120,371,372],{"class":134},"  previousTime ",[120,374,295],{"class":294},[120,376,377],{"class":134}," currentTime\n",[120,379,380],{"class":122,"line":177},[120,381,159],{"emptyLinePlaceholder":158},[120,383,384,386],{"class":122,"line":182},[120,385,141],{"class":130},[120,387,388],{"class":134},"(deltaTime)\n",[120,390,392,394],{"class":122,"line":391},9,[120,393,150],{"class":130},[120,395,144],{"class":134},[120,397,399],{"class":122,"line":398},10,[120,400,159],{"emptyLinePlaceholder":158},[120,402,404,406],{"class":122,"line":403},11,[120,405,165],{"class":130},[120,407,168],{"class":134},[120,409,411],{"class":122,"line":410},12,[120,412,174],{"class":134},[120,414,416],{"class":122,"line":415},13,[120,417,159],{"emptyLinePlaceholder":158},[120,419,421,423],{"class":122,"line":420},14,[120,422,108],{"class":130},[120,424,168],{"class":134},[11,426,427,428,431,432,435,436,439,440,443],{},"Here I'm also normalizing the value to a 60 FPS baseline. That means ",[46,429,430],{},"deltaTime"," is ",[46,433,434],{},"1.0"," at 60 FPS, ",[46,437,438],{},"2.0"," at 30 FPS, and ",[46,441,442],{},"0.5"," at 120 FPS.",[11,445,446],{},"Instead of saying, \"move 5 pixels every frame,\" we say, \"move 5 pixels, scaled by how long this frame took.\"",[11,448,449,450,452],{},"Multiply your movement by ",[46,451,430],{},", and suddenly your game behaves the same everywhere. The player covers the same distance per second whether the browser renders 30 frames or 144. Motion is based on real time, not frame count.",[11,454,455,456,458],{},"The demo below is a bare ",[46,457,108],{}," loop, running as you read this. Nothing is animating the count; the loop just keeps coming around.",[57,460],{"src":461,"title":462,"loading":61,"scrolling":62,"style":463},"/demos/building-a-platformer-game-with-vue-and-pixijs/game-loop.html","A bare game loop counting frames","width:100%;height:200px;border:0;border-radius:16px;margin:1.5rem 0;display:block",[11,465,466],{},"That counter is the whole engine underneath. The loop comes back around roughly 60 times a second whether your state changed or not. Every time it does, your game gets another opportunity to update the world, draw a new frame, and ask the browser to call it again.",[11,468,469,470],{},"That's the fundamental shift from web development to game development. Instead of waiting for something to happen and reacting to it, you're continuously simulating the world. The loop never stops. It just keeps asking, ",[23,471,472],{},"\"What should the next frame look like?\"",[65,474,476],{"id":475},"pixi-for-games-and-more","Pixi, for Games and More",[11,478,479],{},"So far, we've only been incrementing a counter. Drawing a game is a very different challenge.",[11,481,482,483,486],{},"Sure, you ",[23,484,485],{},"could"," use the Canvas 2D API directly, or even WebGL if you're feeling adventurous. But it doesn't take long before you're worrying about batching draw calls, managing textures, applying transforms, and solving a dozen rendering problems that have nothing to do with the game you actually want to build.",[11,488,489,490,492],{},"That's the layer ",[18,491,43],{}," takes care of.",[11,494,495,496,498,499,502],{},"PixiJS is a high-performance 2D rendering engine. It renders everything into an HTML ",[46,497,77],{},", using WebGL under the hood whenever possible, while exposing a friendly scene graph made up of sprites, containers, text, graphics, and other display objects. You tell Pixi ",[23,500,501],{},"what"," to draw; it figures out the fastest way to draw it.",[11,504,505,506,509],{},"Notice what Pixi ",[18,507,508],{},"doesn't"," do: It doesn't know what gravity is, it doesn't know how your enemies move, or whether your player won the game. That's our job. In other words, we have the control.",[11,511,512],{},"Pixi's responsibility is rendering, and it does that extremely well. That's why you'll find it powering not just games, but also interactive websites, visualizations, educational tools, and data-heavy interfaces.",[11,514,515,516,519,520,522],{},"It also provides its own ",[18,517,518],{},"ticker",", which is essentially a wrapper around ",[46,521,108],{},". Instead of writing and managing the game loop ourselves, we just register a callback and Pixi calls it every frame.",[11,524,525],{},"And once Vue enters the picture, we barely even write that.",[65,527,529],{"id":528},"vue-custom-renderer-for-pixi","Vue Custom Renderer for Pixi",[11,531,532,533,535],{},"Here's something you might not know: Vue is not limited to the DOM. Of course, rendering websites and web applications is by far its most common use case, but Vue's renderer is completely independent from its reactivity system. That means we can define our own custom renderer and use the power of Vue reactivity to render to entirely different environments, like a terminal or (you guessed it) inside a ",[46,534,77],{},".",[11,537,538,539,548],{},"That's where ",[540,541,545],"a",{"href":542,"rel":543},"https://vue3-pixi.vercel.app/",[544],"nofollow",[46,546,547],{},"vue3-pixi"," comes in. It's a custom Vue renderer for PixiJS. Instead of mixing Vue with imperative Pixi code, we can build the entire game using components, props, emits, and reactivity: the same tools we already use every day.",[11,550,551,552,555,556,559],{},"It also exposes the Pixi ticker as a composable called ",[46,553,554],{},"onTick",". You can think of it as a ",[46,557,558],{},"watchEffect"," that doesn't wait for dependencies: it simply runs every frame.",[111,561,565],{"className":562,"code":563,"language":564,"meta":116,"style":116},"language-ts shiki shiki-themes monokai","import { ref } from 'vue'\nimport { onTick } from 'vue3-pixi'\n\nconst x = ref(0)\nconst speed = 2\n\nonTick(({ deltaTime }) => {\n  x.value += speed * deltaTime\n})\n","ts",[46,566,567,582,594,598,618,630,634,652,668],{"__ignoreMap":116},[120,568,569,572,575,578],{"class":122,"line":123},[120,570,571],{"class":294},"import",[120,573,574],{"class":134}," { ref } ",[120,576,577],{"class":294},"from",[120,579,581],{"class":580},"s_Ekj"," 'vue'\n",[120,583,584,586,589,591],{"class":122,"line":138},[120,585,571],{"class":294},[120,587,588],{"class":134}," { onTick } ",[120,590,577],{"class":294},[120,592,593],{"class":580}," 'vue3-pixi'\n",[120,595,596],{"class":122,"line":147},[120,597,159],{"emptyLinePlaceholder":158},[120,599,600,603,606,608,611,613,616],{"class":122,"line":155},[120,601,602],{"class":126},"const",[120,604,605],{"class":134}," x ",[120,607,295],{"class":294},[120,609,610],{"class":130}," ref",[120,612,312],{"class":134},[120,614,615],{"class":298},"0",[120,617,367],{"class":134},[120,619,620,622,625,627],{"class":122,"line":162},[120,621,602],{"class":126},[120,623,624],{"class":134}," speed ",[120,626,295],{"class":294},[120,628,629],{"class":298}," 2\n",[120,631,632],{"class":122,"line":171},[120,633,159],{"emptyLinePlaceholder":158},[120,635,636,638,641,643,646,649],{"class":122,"line":177},[120,637,554],{"class":130},[120,639,640],{"class":134},"(({ ",[120,642,430],{"class":315},[120,644,645],{"class":134}," }) ",[120,647,648],{"class":126},"=>",[120,650,651],{"class":134}," {\n",[120,653,654,657,660,662,665],{"class":122,"line":182},[120,655,656],{"class":134},"  x.value ",[120,658,659],{"class":294},"+=",[120,661,624],{"class":134},[120,663,664],{"class":294},"*",[120,666,667],{"class":134}," deltaTime\n",[120,669,670],{"class":122,"line":391},[120,671,672],{"class":134},"})\n",[11,674,675,676,678,679,681],{},"Notice how ",[46,677,430],{}," is simply handed to us now. The ticker has already measured the time between frames, so all the timestamp bookkeeping from our vanilla ",[46,680,108],{}," loop disappears. For now, just think of speed as a constant movement rate. We'll make this more realistic when we get to physics.",[11,683,684,685,687,688,690,691,694],{},"In the real game we'd also cap ",[46,686,430],{},". If the browser pauses the loop (for example, because you switch tabs), the next frame arrives with a huge ",[46,689,430],{},". Without a limit, ",[46,692,693],{},"x += speed * deltaTime"," would make the player leap across the level in a single update.",[11,696,697,698,701,702,704],{},"One important detail: the whole game runs on a ",[18,699,700],{},"single"," ",[46,703,554],{},". Every snippet below is just a slice of that one callback, executed in order. There's no second loop applying gravity twice.",[65,706,708],{"id":707},"smells-like-vue-spirit","Smells Like Vue Spirit",[11,710,711,712,714,715],{},"With ",[46,713,547],{},", game code starts looking... ",[18,716,717],{},"like Vue.",[11,719,720,721,724],{},"The root is an ",[46,722,723],{},"\u003CApplication>",", which creates the canvas and starts the ticker for you.",[111,726,730],{"className":727,"code":728,"language":729,"meta":116,"style":116},"language-vue shiki shiki-themes monokai","\u003Cscript setup lang=\"ts\">\nimport { Application } from 'vue3-pixi'\nimport { WORLD } from './game/level'\nimport World from './components/World.vue'\n\u003C/script>\n\n\u003Ctemplate>\n  \u003CApplication\n    :width=\"WORLD.w\"\n    :height=\"WORLD.h\"\n    :background=\"0xbfe9f7\"\n    :antialias=\"true\"\n  >\n    \u003CWorld />\n  \u003C/Application>\n\u003C/template>\n","vue",[46,731,732,754,765,777,789,798,802,811,819,838,854,870,886,891,902,913],{"__ignoreMap":116},[120,733,734,737,740,743,746,748,751],{"class":122,"line":123},[120,735,736],{"class":134},"\u003C",[120,738,739],{"class":294},"script",[120,741,742],{"class":130}," setup",[120,744,745],{"class":130}," lang",[120,747,295],{"class":134},[120,749,750],{"class":580},"\"ts\"",[120,752,753],{"class":134},">\n",[120,755,756,758,761,763],{"class":122,"line":138},[120,757,571],{"class":294},[120,759,760],{"class":134}," { Application } ",[120,762,577],{"class":294},[120,764,593],{"class":580},[120,766,767,769,772,774],{"class":122,"line":147},[120,768,571],{"class":294},[120,770,771],{"class":134}," { WORLD } ",[120,773,577],{"class":294},[120,775,776],{"class":580}," './game/level'\n",[120,778,779,781,784,786],{"class":122,"line":155},[120,780,571],{"class":294},[120,782,783],{"class":134}," World ",[120,785,577],{"class":294},[120,787,788],{"class":580}," './components/World.vue'\n",[120,790,791,794,796],{"class":122,"line":162},[120,792,793],{"class":134},"\u003C/",[120,795,739],{"class":294},[120,797,753],{"class":134},[120,799,800],{"class":122,"line":171},[120,801,159],{"emptyLinePlaceholder":158},[120,803,804,806,809],{"class":122,"line":177},[120,805,736],{"class":134},[120,807,808],{"class":294},"template",[120,810,753],{"class":134},[120,812,813,816],{"class":122,"line":182},[120,814,815],{"class":134},"  \u003C",[120,817,818],{"class":294},"Application\n",[120,820,821,824,827,829,832,835],{"class":122,"line":391},[120,822,823],{"class":134},"    :",[120,825,826],{"class":130},"width",[120,828,295],{"class":134},[120,830,831],{"class":134},"\"",[120,833,834],{"class":134},"WORLD.w",[120,836,837],{"class":134},"\"\n",[120,839,840,842,845,847,849,852],{"class":122,"line":398},[120,841,823],{"class":134},[120,843,844],{"class":130},"height",[120,846,295],{"class":134},[120,848,831],{"class":134},[120,850,851],{"class":134},"WORLD.h",[120,853,837],{"class":134},[120,855,856,858,861,863,865,868],{"class":122,"line":403},[120,857,823],{"class":134},[120,859,860],{"class":130},"background",[120,862,295],{"class":134},[120,864,831],{"class":134},[120,866,867],{"class":298},"0xbfe9f7",[120,869,837],{"class":134},[120,871,872,874,877,879,881,884],{"class":122,"line":410},[120,873,823],{"class":134},[120,875,876],{"class":130},"antialias",[120,878,295],{"class":134},[120,880,831],{"class":134},[120,882,883],{"class":298},"true",[120,885,837],{"class":134},[120,887,888],{"class":122,"line":415},[120,889,890],{"class":134},"  >\n",[120,892,893,896,899],{"class":122,"line":420},[120,894,895],{"class":134},"    \u003C",[120,897,898],{"class":294},"World",[120,900,901],{"class":134}," />\n",[120,903,905,908,911],{"class":122,"line":904},15,[120,906,907],{"class":134},"  \u003C/",[120,909,910],{"class":294},"Application",[120,912,753],{"class":134},[120,914,916,918,920],{"class":122,"line":915},16,[120,917,793],{"class":134},[120,919,808],{"class":294},[120,921,753],{"class":134},[11,923,924,927],{},[46,925,926],{},"\u003CWorld>"," is just another Vue component. Its template describes the current stage:",[111,929,931],{"className":727,"code":930,"language":729,"meta":116,"style":116},"\u003Ctemplate>\n  \u003Ccontainer>\n    \u003CBackground />\n\n    \u003CPlatform\n      v-for=\"(p, i) in PLATFORMS\"\n      :key=\"`p${i}`\"\n      :rect=\"p\"\n    />\n    \u003CSaw v-for=\"(s, i) in SAWS\" :key=\"`s${i}`\" :pos=\"s\" />\n    \u003CGoal />\n\n    \u003CPlayer :active=\"active\" @hit=\"onHit\" @win=\"onWin\" />\n\n    \u003CHud :lives=\"lives\" :won=\"won\" />\n  \u003C/container>\n\u003C/template>\n",[46,932,933,941,950,959,963,970,990,1019,1034,1039,1101,1110,1114,1165,1169,1204,1212],{"__ignoreMap":116},[120,934,935,937,939],{"class":122,"line":123},[120,936,736],{"class":134},[120,938,808],{"class":294},[120,940,753],{"class":134},[120,942,943,945,948],{"class":122,"line":138},[120,944,815],{"class":134},[120,946,947],{"class":294},"container",[120,949,753],{"class":134},[120,951,952,954,957],{"class":122,"line":147},[120,953,895],{"class":134},[120,955,956],{"class":294},"Background",[120,958,901],{"class":134},[120,960,961],{"class":122,"line":155},[120,962,159],{"emptyLinePlaceholder":158},[120,964,965,967],{"class":122,"line":162},[120,966,895],{"class":134},[120,968,969],{"class":294},"Platform\n",[120,971,972,975,977,979,982,985,988],{"class":122,"line":171},[120,973,974],{"class":294},"      v-for",[120,976,295],{"class":134},[120,978,831],{"class":134},[120,980,981],{"class":134},"(p, i) ",[120,983,984],{"class":294},"in",[120,986,987],{"class":134}," PLATFORMS",[120,989,837],{"class":134},[120,991,992,995,998,1000,1002,1005,1008,1011,1014,1017],{"class":122,"line":177},[120,993,994],{"class":134},"      :",[120,996,997],{"class":130},"key",[120,999,295],{"class":134},[120,1001,831],{"class":134},[120,1003,1004],{"class":580},"`p",[120,1006,1007],{"class":294},"${",[120,1009,1010],{"class":134},"i",[120,1012,1013],{"class":294},"}",[120,1015,1016],{"class":580},"`",[120,1018,837],{"class":134},[120,1020,1021,1023,1026,1028,1030,1032],{"class":122,"line":182},[120,1022,994],{"class":134},[120,1024,1025],{"class":130},"rect",[120,1027,295],{"class":134},[120,1029,831],{"class":134},[120,1031,11],{"class":134},[120,1033,837],{"class":134},[120,1035,1036],{"class":122,"line":391},[120,1037,1038],{"class":134},"    />\n",[120,1040,1041,1043,1046,1049,1051,1053,1056,1058,1061,1063,1066,1068,1070,1072,1075,1077,1079,1081,1083,1085,1087,1090,1092,1094,1097,1099],{"class":122,"line":398},[120,1042,895],{"class":134},[120,1044,1045],{"class":294},"Saw",[120,1047,1048],{"class":294}," v-for",[120,1050,295],{"class":134},[120,1052,831],{"class":134},[120,1054,1055],{"class":134},"(s, i) ",[120,1057,984],{"class":294},[120,1059,1060],{"class":134}," SAWS",[120,1062,831],{"class":134},[120,1064,1065],{"class":134}," :",[120,1067,997],{"class":130},[120,1069,295],{"class":134},[120,1071,831],{"class":134},[120,1073,1074],{"class":580},"`s",[120,1076,1007],{"class":294},[120,1078,1010],{"class":134},[120,1080,1013],{"class":294},[120,1082,1016],{"class":580},[120,1084,831],{"class":134},[120,1086,1065],{"class":134},[120,1088,1089],{"class":130},"pos",[120,1091,295],{"class":134},[120,1093,831],{"class":134},[120,1095,1096],{"class":134},"s",[120,1098,831],{"class":134},[120,1100,901],{"class":134},[120,1102,1103,1105,1108],{"class":122,"line":403},[120,1104,895],{"class":134},[120,1106,1107],{"class":294},"Goal",[120,1109,901],{"class":134},[120,1111,1112],{"class":122,"line":410},[120,1113,159],{"emptyLinePlaceholder":158},[120,1115,1116,1118,1121,1123,1126,1128,1130,1132,1134,1137,1140,1142,1144,1147,1149,1151,1154,1156,1158,1161,1163],{"class":122,"line":415},[120,1117,895],{"class":134},[120,1119,1120],{"class":294},"Player",[120,1122,1065],{"class":134},[120,1124,1125],{"class":130},"active",[120,1127,295],{"class":134},[120,1129,831],{"class":134},[120,1131,1125],{"class":134},[120,1133,831],{"class":134},[120,1135,1136],{"class":134}," @",[120,1138,1139],{"class":130},"hit",[120,1141,295],{"class":134},[120,1143,831],{"class":134},[120,1145,1146],{"class":134},"onHit",[120,1148,831],{"class":134},[120,1150,1136],{"class":134},[120,1152,1153],{"class":130},"win",[120,1155,295],{"class":134},[120,1157,831],{"class":134},[120,1159,1160],{"class":134},"onWin",[120,1162,831],{"class":134},[120,1164,901],{"class":134},[120,1166,1167],{"class":122,"line":420},[120,1168,159],{"emptyLinePlaceholder":158},[120,1170,1171,1173,1176,1178,1181,1183,1185,1187,1189,1191,1194,1196,1198,1200,1202],{"class":122,"line":904},[120,1172,895],{"class":134},[120,1174,1175],{"class":294},"Hud",[120,1177,1065],{"class":134},[120,1179,1180],{"class":130},"lives",[120,1182,295],{"class":134},[120,1184,831],{"class":134},[120,1186,1180],{"class":134},[120,1188,831],{"class":134},[120,1190,1065],{"class":134},[120,1192,1193],{"class":130},"won",[120,1195,295],{"class":134},[120,1197,831],{"class":134},[120,1199,1193],{"class":134},[120,1201,831],{"class":134},[120,1203,901],{"class":134},[120,1205,1206,1208,1210],{"class":122,"line":915},[120,1207,907],{"class":134},[120,1209,947],{"class":294},[120,1211,753],{"class":134},[120,1213,1215,1217,1219],{"class":122,"line":1214},17,[120,1216,793],{"class":134},[120,1218,808],{"class":294},[120,1220,753],{"class":134},[11,1222,1223,1224,1227,1228,1231,1232,40,1235,1238,1239,1242],{},"We are using ",[46,1225,1226],{},"v-for"," to render platforms and saws, and we have a ",[46,1229,1230],{},"\u003CPlayer>"," emitting ",[46,1233,1234],{},"@hit",[46,1236,1237],{},"@win"," to its parent, just like a form component emitting ",[46,1240,1241],{},"@submit",". A game built with components, passing props down and events up.",[11,1244,1245],{},"Suddenly, it feels familiar again!",[11,1247,1248,1249,1251,1252,1255,1256,1259,1260,1263,1264,1267],{},"But remember: this uses a custom Vue renderer, so when you write ",[46,1250,1230],{},", ",[46,1253,1254],{},"\u003CHud>",", or ",[46,1257,1258],{},"\u003CGoal>",", you're ultimately creating a scene graph of PixiJS objects instead of DOM elements. We don't have access to classic ",[46,1261,1262],{},"\u003Cbutton>"," or ",[46,1265,1266],{},"\u003Cinput>"," in the canvas world (HTML in canvas is technically possible... but that's a topic for another day).",[11,1269,1270,1271,40,1273,1276],{},"Everything else is the Vue you already know: directives like ",[46,1272,1226],{},[46,1274,1275],{},"v-if",", props, refs, components, and emits. All of it works exactly as it does in a web app.",[11,1278,1279],{},"However, there's a couple of gotchas!",[1281,1282,1284],"h3",{"id":1283},"gotcha-1-containers","Gotcha 1: Containers",[11,1286,1287,1288,1291,1292,1295],{},"In a web app, a parent contains its children through CSS layout. In a scene graph, a child's position is ",[18,1289,1290],{},"relative to its parent",". Move a ",[46,1293,1294],{},"\u003Ccontainer>"," and everything inside it moves too. That's how you slide an entire level, or move an enemy and its health bar together as a single unit.",[57,1297],{"src":1298,"title":1299,"loading":61,"scrolling":62,"style":1300},"/demos/building-a-platformer-game-with-vue-and-pixijs/scene-graph.html","A parent container moving its children together","width:100%;height:250px;border:0;border-radius:16px;margin:1.5rem 0;display:block",[11,1302,1303],{},"So the thing painting pixels to a canvas is a Vue component tree. You already know how to build one of those.",[1281,1305,1307],{"id":1306},"gotcha-2-down-is-positive","Gotcha 2: Down Is Positive?",[11,1309,1310,1311,1314,1315,1318,1319,1322],{},"On a screen, ",[18,1312,1313],{},"Y goes down."," The origin ",[46,1316,1317],{},"(0, 0)"," sits in the top-left corner, and a bigger Y value means lower on the screen. School-math Y went ",[23,1320,1321],{},"up",", which is exactly why this trips people up.",[11,1324,1325,1326,1329,1330,1333,1334,1337],{},"It's the same as CSS ",[46,1327,1328],{},"top",": a ",[46,1331,1332],{},"top: 200px"," element sits below a ",[46,1335,1336],{},"top: 0"," one.",[11,1339,1340,1341,1344,1345,1348],{},"So, to make the player ",[18,1342,1343],{},"jump",", you set its vertical velocity to a ",[23,1346,1347],{},"negative"," number, because up means heading toward smaller Y:",[111,1350,1352],{"className":562,"code":1351,"language":564,"meta":116,"style":116},"// jump velocity is NEGATIVE (up is smaller Y)\nexport const PHYS = {\n  speed: 3.4,\n  gravity: 0.55,\n  jump: -11.5,\n  pw: 28,\n  ph: 28,\n}\n",[46,1353,1354,1360,1375,1386,1396,1408,1418,1427],{"__ignoreMap":116},[120,1355,1356],{"class":122,"line":123},[120,1357,1359],{"class":1358},"snpHw","// jump velocity is NEGATIVE (up is smaller Y)\n",[120,1361,1362,1365,1368,1371,1373],{"class":122,"line":138},[120,1363,1364],{"class":294},"export",[120,1366,1367],{"class":126}," const",[120,1369,1370],{"class":134}," PHYS ",[120,1372,295],{"class":294},[120,1374,651],{"class":134},[120,1376,1377,1380,1383],{"class":122,"line":147},[120,1378,1379],{"class":134},"  speed: ",[120,1381,1382],{"class":298},"3.4",[120,1384,1385],{"class":134},",\n",[120,1387,1388,1391,1394],{"class":122,"line":155},[120,1389,1390],{"class":134},"  gravity: ",[120,1392,1393],{"class":298},"0.55",[120,1395,1385],{"class":134},[120,1397,1398,1401,1403,1406],{"class":122,"line":162},[120,1399,1400],{"class":134},"  jump: ",[120,1402,335],{"class":294},[120,1404,1405],{"class":298},"11.5",[120,1407,1385],{"class":134},[120,1409,1410,1413,1416],{"class":122,"line":171},[120,1411,1412],{"class":134},"  pw: ",[120,1414,1415],{"class":298},"28",[120,1417,1385],{"class":134},[120,1419,1420,1423,1425],{"class":122,"line":177},[120,1421,1422],{"class":134},"  ph: ",[120,1424,1415],{"class":298},[120,1426,1385],{"class":134},[120,1428,1429],{"class":122,"line":182},[120,1430,174],{"class":134},[11,1432,1433,1434,1251,1437,1440,1441,1443,1444,1447,1448,1450,1451,535],{},"Throughout these snippets I refer to the constants as bare ",[46,1435,1436],{},"speed",[46,1438,1439],{},"gravity",", and ",[46,1442,1343],{}," (the game destructures them out of ",[46,1445,1446],{},"PHYS","). So when you see a lone ",[46,1449,1439],{}," later, that's this ",[46,1452,1393],{},[11,1454,1455,1456,1459],{},"Gravity, meanwhile, is ",[23,1457,1458],{},"positive",", constantly pulling Y back down. Up is negative, down is positive.",[1281,1461,1463],{"id":1462},"gotcha-3-physics","Gotcha 3: Physics!",[11,1465,1466,1467,1470],{},"I'm not great at math. That's why talking about a ",[23,1468,1469],{},"game physics engine"," kind of scares me. Thing is: physics for a platformer just comes down to two rules and middle-school arithmetic, run 60 times a second. Let's break it down.",[11,1472,1473],{},"You track two numbers per moving object:",[1475,1476,1477,1494],"ul",{},[1478,1479,1480,1483,1484,40,1487,1490,1491,1493],"li",{},[18,1481,1482],{},"Position",", where it is: ",[46,1485,1486],{},"x",[46,1488,1489],{},"y",". These are your ",[46,1492,48],{},"s.",[1478,1495,1496,1499,1500,40,1503,1506],{},[18,1497,1498],{},"Velocity",", how fast it's moving: ",[46,1501,1502],{},"vx",[46,1504,1505],{},"vy",". Plain variables.",[11,1508,1509],{},"In the loop, that's three lines doing the heavy lifting:",[111,1511,1513],{"className":562,"code":1512,"language":564,"meta":116,"style":116},"onTick(({ deltaTime }) => {\n  const dt = Math.min(deltaTime, 2)\n\n  // gravity tugs velocity downward every frame (down = +)\n  vy += gravity * dt // gravity = 0.55\n\n  // velocity moves the position\n  x.value += vx * dt\n  y.value += vy * dt\n\n  // ...then we resolve collisions, next section\n})\n",[46,1514,1515,1529,1552,1556,1561,1578,1582,1587,1601,1615,1619,1624],{"__ignoreMap":116},[120,1516,1517,1519,1521,1523,1525,1527],{"class":122,"line":123},[120,1518,554],{"class":130},[120,1520,640],{"class":134},[120,1522,430],{"class":315},[120,1524,645],{"class":134},[120,1526,648],{"class":126},[120,1528,651],{"class":134},[120,1530,1531,1533,1536,1538,1541,1544,1547,1550],{"class":122,"line":138},[120,1532,324],{"class":126},[120,1534,1535],{"class":134}," dt ",[120,1537,295],{"class":294},[120,1539,1540],{"class":134}," Math.",[120,1542,1543],{"class":130},"min",[120,1545,1546],{"class":134},"(deltaTime, ",[120,1548,1549],{"class":298},"2",[120,1551,367],{"class":134},[120,1553,1554],{"class":122,"line":147},[120,1555,159],{"emptyLinePlaceholder":158},[120,1557,1558],{"class":122,"line":155},[120,1559,1560],{"class":1358},"  // gravity tugs velocity downward every frame (down = +)\n",[120,1562,1563,1566,1568,1571,1573,1575],{"class":122,"line":162},[120,1564,1565],{"class":134},"  vy ",[120,1567,659],{"class":294},[120,1569,1570],{"class":134}," gravity ",[120,1572,664],{"class":294},[120,1574,1535],{"class":134},[120,1576,1577],{"class":1358},"// gravity = 0.55\n",[120,1579,1580],{"class":122,"line":171},[120,1581,159],{"emptyLinePlaceholder":158},[120,1583,1584],{"class":122,"line":177},[120,1585,1586],{"class":1358},"  // velocity moves the position\n",[120,1588,1589,1591,1593,1596,1598],{"class":122,"line":182},[120,1590,656],{"class":134},[120,1592,659],{"class":294},[120,1594,1595],{"class":134}," vx ",[120,1597,664],{"class":294},[120,1599,1600],{"class":134}," dt\n",[120,1602,1603,1606,1608,1611,1613],{"class":122,"line":391},[120,1604,1605],{"class":134},"  y.value ",[120,1607,659],{"class":294},[120,1609,1610],{"class":134}," vy ",[120,1612,664],{"class":294},[120,1614,1600],{"class":134},[120,1616,1617],{"class":122,"line":398},[120,1618,159],{"emptyLinePlaceholder":158},[120,1620,1621],{"class":122,"line":403},[120,1622,1623],{"class":1358},"  // ...then we resolve collisions, next section\n",[120,1625,1626],{"class":122,"line":410},[120,1627,672],{"class":134},[11,1629,1630],{},"The ordering matters: velocity updates before position. This is a simple physics choice. There are two ways to update movement each frame:",[1475,1632,1633,1636],{},[1478,1634,1635],{},"Move the object, then apply forces",[1478,1637,1638],{},"Apply forces, then move the object",[11,1640,1641],{},"They look almost identical in simple cases like jumping. The difference is tiny, sometimes just a fraction of a pixel per frame.",[11,1643,1644],{},"But the order matters when things get more dynamic, like springs, bouncing, or anything that should feel stable over time. One ordering can slowly add extra energy into the system, making motion feel slightly “off” after many frames. This version avoids that and stays stable, which is why it's the common default in games.",[11,1646,1647,1648,1650,1651,1653],{},"A jump is just setting ",[46,1649,1505],{}," to a large negative number once. Gravity does the rest: it slows the rise, pauses at the top, then accelerates the fall. The parabolic arc is emergent. Nobody draws that curve; it falls out of adding ",[46,1652,1393],{}," to a number every frame.",[11,1655,1656,1657,1660,1661,1663,1664,1667],{},"Tap ",[18,1658,1659],{},"Jump"," and watch ",[46,1662,1505],{},". It starts at ",[46,1665,1666],{},"-11.5",", climbs toward zero as the box rises, crosses zero at the peak, then goes positive on the way down:",[57,1669],{"src":1670,"title":1671,"loading":61,"scrolling":62,"style":1672},"/demos/building-a-platformer-game-with-vue-and-pixijs/gravity.html","A jumping box driven by gravity and velocity","width:100%;height:280px;border:0;border-radius:16px;margin:1.5rem 0;display:block",[11,1674,1675,1676,40,1679,1682],{},"There are no \"correct\" numbers here. Bigger gravity makes heavier, snappier jumps; smaller gravity gives floaty moon physics. Game feel is mostly twiddling these constants until the jump feels right under your thumb. This game landed on ",[46,1677,1678],{},"gravity 0.55",[46,1680,1681],{},"jump -11.5"," after a lot of tapping.",[1281,1684,1686],{"id":1685},"gotcha-4-handling-input","Gotcha 4: Handling Input",[11,1688,1689,1690,1693,1694,1697],{},"Games ",[18,1691,1692],{},"poll"," input instead of reacting to it. In a web form you handle a ",[46,1695,1696],{},"@keydown"," the moment it arrives, but that's too jittery for a game: held keys auto-repeat with gaps, and simultaneous inputs like left-plus-jump become inconsistent. So instead of reacting directly, you keep a running set of which keys are currently held, and every frame the loop reads that set.",[11,1699,1700],{},"The events only maintain state. They never move anything:",[111,1702,1704],{"className":562,"code":1703,"language":564,"meta":116,"style":116},"const keys = new Set\u003Cstring>()\n\nfunction onKeyDown(e: KeyboardEvent) {\n  keys.add(e.key)\n}\n\nfunction onKeyUp(e: KeyboardEvent) {\n  keys.delete(e.key)\n}\n\nonMounted(() => {\n  window.addEventListener('keydown', onKeyDown)\n  window.addEventListener('keyup', onKeyUp)\n})\n\nonUnmounted(() => {\n  window.removeEventListener('keydown', onKeyDown)\n  window.removeEventListener('keyup', onKeyUp)\n})\n",[46,1705,1706,1729,1733,1753,1764,1768,1772,1789,1798,1802,1806,1818,1834,1848,1852,1856,1867,1880,1893],{"__ignoreMap":116},[120,1707,1708,1710,1713,1715,1718,1721,1723,1726],{"class":122,"line":123},[120,1709,602],{"class":126},[120,1711,1712],{"class":134}," keys ",[120,1714,295],{"class":294},[120,1716,1717],{"class":294}," new",[120,1719,1720],{"class":130}," Set",[120,1722,736],{"class":134},[120,1724,1725],{"class":126},"string",[120,1727,1728],{"class":134},">()\n",[120,1730,1731],{"class":122,"line":138},[120,1732,159],{"emptyLinePlaceholder":158},[120,1734,1735,1737,1740,1742,1745,1747,1751],{"class":122,"line":147},[120,1736,127],{"class":126},[120,1738,1739],{"class":130}," onKeyDown",[120,1741,312],{"class":134},[120,1743,1744],{"class":315},"e",[120,1746,109],{"class":294},[120,1748,1750],{"class":1749},"soZtE"," KeyboardEvent",[120,1752,319],{"class":134},[120,1754,1755,1758,1761],{"class":122,"line":155},[120,1756,1757],{"class":134},"  keys.",[120,1759,1760],{"class":130},"add",[120,1762,1763],{"class":134},"(e.key)\n",[120,1765,1766],{"class":122,"line":162},[120,1767,174],{"class":134},[120,1769,1770],{"class":122,"line":171},[120,1771,159],{"emptyLinePlaceholder":158},[120,1773,1774,1776,1779,1781,1783,1785,1787],{"class":122,"line":177},[120,1775,127],{"class":126},[120,1777,1778],{"class":130}," onKeyUp",[120,1780,312],{"class":134},[120,1782,1744],{"class":315},[120,1784,109],{"class":294},[120,1786,1750],{"class":1749},[120,1788,319],{"class":134},[120,1790,1791,1793,1796],{"class":122,"line":182},[120,1792,1757],{"class":134},[120,1794,1795],{"class":130},"delete",[120,1797,1763],{"class":134},[120,1799,1800],{"class":122,"line":391},[120,1801,174],{"class":134},[120,1803,1804],{"class":122,"line":398},[120,1805,159],{"emptyLinePlaceholder":158},[120,1807,1808,1811,1814,1816],{"class":122,"line":403},[120,1809,1810],{"class":130},"onMounted",[120,1812,1813],{"class":134},"(() ",[120,1815,648],{"class":126},[120,1817,651],{"class":134},[120,1819,1820,1823,1826,1828,1831],{"class":122,"line":410},[120,1821,1822],{"class":134},"  window.",[120,1824,1825],{"class":130},"addEventListener",[120,1827,312],{"class":134},[120,1829,1830],{"class":580},"'keydown'",[120,1832,1833],{"class":134},", onKeyDown)\n",[120,1835,1836,1838,1840,1842,1845],{"class":122,"line":415},[120,1837,1822],{"class":134},[120,1839,1825],{"class":130},[120,1841,312],{"class":134},[120,1843,1844],{"class":580},"'keyup'",[120,1846,1847],{"class":134},", onKeyUp)\n",[120,1849,1850],{"class":122,"line":420},[120,1851,672],{"class":134},[120,1853,1854],{"class":122,"line":904},[120,1855,159],{"emptyLinePlaceholder":158},[120,1857,1858,1861,1863,1865],{"class":122,"line":915},[120,1859,1860],{"class":130},"onUnmounted",[120,1862,1813],{"class":134},[120,1864,648],{"class":126},[120,1866,651],{"class":134},[120,1868,1869,1871,1874,1876,1878],{"class":122,"line":1214},[120,1870,1822],{"class":134},[120,1872,1873],{"class":130},"removeEventListener",[120,1875,312],{"class":134},[120,1877,1830],{"class":580},[120,1879,1833],{"class":134},[120,1881,1883,1885,1887,1889,1891],{"class":122,"line":1882},18,[120,1884,1822],{"class":134},[120,1886,1873],{"class":130},[120,1888,312],{"class":134},[120,1890,1844],{"class":580},[120,1892,1847],{"class":134},[120,1894,1896],{"class":122,"line":1895},19,[120,1897,672],{"class":134},[11,1899,1900,1901,1903],{},"That same single ",[46,1902,554],{}," reads them every frame and converts held keys into velocity:",[111,1905,1907],{"className":562,"code":1906,"language":564,"meta":116,"style":116},"onTick(() => {\n  const left =\n    keys.has('ArrowLeft') || keys.has('a') || keys.has('A')\n\n  const right =\n    keys.has('ArrowRight') || keys.has('d') || keys.has('D')\n\n  vx = (right ? speed : 0) - (left ? speed : 0)\n})\n",[46,1908,1909,1919,1929,1973,1977,1986,2025,2029,2065],{"__ignoreMap":116},[120,1910,1911,1913,1915,1917],{"class":122,"line":123},[120,1912,554],{"class":130},[120,1914,1813],{"class":134},[120,1916,648],{"class":126},[120,1918,651],{"class":134},[120,1920,1921,1923,1926],{"class":122,"line":138},[120,1922,324],{"class":126},[120,1924,1925],{"class":134}," left ",[120,1927,1928],{"class":294},"=\n",[120,1930,1931,1934,1937,1939,1942,1945,1948,1951,1953,1955,1958,1960,1962,1964,1966,1968,1971],{"class":122,"line":147},[120,1932,1933],{"class":134},"    keys.",[120,1935,1936],{"class":130},"has",[120,1938,312],{"class":134},[120,1940,1941],{"class":580},"'ArrowLeft'",[120,1943,1944],{"class":134},") ",[120,1946,1947],{"class":294},"||",[120,1949,1950],{"class":134}," keys.",[120,1952,1936],{"class":130},[120,1954,312],{"class":134},[120,1956,1957],{"class":580},"'a'",[120,1959,1944],{"class":134},[120,1961,1947],{"class":294},[120,1963,1950],{"class":134},[120,1965,1936],{"class":130},[120,1967,312],{"class":134},[120,1969,1970],{"class":580},"'A'",[120,1972,367],{"class":134},[120,1974,1975],{"class":122,"line":155},[120,1976,159],{"emptyLinePlaceholder":158},[120,1978,1979,1981,1984],{"class":122,"line":162},[120,1980,324],{"class":126},[120,1982,1983],{"class":134}," right ",[120,1985,1928],{"class":294},[120,1987,1988,1990,1992,1994,1997,1999,2001,2003,2005,2007,2010,2012,2014,2016,2018,2020,2023],{"class":122,"line":171},[120,1989,1933],{"class":134},[120,1991,1936],{"class":130},[120,1993,312],{"class":134},[120,1995,1996],{"class":580},"'ArrowRight'",[120,1998,1944],{"class":134},[120,2000,1947],{"class":294},[120,2002,1950],{"class":134},[120,2004,1936],{"class":130},[120,2006,312],{"class":134},[120,2008,2009],{"class":580},"'d'",[120,2011,1944],{"class":134},[120,2013,1947],{"class":294},[120,2015,1950],{"class":134},[120,2017,1936],{"class":130},[120,2019,312],{"class":134},[120,2021,2022],{"class":580},"'D'",[120,2024,367],{"class":134},[120,2026,2027],{"class":122,"line":177},[120,2028,159],{"emptyLinePlaceholder":158},[120,2030,2031,2034,2036,2039,2041,2043,2045,2048,2050,2052,2055,2057,2059,2061,2063],{"class":122,"line":182},[120,2032,2033],{"class":134},"  vx ",[120,2035,295],{"class":294},[120,2037,2038],{"class":134}," (right ",[120,2040,247],{"class":294},[120,2042,624],{"class":134},[120,2044,109],{"class":294},[120,2046,2047],{"class":298}," 0",[120,2049,1944],{"class":134},[120,2051,335],{"class":294},[120,2053,2054],{"class":134}," (left ",[120,2056,247],{"class":294},[120,2058,624],{"class":134},[120,2060,109],{"class":294},[120,2062,2047],{"class":298},[120,2064,367],{"class":134},[120,2066,2067],{"class":122,"line":391},[120,2068,672],{"class":134},[11,2070,2071],{},"Keyboard events are fast and bursty; the loop is slow and steady. Letting events only update state, and letting the loop read that state, keeps movement smooth. Walking and jumping at the same time just works.",[11,2073,2074,2075,2078],{},"Jumping is the exception. Holding a key triggers auto-repeat, so if jump just checked “is the key down?”, you'd bounce like a pogo stick. Instead we detect the ",[23,2076,2077],{},"edge"," (the moment the key is first pressed) and store a one-shot flag.",[11,2080,2081],{},"The keydown handler becomes:",[111,2083,2085],{"className":562,"code":2084,"language":564,"meta":116,"style":116},"function onKeyDown(e: KeyboardEvent) {\n  keys.add(e.key)\n\n  if (isJump(e.key)) {\n    // only the first press, not auto-repeat\n    if (!e.repeat) wantJump = true\n    e.preventDefault()\n  }\n}\n",[46,2086,2087,2103,2111,2115,2128,2133,2151,2161,2166],{"__ignoreMap":116},[120,2088,2089,2091,2093,2095,2097,2099,2101],{"class":122,"line":123},[120,2090,127],{"class":126},[120,2092,1739],{"class":130},[120,2094,312],{"class":134},[120,2096,1744],{"class":315},[120,2098,109],{"class":294},[120,2100,1750],{"class":1749},[120,2102,319],{"class":134},[120,2104,2105,2107,2109],{"class":122,"line":138},[120,2106,1757],{"class":134},[120,2108,1760],{"class":130},[120,2110,1763],{"class":134},[120,2112,2113],{"class":122,"line":147},[120,2114,159],{"emptyLinePlaceholder":158},[120,2116,2117,2120,2122,2125],{"class":122,"line":155},[120,2118,2119],{"class":294},"  if",[120,2121,355],{"class":134},[120,2123,2124],{"class":130},"isJump",[120,2126,2127],{"class":134},"(e.key)) {\n",[120,2129,2130],{"class":122,"line":162},[120,2131,2132],{"class":1358},"    // only the first press, not auto-repeat\n",[120,2134,2135,2138,2140,2143,2146,2148],{"class":122,"line":171},[120,2136,2137],{"class":294},"    if",[120,2139,355],{"class":134},[120,2141,2142],{"class":294},"!",[120,2144,2145],{"class":134},"e.repeat) wantJump ",[120,2147,295],{"class":294},[120,2149,2150],{"class":298}," true\n",[120,2152,2153,2156,2159],{"class":122,"line":177},[120,2154,2155],{"class":134},"    e.",[120,2157,2158],{"class":130},"preventDefault",[120,2160,144],{"class":134},[120,2162,2163],{"class":122,"line":182},[120,2164,2165],{"class":134},"  }\n",[120,2167,2168],{"class":122,"line":391},[120,2169,174],{"class":134},[11,2171,2172,431,2175,2178,2179,2181],{},[46,2173,2174],{},"e.repeat",[46,2176,2177],{},"false"," on the initial press and ",[46,2180,883],{}," for the browser's auto-generated repeats. Using it ensures one press produces exactly one jump.",[11,2183,2184,2185,2188],{},"The loop consumes that flag. Early in the same tick, it checks both the request and the ",[46,2186,2187],{},"onGround"," state from the previous frame's collision step, then applies the jump:",[111,2190,2192],{"className":562,"code":2191,"language":564,"meta":116,"style":116},"if (wantJump && onGround) {\n  vy = jump\n  onGround = false\n  wantJump = false\n}\n",[46,2193,2194,2208,2217,2227,2236],{"__ignoreMap":116},[120,2195,2196,2199,2202,2205],{"class":122,"line":123},[120,2197,2198],{"class":294},"if",[120,2200,2201],{"class":134}," (wantJump ",[120,2203,2204],{"class":294},"&&",[120,2206,2207],{"class":134}," onGround) {\n",[120,2209,2210,2212,2214],{"class":122,"line":138},[120,2211,1565],{"class":134},[120,2213,295],{"class":294},[120,2215,2216],{"class":134}," jump\n",[120,2218,2219,2222,2224],{"class":122,"line":147},[120,2220,2221],{"class":134},"  onGround ",[120,2223,295],{"class":294},[120,2225,2226],{"class":298}," false\n",[120,2228,2229,2232,2234],{"class":122,"line":155},[120,2230,2231],{"class":134},"  wantJump ",[120,2233,295],{"class":294},[120,2235,2226],{"class":298},[120,2237,2238],{"class":122,"line":162},[120,2239,174],{"class":134},[11,2241,2242,2243,2245,2246,2249],{},"That ties it together: collision sets ",[46,2244,2187],{},", input sets ",[46,2247,2248],{},"wantJump",", and the loop decides when both conditions are true.",[65,2251,2253],{"id":2252},"lets-make-things-collide","Let's Make Things Collide",[11,2255,2256],{},"Alright, we're ready to start building the actual platformer!",[11,2258,2259,2260,535],{},"But first, a problem we created and never solved. The player has velocity and gravity now, so they fall. And they keep falling, straight through every platform, off the bottom of the screen, gone. Nothing stops them, because nothing is ",[23,2261,2262],{},"checking",[11,2264,2265,2266,2269],{},"That check is most of what a platformer is. Every frame, before anything else moves, you ask one yes/no question of every platform: ",[18,2267,2268],{},"is the player's box overlapping this one?"," If it is, you shove the player back out so they end up resting against it instead of sunk inside it.",[11,2271,2272,2273,2276,2277,1251,2279,1251,2281,1440,2283,2285,2286,2289,2290,2293],{},"You've done something similar before: in the DOM, ",[46,2274,2275],{},"getBoundingClientRect()"," hands you an ",[46,2278,1486],{},[46,2280,1489],{},[46,2282,826],{},[46,2284,844],{},", and you compare two of those boxes to see if a dropdown clears its trigger or a tooltip runs off-screen. In a game, collision is that same comparison, just run 60 times a second. Every platform in the level is a ",[46,2287,2288],{},"Rect",", a plain ",[46,2291,2292],{},"{ x, y, w, h }",", the same four numbers that bounding box gives you.",[11,2295,2296,2297,2300,2301,2304],{},"The comparison has an intimidating name, ",[18,2298,2299],{},"AABB"," (Axis-Aligned Bounding Box), and a friendly definition: two rectangles overlap only when they overlap on ",[23,2302,2303],{},"both"," the X and Y axes. Miss on either one and there's a gap between them. Here it is straight from the game, annotated so each line reads in plain English:",[111,2306,2308],{"className":562,"code":2307,"language":564,"meta":116,"style":116},"function overlaps(\n  ax: number, ay: number,\n  aw: number, ah: number,\n  b: Rect,\n) {\n  return (\n    // a's left is left of b's right\n    ax \u003C b.x + b.w &&\n    // a's right is right of b's left\n    ax + aw > b.x &&\n    // a's top is above b's bottom\n    ay \u003C b.y + b.h &&\n    // a's bottom is below b's top\n    ay + ah > b.y\n  )\n}\n",[46,2309,2310,2320,2341,2361,2373,2377,2385,2390,2409,2414,2430,2435,2452,2457,2471,2476],{"__ignoreMap":116},[120,2311,2312,2314,2317],{"class":122,"line":123},[120,2313,127],{"class":126},[120,2315,2316],{"class":130}," overlaps",[120,2318,2319],{"class":134},"(\n",[120,2321,2322,2325,2327,2330,2332,2335,2337,2339],{"class":122,"line":138},[120,2323,2324],{"class":315},"  ax",[120,2326,109],{"class":294},[120,2328,2329],{"class":126}," number",[120,2331,1251],{"class":134},[120,2333,2334],{"class":315},"ay",[120,2336,109],{"class":294},[120,2338,2329],{"class":126},[120,2340,1385],{"class":134},[120,2342,2343,2346,2348,2350,2352,2355,2357,2359],{"class":122,"line":147},[120,2344,2345],{"class":315},"  aw",[120,2347,109],{"class":294},[120,2349,2329],{"class":126},[120,2351,1251],{"class":134},[120,2353,2354],{"class":315},"ah",[120,2356,109],{"class":294},[120,2358,2329],{"class":126},[120,2360,1385],{"class":134},[120,2362,2363,2366,2368,2371],{"class":122,"line":155},[120,2364,2365],{"class":315},"  b",[120,2367,109],{"class":294},[120,2369,2370],{"class":1749}," Rect",[120,2372,1385],{"class":134},[120,2374,2375],{"class":122,"line":162},[120,2376,319],{"class":134},[120,2378,2379,2382],{"class":122,"line":171},[120,2380,2381],{"class":294},"  return",[120,2383,2384],{"class":134}," (\n",[120,2386,2387],{"class":122,"line":177},[120,2388,2389],{"class":1358},"    // a's left is left of b's right\n",[120,2391,2392,2395,2397,2400,2403,2406],{"class":122,"line":182},[120,2393,2394],{"class":134},"    ax ",[120,2396,736],{"class":294},[120,2398,2399],{"class":134}," b.x ",[120,2401,2402],{"class":294},"+",[120,2404,2405],{"class":134}," b.w ",[120,2407,2408],{"class":294},"&&\n",[120,2410,2411],{"class":122,"line":391},[120,2412,2413],{"class":1358},"    // a's right is right of b's left\n",[120,2415,2416,2418,2420,2423,2426,2428],{"class":122,"line":398},[120,2417,2394],{"class":134},[120,2419,2402],{"class":294},[120,2421,2422],{"class":134}," aw ",[120,2424,2425],{"class":294},">",[120,2427,2399],{"class":134},[120,2429,2408],{"class":294},[120,2431,2432],{"class":122,"line":403},[120,2433,2434],{"class":1358},"    // a's top is above b's bottom\n",[120,2436,2437,2440,2442,2445,2447,2450],{"class":122,"line":410},[120,2438,2439],{"class":134},"    ay ",[120,2441,736],{"class":294},[120,2443,2444],{"class":134}," b.y ",[120,2446,2402],{"class":294},[120,2448,2449],{"class":134}," b.h ",[120,2451,2408],{"class":294},[120,2453,2454],{"class":122,"line":415},[120,2455,2456],{"class":1358},"    // a's bottom is below b's top\n",[120,2458,2459,2461,2463,2466,2468],{"class":122,"line":420},[120,2460,2439],{"class":134},[120,2462,2402],{"class":294},[120,2464,2465],{"class":134}," ah ",[120,2467,2425],{"class":294},[120,2469,2470],{"class":134}," b.y\n",[120,2472,2473],{"class":122,"line":904},[120,2474,2475],{"class":134},"  )\n",[120,2477,2478],{"class":122,"line":915},[120,2479,174],{"class":134},[11,2481,2482,2483,2485,2486,2488,2489,2492,2493,2496,2497,2499],{},"If that returns ",[46,2484,883],{}," while the player is falling, you snap their feet to the top of the platform and zero out ",[46,2487,1505],{},". Now they're standing. The box below tracks back and forth and flips to ",[18,2490,2491],{},"red"," the instant ",[46,2494,2495],{},"overlaps()"," turns ",[46,2498,883],{}," against the wall:",[57,2501],{"src":2502,"title":2503,"loading":61,"scrolling":62,"style":2504},"/demos/building-a-platformer-game-with-vue-and-pixijs/collision.html","Two rectangles overlapping (AABB collision)","width:100%;height:220px;border:0;border-radius:16px;margin:1.5rem 0;display:block",[11,2506,2507,2508,2511],{},"Detecting the overlap is half the job. ",[23,2509,2510],{},"Resolving"," it is the other half, and there's one move that makes it all click: handle one axis at a time. The loop applies horizontal movement and pushes the player out of any wall it hit, then applies vertical movement and pushes out of any floor or ceiling:",[111,2513,2515],{"className":562,"code":2514,"language":564,"meta":116,"style":116},"// horizontal move, then resolve against every platform\nx.value += vx * dt\nfor (const p of PLATFORMS) {\n  if (overlaps(x.value, y.value, pw, ph, p)) {\n    // moving right: snap to the wall's left face\n    if (vx > 0) x.value = p.x - pw\n    // moving left: snap to its right face\n    else if (vx \u003C 0) x.value = p.x + p.w\n    vx = 0\n  }\n}\n\n// vertical move, then resolve again\ny.value += vy * dt\nonGround = false\nfor (const p of PLATFORMS) {\n  if (overlaps(x.value, y.value, pw, ph, p)) {\n    if (vy > 0) {\n      // landed on top\n      y.value = p.y - ph\n      onGround = true\n      vy = 0\n    } else if (vy \u003C 0) {\n      // bonked your head\n      y.value = p.y + p.h\n      vy = 0\n    }\n  }\n}\n",[46,2516,2517,2522,2535,2553,2565,2570,2594,2599,2624,2633,2637,2641,2645,2650,2663,2672,2686,2696,2709,2714,2730,2740,2750,2769,2775,2789,2798,2804,2809],{"__ignoreMap":116},[120,2518,2519],{"class":122,"line":123},[120,2520,2521],{"class":1358},"// horizontal move, then resolve against every platform\n",[120,2523,2524,2527,2529,2531,2533],{"class":122,"line":138},[120,2525,2526],{"class":134},"x.value ",[120,2528,659],{"class":294},[120,2530,1595],{"class":134},[120,2532,664],{"class":294},[120,2534,1600],{"class":134},[120,2536,2537,2540,2542,2544,2547,2550],{"class":122,"line":147},[120,2538,2539],{"class":294},"for",[120,2541,355],{"class":134},[120,2543,602],{"class":126},[120,2545,2546],{"class":134}," p ",[120,2548,2549],{"class":294},"of",[120,2551,2552],{"class":134}," PLATFORMS) {\n",[120,2554,2555,2557,2559,2562],{"class":122,"line":155},[120,2556,2119],{"class":294},[120,2558,355],{"class":134},[120,2560,2561],{"class":130},"overlaps",[120,2563,2564],{"class":134},"(x.value, y.value, pw, ph, p)) {\n",[120,2566,2567],{"class":122,"line":162},[120,2568,2569],{"class":1358},"    // moving right: snap to the wall's left face\n",[120,2571,2572,2574,2577,2579,2581,2584,2586,2589,2591],{"class":122,"line":171},[120,2573,2137],{"class":294},[120,2575,2576],{"class":134}," (vx ",[120,2578,2425],{"class":294},[120,2580,2047],{"class":298},[120,2582,2583],{"class":134},") x.value ",[120,2585,295],{"class":294},[120,2587,2588],{"class":134}," p.x ",[120,2590,335],{"class":294},[120,2592,2593],{"class":134}," pw\n",[120,2595,2596],{"class":122,"line":177},[120,2597,2598],{"class":1358},"    // moving left: snap to its right face\n",[120,2600,2601,2604,2607,2609,2611,2613,2615,2617,2619,2621],{"class":122,"line":182},[120,2602,2603],{"class":294},"    else",[120,2605,2606],{"class":294}," if",[120,2608,2576],{"class":134},[120,2610,736],{"class":294},[120,2612,2047],{"class":298},[120,2614,2583],{"class":134},[120,2616,295],{"class":294},[120,2618,2588],{"class":134},[120,2620,2402],{"class":294},[120,2622,2623],{"class":134}," p.w\n",[120,2625,2626,2629,2631],{"class":122,"line":391},[120,2627,2628],{"class":134},"    vx ",[120,2630,295],{"class":294},[120,2632,299],{"class":298},[120,2634,2635],{"class":122,"line":398},[120,2636,2165],{"class":134},[120,2638,2639],{"class":122,"line":403},[120,2640,174],{"class":134},[120,2642,2643],{"class":122,"line":410},[120,2644,159],{"emptyLinePlaceholder":158},[120,2646,2647],{"class":122,"line":415},[120,2648,2649],{"class":1358},"// vertical move, then resolve again\n",[120,2651,2652,2655,2657,2659,2661],{"class":122,"line":420},[120,2653,2654],{"class":134},"y.value ",[120,2656,659],{"class":294},[120,2658,1610],{"class":134},[120,2660,664],{"class":294},[120,2662,1600],{"class":134},[120,2664,2665,2668,2670],{"class":122,"line":904},[120,2666,2667],{"class":134},"onGround ",[120,2669,295],{"class":294},[120,2671,2226],{"class":298},[120,2673,2674,2676,2678,2680,2682,2684],{"class":122,"line":915},[120,2675,2539],{"class":294},[120,2677,355],{"class":134},[120,2679,602],{"class":126},[120,2681,2546],{"class":134},[120,2683,2549],{"class":294},[120,2685,2552],{"class":134},[120,2687,2688,2690,2692,2694],{"class":122,"line":1214},[120,2689,2119],{"class":294},[120,2691,355],{"class":134},[120,2693,2561],{"class":130},[120,2695,2564],{"class":134},[120,2697,2698,2700,2703,2705,2707],{"class":122,"line":1882},[120,2699,2137],{"class":294},[120,2701,2702],{"class":134}," (vy ",[120,2704,2425],{"class":294},[120,2706,2047],{"class":298},[120,2708,319],{"class":134},[120,2710,2711],{"class":122,"line":1895},[120,2712,2713],{"class":1358},"      // landed on top\n",[120,2715,2717,2720,2722,2725,2727],{"class":122,"line":2716},20,[120,2718,2719],{"class":134},"      y.value ",[120,2721,295],{"class":294},[120,2723,2724],{"class":134}," p.y ",[120,2726,335],{"class":294},[120,2728,2729],{"class":134}," ph\n",[120,2731,2733,2736,2738],{"class":122,"line":2732},21,[120,2734,2735],{"class":134},"      onGround ",[120,2737,295],{"class":294},[120,2739,2150],{"class":298},[120,2741,2743,2746,2748],{"class":122,"line":2742},22,[120,2744,2745],{"class":134},"      vy ",[120,2747,295],{"class":294},[120,2749,299],{"class":298},[120,2751,2753,2756,2759,2761,2763,2765,2767],{"class":122,"line":2752},23,[120,2754,2755],{"class":134},"    } ",[120,2757,2758],{"class":294},"else",[120,2760,2606],{"class":294},[120,2762,2702],{"class":134},[120,2764,736],{"class":294},[120,2766,2047],{"class":298},[120,2768,319],{"class":134},[120,2770,2772],{"class":122,"line":2771},24,[120,2773,2774],{"class":1358},"      // bonked your head\n",[120,2776,2778,2780,2782,2784,2786],{"class":122,"line":2777},25,[120,2779,2719],{"class":134},[120,2781,295],{"class":294},[120,2783,2724],{"class":134},[120,2785,2402],{"class":294},[120,2787,2788],{"class":134}," p.h\n",[120,2790,2792,2794,2796],{"class":122,"line":2791},26,[120,2793,2745],{"class":134},[120,2795,295],{"class":294},[120,2797,299],{"class":298},[120,2799,2801],{"class":122,"line":2800},27,[120,2802,2803],{"class":134},"    }\n",[120,2805,2807],{"class":122,"line":2806},28,[120,2808,2165],{"class":134},[120,2810,2812],{"class":122,"line":2811},29,[120,2813,174],{"class":134},[11,2815,2816,2817,2819],{},"Splitting the axes is what does the work. The same handful of lines makes \"slide along a wall while falling\" and \"land softly on a ledge\" both work. Corners and very fast movers need more care (resolution order matters, and the dt clamp earns its keep here), but per-axis is the clean backbone. Notice ",[46,2818,2187],{}," gets recomputed here every frame, the exact flag the jump code checks before letting you launch.",[65,2821,2823],{"id":2822},"sharp-saws-and-one-goal","Sharp Saws and One Goal!",[11,2825,2826],{},"Two things in the level aren't platforms: the spinning saw that hurts you and the goal you're trying to reach. They need collision too.",[11,2828,2829,2830,2833],{},"The saw is round, so treating it like a rectangle would feel wrong near the edges. Instead it uses ",[18,2831,2832],{},"circle vs box"," collision: you take the saw's center, clamp it to the player's rectangle to find the closest point, then check how far that point is from the center.",[111,2835,2837],{"className":562,"code":2836,"language":564,"meta":116,"style":116},"const r = SAW_RADIUS - 4 // slightly forgiving\n\nfor (const s of SAWS) {\n  // closest X on player box\n  const nx = Math.max(x.value, Math.min(s.x, x.value + pw))\n  // closest Y on player box\n  const ny = Math.max(y.value, Math.min(s.y, y.value + ph))\n\n  const dx = s.x - nx\n  const dy = s.y - ny\n\n  if (dx * dx + dy * dy \u003C r * r) {\n    emit('hit')\n    respawn()\n    return\n  }\n}\n",[46,2838,2839,2859,2863,2879,2884,2911,2916,2942,2946,2963,2980,2984,3012,3024,3031,3036,3040],{"__ignoreMap":116},[120,2840,2841,2843,2846,2848,2851,2853,2856],{"class":122,"line":123},[120,2842,602],{"class":126},[120,2844,2845],{"class":134}," r ",[120,2847,295],{"class":294},[120,2849,2850],{"class":134}," SAW_RADIUS ",[120,2852,335],{"class":294},[120,2854,2855],{"class":298}," 4",[120,2857,2858],{"class":1358}," // slightly forgiving\n",[120,2860,2861],{"class":122,"line":138},[120,2862,159],{"emptyLinePlaceholder":158},[120,2864,2865,2867,2869,2871,2874,2876],{"class":122,"line":147},[120,2866,2539],{"class":294},[120,2868,355],{"class":134},[120,2870,602],{"class":126},[120,2872,2873],{"class":134}," s ",[120,2875,2549],{"class":294},[120,2877,2878],{"class":134}," SAWS) {\n",[120,2880,2881],{"class":122,"line":155},[120,2882,2883],{"class":1358},"  // closest X on player box\n",[120,2885,2886,2888,2891,2893,2895,2898,2901,2903,2906,2908],{"class":122,"line":162},[120,2887,324],{"class":126},[120,2889,2890],{"class":134}," nx ",[120,2892,295],{"class":294},[120,2894,1540],{"class":134},[120,2896,2897],{"class":130},"max",[120,2899,2900],{"class":134},"(x.value, Math.",[120,2902,1543],{"class":130},[120,2904,2905],{"class":134},"(s.x, x.value ",[120,2907,2402],{"class":294},[120,2909,2910],{"class":134}," pw))\n",[120,2912,2913],{"class":122,"line":171},[120,2914,2915],{"class":1358},"  // closest Y on player box\n",[120,2917,2918,2920,2923,2925,2927,2929,2932,2934,2937,2939],{"class":122,"line":177},[120,2919,324],{"class":126},[120,2921,2922],{"class":134}," ny ",[120,2924,295],{"class":294},[120,2926,1540],{"class":134},[120,2928,2897],{"class":130},[120,2930,2931],{"class":134},"(y.value, Math.",[120,2933,1543],{"class":130},[120,2935,2936],{"class":134},"(s.y, y.value ",[120,2938,2402],{"class":294},[120,2940,2941],{"class":134}," ph))\n",[120,2943,2944],{"class":122,"line":182},[120,2945,159],{"emptyLinePlaceholder":158},[120,2947,2948,2950,2953,2955,2958,2960],{"class":122,"line":391},[120,2949,324],{"class":126},[120,2951,2952],{"class":134}," dx ",[120,2954,295],{"class":294},[120,2956,2957],{"class":134}," s.x ",[120,2959,335],{"class":294},[120,2961,2962],{"class":134}," nx\n",[120,2964,2965,2967,2970,2972,2975,2977],{"class":122,"line":398},[120,2966,324],{"class":126},[120,2968,2969],{"class":134}," dy ",[120,2971,295],{"class":294},[120,2973,2974],{"class":134}," s.y ",[120,2976,335],{"class":294},[120,2978,2979],{"class":134}," ny\n",[120,2981,2982],{"class":122,"line":403},[120,2983,159],{"emptyLinePlaceholder":158},[120,2985,2986,2988,2991,2993,2995,2997,2999,3001,3003,3005,3007,3009],{"class":122,"line":410},[120,2987,2119],{"class":294},[120,2989,2990],{"class":134}," (dx ",[120,2992,664],{"class":294},[120,2994,2952],{"class":134},[120,2996,2402],{"class":294},[120,2998,2969],{"class":134},[120,3000,664],{"class":294},[120,3002,2969],{"class":134},[120,3004,736],{"class":294},[120,3006,2845],{"class":134},[120,3008,664],{"class":294},[120,3010,3011],{"class":134}," r) {\n",[120,3013,3014,3017,3019,3022],{"class":122,"line":415},[120,3015,3016],{"class":130},"    emit",[120,3018,312],{"class":134},[120,3020,3021],{"class":580},"'hit'",[120,3023,367],{"class":134},[120,3025,3026,3029],{"class":122,"line":420},[120,3027,3028],{"class":130},"    respawn",[120,3030,144],{"class":134},[120,3032,3033],{"class":122,"line":904},[120,3034,3035],{"class":294},"    return\n",[120,3037,3038],{"class":122,"line":915},[120,3039,2165],{"class":134},[120,3041,3042],{"class":122,"line":1214},[120,3043,174],{"class":134},[11,3045,3046,3047,3050,3051,3054],{},"That ",[46,3048,3049],{},"dx*dx + dy*dy \u003C r*r"," is the whole trick. It avoids ",[46,3052,3053],{},"Math.sqrt"," entirely. You only care if you're inside the radius, not the exact distance.",[11,3056,3057],{},"The goal, a little flag, is simpler. It's just a rectangle, so it reuses the same overlap check as platforms:",[111,3059,3061],{"className":562,"code":3060,"language":564,"meta":116,"style":116},"if (overlaps(x.value, y.value, pw, ph, GOAL)) {\n  emit('win')\n}\n",[46,3062,3063,3074,3086],{"__ignoreMap":116},[120,3064,3065,3067,3069,3071],{"class":122,"line":123},[120,3066,2198],{"class":294},[120,3068,355],{"class":134},[120,3070,2561],{"class":130},[120,3072,3073],{"class":134},"(x.value, y.value, pw, ph, GOAL)) {\n",[120,3075,3076,3079,3081,3084],{"class":122,"line":138},[120,3077,3078],{"class":130},"  emit",[120,3080,312],{"class":134},[120,3082,3083],{"class":580},"'win'",[120,3085,367],{"class":134},[120,3087,3088],{"class":122,"line":147},[120,3089,174],{"class":134},[11,3091,3092],{},"Both cases just emit events upward. The player doesn't “decide” anything. It only reports what happened. The actual game state lives one level above, in Vue reactivity:",[111,3094,3096],{"className":562,"code":3095,"language":564,"meta":116,"style":116},"const lives = ref(START_LIVES)\nconst won = ref(false)\n\nconst active = computed(() => !won.value)\n\nfunction onHit() {\n  lives.value -= 1\n  if (lives.value \u003C= 0) lives.value = START_LIVES\n}\n\nfunction onWin() {\n  won.value = true\n}\n",[46,3097,3098,3112,3129,3133,3155,3159,3168,3179,3199,3203,3207,3216,3225],{"__ignoreMap":116},[120,3099,3100,3102,3105,3107,3109],{"class":122,"line":123},[120,3101,602],{"class":126},[120,3103,3104],{"class":134}," lives ",[120,3106,295],{"class":294},[120,3108,610],{"class":130},[120,3110,3111],{"class":134},"(START_LIVES)\n",[120,3113,3114,3116,3119,3121,3123,3125,3127],{"class":122,"line":138},[120,3115,602],{"class":126},[120,3117,3118],{"class":134}," won ",[120,3120,295],{"class":294},[120,3122,610],{"class":130},[120,3124,312],{"class":134},[120,3126,2177],{"class":298},[120,3128,367],{"class":134},[120,3130,3131],{"class":122,"line":147},[120,3132,159],{"emptyLinePlaceholder":158},[120,3134,3135,3137,3140,3142,3145,3147,3149,3152],{"class":122,"line":155},[120,3136,602],{"class":126},[120,3138,3139],{"class":134}," active ",[120,3141,295],{"class":294},[120,3143,3144],{"class":130}," computed",[120,3146,1813],{"class":134},[120,3148,648],{"class":126},[120,3150,3151],{"class":294}," !",[120,3153,3154],{"class":134},"won.value)\n",[120,3156,3157],{"class":122,"line":162},[120,3158,159],{"emptyLinePlaceholder":158},[120,3160,3161,3163,3166],{"class":122,"line":171},[120,3162,127],{"class":126},[120,3164,3165],{"class":130}," onHit",[120,3167,135],{"class":134},[120,3169,3170,3173,3176],{"class":122,"line":177},[120,3171,3172],{"class":134},"  lives.value ",[120,3174,3175],{"class":294},"-=",[120,3177,3178],{"class":298}," 1\n",[120,3180,3181,3183,3186,3189,3191,3194,3196],{"class":122,"line":182},[120,3182,2119],{"class":294},[120,3184,3185],{"class":134}," (lives.value ",[120,3187,3188],{"class":294},"\u003C=",[120,3190,2047],{"class":298},[120,3192,3193],{"class":134},") lives.value ",[120,3195,295],{"class":294},[120,3197,3198],{"class":134}," START_LIVES\n",[120,3200,3201],{"class":122,"line":391},[120,3202,174],{"class":134},[120,3204,3205],{"class":122,"line":398},[120,3206,159],{"emptyLinePlaceholder":158},[120,3208,3209,3211,3214],{"class":122,"line":403},[120,3210,127],{"class":126},[120,3212,3213],{"class":130}," onWin",[120,3215,135],{"class":134},[120,3217,3218,3221,3223],{"class":122,"line":410},[120,3219,3220],{"class":134},"  won.value ",[120,3222,295],{"class":294},[120,3224,2150],{"class":298},[120,3226,3227],{"class":122,"line":415},[120,3228,174],{"class":134},[11,3230,3231,3234,3235,3237,3238,3241,3242,535],{},[46,3232,3233],{},"respawn()"," stays inside the player, where it resets position and velocity. ",[46,3236,1146],{}," just adjusts lives. Simple separation: the player handles ",[23,3239,3240],{},"where you are",", the world handles ",[23,3243,3244],{},"what that means",[11,3246,3247,3248,3250],{},"The important line here is ",[46,3249,1125],{},". It flows straight into the player:",[111,3252,3254],{"className":562,"code":3253,"language":564,"meta":116,"style":116},"if (!props.active) return\n",[46,3255,3256],{"__ignoreMap":116},[120,3257,3258,3260,3262,3264,3267],{"class":122,"line":123},[120,3259,2198],{"class":294},[120,3261,355],{"class":134},[120,3263,2142],{"class":294},[120,3265,3266],{"class":134},"props.active) ",[120,3268,3269],{"class":294},"return\n",[11,3271,3272,3273,3275,3276,3278],{},"So the moment ",[46,3274,1193],{}," flips to ",[46,3277,883],{},", the entire simulation stops updating. No more physics, no more movement, just a frozen world.",[11,3280,3281,3282,3284,3285,3287],{},"And the HUD is just another Vue component reacting to the same state. ",[46,3283,1180],{}," drives the hearts, ",[46,3286,1193],{}," drives the victory screen.",[11,3289,3290],{},"At this point, the whole game is just state flowing through components, and a loop deciding when that state gets updated.",[65,3292,3294],{"id":3293},"from-start-to-game-over","From Start to Game Over",[11,3296,3297],{},"Here's everything above in the order it actually happens inside a single tick of the loop:",[3299,3300,3301,3314,3329,3337,3343,3351,3361],"ol",{},[1478,3302,3303,3306,3307,3310,3311,3313],{},[18,3304,3305],{},"Read input."," Check which keys are currently in the ",[46,3308,3309],{},"keys"," set, and whether ",[46,3312,2248],{}," was armed.",[1478,3315,3316,3319,3320,40,3322,3324,3325,3328],{},[18,3317,3318],{},"Jump."," If ",[46,3321,2248],{},[46,3323,2187],{}," are true, set ",[46,3326,3327],{},"vy = jump"," and consume the flag.",[1478,3330,3331,701,3334,535],{},[18,3332,3333],{},"Apply gravity.",[46,3335,3336],{},"vy += gravity * dt",[1478,3338,3339,3342],{},[18,3340,3341],{},"Move + resolve X."," Apply horizontal velocity, then push out of any wall collisions.",[1478,3344,3345,3348,3349,535],{},[18,3346,3347],{},"Move + resolve Y."," Apply vertical velocity, then snap to floors or ceilings, and recompute ",[46,3350,2187],{},[1478,3352,3353,3356,3357,1263,3359,535],{},[18,3354,3355],{},"Check the world."," Did we hit a saw, fall into a pit, or reach the goal? Emit ",[46,3358,1139],{},[46,3360,1153],{},[1478,3362,3363,3366],{},[18,3364,3365],{},"Render."," Pixi draws the updated scene, and the loop repeats.",[11,3368,3369,3370,3372,3373,3375,3376,3378],{},"Steps 1–6 are everything inside ",[46,3371,554],{},". Step 7 is automatic: you're only mutating reactive ",[46,3374,48],{},"s, and ",[46,3377,547],{}," turns that into a redraw, just like Vue re-renders a template when state changes.",[65,3380,3382],{"id":3381},"in-closing","In Closing",[11,3384,3385,3386,3388,3389,3392],{},"That's the entire platformer: a loop, a few ",[46,3387,48],{},"s, and a lot of adding numbers. The blue box can now jump over platforms, avoid blades and reach the flag. Under the hood it's just ",[46,3390,3391],{},"vy += 0.55",", a rectangle overlap test, and Vue's reactivity doing what it does best. Suddenly this doesn't feel too alien, right? Let me tell you: if you were able to ship that dashboard or build that crazy data table, you already have what it takes to build a game!",[11,3394,3395,3396,2142],{},"Did you learn something new? Did you build a game? ",[540,3397,3400],{"href":3398,"rel":3399},"https://x.com/nicodevs",[544],"Let me know",[11,3402,3403],{},"Until next time!",[3405,3406,3407],"style",{},"html pre.shiki code .sOx1s, html code.shiki .sOx1s{--shiki-default:#66D9EF;--shiki-default-font-style:italic}html pre.shiki code .sHkqI, html code.shiki .sHkqI{--shiki-default:#A6E22E}html pre.shiki code .sCdxs, html code.shiki .sCdxs{--shiki-default:#F8F8F2}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .s8I7P, html code.shiki .s8I7P{--shiki-default:#F92672}html pre.shiki code .s7s5_, html code.shiki .s7s5_{--shiki-default:#AE81FF}html pre.shiki code .sW0Xf, html code.shiki .sW0Xf{--shiki-default:#FD971F;--shiki-default-font-style:italic}html pre.shiki code .s_Ekj, html code.shiki .s_Ekj{--shiki-default:#E6DB74}html pre.shiki code .snpHw, html code.shiki .snpHw{--shiki-default:#88846F}html pre.shiki code .soZtE, html code.shiki .soZtE{--shiki-default:#A6E22E;--shiki-default-text-decoration:underline}",{"title":116,"searchDepth":138,"depth":138,"links":3409},[3410,3411,3412,3413,3414,3415,3421,3422,3423,3424],{"id":67,"depth":138,"text":68},{"id":101,"depth":138,"text":102},{"id":236,"depth":138,"text":237},{"id":475,"depth":138,"text":476},{"id":528,"depth":138,"text":529},{"id":707,"depth":138,"text":708,"children":3416},[3417,3418,3419,3420],{"id":1283,"depth":147,"text":1284},{"id":1306,"depth":147,"text":1307},{"id":1462,"depth":147,"text":1463},{"id":1685,"depth":147,"text":1686},{"id":2252,"depth":138,"text":2253},{"id":2822,"depth":138,"text":2823},{"id":3293,"depth":138,"text":3294},{"id":3381,"depth":138,"text":3382},"Vue","2026-06-26","We built a little platformer with Vue and PixiJS. Now let's open the hood. Every game concept maps to something you already know from Vue. Mostly, we're just renaming things you do every day.",false,"md","/img/blog/building-a-platformer-game-with-vue-and-pixijs.png",{},"/blog/building-a-platformer-game-with-vue-and-pixijs",{"title":6,"description":3427},"blog/building-a-platformer-game-with-vue-and-pixijs","ZVhKj7KhHUsujnns-qK1COE6HNjFk5RXpdHq0noKPOY",{"id":3437,"title":3438,"body":3439,"category":3425,"date":4742,"description":4743,"draft":3428,"extension":3429,"image":4744,"meta":4745,"navigation":158,"path":4746,"seo":4747,"stem":4748,"tighten":3428,"__hash__":4749},"blog/blog/vue-vapor-goodbye-virtual-dom.md","Vue Vapor: Goodbye, Virtual DOM",{"type":8,"value":3440,"toc":4730},[3441,3454,3461,3464,3468,3471,3477,3480,3485,3488,3511,3514,3519,3522,3527,3531,3534,3545,3552,3660,3663,3666,3698,3701,3705,3708,3716,3719,3722,3736,3743,3750,3753,3756,3760,3763,3766,3809,3819,3822,3825,3832,3836,3842,3845,3854,3872,3875,3941,3944,3979,3982,3986,3993,3996,3999,4243,4246,4253,4256,4263,4266,4286,4289,4296,4299,4302,4312,4319,4322,4326,4329,4345,4353,4360,4400,4406,4417,4572,4579,4582,4586,4594,4694,4702,4705,4708,4711,4715,4718,4725,4727],[3442,3443,3446],"callout",{"color":3444,"icon":3445},"#057ccd","fa6-solid:circle-info",[11,3447,3448,3449,3453],{},"This article is adapted from my talk at Frontend Nation 2026. Head over to ",[540,3450,3452],{"href":3451},"/frontendnation","this page"," for the slides and additional resources!",[11,3455,3456,3457,3460],{},"Today, we're going to take a look at ",[18,3458,3459],{},"Vue Vapor,"," a new compilation mode that can take your Vue apps to the next level. We're going to learn what it is, what makes it so fast, and how you can try it out today.",[11,3462,3463],{},"Plus, we're going to take a look at some benchmarks, a few interesting demos, and the code Vapor generates. Let's go!",[65,3465,3467],{"id":3466},"three-wishes-for-all-your-vue-apps","Three Wishes For all Your Vue Apps",[11,3469,3470],{},"Let me start with a little fantasy story. I know it sounds weird, but it's gonna be fun.",[3472,3473,3474],"blockquote",{},[11,3475,3476],{},"Imagine you're an adventurer in the Kingdom of JavaScript, and you come across a genie lamp...",[11,3478,3479],{},"You've just defeated an ugly bug, and among the loot, you find this lamp — the lamp of the Genie of Vue. In a cloud of mist, the guy appears in front of you and says:",[3472,3481,3482],{},[11,3483,3484],{},"Hey, I'll grant three wishes for all your Vue applications.",[11,3486,3487],{},"What would you ask for?",[1475,3489,3490,3497,3504],{},[1478,3491,3492,3493,3496],{},"Well, perhaps your first wish would be to ",[18,3494,3495],{},"make all your Vue applications faster."," That's an obvious one, right? Let's say 50 times faster. Let's make Vue the fastest JavaScript framework on the face of the Earth. Why not?",[1478,3498,3499,3500,3503],{},"Perhaps your second wish would be for all your apps to ",[18,3501,3502],{},"use less memory,"," so they run smoothly even on potato devices.",[1478,3505,3506,3507,3510],{},"And last but not least, you might wish to get all this incredible efficiency ",[18,3508,3509],{},"without having to rewrite your codebase."," You don't want to change hundreds of lines of code to make this happen. You still remember the transition from Vue 2 to Vue 3, moving from the Options API to the Composition API. You're happy with the current paradigm, so you don't want to go through another major refactor. I don't blame you.",[11,3512,3513],{},"So the genie looks at you and says:",[3472,3515,3516],{},[11,3517,3518],{},"Your wishes are granted. You only have to add a keyword to your components. A single word. What should it be?",[11,3520,3521],{},"You look around, see all the mist, and say:",[3472,3523,3524],{},[11,3525,3526],{},"Vapor... Vue Vapor.",[65,3528,3530],{"id":3529},"vue-vapor-is-here","Vue Vapor is Here!",[11,3532,3533],{},"Of course, this is just a silly fable... but Vue Vapor is very real. It's a new compilation mode available in the upcoming version of Vue, Vue 3.6. And it ticks all the boxes:",[1475,3535,3536,3539,3542],{},[1478,3537,3538],{},"It's faster... way faster.",[1478,3540,3541],{},"It uses way less memory.",[1478,3543,3544],{},"And you can enable it without rewriting your components.",[11,3546,3547,3548,3551],{},"To turn Vapor on, all you need to do is add the ",[46,3549,3550],{},"vapor"," keyword to your script setup components:",[111,3553,3555],{"className":727,"code":3554,"language":729,"meta":116,"style":116},"\u003Cscript setup vapor>\nimport { ref } from 'vue'\n\nconst count = ref(0)\n\u003C/script>\n\n\u003Ctemplate>\n  \u003Cbutton @click=\"count++\">Count is {{ count }}\u003C/button>\n\u003C/template>\n",[46,3556,3557,3570,3580,3584,3601,3609,3613,3621,3652],{"__ignoreMap":116},[120,3558,3559,3561,3563,3565,3568],{"class":122,"line":123},[120,3560,736],{"class":134},[120,3562,739],{"class":294},[120,3564,742],{"class":130},[120,3566,3567],{"class":130}," vapor",[120,3569,753],{"class":134},[120,3571,3572,3574,3576,3578],{"class":122,"line":138},[120,3573,571],{"class":294},[120,3575,574],{"class":134},[120,3577,577],{"class":294},[120,3579,581],{"class":580},[120,3581,3582],{"class":122,"line":147},[120,3583,159],{"emptyLinePlaceholder":158},[120,3585,3586,3588,3591,3593,3595,3597,3599],{"class":122,"line":155},[120,3587,602],{"class":126},[120,3589,3590],{"class":134}," count ",[120,3592,295],{"class":294},[120,3594,610],{"class":130},[120,3596,312],{"class":134},[120,3598,615],{"class":298},[120,3600,367],{"class":134},[120,3602,3603,3605,3607],{"class":122,"line":162},[120,3604,793],{"class":134},[120,3606,739],{"class":294},[120,3608,753],{"class":134},[120,3610,3611],{"class":122,"line":171},[120,3612,159],{"emptyLinePlaceholder":158},[120,3614,3615,3617,3619],{"class":122,"line":177},[120,3616,736],{"class":134},[120,3618,808],{"class":294},[120,3620,753],{"class":134},[120,3622,3623,3625,3628,3630,3633,3635,3637,3640,3643,3645,3648,3650],{"class":122,"line":182},[120,3624,815],{"class":134},[120,3626,3627],{"class":294},"button",[120,3629,1136],{"class":134},[120,3631,3632],{"class":130},"click",[120,3634,295],{"class":134},[120,3636,831],{"class":134},[120,3638,3639],{"class":134},"count",[120,3641,3642],{"class":294},"++",[120,3644,831],{"class":134},[120,3646,3647],{"class":134},">Count is {{ count }}\u003C/",[120,3649,3627],{"class":294},[120,3651,753],{"class":134},[120,3653,3654,3656,3658],{"class":122,"line":391},[120,3655,793],{"class":134},[120,3657,808],{"class":294},[120,3659,753],{"class":134},[11,3661,3662],{},"I know, it sounds too good to be true! Feels like magic. But it's not. It's the result of years of work by the Vue team and many contributors.",[11,3664,3665],{},"Let's review the timeline:",[1475,3667,3668,3680,3686,3692],{},[1478,3669,3670,3673,3674,3679],{},[18,3671,3672],{},"At first:"," The project started in a separate repository, ",[540,3675,3678],{"href":3676,"rel":3677},"https://github.com/vuejs/vue-vapor",[544],"vuejs/vue-vapor",", and was later merged into the main Vue repo.",[1478,3681,3682,3685],{},[18,3683,3684],{},"A year ago:"," We got the first alpha release of Vue 3.6, which includes Vapor Mode.",[1478,3687,3688,3691],{},[18,3689,3690],{},"Now:"," We're on Vue 3.6 beta 13.",[1478,3693,3694,3697],{},[18,3695,3696],{},"Soon... ish:"," We'll have the release candidate and the final 3.6 version.",[11,3699,3700],{},"Once it ships, Vapor Mode will be available to everyone.",[65,3702,3704],{"id":3703},"vue-vapor-is-fast","Vue Vapor is Fast",[11,3706,3707],{},"Now, you've heard it many times: Vue Vapor is fast. But... how fast? How does it rank when compared to other frameworks? Well, let's find out.",[11,3709,3710,3711,535],{},"First, let's take a look at the ",[540,3712,3715],{"href":3713,"rel":3714},"https://krausest.github.io/js-framework-benchmark/",[544],"JavaScript Framework Benchmark",[11,3717,3718],{},"This is a very popular benchmark that measures the speed of different JavaScript frameworks when performing common DOM operations: creating rows, updating them, swapping them, removing them, and so on.",[11,3720,3721],{},"It gives each framework a score: a weighted geometric mean across all tasks. The lower the score, the faster the framework.",[1475,3723,3724,3727,3730,3733],{},[1478,3725,3726],{},"Vanilla JS sits at 1.03 — pretty, pretty good. You can't beat the OG.",[1478,3728,3729],{},"React, the most popular JavaScript framework in the world, comes in at 1.52.",[1478,3731,3732],{},"Now, where's Vue? Vue 3.6 alpha comes in at 1.26, not bad.",[1478,3734,3735],{},"But Svelte and Solid are noticeably faster, at 1.15 and 1.11.",[11,3737,3738,3739,3742],{},"Now, take a quick guess. Where does ",[18,3740,3741],{},"Vue Vapor"," land?",[11,3744,3745,3746,3749],{},"It gets a score of ",[18,3747,3748],{},"1.08",", sitting right next to Vanilla JS.",[11,3751,3752],{},"That is impressive. It means Vue Vapor can put Vue at the top of the pack among popular JavaScript frameworks when it comes to speed. That's outstanding.",[11,3754,3755],{},"But this benchmark used the alpha version. We have the beta now, so let's test that instead.",[1281,3757,3759],{"id":3758},"a-quick-pun-intended-demo","A quick (pun intended) demo",[11,3761,3762],{},"We'll test the same component in three different versions: Vue 3.5, Vue 3.6, and Vue 3.6 with Vapor Mode enabled.",[11,3764,3765],{},"All three demos use exactly the same component. We have a grid of 10,000 blocks and a button that updates one of them. We measure how long that update takes. We start a timer right before the change and stop it immediately after.",[1475,3767,3768,3780,3791],{},[1478,3769,3770,3775,3776,3779],{},[540,3771,3774],{"href":3772,"rel":3773},"https://vue-35.nicodevs.com/",[544],"Vue 3.5"," — about ",[18,3777,3778],{},"16 milliseconds"," to update a single block.",[1478,3781,3782,3775,3787,3790],{},[540,3783,3786],{"href":3784,"rel":3785},"https://vue-36-vdom.nicodevs.com/",[544],"Vue 3.6",[18,3788,3789],{},"10 milliseconds",". Pretty fast, not gonna lie.",[1478,3792,3793,3798,3799,3801,3802,3805,3806,535],{},[540,3794,3797],{"href":3795,"rel":3796},"https://vue-36-vapor.nicodevs.com/",[544],"Vue 3.6 + Vapor"," — same version, same component. The only thing we changed was adding the ",[46,3800,3550],{}," keyword to ",[46,3803,3804],{},"\u003Cscript setup>",". Other than that, it's identical. And we get... ",[18,3807,3808],{},"under one millisecond",[11,3810,3811,3812,3815,3816,3818],{},"From 10 milliseconds to around 0.2 milliseconds for the exact same update. About ",[18,3813,3814],{},"50 times faster",", just by adding the ",[46,3817,3550],{}," keyword.",[11,3820,3821],{},"That's pretty cool, right? Honestly, it's incredible. It's super rare to see this kind of performance jump in a minor framework release.",[11,3823,3824],{},"This can have a huge impact on applications with lots of nodes, lots of components, or real-time updates.",[11,3826,3827,3828,3831],{},"Going from 10 ms to 0.2 ms is 50 times faster. If you prefer percentages, that's a ",[18,3829,3830],{},"4,900% speed increase",". That's the kind of number that can impress your boss!",[65,3833,3835],{"id":3834},"memory-usage","Memory Usage",[11,3837,3838,3839],{},"But speed isn't the only performance win here. Speed was just the first wish. Let's move on to the second one: ",[18,3840,3841],{},"memory usage.",[11,3843,3844],{},"Now, why does memory matter? Well, we've all used applications that consume way too much memory. They drop frames, scrolling gets choppy, everything starts to feel sluggish. It's the worst.",[11,3846,3847,3848,3853],{},"So I ran three experiments, took heap snapshots, and calculated the average memory usage for each one. The code is public at ",[540,3849,3852],{"href":3850,"rel":3851},"https://github.com/nicodevs/demo-vue-memory-usage",[544],"github.com/nicodevs/demo-vue-memory-usage",", so if you want to verify the numbers yourself, you can go to the repo and run the tests.",[1475,3855,3856,3859,3866],{},[1478,3857,3858],{},"First, we have a table of 10,000 rows with dynamic content. Vue 3.6 without Vapor uses around 10 MB of RAM, while Vue Vapor uses only about 3 MB. We cut memory usage down to roughly a third.",[1478,3860,3861,3862,3865],{},"For the second test, I extracted each row into a ",[46,3863,3864],{},"Row"," component. It's basically the same HTML, but now we're using a component instead of rendering everything directly. Vue 3.6 jumps to more than 23 MB, while Vue Vapor stays roughly at the same level.",[1478,3867,3868,3869,3871],{},"And then we can take it one step further. What happens if that ",[46,3870,3864],{}," component doesn't render dynamic content at all and just displays a static \"Hello!\"? In that case, the difference is even bigger. More than 10 times lighter.",[11,3873,3874],{},"Let's check the final results:",[3876,3877,3878,3895],"table",{},[3879,3880,3881],"thead",{},[3882,3883,3884,3888,3890,3892],"tr",{},[3885,3886,3887],"th",{},"Scenario",[3885,3889,3786],{},[3885,3891,3797],{},[3885,3893,3894],{},"Reduction",[3896,3897,3898,3913,3927],"tbody",{},[3882,3899,3900,3904,3907,3910],{},[3901,3902,3903],"td",{},"10k rows",[3901,3905,3906],{},"~9.9 MB",[3901,3908,3909],{},"~2.9 MB",[3901,3911,3912],{},"3.4×",[3882,3914,3915,3918,3921,3924],{},[3901,3916,3917],{},"10k components",[3901,3919,3920],{},"~23.2 MB",[3901,3922,3923],{},"~3.2 MB",[3901,3925,3926],{},"7.3×",[3882,3928,3929,3932,3935,3938],{},[3901,3930,3931],{},"10k static components",[3901,3933,3934],{},"~19.2 MB",[3901,3936,3937],{},"~1.9 MB",[3901,3939,3940],{},"10.1×",[11,3942,3943],{},"You can try it yourself! Clone the repo and run the benchmark:",[111,3945,3949],{"className":3946,"code":3947,"language":3948,"meta":116,"style":116},"language-shell shiki shiki-themes monokai","git clone https://github.com/nicodevs/demo-vue-memory-usage\ncd demo-vue-memory-usage\nnode benchmark.mjs\n","shell",[46,3950,3951,3962,3971],{"__ignoreMap":116},[120,3952,3953,3956,3959],{"class":122,"line":123},[120,3954,3955],{"class":130},"git",[120,3957,3958],{"class":580}," clone",[120,3960,3961],{"class":580}," https://github.com/nicodevs/demo-vue-memory-usage\n",[120,3963,3964,3968],{"class":122,"line":138},[120,3965,3967],{"class":3966},"sYf5A","cd",[120,3969,3970],{"class":580}," demo-vue-memory-usage\n",[120,3972,3973,3976],{"class":122,"line":147},[120,3974,3975],{"class":130},"node",[120,3977,3978],{"class":580}," benchmark.mjs\n",[11,3980,3981],{},"So how can we explain this? What's making Vapor so efficient? How can adding a single keyword have such a massive impact?",[65,3983,3985],{"id":3984},"goodbye-virtual-dom","Goodbye, Virtual DOM",[11,3987,3988,3989,3992],{},"We can explain it with a single change: ",[18,3990,3991],{},"Vapor renders your templates without relying on the Virtual DOM."," But hold on.... what is the Virtual DOM?",[11,3994,3995],{},"Modern JS frameworks like React and Vue use the Virtual DOM to handle DOM changes. When a certain piece of state changes, you need to update the DOM accordingly. Now, touching the DOM is an expensive operation: it takes time and forces the browser to repaint that part of the page. The Virtual DOM helps us reduce the number of nodes we patch.",[11,3997,3998],{},"Consider the following component:",[111,4000,4002],{"className":727,"code":4001,"language":729,"meta":116,"style":116},"\u003Cscript setup>\nimport { ref } from 'vue'\n\nconst products = ref([\n  { id: 1, name: 'Keyboard', price: 49 },\n  { id: 2, name: 'Mouse', price: 25 },\n  { id: 3, name: 'Webcam', price: 75 },\n  { id: 4, name: 'Monitor', price: 199 },\n])\n\u003C/script>\n\n\u003Ctemplate>\n  \u003Cul>\n    \u003Cli v-for=\"product in products\" :key=\"product.id\">\n      \u003Cstrong>{{ product.name }}\u003C/strong>\n      \u003Cspan>$ {{ product.price }}\u003C/span>\n    \u003C/li>\n  \u003C/ul>\n\u003C/template>\n",[46,4003,4004,4014,4024,4028,4042,4065,4083,4102,4121,4126,4134,4138,4146,4154,4191,4205,4218,4227,4235],{"__ignoreMap":116},[120,4005,4006,4008,4010,4012],{"class":122,"line":123},[120,4007,736],{"class":134},[120,4009,739],{"class":294},[120,4011,742],{"class":130},[120,4013,753],{"class":134},[120,4015,4016,4018,4020,4022],{"class":122,"line":138},[120,4017,571],{"class":294},[120,4019,574],{"class":134},[120,4021,577],{"class":294},[120,4023,581],{"class":580},[120,4025,4026],{"class":122,"line":147},[120,4027,159],{"emptyLinePlaceholder":158},[120,4029,4030,4032,4035,4037,4039],{"class":122,"line":155},[120,4031,602],{"class":126},[120,4033,4034],{"class":134}," products ",[120,4036,295],{"class":294},[120,4038,610],{"class":130},[120,4040,4041],{"class":134},"([\n",[120,4043,4044,4047,4050,4053,4056,4059,4062],{"class":122,"line":162},[120,4045,4046],{"class":134},"  { id: ",[120,4048,4049],{"class":298},"1",[120,4051,4052],{"class":134},", name: ",[120,4054,4055],{"class":580},"'Keyboard'",[120,4057,4058],{"class":134},", price: ",[120,4060,4061],{"class":298},"49",[120,4063,4064],{"class":134}," },\n",[120,4066,4067,4069,4071,4073,4076,4078,4081],{"class":122,"line":171},[120,4068,4046],{"class":134},[120,4070,1549],{"class":298},[120,4072,4052],{"class":134},[120,4074,4075],{"class":580},"'Mouse'",[120,4077,4058],{"class":134},[120,4079,4080],{"class":298},"25",[120,4082,4064],{"class":134},[120,4084,4085,4087,4090,4092,4095,4097,4100],{"class":122,"line":177},[120,4086,4046],{"class":134},[120,4088,4089],{"class":298},"3",[120,4091,4052],{"class":134},[120,4093,4094],{"class":580},"'Webcam'",[120,4096,4058],{"class":134},[120,4098,4099],{"class":298},"75",[120,4101,4064],{"class":134},[120,4103,4104,4106,4109,4111,4114,4116,4119],{"class":122,"line":182},[120,4105,4046],{"class":134},[120,4107,4108],{"class":298},"4",[120,4110,4052],{"class":134},[120,4112,4113],{"class":580},"'Monitor'",[120,4115,4058],{"class":134},[120,4117,4118],{"class":298},"199",[120,4120,4064],{"class":134},[120,4122,4123],{"class":122,"line":391},[120,4124,4125],{"class":134},"])\n",[120,4127,4128,4130,4132],{"class":122,"line":398},[120,4129,793],{"class":134},[120,4131,739],{"class":294},[120,4133,753],{"class":134},[120,4135,4136],{"class":122,"line":403},[120,4137,159],{"emptyLinePlaceholder":158},[120,4139,4140,4142,4144],{"class":122,"line":410},[120,4141,736],{"class":134},[120,4143,808],{"class":294},[120,4145,753],{"class":134},[120,4147,4148,4150,4152],{"class":122,"line":415},[120,4149,815],{"class":134},[120,4151,1475],{"class":294},[120,4153,753],{"class":134},[120,4155,4156,4158,4160,4162,4164,4166,4169,4171,4174,4176,4178,4180,4182,4184,4187,4189],{"class":122,"line":420},[120,4157,895],{"class":134},[120,4159,1478],{"class":294},[120,4161,1048],{"class":294},[120,4163,295],{"class":134},[120,4165,831],{"class":134},[120,4167,4168],{"class":134},"product ",[120,4170,984],{"class":294},[120,4172,4173],{"class":134}," products",[120,4175,831],{"class":134},[120,4177,1065],{"class":134},[120,4179,997],{"class":130},[120,4181,295],{"class":134},[120,4183,831],{"class":134},[120,4185,4186],{"class":134},"product.id",[120,4188,831],{"class":134},[120,4190,753],{"class":134},[120,4192,4193,4196,4198,4201,4203],{"class":122,"line":904},[120,4194,4195],{"class":134},"      \u003C",[120,4197,18],{"class":294},[120,4199,4200],{"class":134},">{{ product.name }}\u003C/",[120,4202,18],{"class":294},[120,4204,753],{"class":134},[120,4206,4207,4209,4211,4214,4216],{"class":122,"line":915},[120,4208,4195],{"class":134},[120,4210,120],{"class":294},[120,4212,4213],{"class":134},">$ {{ product.price }}\u003C/",[120,4215,120],{"class":294},[120,4217,753],{"class":134},[120,4219,4220,4223,4225],{"class":122,"line":1214},[120,4221,4222],{"class":134},"    \u003C/",[120,4224,1478],{"class":294},[120,4226,753],{"class":134},[120,4228,4229,4231,4233],{"class":122,"line":1882},[120,4230,907],{"class":134},[120,4232,1475],{"class":294},[120,4234,753],{"class":134},[120,4236,4237,4239,4241],{"class":122,"line":1895},[120,4238,793],{"class":134},[120,4240,808],{"class":294},[120,4242,753],{"class":134},[11,4244,4245],{},"If a couple of prices change (inflation!), as result of an interaction or API request, we would need to reflect that change on the screen.",[11,4247,4248,4249,4252],{},"Now, we wouldn't want to patch the whole list. There are nodes that don't need to be updated, like all the product names and the prices that didn't change. We just need to patch two ",[46,4250,4251],{},"\u003Cspan>","s!",[11,4254,4255],{},"Then, how can we figure out which nodes need patching? Well, perhaps we can have an in-memory copy of the real DOM. This virtual tree is the Virtual DOM.",[11,4257,4258,4259,4262],{},"On a ",[18,4260,4261],{},"state change,"," the runtime creates a new tree and compares it to the old one: it walks through it, node by node, checking the differences. Once it gathers those differences, it patches the real DOM, affecting only those nodes.",[11,4264,4265],{},"Now, Vue 3 is already clever about this. It has some tricks to make it better:",[1475,4267,4268,4274,4280],{},[1478,4269,4270,4273],{},[18,4271,4272],{},"Static hoisting"," — static nodes are created once and reused",[1478,4275,4276,4279],{},[18,4277,4278],{},"Patch flags"," — each dynamic binding is tagged so Vue knows exactly where to look",[1478,4281,4282,4285],{},[18,4283,4284],{},"Tree flattening"," — all dynamic nodes are collected into one flat list",[11,4287,4288],{},"But look at what's left: we still rebuild that whole tree of objects, recheck every dynamic value in the list, compare each one just to find the two that moved... All those objects, created and thrown away, on every single change. That's a lot of work.",[11,4290,4291,4292,4295],{},"So, what's the story with Vue Vapor? Well, ",[18,4293,4294],{},"Vapor doesn't use the Virtual DOM",". No copy, no rebuild, no diffing.",[11,4297,4298],{},"You might be wondering: if there's no copy and nothing to compare, how does Vapor know what to update? It knows thanks to the compiler.",[11,4300,4301],{},"When Vapor compiles your component, it follows the exact path from each piece of state to the one DOM node it controls, and wires them together.",[11,4303,4304,4305,4308,4309,4311],{},"That's the reason Vapor asks for one thing: ",[46,4306,4307],{},"script setup",". Vapor only works with single-file components using ",[46,4310,4307],{},", for this very reason: it gives the compiler a clear, predictable structure to read.",[11,4313,4314,4315,4318],{},"We call this ",[18,4316,4317],{},"fine-grained reactivity",". Solid has been doing it for years, and now it's available in Vue.",[11,4320,4321],{},"Thanks to this paradigm shift, the updates in Vue Vapor apps are much faster and take way less memory. That's two wishes down! But the third one was the big one: get all of this, without rewriting your code. That's my favorite part. So let's turn Vapor on.",[65,4323,4325],{"id":4324},"installing-vapor","Installing Vapor",[11,4327,4328],{},"First, you'll need Vue 3.6 beta. If you create a fresh project, it will ask you if you want to use 3.6:",[111,4330,4332],{"className":3946,"code":4331,"language":3948,"meta":116,"style":116},"pnpm create vue@latest\n",[46,4333,4334],{"__ignoreMap":116},[120,4335,4336,4339,4342],{"class":122,"line":123},[120,4337,4338],{"class":130},"pnpm",[120,4340,4341],{"class":580}," create",[120,4343,4344],{"class":580}," vue@latest\n",[111,4346,4351],{"className":4347,"code":4349,"language":4350},[4348],"language-text","> Select experimental features to include in your project:\n> Vue 3.6 (beta)\n","text",[46,4352,4349],{"__ignoreMap":116},[11,4354,4355,4356,4359],{},"If you are on an existing 3.5 application, here's what you can do. Open your ",[46,4357,4358],{},"package.json"," and point Vue at the beta:",[111,4361,4365],{"className":4362,"code":4363,"language":4364,"meta":116,"style":116},"language-json shiki shiki-themes monokai","{\n  \"dependencies\": {\n    \"vue\": \"beta\"\n  }\n}\n","json",[46,4366,4367,4372,4380,4392,4396],{"__ignoreMap":116},[120,4368,4369],{"class":122,"line":123},[120,4370,4371],{"class":134},"{\n",[120,4373,4374,4377],{"class":122,"line":138},[120,4375,4376],{"class":126},"  \"dependencies\"",[120,4378,4379],{"class":134},": {\n",[120,4381,4382,4385,4388],{"class":122,"line":147},[120,4383,4384],{"class":126},"    \"vue\"",[120,4386,4387],{"class":134},": ",[120,4389,4391],{"class":4390},"susgL","\"beta\"\n",[120,4393,4394],{"class":122,"line":155},[120,4395,2165],{"class":134},[120,4397,4398],{"class":122,"line":162},[120,4399,174],{"class":134},[11,4401,4402,4403,4405],{},"But there's a catch. Vue isn't one package, it's a whole family of them: the runtime, the compiler, and so on. And your build tools quietly depend on some of those too. So if you only bump Vue, your compiler can stay behind on 3.5 — and a 3.5 compiler has no idea what the ",[46,4404,3550],{}," keyword means.",[11,4407,4408,4409,4412,4413,4416],{},"So if you are using ",[18,4410,4411],{},"npm,"," you may need to use the ",[46,4414,4415],{},"overrides"," block:",[111,4418,4420],{"className":4362,"code":4419,"language":4364,"meta":116,"style":116},"{\n  \"overrides\": {\n    \"@vue/compiler-core\": \"beta\",\n    \"@vue/compiler-dom\": \"beta\",\n    \"@vue/compiler-sfc\": \"beta\",\n    \"@vue/compiler-ssr\": \"beta\",\n    \"@vue/compiler-vapor\": \"beta\",\n    \"@vue/reactivity\": \"beta\",\n    \"@vue/runtime-core\": \"beta\",\n    \"@vue/runtime-dom\": \"beta\",\n    \"@vue/runtime-vapor\": \"beta\",\n    \"@vue/server-renderer\": \"beta\",\n    \"@vue/shared\": \"beta\",\n    \"@vue/compat\": \"beta\"\n  }\n}\n",[46,4421,4422,4426,4433,4445,4456,4467,4478,4489,4500,4511,4522,4533,4544,4555,4564,4568],{"__ignoreMap":116},[120,4423,4424],{"class":122,"line":123},[120,4425,4371],{"class":134},[120,4427,4428,4431],{"class":122,"line":138},[120,4429,4430],{"class":126},"  \"overrides\"",[120,4432,4379],{"class":134},[120,4434,4435,4438,4440,4443],{"class":122,"line":147},[120,4436,4437],{"class":126},"    \"@vue/compiler-core\"",[120,4439,4387],{"class":134},[120,4441,4442],{"class":4390},"\"beta\"",[120,4444,1385],{"class":134},[120,4446,4447,4450,4452,4454],{"class":122,"line":155},[120,4448,4449],{"class":126},"    \"@vue/compiler-dom\"",[120,4451,4387],{"class":134},[120,4453,4442],{"class":4390},[120,4455,1385],{"class":134},[120,4457,4458,4461,4463,4465],{"class":122,"line":162},[120,4459,4460],{"class":126},"    \"@vue/compiler-sfc\"",[120,4462,4387],{"class":134},[120,4464,4442],{"class":4390},[120,4466,1385],{"class":134},[120,4468,4469,4472,4474,4476],{"class":122,"line":171},[120,4470,4471],{"class":126},"    \"@vue/compiler-ssr\"",[120,4473,4387],{"class":134},[120,4475,4442],{"class":4390},[120,4477,1385],{"class":134},[120,4479,4480,4483,4485,4487],{"class":122,"line":177},[120,4481,4482],{"class":126},"    \"@vue/compiler-vapor\"",[120,4484,4387],{"class":134},[120,4486,4442],{"class":4390},[120,4488,1385],{"class":134},[120,4490,4491,4494,4496,4498],{"class":122,"line":182},[120,4492,4493],{"class":126},"    \"@vue/reactivity\"",[120,4495,4387],{"class":134},[120,4497,4442],{"class":4390},[120,4499,1385],{"class":134},[120,4501,4502,4505,4507,4509],{"class":122,"line":391},[120,4503,4504],{"class":126},"    \"@vue/runtime-core\"",[120,4506,4387],{"class":134},[120,4508,4442],{"class":4390},[120,4510,1385],{"class":134},[120,4512,4513,4516,4518,4520],{"class":122,"line":398},[120,4514,4515],{"class":126},"    \"@vue/runtime-dom\"",[120,4517,4387],{"class":134},[120,4519,4442],{"class":4390},[120,4521,1385],{"class":134},[120,4523,4524,4527,4529,4531],{"class":122,"line":403},[120,4525,4526],{"class":126},"    \"@vue/runtime-vapor\"",[120,4528,4387],{"class":134},[120,4530,4442],{"class":4390},[120,4532,1385],{"class":134},[120,4534,4535,4538,4540,4542],{"class":122,"line":410},[120,4536,4537],{"class":126},"    \"@vue/server-renderer\"",[120,4539,4387],{"class":134},[120,4541,4442],{"class":4390},[120,4543,1385],{"class":134},[120,4545,4546,4549,4551,4553],{"class":122,"line":415},[120,4547,4548],{"class":126},"    \"@vue/shared\"",[120,4550,4387],{"class":134},[120,4552,4442],{"class":4390},[120,4554,1385],{"class":134},[120,4556,4557,4560,4562],{"class":122,"line":420},[120,4558,4559],{"class":126},"    \"@vue/compat\"",[120,4561,4387],{"class":134},[120,4563,4391],{"class":4390},[120,4565,4566],{"class":122,"line":904},[120,4567,2165],{"class":134},[120,4569,4570],{"class":122,"line":915},[120,4571,174],{"class":134},[11,4573,4574,4575,4578],{},"Then delete your lock file and your ",[46,4576,4577],{},"node_modules",", reinstall, and you're on 3.6.",[11,4580,4581],{},"A word of advice: for now, just try it out. Don't move your production applications to Vue 3.6 Vapor just yet. We are still in beta. But feel free to test it out locally and see how it works.",[65,4583,4585],{"id":4584},"using-vapor","Using Vapor",[11,4587,4588,4589,4591,4592,3818],{},"Now you're ready to opt into Vapor. You already know how to do it: on any Single-File Component using ",[46,4590,4307],{},", add the ",[46,4593,3550],{},[111,4595,4596],{"className":727,"code":3554,"language":729,"meta":116,"style":116},[46,4597,4598,4610,4620,4624,4640,4648,4652,4660,4686],{"__ignoreMap":116},[120,4599,4600,4602,4604,4606,4608],{"class":122,"line":123},[120,4601,736],{"class":134},[120,4603,739],{"class":294},[120,4605,742],{"class":130},[120,4607,3567],{"class":130},[120,4609,753],{"class":134},[120,4611,4612,4614,4616,4618],{"class":122,"line":138},[120,4613,571],{"class":294},[120,4615,574],{"class":134},[120,4617,577],{"class":294},[120,4619,581],{"class":580},[120,4621,4622],{"class":122,"line":147},[120,4623,159],{"emptyLinePlaceholder":158},[120,4625,4626,4628,4630,4632,4634,4636,4638],{"class":122,"line":155},[120,4627,602],{"class":126},[120,4629,3590],{"class":134},[120,4631,295],{"class":294},[120,4633,610],{"class":130},[120,4635,312],{"class":134},[120,4637,615],{"class":298},[120,4639,367],{"class":134},[120,4641,4642,4644,4646],{"class":122,"line":162},[120,4643,793],{"class":134},[120,4645,739],{"class":294},[120,4647,753],{"class":134},[120,4649,4650],{"class":122,"line":171},[120,4651,159],{"emptyLinePlaceholder":158},[120,4653,4654,4656,4658],{"class":122,"line":177},[120,4655,736],{"class":134},[120,4657,808],{"class":294},[120,4659,753],{"class":134},[120,4661,4662,4664,4666,4668,4670,4672,4674,4676,4678,4680,4682,4684],{"class":122,"line":182},[120,4663,815],{"class":134},[120,4665,3627],{"class":294},[120,4667,1136],{"class":134},[120,4669,3632],{"class":130},[120,4671,295],{"class":134},[120,4673,831],{"class":134},[120,4675,3639],{"class":134},[120,4677,3642],{"class":294},[120,4679,831],{"class":134},[120,4681,3647],{"class":134},[120,4683,3627],{"class":294},[120,4685,753],{"class":134},[120,4687,4688,4690,4692],{"class":122,"line":391},[120,4689,793],{"class":134},[120,4691,808],{"class":294},[120,4693,753],{"class":134},[11,4695,4696,4697,4699,4700,535],{},"Just remember the one rule: it has to be a Single-File Component using ",[46,4698,4307],{},". No Options API, and no Composition API outside of ",[46,4701,4307],{},[11,4703,4704],{},"Now, should you convert every component? You can. But the beautiful thing is, you don't have to.",[11,4706,4707],{},"Your app can run Vapor and classic components together, no problem. A classic component can hold Vapor children, or the other way around. The compiler can handle it.",[11,4709,4710],{},"And just like that, the third wish, granted. We get a massive performance boost, keeping the code we already have, just adding a keyword.",[65,4712,4714],{"id":4713},"wrapping-up","Wrapping Up",[11,4716,4717],{},"I hope you enjoyed this post! I'm super excited about Vapor and the future of Vue.",[11,4719,4720,4721,4724],{},"The demos, slides, and the memory benchmark script are all at ",[540,4722,4723],{"href":3451},"nicodevs.com/frontendnation",". Give it a spin and let me know what you find.",[11,4726,3403],{},[3405,4728,4729],{},"html pre.shiki code .sCdxs, html code.shiki .sCdxs{--shiki-default:#F8F8F2}html pre.shiki code .s8I7P, html code.shiki .s8I7P{--shiki-default:#F92672}html pre.shiki code .sHkqI, html code.shiki .sHkqI{--shiki-default:#A6E22E}html pre.shiki code .s_Ekj, html code.shiki .s_Ekj{--shiki-default:#E6DB74}html pre.shiki code .sOx1s, html code.shiki .sOx1s{--shiki-default:#66D9EF;--shiki-default-font-style:italic}html pre.shiki code .s7s5_, html code.shiki .s7s5_{--shiki-default:#AE81FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sYf5A, html code.shiki .sYf5A{--shiki-default:#66D9EF}html pre.shiki code .susgL, html code.shiki .susgL{--shiki-default:#CFCFC2}",{"title":116,"searchDepth":138,"depth":138,"links":4731},[4732,4733,4734,4737,4738,4739,4740,4741],{"id":3466,"depth":138,"text":3467},{"id":3529,"depth":138,"text":3530},{"id":3703,"depth":138,"text":3704,"children":4735},[4736],{"id":3758,"depth":147,"text":3759},{"id":3834,"depth":138,"text":3835},{"id":3984,"depth":138,"text":3985},{"id":4324,"depth":138,"text":4325},{"id":4584,"depth":138,"text":4585},{"id":4713,"depth":138,"text":4714},"2026-06-04","Vue Vapor is a new compilation mode that ditches the Virtual DOM entirely. It's faster, lighter, and you can enable it with a single keyword — no refactoring needed.","/img/blog/vue-vapor-goodbye-virtual-dom.png",{},"/blog/vue-vapor-goodbye-virtual-dom",{"title":3438,"description":4743},"blog/vue-vapor-goodbye-virtual-dom","Vzz1w5hqvVDd7zNuKMK_uD6na9YmTFyJ4widWqBvtbM",{"id":4751,"title":4752,"body":4753,"category":3425,"date":6520,"description":6521,"draft":3428,"extension":3429,"image":6522,"meta":6523,"navigation":158,"path":6524,"seo":6525,"stem":6526,"tighten":3428,"__hash__":6527},"blog/blog/real-time-vue-apps-part-3-webrtc-and-peerjs.md","Real-Time Vue Apps, Part 3: WebRTC and PeerJS",{"type":8,"value":4754,"toc":6508},[4755,4765,4778,4781,4785,4788,4808,4812,4818,4821,4824,4841,4844,4847,4862,4866,4869,4872,4880,4883,4888,4898,4973,4976,4990,4994,5001,5005,5012,5018,5033,5037,5043,5046,5055,5058,5062,5068,5071,5075,5078,5081,5086,5093,5096,5100,5103,5690,5693,5700,5704,5715,5718,5727,5730,5733,5740,5766,5769,5872,5878,5945,5954,6148,6151,6155,6158,6460,6463,6467,6470,6492,6495,6502,6505],[3442,4756,4758],{"color":4757,"icon":3445},"#ff8a3d",[11,4759,4760,4761,4764],{},"Hello there! This article is adapted from my talk at MadVue 2026, the amazing Vue conference that took place in Madrid, Spain. Make sure to check out ",[540,4762,3452],{"href":4763},"/madvue"," for the slides, all the articles and some pics!",[11,4766,4767,4768,4772,4773,4777],{},"This is the third and final article in a three-part series about adding real-time features to your Vue and Nuxt applications. In ",[540,4769,4771],{"href":4770},"/blog/real-time-vue-apps-part-1-polling-and-server-sent-events","Part 1"," we covered Server-Sent Events, and in ",[540,4774,4776],{"href":4775},"/blog/real-time-vue-apps-part-2-websockets-and-real-time-databases","Part 2"," we covered WebSockets and real-time databases.",[11,4779,4780],{},"We ended Part 2 with a question: WebSockets are great, but you depend on a server. What if you don't want a server in the middle? What if you could connect directly to a peer?",[65,4782,4784],{"id":4783},"why-peer-to-peer","Why Peer-to-Peer?",[11,4786,4787],{},"There are some very valid reasons to go for a peer-to-peer solution. Some of these might apply to you:",[1475,4789,4790,4796,4802],{},[1478,4791,4792,4795],{},[18,4793,4794],{},"Latency."," Imagine you're close to your peer, but the server is far away from both of you. Instead of sending data to the server and waiting for the server to relay it to your peer, you exchange the data directly. That can be much quicker in many scenarios.",[1478,4797,4798,4801],{},[18,4799,4800],{},"Privacy."," You share data directly with your peer, with no third party able to read or leak it... accidentally, of course.",[1478,4803,4804,4807],{},[18,4805,4806],{},"Cost."," With no server in the middle, you have no bandwidth bill. This is great for sharing audio and video, at no extra cost.",[65,4809,4811],{"id":4810},"webrtc","WebRTC",[11,4813,4814,4815,4817],{},"We have a technology that ticks all those boxes: ",[18,4816,4811],{}," (Web Real-Time Communication).",[11,4819,4820],{},"With WebRTC you can send data peer to peer, end to end encrypted by default. And you're not limited to text: you can also share audio and video, which makes it the ideal solution for video calls.",[11,4822,4823],{},"Now, if you've ever tried to implement WebRTC, you may have stumbled into a wall of lovely acronyms:",[1475,4825,4826,4829,4832,4835,4838],{},[1478,4827,4828],{},"SDP (Session Description Protocol)",[1478,4830,4831],{},"STUN (Session Traversal Utilities for NAT)",[1478,4833,4834],{},"TURN (Traversal Using Relays around NAT)",[1478,4836,4837],{},"ICE (Interactive Connectivity Establishment)",[1478,4839,4840],{},"And... trickle ICE?",[11,4842,4843],{},"If it feels overwhelming... I don't blame you. To be honest, WebRTC is awesome, but it's kind of tricky.",[11,4845,4846],{},"So I have two things for you in this article:",[1475,4848,4849,4856],{},[1478,4850,4851,4852,4855],{},"A ",[18,4853,4854],{},"quick explanation"," of all the scary parts.",[1478,4857,4858,4859,535],{},"A library that makes working with WebRTC ",[18,4860,4861],{},"way less complicated",[1281,4863,4865],{"id":4864},"how-does-webrtc-work","How Does WebRTC Work?",[11,4867,4868],{},"Let's start with the confusing part, and make it less confusing. By reviewing the flow, we'll understand what these acronyms mean.",[11,4870,4871],{},"Think of it this way. If you want to call me using WebRTC, what do you need? Well, you need to know at least two things:",[1475,4873,4874,4877],{},[1478,4875,4876],{},"The codecs my browser supports, so we can share audio and video.",[1478,4878,4879],{},"And where to find me: a network address.",[11,4881,4882],{},"And vice versa, I need to know the same about you. Peers have to exchange that information in a standard, predefined way that browsers can understand.",[4884,4885,4887],"h4",{"id":4886},"sdp","SDP",[11,4889,4890,4891,4893,4894,4897],{},"For that, we use an ",[18,4892,4887],{},", a ",[18,4895,4896],{},"Session Description Protocol",". It looks something like this:",[111,4899,4903],{"className":4900,"code":4901,"language":4902,"meta":116,"style":116},"language-sh shiki shiki-themes monokai","# Metadata\nv=0\no=- 461173140043 2 IN IP4 127.0.0.1\ns=-\nt=0 0\n\n# Codecs\nm=audio 9 UDP/TLS/RTP/SAVPF 111\na=rtpmap:111 opus/48000/2\nm=video 9 UDP/TLS/RTP/SAVPF 96\na=rtpmap:96 VP8/90000\n\n# ICE (Interactive Connectivity Establishment) candidates\na=candidate:1 1 udp 2130706431 abc123.local 54321 typ host\n","sh",[46,4904,4905,4910,4915,4920,4925,4930,4934,4939,4944,4949,4954,4959,4963,4968],{"__ignoreMap":116},[120,4906,4907],{"class":122,"line":123},[120,4908,4909],{},"# Metadata\n",[120,4911,4912],{"class":122,"line":138},[120,4913,4914],{},"v=0\n",[120,4916,4917],{"class":122,"line":147},[120,4918,4919],{},"o=- 461173140043 2 IN IP4 127.0.0.1\n",[120,4921,4922],{"class":122,"line":155},[120,4923,4924],{},"s=-\n",[120,4926,4927],{"class":122,"line":162},[120,4928,4929],{},"t=0 0\n",[120,4931,4932],{"class":122,"line":171},[120,4933,159],{"emptyLinePlaceholder":158},[120,4935,4936],{"class":122,"line":177},[120,4937,4938],{},"# Codecs\n",[120,4940,4941],{"class":122,"line":182},[120,4942,4943],{},"m=audio 9 UDP/TLS/RTP/SAVPF 111\n",[120,4945,4946],{"class":122,"line":391},[120,4947,4948],{},"a=rtpmap:111 opus/48000/2\n",[120,4950,4951],{"class":122,"line":398},[120,4952,4953],{},"m=video 9 UDP/TLS/RTP/SAVPF 96\n",[120,4955,4956],{"class":122,"line":403},[120,4957,4958],{},"a=rtpmap:96 VP8/90000\n",[120,4960,4961],{"class":122,"line":410},[120,4962,159],{"emptyLinePlaceholder":158},[120,4964,4965],{"class":122,"line":415},[120,4966,4967],{},"# ICE (Interactive Connectivity Establishment) candidates\n",[120,4969,4970],{"class":122,"line":420},[120,4971,4972],{},"a=candidate:1 1 udp 2130706431 abc123.local 54321 typ host\n",[11,4974,4975],{},"The SDP has a few parts:",[1475,4977,4978,4981,4984],{},[1478,4979,4980],{},"The first is just metadata, don't worry too much about that",[1478,4982,4983],{},"Then the codecs: these are the ones my browser supports",[1478,4985,4986,4987],{},"Last but not least, the ",[18,4988,4989],{},"ICE candidates",[4884,4991,4993],{"id":4992},"ice","ICE",[11,4995,4996,4997,5000],{},"ICE stands for ",[18,4998,4999],{},"Interactive Connectivity Establishment",". These are all the possible network endpoints that can be used to connect to me. They are called candidates because the browsers pick the one that works best for both sides. For example, if two peers are on the same WiFi, they'll happily use their local IPs and skip the public internet entirely.",[4884,5002,5004],{"id":5003},"stun","STUN",[11,5006,5007,5008,5011],{},"Now, the browser can list all your local addresses, but it usually doesn't know your public IP. By default, your local address is even masked behind a fake ",[46,5009,5010],{},".local"," hostname (for privacy, so a random page can't fingerprint you by your network).",[11,5013,5014,5015,5017],{},"So how do you get your public IP? You ask someone outside. That's what a ",[18,5016,4831],{}," server is for: you send it a packet, and it pongs back \"this is the IP and port I see from the outside, that must be your public IP.\" That's it. Thanks to the STUN server, we can add a candidate with our public IP to the list of ICE candidates.",[111,5019,5021],{"className":4900,"code":5020,"language":4902,"meta":116,"style":116},"# STUN server (stun.l.google.com:19302) gives me my public IP\na=candidate:2 1 udp 1694498815 203.0.113.1 54321 typ srflx raddr 0.0.0.0 rport 0\n",[46,5022,5023,5028],{"__ignoreMap":116},[120,5024,5025],{"class":122,"line":123},[120,5026,5027],{},"# STUN server (stun.l.google.com:19302) gives me my public IP\n",[120,5029,5030],{"class":122,"line":138},[120,5031,5032],{},"a=candidate:2 1 udp 1694498815 203.0.113.1 54321 typ srflx raddr 0.0.0.0 rport 0\n",[4884,5034,5036],{"id":5035},"trickle-ice","Trickle ICE",[11,5038,5039,5040,5042],{},"Now, gathering all these candidates can take a few seconds. So you might resort to a technique known as ",[18,5041,5036],{},". You send the SDP first, and then send the candidates over as you discover them, over time, asynchronously.",[11,5044,5045],{},"In your SDP, you announce it like this:",[111,5047,5049],{"className":4900,"code":5048,"language":4902,"meta":116,"style":116},"a=ice-options:trickle\n",[46,5050,5051],{"__ignoreMap":116},[120,5052,5053],{"class":122,"line":123},[120,5054,5048],{},[11,5056,5057],{},"And then you send each candidate to the peer as soon as the browser hands it to you.",[4884,5059,5061],{"id":5060},"turn","TURN",[11,5063,5064,5065,5067],{},"Sometimes peers can't reach each other directly because they're behind strict firewalls, symmetric NATs, or corporate networks. In those cases, you can decide to fall back to a ",[18,5066,5061],{}," server, which actually forwards the data between peers.",[11,5069,5070],{},"It's slower, it costs bandwidth, and it kind of defeats the \"no server in the middle\" promise. But it's the safety net when a direct connection just isn't possible. It's totally optional, so we can avoid opting in if you don't need it.",[1281,5072,5074],{"id":5073},"the-signaling-server","The Signaling Server",[11,5076,5077],{},"Alright! How do peers actually share their SDP and ICE candidates? Well, WebRTC doesn't care. The spec leaves that completely up to you. Write it on a napkin, send a carrier pigeon, whatever works, as long as the data gets to the other peer.",[11,5079,5080],{},"The practical answer, of course, is a server.",[3472,5082,5083],{},[11,5084,5085],{},"Nico, you cheater! You told us we didn't need a server!",[11,5087,5088,5089,5092],{},"I hear you! You are right. But this server, which we call ",[18,5090,5091],{},"the signaling server,"," is only there so the peers can exchange connection data. Once they do, the server is out of the picture. It can go down, get unplugged, catch on fire. The peers keep talking.",[11,5094,5095],{},"Once the peers are connected, they can start sending each other text, audio, and video directly. No server in the middle!",[65,5097,5099],{"id":5098},"now-the-code","Now, the Code!",[11,5101,5102],{},"Dealing with the native WebRTC API is, honestly, very tricky. Here's a taste of what setting up a connection looks like:",[111,5104,5106],{"className":113,"code":5105,"language":115,"meta":116,"style":116},"const SIGNAL_URL = '/api/signal'\nconst POLL_INTERVAL = 1000\n\nconst config = {\n  iceServers: [\n    { urls: 'stun:stun.l.google.com:19302' },\n    { urls: 'turn:your.turn.server:3478', username: 'user', credential: 'pass' }\n  ]\n}\n\nconst peerId = crypto.randomUUID()\nconst roomId = new URLSearchParams(location.search).get('room') || 'default'\nconst isCaller = new URLSearchParams(location.search).get('role') === 'caller'\n\nconst pc = new RTCPeerConnection(config)\nlet channel = null\nlet remoteDescSet = false\nconst pendingCandidates = []\nconst seenMessages = new Set()\n\nfunction wireChannel(dc) {\n  channel = dc\n  dc.onopen = () => {\n    console.log('Data channel open')\n    dc.send(`hello from ${peerId}`)\n  }\n  dc.onclose = () => console.log('Data channel closed')\n  dc.onerror = (e) => console.error('Data channel error:', e)\n  dc.onmessage = (event) => console.log('Received:', event.data)\n}\n\nif (isCaller) {\n  wireChannel(pc.createDataChannel('chat'))\n} else {\n  pc.ondatachannel = (event) => wireChannel(event.channel)\n}\n\npc.onicecandidate = (event) => {\n  if (event.candidate) {\n    sendSignal({\n      type: 'candidate',\n      candidate: event.candidate.toJSON()\n    })\n  }\n}\n\n// And many, many more lines of code...\n",[46,5107,5108,5120,5132,5136,5147,5152,5162,5184,5189,5193,5197,5214,5246,5276,5280,5297,5309,5320,5332,5347,5351,5365,5375,5393,5408,5432,5436,5461,5491,5521,5526,5531,5539,5559,5569,5593,5598,5603,5624,5632,5641,5652,5663,5669,5674,5679,5684],{"__ignoreMap":116},[120,5109,5110,5112,5115,5117],{"class":122,"line":123},[120,5111,602],{"class":126},[120,5113,5114],{"class":134}," SIGNAL_URL ",[120,5116,295],{"class":294},[120,5118,5119],{"class":580}," '/api/signal'\n",[120,5121,5122,5124,5127,5129],{"class":122,"line":138},[120,5123,602],{"class":126},[120,5125,5126],{"class":134}," POLL_INTERVAL ",[120,5128,295],{"class":294},[120,5130,5131],{"class":298}," 1000\n",[120,5133,5134],{"class":122,"line":147},[120,5135,159],{"emptyLinePlaceholder":158},[120,5137,5138,5140,5143,5145],{"class":122,"line":155},[120,5139,602],{"class":126},[120,5141,5142],{"class":134}," config ",[120,5144,295],{"class":294},[120,5146,651],{"class":134},[120,5148,5149],{"class":122,"line":162},[120,5150,5151],{"class":134},"  iceServers: [\n",[120,5153,5154,5157,5160],{"class":122,"line":171},[120,5155,5156],{"class":134},"    { urls: ",[120,5158,5159],{"class":580},"'stun:stun.l.google.com:19302'",[120,5161,4064],{"class":134},[120,5163,5164,5166,5169,5172,5175,5178,5181],{"class":122,"line":177},[120,5165,5156],{"class":134},[120,5167,5168],{"class":580},"'turn:your.turn.server:3478'",[120,5170,5171],{"class":134},", username: ",[120,5173,5174],{"class":580},"'user'",[120,5176,5177],{"class":134},", credential: ",[120,5179,5180],{"class":580},"'pass'",[120,5182,5183],{"class":134}," }\n",[120,5185,5186],{"class":122,"line":182},[120,5187,5188],{"class":134},"  ]\n",[120,5190,5191],{"class":122,"line":391},[120,5192,174],{"class":134},[120,5194,5195],{"class":122,"line":398},[120,5196,159],{"emptyLinePlaceholder":158},[120,5198,5199,5201,5204,5206,5209,5212],{"class":122,"line":403},[120,5200,602],{"class":126},[120,5202,5203],{"class":134}," peerId ",[120,5205,295],{"class":294},[120,5207,5208],{"class":134}," crypto.",[120,5210,5211],{"class":130},"randomUUID",[120,5213,144],{"class":134},[120,5215,5216,5218,5221,5223,5225,5228,5231,5234,5236,5239,5241,5243],{"class":122,"line":410},[120,5217,602],{"class":126},[120,5219,5220],{"class":134}," roomId ",[120,5222,295],{"class":294},[120,5224,1717],{"class":294},[120,5226,5227],{"class":130}," URLSearchParams",[120,5229,5230],{"class":134},"(location.search).",[120,5232,5233],{"class":130},"get",[120,5235,312],{"class":134},[120,5237,5238],{"class":580},"'room'",[120,5240,1944],{"class":134},[120,5242,1947],{"class":294},[120,5244,5245],{"class":580}," 'default'\n",[120,5247,5248,5250,5253,5255,5257,5259,5261,5263,5265,5268,5270,5273],{"class":122,"line":415},[120,5249,602],{"class":126},[120,5251,5252],{"class":134}," isCaller ",[120,5254,295],{"class":294},[120,5256,1717],{"class":294},[120,5258,5227],{"class":130},[120,5260,5230],{"class":134},[120,5262,5233],{"class":130},[120,5264,312],{"class":134},[120,5266,5267],{"class":580},"'role'",[120,5269,1944],{"class":134},[120,5271,5272],{"class":294},"===",[120,5274,5275],{"class":580}," 'caller'\n",[120,5277,5278],{"class":122,"line":420},[120,5279,159],{"emptyLinePlaceholder":158},[120,5281,5282,5284,5287,5289,5291,5294],{"class":122,"line":904},[120,5283,602],{"class":126},[120,5285,5286],{"class":134}," pc ",[120,5288,295],{"class":294},[120,5290,1717],{"class":294},[120,5292,5293],{"class":130}," RTCPeerConnection",[120,5295,5296],{"class":134},"(config)\n",[120,5298,5299,5301,5304,5306],{"class":122,"line":915},[120,5300,288],{"class":126},[120,5302,5303],{"class":134}," channel ",[120,5305,295],{"class":294},[120,5307,5308],{"class":298}," null\n",[120,5310,5311,5313,5316,5318],{"class":122,"line":1214},[120,5312,288],{"class":126},[120,5314,5315],{"class":134}," remoteDescSet ",[120,5317,295],{"class":294},[120,5319,2226],{"class":298},[120,5321,5322,5324,5327,5329],{"class":122,"line":1882},[120,5323,602],{"class":126},[120,5325,5326],{"class":134}," pendingCandidates ",[120,5328,295],{"class":294},[120,5330,5331],{"class":134}," []\n",[120,5333,5334,5336,5339,5341,5343,5345],{"class":122,"line":1895},[120,5335,602],{"class":126},[120,5337,5338],{"class":134}," seenMessages ",[120,5340,295],{"class":294},[120,5342,1717],{"class":294},[120,5344,1720],{"class":130},[120,5346,144],{"class":134},[120,5348,5349],{"class":122,"line":2716},[120,5350,159],{"emptyLinePlaceholder":158},[120,5352,5353,5355,5358,5360,5363],{"class":122,"line":2732},[120,5354,127],{"class":126},[120,5356,5357],{"class":130}," wireChannel",[120,5359,312],{"class":134},[120,5361,5362],{"class":315},"dc",[120,5364,319],{"class":134},[120,5366,5367,5370,5372],{"class":122,"line":2742},[120,5368,5369],{"class":134},"  channel ",[120,5371,295],{"class":294},[120,5373,5374],{"class":134}," dc\n",[120,5376,5377,5380,5383,5386,5389,5391],{"class":122,"line":2752},[120,5378,5379],{"class":134},"  dc.",[120,5381,5382],{"class":130},"onopen",[120,5384,5385],{"class":294}," =",[120,5387,5388],{"class":134}," () ",[120,5390,648],{"class":126},[120,5392,651],{"class":134},[120,5394,5395,5398,5401,5403,5406],{"class":122,"line":2771},[120,5396,5397],{"class":134},"    console.",[120,5399,5400],{"class":130},"log",[120,5402,312],{"class":134},[120,5404,5405],{"class":580},"'Data channel open'",[120,5407,367],{"class":134},[120,5409,5410,5413,5416,5418,5421,5423,5426,5428,5430],{"class":122,"line":2777},[120,5411,5412],{"class":134},"    dc.",[120,5414,5415],{"class":130},"send",[120,5417,312],{"class":134},[120,5419,5420],{"class":580},"`hello from ",[120,5422,1007],{"class":294},[120,5424,5425],{"class":134},"peerId",[120,5427,1013],{"class":294},[120,5429,1016],{"class":580},[120,5431,367],{"class":134},[120,5433,5434],{"class":122,"line":2791},[120,5435,2165],{"class":134},[120,5437,5438,5440,5443,5445,5447,5449,5452,5454,5456,5459],{"class":122,"line":2800},[120,5439,5379],{"class":134},[120,5441,5442],{"class":130},"onclose",[120,5444,5385],{"class":294},[120,5446,5388],{"class":134},[120,5448,648],{"class":126},[120,5450,5451],{"class":134}," console.",[120,5453,5400],{"class":130},[120,5455,312],{"class":134},[120,5457,5458],{"class":580},"'Data channel closed'",[120,5460,367],{"class":134},[120,5462,5463,5465,5468,5470,5472,5474,5476,5478,5480,5483,5485,5488],{"class":122,"line":2806},[120,5464,5379],{"class":134},[120,5466,5467],{"class":130},"onerror",[120,5469,5385],{"class":294},[120,5471,355],{"class":134},[120,5473,1744],{"class":315},[120,5475,1944],{"class":134},[120,5477,648],{"class":126},[120,5479,5451],{"class":134},[120,5481,5482],{"class":130},"error",[120,5484,312],{"class":134},[120,5486,5487],{"class":580},"'Data channel error:'",[120,5489,5490],{"class":134},", e)\n",[120,5492,5493,5495,5498,5500,5502,5505,5507,5509,5511,5513,5515,5518],{"class":122,"line":2811},[120,5494,5379],{"class":134},[120,5496,5497],{"class":130},"onmessage",[120,5499,5385],{"class":294},[120,5501,355],{"class":134},[120,5503,5504],{"class":315},"event",[120,5506,1944],{"class":134},[120,5508,648],{"class":126},[120,5510,5451],{"class":134},[120,5512,5400],{"class":130},[120,5514,312],{"class":134},[120,5516,5517],{"class":580},"'Received:'",[120,5519,5520],{"class":134},", event.data)\n",[120,5522,5524],{"class":122,"line":5523},30,[120,5525,174],{"class":134},[120,5527,5529],{"class":122,"line":5528},31,[120,5530,159],{"emptyLinePlaceholder":158},[120,5532,5534,5536],{"class":122,"line":5533},32,[120,5535,2198],{"class":294},[120,5537,5538],{"class":134}," (isCaller) {\n",[120,5540,5542,5545,5548,5551,5553,5556],{"class":122,"line":5541},33,[120,5543,5544],{"class":130},"  wireChannel",[120,5546,5547],{"class":134},"(pc.",[120,5549,5550],{"class":130},"createDataChannel",[120,5552,312],{"class":134},[120,5554,5555],{"class":580},"'chat'",[120,5557,5558],{"class":134},"))\n",[120,5560,5562,5565,5567],{"class":122,"line":5561},34,[120,5563,5564],{"class":134},"} ",[120,5566,2758],{"class":294},[120,5568,651],{"class":134},[120,5570,5572,5575,5578,5580,5582,5584,5586,5588,5590],{"class":122,"line":5571},35,[120,5573,5574],{"class":134},"  pc.",[120,5576,5577],{"class":130},"ondatachannel",[120,5579,5385],{"class":294},[120,5581,355],{"class":134},[120,5583,5504],{"class":315},[120,5585,1944],{"class":134},[120,5587,648],{"class":126},[120,5589,5357],{"class":130},[120,5591,5592],{"class":134},"(event.channel)\n",[120,5594,5596],{"class":122,"line":5595},36,[120,5597,174],{"class":134},[120,5599,5601],{"class":122,"line":5600},37,[120,5602,159],{"emptyLinePlaceholder":158},[120,5604,5606,5609,5612,5614,5616,5618,5620,5622],{"class":122,"line":5605},38,[120,5607,5608],{"class":134},"pc.",[120,5610,5611],{"class":130},"onicecandidate",[120,5613,5385],{"class":294},[120,5615,355],{"class":134},[120,5617,5504],{"class":315},[120,5619,1944],{"class":134},[120,5621,648],{"class":126},[120,5623,651],{"class":134},[120,5625,5627,5629],{"class":122,"line":5626},39,[120,5628,2119],{"class":294},[120,5630,5631],{"class":134}," (event.candidate) {\n",[120,5633,5635,5638],{"class":122,"line":5634},40,[120,5636,5637],{"class":130},"    sendSignal",[120,5639,5640],{"class":134},"({\n",[120,5642,5644,5647,5650],{"class":122,"line":5643},41,[120,5645,5646],{"class":134},"      type: ",[120,5648,5649],{"class":580},"'candidate'",[120,5651,1385],{"class":134},[120,5653,5655,5658,5661],{"class":122,"line":5654},42,[120,5656,5657],{"class":134},"      candidate: event.candidate.",[120,5659,5660],{"class":130},"toJSON",[120,5662,144],{"class":134},[120,5664,5666],{"class":122,"line":5665},43,[120,5667,5668],{"class":134},"    })\n",[120,5670,5672],{"class":122,"line":5671},44,[120,5673,2165],{"class":134},[120,5675,5677],{"class":122,"line":5676},45,[120,5678,174],{"class":134},[120,5680,5682],{"class":122,"line":5681},46,[120,5683,159],{"emptyLinePlaceholder":158},[120,5685,5687],{"class":122,"line":5686},47,[120,5688,5689],{"class":1358},"// And many, many more lines of code...\n",[11,5691,5692],{},"In this snippet, we've only scratched the surface of the WebRTC API. To handle errors, edge cases and race conditions, you'd need to write a lot more.",[11,5694,5695,5696,5699],{},"However, my intention is ",[23,5697,5698],{},"not"," for you to remember this snippet, because, as promised, there's a much easier way.",[65,5701,5703],{"id":5702},"peerjs-webrtc-without-the-boilerplate","PeerJS: WebRTC Without the Boilerplate",[11,5705,5706,5707,5714],{},"I promised you a better way to work with WebRTC, and here it is: ",[540,5708,5711],{"href":5709,"rel":5710},"https://peerjs.com/",[544],[18,5712,5713],{},"PeerJS",". It's a free, open-source library that takes all the complex orchestration off your shoulders.",[11,5716,5717],{},"You can install it like this:",[111,5719,5721],{"className":4900,"code":5720,"language":4902,"meta":116,"style":116},"npm install peerjs\n",[46,5722,5723],{"__ignoreMap":116},[120,5724,5725],{"class":122,"line":123},[120,5726,5720],{},[11,5728,5729],{},"... and be ready to use it.",[11,5731,5732],{},"Instead of worrying about SDPs, ICE candidates, and all that, you just need to pick an ID. That's the core concept of PeerJS: peers are identified by a unique ID.",[11,5734,5735,5736,5739],{},"To use an ID, you just instantiate a ",[46,5737,5738],{},"Peer"," object passing the ID of choice as a parameter:",[111,5741,5743],{"className":113,"code":5742,"language":115,"meta":116,"style":116},"const peer = new Peer('bob')\n",[46,5744,5745],{"__ignoreMap":116},[120,5746,5747,5749,5752,5754,5756,5759,5761,5764],{"class":122,"line":123},[120,5748,602],{"class":126},[120,5750,5751],{"class":134}," peer ",[120,5753,295],{"class":294},[120,5755,1717],{"class":294},[120,5757,5758],{"class":130}," Peer",[120,5760,312],{"class":134},[120,5762,5763],{"class":580},"'bob'",[120,5765,367],{"class":134},[11,5767,5768],{},"Keep in mind that the ID must be unique per server. As server, you can use the free cloud service PeerJS provides. But I strongly recommend self-hosting your own: the server is free, open source and easy to set up.",[111,5770,5772],{"className":3946,"code":5771,"language":3948,"meta":116,"style":116},"# Install Node.js (LTS) on Ubuntu\ncurl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -\nsudo apt-get install -y nodejs\n\n# Install the PeerJS server globally\nsudo npm install -g peer\n\n# Run it on port 9000 under the /myapp path\npeerjs --port 9000 --path /myapp\n",[46,5773,5774,5779,5805,5822,5826,5831,5846,5850,5855],{"__ignoreMap":116},[120,5775,5776],{"class":122,"line":123},[120,5777,5778],{"class":1358},"# Install Node.js (LTS) on Ubuntu\n",[120,5780,5781,5784,5787,5790,5793,5796,5799,5802],{"class":122,"line":138},[120,5782,5783],{"class":130},"curl",[120,5785,5786],{"class":298}," -fsSL",[120,5788,5789],{"class":580}," https://deb.nodesource.com/setup_lts.x",[120,5791,5792],{"class":294}," |",[120,5794,5795],{"class":130}," sudo",[120,5797,5798],{"class":298}," -E",[120,5800,5801],{"class":580}," bash",[120,5803,5804],{"class":580}," -\n",[120,5806,5807,5810,5813,5816,5819],{"class":122,"line":147},[120,5808,5809],{"class":130},"sudo",[120,5811,5812],{"class":580}," apt-get",[120,5814,5815],{"class":580}," install",[120,5817,5818],{"class":298}," -y",[120,5820,5821],{"class":580}," nodejs\n",[120,5823,5824],{"class":122,"line":155},[120,5825,159],{"emptyLinePlaceholder":158},[120,5827,5828],{"class":122,"line":162},[120,5829,5830],{"class":1358},"# Install the PeerJS server globally\n",[120,5832,5833,5835,5838,5840,5843],{"class":122,"line":171},[120,5834,5809],{"class":130},[120,5836,5837],{"class":580}," npm",[120,5839,5815],{"class":580},[120,5841,5842],{"class":298}," -g",[120,5844,5845],{"class":580}," peer\n",[120,5847,5848],{"class":122,"line":177},[120,5849,159],{"emptyLinePlaceholder":158},[120,5851,5852],{"class":122,"line":182},[120,5853,5854],{"class":1358},"# Run it on port 9000 under the /myapp path\n",[120,5856,5857,5860,5863,5866,5869],{"class":122,"line":391},[120,5858,5859],{"class":130},"peerjs",[120,5861,5862],{"class":298}," --port",[120,5864,5865],{"class":298}," 9000",[120,5867,5868],{"class":298}," --path",[120,5870,5871],{"class":580}," /myapp\n",[11,5873,5874,5875,5877],{},"Then, on the client, point your ",[46,5876,5738],{}," instance at it:",[111,5879,5881],{"className":113,"code":5880,"language":115,"meta":116,"style":116},"const peer = new Peer('bob', {\n  host: 'example.com',\n  port: 9000,\n  path: '/myapp',\n  secure: true,\n})\n",[46,5882,5883,5902,5912,5922,5932,5941],{"__ignoreMap":116},[120,5884,5885,5887,5889,5891,5893,5895,5897,5899],{"class":122,"line":123},[120,5886,602],{"class":126},[120,5888,5751],{"class":134},[120,5890,295],{"class":294},[120,5892,1717],{"class":294},[120,5894,5758],{"class":130},[120,5896,312],{"class":134},[120,5898,5763],{"class":580},[120,5900,5901],{"class":134},", {\n",[120,5903,5904,5907,5910],{"class":122,"line":138},[120,5905,5906],{"class":134},"  host: ",[120,5908,5909],{"class":580},"'example.com'",[120,5911,1385],{"class":134},[120,5913,5914,5917,5920],{"class":122,"line":147},[120,5915,5916],{"class":134},"  port: ",[120,5918,5919],{"class":298},"9000",[120,5921,1385],{"class":134},[120,5923,5924,5927,5930],{"class":122,"line":155},[120,5925,5926],{"class":134},"  path: ",[120,5928,5929],{"class":580},"'/myapp'",[120,5931,1385],{"class":134},[120,5933,5934,5937,5939],{"class":122,"line":162},[120,5935,5936],{"class":134},"  secure: ",[120,5938,883],{"class":298},[120,5940,1385],{"class":134},[120,5942,5943],{"class":122,"line":171},[120,5944,672],{"class":134},[11,5946,5947,5948,5950,5951,5953],{},"Here we create our ",[46,5949,5738],{}," object, passing an ID of our choice, in this case ",[46,5952,5763],{},". Then we connect to Alice, listen to her messages, and send her one:",[111,5955,5957],{"className":113,"code":5956,"language":115,"meta":116,"style":116},"import Peer from 'peerjs'\n\n// Select your ID\nconst peer = new Peer('bob')\n\npeer.on('open', () => {\n\n  // Connect to Alice\n  const conn = peer.connect('alice')\n\n  conn.on('open', () => {\n\n    // Listen to her messages\n    conn.on('data', (data) => console.log('Received', data))\n\n    // Send her a message\n    conn.send('Hello!')\n  })\n})\n",[46,5958,5959,5971,5975,5980,5998,6002,6022,6026,6031,6053,6057,6074,6078,6083,6117,6121,6126,6139,6144],{"__ignoreMap":116},[120,5960,5961,5963,5966,5968],{"class":122,"line":123},[120,5962,571],{"class":294},[120,5964,5965],{"class":134}," Peer ",[120,5967,577],{"class":294},[120,5969,5970],{"class":580}," 'peerjs'\n",[120,5972,5973],{"class":122,"line":138},[120,5974,159],{"emptyLinePlaceholder":158},[120,5976,5977],{"class":122,"line":147},[120,5978,5979],{"class":1358},"// Select your ID\n",[120,5981,5982,5984,5986,5988,5990,5992,5994,5996],{"class":122,"line":155},[120,5983,602],{"class":126},[120,5985,5751],{"class":134},[120,5987,295],{"class":294},[120,5989,1717],{"class":294},[120,5991,5758],{"class":130},[120,5993,312],{"class":134},[120,5995,5763],{"class":580},[120,5997,367],{"class":134},[120,5999,6000],{"class":122,"line":162},[120,6001,159],{"emptyLinePlaceholder":158},[120,6003,6004,6007,6010,6012,6015,6018,6020],{"class":122,"line":171},[120,6005,6006],{"class":134},"peer.",[120,6008,6009],{"class":130},"on",[120,6011,312],{"class":134},[120,6013,6014],{"class":580},"'open'",[120,6016,6017],{"class":134},", () ",[120,6019,648],{"class":126},[120,6021,651],{"class":134},[120,6023,6024],{"class":122,"line":177},[120,6025,159],{"emptyLinePlaceholder":158},[120,6027,6028],{"class":122,"line":182},[120,6029,6030],{"class":1358},"  // Connect to Alice\n",[120,6032,6033,6035,6038,6040,6043,6046,6048,6051],{"class":122,"line":391},[120,6034,324],{"class":126},[120,6036,6037],{"class":134}," conn ",[120,6039,295],{"class":294},[120,6041,6042],{"class":134}," peer.",[120,6044,6045],{"class":130},"connect",[120,6047,312],{"class":134},[120,6049,6050],{"class":580},"'alice'",[120,6052,367],{"class":134},[120,6054,6055],{"class":122,"line":398},[120,6056,159],{"emptyLinePlaceholder":158},[120,6058,6059,6062,6064,6066,6068,6070,6072],{"class":122,"line":403},[120,6060,6061],{"class":134},"  conn.",[120,6063,6009],{"class":130},[120,6065,312],{"class":134},[120,6067,6014],{"class":580},[120,6069,6017],{"class":134},[120,6071,648],{"class":126},[120,6073,651],{"class":134},[120,6075,6076],{"class":122,"line":410},[120,6077,159],{"emptyLinePlaceholder":158},[120,6079,6080],{"class":122,"line":415},[120,6081,6082],{"class":1358},"    // Listen to her messages\n",[120,6084,6085,6088,6090,6092,6095,6098,6101,6103,6105,6107,6109,6111,6114],{"class":122,"line":420},[120,6086,6087],{"class":134},"    conn.",[120,6089,6009],{"class":130},[120,6091,312],{"class":134},[120,6093,6094],{"class":580},"'data'",[120,6096,6097],{"class":134},", (",[120,6099,6100],{"class":315},"data",[120,6102,1944],{"class":134},[120,6104,648],{"class":126},[120,6106,5451],{"class":134},[120,6108,5400],{"class":130},[120,6110,312],{"class":134},[120,6112,6113],{"class":580},"'Received'",[120,6115,6116],{"class":134},", data))\n",[120,6118,6119],{"class":122,"line":904},[120,6120,159],{"emptyLinePlaceholder":158},[120,6122,6123],{"class":122,"line":915},[120,6124,6125],{"class":1358},"    // Send her a message\n",[120,6127,6128,6130,6132,6134,6137],{"class":122,"line":1214},[120,6129,6087],{"class":134},[120,6131,5415],{"class":130},[120,6133,312],{"class":134},[120,6135,6136],{"class":580},"'Hello!'",[120,6138,367],{"class":134},[120,6140,6141],{"class":122,"line":1882},[120,6142,6143],{"class":134},"  })\n",[120,6145,6146],{"class":122,"line":1895},[120,6147,672],{"class":134},[11,6149,6150],{},"It's super easy: get your ID, connect to your peer, start sending and receiving data. You can have your own peer-to-peer, end-to-end encrypted chat working in almost no time.",[1281,6152,6154],{"id":6153},"sending-audio-and-video","Sending Audio and Video",[11,6156,6157],{},"The data channel is cool, but remember, we can also make audio and video calls. With PeerJS, sharing a video stream is just as approachable:",[111,6159,6161],{"className":727,"code":6160,"language":729,"meta":116,"style":116},"\u003Cscript setup>\nimport Peer from 'peerjs'\n\nconst localVideo = useTemplateRef('local')\nconst remoteVideo = useTemplateRef('remote')\n\nonMounted(async () => {\n  const stream = await navigator.mediaDevices.getUserMedia({\n    video: true,\n    audio: true\n  })\n\n  localVideo.value.muted = true\n  localVideo.value.srcObject = stream\n\n  new Peer('bob').on('call', c => {\n    c.answer(stream)\n    c.on('stream', s => remoteVideo.value.srcObject = s)\n  })\n})\n\u003C/script>\n\n\u003Ctemplate>\n  \u003Cvideo ref=\"local\" autoplay playsinline />\n  \u003Cvideo ref=\"remote\" autoplay playsinline />\n\u003C/template>\n",[46,6162,6163,6173,6183,6187,6206,6224,6228,6243,6263,6272,6280,6284,6288,6297,6307,6311,6342,6353,6378,6382,6386,6394,6398,6406,6431,6452],{"__ignoreMap":116},[120,6164,6165,6167,6169,6171],{"class":122,"line":123},[120,6166,736],{"class":134},[120,6168,739],{"class":294},[120,6170,742],{"class":130},[120,6172,753],{"class":134},[120,6174,6175,6177,6179,6181],{"class":122,"line":138},[120,6176,571],{"class":294},[120,6178,5965],{"class":134},[120,6180,577],{"class":294},[120,6182,5970],{"class":580},[120,6184,6185],{"class":122,"line":147},[120,6186,159],{"emptyLinePlaceholder":158},[120,6188,6189,6191,6194,6196,6199,6201,6204],{"class":122,"line":155},[120,6190,602],{"class":126},[120,6192,6193],{"class":134}," localVideo ",[120,6195,295],{"class":294},[120,6197,6198],{"class":130}," useTemplateRef",[120,6200,312],{"class":134},[120,6202,6203],{"class":580},"'local'",[120,6205,367],{"class":134},[120,6207,6208,6210,6213,6215,6217,6219,6222],{"class":122,"line":162},[120,6209,602],{"class":126},[120,6211,6212],{"class":134}," remoteVideo ",[120,6214,295],{"class":294},[120,6216,6198],{"class":130},[120,6218,312],{"class":134},[120,6220,6221],{"class":580},"'remote'",[120,6223,367],{"class":134},[120,6225,6226],{"class":122,"line":171},[120,6227,159],{"emptyLinePlaceholder":158},[120,6229,6230,6232,6234,6237,6239,6241],{"class":122,"line":177},[120,6231,1810],{"class":130},[120,6233,312],{"class":134},[120,6235,6236],{"class":294},"async",[120,6238,5388],{"class":134},[120,6240,648],{"class":126},[120,6242,651],{"class":134},[120,6244,6245,6247,6250,6252,6255,6258,6261],{"class":122,"line":182},[120,6246,324],{"class":126},[120,6248,6249],{"class":134}," stream ",[120,6251,295],{"class":294},[120,6253,6254],{"class":294}," await",[120,6256,6257],{"class":134}," navigator.mediaDevices.",[120,6259,6260],{"class":130},"getUserMedia",[120,6262,5640],{"class":134},[120,6264,6265,6268,6270],{"class":122,"line":391},[120,6266,6267],{"class":134},"    video: ",[120,6269,883],{"class":298},[120,6271,1385],{"class":134},[120,6273,6274,6277],{"class":122,"line":398},[120,6275,6276],{"class":134},"    audio: ",[120,6278,6279],{"class":298},"true\n",[120,6281,6282],{"class":122,"line":403},[120,6283,6143],{"class":134},[120,6285,6286],{"class":122,"line":410},[120,6287,159],{"emptyLinePlaceholder":158},[120,6289,6290,6293,6295],{"class":122,"line":415},[120,6291,6292],{"class":134},"  localVideo.value.muted ",[120,6294,295],{"class":294},[120,6296,2150],{"class":298},[120,6298,6299,6302,6304],{"class":122,"line":420},[120,6300,6301],{"class":134},"  localVideo.value.srcObject ",[120,6303,295],{"class":294},[120,6305,6306],{"class":134}," stream\n",[120,6308,6309],{"class":122,"line":904},[120,6310,159],{"emptyLinePlaceholder":158},[120,6312,6313,6316,6318,6320,6322,6325,6327,6329,6332,6334,6337,6340],{"class":122,"line":915},[120,6314,6315],{"class":294},"  new",[120,6317,5758],{"class":130},[120,6319,312],{"class":134},[120,6321,5763],{"class":580},[120,6323,6324],{"class":134},").",[120,6326,6009],{"class":130},[120,6328,312],{"class":134},[120,6330,6331],{"class":580},"'call'",[120,6333,1251],{"class":134},[120,6335,6336],{"class":315},"c",[120,6338,6339],{"class":126}," =>",[120,6341,651],{"class":134},[120,6343,6344,6347,6350],{"class":122,"line":1214},[120,6345,6346],{"class":134},"    c.",[120,6348,6349],{"class":130},"answer",[120,6351,6352],{"class":134},"(stream)\n",[120,6354,6355,6357,6359,6361,6364,6366,6368,6370,6373,6375],{"class":122,"line":1882},[120,6356,6346],{"class":134},[120,6358,6009],{"class":130},[120,6360,312],{"class":134},[120,6362,6363],{"class":580},"'stream'",[120,6365,1251],{"class":134},[120,6367,1096],{"class":315},[120,6369,6339],{"class":126},[120,6371,6372],{"class":134}," remoteVideo.value.srcObject ",[120,6374,295],{"class":294},[120,6376,6377],{"class":134}," s)\n",[120,6379,6380],{"class":122,"line":1895},[120,6381,6143],{"class":134},[120,6383,6384],{"class":122,"line":2716},[120,6385,672],{"class":134},[120,6387,6388,6390,6392],{"class":122,"line":2732},[120,6389,793],{"class":134},[120,6391,739],{"class":294},[120,6393,753],{"class":134},[120,6395,6396],{"class":122,"line":2742},[120,6397,159],{"emptyLinePlaceholder":158},[120,6399,6400,6402,6404],{"class":122,"line":2752},[120,6401,736],{"class":134},[120,6403,808],{"class":294},[120,6405,753],{"class":134},[120,6407,6408,6410,6413,6415,6417,6420,6423,6426,6429],{"class":122,"line":2771},[120,6409,815],{"class":134},[120,6411,6412],{"class":294},"video",[120,6414,610],{"class":130},[120,6416,295],{"class":134},[120,6418,6419],{"class":580},"\"local\"",[120,6421,6422],{"class":130}," autoplay",[120,6424,6425],{"class":130}," playsinline",[120,6427,361],{"class":6428},"sHkYM",[120,6430,753],{"class":134},[120,6432,6433,6435,6437,6439,6441,6444,6446,6448,6450],{"class":122,"line":2777},[120,6434,815],{"class":134},[120,6436,6412],{"class":294},[120,6438,610],{"class":130},[120,6440,295],{"class":134},[120,6442,6443],{"class":580},"\"remote\"",[120,6445,6422],{"class":130},[120,6447,6425],{"class":130},[120,6449,361],{"class":6428},[120,6451,753],{"class":134},[120,6453,6454,6456,6458],{"class":122,"line":2791},[120,6455,793],{"class":134},[120,6457,808],{"class":294},[120,6459,753],{"class":134},[11,6461,6462],{},"You can share your webcam, your screen, whatever. Compared to the native API, it's a breeze.",[65,6464,6466],{"id":6465},"wrapping-up-the-series","Wrapping Up the Series",[11,6468,6469],{},"And that's a wrap! Across these three articles, we went from the bad old days of polling all the way to peer-to-peer video calls:",[1475,6471,6472,6479,6486],{},[1478,6473,6474,6478],{},[18,6475,6476,109],{},[540,6477,4771],{"href":4770}," Polling and Server-Sent Events, for when the server needs to push to the client.",[1478,6480,6481,6485],{},[18,6482,6483,109],{},[540,6484,4776],{"href":4775}," WebSockets and real-time databases, for two-way communication.",[1478,6487,6488,6491],{},[18,6489,6490],{},"Part 3 (this one):"," WebRTC and PeerJS, for direct, server-less, end-to-end encrypted connections.",[11,6493,6494],{},"My advice? Pick the simplest tool that solves your problem. Need notifications or a live dashboard? SSE. Need a chat or collaborative drawing? WebSockets. Need direct audio and video, or you really don't want a server in the middle? WebRTC with PeerJS.",[11,6496,6497,6498],{},"Did you enjoy the series? ",[540,6499,6501],{"href":3398,"rel":6500},[544],"Let me know!",[11,6503,6504],{},"See you next time!",[3405,6506,6507],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sOx1s, html code.shiki .sOx1s{--shiki-default:#66D9EF;--shiki-default-font-style:italic}html pre.shiki code .sCdxs, html code.shiki .sCdxs{--shiki-default:#F8F8F2}html pre.shiki code .s8I7P, html code.shiki .s8I7P{--shiki-default:#F92672}html pre.shiki code .s_Ekj, html code.shiki .s_Ekj{--shiki-default:#E6DB74}html pre.shiki code .s7s5_, html code.shiki .s7s5_{--shiki-default:#AE81FF}html pre.shiki code .sHkqI, html code.shiki .sHkqI{--shiki-default:#A6E22E}html pre.shiki code .sW0Xf, html code.shiki .sW0Xf{--shiki-default:#FD971F;--shiki-default-font-style:italic}html pre.shiki code .snpHw, html code.shiki .snpHw{--shiki-default:#88846F}html pre.shiki code .sHkYM, html code.shiki .sHkYM{--shiki-default:#F44747}",{"title":116,"searchDepth":138,"depth":138,"links":6509},[6510,6511,6515,6516,6519],{"id":4783,"depth":138,"text":4784},{"id":4810,"depth":138,"text":4811,"children":6512},[6513,6514],{"id":4864,"depth":147,"text":4865},{"id":5073,"depth":147,"text":5074},{"id":5098,"depth":138,"text":5099},{"id":5702,"depth":138,"text":5703,"children":6517},[6518],{"id":6153,"depth":147,"text":6154},{"id":6465,"depth":138,"text":6466},"2026-05-28","Direct, peer-to-peer connections with WebRTC: a friendly tour of SDP, ICE, STUN, and TURN, plus how PeerJS makes building chats and video calls actually fun.","/img/blog/real-time-vue-apps-part-3-webrtc-and-peerjs.png",{},"/blog/real-time-vue-apps-part-3-webrtc-and-peerjs",{"title":4752,"description":6521},"blog/real-time-vue-apps-part-3-webrtc-and-peerjs","Aw4SqQIx0OVgJaQ6l6QhYeAVLecoQbYaYgIZOVOpgqc",{"id":6529,"title":6530,"body":6531,"category":3425,"date":8014,"description":8015,"draft":3428,"extension":3429,"image":8016,"meta":8017,"navigation":158,"path":4775,"seo":8018,"stem":8019,"tighten":3428,"__hash__":8020},"blog/blog/real-time-vue-apps-part-2-websockets-and-real-time-databases.md","Real-Time Vue Apps, Part 2: WebSockets and Real-Time Databases",{"type":8,"value":6532,"toc":8005},[6533,6540,6554,6558,6565,6576,6579,6582,6586,6597,6613,6627,6630,6634,6658,6664,6667,6676,6683,6800,6817,6829,6835,6993,7000,7006,7009,7290,7300,7303,7307,7310,7319,7394,7409,7476,7483,7487,7490,7501,7504,7507,7622,7628,7636,7659,7734,7743,7751,7769,7974,7977,7981,7987,7990,7999,8002],[3442,6534,6536],{"color":6535,"icon":3445},"#7a3cff",[11,6537,4760,6538,4764],{},[540,6539,3452],{"href":4763},[11,6541,6542,6543,6545,6546,6549,6550,6553],{},"Let's continue exploring how can we implement real-time features in our Vue and Nuxt apps. In ",[540,6544,4771],{"href":4770},", we discovered that ",[18,6547,6548],{},"polling"," is not great and that ",[18,6551,6552],{},"SSE (Server-Sent Events)"," are a great alternative when you can just use a classic HTTP connection.",[65,6555,6557],{"id":6556},"websockets","WebSockets",[11,6559,6560,6561,6564],{},"But there's another option for real-time apps, perhaps the most popular one: ",[18,6562,6563],{},"WebSockets."," They allow not only sending data from server to client, but sending data from client to server as well. They are a bi-directional way, and the ideal solution for many apps in need of real-time features:",[1475,6566,6567,6570,6573],{},[1478,6568,6569],{},"Chats and chatbots",[1478,6571,6572],{},"Collaborative apps (like Notion, Figma and more)",[1478,6574,6575],{},"Multiplayer games",[11,6577,6578],{},"... and any other scenario where you need a fast way of sending and receiving data from the server.",[11,6580,6581],{},"In this article we'll explore how they work, how can we implement them using Vue and Nuxt, and how they server other solutions like real-time databases. Ready? Let's go!",[65,6583,6585],{"id":6584},"how-do-websockets-work","How Do WebSockets Work?",[11,6587,6588,6589,6592,6593,6596],{},"A WebSocket connection starts as a regular HTTP request. The client sends a special header, ",[46,6590,6591],{},"Upgrade: websocket",", basically asking the server \"hey, can we upgrade this connection to a socket?\". It also sends a random ",[46,6594,6595],{},"Sec-WebSocket-Key"," along with it.",[11,6598,6599,6600,6603,6604,6607,6608,535],{},"If the server supports websockets and accepts, it hashes that key with a magic GUID and returns the result in a ",[46,6601,6602],{},"Sec-WebSocket-Accept"," header, alongside a ",[46,6605,6606],{},"101 Switching Protocols"," response. This little crypto dance proves the server actually understood the request. You don't need to worry about it, the browser and the server ",[540,6609,6612],{"href":6610,"rel":6611},"https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers#the_websocket_handshake",[544],"handle it for you",[11,6614,6615,6616,94,6619,6622,6623,6626],{},"After that, the protocol changes from ",[46,6617,6618],{},"http://",[46,6620,6621],{},"ws://"," (or ",[46,6624,6625],{},"wss://"," for secure).",[11,6628,6629],{},"Once that's done, the connection is established and both sides can send messages whenever they want.",[65,6631,6633],{"id":6632},"implementing-websockets-with-vue-nuxt","Implementing WebSockets with Vue & Nuxt",[11,6635,6636,6637,1251,6642,6647,6648,1263,6653,535],{},"Many applications use ",[540,6638,6641],{"href":6639,"rel":6640},"https://pusher.com/",[544],"Pusher",[540,6643,6646],{"href":6644,"rel":6645},"https://socket.io/",[544],"Socket.IO",", or similar services to support WebSockets. Many others use a dedicated, self-hosted solution like ",[540,6649,6652],{"href":6650,"rel":6651},"https://reverb.laravel.com/",[544],"Reverb",[540,6654,6657],{"href":6655,"rel":6656},"https://github.com/websockets/ws",[544],"WS",[11,6659,6660,6661],{},"The cool thing is that, if you're using Nuxt, you don't need a special service, perhaps not event a dedicated server: ",[18,6662,6663],{},"WebSockets are built into Nuxt thanks to Nitro.",[11,6665,6666],{},"Your Nuxt application can handle them, out of the box. Let's check that out!",[6668,6669,6673],"pill",{"className":6670,"icon":6672},[6671],"bg-sky-500","fa6-solid:server",[11,6674,6675],{},"Server-Side",[11,6677,6678,6679,6682],{},"In a server route, you define a special handler with ",[46,6680,6681],{},"defineWebSocketHandler",". This handler gives you access to a set of hooks:",[111,6684,6686],{"className":113,"code":6685,"language":115,"meta":116,"style":116},"export default defineWebSocketHandler({\n  // Runs when a peer connects\n  open(peer) {\n    // Subscribe the peer to a topic\n    peer.subscribe('room-123')\n  },\n  // Runs when a peer sends a message\n  message(peer, message) {\n    // Broadcast the message to everyone else\n    peer.publish('room-123', message.text())\n  }\n  // Other hooks: upgrade(), close(), error()\n})\n",[46,6687,6688,6700,6705,6717,6722,6737,6742,6747,6763,6768,6787,6791,6796],{"__ignoreMap":116},[120,6689,6690,6692,6695,6698],{"class":122,"line":123},[120,6691,1364],{"class":294},[120,6693,6694],{"class":294}," default",[120,6696,6697],{"class":130}," defineWebSocketHandler",[120,6699,5640],{"class":134},[120,6701,6702],{"class":122,"line":138},[120,6703,6704],{"class":1358},"  // Runs when a peer connects\n",[120,6706,6707,6710,6712,6715],{"class":122,"line":147},[120,6708,6709],{"class":130},"  open",[120,6711,312],{"class":134},[120,6713,6714],{"class":315},"peer",[120,6716,319],{"class":134},[120,6718,6719],{"class":122,"line":155},[120,6720,6721],{"class":1358},"    // Subscribe the peer to a topic\n",[120,6723,6724,6727,6730,6732,6735],{"class":122,"line":162},[120,6725,6726],{"class":134},"    peer.",[120,6728,6729],{"class":130},"subscribe",[120,6731,312],{"class":134},[120,6733,6734],{"class":580},"'room-123'",[120,6736,367],{"class":134},[120,6738,6739],{"class":122,"line":171},[120,6740,6741],{"class":134},"  },\n",[120,6743,6744],{"class":122,"line":177},[120,6745,6746],{"class":1358},"  // Runs when a peer sends a message\n",[120,6748,6749,6752,6754,6756,6758,6761],{"class":122,"line":182},[120,6750,6751],{"class":130},"  message",[120,6753,312],{"class":134},[120,6755,6714],{"class":315},[120,6757,1251],{"class":134},[120,6759,6760],{"class":315},"message",[120,6762,319],{"class":134},[120,6764,6765],{"class":122,"line":391},[120,6766,6767],{"class":1358},"    // Broadcast the message to everyone else\n",[120,6769,6770,6772,6775,6777,6779,6782,6784],{"class":122,"line":398},[120,6771,6726],{"class":134},[120,6773,6774],{"class":130},"publish",[120,6776,312],{"class":134},[120,6778,6734],{"class":580},[120,6780,6781],{"class":134},", message.",[120,6783,4350],{"class":130},[120,6785,6786],{"class":134},"())\n",[120,6788,6789],{"class":122,"line":403},[120,6790,2165],{"class":134},[120,6792,6793],{"class":122,"line":410},[120,6794,6795],{"class":1358},"  // Other hooks: upgrade(), close(), error()\n",[120,6797,6798],{"class":122,"line":415},[120,6799,672],{"class":134},[1475,6801,6802,6812],{},[1478,6803,6804,6805,6808,6809,6811],{},"The ",[46,6806,6807],{},"open"," hook runs whenever a peer connects. In this case, we're subscribing the peer to a topic: the ID of a chat room, ",[46,6810,6734],{},". It can be whatever string you want.",[1478,6813,6804,6814,6816],{},[46,6815,6760],{}," hook runs whenever a peer sends a message. Here, we're publishing the text of that message to everyone subscribed to that topic.",[11,6818,6819,6820,1251,6823,1440,6826,6828],{},"You have other hooks too, like ",[46,6821,6822],{},"upgrade",[46,6824,6825],{},"close",[46,6827,5482],{},". You can explore them in the official documentation.",[11,6830,6831,6832,6834],{},"Let's check out the methods of the ",[46,6833,6714],{}," object:",[111,6836,6838],{"className":113,"code":6837,"language":115,"meta":116,"style":116},"// Subscribes the peer to a topic\npeer.subscribe('room-123')\n\n// Sends a message to everyone in the topic (the sender excluded)\npeer.publish('room-123', 'Hello there!')\npeer.publish('room-123', { user: 'Alice', text: 'Hi everyone!' })\n\n// Sends something to that peer only.\n// Great for messages like \"Welcome to the chat room\", notifications, etc.\npeer.send('Welcome!')\npeer.send({ type: 'notification', text: 'You are awesome' })\n\n// Stops listening to the topic\npeer.unsubscribe('room-123')\n\n// Gracefully closes the connection\npeer.close()\n",[46,6839,6840,6845,6857,6861,6866,6883,6908,6912,6917,6922,6935,6954,6958,6963,6976,6980,6985],{"__ignoreMap":116},[120,6841,6842],{"class":122,"line":123},[120,6843,6844],{"class":1358},"// Subscribes the peer to a topic\n",[120,6846,6847,6849,6851,6853,6855],{"class":122,"line":138},[120,6848,6006],{"class":134},[120,6850,6729],{"class":130},[120,6852,312],{"class":134},[120,6854,6734],{"class":580},[120,6856,367],{"class":134},[120,6858,6859],{"class":122,"line":147},[120,6860,159],{"emptyLinePlaceholder":158},[120,6862,6863],{"class":122,"line":155},[120,6864,6865],{"class":1358},"// Sends a message to everyone in the topic (the sender excluded)\n",[120,6867,6868,6870,6872,6874,6876,6878,6881],{"class":122,"line":162},[120,6869,6006],{"class":134},[120,6871,6774],{"class":130},[120,6873,312],{"class":134},[120,6875,6734],{"class":580},[120,6877,1251],{"class":134},[120,6879,6880],{"class":580},"'Hello there!'",[120,6882,367],{"class":134},[120,6884,6885,6887,6889,6891,6893,6896,6899,6902,6905],{"class":122,"line":171},[120,6886,6006],{"class":134},[120,6888,6774],{"class":130},[120,6890,312],{"class":134},[120,6892,6734],{"class":580},[120,6894,6895],{"class":134},", { user: ",[120,6897,6898],{"class":580},"'Alice'",[120,6900,6901],{"class":134},", text: ",[120,6903,6904],{"class":580},"'Hi everyone!'",[120,6906,6907],{"class":134}," })\n",[120,6909,6910],{"class":122,"line":177},[120,6911,159],{"emptyLinePlaceholder":158},[120,6913,6914],{"class":122,"line":182},[120,6915,6916],{"class":1358},"// Sends something to that peer only.\n",[120,6918,6919],{"class":122,"line":391},[120,6920,6921],{"class":1358},"// Great for messages like \"Welcome to the chat room\", notifications, etc.\n",[120,6923,6924,6926,6928,6930,6933],{"class":122,"line":398},[120,6925,6006],{"class":134},[120,6927,5415],{"class":130},[120,6929,312],{"class":134},[120,6931,6932],{"class":580},"'Welcome!'",[120,6934,367],{"class":134},[120,6936,6937,6939,6941,6944,6947,6949,6952],{"class":122,"line":403},[120,6938,6006],{"class":134},[120,6940,5415],{"class":130},[120,6942,6943],{"class":134},"({ type: ",[120,6945,6946],{"class":580},"'notification'",[120,6948,6901],{"class":134},[120,6950,6951],{"class":580},"'You are awesome'",[120,6953,6907],{"class":134},[120,6955,6956],{"class":122,"line":410},[120,6957,159],{"emptyLinePlaceholder":158},[120,6959,6960],{"class":122,"line":415},[120,6961,6962],{"class":1358},"// Stops listening to the topic\n",[120,6964,6965,6967,6970,6972,6974],{"class":122,"line":420},[120,6966,6006],{"class":134},[120,6968,6969],{"class":130},"unsubscribe",[120,6971,312],{"class":134},[120,6973,6734],{"class":580},[120,6975,367],{"class":134},[120,6977,6978],{"class":122,"line":904},[120,6979,159],{"emptyLinePlaceholder":158},[120,6981,6982],{"class":122,"line":915},[120,6983,6984],{"class":1358},"// Gracefully closes the connection\n",[120,6986,6987,6989,6991],{"class":122,"line":1214},[120,6988,6006],{"class":134},[120,6990,6825],{"class":130},[120,6992,144],{"class":134},[6668,6994,6997],{"className":6995,"icon":6996},[6671],"fa6-solid:laptop",[11,6998,6999],{},"Client-Side",[11,7001,7002,7003,535],{},"On the client side, VueUse has us covered again with ",[46,7004,7005],{},"useWebSocket",[11,7007,7008],{},"Take a look at this code. It's a minimal chat component:",[111,7010,7012],{"className":727,"code":7011,"language":729,"meta":116,"style":116},"\u003Cscript setup>\nimport { useWebSocket } from '@vueuse/core'\n\nconst messages = ref([])\nconst { send } = useWebSocket('ws://localhost:3000/_ws', {\n  onMessage: (ws, e) => messages.value.push(e.data)\n})\n\nconst input = ref('')\nfunction submit() {\n  send(input.value)\n  messages.value.push(`Me: ${input.value}`)\n  input.value = ''\n}\n\u003C/script>\n\n\u003Ctemplate>\n  \u003Cdiv v-for=\"(msg, i) in messages\" :key=\"i\">{{ msg }}\u003C/div>\n  \u003Cinput v-model=\"input\" @keyup.enter=\"submit\" />\n\u003C/template>\n",[46,7013,7014,7024,7036,7040,7054,7073,7101,7105,7109,7127,7136,7144,7167,7177,7181,7189,7193,7201,7243,7282],{"__ignoreMap":116},[120,7015,7016,7018,7020,7022],{"class":122,"line":123},[120,7017,736],{"class":134},[120,7019,739],{"class":294},[120,7021,742],{"class":130},[120,7023,753],{"class":134},[120,7025,7026,7028,7031,7033],{"class":122,"line":138},[120,7027,571],{"class":294},[120,7029,7030],{"class":134}," { useWebSocket } ",[120,7032,577],{"class":294},[120,7034,7035],{"class":580}," '@vueuse/core'\n",[120,7037,7038],{"class":122,"line":147},[120,7039,159],{"emptyLinePlaceholder":158},[120,7041,7042,7044,7047,7049,7051],{"class":122,"line":155},[120,7043,602],{"class":126},[120,7045,7046],{"class":134}," messages ",[120,7048,295],{"class":294},[120,7050,610],{"class":130},[120,7052,7053],{"class":134},"([])\n",[120,7055,7056,7058,7061,7063,7066,7068,7071],{"class":122,"line":162},[120,7057,602],{"class":126},[120,7059,7060],{"class":134}," { send } ",[120,7062,295],{"class":294},[120,7064,7065],{"class":130}," useWebSocket",[120,7067,312],{"class":134},[120,7069,7070],{"class":580},"'ws://localhost:3000/_ws'",[120,7072,5901],{"class":134},[120,7074,7075,7078,7081,7084,7086,7088,7090,7092,7095,7098],{"class":122,"line":171},[120,7076,7077],{"class":130},"  onMessage",[120,7079,7080],{"class":134},": (",[120,7082,7083],{"class":315},"ws",[120,7085,1251],{"class":134},[120,7087,1744],{"class":315},[120,7089,1944],{"class":134},[120,7091,648],{"class":126},[120,7093,7094],{"class":134}," messages.value.",[120,7096,7097],{"class":130},"push",[120,7099,7100],{"class":134},"(e.data)\n",[120,7102,7103],{"class":122,"line":177},[120,7104,672],{"class":134},[120,7106,7107],{"class":122,"line":182},[120,7108,159],{"emptyLinePlaceholder":158},[120,7110,7111,7113,7116,7118,7120,7122,7125],{"class":122,"line":391},[120,7112,602],{"class":126},[120,7114,7115],{"class":134}," input ",[120,7117,295],{"class":294},[120,7119,610],{"class":130},[120,7121,312],{"class":134},[120,7123,7124],{"class":580},"''",[120,7126,367],{"class":134},[120,7128,7129,7131,7134],{"class":122,"line":398},[120,7130,127],{"class":126},[120,7132,7133],{"class":130}," submit",[120,7135,135],{"class":134},[120,7137,7138,7141],{"class":122,"line":403},[120,7139,7140],{"class":130},"  send",[120,7142,7143],{"class":134},"(input.value)\n",[120,7145,7146,7149,7151,7153,7156,7158,7161,7163,7165],{"class":122,"line":410},[120,7147,7148],{"class":134},"  messages.value.",[120,7150,7097],{"class":130},[120,7152,312],{"class":134},[120,7154,7155],{"class":580},"`Me: ",[120,7157,1007],{"class":294},[120,7159,7160],{"class":134},"input.value",[120,7162,1013],{"class":294},[120,7164,1016],{"class":580},[120,7166,367],{"class":134},[120,7168,7169,7172,7174],{"class":122,"line":415},[120,7170,7171],{"class":134},"  input.value ",[120,7173,295],{"class":294},[120,7175,7176],{"class":580}," ''\n",[120,7178,7179],{"class":122,"line":420},[120,7180,174],{"class":134},[120,7182,7183,7185,7187],{"class":122,"line":904},[120,7184,793],{"class":134},[120,7186,739],{"class":294},[120,7188,753],{"class":134},[120,7190,7191],{"class":122,"line":915},[120,7192,159],{"emptyLinePlaceholder":158},[120,7194,7195,7197,7199],{"class":122,"line":1214},[120,7196,736],{"class":134},[120,7198,808],{"class":294},[120,7200,753],{"class":134},[120,7202,7203,7205,7208,7210,7212,7214,7217,7219,7222,7224,7226,7228,7230,7232,7234,7236,7239,7241],{"class":122,"line":1882},[120,7204,815],{"class":134},[120,7206,7207],{"class":294},"div",[120,7209,1048],{"class":294},[120,7211,295],{"class":134},[120,7213,831],{"class":134},[120,7215,7216],{"class":134},"(msg, i) ",[120,7218,984],{"class":294},[120,7220,7221],{"class":134}," messages",[120,7223,831],{"class":134},[120,7225,1065],{"class":134},[120,7227,997],{"class":130},[120,7229,295],{"class":134},[120,7231,831],{"class":134},[120,7233,1010],{"class":134},[120,7235,831],{"class":134},[120,7237,7238],{"class":134},">{{ msg }}\u003C/",[120,7240,7207],{"class":294},[120,7242,753],{"class":134},[120,7244,7245,7247,7250,7253,7255,7257,7259,7261,7263,7266,7268,7271,7273,7275,7278,7280],{"class":122,"line":1895},[120,7246,815],{"class":134},[120,7248,7249],{"class":294},"input",[120,7251,7252],{"class":130}," v-model",[120,7254,295],{"class":134},[120,7256,831],{"class":134},[120,7258,7249],{"class":134},[120,7260,831],{"class":134},[120,7262,1136],{"class":134},[120,7264,7265],{"class":130},"keyup",[120,7267,535],{"class":134},[120,7269,7270],{"class":130},"enter",[120,7272,295],{"class":134},[120,7274,831],{"class":134},[120,7276,7277],{"class":134},"submit",[120,7279,831],{"class":134},[120,7281,901],{"class":134},[120,7283,7284,7286,7288],{"class":122,"line":2716},[120,7285,793],{"class":134},[120,7287,808],{"class":294},[120,7289,753],{"class":134},[11,7291,7292,7293,7296,7297,7299],{},"Let's explore what we have. We're connecting to the WebSocket, and we say: every time you get a message, push it into the ",[46,7294,7295],{},"messages"," array. We also destructure the ",[46,7298,5415],{}," method, which we use below: whenever the user types something into the input and presses enter, we send the message via the socket and push it into the array of messages.",[11,7301,7302],{},"In just about 20 lines of code, we have a working chat. Of course, in a real-world application you'd need many more features, but this shows how simple it is to work with WebSockets in Vue.",[1281,7304,7306],{"id":7305},"two-more-things-auto-reconnect-and-heartbeat","Two More Things: Auto-Reconnect and Heartbeat",[11,7308,7309],{},"Before moving on, two cool things about WebSockets that you'll want to keep in mind.",[11,7311,7312,7315,7316,7318],{},[18,7313,7314],{},"Auto-reconnect"," is pretty similar to SSE. The only difference is that with ",[46,7317,7005],{}," you have to opt in, it's not on by default:",[111,7320,7322],{"className":113,"code":7321,"language":115,"meta":116,"style":116},"// Same idea as SSE, but with WebSockets you have to opt in\nuseWebSocket('ws://localhost:3000/_ws', {\n  autoReconnect: {\n    retries: 5,\n    delay: 2000,\n    onFailed: () => console.log('Could not reconnect'),\n  }\n})\n",[46,7323,7324,7329,7339,7344,7354,7364,7386,7390],{"__ignoreMap":116},[120,7325,7326],{"class":122,"line":123},[120,7327,7328],{"class":1358},"// Same idea as SSE, but with WebSockets you have to opt in\n",[120,7330,7331,7333,7335,7337],{"class":122,"line":138},[120,7332,7005],{"class":130},[120,7334,312],{"class":134},[120,7336,7070],{"class":580},[120,7338,5901],{"class":134},[120,7340,7341],{"class":122,"line":147},[120,7342,7343],{"class":134},"  autoReconnect: {\n",[120,7345,7346,7349,7352],{"class":122,"line":155},[120,7347,7348],{"class":134},"    retries: ",[120,7350,7351],{"class":298},"5",[120,7353,1385],{"class":134},[120,7355,7356,7359,7362],{"class":122,"line":162},[120,7357,7358],{"class":134},"    delay: ",[120,7360,7361],{"class":298},"2000",[120,7363,1385],{"class":134},[120,7365,7366,7369,7372,7374,7376,7378,7380,7383],{"class":122,"line":171},[120,7367,7368],{"class":130},"    onFailed",[120,7370,7371],{"class":134},": () ",[120,7373,648],{"class":126},[120,7375,5451],{"class":134},[120,7377,5400],{"class":130},[120,7379,312],{"class":134},[120,7381,7382],{"class":580},"'Could not reconnect'",[120,7384,7385],{"class":134},"),\n",[120,7387,7388],{"class":122,"line":177},[120,7389,2165],{"class":134},[120,7391,7392],{"class":122,"line":182},[120,7393,672],{"class":134},[11,7395,7396,7397,7400,7401,7404,7405,7408],{},"And then there's the ",[18,7398,7399],{},"heartbeat",". WebSockets stay open forever, in theory, but proxies and load balancers love to kill idle connections. So when nothing is being exchanged between server and client, it's recommended to regularly send a ",[23,7402,7403],{},"ping"," to the server and receive a ",[23,7406,7407],{},"pong"," back, just to keep the connection alive.",[111,7410,7412],{"className":113,"code":7411,"language":115,"meta":116,"style":116},"// Proxies and load balancers love to kill idle connections,\n// send a ping every 30 seconds to keep them alive\nuseWebSocket('ws://localhost:3000/_ws', {\n  heartbeat: {\n    message: 'ping',\n    interval: 30000,\n    pongTimeout: 1000,\n  }\n})\n",[46,7413,7414,7419,7424,7434,7439,7449,7459,7468,7472],{"__ignoreMap":116},[120,7415,7416],{"class":122,"line":123},[120,7417,7418],{"class":1358},"// Proxies and load balancers love to kill idle connections,\n",[120,7420,7421],{"class":122,"line":138},[120,7422,7423],{"class":1358},"// send a ping every 30 seconds to keep them alive\n",[120,7425,7426,7428,7430,7432],{"class":122,"line":147},[120,7427,7005],{"class":130},[120,7429,312],{"class":134},[120,7431,7070],{"class":580},[120,7433,5901],{"class":134},[120,7435,7436],{"class":122,"line":155},[120,7437,7438],{"class":134},"  heartbeat: {\n",[120,7440,7441,7444,7447],{"class":122,"line":162},[120,7442,7443],{"class":134},"    message: ",[120,7445,7446],{"class":580},"'ping'",[120,7448,1385],{"class":134},[120,7450,7451,7454,7457],{"class":122,"line":171},[120,7452,7453],{"class":134},"    interval: ",[120,7455,7456],{"class":298},"30000",[120,7458,1385],{"class":134},[120,7460,7461,7464,7466],{"class":122,"line":177},[120,7462,7463],{"class":134},"    pongTimeout: ",[120,7465,358],{"class":298},[120,7467,1385],{"class":134},[120,7469,7470],{"class":122,"line":182},[120,7471,2165],{"class":134},[120,7473,7474],{"class":122,"line":391},[120,7475,672],{"class":134},[11,7477,7478,7479,7482],{},"If the server doesn't pong back within ",[46,7480,7481],{},"pongTimeout",", the composable considers the connection dead and (if you opted in) reconnects.",[65,7484,7486],{"id":7485},"real-time-databases","Real-Time Databases",[11,7488,7489],{},"This is a great moment to talk about real-time databases, because WebSockets are exactly what they use under the hood to broadcast changes.",[11,7491,7492,7493,7496,7497,7500],{},"What's the idea? In your client-side code, you subscribe to listen for changes on certain ",[23,7494,7495],{},"collections"," (in NoSQL databases) or ",[23,7498,7499],{},"tables"," (in relational ones). If a record is created, updated, or deleted, every connected client gets the details in real time.",[11,7502,7503],{},"You don't need to worry about wiring up the socket: the libraries handles that for you. You just pick the changes you want to listen for, and handle what to do with them by defining a callback.",[11,7505,7506],{},"For example, let's say you have a list of tasks, and whenever those tasks change you want this template to automatically re-render:",[111,7508,7510],{"className":727,"code":7509,"language":729,"meta":116,"style":116},"\u003Ctemplate>\n  \u003Cul v-if=\"tasks\">\n    \u003Cli v-for=\"task in tasks\" :key=\"task.id\">\n      {{ task.user }}: {{ task.text }}\n    \u003C/li>\n  \u003C/ul>\n  \u003Cp v-else>Loading...\u003C/p>\n\u003C/template>\n",[46,7511,7512,7520,7540,7577,7582,7590,7598,7614],{"__ignoreMap":116},[120,7513,7514,7516,7518],{"class":122,"line":123},[120,7515,736],{"class":134},[120,7517,808],{"class":294},[120,7519,753],{"class":134},[120,7521,7522,7524,7526,7529,7531,7533,7536,7538],{"class":122,"line":138},[120,7523,815],{"class":134},[120,7525,1475],{"class":294},[120,7527,7528],{"class":294}," v-if",[120,7530,295],{"class":134},[120,7532,831],{"class":134},[120,7534,7535],{"class":134},"tasks",[120,7537,831],{"class":134},[120,7539,753],{"class":134},[120,7541,7542,7544,7546,7548,7550,7552,7555,7557,7560,7562,7564,7566,7568,7570,7573,7575],{"class":122,"line":147},[120,7543,895],{"class":134},[120,7545,1478],{"class":294},[120,7547,1048],{"class":294},[120,7549,295],{"class":134},[120,7551,831],{"class":134},[120,7553,7554],{"class":134},"task ",[120,7556,984],{"class":294},[120,7558,7559],{"class":134}," tasks",[120,7561,831],{"class":134},[120,7563,1065],{"class":134},[120,7565,997],{"class":130},[120,7567,295],{"class":134},[120,7569,831],{"class":134},[120,7571,7572],{"class":134},"task.id",[120,7574,831],{"class":134},[120,7576,753],{"class":134},[120,7578,7579],{"class":122,"line":155},[120,7580,7581],{"class":134},"      {{ task.user }}: {{ task.text }}\n",[120,7583,7584,7586,7588],{"class":122,"line":162},[120,7585,4222],{"class":134},[120,7587,1478],{"class":294},[120,7589,753],{"class":134},[120,7591,7592,7594,7596],{"class":122,"line":171},[120,7593,907],{"class":134},[120,7595,1475],{"class":294},[120,7597,753],{"class":134},[120,7599,7600,7602,7604,7607,7610,7612],{"class":122,"line":177},[120,7601,815],{"class":134},[120,7603,11],{"class":294},[120,7605,7606],{"class":294}," v-else",[120,7608,7609],{"class":134},">Loading...\u003C/",[120,7611,11],{"class":294},[120,7613,753],{"class":134},[120,7615,7616,7618,7620],{"class":122,"line":182},[120,7617,793],{"class":134},[120,7619,808],{"class":294},[120,7621,753],{"class":134},[11,7623,7624,7625,7627],{},"How can we keep ",[46,7626,7535],{}," up to date in real time? Let's look at two great options.",[6668,7629,7633],{"className":7630,"icon":7632},[7631],"bg-yellow-200","logos:firebase-icon",[11,7634,7635],{},"Firebase and VueFire",[11,7637,7638,7643,7644,40,7647,7650,7651,7658],{},[540,7639,7642],{"href":7640,"rel":7641},"https://firebase.google.com/",[544],"Firebase"," comes in two flavors, the ",[18,7645,7646],{},"Realtime Database",[18,7648,7649],{},"Firestore",", both NoSQL, both real-time. The best way to use it in Vue is with ",[540,7652,7655],{"href":7653,"rel":7654},"https://vuefire.vuejs.org/",[544],[18,7656,7657],{},"VueFire",", the amazing library by Eduardo San Martín Morote.",[111,7660,7662],{"className":113,"code":7661,"language":115,"meta":116,"style":116},"// VueFire 🔥\nimport { useCollection } from 'vuefire'\nimport { collection } from 'firebase/firestore'\nimport { db } from '~/firebase'\n\nconst tasks = useCollection(collection(db, 'tasks'))\n",[46,7663,7664,7669,7681,7693,7705,7709],{"__ignoreMap":116},[120,7665,7666],{"class":122,"line":123},[120,7667,7668],{"class":1358},"// VueFire 🔥\n",[120,7670,7671,7673,7676,7678],{"class":122,"line":138},[120,7672,571],{"class":294},[120,7674,7675],{"class":134}," { useCollection } ",[120,7677,577],{"class":294},[120,7679,7680],{"class":580}," 'vuefire'\n",[120,7682,7683,7685,7688,7690],{"class":122,"line":147},[120,7684,571],{"class":294},[120,7686,7687],{"class":134}," { collection } ",[120,7689,577],{"class":294},[120,7691,7692],{"class":580}," 'firebase/firestore'\n",[120,7694,7695,7697,7700,7702],{"class":122,"line":155},[120,7696,571],{"class":294},[120,7698,7699],{"class":134}," { db } ",[120,7701,577],{"class":294},[120,7703,7704],{"class":580}," '~/firebase'\n",[120,7706,7707],{"class":122,"line":162},[120,7708,159],{"emptyLinePlaceholder":158},[120,7710,7711,7713,7716,7718,7721,7723,7726,7729,7732],{"class":122,"line":171},[120,7712,602],{"class":126},[120,7714,7715],{"class":134}," tasks ",[120,7717,295],{"class":294},[120,7719,7720],{"class":130}," useCollection",[120,7722,312],{"class":134},[120,7724,7725],{"class":130},"collection",[120,7727,7728],{"class":134},"(db, ",[120,7730,7731],{"class":580},"'tasks'",[120,7733,5558],{"class":134},[11,7735,7736,7737,7739,7740,7742],{},"That's it. Super short. ",[46,7738,7535],{}," is a reactive ref, so whenever the ",[46,7741,7535],{}," collection changes, the template re-renders.",[6668,7744,7748],{"className":7745,"icon":7747},[7746],"bg-green-200","logos:supabase-icon",[11,7749,7750],{},"Supabase and the Nuxt module",[11,7752,7753,7754,7761,7762,535],{},"If you want an open-source alternative, there's ",[540,7755,7758],{"href":7756,"rel":7757},"https://supabase.com/",[544],[18,7759,7760],{},"Supabase",". It's a Postgres database with real-time updates you can subscribe to. If you're using Nuxt, I recommend the excellent ",[540,7763,7766],{"href":7764,"rel":7765},"https://supabase.nuxtjs.org/",[544],[18,7767,7768],{},"Nuxt Supabase module",[111,7770,7772],{"className":113,"code":7771,"language":115,"meta":116,"style":116},"// Nuxt Supabase ⚡️\nconst client = useSupabaseClient()\n\nconst { data: tasks, refresh } = await useAsyncData('tasks', async () =>\n  (await client.from('tasks').select('*')).data\n)\n\nlet channel\n\nonMounted(() =>\n  channel = client\n    .channel('public:tasks')\n    .on('postgres_changes', {\n      event: '*', schema: 'public', table: 'tasks'\n    }, refresh)\n    .subscribe()\n)\n\nonUnmounted(() => client.removeChannel(channel))\n",[46,7773,7774,7779,7793,7797,7824,7854,7858,7862,7869,7873,7881,7890,7905,7918,7937,7942,7950,7954,7958],{"__ignoreMap":116},[120,7775,7776],{"class":122,"line":123},[120,7777,7778],{"class":1358},"// Nuxt Supabase ⚡️\n",[120,7780,7781,7783,7786,7788,7791],{"class":122,"line":138},[120,7782,602],{"class":126},[120,7784,7785],{"class":134}," client ",[120,7787,295],{"class":294},[120,7789,7790],{"class":130}," useSupabaseClient",[120,7792,144],{"class":134},[120,7794,7795],{"class":122,"line":147},[120,7796,159],{"emptyLinePlaceholder":158},[120,7798,7799,7801,7804,7806,7808,7811,7813,7815,7817,7819,7821],{"class":122,"line":155},[120,7800,602],{"class":126},[120,7802,7803],{"class":134}," { data: tasks, refresh } ",[120,7805,295],{"class":294},[120,7807,6254],{"class":294},[120,7809,7810],{"class":130}," useAsyncData",[120,7812,312],{"class":134},[120,7814,7731],{"class":580},[120,7816,1251],{"class":134},[120,7818,6236],{"class":294},[120,7820,5388],{"class":134},[120,7822,7823],{"class":126},"=>\n",[120,7825,7826,7829,7832,7835,7837,7839,7841,7843,7846,7848,7851],{"class":122,"line":162},[120,7827,7828],{"class":134},"  (",[120,7830,7831],{"class":294},"await",[120,7833,7834],{"class":134}," client.",[120,7836,577],{"class":130},[120,7838,312],{"class":134},[120,7840,7731],{"class":580},[120,7842,6324],{"class":134},[120,7844,7845],{"class":130},"select",[120,7847,312],{"class":134},[120,7849,7850],{"class":580},"'*'",[120,7852,7853],{"class":134},")).data\n",[120,7855,7856],{"class":122,"line":171},[120,7857,367],{"class":134},[120,7859,7860],{"class":122,"line":177},[120,7861,159],{"emptyLinePlaceholder":158},[120,7863,7864,7866],{"class":122,"line":182},[120,7865,288],{"class":126},[120,7867,7868],{"class":134}," channel\n",[120,7870,7871],{"class":122,"line":391},[120,7872,159],{"emptyLinePlaceholder":158},[120,7874,7875,7877,7879],{"class":122,"line":398},[120,7876,1810],{"class":130},[120,7878,1813],{"class":134},[120,7880,7823],{"class":126},[120,7882,7883,7885,7887],{"class":122,"line":403},[120,7884,5369],{"class":134},[120,7886,295],{"class":294},[120,7888,7889],{"class":134}," client\n",[120,7891,7892,7895,7898,7900,7903],{"class":122,"line":410},[120,7893,7894],{"class":134},"    .",[120,7896,7897],{"class":130},"channel",[120,7899,312],{"class":134},[120,7901,7902],{"class":580},"'public:tasks'",[120,7904,367],{"class":134},[120,7906,7907,7909,7911,7913,7916],{"class":122,"line":415},[120,7908,7894],{"class":134},[120,7910,6009],{"class":130},[120,7912,312],{"class":134},[120,7914,7915],{"class":580},"'postgres_changes'",[120,7917,5901],{"class":134},[120,7919,7920,7923,7925,7928,7931,7934],{"class":122,"line":420},[120,7921,7922],{"class":134},"      event: ",[120,7924,7850],{"class":580},[120,7926,7927],{"class":134},", schema: ",[120,7929,7930],{"class":580},"'public'",[120,7932,7933],{"class":134},", table: ",[120,7935,7936],{"class":580},"'tasks'\n",[120,7938,7939],{"class":122,"line":904},[120,7940,7941],{"class":134},"    }, refresh)\n",[120,7943,7944,7946,7948],{"class":122,"line":915},[120,7945,7894],{"class":134},[120,7947,6729],{"class":130},[120,7949,144],{"class":134},[120,7951,7952],{"class":122,"line":1214},[120,7953,367],{"class":134},[120,7955,7956],{"class":122,"line":1882},[120,7957,159],{"emptyLinePlaceholder":158},[120,7959,7960,7962,7964,7966,7968,7971],{"class":122,"line":1895},[120,7961,1860],{"class":130},[120,7963,1813],{"class":134},[120,7965,648],{"class":126},[120,7967,7834],{"class":134},[120,7969,7970],{"class":130},"removeChannel",[120,7972,7973],{"class":134},"(channel))\n",[11,7975,7976],{},"Same idea: you write to your database, and every subscribed client gets the change in real time.",[65,7978,7980],{"id":7979},"websockets-are-great-but","WebSockets Are Great! But...",[11,7982,7983,7984],{},"WebSockets are great, and real-time databases make them even more convenient. But there's a catch: ",[18,7985,7986],{},"you depend on a server.",[11,7988,7989],{},"What if you don't want a server in the middle? What if you could connect directly to your peer, browser to browser?",[11,7991,7992,7993,7995,7996,535],{},"Well, that's possible thanks to ",[18,7994,4811],{},", and that's exactly what we'll explore in ",[540,7997,7998],{"href":6524},"Part 3: WebRTC and PeerJS",[11,8000,8001],{},"See you there!",[3405,8003,8004],{},"html pre.shiki code .s8I7P, html code.shiki .s8I7P{--shiki-default:#F92672}html pre.shiki code .sHkqI, html code.shiki .sHkqI{--shiki-default:#A6E22E}html pre.shiki code .sCdxs, html code.shiki .sCdxs{--shiki-default:#F8F8F2}html pre.shiki code .snpHw, html code.shiki .snpHw{--shiki-default:#88846F}html pre.shiki code .sW0Xf, html code.shiki .sW0Xf{--shiki-default:#FD971F;--shiki-default-font-style:italic}html pre.shiki code .s_Ekj, html code.shiki .s_Ekj{--shiki-default:#E6DB74}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sOx1s, html code.shiki .sOx1s{--shiki-default:#66D9EF;--shiki-default-font-style:italic}html pre.shiki code .s7s5_, html code.shiki .s7s5_{--shiki-default:#AE81FF}",{"title":116,"searchDepth":138,"depth":138,"links":8006},[8007,8008,8009,8012,8013],{"id":6556,"depth":138,"text":6557},{"id":6584,"depth":138,"text":6585},{"id":6632,"depth":138,"text":6633,"children":8010},[8011],{"id":7305,"depth":147,"text":7306},{"id":7485,"depth":138,"text":7486},{"id":7979,"depth":138,"text":7980},"2026-05-27","Bidirectional real-time with WebSockets, built right into Nuxt thanks to Nitro. Plus a tour of real-time databases like Firebase and Supabase, and how they use WebSockets under the hood.","/img/blog/real-time-vue-apps-part-2-websockets-and-real-time-databases.png",{},{"title":6530,"description":8015},"blog/real-time-vue-apps-part-2-websockets-and-real-time-databases","BsdtbVTT8kIu7XOjW6rCwQJn2CfBctCNcBxZfckLCdA",{"id":5,"title":6,"body":8022,"category":3425,"date":3426,"description":3427,"draft":3428,"extension":3429,"image":3430,"meta":10649,"navigation":158,"path":3432,"seo":10650,"stem":3434,"tighten":3428,"__hash__":3435},{"type":8,"value":8023,"toc":10632},[8024,8026,8032,8036,8044,8046,8048,8050,8052,8054,8058,8060,8064,8070,8072,8076,8124,8130,8134,8138,8142,8152,8154,8156,8158,8162,8164,8168,8174,8180,8294,8304,8306,8310,8314,8316,8318,8322,8324,8326,8330,8334,8340,8344,8346,8352,8354,8356,8360,8367,8373,8461,8467,8475,8481,8483,8489,8493,8653,8657,8913,8925,8927,8939,8945,8947,8949,8955,8957,8959,8961,8969,8977,8983,9049,9063,9067,9069,9073,9075,9095,9097,9199,9201,9207,9209,9211,9217,9225,9227,9233,9235,9241,9243,9413,9417,9563,9565,9569,9571,9647,9655,9659,9701,9707,9709,9711,9715,9719,9735,9741,9887,9899,9901,9905,10163,10167,10169,10171,10175,10355,10361,10363,10391,10393,10511,10521,10525,10541,10547,10553,10555,10557,10559,10607,10615,10617,10623,10628,10630],[11,8025,13],{},[11,8027,16,8028,21,8030,26],{},[18,8029,20],{},[23,8031,25],{},[11,8033,29,8034,33],{},[18,8035,32],{},[11,8037,36,8038,40,8040,44,8042,49],{},[18,8039,39],{},[18,8041,43],{},[46,8043,48],{},[11,8045,52],{},[11,8047,55],{},[57,8049],{"src":59,"title":60,"loading":61,"scrolling":62,"style":63},[65,8051,68],{"id":67},[11,8053,71],{},[11,8055,74,8056,78],{},[46,8057,77],{},[11,8059,81],{},[11,8061,84,8062,87],{},[46,8063,77],{},[11,8065,90,8066,94,8068,98],{},[23,8067,93],{},[23,8069,97],{},[65,8071,102],{"id":101},[11,8073,105,8074,109],{},[46,8075,108],{},[111,8077,8078],{"className":113,"code":114,"language":115,"meta":116,"style":116},[46,8079,8080,8088,8094,8100,8104,8110,8114,8118],{"__ignoreMap":116},[120,8081,8082,8084,8086],{"class":122,"line":123},[120,8083,127],{"class":126},[120,8085,131],{"class":130},[120,8087,135],{"class":134},[120,8089,8090,8092],{"class":122,"line":138},[120,8091,141],{"class":130},[120,8093,144],{"class":134},[120,8095,8096,8098],{"class":122,"line":147},[120,8097,150],{"class":130},[120,8099,144],{"class":134},[120,8101,8102],{"class":122,"line":155},[120,8103,159],{"emptyLinePlaceholder":158},[120,8105,8106,8108],{"class":122,"line":162},[120,8107,165],{"class":130},[120,8109,168],{"class":134},[120,8111,8112],{"class":122,"line":171},[120,8113,174],{"class":134},[120,8115,8116],{"class":122,"line":177},[120,8117,159],{"emptyLinePlaceholder":158},[120,8119,8120,8122],{"class":122,"line":182},[120,8121,108],{"class":130},[120,8123,168],{"class":134},[11,8125,8126,191,8128],{},[46,8127,108],{},[23,8129,194],{},[11,8131,197,8132,200],{},[46,8133,108],{},[11,8135,203,8136,207],{},[46,8137,206],{},[11,8139,8140,212],{},[46,8141,108],{},[11,8143,215,8144,218,8146,222,8148,226,8150,230],{},[46,8145,108],{},[18,8147,221],{},[46,8149,225],{},[46,8151,229],{},[11,8153,233],{},[65,8155,237],{"id":236},[11,8157,240],{},[11,8159,243,8160,247],{},[23,8161,246],{},[11,8163,250],{},[11,8165,253,8166,257],{},[18,8167,256],{},[11,8169,260,8170,264,8172,268],{},[18,8171,263],{},[18,8173,267],{},[11,8175,271,8176,275,8178,278],{},[18,8177,274],{},[46,8179,108],{},[111,8181,8182],{"className":113,"code":281,"language":115,"meta":116,"style":116},[46,8183,8184,8194,8198,8210,8224,8246,8254,8258,8264,8270,8274,8280,8284,8288],{"__ignoreMap":116},[120,8185,8186,8188,8190,8192],{"class":122,"line":123},[120,8187,288],{"class":126},[120,8189,291],{"class":134},[120,8191,295],{"class":294},[120,8193,299],{"class":298},[120,8195,8196],{"class":122,"line":138},[120,8197,159],{"emptyLinePlaceholder":158},[120,8199,8200,8202,8204,8206,8208],{"class":122,"line":147},[120,8201,127],{"class":126},[120,8203,131],{"class":130},[120,8205,312],{"class":134},[120,8207,316],{"class":315},[120,8209,319],{"class":134},[120,8211,8212,8214,8216,8218,8220,8222],{"class":122,"line":155},[120,8213,324],{"class":126},[120,8215,327],{"class":134},[120,8217,295],{"class":294},[120,8219,332],{"class":134},[120,8221,335],{"class":294},[120,8223,338],{"class":134},[120,8225,8226,8228,8230,8232,8234,8236,8238,8240,8242,8244],{"class":122,"line":162},[120,8227,324],{"class":126},[120,8229,345],{"class":134},[120,8231,295],{"class":294},[120,8233,327],{"class":134},[120,8235,352],{"class":294},[120,8237,355],{"class":134},[120,8239,358],{"class":298},[120,8241,361],{"class":294},[120,8243,364],{"class":298},[120,8245,367],{"class":134},[120,8247,8248,8250,8252],{"class":122,"line":171},[120,8249,372],{"class":134},[120,8251,295],{"class":294},[120,8253,377],{"class":134},[120,8255,8256],{"class":122,"line":177},[120,8257,159],{"emptyLinePlaceholder":158},[120,8259,8260,8262],{"class":122,"line":182},[120,8261,141],{"class":130},[120,8263,388],{"class":134},[120,8265,8266,8268],{"class":122,"line":391},[120,8267,150],{"class":130},[120,8269,144],{"class":134},[120,8271,8272],{"class":122,"line":398},[120,8273,159],{"emptyLinePlaceholder":158},[120,8275,8276,8278],{"class":122,"line":403},[120,8277,165],{"class":130},[120,8279,168],{"class":134},[120,8281,8282],{"class":122,"line":410},[120,8283,174],{"class":134},[120,8285,8286],{"class":122,"line":415},[120,8287,159],{"emptyLinePlaceholder":158},[120,8289,8290,8292],{"class":122,"line":420},[120,8291,108],{"class":130},[120,8293,168],{"class":134},[11,8295,427,8296,431,8298,435,8300,439,8302,443],{},[46,8297,430],{},[46,8299,434],{},[46,8301,438],{},[46,8303,442],{},[11,8305,446],{},[11,8307,449,8308,452],{},[46,8309,430],{},[11,8311,455,8312,458],{},[46,8313,108],{},[57,8315],{"src":461,"title":462,"loading":61,"scrolling":62,"style":463},[11,8317,466],{},[11,8319,469,8320],{},[23,8321,472],{},[65,8323,476],{"id":475},[11,8325,479],{},[11,8327,482,8328,486],{},[23,8329,485],{},[11,8331,489,8332,492],{},[18,8333,43],{},[11,8335,495,8336,498,8338,502],{},[46,8337,77],{},[23,8339,501],{},[11,8341,505,8342,509],{},[18,8343,508],{},[11,8345,512],{},[11,8347,515,8348,519,8350,522],{},[18,8349,518],{},[46,8351,108],{},[11,8353,525],{},[65,8355,529],{"id":528},[11,8357,532,8358,535],{},[46,8359,77],{},[11,8361,538,8362,548],{},[540,8363,8365],{"href":542,"rel":8364},[544],[46,8366,547],{},[11,8368,551,8369,555,8371,559],{},[46,8370,554],{},[46,8372,558],{},[111,8374,8375],{"className":562,"code":563,"language":564,"meta":116,"style":116},[46,8376,8377,8387,8397,8401,8417,8427,8431,8445,8457],{"__ignoreMap":116},[120,8378,8379,8381,8383,8385],{"class":122,"line":123},[120,8380,571],{"class":294},[120,8382,574],{"class":134},[120,8384,577],{"class":294},[120,8386,581],{"class":580},[120,8388,8389,8391,8393,8395],{"class":122,"line":138},[120,8390,571],{"class":294},[120,8392,588],{"class":134},[120,8394,577],{"class":294},[120,8396,593],{"class":580},[120,8398,8399],{"class":122,"line":147},[120,8400,159],{"emptyLinePlaceholder":158},[120,8402,8403,8405,8407,8409,8411,8413,8415],{"class":122,"line":155},[120,8404,602],{"class":126},[120,8406,605],{"class":134},[120,8408,295],{"class":294},[120,8410,610],{"class":130},[120,8412,312],{"class":134},[120,8414,615],{"class":298},[120,8416,367],{"class":134},[120,8418,8419,8421,8423,8425],{"class":122,"line":162},[120,8420,602],{"class":126},[120,8422,624],{"class":134},[120,8424,295],{"class":294},[120,8426,629],{"class":298},[120,8428,8429],{"class":122,"line":171},[120,8430,159],{"emptyLinePlaceholder":158},[120,8432,8433,8435,8437,8439,8441,8443],{"class":122,"line":177},[120,8434,554],{"class":130},[120,8436,640],{"class":134},[120,8438,430],{"class":315},[120,8440,645],{"class":134},[120,8442,648],{"class":126},[120,8444,651],{"class":134},[120,8446,8447,8449,8451,8453,8455],{"class":122,"line":182},[120,8448,656],{"class":134},[120,8450,659],{"class":294},[120,8452,624],{"class":134},[120,8454,664],{"class":294},[120,8456,667],{"class":134},[120,8458,8459],{"class":122,"line":391},[120,8460,672],{"class":134},[11,8462,675,8463,678,8465,681],{},[46,8464,430],{},[46,8466,108],{},[11,8468,684,8469,687,8471,690,8473,694],{},[46,8470,430],{},[46,8472,430],{},[46,8474,693],{},[11,8476,697,8477,701,8479,704],{},[18,8478,700],{},[46,8480,554],{},[65,8482,708],{"id":707},[11,8484,711,8485,714,8487],{},[46,8486,547],{},[18,8488,717],{},[11,8490,720,8491,724],{},[46,8492,723],{},[111,8494,8495],{"className":727,"code":728,"language":729,"meta":116,"style":116},[46,8496,8497,8513,8523,8533,8543,8551,8555,8563,8569,8583,8597,8611,8625,8629,8637,8645],{"__ignoreMap":116},[120,8498,8499,8501,8503,8505,8507,8509,8511],{"class":122,"line":123},[120,8500,736],{"class":134},[120,8502,739],{"class":294},[120,8504,742],{"class":130},[120,8506,745],{"class":130},[120,8508,295],{"class":134},[120,8510,750],{"class":580},[120,8512,753],{"class":134},[120,8514,8515,8517,8519,8521],{"class":122,"line":138},[120,8516,571],{"class":294},[120,8518,760],{"class":134},[120,8520,577],{"class":294},[120,8522,593],{"class":580},[120,8524,8525,8527,8529,8531],{"class":122,"line":147},[120,8526,571],{"class":294},[120,8528,771],{"class":134},[120,8530,577],{"class":294},[120,8532,776],{"class":580},[120,8534,8535,8537,8539,8541],{"class":122,"line":155},[120,8536,571],{"class":294},[120,8538,783],{"class":134},[120,8540,577],{"class":294},[120,8542,788],{"class":580},[120,8544,8545,8547,8549],{"class":122,"line":162},[120,8546,793],{"class":134},[120,8548,739],{"class":294},[120,8550,753],{"class":134},[120,8552,8553],{"class":122,"line":171},[120,8554,159],{"emptyLinePlaceholder":158},[120,8556,8557,8559,8561],{"class":122,"line":177},[120,8558,736],{"class":134},[120,8560,808],{"class":294},[120,8562,753],{"class":134},[120,8564,8565,8567],{"class":122,"line":182},[120,8566,815],{"class":134},[120,8568,818],{"class":294},[120,8570,8571,8573,8575,8577,8579,8581],{"class":122,"line":391},[120,8572,823],{"class":134},[120,8574,826],{"class":130},[120,8576,295],{"class":134},[120,8578,831],{"class":134},[120,8580,834],{"class":134},[120,8582,837],{"class":134},[120,8584,8585,8587,8589,8591,8593,8595],{"class":122,"line":398},[120,8586,823],{"class":134},[120,8588,844],{"class":130},[120,8590,295],{"class":134},[120,8592,831],{"class":134},[120,8594,851],{"class":134},[120,8596,837],{"class":134},[120,8598,8599,8601,8603,8605,8607,8609],{"class":122,"line":403},[120,8600,823],{"class":134},[120,8602,860],{"class":130},[120,8604,295],{"class":134},[120,8606,831],{"class":134},[120,8608,867],{"class":298},[120,8610,837],{"class":134},[120,8612,8613,8615,8617,8619,8621,8623],{"class":122,"line":410},[120,8614,823],{"class":134},[120,8616,876],{"class":130},[120,8618,295],{"class":134},[120,8620,831],{"class":134},[120,8622,883],{"class":298},[120,8624,837],{"class":134},[120,8626,8627],{"class":122,"line":415},[120,8628,890],{"class":134},[120,8630,8631,8633,8635],{"class":122,"line":420},[120,8632,895],{"class":134},[120,8634,898],{"class":294},[120,8636,901],{"class":134},[120,8638,8639,8641,8643],{"class":122,"line":904},[120,8640,907],{"class":134},[120,8642,910],{"class":294},[120,8644,753],{"class":134},[120,8646,8647,8649,8651],{"class":122,"line":915},[120,8648,793],{"class":134},[120,8650,808],{"class":294},[120,8652,753],{"class":134},[11,8654,8655,927],{},[46,8656,926],{},[111,8658,8659],{"className":727,"code":930,"language":729,"meta":116,"style":116},[46,8660,8661,8669,8677,8685,8689,8695,8711,8733,8747,8751,8805,8813,8817,8861,8865,8897,8905],{"__ignoreMap":116},[120,8662,8663,8665,8667],{"class":122,"line":123},[120,8664,736],{"class":134},[120,8666,808],{"class":294},[120,8668,753],{"class":134},[120,8670,8671,8673,8675],{"class":122,"line":138},[120,8672,815],{"class":134},[120,8674,947],{"class":294},[120,8676,753],{"class":134},[120,8678,8679,8681,8683],{"class":122,"line":147},[120,8680,895],{"class":134},[120,8682,956],{"class":294},[120,8684,901],{"class":134},[120,8686,8687],{"class":122,"line":155},[120,8688,159],{"emptyLinePlaceholder":158},[120,8690,8691,8693],{"class":122,"line":162},[120,8692,895],{"class":134},[120,8694,969],{"class":294},[120,8696,8697,8699,8701,8703,8705,8707,8709],{"class":122,"line":171},[120,8698,974],{"class":294},[120,8700,295],{"class":134},[120,8702,831],{"class":134},[120,8704,981],{"class":134},[120,8706,984],{"class":294},[120,8708,987],{"class":134},[120,8710,837],{"class":134},[120,8712,8713,8715,8717,8719,8721,8723,8725,8727,8729,8731],{"class":122,"line":177},[120,8714,994],{"class":134},[120,8716,997],{"class":130},[120,8718,295],{"class":134},[120,8720,831],{"class":134},[120,8722,1004],{"class":580},[120,8724,1007],{"class":294},[120,8726,1010],{"class":134},[120,8728,1013],{"class":294},[120,8730,1016],{"class":580},[120,8732,837],{"class":134},[120,8734,8735,8737,8739,8741,8743,8745],{"class":122,"line":182},[120,8736,994],{"class":134},[120,8738,1025],{"class":130},[120,8740,295],{"class":134},[120,8742,831],{"class":134},[120,8744,11],{"class":134},[120,8746,837],{"class":134},[120,8748,8749],{"class":122,"line":391},[120,8750,1038],{"class":134},[120,8752,8753,8755,8757,8759,8761,8763,8765,8767,8769,8771,8773,8775,8777,8779,8781,8783,8785,8787,8789,8791,8793,8795,8797,8799,8801,8803],{"class":122,"line":398},[120,8754,895],{"class":134},[120,8756,1045],{"class":294},[120,8758,1048],{"class":294},[120,8760,295],{"class":134},[120,8762,831],{"class":134},[120,8764,1055],{"class":134},[120,8766,984],{"class":294},[120,8768,1060],{"class":134},[120,8770,831],{"class":134},[120,8772,1065],{"class":134},[120,8774,997],{"class":130},[120,8776,295],{"class":134},[120,8778,831],{"class":134},[120,8780,1074],{"class":580},[120,8782,1007],{"class":294},[120,8784,1010],{"class":134},[120,8786,1013],{"class":294},[120,8788,1016],{"class":580},[120,8790,831],{"class":134},[120,8792,1065],{"class":134},[120,8794,1089],{"class":130},[120,8796,295],{"class":134},[120,8798,831],{"class":134},[120,8800,1096],{"class":134},[120,8802,831],{"class":134},[120,8804,901],{"class":134},[120,8806,8807,8809,8811],{"class":122,"line":403},[120,8808,895],{"class":134},[120,8810,1107],{"class":294},[120,8812,901],{"class":134},[120,8814,8815],{"class":122,"line":410},[120,8816,159],{"emptyLinePlaceholder":158},[120,8818,8819,8821,8823,8825,8827,8829,8831,8833,8835,8837,8839,8841,8843,8845,8847,8849,8851,8853,8855,8857,8859],{"class":122,"line":415},[120,8820,895],{"class":134},[120,8822,1120],{"class":294},[120,8824,1065],{"class":134},[120,8826,1125],{"class":130},[120,8828,295],{"class":134},[120,8830,831],{"class":134},[120,8832,1125],{"class":134},[120,8834,831],{"class":134},[120,8836,1136],{"class":134},[120,8838,1139],{"class":130},[120,8840,295],{"class":134},[120,8842,831],{"class":134},[120,8844,1146],{"class":134},[120,8846,831],{"class":134},[120,8848,1136],{"class":134},[120,8850,1153],{"class":130},[120,8852,295],{"class":134},[120,8854,831],{"class":134},[120,8856,1160],{"class":134},[120,8858,831],{"class":134},[120,8860,901],{"class":134},[120,8862,8863],{"class":122,"line":420},[120,8864,159],{"emptyLinePlaceholder":158},[120,8866,8867,8869,8871,8873,8875,8877,8879,8881,8883,8885,8887,8889,8891,8893,8895],{"class":122,"line":904},[120,8868,895],{"class":134},[120,8870,1175],{"class":294},[120,8872,1065],{"class":134},[120,8874,1180],{"class":130},[120,8876,295],{"class":134},[120,8878,831],{"class":134},[120,8880,1180],{"class":134},[120,8882,831],{"class":134},[120,8884,1065],{"class":134},[120,8886,1193],{"class":130},[120,8888,295],{"class":134},[120,8890,831],{"class":134},[120,8892,1193],{"class":134},[120,8894,831],{"class":134},[120,8896,901],{"class":134},[120,8898,8899,8901,8903],{"class":122,"line":915},[120,8900,907],{"class":134},[120,8902,947],{"class":294},[120,8904,753],{"class":134},[120,8906,8907,8909,8911],{"class":122,"line":1214},[120,8908,793],{"class":134},[120,8910,808],{"class":294},[120,8912,753],{"class":134},[11,8914,1223,8915,1227,8917,1231,8919,40,8921,1238,8923,1242],{},[46,8916,1226],{},[46,8918,1230],{},[46,8920,1234],{},[46,8922,1237],{},[46,8924,1241],{},[11,8926,1245],{},[11,8928,1248,8929,1251,8931,1255,8933,1259,8935,1263,8937,1267],{},[46,8930,1230],{},[46,8932,1254],{},[46,8934,1258],{},[46,8936,1262],{},[46,8938,1266],{},[11,8940,1270,8941,40,8943,1276],{},[46,8942,1226],{},[46,8944,1275],{},[11,8946,1279],{},[1281,8948,1284],{"id":1283},[11,8950,1287,8951,1291,8953,1295],{},[18,8952,1290],{},[46,8954,1294],{},[57,8956],{"src":1298,"title":1299,"loading":61,"scrolling":62,"style":1300},[11,8958,1303],{},[1281,8960,1307],{"id":1306},[11,8962,1310,8963,1314,8965,1318,8967,1322],{},[18,8964,1313],{},[46,8966,1317],{},[23,8968,1321],{},[11,8970,1325,8971,1329,8973,1333,8975,1337],{},[46,8972,1328],{},[46,8974,1332],{},[46,8976,1336],{},[11,8978,1340,8979,1344,8981,1348],{},[18,8980,1343],{},[23,8982,1347],{},[111,8984,8985],{"className":562,"code":1351,"language":564,"meta":116,"style":116},[46,8986,8987,8991,9003,9011,9019,9029,9037,9045],{"__ignoreMap":116},[120,8988,8989],{"class":122,"line":123},[120,8990,1359],{"class":1358},[120,8992,8993,8995,8997,8999,9001],{"class":122,"line":138},[120,8994,1364],{"class":294},[120,8996,1367],{"class":126},[120,8998,1370],{"class":134},[120,9000,295],{"class":294},[120,9002,651],{"class":134},[120,9004,9005,9007,9009],{"class":122,"line":147},[120,9006,1379],{"class":134},[120,9008,1382],{"class":298},[120,9010,1385],{"class":134},[120,9012,9013,9015,9017],{"class":122,"line":155},[120,9014,1390],{"class":134},[120,9016,1393],{"class":298},[120,9018,1385],{"class":134},[120,9020,9021,9023,9025,9027],{"class":122,"line":162},[120,9022,1400],{"class":134},[120,9024,335],{"class":294},[120,9026,1405],{"class":298},[120,9028,1385],{"class":134},[120,9030,9031,9033,9035],{"class":122,"line":171},[120,9032,1412],{"class":134},[120,9034,1415],{"class":298},[120,9036,1385],{"class":134},[120,9038,9039,9041,9043],{"class":122,"line":177},[120,9040,1422],{"class":134},[120,9042,1415],{"class":298},[120,9044,1385],{"class":134},[120,9046,9047],{"class":122,"line":182},[120,9048,174],{"class":134},[11,9050,1433,9051,1251,9053,1440,9055,1443,9057,1447,9059,1450,9061,535],{},[46,9052,1436],{},[46,9054,1439],{},[46,9056,1343],{},[46,9058,1446],{},[46,9060,1439],{},[46,9062,1393],{},[11,9064,1455,9065,1459],{},[23,9066,1458],{},[1281,9068,1463],{"id":1462},[11,9070,1466,9071,1470],{},[23,9072,1469],{},[11,9074,1473],{},[1475,9076,9077,9087],{},[1478,9078,9079,1483,9081,40,9083,1490,9085,1493],{},[18,9080,1482],{},[46,9082,1486],{},[46,9084,1489],{},[46,9086,48],{},[1478,9088,9089,1499,9091,40,9093,1506],{},[18,9090,1498],{},[46,9092,1502],{},[46,9094,1505],{},[11,9096,1509],{},[111,9098,9099],{"className":562,"code":1512,"language":564,"meta":116,"style":116},[46,9100,9101,9115,9133,9137,9141,9155,9159,9163,9175,9187,9191,9195],{"__ignoreMap":116},[120,9102,9103,9105,9107,9109,9111,9113],{"class":122,"line":123},[120,9104,554],{"class":130},[120,9106,640],{"class":134},[120,9108,430],{"class":315},[120,9110,645],{"class":134},[120,9112,648],{"class":126},[120,9114,651],{"class":134},[120,9116,9117,9119,9121,9123,9125,9127,9129,9131],{"class":122,"line":138},[120,9118,324],{"class":126},[120,9120,1535],{"class":134},[120,9122,295],{"class":294},[120,9124,1540],{"class":134},[120,9126,1543],{"class":130},[120,9128,1546],{"class":134},[120,9130,1549],{"class":298},[120,9132,367],{"class":134},[120,9134,9135],{"class":122,"line":147},[120,9136,159],{"emptyLinePlaceholder":158},[120,9138,9139],{"class":122,"line":155},[120,9140,1560],{"class":1358},[120,9142,9143,9145,9147,9149,9151,9153],{"class":122,"line":162},[120,9144,1565],{"class":134},[120,9146,659],{"class":294},[120,9148,1570],{"class":134},[120,9150,664],{"class":294},[120,9152,1535],{"class":134},[120,9154,1577],{"class":1358},[120,9156,9157],{"class":122,"line":171},[120,9158,159],{"emptyLinePlaceholder":158},[120,9160,9161],{"class":122,"line":177},[120,9162,1586],{"class":1358},[120,9164,9165,9167,9169,9171,9173],{"class":122,"line":182},[120,9166,656],{"class":134},[120,9168,659],{"class":294},[120,9170,1595],{"class":134},[120,9172,664],{"class":294},[120,9174,1600],{"class":134},[120,9176,9177,9179,9181,9183,9185],{"class":122,"line":391},[120,9178,1605],{"class":134},[120,9180,659],{"class":294},[120,9182,1610],{"class":134},[120,9184,664],{"class":294},[120,9186,1600],{"class":134},[120,9188,9189],{"class":122,"line":398},[120,9190,159],{"emptyLinePlaceholder":158},[120,9192,9193],{"class":122,"line":403},[120,9194,1623],{"class":1358},[120,9196,9197],{"class":122,"line":410},[120,9198,672],{"class":134},[11,9200,1630],{},[1475,9202,9203,9205],{},[1478,9204,1635],{},[1478,9206,1638],{},[11,9208,1641],{},[11,9210,1644],{},[11,9212,1647,9213,1650,9215,1653],{},[46,9214,1505],{},[46,9216,1393],{},[11,9218,1656,9219,1660,9221,1663,9223,1667],{},[18,9220,1659],{},[46,9222,1505],{},[46,9224,1666],{},[57,9226],{"src":1670,"title":1671,"loading":61,"scrolling":62,"style":1672},[11,9228,1675,9229,40,9231,1682],{},[46,9230,1678],{},[46,9232,1681],{},[1281,9234,1686],{"id":1685},[11,9236,1689,9237,1693,9239,1697],{},[18,9238,1692],{},[46,9240,1696],{},[11,9242,1700],{},[111,9244,9245],{"className":562,"code":1703,"language":564,"meta":116,"style":116},[46,9246,9247,9265,9269,9285,9293,9297,9301,9317,9325,9329,9333,9343,9355,9367,9371,9375,9385,9397,9409],{"__ignoreMap":116},[120,9248,9249,9251,9253,9255,9257,9259,9261,9263],{"class":122,"line":123},[120,9250,602],{"class":126},[120,9252,1712],{"class":134},[120,9254,295],{"class":294},[120,9256,1717],{"class":294},[120,9258,1720],{"class":130},[120,9260,736],{"class":134},[120,9262,1725],{"class":126},[120,9264,1728],{"class":134},[120,9266,9267],{"class":122,"line":138},[120,9268,159],{"emptyLinePlaceholder":158},[120,9270,9271,9273,9275,9277,9279,9281,9283],{"class":122,"line":147},[120,9272,127],{"class":126},[120,9274,1739],{"class":130},[120,9276,312],{"class":134},[120,9278,1744],{"class":315},[120,9280,109],{"class":294},[120,9282,1750],{"class":1749},[120,9284,319],{"class":134},[120,9286,9287,9289,9291],{"class":122,"line":155},[120,9288,1757],{"class":134},[120,9290,1760],{"class":130},[120,9292,1763],{"class":134},[120,9294,9295],{"class":122,"line":162},[120,9296,174],{"class":134},[120,9298,9299],{"class":122,"line":171},[120,9300,159],{"emptyLinePlaceholder":158},[120,9302,9303,9305,9307,9309,9311,9313,9315],{"class":122,"line":177},[120,9304,127],{"class":126},[120,9306,1778],{"class":130},[120,9308,312],{"class":134},[120,9310,1744],{"class":315},[120,9312,109],{"class":294},[120,9314,1750],{"class":1749},[120,9316,319],{"class":134},[120,9318,9319,9321,9323],{"class":122,"line":182},[120,9320,1757],{"class":134},[120,9322,1795],{"class":130},[120,9324,1763],{"class":134},[120,9326,9327],{"class":122,"line":391},[120,9328,174],{"class":134},[120,9330,9331],{"class":122,"line":398},[120,9332,159],{"emptyLinePlaceholder":158},[120,9334,9335,9337,9339,9341],{"class":122,"line":403},[120,9336,1810],{"class":130},[120,9338,1813],{"class":134},[120,9340,648],{"class":126},[120,9342,651],{"class":134},[120,9344,9345,9347,9349,9351,9353],{"class":122,"line":410},[120,9346,1822],{"class":134},[120,9348,1825],{"class":130},[120,9350,312],{"class":134},[120,9352,1830],{"class":580},[120,9354,1833],{"class":134},[120,9356,9357,9359,9361,9363,9365],{"class":122,"line":415},[120,9358,1822],{"class":134},[120,9360,1825],{"class":130},[120,9362,312],{"class":134},[120,9364,1844],{"class":580},[120,9366,1847],{"class":134},[120,9368,9369],{"class":122,"line":420},[120,9370,672],{"class":134},[120,9372,9373],{"class":122,"line":904},[120,9374,159],{"emptyLinePlaceholder":158},[120,9376,9377,9379,9381,9383],{"class":122,"line":915},[120,9378,1860],{"class":130},[120,9380,1813],{"class":134},[120,9382,648],{"class":126},[120,9384,651],{"class":134},[120,9386,9387,9389,9391,9393,9395],{"class":122,"line":1214},[120,9388,1822],{"class":134},[120,9390,1873],{"class":130},[120,9392,312],{"class":134},[120,9394,1830],{"class":580},[120,9396,1833],{"class":134},[120,9398,9399,9401,9403,9405,9407],{"class":122,"line":1882},[120,9400,1822],{"class":134},[120,9402,1873],{"class":130},[120,9404,312],{"class":134},[120,9406,1844],{"class":580},[120,9408,1847],{"class":134},[120,9410,9411],{"class":122,"line":1895},[120,9412,672],{"class":134},[11,9414,1900,9415,1903],{},[46,9416,554],{},[111,9418,9419],{"className":562,"code":1906,"language":564,"meta":116,"style":116},[46,9420,9421,9431,9439,9475,9479,9487,9523,9527,9559],{"__ignoreMap":116},[120,9422,9423,9425,9427,9429],{"class":122,"line":123},[120,9424,554],{"class":130},[120,9426,1813],{"class":134},[120,9428,648],{"class":126},[120,9430,651],{"class":134},[120,9432,9433,9435,9437],{"class":122,"line":138},[120,9434,324],{"class":126},[120,9436,1925],{"class":134},[120,9438,1928],{"class":294},[120,9440,9441,9443,9445,9447,9449,9451,9453,9455,9457,9459,9461,9463,9465,9467,9469,9471,9473],{"class":122,"line":147},[120,9442,1933],{"class":134},[120,9444,1936],{"class":130},[120,9446,312],{"class":134},[120,9448,1941],{"class":580},[120,9450,1944],{"class":134},[120,9452,1947],{"class":294},[120,9454,1950],{"class":134},[120,9456,1936],{"class":130},[120,9458,312],{"class":134},[120,9460,1957],{"class":580},[120,9462,1944],{"class":134},[120,9464,1947],{"class":294},[120,9466,1950],{"class":134},[120,9468,1936],{"class":130},[120,9470,312],{"class":134},[120,9472,1970],{"class":580},[120,9474,367],{"class":134},[120,9476,9477],{"class":122,"line":155},[120,9478,159],{"emptyLinePlaceholder":158},[120,9480,9481,9483,9485],{"class":122,"line":162},[120,9482,324],{"class":126},[120,9484,1983],{"class":134},[120,9486,1928],{"class":294},[120,9488,9489,9491,9493,9495,9497,9499,9501,9503,9505,9507,9509,9511,9513,9515,9517,9519,9521],{"class":122,"line":171},[120,9490,1933],{"class":134},[120,9492,1936],{"class":130},[120,9494,312],{"class":134},[120,9496,1996],{"class":580},[120,9498,1944],{"class":134},[120,9500,1947],{"class":294},[120,9502,1950],{"class":134},[120,9504,1936],{"class":130},[120,9506,312],{"class":134},[120,9508,2009],{"class":580},[120,9510,1944],{"class":134},[120,9512,1947],{"class":294},[120,9514,1950],{"class":134},[120,9516,1936],{"class":130},[120,9518,312],{"class":134},[120,9520,2022],{"class":580},[120,9522,367],{"class":134},[120,9524,9525],{"class":122,"line":177},[120,9526,159],{"emptyLinePlaceholder":158},[120,9528,9529,9531,9533,9535,9537,9539,9541,9543,9545,9547,9549,9551,9553,9555,9557],{"class":122,"line":182},[120,9530,2033],{"class":134},[120,9532,295],{"class":294},[120,9534,2038],{"class":134},[120,9536,247],{"class":294},[120,9538,624],{"class":134},[120,9540,109],{"class":294},[120,9542,2047],{"class":298},[120,9544,1944],{"class":134},[120,9546,335],{"class":294},[120,9548,2054],{"class":134},[120,9550,247],{"class":294},[120,9552,624],{"class":134},[120,9554,109],{"class":294},[120,9556,2047],{"class":298},[120,9558,367],{"class":134},[120,9560,9561],{"class":122,"line":391},[120,9562,672],{"class":134},[11,9564,2071],{},[11,9566,2074,9567,2078],{},[23,9568,2077],{},[11,9570,2081],{},[111,9572,9573],{"className":562,"code":2084,"language":564,"meta":116,"style":116},[46,9574,9575,9591,9599,9603,9613,9617,9631,9639,9643],{"__ignoreMap":116},[120,9576,9577,9579,9581,9583,9585,9587,9589],{"class":122,"line":123},[120,9578,127],{"class":126},[120,9580,1739],{"class":130},[120,9582,312],{"class":134},[120,9584,1744],{"class":315},[120,9586,109],{"class":294},[120,9588,1750],{"class":1749},[120,9590,319],{"class":134},[120,9592,9593,9595,9597],{"class":122,"line":138},[120,9594,1757],{"class":134},[120,9596,1760],{"class":130},[120,9598,1763],{"class":134},[120,9600,9601],{"class":122,"line":147},[120,9602,159],{"emptyLinePlaceholder":158},[120,9604,9605,9607,9609,9611],{"class":122,"line":155},[120,9606,2119],{"class":294},[120,9608,355],{"class":134},[120,9610,2124],{"class":130},[120,9612,2127],{"class":134},[120,9614,9615],{"class":122,"line":162},[120,9616,2132],{"class":1358},[120,9618,9619,9621,9623,9625,9627,9629],{"class":122,"line":171},[120,9620,2137],{"class":294},[120,9622,355],{"class":134},[120,9624,2142],{"class":294},[120,9626,2145],{"class":134},[120,9628,295],{"class":294},[120,9630,2150],{"class":298},[120,9632,9633,9635,9637],{"class":122,"line":177},[120,9634,2155],{"class":134},[120,9636,2158],{"class":130},[120,9638,144],{"class":134},[120,9640,9641],{"class":122,"line":182},[120,9642,2165],{"class":134},[120,9644,9645],{"class":122,"line":391},[120,9646,174],{"class":134},[11,9648,9649,431,9651,2178,9653,2181],{},[46,9650,2174],{},[46,9652,2177],{},[46,9654,883],{},[11,9656,2184,9657,2188],{},[46,9658,2187],{},[111,9660,9661],{"className":562,"code":2191,"language":564,"meta":116,"style":116},[46,9662,9663,9673,9681,9689,9697],{"__ignoreMap":116},[120,9664,9665,9667,9669,9671],{"class":122,"line":123},[120,9666,2198],{"class":294},[120,9668,2201],{"class":134},[120,9670,2204],{"class":294},[120,9672,2207],{"class":134},[120,9674,9675,9677,9679],{"class":122,"line":138},[120,9676,1565],{"class":134},[120,9678,295],{"class":294},[120,9680,2216],{"class":134},[120,9682,9683,9685,9687],{"class":122,"line":147},[120,9684,2221],{"class":134},[120,9686,295],{"class":294},[120,9688,2226],{"class":298},[120,9690,9691,9693,9695],{"class":122,"line":155},[120,9692,2231],{"class":134},[120,9694,295],{"class":294},[120,9696,2226],{"class":298},[120,9698,9699],{"class":122,"line":162},[120,9700,174],{"class":134},[11,9702,2242,9703,2245,9705,2249],{},[46,9704,2187],{},[46,9706,2248],{},[65,9708,2253],{"id":2252},[11,9710,2256],{},[11,9712,2259,9713,535],{},[23,9714,2262],{},[11,9716,2265,9717,2269],{},[18,9718,2268],{},[11,9720,2272,9721,2276,9723,1251,9725,1251,9727,1440,9729,2285,9731,2289,9733,2293],{},[46,9722,2275],{},[46,9724,1486],{},[46,9726,1489],{},[46,9728,826],{},[46,9730,844],{},[46,9732,2288],{},[46,9734,2292],{},[11,9736,2296,9737,2300,9739,2304],{},[18,9738,2299],{},[23,9740,2303],{},[111,9742,9743],{"className":562,"code":2307,"language":564,"meta":116,"style":116},[46,9744,9745,9753,9771,9789,9799,9803,9809,9813,9827,9831,9845,9849,9863,9867,9879,9883],{"__ignoreMap":116},[120,9746,9747,9749,9751],{"class":122,"line":123},[120,9748,127],{"class":126},[120,9750,2316],{"class":130},[120,9752,2319],{"class":134},[120,9754,9755,9757,9759,9761,9763,9765,9767,9769],{"class":122,"line":138},[120,9756,2324],{"class":315},[120,9758,109],{"class":294},[120,9760,2329],{"class":126},[120,9762,1251],{"class":134},[120,9764,2334],{"class":315},[120,9766,109],{"class":294},[120,9768,2329],{"class":126},[120,9770,1385],{"class":134},[120,9772,9773,9775,9777,9779,9781,9783,9785,9787],{"class":122,"line":147},[120,9774,2345],{"class":315},[120,9776,109],{"class":294},[120,9778,2329],{"class":126},[120,9780,1251],{"class":134},[120,9782,2354],{"class":315},[120,9784,109],{"class":294},[120,9786,2329],{"class":126},[120,9788,1385],{"class":134},[120,9790,9791,9793,9795,9797],{"class":122,"line":155},[120,9792,2365],{"class":315},[120,9794,109],{"class":294},[120,9796,2370],{"class":1749},[120,9798,1385],{"class":134},[120,9800,9801],{"class":122,"line":162},[120,9802,319],{"class":134},[120,9804,9805,9807],{"class":122,"line":171},[120,9806,2381],{"class":294},[120,9808,2384],{"class":134},[120,9810,9811],{"class":122,"line":177},[120,9812,2389],{"class":1358},[120,9814,9815,9817,9819,9821,9823,9825],{"class":122,"line":182},[120,9816,2394],{"class":134},[120,9818,736],{"class":294},[120,9820,2399],{"class":134},[120,9822,2402],{"class":294},[120,9824,2405],{"class":134},[120,9826,2408],{"class":294},[120,9828,9829],{"class":122,"line":391},[120,9830,2413],{"class":1358},[120,9832,9833,9835,9837,9839,9841,9843],{"class":122,"line":398},[120,9834,2394],{"class":134},[120,9836,2402],{"class":294},[120,9838,2422],{"class":134},[120,9840,2425],{"class":294},[120,9842,2399],{"class":134},[120,9844,2408],{"class":294},[120,9846,9847],{"class":122,"line":403},[120,9848,2434],{"class":1358},[120,9850,9851,9853,9855,9857,9859,9861],{"class":122,"line":410},[120,9852,2439],{"class":134},[120,9854,736],{"class":294},[120,9856,2444],{"class":134},[120,9858,2402],{"class":294},[120,9860,2449],{"class":134},[120,9862,2408],{"class":294},[120,9864,9865],{"class":122,"line":415},[120,9866,2456],{"class":1358},[120,9868,9869,9871,9873,9875,9877],{"class":122,"line":420},[120,9870,2439],{"class":134},[120,9872,2402],{"class":294},[120,9874,2465],{"class":134},[120,9876,2425],{"class":294},[120,9878,2470],{"class":134},[120,9880,9881],{"class":122,"line":904},[120,9882,2475],{"class":134},[120,9884,9885],{"class":122,"line":915},[120,9886,174],{"class":134},[11,9888,2482,9889,2485,9891,2488,9893,2492,9895,2496,9897,2499],{},[46,9890,883],{},[46,9892,1505],{},[18,9894,2491],{},[46,9896,2495],{},[46,9898,883],{},[57,9900],{"src":2502,"title":2503,"loading":61,"scrolling":62,"style":2504},[11,9902,2507,9903,2511],{},[23,9904,2510],{},[111,9906,9907],{"className":562,"code":2514,"language":564,"meta":116,"style":116},[46,9908,9909,9913,9925,9939,9949,9953,9973,9977,9999,10007,10011,10015,10019,10023,10035,10043,10057,10067,10079,10083,10095,10103,10111,10127,10131,10143,10151,10155,10159],{"__ignoreMap":116},[120,9910,9911],{"class":122,"line":123},[120,9912,2521],{"class":1358},[120,9914,9915,9917,9919,9921,9923],{"class":122,"line":138},[120,9916,2526],{"class":134},[120,9918,659],{"class":294},[120,9920,1595],{"class":134},[120,9922,664],{"class":294},[120,9924,1600],{"class":134},[120,9926,9927,9929,9931,9933,9935,9937],{"class":122,"line":147},[120,9928,2539],{"class":294},[120,9930,355],{"class":134},[120,9932,602],{"class":126},[120,9934,2546],{"class":134},[120,9936,2549],{"class":294},[120,9938,2552],{"class":134},[120,9940,9941,9943,9945,9947],{"class":122,"line":155},[120,9942,2119],{"class":294},[120,9944,355],{"class":134},[120,9946,2561],{"class":130},[120,9948,2564],{"class":134},[120,9950,9951],{"class":122,"line":162},[120,9952,2569],{"class":1358},[120,9954,9955,9957,9959,9961,9963,9965,9967,9969,9971],{"class":122,"line":171},[120,9956,2137],{"class":294},[120,9958,2576],{"class":134},[120,9960,2425],{"class":294},[120,9962,2047],{"class":298},[120,9964,2583],{"class":134},[120,9966,295],{"class":294},[120,9968,2588],{"class":134},[120,9970,335],{"class":294},[120,9972,2593],{"class":134},[120,9974,9975],{"class":122,"line":177},[120,9976,2598],{"class":1358},[120,9978,9979,9981,9983,9985,9987,9989,9991,9993,9995,9997],{"class":122,"line":182},[120,9980,2603],{"class":294},[120,9982,2606],{"class":294},[120,9984,2576],{"class":134},[120,9986,736],{"class":294},[120,9988,2047],{"class":298},[120,9990,2583],{"class":134},[120,9992,295],{"class":294},[120,9994,2588],{"class":134},[120,9996,2402],{"class":294},[120,9998,2623],{"class":134},[120,10000,10001,10003,10005],{"class":122,"line":391},[120,10002,2628],{"class":134},[120,10004,295],{"class":294},[120,10006,299],{"class":298},[120,10008,10009],{"class":122,"line":398},[120,10010,2165],{"class":134},[120,10012,10013],{"class":122,"line":403},[120,10014,174],{"class":134},[120,10016,10017],{"class":122,"line":410},[120,10018,159],{"emptyLinePlaceholder":158},[120,10020,10021],{"class":122,"line":415},[120,10022,2649],{"class":1358},[120,10024,10025,10027,10029,10031,10033],{"class":122,"line":420},[120,10026,2654],{"class":134},[120,10028,659],{"class":294},[120,10030,1610],{"class":134},[120,10032,664],{"class":294},[120,10034,1600],{"class":134},[120,10036,10037,10039,10041],{"class":122,"line":904},[120,10038,2667],{"class":134},[120,10040,295],{"class":294},[120,10042,2226],{"class":298},[120,10044,10045,10047,10049,10051,10053,10055],{"class":122,"line":915},[120,10046,2539],{"class":294},[120,10048,355],{"class":134},[120,10050,602],{"class":126},[120,10052,2546],{"class":134},[120,10054,2549],{"class":294},[120,10056,2552],{"class":134},[120,10058,10059,10061,10063,10065],{"class":122,"line":1214},[120,10060,2119],{"class":294},[120,10062,355],{"class":134},[120,10064,2561],{"class":130},[120,10066,2564],{"class":134},[120,10068,10069,10071,10073,10075,10077],{"class":122,"line":1882},[120,10070,2137],{"class":294},[120,10072,2702],{"class":134},[120,10074,2425],{"class":294},[120,10076,2047],{"class":298},[120,10078,319],{"class":134},[120,10080,10081],{"class":122,"line":1895},[120,10082,2713],{"class":1358},[120,10084,10085,10087,10089,10091,10093],{"class":122,"line":2716},[120,10086,2719],{"class":134},[120,10088,295],{"class":294},[120,10090,2724],{"class":134},[120,10092,335],{"class":294},[120,10094,2729],{"class":134},[120,10096,10097,10099,10101],{"class":122,"line":2732},[120,10098,2735],{"class":134},[120,10100,295],{"class":294},[120,10102,2150],{"class":298},[120,10104,10105,10107,10109],{"class":122,"line":2742},[120,10106,2745],{"class":134},[120,10108,295],{"class":294},[120,10110,299],{"class":298},[120,10112,10113,10115,10117,10119,10121,10123,10125],{"class":122,"line":2752},[120,10114,2755],{"class":134},[120,10116,2758],{"class":294},[120,10118,2606],{"class":294},[120,10120,2702],{"class":134},[120,10122,736],{"class":294},[120,10124,2047],{"class":298},[120,10126,319],{"class":134},[120,10128,10129],{"class":122,"line":2771},[120,10130,2774],{"class":1358},[120,10132,10133,10135,10137,10139,10141],{"class":122,"line":2777},[120,10134,2719],{"class":134},[120,10136,295],{"class":294},[120,10138,2724],{"class":134},[120,10140,2402],{"class":294},[120,10142,2788],{"class":134},[120,10144,10145,10147,10149],{"class":122,"line":2791},[120,10146,2745],{"class":134},[120,10148,295],{"class":294},[120,10150,299],{"class":298},[120,10152,10153],{"class":122,"line":2800},[120,10154,2803],{"class":134},[120,10156,10157],{"class":122,"line":2806},[120,10158,2165],{"class":134},[120,10160,10161],{"class":122,"line":2811},[120,10162,174],{"class":134},[11,10164,2816,10165,2819],{},[46,10166,2187],{},[65,10168,2823],{"id":2822},[11,10170,2826],{},[11,10172,2829,10173,2833],{},[18,10174,2832],{},[111,10176,10177],{"className":562,"code":2836,"language":564,"meta":116,"style":116},[46,10178,10179,10195,10199,10213,10217,10239,10243,10265,10269,10283,10297,10301,10327,10337,10343,10347,10351],{"__ignoreMap":116},[120,10180,10181,10183,10185,10187,10189,10191,10193],{"class":122,"line":123},[120,10182,602],{"class":126},[120,10184,2845],{"class":134},[120,10186,295],{"class":294},[120,10188,2850],{"class":134},[120,10190,335],{"class":294},[120,10192,2855],{"class":298},[120,10194,2858],{"class":1358},[120,10196,10197],{"class":122,"line":138},[120,10198,159],{"emptyLinePlaceholder":158},[120,10200,10201,10203,10205,10207,10209,10211],{"class":122,"line":147},[120,10202,2539],{"class":294},[120,10204,355],{"class":134},[120,10206,602],{"class":126},[120,10208,2873],{"class":134},[120,10210,2549],{"class":294},[120,10212,2878],{"class":134},[120,10214,10215],{"class":122,"line":155},[120,10216,2883],{"class":1358},[120,10218,10219,10221,10223,10225,10227,10229,10231,10233,10235,10237],{"class":122,"line":162},[120,10220,324],{"class":126},[120,10222,2890],{"class":134},[120,10224,295],{"class":294},[120,10226,1540],{"class":134},[120,10228,2897],{"class":130},[120,10230,2900],{"class":134},[120,10232,1543],{"class":130},[120,10234,2905],{"class":134},[120,10236,2402],{"class":294},[120,10238,2910],{"class":134},[120,10240,10241],{"class":122,"line":171},[120,10242,2915],{"class":1358},[120,10244,10245,10247,10249,10251,10253,10255,10257,10259,10261,10263],{"class":122,"line":177},[120,10246,324],{"class":126},[120,10248,2922],{"class":134},[120,10250,295],{"class":294},[120,10252,1540],{"class":134},[120,10254,2897],{"class":130},[120,10256,2931],{"class":134},[120,10258,1543],{"class":130},[120,10260,2936],{"class":134},[120,10262,2402],{"class":294},[120,10264,2941],{"class":134},[120,10266,10267],{"class":122,"line":182},[120,10268,159],{"emptyLinePlaceholder":158},[120,10270,10271,10273,10275,10277,10279,10281],{"class":122,"line":391},[120,10272,324],{"class":126},[120,10274,2952],{"class":134},[120,10276,295],{"class":294},[120,10278,2957],{"class":134},[120,10280,335],{"class":294},[120,10282,2962],{"class":134},[120,10284,10285,10287,10289,10291,10293,10295],{"class":122,"line":398},[120,10286,324],{"class":126},[120,10288,2969],{"class":134},[120,10290,295],{"class":294},[120,10292,2974],{"class":134},[120,10294,335],{"class":294},[120,10296,2979],{"class":134},[120,10298,10299],{"class":122,"line":403},[120,10300,159],{"emptyLinePlaceholder":158},[120,10302,10303,10305,10307,10309,10311,10313,10315,10317,10319,10321,10323,10325],{"class":122,"line":410},[120,10304,2119],{"class":294},[120,10306,2990],{"class":134},[120,10308,664],{"class":294},[120,10310,2952],{"class":134},[120,10312,2402],{"class":294},[120,10314,2969],{"class":134},[120,10316,664],{"class":294},[120,10318,2969],{"class":134},[120,10320,736],{"class":294},[120,10322,2845],{"class":134},[120,10324,664],{"class":294},[120,10326,3011],{"class":134},[120,10328,10329,10331,10333,10335],{"class":122,"line":415},[120,10330,3016],{"class":130},[120,10332,312],{"class":134},[120,10334,3021],{"class":580},[120,10336,367],{"class":134},[120,10338,10339,10341],{"class":122,"line":420},[120,10340,3028],{"class":130},[120,10342,144],{"class":134},[120,10344,10345],{"class":122,"line":904},[120,10346,3035],{"class":294},[120,10348,10349],{"class":122,"line":915},[120,10350,2165],{"class":134},[120,10352,10353],{"class":122,"line":1214},[120,10354,174],{"class":134},[11,10356,3046,10357,3050,10359,3054],{},[46,10358,3049],{},[46,10360,3053],{},[11,10362,3057],{},[111,10364,10365],{"className":562,"code":3060,"language":564,"meta":116,"style":116},[46,10366,10367,10377,10387],{"__ignoreMap":116},[120,10368,10369,10371,10373,10375],{"class":122,"line":123},[120,10370,2198],{"class":294},[120,10372,355],{"class":134},[120,10374,2561],{"class":130},[120,10376,3073],{"class":134},[120,10378,10379,10381,10383,10385],{"class":122,"line":138},[120,10380,3078],{"class":130},[120,10382,312],{"class":134},[120,10384,3083],{"class":580},[120,10386,367],{"class":134},[120,10388,10389],{"class":122,"line":147},[120,10390,174],{"class":134},[11,10392,3092],{},[111,10394,10395],{"className":562,"code":3095,"language":564,"meta":116,"style":116},[46,10396,10397,10409,10425,10429,10447,10451,10459,10467,10483,10487,10491,10499,10507],{"__ignoreMap":116},[120,10398,10399,10401,10403,10405,10407],{"class":122,"line":123},[120,10400,602],{"class":126},[120,10402,3104],{"class":134},[120,10404,295],{"class":294},[120,10406,610],{"class":130},[120,10408,3111],{"class":134},[120,10410,10411,10413,10415,10417,10419,10421,10423],{"class":122,"line":138},[120,10412,602],{"class":126},[120,10414,3118],{"class":134},[120,10416,295],{"class":294},[120,10418,610],{"class":130},[120,10420,312],{"class":134},[120,10422,2177],{"class":298},[120,10424,367],{"class":134},[120,10426,10427],{"class":122,"line":147},[120,10428,159],{"emptyLinePlaceholder":158},[120,10430,10431,10433,10435,10437,10439,10441,10443,10445],{"class":122,"line":155},[120,10432,602],{"class":126},[120,10434,3139],{"class":134},[120,10436,295],{"class":294},[120,10438,3144],{"class":130},[120,10440,1813],{"class":134},[120,10442,648],{"class":126},[120,10444,3151],{"class":294},[120,10446,3154],{"class":134},[120,10448,10449],{"class":122,"line":162},[120,10450,159],{"emptyLinePlaceholder":158},[120,10452,10453,10455,10457],{"class":122,"line":171},[120,10454,127],{"class":126},[120,10456,3165],{"class":130},[120,10458,135],{"class":134},[120,10460,10461,10463,10465],{"class":122,"line":177},[120,10462,3172],{"class":134},[120,10464,3175],{"class":294},[120,10466,3178],{"class":298},[120,10468,10469,10471,10473,10475,10477,10479,10481],{"class":122,"line":182},[120,10470,2119],{"class":294},[120,10472,3185],{"class":134},[120,10474,3188],{"class":294},[120,10476,2047],{"class":298},[120,10478,3193],{"class":134},[120,10480,295],{"class":294},[120,10482,3198],{"class":134},[120,10484,10485],{"class":122,"line":391},[120,10486,174],{"class":134},[120,10488,10489],{"class":122,"line":398},[120,10490,159],{"emptyLinePlaceholder":158},[120,10492,10493,10495,10497],{"class":122,"line":403},[120,10494,127],{"class":126},[120,10496,3213],{"class":130},[120,10498,135],{"class":134},[120,10500,10501,10503,10505],{"class":122,"line":410},[120,10502,3220],{"class":134},[120,10504,295],{"class":294},[120,10506,2150],{"class":298},[120,10508,10509],{"class":122,"line":415},[120,10510,174],{"class":134},[11,10512,10513,3234,10515,3237,10517,3241,10519,535],{},[46,10514,3233],{},[46,10516,1146],{},[23,10518,3240],{},[23,10520,3244],{},[11,10522,3247,10523,3250],{},[46,10524,1125],{},[111,10526,10527],{"className":562,"code":3253,"language":564,"meta":116,"style":116},[46,10528,10529],{"__ignoreMap":116},[120,10530,10531,10533,10535,10537,10539],{"class":122,"line":123},[120,10532,2198],{"class":294},[120,10534,355],{"class":134},[120,10536,2142],{"class":294},[120,10538,3266],{"class":134},[120,10540,3269],{"class":294},[11,10542,3272,10543,3275,10545,3278],{},[46,10544,1193],{},[46,10546,883],{},[11,10548,3281,10549,3284,10551,3287],{},[46,10550,1180],{},[46,10552,1193],{},[11,10554,3290],{},[65,10556,3294],{"id":3293},[11,10558,3297],{},[3299,10560,10561,10569,10579,10585,10589,10595,10603],{},[1478,10562,10563,3306,10565,3310,10567,3313],{},[18,10564,3305],{},[46,10566,3309],{},[46,10568,2248],{},[1478,10570,10571,3319,10573,40,10575,3324,10577,3328],{},[18,10572,3318],{},[46,10574,2248],{},[46,10576,2187],{},[46,10578,3327],{},[1478,10580,10581,701,10583,535],{},[18,10582,3333],{},[46,10584,3336],{},[1478,10586,10587,3342],{},[18,10588,3341],{},[1478,10590,10591,3348,10593,535],{},[18,10592,3347],{},[46,10594,2187],{},[1478,10596,10597,3356,10599,1263,10601,535],{},[18,10598,3355],{},[46,10600,1139],{},[46,10602,1153],{},[1478,10604,10605,3366],{},[18,10606,3365],{},[11,10608,3369,10609,3372,10611,3375,10613,3378],{},[46,10610,554],{},[46,10612,48],{},[46,10614,547],{},[65,10616,3382],{"id":3381},[11,10618,3385,10619,3388,10621,3392],{},[46,10620,48],{},[46,10622,3391],{},[11,10624,3395,10625,2142],{},[540,10626,3400],{"href":3398,"rel":10627},[544],[11,10629,3403],{},[3405,10631,3407],{},{"title":116,"searchDepth":138,"depth":138,"links":10633},[10634,10635,10636,10637,10638,10639,10645,10646,10647,10648],{"id":67,"depth":138,"text":68},{"id":101,"depth":138,"text":102},{"id":236,"depth":138,"text":237},{"id":475,"depth":138,"text":476},{"id":528,"depth":138,"text":529},{"id":707,"depth":138,"text":708,"children":10640},[10641,10642,10643,10644],{"id":1283,"depth":147,"text":1284},{"id":1306,"depth":147,"text":1307},{"id":1462,"depth":147,"text":1463},{"id":1685,"depth":147,"text":1686},{"id":2252,"depth":138,"text":2253},{"id":2822,"depth":138,"text":2823},{"id":3293,"depth":138,"text":3294},{"id":3381,"depth":138,"text":3382},{},{"title":6,"description":3427},[10652,11670,13089],{"id":3437,"title":3438,"body":10653,"category":3425,"date":4742,"description":4743,"draft":3428,"extension":3429,"image":4744,"meta":11668,"navigation":158,"path":4746,"seo":11669,"stem":4748,"tighten":3428,"__hash__":4749},{"type":8,"value":10654,"toc":11656},[10655,10661,10665,10667,10669,10671,10675,10677,10681,10683,10697,10699,10703,10705,10709,10711,10713,10721,10725,10825,10827,10829,10850,10852,10854,10856,10861,10863,10865,10875,10879,10883,10885,10887,10889,10891,10893,10920,10926,10928,10930,10934,10936,10940,10942,10947,10959,10961,11007,11009,11033,11035,11037,11041,11043,11045,11263,11265,11269,11271,11275,11277,11291,11293,11297,11299,11301,11307,11311,11313,11315,11317,11329,11334,11338,11368,11372,11378,11518,11522,11524,11526,11532,11632,11638,11640,11642,11644,11646,11648,11652,11654],[3442,10656,10657],{"color":3444,"icon":3445},[11,10658,3448,10659,3453],{},[540,10660,3452],{"href":3451},[11,10662,3456,10663,3460],{},[18,10664,3459],{},[11,10666,3463],{},[65,10668,3467],{"id":3466},[11,10670,3470],{},[3472,10672,10673],{},[11,10674,3476],{},[11,10676,3479],{},[3472,10678,10679],{},[11,10680,3484],{},[11,10682,3487],{},[1475,10684,10685,10689,10693],{},[1478,10686,3492,10687,3496],{},[18,10688,3495],{},[1478,10690,3499,10691,3503],{},[18,10692,3502],{},[1478,10694,3506,10695,3510],{},[18,10696,3509],{},[11,10698,3513],{},[3472,10700,10701],{},[11,10702,3518],{},[11,10704,3521],{},[3472,10706,10707],{},[11,10708,3526],{},[65,10710,3530],{"id":3529},[11,10712,3533],{},[1475,10714,10715,10717,10719],{},[1478,10716,3538],{},[1478,10718,3541],{},[1478,10720,3544],{},[11,10722,3547,10723,3551],{},[46,10724,3550],{},[111,10726,10727],{"className":727,"code":3554,"language":729,"meta":116,"style":116},[46,10728,10729,10741,10751,10755,10771,10779,10783,10791,10817],{"__ignoreMap":116},[120,10730,10731,10733,10735,10737,10739],{"class":122,"line":123},[120,10732,736],{"class":134},[120,10734,739],{"class":294},[120,10736,742],{"class":130},[120,10738,3567],{"class":130},[120,10740,753],{"class":134},[120,10742,10743,10745,10747,10749],{"class":122,"line":138},[120,10744,571],{"class":294},[120,10746,574],{"class":134},[120,10748,577],{"class":294},[120,10750,581],{"class":580},[120,10752,10753],{"class":122,"line":147},[120,10754,159],{"emptyLinePlaceholder":158},[120,10756,10757,10759,10761,10763,10765,10767,10769],{"class":122,"line":155},[120,10758,602],{"class":126},[120,10760,3590],{"class":134},[120,10762,295],{"class":294},[120,10764,610],{"class":130},[120,10766,312],{"class":134},[120,10768,615],{"class":298},[120,10770,367],{"class":134},[120,10772,10773,10775,10777],{"class":122,"line":162},[120,10774,793],{"class":134},[120,10776,739],{"class":294},[120,10778,753],{"class":134},[120,10780,10781],{"class":122,"line":171},[120,10782,159],{"emptyLinePlaceholder":158},[120,10784,10785,10787,10789],{"class":122,"line":177},[120,10786,736],{"class":134},[120,10788,808],{"class":294},[120,10790,753],{"class":134},[120,10792,10793,10795,10797,10799,10801,10803,10805,10807,10809,10811,10813,10815],{"class":122,"line":182},[120,10794,815],{"class":134},[120,10796,3627],{"class":294},[120,10798,1136],{"class":134},[120,10800,3632],{"class":130},[120,10802,295],{"class":134},[120,10804,831],{"class":134},[120,10806,3639],{"class":134},[120,10808,3642],{"class":294},[120,10810,831],{"class":134},[120,10812,3647],{"class":134},[120,10814,3627],{"class":294},[120,10816,753],{"class":134},[120,10818,10819,10821,10823],{"class":122,"line":391},[120,10820,793],{"class":134},[120,10822,808],{"class":294},[120,10824,753],{"class":134},[11,10826,3662],{},[11,10828,3665],{},[1475,10830,10831,10838,10842,10846],{},[1478,10832,10833,3673,10835,3679],{},[18,10834,3672],{},[540,10836,3678],{"href":3676,"rel":10837},[544],[1478,10839,10840,3685],{},[18,10841,3684],{},[1478,10843,10844,3691],{},[18,10845,3690],{},[1478,10847,10848,3697],{},[18,10849,3696],{},[11,10851,3700],{},[65,10853,3704],{"id":3703},[11,10855,3707],{},[11,10857,3710,10858,535],{},[540,10859,3715],{"href":3713,"rel":10860},[544],[11,10862,3718],{},[11,10864,3721],{},[1475,10866,10867,10869,10871,10873],{},[1478,10868,3726],{},[1478,10870,3729],{},[1478,10872,3732],{},[1478,10874,3735],{},[11,10876,3738,10877,3742],{},[18,10878,3741],{},[11,10880,3745,10881,3749],{},[18,10882,3748],{},[11,10884,3752],{},[11,10886,3755],{},[1281,10888,3759],{"id":3758},[11,10890,3762],{},[11,10892,3765],{},[1475,10894,10895,10902,10909],{},[1478,10896,10897,3775,10900,3779],{},[540,10898,3774],{"href":3772,"rel":10899},[544],[18,10901,3778],{},[1478,10903,10904,3775,10907,3790],{},[540,10905,3786],{"href":3784,"rel":10906},[544],[18,10908,3789],{},[1478,10910,10911,3798,10914,3801,10916,3805,10918,535],{},[540,10912,3797],{"href":3795,"rel":10913},[544],[46,10915,3550],{},[46,10917,3804],{},[18,10919,3808],{},[11,10921,3811,10922,3815,10924,3818],{},[18,10923,3814],{},[46,10925,3550],{},[11,10927,3821],{},[11,10929,3824],{},[11,10931,3827,10932,3831],{},[18,10933,3830],{},[65,10935,3835],{"id":3834},[11,10937,3838,10938],{},[18,10939,3841],{},[11,10941,3844],{},[11,10943,3847,10944,3853],{},[540,10945,3852],{"href":3850,"rel":10946},[544],[1475,10948,10949,10951,10955],{},[1478,10950,3858],{},[1478,10952,3861,10953,3865],{},[46,10954,3864],{},[1478,10956,3868,10957,3871],{},[46,10958,3864],{},[11,10960,3874],{},[3876,10962,10963,10975],{},[3879,10964,10965],{},[3882,10966,10967,10969,10971,10973],{},[3885,10968,3887],{},[3885,10970,3786],{},[3885,10972,3797],{},[3885,10974,3894],{},[3896,10976,10977,10987,10997],{},[3882,10978,10979,10981,10983,10985],{},[3901,10980,3903],{},[3901,10982,3906],{},[3901,10984,3909],{},[3901,10986,3912],{},[3882,10988,10989,10991,10993,10995],{},[3901,10990,3917],{},[3901,10992,3920],{},[3901,10994,3923],{},[3901,10996,3926],{},[3882,10998,10999,11001,11003,11005],{},[3901,11000,3931],{},[3901,11002,3934],{},[3901,11004,3937],{},[3901,11006,3940],{},[11,11008,3943],{},[111,11010,11011],{"className":3946,"code":3947,"language":3948,"meta":116,"style":116},[46,11012,11013,11021,11027],{"__ignoreMap":116},[120,11014,11015,11017,11019],{"class":122,"line":123},[120,11016,3955],{"class":130},[120,11018,3958],{"class":580},[120,11020,3961],{"class":580},[120,11022,11023,11025],{"class":122,"line":138},[120,11024,3967],{"class":3966},[120,11026,3970],{"class":580},[120,11028,11029,11031],{"class":122,"line":147},[120,11030,3975],{"class":130},[120,11032,3978],{"class":580},[11,11034,3981],{},[65,11036,3985],{"id":3984},[11,11038,3988,11039,3992],{},[18,11040,3991],{},[11,11042,3995],{},[11,11044,3998],{},[111,11046,11047],{"className":727,"code":4001,"language":729,"meta":116,"style":116},[46,11048,11049,11059,11069,11073,11085,11101,11117,11133,11149,11153,11161,11165,11173,11181,11215,11227,11239,11247,11255],{"__ignoreMap":116},[120,11050,11051,11053,11055,11057],{"class":122,"line":123},[120,11052,736],{"class":134},[120,11054,739],{"class":294},[120,11056,742],{"class":130},[120,11058,753],{"class":134},[120,11060,11061,11063,11065,11067],{"class":122,"line":138},[120,11062,571],{"class":294},[120,11064,574],{"class":134},[120,11066,577],{"class":294},[120,11068,581],{"class":580},[120,11070,11071],{"class":122,"line":147},[120,11072,159],{"emptyLinePlaceholder":158},[120,11074,11075,11077,11079,11081,11083],{"class":122,"line":155},[120,11076,602],{"class":126},[120,11078,4034],{"class":134},[120,11080,295],{"class":294},[120,11082,610],{"class":130},[120,11084,4041],{"class":134},[120,11086,11087,11089,11091,11093,11095,11097,11099],{"class":122,"line":162},[120,11088,4046],{"class":134},[120,11090,4049],{"class":298},[120,11092,4052],{"class":134},[120,11094,4055],{"class":580},[120,11096,4058],{"class":134},[120,11098,4061],{"class":298},[120,11100,4064],{"class":134},[120,11102,11103,11105,11107,11109,11111,11113,11115],{"class":122,"line":171},[120,11104,4046],{"class":134},[120,11106,1549],{"class":298},[120,11108,4052],{"class":134},[120,11110,4075],{"class":580},[120,11112,4058],{"class":134},[120,11114,4080],{"class":298},[120,11116,4064],{"class":134},[120,11118,11119,11121,11123,11125,11127,11129,11131],{"class":122,"line":177},[120,11120,4046],{"class":134},[120,11122,4089],{"class":298},[120,11124,4052],{"class":134},[120,11126,4094],{"class":580},[120,11128,4058],{"class":134},[120,11130,4099],{"class":298},[120,11132,4064],{"class":134},[120,11134,11135,11137,11139,11141,11143,11145,11147],{"class":122,"line":182},[120,11136,4046],{"class":134},[120,11138,4108],{"class":298},[120,11140,4052],{"class":134},[120,11142,4113],{"class":580},[120,11144,4058],{"class":134},[120,11146,4118],{"class":298},[120,11148,4064],{"class":134},[120,11150,11151],{"class":122,"line":391},[120,11152,4125],{"class":134},[120,11154,11155,11157,11159],{"class":122,"line":398},[120,11156,793],{"class":134},[120,11158,739],{"class":294},[120,11160,753],{"class":134},[120,11162,11163],{"class":122,"line":403},[120,11164,159],{"emptyLinePlaceholder":158},[120,11166,11167,11169,11171],{"class":122,"line":410},[120,11168,736],{"class":134},[120,11170,808],{"class":294},[120,11172,753],{"class":134},[120,11174,11175,11177,11179],{"class":122,"line":415},[120,11176,815],{"class":134},[120,11178,1475],{"class":294},[120,11180,753],{"class":134},[120,11182,11183,11185,11187,11189,11191,11193,11195,11197,11199,11201,11203,11205,11207,11209,11211,11213],{"class":122,"line":420},[120,11184,895],{"class":134},[120,11186,1478],{"class":294},[120,11188,1048],{"class":294},[120,11190,295],{"class":134},[120,11192,831],{"class":134},[120,11194,4168],{"class":134},[120,11196,984],{"class":294},[120,11198,4173],{"class":134},[120,11200,831],{"class":134},[120,11202,1065],{"class":134},[120,11204,997],{"class":130},[120,11206,295],{"class":134},[120,11208,831],{"class":134},[120,11210,4186],{"class":134},[120,11212,831],{"class":134},[120,11214,753],{"class":134},[120,11216,11217,11219,11221,11223,11225],{"class":122,"line":904},[120,11218,4195],{"class":134},[120,11220,18],{"class":294},[120,11222,4200],{"class":134},[120,11224,18],{"class":294},[120,11226,753],{"class":134},[120,11228,11229,11231,11233,11235,11237],{"class":122,"line":915},[120,11230,4195],{"class":134},[120,11232,120],{"class":294},[120,11234,4213],{"class":134},[120,11236,120],{"class":294},[120,11238,753],{"class":134},[120,11240,11241,11243,11245],{"class":122,"line":1214},[120,11242,4222],{"class":134},[120,11244,1478],{"class":294},[120,11246,753],{"class":134},[120,11248,11249,11251,11253],{"class":122,"line":1882},[120,11250,907],{"class":134},[120,11252,1475],{"class":294},[120,11254,753],{"class":134},[120,11256,11257,11259,11261],{"class":122,"line":1895},[120,11258,793],{"class":134},[120,11260,808],{"class":294},[120,11262,753],{"class":134},[11,11264,4245],{},[11,11266,4248,11267,4252],{},[46,11268,4251],{},[11,11270,4255],{},[11,11272,4258,11273,4262],{},[18,11274,4261],{},[11,11276,4265],{},[1475,11278,11279,11283,11287],{},[1478,11280,11281,4273],{},[18,11282,4272],{},[1478,11284,11285,4279],{},[18,11286,4278],{},[1478,11288,11289,4285],{},[18,11290,4284],{},[11,11292,4288],{},[11,11294,4291,11295,4295],{},[18,11296,4294],{},[11,11298,4298],{},[11,11300,4301],{},[11,11302,4304,11303,4308,11305,4311],{},[46,11304,4307],{},[46,11306,4307],{},[11,11308,4314,11309,4318],{},[18,11310,4317],{},[11,11312,4321],{},[65,11314,4325],{"id":4324},[11,11316,4328],{},[111,11318,11319],{"className":3946,"code":4331,"language":3948,"meta":116,"style":116},[46,11320,11321],{"__ignoreMap":116},[120,11322,11323,11325,11327],{"class":122,"line":123},[120,11324,4338],{"class":130},[120,11326,4341],{"class":580},[120,11328,4344],{"class":580},[111,11330,11332],{"className":11331,"code":4349,"language":4350},[4348],[46,11333,4349],{"__ignoreMap":116},[11,11335,4355,11336,4359],{},[46,11337,4358],{},[111,11339,11340],{"className":4362,"code":4363,"language":4364,"meta":116,"style":116},[46,11341,11342,11346,11352,11360,11364],{"__ignoreMap":116},[120,11343,11344],{"class":122,"line":123},[120,11345,4371],{"class":134},[120,11347,11348,11350],{"class":122,"line":138},[120,11349,4376],{"class":126},[120,11351,4379],{"class":134},[120,11353,11354,11356,11358],{"class":122,"line":147},[120,11355,4384],{"class":126},[120,11357,4387],{"class":134},[120,11359,4391],{"class":4390},[120,11361,11362],{"class":122,"line":155},[120,11363,2165],{"class":134},[120,11365,11366],{"class":122,"line":162},[120,11367,174],{"class":134},[11,11369,4402,11370,4405],{},[46,11371,3550],{},[11,11373,4408,11374,4412,11376,4416],{},[18,11375,4411],{},[46,11377,4415],{},[111,11379,11380],{"className":4362,"code":4419,"language":4364,"meta":116,"style":116},[46,11381,11382,11386,11392,11402,11412,11422,11432,11442,11452,11462,11472,11482,11492,11502,11510,11514],{"__ignoreMap":116},[120,11383,11384],{"class":122,"line":123},[120,11385,4371],{"class":134},[120,11387,11388,11390],{"class":122,"line":138},[120,11389,4430],{"class":126},[120,11391,4379],{"class":134},[120,11393,11394,11396,11398,11400],{"class":122,"line":147},[120,11395,4437],{"class":126},[120,11397,4387],{"class":134},[120,11399,4442],{"class":4390},[120,11401,1385],{"class":134},[120,11403,11404,11406,11408,11410],{"class":122,"line":155},[120,11405,4449],{"class":126},[120,11407,4387],{"class":134},[120,11409,4442],{"class":4390},[120,11411,1385],{"class":134},[120,11413,11414,11416,11418,11420],{"class":122,"line":162},[120,11415,4460],{"class":126},[120,11417,4387],{"class":134},[120,11419,4442],{"class":4390},[120,11421,1385],{"class":134},[120,11423,11424,11426,11428,11430],{"class":122,"line":171},[120,11425,4471],{"class":126},[120,11427,4387],{"class":134},[120,11429,4442],{"class":4390},[120,11431,1385],{"class":134},[120,11433,11434,11436,11438,11440],{"class":122,"line":177},[120,11435,4482],{"class":126},[120,11437,4387],{"class":134},[120,11439,4442],{"class":4390},[120,11441,1385],{"class":134},[120,11443,11444,11446,11448,11450],{"class":122,"line":182},[120,11445,4493],{"class":126},[120,11447,4387],{"class":134},[120,11449,4442],{"class":4390},[120,11451,1385],{"class":134},[120,11453,11454,11456,11458,11460],{"class":122,"line":391},[120,11455,4504],{"class":126},[120,11457,4387],{"class":134},[120,11459,4442],{"class":4390},[120,11461,1385],{"class":134},[120,11463,11464,11466,11468,11470],{"class":122,"line":398},[120,11465,4515],{"class":126},[120,11467,4387],{"class":134},[120,11469,4442],{"class":4390},[120,11471,1385],{"class":134},[120,11473,11474,11476,11478,11480],{"class":122,"line":403},[120,11475,4526],{"class":126},[120,11477,4387],{"class":134},[120,11479,4442],{"class":4390},[120,11481,1385],{"class":134},[120,11483,11484,11486,11488,11490],{"class":122,"line":410},[120,11485,4537],{"class":126},[120,11487,4387],{"class":134},[120,11489,4442],{"class":4390},[120,11491,1385],{"class":134},[120,11493,11494,11496,11498,11500],{"class":122,"line":415},[120,11495,4548],{"class":126},[120,11497,4387],{"class":134},[120,11499,4442],{"class":4390},[120,11501,1385],{"class":134},[120,11503,11504,11506,11508],{"class":122,"line":420},[120,11505,4559],{"class":126},[120,11507,4387],{"class":134},[120,11509,4391],{"class":4390},[120,11511,11512],{"class":122,"line":904},[120,11513,2165],{"class":134},[120,11515,11516],{"class":122,"line":915},[120,11517,174],{"class":134},[11,11519,4574,11520,4578],{},[46,11521,4577],{},[11,11523,4581],{},[65,11525,4585],{"id":4584},[11,11527,4588,11528,4591,11530,3818],{},[46,11529,4307],{},[46,11531,3550],{},[111,11533,11534],{"className":727,"code":3554,"language":729,"meta":116,"style":116},[46,11535,11536,11548,11558,11562,11578,11586,11590,11598,11624],{"__ignoreMap":116},[120,11537,11538,11540,11542,11544,11546],{"class":122,"line":123},[120,11539,736],{"class":134},[120,11541,739],{"class":294},[120,11543,742],{"class":130},[120,11545,3567],{"class":130},[120,11547,753],{"class":134},[120,11549,11550,11552,11554,11556],{"class":122,"line":138},[120,11551,571],{"class":294},[120,11553,574],{"class":134},[120,11555,577],{"class":294},[120,11557,581],{"class":580},[120,11559,11560],{"class":122,"line":147},[120,11561,159],{"emptyLinePlaceholder":158},[120,11563,11564,11566,11568,11570,11572,11574,11576],{"class":122,"line":155},[120,11565,602],{"class":126},[120,11567,3590],{"class":134},[120,11569,295],{"class":294},[120,11571,610],{"class":130},[120,11573,312],{"class":134},[120,11575,615],{"class":298},[120,11577,367],{"class":134},[120,11579,11580,11582,11584],{"class":122,"line":162},[120,11581,793],{"class":134},[120,11583,739],{"class":294},[120,11585,753],{"class":134},[120,11587,11588],{"class":122,"line":171},[120,11589,159],{"emptyLinePlaceholder":158},[120,11591,11592,11594,11596],{"class":122,"line":177},[120,11593,736],{"class":134},[120,11595,808],{"class":294},[120,11597,753],{"class":134},[120,11599,11600,11602,11604,11606,11608,11610,11612,11614,11616,11618,11620,11622],{"class":122,"line":182},[120,11601,815],{"class":134},[120,11603,3627],{"class":294},[120,11605,1136],{"class":134},[120,11607,3632],{"class":130},[120,11609,295],{"class":134},[120,11611,831],{"class":134},[120,11613,3639],{"class":134},[120,11615,3642],{"class":294},[120,11617,831],{"class":134},[120,11619,3647],{"class":134},[120,11621,3627],{"class":294},[120,11623,753],{"class":134},[120,11625,11626,11628,11630],{"class":122,"line":391},[120,11627,793],{"class":134},[120,11629,808],{"class":294},[120,11631,753],{"class":134},[11,11633,4696,11634,4699,11636,535],{},[46,11635,4307],{},[46,11637,4307],{},[11,11639,4704],{},[11,11641,4707],{},[11,11643,4710],{},[65,11645,4714],{"id":4713},[11,11647,4717],{},[11,11649,4720,11650,4724],{},[540,11651,4723],{"href":3451},[11,11653,3403],{},[3405,11655,4729],{},{"title":116,"searchDepth":138,"depth":138,"links":11657},[11658,11659,11660,11663,11664,11665,11666,11667],{"id":3466,"depth":138,"text":3467},{"id":3529,"depth":138,"text":3530},{"id":3703,"depth":138,"text":3704,"children":11661},[11662],{"id":3758,"depth":147,"text":3759},{"id":3834,"depth":138,"text":3835},{"id":3984,"depth":138,"text":3985},{"id":4324,"depth":138,"text":4325},{"id":4584,"depth":138,"text":4585},{"id":4713,"depth":138,"text":4714},{},{"title":3438,"description":4743},{"id":4751,"title":4752,"body":11671,"category":3425,"date":6520,"description":6521,"draft":3428,"extension":3429,"image":6522,"meta":13087,"navigation":158,"path":6524,"seo":13088,"stem":6526,"tighten":3428,"__hash__":6527},{"type":8,"value":11672,"toc":13075},[11673,11679,11685,11687,11689,11691,11705,11707,11711,11713,11715,11727,11729,11731,11741,11743,11745,11747,11753,11755,11757,11763,11823,11825,11835,11837,11841,11843,11847,11851,11863,11865,11869,11871,11879,11881,11883,11887,11889,11891,11893,11895,11899,11903,11905,11907,11909,12393,12395,12399,12401,12408,12410,12418,12420,12422,12426,12448,12450,12528,12532,12590,12596,12766,12768,12770,12772,13040,13042,13044,13046,13064,13066,13071,13073],[3442,11674,11675],{"color":4757,"icon":3445},[11,11676,4760,11677,4764],{},[540,11678,3452],{"href":4763},[11,11680,4767,11681,4772,11683,4777],{},[540,11682,4771],{"href":4770},[540,11684,4776],{"href":4775},[11,11686,4780],{},[65,11688,4784],{"id":4783},[11,11690,4787],{},[1475,11692,11693,11697,11701],{},[1478,11694,11695,4795],{},[18,11696,4794],{},[1478,11698,11699,4801],{},[18,11700,4800],{},[1478,11702,11703,4807],{},[18,11704,4806],{},[65,11706,4811],{"id":4810},[11,11708,4814,11709,4817],{},[18,11710,4811],{},[11,11712,4820],{},[11,11714,4823],{},[1475,11716,11717,11719,11721,11723,11725],{},[1478,11718,4828],{},[1478,11720,4831],{},[1478,11722,4834],{},[1478,11724,4837],{},[1478,11726,4840],{},[11,11728,4843],{},[11,11730,4846],{},[1475,11732,11733,11737],{},[1478,11734,4851,11735,4855],{},[18,11736,4854],{},[1478,11738,4858,11739,535],{},[18,11740,4861],{},[1281,11742,4865],{"id":4864},[11,11744,4868],{},[11,11746,4871],{},[1475,11748,11749,11751],{},[1478,11750,4876],{},[1478,11752,4879],{},[11,11754,4882],{},[4884,11756,4887],{"id":4886},[11,11758,4890,11759,4893,11761,4897],{},[18,11760,4887],{},[18,11762,4896],{},[111,11764,11765],{"className":4900,"code":4901,"language":4902,"meta":116,"style":116},[46,11766,11767,11771,11775,11779,11783,11787,11791,11795,11799,11803,11807,11811,11815,11819],{"__ignoreMap":116},[120,11768,11769],{"class":122,"line":123},[120,11770,4909],{},[120,11772,11773],{"class":122,"line":138},[120,11774,4914],{},[120,11776,11777],{"class":122,"line":147},[120,11778,4919],{},[120,11780,11781],{"class":122,"line":155},[120,11782,4924],{},[120,11784,11785],{"class":122,"line":162},[120,11786,4929],{},[120,11788,11789],{"class":122,"line":171},[120,11790,159],{"emptyLinePlaceholder":158},[120,11792,11793],{"class":122,"line":177},[120,11794,4938],{},[120,11796,11797],{"class":122,"line":182},[120,11798,4943],{},[120,11800,11801],{"class":122,"line":391},[120,11802,4948],{},[120,11804,11805],{"class":122,"line":398},[120,11806,4953],{},[120,11808,11809],{"class":122,"line":403},[120,11810,4958],{},[120,11812,11813],{"class":122,"line":410},[120,11814,159],{"emptyLinePlaceholder":158},[120,11816,11817],{"class":122,"line":415},[120,11818,4967],{},[120,11820,11821],{"class":122,"line":420},[120,11822,4972],{},[11,11824,4975],{},[1475,11826,11827,11829,11831],{},[1478,11828,4980],{},[1478,11830,4983],{},[1478,11832,4986,11833],{},[18,11834,4989],{},[4884,11836,4993],{"id":4992},[11,11838,4996,11839,5000],{},[18,11840,4999],{},[4884,11842,5004],{"id":5003},[11,11844,5007,11845,5011],{},[46,11846,5010],{},[11,11848,5014,11849,5017],{},[18,11850,4831],{},[111,11852,11853],{"className":4900,"code":5020,"language":4902,"meta":116,"style":116},[46,11854,11855,11859],{"__ignoreMap":116},[120,11856,11857],{"class":122,"line":123},[120,11858,5027],{},[120,11860,11861],{"class":122,"line":138},[120,11862,5032],{},[4884,11864,5036],{"id":5035},[11,11866,5039,11867,5042],{},[18,11868,5036],{},[11,11870,5045],{},[111,11872,11873],{"className":4900,"code":5048,"language":4902,"meta":116,"style":116},[46,11874,11875],{"__ignoreMap":116},[120,11876,11877],{"class":122,"line":123},[120,11878,5048],{},[11,11880,5057],{},[4884,11882,5061],{"id":5060},[11,11884,5064,11885,5067],{},[18,11886,5061],{},[11,11888,5070],{},[1281,11890,5074],{"id":5073},[11,11892,5077],{},[11,11894,5080],{},[3472,11896,11897],{},[11,11898,5085],{},[11,11900,5088,11901,5092],{},[18,11902,5091],{},[11,11904,5095],{},[65,11906,5099],{"id":5098},[11,11908,5102],{},[111,11910,11911],{"className":113,"code":5105,"language":115,"meta":116,"style":116},[46,11912,11913,11923,11933,11937,11947,11951,11959,11975,11979,11983,11987,12001,12027,12053,12057,12071,12081,12091,12101,12115,12119,12131,12139,12153,12165,12185,12189,12211,12237,12263,12267,12271,12277,12291,12299,12319,12323,12327,12345,12351,12357,12365,12373,12377,12381,12385,12389],{"__ignoreMap":116},[120,11914,11915,11917,11919,11921],{"class":122,"line":123},[120,11916,602],{"class":126},[120,11918,5114],{"class":134},[120,11920,295],{"class":294},[120,11922,5119],{"class":580},[120,11924,11925,11927,11929,11931],{"class":122,"line":138},[120,11926,602],{"class":126},[120,11928,5126],{"class":134},[120,11930,295],{"class":294},[120,11932,5131],{"class":298},[120,11934,11935],{"class":122,"line":147},[120,11936,159],{"emptyLinePlaceholder":158},[120,11938,11939,11941,11943,11945],{"class":122,"line":155},[120,11940,602],{"class":126},[120,11942,5142],{"class":134},[120,11944,295],{"class":294},[120,11946,651],{"class":134},[120,11948,11949],{"class":122,"line":162},[120,11950,5151],{"class":134},[120,11952,11953,11955,11957],{"class":122,"line":171},[120,11954,5156],{"class":134},[120,11956,5159],{"class":580},[120,11958,4064],{"class":134},[120,11960,11961,11963,11965,11967,11969,11971,11973],{"class":122,"line":177},[120,11962,5156],{"class":134},[120,11964,5168],{"class":580},[120,11966,5171],{"class":134},[120,11968,5174],{"class":580},[120,11970,5177],{"class":134},[120,11972,5180],{"class":580},[120,11974,5183],{"class":134},[120,11976,11977],{"class":122,"line":182},[120,11978,5188],{"class":134},[120,11980,11981],{"class":122,"line":391},[120,11982,174],{"class":134},[120,11984,11985],{"class":122,"line":398},[120,11986,159],{"emptyLinePlaceholder":158},[120,11988,11989,11991,11993,11995,11997,11999],{"class":122,"line":403},[120,11990,602],{"class":126},[120,11992,5203],{"class":134},[120,11994,295],{"class":294},[120,11996,5208],{"class":134},[120,11998,5211],{"class":130},[120,12000,144],{"class":134},[120,12002,12003,12005,12007,12009,12011,12013,12015,12017,12019,12021,12023,12025],{"class":122,"line":410},[120,12004,602],{"class":126},[120,12006,5220],{"class":134},[120,12008,295],{"class":294},[120,12010,1717],{"class":294},[120,12012,5227],{"class":130},[120,12014,5230],{"class":134},[120,12016,5233],{"class":130},[120,12018,312],{"class":134},[120,12020,5238],{"class":580},[120,12022,1944],{"class":134},[120,12024,1947],{"class":294},[120,12026,5245],{"class":580},[120,12028,12029,12031,12033,12035,12037,12039,12041,12043,12045,12047,12049,12051],{"class":122,"line":415},[120,12030,602],{"class":126},[120,12032,5252],{"class":134},[120,12034,295],{"class":294},[120,12036,1717],{"class":294},[120,12038,5227],{"class":130},[120,12040,5230],{"class":134},[120,12042,5233],{"class":130},[120,12044,312],{"class":134},[120,12046,5267],{"class":580},[120,12048,1944],{"class":134},[120,12050,5272],{"class":294},[120,12052,5275],{"class":580},[120,12054,12055],{"class":122,"line":420},[120,12056,159],{"emptyLinePlaceholder":158},[120,12058,12059,12061,12063,12065,12067,12069],{"class":122,"line":904},[120,12060,602],{"class":126},[120,12062,5286],{"class":134},[120,12064,295],{"class":294},[120,12066,1717],{"class":294},[120,12068,5293],{"class":130},[120,12070,5296],{"class":134},[120,12072,12073,12075,12077,12079],{"class":122,"line":915},[120,12074,288],{"class":126},[120,12076,5303],{"class":134},[120,12078,295],{"class":294},[120,12080,5308],{"class":298},[120,12082,12083,12085,12087,12089],{"class":122,"line":1214},[120,12084,288],{"class":126},[120,12086,5315],{"class":134},[120,12088,295],{"class":294},[120,12090,2226],{"class":298},[120,12092,12093,12095,12097,12099],{"class":122,"line":1882},[120,12094,602],{"class":126},[120,12096,5326],{"class":134},[120,12098,295],{"class":294},[120,12100,5331],{"class":134},[120,12102,12103,12105,12107,12109,12111,12113],{"class":122,"line":1895},[120,12104,602],{"class":126},[120,12106,5338],{"class":134},[120,12108,295],{"class":294},[120,12110,1717],{"class":294},[120,12112,1720],{"class":130},[120,12114,144],{"class":134},[120,12116,12117],{"class":122,"line":2716},[120,12118,159],{"emptyLinePlaceholder":158},[120,12120,12121,12123,12125,12127,12129],{"class":122,"line":2732},[120,12122,127],{"class":126},[120,12124,5357],{"class":130},[120,12126,312],{"class":134},[120,12128,5362],{"class":315},[120,12130,319],{"class":134},[120,12132,12133,12135,12137],{"class":122,"line":2742},[120,12134,5369],{"class":134},[120,12136,295],{"class":294},[120,12138,5374],{"class":134},[120,12140,12141,12143,12145,12147,12149,12151],{"class":122,"line":2752},[120,12142,5379],{"class":134},[120,12144,5382],{"class":130},[120,12146,5385],{"class":294},[120,12148,5388],{"class":134},[120,12150,648],{"class":126},[120,12152,651],{"class":134},[120,12154,12155,12157,12159,12161,12163],{"class":122,"line":2771},[120,12156,5397],{"class":134},[120,12158,5400],{"class":130},[120,12160,312],{"class":134},[120,12162,5405],{"class":580},[120,12164,367],{"class":134},[120,12166,12167,12169,12171,12173,12175,12177,12179,12181,12183],{"class":122,"line":2777},[120,12168,5412],{"class":134},[120,12170,5415],{"class":130},[120,12172,312],{"class":134},[120,12174,5420],{"class":580},[120,12176,1007],{"class":294},[120,12178,5425],{"class":134},[120,12180,1013],{"class":294},[120,12182,1016],{"class":580},[120,12184,367],{"class":134},[120,12186,12187],{"class":122,"line":2791},[120,12188,2165],{"class":134},[120,12190,12191,12193,12195,12197,12199,12201,12203,12205,12207,12209],{"class":122,"line":2800},[120,12192,5379],{"class":134},[120,12194,5442],{"class":130},[120,12196,5385],{"class":294},[120,12198,5388],{"class":134},[120,12200,648],{"class":126},[120,12202,5451],{"class":134},[120,12204,5400],{"class":130},[120,12206,312],{"class":134},[120,12208,5458],{"class":580},[120,12210,367],{"class":134},[120,12212,12213,12215,12217,12219,12221,12223,12225,12227,12229,12231,12233,12235],{"class":122,"line":2806},[120,12214,5379],{"class":134},[120,12216,5467],{"class":130},[120,12218,5385],{"class":294},[120,12220,355],{"class":134},[120,12222,1744],{"class":315},[120,12224,1944],{"class":134},[120,12226,648],{"class":126},[120,12228,5451],{"class":134},[120,12230,5482],{"class":130},[120,12232,312],{"class":134},[120,12234,5487],{"class":580},[120,12236,5490],{"class":134},[120,12238,12239,12241,12243,12245,12247,12249,12251,12253,12255,12257,12259,12261],{"class":122,"line":2811},[120,12240,5379],{"class":134},[120,12242,5497],{"class":130},[120,12244,5385],{"class":294},[120,12246,355],{"class":134},[120,12248,5504],{"class":315},[120,12250,1944],{"class":134},[120,12252,648],{"class":126},[120,12254,5451],{"class":134},[120,12256,5400],{"class":130},[120,12258,312],{"class":134},[120,12260,5517],{"class":580},[120,12262,5520],{"class":134},[120,12264,12265],{"class":122,"line":5523},[120,12266,174],{"class":134},[120,12268,12269],{"class":122,"line":5528},[120,12270,159],{"emptyLinePlaceholder":158},[120,12272,12273,12275],{"class":122,"line":5533},[120,12274,2198],{"class":294},[120,12276,5538],{"class":134},[120,12278,12279,12281,12283,12285,12287,12289],{"class":122,"line":5541},[120,12280,5544],{"class":130},[120,12282,5547],{"class":134},[120,12284,5550],{"class":130},[120,12286,312],{"class":134},[120,12288,5555],{"class":580},[120,12290,5558],{"class":134},[120,12292,12293,12295,12297],{"class":122,"line":5561},[120,12294,5564],{"class":134},[120,12296,2758],{"class":294},[120,12298,651],{"class":134},[120,12300,12301,12303,12305,12307,12309,12311,12313,12315,12317],{"class":122,"line":5571},[120,12302,5574],{"class":134},[120,12304,5577],{"class":130},[120,12306,5385],{"class":294},[120,12308,355],{"class":134},[120,12310,5504],{"class":315},[120,12312,1944],{"class":134},[120,12314,648],{"class":126},[120,12316,5357],{"class":130},[120,12318,5592],{"class":134},[120,12320,12321],{"class":122,"line":5595},[120,12322,174],{"class":134},[120,12324,12325],{"class":122,"line":5600},[120,12326,159],{"emptyLinePlaceholder":158},[120,12328,12329,12331,12333,12335,12337,12339,12341,12343],{"class":122,"line":5605},[120,12330,5608],{"class":134},[120,12332,5611],{"class":130},[120,12334,5385],{"class":294},[120,12336,355],{"class":134},[120,12338,5504],{"class":315},[120,12340,1944],{"class":134},[120,12342,648],{"class":126},[120,12344,651],{"class":134},[120,12346,12347,12349],{"class":122,"line":5626},[120,12348,2119],{"class":294},[120,12350,5631],{"class":134},[120,12352,12353,12355],{"class":122,"line":5634},[120,12354,5637],{"class":130},[120,12356,5640],{"class":134},[120,12358,12359,12361,12363],{"class":122,"line":5643},[120,12360,5646],{"class":134},[120,12362,5649],{"class":580},[120,12364,1385],{"class":134},[120,12366,12367,12369,12371],{"class":122,"line":5654},[120,12368,5657],{"class":134},[120,12370,5660],{"class":130},[120,12372,144],{"class":134},[120,12374,12375],{"class":122,"line":5665},[120,12376,5668],{"class":134},[120,12378,12379],{"class":122,"line":5671},[120,12380,2165],{"class":134},[120,12382,12383],{"class":122,"line":5676},[120,12384,174],{"class":134},[120,12386,12387],{"class":122,"line":5681},[120,12388,159],{"emptyLinePlaceholder":158},[120,12390,12391],{"class":122,"line":5686},[120,12392,5689],{"class":1358},[11,12394,5692],{},[11,12396,5695,12397,5699],{},[23,12398,5698],{},[65,12400,5703],{"id":5702},[11,12402,5706,12403,5714],{},[540,12404,12406],{"href":5709,"rel":12405},[544],[18,12407,5713],{},[11,12409,5717],{},[111,12411,12412],{"className":4900,"code":5720,"language":4902,"meta":116,"style":116},[46,12413,12414],{"__ignoreMap":116},[120,12415,12416],{"class":122,"line":123},[120,12417,5720],{},[11,12419,5729],{},[11,12421,5732],{},[11,12423,5735,12424,5739],{},[46,12425,5738],{},[111,12427,12428],{"className":113,"code":5742,"language":115,"meta":116,"style":116},[46,12429,12430],{"__ignoreMap":116},[120,12431,12432,12434,12436,12438,12440,12442,12444,12446],{"class":122,"line":123},[120,12433,602],{"class":126},[120,12435,5751],{"class":134},[120,12437,295],{"class":294},[120,12439,1717],{"class":294},[120,12441,5758],{"class":130},[120,12443,312],{"class":134},[120,12445,5763],{"class":580},[120,12447,367],{"class":134},[11,12449,5768],{},[111,12451,12452],{"className":3946,"code":5771,"language":3948,"meta":116,"style":116},[46,12453,12454,12458,12476,12488,12492,12496,12508,12512,12516],{"__ignoreMap":116},[120,12455,12456],{"class":122,"line":123},[120,12457,5778],{"class":1358},[120,12459,12460,12462,12464,12466,12468,12470,12472,12474],{"class":122,"line":138},[120,12461,5783],{"class":130},[120,12463,5786],{"class":298},[120,12465,5789],{"class":580},[120,12467,5792],{"class":294},[120,12469,5795],{"class":130},[120,12471,5798],{"class":298},[120,12473,5801],{"class":580},[120,12475,5804],{"class":580},[120,12477,12478,12480,12482,12484,12486],{"class":122,"line":147},[120,12479,5809],{"class":130},[120,12481,5812],{"class":580},[120,12483,5815],{"class":580},[120,12485,5818],{"class":298},[120,12487,5821],{"class":580},[120,12489,12490],{"class":122,"line":155},[120,12491,159],{"emptyLinePlaceholder":158},[120,12493,12494],{"class":122,"line":162},[120,12495,5830],{"class":1358},[120,12497,12498,12500,12502,12504,12506],{"class":122,"line":171},[120,12499,5809],{"class":130},[120,12501,5837],{"class":580},[120,12503,5815],{"class":580},[120,12505,5842],{"class":298},[120,12507,5845],{"class":580},[120,12509,12510],{"class":122,"line":177},[120,12511,159],{"emptyLinePlaceholder":158},[120,12513,12514],{"class":122,"line":182},[120,12515,5854],{"class":1358},[120,12517,12518,12520,12522,12524,12526],{"class":122,"line":391},[120,12519,5859],{"class":130},[120,12521,5862],{"class":298},[120,12523,5865],{"class":298},[120,12525,5868],{"class":298},[120,12527,5871],{"class":580},[11,12529,5874,12530,5877],{},[46,12531,5738],{},[111,12533,12534],{"className":113,"code":5880,"language":115,"meta":116,"style":116},[46,12535,12536,12554,12562,12570,12578,12586],{"__ignoreMap":116},[120,12537,12538,12540,12542,12544,12546,12548,12550,12552],{"class":122,"line":123},[120,12539,602],{"class":126},[120,12541,5751],{"class":134},[120,12543,295],{"class":294},[120,12545,1717],{"class":294},[120,12547,5758],{"class":130},[120,12549,312],{"class":134},[120,12551,5763],{"class":580},[120,12553,5901],{"class":134},[120,12555,12556,12558,12560],{"class":122,"line":138},[120,12557,5906],{"class":134},[120,12559,5909],{"class":580},[120,12561,1385],{"class":134},[120,12563,12564,12566,12568],{"class":122,"line":147},[120,12565,5916],{"class":134},[120,12567,5919],{"class":298},[120,12569,1385],{"class":134},[120,12571,12572,12574,12576],{"class":122,"line":155},[120,12573,5926],{"class":134},[120,12575,5929],{"class":580},[120,12577,1385],{"class":134},[120,12579,12580,12582,12584],{"class":122,"line":162},[120,12581,5936],{"class":134},[120,12583,883],{"class":298},[120,12585,1385],{"class":134},[120,12587,12588],{"class":122,"line":171},[120,12589,672],{"class":134},[11,12591,5947,12592,5950,12594,5953],{},[46,12593,5738],{},[46,12595,5763],{},[111,12597,12598],{"className":113,"code":5956,"language":115,"meta":116,"style":116},[46,12599,12600,12610,12614,12618,12636,12640,12656,12660,12664,12682,12686,12702,12706,12710,12738,12742,12746,12758,12762],{"__ignoreMap":116},[120,12601,12602,12604,12606,12608],{"class":122,"line":123},[120,12603,571],{"class":294},[120,12605,5965],{"class":134},[120,12607,577],{"class":294},[120,12609,5970],{"class":580},[120,12611,12612],{"class":122,"line":138},[120,12613,159],{"emptyLinePlaceholder":158},[120,12615,12616],{"class":122,"line":147},[120,12617,5979],{"class":1358},[120,12619,12620,12622,12624,12626,12628,12630,12632,12634],{"class":122,"line":155},[120,12621,602],{"class":126},[120,12623,5751],{"class":134},[120,12625,295],{"class":294},[120,12627,1717],{"class":294},[120,12629,5758],{"class":130},[120,12631,312],{"class":134},[120,12633,5763],{"class":580},[120,12635,367],{"class":134},[120,12637,12638],{"class":122,"line":162},[120,12639,159],{"emptyLinePlaceholder":158},[120,12641,12642,12644,12646,12648,12650,12652,12654],{"class":122,"line":171},[120,12643,6006],{"class":134},[120,12645,6009],{"class":130},[120,12647,312],{"class":134},[120,12649,6014],{"class":580},[120,12651,6017],{"class":134},[120,12653,648],{"class":126},[120,12655,651],{"class":134},[120,12657,12658],{"class":122,"line":177},[120,12659,159],{"emptyLinePlaceholder":158},[120,12661,12662],{"class":122,"line":182},[120,12663,6030],{"class":1358},[120,12665,12666,12668,12670,12672,12674,12676,12678,12680],{"class":122,"line":391},[120,12667,324],{"class":126},[120,12669,6037],{"class":134},[120,12671,295],{"class":294},[120,12673,6042],{"class":134},[120,12675,6045],{"class":130},[120,12677,312],{"class":134},[120,12679,6050],{"class":580},[120,12681,367],{"class":134},[120,12683,12684],{"class":122,"line":398},[120,12685,159],{"emptyLinePlaceholder":158},[120,12687,12688,12690,12692,12694,12696,12698,12700],{"class":122,"line":403},[120,12689,6061],{"class":134},[120,12691,6009],{"class":130},[120,12693,312],{"class":134},[120,12695,6014],{"class":580},[120,12697,6017],{"class":134},[120,12699,648],{"class":126},[120,12701,651],{"class":134},[120,12703,12704],{"class":122,"line":410},[120,12705,159],{"emptyLinePlaceholder":158},[120,12707,12708],{"class":122,"line":415},[120,12709,6082],{"class":1358},[120,12711,12712,12714,12716,12718,12720,12722,12724,12726,12728,12730,12732,12734,12736],{"class":122,"line":420},[120,12713,6087],{"class":134},[120,12715,6009],{"class":130},[120,12717,312],{"class":134},[120,12719,6094],{"class":580},[120,12721,6097],{"class":134},[120,12723,6100],{"class":315},[120,12725,1944],{"class":134},[120,12727,648],{"class":126},[120,12729,5451],{"class":134},[120,12731,5400],{"class":130},[120,12733,312],{"class":134},[120,12735,6113],{"class":580},[120,12737,6116],{"class":134},[120,12739,12740],{"class":122,"line":904},[120,12741,159],{"emptyLinePlaceholder":158},[120,12743,12744],{"class":122,"line":915},[120,12745,6125],{"class":1358},[120,12747,12748,12750,12752,12754,12756],{"class":122,"line":1214},[120,12749,6087],{"class":134},[120,12751,5415],{"class":130},[120,12753,312],{"class":134},[120,12755,6136],{"class":580},[120,12757,367],{"class":134},[120,12759,12760],{"class":122,"line":1882},[120,12761,6143],{"class":134},[120,12763,12764],{"class":122,"line":1895},[120,12765,672],{"class":134},[11,12767,6150],{},[1281,12769,6154],{"id":6153},[11,12771,6157],{},[111,12773,12774],{"className":727,"code":6160,"language":729,"meta":116,"style":116},[46,12775,12776,12786,12796,12800,12816,12832,12836,12850,12866,12874,12880,12884,12888,12896,12904,12908,12934,12942,12964,12968,12972,12980,12984,12992,13012,13032],{"__ignoreMap":116},[120,12777,12778,12780,12782,12784],{"class":122,"line":123},[120,12779,736],{"class":134},[120,12781,739],{"class":294},[120,12783,742],{"class":130},[120,12785,753],{"class":134},[120,12787,12788,12790,12792,12794],{"class":122,"line":138},[120,12789,571],{"class":294},[120,12791,5965],{"class":134},[120,12793,577],{"class":294},[120,12795,5970],{"class":580},[120,12797,12798],{"class":122,"line":147},[120,12799,159],{"emptyLinePlaceholder":158},[120,12801,12802,12804,12806,12808,12810,12812,12814],{"class":122,"line":155},[120,12803,602],{"class":126},[120,12805,6193],{"class":134},[120,12807,295],{"class":294},[120,12809,6198],{"class":130},[120,12811,312],{"class":134},[120,12813,6203],{"class":580},[120,12815,367],{"class":134},[120,12817,12818,12820,12822,12824,12826,12828,12830],{"class":122,"line":162},[120,12819,602],{"class":126},[120,12821,6212],{"class":134},[120,12823,295],{"class":294},[120,12825,6198],{"class":130},[120,12827,312],{"class":134},[120,12829,6221],{"class":580},[120,12831,367],{"class":134},[120,12833,12834],{"class":122,"line":171},[120,12835,159],{"emptyLinePlaceholder":158},[120,12837,12838,12840,12842,12844,12846,12848],{"class":122,"line":177},[120,12839,1810],{"class":130},[120,12841,312],{"class":134},[120,12843,6236],{"class":294},[120,12845,5388],{"class":134},[120,12847,648],{"class":126},[120,12849,651],{"class":134},[120,12851,12852,12854,12856,12858,12860,12862,12864],{"class":122,"line":182},[120,12853,324],{"class":126},[120,12855,6249],{"class":134},[120,12857,295],{"class":294},[120,12859,6254],{"class":294},[120,12861,6257],{"class":134},[120,12863,6260],{"class":130},[120,12865,5640],{"class":134},[120,12867,12868,12870,12872],{"class":122,"line":391},[120,12869,6267],{"class":134},[120,12871,883],{"class":298},[120,12873,1385],{"class":134},[120,12875,12876,12878],{"class":122,"line":398},[120,12877,6276],{"class":134},[120,12879,6279],{"class":298},[120,12881,12882],{"class":122,"line":403},[120,12883,6143],{"class":134},[120,12885,12886],{"class":122,"line":410},[120,12887,159],{"emptyLinePlaceholder":158},[120,12889,12890,12892,12894],{"class":122,"line":415},[120,12891,6292],{"class":134},[120,12893,295],{"class":294},[120,12895,2150],{"class":298},[120,12897,12898,12900,12902],{"class":122,"line":420},[120,12899,6301],{"class":134},[120,12901,295],{"class":294},[120,12903,6306],{"class":134},[120,12905,12906],{"class":122,"line":904},[120,12907,159],{"emptyLinePlaceholder":158},[120,12909,12910,12912,12914,12916,12918,12920,12922,12924,12926,12928,12930,12932],{"class":122,"line":915},[120,12911,6315],{"class":294},[120,12913,5758],{"class":130},[120,12915,312],{"class":134},[120,12917,5763],{"class":580},[120,12919,6324],{"class":134},[120,12921,6009],{"class":130},[120,12923,312],{"class":134},[120,12925,6331],{"class":580},[120,12927,1251],{"class":134},[120,12929,6336],{"class":315},[120,12931,6339],{"class":126},[120,12933,651],{"class":134},[120,12935,12936,12938,12940],{"class":122,"line":1214},[120,12937,6346],{"class":134},[120,12939,6349],{"class":130},[120,12941,6352],{"class":134},[120,12943,12944,12946,12948,12950,12952,12954,12956,12958,12960,12962],{"class":122,"line":1882},[120,12945,6346],{"class":134},[120,12947,6009],{"class":130},[120,12949,312],{"class":134},[120,12951,6363],{"class":580},[120,12953,1251],{"class":134},[120,12955,1096],{"class":315},[120,12957,6339],{"class":126},[120,12959,6372],{"class":134},[120,12961,295],{"class":294},[120,12963,6377],{"class":134},[120,12965,12966],{"class":122,"line":1895},[120,12967,6143],{"class":134},[120,12969,12970],{"class":122,"line":2716},[120,12971,672],{"class":134},[120,12973,12974,12976,12978],{"class":122,"line":2732},[120,12975,793],{"class":134},[120,12977,739],{"class":294},[120,12979,753],{"class":134},[120,12981,12982],{"class":122,"line":2742},[120,12983,159],{"emptyLinePlaceholder":158},[120,12985,12986,12988,12990],{"class":122,"line":2752},[120,12987,736],{"class":134},[120,12989,808],{"class":294},[120,12991,753],{"class":134},[120,12993,12994,12996,12998,13000,13002,13004,13006,13008,13010],{"class":122,"line":2771},[120,12995,815],{"class":134},[120,12997,6412],{"class":294},[120,12999,610],{"class":130},[120,13001,295],{"class":134},[120,13003,6419],{"class":580},[120,13005,6422],{"class":130},[120,13007,6425],{"class":130},[120,13009,361],{"class":6428},[120,13011,753],{"class":134},[120,13013,13014,13016,13018,13020,13022,13024,13026,13028,13030],{"class":122,"line":2777},[120,13015,815],{"class":134},[120,13017,6412],{"class":294},[120,13019,610],{"class":130},[120,13021,295],{"class":134},[120,13023,6443],{"class":580},[120,13025,6422],{"class":130},[120,13027,6425],{"class":130},[120,13029,361],{"class":6428},[120,13031,753],{"class":134},[120,13033,13034,13036,13038],{"class":122,"line":2791},[120,13035,793],{"class":134},[120,13037,808],{"class":294},[120,13039,753],{"class":134},[11,13041,6462],{},[65,13043,6466],{"id":6465},[11,13045,6469],{},[1475,13047,13048,13054,13060],{},[1478,13049,13050,6478],{},[18,13051,13052,109],{},[540,13053,4771],{"href":4770},[1478,13055,13056,6485],{},[18,13057,13058,109],{},[540,13059,4776],{"href":4775},[1478,13061,13062,6491],{},[18,13063,6490],{},[11,13065,6494],{},[11,13067,6497,13068],{},[540,13069,6501],{"href":3398,"rel":13070},[544],[11,13072,6504],{},[3405,13074,6507],{},{"title":116,"searchDepth":138,"depth":138,"links":13076},[13077,13078,13082,13083,13086],{"id":4783,"depth":138,"text":4784},{"id":4810,"depth":138,"text":4811,"children":13079},[13080,13081],{"id":4864,"depth":147,"text":4865},{"id":5073,"depth":147,"text":5074},{"id":5098,"depth":138,"text":5099},{"id":5702,"depth":138,"text":5703,"children":13084},[13085],{"id":6153,"depth":147,"text":6154},{"id":6465,"depth":138,"text":6466},{},{"title":4752,"description":6521},{"id":6529,"title":6530,"body":13090,"category":3425,"date":8014,"description":8015,"draft":3428,"extension":3429,"image":8016,"meta":14290,"navigation":158,"path":4775,"seo":14291,"stem":8019,"tighten":3428,"__hash__":8020},{"type":8,"value":13091,"toc":14281},[13092,13098,13106,13108,13112,13120,13122,13124,13126,13132,13141,13149,13151,13153,13167,13171,13173,13178,13182,13280,13292,13300,13304,13444,13449,13453,13455,13703,13709,13711,13713,13715,13721,13785,13793,13851,13855,13857,13859,13865,13867,13869,13975,13979,13984,13998,14060,14066,14071,14083,14261,14263,14265,14269,14271,14277,14279],[3442,13093,13094],{"color":6535,"icon":3445},[11,13095,4760,13096,4764],{},[540,13097,3452],{"href":4763},[11,13099,6542,13100,6545,13102,6549,13104,6553],{},[540,13101,4771],{"href":4770},[18,13103,6548],{},[18,13105,6552],{},[65,13107,6557],{"id":6556},[11,13109,6560,13110,6564],{},[18,13111,6563],{},[1475,13113,13114,13116,13118],{},[1478,13115,6569],{},[1478,13117,6572],{},[1478,13119,6575],{},[11,13121,6578],{},[11,13123,6581],{},[65,13125,6585],{"id":6584},[11,13127,6588,13128,6592,13130,6596],{},[46,13129,6591],{},[46,13131,6595],{},[11,13133,6599,13134,6603,13136,6607,13138,535],{},[46,13135,6602],{},[46,13137,6606],{},[540,13139,6612],{"href":6610,"rel":13140},[544],[11,13142,6615,13143,94,13145,6622,13147,6626],{},[46,13144,6618],{},[46,13146,6621],{},[46,13148,6625],{},[11,13150,6629],{},[65,13152,6633],{"id":6632},[11,13154,6636,13155,1251,13158,6647,13161,1263,13164,535],{},[540,13156,6641],{"href":6639,"rel":13157},[544],[540,13159,6646],{"href":6644,"rel":13160},[544],[540,13162,6652],{"href":6650,"rel":13163},[544],[540,13165,6657],{"href":6655,"rel":13166},[544],[11,13168,6660,13169],{},[18,13170,6663],{},[11,13172,6666],{},[6668,13174,13176],{"className":13175,"icon":6672},[6671],[11,13177,6675],{},[11,13179,6678,13180,6682],{},[46,13181,6681],{},[111,13183,13184],{"className":113,"code":6685,"language":115,"meta":116,"style":116},[46,13185,13186,13196,13200,13210,13214,13226,13230,13234,13248,13252,13268,13272,13276],{"__ignoreMap":116},[120,13187,13188,13190,13192,13194],{"class":122,"line":123},[120,13189,1364],{"class":294},[120,13191,6694],{"class":294},[120,13193,6697],{"class":130},[120,13195,5640],{"class":134},[120,13197,13198],{"class":122,"line":138},[120,13199,6704],{"class":1358},[120,13201,13202,13204,13206,13208],{"class":122,"line":147},[120,13203,6709],{"class":130},[120,13205,312],{"class":134},[120,13207,6714],{"class":315},[120,13209,319],{"class":134},[120,13211,13212],{"class":122,"line":155},[120,13213,6721],{"class":1358},[120,13215,13216,13218,13220,13222,13224],{"class":122,"line":162},[120,13217,6726],{"class":134},[120,13219,6729],{"class":130},[120,13221,312],{"class":134},[120,13223,6734],{"class":580},[120,13225,367],{"class":134},[120,13227,13228],{"class":122,"line":171},[120,13229,6741],{"class":134},[120,13231,13232],{"class":122,"line":177},[120,13233,6746],{"class":1358},[120,13235,13236,13238,13240,13242,13244,13246],{"class":122,"line":182},[120,13237,6751],{"class":130},[120,13239,312],{"class":134},[120,13241,6714],{"class":315},[120,13243,1251],{"class":134},[120,13245,6760],{"class":315},[120,13247,319],{"class":134},[120,13249,13250],{"class":122,"line":391},[120,13251,6767],{"class":1358},[120,13253,13254,13256,13258,13260,13262,13264,13266],{"class":122,"line":398},[120,13255,6726],{"class":134},[120,13257,6774],{"class":130},[120,13259,312],{"class":134},[120,13261,6734],{"class":580},[120,13263,6781],{"class":134},[120,13265,4350],{"class":130},[120,13267,6786],{"class":134},[120,13269,13270],{"class":122,"line":403},[120,13271,2165],{"class":134},[120,13273,13274],{"class":122,"line":410},[120,13275,6795],{"class":1358},[120,13277,13278],{"class":122,"line":415},[120,13279,672],{"class":134},[1475,13281,13282,13288],{},[1478,13283,6804,13284,6808,13286,6811],{},[46,13285,6807],{},[46,13287,6734],{},[1478,13289,6804,13290,6816],{},[46,13291,6760],{},[11,13293,6819,13294,1251,13296,1440,13298,6828],{},[46,13295,6822],{},[46,13297,6825],{},[46,13299,5482],{},[11,13301,6831,13302,6834],{},[46,13303,6714],{},[111,13305,13306],{"className":113,"code":6837,"language":115,"meta":116,"style":116},[46,13307,13308,13312,13324,13328,13332,13348,13368,13372,13376,13380,13392,13408,13412,13416,13428,13432,13436],{"__ignoreMap":116},[120,13309,13310],{"class":122,"line":123},[120,13311,6844],{"class":1358},[120,13313,13314,13316,13318,13320,13322],{"class":122,"line":138},[120,13315,6006],{"class":134},[120,13317,6729],{"class":130},[120,13319,312],{"class":134},[120,13321,6734],{"class":580},[120,13323,367],{"class":134},[120,13325,13326],{"class":122,"line":147},[120,13327,159],{"emptyLinePlaceholder":158},[120,13329,13330],{"class":122,"line":155},[120,13331,6865],{"class":1358},[120,13333,13334,13336,13338,13340,13342,13344,13346],{"class":122,"line":162},[120,13335,6006],{"class":134},[120,13337,6774],{"class":130},[120,13339,312],{"class":134},[120,13341,6734],{"class":580},[120,13343,1251],{"class":134},[120,13345,6880],{"class":580},[120,13347,367],{"class":134},[120,13349,13350,13352,13354,13356,13358,13360,13362,13364,13366],{"class":122,"line":171},[120,13351,6006],{"class":134},[120,13353,6774],{"class":130},[120,13355,312],{"class":134},[120,13357,6734],{"class":580},[120,13359,6895],{"class":134},[120,13361,6898],{"class":580},[120,13363,6901],{"class":134},[120,13365,6904],{"class":580},[120,13367,6907],{"class":134},[120,13369,13370],{"class":122,"line":177},[120,13371,159],{"emptyLinePlaceholder":158},[120,13373,13374],{"class":122,"line":182},[120,13375,6916],{"class":1358},[120,13377,13378],{"class":122,"line":391},[120,13379,6921],{"class":1358},[120,13381,13382,13384,13386,13388,13390],{"class":122,"line":398},[120,13383,6006],{"class":134},[120,13385,5415],{"class":130},[120,13387,312],{"class":134},[120,13389,6932],{"class":580},[120,13391,367],{"class":134},[120,13393,13394,13396,13398,13400,13402,13404,13406],{"class":122,"line":403},[120,13395,6006],{"class":134},[120,13397,5415],{"class":130},[120,13399,6943],{"class":134},[120,13401,6946],{"class":580},[120,13403,6901],{"class":134},[120,13405,6951],{"class":580},[120,13407,6907],{"class":134},[120,13409,13410],{"class":122,"line":410},[120,13411,159],{"emptyLinePlaceholder":158},[120,13413,13414],{"class":122,"line":415},[120,13415,6962],{"class":1358},[120,13417,13418,13420,13422,13424,13426],{"class":122,"line":420},[120,13419,6006],{"class":134},[120,13421,6969],{"class":130},[120,13423,312],{"class":134},[120,13425,6734],{"class":580},[120,13427,367],{"class":134},[120,13429,13430],{"class":122,"line":904},[120,13431,159],{"emptyLinePlaceholder":158},[120,13433,13434],{"class":122,"line":915},[120,13435,6984],{"class":1358},[120,13437,13438,13440,13442],{"class":122,"line":1214},[120,13439,6006],{"class":134},[120,13441,6825],{"class":130},[120,13443,144],{"class":134},[6668,13445,13447],{"className":13446,"icon":6996},[6671],[11,13448,6999],{},[11,13450,7002,13451,535],{},[46,13452,7005],{},[11,13454,7008],{},[111,13456,13457],{"className":727,"code":7011,"language":729,"meta":116,"style":116},[46,13458,13459,13469,13479,13483,13495,13511,13533,13537,13541,13557,13565,13571,13591,13599,13603,13611,13615,13623,13661,13695],{"__ignoreMap":116},[120,13460,13461,13463,13465,13467],{"class":122,"line":123},[120,13462,736],{"class":134},[120,13464,739],{"class":294},[120,13466,742],{"class":130},[120,13468,753],{"class":134},[120,13470,13471,13473,13475,13477],{"class":122,"line":138},[120,13472,571],{"class":294},[120,13474,7030],{"class":134},[120,13476,577],{"class":294},[120,13478,7035],{"class":580},[120,13480,13481],{"class":122,"line":147},[120,13482,159],{"emptyLinePlaceholder":158},[120,13484,13485,13487,13489,13491,13493],{"class":122,"line":155},[120,13486,602],{"class":126},[120,13488,7046],{"class":134},[120,13490,295],{"class":294},[120,13492,610],{"class":130},[120,13494,7053],{"class":134},[120,13496,13497,13499,13501,13503,13505,13507,13509],{"class":122,"line":162},[120,13498,602],{"class":126},[120,13500,7060],{"class":134},[120,13502,295],{"class":294},[120,13504,7065],{"class":130},[120,13506,312],{"class":134},[120,13508,7070],{"class":580},[120,13510,5901],{"class":134},[120,13512,13513,13515,13517,13519,13521,13523,13525,13527,13529,13531],{"class":122,"line":171},[120,13514,7077],{"class":130},[120,13516,7080],{"class":134},[120,13518,7083],{"class":315},[120,13520,1251],{"class":134},[120,13522,1744],{"class":315},[120,13524,1944],{"class":134},[120,13526,648],{"class":126},[120,13528,7094],{"class":134},[120,13530,7097],{"class":130},[120,13532,7100],{"class":134},[120,13534,13535],{"class":122,"line":177},[120,13536,672],{"class":134},[120,13538,13539],{"class":122,"line":182},[120,13540,159],{"emptyLinePlaceholder":158},[120,13542,13543,13545,13547,13549,13551,13553,13555],{"class":122,"line":391},[120,13544,602],{"class":126},[120,13546,7115],{"class":134},[120,13548,295],{"class":294},[120,13550,610],{"class":130},[120,13552,312],{"class":134},[120,13554,7124],{"class":580},[120,13556,367],{"class":134},[120,13558,13559,13561,13563],{"class":122,"line":398},[120,13560,127],{"class":126},[120,13562,7133],{"class":130},[120,13564,135],{"class":134},[120,13566,13567,13569],{"class":122,"line":403},[120,13568,7140],{"class":130},[120,13570,7143],{"class":134},[120,13572,13573,13575,13577,13579,13581,13583,13585,13587,13589],{"class":122,"line":410},[120,13574,7148],{"class":134},[120,13576,7097],{"class":130},[120,13578,312],{"class":134},[120,13580,7155],{"class":580},[120,13582,1007],{"class":294},[120,13584,7160],{"class":134},[120,13586,1013],{"class":294},[120,13588,1016],{"class":580},[120,13590,367],{"class":134},[120,13592,13593,13595,13597],{"class":122,"line":415},[120,13594,7171],{"class":134},[120,13596,295],{"class":294},[120,13598,7176],{"class":580},[120,13600,13601],{"class":122,"line":420},[120,13602,174],{"class":134},[120,13604,13605,13607,13609],{"class":122,"line":904},[120,13606,793],{"class":134},[120,13608,739],{"class":294},[120,13610,753],{"class":134},[120,13612,13613],{"class":122,"line":915},[120,13614,159],{"emptyLinePlaceholder":158},[120,13616,13617,13619,13621],{"class":122,"line":1214},[120,13618,736],{"class":134},[120,13620,808],{"class":294},[120,13622,753],{"class":134},[120,13624,13625,13627,13629,13631,13633,13635,13637,13639,13641,13643,13645,13647,13649,13651,13653,13655,13657,13659],{"class":122,"line":1882},[120,13626,815],{"class":134},[120,13628,7207],{"class":294},[120,13630,1048],{"class":294},[120,13632,295],{"class":134},[120,13634,831],{"class":134},[120,13636,7216],{"class":134},[120,13638,984],{"class":294},[120,13640,7221],{"class":134},[120,13642,831],{"class":134},[120,13644,1065],{"class":134},[120,13646,997],{"class":130},[120,13648,295],{"class":134},[120,13650,831],{"class":134},[120,13652,1010],{"class":134},[120,13654,831],{"class":134},[120,13656,7238],{"class":134},[120,13658,7207],{"class":294},[120,13660,753],{"class":134},[120,13662,13663,13665,13667,13669,13671,13673,13675,13677,13679,13681,13683,13685,13687,13689,13691,13693],{"class":122,"line":1895},[120,13664,815],{"class":134},[120,13666,7249],{"class":294},[120,13668,7252],{"class":130},[120,13670,295],{"class":134},[120,13672,831],{"class":134},[120,13674,7249],{"class":134},[120,13676,831],{"class":134},[120,13678,1136],{"class":134},[120,13680,7265],{"class":130},[120,13682,535],{"class":134},[120,13684,7270],{"class":130},[120,13686,295],{"class":134},[120,13688,831],{"class":134},[120,13690,7277],{"class":134},[120,13692,831],{"class":134},[120,13694,901],{"class":134},[120,13696,13697,13699,13701],{"class":122,"line":2716},[120,13698,793],{"class":134},[120,13700,808],{"class":294},[120,13702,753],{"class":134},[11,13704,7292,13705,7296,13707,7299],{},[46,13706,7295],{},[46,13708,5415],{},[11,13710,7302],{},[1281,13712,7306],{"id":7305},[11,13714,7309],{},[11,13716,13717,7315,13719,7318],{},[18,13718,7314],{},[46,13720,7005],{},[111,13722,13723],{"className":113,"code":7321,"language":115,"meta":116,"style":116},[46,13724,13725,13729,13739,13743,13751,13759,13777,13781],{"__ignoreMap":116},[120,13726,13727],{"class":122,"line":123},[120,13728,7328],{"class":1358},[120,13730,13731,13733,13735,13737],{"class":122,"line":138},[120,13732,7005],{"class":130},[120,13734,312],{"class":134},[120,13736,7070],{"class":580},[120,13738,5901],{"class":134},[120,13740,13741],{"class":122,"line":147},[120,13742,7343],{"class":134},[120,13744,13745,13747,13749],{"class":122,"line":155},[120,13746,7348],{"class":134},[120,13748,7351],{"class":298},[120,13750,1385],{"class":134},[120,13752,13753,13755,13757],{"class":122,"line":162},[120,13754,7358],{"class":134},[120,13756,7361],{"class":298},[120,13758,1385],{"class":134},[120,13760,13761,13763,13765,13767,13769,13771,13773,13775],{"class":122,"line":171},[120,13762,7368],{"class":130},[120,13764,7371],{"class":134},[120,13766,648],{"class":126},[120,13768,5451],{"class":134},[120,13770,5400],{"class":130},[120,13772,312],{"class":134},[120,13774,7382],{"class":580},[120,13776,7385],{"class":134},[120,13778,13779],{"class":122,"line":177},[120,13780,2165],{"class":134},[120,13782,13783],{"class":122,"line":182},[120,13784,672],{"class":134},[11,13786,7396,13787,7400,13789,7404,13791,7408],{},[18,13788,7399],{},[23,13790,7403],{},[23,13792,7407],{},[111,13794,13795],{"className":113,"code":7411,"language":115,"meta":116,"style":116},[46,13796,13797,13801,13805,13815,13819,13827,13835,13843,13847],{"__ignoreMap":116},[120,13798,13799],{"class":122,"line":123},[120,13800,7418],{"class":1358},[120,13802,13803],{"class":122,"line":138},[120,13804,7423],{"class":1358},[120,13806,13807,13809,13811,13813],{"class":122,"line":147},[120,13808,7005],{"class":130},[120,13810,312],{"class":134},[120,13812,7070],{"class":580},[120,13814,5901],{"class":134},[120,13816,13817],{"class":122,"line":155},[120,13818,7438],{"class":134},[120,13820,13821,13823,13825],{"class":122,"line":162},[120,13822,7443],{"class":134},[120,13824,7446],{"class":580},[120,13826,1385],{"class":134},[120,13828,13829,13831,13833],{"class":122,"line":171},[120,13830,7453],{"class":134},[120,13832,7456],{"class":298},[120,13834,1385],{"class":134},[120,13836,13837,13839,13841],{"class":122,"line":177},[120,13838,7463],{"class":134},[120,13840,358],{"class":298},[120,13842,1385],{"class":134},[120,13844,13845],{"class":122,"line":182},[120,13846,2165],{"class":134},[120,13848,13849],{"class":122,"line":391},[120,13850,672],{"class":134},[11,13852,7478,13853,7482],{},[46,13854,7481],{},[65,13856,7486],{"id":7485},[11,13858,7489],{},[11,13860,7492,13861,7496,13863,7500],{},[23,13862,7495],{},[23,13864,7499],{},[11,13866,7503],{},[11,13868,7506],{},[111,13870,13871],{"className":727,"code":7509,"language":729,"meta":116,"style":116},[46,13872,13873,13881,13899,13933,13937,13945,13953,13967],{"__ignoreMap":116},[120,13874,13875,13877,13879],{"class":122,"line":123},[120,13876,736],{"class":134},[120,13878,808],{"class":294},[120,13880,753],{"class":134},[120,13882,13883,13885,13887,13889,13891,13893,13895,13897],{"class":122,"line":138},[120,13884,815],{"class":134},[120,13886,1475],{"class":294},[120,13888,7528],{"class":294},[120,13890,295],{"class":134},[120,13892,831],{"class":134},[120,13894,7535],{"class":134},[120,13896,831],{"class":134},[120,13898,753],{"class":134},[120,13900,13901,13903,13905,13907,13909,13911,13913,13915,13917,13919,13921,13923,13925,13927,13929,13931],{"class":122,"line":147},[120,13902,895],{"class":134},[120,13904,1478],{"class":294},[120,13906,1048],{"class":294},[120,13908,295],{"class":134},[120,13910,831],{"class":134},[120,13912,7554],{"class":134},[120,13914,984],{"class":294},[120,13916,7559],{"class":134},[120,13918,831],{"class":134},[120,13920,1065],{"class":134},[120,13922,997],{"class":130},[120,13924,295],{"class":134},[120,13926,831],{"class":134},[120,13928,7572],{"class":134},[120,13930,831],{"class":134},[120,13932,753],{"class":134},[120,13934,13935],{"class":122,"line":155},[120,13936,7581],{"class":134},[120,13938,13939,13941,13943],{"class":122,"line":162},[120,13940,4222],{"class":134},[120,13942,1478],{"class":294},[120,13944,753],{"class":134},[120,13946,13947,13949,13951],{"class":122,"line":171},[120,13948,907],{"class":134},[120,13950,1475],{"class":294},[120,13952,753],{"class":134},[120,13954,13955,13957,13959,13961,13963,13965],{"class":122,"line":177},[120,13956,815],{"class":134},[120,13958,11],{"class":294},[120,13960,7606],{"class":294},[120,13962,7609],{"class":134},[120,13964,11],{"class":294},[120,13966,753],{"class":134},[120,13968,13969,13971,13973],{"class":122,"line":182},[120,13970,793],{"class":134},[120,13972,808],{"class":294},[120,13974,753],{"class":134},[11,13976,7624,13977,7627],{},[46,13978,7535],{},[6668,13980,13982],{"className":13981,"icon":7632},[7631],[11,13983,7635],{},[11,13985,13986,7643,13989,40,13991,7650,13993,7658],{},[540,13987,7642],{"href":7640,"rel":13988},[544],[18,13990,7646],{},[18,13992,7649],{},[540,13994,13996],{"href":7653,"rel":13995},[544],[18,13997,7657],{},[111,13999,14000],{"className":113,"code":7661,"language":115,"meta":116,"style":116},[46,14001,14002,14006,14016,14026,14036,14040],{"__ignoreMap":116},[120,14003,14004],{"class":122,"line":123},[120,14005,7668],{"class":1358},[120,14007,14008,14010,14012,14014],{"class":122,"line":138},[120,14009,571],{"class":294},[120,14011,7675],{"class":134},[120,14013,577],{"class":294},[120,14015,7680],{"class":580},[120,14017,14018,14020,14022,14024],{"class":122,"line":147},[120,14019,571],{"class":294},[120,14021,7687],{"class":134},[120,14023,577],{"class":294},[120,14025,7692],{"class":580},[120,14027,14028,14030,14032,14034],{"class":122,"line":155},[120,14029,571],{"class":294},[120,14031,7699],{"class":134},[120,14033,577],{"class":294},[120,14035,7704],{"class":580},[120,14037,14038],{"class":122,"line":162},[120,14039,159],{"emptyLinePlaceholder":158},[120,14041,14042,14044,14046,14048,14050,14052,14054,14056,14058],{"class":122,"line":171},[120,14043,602],{"class":126},[120,14045,7715],{"class":134},[120,14047,295],{"class":294},[120,14049,7720],{"class":130},[120,14051,312],{"class":134},[120,14053,7725],{"class":130},[120,14055,7728],{"class":134},[120,14057,7731],{"class":580},[120,14059,5558],{"class":134},[11,14061,7736,14062,7739,14064,7742],{},[46,14063,7535],{},[46,14065,7535],{},[6668,14067,14069],{"className":14068,"icon":7747},[7746],[11,14070,7750],{},[11,14072,7753,14073,7761,14078,535],{},[540,14074,14076],{"href":7756,"rel":14075},[544],[18,14077,7760],{},[540,14079,14081],{"href":7764,"rel":14080},[544],[18,14082,7768],{},[111,14084,14085],{"className":113,"code":7771,"language":115,"meta":116,"style":116},[46,14086,14087,14091,14103,14107,14131,14155,14159,14163,14169,14173,14181,14189,14201,14213,14227,14231,14239,14243,14247],{"__ignoreMap":116},[120,14088,14089],{"class":122,"line":123},[120,14090,7778],{"class":1358},[120,14092,14093,14095,14097,14099,14101],{"class":122,"line":138},[120,14094,602],{"class":126},[120,14096,7785],{"class":134},[120,14098,295],{"class":294},[120,14100,7790],{"class":130},[120,14102,144],{"class":134},[120,14104,14105],{"class":122,"line":147},[120,14106,159],{"emptyLinePlaceholder":158},[120,14108,14109,14111,14113,14115,14117,14119,14121,14123,14125,14127,14129],{"class":122,"line":155},[120,14110,602],{"class":126},[120,14112,7803],{"class":134},[120,14114,295],{"class":294},[120,14116,6254],{"class":294},[120,14118,7810],{"class":130},[120,14120,312],{"class":134},[120,14122,7731],{"class":580},[120,14124,1251],{"class":134},[120,14126,6236],{"class":294},[120,14128,5388],{"class":134},[120,14130,7823],{"class":126},[120,14132,14133,14135,14137,14139,14141,14143,14145,14147,14149,14151,14153],{"class":122,"line":162},[120,14134,7828],{"class":134},[120,14136,7831],{"class":294},[120,14138,7834],{"class":134},[120,14140,577],{"class":130},[120,14142,312],{"class":134},[120,14144,7731],{"class":580},[120,14146,6324],{"class":134},[120,14148,7845],{"class":130},[120,14150,312],{"class":134},[120,14152,7850],{"class":580},[120,14154,7853],{"class":134},[120,14156,14157],{"class":122,"line":171},[120,14158,367],{"class":134},[120,14160,14161],{"class":122,"line":177},[120,14162,159],{"emptyLinePlaceholder":158},[120,14164,14165,14167],{"class":122,"line":182},[120,14166,288],{"class":126},[120,14168,7868],{"class":134},[120,14170,14171],{"class":122,"line":391},[120,14172,159],{"emptyLinePlaceholder":158},[120,14174,14175,14177,14179],{"class":122,"line":398},[120,14176,1810],{"class":130},[120,14178,1813],{"class":134},[120,14180,7823],{"class":126},[120,14182,14183,14185,14187],{"class":122,"line":403},[120,14184,5369],{"class":134},[120,14186,295],{"class":294},[120,14188,7889],{"class":134},[120,14190,14191,14193,14195,14197,14199],{"class":122,"line":410},[120,14192,7894],{"class":134},[120,14194,7897],{"class":130},[120,14196,312],{"class":134},[120,14198,7902],{"class":580},[120,14200,367],{"class":134},[120,14202,14203,14205,14207,14209,14211],{"class":122,"line":415},[120,14204,7894],{"class":134},[120,14206,6009],{"class":130},[120,14208,312],{"class":134},[120,14210,7915],{"class":580},[120,14212,5901],{"class":134},[120,14214,14215,14217,14219,14221,14223,14225],{"class":122,"line":420},[120,14216,7922],{"class":134},[120,14218,7850],{"class":580},[120,14220,7927],{"class":134},[120,14222,7930],{"class":580},[120,14224,7933],{"class":134},[120,14226,7936],{"class":580},[120,14228,14229],{"class":122,"line":904},[120,14230,7941],{"class":134},[120,14232,14233,14235,14237],{"class":122,"line":915},[120,14234,7894],{"class":134},[120,14236,6729],{"class":130},[120,14238,144],{"class":134},[120,14240,14241],{"class":122,"line":1214},[120,14242,367],{"class":134},[120,14244,14245],{"class":122,"line":1882},[120,14246,159],{"emptyLinePlaceholder":158},[120,14248,14249,14251,14253,14255,14257,14259],{"class":122,"line":1895},[120,14250,1860],{"class":130},[120,14252,1813],{"class":134},[120,14254,648],{"class":126},[120,14256,7834],{"class":134},[120,14258,7970],{"class":130},[120,14260,7973],{"class":134},[11,14262,7976],{},[65,14264,7980],{"id":7979},[11,14266,7983,14267],{},[18,14268,7986],{},[11,14270,7989],{},[11,14272,7992,14273,7995,14275,535],{},[18,14274,4811],{},[540,14276,7998],{"href":6524},[11,14278,8001],{},[3405,14280,8004],{},{"title":116,"searchDepth":138,"depth":138,"links":14282},[14283,14284,14285,14288,14289],{"id":6556,"depth":138,"text":6557},{"id":6584,"depth":138,"text":6585},{"id":6632,"depth":138,"text":6633,"children":14286},[14287],{"id":7305,"depth":147,"text":7306},{"id":7485,"depth":138,"text":7486},{"id":7979,"depth":138,"text":7980},{},{"title":6530,"description":8015},1782570546344]