Added TOC support

This commit is contained in:
benweet 2013-05-26 02:10:58 +01:00
parent c6244d3190
commit ce35c5c656
10 changed files with 160 additions and 19 deletions

View File

@ -191,6 +191,14 @@ h1 {
margin: 30px 0 30px; margin: 30px 0 30px;
} }
h4, h5, h6 {
line-height: 40px;
}
.toc ul {
list-style-type: none;
}
p, pre, blockquote { p, pre, blockquote {
margin: 0 0 20px; margin: 0 0 20px;
} }

View File

@ -309,8 +309,8 @@
data-dismiss="modal"><i class="icon-ok"></i></a> data-dismiss="modal"><i class="icon-ok"></i></a>
</div> </div>
<blockquote class="muted"> <blockquote class="muted">
<b>NOTE:</b> This will upload the local document firstly and <b>NOTE:</b> This will first upload the document and overwrite the
overwrite the existing file on the server. existing file on the server.
</blockquote> </blockquote>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
@ -627,11 +627,10 @@
</div> </div>
</div> </div>
<div class="tab-pane" id="tabpane-settings-extensions"> <div class="tab-pane" id="tabpane-settings-extensions">
<div class="accordion" id="accordion-extensions"> <div class="accordion" id="accordion-extensions"></div>
</div>
<span class="help-block pull-right"><a target="_blank" <span class="help-block pull-right"><a target="_blank"
href="https://github.com/benweet/stackedit/blob/master/doc/theming.md#stackedit-theming-guide">Create href="https://github.com/benweet/stackedit/blob/master/doc/theming.md#stackedit-theming-guide">Create
your own extension...</a></span> your own extension...</a></span>
</div> </div>
<div class="tab-pane" id="tabpane-settings-utils"> <div class="tab-pane" id="tabpane-settings-utils">
<div style="width: 200px; margin: 10px auto;"> <div style="width: 200px; margin: 10px auto;">

View File

