# NodesDisplay.ts

`

1<comment>/**</comment>

2<comment> * </comment>

3<comment> */</comment>

4

5 <comment>/** */</comment>

6import { m, Vnode} from <quote>'hslayout'</quote>;

7import { Log } from <quote>'hsutil'</quote>; const log = new Log(<quote>'NodesDisplay'</quote>);

8import { DocsNode, DocsSignature } from <quote>'./Nodes'</quote>;

9import { DocsParameter } from <quote>'./Nodes'</quote>;

10import { DocSets } from <quote>'./DocSets'</quote>;

11import { DocsParamaterized } from <quote>'./Nodes'</quote>;

12import { flagsDisplay } from <quote>'./Flags'</quote>;

13import { DocsReferenceIdType } from <quote>'./Types'</quote>;

14import { DocsNamedType } from <quote>'./Types'</quote>;

15import { json } from <quote>'./DocSets'</quote>;

16import { example } from <quote>'./view/MainExample'</quote>;

17import { markDown } from <quote>'./markdown'</quote>;

18

19

20const SourceBase = <quote>'data/src/'</quote>;

21

22interface Tag {

23 tag: string;

24 text: <quote>'string'</quote>;

25 param: string;

26}

27

28

29<comment>/**</comment>

30<comment> * creates a library link on the specified `name`. </comment>

31<comment> * The link points to `/api/<lib>/<id>`</comment>

32<comment> * @param css the css tag selector to use</comment>

33<comment> * @param cls the css class selector to use</comment>

34<comment> * @param lib the lib string to point to</comment>

35<comment> * @param id the id number to point to</comment>

36<comment> * @param name the name on which to formt he link</comment>

37<comment> */</comment>

38export function libLinkByPath(lib:string, path:string, name:string, css=<quote>''</quote>):Vnode {

39 return m(`a${css}`, { href:`#!/api/${lib}/${path}`, oncreate: m.route.link, onupdate: m.route.link }, name);

40}

41

42function libLinkByID(lib:string, id:string|number, name:string, css=<quote>''</quote>):Vnode {

43<comment>// log.info(`libLinkByID ${lib} ${id} ${name}`);</comment>

44<comment></comment> const path = (DocSets.getNode(id, lib)||{fullPath:<quote>''</quote>}).fullPath||<quote>''</quote>;

45 return libLinkByPath(lib, path, name, css);

46}

47

