├── .babelrc ├── .editorconfig ├── .gitignore ├── .idea ├── .gitignore ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── if-script.iml ├── misc.xml ├── modules.xml └── vcs.xml ├── .prettierrc.js ├── README.md ├── config ├── paths.js ├── webpack.config.js ├── webpack.dev.js ├── webpack.gh.js └── webpack.prod.js ├── dist ├── 08da13c09fcc675ab837.png ├── if-logo-nobg.png ├── index.html ├── preview │ └── index.html ├── scripts │ ├── 514.b1dd7ca94e8c1f0ef18f.bundle.js │ ├── 514.b1dd7ca94e8c1f0ef18f.bundle.js.LICENSE.txt │ ├── 886.1619d691d03b4fd46521.bundle.js │ ├── 886.1619d691d03b4fd46521.bundle.js.LICENSE.txt │ ├── main.f1255e2b0bead4cf4121.bundle.js │ ├── main.f1255e2b0bead4cf4121.bundle.js.LICENSE.txt │ ├── preview.b5ba595287997bff1d77.bundle.js │ ├── preview.b5ba595287997bff1d77.bundle.js.LICENSE.txt │ ├── runtime.0c68d17123e005a7980a.bundle.js │ └── runtime.0c68d17123e005a7980a.bundle.js.LICENSE.txt └── styles │ ├── 824.css │ ├── main.f38ec3779613ca9c7584.css │ └── preview.6db1bab1bb1452ba5383.css ├── package-lock.json ├── package.json └── src ├── assets ├── fonts │ ├── FiraMono-Regular.ttf │ └── VarelaRound-Regular.ttf └── images │ ├── book-background0.jpg │ ├── if-logo-nobg.png │ └── if-logo.png ├── banner-html.txt ├── banner.txt ├── lib ├── nearley.js ├── showdown.min.js └── w3Highlighter.js └── web ├── css └── index.css ├── index.html ├── index.template.html ├── js ├── IFError.js ├── globals.js └── index.js ├── preview ├── index.js └── index.template.html └── service-worker.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"], 3 | "plugins": ["@babel/plugin-proposal-class-properties"] 4 | } 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | [*] 3 | indent_style = spaces 4 | indent_size = 2 5 | end_of_line = lf 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | max_line_length = 160 10 | 11 | [*.md] 12 | max_line_length = off 13 | 14 | [*.json] 15 | max_line_length = off 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | todo.html 2 | assets/grammar-archive 3 | assets/help.html 4 | js/if-highlighter.js 5 | file.html 6 | 7 | # dependencies 8 | /node_modules 9 | /.pnp 10 | .pnp.js 11 | /auth.json 12 | 13 | # testing 14 | /coverage 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | .env.local 22 | .env.development.local 23 | .env.test.local 24 | .env.production.local 25 | config/.env 26 | 27 | npm-debug.log* 28 | yarn-debug.log* 29 | yarn-error.log* 30 | 31 | # vscode 32 | .vscode 33 | *.code-workspace 34 | 35 | # nano 36 | *.save 37 | 38 | # db 39 | /data/* 40 | !/data/.gitkeep 41 | 42 | # sublime 43 | *.tern-project 44 | *.sublime-project 45 | *.flowconfig 46 | *.je-project-settings 47 | *.sublime-workspace -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Datasource local storage ignored files 5 | /dataSources/ 6 | /dataSources.local.xml 7 | # Editor-based HTTP Client requests 8 | /httpRequests/ 9 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | 15 | 16 | 29 | 30 | 32 | 33 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/if-script.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = 'prettier-config-standard' -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # IF-SCRIPT 2 | A straightforward syntax for writing interactive fiction can be embedded in any website. 3 | 4 | Make interactive fiction with variables, timers, conditions, music and statistics. The story is parsed into plain HTML, CSS and JavaScript. 5 | 6 | [Try it!](https://plytonrexus.github.io/if-script/) 7 | 8 | You can use Markdown to format your story. 9 | _[Markdown cheat-sheet](https://www.markdownguide.org/cheat-sheet/) for reference._ 10 | 11 | ### Dependencies 12 | [Nearley](https://github.com/kach/nearley) for parsing. 13 | [Showdown](https://github.com/showdownjs/showdown) for markdown rendering. 14 | 15 | A Regular Expression based parser is on the [if-script-regex](https://github.com/PlytonRexus/if-script/tree/if-script-regex) branch. 16 | 17 | ### [Embedding](#embedding) 18 | 19 | Within the head tag, add the following. 20 | 21 | ```html 22 | 23 | ``` 24 | 25 | In your body, add the following scripts. 26 | 27 | ```html 28 |
29 | 30 | 31 | 32 | 33 | ``` 34 | 35 | *The indentation does not matter.* 36 | 37 | ### [Comments](#comments) 38 | 39 | A comment is a text block that is not integrated into the final model of the story. Comments exist to help write cleaner stories, for example, by pointing out the purposes of certain portions of the story. 40 | ``` 41 | /* A 42 | multi-line 43 | comment */ 44 | ``` 45 | 46 | ### [Variables](#variables) 47 | 48 | Variables truly create dynamic stories. You can use variables to store many things like character names, inventory items, visited scenes, number of things and many others. You can then display the values of these variables anywhere like so: 49 | ``` 50 | Your name is ${name}. 51 | ``` 52 | Here, variable `name` was used to store the name of a character. 53 | The variables can also be used in conditional logic to determine which [choices](#choice-syntax) and (in a future release) what paragraphs of a [section's](#section-syntax) text should be visible to the reader. You can find more about this under the [choices](#choice-syntax) heading. 54 | 55 | You can assign variables beforehand inside story [settings](#settings-syntax). 56 | 57 | ``` 58 | ${name='Felicity'} 59 | ``` 60 | 61 | *It is recommended (albeit not required) to keep the* `title`*variable set as the title of the story.* 62 | 63 | ### [Story Settings](#story-settings) 64 | 65 | Story settings allow you to customise the overall experiance of the story. All of the settings are optional. The available settings are: 66 | 67 | - `@startAt` decides the starting section of a story. (Default: 1) 68 | - `@referrable` decides if the older sections remain visible once the reader moves to a new section. (Default: false) 69 | - `@fullTimer` decides the amount of time alloted for completing a story. (Default: none) 70 | ``` 71 | settings> 72 | @startAt 73 | @referrable 74 | @fullTimer 75 | 83 | @first section_number (optional) 84 | @music link (optional) 85 | @sections space_seperated_section_numbers 86 | @name custom_name_for_scene 87 | 94 | tt> Section Title 109 | You are named five [[5]] 110 | 115 | You are named five [[scene:5]] 116 | 121 | Choose 'Felicity' as your name ${__name} [[5]] 122 | 127 | Type in your name here: __input ${__name} [[5]] 128 | = var2` First is greater than or equal to second 135 | - ` var1 <= var2` Second is greater than or equal to first 136 | - ` var1 > var2` First is greater than second 137 | - ` var1 < var2` Second is greater than first 138 | ``` 139 | ch> 140 | ${__if name == "" || namePower <= 0} 141 | Type in your name here __input ${__name} [[5]] 142 | 155 | The power of your name goes up 10 units, and your health 156 | is multiplied \${namePower} times. 157 | ${__namePower + 10} ${__health * namePower} [[5]] 158 | IF | Web
-------------------------------------------------------------------------------- /dist/preview/index.html: -------------------------------------------------------------------------------- 1 | Previewing...
-------------------------------------------------------------------------------- /dist/scripts/514.b1dd7ca94e8c1f0ef18f.bundle.js: -------------------------------------------------------------------------------- 1 | /*! For license information please see 514.b1dd7ca94e8c1f0ef18f.bundle.js.LICENSE.txt */ 2 | (self.webpackChunkif_script=self.webpackChunkif_script||[]).push([[514],{514:(s,c,e)=>{"use strict";e.r(c)}}]); -------------------------------------------------------------------------------- /dist/scripts/514.b1dd7ca94e8c1f0ef18f.bundle.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * IF-Script v0.2.1 4 | * ============== 5 | * 6 | * Built: Thu Jun 17 2021 7 | * 8 | * Copyright (c) 2021 IF-Script Contributors 9 | * Author(s): Mihir Jichkar 10 | * 11 | * MIT Licensed 12 | * https://github.com/PlytonRexus/if-script.git 13 | * 14 | * 15 | */ 16 | -------------------------------------------------------------------------------- /dist/scripts/886.1619d691d03b4fd46521.bundle.js: -------------------------------------------------------------------------------- 1 | /*! For license information please see 886.1619d691d03b4fd46521.bundle.js.LICENSE.txt */ 2 | (self.webpackChunkif_script=self.webpackChunkif_script||[]).push([[886],{886:(s,c,e)=>{"use strict";e.r(c)}}]); -------------------------------------------------------------------------------- /dist/scripts/886.1619d691d03b4fd46521.bundle.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * IF-Script v0.2.1 4 | * ============== 5 | * 6 | * Built: Thu Jun 17 2021 7 | * 8 | * Copyright (c) 2021 IF-Script Contributors 9 | * Author(s): Mihir Jichkar 10 | * 11 | * MIT Licensed 12 | * https://github.com/PlytonRexus/if-script.git 13 | * 14 | * 15 | */ 16 | -------------------------------------------------------------------------------- /dist/scripts/main.f1255e2b0bead4cf4121.bundle.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * IF-Script v0.2.1 4 | * ============== 5 | * 6 | * Built: Thu Jun 17 2021 7 | * 8 | * Copyright (c) 2021 IF-Script Contributors 9 | * Author(s): Mihir Jichkar 10 | * 11 | * MIT Licensed 12 | * https://github.com/PlytonRexus/if-script.git 13 | * 14 | * 15 | */ 16 | 17 | /*! showdown v 1.9.1 - 02-11-2019 */ 18 | -------------------------------------------------------------------------------- /dist/scripts/preview.b5ba595287997bff1d77.bundle.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * IF-Script v0.2.1 4 | * ============== 5 | * 6 | * Built: Thu Jun 17 2021 7 | * 8 | * Copyright (c) 2021 IF-Script Contributors 9 | * Author(s): Mihir Jichkar 10 | * 11 | * MIT Licensed 12 | * https://github.com/PlytonRexus/if-script.git 13 | * 14 | * 15 | */ 16 | 17 | /*! showdown v 1.9.1 - 02-11-2019 */ 18 | -------------------------------------------------------------------------------- /dist/scripts/runtime.0c68d17123e005a7980a.bundle.js: -------------------------------------------------------------------------------- 1 | /*! For license information please see runtime.0c68d17123e005a7980a.bundle.js.LICENSE.txt */ 2 | (()=>{"use strict";var e,r,t,a,n,o={},i={};function s(e){var r=i[e];if(void 0!==r)return r.exports;var t=i[e]={exports:{}};return o[e].call(t.exports,t,t.exports,s),t.exports}s.m=o,e=[],s.O=(r,t,a,n)=>{if(!t){var o=1/0;for(d=0;d=n)&&Object.keys(s.O).every((e=>s.O[e](t[l])))?t.splice(l--,1):(i=!1,n0&&e[d-1][2]>n;d--)e[d]=e[d-1];e[d]=[t,a,n]},s.d=(e,r)=>{for(var t in r)s.o(r,t)&&!s.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},s.f={},s.e=e=>Promise.all(Object.keys(s.f).reduce(((r,t)=>(s.f[t](e,r),r)),[])),s.u=e=>"scripts/"+e+"."+{514:"b1dd7ca94e8c1f0ef18f",824:"46c376c8f8b5c41ba9c6",886:"1619d691d03b4fd46521"}[e]+".bundle.js",s.miniCssF=e=>824===e?"styles/824.css":"styles/"+{179:"main",493:"preview"}[e]+"."+{179:"f38ec3779613ca9c7584",493:"6db1bab1bb1452ba5383"}[e]+".css",s.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),r={},t="if-script:",s.l=(e,a,n,o)=>{if(r[e])r[e].push(a);else{var i,l;if(void 0!==n)for(var d=document.getElementsByTagName("script"),c=0;c{i.onerror=i.onload=null,clearTimeout(p);var n=r[e];if(delete r[e],i.parentNode&&i.parentNode.removeChild(i),n&&n.forEach((e=>e(a))),t)return t(a)},p=setTimeout(f.bind(null,void 0,{type:"timeout",target:i}),12e4);i.onerror=f.bind(null,i.onerror),i.onload=f.bind(null,i.onload),l&&document.head.appendChild(i)}},s.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},s.p="/if-script/",a=e=>new Promise(((r,t)=>{var a=s.miniCssF(e),n=s.p+a;if(((e,r)=>{for(var t=document.getElementsByTagName("link"),a=0;a{var n=document.createElement("link");n.rel="stylesheet",n.type="text/css",n.onerror=n.onload=o=>{if(n.onerror=n.onload=null,"load"===o.type)t();else{var i=o&&("load"===o.type?"missing":o.type),s=o&&o.target&&o.target.href||r,l=new Error("Loading CSS chunk "+e+" failed.\n("+s+")");l.code="CSS_CHUNK_LOAD_FAILED",l.type=i,l.request=s,n.parentNode.removeChild(n),a(l)}},n.href=r,document.head.appendChild(n)})(e,n,r,t)})),n={666:0},s.f.miniCss=(e,r)=>{n[e]?r.push(n[e]):0!==n[e]&&{824:1}[e]&&r.push(n[e]=a(e).then((()=>{n[e]=0}),(r=>{throw delete n[e],r})))},(()=>{var e={666:0};s.f.j=(r,t)=>{var a=s.o(e,r)?e[r]:void 0;if(0!==a)if(a)t.push(a[2]);else if(/^(666|824)$/.test(r))e[r]=0;else{var n=new Promise(((t,n)=>a=e[r]=[t,n]));t.push(a[2]=n);var o=s.p+s.u(r),i=new Error;s.l(o,(t=>{if(s.o(e,r)&&(0!==(a=e[r])&&(e[r]=void 0),a)){var n=t&&("load"===t.type?"missing":t.type),o=t&&t.target&&t.target.src;i.message="Loading chunk "+r+" failed.\n("+n+": "+o+")",i.name="ChunkLoadError",i.type=n,i.request=o,a[1](i)}}),"chunk-"+r,r)}},s.O.j=r=>0===e[r];var r=(r,t)=>{var a,n,[o,i,l]=t,d=0;for(a in i)s.o(i,a)&&(s.m[a]=i[a]);if(l)var c=l(s);for(r&&r(t);d 3 | ! ============== 4 | ! 5 | ! Built: <%= grunt.template.today('yyyy-mm-dd') %> 6 | ! 7 | ! Copyright (c) <%= grunt.template.today('yyyy') %> Mihir Jichkar (PlytonRexus) 8 | ! Authors: <%= app.author %> 9 | ! 10 | ! MIT Licensed 11 | ! https://github.com/PlytonRexus/if-script.git 12 | --> 13 | -------------------------------------------------------------------------------- /src/banner.txt: -------------------------------------------------------------------------------- 1 | /** 2 | * IF-Script v<%= app.version %> 3 | * ============== 4 | * 5 | * Built: <%= grunt.template.today('yyyy-mm-dd') %> 6 | * 7 | * Copyright (c) <%= grunt.template.today('yyyy') %> The IF-Script Contributors 8 | * Authors: <%= app.author %> 9 | * 10 | * MIT Licensed 11 | * https://github.com/PlytonRexus/if-script.git 12 | */ 13 | -------------------------------------------------------------------------------- /src/lib/nearley.js: -------------------------------------------------------------------------------- 1 | (function(root, factory) { 2 | if (typeof module === 'object' && module.exports) { 3 | module.exports = factory(); 4 | } else { 5 | root.nearley = factory(); 6 | } 7 | }(this, function() { 8 | 9 | function Rule(name, symbols, postprocess) { 10 | this.id = ++Rule.highestId; 11 | this.name = name; 12 | this.symbols = symbols; // a list of literal | regex class | nonterminal 13 | this.postprocess = postprocess; 14 | return this; 15 | } 16 | Rule.highestId = 0; 17 | 18 | Rule.prototype.toString = function(withCursorAt) { 19 | var symbolSequence = (typeof withCursorAt === "undefined") 20 | ? this.symbols.map(getSymbolShortDisplay).join(' ') 21 | : ( this.symbols.slice(0, withCursorAt).map(getSymbolShortDisplay).join(' ') 22 | + " ● " 23 | + this.symbols.slice(withCursorAt).map(getSymbolShortDisplay).join(' ') ); 24 | return this.name + " → " + symbolSequence; 25 | } 26 | 27 | 28 | // a State is a rule at a position from a given starting point in the input stream (reference) 29 | function State(rule, dot, reference, wantedBy) { 30 | this.rule = rule; 31 | this.dot = dot; 32 | this.reference = reference; 33 | this.data = []; 34 | this.wantedBy = wantedBy; 35 | this.isComplete = this.dot === rule.symbols.length; 36 | } 37 | 38 | State.prototype.toString = function() { 39 | return "{" + this.rule.toString(this.dot) + "}, from: " + (this.reference || 0); 40 | }; 41 | 42 | State.prototype.nextState = function(child) { 43 | var state = new State(this.rule, this.dot + 1, this.reference, this.wantedBy); 44 | state.left = this; 45 | state.right = child; 46 | if (state.isComplete) { 47 | state.data = state.build(); 48 | // Having right set here will prevent the right state and its children 49 | // form being garbage collected 50 | state.right = undefined; 51 | } 52 | return state; 53 | }; 54 | 55 | State.prototype.build = function() { 56 | var children = []; 57 | var node = this; 58 | do { 59 | children.push(node.right.data); 60 | node = node.left; 61 | } while (node.left); 62 | children.reverse(); 63 | return children; 64 | }; 65 | 66 | State.prototype.finish = function() { 67 | if (this.rule.postprocess) { 68 | this.data = this.rule.postprocess(this.data, this.reference, Parser.fail); 69 | } 70 | }; 71 | 72 | 73 | function Column(grammar, index) { 74 | this.grammar = grammar; 75 | this.index = index; 76 | this.states = []; 77 | this.wants = {}; // states indexed by the non-terminal they expect 78 | this.scannable = []; // list of states that expect a token 79 | this.completed = {}; // states that are nullable 80 | } 81 | 82 | 83 | Column.prototype.process = function(nextColumn) { 84 | var states = this.states; 85 | var wants = this.wants; 86 | var completed = this.completed; 87 | 88 | for (var w = 0; w < states.length; w++) { // nb. we push() during iteration 89 | var state = states[w]; 90 | 91 | if (state.isComplete) { 92 | state.finish(); 93 | if (state.data !== Parser.fail) { 94 | // complete 95 | var wantedBy = state.wantedBy; 96 | for (var i = wantedBy.length; i--; ) { // this line is hot 97 | var left = wantedBy[i]; 98 | this.complete(left, state); 99 | } 100 | 101 | // special-case nullables 102 | if (state.reference === this.index) { 103 | // make sure future predictors of this rule get completed. 104 | var exp = state.rule.name; 105 | (this.completed[exp] = this.completed[exp] || []).push(state); 106 | } 107 | } 108 | 109 | } else { 110 | // queue scannable states 111 | var exp = state.rule.symbols[state.dot]; 112 | if (typeof exp !== 'string') { 113 | this.scannable.push(state); 114 | continue; 115 | } 116 | 117 | // predict 118 | if (wants[exp]) { 119 | wants[exp].push(state); 120 | 121 | if (completed.hasOwnProperty(exp)) { 122 | var nulls = completed[exp]; 123 | for (var i = 0; i < nulls.length; i++) { 124 | var right = nulls[i]; 125 | this.complete(state, right); 126 | } 127 | } 128 | } else { 129 | wants[exp] = [state]; 130 | this.predict(exp); 131 | } 132 | } 133 | } 134 | } 135 | 136 | Column.prototype.predict = function(exp) { 137 | var rules = this.grammar.byName[exp] || []; 138 | 139 | for (var i = 0; i < rules.length; i++) { 140 | var r = rules[i]; 141 | var wantedBy = this.wants[exp]; 142 | var s = new State(r, 0, this.index, wantedBy); 143 | this.states.push(s); 144 | } 145 | } 146 | 147 | Column.prototype.complete = function(left, right) { 148 | var copy = left.nextState(right); 149 | this.states.push(copy); 150 | } 151 | 152 | 153 | function Grammar(rules, start) { 154 | this.rules = rules; 155 | this.start = start || this.rules[0].name; 156 | var byName = this.byName = {}; 157 | this.rules.forEach(function(rule) { 158 | if (!byName.hasOwnProperty(rule.name)) { 159 | byName[rule.name] = []; 160 | } 161 | byName[rule.name].push(rule); 162 | }); 163 | } 164 | 165 | // So we can allow passing (rules, start) directly to Parser for backwards compatibility 166 | Grammar.fromCompiled = function(rules, start) { 167 | var lexer = rules.Lexer; 168 | if (rules.ParserStart) { 169 | start = rules.ParserStart; 170 | rules = rules.ParserRules; 171 | } 172 | var rules = rules.map(function (r) { return (new Rule(r.name, r.symbols, r.postprocess)); }); 173 | var g = new Grammar(rules, start); 174 | g.lexer = lexer; // nb. storing lexer on Grammar is iffy, but unavoidable 175 | return g; 176 | } 177 | 178 | 179 | function StreamLexer() { 180 | this.reset(""); 181 | } 182 | 183 | StreamLexer.prototype.reset = function(data, state) { 184 | this.buffer = data; 185 | this.index = 0; 186 | this.line = state ? state.line : 1; 187 | this.lastLineBreak = state ? -state.col : 0; 188 | } 189 | 190 | StreamLexer.prototype.next = function() { 191 | if (this.index < this.buffer.length) { 192 | var ch = this.buffer[this.index++]; 193 | if (ch === '\n') { 194 | this.line += 1; 195 | this.lastLineBreak = this.index; 196 | } 197 | return {value: ch}; 198 | } 199 | } 200 | 201 | StreamLexer.prototype.save = function() { 202 | return { 203 | line: this.line, 204 | col: this.index - this.lastLineBreak, 205 | } 206 | } 207 | 208 | StreamLexer.prototype.formatError = function(token, message) { 209 | // nb. this gets called after consuming the offending token, 210 | // so the culprit is index-1 211 | var buffer = this.buffer; 212 | if (typeof buffer === 'string') { 213 | var nextLineBreak = buffer.indexOf('\n', this.index); 214 | if (nextLineBreak === -1) nextLineBreak = buffer.length; 215 | var line = buffer.substring(this.lastLineBreak, nextLineBreak) 216 | var col = this.index - this.lastLineBreak; 217 | message += " at line " + this.line + " col " + col + ":\n\n"; 218 | message += " " + line + "\n" 219 | message += " " + Array(col).join(" ") + "^" 220 | return message; 221 | } else { 222 | return message + " at index " + (this.index - 1); 223 | } 224 | } 225 | 226 | 227 | function Parser(rules, start, options) { 228 | if (rules instanceof Grammar) { 229 | var grammar = rules; 230 | var options = start; 231 | } else { 232 | var grammar = Grammar.fromCompiled(rules, start); 233 | } 234 | this.grammar = grammar; 235 | 236 | // Read options 237 | this.options = { 238 | keepHistory: false, 239 | lexer: grammar.lexer || new StreamLexer, 240 | }; 241 | for (var key in (options || {})) { 242 | this.options[key] = options[key]; 243 | } 244 | 245 | // Setup lexer 246 | this.lexer = this.options.lexer; 247 | this.lexerState = undefined; 248 | 249 | // Setup a table 250 | var column = new Column(grammar, 0); 251 | var table = this.table = [column]; 252 | 253 | // I could be expecting anything. 254 | column.wants[grammar.start] = []; 255 | column.predict(grammar.start); 256 | // TODO what if start rule is nullable? 257 | column.process(); 258 | this.current = 0; // token index 259 | } 260 | 261 | // create a reserved token for indicating a parse fail 262 | Parser.fail = {}; 263 | 264 | Parser.prototype.feed = function(chunk) { 265 | var lexer = this.lexer; 266 | lexer.reset(chunk, this.lexerState); 267 | 268 | var token; 269 | while (token = lexer.next()) { 270 | // We add new states to table[current+1] 271 | var column = this.table[this.current]; 272 | 273 | // GC unused states 274 | if (!this.options.keepHistory) { 275 | delete this.table[this.current - 1]; 276 | } 277 | 278 | var n = this.current + 1; 279 | var nextColumn = new Column(this.grammar, n); 280 | this.table.push(nextColumn); 281 | 282 | // Advance all tokens that expect the symbol 283 | var literal = token.text !== undefined ? token.text : token.value; 284 | var value = lexer.constructor === StreamLexer ? token.value : token; 285 | var scannable = column.scannable; 286 | for (var w = scannable.length; w--; ) { 287 | var state = scannable[w]; 288 | var expect = state.rule.symbols[state.dot]; 289 | // Try to consume the token 290 | // either regex or literal 291 | if (expect.test ? expect.test(value) : 292 | expect.type ? expect.type === token.type 293 | : expect.literal === literal) { 294 | // Add it 295 | var next = state.nextState({data: value, token: token, isToken: true, reference: n - 1}); 296 | nextColumn.states.push(next); 297 | } 298 | } 299 | 300 | // Next, for each of the rules, we either 301 | // (a) complete it, and try to see if the reference row expected that 302 | // rule 303 | // (b) predict the next nonterminal it expects by adding that 304 | // nonterminal's start state 305 | // To prevent duplication, we also keep track of rules we have already 306 | // added 307 | 308 | nextColumn.process(); 309 | 310 | // If needed, throw an error: 311 | if (nextColumn.states.length === 0) { 312 | // No states at all! This is not good. 313 | // var err = new IFError(this.reportError(token), null, true); 314 | // err.offset = this.current; 315 | // err.token = token; 316 | throw new IFError(this.reportError(token)); 317 | } 318 | 319 | // maybe save lexer state 320 | if (this.options.keepHistory) { 321 | column.lexerState = lexer.save() 322 | } 323 | 324 | this.current++; 325 | } 326 | if (column) { 327 | this.lexerState = lexer.save() 328 | } 329 | 330 | // Incrementally keep track of results 331 | this.results = this.finish(); 332 | 333 | // Allow chaining, for whatever it's worth 334 | return this; 335 | }; 336 | 337 | Parser.prototype.reportError = function(token) { 338 | var lines = []; 339 | var tokenDisplay = (token.type ? token.type + " token: " : "") + JSON.stringify(token.value !== undefined ? token.value : token); 340 | lines.push(this.lexer.formatError(token, "Syntax error")); 341 | lines.push('Unexpected ' + tokenDisplay + '. Instead, I was expecting to see one of the following:\n'); 342 | var lastColumnIndex = this.table.length - 2; 343 | var lastColumn = this.table[lastColumnIndex]; 344 | var expectantStates = lastColumn.states 345 | .filter(function(state) { 346 | var nextSymbol = state.rule.symbols[state.dot]; 347 | return nextSymbol && typeof nextSymbol !== "string"; 348 | }); 349 | 350 | // Display a "state stack" for each expectant state 351 | // - which shows you how this state came to be, step by step. 352 | // If there is more than one derivation, we only display the first one. 353 | var stateStacks = expectantStates 354 | .map(function(state) { 355 | return this.buildFirstStateStack(state, []) || [state]; 356 | }, this); 357 | // Display each state that is expecting a terminal symbol next. 358 | stateStacks.forEach(function(stateStack) { 359 | var state = stateStack[0]; 360 | var nextSymbol = state.rule.symbols[state.dot]; 361 | var symbolDisplay = this.getSymbolDisplay(nextSymbol); 362 | lines.push('A ' + symbolDisplay + ' based on:'); 363 | this.displayStateStack(stateStack, lines); 364 | }, this); 365 | 366 | lines.push(""); 367 | return lines.join("\n"); 368 | }; 369 | 370 | Parser.prototype.displayStateStack = function(stateStack, lines) { 371 | var lastDisplay; 372 | var sameDisplayCount = 0; 373 | for (var j = 0; j < stateStack.length; j++) { 374 | var state = stateStack[j]; 375 | var display = state.rule.toString(state.dot); 376 | if (display === lastDisplay) { 377 | sameDisplayCount++; 378 | } else { 379 | if (sameDisplayCount > 0) { 380 | lines.push(' ⬆ ︎' + sameDisplayCount + ' more lines identical to this'); 381 | } 382 | sameDisplayCount = 0; 383 | lines.push(' ' + display); 384 | } 385 | lastDisplay = display; 386 | } 387 | }; 388 | 389 | Parser.prototype.getSymbolDisplay = function(symbol) { 390 | return getSymbolLongDisplay(symbol); 391 | }; 392 | 393 | /* 394 | Builds a the first state stack. You can think of a state stack as the call stack 395 | of the recursive-descent parser which the Nearley parse algorithm simulates. 396 | A state stack is represented as an array of state objects. Within a 397 | state stack, the first item of the array will be the starting 398 | state, with each successive item in the array going further back into history. 399 | 400 | This function needs to be given a starting state and an empty array representing 401 | the visited states, and it returns an single state stack. 402 | 403 | */ 404 | Parser.prototype.buildFirstStateStack = function(state, visited) { 405 | if (visited.indexOf(state) !== -1) { 406 | // Found cycle, return null 407 | // to eliminate this path from the results, because 408 | // we don't know how to display it meaningfully 409 | return null; 410 | } 411 | if (state.wantedBy.length === 0) { 412 | return [state]; 413 | } 414 | var prevState = state.wantedBy[0]; 415 | var childVisited = [state].concat(visited); 416 | var childResult = this.buildFirstStateStack(prevState, childVisited); 417 | if (childResult === null) { 418 | return null; 419 | } 420 | return [state].concat(childResult); 421 | }; 422 | 423 | Parser.prototype.save = function() { 424 | var column = this.table[this.current]; 425 | column.lexerState = this.lexerState; 426 | return column; 427 | }; 428 | 429 | Parser.prototype.restore = function(column) { 430 | var index = column.index; 431 | this.current = index; 432 | this.table[index] = column; 433 | this.table.splice(index + 1); 434 | this.lexerState = column.lexerState; 435 | 436 | // Incrementally keep track of results 437 | this.results = this.finish(); 438 | }; 439 | 440 | // nb. deprecated: use save/restore instead! 441 | Parser.prototype.rewind = function(index) { 442 | if (!this.options.keepHistory) { 443 | throw new IFError('set option `keepHistory` to enable rewinding') 444 | } 445 | // nb. recall column (table) indicies fall between token indicies. 446 | // col 0 -- token 0 -- col 1 447 | this.restore(this.table[index]); 448 | }; 449 | 450 | Parser.prototype.finish = function() { 451 | // Return the possible parsings 452 | var considerations = []; 453 | var start = this.grammar.start; 454 | var column = this.table[this.table.length - 1] 455 | column.states.forEach(function (t) { 456 | if (t.rule.name === start 457 | && t.dot === t.rule.symbols.length 458 | && t.reference === 0 459 | && t.data !== Parser.fail) { 460 | considerations.push(t); 461 | } 462 | }); 463 | return considerations.map(function(c) {return c.data; }); 464 | }; 465 | 466 | function getSymbolLongDisplay(symbol) { 467 | var type = typeof symbol; 468 | if (type === "string") { 469 | return symbol; 470 | } else if (type === "object") { 471 | if (symbol.literal) { 472 | return JSON.stringify(symbol.literal); 473 | } else if (symbol instanceof RegExp) { 474 | return 'character matching ' + symbol; 475 | } else if (symbol.type) { 476 | return symbol.type + ' token'; 477 | } else if (symbol.test) { 478 | return 'token matching ' + String(symbol.test); 479 | } else { 480 | throw new IFError('Unknown symbol type: ' + symbol); 481 | } 482 | } 483 | } 484 | 485 | function getSymbolShortDisplay(symbol) { 486 | var type = typeof symbol; 487 | if (type === "string") { 488 | return symbol; 489 | } else if (type === "object") { 490 | if (symbol.literal) { 491 | return JSON.stringify(symbol.literal); 492 | } else if (symbol instanceof RegExp) { 493 | return symbol.toString(); 494 | } else if (symbol.type) { 495 | return '%' + symbol.type; 496 | } else if (symbol.test) { 497 | return '<' + String(symbol.test) + '>'; 498 | } else { 499 | throw new IFError('Unknown symbol type: ' + symbol); 500 | } 501 | } 502 | } 503 | 504 | return { 505 | Parser: Parser, 506 | Grammar: Grammar, 507 | Rule: Rule, 508 | }; 509 | 510 | })); 511 | -------------------------------------------------------------------------------- /src/lib/showdown.min.js: -------------------------------------------------------------------------------- 1 | /*! showdown v 1.9.1 - 02-11-2019 */ 2 | (function(){function e(e){"use strict";var r={omitExtraWLInCodeBlocks:{defaultValue:!1,describe:"Omit the default extra whiteline added to code blocks",type:"boolean"},noHeaderId:{defaultValue:!1,describe:"Turn on/off generated header id",type:"boolean"},prefixHeaderId:{defaultValue:!1,describe:"Add a prefix to the generated header ids. Passing a string will prefix that string to the header id. Setting to true will add a generic 'section-' prefix",type:"string"},rawPrefixHeaderId:{defaultValue:!1,describe:'Setting this option to true will prevent showdown from modifying the prefix. This might result in malformed IDs (if, for instance, the " char is used in the prefix)',type:"boolean"},ghCompatibleHeaderId:{defaultValue:!1,describe:"Generate header ids compatible with github style (spaces are replaced with dashes, a bunch of non alphanumeric chars are removed)",type:"boolean"},rawHeaderId:{defaultValue:!1,describe:"Remove only spaces, ' and \" from generated header ids (including prefixes), replacing them with dashes (-). WARNING: This might result in malformed ids",type:"boolean"},headerLevelStart:{defaultValue:!1,describe:"The header blocks level start",type:"integer"},parseImgDimensions:{defaultValue:!1,describe:"Turn on/off image dimension parsing",type:"boolean"},simplifiedAutoLink:{defaultValue:!1,describe:"Turn on/off GFM autolink style",type:"boolean"},excludeTrailingPunctuationFromURLs:{defaultValue:!1,describe:"Excludes trailing punctuation from links generated with autoLinking",type:"boolean"},literalMidWordUnderscores:{defaultValue:!1,describe:"Parse midword underscores as literal underscores",type:"boolean"},literalMidWordAsterisks:{defaultValue:!1,describe:"Parse midword asterisks as literal asterisks",type:"boolean"},strikethrough:{defaultValue:!1,describe:"Turn on/off strikethrough support",type:"boolean"},tables:{defaultValue:!1,describe:"Turn on/off tables support",type:"boolean"},tablesHeaderId:{defaultValue:!1,describe:"Add an id to table headers",type:"boolean"},ghCodeBlocks:{defaultValue:!0,describe:"Turn on/off GFM fenced code blocks support",type:"boolean"},tasklists:{defaultValue:!1,describe:"Turn on/off GFM tasklist support",type:"boolean"},smoothLivePreview:{defaultValue:!1,describe:"Prevents weird effects in live previews due to incomplete input",type:"boolean"},smartIndentationFix:{defaultValue:!1,description:"Tries to smartly fix indentation in es6 strings",type:"boolean"},disableForced4SpacesIndentedSublists:{defaultValue:!1,description:"Disables the requirement of indenting nested sublists by 4 spaces",type:"boolean"},simpleLineBreaks:{defaultValue:!1,description:"Parses simple line breaks as
(GFM Style)",type:"boolean"},requireSpaceBeforeHeadingText:{defaultValue:!1,description:"Makes adding a space between `#` and the header text mandatory (GFM Style)",type:"boolean"},ghMentions:{defaultValue:!1,description:"Enables github @mentions",type:"boolean"},ghMentionsLink:{defaultValue:"https://github.com/{u}",description:"Changes the link generated by @mentions. Only applies if ghMentions option is enabled.",type:"string"},encodeEmails:{defaultValue:!0,description:"Encode e-mail addresses through the use of Character Entities, transforming ASCII e-mail addresses into its equivalent decimal entities",type:"boolean"},openLinksInNewWindow:{defaultValue:!1,description:"Open all links in new windows",type:"boolean"},backslashEscapesHTMLTags:{defaultValue:!1,description:"Support for HTML Tag escaping. ex:
foo
",type:"boolean"},emoji:{defaultValue:!1,description:"Enable emoji support. Ex: `this is a :smile: emoji`",type:"boolean"},underline:{defaultValue:!1,description:"Enable support for underline. Syntax is double or triple underscores: `__underline word__`. With this option enabled, underscores no longer parses into `` and ``",type:"boolean"},completeHTMLDocument:{defaultValue:!1,description:"Outputs a complete html document, including ``, `` and `` tags",type:"boolean"},metadata:{defaultValue:!1,description:"Enable support for document metadata (defined at the top of the document between `«««` and `»»»` or between `---` and `---`).",type:"boolean"},splitAdjacentBlockquotes:{defaultValue:!1,description:"Split adjacent blockquote blocks",type:"boolean"}};if(!1===e)return JSON.parse(JSON.stringify(r));var t={};for(var a in r)r.hasOwnProperty(a)&&(t[a]=r[a].defaultValue);return t}function r(e,r){"use strict";var t=r?"Error in "+r+" extension->":"Error in unnamed extension",n={valid:!0,error:""};a.helper.isArray(e)||(e=[e]);for(var s=0;s").replace(/&/g,"&")};var c=function(e,r,t,a){"use strict";var n,s,o,i,l,c=a||"",u=c.indexOf("g")>-1,d=new RegExp(r+"|"+t,"g"+c.replace(/g/g,"")),p=new RegExp(r,c.replace(/g/g,"")),h=[];do{for(n=0;o=d.exec(e);)if(p.test(o[0]))n++||(i=(s=d.lastIndex)-o[0].length);else if(n&&!--n){l=o.index+o[0].length;var _={left:{start:i,end:s},match:{start:s,end:o.index},right:{start:o.index,end:l},wholeMatch:{start:i,end:l}};if(h.push(_),!u)return h}}while(n&&(d.lastIndex=s));return h};a.helper.matchRecursiveRegExp=function(e,r,t,a){"use strict";for(var n=c(e,r,t,a),s=[],o=0;o0){var d=[];0!==i[0].wholeMatch.start&&d.push(e.slice(0,i[0].wholeMatch.start));for(var p=0;p=0?n+(t||0):n},a.helper.splitAtIndex=function(e,r){"use strict";if(!a.helper.isString(e))throw"InvalidArgumentError: first parameter of showdown.helper.regexIndexOf function must be a string";return[e.substring(0,r),e.substring(r)]},a.helper.encodeEmailAddress=function(e){"use strict";var r=[function(e){return"&#"+e.charCodeAt(0)+";"},function(e){return"&#x"+e.charCodeAt(0).toString(16)+";"},function(e){return e}];return e=e.replace(/./g,function(e){if("@"===e)e=r[Math.floor(2*Math.random())](e);else{var t=Math.random();e=t>.9?r[2](e):t>.45?r[1](e):r[0](e)}return e})},a.helper.padEnd=function(e,r,t){"use strict";return r>>=0,t=String(t||" "),e.length>r?String(e):((r-=e.length)>t.length&&(t+=t.repeat(r/t.length)),String(e)+t.slice(0,r))},"undefined"==typeof console&&(console={warn:function(e){"use strict";alert(e)},log:function(e){"use strict";alert(e)},error:function(e){"use strict";throw e}}),a.helper.regexes={asteriskDashAndColon:/([*_:~])/g},a.helper.emojis={"+1":"👍","-1":"👎",100:"💯",1234:"🔢","1st_place_medal":"🥇","2nd_place_medal":"🥈","3rd_place_medal":"🥉","8ball":"🎱",a:"🅰️",ab:"🆎",abc:"🔤",abcd:"🔡",accept:"🉑",aerial_tramway:"🚡",airplane:"✈️",alarm_clock:"⏰",alembic:"⚗️",alien:"👽",ambulance:"🚑",amphora:"🏺",anchor:"⚓️",angel:"👼",anger:"💢",angry:"😠",anguished:"😧",ant:"🐜",apple:"🍎",aquarius:"♒️",aries:"♈️",arrow_backward:"◀️",arrow_double_down:"⏬",arrow_double_up:"⏫",arrow_down:"⬇️",arrow_down_small:"🔽",arrow_forward:"▶️",arrow_heading_down:"⤵️",arrow_heading_up:"⤴️",arrow_left:"⬅️",arrow_lower_left:"↙️",arrow_lower_right:"↘️",arrow_right:"➡️",arrow_right_hook:"↪️",arrow_up:"⬆️",arrow_up_down:"↕️",arrow_up_small:"🔼",arrow_upper_left:"↖️",arrow_upper_right:"↗️",arrows_clockwise:"🔃",arrows_counterclockwise:"🔄",art:"🎨",articulated_lorry:"🚛",artificial_satellite:"🛰",astonished:"😲",athletic_shoe:"👟",atm:"🏧",atom_symbol:"⚛️",avocado:"🥑",b:"🅱️",baby:"👶",baby_bottle:"🍼",baby_chick:"🐤",baby_symbol:"🚼",back:"🔙",bacon:"🥓",badminton:"🏸",baggage_claim:"🛄",baguette_bread:"🥖",balance_scale:"⚖️",balloon:"🎈",ballot_box:"🗳",ballot_box_with_check:"☑️",bamboo:"🎍",banana:"🍌",bangbang:"‼️",bank:"🏦",bar_chart:"📊",barber:"💈",baseball:"⚾️",basketball:"🏀",basketball_man:"⛹️",basketball_woman:"⛹️‍♀️",bat:"🦇",bath:"🛀",bathtub:"🛁",battery:"🔋",beach_umbrella:"🏖",bear:"🐻",bed:"🛏",bee:"🐝",beer:"🍺",beers:"🍻",beetle:"🐞",beginner:"🔰",bell:"🔔",bellhop_bell:"🛎",bento:"🍱",biking_man:"🚴",bike:"🚲",biking_woman:"🚴‍♀️",bikini:"👙",biohazard:"☣️",bird:"🐦",birthday:"🎂",black_circle:"⚫️",black_flag:"🏴",black_heart:"🖤",black_joker:"🃏",black_large_square:"⬛️",black_medium_small_square:"◾️",black_medium_square:"◼️",black_nib:"✒️",black_small_square:"▪️",black_square_button:"🔲",blonde_man:"👱",blonde_woman:"👱‍♀️",blossom:"🌼",blowfish:"🐡",blue_book:"📘",blue_car:"🚙",blue_heart:"💙",blush:"😊",boar:"🐗",boat:"⛵️",bomb:"💣",book:"📖",bookmark:"🔖",bookmark_tabs:"📑",books:"📚",boom:"💥",boot:"👢",bouquet:"💐",bowing_man:"🙇",bow_and_arrow:"🏹",bowing_woman:"🙇‍♀️",bowling:"🎳",boxing_glove:"🥊",boy:"👦",bread:"🍞",bride_with_veil:"👰",bridge_at_night:"🌉",briefcase:"💼",broken_heart:"💔",bug:"🐛",building_construction:"🏗",bulb:"💡",bullettrain_front:"🚅",bullettrain_side:"🚄",burrito:"🌯",bus:"🚌",business_suit_levitating:"🕴",busstop:"🚏",bust_in_silhouette:"👤",busts_in_silhouette:"👥",butterfly:"🦋",cactus:"🌵",cake:"🍰",calendar:"📆",call_me_hand:"🤙",calling:"📲",camel:"🐫",camera:"📷",camera_flash:"📸",camping:"🏕",cancer:"♋️",candle:"🕯",candy:"🍬",canoe:"🛶",capital_abcd:"🔠",capricorn:"♑️",car:"🚗",card_file_box:"🗃",card_index:"📇",card_index_dividers:"🗂",carousel_horse:"🎠",carrot:"🥕",cat:"🐱",cat2:"🐈",cd:"💿",chains:"⛓",champagne:"🍾",chart:"💹",chart_with_downwards_trend:"📉",chart_with_upwards_trend:"📈",checkered_flag:"🏁",cheese:"🧀",cherries:"🍒",cherry_blossom:"🌸",chestnut:"🌰",chicken:"🐔",children_crossing:"🚸",chipmunk:"🐿",chocolate_bar:"🍫",christmas_tree:"🎄",church:"⛪️",cinema:"🎦",circus_tent:"🎪",city_sunrise:"🌇",city_sunset:"🌆",cityscape:"🏙",cl:"🆑",clamp:"🗜",clap:"👏",clapper:"🎬",classical_building:"🏛",clinking_glasses:"🥂",clipboard:"📋",clock1:"🕐",clock10:"🕙",clock1030:"🕥",clock11:"🕚",clock1130:"🕦",clock12:"🕛",clock1230:"🕧",clock130:"🕜",clock2:"🕑",clock230:"🕝",clock3:"🕒",clock330:"🕞",clock4:"🕓",clock430:"🕟",clock5:"🕔",clock530:"🕠",clock6:"🕕",clock630:"🕡",clock7:"🕖",clock730:"🕢",clock8:"🕗",clock830:"🕣",clock9:"🕘",clock930:"🕤",closed_book:"📕",closed_lock_with_key:"🔐",closed_umbrella:"🌂",cloud:"☁️",cloud_with_lightning:"🌩",cloud_with_lightning_and_rain:"⛈",cloud_with_rain:"🌧",cloud_with_snow:"🌨",clown_face:"🤡",clubs:"♣️",cocktail:"🍸",coffee:"☕️",coffin:"⚰️",cold_sweat:"😰",comet:"☄️",computer:"💻",computer_mouse:"🖱",confetti_ball:"🎊",confounded:"😖",confused:"😕",congratulations:"㊗️",construction:"🚧",construction_worker_man:"👷",construction_worker_woman:"👷‍♀️",control_knobs:"🎛",convenience_store:"🏪",cookie:"🍪",cool:"🆒",policeman:"👮",copyright:"©️",corn:"🌽",couch_and_lamp:"🛋",couple:"👫",couple_with_heart_woman_man:"💑",couple_with_heart_man_man:"👨‍❤️‍👨",couple_with_heart_woman_woman:"👩‍❤️‍👩",couplekiss_man_man:"👨‍❤️‍💋‍👨",couplekiss_man_woman:"💏",couplekiss_woman_woman:"👩‍❤️‍💋‍👩",cow:"🐮",cow2:"🐄",cowboy_hat_face:"🤠",crab:"🦀",crayon:"🖍",credit_card:"💳",crescent_moon:"🌙",cricket:"🏏",crocodile:"🐊",croissant:"🥐",crossed_fingers:"🤞",crossed_flags:"🎌",crossed_swords:"⚔️",crown:"👑",cry:"😢",crying_cat_face:"😿",crystal_ball:"🔮",cucumber:"🥒",cupid:"💘",curly_loop:"➰",currency_exchange:"💱",curry:"🍛",custard:"🍮",customs:"🛃",cyclone:"🌀",dagger:"🗡",dancer:"💃",dancing_women:"👯",dancing_men:"👯‍♂️",dango:"🍡",dark_sunglasses:"🕶",dart:"🎯",dash:"💨",date:"📅",deciduous_tree:"🌳",deer:"🦌",department_store:"🏬",derelict_house:"🏚",desert:"🏜",desert_island:"🏝",desktop_computer:"🖥",male_detective:"🕵️",diamond_shape_with_a_dot_inside:"💠",diamonds:"♦️",disappointed:"😞",disappointed_relieved:"😥",dizzy:"💫",dizzy_face:"😵",do_not_litter:"🚯",dog:"🐶",dog2:"🐕",dollar:"💵",dolls:"🎎",dolphin:"🐬",door:"🚪",doughnut:"🍩",dove:"🕊",dragon:"🐉",dragon_face:"🐲",dress:"👗",dromedary_camel:"🐪",drooling_face:"🤤",droplet:"💧",drum:"🥁",duck:"🦆",dvd:"📀","e-mail":"📧",eagle:"🦅",ear:"👂",ear_of_rice:"🌾",earth_africa:"🌍",earth_americas:"🌎",earth_asia:"🌏",egg:"🥚",eggplant:"🍆",eight_pointed_black_star:"✴️",eight_spoked_asterisk:"✳️",electric_plug:"🔌",elephant:"🐘",email:"✉️",end:"🔚",envelope_with_arrow:"📩",euro:"💶",european_castle:"🏰",european_post_office:"🏤",evergreen_tree:"🌲",exclamation:"❗️",expressionless:"😑",eye:"👁",eye_speech_bubble:"👁‍🗨",eyeglasses:"👓",eyes:"👀",face_with_head_bandage:"🤕",face_with_thermometer:"🤒",fist_oncoming:"👊",factory:"🏭",fallen_leaf:"🍂",family_man_woman_boy:"👪",family_man_boy:"👨‍👦",family_man_boy_boy:"👨‍👦‍👦",family_man_girl:"👨‍👧",family_man_girl_boy:"👨‍👧‍👦",family_man_girl_girl:"👨‍👧‍👧",family_man_man_boy:"👨‍👨‍👦",family_man_man_boy_boy:"👨‍👨‍👦‍👦",family_man_man_girl:"👨‍👨‍👧",family_man_man_girl_boy:"👨‍👨‍👧‍👦",family_man_man_girl_girl:"👨‍👨‍👧‍👧",family_man_woman_boy_boy:"👨‍👩‍👦‍👦",family_man_woman_girl:"👨‍👩‍👧",family_man_woman_girl_boy:"👨‍👩‍👧‍👦",family_man_woman_girl_girl:"👨‍👩‍👧‍👧",family_woman_boy:"👩‍👦",family_woman_boy_boy:"👩‍👦‍👦",family_woman_girl:"👩‍👧",family_woman_girl_boy:"👩‍👧‍👦",family_woman_girl_girl:"👩‍👧‍👧",family_woman_woman_boy:"👩‍👩‍👦",family_woman_woman_boy_boy:"👩‍👩‍👦‍👦",family_woman_woman_girl:"👩‍👩‍👧",family_woman_woman_girl_boy:"👩‍👩‍👧‍👦",family_woman_woman_girl_girl:"👩‍👩‍👧‍👧",fast_forward:"⏩",fax:"📠",fearful:"😨",feet:"🐾",female_detective:"🕵️‍♀️",ferris_wheel:"🎡",ferry:"⛴",field_hockey:"🏑",file_cabinet:"🗄",file_folder:"📁",film_projector:"📽",film_strip:"🎞",fire:"🔥",fire_engine:"🚒",fireworks:"🎆",first_quarter_moon:"🌓",first_quarter_moon_with_face:"🌛",fish:"🐟",fish_cake:"🍥",fishing_pole_and_fish:"🎣",fist_raised:"✊",fist_left:"🤛",fist_right:"🤜",flags:"🎏",flashlight:"🔦",fleur_de_lis:"⚜️",flight_arrival:"🛬",flight_departure:"🛫",floppy_disk:"💾",flower_playing_cards:"🎴",flushed:"😳",fog:"🌫",foggy:"🌁",football:"🏈",footprints:"👣",fork_and_knife:"🍴",fountain:"⛲️",fountain_pen:"🖋",four_leaf_clover:"🍀",fox_face:"🦊",framed_picture:"🖼",free:"🆓",fried_egg:"🍳",fried_shrimp:"🍤",fries:"🍟",frog:"🐸",frowning:"😦",frowning_face:"☹️",frowning_man:"🙍‍♂️",frowning_woman:"🙍",middle_finger:"🖕",fuelpump:"⛽️",full_moon:"🌕",full_moon_with_face:"🌝",funeral_urn:"⚱️",game_die:"🎲",gear:"⚙️",gem:"💎",gemini:"♊️",ghost:"👻",gift:"🎁",gift_heart:"💝",girl:"👧",globe_with_meridians:"🌐",goal_net:"🥅",goat:"🐐",golf:"⛳️",golfing_man:"🏌️",golfing_woman:"🏌️‍♀️",gorilla:"🦍",grapes:"🍇",green_apple:"🍏",green_book:"📗",green_heart:"💚",green_salad:"🥗",grey_exclamation:"❕",grey_question:"❔",grimacing:"😬",grin:"😁",grinning:"😀",guardsman:"💂",guardswoman:"💂‍♀️",guitar:"🎸",gun:"🔫",haircut_woman:"💇",haircut_man:"💇‍♂️",hamburger:"🍔",hammer:"🔨",hammer_and_pick:"⚒",hammer_and_wrench:"🛠",hamster:"🐹",hand:"✋",handbag:"👜",handshake:"🤝",hankey:"💩",hatched_chick:"🐥",hatching_chick:"🐣",headphones:"🎧",hear_no_evil:"🙉",heart:"❤️",heart_decoration:"💟",heart_eyes:"😍",heart_eyes_cat:"😻",heartbeat:"💓",heartpulse:"💗",hearts:"♥️",heavy_check_mark:"✔️",heavy_division_sign:"➗",heavy_dollar_sign:"💲",heavy_heart_exclamation:"❣️",heavy_minus_sign:"➖",heavy_multiplication_x:"✖️",heavy_plus_sign:"➕",helicopter:"🚁",herb:"🌿",hibiscus:"🌺",high_brightness:"🔆",high_heel:"👠",hocho:"🔪",hole:"🕳",honey_pot:"🍯",horse:"🐴",horse_racing:"🏇",hospital:"🏥",hot_pepper:"🌶",hotdog:"🌭",hotel:"🏨",hotsprings:"♨️",hourglass:"⌛️",hourglass_flowing_sand:"⏳",house:"🏠",house_with_garden:"🏡",houses:"🏘",hugs:"🤗",hushed:"😯",ice_cream:"🍨",ice_hockey:"🏒",ice_skate:"⛸",icecream:"🍦",id:"🆔",ideograph_advantage:"🉐",imp:"👿",inbox_tray:"📥",incoming_envelope:"📨",tipping_hand_woman:"💁",information_source:"ℹ️",innocent:"😇",interrobang:"⁉️",iphone:"📱",izakaya_lantern:"🏮",jack_o_lantern:"🎃",japan:"🗾",japanese_castle:"🏯",japanese_goblin:"👺",japanese_ogre:"👹",jeans:"👖",joy:"😂",joy_cat:"😹",joystick:"🕹",kaaba:"🕋",key:"🔑",keyboard:"⌨️",keycap_ten:"🔟",kick_scooter:"🛴",kimono:"👘",kiss:"💋",kissing:"😗",kissing_cat:"😽",kissing_closed_eyes:"😚",kissing_heart:"😘",kissing_smiling_eyes:"😙",kiwi_fruit:"🥝",koala:"🐨",koko:"🈁",label:"🏷",large_blue_circle:"🔵",large_blue_diamond:"🔷",large_orange_diamond:"🔶",last_quarter_moon:"🌗",last_quarter_moon_with_face:"🌜",latin_cross:"✝️",laughing:"😆",leaves:"🍃",ledger:"📒",left_luggage:"🛅",left_right_arrow:"↔️",leftwards_arrow_with_hook:"↩️",lemon:"🍋",leo:"♌️",leopard:"🐆",level_slider:"🎚",libra:"♎️",light_rail:"🚈",link:"🔗",lion:"🦁",lips:"👄",lipstick:"💄",lizard:"🦎",lock:"🔒",lock_with_ink_pen:"🔏",lollipop:"🍭",loop:"➿",loud_sound:"🔊",loudspeaker:"📢",love_hotel:"🏩",love_letter:"💌",low_brightness:"🔅",lying_face:"🤥",m:"Ⓜ️",mag:"🔍",mag_right:"🔎",mahjong:"🀄️",mailbox:"📫",mailbox_closed:"📪",mailbox_with_mail:"📬",mailbox_with_no_mail:"📭",man:"👨",man_artist:"👨‍🎨",man_astronaut:"👨‍🚀",man_cartwheeling:"🤸‍♂️",man_cook:"👨‍🍳",man_dancing:"🕺",man_facepalming:"🤦‍♂️",man_factory_worker:"👨‍🏭",man_farmer:"👨‍🌾",man_firefighter:"👨‍🚒",man_health_worker:"👨‍⚕️",man_in_tuxedo:"🤵",man_judge:"👨‍⚖️",man_juggling:"🤹‍♂️",man_mechanic:"👨‍🔧",man_office_worker:"👨‍💼",man_pilot:"👨‍✈️",man_playing_handball:"🤾‍♂️",man_playing_water_polo:"🤽‍♂️",man_scientist:"👨‍🔬",man_shrugging:"🤷‍♂️",man_singer:"👨‍🎤",man_student:"👨‍🎓",man_teacher:"👨‍🏫",man_technologist:"👨‍💻",man_with_gua_pi_mao:"👲",man_with_turban:"👳",tangerine:"🍊",mans_shoe:"👞",mantelpiece_clock:"🕰",maple_leaf:"🍁",martial_arts_uniform:"🥋",mask:"😷",massage_woman:"💆",massage_man:"💆‍♂️",meat_on_bone:"🍖",medal_military:"🎖",medal_sports:"🏅",mega:"📣",melon:"🍈",memo:"📝",men_wrestling:"🤼‍♂️",menorah:"🕎",mens:"🚹",metal:"🤘",metro:"🚇",microphone:"🎤",microscope:"🔬",milk_glass:"🥛",milky_way:"🌌",minibus:"🚐",minidisc:"💽",mobile_phone_off:"📴",money_mouth_face:"🤑",money_with_wings:"💸",moneybag:"💰",monkey:"🐒",monkey_face:"🐵",monorail:"🚝",moon:"🌔",mortar_board:"🎓",mosque:"🕌",motor_boat:"🛥",motor_scooter:"🛵",motorcycle:"🏍",motorway:"🛣",mount_fuji:"🗻",mountain:"⛰",mountain_biking_man:"🚵",mountain_biking_woman:"🚵‍♀️",mountain_cableway:"🚠",mountain_railway:"🚞",mountain_snow:"🏔",mouse:"🐭",mouse2:"🐁",movie_camera:"🎥",moyai:"🗿",mrs_claus:"🤶",muscle:"💪",mushroom:"🍄",musical_keyboard:"🎹",musical_note:"🎵",musical_score:"🎼",mute:"🔇",nail_care:"💅",name_badge:"📛",national_park:"🏞",nauseated_face:"🤢",necktie:"👔",negative_squared_cross_mark:"❎",nerd_face:"🤓",neutral_face:"😐",new:"🆕",new_moon:"🌑",new_moon_with_face:"🌚",newspaper:"📰",newspaper_roll:"🗞",next_track_button:"⏭",ng:"🆖",no_good_man:"🙅‍♂️",no_good_woman:"🙅",night_with_stars:"🌃",no_bell:"🔕",no_bicycles:"🚳",no_entry:"⛔️",no_entry_sign:"🚫",no_mobile_phones:"📵",no_mouth:"😶",no_pedestrians:"🚷",no_smoking:"🚭","non-potable_water":"🚱",nose:"👃",notebook:"📓",notebook_with_decorative_cover:"📔",notes:"🎶",nut_and_bolt:"🔩",o:"⭕️",o2:"🅾️",ocean:"🌊",octopus:"🐙",oden:"🍢",office:"🏢",oil_drum:"🛢",ok:"🆗",ok_hand:"👌",ok_man:"🙆‍♂️",ok_woman:"🙆",old_key:"🗝",older_man:"👴",older_woman:"👵",om:"🕉",on:"🔛",oncoming_automobile:"🚘",oncoming_bus:"🚍",oncoming_police_car:"🚔",oncoming_taxi:"🚖",open_file_folder:"📂",open_hands:"👐",open_mouth:"😮",open_umbrella:"☂️",ophiuchus:"⛎",orange_book:"📙",orthodox_cross:"☦️",outbox_tray:"📤",owl:"🦉",ox:"🐂",package:"📦",page_facing_up:"📄",page_with_curl:"📃",pager:"📟",paintbrush:"🖌",palm_tree:"🌴",pancakes:"🥞",panda_face:"🐼",paperclip:"📎",paperclips:"🖇",parasol_on_ground:"⛱",parking:"🅿️",part_alternation_mark:"〽️",partly_sunny:"⛅️",passenger_ship:"🛳",passport_control:"🛂",pause_button:"⏸",peace_symbol:"☮️",peach:"🍑",peanuts:"🥜",pear:"🍐",pen:"🖊",pencil2:"✏️",penguin:"🐧",pensive:"😔",performing_arts:"🎭",persevere:"😣",person_fencing:"🤺",pouting_woman:"🙎",phone:"☎️",pick:"⛏",pig:"🐷",pig2:"🐖",pig_nose:"🐽",pill:"💊",pineapple:"🍍",ping_pong:"🏓",pisces:"♓️",pizza:"🍕",place_of_worship:"🛐",plate_with_cutlery:"🍽",play_or_pause_button:"⏯",point_down:"👇",point_left:"👈",point_right:"👉",point_up:"☝️",point_up_2:"👆",police_car:"🚓",policewoman:"👮‍♀️",poodle:"🐩",popcorn:"🍿",post_office:"🏣",postal_horn:"📯",postbox:"📮",potable_water:"🚰",potato:"🥔",pouch:"👝",poultry_leg:"🍗",pound:"💷",rage:"😡",pouting_cat:"😾",pouting_man:"🙎‍♂️",pray:"🙏",prayer_beads:"📿",pregnant_woman:"🤰",previous_track_button:"⏮",prince:"🤴",princess:"👸",printer:"🖨",purple_heart:"💜",purse:"👛",pushpin:"📌",put_litter_in_its_place:"🚮",question:"❓",rabbit:"🐰",rabbit2:"🐇",racehorse:"🐎",racing_car:"🏎",radio:"📻",radio_button:"🔘",radioactive:"☢️",railway_car:"🚃",railway_track:"🛤",rainbow:"🌈",rainbow_flag:"🏳️‍🌈",raised_back_of_hand:"🤚",raised_hand_with_fingers_splayed:"🖐",raised_hands:"🙌",raising_hand_woman:"🙋",raising_hand_man:"🙋‍♂️",ram:"🐏",ramen:"🍜",rat:"🐀",record_button:"⏺",recycle:"♻️",red_circle:"🔴",registered:"®️",relaxed:"☺️",relieved:"😌",reminder_ribbon:"🎗",repeat:"🔁",repeat_one:"🔂",rescue_worker_helmet:"⛑",restroom:"🚻",revolving_hearts:"💞",rewind:"⏪",rhinoceros:"🦏",ribbon:"🎀",rice:"🍚",rice_ball:"🍙",rice_cracker:"🍘",rice_scene:"🎑",right_anger_bubble:"🗯",ring:"💍",robot:"🤖",rocket:"🚀",rofl:"🤣",roll_eyes:"🙄",roller_coaster:"🎢",rooster:"🐓",rose:"🌹",rosette:"🏵",rotating_light:"🚨",round_pushpin:"📍",rowing_man:"🚣",rowing_woman:"🚣‍♀️",rugby_football:"🏉",running_man:"🏃",running_shirt_with_sash:"🎽",running_woman:"🏃‍♀️",sa:"🈂️",sagittarius:"♐️",sake:"🍶",sandal:"👡",santa:"🎅",satellite:"📡",saxophone:"🎷",school:"🏫",school_satchel:"🎒",scissors:"✂️",scorpion:"🦂",scorpius:"♏️",scream:"😱",scream_cat:"🙀",scroll:"📜",seat:"💺",secret:"㊙️",see_no_evil:"🙈",seedling:"🌱",selfie:"🤳",shallow_pan_of_food:"🥘",shamrock:"☘️",shark:"🦈",shaved_ice:"🍧",sheep:"🐑",shell:"🐚",shield:"🛡",shinto_shrine:"⛩",ship:"🚢",shirt:"👕",shopping:"🛍",shopping_cart:"🛒",shower:"🚿",shrimp:"🦐",signal_strength:"📶",six_pointed_star:"🔯",ski:"🎿",skier:"⛷",skull:"💀",skull_and_crossbones:"☠️",sleeping:"😴",sleeping_bed:"🛌",sleepy:"😪",slightly_frowning_face:"🙁",slightly_smiling_face:"🙂",slot_machine:"🎰",small_airplane:"🛩",small_blue_diamond:"🔹",small_orange_diamond:"🔸",small_red_triangle:"🔺",small_red_triangle_down:"🔻",smile:"😄",smile_cat:"😸",smiley:"😃",smiley_cat:"😺",smiling_imp:"😈",smirk:"😏",smirk_cat:"😼",smoking:"🚬",snail:"🐌",snake:"🐍",sneezing_face:"🤧",snowboarder:"🏂",snowflake:"❄️",snowman:"⛄️",snowman_with_snow:"☃️",sob:"😭",soccer:"⚽️",soon:"🔜",sos:"🆘",sound:"🔉",space_invader:"👾",spades:"♠️",spaghetti:"🍝",sparkle:"❇️",sparkler:"🎇",sparkles:"✨",sparkling_heart:"💖",speak_no_evil:"🙊",speaker:"🔈",speaking_head:"🗣",speech_balloon:"💬",speedboat:"🚤",spider:"🕷",spider_web:"🕸",spiral_calendar:"🗓",spiral_notepad:"🗒",spoon:"🥄",squid:"🦑",stadium:"🏟",star:"⭐️",star2:"🌟",star_and_crescent:"☪️",star_of_david:"✡️",stars:"🌠",station:"🚉",statue_of_liberty:"🗽",steam_locomotive:"🚂",stew:"🍲",stop_button:"⏹",stop_sign:"🛑",stopwatch:"⏱",straight_ruler:"📏",strawberry:"🍓",stuck_out_tongue:"😛",stuck_out_tongue_closed_eyes:"😝",stuck_out_tongue_winking_eye:"😜",studio_microphone:"🎙",stuffed_flatbread:"🥙",sun_behind_large_cloud:"🌥",sun_behind_rain_cloud:"🌦",sun_behind_small_cloud:"🌤",sun_with_face:"🌞",sunflower:"🌻",sunglasses:"😎",sunny:"☀️",sunrise:"🌅",sunrise_over_mountains:"🌄",surfing_man:"🏄",surfing_woman:"🏄‍♀️",sushi:"🍣",suspension_railway:"🚟",sweat:"😓",sweat_drops:"💦",sweat_smile:"😅",sweet_potato:"🍠",swimming_man:"🏊",swimming_woman:"🏊‍♀️",symbols:"🔣",synagogue:"🕍",syringe:"💉",taco:"🌮",tada:"🎉",tanabata_tree:"🎋",taurus:"♉️",taxi:"🚕",tea:"🍵",telephone_receiver:"📞",telescope:"🔭",tennis:"🎾",tent:"⛺️",thermometer:"🌡",thinking:"🤔",thought_balloon:"💭",ticket:"🎫",tickets:"🎟",tiger:"🐯",tiger2:"🐅",timer_clock:"⏲",tipping_hand_man:"💁‍♂️",tired_face:"😫",tm:"™️",toilet:"🚽",tokyo_tower:"🗼",tomato:"🍅",tongue:"👅",top:"🔝",tophat:"🎩",tornado:"🌪",trackball:"🖲",tractor:"🚜",traffic_light:"🚥",train:"🚋",train2:"🚆",tram:"🚊",triangular_flag_on_post:"🚩",triangular_ruler:"📐",trident:"🔱",triumph:"😤",trolleybus:"🚎",trophy:"🏆",tropical_drink:"🍹",tropical_fish:"🐠",truck:"🚚",trumpet:"🎺",tulip:"🌷",tumbler_glass:"🥃",turkey:"🦃",turtle:"🐢",tv:"📺",twisted_rightwards_arrows:"🔀",two_hearts:"💕",two_men_holding_hands:"👬",two_women_holding_hands:"👭",u5272:"🈹",u5408:"🈴",u55b6:"🈺",u6307:"🈯️",u6708:"🈷️",u6709:"🈶",u6e80:"🈵",u7121:"🈚️",u7533:"🈸",u7981:"🈲",u7a7a:"🈳",umbrella:"☔️",unamused:"😒",underage:"🔞",unicorn:"🦄",unlock:"🔓",up:"🆙",upside_down_face:"🙃",v:"✌️",vertical_traffic_light:"🚦",vhs:"📼",vibration_mode:"📳",video_camera:"📹",video_game:"🎮",violin:"🎻",virgo:"♍️",volcano:"🌋",volleyball:"🏐",vs:"🆚",vulcan_salute:"🖖",walking_man:"🚶",walking_woman:"🚶‍♀️",waning_crescent_moon:"🌘",waning_gibbous_moon:"🌖",warning:"⚠️",wastebasket:"🗑",watch:"⌚️",water_buffalo:"🐃",watermelon:"🍉",wave:"👋",wavy_dash:"〰️",waxing_crescent_moon:"🌒",wc:"🚾",weary:"😩",wedding:"💒",weight_lifting_man:"🏋️",weight_lifting_woman:"🏋️‍♀️",whale:"🐳",whale2:"🐋",wheel_of_dharma:"☸️",wheelchair:"♿️",white_check_mark:"✅",white_circle:"⚪️",white_flag:"🏳️",white_flower:"💮",white_large_square:"⬜️",white_medium_small_square:"◽️",white_medium_square:"◻️",white_small_square:"▫️",white_square_button:"🔳",wilted_flower:"🥀",wind_chime:"🎐",wind_face:"🌬",wine_glass:"🍷",wink:"😉",wolf:"🐺",woman:"👩",woman_artist:"👩‍🎨",woman_astronaut:"👩‍🚀",woman_cartwheeling:"🤸‍♀️",woman_cook:"👩‍🍳",woman_facepalming:"🤦‍♀️",woman_factory_worker:"👩‍🏭",woman_farmer:"👩‍🌾",woman_firefighter:"👩‍🚒",woman_health_worker:"👩‍⚕️",woman_judge:"👩‍⚖️",woman_juggling:"🤹‍♀️",woman_mechanic:"👩‍🔧",woman_office_worker:"👩‍💼",woman_pilot:"👩‍✈️",woman_playing_handball:"🤾‍♀️",woman_playing_water_polo:"🤽‍♀️",woman_scientist:"👩‍🔬",woman_shrugging:"🤷‍♀️",woman_singer:"👩‍🎤",woman_student:"👩‍🎓",woman_teacher:"👩‍🏫",woman_technologist:"👩‍💻",woman_with_turban:"👳‍♀️",womans_clothes:"👚",womans_hat:"👒",women_wrestling:"🤼‍♀️",womens:"🚺",world_map:"🗺",worried:"😟",wrench:"🔧",writing_hand:"✍️",x:"❌",yellow_heart:"💛",yen:"💴",yin_yang:"☯️",yum:"😋",zap:"⚡️",zipper_mouth_face:"🤐",zzz:"💤",octocat:':octocat:',showdown:"S"},a.Converter=function(e){"use strict";function t(e,t){if(t=t||null,a.helper.isString(e)){if(e=a.helper.stdExtName(e),t=e,a.extensions[e])return console.warn("DEPRECATION WARNING: "+e+" is an old extension that uses a deprecated loading method.Please inform the developer that the extension should be updated!"),void function(e,t){"function"==typeof e&&(e=e(new a.Converter));a.helper.isArray(e)||(e=[e]);var n=r(e,t);if(!n.valid)throw Error(n.error);for(var s=0;s[ \t]+¨NBSP;<"),!r){if(!window||!window.document)throw new Error("HTMLParser is undefined. If in a webworker or nodejs environment, you need to provide a WHATWG DOM and HTML such as JSDOM");r=window.document}var n=r.createElement("div");n.innerHTML=e;var s={preList:function(e){for(var r=e.querySelectorAll("pre"),t=[],n=0;n'}else t.push(r[n].innerHTML),r[n].innerHTML="",r[n].setAttribute("prenum",n.toString());return t}(n)};t(n);for(var o=n.childNodes,i="",l=0;l? ?(['"].*['"])?\)$/m)>-1)o="";else if(!o){if(s||(s=n.toLowerCase().replace(/ ?\n/g," ")),o="#"+s,a.helper.isUndefined(t.gUrls[s]))return e;o=t.gUrls[s],a.helper.isUndefined(t.gTitles[s])||(c=t.gTitles[s])}var u='"};return e=(e=t.converter._dispatch("anchors.before",e,r,t)).replace(/\[((?:\[[^\]]*]|[^\[\]])*)] ?(?:\n *)?\[(.*?)]()()()()/g,n),e=e.replace(/\[((?:\[[^\]]*]|[^\[\]])*)]()[ \t]*\([ \t]?<([^>]*)>(?:[ \t]*((["'])([^"]*?)\5))?[ \t]?\)/g,n),e=e.replace(/\[((?:\[[^\]]*]|[^\[\]])*)]()[ \t]*\([ \t]??(?:[ \t]*((["'])([^"]*?)\5))?[ \t]?\)/g,n),e=e.replace(/\[([^\[\]]+)]()()()()()/g,n),r.ghMentions&&(e=e.replace(/(^|\s)(\\)?(@([a-z\d]+(?:[a-z\d.-]+?[a-z\d]+)*))/gim,function(e,t,n,s,o){if("\\"===n)return t+s;if(!a.helper.isString(r.ghMentionsLink))throw new Error("ghMentionsLink option must be a string");var i=r.ghMentionsLink.replace(/\{u}/g,o),l="";return r.openLinksInNewWindow&&(l=' rel="noopener noreferrer" target="¨E95Eblank"'),t+'"+s+""})),e=t.converter._dispatch("anchors.after",e,r,t)});var u=/([*~_]+|\b)(((https?|ftp|dict):\/\/|www\.)[^'">\s]+?\.[^'">\s]+?)()(\1)?(?=\s|$)(?!["<>])/gi,d=/([*~_]+|\b)(((https?|ftp|dict):\/\/|www\.)[^'">\s]+\.[^'">\s]+?)([.!?,()\[\]])?(\1)?(?=\s|$)(?!["<>])/gi,p=/()<(((https?|ftp|dict):\/\/|www\.)[^'">\s]+)()>()/gi,h=/(^|\s)(?:mailto:)?([A-Za-z0-9!#$%&'*+-/=?^_`{|}~.]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)(?=$|\s)/gim,_=/<()(?:mailto:)?([-.\w]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi,g=function(e){"use strict";return function(r,t,n,s,o,i,l){var c=n=n.replace(a.helper.regexes.asteriskDashAndColon,a.helper.escapeCharactersCallback),u="",d="",p=t||"",h=l||"";return/^www\./i.test(n)&&(n=n.replace(/^www\./i,"http://www.")),e.excludeTrailingPunctuationFromURLs&&i&&(u=i),e.openLinksInNewWindow&&(d=' rel="noopener noreferrer" target="¨E95Eblank"'),p+'"+c+""+u+h}},m=function(e,r){"use strict";return function(t,n,s){var o="mailto:";return n=n||"",s=a.subParser("unescapeSpecialChars")(s,e,r),e.encodeEmails?(o=a.helper.encodeEmailAddress(o+s),s=a.helper.encodeEmailAddress(s)):o+=s,n+''+s+""}};a.subParser("autoLinks",function(e,r,t){"use strict";return e=t.converter._dispatch("autoLinks.before",e,r,t),e=e.replace(p,g(r)),e=e.replace(_,m(r,t)),e=t.converter._dispatch("autoLinks.after",e,r,t)}),a.subParser("simplifiedAutoLinks",function(e,r,t){"use strict";return r.simplifiedAutoLink?(e=t.converter._dispatch("simplifiedAutoLinks.before",e,r,t),e=r.excludeTrailingPunctuationFromURLs?e.replace(d,g(r)):e.replace(u,g(r)),e=e.replace(h,m(r,t)),e=t.converter._dispatch("simplifiedAutoLinks.after",e,r,t)):e}),a.subParser("blockGamut",function(e,r,t){"use strict";return e=t.converter._dispatch("blockGamut.before",e,r,t),e=a.subParser("blockQuotes")(e,r,t),e=a.subParser("headers")(e,r,t),e=a.subParser("horizontalRule")(e,r,t),e=a.subParser("lists")(e,r,t),e=a.subParser("codeBlocks")(e,r,t),e=a.subParser("tables")(e,r,t),e=a.subParser("hashHTMLBlocks")(e,r,t),e=a.subParser("paragraphs")(e,r,t),e=t.converter._dispatch("blockGamut.after",e,r,t)}),a.subParser("blockQuotes",function(e,r,t){"use strict";e=t.converter._dispatch("blockQuotes.before",e,r,t),e+="\n\n";var n=/(^ {0,3}>[ \t]?.+\n(.+\n)*\n*)+/gm;return r.splitAdjacentBlockquotes&&(n=/^ {0,3}>[\s\S]*?(?:\n\n)/gm),e=e.replace(n,function(e){return e=e.replace(/^[ \t]*>[ \t]?/gm,""),e=e.replace(/¨0/g,""),e=e.replace(/^[ \t]+$/gm,""),e=a.subParser("githubCodeBlocks")(e,r,t),e=a.subParser("blockGamut")(e,r,t),e=e.replace(/(^|\n)/g,"$1 "),e=e.replace(/(\s*
[^\r]+?<\/pre>)/gm,function(e,r){var t=r;return t=t.replace(/^  /gm,"¨0"),t=t.replace(/¨0/g,"")}),a.subParser("hashBlock")("
\n"+e+"\n
",r,t)}),e=t.converter._dispatch("blockQuotes.after",e,r,t)}),a.subParser("codeBlocks",function(e,r,t){"use strict";e=t.converter._dispatch("codeBlocks.before",e,r,t);return e=(e+="¨0").replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=¨0))/g,function(e,n,s){var o=n,i=s,l="\n";return o=a.subParser("outdent")(o,r,t),o=a.subParser("encodeCode")(o,r,t),o=a.subParser("detab")(o,r,t),o=o.replace(/^\n+/g,""),o=o.replace(/\n+$/g,""),r.omitExtraWLInCodeBlocks&&(l=""),o="
"+o+l+"
",a.subParser("hashBlock")(o,r,t)+i}),e=e.replace(/¨0/,""),e=t.converter._dispatch("codeBlocks.after",e,r,t)}),a.subParser("codeSpans",function(e,r,t){"use strict";return void 0===(e=t.converter._dispatch("codeSpans.before",e,r,t))&&(e=""),e=e.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,function(e,n,s,o){var i=o;return i=i.replace(/^([ \t]*)/g,""),i=i.replace(/[ \t]*$/g,""),i=a.subParser("encodeCode")(i,r,t),i=n+""+i+"",i=a.subParser("hashHTMLSpans")(i,r,t)}),e=t.converter._dispatch("codeSpans.after",e,r,t)}),a.subParser("completeHTMLDocument",function(e,r,t){"use strict";if(!r.completeHTMLDocument)return e;e=t.converter._dispatch("completeHTMLDocument.before",e,r,t);var a="html",n="\n",s="",o='\n',i="",l="";void 0!==t.metadata.parsed.doctype&&(n="\n","html"!==(a=t.metadata.parsed.doctype.toString().toLowerCase())&&"html5"!==a||(o=''));for(var c in t.metadata.parsed)if(t.metadata.parsed.hasOwnProperty(c))switch(c.toLowerCase()){case"doctype":break;case"title":s=""+t.metadata.parsed.title+"\n";break;case"charset":o="html"===a||"html5"===a?'\n':'\n';break;case"language":case"lang":i=' lang="'+t.metadata.parsed[c]+'"',l+='\n';break;default:l+='\n'}return e=n+"\n\n"+s+o+l+"\n\n"+e.trim()+"\n\n",e=t.converter._dispatch("completeHTMLDocument.after",e,r,t)}),a.subParser("detab",function(e,r,t){"use strict";return e=t.converter._dispatch("detab.before",e,r,t),e=e.replace(/\t(?=\t)/g," "),e=e.replace(/\t/g,"¨A¨B"),e=e.replace(/¨B(.+?)¨A/g,function(e,r){for(var t=r,a=4-t.length%4,n=0;n/g,">"),e=t.converter._dispatch("encodeAmpsAndAngles.after",e,r,t)}),a.subParser("encodeBackslashEscapes",function(e,r,t){"use strict";return e=t.converter._dispatch("encodeBackslashEscapes.before",e,r,t),e=e.replace(/\\(\\)/g,a.helper.escapeCharactersCallback),e=e.replace(/\\([`*_{}\[\]()>#+.!~=|-])/g,a.helper.escapeCharactersCallback),e=t.converter._dispatch("encodeBackslashEscapes.after",e,r,t)}),a.subParser("encodeCode",function(e,r,t){"use strict";return e=t.converter._dispatch("encodeCode.before",e,r,t),e=e.replace(/&/g,"&").replace(//g,">").replace(/([*_{}\[\]\\=~-])/g,a.helper.escapeCharactersCallback),e=t.converter._dispatch("encodeCode.after",e,r,t)}),a.subParser("escapeSpecialCharsWithinTagAttributes",function(e,r,t){"use strict";return e=(e=t.converter._dispatch("escapeSpecialCharsWithinTagAttributes.before",e,r,t)).replace(/<\/?[a-z\d_:-]+(?:[\s]+[\s\S]+?)?>/gi,function(e){return e.replace(/(.)<\/?code>(?=.)/g,"$1`").replace(/([\\`*_~=|])/g,a.helper.escapeCharactersCallback)}),e=e.replace(/-]|-[^>])(?:[^-]|-[^-])*)--)>/gi,function(e){return e.replace(/([\\`*_~=|])/g,a.helper.escapeCharactersCallback)}),e=t.converter._dispatch("escapeSpecialCharsWithinTagAttributes.after",e,r,t)}),a.subParser("githubCodeBlocks",function(e,r,t){"use strict";return r.ghCodeBlocks?(e=t.converter._dispatch("githubCodeBlocks.before",e,r,t),e+="¨0",e=e.replace(/(?:^|\n)(?: {0,3})(```+|~~~+)(?: *)([^\s`~]*)\n([\s\S]*?)\n(?: {0,3})\1/g,function(e,n,s,o){var i=r.omitExtraWLInCodeBlocks?"":"\n";return o=a.subParser("encodeCode")(o,r,t),o=a.subParser("detab")(o,r,t),o=o.replace(/^\n+/g,""),o=o.replace(/\n+$/g,""),o="
"+o+i+"
",o=a.subParser("hashBlock")(o,r,t),"\n\n¨G"+(t.ghCodeBlocks.push({text:e,codeblock:o})-1)+"G\n\n"}),e=e.replace(/¨0/,""),t.converter._dispatch("githubCodeBlocks.after",e,r,t)):e}),a.subParser("hashBlock",function(e,r,t){"use strict";return e=t.converter._dispatch("hashBlock.before",e,r,t),e=e.replace(/(^\n+|\n+$)/g,""),e="\n\n¨K"+(t.gHtmlBlocks.push(e)-1)+"K\n\n",e=t.converter._dispatch("hashBlock.after",e,r,t)}),a.subParser("hashCodeTags",function(e,r,t){"use strict";e=t.converter._dispatch("hashCodeTags.before",e,r,t);return e=a.helper.replaceRecursiveRegExp(e,function(e,n,s,o){var i=s+a.subParser("encodeCode")(n,r,t)+o;return"¨C"+(t.gHtmlSpans.push(i)-1)+"C"},"]*>","","gim"),e=t.converter._dispatch("hashCodeTags.after",e,r,t)}),a.subParser("hashElement",function(e,r,t){"use strict";return function(e,r){var a=r;return a=a.replace(/\n\n/g,"\n"),a=a.replace(/^\n/,""),a=a.replace(/\n+$/g,""),a="\n\n¨K"+(t.gHtmlBlocks.push(a)-1)+"K\n\n"}}),a.subParser("hashHTMLBlocks",function(e,r,t){"use strict";e=t.converter._dispatch("hashHTMLBlocks.before",e,r,t);var n=["pre","div","h1","h2","h3","h4","h5","h6","blockquote","table","dl","ol","ul","script","noscript","form","fieldset","iframe","math","style","section","header","footer","nav","article","aside","address","audio","canvas","figure","hgroup","output","video","p"],s=function(e,r,a,n){var s=e;return-1!==a.search(/\bmarkdown\b/)&&(s=a+t.converter.makeHtml(r)+n),"\n\n¨K"+(t.gHtmlBlocks.push(s)-1)+"K\n\n"};r.backslashEscapesHTMLTags&&(e=e.replace(/\\<(\/?[^>]+?)>/g,function(e,r){return"<"+r+">"}));for(var o=0;o]*>)","im"),c="<"+n[o]+"\\b[^>]*>",u="";-1!==(i=a.helper.regexIndexOf(e,l));){var d=a.helper.splitAtIndex(e,i),p=a.helper.replaceRecursiveRegExp(d[1],s,c,u,"im");if(p===d[1])break;e=d[0].concat(p)}return e=e.replace(/(\n {0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,a.subParser("hashElement")(e,r,t)),e=a.helper.replaceRecursiveRegExp(e,function(e){return"\n\n¨K"+(t.gHtmlBlocks.push(e)-1)+"K\n\n"},"^ {0,3}\x3c!--","--\x3e","gm"),e=e.replace(/(?:\n\n)( {0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,a.subParser("hashElement")(e,r,t)),e=t.converter._dispatch("hashHTMLBlocks.after",e,r,t)}),a.subParser("hashHTMLSpans",function(e,r,t){"use strict";function a(e){return"¨C"+(t.gHtmlSpans.push(e)-1)+"C"}return e=t.converter._dispatch("hashHTMLSpans.before",e,r,t),e=e.replace(/<[^>]+?\/>/gi,function(e){return a(e)}),e=e.replace(/<([^>]+?)>[\s\S]*?<\/\1>/g,function(e){return a(e)}),e=e.replace(/<([^>]+?)\s[^>]+?>[\s\S]*?<\/\1>/g,function(e){return a(e)}),e=e.replace(/<[^>]+?>/gi,function(e){return a(e)}),e=t.converter._dispatch("hashHTMLSpans.after",e,r,t)}),a.subParser("unhashHTMLSpans",function(e,r,t){"use strict";e=t.converter._dispatch("unhashHTMLSpans.before",e,r,t);for(var a=0;a]*>\\s*]*>","^ {0,3}\\s*
","gim"),e=t.converter._dispatch("hashPreCodeTags.after",e,r,t)}),a.subParser("headers",function(e,r,t){"use strict";function n(e){var n,s;if(r.customizedHeaderId){var o=e.match(/\{([^{]+?)}\s*$/);o&&o[1]&&(e=o[1])}return n=e,s=a.helper.isString(r.prefixHeaderId)?r.prefixHeaderId:!0===r.prefixHeaderId?"section-":"",r.rawPrefixHeaderId||(n=s+n),n=r.ghCompatibleHeaderId?n.replace(/ /g,"-").replace(/&/g,"").replace(/¨T/g,"").replace(/¨D/g,"").replace(/[&+$,\/:;=?@"#{}|^¨~\[\]`\\*)(%.!'<>]/g,"").toLowerCase():r.rawHeaderId?n.replace(/ /g,"-").replace(/&/g,"&").replace(/¨T/g,"¨").replace(/¨D/g,"$").replace(/["']/g,"-").toLowerCase():n.replace(/[^\w]/g,"").toLowerCase(),r.rawPrefixHeaderId&&(n=s+n),t.hashLinkCounts[n]?n=n+"-"+t.hashLinkCounts[n]++:t.hashLinkCounts[n]=1,n}e=t.converter._dispatch("headers.before",e,r,t);var s=isNaN(parseInt(r.headerLevelStart))?1:parseInt(r.headerLevelStart),o=r.smoothLivePreview?/^(.+)[ \t]*\n={2,}[ \t]*\n+/gm:/^(.+)[ \t]*\n=+[ \t]*\n+/gm,i=r.smoothLivePreview?/^(.+)[ \t]*\n-{2,}[ \t]*\n+/gm:/^(.+)[ \t]*\n-+[ \t]*\n+/gm;e=(e=e.replace(o,function(e,o){var i=a.subParser("spanGamut")(o,r,t),l=r.noHeaderId?"":' id="'+n(o)+'"',c=""+i+"";return a.subParser("hashBlock")(c,r,t)})).replace(i,function(e,o){var i=a.subParser("spanGamut")(o,r,t),l=r.noHeaderId?"":' id="'+n(o)+'"',c=s+1,u=""+i+"";return a.subParser("hashBlock")(u,r,t)});var l=r.requireSpaceBeforeHeadingText?/^(#{1,6})[ \t]+(.+?)[ \t]*#*\n+/gm:/^(#{1,6})[ \t]*(.+?)[ \t]*#*\n+/gm;return e=e.replace(l,function(e,o,i){var l=i;r.customizedHeaderId&&(l=i.replace(/\s?\{([^{]+?)}\s*$/,""));var c=a.subParser("spanGamut")(l,r,t),u=r.noHeaderId?"":' id="'+n(i)+'"',d=s-1+o.length,p=""+c+"";return a.subParser("hashBlock")(p,r,t)}),e=t.converter._dispatch("headers.after",e,r,t)}),a.subParser("horizontalRule",function(e,r,t){"use strict";e=t.converter._dispatch("horizontalRule.before",e,r,t);var n=a.subParser("hashBlock")("
",r,t);return e=e.replace(/^ {0,2}( ?-){3,}[ \t]*$/gm,n),e=e.replace(/^ {0,2}( ?\*){3,}[ \t]*$/gm,n),e=e.replace(/^ {0,2}( ?_){3,}[ \t]*$/gm,n),e=t.converter._dispatch("horizontalRule.after",e,r,t)}),a.subParser("images",function(e,r,t){"use strict";function n(e,r,n,s,o,i,l,c){var u=t.gUrls,d=t.gTitles,p=t.gDimensions;if(n=n.toLowerCase(),c||(c=""),e.search(/\(? ?(['"].*['"])?\)$/m)>-1)s="";else if(""===s||null===s){if(""!==n&&null!==n||(n=r.toLowerCase().replace(/ ?\n/g," ")),s="#"+n,a.helper.isUndefined(u[n]))return e;s=u[n],a.helper.isUndefined(d[n])||(c=d[n]),a.helper.isUndefined(p[n])||(o=p[n].width,i=p[n].height)}r=r.replace(/"/g,""").replace(a.helper.regexes.asteriskDashAndColon,a.helper.escapeCharactersCallback);var h=''+r+'"}return e=(e=t.converter._dispatch("images.before",e,r,t)).replace(/!\[([^\]]*?)] ?(?:\n *)?\[([\s\S]*?)]()()()()()/g,n),e=e.replace(/!\[([^\]]*?)][ \t]*()\([ \t]??(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(["'])([^"]*?)\6)?[ \t]?\)/g,function(e,r,t,a,s,o,i,l){return a=a.replace(/\s/g,""),n(e,r,t,a,s,o,0,l)}),e=e.replace(/!\[([^\]]*?)][ \t]*()\([ \t]?<([^>]*)>(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(?:(["'])([^"]*?)\6))?[ \t]?\)/g,n),e=e.replace(/!\[([^\]]*?)][ \t]*()\([ \t]??(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(["'])([^"]*?)\6)?[ \t]?\)/g,n),e=e.replace(/!\[([^\[\]]+)]()()()()()/g,n),e=t.converter._dispatch("images.after",e,r,t)}),a.subParser("italicsAndBold",function(e,r,t){"use strict";function a(e,r,t){return r+e+t}return e=t.converter._dispatch("italicsAndBold.before",e,r,t),e=r.literalMidWordUnderscores?(e=(e=e.replace(/\b___(\S[\s\S]*?)___\b/g,function(e,r){return a(r,"","")})).replace(/\b__(\S[\s\S]*?)__\b/g,function(e,r){return a(r,"","")})).replace(/\b_(\S[\s\S]*?)_\b/g,function(e,r){return a(r,"","")}):(e=(e=e.replace(/___(\S[\s\S]*?)___/g,function(e,r){return/\S$/.test(r)?a(r,"",""):e})).replace(/__(\S[\s\S]*?)__/g,function(e,r){return/\S$/.test(r)?a(r,"",""):e})).replace(/_([^\s_][\s\S]*?)_/g,function(e,r){return/\S$/.test(r)?a(r,"",""):e}),e=r.literalMidWordAsterisks?(e=(e=e.replace(/([^*]|^)\B\*\*\*(\S[\s\S]*?)\*\*\*\B(?!\*)/g,function(e,r,t){return a(t,r+"","")})).replace(/([^*]|^)\B\*\*(\S[\s\S]*?)\*\*\B(?!\*)/g,function(e,r,t){return a(t,r+"","")})).replace(/([^*]|^)\B\*(\S[\s\S]*?)\*\B(?!\*)/g,function(e,r,t){return a(t,r+"","")}):(e=(e=e.replace(/\*\*\*(\S[\s\S]*?)\*\*\*/g,function(e,r){return/\S$/.test(r)?a(r,"",""):e})).replace(/\*\*(\S[\s\S]*?)\*\*/g,function(e,r){return/\S$/.test(r)?a(r,"",""):e})).replace(/\*([^\s*][\s\S]*?)\*/g,function(e,r){return/\S$/.test(r)?a(r,"",""):e}),e=t.converter._dispatch("italicsAndBold.after",e,r,t)}),a.subParser("lists",function(e,r,t){"use strict";function n(e,n){t.gListLevel++,e=e.replace(/\n{2,}$/,"\n");var s=/(\n)?(^ {0,3})([*+-]|\d+[.])[ \t]+((\[(x|X| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(¨0| {0,3}([*+-]|\d+[.])[ \t]+))/gm,o=/\n[ \t]*\n(?!¨0)/.test(e+="¨0");return r.disableForced4SpacesIndentedSublists&&(s=/(\n)?(^ {0,3})([*+-]|\d+[.])[ \t]+((\[(x|X| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(¨0|\2([*+-]|\d+[.])[ \t]+))/gm),e=e.replace(s,function(e,n,s,i,l,c,u){u=u&&""!==u.trim();var d=a.subParser("outdent")(l,r,t),p="";return c&&r.tasklists&&(p=' class="task-list-item" style="list-style-type: none;"',d=d.replace(/^[ \t]*\[(x|X| )?]/m,function(){var e='-1?(d=a.subParser("githubCodeBlocks")(d,r,t),d=a.subParser("blockGamut")(d,r,t)):(d=(d=a.subParser("lists")(d,r,t)).replace(/\n$/,""),d=(d=a.subParser("hashHTMLBlocks")(d,r,t)).replace(/\n\n+/g,"\n\n"),d=o?a.subParser("paragraphs")(d,r,t):a.subParser("spanGamut")(d,r,t)),d=d.replace("¨A",""),d=""+d+"\n"}),e=e.replace(/¨0/g,""),t.gListLevel--,n&&(e=e.replace(/\s+$/,"")),e}function s(e,r){if("ol"===r){var t=e.match(/^ *(\d+)\./);if(t&&"1"!==t[1])return' start="'+t[1]+'"'}return""}function o(e,t,a){var o=r.disableForced4SpacesIndentedSublists?/^ ?\d+\.[ \t]/gm:/^ {0,3}\d+\.[ \t]/gm,i=r.disableForced4SpacesIndentedSublists?/^ ?[*+-][ \t]/gm:/^ {0,3}[*+-][ \t]/gm,l="ul"===t?o:i,c="";if(-1!==e.search(l))!function r(u){var d=u.search(l),p=s(e,t);-1!==d?(c+="\n\n<"+t+p+">\n"+n(u.slice(0,d),!!a)+"\n",l="ul"===(t="ul"===t?"ol":"ul")?o:i,r(u.slice(d))):c+="\n\n<"+t+p+">\n"+n(u,!!a)+"\n"}(e);else{var u=s(e,t);c="\n\n<"+t+u+">\n"+n(e,!!a)+"\n"}return c}return e=t.converter._dispatch("lists.before",e,r,t),e+="¨0",e=t.gListLevel?e.replace(/^(( {0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(¨0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm,function(e,r,t){return o(r,t.search(/[*+-]/g)>-1?"ul":"ol",!0)}):e.replace(/(\n\n|^\n?)(( {0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(¨0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm,function(e,r,t,a){return o(t,a.search(/[*+-]/g)>-1?"ul":"ol",!1)}),e=e.replace(/¨0/,""),e=t.converter._dispatch("lists.after",e,r,t)}),a.subParser("metadata",function(e,r,t){"use strict";function a(e){t.metadata.raw=e,(e=(e=e.replace(/&/g,"&").replace(/"/g,""")).replace(/\n {4}/g," ")).replace(/^([\S ]+): +([\s\S]+?)$/gm,function(e,r,a){return t.metadata.parsed[r]=a,""})}return r.metadata?(e=t.converter._dispatch("metadata.before",e,r,t),e=e.replace(/^\s*«««+(\S*?)\n([\s\S]+?)\n»»»+\n/,function(e,r,t){return a(t),"¨M"}),e=e.replace(/^\s*---+(\S*?)\n([\s\S]+?)\n---+\n/,function(e,r,n){return r&&(t.metadata.format=r),a(n),"¨M"}),e=e.replace(/¨M/g,""),e=t.converter._dispatch("metadata.after",e,r,t)):e}),a.subParser("outdent",function(e,r,t){"use strict";return e=t.converter._dispatch("outdent.before",e,r,t),e=e.replace(/^(\t|[ ]{1,4})/gm,"¨0"),e=e.replace(/¨0/g,""),e=t.converter._dispatch("outdent.after",e,r,t)}),a.subParser("paragraphs",function(e,r,t){"use strict";for(var n=(e=(e=(e=t.converter._dispatch("paragraphs.before",e,r,t)).replace(/^\n+/g,"")).replace(/\n+$/g,"")).split(/\n{2,}/g),s=[],o=n.length,i=0;i=0?s.push(l):l.search(/\S/)>=0&&(l=(l=a.subParser("spanGamut")(l,r,t)).replace(/^([ \t]*)/g,"

"),l+="

",s.push(l))}for(o=s.length,i=0;i]*>\s*]*>/.test(u)&&(d=!0)}s[i]=u}return e=s.join("\n"),e=e.replace(/^\n+/g,""),e=e.replace(/\n+$/g,""),t.converter._dispatch("paragraphs.after",e,r,t)}),a.subParser("runExtension",function(e,r,t,a){"use strict";if(e.filter)r=e.filter(r,a.converter,t);else if(e.regex){var n=e.regex;n instanceof RegExp||(n=new RegExp(n,"g")),r=r.replace(n,e.replace)}return r}),a.subParser("spanGamut",function(e,r,t){"use strict";return e=t.converter._dispatch("spanGamut.before",e,r,t),e=a.subParser("codeSpans")(e,r,t),e=a.subParser("escapeSpecialCharsWithinTagAttributes")(e,r,t),e=a.subParser("encodeBackslashEscapes")(e,r,t),e=a.subParser("images")(e,r,t),e=a.subParser("anchors")(e,r,t),e=a.subParser("autoLinks")(e,r,t),e=a.subParser("simplifiedAutoLinks")(e,r,t),e=a.subParser("emoji")(e,r,t),e=a.subParser("underline")(e,r,t),e=a.subParser("italicsAndBold")(e,r,t),e=a.subParser("strikethrough")(e,r,t),e=a.subParser("ellipsis")(e,r,t),e=a.subParser("hashHTMLSpans")(e,r,t),e=a.subParser("encodeAmpsAndAngles")(e,r,t),r.simpleLineBreaks?/\n\n¨K/.test(e)||(e=e.replace(/\n+/g,"
\n")):e=e.replace(/ +\n/g,"
\n"),e=t.converter._dispatch("spanGamut.after",e,r,t)}),a.subParser("strikethrough",function(e,r,t){"use strict";return r.strikethrough&&(e=(e=t.converter._dispatch("strikethrough.before",e,r,t)).replace(/(?:~){2}([\s\S]+?)(?:~){2}/g,function(e,n){return function(e){return r.simplifiedAutoLink&&(e=a.subParser("simplifiedAutoLinks")(e,r,t)),""+e+""}(n)}),e=t.converter._dispatch("strikethrough.after",e,r,t)),e}),a.subParser("stripLinkDefinitions",function(e,r,t){"use strict";var n=function(e,n,s,o,i,l,c){return n=n.toLowerCase(),s.match(/^data:.+?\/.+?;base64,/)?t.gUrls[n]=s.replace(/\s/g,""):t.gUrls[n]=a.subParser("encodeAmpsAndAngles")(s,r,t),l?l+c:(c&&(t.gTitles[n]=c.replace(/"|'/g,""")),r.parseImgDimensions&&o&&i&&(t.gDimensions[n]={width:o,height:i}),"")};return e=(e+="¨0").replace(/^ {0,3}\[(.+)]:[ \t]*\n?[ \t]*?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n\n|(?=¨0)|(?=\n\[))/gm,n),e=e.replace(/^ {0,3}\[(.+)]:[ \t]*\n?[ \t]*\s]+)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n+|(?=¨0))/gm,n),e=e.replace(/¨0/,"")}),a.subParser("tables",function(e,r,t){"use strict";function n(e){return/^:[ \t]*--*$/.test(e)?' style="text-align:left;"':/^--*[ \t]*:[ \t]*$/.test(e)?' style="text-align:right;"':/^:[ \t]*--*[ \t]*:$/.test(e)?' style="text-align:center;"':""}function s(e,n){var s="";return e=e.trim(),(r.tablesHeaderId||r.tableHeaderId)&&(s=' id="'+e.replace(/ /g,"_").toLowerCase()+'"'),e=a.subParser("spanGamut")(e,r,t),""+e+"\n"}function o(e,n){return""+a.subParser("spanGamut")(e,r,t)+"\n"}function i(e){var i,l=e.split("\n");for(i=0;i\n\n\n",n=0;n\n";for(var s=0;s\n"}return t+="\n\n"}(p,_)}if(!r.tables)return e;return e=t.converter._dispatch("tables.before",e,r,t),e=e.replace(/\\(\|)/g,a.helper.escapeCharactersCallback),e=e.replace(/^ {0,3}\|?.+\|.+\n {0,3}\|?[ \t]*:?[ \t]*(?:[-=]){2,}[ \t]*:?[ \t]*\|[ \t]*:?[ \t]*(?:[-=]){2,}[\s\S]+?(?:\n\n|¨0)/gm,i),e=e.replace(/^ {0,3}\|.+\|[ \t]*\n {0,3}\|[ \t]*:?[ \t]*(?:[-=]){2,}[ \t]*:?[ \t]*\|[ \t]*\n( {0,3}\|.+\|[ \t]*\n)*(?:\n|¨0)/gm,i),e=t.converter._dispatch("tables.after",e,r,t)}),a.subParser("underline",function(e,r,t){"use strict";return r.underline?(e=t.converter._dispatch("underline.before",e,r,t),e=r.literalMidWordUnderscores?(e=e.replace(/\b___(\S[\s\S]*?)___\b/g,function(e,r){return""+r+""})).replace(/\b__(\S[\s\S]*?)__\b/g,function(e,r){return""+r+""}):(e=e.replace(/___(\S[\s\S]*?)___/g,function(e,r){return/\S$/.test(r)?""+r+"":e})).replace(/__(\S[\s\S]*?)__/g,function(e,r){return/\S$/.test(r)?""+r+"":e}),e=e.replace(/(_)/g,a.helper.escapeCharactersCallback),e=t.converter._dispatch("underline.after",e,r,t)):e}),a.subParser("unescapeSpecialChars",function(e,r,t){"use strict";return e=t.converter._dispatch("unescapeSpecialChars.before",e,r,t),e=e.replace(/¨E(\d+)E/g,function(e,r){var t=parseInt(r);return String.fromCharCode(t)}),e=t.converter._dispatch("unescapeSpecialChars.after",e,r,t)}),a.subParser("makeMarkdown.blockquote",function(e,r){"use strict";var t="";if(e.hasChildNodes())for(var n=e.childNodes,s=n.length,o=0;o "+t.split("\n").join("\n> ")}),a.subParser("makeMarkdown.codeBlock",function(e,r){"use strict";var t=e.getAttribute("language"),a=e.getAttribute("precodenum");return"```"+t+"\n"+r.preList[a]+"\n```"}),a.subParser("makeMarkdown.codeSpan",function(e){"use strict";return"`"+e.innerHTML+"`"}),a.subParser("makeMarkdown.emphasis",function(e,r){"use strict";var t="";if(e.hasChildNodes()){t+="*";for(var n=e.childNodes,s=n.length,o=0;o",e.hasAttribute("width")&&e.hasAttribute("height")&&(r+=" ="+e.getAttribute("width")+"x"+e.getAttribute("height")),e.hasAttribute("title")&&(r+=' "'+e.getAttribute("title")+'"'),r+=")"),r}),a.subParser("makeMarkdown.links",function(e,r){"use strict";var t="";if(e.hasChildNodes()&&e.hasAttribute("href")){var n=e.childNodes,s=n.length;t="[";for(var o=0;o",e.hasAttribute("title")&&(t+=' "'+e.getAttribute("title")+'"'),t+=")"}return t}),a.subParser("makeMarkdown.list",function(e,r,t){"use strict";var n="";if(!e.hasChildNodes())return"";for(var s=e.childNodes,o=s.length,i=e.getAttribute("start")||1,l=0;l"+r.preList[t]+""}),a.subParser("makeMarkdown.strikethrough",function(e,r){"use strict";var t="";if(e.hasChildNodes()){t+="~~";for(var n=e.childNodes,s=n.length,o=0;otr>th"),l=e.querySelectorAll("tbody>tr");for(t=0;t_&&(_=g)}for(t=0;t/g,"\\$1>"),r=r.replace(/^#/gm,"\\#"),r=r.replace(/^(\s*)([-=]{3,})(\s*)$/,"$1\\$2$3"),r=r.replace(/^( {0,3}\d+)\./gm,"$1\\."),r=r.replace(/^( {0,3})([+-])/gm,"$1\\$2"),r=r.replace(/]([\s]*)\(/g,"\\]$1\\("),r=r.replace(/^ {0,3}\[([\S \t]*?)]:/gm,"\\[$1]:")});"function"==typeof define&&define.amd?define(function(){"use strict";return a}):"undefined"!=typeof module&&module.exports?module.exports=a:this.showdown=a}).call(this); 3 | //# sourceMappingURL=showdown.min.js.map 4 | -------------------------------------------------------------------------------- /src/lib/w3Highlighter.js: -------------------------------------------------------------------------------- 1 | export default function w3CodeColor(elmnt, mode) { 2 | var lang = mode || 'html' 3 | var elmntObj = 4 | typeof elmnt === 'string' ? document.getElementById(elmnt) : elmnt 5 | var elmntTxt = elmntObj.innerHTML 6 | var tagcolor = '#444' 7 | var tagnamecolor = 'mediumblue' 8 | var attributecolor = '#3b8708' 9 | var attributevaluecolor = 'brown' // #3b8708 10 | var commentcolor = 'green' 11 | var cssselectorcolor = 'brown' 12 | var csspropertycolor = 'red' 13 | var csspropertyvaluecolor = 'mediumblue' 14 | var cssdelimitercolor = 'black' 15 | var cssimportantcolor = 'red' 16 | var jscolor = 'black' 17 | var jskeywordcolor = 'mediumblue' 18 | var jsstringcolor = 'brown' 19 | var jsnumbercolor = 'red' 20 | var jspropertycolor = 'black' 21 | elmntObj.style.fontFamily = 'monospace' 22 | if (!lang) { 23 | lang = 'html' 24 | } 25 | if (lang == 'html') { 26 | elmntTxt = htmlMode(elmntTxt) 27 | } 28 | if (lang == 'css') { 29 | elmntTxt = cssMode(elmntTxt) 30 | } 31 | if (lang == 'js') { 32 | elmntTxt = jsMode(elmntTxt) 33 | } 34 | elmntObj.innerHTML = elmntTxt 35 | 36 | function extract(str, start, end, func, repl) { 37 | var s, 38 | e, 39 | d = '', 40 | a = [] 41 | while (str.search(start) > -1) { 42 | s = str.search(start) 43 | e = str.indexOf(end, s) 44 | if (e == -1) { 45 | e = str.length 46 | } 47 | if (repl) { 48 | a.push(func(str.substring(s, e + end.length))) 49 | str = str.substring(0, s) + repl + str.substr(e + end.length) 50 | } else { 51 | d += str.substring(0, s) 52 | d += func(str.substring(s, e + end.length)) 53 | str = str.substr(e + end.length) 54 | } 55 | } 56 | this.rest = d + str 57 | this.arr = a 58 | } 59 | 60 | function htmlMode(txt) { 61 | var rest = txt, 62 | done = '', 63 | php, 64 | comment, 65 | angular, 66 | startpos, 67 | endpos, 68 | note, 69 | i 70 | comment = new extract( 71 | rest, 72 | '<!--', 73 | '-->', 74 | commentMode, 75 | 'W3HTMLCOMMENTPOS' 76 | ) 77 | rest = comment.rest 78 | while (rest.indexOf('<') > -1) { 79 | note = '' 80 | startpos = rest.indexOf('<') 81 | if (rest.substr(startpos, 9).toUpperCase() == '<STYLE') { 82 | note = 'css' 83 | } 84 | if (rest.substr(startpos, 10).toUpperCase() == '<SCRIPT') { 85 | note = 'javascript' 86 | } 87 | endpos = rest.indexOf('>', startpos) 88 | if (endpos == -1) { 89 | endpos = rest.length 90 | } 91 | done += rest.substring(0, startpos) 92 | done += tagMode(rest.substring(startpos, endpos + 4)) 93 | rest = rest.substr(endpos + 4) 94 | if (note == 'css') { 95 | endpos = rest.indexOf('</style>') 96 | if (endpos > -1) { 97 | done += cssMode(rest.substring(0, endpos)) 98 | rest = rest.substr(endpos) 99 | } 100 | } 101 | if (note == 'javascript') { 102 | endpos = rest.indexOf('</script>') 103 | if (endpos > -1) { 104 | done += jsMode(rest.substring(0, endpos)) 105 | rest = rest.substr(endpos) 106 | } 107 | } 108 | } 109 | rest = done + rest 110 | for (i = 0; i < comment.arr.length; i++) { 111 | rest = rest.replace('W3HTMLCOMMENTPOS', comment.arr[i]) 112 | } 113 | return rest 114 | } 115 | function tagMode(txt) { 116 | var rest = txt, 117 | done = '', 118 | startpos, 119 | endpos, 120 | result 121 | while (rest.search(/(\s|
)/) > -1) { 122 | startpos = rest.search(/(\s|
)/) 123 | endpos = rest.indexOf('>') 124 | if (endpos == -1) { 125 | endpos = rest.length 126 | } 127 | done += rest.substring(0, startpos) 128 | done += attributeMode(rest.substring(startpos, endpos)) 129 | rest = rest.substr(endpos) 130 | } 131 | result = done + rest 132 | result = 133 | '<' + result.substring(4) 134 | if (result.substr(result.length - 4, 4) == '>') { 135 | result = 136 | result.substring(0, result.length - 4) + 137 | '>' 140 | } 141 | return '' + result + '' 142 | } 143 | function attributeMode(txt) { 144 | var rest = txt, 145 | done = '', 146 | startpos, 147 | endpos, 148 | singlefnuttpos, 149 | doublefnuttpos, 150 | spacepos 151 | while (rest.indexOf('=') > -1) { 152 | endpos = -1 153 | startpos = rest.indexOf('=') 154 | singlefnuttpos = rest.indexOf("'", startpos) 155 | doublefnuttpos = rest.indexOf('"', startpos) 156 | spacepos = rest.indexOf(' ', startpos + 2) 157 | if ( 158 | spacepos > -1 && 159 | (spacepos < singlefnuttpos || singlefnuttpos == -1) && 160 | (spacepos < doublefnuttpos || doublefnuttpos == -1) 161 | ) { 162 | endpos = rest.indexOf(' ', startpos) 163 | } else if ( 164 | doublefnuttpos > -1 && 165 | (doublefnuttpos < singlefnuttpos || singlefnuttpos == -1) && 166 | (doublefnuttpos < spacepos || spacepos == -1) 167 | ) { 168 | endpos = rest.indexOf('"', rest.indexOf('"', startpos) + 1) 169 | } else if ( 170 | singlefnuttpos > -1 && 171 | (singlefnuttpos < doublefnuttpos || doublefnuttpos == -1) && 172 | (singlefnuttpos < spacepos || spacepos == -1) 173 | ) { 174 | endpos = rest.indexOf("'", rest.indexOf("'", startpos) + 1) 175 | } 176 | if (!endpos || endpos == -1 || endpos < startpos) { 177 | endpos = rest.length 178 | } 179 | done += rest.substring(0, startpos) 180 | done += attributeValueMode(rest.substring(startpos, endpos + 1)) 181 | rest = rest.substr(endpos + 1) 182 | } 183 | return '' + done + rest + '' 184 | } 185 | function attributeValueMode(txt) { 186 | return '' + txt + '' 187 | } 188 | function commentMode(txt) { 189 | return '' + txt + '' 190 | } 191 | function cssMode(txt) { 192 | var rest = txt, 193 | done = '', 194 | s, 195 | e, 196 | comment, 197 | i, 198 | midz, 199 | c, 200 | cc 201 | comment = new extract(rest, /\/\*/, '*/', commentMode, 'W3CSSCOMMENTPOS') 202 | rest = comment.rest 203 | while (rest.search('{') > -1) { 204 | s = rest.search('{') 205 | midz = rest.substr(s + 1) 206 | cc = 1 207 | c = 0 208 | for (i = 0; i < midz.length; i++) { 209 | if (midz.substr(i, 1) == '{') { 210 | cc++ 211 | c++ 212 | } 213 | if (midz.substr(i, 1) == '}') { 214 | cc-- 215 | } 216 | if (cc == 0) { 217 | break 218 | } 219 | } 220 | if (cc != 0) { 221 | c = 0 222 | } 223 | e = s 224 | for (i = 0; i <= c; i++) { 225 | e = rest.indexOf('}', e + 1) 226 | } 227 | if (e == -1) { 228 | e = rest.length 229 | } 230 | done += rest.substring(0, s + 1) 231 | done += cssPropertyMode(rest.substring(s + 1, e)) 232 | rest = rest.substr(e) 233 | } 234 | rest = done + rest 235 | rest = rest.replace( 236 | /{/g, 237 | '{' 238 | ) 239 | rest = rest.replace( 240 | /}/g, 241 | '}' 242 | ) 243 | for (i = 0; i < comment.arr.length; i++) { 244 | rest = rest.replace('W3CSSCOMMENTPOS', comment.arr[i]) 245 | } 246 | return '' + rest + '' 247 | } 248 | function cssPropertyMode(txt) { 249 | var rest = txt, 250 | done = '', 251 | s, 252 | e, 253 | n, 254 | loop 255 | if (rest.indexOf('{') > -1) { 256 | return cssMode(rest) 257 | } 258 | while (rest.search(':') > -1) { 259 | s = rest.search(':') 260 | loop = true 261 | n = s 262 | while (loop == true) { 263 | loop = false 264 | e = rest.indexOf(';', n) 265 | if (rest.substring(e - 5, e + 1) == ' ') { 266 | loop = true 267 | n = e + 1 268 | } 269 | } 270 | if (e == -1) { 271 | e = rest.length 272 | } 273 | done += rest.substring(0, s) 274 | done += cssPropertyValueMode(rest.substring(s, e + 1)) 275 | rest = rest.substr(e + 1) 276 | } 277 | return ( 278 | '' + done + rest + '' 279 | ) 280 | } 281 | function cssPropertyValueMode(txt) { 282 | var rest = txt, 283 | done = '', 284 | s 285 | rest = 286 | ':' + rest.substring(1) 287 | while (rest.search(/!important/i) > -1) { 288 | s = rest.search(/!important/i) 289 | done += rest.substring(0, s) 290 | done += cssImportantMode(rest.substring(s, s + 10)) 291 | rest = rest.substr(s + 10) 292 | } 293 | result = done + rest 294 | if ( 295 | result.substr(result.length - 1, 1) == ';' && 296 | result.substr(result.length - 6, 6) != ' ' && 297 | result.substr(result.length - 4, 4) != '<' && 298 | result.substr(result.length - 4, 4) != '>' && 299 | result.substr(result.length - 5, 5) != '&' 300 | ) { 301 | result = 302 | result.substring(0, result.length - 1) + 303 | ';' 306 | } 307 | return ( 308 | '' + result + '' 309 | ) 310 | } 311 | function cssImportantMode(txt) { 312 | return ( 313 | '' + 316 | txt + 317 | '' 318 | ) 319 | } 320 | function jsMode(txt) { 321 | var rest = txt, 322 | done = '', 323 | esc = [], 324 | i, 325 | cc, 326 | tt = '', 327 | sfnuttpos, 328 | dfnuttpos, 329 | compos, 330 | comlinepos, 331 | keywordpos, 332 | numpos, 333 | mypos, 334 | dotpos, 335 | y 336 | for (i = 0; i < rest.length; i++) { 337 | cc = rest.substr(i, 1) 338 | if (cc == '\\') { 339 | esc.push(rest.substr(i, 2)) 340 | cc = 'W3JSESCAPE' 341 | i++ 342 | } 343 | tt += cc 344 | } 345 | rest = tt 346 | y = 1 347 | while (y == 1) { 348 | sfnuttpos = getPos(rest, "'", "'", jsStringMode) 349 | dfnuttpos = getPos(rest, '"', '"', jsStringMode) 350 | compos = getPos(rest, /\/\*/, '*/', commentMode) 351 | comlinepos = getPos(rest, /\/\//, '
', commentMode) 352 | numpos = getNumPos(rest, jsNumberMode) 353 | keywordpos = getKeywordPos('js', rest, jsKeywordMode) 354 | dotpos = getDotPos(rest, jsPropertyMode) 355 | if ( 356 | Math.max( 357 | numpos[0], 358 | sfnuttpos[0], 359 | dfnuttpos[0], 360 | compos[0], 361 | comlinepos[0], 362 | keywordpos[0], 363 | dotpos[0] 364 | ) == -1 365 | ) { 366 | break 367 | } 368 | mypos = getMinPos( 369 | numpos, 370 | sfnuttpos, 371 | dfnuttpos, 372 | compos, 373 | comlinepos, 374 | keywordpos, 375 | dotpos 376 | ) 377 | if (mypos[0] == -1) { 378 | break 379 | } 380 | if (mypos[0] > -1) { 381 | done += rest.substring(0, mypos[0]) 382 | done += mypos[2](rest.substring(mypos[0], mypos[1])) 383 | rest = rest.substr(mypos[1]) 384 | } 385 | } 386 | rest = done + rest 387 | for (i = 0; i < esc.length; i++) { 388 | rest = rest.replace('W3JSESCAPE', esc[i]) 389 | } 390 | return '' + rest + '' 391 | } 392 | function jsStringMode(txt) { 393 | return '' + txt + '' 394 | } 395 | function jsKeywordMode(txt) { 396 | return '' + txt + '' 397 | } 398 | function jsNumberMode(txt) { 399 | return '' + txt + '' 400 | } 401 | function jsPropertyMode(txt) { 402 | return '' + txt + '' 403 | } 404 | function getDotPos(txt, func) { 405 | var x, 406 | i, 407 | j, 408 | s, 409 | e, 410 | arr = [ 411 | '.', 412 | '<', 413 | ' ', 414 | ';', 415 | '(', 416 | '+', 417 | ')', 418 | '[', 419 | ']', 420 | ',', 421 | '&', 422 | ':', 423 | '{', 424 | '}', 425 | '../index.html', 426 | '-', 427 | '*', 428 | '|', 429 | '%' 430 | ], 431 | cc 432 | s = txt.indexOf('.') 433 | if (s > -1) { 434 | x = txt.substr(s + 1) 435 | for (j = 0; j < x.length; j++) { 436 | cc = x[j] 437 | for (i = 0; i < arr.length; i++) { 438 | if (cc.indexOf(arr[i]) > -1) { 439 | e = j 440 | return [s + 1, e + s + 1, func] 441 | } 442 | } 443 | } 444 | } 445 | return [-1, -1, func] 446 | } 447 | function getMinPos() { 448 | var i, 449 | arr = [] 450 | for (i = 0; i < arguments.length; i++) { 451 | if (arguments[i][0] > -1) { 452 | if (arr.length == 0 || arguments[i][0] < arr[0]) { 453 | arr = arguments[i] 454 | } 455 | } 456 | } 457 | if (arr.length == 0) { 458 | arr = arguments[i] 459 | } 460 | return arr 461 | } 462 | function getKeywordPos(typ, txt, func) { 463 | var words, 464 | i, 465 | pos, 466 | rpos = -1, 467 | rpos2 = -1, 468 | patt 469 | if (typ == 'js') { 470 | words = [ 471 | 'abstract', 472 | 'arguments', 473 | 'boolean', 474 | 'break', 475 | 'byte', 476 | 'case', 477 | 'catch', 478 | 'char', 479 | 'class', 480 | 'const', 481 | 'continue', 482 | 'debugger', 483 | 'default', 484 | 'delete', 485 | 'do', 486 | 'double', 487 | 'else', 488 | 'enum', 489 | 'eval', 490 | 'export', 491 | 'extends', 492 | 'false', 493 | 'final', 494 | 'finally', 495 | 'float', 496 | 'for', 497 | 'function', 498 | 'goto', 499 | 'if', 500 | 'implements', 501 | 'import', 502 | 'in', 503 | 'instanceof', 504 | 'int', 505 | 'interface', 506 | 'let', 507 | 'long', 508 | 'NaN', 509 | 'native', 510 | 'new', 511 | 'null', 512 | 'package', 513 | 'private', 514 | 'protected', 515 | 'public', 516 | 'return', 517 | 'short', 518 | 'static', 519 | 'super', 520 | 'switch', 521 | 'synchronized', 522 | 'this', 523 | 'throw', 524 | 'throws', 525 | 'transient', 526 | 'true', 527 | 'try', 528 | 'typeof', 529 | 'var', 530 | 'void', 531 | 'volatile', 532 | 'while', 533 | 'with', 534 | 'yield' 535 | ] 536 | } 537 | for (i = 0; i < words.length; i++) { 538 | pos = txt.indexOf(words[i]) 539 | if (pos > -1) { 540 | patt = /\W/g 541 | if ( 542 | txt.substr(pos + words[i].length, 1).match(patt) && 543 | txt.substr(pos - 1, 1).match(patt) 544 | ) { 545 | if (pos > -1 && (rpos == -1 || pos < rpos)) { 546 | rpos = pos 547 | rpos2 = rpos + words[i].length 548 | } 549 | } 550 | } 551 | } 552 | return [rpos, rpos2, func] 553 | } 554 | function getPos(txt, start, end, func) { 555 | var s, e 556 | s = txt.search(start) 557 | e = txt.indexOf(end, s + end.length) 558 | if (e == -1) { 559 | e = txt.length 560 | } 561 | return [s, e + end.length, func] 562 | } 563 | function getNumPos(txt, func) { 564 | var arr = [ 565 | '
', 566 | ' ', 567 | ';', 568 | '(', 569 | '+', 570 | ')', 571 | '[', 572 | ']', 573 | ',', 574 | '&', 575 | ':', 576 | '{', 577 | '}', 578 | '../index.html', 579 | '-', 580 | '*', 581 | '|', 582 | '%', 583 | '=' 584 | ], 585 | i, 586 | j, 587 | c, 588 | startpos = 0, 589 | endpos, 590 | word 591 | for (i = 0; i < txt.length; i++) { 592 | for (j = 0; j < arr.length; j++) { 593 | c = txt.substr(i, arr[j].length) 594 | if (c == arr[j]) { 595 | if ( 596 | c == '-' && 597 | (txt.substr(i - 1, 1) == 'e' || txt.substr(i - 1, 1) == 'E') 598 | ) { 599 | continue 600 | } 601 | endpos = i 602 | if (startpos < endpos) { 603 | word = txt.substring(startpos, endpos) 604 | if (!isNaN(word)) { 605 | return [startpos, endpos, func] 606 | } 607 | } 608 | i += arr[j].length 609 | startpos = i 610 | i -= 1 611 | break 612 | } 613 | } 614 | } 615 | return [-1, -1, func] 616 | } 617 | } 618 | -------------------------------------------------------------------------------- /src/web/css/index.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'VarelaRound-Regular'; 3 | src: url('../../assets/fonts/VarelaRound-Regular.ttf'); 4 | } 5 | 6 | @font-face { 7 | font-family: 'FiraMono-Regular'; 8 | src: url('../../assets/fonts/FiraMono-Regular.ttf'); 9 | } 10 | 11 | html { 12 | padding: 0; 13 | margin: 0; 14 | } 15 | 16 | body { 17 | padding: 0; 18 | margin: 0; 19 | background-attachment: fixed; 20 | overflow: auto; 21 | } 22 | 23 | /* The Modal */ 24 | .modal { 25 | display: none; 26 | /* Hidden by default */ 27 | position: fixed; 28 | /* Stay in place */ 29 | z-index: 1; 30 | /* Sit on top */ 31 | padding-top: 100px; 32 | /* Location of the box */ 33 | left: 0; 34 | top: 0; 35 | width: 100%; 36 | /* Full width */ 37 | height: 100%; 38 | /* Full height */ 39 | overflow: auto; 40 | /* Enable scroll if needed */ 41 | background-color: rgb(0, 0, 0); 42 | /* Fallback color */ 43 | background-color: rgba(0, 0, 0, 0.4); 44 | /* Black w/ opacity */ 45 | } 46 | 47 | /* Modal Content */ 48 | .modal-content { 49 | position: relative; 50 | background-color: #fefefe; 51 | margin: auto; 52 | border: 1px solid #888; 53 | width: 80%; 54 | box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19); 55 | -webkit-animation-name: animatetop; 56 | -webkit-animation-duration: 0.4s; 57 | animation-name: animatetop; 58 | animation-duration: 0.4s; 59 | padding: 10px 15px 10px 15px; 60 | } 61 | 62 | /* Add Animation */ 63 | @-webkit-keyframes animatetop { 64 | from { 65 | top: -300px; 66 | opacity: 0; 67 | } 68 | 69 | to { 70 | top: 0; 71 | opacity: 1; 72 | } 73 | } 74 | 75 | @keyframes animatetop { 76 | from { 77 | top: -300px; 78 | opacity: 0; 79 | } 80 | 81 | to { 82 | top: 0; 83 | opacity: 1; 84 | } 85 | } 86 | 87 | /* The Close Button */ 88 | .modal-close { 89 | color: black; 90 | float: right; 91 | font-size: 36px; 92 | font-weight: bold; 93 | padding: 10px; 94 | } 95 | 96 | .modal-close:hover, 97 | .modal-close:focus { 98 | color: red; 99 | text-decoration: none; 100 | cursor: pointer; 101 | } 102 | /* End Modal */ 103 | 104 | /* Tooltip */ 105 | .tooltip { 106 | position: relative; 107 | display: inline-block; 108 | } 109 | 110 | .tooltip .tooltiptext { 111 | visibility: hidden; 112 | width: 120px; 113 | background-color: #6060cc; 114 | color: #fff; 115 | text-align: center; 116 | border-radius: 6px; 117 | padding: 5px 0; 118 | position: absolute; 119 | z-index: 1; 120 | top: -5px; 121 | left: 110%; 122 | } 123 | 124 | /* The tootltip arrow */ 125 | .tooltip .tooltiptext::after { 126 | content: ''; 127 | position: absolute; 128 | top: 50%; 129 | right: 100%; 130 | margin-top: -5px; 131 | border-width: 5px; 132 | border-style: solid; 133 | border-color: transparent #6060cc transparent transparent; 134 | } 135 | 136 | .tooltip:hover .tooltiptext { 137 | visibility: visible; 138 | } 139 | 140 | /* Main body */ 141 | .parallax { 142 | /* The image used */ 143 | /* background-image: url("book-background0.jpg"); */ 144 | background-color: rgb(42, 128, 139); 145 | 146 | /* Set a specific height */ 147 | height: 100vh; 148 | 149 | /* Create the parallax scrolling effect */ 150 | background-attachment: fixed; 151 | background-position: center; 152 | background-repeat: no-repeat; 153 | background-size: cover; 154 | } 155 | 156 | #root { 157 | display: flex; 158 | flex-shrink: 4; 159 | height: 100vh; 160 | padding: 0; 161 | margin: 0; 162 | left: 0; 163 | top: 0; 164 | bottom: 0; 165 | right: 0; 166 | flex-direction: column; 167 | /* flex-wrap: wrap; */ 168 | } 169 | 170 | #main { 171 | display: flex; 172 | flex-shrink: 3; 173 | height: 90vh; 174 | width: 100vw; 175 | } 176 | 177 | /* End main body */ 178 | 179 | /* Auxilliary button */ 180 | #auxbtn { 181 | position: fixed; 182 | top: 0; 183 | right: 0; 184 | color: red; 185 | display: none; 186 | transition: all 0.3s; 187 | font-size: large; 188 | background: rgba(102, 102, 102, 0); 189 | /* border: 1px solid red; */ 190 | height: 50px; 191 | width: 50px; 192 | } 193 | 194 | #auxbtn:hover, 195 | #auxbtn:focus { 196 | background: white; 197 | border: none; 198 | color: black; 199 | } 200 | 201 | /* Topnav */ 202 | .topnav { 203 | background-color: #f1f1f1; 204 | width: 100%; 205 | height: 10%; 206 | overflow-x: auto; 207 | overflow-y: hidden; 208 | display: flex; 209 | align-content: center; 210 | vertical-align: middle; 211 | transition: all 0.3s; 212 | } 213 | 214 | .topnav a { 215 | float: left; 216 | color: #f2f2f2; 217 | text-align: center; 218 | text-decoration: none; 219 | font-size: 16px; 220 | display: flex; 221 | align-content: center; 222 | background: #4286f4; 223 | height: 20px; 224 | padding-top: 12px; 225 | padding-bottom: 15px; 226 | margin: auto 8px auto 8px; 227 | font-family: sans-serif; 228 | } 229 | 230 | .topnav a:hover { 231 | background-color: white; 232 | color: black; 233 | } 234 | 235 | .topnav a.active { 236 | background-color: #4caf50; 237 | color: white; 238 | } 239 | 240 | #settings-btn, 241 | #submit-btn, 242 | .download-btn, 243 | #write-btn, 244 | #read-btn, 245 | #balance-btn { 246 | padding: 12px 15px; 247 | cursor: pointer; 248 | } 249 | 250 | #submit-btn { 251 | background: #1fa01f; 252 | padding: 12px 15px 12px 15px; 253 | } 254 | 255 | #preview-btn { 256 | background: rgb(201, 20, 20); 257 | } 258 | 259 | #settings-btn { 260 | background: rgb(15, 15, 15); 261 | padding: 12px 15px 12px 15px; 262 | font-size: 18px; 263 | } 264 | 265 | #write-btn, 266 | #read-btn, 267 | #balance-btn { 268 | background-color: #4286f4; 269 | } 270 | 271 | #logo { 272 | margin-left: auto; 273 | background: rgba(102, 102, 102, 0); 274 | } 275 | 276 | #logo-img { 277 | transform: translate(0, -12px); 278 | } 279 | 280 | #upbtn { 281 | background: rgb(201, 20, 20); 282 | } 283 | 284 | #write-btn:hover, 285 | #read-btn:hover, 286 | #balance-btn:hover, 287 | #settings-btn:hover, 288 | #preview-btn:hover, 289 | #submit-btn:hover, 290 | #upbtn:hover { 291 | background: white; 292 | cursor: pointer; 293 | } 294 | 295 | .settings-modal { 296 | font-family: monospace; 297 | } 298 | 299 | .setting { 300 | font-family: monospace; 301 | margin: 2px; 302 | padding: 15px; 303 | } 304 | 305 | /* Topnav end */ 306 | 307 | /* Main div (editor-div + output-div) */ 308 | .main-div { 309 | background: rgba(102, 102, 102, 0); 310 | color: white; 311 | transition: all 0.3s; 312 | width: 50%; 313 | height: 100%; 314 | overflow-y: auto; 315 | overflow-x: hidden; 316 | font-family: monospace; 317 | text-align: justify; 318 | resize: horizontal; 319 | box-sizing: border-box; 320 | } 321 | 322 | #editor-div { 323 | overflow: hidden; 324 | left: 0; 325 | } 326 | 327 | #output-div { 328 | right: 0; 329 | } 330 | 331 | .right-align-top { 332 | margin-left: auto; 333 | margin-right: 8px; 334 | } 335 | 336 | .content-opts { 337 | padding-left: 15px; 338 | padding-right: 15px; 339 | } 340 | 341 | button { 342 | outline: none; 343 | border: none; 344 | padding: 5px; 345 | cursor: pointer; 346 | } 347 | 348 | button:hover { 349 | background: #74637f; 350 | color: #eee; 351 | } 352 | 353 | .editor { 354 | font-family: FiraMono-Regular; 355 | height: 100%; 356 | width: 100%; 357 | background: white; 358 | color: rgb(40, 40, 40); 359 | margin: 0px; 360 | border-radius: 0; 361 | resize: none; 362 | overflow: auto; 363 | font-size: 16px; 364 | padding: 25px; 365 | box-sizing: border-box; 366 | outline: none; 367 | } 368 | 369 | /*#alert-area { 370 | position: fixed; 371 | bottom: 0; 372 | width: 100vw; 373 | height: 20px; 374 | background-color: rgb(42, 128, 139); 375 | font-size: large; 376 | text-align: center; 377 | color: #fff; 378 | padding: 10px; 379 | }*/ 380 | 381 | #alerts-area { 382 | display: none; 383 | } 384 | 385 | /* Style the tab */ 386 | .tab { 387 | overflow-x: auto; 388 | /* border: 1px solid #ccc; */ 389 | background-color: #f1f1f1; 390 | height: 8%; 391 | /*position: fixed;*/ 392 | } 393 | 394 | /* Style the buttons inside the tab */ 395 | .tab button { 396 | background-color: inherit; 397 | float: left; 398 | border: none; 399 | outline: none; 400 | cursor: pointer; 401 | padding: 10px 12px; 402 | transition: 0.3s; 403 | font-size: 16px; 404 | width: 100px; 405 | color: black; 406 | height: 100%; 407 | } 408 | 409 | /* Change background color of buttons on hover */ 410 | .tab button:hover { 411 | background-color: #ddd; 412 | } 413 | 414 | /* Create an active/current tablink class */ 415 | .tab button.active { 416 | /* background-color: rgb(201, 20, 20); */ 417 | color: black; 418 | box-sizing: border-box; 419 | /* border-top: 3px solid rgb(201, 20, 20); */ 420 | border-bottom: 3px solid rgb(201, 20, 20); 421 | } 422 | 423 | /* Style the tab content */ 424 | .tabcontent1, 425 | .tabcontent2 { 426 | display: none; 427 | padding: 6px 12px; 428 | border-top: none; 429 | min-height: 88%; 430 | } 431 | 432 | .tabcontent1 { 433 | padding: 0; 434 | height: 92%; 435 | } 436 | 437 | .tabcontent2 { 438 | height: 100%; 439 | } 440 | 441 | .main-div a { 442 | color: white; 443 | text-decoration: none; 444 | padding: 1px; 445 | margin: 3px 0 3px 0; 446 | border-bottom: 1px dashed white; 447 | /*font-weight: bolder;*/ 448 | } 449 | 450 | .main-div a:hover { 451 | background: #f1f1f1; 452 | color: #3e3e3e; 453 | text-decoration: none; 454 | } 455 | 456 | /* Resizer */ 457 | #resizer { 458 | width: 100%; 459 | height: 100%; 460 | border-right: 3px solid #4286f4; 461 | box-sizing: border-box; 462 | } 463 | 464 | /* Output area */ 465 | #if_r-output-area { 466 | display: none; 467 | } 468 | 469 | /* General formatting */ 470 | .code:not(#if_r-output-area), 471 | code:not(#if_r-output-area) { 472 | padding: 5px; 473 | background: lightgrey; 474 | color: black; 475 | font-family: monospace; 476 | line-height: 1.9; 477 | margin: 10px auto; 478 | } 479 | 480 | .highlighted { 481 | background: lightgrey; 482 | padding: 10px; 483 | margin: 10px auto; 484 | color: blue; 485 | } 486 | 487 | .plain-text { 488 | font-family: sans-serif; 489 | font-size: normal; 490 | line-height: 1.3; 491 | } 492 | 493 | .symlink { 494 | cursor: pointer; 495 | } 496 | 497 | .main-div h3 { 498 | font-family: sans-serif; 499 | } 500 | 501 | /* Tools */ 502 | .tool-btn { 503 | display: block; 504 | width: 80%; 505 | margin: 3px auto; 506 | padding: 10px; 507 | border: none; 508 | color: black; 509 | background-color: white; 510 | font: 18px 'Opens Sans', sans-serif; 511 | letter-spacing: 1px; 512 | appearance: none; 513 | cursor: pointer; 514 | margin-bottom: 15px; 515 | } 516 | 517 | @media (max-width: 850px) { 518 | #root { 519 | flex-wrap: wrap; 520 | } 521 | 522 | .main-div { 523 | width: 100vw; 524 | height: 90vh; 525 | } 526 | } 527 | -------------------------------------------------------------------------------- /src/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IF 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/web/index.template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <%= htmlWebpackPlugin.options.title %> 6 | 7 | 8 | 9 | 10 | 16 |
17 | 18 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/web/js/IFError.js: -------------------------------------------------------------------------------- 1 | class IFError extends Error { 2 | constructor (str = '', code, dontLogInstantly) { 3 | this.message = str 4 | this.expectedLine = this.message.split('\n')[2] 5 | this.code = code || null 6 | if (!dontLogInstantly) this.log() 7 | } 8 | 9 | log () { 10 | console.log('Error at:\n', this.expectedLine) 11 | }; 12 | } 13 | 14 | export default IFError 15 | -------------------------------------------------------------------------------- /src/web/js/globals.js: -------------------------------------------------------------------------------- 1 | const ua = window.navigator.userAgent.toLowerCase() 2 | const isIE = !!ua.match(/msie|trident\/7|edge/) 3 | 4 | const clickEvent = new Event('click') 5 | 6 | const tracking = [{ 0: 's', 1: 'Control' }] 7 | const down = [] 8 | 9 | const instructions = `/*---------- Tutorial Story ----------*/ 10 | 11 | settings> 12 | 13 | @referrable false 14 | 15 | @startAt 1 16 | 17 | @fullTimer 30000 [[3]] 18 | 19 | \${title=Tutorial Story} 20 | \${new=100} 21 | \${ten=12} 22 | \${one=a string} 23 | \${rand=random(5,10)} 24 | 25 | 31 | @first 3 32 | @music https://www.soundhelix.com/examples/mp3/SoundHelix-Song-10.mp3 33 | @sections 3 34 | @name first scene 35 | 38 | @first 2 39 | @music https://www.soundhelix.com/examples/mp3/SoundHelix-Song-8.mp3 40 | @sections 2 41 | @name second scene 42 | 45 | @first 1 46 | @music https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3 47 | @sections 1 48 | @name third scene 49 | 55 | 56 | secset> 57 | @timer 5000 [[3]] 58 | The first section Start scene two [[scene:2]] Go to the next section [[2]] Input Choice: __input \${__one} [[3]] 76 | tt>The second section Start third scene [[scene:3]] Go to next section [[3]] 88 | tt>The third \${title} Go to previous section [[2]] Start the first scene \${__new=ten} [[scene:1]] 106 | 107 | 110 | 111 |

112 | View/Fork 113 | 114 | the core Parser + Renderer 115 | or 116 | 117 | the webapp This website (editors + preview tools) 118 | 119 | on GitHub. 120 |

121 | 122 |

123 | You can use Markdown to format your story. 124 | Markdown cheat-sheet 125 |

126 | 127 |

Embedding

128 |

Within the head tag, add the following.

129 |
130 | <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/plytonrexus/if-script@v0.4-alpha/downloadable/if_r.css"> 131 |
132 | 133 |

In your body, add the following scripts.

134 |
135 | <div id="if_r-output-area"></div> 136 |
137 | <script src="Story.js"></script> 138 |
139 | <script src="https://cdn.jsdelivr.net/gh/plytonrexus/if-script@v0.4-alpha/js/if_r-terp.js"></script> 140 |
141 | <script src="https://cdn.jsdelivr.net/npm/showdown@1.9.1/dist/showdown.min.js"></script> 142 |
143 | <script> 144 | IF.methods.loadStory(IF.story); 145 | </script> 146 | 147 |
148 | 149 |

The indentation does not matter.

150 | 151 |

Comments

152 |

153 | A comment is a text block that is not integrated into the final model of the story. 154 | Comments exist to help write cleaner stories. 155 | For example, by pointing out purposes of certain portions of the story. 156 |

157 | /* 158 | A 159 |
160 | multi-line 161 |
162 | comment 163 | */ 164 |
165 | 166 |

Variables

167 |

168 | Variables truly create dynamic stories. 169 | You can use variables to store a lot of things like character names, 170 | inventory items, visited scenes, number of things and many others. 171 | You can then display the values of these variables anywhere like so: 172 |

173 | 174 |
175 | Your name is \${name}. 176 |
177 | 178 |

179 | Obviously, here, variable name was used to store the name of a character. 180 |
181 | The variables can also be used in conditional logic to determine which choices 182 | and (in a future release) what paragraphs of a section's 183 | text should be visible to the reader. 184 | You can find more about this under the choices heading. 185 |
186 | 187 |

188 | You can assign variables beforehand inside story settings. 189 |

190 | 191 |
192 | \${name='Felicity'} 193 |
194 | 195 | It is recommended (albeit not required) to keep the 196 | title variable set as the title of the story. 197 |

198 | 199 |

Story Settings

200 |

201 | Story settings allow you to customise the overall experience of the story. 202 | All of the settings are optional. 203 | The available settings are: 204 |

    205 |
  • @startAt decides the starting section of a story. (Default: 1) 206 |
  • @referrable decides if the older sections remain visible once the reader moves to a new section. (Default: false) 207 |
  • @fullTimer decides the amount of time alloted for completing a story. (Default: none) 208 |
209 |

210 |
211 | settings> 212 |
213 |   @startAt 214 |
215 |   @referrable 216 |
217 |   @fullTimer 218 |
219 | <settings 220 |
221 | 222 |

Scenes

223 |

224 | Scenes are collections of sections 225 |

226 | scene> 227 |
228 |   @first section_number (optional) 229 |
230 |   @music link (optional) 231 |
232 |   @sections space_seperated_section_numbers 233 |
234 |   @name custom_name_for_scene 235 |
236 | <scene 237 |
238 | 239 |

Sections

240 |

241 | Sections are independent locations/situations in a story. 242 | These can be reached through choices. 243 | Each section can have its own set of settings that allow it to have separate timers 244 | that send the reader to a separate section if they do not choose within specified time, 245 | and its own music. These features are particularly helpful in dramatic situations. 246 |

247 |
248 | ss> 249 |
250 |   tt> Section Title <tt 251 |
252 |   Section text. 253 |
254 |   In paragraphs. 255 |
256 |   You can use variables here. 257 |
258 |   /* 259 |
260 |     You can write choices about now. 261 |
262 |     Read on to find out how to create them. 263 |
264 |   */ 265 |
266 | <ss 267 |
268 | 269 |

Choices

270 |

271 | Choice are the sole method to navigate a story by reaching 272 | sections or scenes. 273 | To send to a section: 274 |

275 |
276 | ch> 277 |
278 |   You are named five [[5]] 279 |
280 | <ch 281 |
282 | 283 |

284 | To send to the beginning of a scene: 285 |

286 |
287 | ch> 288 |
289 |   You are named five [[scene:5]] 290 |
291 | <ch 292 |
293 | 294 |

295 | Choices can assign variables. 296 |

297 | 298 |
299 | ch> 300 |
301 |   Choose 'Felicity' as your name \${__name} [[5]] 302 |
303 | <ch 304 |
305 | 306 |

307 | Choices can also have input boxes. These input boxes can be used to 308 | take in custom values from the user and then stored in variables for 309 | later use. 310 |

311 | 312 |
313 | ch> 314 |
315 |   Type in your name here: __input \${__name} [[5]] 316 |
317 | <ch 318 |
319 | 320 |

321 | Choices can have conditions. Only if these conditions are met is the choice displayed. 322 | Available operators are: 323 |

    324 |
  • var1 == var2   Both are equal 325 |
  • var1 != var2   Both are inequal 326 |
  • var1 >= var2   First is greater than or equal to second 327 |
  • var1 <= var2   Second is greater than or equal to first 328 |
  • var1 >  var2   First is greater than second 329 |
  • var1 <  var2   Second is greater than first 330 |
331 |

332 | 333 |
334 | ch> 335 |
336 |   \${__if name == "" || namePower <= 0} 337 |
338 |     Type in your name here __input \${__name} [[5]] 339 |
340 | <ch 341 |
342 | 343 |

344 | Choices can also do actions like 345 |

346 |
    347 |
  • \${__var1 = var2}   Assignment 348 |
  • \${__var1 + var2}   Addition 349 |
  • \${__var1 - var2}   Subtraction 350 |
  • \${__var1 * var2}   Multiplication 351 |
  • \${__var1 / var2}   Division 352 |
353 |

354 | In each of these, the first variable is assigned values that result from the operation. 355 |

356 | 357 |
358 | ch> 359 |
360 |   The power of your name goes up 10 units and your health 361 | is multiplied \${namePower} times. 362 |
363 |   \${__namePower + 10} \${__health * namePower} [[5]] 364 |
365 | <ch 366 |
367 | ` 368 | 369 | const settingsHtml = ` 370 |
371 |

Settings

372 |
` 373 | 374 | export { 375 | ua, 376 | isIE, 377 | clickEvent, 378 | tracking, 379 | down, 380 | instructions, 381 | statsInstructions, 382 | helpHtml, 383 | settingsHtml 384 | } 385 | -------------------------------------------------------------------------------- /src/web/js/index.js: -------------------------------------------------------------------------------- 1 | import IFScript from 'if-script-core/src/IFScript.mjs' 2 | import { clickEvent, down, helpHtml, statsInstructions, tracking } from './globals' 3 | import w3CodeColor from '../../lib/w3Highlighter' 4 | import logo from '../../assets/images/if-logo-nobg.png' 5 | import instructions from 'if-script-core/test/examples/introduction.js' 6 | import '../css/index.css' 7 | 8 | const IF = new IFScript(IFScript.versions().STREAM) 9 | IF.start() 10 | const parseText = IF.parser.parseText 11 | const interpreter = new IF.interpreter() 12 | 13 | const $ = document.querySelector.bind(document) 14 | const $All = document.querySelectorAll.bind(document) 15 | const root = $('#root') 16 | 17 | console.log('Serving on: ' + __webpack_public_path__) 18 | 19 | const localStorageKeys = { 20 | storyText: 'if_r-story-text', 21 | statsText: 'if_r-story-stats', 22 | schemePreference: 'if_r-scheme-preference', 23 | modePreference: 'if_r-mode-preference', 24 | viewPreference: 'if_r-view-preference', 25 | objectStorage: 'if_r-if-object', 26 | editorPreference: 'if_r-editor-preference', 27 | outputPreference: 'if_r-output-preference' 28 | } 29 | 30 | const refs = { 31 | darkScheme: 'dark', 32 | lightScheme: 'light' 33 | } 34 | 35 | /// //////////////////////////////// 36 | // // 37 | // HELPER FUNCTIONS // 38 | // // 39 | /// //////////////////////////////// 40 | 41 | function fetchFile (addr, site) { 42 | fetch(addr) 43 | .then((response) => { 44 | if (!response.ok) { 45 | throw new Error('Network response was not ok') 46 | } 47 | return response.blob() 48 | }) 49 | .then((blob) => { 50 | IF[site] = window.URL.createObjectURL(blob) 51 | }) 52 | .catch((err) => console.log('Some fetch error occured.')) 53 | } 54 | 55 | function lread (key = localStorageKeys.storyText) { 56 | return localStorage.getItem(key) 57 | } 58 | 59 | function lwrite (key = localStorageKeys.storyText, value = instructions) { 60 | return localStorage.setItem(key, value) 61 | } 62 | 63 | function showAlert (text) { 64 | const alertArea = $('#alerts-area') 65 | alertArea.style.display = 'block' 66 | alertArea.innerHTML = text 67 | } 68 | 69 | function insertAtCursor (field, value) { 70 | // For textarea editor 71 | if (document.selection) { 72 | field.focus() 73 | let sel = document.selection.createRange() 74 | sel.text = value 75 | } else if (field.selectionStart || field.selectionStart == '0') { 76 | const startPos = field.selectionStart 77 | const endPos = field.selectionEnd 78 | field.value = 79 | field.value.substring(0, startPos) + 80 | value + 81 | field.value.substring(endPos, field.value.length) 82 | field.selectionStart = startPos + value.length 83 | field.selectionEnd = startPos + value.length 84 | } else { 85 | field.value += value 86 | } 87 | } 88 | 89 | function insertTextAtCaret (text) { 90 | // For editablecontent elements 91 | let sel, range 92 | if (window.getSelection) { 93 | sel = window.getSelection() 94 | if (sel.getRangeAt && sel.rangeCount) { 95 | range = sel.getRangeAt(0) 96 | range.deleteContents() 97 | range.insertNode(document.createTextNode(text)) 98 | } 99 | } else if (document.selection && document.selection.createRange) { 100 | document.selection.createRange().text = text 101 | } 102 | } 103 | 104 | function formatDoc (cmd, value) { 105 | // For editablecontent elements 106 | if ($('#if_r-input-area').style.display !== 'none') { 107 | document.execCommand(cmd, false, value) 108 | $('#if_r-input-area').focus() 109 | } 110 | } 111 | 112 | function showModal (html, attrs, styles, ...nodes) { 113 | // todo: implement unclosabillity 114 | const modal = $('#modal') 115 | modal.style.display = 'block' 116 | 117 | const content = $('.modal-content') 118 | content.innerHTML = `×
${ 119 | html ?? '' 120 | }` 121 | 122 | if (nodes) { 123 | nodes.forEach((el) => content.appendChild(el)) 124 | } 125 | 126 | Object.keys(styles).forEach((sty) => (content.style[sty] = styles[sty])) 127 | 128 | window.onclick = function (event) { 129 | if (event.target === modal) { 130 | modal.style.display = 'none' 131 | } 132 | } 133 | 134 | $('.modal-close').onclick = function () { 135 | modal.style.display = 'none' 136 | } 137 | } 138 | 139 | function closeModal (node) { 140 | if (node) return (node.style.display = 'none') 141 | $('#modal').style.display = 'none' 142 | } 143 | 144 | function useMode (mode) { 145 | const modePref = localStorageKeys.modePreference 146 | 147 | if (mode === 'compact') { 148 | $('.topnav').style.height = '0' 149 | $('.main-div').style.height = '100vh' 150 | $('#output-div').style.height = '100vh' 151 | $('#editor-div').style.height = '100vh' 152 | 153 | const aux = $('#auxbtn') 154 | aux.style.display = 'block' 155 | aux.onclick = function () { 156 | useMode('regular') 157 | aux.onclick = '' 158 | aux.style.display = 'none' 159 | } 160 | 161 | lwrite(modePref, 'compact') 162 | } else if (mode === 'regular') { 163 | $('.topnav').style.height = '10vh' 164 | $('.main-div').style.height = '90vh' 165 | $('#output-div').style.height = '90vh' 166 | $('#editor-div').style.height = '90vh' 167 | 168 | lwrite(modePref, 'regular') 169 | } 170 | } 171 | 172 | function useScheme (type) { 173 | const editor = $('#if_r-input-area') 174 | const statsEditor = $('#if_r-stats-editor') 175 | const top = $('.topnav') 176 | const tabs = $All('.tab') 177 | const tBtn = $All('.tab button') 178 | const tabCon2 = $('#output-div') 179 | 180 | if (type === refs.darkScheme) { 181 | editor.style.background = 'rgb(50, 50, 50)' 182 | editor.style.color = 'white' 183 | 184 | statsEditor.style.background = 'rgb(50, 50, 50)' 185 | statsEditor.style.color = 'white' 186 | 187 | top.style.background = 'rgb(50, 50, 50)' 188 | top.style.color = 'black' 189 | 190 | tabs.forEach((el) => (el.style.background = 'rgb(50, 50, 50)')) 191 | tBtn.forEach((el) => (el.style.color = 'whitesmoke')) 192 | 193 | tBtnHover.forEach((el) => (el.style.color = 'red')) 194 | 195 | tabCon2.style.background = 'rgb(50, 50, 50)' 196 | 197 | // tBtnHov.style.color = "black"; 198 | 199 | lwrite(localStorageKeys.schemePreference, refs.darkScheme) 200 | } else if (type === refs.lightScheme) { 201 | editor.style.background = 'whitesmoke' 202 | editor.style.color = 'rgb(40, 40, 40)' 203 | 204 | statsEditor.style.background = 'whitesmoke' 205 | statsEditor.style.color = 'rgb(40, 40, 40)' 206 | 207 | top.style.background = 'whitesmoke' 208 | // top.style.color = "black"; 209 | 210 | tabs.forEach((el) => (el.style.background = 'whitesmoke')) 211 | tBtn.forEach((el) => (el.style.color = 'black')) 212 | 213 | tabCon2.style.background = 'rgba(102, 102, 102, 0)' 214 | 215 | lwrite(localStorageKeys.schemePreference, refs.lightScheme) 216 | } 217 | } 218 | 219 | function useView (view) { 220 | const $outputDiv = $('#output-div') 221 | const $editorDiv = $('#editor-div') 222 | const viewPref = localStorageKeys.viewPreference 223 | 224 | if (!view) console.warn('The view argument is required!') 225 | 226 | if (view === 'read') { 227 | $editorDiv.style.width = '0' 228 | $outputDiv.style.width = '100%' 229 | 230 | lwrite(viewPref, 'read') 231 | } else if (view === 'write') { 232 | $outputDiv.style.width = '0' 233 | $editorDiv.style.width = '100%' 234 | 235 | lwrite(viewPref, 'write') 236 | } else if (view === 'balanced') { 237 | $outputDiv.style.width = '50%' 238 | $editorDiv.style.width = '50%' 239 | 240 | lwrite(viewPref, 'balanced') 241 | } 242 | } 243 | 244 | function isLeftClick (event) { 245 | if (event.metaKey || event.ctrlKey || event.altKey || event.shiftKey) { 246 | return false 247 | } else if ('buttons' in event) { 248 | return event.buttons === 1 249 | } else if ('which' in event) { 250 | return event.which === 1 251 | } else { 252 | return event.button === 1 || event.type === 'click' 253 | } 254 | } 255 | 256 | function handleSymlink (e) { 257 | if (e.ctrlKey) { 258 | const target = e.target.getAttribute('data-target-button') 259 | if (IF.DEBUG) { 260 | console.log(target) 261 | } 262 | if ($(target)) { 263 | $(target).click() 264 | } 265 | } 266 | } 267 | 268 | /// //////////////////////////////// 269 | // // 270 | // CREATE ELEMENT // 271 | // // 272 | /// //////////////////////////////// 273 | 274 | /** 275 | * Creates DOM nodes based on passed parameters. 276 | * 277 | * @param {string} name Name of the element 278 | * @param {object} attrs Attributes passed as an object 279 | * @param {object} styles CSS styles passed as an object 280 | * @param {object} listeners Other properties like "onclick" 281 | * and "innerHTML" of the element 282 | * @param {array} children Child nodes of the element 283 | * @returns {Node} element 284 | */ 285 | function createElement (name, attrs, styles, listeners, children) { 286 | const ele = document.createElement(name) 287 | if (attrs) { 288 | Object.keys(attrs).forEach((attr) => { 289 | ele.setAttribute(attr, attrs[attr]) 290 | }) 291 | } 292 | if (styles) { 293 | Object.keys(styles).forEach((sty) => { 294 | ele.style[sty] = styles[sty] 295 | }) 296 | } 297 | if (children) { 298 | children.forEach((child) => ele.appendChild(child)) 299 | } 300 | if (listeners) { 301 | Object.keys(listeners).forEach((lsnr) => (ele[lsnr] = listeners[lsnr])) 302 | } 303 | return ele 304 | } 305 | 306 | /* Auxilliary Button */ 307 | const auxBtn = createElement( 308 | 'button', 309 | { 310 | id: 'auxbtn', 311 | class: 'navbtn', 312 | title: 'Show menubar' 313 | }, 314 | null, 315 | { 316 | innerHTML: '☰' 317 | } 318 | ) 319 | 320 | /* Modal */ 321 | /// //////////////////////////////// 322 | // // 323 | // MODAL // 324 | // // 325 | /// //////////////////////////////// 326 | const modal = createElement( 327 | 'div', 328 | { 329 | id: 'modal', 330 | class: 'modal' 331 | }, 332 | null, 333 | null, 334 | [ 335 | createElement('div', { class: 'modal-content' }, null, { 336 | innerHTML: '×' 337 | }) 338 | ] 339 | ) 340 | 341 | /* Topnav */ 342 | /// //////////////////////////////// 343 | // // 344 | // TOPNAV // 345 | // // 346 | /// //////////////////////////////// 347 | const storyBtn = createElement( 348 | 'a', 349 | { 350 | class: 'download-btn tooltip navbtn', 351 | download: 'story.js' 352 | }, 353 | null, 354 | { 355 | onclick: function () { 356 | if (!interpreter.run.story || Object.keys(interpreter.run.story).length <= 0) { 357 | return console.log('Parse a story at least once.') 358 | } 359 | const data = new Blob([`const IF = ${JSON.stringify(IF)}`], { 360 | type: 'text/javascript' 361 | }) 362 | storyBtn.setAttribute('href', window.URL.createObjectURL(data)) 363 | }, 364 | innerHTML: `Download story 365 | Download the story file for embedding` 366 | } 367 | ) 368 | 369 | const storyTextBtn = createElement( 370 | 'a', 371 | { 372 | class: 'download-btn tooltip navbtn', 373 | download: 'story.txt' 374 | }, 375 | null, 376 | { 377 | onclick: function () { 378 | const data = new Blob([$('#if_r-input-area').innerText], { 379 | type: 'text/plain' 380 | }) 381 | storyTextBtn.setAttribute('href', window.URL.createObjectURL(data)) 382 | }, 383 | innerHTML: `Download story text 384 | Download the text of the story` 385 | } 386 | ) 387 | 388 | const parseBtn = createElement( 389 | 'a', 390 | { 391 | id: 'submit-btn', 392 | class: 'tooltip navbtn', 393 | title: 'Run' 394 | }, 395 | null, 396 | { 397 | onclick: runSubmit, 398 | innerHTML: '▶ Run Play the story' 399 | } 400 | ) 401 | 402 | const previewBtn = createElement( 403 | 'a', 404 | { 405 | id: 'preview-btn', 406 | class: 'download-btn tooltip navbtn' 407 | }, 408 | null, 409 | { 410 | onclick: function () { 411 | if (Object.keys(interpreter.run.story).length <= 0) 412 | return showAlert('You haven\'t run a story yet.') 413 | else { 414 | window.open('preview') 415 | } 416 | }, 417 | innerHTML: '👁 Preview' 418 | } 419 | ) 420 | 421 | /// /////// SETTINGS ////////// 422 | const darkBtn = createElement( 423 | 'button', 424 | { 425 | id: 'darkbtn', 426 | class: 'setting' 427 | }, 428 | { background: 'rgb(50, 50, 50)', color: 'white' }, 429 | { 430 | onclick: function (e) { 431 | useScheme(refs.darkScheme) 432 | e.target.style.display = 'none' 433 | $('#lightbtn').style.display = 'block' 434 | }, 435 | innerHTML: 'Dark Mode' 436 | } 437 | ) 438 | 439 | const lightBtn = createElement( 440 | 'button', 441 | { 442 | id: 'lightbtn', 443 | class: 'setting' 444 | }, 445 | null, 446 | { 447 | onclick: function (e) { 448 | useScheme(refs.lightScheme) 449 | e.target.style.display = 'none' 450 | $('#darkbtn').style.display = 'block' 451 | }, 452 | innerHTML: 'Light Mode' 453 | } 454 | ) 455 | 456 | const settingsBtn = createElement( 457 | 'a', 458 | { 459 | id: 'settings-btn', 460 | class: 'tooltip navbtn' 461 | }, 462 | null, 463 | { 464 | onclick: function () { 465 | const scheme = lread(localStorageKeys.schemePreference) 466 | showModal( 467 | null, 468 | null, 469 | { 'font-family': 'monospace' }, 470 | createElement('div', null, null, { innerHTML: '

Settings

' }), 471 | createElement('h3', null, null, { innerHTML: 'Scheme Preference' }), 472 | lightBtn, 473 | darkBtn, 474 | createElement('h3', null, null, { 475 | innerHTML: 'Editor Alignment (Stub)' 476 | }), 477 | createElement('h3', null, null, { innerHTML: 'Font Size (Stub)' }), 478 | createElement('h3', null, null, { innerHTML: 'Font (Stub)' }) 479 | ) 480 | }, 481 | innerHTML: '⚙Setings' 482 | } 483 | ) 484 | 485 | /// /////// SETTINGS END ////////// 486 | 487 | const readBtn = createElement( 488 | 'a', 489 | { 490 | id: 'read-btn', 491 | class: 'right-align-top content-opts tooltip navbtn' 492 | }, 493 | null, 494 | { 495 | onclick: function () { 496 | useView('read') 497 | }, 498 | innerHTML: '←Read View' 499 | } 500 | ) 501 | 502 | const writeBtn = createElement( 503 | 'a', 504 | { 505 | id: 'write-btn', 506 | class: 'right-align-top content-opts tooltip navbtn' 507 | }, 508 | null, 509 | { 510 | onclick: function () { 511 | useView('write') 512 | }, 513 | innerHTML: '→Write View' 514 | } 515 | ) 516 | 517 | const balanceBtn = createElement( 518 | 'a', 519 | { 520 | id: 'balance-btn', 521 | class: 'right-align-top content-opts tooltip navbtn' 522 | }, 523 | null, 524 | { 525 | onclick: function () { 526 | useView('balanced') 527 | }, 528 | innerHTML: '⇆Balanced View' 529 | } 530 | ) 531 | 532 | const upBtn = createElement( 533 | 'a', 534 | { 535 | id: 'upbtn', 536 | class: 'right-align-top content-opts tooltip navbtn' 537 | }, 538 | null, 539 | { 540 | onclick: function () { 541 | useMode('compact') 542 | }, 543 | innerHTML: `× 544 | Close Menubar` 545 | } 546 | ) 547 | 548 | const logoBtn = createElement( 549 | 'a', 550 | { 551 | id: 'logo', 552 | class: 'topnav-btn', 553 | href: __webpack_public_path__ || '/', 554 | title: 'IF-Script logo; Reload this page' 555 | }, 556 | null, 557 | null, 558 | [ 559 | (function () { 560 | let img = 561 | createElement('img', { src: logo, id: 'logo-img' }, { height: '50px', width: '50px' }) 562 | img.src = logo 563 | return img 564 | })() 565 | ] 566 | ) 567 | 568 | const topnav = createElement( 569 | 'div', 570 | { 571 | class: 'topnav' 572 | }, 573 | null, 574 | null, 575 | [ 576 | parseBtn, 577 | previewBtn, 578 | storyBtn, 579 | storyTextBtn, 580 | settingsBtn, 581 | readBtn, 582 | writeBtn, 583 | balanceBtn, 584 | upBtn, 585 | logoBtn 586 | ] 587 | ) 588 | 589 | /// //////////////////////////////// 590 | // // 591 | // EDITOR - DIV // 592 | // // 593 | /// //////////////////////////////// 594 | /* div1 */ 595 | const editor = createElement('textarea', { 596 | id: 'if_r-input-area', 597 | class: 'editor', 598 | contenteditable: 'true', 599 | spellcheck: 'false' 600 | }) 601 | 602 | const statsEditor = createElement('textarea', { 603 | id: 'if_r-stats-editor', 604 | class: 'editor' 605 | }) 606 | 607 | function openCity1 (evt, target) { 608 | let i, tabcontent, tablinks 609 | tabcontent = document.getElementsByClassName('tabcontent1') 610 | for (i = 0; i < tabcontent.length; i++) { 611 | tabcontent[i].style.display = 'none' 612 | } 613 | tablinks = document.getElementsByClassName('tablinks1') 614 | for (i = 0; i < tablinks.length; i++) { 615 | tablinks[i].className = tablinks[i].className.replace(' active', '') 616 | } 617 | $(target).style.display = 'block' 618 | evt.currentTarget.className += ' active' 619 | 620 | lwrite( 621 | localStorageKeys.editorPreference, 622 | '#' + evt.currentTarget.getAttribute('id') 623 | ) 624 | } 625 | 626 | /// //////////////////////////////// 627 | // // 628 | // TABLINKS - 1 // 629 | // // 630 | /// //////////////////////////////// 631 | /* Tab links */ 632 | const storyEd = createElement( 633 | 'button', 634 | { 635 | class: 'tablinks1 active', 636 | id: 'story-tablink', 637 | title: 'Story Editor' 638 | }, 639 | null, 640 | { 641 | onclick: (e) => openCity1(e, '#Story'), 642 | innerHTML: 'Story' 643 | } 644 | ) 645 | 646 | const statEd = createElement( 647 | 'button', 648 | { 649 | class: 'tablinks1', 650 | id: 'stats-tablink', 651 | title: 'Stats Editor' 652 | }, 653 | null, 654 | { 655 | onclick: (e) => openCity1(e, '#Stats'), 656 | innerHTML: 'Stats' 657 | } 658 | ) 659 | 660 | /// //////////////////////////////// 661 | // // 662 | // TABS - 1 // 663 | // // 664 | /// //////////////////////////////// 665 | /* Tabs drawer */ 666 | const tabs1 = createElement( 667 | 'div', 668 | { 669 | class: 'tab' 670 | }, 671 | null, 672 | null, 673 | [storyEd, statEd] 674 | ) 675 | 676 | /// //////////////////////////////// 677 | // // 678 | // TAB - CONTENT - 1 // 679 | // // 680 | /// //////////////////////////////// 681 | /* Tab content */ 682 | const storcon = createElement( 683 | 'div', 684 | { 685 | id: 'Story', 686 | class: 'tabcontent1' 687 | }, 688 | null, 689 | null, 690 | [editor] 691 | ) 692 | 693 | const statcon = createElement( 694 | 'div', 695 | { 696 | id: 'Stats', 697 | class: 'tabcontent1' 698 | }, 699 | null, 700 | null, 701 | [statsEditor] 702 | ) 703 | 704 | /// //////////////////////////////// 705 | // // 706 | // OUTPUT - DIV // 707 | // // 708 | /// //////////////////////////////// 709 | /* div2 */ 710 | function openCity2 (evt, target) { 711 | let i, tabcontent, tablinks 712 | tabcontent = document.getElementsByClassName('tabcontent2') 713 | for (i = 0; i < tabcontent.length; i++) { 714 | tabcontent[i].style.display = 'none' 715 | } 716 | tablinks = document.getElementsByClassName('tablinks2') 717 | for (i = 0; i < tablinks.length; i++) { 718 | tablinks[i].className = tablinks[i].className.replace(' active', '') 719 | } 720 | 721 | $(target).style.display = 'block' 722 | evt.currentTarget.className += ' active' 723 | 724 | lwrite( 725 | localStorageKeys.outputPreference, 726 | '#' + evt.currentTarget.getAttribute('id') 727 | ) 728 | } 729 | 730 | const output = createElement( 731 | 'div', 732 | { 733 | id: 'if_r-output-area' 734 | }, 735 | { 736 | display: 'none' 737 | } 738 | ) 739 | 740 | /// //////////////////////////////// 741 | // // 742 | // TABLINKS - 2 // 743 | // // 744 | /// //////////////////////////////// 745 | /* Tab links */ 746 | const help = createElement( 747 | 'button', 748 | { 749 | class: 'tablinks2', 750 | id: 'help-tablink', 751 | title: 'Documentation for IF-Script' 752 | }, 753 | null, 754 | { 755 | onclick: (e) => openCity2(e, '#Help'), 756 | innerHTML: 'Help' 757 | } 758 | ) 759 | 760 | const tools = createElement( 761 | 'button', 762 | { 763 | class: 'tablinks2', 764 | id: 'tools-tablink', 765 | title: 'Tools for editing the story' 766 | }, 767 | null, 768 | { 769 | onclick: (e) => openCity2(e, '#Tools'), 770 | innerHTML: 'Tools' 771 | } 772 | ) 773 | 774 | const outputDiv = createElement( 775 | 'button', 776 | { 777 | class: 'tablinks2', 778 | id: 'output-tablink', 779 | title: 'Preview your story' 780 | }, 781 | null, 782 | { 783 | onclick: (e) => openCity2(e, '#Output'), 784 | innerHTML: 'Output' 785 | } 786 | ) 787 | 788 | /// //////////////////////////////// 789 | // // 790 | // TABS - 2 // 791 | // // 792 | /// //////////////////////////////// 793 | /* Tabs drawer */ 794 | const tabs2 = createElement( 795 | 'div', 796 | { 797 | class: 'tab' 798 | }, 799 | null, 800 | null, 801 | [help, tools, outputDiv] 802 | ) 803 | 804 | /// //////////////////////////////// 805 | // // 806 | // TAB - CONTENT - 2 // 807 | // // 808 | /// //////////////////////////////// 809 | /* Tab content */ 810 | 811 | const helpcon = createElement( 812 | 'div', 813 | { 814 | id: 'Help', 815 | class: 'tabcontent2' 816 | }, 817 | null, 818 | { 819 | innerHTML: helpHtml 820 | } 821 | ) 822 | 823 | /// ////////// TOOLS /////////////// 824 | 825 | const insertSectionBtn = createElement( 826 | 'button', 827 | { 828 | class: 'tool-btn' 829 | }, 830 | null, 831 | { 832 | innerHTML: 'Add Section', 833 | onclick: function (event) { 834 | const sectionText = ` 835 | ss> 836 | secset> 837 | @timer 10000 838 | @music link 839 | Untitled Section A choice [[1]] Another choice [[2]] 865 | @first section_number 866 | @music link 867 | @sections space_seperated_section_numbers 868 | @name custom_name_for_scene 869 | \${__if name == "" || namePower <= 0} Type in your name here __input \${__name} [[5]] Click Run to start playing!

' 933 | } 934 | ) 935 | 936 | const outcon = createElement( 937 | 'div', 938 | { 939 | id: 'Output', 940 | class: 'tabcontent2' 941 | }, 942 | null, 943 | null, 944 | [instructDiv, output] 945 | ) 946 | 947 | /// //////////////////////////////// 948 | // // 949 | // MAIN - DIVs // 950 | // // 951 | /// //////////////////////////////// 952 | /* Main divs */ 953 | const div1 = createElement( 954 | 'div', 955 | { 956 | class: 'main-div', 957 | id: 'editor-div' 958 | }, 959 | { 960 | left: '0' 961 | }, 962 | null, 963 | [tabs1, storcon, statcon] 964 | ) 965 | const div2 = createElement( 966 | 'div', 967 | { 968 | class: 'main-div', 969 | id: 'output-div' 970 | }, 971 | { 972 | right: '0' 973 | }, 974 | null, 975 | [tabs2, helpcon, toolcon, outcon] 976 | ) 977 | 978 | const mainDiv = createElement( 979 | 'div', 980 | { 981 | id: 'main' 982 | }, 983 | null, 984 | null, 985 | [div1, div2] 986 | ) 987 | 988 | /// //////////////////////////////// 989 | // // 990 | // APPEND TO ROOT // 991 | // // 992 | /// //////////////////////////////// 993 | /* Append to root */ 994 | root.appendChild(modal) 995 | root.appendChild(auxBtn) 996 | root.appendChild(topnav) 997 | root.appendChild(mainDiv) 998 | 999 | /// //////////////////////////////// 1000 | // // 1001 | // GETTING READY, FINALLY! // 1002 | // // 1003 | /// //////////////////////////////// 1004 | /* Getting ready... */ 1005 | 1006 | /* Tab preferences */ 1007 | $(lread(localStorageKeys.outputPreference) || '#help-tablink').dispatchEvent( 1008 | clickEvent 1009 | ) 1010 | $(lread(localStorageKeys.editorPreference) || '#story-tablink').dispatchEvent( 1011 | clickEvent 1012 | ) 1013 | 1014 | /* For textarea-style editors */ 1015 | $('#if_r-input-area').value = lread(localStorageKeys.storyText) || instructions 1016 | $('#if_r-stats-editor').value = 1017 | lread(localStorageKeys.statsText) || statsInstructions 1018 | $All('.highlighted').forEach((ele) => w3CodeColor(ele)) 1019 | 1020 | /* Colour scheme preferences */ 1021 | if (lread(localStorageKeys.schemePreference)) { 1022 | useScheme(lread(localStorageKeys.schemePreference)) 1023 | } else { 1024 | useScheme(refs.lightScheme) 1025 | } 1026 | /* Menubar preferences */ 1027 | if (lread(localStorageKeys.modePreference)) { 1028 | useMode(lread(localStorageKeys.modePreference)) 1029 | } 1030 | /* Read/Write View Preferences */ 1031 | if (lread(localStorageKeys.viewPreference)) { 1032 | useView(lread(localStorageKeys.viewPreference)) 1033 | } 1034 | 1035 | /* Ctrl + S Save implementation */ 1036 | window.onkeydown = function (e) { 1037 | if (!down.includes(e.key)) down.push(e.key) 1038 | if (e.ctrlKey && !down.includes(tracking[0]['1'])) down.push('Control') 1039 | 1040 | if (down.length === 2 && down.includes(tracking[0]['0']) && e.ctrlKey) { 1041 | e.preventDefault() 1042 | saveStory($('#if_r-input-area').value) 1043 | saveStats($('#if_r-stats-editor').value) 1044 | document.title = 'Saved all edits' 1045 | setTimeout(() => { 1046 | document.title = story_title ?? 'IF' 1047 | }, 1500) 1048 | } 1049 | } 1050 | 1051 | window.onkeyup = function (e) { 1052 | let idx 1053 | if (e.ctrlKey) { 1054 | idx = down.findIndex((val) => val === 'Control') 1055 | } else { 1056 | idx = down.findIndex((val) => val === e.key) 1057 | } 1058 | down.splice(idx, 1) 1059 | } 1060 | 1061 | $All('.symlink').forEach((el) => el.addEventListener('click', handleSymlink)) 1062 | 1063 | /// //////////////////////////////// 1064 | // // 1065 | // AUTOSAVE IMPLEMENTATION // 1066 | // // 1067 | /// //////////////////////////////// 1068 | /* Autosave implementation */ 1069 | let editor_timeout, stats_timeout 1070 | 1071 | function saveStory (text) { 1072 | const story_title = interpreter.run.story.name ? interpreter.run.state.name : null 1073 | lwrite(localStorageKeys.storyText, text) 1074 | document.title = 'Story Saved.' 1075 | setTimeout(() => { 1076 | document.title = story_title ?? 'IF' 1077 | }, 1500) 1078 | } 1079 | 1080 | function saveStats (text) { 1081 | const story_title = interpreter.run.story.name ? interpreter.run.story.name : null 1082 | lwrite(localStorageKeys.statsText, text) 1083 | document.title = 'Stats Saved.' 1084 | setTimeout(() => { 1085 | document.title = story_title ?? 'IF' 1086 | }, 1500) 1087 | } 1088 | 1089 | $('#if_r-input-area').addEventListener('keyup', function (e) { 1090 | if (editor_timeout) clearTimeout(editor_timeout) 1091 | 1092 | editor_timeout = setTimeout(function () { 1093 | saveStory(e.target.value) 1094 | }, 750) 1095 | }) 1096 | 1097 | $('#if_r-stats-editor').addEventListener('keyup', function (e) { 1098 | if (stats_timeout) clearTimeout(stats_timeout) 1099 | 1100 | editor_timeout = setTimeout(function () { 1101 | saveStats(e.target.value) 1102 | }, 750) 1103 | }) 1104 | 1105 | /// //////////////////////////////// 1106 | // // 1107 | // PARSING // 1108 | // // 1109 | /// //////////////////////////////// 1110 | /* Parsing */ 1111 | function runSubmit () { 1112 | const storyValue = $('#if_r-input-area').value 1113 | const statsValue = $('#if_r-stats-editor').value 1114 | 1115 | $('#instructDiv').style.display = 'none' 1116 | $('#if_r-output-area').style.display = 'flex' 1117 | 1118 | $('#output-tablink').dispatchEvent(clickEvent) 1119 | 1120 | lwrite(localStorageKeys.storyText, storyValue) 1121 | lwrite(localStorageKeys.statsText, statsValue) 1122 | 1123 | const story = parseText(storyValue) 1124 | 1125 | lwrite(localStorageKeys.objectStorage, JSON.stringify(story)) 1126 | 1127 | interpreter.loadStory(story, null, 'default') 1128 | } 1129 | -------------------------------------------------------------------------------- /src/web/preview/index.js: -------------------------------------------------------------------------------- 1 | import IFScript from 'if-script-core/src/IFScript.mjs' 2 | import Story from 'if-script-core/src/models/Story.mjs' 3 | 4 | const IF = new IFScript(IFScript.versions().STREAM) 5 | 6 | const interpreter = new IF.interpreter() 7 | 8 | let storyObj = JSON.parse(localStorage.getItem('if_r-if-object')) 9 | const story = new Story(storyObj.name, { sections: storyObj.sections, passages: [], scenes: storyObj.scenes }, storyObj.settings, {globals: null, stats: null}) 10 | interpreter.loadStory(story, null, 'bricks') 11 | document.title = interpreter.run.story.name ?? 'IF | Preview' 12 | -------------------------------------------------------------------------------- /src/web/preview/index.template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Previewing... 7 | 15 | 16 | 17 |
18 | 19 | 20 | -------------------------------------------------------------------------------- /src/web/service-worker.js: -------------------------------------------------------------------------------- 1 | const CACHE_VERSIONS = { 2 | css: 1, 3 | terp: 1, 4 | fonts: 2, 5 | images: 1 6 | } 7 | 8 | const CURRENT_CACHES = {} 9 | 10 | const addrs = { 11 | css: ['css/index.css', 'downloadable/if_r.css'], 12 | fonts: ['assets/fonts/FiraMono-Regular.ttf', 'assets/fonts/VarelaRound-Regular.ttf'], 13 | images: ['assets/images/book-background0.jpg', 'assets/images/if-logo.png'] 14 | } 15 | 16 | const props = Object.keys(CACHE_VERSIONS) 17 | props.forEach(val => { 18 | CURRENT_CACHES[val] = `${val}-v${CACHE_VERSIONS[val]}` 19 | }) 20 | 21 | self.addEventListener('install', event => 22 | event.waitUntil( 23 | props.forEach(prop => 24 | caches 25 | .open(CURRENT_CACHES[prop]) 26 | .then(cache => Object.keys(addrs).forEach(val => val.forEach(addr => cache.add(addr)))) 27 | .catch(e => console.log(e))) 28 | )) 29 | 30 | self.addEventListener('fetch', function (event) { 31 | const preTypes = [/\/js\//, /\/css\//, /\/images\//, /\/fonts\//] 32 | 33 | const index = preTypes.findIndex(preType => { 34 | return !!event.request.url.match(preType) 35 | }) 36 | 37 | let prop = 'css' 38 | 39 | if (index !== -1) { prop = event.request.url.match(preTypes[index])[0].replace(/\//g, '') } 40 | 41 | event.respondWith( 42 | caches.open(CURRENT_CACHES[prop]).then(function (cache) { 43 | return cache.match(event.request).then(function (response) { 44 | if (response) { 45 | return response 46 | } 47 | return fetch(event.request.clone()).then(function (response) { 48 | if (response.status < 400 && 49 | response.url && 50 | response.url.match(new RegExp(prop, 'i'))) { 51 | cache.put(event.request, response.clone()) 52 | } 53 | return response 54 | }).catch(e => console.log('Why not?', e)) 55 | }).catch(function (error) { 56 | throw error 57 | }) 58 | }) 59 | ) 60 | }) 61 | --------------------------------------------------------------------------------