diff --git a/public/res/extensions/partialRendering.js b/public/res/extensions/partialRendering.js index e2d9dc7a..26453499 100644 --- a/public/res/extensions/partialRendering.js +++ b/public/res/extensions/partialRendering.js @@ -1,225 +1,229 @@ define([ - "underscore", - "crel", - "extensions/markdownExtra", - "classes/Extension", - "text!html/partialRenderingSettingsBlock.html", + "underscore", + "crel", + "extensions/markdownExtra", + "classes/Extension", + "text!html/partialRenderingSettingsBlock.html", ], function(_, crel, markdownExtra, Extension, partialRenderingSettingsBlockHTML) { - var partialRendering = new Extension("partialRendering", "Partial Rendering", true); - partialRendering.settingsBlock = partialRenderingSettingsBlockHTML; + var partialRendering = new Extension("partialRendering", "Partial Rendering", true); + partialRendering.settingsBlock = partialRenderingSettingsBlockHTML; - var converter; - var doFootnotes = false; - var hasFootnotes = false; - 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
\n\n' + section.text + '\n\n'; + var converter; + var doFootnotes = false; + var hasFootnotes = false; + var currentSectionList = []; - // Strip footnotes - 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) { - if(footnote) { - hasFootnotes = true; - newLinkDefinition += wholeMatch.replace(/^\s*\n/gm, '') + '\n'; - return ""; - } - return wholeMatch; - }); - } + var sectionList = []; + var linkDefinition; + var sectionsToRemove = []; + var modifiedSections = []; + var insertBeforeSection; + var fileChanged = false; - // Strip link definitions - text = text.replace(/^```.*\n[\s\S]*?\n```|^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*?(?=\s|$)[ \t]*\n?[ \t]*((\n*)["(](.+?)[")][ \t]*)?(?:\n+)/gm, function(wholeMatch, link) { - if(link) { - newLinkDefinition += wholeMatch.replace(/^\s*\n/gm, '') + '\n'; - return ""; - } - return wholeMatch; - }); + function updateSectionList() { + var newSectionList = []; + var newLinkDefinition = '\n'; + hasFootnotes = false; + _.each(currentSectionList, function(section) { + var text = '\n
\n\n' + section.text + '\n\n'; - // Add section to the newSectionList - newSectionList.push({ - id: section.id, - text: text + '\n' - }); - }); + // Strip footnotes + 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) { + if(footnote) { + hasFootnotes = true; + newLinkDefinition += wholeMatch.replace(/^\s*\n/gm, '') + '\n'; + return ""; + } + return wholeMatch; + }); + } - modifiedSections = []; - sectionsToRemove = []; - insertBeforeSection = undefined; + // Strip link definitions + text = text.replace(/^```.*\n[\s\S]*?\n```|^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*?(?=\s|$)[ \t]*\n?[ \t]*((\n*)["(](.+?)[")][ \t]*)?(?:\n+)/gm, function(wholeMatch, link) { + if(link) { + newLinkDefinition += wholeMatch.replace(/^\s*\n/gm, '') + '\n'; + return ""; + } + return wholeMatch; + }); - // Render everything if file or linkDefinition changed - if(fileChanged === true || linkDefinition != newLinkDefinition) { - fileChanged = false; - linkDefinition = newLinkDefinition; - sectionsToRemove = sectionList; - sectionList = newSectionList; - modifiedSections = newSectionList; - return; - } + // Add section to the newSectionList + newSectionList.push({ + id: section.id, + text: text + '\n' + }); + }); - // Find modified section starting from top - var leftIndex = sectionList.length; - _.some(sectionList, function(section, index) { - 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; - } + modifiedSections = []; + sectionsToRemove = []; + insertBeforeSection = undefined; - // Create an array composed of left unmodified, modified, right - // 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); - } - - 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() { + // Render everything if file or linkDefinition changed + if(fileChanged === true || linkDefinition != newLinkDefinition) { + fileChanged = false; + linkDefinition = newLinkDefinition; + sectionsToRemove = sectionList; + sectionList = newSectionList; + modifiedSections = newSectionList; + return; + } - // 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; - } + // Find modified section starting from top + var leftIndex = sectionList.length; + _.some(sectionList, function(section, index) { + if(index >= newSectionList.length || section.text != newSectionList[index].text) { + leftIndex = index; + return true; + } + }); - 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); + // 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; + } + }); - // 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); - 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); - } - } + if(leftIndex - rightIndex > sectionList.length) { + // Prevent overlap + rightIndex = leftIndex - sectionList.length; + } - partialRendering.onSectionsCreated = function(sectionListParam) { - currentSectionList = sectionListParam; - }; + // Create an array composed of left unmodified, modified, right + // 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) { - 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(); - }); - }; + var footnoteMap = {}; + // Store one footnote elt in the footnote map + function storeFootnote(footnoteElt) { + var id = footnoteElt.id.substring(3); + footnoteMap[id] = footnoteElt; + } - 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); - }; + var footnoteContainerElt; + var previewContentsElt; - partialRendering.onFileSelected = function() { - fileChanged = true; - }; + function refreshSections() { - 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; }); \ No newline at end of file diff --git a/public/res/utils.js b/public/res/utils.js index 9620ab6c..9b435106 100644 --- a/public/res/utils.js +++ b/public/res/utils.js @@ -10,6 +10,47 @@ define([ 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) utils.defer = (function() { var timeouts = [];