Create header.html
This commit is contained in:
parent
81eaa9fb20
commit
bc12559fce
|
@ -0,0 +1,402 @@
|
|||
<script type="text/discourse-plugin" version="0.1">
|
||||
const mobileView = $("html").hasClass("mobile-view"),
|
||||
{ iconHTML } = require("discourse-common/lib/icon-library"),
|
||||
{ run } = Ember,
|
||||
linkIcon = iconHTML("link"),
|
||||
closeIcon = iconHTML("times"),
|
||||
dtocIcon = iconHTML("align-left"),
|
||||
items = [],
|
||||
cleanUp = item => {
|
||||
return item
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replace(/[^\w]+/g, "-")
|
||||
.replace(/^\-/, "")
|
||||
.replace(/\-$/, "")
|
||||
.replace(/\-\-+/g, "-");
|
||||
},
|
||||
createAnchors = id => {
|
||||
let link = $("<a/>", {
|
||||
href: `#${id}`,
|
||||
class: "d-toc-anchor-link",
|
||||
html: linkIcon
|
||||
});
|
||||
|
||||
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")
|
||||
);
|
||||
|
||||
(function(dToc) {
|
||||
dToc($, window);
|
||||
$.widget("discourse.dToc", {
|
||||
_create: function() {
|
||||
this.generateDtoc();
|
||||
this.setEventHandlers();
|
||||
},
|
||||
|
||||
generateDtoc: function() {
|
||||
let self = this,
|
||||
primaryHeadings = $(this.options.cooked).find(
|
||||
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;
|
||||
const dToc = $elem.find(`[data-theme-toc="true"]`);
|
||||
|
||||
if (!dToc.length) return this;
|
||||
|
||||
const body = $elem;
|
||||
|
||||
body.find("div, aside, blockquote, article").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() {
|
||||
if ($(this).hasClass("d-toc-ignore")) return;
|
||||
|
||||
let heading = $(this),
|
||||
id = heading.attr("id") || "";
|
||||
|
||||
if (id.length) {
|
||||
heading.attr("id", id);
|
||||
heading.append(createAnchors(id));
|
||||
return;
|
||||
}
|
||||
|
||||
id = cleanUp(heading.text());
|
||||
heading.attr("id", id);
|
||||
heading.append(createAnchors(id));
|
||||
});
|
||||
|
||||
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()
|
||||
.addClass("d-toc-timeline");
|
||||
};
|
||||
|
||||
api.decorateCooked($elem => $elem.dtoc($elem));
|
||||
|
||||
api.modifyClass("component:topic-timeline", {
|
||||
willDestroyElement() {
|
||||
$(window).off("scroll.d-toc");
|
||||
$(".d-toc-post").off("click.toggleDtoc");
|
||||
}
|
||||
});
|
||||
|
||||
api.onAppEvent("topic:current-post-changed", post => {
|
||||
if (!$(".d-toc-timeline").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>
|
Loading…
Reference in New Issue