48export class DocsComment {

49 shortText?: string;

50 returns?: string;

51 text?: string;

52 tags?: Tag[];

53

54 constructor(comment:json, node:DocsNode) {

55 this.shortText = comment.shortText;

56 this.returns = comment.returns;

57 this.text = comment.text;

58 this.tags = comment.tags || [];

59 }

60

61 <comment>/**</comment>

62<comment> * Report the item<quote>'s description. This can come in different forms that are all handled here:</comment>

63<comment> * - comment.shortText: <description></comment>

64<comment> * - comment.text: <description></comment>

65<comment> * - comment.tags[{tag:'</quote>description}, text:<description>]</comment>

66<comment> * Any resulting comment will be translated from markdown to html and returned as a `Vnode`.</comment>

67<comment> * @param short boolean; if true, only the first paragraph of the description will be returned</comment>

68<comment> */</comment>

69 getDescription(short: boolean) {

70 let text = (this.shortText || <quote>''</quote>);

71 if (this.text) { text += <quote>'\n'</quote>+ (this.text || <quote>''</quote>); }

72 this.tags.map((tag:Tag) => {

73 switch (tag.tag) {

74 case <quote>'shortText'</quote>:

75 case <quote>'description'</quote>: text = `${text}\n${tag.text}`; break;

76 case <quote>'param'</quote>: text = `${text}\n- **${tag.param}**: ${tag.text}`; break;

77 }

78 });

79 <comment>// search for pattern <example...<file...</example></comment>

80<comment></comment> text = short? <quote>''</quote> : text.replace(/<example[^<]*?(<file[\S\s]*?)<\/example>/gi, example);

81 return m(<quote>'span.hsdocs_comment_desc'</quote>, prettifyCode(text, short));

82 }

83

84 <comment>/**</comment>

85<comment> * creates the `returns` message for a function or method.</comment>

86<comment> */</comment>

87 getParams(params?:DocsParameter[]) {

88 let text = params;

89 return !text? undefined: m(<quote>'.hsdocs_comment_params'</quote>, [

90 m(<quote>'.hsdocs_params_title'</quote>, <quote>'parameters:'</quote>),

91 ...params.map(p =>

92 m(<quote>'.hsdocs_comment_param'</quote>,[

93 m(<quote>'span.hsdocs_param_name'</quote>, [p.getName(), <quote>': '</quote>]),

94 m(<quote>'span.hsdocs_param_comment'</quote>, commentLong(p))

95 ]))

96 ]);

97 }

98

99 <comment>/**</comment>

100<comment> * creates the `returns` message for a function or method.</comment>

101<comment> */</comment>

102 getReturns() {

103 let text = this.returns;

104 return !text? undefined: m(<quote>'span.hsdocs_comment_return'</quote>, [

105 m(<quote>'span.hsdocs_comment_return_tag'</quote>, <quote>'returns:'</quote>),

106 m(<quote>'span.hsdocs_comment_return_text'</quote>, text)

107 ]);

108 }

109

110 getCommentRemainder() {

111 const tags = Object.getOwnPropertyNames(this);

112 return tags.length===0? undefined : m(<quote>''</quote>, tags.map((tag:string) => {

113 switch(tag) {

114 case <quote>'tags'</quote>: <comment>// already handled</comment>

115<comment></comment> case <quote>'shortText'</quote>: <comment>// already handled</comment>

116<comment></comment> case <quote>'text'</quote>: <comment>// already handled</comment>

117<comment></comment> case <quote>'description'</quote>: <comment>// already handled</comment>

118<comment></comment> case <quote>'returns'</quote>: <comment>// already handled</comment>

119<comment></comment> return undefined;

120 default: return m(<quote>'.hsdocs_comment_remainder'</quote>, [

121 m(<quote>'span.hsdocs_comment_return_tag'</quote>, tag),

122 m(<quote>'span.hsdocs_comment_return_text'</quote>, this[tag])

123 ]);

124 }

125 }));

126 }

127}

128

129export function title(node:DocsNode, parent?:DocsNode):Vnode {

130 if (node.getSignatures()) {

131 return node.getSignatures().map((s:DocsNode) =>

132 m(<quote>'.hsdocs_child.hsdocs_signature'</quote>, [title(s, node), m(<quote>'.hsdocs_sig_comment'</quote>, s.commentLong())])

133 );

134 }

135 return m(<quote>'.hsdocs_title'</quote>, {id: <quote>'title_'</quote> + node.getName().toLowerCase()}, titleElements(node, parent));

136}

137

138<comment>/**</comment>

139<comment> * creates a signature row.</comment>

140<comment> * @param node the node for which to generate the signature row.</comment>

141<comment> * @param parent optional parent node for use in `mItemName`, used </comment>

142<comment> * when the node has a `signature` array. </comment>

143<comment> */</comment>

144export function titleArr(node:DocsNode, parent?:DocsNode):Vnode {

145 return m(<quote>'span'</quote>, titleElements(node, parent));

146}

147

148function titleElements(node:DocsNode, parent?:DocsNode) {

149 try { return [

150 mFlags(parent || node),

151 mKindString(parent || node),

152 mItemName(parent || node),

153 mSignature(node),

154 m(<quote>'span.hsdocs_title_colon'</quote>,<quote>': '</quote>),

155 mType(node),

156 mExtensionOf(node),

157 mImplementationOf(node),

158 mInheritedFrom(node),

159 mSourceLink(node),

160 mExtendedBy(node),

161 mImplementedBy(node)

162 ];

163 }

164 catch(e) {

165 log.error(e.message);

166 log.error(e.stack);

167 log.error(`creating title #${node.id} for ${node.kindString}`);

168 return [m(<quote>'span.hsdocs_error'</quote>, e.message)];

169 }

170}

171

172export function inlineTitle(node:DocsNode):Vnode {

173 let desc:Vnode = <quote>''</quote>;

174 try { desc = [

175 mFlags(node),

176 mKindString(node),

177 mSignature(node),

178 m(<quote>'span.hsdocs_title_arrow'</quote>,<quote>' => '</quote>),

179 mType(node)

180 ];

181 }

182 catch(e) {

183 log.error(e.message);

184 log.error(e.stack);

185 log.error(`creating title #${node.id} for ${node.kindString}`);

186 }

187 return m(<quote>'span.hsdocs_title_inline'</quote>, desc);

188}

