')
.replace(/^]*)>([^<]+)/gm, MDaddHeaderIDs) // add header IDs;
.replace(/<\/h(\d)>/g,'↑');
return html;
}
//MD Render markdown from preprocessed source text
function MDmarkdown(sourceText,previewEl) {
const MDit = window.markdownit({linkify:false,typography:false,html:true})
.use(window.markdownitMultimdTable, {enableMultilineRows: true})
.use(window.markdownitSub)
.use(window.markdownitSup)
.use(window.markdownitFootnote)
.use(window.markdownitCentertext)
.use(window.markdownitDeflist)
.use(window.markdownitTocDoneRight)
;
let MDpreview = MDit.render( MDcustomPreProcess( sourceText ) );
previewEl.html( MDcustomPostProcess( MDpreview ) ); // set previewed html
}
// MD Live preview, add edited warning
function MDlivePreview(sourceEl,previewEl) {
MDmarkdown( sourceEl.val(),previewEl );
MDsetChecklistClass();
}
// MD Live Checkboxes prep: find each instance of [ ] or [x] and replace text in index = to clicked checkbox in Preview.
function MDreplaceAt(str, replacement, position) {
str = str.substring(0, position) + replacement + str.substring(position + replacement.length);
return str;
}
function MDreplaceNthSubStr(str,substr,replacement,index) {
let count = 0;
let found = substr.exec(str);
while ( found !== null ) {
if ( count === index ) {
return MDreplaceAt(str, replacement, found.index );
} else {
count++;
found = substr.exec(str);
}
}
}
// MD Live Checkboxes
function MDliveCheckBoxes(checkbox,sourceEl,previewEl) {
$('.checklist').removeClass('clicked');
checkbox.closest('p,li,dt,dd').addClass('clicked');
const thisIndex = previewEl.find('.checklist').index( $('.clicked') );
const srctext = sourceEl.val();
const substr = new RegExp(/\[\s*.\s*\]/g);
const replacement = ( checkbox.is(':checked') ? '[x]' : '[ ]' );
sourceEl.val( MDreplaceNthSubStr(srctext, substr, replacement, thisIndex) );
}
// MD Checkbox list class: Prevent checkbox lists from having list bullets
function MDsetChecklistClass() {
$('input[type="checkbox"]').closest('ul').addClass('no_list');
}
// MD Resize Split View
function MDresizeSplit(handle,sourceEl,previewEl) {
let $sidebarWidth = $('#sidebar').outerWidth();
let $pageWidth = window.innerWidth;
$(document).on('mousemove',function(e) {
e.stopPropagation();
e.preventDefault();
let pageX = e.pageX;
if ( pageX > $sidebarWidth + 100 && pageX < $pageWidth - 100 ) { // min widths
handle.css({'left': pageX - $sidebarWidth - 4 + 'px'});
sourceEl.css({'width': pageX - $sidebarWidth + 'px'});
previewEl.css({'left': sourceEl.outerWidth() + 'px'});
}
});
handle.on('mouseup',function() {
$(document).off('mousemove');
});
}
// MD UI Sync Scroll
function MDpercentage(el) { return (el.scrollTop / (el.scrollHeight - el.offsetHeight)); }
function MDsyncScroll(el1) {
let el2 = ( el1.getAttribute('id') === 'content_preview' ? document.getElementById('content_source') : document.getElementById('content_preview') );
if ( document.querySelector('input[name="sync_scroll"').checked ) {
el2.scrollTo( 0, (MDpercentage(el1) * (el2.scrollHeight - el2.offsetHeight)).toFixed(0) ); // toFixed(0) prevents scrolling feedback loop
}
}
// click TOC anchors
function MDtocClick(el,previewEl) {
let thisId = el.attr('href');
if ( thisId ) {
previewEl.scrollTop( $(thisId).offset().top - 48 );
}
}
// click Headers to return to TOC or top
function MDheaderClick(previewEl) {
if ( previewEl.find('.table-of-contents').length > 0 ) {
document.getElementsByClassName('table-of-contents')[0].scrollIntoView(true);
} else {
document.getElementById('preview').scroll(0,0);
}
}
// MD Clear text source
function clearText(container_el) {
if ( window.top !== window.self ) { // if iframe, send message to top (to remove iframe_edited class)
sendMessage('top','clear');
}
container_el.find('#content_source').show().focus().val('');
container_el.find('#content_preview').empty();
container_el.removeClass('edited has_warning');
}
// MD SAVE SOURCE or HTML
function MDprepHTML(data) {
data = data.replace(/.<\/span>/g,'');
return data;
}
function saveMD(filename, data) {
let blob = new Blob([data], {type: 'text/plain'});
let downloadEl = window.document.createElement('a');
downloadEl.href = window.URL.createObjectURL(blob);
downloadEl.download = filename;
document.body.appendChild(downloadEl);
downloadEl.click();
document.body.removeChild(downloadEl);
URL.revokeObjectURL(blob);
if ( window.top !== window.self ) { // if iframe, send message to top
sendMessage('top','clear');
}
$('body,#content_source,#content_text').removeClass('edited');
}
// list of functions to remember while sending messages and then execute after warning button click
function doFunction(funcName,args) {
var funcDictionary = { 'setLocation':setLocation, 'resetContent':resetContent, 'closeContent':closeContent, 'clickThis':clickThis, 'clickRow':clickRow, 'doubleClickRow':doubleClickRow, 'indexNavigation':indexNavigation, 'clearText':clearText, 'null':null };
return funcName === 'null' ? null : funcDictionary[funcName](args);
}
// Show warning after certain user actions if text editor or iframe has edited text; otherwise do the action.
function showWarning(funcName,args) {
// Don't show the warning if func = indexNavigation or clickRow; i.e., just hide text editor;
// In other words, only show warning when changing directories or if iframe content has been edited
if ( ( $('body').hasClass('edited') && funcName !== 'indexNavigation' && funcName !== 'clickRow' ) || $('body').hasClass('iframe_edited') ) {
if ( $('body').hasClass('edited') ) { // show warning and text editor (if hidden)
$body.addClass('has_warning').find('#warnings').removeClass().addClass('unloading');
$body.removeClass('has_hidden_text').addClass('has_text');
}
if ( $('body').hasClass('iframe_edited') ) { // if iframe is edited, send unloading message
sendMessage('iframe','unloading',funcName,args); // upon receipt of message, iframe will show its warning message, based on the funcName
}
} else {
doFunction(funcName,args);
}
}
// Send a message to iframe or parent
function sendMessage(target,message,funcName,args) {
var messageObj = { 'messageContent': message, 'functionName': funcName, 'arguments': args };
if ( target === 'iframe' ) {
let contentIFrame = document.getElementById('content_iframe');
contentIFrame.contentWindow.postMessage( messageObj, '*' );
}
if ( target === 'top' ) {
window.parent.postMessage( messageObj, '*');
}
}
// Receive a message from iframe or parent, do appropriate action
function receiveMessage(e) {
if ( e.origin === 'null' || e.origin === $origin ) {
let $message = e.data.messageContent;
let funcName = e.data.functionName;
let args = e.data.arguments;
if ( $message === 'split_view' ) {
$iframe_body.toggleClass('split_view');
}
if ( $message === 'default_text_view' ) {
$iframe_body.toggleClass('preview_text source_text').removeClass('split_view');
}
// warn iframe that user wants to change iframes
if ( $message === 'unloading' && !$iframe_body.hasClass('has_warning') ) {
$iframe_body.addClass('has_warning').find('#warnings').removeClass().addClass('unloading').attr('data-function_name',funcName).attr('data-args',args);
}
// let top know iframe text has been edited
if ( $message === 'iframe_edited' && !$('body#top').hasClass('iframe_edited') ) {
$('body#top').addClass('iframe_edited');
}
if ( $message === 'ignore' || $message === 'clear' ) {
$('body#top').removeClass('iframe_edited');
if ( $message === 'ignore' ) { doFunction(funcName,args); }
}
}
}
window.addEventListener('message',receiveMessage,false);
// Edited Warning buttons: what to do when the user clicks a warning button
function editedWarningButtons(id) {
let btn = $(document.getElementById(id));
let container_el = btn.closest('body');
let func = $('#warnings').attr('data-function_name');
let args = $('#warnings').attr('data-args');
switch(id) {
case 'warning_ignore_btn': // do the user initiated func without saving the edited text
if ( window.self !== window.top ) { // if iframe, send message to top
sendMessage('top','ignore',func,args);
}
container_el.removeClass('edited has_text has_warning');
clearText(container_el);
break;
case 'warning_cancel_btn': // cancel the func
container_el.removeClass('has_warning').find('#warnings').removeClass();
break;
case 'warning_clear_btn': // clear the text editor
clearText(container_el);
break;
case 'warning_save_btn': // save the text
if ( window.top !== window.self ) { // if iframe, send message to top
sendMessage('top','clear');
}
container_el.removeClass('edited has_warning');
$('#save_text').click();
break;
case 'warning_ok_btn': // clear the text editor
$('body').removeClass('has_warning').find('#warnings').removeClass('local');
break;
}
}
$('#warnings').on('click','button',function(e) {
e.preventDefault();
editedWarningButtons( $(this).attr('id') );
});
// Edited Warning overlay: prevent user clicks on rest of UI
$('#overlay').on('click mousedown mouseup',function(e) {
e.preventDefault();
e.stopPropagation();
return;
});
// END Text Editing
})();
// THE END!