| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include <process/help.hpp> |
| |
| #include <map> |
| #include <string> |
| #include <vector> |
| |
| #include <process/future.hpp> |
| #include <process/http.hpp> |
| #include <process/process.hpp> |
| |
| #include <stout/foreach.hpp> |
| #include <stout/json.hpp> |
| #include <stout/option.hpp> |
| #include <stout/preprocessor.hpp> |
| #include <stout/stringify.hpp> |
| #include <stout/strings.hpp> |
| |
| using std::map; |
| using std::string; |
| using std::vector; |
| |
| namespace process { |
| |
| string HELP( |
| const string& tldr, |
| const Option<string>& description, |
| const Option<string>& authentication, |
| const Option<string>& authorization, |
| const Option<string>& references) |
| { |
| // Construct the help string. |
| string help = |
| "### TL;DR; ###\n" + |
| tldr; |
| |
| if (!strings::endsWith(help, "\n")) { |
| help += "\n"; |
| } |
| |
| if (description.isSome()) { |
| help += |
| "\n" |
| "### DESCRIPTION ###\n" + |
| description.get(); |
| } |
| |
| if (authentication.isSome()) { |
| help += |
| "\n" |
| "### AUTHENTICATION ###\n" + |
| authentication.get(); |
| } |
| |
| if (authorization.isSome()) { |
| help += |
| "\n" |
| "### AUTHORIZATION ###\n" + |
| authorization.get(); |
| } |
| |
| if (!strings::endsWith(help, "\n")) { |
| help += "\n"; |
| } |
| |
| if (references.isSome()) { |
| help += "\n"; |
| help += references.get(); |
| } |
| |
| return help; |
| } |
| |
| |
| Help::Help(const Option<string>& _delegate) |
| : ProcessBase("help"), |
| delegate(_delegate) {} |
| |
| |
| string Help::getUsagePath(const string& id, const string& name) |
| { |
| return id + strings::remove(name, "/", strings::Mode::SUFFIX); |
| } |
| |
| |
| void Help::add( |
| const string& id, |
| const string& name, |
| const Option<string>& help) |
| { |
| // TODO(benh): Enable help for help. |
| if (id != "help" && id != "__processes__") { |
| // Remove tail slash in usage information. |
| const string path = "/" + getUsagePath(id, name); |
| |
| if (help.isSome()) { |
| string usage = "### USAGE ###\n"; |
| // If a delegate is set, we have 2 valid endpoints we could hit. |
| // /name *and* /id/name. Add it in. |
| if (delegate == id) { |
| usage += USAGE(getUsagePath("", name)); |
| } |
| usage += USAGE(path) + "\n"; |
| helps[id][name] = usage + help.get(); |
| } else { |
| helps[id][name] = "## No help page for `" + path + "`\n"; |
| } |
| |
| route("/" + id, "Help for " + id, &Help::help); |
| } |
| } |
| |
| |
| bool Help::remove(const string& id, const string& name) |
| { |
| if (helps.count(id) == 0 || helps[id].count(name) == 0) { |
| return false; |
| } |
| |
| helps[id].erase(name); |
| |
| if (helps[id].empty()) { |
| helps.erase(id); |
| } |
| |
| return true; |
| } |
| |
| |
| bool Help::remove(const string& id) |
| { |
| return helps.erase(id) == 1; |
| } |
| |
| |
| void Help::initialize() |
| { |
| route("/", None(), &Help::help); |
| } |
| |
| |
| // Write the help strings contained in the help process to JSON. |
| // The schema looks as follows: |
| // { |
| // "processes": |
| // [ |
| // { |
| // "id": id, |
| // "endpoints": [ { "name" : name, "text" : text }, ... ] |
| // }, |
| // ... |
| // ] |
| // } |
| void json(JSON::ObjectWriter* writer, const Help& help) |
| { |
| // We must declare this temporary typedef in order to make the |
| // foreachpair macro happy. Otherwise it interprets the ',' in the |
| // map definition to denote a new parameter in the macro invocation. |
| typedef map<string, string> StringStringMap; |
| |
| writer->field("processes", [&help](JSON::ArrayWriter* writer) { |
| foreachpair (const string& id, const StringStringMap& names, help.helps) { |
| writer->element([&id, &names](JSON::ObjectWriter* writer) { |
| writer->field("id", id); |
| writer->field("endpoints", [&names](JSON::ArrayWriter* writer) { |
| foreachpair (const string& name, const string& text, names) { |
| writer->element([&name, &text](JSON::ObjectWriter* writer) { |
| writer->field("name", name); |
| writer->field("text", text); |
| }); |
| } |
| }); |
| }); |
| } |
| }); |
| } |
| |
| |
| Future<http::Response> Help::help(const http::Request& request) |
| { |
| // Path format of the url: /help/<id>[/<name>]. |
| // Note that <name> may contain slashes. |
| vector<string> tokens = strings::tokenize(request.url.path, "/", 3); |
| |
| Option<string> id = None(); |
| Option<string> name = None(); |
| |
| if (tokens.size() > 1) { |
| id = tokens[1]; |
| } |
| |
| if (tokens.size() > 2) { |
| name = tokens[2]; |
| } |
| |
| string document; |
| string references; |
| |
| if (id.isNone()) { // http://ip:port/help |
| // If the request query string has format=json, return the JSON |
| // representation of the helps strings. |
| // |
| // NOTE: We avoided relying on the 'Accept' header to specify the |
| // format because if users are hitting the endpoint from a browser |
| // it is difficult to change the 'Accept' header. |
| // |
| // TODO(klueska): add the ability for all /help/* endpoints to be |
| // returned as json, not just /help. |
| if (request.url.query.get("format") == Some("json")) { |
| return http::OK(jsonify(*this)); |
| } |
| |
| document += "## HELP\n"; |
| foreachkey (const string& id, helps) { |
| document += "> [/" + id + "][" + id + "]\n"; |
| references += "[" + id + "]: help/" + id + "\n"; |
| } |
| } else if (name.isNone()) { // http://ip:port/help/id |
| if (helps.count(id.get()) == 0) { |
| return http::BadRequest( |
| "No help available for '/" + id.get() + "'.\n"); |
| } |
| |
| document += "## `/" + id.get() + "` ##\n"; |
| foreachkey (const string& name, helps[id.get()]) { |
| const string path = getUsagePath(id.get(), name); |
| document += "> [/" + path + "][" + path + "]\n"; |
| references += "[" + path + "]: " + path + "\n"; |
| } |
| } else { // http://ip:port/help/id/name |
| if (helps.count(id.get()) == 0) { |
| return http::BadRequest( |
| "No help available for '/" + id.get() + "'.\n"); |
| } else if (helps[id.get()].count("/" + name.get()) == 0) { |
| return http::BadRequest( |
| "No help available for '/" + id.get() + "/" + name.get() + "'.\n"); |
| } |
| |
| document += helps[id.get()]["/" + name.get()]; |
| } |
| |
| // Final Markdown is 'document' followed by the 'references'. |
| string markdown = document + "\n" + references; |
| |
| // Just send the Markdown if we aren't speaking to a browser. For |
| // now we only check for the 'curl' or 'http' utilities. |
| Option<string> agent = request.headers.get("User-Agent"); |
| |
| if (agent.isSome() && |
| (strings::startsWith(agent.get(), "curl") || |
| strings::startsWith(agent.get(), "HTTPie"))) { |
| http::Response response = http::OK(markdown); |
| response.headers["Content-Type"] = "text/x-markdown"; |
| return response; |
| } |
| |
| // Need to JSONify the markdown for embedding into JavaScript. |
| markdown = stringify(JSON::String(markdown)); |
| |
| // Provide some JavaScript to render the Markdown into some aesthetically |
| // pleasing HTML. ;) |
| http::Response response = http::OK( |
| "<html>" |
| "<head>" |
| "<title>Help</title>" |
| "<script>" |
| " /**" |
| " * Minified version of:" |
| " * marked - a markdown parser" |
| " * Copyright (c) 2011-2013, Christopher Jeffrey. (MIT Licensed)" |
| " * https://github.com/chjj/marked" |
| " */" |
| " (function(){var d={newline:/^\\n+/,code:/^( {4}[^\\n]+\\n*)+/,fences:j,hr:/^( *[-*_]){3,} *(?:\\n+|$)/,heading:/^ *(#{1,6}) *([^\\n]+?) *#* *(?:\\n+|$)/,nptable:j,lheading:/^([^\\n]+)\\n *(=|-){2,} *(?:\\n+|$)/,blockquote:/^( *>[^\\n]+(\\n[^\\n]+)*\\n*)+/,list:/^( *)(bull) [\\s\\S]+?(?:hr|\\n{2,}(?! )(?!\\1bull )\\n*|\\s*$)/,html:/^ *(?:comment|closed|closing) *(?:\\n{2,}|\\s*$)/,def:/^ *\\[([^\\]]+)\\]: *<?([^\\s>]+)>?(?: +[\"(]([^\\n]+)[\")])? *(?:\\n+|$)/,table:j,paragraph:/^((?:[^\\n]+\\n?(?!hr|heading|lheading|blockquote|tag|def))+)\\n*/,text:/^[^\\n]+/};d.bullet=/(?:[*+-]|\\d+\\.)/;d.item=/^( *)(bull) [^\\n]*(?:\\n(?!\\1bull )[^\\n]*)*/;d.item=c(d.item,\"gm\")(/bull/g,d.bullet)();d.list=c(d.list)(/bull/g,d.bullet)(\"hr\",/\\n+(?=(?: *[-*_]){3,} *(?:\\n+|$))/)();d._tag=\"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\\\b)\\\\w+(?!:/|@)\\\\b\";d.html=c(d.html)(\"comment\",/<!--[\\s\\S]*?-->/)(\"closed\",/<(tag)[\\s\\S]+?<\\/\\1>/)(\"closing\",/<tag(?:\"[^\"]*\"|'[^']*'|[^'\">])*?>/)(/tag/g,d._tag)();d.paragraph=c(d.paragraph)(\"hr\",d.hr)(\"heading\",d.heading)(\"lheading\",d.lheading)(\"blockquote\",d.blockquote)(\"tag\",\"<\"+d._tag)(\"def\",d.def)();d.normal=g({},d);d.gfm=g({},d.normal,{fences:/^ *(`{3,}|~{3,}) *(\\S+)? *\\n([\\s\\S]+?)\\s*\\1 *(?:\\n+|$)/,paragraph:/^/});d.gfm.paragraph=c(d.paragraph)(\"(?!\",\"(?!\"+d.gfm.fences.source.replace(\"\\\\1\",\"\\\\2\")+\"|\"+d.list.source.replace(\"\\\\1\",\"\\\\3\")+\"|\")();d.tables=g({},d.gfm,{nptable:/^ *(\\S.*\\|.*)\\n *([-:]+ *\\|[-| :]*)\\n((?:.*\\|.*(?:\\n|$))*)\\n*/,table:/^ *\\|(.+)\\n *\\|( *[-:]+[-| :]*)\\n((?: *\\|.*(?:\\n|$))*)\\n*/});function b(k){this.tokens=[];this.tokens.links={};this.options=k||a.defaults;this.rules=d.normal;if(this.options.gfm){if(this.options.tables){this.rules=d.tables}else{this.rules=d.gfm}}}b.rules=d;b.lex=function(m,k){var l=new b(k);return l.lex(m)};b.prototype.lex=function(k){k=k.replace(/\\r\\n|\\r/g,\"\\n\").replace(/\\t/g,\" \").replace(/\\u00a0/g,\" \").replace(/\\u2424/g,\"\\n\");return this.token(k,true)};b.prototype.token=function(m,s){var m=m.replace(/^ +$/gm,\"\"),q,o,u,r,t,v,k,p,n;while(m){if(u=this.rules.newline.exec(m)){m=m.substring(u[0].length);if(u[0].length>1){this.tokens.push({type:\"space\"})}}if(u=this.rules.code.exec(m)){m=m.substring(u[0].length);u=u[0].replace(/^ {4}/gm,\"\");this.tokens.push({type:\"code\",text:!this.options.pedantic?u.replace(/\\n+$/,\"\"):u});continue}if(u=this.rules.fences.exec(m)){m=m.substring(u[0].length);this.tokens.push({type:\"code\",lang:u[2],text:u[3]});continue}if(u=this.rules.heading.exec(m)){m=m.substring(u[0].length);this.tokens.push({type:\"heading\",depth:u[1].length,text:u[2]});continue}if(s&&(u=this.rules.nptable.exec(m))){m=m.substring(u[0].length);v={type:\"table\",header:u[1].replace(/^ *| *\\| *$/g,\"\").split(/ *\\| */),align:u[2].replace(/^ *|\\| *$/g,\"\").split(/ *\\| */),cells:u[3].replace(/\\n$/,\"\").split(\"\\n\")};for(p=0;p<v.align.length;p++){if(/^ *-+: *$/.test(v.align[p])){v.align[p]=\"right\"}else{if(/^ *:-+: *$/.test(v.align[p])){v.align[p]=\"center\"}else{if(/^ *:-+ *$/.test(v.align[p])){v.align[p]=\"left\"}else{v.align[p]=null}}}}for(p=0;p<v.cells.length;p++){v.cells[p]=v.cells[p].split(/ *\\| */)}this.tokens.push(v);continue}if(u=this.rules.lheading.exec(m)){m=m.substring(u[0].length);this.tokens.push({type:\"heading\",depth:u[2]===\"=\"?1:2,text:u[1]});continue}if(u=this.rules.hr.exec(m)){m=m.substring(u[0].length);this.tokens.push({type:\"hr\"});continue}if(u=this.rules.blockquote.exec(m)){m=m.substring(u[0].length);this.tokens.push({type:\"blockquote_start\"});u=u[0].replace(/^ *> ?/gm,\"\");this.token(u,s);this.tokens.push({type:\"blockquote_end\"});continue}if(u=this.rules.list.exec(m)){m=m.substring(u[0].length);r=u[2];this.tokens.push({type:\"list_start\",ordered:r.length>1});u=u[0].match(this.rules.item);q=false;n=u.length;p=0;for(;p<n;p++){v=u[p];k=v.length;v=v.replace(/^ *([*+-]|\\d+\\.) +/,\"\");if(~v.indexOf(\"\\n \")){k-=v.length;v=!this.options.pedantic?v.replace(new RegExp(\"^ {1,\"+k+\"}\",\"gm\"),\"\"):v.replace(/^ {1,4}/gm,\"\")}if(this.options.smartLists&&p!==n-1){t=d.bullet.exec(u[p+1])[0];if(r!==t&&!(r.length>1&&t.length>1)){m=u.slice(p+1).join(\"\\n\")+m;p=n-1}}o=q||/\\n\\n(?!\\s*$)/.test(v);if(p!==n-1){q=v.charAt(v.length-1)===\"\\n\";if(!o){o=q}}this.tokens.push({type:o?\"loose_item_start\":\"list_item_start\"});this.token(v,false);this.tokens.push({type:\"list_item_end\"})}this.tokens.push({type:\"list_end\"});continue}if(u=this.rules.html.exec(m)){m=m.substring(u[0].length);this.tokens.push({type:this.options.sanitize?\"paragraph\":\"html\",pre:u[1]===\"pre\"||u[1]===\"script\"||u[1]===\"style\",text:u[0]});continue}if(s&&(u=this.rules.def.exec(m))){m=m.substring(u[0].length);this.tokens.links[u[1].toLowerCase()]={href:u[2],title:u[3]};continue}if(s&&(u=this.rules.table.exec(m))){m=m.substring(u[0].length);v={type:\"table\",header:u[1].replace(/^ *| *\\| *$/g,\"\").split(/ *\\| */),align:u[2].replace(/^ *|\\| *$/g,\"\").split(/ *\\| */),cells:u[3].replace(/(?: *\\| *)?\\n$/,\"\").split(\"\\n\")};for(p=0;p<v.align.length;p++){if(/^ *-+: *$/.test(v.align[p])){v.align[p]=\"right\"}else{if(/^ *:-+: *$/.test(v.align[p])){v.align[p]=\"center\"}else{if(/^ *:-+ *$/.test(v.align[p])){v.align[p]=\"left\"}else{v.align[p]=null}}}}for(p=0;p<v.cells.length;p++){v.cells[p]=v.cells[p].replace(/^ *\\| *| *\\| *$/g,\"\").split(/ *\\| */)}this.tokens.push(v);continue}if(s&&(u=this.rules.paragraph.exec(m))){m=m.substring(u[0].length);this.tokens.push({type:\"paragraph\",text:u[1].charAt(u[1].length-1)===\"\\n\"?u[1].slice(0,-1):u[1]});continue}if(u=this.rules.text.exec(m)){m=m.substring(u[0].length);this.tokens.push({type:\"text\",text:u[0]});continue}if(m){throw new Error(\"Infinite loop on byte: \"+m.charCodeAt(0))}}return this.tokens};var f={escape:/^\\\\([\\\\`*{}\\[\\]()#+\\-.!_>])/,autolink:/^<([^ >]+(@|:\\/)[^ >]+)>/,url:j,tag:/^<!--[\\s\\S]*?-->|^<\\/?\\w+(?:\"[^\"]*\"|'[^']*'|[^'\">])*?>/,link:/^!?\\[(inside)\\]\\(href\\)/,reflink:/^!?\\[(inside)\\]\\s*\\[([^\\]]*)\\]/,nolink:/^!?\\[((?:\\[[^\\]]*\\]|[^\\[\\]])*)\\]/,strong:/^__([\\s\\S]+?)__(?!_)|^\\*\\*([\\s\\S]+?)\\*\\*(?!\\*)/,em:/^\\b_((?:__|[\\s\\S])+?)_\\b|^\\*((?:\\*\\*|[\\s\\S])+?)\\*(?!\\*)/,code:/^(`+)\\s*([\\s\\S]*?[^`])\\s*\\1(?!`)/,br:/^ {2,}\\n(?!\\s*$)/,del:j,text:/^[\\s\\S]+?(?=[\\\\<!\\[_*`]| {2,}\\n|$)/};f._inside=/(?:\\[[^\\]]*\\]|[^\\[\\]]|\\](?=[^\\[]*\\]))*/;f._href=/\\s*<?([\\s\\S]*?)>?(?:\\s+['\"]([\\s\\S]*?)['\"])?\\s*/;f.link=c(f.link)(\"inside\",f._inside)(\"href\",f._href)();f.reflink=c(f.reflink)(\"inside\",f._inside)();f.normal=g({},f);f.pedantic=g({},f.normal,{strong:/^__(?=\\S)([\\s\\S]*?\\S)__(?!_)|^\\*\\*(?=\\S)([\\s\\S]*?\\S)\\*\\*(?!\\*)/,em:/^_(?=\\S)([\\s\\S]*?\\S)_(?!_)|^\\*(?=\\S)([\\s\\S]*?\\S)\\*(?!\\*)/});f.gfm=g({},f.normal,{escape:c(f.escape)(\"])\",\"~|])\")(),url:/^(https?:\\/\\/[^\\s<]+[^<.,:;\"')\\]\\s])/,del:/^~~(?=\\S)([\\s\\S]*?\\S)~~/,text:c(f.text)(\"]|\",\"~]|\")(\"|\",\"|https?://|\")()});f.breaks=g({},f.gfm,{br:c(f.br)(\"{2,}\",\"*\")(),text:c(f.gfm.text)(\"{2,}\",\"*\")()});function h(k,l){this.options=l||a.defaults;this.links=k;this.rules=f.normal;if(!this.links){throw new Error(\"Tokens array requires a `links` property.\")}if(this.options.gfm){if(this.options.breaks){this.rules=f.breaks}else{this.rules=f.gfm}}else{if(this.options.pedantic){this.rules=f.pedantic}}}h.rules=f;h.output=function(n,k,l){var m=new h(k,l);return m.output(n)};h.prototype.output=function(p){var l=\"\",n,o,k,m;while(p){if(m=this.rules.escape.exec(p)){p=p.substring(m[0].length);l+=m[1];continue}if(m=this.rules.autolink.exec(p)){p=p.substring(m[0].length);if(m[2]===\"@\"){o=m[1].charAt(6)===\":\"?this.mangle(m[1].substring(7)):this.mangle(m[1]);k=this.mangle(\"mailto:\")+o}else{o=i(m[1]);k=o}l+='<a href=\"'+k+'\">'+o+\"</a>\";continue}if(m=this.rules.url.exec(p)){p=p.substring(m[0].length);o=i(m[1]);k=o;l+='<a href=\"'+k+'\">'+o+\"</a>\";continue}if(m=this.rules.tag.exec(p)){p=p.substring(m[0].length);l+=this.options.sanitize?i(m[0]):m[0];continue}if(m=this.rules.link.exec(p)){p=p.substring(m[0].length);l+=this.outputLink(m,{href:m[2],title:m[3]});continue}if((m=this.rules.reflink.exec(p))||(m=this.rules.nolink.exec(p))){p=p.substring(m[0].length);n=(m[2]||m[1]).replace(/\\s+/g,\" \");n=this.links[n.toLowerCase()];if(!n||!n.href){l+=m[0].charAt(0);p=m[0].substring(1)+p;continue}l+=this.outputLink(m,n);continue}if(m=this.rules.strong.exec(p)){p=p.substring(m[0].length);l+=\"<strong>\"+this.output(m[2]||m[1])+\"</strong>\";continue}if(m=this.rules.em.exec(p)){p=p.substring(m[0].length);l+=\"<em>\"+this.output(m[2]||m[1])+\"</em>\";continue}if(m=this.rules.code.exec(p)){p=p.substring(m[0].length);l+=\"<code>\"+i(m[2],true)+\"</code>\";continue}if(m=this.rules.br.exec(p)){p=p.substring(m[0].length);l+=\"<br>\";continue}if(m=this.rules.del.exec(p)){p=p.substring(m[0].length);l+=\"<del>\"+this.output(m[1])+\"</del>\";continue}if(m=this.rules.text.exec(p)){p=p.substring(m[0].length);l+=i(this.smartypants(m[0]));continue}if(p){throw new Error(\"Infinite loop on byte: \"+p.charCodeAt(0))}}return l};h.prototype.outputLink=function(k,l){if(k[0].charAt(0)!==\"!\"){return'<a href=\"'+i(l.href)+'\"'+(l.title?' title=\"'+i(l.title)+'\"':\"\")+\">\"+this.output(k[1])+\"</a>\"}else{return'<img src=\"'+i(l.href)+'\" alt=\"'+i(k[1])+'\"'+(l.title?' title=\"'+i(l.title)+'\"':\"\")+\">\"}};h.prototype.smartypants=function(k){if(!this.options.smartypants){return k}return k.replace(/--/g,\"\\u2014\").replace(/(^|[-\\u2014/(\\[{\"\\s])'/g,\"$1\\u2018\").replace(/'/g,\"\\u2019\").replace(/(^|[-\\u2014/(\\[{\\u2018\\s])\"/g,\"$1\\u201c\").replace(/\"/g,\"\\u201d\").replace(/\\.{3}/g,\"\\u2026\")};h.prototype.mangle=function(p){var m=\"\",k=p.length,n=0,o;for(;n<k;n++){o=p.charCodeAt(n);if(Math.random()>0.5){o=\"x\"+o.toString(16)}m+=\"&#\"+o+\";\"}return m};function e(k){this.tokens=[];this.token=null;this.options=k||a.defaults}e.parse=function(l,k){var m=new e(k);return m.parse(l)};e.prototype.parse=function(l){this.inline=new h(l.links,this.options);this.tokens=l.reverse();var k=\"\";while(this.next()){k+=this.tok()}return k};e.prototype.next=function(){return this.token=this.tokens.pop()};e.prototype.peek=function(){return this.tokens[this.tokens.length-1]||0};e.prototype.parseText=function(){var k=this.token.text;while(this.peek().type===\"text\"){k+=\"\\n\"+this.next().text}return this.inline.output(k)};e.prototype.tok=function(){switch(this.token.type){case\"space\":return\"\";case\"hr\":return\"<hr>\\n\";case\"heading\":return\"<h\"+this.token.depth+' id=\"'+this.token.text.toLowerCase().replace(/[^\\w]+/g,\"-\")+'\">'+this.inline.output(this.token.text)+\"</h\"+this.token.depth+\">\\n\";case\"code\":if(this.options.highlight){var p=this.options.highlight(this.token.text,this.token.lang);if(p!=null&&p!==this.token.text){this.token.escaped=true;this.token.text=p}}if(!this.token.escaped){this.token.text=i(this.token.text,true)}return\"<pre><code\"+(this.token.lang?' class=\"'+this.options.langPrefix+this.token.lang+'\"':\"\")+\">\"+this.token.text+\"</code></pre>\\n\";case\"table\":var l=\"\",q,n,r,k,m;l+=\"<thead>\\n<tr>\\n\";for(n=0;n<this.token.header.length;n++){q=this.inline.output(this.token.header[n]);l+=\"<th\";if(this.token.align[n]){l+=' style=\"text-align:'+this.token.align[n]+'\"'}l+=\">\"+q+\"</th>\\n\"}l+=\"</tr>\\n</thead>\\n\";l+=\"<tbody>\\n\";for(n=0;n<this.token.cells.length;n++){r=this.token.cells[n];l+=\"<tr>\\n\";for(m=0;m<r.length;m++){k=this.inline.output(r[m]);l+=\"<td\";if(this.token.align[m]){l+=' style=\"text-align:'+this.token.align[m]+'\"'}l+=\">\"+k+\"</td>\\n\"}l+=\"</tr>\\n\"}l+=\"</tbody>\\n\";return\"<table>\\n\"+l+\"</table>\\n\";case\"blockquote_start\":var l=\"\";while(this.next().type!==\"blockquote_end\"){l+=this.tok()}return\"<blockquote>\\n\"+l+\"</blockquote>\\n\";case\"list_start\":var o=this.token.ordered?\"ol\":\"ul\",l=\"\";while(this.next().type!==\"list_end\"){l+=this.tok()}return\"<\"+o+\">\\n\"+l+\"</\"+o+\">\\n\";case\"list_item_start\":var l=\"\";while(this.next().type!==\"list_item_end\"){l+=this.token.type===\"text\"?this.parseText():this.tok()}return\"<li>\"+l+\"</li>\\n\";case\"loose_item_start\":var l=\"\";while(this.next().type!==\"list_item_end\"){l+=this.tok()}return\"<li>\"+l+\"</li>\\n\";case\"html\":return !this.token.pre&&!this.options.pedantic?this.inline.output(this.token.text):this.token.text;case\"paragraph\":return\"<p>\"+this.inline.output(this.token.text)+\"</p>\\n\";case\"text\":return\"<p>\"+this.parseText()+\"</p>\\n\"}};function i(k,l){return k.replace(!l?/&(?!#?\\w+;)/g:/&/g,\"&\").replace(/</g,\"<\").replace(/>/g,\">\").replace(/\"/g,\""\").replace(/'/g,\"'\")}function c(m,l){m=m.source;l=l||\"\";return function k(n,o){if(!n){return new RegExp(m,l)}o=o.source||o;o=o.replace(/(^|[^\\[])\\^/g,\"$1\");m=m.replace(n,o);return k}}function j(){}j.exec=j;function g(n){var l=1,m,k;for(;l<arguments.length;l++){m=arguments[l];for(k in m){if(Object.prototype.hasOwnProperty.call(m,k)){n[k]=m[k]}}}return n}function a(k,m,s){if(s||typeof m===\"function\"){if(!s){s=m;m=null}m=g({},a.defaults,m||{});var n=m.highlight,r,l,p=0;try{r=b.lex(k,m)}catch(q){return s(q)}l=r.length;var o=function(){var t,u;try{t=e.parse(r,m)}catch(v){u=v}m.highlight=n;return u?s(u):s(null,t)};if(!n||n.length<3){return o()}delete m.highlight;if(!l){return o()}for(;p<r.length;p++){(function(t){if(t.type!==\"code\"){return --l||o()}return n(t.text,t.lang,function(v,u){if(u==null||u===t.text){return --l||o()}t.text=u;t.escaped=true;--l||o()})})(r[p])}return}try{if(m){m=g({},a.defaults,m)}return e.parse(b.lex(k,m),m)}catch(q){q.message+=\"\\nPlease report this to https://github.com/chjj/marked.\";if((m||a.defaults).silent){return\"<p>An error occurred:</p><pre>\"+i(q.message+\"\",true)+\"</pre>\"}throw q}}a.options=a.setOptions=function(k){g(a.defaults,k);return a};a.defaults={gfm:true,tables:true,breaks:false,pedantic:false,sanitize:false,smartLists:false,silent:false,highlight:null,langPrefix:\"lang-\",smartypants:false};a.Parser=e;a.parser=e.parse;a.Lexer=b;a.lexer=b.lex;a.InlineLexer=h;a.inlineLexer=h.output;a.parse=a;if(typeof exports===\"object\"){module.exports=a}else{if(typeof define===\"function\"&&define.amd){define(function(){return a})}else{this.marked=a}}}).call(function(){return this||(typeof window!==\"undefined\"?window:global)}());" // NOLINT(whitespace/line_length) |
| " function loaded() {" |
| " marked.setOptions({ breaks: true });" |
| " document.body.innerHTML = marked(" + markdown + ");" |
| " }" |
| "</script>" |
| "<style>" |
| "body {" |
| " font-family: Helvetica, arial, sans-serif;" |
| " font-size: 14px;" |
| " line-height: 1.6;" |
| " padding-top: 10px;" |
| " padding-bottom: 10px;" |
| " background-color: white;" |
| " padding: 30px;" |
| "}" |
| "blockquote {" |
| " border-left: 5px solid #dddddd;" |
| " padding: 0 10px;" |
| " color: #777777;" |
| " margin: 0 0 20px;" |
| "}" |
| "a {" |
| " color: #0088cc;" |
| " text-decoration: none;" |
| "}" |
| "</style>" |
| "</head>" |
| "<body onload=\"loaded()\">" |
| "</body>" |
| "</html>"); |
| |
| response.headers["Content-Type"] = "text/html"; |
| return response; |
| } |
| |
| } // namespace process { |