Stackedit/public/res/extensions/partialRendering.js

223 lines
8.5 KiB
JavaScript
Raw Normal View History

2013-07-23 23:51:21 +00:00
define([
"underscore",
2013-07-28 17:14:42 +00:00
"crel",
2013-11-17 22:59:03 +00:00
"extensions/markdownExtra",
2013-07-23 23:51:21 +00:00
"classes/Extension",
"text!html/partialRenderingSettingsBlock.html",
2013-11-17 22:59:03 +00:00
], function(_, crel, markdownExtra, Extension, partialRenderingSettingsBlockHTML) {
2013-07-23 23:51:21 +00:00
2013-07-28 10:35:04 +00:00
var partialRendering = new Extension("partialRendering", "Partial Rendering", true);
2013-07-23 23:51:21 +00:00
partialRendering.settingsBlock = partialRenderingSettingsBlockHTML;
2013-11-07 23:10:38 +00:00
var converter;
2013-07-26 00:44:12 +00:00
var sectionCounter = 0;
var sectionList = [];
2013-11-07 23:10:38 +00:00
var linkDefinition;
2013-07-26 00:44:12 +00:00
var sectionsToRemove = [];
var modifiedSections = [];
2013-11-07 23:10:38 +00:00
var insertBeforeSection;
2013-07-28 10:35:04 +00:00
var fileChanged = false;
2013-07-26 00:44:12 +00:00
function updateSectionList(newSectionList, newLinkDefinition) {
modifiedSections = [];
sectionsToRemove = [];
2013-07-28 17:14:42 +00:00
insertBeforeSection = undefined;
2013-07-26 00:44:12 +00:00
2013-07-28 10:35:04 +00:00
// Render everything if file or linkDefinition changed
2013-07-26 00:44:12 +00:00
if(fileChanged === true || linkDefinition != newLinkDefinition) {
fileChanged = false;
linkDefinition = newLinkDefinition;
sectionsToRemove = sectionList;
sectionList = newSectionList;
modifiedSections = newSectionList;
return;
2013-07-24 23:20:56 +00:00
}
2013-07-31 21:11:29 +00:00
// Find modified section starting from top
2013-07-24 23:20:56 +00:00
var leftIndex = sectionList.length;
_.some(sectionList, function(section, index) {
if(index >= newSectionList.length || section.text != newSectionList[index].text) {
leftIndex = index;
return true;
}
});
2013-09-16 00:03:17 +00:00
2013-07-31 21:11:29 +00:00
// Find modified section starting from bottom
2013-07-24 23:20:56 +00:00
var rightIndex = -sectionList.length;
_.some(sectionList.slice().reverse(), function(section, index) {
if(index >= newSectionList.length || section.text != newSectionList[newSectionList.length - index - 1].text) {
rightIndex = -index;
return true;
}
});
2013-09-15 14:14:42 +00:00
2013-09-16 00:03:17 +00:00
if(leftIndex - rightIndex > sectionList.length) {
// Prevent overlap
rightIndex = leftIndex - sectionList.length;
2013-09-15 14:14:42 +00:00
}
2013-07-24 23:20:56 +00:00
// Create an array composed of left unmodified, modified, right
// unmodified sections
var leftSections = sectionList.slice(0, leftIndex);
2013-07-26 00:44:12 +00:00
modifiedSections = newSectionList.slice(leftIndex, newSectionList.length + rightIndex);
2013-07-24 23:20:56 +00:00
var rightSections = sectionList.slice(sectionList.length + rightIndex, sectionList.length);
2013-07-28 17:14:42 +00:00
insertBeforeSection = _.first(rightSections);
2013-07-24 23:20:56 +00:00
sectionsToRemove = sectionList.slice(leftIndex, sectionList.length + rightIndex);
sectionList = leftSections.concat(modifiedSections).concat(rightSections);
}
2013-11-17 22:59:03 +00:00
2013-07-26 00:44:12 +00:00
var doFootnotes = false;
2013-07-23 23:51:21 +00:00
var hasFootnotes = false;
2013-07-26 00:44:12 +00:00
partialRendering.onSectionsCreated = function(sectionListParam) {
2013-07-23 23:51:21 +00:00
2013-07-26 00:44:12 +00:00
var newSectionList = [];
2013-09-15 14:14:42 +00:00
var newLinkDefinition = '\n';
2013-07-23 23:51:21 +00:00
hasFootnotes = false;
2013-11-17 22:59:03 +00:00
_.each(sectionListParam, function(section) {
var text = section.textWithDelimiter + '\n';
2013-07-23 23:51:21 +00:00
2013-07-28 17:14:42 +00:00
// Strip footnotes
2013-07-26 00:44:12 +00:00
if(doFootnotes) {
text = text.replace(/^```.*\n[\s\S]*?\n```|\n[ ]{0,3}\[\^(.+?)\]\:[ \t]*\n?([\s\S]*?)\n{1,2}((?=\n[ ]{0,3}\S)|$)/gm, function(wholeMatch, footnote) {
2013-07-26 00:44:12 +00:00
if(footnote) {
hasFootnotes = true;
2013-09-15 14:14:42 +00:00
newLinkDefinition += wholeMatch.replace(/^\s*\n/gm, '') + '\n';
2013-07-26 00:44:12 +00:00
return "";
}
return wholeMatch;
});
}
2013-07-28 17:14:42 +00:00
// Strip link definitions
text = text.replace(/^```.*\n[\s\S]*?\n```|^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*<?(\S+?)>?(?=\s|$)[ \t]*\n?[ \t]*((\n*)["(](.+?)[")][ \t]*)?(?:\n+)/gm, function(wholeMatch, link) {
if(link) {
2013-09-15 14:14:42 +00:00
newLinkDefinition += wholeMatch.replace(/^\s*\n/gm, '') + '\n';
2013-07-28 17:14:42 +00:00
return "";
}
return wholeMatch;
});
2013-11-17 22:59:03 +00:00
// Add section to the newSectionList
newSectionList.push({
id: ++sectionCounter,
text: text + '\n'
});
2013-07-24 23:20:56 +00:00
});
2013-07-23 23:51:21 +00:00
2013-07-26 00:44:12 +00:00
updateSectionList(newSectionList, newLinkDefinition);
};
2013-07-28 17:14:42 +00:00
var footnoteMap = {};
2013-11-07 23:10:38 +00:00
// Store one footnote elt in the footnote map
function storeFootnote(footnoteElt) {
var id = footnoteElt.id.substring(3);
footnoteMap[id] = footnoteElt;
}
var footnoteContainerElt;
var previewContentsElt;
2013-07-26 00:44:12 +00:00
function refreshSections() {
2013-07-23 23:51:21 +00:00
2013-07-24 23:20:56 +00:00
// Remove outdated sections
_.each(sectionsToRemove, function(section) {
2013-07-28 17:14:42 +00:00
var sectionElt = document.getElementById("wmd-preview-section-" + section.id);
previewContentsElt.removeChild(sectionElt);
2013-07-24 23:20:56 +00:00
});
2013-11-07 23:10:38 +00:00
2013-07-28 17:14:42 +00:00
var wmdPreviewElt = document.getElementById("wmd-preview");
2013-09-03 22:30:06 +00:00
var childNode = wmdPreviewElt.firstChild;
2013-11-07 23:10:38 +00:00
function createSectionElt(section) {
2013-07-28 17:14:42 +00:00
var sectionElt = crel('div', {
id: 'wmd-preview-section-' + section.id,
class: 'wmd-preview-section preview-content'
});
2013-11-17 22:59:03 +00:00
var isNextDelimiter = false;
2013-09-15 01:35:58 +00:00
while (childNode) {
2013-09-03 22:30:06 +00:00
var nextNode = childNode.nextSibling;
2013-11-17 22:59:03 +00:00
if(isNextDelimiter === true && childNode.tagName == 'DIV' && childNode.className == 'se-section-delimiter') {
// Stop when encountered the next delimiter
2013-07-28 17:14:42 +00:00
break;
2013-07-26 00:44:12 +00:00
}
2013-11-17 22:59:03 +00:00
isNextDelimiter = true;
2013-09-03 22:30:06 +00:00
if(childNode.tagName == 'DIV' && childNode.className == 'footnotes') {
2013-11-07 23:10:38 +00:00
_.each(childNode.querySelectorAll("ol > li"), storeFootnote);
2013-07-26 00:44:12 +00:00
}
else {
2013-09-03 22:30:06 +00:00
sectionElt.appendChild(childNode);
2013-07-26 00:44:12 +00:00
}
2013-09-03 22:30:06 +00:00
childNode = nextNode;
2013-09-15 01:35:58 +00:00
}
2013-11-07 23:10:38 +00:00
return sectionElt;
}
var newSectionEltList = document.createDocumentFragment();
_.each(modifiedSections, function(section) {
newSectionEltList.appendChild(createSectionElt(section));
2013-07-24 23:20:56 +00:00
});
2013-09-03 22:30:06 +00:00
wmdPreviewElt.innerHTML = '';
2013-07-28 17:14:42 +00:00
var insertBeforeElt = footnoteContainerElt;
if(insertBeforeSection !== undefined) {
insertBeforeElt = document.getElementById("wmd-preview-section-" + insertBeforeSection.id);
}
previewContentsElt.insertBefore(newSectionEltList, insertBeforeElt);
2013-07-23 23:51:21 +00:00
2013-07-26 00:44:12 +00:00
// Rewrite footnotes in the footer and update footnote numbers
2013-07-28 17:14:42 +00:00
footnoteContainerElt.innerHTML = '';
2013-07-26 00:44:12 +00:00
var usedFootnoteIds = [];
2013-07-23 23:51:21 +00:00
if(hasFootnotes === true) {
2013-07-28 17:14:42 +00:00
var footnoteElts = crel('ol');
_.each(previewContentsElt.querySelectorAll('a.footnote'), function(elt, index) {
elt.textContent = index + 1;
var id = elt.id.substring(6);
2013-07-26 00:44:12 +00:00
usedFootnoteIds.push(id);
2013-07-28 17:14:42 +00:00
footnoteElts.appendChild(footnoteMap[id].cloneNode(true));
2013-07-23 23:51:21 +00:00
});
2013-07-26 00:44:12 +00:00
if(usedFootnoteIds.length > 0) {
// Append the whole footnotes at the end of the document
2013-07-28 17:14:42 +00:00
footnoteContainerElt.appendChild(crel('div', {
class: 'footnotes'
}, crel('hr'), footnoteElts));
2013-07-26 00:44:12 +00:00
}
2013-07-28 10:35:04 +00:00
// Keep used footnotes only in our map
2013-07-28 17:14:42 +00:00
footnoteMap = _.pick(footnoteMap, usedFootnoteIds);
2013-07-23 23:51:21 +00:00
}
}
2013-09-09 23:32:24 +00:00
partialRendering.onPagedownConfigure = function(editor) {
2013-07-23 23:51:21 +00:00
converter = editor.getConverter();
2013-11-07 23:10:38 +00:00
converter.hooks.chain("preConversion", function() {
2013-07-26 00:44:12 +00:00
var result = _.map(modifiedSections, function(section) {
return section.text;
});
result.push(linkDefinition + "\n\n");
return result.join("");
2013-07-23 23:51:21 +00:00
});
editor.hooks.chain("onPreviewRefresh", function() {
2013-07-26 00:44:12 +00:00
refreshSections();
2013-07-23 23:51:21 +00:00
});
};
2013-07-24 23:20:56 +00:00
2013-11-17 22:59:03 +00:00
partialRendering.onInit = function(extraExtensions) {
if(markdownExtra.config.enabled) {
if(_.some(markdownExtra.config.extensions, function(extension) {
return extension == "footnotes";
})) {
doFootnotes = true;
}
}
};
2013-07-26 00:44:12 +00:00
partialRendering.onReady = function() {
2013-07-28 17:14:42 +00:00
footnoteContainerElt = crel('div', {
id: 'wmd-preview-section-footnotes',
class: 'preview-content'
});
previewContentsElt = document.getElementById("preview-contents");
previewContentsElt.appendChild(footnoteContainerElt);
2013-07-26 00:44:12 +00:00
};
2013-07-28 17:14:42 +00:00
2013-07-28 10:35:04 +00:00
partialRendering.onFileSelected = function() {
2013-07-26 00:44:12 +00:00
fileChanged = true;
};
2013-07-28 17:14:42 +00:00
2013-07-23 23:51:21 +00:00
return partialRendering;
});