MediaWiki:FloatingToc/code.js
Jump to navigation
Jump to search
Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
- Opera: Press Ctrl-F5.
/**
* @name FloatingToc
* @author Pecoes, KhangND
* @desc Makes the Table of Contents floatable
* @doc https://dev.fandom.com/wiki/FloatingToc
* @files used:
* [[File:Ui-icons_ffffff_256x240.png]]
* [[File:Ui-icons_222222_256x240.png]]
*/
(function (mw, $, window) {
if (mw.config.get('skin') === 'fandomdesktop') return;
var root = $("#toc");
if (window.FloatingTocLoaded || !root.length) return;
window.FloatingTocLoaded = true;
function importArticle(page) {
const isScript = page.split(".").pop() === "js";
return $.ajax({
url: mw.config.get("wgLoadScript"),
data: {
mode: "articles",
only: isScript ? "scripts" : "styles",
articles: "u:dev:MediaWiki:" + page,
},
}).then(function (content) {
$("head").append($(isScript ? "<script>" : "<style>").html(content));
});
}
var css = importArticle("FloatingToc.css");
var colors = (window.dev || {}).colors
? $.Deferred().resolve()
: importArticle("Colors/code.js");
var i18n = ((window.dev || {}).i18n || {}).loadMessages
? $.Deferred().resolve()
: importArticle("I18n-js/code.js");
var jQueryUI = mw.loader.using([
"jquery.ui.dialog",
"jquery.ui.draggable",
"jquery.ui.resizable",
"jquery.effects.slide",
]);
$.when(i18n, css, colors, jQueryUI)
.then(function () {
return window.dev.i18n.loadMessages("FloatingToc");
})
.then(function (_i18n) {
var colors = window.dev.colors;
var pageBright = colors.parse(colors.wikia.page).isBright();
var menuBright = colors.parse(colors.wikia.menu).isBright();
var iconLight = 'url("https://images.wikia.nocookie.net/dev/images/c/c2/Ui-icons_ffffff_256x240.png")';
var iconDark = 'url("https://images.wikia.nocookie.net/dev/images/a/aa/Ui-icons_222222_256x240.png")';
var styles =
".toc-dialog{\
border: 2px solid $border;\
}\
.toc-dialog .ui-dialog-titlebar {\
background: $menu;\
border: 1px solid $menu;\
color: $contrast;\
}\
.toc-dialog.ui-widget-content .ui-icon {\
background-image: $menu-icons;\
}\
.toc-dialog.ui-widget-content a {\
color: $link;\
}\
.toc-dialog,\
.toc-dialog .toc {\
background-color: $page;\
color: $text;\
}\
#toc-open {\
border: 1px solid $page-border-gray;\
background-image: $page-icons;\
}\
.toc-btn, .toc-btn:hover {\
background-image: $menu-icons;\
}\
.toc-btn:hover {\
background-color: $gradient;\
}";
mw.util.addCSS(
colors.replace(styles, {
"page-border-gray": colors.wikia.border,
"menu-icons": menuBright ? iconDark : iconLight,
"page-icons": pageBright ? iconDark : iconLight,
})
);
var win = $(window),
page = $("#WikiaPage"),
rail = $("#WikiaRail"),
navHeight = $("#globalNavigation").height(),
barHeight = $("#WikiaBarWrapper").height(),
speed = 250,
i18n = _i18n.msg,
options = $.extend(
{
auto: false,
enableKey: true,
},
window.FloatingToc
);
// root ToC components
var rootToc = {
top: root.offset().top,
width: root.outerWidth(),
height: root.outerHeight(),
};
// creates a clone of the root ToC
// and transforms it to jQuery UI Dialog
var toc = {
resizing: false,
padWrap: 0,
wrapper: $(),
clone: $("<nav>", {
class: "toc show",
id: "tocDialog",
title: i18n("contents").plain(),
css: { display: "none" },
appendTo: page,
}),
state: function () {
var w = rootToc.width;
var h = Math.min(
rootToc.height,
win.height() - navHeight - barHeight
);
return {
position: [(win.width() - w) / 2, navHeight],
width: w,
height: h,
maxWidth: win.height() * (w / h),
maxHeight: win.height() - navHeight - barHeight,
};
},
toggle: function (action) {
var links = $(".toc-link");
if (links.css("display") === "none") links.show(speed);
else links.hide(speed);
if (root.css("display") === "none")
root.slideDown(speed, toc.keepPos(action));
else root.slideUp(speed, toc.keepPos(action));
},
keepPos: function (action) {
var winPos = win.scrollTop();
if (action === "open" && winPos > rootToc.top + rootToc.height) {
win.scrollTop(winPos - rootToc.height);
}
if (action === "close") {
if (!options.enableKey) {
win.scrollTop(rootToc.top - navHeight);
return;
}
if (winPos > rootToc.top) win.scrollTop(winPos + rootToc.height);
}
},
fixPos: function () {
if (toc.wrapper.css("position") === "fixed") return;
toc.wrapper.css({
top: toc.wrapper.offset().top - win.scrollTop(),
left: toc.wrapper.offset().left - win.scrollLeft(),
position: "fixed",
});
},
reset: function () {
toc.clone.dialog("option", toc.state());
},
dock: function () {
toc.clone.dialog("option", {
width:
rail.width() + parseInt(page.css("padding-right")) - toc.padWrap,
position: [rail.offset().left, navHeight],
});
},
back: function () {
win.scrollTop(0);
},
open: function () {
toc.clone.dialog("open");
},
close: function () {
toc.clone.dialog("close");
},
button: function (action) {
return $("<button>", {
class: "toc-btn",
id: "toc-" + action,
title: i18n(action).plain(),
click: toc[action],
});
},
onOpen: function () {
toc.reset();
toc.fixPos();
toc.toggle("open");
},
onClose: function () {
toc.toggle("close");
},
onCreate: function () {
// sets properties
toc.wrapper = $(this).parent();
toc.padWrap =
(parseInt(toc.wrapper.css("border-width")) +
parseInt(toc.wrapper.css("padding"))) *
2;
// modifies components
toc.toggle();
toc.wrapper.find(".ui-dialog-titlebar-close").remove();
$("<div>", {
id: "toc-btns",
appendTo: toc.wrapper.find(".ui-dialog-titlebar"),
append: [
toc.button("back"),
toc.button("dock"),
toc.button("reset"),
toc.button("close"),
],
});
toc.wrapper.draggable({
scroll: false,
containment: "window",
});
},
init: function () {
var selector = root.find("ol")[0] ? "ol" : "ul";
toc.clone.append(root.find(selector + ":first")[0].outerHTML);
toc.clone.dialog(
$.extend(
{
dialogClass: "toc-dialog",
show: { effect: "slideDown", duration: speed },
hide: { effect: "slideUp", duration: speed },
create: toc.onCreate,
open: toc.onOpen,
close: toc.onClose,
resizeStart: function () {
toc.resizing = true;
},
resizeStop: function () {
toc.resizing = false;
toc.fixPos();
},
},
toc.state()
)
);
win.on("resize", function () {
if (!toc.resizing && toc.clone.dialog("isOpen")) toc.reset();
});
// overrides click
$(this).on("click", function () {
if (toc.clone.dialog("isOpen")) toc.close();
else toc.open();
});
},
};
// adds ToC links to h2, h3
$("#mw-content-text")
.find("h2,h3")
.find(".mw-headline")
.after(
$("<a>", {
class: "toc-link",
href: "",
title: i18n("toc").plain(),
click: function () {
win.scrollTop(rootToc.top - navHeight);
return false;
},
})
);
var tocButton = $("<button>", {
id: "toc-open",
title: i18n("open").plain(),
appendTo: root.find("div:first"),
}).one("click", toc.init);
if (options.auto) tocButton.trigger("click");
if (options.enableKey) {
$(document).on("keyup", function (e) {
var elem = document.activeElement;
if (
$("body").hasClass("ve") ||
$("html").hasClass("ve-active") ||
elem.tagName === "INPUT" ||
elem.tagName === "TEXTAREA" ||
elem.hasAttribute("contenteditable")
)
return;
if (e.key === "t") tocButton.trigger("click");
});
}
});
})(mw, $, window);