189

190function mFlags(node:DocsNode):Vnode {

191 return m(<quote>'span.hsdocs_flags'</quote>, flagsDisplay(node));

192}

193

194function mKindString(node:DocsNode):Vnode {

195 const kind = node.kindPrint;

196 return kind===<quote>''</quote>? <quote>''</quote> : m(<quote>'span.hsdocs_kind'</quote>, node.getSignature? <quote>'get '</quote> : (node.setSignature? <quote>'set '</quote> : kind));

197}

198

199function mItemName(node:DocsNode):Vnode {

200 return m(<quote>'span.hsdocs_itemname'</quote>, !node.fullPath? node.getName() : libLinkByPath(node.lib, node.fullPath, node.getName()));

201}

202

203function mSignature(node:DocsNode):Vnode {

204 const optional = (flags:any) => (flags && flags.isOptional)? <quote>'.hsdocs_flag_optional'</quote> : <quote>''</quote>;

205 const params = (s:DocsParamaterized):Vnode[] =>

206 !s.parameters? undefined : s.parameters.map((p:DocsParameter, i:number) => m(<quote>'span'</quote>, [

207 (i>0)? <quote>', '</quote>: <quote>''</quote>,

208 m(<quote>'span.hsdocs_signature_param'</quote>, [

209 m(`span.hsdocs_itemname${optional(p.flags)}`, p.getName()),

210 m(<quote>'span.hsdocs_colon'</quote>, <quote>':'</quote>),

211 mType(p)

212 ])

213 ]));

214 const sigText = (s:DocsParamaterized) => {

215 let result:Vnode[];

216 if (s.type && s.type.declaration) {

217 const dec = s.type.declaration;

218 const sig = dec.indexSignature || dec.getSignatures();

219 if (sig) {

220 result = sig.map((isig:DocsSignature) => params(isig));

221 }

222 }

223 result = params(s);

224 if (result) {

225 result.unshift(m(<quote>'span.hsdocs_itemname'</quote>, <quote>' ('</quote>));

226 result.push(m(<quote>'span.hsdocs_itemname'</quote>, <quote>')'</quote>));

227 }

228 if (s.typeParameter) {

229 result.unshift(...s.typeParameter.map(t => m(<quote>'span'</quote>,` <${t.name}>`)));

230 }

231 return result;

232 };

233

234 const sigs:DocsParamaterized[] = node.getSignatures();

235 return m(<quote>'span.hsdocs_signatures'</quote>, !sigs? sigText(node) : sigs.map(s => m(<quote>'span.hsdocs_signature'</quote>, sigText(s))));

236}

237

238function mType(node:DocsNode):Vnode {

239 const defVal = !node.defaultValue? undefined : m(<quote>'span.hsdocs_type_default'</quote>,

240 ` = ${node.defaultValue.replace(/{/gi, <quote>'{ '</quote>).replace(/}/gi, <quote>' }'</quote>)}`);

241

242 if (!node.type) {

243 if (node.getSignatures()) {

244 return m(<quote>'span'</quote>, node.getSignatures().map(s => mType(s)));

245 } else {

246 return defVal || m(<quote>'span'</quote>, <quote>''</quote>);

247 }

248 }

249 if (!node.type.mType) {

250 log.warn(``);

251 }

252 try {

253 return m(<quote>'span'</quote>, [

254 m(<quote>'span.hsdocs_type'</quote>, node.type.mType(node)),

255 defVal

256 ]);

257 } catch(e) {

258 log.error(e); log.error(e.trace);

259 }

260}

261

262function mExtensionOf(node:DocsNode):Vnode {

263 const types = node.extendedTypes;

264 return m(<quote>'span.hsdocs_extensionOf'</quote>, !types? undefined : [

265 m(<quote>'span.hsdocs_extends'</quote>, <quote>'extends'</quote>),

266 m(<quote>'span'</quote>, types.map((t:DocsReferenceIdType, i:number) =>

267 m(<quote>'span.hsdocs_extensionType'</quote>, [(i>0)?<quote>', '</quote> : <quote>''</quote>,

268 t.id? libLinkByID(node.lib, t.id, t.name) : t.name,

269 ])

270 )),

271 ]);

272}

273

