mirror of
https://github.com/vale981/DiscoTOC
synced 2025-03-04 17:11:42 -05:00
DiscoTOC v0.2
This commit is contained in:
parent
94cb1f1a4b
commit
1ee43956c2
4 changed files with 420 additions and 345 deletions
|
@ -1,23 +1,6 @@
|
||||||
@import "common/foundation/variables";
|
@import "common/foundation/variables";
|
||||||
|
|
||||||
.d-toc-regular {
|
.d-toc-regular {
|
||||||
.cooked {
|
|
||||||
h1,
|
|
||||||
h2,
|
|
||||||
h3,
|
|
||||||
h4,
|
|
||||||
h5,
|
|
||||||
h6 {
|
|
||||||
&:before {
|
|
||||||
display: block;
|
|
||||||
content: "";
|
|
||||||
height: 70px;
|
|
||||||
margin-top: -70px;
|
|
||||||
pointer-events: none;
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
[data-theme-toc] {
|
[data-theme-toc] {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
@ -38,9 +21,9 @@
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
border: none;
|
border: none;
|
||||||
line-height: 30px;
|
|
||||||
}
|
}
|
||||||
.d-toc-item {
|
.d-toc-item {
|
||||||
|
padding: 6px 0;
|
||||||
a {
|
a {
|
||||||
color: $primary-high;
|
color: $primary-high;
|
||||||
}
|
}
|
||||||
|
@ -149,14 +132,20 @@
|
||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.d-toc-anchor-link {
|
.d-toc-post-heading {
|
||||||
font-size: initial;
|
.d-toc-anchor-link {
|
||||||
color: $primary-medium;
|
font-size: initial;
|
||||||
:not(.rtl) & {
|
color: transparent;
|
||||||
margin-left: 5px;
|
transition: color 0.15s linear;
|
||||||
|
:not(.rtl) & {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
.rtl & {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.rtl & {
|
&:hover .d-toc-anchor-link {
|
||||||
margin-right: 5px;
|
color: rgba($primary-medium, 0.6);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -179,7 +168,7 @@
|
||||||
max-height: 85vh;
|
max-height: 85vh;
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
margin-top: 50px;
|
margin-top: 50px;
|
||||||
|
position: -webkit-sticky;
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 75px;
|
top: 75px;
|
||||||
margin-bottom: 135px;
|
margin-bottom: 135px;
|
||||||
|
@ -197,6 +186,12 @@
|
||||||
}
|
}
|
||||||
.d-toc-article {
|
.d-toc-article {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
.post-notice {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.topic-map {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
> .row {
|
> .row {
|
||||||
:not(.rtl) & {
|
:not(.rtl) & {
|
||||||
border-right: 1px solid $primary-low;
|
border-right: 1px solid $primary-low;
|
||||||
|
@ -260,6 +255,7 @@
|
||||||
background: $secondary;
|
background: $secondary;
|
||||||
|
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
|
position: -webkit-sticky;
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -303,14 +299,33 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.d-toc-timeline {
|
||||||
|
.timeline-container,
|
||||||
|
#topic-progress-wrapper {
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
&.d-toc-timeline-visible {
|
||||||
|
.timeline-container,
|
||||||
|
#topic-progress-wrapper {
|
||||||
|
opacity: 1;
|
||||||
|
pointer-events: initial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.edit-title .d-editor-preview [data-theme-toc] {
|
.edit-title .d-editor-preview [data-theme-toc] {
|
||||||
background: $tertiary;
|
background: $tertiary;
|
||||||
color: $secondary;
|
color: $secondary;
|
||||||
border-top: 2px solid $secondary;
|
border-top: 2px solid $secondary;
|
||||||
|
position: -webkit-sticky;
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
&:before {
|
||||||
|
content: "#{$composer_toc_text}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,345 +1,391 @@
|
||||||
<script type="text/discourse-plugin" version="0.1">
|
<script type="text/discourse-plugin" version="0.1">
|
||||||
const mobileView = $("html").hasClass("mobile-view"),
|
const computed = require("ember-addons/ember-computed-decorators").default;
|
||||||
{ iconHTML } = require("discourse-common/lib/icon-library"),
|
const minimumOffset = require("discourse/lib/offset-calculator").minimumOffset;
|
||||||
{ run } = Ember,
|
const { iconHTML } = require("discourse-common/lib/icon-library");
|
||||||
linkIcon = iconHTML("link"),
|
const { run } = Ember;
|
||||||
closeIcon = iconHTML("times"),
|
|
||||||
dtocIcon = iconHTML("align-left"),
|
const mobileView = $("html").hasClass("mobile-view");
|
||||||
items = [],
|
const isSafari = !!navigator.userAgent.match(/Version\/[\d\.]+.*Safari/);
|
||||||
cleanUp = item => {
|
const scrollElemnt = isSafari ? "body" : "html";
|
||||||
return item
|
|
||||||
|
const linkIcon = iconHTML("hashtag");
|
||||||
|
const closeIcon = iconHTML("times");
|
||||||
|
const dtocIcon = iconHTML("align-left");
|
||||||
|
const currUser = api.getCurrentUser();
|
||||||
|
const currUserTrustLevel = currUser ? currUser.trust_level : "";
|
||||||
|
const minimumTrustLevel = settings.minimum_trust_level_to_create_TOC;
|
||||||
|
|
||||||
|
const SCROLL_THROTTLE = 300;
|
||||||
|
const SMOOTH_SCROLL_SPEED = 300;
|
||||||
|
const TOC_ANIMATION_SPEED = 300;
|
||||||
|
|
||||||
|
const cleanUp = item => {
|
||||||
|
const cleanItem = item
|
||||||
.trim()
|
.trim()
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.replace(/[^\w]+/g, "-")
|
.replace(/[^\w]+/g, "-")
|
||||||
.replace(/^\-/, "")
|
.replace(/^\-/, "")
|
||||||
.replace(/\-$/, "")
|
.replace(/\-$/, "")
|
||||||
.replace(/\-\-+/g, "-");
|
.replace(/\-\-+/g, "-");
|
||||||
},
|
|
||||||
createAnchors = id => {
|
return cleanItem;
|
||||||
let link = $("<a/>", {
|
};
|
||||||
|
|
||||||
|
const createAnchors = id => {
|
||||||
|
const link = $("<a/>", {
|
||||||
href: `#${id}`,
|
href: `#${id}`,
|
||||||
class: "d-toc-anchor-link",
|
class: "d-toc-anchor-link",
|
||||||
html: linkIcon
|
html: linkIcon
|
||||||
});
|
});
|
||||||
|
|
||||||
return link;
|
return link;
|
||||||
},
|
|
||||||
dtocMobile = () => {
|
|
||||||
$(".d-toc").toggleClass("d-toc-mobile");
|
|
||||||
};
|
};
|
||||||
I18n.translations[I18n.currentLocale()].js.composer.contains_dtoc = I18n.t(
|
|
||||||
themePrefix("topic_will_contain_a_table_of_contents")
|
const setUpTocItem = function(item) {
|
||||||
);
|
const unique = item.attr("id");
|
||||||
(function(dToc) {
|
const text = item.text();
|
||||||
dToc($, window);
|
|
||||||
$.widget("discourse.dToc", {
|
item.attr({ "data-d-toc": unique });
|
||||||
_create: function() {
|
|
||||||
this.generateDtoc();
|
const tocItem = $("<li/>", {
|
||||||
this.setEventHandlers();
|
class: "d-toc-item",
|
||||||
},
|
"data-d-toc": unique
|
||||||
generateDtoc: function() {
|
});
|
||||||
let self = this,
|
|
||||||
primaryHeadings = $(this.options.cooked).find(
|
tocItem.append(
|
||||||
|
$("<a/>", {
|
||||||
|
text: text
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return tocItem;
|
||||||
|
};
|
||||||
|
|
||||||
|
(function(dToc) {
|
||||||
|
dToc($, window);
|
||||||
|
$.widget("discourse.dToc", {
|
||||||
|
_create: function() {
|
||||||
|
this.generateDtoc();
|
||||||
|
this.setEventHandlers();
|
||||||
|
},
|
||||||
|
|
||||||
|
generateDtoc: function() {
|
||||||
|
const self = this;
|
||||||
|
|
||||||
|
const primaryHeadings = $(this.options.cooked).find(
|
||||||
this.options.selectors.substr(0, this.options.selectors.indexOf(","))
|
this.options.selectors.substr(0, this.options.selectors.indexOf(","))
|
||||||
);
|
);
|
||||||
self.element.addClass("d-toc");
|
|
||||||
primaryHeadings.each(function(index) {
|
|
||||||
let selectors = self.options.selectors,
|
|
||||||
ul = $("<ul/>", {
|
|
||||||
id: `d-toc-top-heading-${index}`,
|
|
||||||
class: "d-toc-heading"
|
|
||||||
});
|
|
||||||
ul.append(self.nestElements($(this), index));
|
|
||||||
self.element.append(ul);
|
|
||||||
$(this)
|
|
||||||
.nextUntil(this.nodeName.toLowerCase())
|
|
||||||
.each(function() {
|
|
||||||
headings = $(this).find(selectors).length
|
|
||||||
? $(this).find(selectors)
|
|
||||||
: $(this).filter(selectors);
|
|
||||||
headings.each(function() {
|
|
||||||
self.subheadings.call(this, self, ul);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
nestElements: function(self, index) {
|
|
||||||
let unique = self.attr("id"),
|
|
||||||
text = self.text(),
|
|
||||||
arr,
|
|
||||||
item;
|
|
||||||
arr = $.grep(items, i => i === text);
|
|
||||||
items.push(text);
|
|
||||||
item = $("<li/>", {
|
|
||||||
class: "d-toc-item",
|
|
||||||
"data-d-toc": unique
|
|
||||||
});
|
|
||||||
item.append(
|
|
||||||
$("<a/>", {
|
|
||||||
text: text
|
|
||||||
})
|
|
||||||
);
|
|
||||||
self.attr({ "data-d-toc": unique });
|
|
||||||
return item;
|
|
||||||
},
|
|
||||||
subheadings: function(self, ul) {
|
|
||||||
let index = $(this).index(self.options.selectors),
|
|
||||||
previousHeader = $(self.options.selectors).eq(index - 1),
|
|
||||||
currentTagName = +$(this)
|
|
||||||
.prop("tagName")
|
|
||||||
.charAt(1),
|
|
||||||
previousTagName = +previousHeader.prop("tagName").charAt(1);
|
|
||||||
if (currentTagName < previousTagName) {
|
|
||||||
self.element
|
|
||||||
.find(`.d-toc-subheading[data-tag="${currentTagName}"]`)
|
|
||||||
.last()
|
|
||||||
.append(self.nestElements($(this), index));
|
|
||||||
} else if (currentTagName === previousTagName) {
|
|
||||||
ul.find(".d-toc-item")
|
|
||||||
.last()
|
|
||||||
.after(self.nestElements($(this), index));
|
|
||||||
} else {
|
|
||||||
ul.find(".d-toc-item")
|
|
||||||
.last()
|
|
||||||
.after(
|
|
||||||
$("<ul/>", {
|
|
||||||
class: "d-toc-subheading",
|
|
||||||
"data-tag": currentTagName
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.next(".d-toc-subheading")
|
|
||||||
.append(self.nestElements($(this), index));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
setEventHandlers: function() {
|
|
||||||
let self = this,
|
|
||||||
scrollThrottle = mobileView ? 10 : 50,
|
|
||||||
dTocTimeout;
|
|
||||||
this.element.on("click.d-toc", "li", function() {
|
|
||||||
self.element.find(".d-toc-active").removeClass("d-toc-active");
|
|
||||||
$(this).addClass("d-toc-active");
|
|
||||||
if (!mobileView) {
|
|
||||||
let elem = $(`li[data-d-toc="${$(this).attr("data-d-toc")}"]`);
|
|
||||||
self.triggerShow(elem);
|
|
||||||
}
|
|
||||||
self.scrollTo($(this));
|
|
||||||
if (mobileView) {
|
|
||||||
$("#d-toc").removeClass("d-toc-mobile");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
$(window).on("scroll.d-toc", function() {
|
|
||||||
if (!dTocTimeout) {
|
|
||||||
dTocTimeout = setTimeout(function() {
|
|
||||||
$("html")
|
|
||||||
.promise()
|
|
||||||
.done(function() {
|
|
||||||
let winScrollTop = $(window).scrollTop(),
|
|
||||||
elem;
|
|
||||||
let closestAnchorDistance = null,
|
|
||||||
closestAnchorIdx = null,
|
|
||||||
anchors = $(self.options.cooked).find("[data-d-toc]"),
|
|
||||||
anchorText;
|
|
||||||
anchors.each(function(idx) {
|
|
||||||
let distance = Math.abs($(this).offset().top - winScrollTop);
|
|
||||||
if (
|
|
||||||
closestAnchorDistance == null ||
|
|
||||||
distance < closestAnchorDistance
|
|
||||||
) {
|
|
||||||
closestAnchorDistance = distance;
|
|
||||||
closestAnchorIdx = idx;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
anchorText = $(anchors[closestAnchorIdx]).attr("data-d-toc");
|
|
||||||
elem = $(`li[data-d-toc="${anchorText}"]`);
|
|
||||||
if (elem.length) {
|
|
||||||
self.element
|
|
||||||
.find(".d-toc-active")
|
|
||||||
.removeClass("d-toc-active");
|
|
||||||
elem.addClass("d-toc-active");
|
|
||||||
}
|
|
||||||
self.triggerShow(elem, true);
|
|
||||||
});
|
|
||||||
dTocTimeout = null;
|
|
||||||
}, scrollThrottle);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
show: function(elem) {
|
|
||||||
if (!elem.is(":visible")) {
|
|
||||||
if (
|
|
||||||
!elem.find(".d-toc-subheading").length &&
|
|
||||||
!elem.parent().is(".d-toc-heading") &&
|
|
||||||
!elem.parent().is(":visible")
|
|
||||||
) {
|
|
||||||
elem = elem.parents(".d-toc-subheading").add(elem);
|
|
||||||
} else if (
|
|
||||||
!elem.children(".d-toc-subheading").length &&
|
|
||||||
!elem.parent().is(".d-toc-heading")
|
|
||||||
) {
|
|
||||||
elem = elem.closest(".d-toc-subheading");
|
|
||||||
}
|
|
||||||
elem.slideDown("medium");
|
|
||||||
}
|
|
||||||
if (!mobileView) {
|
|
||||||
if (elem.parent().is(".d-toc-heading")) {
|
|
||||||
$(".d-toc-subheading")
|
|
||||||
.not(elem)
|
|
||||||
.slideUp("medium");
|
|
||||||
$(".d-toc-subheading")
|
|
||||||
.not(
|
|
||||||
elem
|
|
||||||
.closest(".d-toc-heading")
|
|
||||||
.find(".d-toc-subheading")
|
|
||||||
.not(elem.siblings())
|
|
||||||
)
|
|
||||||
.slideUp("medium");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
triggerShow: function(elem, scroll) {
|
|
||||||
if (
|
|
||||||
elem.parent().is(".d-toc-heading") ||
|
|
||||||
elem.next().is(".d-toc-subheading")
|
|
||||||
) {
|
|
||||||
this.show(elem.next(".d-toc-subheading"), scroll);
|
|
||||||
} else if (elem.parent().is(".d-toc-subheading")) {
|
|
||||||
this.show(elem.parent(), scroll);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
setOptions: function() {
|
|
||||||
$.Widget.prototype._setOptions.apply(this, arguments);
|
|
||||||
},
|
|
||||||
scrollTo: function(elem) {
|
|
||||||
let currentDiv = $(`[data-d-toc="${elem.attr("data-d-toc")}"]`);
|
|
||||||
$("html").animate(
|
|
||||||
{
|
|
||||||
scrollTop: currentDiv.offset().top + "px"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
duration: 750
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})(() => {});
|
|
||||||
$.fn.dtoc = $elem => {
|
|
||||||
run.scheduleOnce("sync", () => {
|
|
||||||
if ($elem.hasClass("d-editor-preview")) return;
|
|
||||||
if (!$elem.parents("article#post_1").length) return;
|
|
||||||
const dToc = $elem.find(`[data-theme-toc="true"]`);
|
|
||||||
|
|
||||||
if (!dToc.length) return this;
|
self.element.addClass("d-toc");
|
||||||
const body = $elem;
|
|
||||||
body.find("div, aside, blockquote, article").each(function() {
|
primaryHeadings.each(function(index) {
|
||||||
$(this)
|
const selectors = self.options.selectors,
|
||||||
.children("h1,h2,h3,h4,h5,h6")
|
ul = $("<ul/>", {
|
||||||
.each(function() {
|
id: `d-toc-top-heading-${index}`,
|
||||||
$(this).replaceWith(
|
class: "d-toc-heading"
|
||||||
`<div class="d-toc-ignore">${$(this).html()}</div>`
|
});
|
||||||
);
|
|
||||||
|
ul.append(setUpTocItem($(this)));
|
||||||
|
self.element.append(ul);
|
||||||
|
|
||||||
|
$(this)
|
||||||
|
.nextUntil(this.nodeName.toLowerCase())
|
||||||
|
.each(function() {
|
||||||
|
const headings = $(this).find(selectors).length
|
||||||
|
? $(this).find(selectors)
|
||||||
|
: $(this).filter(selectors);
|
||||||
|
|
||||||
|
headings.each(function() {
|
||||||
|
self.nestTocItem.call(this, self, ul);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
nestTocItem: function(self, ul) {
|
||||||
|
const index = $(this).index(self.options.selectors);
|
||||||
|
const previousHeader = $(self.options.selectors).eq(index - 1);
|
||||||
|
const previousTagName = previousHeader.prop("tagName").charAt(1);
|
||||||
|
|
||||||
|
const currentTagName = $(this)
|
||||||
|
.prop("tagName")
|
||||||
|
.charAt(1);
|
||||||
|
|
||||||
|
if (currentTagName < previousTagName) {
|
||||||
|
self.element
|
||||||
|
.find(`.d-toc-subheading[data-tag="${currentTagName}"]`)
|
||||||
|
.last()
|
||||||
|
.append(setUpTocItem($(this)));
|
||||||
|
} else if (currentTagName === previousTagName) {
|
||||||
|
ul.find(".d-toc-item")
|
||||||
|
.last()
|
||||||
|
.after(setUpTocItem($(this)));
|
||||||
|
} else {
|
||||||
|
ul.find(".d-toc-item")
|
||||||
|
.last()
|
||||||
|
.after(
|
||||||
|
$("<ul/>", {
|
||||||
|
class: "d-toc-subheading",
|
||||||
|
"data-tag": currentTagName
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.next(".d-toc-subheading")
|
||||||
|
.append(setUpTocItem($(this)));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setEventHandlers: function() {
|
||||||
|
const self = this;
|
||||||
|
|
||||||
|
const dtocMobile = () => {
|
||||||
|
$(".d-toc").toggleClass("d-toc-mobile");
|
||||||
|
};
|
||||||
|
|
||||||
|
this.element.on("click.d-toc", "li", function() {
|
||||||
|
self.element.find(".d-toc-active").removeClass("d-toc-active");
|
||||||
|
$(this).addClass("d-toc-active");
|
||||||
|
if (mobileView) {
|
||||||
|
dtocMobile();
|
||||||
|
} else {
|
||||||
|
let elem = $(`li[data-d-toc="${$(this).attr("data-d-toc")}"]`);
|
||||||
|
self.triggerShowHide(elem);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.scrollTo($(this));
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#main").on(
|
||||||
|
"click.toggleDtoc",
|
||||||
|
".d-toc-toggle, .d-toc-close",
|
||||||
|
dtocMobile
|
||||||
|
);
|
||||||
|
|
||||||
|
const onScroll = () => {
|
||||||
|
run.throttle(this, self.highlightItemsOnScroll, self, SCROLL_THROTTLE);
|
||||||
|
};
|
||||||
|
|
||||||
|
$(window).on("scroll.d-toc", onScroll);
|
||||||
|
},
|
||||||
|
|
||||||
|
highlightItemsOnScroll: self => {
|
||||||
|
$(scrollElemnt)
|
||||||
|
.promise()
|
||||||
|
.done(function() {
|
||||||
|
const winScrollTop = $(window).scrollTop();
|
||||||
|
const anchors = $(self.options.cooked).find("[data-d-toc]");
|
||||||
|
|
||||||
|
let closestAnchorDistance = null;
|
||||||
|
let closestAnchorIdx = null;
|
||||||
|
|
||||||
|
anchors.each(function(idx) {
|
||||||
|
const distance = Math.abs(
|
||||||
|
$(this).offset().top - minimumOffset() - winScrollTop
|
||||||
|
);
|
||||||
|
if (
|
||||||
|
closestAnchorDistance == null ||
|
||||||
|
distance < closestAnchorDistance
|
||||||
|
) {
|
||||||
|
closestAnchorDistance = distance;
|
||||||
|
closestAnchorIdx = idx;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const anchorText = $(anchors[closestAnchorIdx]).attr("data-d-toc");
|
||||||
|
const elem = $(`li[data-d-toc="${anchorText}"]`);
|
||||||
|
|
||||||
|
if (elem.length) {
|
||||||
|
self.element.find(".d-toc-active").removeClass("d-toc-active");
|
||||||
|
elem.addClass("d-toc-active");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mobileView) {
|
||||||
|
self.triggerShowHide(elem);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
triggerShowHide: function(elem) {
|
||||||
|
if (
|
||||||
|
elem.parent().is(".d-toc-heading") ||
|
||||||
|
elem.next().is(".d-toc-subheading")
|
||||||
|
) {
|
||||||
|
this.showHide(elem.next(".d-toc-subheading"));
|
||||||
|
} else if (elem.parent().is(".d-toc-subheading")) {
|
||||||
|
this.showHide(elem.parent());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
showHide: function(elem) {
|
||||||
|
return elem.is(":visible") ? this.hide(elem) : this.show(elem);
|
||||||
|
},
|
||||||
|
|
||||||
|
hide: function(elem) {
|
||||||
|
const target = $(".d-toc-subheading")
|
||||||
|
.not(elem)
|
||||||
|
.not(elem.parent(".d-toc-subheading:has(.d-toc-active)"));
|
||||||
|
|
||||||
|
return isSafari
|
||||||
|
? target.fadeOut(TOC_ANIMATION_SPEED)
|
||||||
|
: target.slideUp(TOC_ANIMATION_SPEED);
|
||||||
|
},
|
||||||
|
|
||||||
|
show: function(elem) {
|
||||||
|
return isSafari
|
||||||
|
? elem.fadeIn(TOC_ANIMATION_SPEED)
|
||||||
|
: elem.slideDown(TOC_ANIMATION_SPEED);
|
||||||
|
},
|
||||||
|
|
||||||
|
scrollTo: function(elem) {
|
||||||
|
const currentDiv = $(`[data-d-toc="${elem.attr("data-d-toc")}"]`);
|
||||||
|
|
||||||
|
$(scrollElemnt).animate(
|
||||||
|
{
|
||||||
|
scrollTop: `${currentDiv.offset().top - minimumOffset()}`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
duration: SMOOTH_SCROLL_SPEED
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
setOptions: () => {
|
||||||
|
$.Widget.prototype._setOptions.apply(this, arguments);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
let dTocHeadingSelectors = "h1,h2,h3,h4,h5,h6";
|
})(() => {});
|
||||||
if (!body.has(">h1").length) {
|
|
||||||
dTocHeadingSelectors = "h2,h3,h4,h5,h6";
|
api.decorateCooked($elem => {
|
||||||
if (!body.has(">h2").length) {
|
run.scheduleOnce("afterRender", () => {
|
||||||
dTocHeadingSelectors = "h3,h4,h5,h6";
|
if ($elem.hasClass("d-editor-preview")) return;
|
||||||
if (!body.has(">h3").length) {
|
if (!$elem.parents("article#post_1").length) return;
|
||||||
dTocHeadingSelectors = "h4,h5,h6";
|
|
||||||
if (!body.has(">h4").length) {
|
const dToc = $elem.find(`[data-theme-toc="true"]`);
|
||||||
dTocHeadingSelectors = "h5,h6";
|
|
||||||
if (!body.has(">h5").length) {
|
if (!dToc.length) return this;
|
||||||
dTocHeadingSelectors = "h6";
|
const body = $elem;
|
||||||
|
body.find("div, aside, blockquote, article, details").each(function() {
|
||||||
|
$(this)
|
||||||
|
.children("h1,h2,h3,h4,h5,h6")
|
||||||
|
.each(function() {
|
||||||
|
$(this).replaceWith(
|
||||||
|
`<div class="d-toc-ignore">${$(this).html()}</div>`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
let dTocHeadingSelectors = "h1,h2,h3,h4,h5,h6";
|
||||||
|
if (!body.has(">h1").length) {
|
||||||
|
dTocHeadingSelectors = "h2,h3,h4,h5,h6";
|
||||||
|
if (!body.has(">h2").length) {
|
||||||
|
dTocHeadingSelectors = "h3,h4,h5,h6";
|
||||||
|
if (!body.has(">h3").length) {
|
||||||
|
dTocHeadingSelectors = "h4,h5,h6";
|
||||||
|
if (!body.has(">h4").length) {
|
||||||
|
dTocHeadingSelectors = "h5,h6";
|
||||||
|
if (!body.has(">h5").length) {
|
||||||
|
dTocHeadingSelectors = "h6";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
body.find(dTocHeadingSelectors).each(function() {
|
body.find(dTocHeadingSelectors).each(function() {
|
||||||
if ($(this).hasClass("d-toc-ignore")) return;
|
if ($(this).hasClass("d-toc-ignore")) return;
|
||||||
let heading = $(this),
|
const heading = $(this);
|
||||||
id = heading.attr("id") || "";
|
|
||||||
if (id.length) {
|
let id = heading.attr("id") || "";
|
||||||
heading.attr("id", id);
|
|
||||||
heading.append(createAnchors(id));
|
if (id.length) {
|
||||||
return;
|
heading
|
||||||
}
|
.attr("id", id)
|
||||||
id = cleanUp(heading.text());
|
.append(createAnchors(id))
|
||||||
heading.attr("id", id);
|
.addClass("d-toc-post-heading");
|
||||||
heading.append(createAnchors(id));
|
} else {
|
||||||
|
id = cleanUp(heading.text());
|
||||||
|
heading
|
||||||
|
.attr("id", id)
|
||||||
|
.append(createAnchors(id))
|
||||||
|
.addClass("d-toc-post-heading");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
body
|
||||||
|
.addClass("d-toc-cooked")
|
||||||
|
.prepend(
|
||||||
|
`<span class="d-toc-toggle">
|
||||||
|
${dtocIcon} ${I18n.t(themePrefix("table_of_contents"))}
|
||||||
|
</span>`
|
||||||
|
)
|
||||||
|
.parents(".regular")
|
||||||
|
.addClass("d-toc-regular")
|
||||||
|
.parents("article")
|
||||||
|
.addClass("d-toc-article")
|
||||||
|
.append(
|
||||||
|
`<ul id="d-toc">
|
||||||
|
<div class="d-toc-close-wrapper">
|
||||||
|
<div class="d-toc-close">
|
||||||
|
${closeIcon}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ul>`
|
||||||
|
)
|
||||||
|
.parents(".topic-post")
|
||||||
|
.addClass("d-toc-post")
|
||||||
|
.parents("body")
|
||||||
|
.addClass("d-toc-timeline");
|
||||||
|
|
||||||
|
$("#d-toc").dToc({
|
||||||
|
cooked: body,
|
||||||
|
selectors: dTocHeadingSelectors
|
||||||
|
});
|
||||||
});
|
});
|
||||||
body
|
|
||||||
.addClass("d-toc-cooked")
|
|
||||||
.prepend(
|
|
||||||
`<span class="d-toc-toggle">
|
|
||||||
${dtocIcon} ${I18n.t(themePrefix("table_of_contents"))}
|
|
||||||
</span>`
|
|
||||||
)
|
|
||||||
.parents(".regular")
|
|
||||||
.addClass("d-toc-regular")
|
|
||||||
.parents("article")
|
|
||||||
.addClass("d-toc-article")
|
|
||||||
.append(
|
|
||||||
`<ul id="d-toc">
|
|
||||||
<div class="d-toc-close-wrapper">
|
|
||||||
<div class="d-toc-close">
|
|
||||||
${closeIcon}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ul>`
|
|
||||||
)
|
|
||||||
.parents(".topic-post")
|
|
||||||
.addClass("d-toc-post");
|
|
||||||
$("#d-toc").dToc({
|
|
||||||
cooked: body,
|
|
||||||
selectors: dTocHeadingSelectors
|
|
||||||
});
|
|
||||||
$(".d-toc-post").on(
|
|
||||||
"click.toggleDtoc",
|
|
||||||
".d-toc-toggle, .d-toc-close",
|
|
||||||
dtocMobile
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
$(".selected-posts")
|
|
||||||
.next()
|
api.cleanupStream(() => {
|
||||||
.addClass("d-toc-timeline");
|
|
||||||
};
|
|
||||||
api.decorateCooked($elem => $elem.dtoc($elem));
|
|
||||||
api.modifyClass("component:topic-timeline", {
|
|
||||||
willDestroyElement() {
|
|
||||||
$(window).off("scroll.d-toc");
|
$(window).off("scroll.d-toc");
|
||||||
$(".d-toc-post").off("click.toggleDtoc");
|
$("#main").off("click.toggleDtoc");
|
||||||
|
$(".d-toc-timeline").removeClass("d-toc-timeline d-toc-timeline-visible");
|
||||||
|
});
|
||||||
|
|
||||||
|
api.onAppEvent("topic:current-post-changed", post => {
|
||||||
|
if (!$(".d-toc-timeline").length) return;
|
||||||
|
run.scheduleOnce("afterRender", () => {
|
||||||
|
if (post.post.post_number <= 2) {
|
||||||
|
$("body").removeClass("d-toc-timeline-visible");
|
||||||
|
$(".d-toc-toggle").fadeIn(100);
|
||||||
|
} else {
|
||||||
|
$("body").addClass("d-toc-timeline-visible");
|
||||||
|
$(".d-toc-toggle").fadeOut(100);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (currUserTrustLevel >= minimumTrustLevel) {
|
||||||
|
I18n.translations[I18n.currentLocale()].js.composer.contains_dtoc = "";
|
||||||
|
|
||||||
|
api.addToolbarPopupMenuOptionsCallback(() => {
|
||||||
|
const composerController = api.container.lookup("controller:composer");
|
||||||
|
return {
|
||||||
|
action: "insertDtoc",
|
||||||
|
icon: "align-left",
|
||||||
|
label: themePrefix("insert_table_of_contents"),
|
||||||
|
condition: composerController.get("model.canCategorize")
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
api.modifyClass("controller:composer", {
|
||||||
|
actions: {
|
||||||
|
insertDtoc() {
|
||||||
|
this.get("toolbarEvent").applySurround(
|
||||||
|
`<div data-theme-toc="true">`,
|
||||||
|
`</div>`,
|
||||||
|
"contains_dtoc"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
|
||||||
api.onAppEvent("topic:current-post-changed", post => {
|
|
||||||
if (!$(".d-toc-regular").length) return;
|
|
||||||
if (post.post.post_number === 1) {
|
|
||||||
$(".d-toc-timeline").hide();
|
|
||||||
$(".d-toc-toggle").fadeIn();
|
|
||||||
} else {
|
|
||||||
$(".d-toc-timeline").fadeIn();
|
|
||||||
$(".d-toc-toggle").fadeOut();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
api.addToolbarPopupMenuOptionsCallback(() => {
|
|
||||||
composerController = api.container.lookup("controller:composer");
|
|
||||||
return {
|
|
||||||
action: "insertDtoc",
|
|
||||||
icon: "align-left",
|
|
||||||
label: themePrefix("insert_table_of_contents"),
|
|
||||||
condition: composerController.get("model.canCategorize")
|
|
||||||
};
|
|
||||||
});
|
|
||||||
api.modifyClass("controller:composer", {
|
|
||||||
actions: {
|
|
||||||
insertDtoc() {
|
|
||||||
this.get("toolbarEvent").applySurround(
|
|
||||||
`<div data-theme-toc="true">`,
|
|
||||||
`</div>`,
|
|
||||||
"contains_dtoc"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
en:
|
en:
|
||||||
table_of_contents: "table of contents"
|
table_of_contents: "table of contents"
|
||||||
insert_table_of_contents: "Insert table of contents"
|
insert_table_of_contents: "Insert table of contents"
|
||||||
topic_will_contain_a_table_of_contents: "This topic will contain a table of contents"
|
|
||||||
|
|
15
settings.yml
15
settings.yml
|
@ -1,2 +1,17 @@
|
||||||
|
minimum_trust_level_to_create_TOC:
|
||||||
|
default: 1
|
||||||
|
type: enum
|
||||||
|
choices:
|
||||||
|
- 0
|
||||||
|
- 1
|
||||||
|
- 2
|
||||||
|
- 3
|
||||||
|
- 4
|
||||||
|
description:
|
||||||
|
en: "The minimum trust level a user must have in order to see the TOC button in the composer"
|
||||||
|
composer_toc_text:
|
||||||
|
default: "This topic will contain a table of contents"
|
||||||
table_of_contents_icon:
|
table_of_contents_icon:
|
||||||
default: "align-left"
|
default: "align-left"
|
||||||
|
anchor_icon:
|
||||||
|
default: "hashtag"
|
||||||
|
|
Loading…
Add table
Reference in a new issue