@ -288,7 +288,7 @@ define(
// Custom insert link dialog // Custom insert link dialog
editor.hooks.set("insertLinkDialog", function (callback) { editor.hooks.set("insertLinkDialog", function (callback) {
insertLinkCallback = callback; insertLinkCallback = callback;
core.resetModalInputs(); utils.resetModalInputs();
$("#modal-insert-link").modal(); $("#modal-insert-link").modal();
_.defer(function() { _.defer(function() {
$("#input-insert-link").focus(); $("#input-insert-link").focus();
@ -298,7 +298,7 @@ define(
// Custom insert image dialog // Custom insert image dialog
editor.hooks.set("insertImageDialog", function (callback) { editor.hooks.set("insertImageDialog", function (callback) {
insertLinkCallback = callback; insertLinkCallback = callback;
core.resetModalInputs(); utils.resetModalInputs();
$("#modal-insert-image").modal(); $("#modal-insert-image").modal();
_.defer(function() { _.defer(function() {
$("#input-insert-image").focus(); $("#input-insert-image").focus();
@ -316,15 +316,21 @@ define(
}; };
}; };
if(core.settings.lazyRendering === true) { if(core.settings.lazyRendering === true) {
var lastRefresh = 0;
previewWrapper = function(makePreview) { previewWrapper = function(makePreview) {
var debouncedMakePreview = _.debounce(makePreview, 500); //var debouncedMakePreview = _.debounce(makePreview, 500);
return function() { return function() {
if(firstChange === true) { if(firstChange === true) {
makePreview(); makePreview();
} }
else { else {
onTextChange(); onTextChange();
debouncedMakePreview(); var currentDate = new Date().getTime();
if(currentDate - lastRefresh > 500) {
makePreview();
lastRefresh = currentDate;
}
//debouncedMakePreview();
} }
}; };
}; };
@ -519,7 +525,7 @@ define(
html: true, html: true,
container: '#modal-settings', container: '#modal-settings',
placement: 'right', placement: 'right',
title: 'Thank you for supporting StackEdit by adding a backlink in your documents!' title: 'Thanks for supporting StackEdit by adding a backlink in your documents!'
}); });
$(".tooltip-template").tooltip({ $(".tooltip-template").tooltip({
html: true, html: true,
@ -550,7 +556,7 @@ define(
// Reset inputs // Reset inputs
$(".action-reset-input").click(function() { $(".action-reset-input").click(function() {
core.resetModalInputs(); utils.resetModalInputs();
}); });
// Do periodic tasks // Do periodic tasks

View File

@ -5,6 +5,7 @@ define( [
"bootstrap", "bootstrap",
"extensions/notifications", "extensions/notifications",
"extensions/markdown-extra", "extensions/markdown-extra",
"extensions/toc",
"extensions/math-jax", "extensions/math-jax",
"extensions/scroll-link" "extensions/scroll-link"
], function($, utils) { ], function($, utils) {
@ -71,7 +72,7 @@ define( [
extensionSettings = extensionSettings || {}; extensionSettings = extensionSettings || {};
_.each(extensionList, function(extension) { _.each(extensionList, function(extension) {
extension.config = _.extend({}, extension.defaultConfig, extensionSettings[extension.extensionId]); extension.config = _.extend({}, extension.defaultConfig, extensionSettings[extension.extensionId]);
extension.config.enabled = !extension.optional || extension.config.enabled; extension.config.enabled = !extension.optional || extension.config.enabled === undefined || extension.config.enabled === true;
}); });
// Create accordion in settings dialog // Create accordion in settings dialog

View File

@ -8,7 +8,7 @@ define( [ "jquery", "underscore" ], function($) {
'<p>Binds together editor and preview scrollbars.</p>', '<p>Binds together editor and preview scrollbars.</p>',
'<blockquote class="muted"><b>NOTE:</b> ', '<blockquote class="muted"><b>NOTE:</b> ',
'The mapping between Markdown and HTML is based on the position of the title elements (h1, h2, ...) in the page. ', 'The mapping between Markdown and HTML is based on the position of the title elements (h1, h2, ...) in the page. ',
'Therefore, if your document does not contain any title, the mapping will be linear and consequently less efficient.', 'Therefore, if your document does not contain any title, the mapping will be linear and consequently less accurate.',
'</bloquote>' '</bloquote>'
].join("") ].join("")
}; };

127
js/extensions/toc.js Normal file
View File

@ -0,0 +1,127 @@
define( [ "jquery", "underscore" ], function($) {
var toc = {
extensionId: "toc",
extensionName: "Table Of Content",
optional: true,
settingsBloc: [
'<p>Generates tables of content using the marker [TOC].</p>'
].join("")
};
// Used to generate an anchor
function slugify(text)
{
return text.toLowerCase()
.replace(/\s+/g, '-') // Replace spaces with -
.replace(/[^\w\-]+/g, '') // Remove all non-word chars
.replace(/\-\-+/g, '-') // Replace multiple - with single -
.replace(/^-+/, '') // Trim - from start of text
.replace(/-+$/, ''); // Trim - from end of text
}
// TOC element description
function TocElement(tagName, anchor, text) {
this.tagName = tagName;
this.anchor = anchor;
this.text = text;
this.children = [];
}
TocElement.prototype.childrenToString = function() {
if(this.children.length === 0) {
return "";
}
var result = "<ul>";
_.each(this.children, function(child) {
result += child.toString();
});
result += "</ul>";
return result;
};
TocElement.prototype.toString = function() {
var result = "<li>";
if(this.anchor && this.text) {
result += '<a href="#' + this.anchor + '">' + this.text + '</a>';
}
result += this.childrenToString() + "</li>";
return result;
};
// Transform flat list of TocElement into a tree
function groupTags(array, level) {
level = level || 1;
var tagName = "H" + level;
var result = [];
var currentElement = undefined;
function pushCurrentElement() {
if(currentElement !== undefined) {
if(currentElement.children.length > 0) {
currentElement.children = groupTags(currentElement.children, level + 1);
}
result.push(currentElement);
}
}
_.each(array, function(element, index) {
if(element.tagName != tagName) {
if(currentElement === undefined) {
currentElement = new TocElement();
}
currentElement.children.push(element);
}
else {
pushCurrentElement();
currentElement = element;
}
});
pushCurrentElement();
return result;
}
// Build the TOC
function buildToc() {
var anchorList = {};
function createAnchor(element) {
var id = element.prop("id") || slugify(element.text());
var anchor = id;
var index = 0;
while(_.has(anchorList, anchor)) {
anchor = id + "-" + (++index);
}
anchorList[anchor] = true;
// Update the id of the element
element.prop("id", anchor);
return anchor;
}
var elementList = [];
$("#wmd-preview > h1," +
"#wmd-preview > h2," +
"#wmd-preview > h3," +
"#wmd-preview > h4," +
"#wmd-preview > h5," +
"#wmd-preview > h6").each(function() {
elementList.push(new TocElement(
$(this).prop("tagName"),
createAnchor($(this)),
$(this).text()
));
});
elementList = groupTags(elementList);
return '<div class="toc"><ul>' + elementList.toString() + '</ul></div>';
}
toc.onEditorConfigure = function(editor) {
// Run TOC generation when conversion is finished directly on HTML
editor.hooks.chain("onPreviewRefresh", function() {
var toc = buildToc();
var html = $("#wmd-preview").html();
html = html.replace(/<p>\[TOC\]<\/p>/g, toc);
$("#wmd-preview").html(html);
});
};
return toc;
});

View File

@ -1,4 +1,4 @@
define(["jquery", "core", "async-runner"], function($, core, asyncRunner) { define(["jquery", "core", "utils", "async-runner"], function($, core, utils, asyncRunner) {
var connected = false; var connected = false;
var authenticated = false; var authenticated = false;

View File

@ -1,5 +1,5 @@
define(["jquery", "core", "utils", "sharing", "blogger-provider", "dropbox-provider", "gist-provider", "github-provider", "gdrive-provider", "ssh-provider", "tumblr-provider", "wordpress-provider", "underscore"], define(["jquery", "core", "utils", "sharing", "blogger-provider", "dropbox-provider", "gist-provider", "github-provider", "gdrive-provider", "ssh-provider", "tumblr-provider", "wordpress-provider", "underscore"],
function($, core, sharing, utils) { function($, core, utils, sharing) {
var publisher = {}; var publisher = {};
@ -148,7 +148,7 @@ define(["jquery", "core", "utils", "sharing", "blogger-provider", "dropbox-provi
$('div[class*=" modal-publish-"]').hide().filter(".modal-publish-" + provider.providerId).show(); $('div[class*=" modal-publish-"]').hide().filter(".modal-publish-" + provider.providerId).show();
// Reset fields // Reset fields
core.resetModalInputs(); utils.resetModalInputs();
$("input:radio[name=radio-publish-format][value=" + defaultPublishFormat + "]").prop("checked", true); $("input:radio[name=radio-publish-format][value=" + defaultPublishFormat + "]").prop("checked", true);
// Load preferences // Load preferences

View File

@ -240,7 +240,7 @@ define(["jquery", "core", "utils", "dropbox-provider", "gdrive-provider", "under
function initExportDialog(provider) { function initExportDialog(provider) {
// Reset fields // Reset fields
core.resetModalInputs(); utils.resetModalInputs();
// Load preferences // Load preferences
var serializedPreferences = localStorage[provider.providerId + ".exportPreferences"]; var serializedPreferences = localStorage[provider.providerId + ".exportPreferences"];

View File

@ -104,7 +104,7 @@ define([ "jquery", "underscore" ], function($) {
// Basic trim function // Basic trim function
utils.trim = function(str) { utils.trim = function(str) {
return str.replace(/^\s+|\s+$/g, ''); return $.trim(str);
}; };
// Check an URL // Check an URL