274function mImplementationOf(node:DocsNode):Vnode {

275 return !node.implementedTypes? undefined : m(<quote>'span.hsdocs_implementationsOf'</quote>, [

276 m(<quote>'span.hsdocs_implements'</quote>, <quote>'implements'</quote>),

277 m(<quote>'span'</quote>, node.implementedTypes.map((t:DocsNamedType, i:number) =>

278 m(<quote>'span.hsdocs_implementationType'</quote>, [ (i>0)?<quote>', '</quote> : <quote>''</quote>, libLinkByID(node.lib, t.id, t.name)])

279 )),

280 ]);

281}

282

283function mInheritedFrom(node:DocsNode):Vnode {

284 if (node.inheritedFrom) {

285 if (node.inheritedFrom.id) {

286 let parent = DocSets.getNode(node.inheritedFrom.id, node.lib);

287 parent = DocSets.getNode(parent.fullPath.substring(0, parent.fullPath.lastIndexOf(<quote>'.'</quote>)), node.lib);

288 return m(<quote>'span.hsdocs_inheritedFrom'</quote>, [

289 m(<quote>'span'</quote>, <quote>'inherited from '</quote>),

290 libLinkByPath(parent.lib, parent.fullPath, parent.getName())

291 ]);

292 } else {

293 return m(<quote>'span.hsdocs_inheritedFrom'</quote>, [

294 m(<quote>'span'</quote>, <quote>'inherited from '</quote>),

295 m(<quote>'span'</quote>, node.inheritedFrom.name)

296 ]);

297 }

298 } else {

299 return m(<quote>'span.hsdocs_inheritedFrom'</quote>, undefined);

300 }

301}

302

303function adjustCase(name:string) {

304 if (name.indexOf(<quote>'hs'</quote>) === 0) {

305 return <quote>'hs'</quote> + name.slice(2).charAt(0).toUpperCase() + name.slice(3).toLowerCase();

306 }

307 return name;

308}

309

310function mSourceLink(node:DocsNode):Vnode {

311 const source = node.sources? node.sources[0] : undefined;

312 if (source) {

313 let file = (source.fileName || <quote>''</quote>).replace(<quote>'.ts'</quote>, <quote>'.html'</quote>);

314 const index = file.indexOf(node.lib);

315 if (index>0) {

316 file = file.substr(index+node.lib.length+5); <comment>// only consider links within the docSet (everything after the lib name)</comment>

317<comment></comment> }

318 return m(<quote>'span.hsdocs_source'</quote>,

319 m(<quote>'a'</quote>, { href:`${SourceBase}${adjustCase(node.lib)}/${file}#${Math.max(0,source.line-5)}`, target:<quote>"_blank"</quote>}, <quote>'[source]'</quote>)

320 );

321 } else {

322 return m(<quote>'span.hsdocs_source'</quote>, <quote>''</quote>);

323 }

324}

325

326function mExtendedBy(node:DocsNode):Vnode {

327 return !node.extendedBy? undefined : m(<quote>'div.hsdocs_extendedBy'</quote>,[

328 m(<quote>'span.hsdocs_extended_by'</quote>, <quote>'extended by'</quote>),

329 m(<quote>'span'</quote>, node.extendedBy.map((t:any, i:number) =>

330 m(<quote>'span.hsdocs_extendedby_type'</quote>, [(i>0)?<quote>', '</quote> : <quote>''</quote>, libLinkByID(node.lib, t.id, t.name)])

331 )),

332 ]);

333}

334

335function mImplementedBy(node:DocsNode):Vnode {

336 return !node.implementedBy? undefined : m(<quote>'div.hsdocs_implementedBy'</quote>,[

337 m(<quote>'span.hsdocs_implemented_by'</quote>, <quote>'implemented by'</quote>),

338 m(<quote>'span'</quote>, node.implementedBy.map((t:any, i:number) =>

339 m(<quote>'span.hsdocs_implementedby_type'</quote>, [(i>0)?<quote>', '</quote> : <quote>''</quote>, libLinkByID(node.lib, t.id, t.name)])

340 )),

341 ]);

342}

343

344

345<comment>/**</comment>

346<comment> * Main comment processing. The result appears directly below the title in the main panel.</comment>

347<comment> * Function parameters are not reported in short form here since it is assumed they will be listed </comment>

348<comment> * individually below the main comment</comment>

349<comment> * @param node the module to scan for comments</comment>

350<comment> * @return a vnode representing the comment entries</comment>

351<comment> */</comment>

