MediaWiki:QDmodal.js

/* QDmodal - flexbox-based modal library */

/*jslint browser, this */ /*global jQuery, mediaWiki */

(function ($, mw) {   "use strict";

var closeText = { en: "Close" };

var lang = mw.config.get("wgUserLanguage"); closeText = closeText[lang] || closeText[lang.split("-")[0]] || closeText.en;

var $body = $(document.body); var $closeIcon = $(       ""          + " " + closeText + " "          + ""        + " "    );

//// QDmodal constructor ////

mw.libs.QDmodal = function (id) { var $close = $closeIcon.clone;

this.$container = $(" ").addClass("qdmodal-container"); this.$element = $(" ").addClass("qdmodal"); this.$title = $(" "); this.$content = $(" "); this.$footer = $(" ");

this.$container.append(           this.$element.append( $(" ").append(                   this.$title,                    $close                ), this.$content, this.$footer )       );

this.visible = false;

if (typeof id === "string") { this.$element.attr("id", id); }

$close.on("click", this.hide.bind(this));

this.$container.on("click", function (event) {           // only clicks on the container itself should close the modal            if (event.target === event.delegateTarget) {                this.hide;            }        }.bind(this)); };

mw.libs.QDmodal.prototype.hide = function { this.visible = false; this.$container.detach; $body.removeClass("qdmodal-no-scroll"); };

mw.libs.QDmodal.prototype.show = function (data) { if (!data) { return; }

this.data = data;

this.$content.toggleClass("mw-ajax-loader", Boolean(data.loading));

// only set title if one is given, else keep previous title if (data.title) { this.$title.text(data.title); }

this.$content.html(data.content || "");

// always clear the footer - hook can be used to add things here this.$footer.empty;

if (data.hook) { mw.hook(data.hook).fire(this); }

if (!this.visible) { $body .addClass("qdmodal-no-scroll") .append(this.$container); this.visible = true; }   };

//// QDmodal theming ////

function isDark(colour) { // use canvas to normalise a CSS colour string to #RRGGBB // https://stackoverflow.com/a/47355187 var ctx = document.createElement("canvas").getContext("2d"); ctx.fillStyle = colour; var rgb = ctx.fillStyle;

// get the lightness of a #RRGGBB colour // https://github.com/Wikia/app/blob/7df3d19/extensions/wikia/SASS/SassUtil.php#L239-L254 var r = parseInt(rgb.slice(1, 3), 16) / 255; var g = parseInt(rgb.slice(3, 5), 16) / 255; var b = parseInt(rgb.slice(5, 7), 16) / 255; var l = (Math.max(r, g, b) + Math.min(r, g, b)) / 2;

return l < 0.5; }

function initStyles { var theme = { background: "#fff", text: "#222",

langStart: document.dir === "rtl" ? "right" : "left", langEnd: document.dir === "rtl" ? "left" : "right", scrollbarWidth: window.innerWidth - document.body.offsetWidth };

// if available, use wiki's custom colours var wikiTheme = mw.config.get("wgSassParams"); if (wikiTheme) { theme.background = wikiTheme["color-page"]; theme.isDark = isDark(theme.background); theme.links = wikiTheme["color-links"]; theme.text = theme.isDark ? "#d5d4d4" : "#3a3a3a"; }

var styles = (           "body.qdmodal-no-scroll {"              // add a margin equivalent to the scrollbar width to              // prevent page content moving due to hidden overflow              + "margin-${langEnd}: ${scrollbarWidth}px;"              + "overflow-y: hidden;"            + "}"

+ ".qdmodal-container {" + "align-items: center;" + "background-color: hsla(0, 0%, 0%, 0.4);" + "bottom: 0;" + "display: flex;" + "font-size: 13px;" + "justify-content: center;" + "line-height: 21px;" + "left: 0;" + "position: fixed;" + "right: 0;" + "top: 0;" // to appear above #globalNavigation element on wikia + "z-index: 5000101;" + "}"

+ ".qdmodal-container ~ .modal-blackout.visible {" // fix wikia uifactory modal shown above qdmodal + "z-index: 5000102;" + "}"

+ ".modalWrapper ~ .qdmodal-container {" // fix qdmodal shown above wikia $.showModal + "z-index: 5001102;" + "}"

+ ".qdmodal {" + "background-color: ${background};" + "border-radius: 3px;" + "color: ${text};" + "display: flex;" + "flex-direction: column;" + "height: calc(100% - 50px);" + "overflow: hidden;" + "position: relative;" + "width: calc(100% - 50px);" + "}"

+ ".qdmodal > section {" + "flex-grow: 1;" + "overflow-y: auto;" + "padding: 20px;" + "}"

+ ".qdmodal > header," + ".qdmodal > footer {" + "background-color: hsla(0, 0%, 75%, 0.4);" + "display: flex;" + "}"

+ ".qdmodal > header {" + "min-height: 22px;" + "padding: 12px 20px;" + "}"

+ ".qdmodal > footer {" + "align-items: center;" + "flex-direction: row-reverse;" + "min-height: 28px;" + "padding: 9px;" + "}"

+ ".qdmodal > header > h3 {" + "color: inherit;" + "flex-grow: 1;" + "font-size: 1.4em;" + "font-weight: bold;" + "margin: 0;" + "overflow: hidden;" + "padding: 0;" + "text-overflow: ellipsis;" + "white-space: nowrap;" + "}"

+ ".qdmodal-close {" + "height: 28px;" + "margin: -12px -20px;" + "margin-${langStart}: 0;" + "min-width: 28px;" + "padding: 9px;" + "width: 28px;" + "}"

+ ".qdmodal-button {" + "border: 1px solid #999;" + "border-radius: 3px;" + "color: inherit;" + "display: block;" + "height: 1em;" + "line-height: 1;" + "margin-${langStart}: 12px;" + "padding: 6px 12px;" + "white-space: nowrap;" + "}"

+ ".qdmodal-close," + ".qdmodal-button {" + "cursor: pointer;" + "transition: background-color 0.2s;" + "}"

+ ".qdmodal-close:hover," + ".qdmodal-button:hover {" + "background-color: ${background};" + "border-color: #777;" + "text-decoration: none;" + "}"

/* fix "strong" styling in oasis skin */ + ".qdmodal strong {" + "font-weight: bold;" + "}"       );

if (theme.links) { styles += ".qdmodal > section a:not(.new) { color: ${links}; }"; }

if (theme.isDark) { styles += ".qdmodal .mw-ajax-loader { filter: invert(100%); }"; }

// replace placeholders with theme data mw.util.addCSS(styles.replace(/\$\{([^}]+)\}/g, function (ignore, p1) { return theme[p1]; }));   }

mw.loader.using("mediawiki.util").then(initStyles); }(jQuery, mediaWiki));