Added sanitizer extension
This commit is contained in:
parent
3ccc267028
commit
580fd66b15
@ -1,225 +1,229 @@
|
|||||||
define([
|
define([
|
||||||
"underscore",
|
"underscore",
|
||||||
"crel",
|
"crel",
|
||||||
"extensions/markdownExtra",
|
"extensions/markdownExtra",
|
||||||
"classes/Extension",
|
"classes/Extension",
|
||||||
"text!html/partialRenderingSettingsBlock.html",
|
"text!html/partialRenderingSettingsBlock.html",
|
||||||
], function(_, crel, markdownExtra, Extension, partialRenderingSettingsBlockHTML) {
|
], function(_, crel, markdownExtra, Extension, partialRenderingSettingsBlockHTML) {
|
||||||
|
|
||||||
var partialRendering = new Extension("partialRendering", "Partial Rendering", true);
|
var partialRendering = new Extension("partialRendering", "Partial Rendering", true);
|
||||||
partialRendering.settingsBlock = partialRenderingSettingsBlockHTML;
|
partialRendering.settingsBlock = partialRenderingSettingsBlockHTML;
|
||||||
|
|
||||||
var converter;
|
var converter;
|
||||||
var doFootnotes = false;
|
var doFootnotes = false;
|
||||||
var hasFootnotes = false;
|
var hasFootnotes = false;
|
||||||
var currentSectionList = [];
|
var currentSectionList = [];
|
||||||
|
|
||||||
var sectionList = [];
|
|
||||||
var linkDefinition;
|
|
||||||
var sectionsToRemove = [];
|
|
||||||
var modifiedSections = [];
|
|
||||||
var insertBeforeSection;
|
|
||||||
var fileChanged = false;
|
|
||||||
function updateSectionList() {
|
|
||||||
var newSectionList = [];
|
|
||||||
var newLinkDefinition = '\n';
|
|
||||||
hasFootnotes = false;
|
|
||||||
_.each(currentSectionList, function(section) {
|
|
||||||
var text = '\n<div class="se-preview-section-delimiter"></div>\n\n' + section.text + '\n\n';
|
|
||||||
|
|
||||||
// Strip footnotes
|
var sectionList = [];
|
||||||
if(doFootnotes) {
|
var linkDefinition;
|
||||||
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) {
|
var sectionsToRemove = [];
|
||||||
if(footnote) {
|
var modifiedSections = [];
|
||||||
hasFootnotes = true;
|
var insertBeforeSection;
|
||||||
newLinkDefinition += wholeMatch.replace(/^\s*\n/gm, '') + '\n';
|
var fileChanged = false;
|
||||||
return "";
|
|
||||||
}
|
|
||||||
return wholeMatch;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Strip link definitions
|
function updateSectionList() {
|
||||||
text = text.replace(/^```.*\n[\s\S]*?\n```|^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*<?(\S+?)>?(?=\s|$)[ \t]*\n?[ \t]*((\n*)["(](.+?)[")][ \t]*)?(?:\n+)/gm, function(wholeMatch, link) {
|
var newSectionList = [];
|
||||||
if(link) {
|
var newLinkDefinition = '\n';
|
||||||
newLinkDefinition += wholeMatch.replace(/^\s*\n/gm, '') + '\n';
|
hasFootnotes = false;
|
||||||
return "";
|
_.each(currentSectionList, function(section) {
|
||||||
}
|
var text = '\n<div class="se-preview-section-delimiter"></div>\n\n' + section.text + '\n\n';
|
||||||
return wholeMatch;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add section to the newSectionList
|
// Strip footnotes
|
||||||
newSectionList.push({
|
if(doFootnotes) {
|
||||||
id: section.id,
|
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) {
|
||||||
text: text + '\n'
|
if(footnote) {
|
||||||
});
|
hasFootnotes = true;
|
||||||
});
|
newLinkDefinition += wholeMatch.replace(/^\s*\n/gm, '') + '\n';
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return wholeMatch;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
modifiedSections = [];
|
// Strip link definitions
|
||||||
sectionsToRemove = [];
|
text = text.replace(/^```.*\n[\s\S]*?\n```|^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*<?(\S+?)>?(?=\s|$)[ \t]*\n?[ \t]*((\n*)["(](.+?)[")][ \t]*)?(?:\n+)/gm, function(wholeMatch, link) {
|
||||||
insertBeforeSection = undefined;
|
if(link) {
|
||||||
|
newLinkDefinition += wholeMatch.replace(/^\s*\n/gm, '') + '\n';
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return wholeMatch;
|
||||||
|
});
|
||||||
|
|
||||||
// Render everything if file or linkDefinition changed
|
// Add section to the newSectionList
|
||||||
if(fileChanged === true || linkDefinition != newLinkDefinition) {
|
newSectionList.push({
|
||||||
fileChanged = false;
|
id: section.id,
|
||||||
linkDefinition = newLinkDefinition;
|
text: text + '\n'
|
||||||
sectionsToRemove = sectionList;
|
});
|
||||||
sectionList = newSectionList;
|
});
|
||||||
modifiedSections = newSectionList;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find modified section starting from top
|
modifiedSections = [];
|
||||||
var leftIndex = sectionList.length;
|
sectionsToRemove = [];
|
||||||
_.some(sectionList, function(section, index) {
|
insertBeforeSection = undefined;
|
||||||
if(index >= newSectionList.length || section.text != newSectionList[index].text) {
|
|
||||||
leftIndex = index;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Find modified section starting from bottom
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if(leftIndex - rightIndex > sectionList.length) {
|
|
||||||
// Prevent overlap
|
|
||||||
rightIndex = leftIndex - sectionList.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create an array composed of left unmodified, modified, right
|
// Render everything if file or linkDefinition changed
|
||||||
// unmodified sections
|
if(fileChanged === true || linkDefinition != newLinkDefinition) {
|
||||||
var leftSections = sectionList.slice(0, leftIndex);
|
fileChanged = false;
|
||||||
modifiedSections = newSectionList.slice(leftIndex, newSectionList.length + rightIndex);
|
linkDefinition = newLinkDefinition;
|
||||||
var rightSections = sectionList.slice(sectionList.length + rightIndex, sectionList.length);
|
sectionsToRemove = sectionList;
|
||||||
insertBeforeSection = _.first(rightSections);
|
sectionList = newSectionList;
|
||||||
sectionsToRemove = sectionList.slice(leftIndex, sectionList.length + rightIndex);
|
modifiedSections = newSectionList;
|
||||||
sectionList = leftSections.concat(modifiedSections).concat(rightSections);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var footnoteMap = {};
|
|
||||||
// Store one footnote elt in the footnote map
|
|
||||||
function storeFootnote(footnoteElt) {
|
|
||||||
var id = footnoteElt.id.substring(3);
|
|
||||||
footnoteMap[id] = footnoteElt;
|
|
||||||
}
|
|
||||||
|
|
||||||
var footnoteContainerElt;
|
|
||||||
var previewContentsElt;
|
|
||||||
function refreshSections() {
|
|
||||||
|
|
||||||
// Remove outdated sections
|
// Find modified section starting from top
|
||||||
_.each(sectionsToRemove, function(section) {
|
var leftIndex = sectionList.length;
|
||||||
var sectionElt = document.getElementById("wmd-preview-section-" + section.id);
|
_.some(sectionList, function(section, index) {
|
||||||
previewContentsElt.removeChild(sectionElt);
|
if(index >= newSectionList.length || section.text != newSectionList[index].text) {
|
||||||
});
|
leftIndex = index;
|
||||||
|
return true;
|
||||||
var wmdPreviewElt = document.getElementById("wmd-preview");
|
}
|
||||||
var childNode = wmdPreviewElt.firstChild;
|
});
|
||||||
function createSectionElt(section) {
|
|
||||||
var sectionElt = crel('div', {
|
|
||||||
id: 'wmd-preview-section-' + section.id,
|
|
||||||
class: 'wmd-preview-section preview-content'
|
|
||||||
});
|
|
||||||
var isNextDelimiter = false;
|
|
||||||
while (childNode) {
|
|
||||||
var nextNode = childNode.nextSibling;
|
|
||||||
var isDelimiter = childNode.className == 'se-preview-section-delimiter';
|
|
||||||
if(isNextDelimiter === true && childNode.tagName == 'DIV' && isDelimiter) {
|
|
||||||
// Stop when encountered the next delimiter
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
isNextDelimiter = true;
|
|
||||||
if(childNode.tagName == 'DIV' && childNode.className == 'footnotes') {
|
|
||||||
_.each(childNode.querySelectorAll("ol > li"), storeFootnote);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
isDelimiter || sectionElt.appendChild(childNode);
|
|
||||||
}
|
|
||||||
childNode = nextNode;
|
|
||||||
}
|
|
||||||
return sectionElt;
|
|
||||||
}
|
|
||||||
|
|
||||||
var newSectionEltList = document.createDocumentFragment();
|
// Find modified section starting from bottom
|
||||||
_.each(modifiedSections, function(section) {
|
var rightIndex = -sectionList.length;
|
||||||
newSectionEltList.appendChild(createSectionElt(section));
|
_.some(sectionList.slice().reverse(), function(section, index) {
|
||||||
});
|
if(index >= newSectionList.length || section.text != newSectionList[newSectionList.length - index - 1].text) {
|
||||||
wmdPreviewElt.innerHTML = '';
|
rightIndex = -index;
|
||||||
var insertBeforeElt = footnoteContainerElt;
|
return true;
|
||||||
if(insertBeforeSection !== undefined) {
|
}
|
||||||
insertBeforeElt = document.getElementById("wmd-preview-section-" + insertBeforeSection.id);
|
});
|
||||||
}
|
|
||||||
previewContentsElt.insertBefore(newSectionEltList, insertBeforeElt);
|
|
||||||
|
|
||||||
// Rewrite footnotes in the footer and update footnote numbers
|
if(leftIndex - rightIndex > sectionList.length) {
|
||||||
footnoteContainerElt.innerHTML = '';
|
// Prevent overlap
|
||||||
var usedFootnoteIds = [];
|
rightIndex = leftIndex - sectionList.length;
|
||||||
if(hasFootnotes === true) {
|
}
|
||||||
var footnoteElts = crel('ol');
|
|
||||||
_.each(previewContentsElt.querySelectorAll('a.footnote'), function(elt, index) {
|
|
||||||
elt.textContent = index + 1;
|
|
||||||
var id = elt.id.substring(6);
|
|
||||||
usedFootnoteIds.push(id);
|
|
||||||
footnoteElts.appendChild(footnoteMap[id].cloneNode(true));
|
|
||||||
});
|
|
||||||
if(usedFootnoteIds.length > 0) {
|
|
||||||
// Append the whole footnotes at the end of the document
|
|
||||||
footnoteContainerElt.appendChild(crel('div', {
|
|
||||||
class: 'footnotes'
|
|
||||||
}, crel('hr'), footnoteElts));
|
|
||||||
}
|
|
||||||
// Keep used footnotes only in our map
|
|
||||||
footnoteMap = _.pick(footnoteMap, usedFootnoteIds);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
partialRendering.onSectionsCreated = function(sectionListParam) {
|
// Create an array composed of left unmodified, modified, right
|
||||||
currentSectionList = sectionListParam;
|
// unmodified sections
|
||||||
};
|
var leftSections = sectionList.slice(0, leftIndex);
|
||||||
|
modifiedSections = newSectionList.slice(leftIndex, newSectionList.length + rightIndex);
|
||||||
|
var rightSections = sectionList.slice(sectionList.length + rightIndex, sectionList.length);
|
||||||
|
insertBeforeSection = _.first(rightSections);
|
||||||
|
sectionsToRemove = sectionList.slice(leftIndex, sectionList.length + rightIndex);
|
||||||
|
sectionList = leftSections.concat(modifiedSections).concat(rightSections);
|
||||||
|
}
|
||||||
|
|
||||||
partialRendering.onPagedownConfigure = function(editor) {
|
var footnoteMap = {};
|
||||||
converter = editor.getConverter();
|
// Store one footnote elt in the footnote map
|
||||||
converter.hooks.chain("preConversion", function() {
|
function storeFootnote(footnoteElt) {
|
||||||
updateSectionList();
|
var id = footnoteElt.id.substring(3);
|
||||||
var result = _.map(modifiedSections, function(section) {
|
footnoteMap[id] = footnoteElt;
|
||||||
return section.text;
|
}
|
||||||
});
|
|
||||||
result.push(linkDefinition + "\n\n");
|
|
||||||
return result.join("");
|
|
||||||
});
|
|
||||||
editor.hooks.chain("onPreviewRefresh", function() {
|
|
||||||
refreshSections();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
partialRendering.onInit = function() {
|
var footnoteContainerElt;
|
||||||
if(markdownExtra.enabled) {
|
var previewContentsElt;
|
||||||
if(_.some(markdownExtra.config.extensions, function(extension) {
|
|
||||||
return extension == "footnotes";
|
|
||||||
})) {
|
|
||||||
doFootnotes = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
partialRendering.onReady = function() {
|
|
||||||
footnoteContainerElt = crel('div', {
|
|
||||||
id: 'wmd-preview-section-footnotes',
|
|
||||||
class: 'preview-content'
|
|
||||||
});
|
|
||||||
previewContentsElt = document.getElementById("preview-contents");
|
|
||||||
previewContentsElt.appendChild(footnoteContainerElt);
|
|
||||||
};
|
|
||||||
|
|
||||||
partialRendering.onFileSelected = function() {
|
function refreshSections() {
|
||||||
fileChanged = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
return partialRendering;
|
// Remove outdated sections
|
||||||
|
_.each(sectionsToRemove, function(section) {
|
||||||
|
var sectionElt = document.getElementById("wmd-preview-section-" + section.id);
|
||||||
|
previewContentsElt.removeChild(sectionElt);
|
||||||
|
});
|
||||||
|
|
||||||
|
var wmdPreviewElt = document.getElementById("wmd-preview");
|
||||||
|
var childNode = wmdPreviewElt.firstChild;
|
||||||
|
|
||||||
|
function createSectionElt(section) {
|
||||||
|
var sectionElt = crel('div', {
|
||||||
|
id: 'wmd-preview-section-' + section.id,
|
||||||
|
class: 'wmd-preview-section preview-content'
|
||||||
|
});
|
||||||
|
var isNextDelimiter = false;
|
||||||
|
while(childNode) {
|
||||||
|
var nextNode = childNode.nextSibling;
|
||||||
|
var isDelimiter = childNode.className == 'se-preview-section-delimiter';
|
||||||
|
if(isNextDelimiter === true && childNode.tagName == 'DIV' && isDelimiter) {
|
||||||
|
// Stop when encountered the next delimiter
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
isNextDelimiter = true;
|
||||||
|
if(childNode.tagName == 'DIV' && childNode.className == 'footnotes') {
|
||||||
|
_.each(childNode.querySelectorAll("ol > li"), storeFootnote);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
isDelimiter || sectionElt.appendChild(childNode);
|
||||||
|
}
|
||||||
|
childNode = nextNode;
|
||||||
|
}
|
||||||
|
return sectionElt;
|
||||||
|
}
|
||||||
|
|
||||||
|
var newSectionEltList = document.createDocumentFragment();
|
||||||
|
_.each(modifiedSections, function(section) {
|
||||||
|
newSectionEltList.appendChild(createSectionElt(section));
|
||||||
|
});
|
||||||
|
wmdPreviewElt.innerHTML = '';
|
||||||
|
var insertBeforeElt = footnoteContainerElt;
|
||||||
|
if(insertBeforeSection !== undefined) {
|
||||||
|
insertBeforeElt = document.getElementById("wmd-preview-section-" + insertBeforeSection.id);
|
||||||
|
}
|
||||||
|
previewContentsElt.insertBefore(newSectionEltList, insertBeforeElt);
|
||||||
|
|
||||||
|
// Rewrite footnotes in the footer and update footnote numbers
|
||||||
|
footnoteContainerElt.innerHTML = '';
|
||||||
|
var usedFootnoteIds = [];
|
||||||
|
if(hasFootnotes === true) {
|
||||||
|
var footnoteElts = crel('ol');
|
||||||
|
_.each(previewContentsElt.querySelectorAll('a.footnote'), function(elt, index) {
|
||||||
|
elt.textContent = index + 1;
|
||||||
|
var id = elt.id.substring(6);
|
||||||
|
usedFootnoteIds.push(id);
|
||||||
|
var footnoteElt = footnoteMap[id];
|
||||||
|
footnoteElt && footnoteElts.appendChild(footnoteElt.cloneNode(true));
|
||||||
|
});
|
||||||
|
if(usedFootnoteIds.length > 0) {
|
||||||
|
// Append the whole footnotes at the end of the document
|
||||||
|
footnoteContainerElt.appendChild(crel('div', {
|
||||||
|
class: 'footnotes'
|
||||||
|
}, crel('hr'), footnoteElts));
|
||||||
|
}
|
||||||
|
// Keep used footnotes only in our map
|
||||||
|
footnoteMap = _.pick(footnoteMap, usedFootnoteIds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
partialRendering.onSectionsCreated = function(sectionListParam) {
|
||||||
|
currentSectionList = sectionListParam;
|
||||||
|
};
|
||||||
|
|
||||||
|
partialRendering.onPagedownConfigure = function(editor) {
|
||||||
|
converter = editor.getConverter();
|
||||||
|
converter.hooks.chain("preConversion", function() {
|
||||||
|
updateSectionList();
|
||||||
|
var result = _.map(modifiedSections, function(section) {
|
||||||
|
return section.text;
|
||||||
|
});
|
||||||
|
result.push(linkDefinition + "\n\n");
|
||||||
|
return result.join("");
|
||||||
|
});
|
||||||
|
editor.hooks.chain("onPreviewRefresh", function() {
|
||||||
|
refreshSections();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
partialRendering.onInit = function() {
|
||||||
|
if(markdownExtra.enabled) {
|
||||||
|
if(_.some(markdownExtra.config.extensions, function(extension) {
|
||||||
|
return extension == "footnotes";
|
||||||
|
})) {
|
||||||
|
doFootnotes = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
partialRendering.onReady = function() {
|
||||||
|
footnoteContainerElt = crel('div', {
|
||||||
|
id: 'wmd-preview-section-footnotes',
|
||||||
|
class: 'preview-content'
|
||||||
|
});
|
||||||
|
previewContentsElt = document.getElementById("preview-contents");
|
||||||
|
previewContentsElt.appendChild(footnoteContainerElt);
|
||||||
|
};
|
||||||
|
|
||||||
|
partialRendering.onFileSelected = function() {
|
||||||
|
fileChanged = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
return partialRendering;
|
||||||
});
|
});
|
@ -10,6 +10,47 @@ define([
|
|||||||
|
|
||||||
var utils = {};
|
var utils = {};
|
||||||
|
|
||||||
|
utils.msie = (function() {
|
||||||
|
/**
|
||||||
|
* IE 11 changed the format of the UserAgent string.
|
||||||
|
* See http://msdn.microsoft.com/en-us/library/ms537503.aspx
|
||||||
|
*/
|
||||||
|
var msie = parseInt((/msie (\d+)/.exec(navigator.userAgent.toLowerCase()) || [])[1], 10);
|
||||||
|
if (isNaN(msie)) {
|
||||||
|
msie = parseInt((/trident\/.*; rv:(\d+)/.exec(navigator.userAgent.toLowerCase()) || [])[1], 10);
|
||||||
|
}
|
||||||
|
return msie;
|
||||||
|
})();
|
||||||
|
|
||||||
|
utils.urlResolve = (function() {
|
||||||
|
var urlParsingNode = document.createElement("a");
|
||||||
|
return function urlResolve(url) {
|
||||||
|
var href = url;
|
||||||
|
|
||||||
|
if (utils.msie) {
|
||||||
|
// Normalize before parse. Refer Implementation Notes on why this is
|
||||||
|
// done in two steps on IE.
|
||||||
|
urlParsingNode.setAttribute("href", href);
|
||||||
|
href = urlParsingNode.href;
|
||||||
|
}
|
||||||
|
|
||||||
|
urlParsingNode.setAttribute('href', href);
|
||||||
|
|
||||||
|
// urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils
|
||||||
|
return {
|
||||||
|
href: urlParsingNode.href,
|
||||||
|
protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',
|
||||||
|
host: urlParsingNode.host,
|
||||||
|
search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '',
|
||||||
|
hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',
|
||||||
|
hostname: urlParsingNode.hostname,
|
||||||
|
port: urlParsingNode.port,
|
||||||
|
pathname: (urlParsingNode.pathname.charAt(0) === '/') ?
|
||||||
|
urlParsingNode.pathname : '/' + urlParsingNode.pathname
|
||||||
|
};
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
// Faster than setTimeout (see http://dbaron.org/log/20100309-faster-timeouts)
|
// Faster than setTimeout (see http://dbaron.org/log/20100309-faster-timeouts)
|
||||||
utils.defer = (function() {
|
utils.defer = (function() {
|
||||||
var timeouts = [];
|
var timeouts = [];
|
||||||
|
Loading…
Reference in New Issue
Block a user