352export function commentLong(node:DocsNode):Vnode[] {

353 let content:any[] = [];

354 if (node.comment) {

355 content.push(node.comment.getDescription(false));

356 content.push(node.comment.getParams(node.parameters));

357 content.push(node.comment.getReturns());

358 content.push(node.comment.getCommentRemainder());

359 } else if (node.getSignatures()) {

360 return node.getSignatures().map(s => commentLong(s));

361 }

362 return content.length? content : undefined;

363}

364

365interface TestFunction {

366 (n:DocsNode): boolean;

367}

368interface Access {

369 text: {true:string; false:string;}[];

370 sort: TestFunction[];

371 css: string[];

372}

373export function members(node:DocsNode):Vnode {

374 const not = (fn:TestFunction) => (n:DocsNode) => !fn(n);

375 const isStatic = (n:DocsNode) => n.flags.isStatic === true;

376 const isExported = (n:DocsNode) => n.flags.isExported === true;

377 const isPublic = (n:DocsNode) => (n.flags.isPublic || (!n.flags.isPrivate && !n.flags.isProtected))? true : false;

378 const filterChildren = (cs:[string,DocsNode[]][], filters:((n:DocsNode)=>boolean)[]):[string,DocsNode[]][] => {

379 if (filters.length===0) { return cs; }

380 const remaining = filters.slice(1);

381 return [

382 ...filterChildren(cs.map(m => [m[0], m[1].filter(filters[0])]), remaining),

383 ...filterChildren(cs.map(m => [m[0], m[1].filter(not(filters[0]))]), remaining)

384 ].filter(m => m[1].length);

385 };

386

387 <comment>/** flags to sort by */</comment>

388 let sorts:((n:DocsNode)=>boolean)[];

389 <comment>/** the */</comment>

390 const access:Access = (node.kindString===<quote>'External module'</quote>)? {

391 text: [{true:<quote>'Static'</quote>, false: <quote>''</quote>}, {true:<quote>'Exported'</quote>, false:<quote>'Internal'</quote>}],

392 css: [<quote>'.hsdocs_flag_static'</quote>, <quote>'.hsdocs_flag_public'</quote>],

393 sort: [isStatic, isExported]

394 } : {

395 text: [{true:<quote>'Static'</quote>, false: <quote>''</quote>}, {true:<quote>'Public'</quote>, false:<quote>'Protected or Private'</quote>}],

396 sort: [isStatic, isPublic],

397 css: [<quote>'.hsdocs_flag_static'</quote>, <quote>'.hsdocs_flag_public'</quote>],

398 };

399

400 const grp = node.groups;

401 if (grp) {

402 const children:[string,DocsNode[]][] = grp.map(g => [g.title, g.children.map(id => DocSets.getNode(id, node.lib))]);

403 <comment>// flags to sort by: </comment>

404<comment></comment> const _members = filterChildren(children, access.sort);

405 return m(<quote>'.hsdocs_members'</quote>, _members.map(m => member(m[1], m[0], access)));

406

407 } else if (node.parameters) {

408 return m(<quote>'.hsdocs_members'</quote>, node.parameters);

409

410 <comment>// } else if (node.getSignatures()) {</comment>

411<comment></comment> <comment>// return m(<quote>'.hsdocs_members'</quote>, node.getSignatures().map((s:DocsParamaterized) => s.parameters));</comment>

412<comment></comment>

413 } else if (node.type && node.type.declaration) {

414 node.type.declaration.lib = node.lib;

415 return m(<quote>'.hsdocs_members'</quote>, node.type.declaration.members());

416

417 }

418 return m(<quote>'.hsdocs_members'</quote>);

419}

420

