Added sanitizer extension

This commit is contained in:
benweet 2014-05-28 00:34:49 +01:00
parent 3ccc267028
commit 580fd66b15
2 changed files with 250 additions and 205 deletions

View File

@ -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;
}); });

View File

@ -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 = [];