2015-05-03 21:50:25 -07:00
"use strict" ;
( function main ( ) {
2022-12-29 00:43:53 +00:00
// Dependencies
2023-05-28 12:57:50 -07:00
var markdownit = window . markdownit ;
2022-12-29 00:43:53 +00:00
var markdownlint = window . markdownlint . library ;
var helpers = window . markdownlint . helpers ;
2023-05-28 12:57:50 -07:00
var micromark = window . micromarkBrowser ;
var micromarkHtml = window . micromarkHtmlBrowser ;
2022-12-29 00:43:53 +00:00
2015-05-03 21:50:25 -07:00
// DOM elements
var markdown = document . getElementById ( "markdown" ) ;
var markup = document . getElementById ( "markup" ) ;
var numbered = document . getElementById ( "numbered" ) ;
var violations = document . getElementById ( "violations" ) ;
var form = document . getElementsByTagName ( "form" ) [ 0 ] ;
var openFile = document . getElementById ( "openFile" ) ;
2019-04-02 22:07:32 -07:00
var copyLink = document . getElementById ( "copyLink" ) ;
2015-05-03 21:50:25 -07:00
2015-05-05 00:00:04 -07:00
// Variables
2015-05-03 21:50:25 -07:00
var newLineRe = /\r\n|\r|\n/ ;
2019-04-02 22:07:32 -07:00
var hashPrefix = "%m" ;
2021-02-06 15:55:21 -08:00
var allLintErrors = [ ] ;
2015-05-03 21:50:25 -07:00
2019-04-02 22:07:32 -07:00
// Do-nothing function
function noop ( ) { }
2016-10-16 21:46:02 -07:00
// Sanitize string for HTML display
function sanitize ( str ) {
return str
. replace ( /&/g , "&" )
. replace ( /</g , "<" )
. replace ( />/g , ">" ) ;
}
2023-05-28 12:57:50 -07:00
// Renders Markdown to HTML
function render ( markdown ) {
const match = /^\?renderer=([a-z-]+)$/ . exec ( window . location . search ) ;
const renderer = match ? match [ 1 ] : "micromark" ;
if ( renderer === "markdown-it" ) {
return markdownit ( { "html" : true } ) . render ( markdown ) ;
} else if ( renderer === "micromark" ) {
const parseOptions = {
"extensions" : [
2023-07-03 19:42:38 +00:00
micromark . gfmAutolinkLiteral ( ) ,
2023-05-28 12:57:50 -07:00
micromark . gfmFootnote ( ) ,
2023-07-16 22:07:34 -07:00
micromark . gfmTable ( ) ,
micromark . math ( )
2023-05-28 12:57:50 -07:00
]
} ;
const context = micromark . parse ( parseOptions ) ;
const chunks = micromark . preprocess ( ) ( markdown , undefined , true ) ;
const events = micromark . postprocess ( context . document ( ) . write ( chunks ) ) ;
const compileOptions = {
"allowDangerousHtml" : true ,
"htmlExtensions" : [
2023-07-03 19:42:38 +00:00
micromarkHtml . gfmAutolinkLiteralHtml ( ) ,
2023-05-28 12:57:50 -07:00
micromarkHtml . gfmFootnoteHtml ( ) ,
2023-07-16 22:07:34 -07:00
micromarkHtml . gfmTableHtml ( ) ,
micromarkHtml . mathHtml ( )
2023-05-28 12:57:50 -07:00
]
} ;
2023-07-24 21:36:55 -07:00
try {
return micromarkHtml . compile ( compileOptions ) ( events ) ;
} catch ( error ) {
return ` [Exception: " ${ error } "] ` ;
}
2023-05-28 12:57:50 -07:00
}
return ` [Unsupported renderer " ${ renderer } "] ` ;
}
2015-05-03 21:50:25 -07:00
// Handle input
function onMarkdownInput ( ) {
// Markdown
var content = markdown . value ;
// Markup
2023-05-28 12:57:50 -07:00
markup . innerHTML = render ( content ) ;
2015-05-03 21:50:25 -07:00
// Numbered
2015-05-05 00:00:04 -07:00
var lines = content . split ( newLineRe ) ;
var padding = lines . length . toString ( ) . replace ( /\d/g , " " ) ;
numbered . innerHTML = lines
2015-05-03 21:50:25 -07:00
. map ( function mapNumberedLine ( line , index ) {
2016-10-16 21:46:02 -07:00
line = sanitize ( line ) ;
2015-05-05 00:00:04 -07:00
index ++ ;
var paddedIndex = ( padding + index ) . slice ( - padding . length ) ;
return "<span id='l" + index + "'><em>" + paddedIndex + "</em>: " +
line + "</span>" ;
2015-05-03 21:50:25 -07:00
} ) . join ( "\n" ) ;
// Violations
var options = {
"strings" : {
"content" : content
2015-05-07 09:15:41 -07:00
} ,
"config" : {
"MD013" : false
2019-05-18 13:29:32 -07:00
} ,
2022-04-21 21:30:56 -07:00
"handleRuleFailures" : true
2015-05-03 21:50:25 -07:00
} ;
2022-12-29 00:43:53 +00:00
allLintErrors = markdownlint . sync ( options ) . content ;
2021-02-06 15:55:21 -08:00
violations . innerHTML = allLintErrors . map ( function mapResult ( result ) {
2020-09-15 21:15:35 -07:00
var ruleName = result . ruleNames . slice ( 0 , 2 ) . join ( " / " ) ;
return "<em><a href='#line' target='" + result . lineNumber + "'>" +
result . lineNumber + "</a></em> - <a href='" + result . ruleInformation +
"'>" + ruleName + "</a> " +
2016-10-16 21:46:02 -07:00
result . ruleDescription +
( result . errorDetail ?
" [<span class='detail'>" +
sanitize ( result . errorDetail ) +
"</span>]" :
"" ) +
( result . errorContext ?
" [<span class='detail'>Context: \"" +
sanitize ( result . errorContext ) +
"\"</span>]" :
2020-09-15 21:15:35 -07:00
"" ) +
( result . fixInfo ?
" [<a href='#fix' target='" +
encodeURIComponent ( JSON . stringify ( result ) ) +
"' class='detail'>Fix</a>]" :
2016-10-16 21:46:02 -07:00
"" ) ;
} ) . join ( "<br/>" ) ;
2015-05-03 21:50:25 -07:00
}
// Load from a string or File object
function loadMarkdown ( source ) {
// Reset input element
form . reset ( ) ;
if ( typeof source === "string" ) {
// Update from string
markdown . value = source ;
onMarkdownInput ( ) ;
} else {
// Update from File object
2023-05-07 20:49:38 -07:00
source . text ( ) . then ( ( text ) => {
markdown . value = text ;
2015-05-03 21:50:25 -07:00
onMarkdownInput ( ) ;
2023-05-07 20:49:38 -07:00
} ) ;
2015-05-03 21:50:25 -07:00
}
}
// Handle drag-and-drop
function onDragOver ( e ) {
if ( e . dataTransfer && e . dataTransfer . dropEffect ) {
e . dataTransfer . dropEffect = "link" ;
e . preventDefault ( ) ;
}
}
function onDrop ( e ) {
if ( e . dataTransfer && e . dataTransfer . files ) {
loadMarkdown ( e . dataTransfer . files [ 0 ] ) ;
e . preventDefault ( ) ;
}
}
// Handle file open
function onOpenFileChange ( e ) {
if ( e . target && e . target . files ) {
loadMarkdown ( e . target . files [ 0 ] ) ;
}
}
2015-05-05 00:00:04 -07:00
// Handle violation navigation
2020-09-15 21:15:35 -07:00
function onViolationClick ( e ) {
switch ( e . target . hash ) {
case "#fix" :
2021-02-06 15:55:21 -08:00
var errors = e . shiftKey ?
allLintErrors :
[ JSON . parse ( decodeURIComponent ( e . target . target ) ) ] ;
2022-12-29 00:43:53 +00:00
var fixed = helpers . applyFixes ( markdown . value , errors ) ;
2020-09-15 21:15:35 -07:00
markdown . value = fixed ;
onMarkdownInput ( ) ;
e . preventDefault ( ) ;
break ;
case "#line" :
var line = document . getElementById ( "l" + e . target . target ) ;
if ( line ) {
var highlighted = document . getElementsByClassName ( "highlight" ) ;
Array . prototype . forEach . call (
highlighted ,
function forElement ( element ) {
element . classList . remove ( "highlight" ) ;
}
) ;
line . classList . add ( "highlight" ) ;
line . scrollIntoView ( ) ;
}
e . preventDefault ( ) ;
break ;
default :
break ;
2015-05-05 00:00:04 -07:00
}
}
2019-04-02 22:07:32 -07:00
// Updates the URL hash and copies the URL to the clipboard
function onCopyLinkClick ( e ) {
window . location . hash = encodeURIComponent ( hashPrefix + markdown . value ) ;
2020-04-09 20:35:32 -07:00
if ( navigator . clipboard && navigator . clipboard . writeText ) {
2019-04-02 22:07:32 -07:00
navigator . clipboard . writeText ( window . location ) . then ( noop , noop ) ;
2020-04-09 20:35:32 -07:00
} else {
/* eslint-disable-next-line no-alert */
alert ( "Document URL updated, select and copy it now." ) ;
}
2019-04-02 22:07:32 -07:00
e . preventDefault ( ) ;
}
2020-11-14 16:42:18 -08:00
// Show library version
document . getElementById ( "version" ) . textContent =
2022-12-29 00:43:53 +00:00
"(v" + markdownlint . getVersion ( ) + ")" ;
2020-11-14 16:42:18 -08:00
2015-05-03 21:50:25 -07:00
// Add event listeners
document . body . addEventListener ( "dragover" , onDragOver ) ;
document . body . addEventListener ( "drop" , onDrop ) ;
openFile . addEventListener ( "change" , onOpenFileChange ) ;
markdown . addEventListener ( "input" , onMarkdownInput ) ;
2020-09-15 21:15:35 -07:00
violations . addEventListener ( "click" , onViolationClick , true ) ;
2019-04-02 22:07:32 -07:00
copyLink . addEventListener ( "click" , onCopyLinkClick ) ;
2015-05-07 09:15:41 -07:00
markdown . value = [
"## Introduction" ,
"" ,
2019-04-01 22:08:53 -07:00
"`markdownlint` is a [Node.js](https://nodejs.org/) style checker and lint tool for [Markdown](https://en.wikipedia.org/wiki/Markdown)/[CommonMark](https://commonmark.org/) files to automatically validate content, prevent rendering problems, and promote consistency." ,
2015-05-07 09:15:41 -07:00
"This page offers an easy way to try it out interactively!" ,
"" ,
"#### Instructions" ,
"" ,
"Type or paste `Markdown ` content in the upper-left box, drag-and-drop a file, or open one with the chooser at the top." ,
"Content gets parsed and displayed in the upper-right box; rule violations (if any) show up in the lower-right box." ,
"Click a violation for information about it or click its line number to highlighted it in the lower-left box." ,
"" ,
2022-10-30 14:58:45 -07:00
"> *Note*: [All rules](https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md) are enabled except [MD013/line-length](https://github.com/DavidAnson/markdownlint/blob/main/doc/md013.md)." ,
2015-05-07 09:15:41 -07:00
"" ,
"" ,
"#### Resources" ,
"* [`markdownlint` on GitHub](https://github.com/DavidAnson/markdownlint)" ,
"* [`markdownlint` on npm](https://www.npmjs.com/package/markdownlint)" ,
2019-02-10 11:38:01 -08:00
"* [Markdown specification](https://daringfireball.net/projects/markdown/)" ,
"*\t[CommonMark specification](https://commonmark.org/)" ,
2015-05-07 09:15:41 -07:00
"" ,
"#### Thanks" ,
"" ,
2019-05-06 22:00:38 -07:00
"[`markdownlint/Ruby`](https://github.com/markdownlint/markdownlint) for the inspiration and [`markdown-it`](https://github.com/markdown-it/markdown-it) for the parser and interactive demo idea!" ,
""
2015-05-07 09:15:41 -07:00
] . join ( "\n" ) ;
2019-04-01 22:08:53 -07:00
2019-04-02 22:07:32 -07:00
// Update Markdown from hash (if present)
if ( window . location . hash ) {
try {
2020-12-08 00:17:14 -05:00
var decodedHash = decodeURIComponent ( window . location . hash . substring ( 1 ) ) ;
2019-04-02 22:07:32 -07:00
if ( hashPrefix === decodedHash . substring ( 0 , hashPrefix . length ) ) {
markdown . value = decodedHash . substring ( hashPrefix . length ) ;
}
2020-09-06 20:34:10 -07:00
/* eslint-disable-next-line unicorn/prefer-optional-catch-binding */
} catch ( error ) {
2019-04-02 22:07:32 -07:00
// Invalid
}
}
2019-04-01 22:08:53 -07:00
// Detect legacy browsers
try {
/* eslint-disable-next-line no-new */
2020-09-15 21:15:35 -07:00
new URL ( "https://example.com/" ) ;
2020-09-06 20:34:10 -07:00
/* eslint-disable-next-line unicorn/prefer-optional-catch-binding */
} catch ( error ) {
2019-04-01 22:08:53 -07:00
markdown . value = [
"# Sorry" ,
"" ,
"This browser is not supported."
] . join ( "\n" ) ;
}
// Initialize
2015-05-07 09:15:41 -07:00
onMarkdownInput ( ) ;
2015-05-03 21:50:25 -07:00
} ( ) ) ;