421function member(nodes:DocsNode[], title:string, access:Access): Vnode {

422 const directChildren = ((node:DocsNode) => !node.inheritedFrom);

423 const inheritedChildren = ((node:DocsNode) => node.inheritedFrom);

424 const [css, flagText] = nodes.length? getFlagText(nodes[0], access) : [<quote>''</quote>,<quote>''</quote>];

425

426 const content:Vnode[] = nodes.filter(directChildren)

427 .map((c:DocsNode) => m(<quote>'.hsdocs_node'</quote>, {id:c.fullPath}, itemChild(c)));

428 const inherited:Vnode[] = nodes.filter(inheritedChildren)

429 .map((c:DocsNode) => m(<quote>'.hsdocs_node .hsdocs_node_inherited'</quote>, {id:c.fullPath}, itemChild(c)));

430 if (inherited.length>0) {

431 inherited.unshift(m(<quote>'div.hsdocs_member_title'</quote>, `${flagText} Inherited ${title}`));

432 }

433 if (content.length>0) {

434 content.unshift(m(<quote>'div.hsdocs_member_title'</quote>, `${flagText} ${title}`));

435 }

436 const members:DocsNode[] = content.concat(inherited);

437 return !members.length? <quote>''</quote> : m(`.hsdocs_member ${css}`, members);

438}

439

440function itemChild(node:DocsNode): Vnode[] {

441 if (node.getSignatures()) {

442 return node.getSignatures().map((s:DocsNode) =>

443 m(<quote>'.hsdocs_child.hsdocs_signature'</quote>, [titleArr(s, node), m(<quote>'.hsdocs_childsig_comment'</quote>, s.commentLong())])

444 );

445 } else if (node.commentLong()) {

446 return m(<quote>'.hsdocs_child'</quote>, [titleArr(node), m(<quote>'.hsdocs_sig_comment'</quote>,node.commentLong())]);

447 } else if (node.inheritedFrom) {

448 return m(<quote>'.hsdocs_child'</quote>, node.inheritedFrom.name);

449 } else {

450 return m(<quote>'.hsdocs_child'</quote>, titleArr(node));

451 }

452}

453

454function getFlagText(n:DocsNode, access:Access) {

455 const select:boolean[] = access.sort.map(f => f(n));

456 const css = select.map((f,i) => f? access.css[i]:<quote>''</quote>).join(<quote>' '</quote>).trim();

457 const flagText = select.map((f,i) => access.text[i][<quote>''</quote>+f]).join(<quote>' '</quote>).trim();

458 return [css, flagText];

459 <comment>// const css = `${f.isStatic?<quote>'.hsdocs_flag_static'</quote> : <quote>''</quote>} ${select?<quote>'.hsdocs_flag_public'</quote> : <quote>''</quote>}`;</comment>

460<comment></comment> <comment>// return [css, `${access.text[<quote>''</quote>+select]} ${f.isStatic? <quote>'Static'</quote> : <quote>''</quote>}`];</comment>

461<comment></comment>}

462

463<comment>/**</comment>

464<comment> * finds segments of ``...`` in `comment` and replaces them with a prettified version.</comment>

465<comment> * Currently the function performs operations in this sequence:</comment>

466<comment> * - add indentation for brackets {...}</comment>

467<comment> * - wrap the <code>...</code> part within <pre>...</pre> brackets</comment>

468<comment> * - apply markdown syntax</comment>

469<comment> * - wrap results in a trusted mithril node</comment>

470<comment> * @param comment the comment comment </comment>

471<comment> * @param short only return the first paragraph</comment>

472<comment> */</comment>

473function prettifyCode(comment:string, short:boolean):Vnode {

474 let result = comment;

475

476 function braceIndenting(text:string): string {

477 let indent = 0;

478 const result = text

479 .substring(6, text.length-7) <comment><comment>// remove `and` </comment></comment>

480<comment></comment> .trim()

481 .replace(/(<)/g, <quote>'<'</quote>).replace(/(>)/g, <quote>'>'</quote>)

482 .split(<quote>'\n'</quote>)

483 .map((l:string) => {

484 let oldIndent = indent;

485 let k = l.trim();

486 indent += Math.max(-1, Math.min(1, k.split(<quote>'{'</quote>).length - k.split(<quote>'}'</quote>).length));

487 indent += Math.max(-1, Math.min(1, k.split(<quote>'['</quote>).length - k.split(<quote>']'</quote>).length));

488 return <quote>'<span class=<quote>"hs-code-indent"</quote>>'</quote>.repeat(((indent < oldIndent)?indent:oldIndent)) + k;

489 })

490 .join(<quote>'\n'</quote>)

491 .trim();

492 return <quote>'

    '</quote> + result + <quote>'

'</quote>;

493 }

494

495 result = result.replace(/`([\S\s]*?)<\/code>/gi, braceIndenting);`

496 return m.trust(markDown(result, short, m.route.get()));

497}

498

`