Skip to content

Commit

Permalink
implement mdEscape markdown function
Browse files Browse the repository at this point in the history
  • Loading branch information
RFSH committed Feb 6, 2024
1 parent f868a66 commit 72a989a
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 6 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ LIB=$(VENDOR)/lz-string.min.js \
$(VENDOR)/markdown-it-sub.min.js \
$(VENDOR)/markdown-it-sup.min.js \
$(VENDOR)/markdown-it-span.js \
$(VENDOR)/markdown-it-escape.js \
$(VENDOR)/markdown-it-attrs.js \
$(VENDOR)/markdown-it-container.min.js

Expand Down
15 changes: 15 additions & 0 deletions docs/dev-docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,21 +47,25 @@ to use for ERMrest JavaScript agents.</p>
<dd><p>{{#replace substr newSubstr}}
string
{{/replace}}</p>
<p>{{replace value regexp flags=&quot;ig&quot;}}</p>
</dd>
<dt><a href="#regexMatch">regexMatch()</a> ⇒</dt>
<dd><p>{{#if (regexMatch value regexp)}}
.. content
{{/if}}</p>
<p>{{regexMatch value regexp flags=&quot;i&quot;}}</p>
</dd>
<dt><a href="#regexFindFirst">regexFindFirst()</a> ⇒</dt>
<dd><p>{{#each (regexFindFirst value regexp)}}
{{this}}
{{/each}}</p>
<p>{{regexFindFirst value regexp flags=&quot;i&quot;}}</p>
</dd>
<dt><a href="#regexFindAll">regexFindAll()</a> ⇒</dt>
<dd><p>{{#each (regexFindAll value regexp)}}
{{this}}
{{/each}}</p>
<p>{{regexFindFirst value regexp flags=&quot;ig&quot;}}</p>
</dd>
<dt><a href="#toTitleCase">toTitleCase()</a> ⇒</dt>
<dd><p>{{#toTitleCase}}
Expand Down Expand Up @@ -5454,6 +5458,9 @@ The logic is as follows,
4. return choices if int or serial, part of key, and not null.
5. return ranges or choices based on the type.

Note:
- null and not-null are applicaple in all types, so we're ignoring those while figuring out the preferred mode.

**Kind**: instance property of [<code>FacetColumn</code>](#ERMrest.FacetColumn)
<a name="ERMrest.FacetColumn+barPlot"></a>

Expand Down Expand Up @@ -8395,6 +8402,8 @@ or
string
{{/replace}}

{{replace value regexp flags="ig"}}

**Kind**: global function
**Returns**: replaces each match of the regexp with newSubstr
<a name="regexMatch"></a>
Expand All @@ -8404,6 +8413,8 @@ or
.. content
{{/if}}

{{regexMatch value regexp flags="i"}}

**Kind**: global function
**Returns**: boolean if the value matches the regexp
<a name="regexFindFirst"></a>
Expand All @@ -8413,6 +8424,8 @@ or
{{this}}
{{/each}}

{{regexFindFirst value regexp flags="i"}}

**Kind**: global function
**Returns**: first string from value that matches the regular expression or empty string
<a name="regexFindAll"></a>
Expand All @@ -8422,6 +8435,8 @@ or
{{this}}
{{/each}}

{{regexFindFirst value regexp flags="ig"}}

**Kind**: global function
**Returns**: array of strings from value that match the regular expression or
<a name="toTitleCase"></a>
Expand Down
36 changes: 32 additions & 4 deletions docs/user-docs/markdown-formatting.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,9 @@ For common markdown syntax please refer to [this reference sheet](http://commonm
+ [11. Subscript](#11-subscript)
+ [12. Superscript](#12-superscript)
+ [13. Span (Attach Attributes To Text)](#13-span-attach-attributes-to-text)
+ [14. RID link](#14-rid-link)
+ [15. Table](#15-table)
+ [14. Escape markdown content](#14-escape-markdown-content)
+ [15. RID link](#15-rid-link)
+ [16. Table](#15-table)



Expand Down Expand Up @@ -945,7 +946,34 @@ You can also have empty span. You can use this to display icons.
```
> <p><span class="fa-solid fa-download"></span></p>
### 14. RID link

### 14. Escape markdown content

This is not part of commonMark specification and it will result in an [inline](#inline-vs-block) element. Opening tag is `:mdEscape:` and closing is `:/mdEscape:`.

This tag can be used to print the content as is and escape the markdown rendering. For example, if you want to show a JSON column value, by default, the `{}` will be treated as attributes of markdown, so you would need to escape the content.

```html
:mdEscape:{"first_name": "John", "last_name": "Smith"}:/mdEscape: should be superscript.

# OUTPUT:
<p>
<span>{"first_name": "John", "last_name": "Smith"}</span>
</p>
```
> <p>{"first_name": "John", "last_name": "Smith"}</p>
```html
The following is how you can add links: :mdEscape:[caption](link):/mdEscape:

# OUTPUT:
<p>
The following is how you can add links: <span>[caption](link)</span>
</p>
```
> The following is how you can add links: <span>\[caption](link)</span>
### 15. RID link

Takes an RID of an existing record and generates a resolvable link for that record. This is not part of commonMark specification. It will result in an [inline](#inline-vs-block) element. You have to follow the syntax completely.

Expand All @@ -965,7 +993,7 @@ Takes an RID of an existing record and generates a resolvable link for that reco
> <a href="/id/1-3X0H">1-3X0H</a>

### 15. Table
### 16. Table

Tables are not part of the commonMark specifications, but the parser that we use follows the [GitHub Flavored Markdown specification for tables](https://github.github.com/gfm/#tables-extension-).

Expand Down
4 changes: 3 additions & 1 deletion js/setup/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ if (typeof module === 'object' && module.exports && typeof require === 'function
.use(require('../vendor/markdown-it-sub.min.js')) // add subscript support
.use(require('../vendor/markdown-it-sup.min.js')) // add superscript support;
.use(require('../vendor/markdown-it-span.js')) // add span support
.use(require('../vendor/markdown-it-escape')) // add escape support
.use(require('../vendor/markdown-it-attrs.js')); // add attrs support


Expand Down Expand Up @@ -150,7 +151,8 @@ if (typeof module === 'object' && module.exports && typeof require === 'function
.use(window.markdownitSub)
.use(window.markdownitSup)
.use(window.markdownItAttrs)
.use(window.markdownitSpan);
.use(window.markdownitSpan)
.use(window.markdownitEscape);

// set custom markdown tags using markdown-it-container plugin
ERMrest._bindCustomMarkdownTags(ERMrest._markdownIt, markdownitContainer);
Expand Down
7 changes: 7 additions & 0 deletions test/specs/print_utils/tests/01.print_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,13 @@ exports.execute = function (options) {
expect(printMarkdown(":span::/span:{.glyph-icon .glyph-danger}", {inline: true})).toBe('<span class="glyph-icon glyph-danger"></span>', "invalid empty inline span with attrs");
});

it ("should support :mdEscape:.", function () {
expect(printMarkdown("This :mdEscape: [caption](example.com) should not be rendered :/mdEscape:.")).toBe("<p>This <span> [caption](example.com) should not be rendered </span>.</p>\n", "invalid span");
expect(printMarkdown("This :mdEscape: [caption](example.com) should not be rendered :/mdEscape:.", {inline: true})).toBe("This <span> [caption](example.com) should not be rendered </span>.", "invalid inline span");

expect(printMarkdown("JSON: :mdEscape:{\"name\": \"a valid name\"}:/mdEscape:", {inline: true})).toEqual('JSON: <span>{“name”: “a valid name”}</span>');
});

it("should support table with classname attribute.", function () {
var mkString = "|heading|\n|-|\n|text|\n\n{.class-name}";
expect(printMarkdown(mkString)).toBe('<table class="class-name">\n<thead>\n<tr>\n<th>heading</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>text</td>\n</tr>\n</tbody>\n</table>\n');
Expand Down
90 changes: 90 additions & 0 deletions vendor/markdown-it-escape.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.markdownitEscape = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){

'use strict';

// same as UNESCAPE_MD_RE plus a space
var UNESCAPE_RE = /\\([ \\!"#$%&'()*+,.\/:;<=>?@[\]^_`{|}~-])/g;


function renderInlineEscape(state, silent) {
var found,
content,
token,
max = state.posMax,
start = state.pos;

if (silent) { return false; } // don't run any pairs in validation mode
if (start + 23 >= max) { return false; }

if (
state.src.charCodeAt(start) !== 0x3A /* : */ ||
state.src.charCodeAt(start + 1) !== 0x6D/* m */ ||
state.src.charCodeAt(start + 2) !== 0x64/* d */ ||
state.src.charCodeAt(start + 3) !== 0x45/* E */ ||
state.src.charCodeAt(start + 4) !== 0x73/* s */ ||
state.src.charCodeAt(start + 5) !== 0x63/* c */ ||
state.src.charCodeAt(start + 6) !== 0x61/* a */ ||
state.src.charCodeAt(start + 7) !== 0x70/* p */ ||
state.src.charCodeAt(start + 8) !== 0x65/* e */ ||
state.src.charCodeAt(start + 9) !== 0x3A /* : */
) {
return false;
}

state.pos = start + 10;

//find the end
while (state.pos < max) {
if (
state.src.charCodeAt(state.pos) === 0x3A /* : */ &&
state.src.charCodeAt(state.pos + 1) === 0x2F/* / */ &&
state.src.charCodeAt(state.pos + 2) === 0x6D/* m */ &&
state.src.charCodeAt(state.pos + 3) === 0x64/* d */ &&
state.src.charCodeAt(state.pos + 4) === 0x45/* E */ &&
state.src.charCodeAt(state.pos + 5) === 0x73/* s */ &&
state.src.charCodeAt(state.pos + 6) === 0x63/* c */ &&
state.src.charCodeAt(state.pos + 7) === 0x61/* a */ &&
state.src.charCodeAt(state.pos + 8) === 0x70/* p */ &&
state.src.charCodeAt(state.pos + 9) === 0x65/* e */ &&
state.src.charCodeAt(state.pos + 10) === 0x3A /* : */
) {
found = true;
break;
}

state.md.inline.skipToken(state);
}

if (!found || start + 1 === state.pos) {
state.pos = start;
return false;
}

content = state.src.slice(start + 10, state.pos);

// found!
state.posMax = state.pos;
state.pos = start + 10;

// Earlier we checked !silent, but this implementation does not need it
token = state.push('escape_open', 'span', 1);
token.markup = ':mdEscape:';

token = state.push('text', '', 0);
token.content = content.replace(UNESCAPE_RE, '$1');

token = state.push('escape_close', 'span', -1);
token.markup = ':/mdEscape:';

state.pos = state.posMax + 11;
state.posMax = max;
return true;
}


module.exports = function sub_plugin(md) {
md.inline.ruler.after('emphasis', 'md_escape', renderInlineEscape);
};

},{}]},{},[1])(1)
});
1 change: 0 additions & 1 deletion vendor/markdown-it-span.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
// same as UNESCAPE_MD_RE plus a space
var UNESCAPE_RE = /\\([ \\!"#$%&'()*+,.\/:;<=>?@[\]^_`{|}~-])/g;


function renderSpan(state, silent) {
var found,
content,
Expand Down

0 comments on commit 72a989a

Please sign in to comment.