User:Winuks/Wikimarks

/** * Wikimarks v2  * Replaces the original version following it being broken by verbatim being disabled * * @author Pecoes  * @author Cqm    * * Changes from the original: * - No longer uses verbatim, thus should be more stable. * - No longer allows javascript: URLs (and thus custom functions) * - Supports wikitext as well as (most of) the old syntax */ /*global importStylesheetURI */ /*jshint bitwise:true, camelcase:true, curly:true, eqeqeq:true, es3:false, forin:true, immed:true, indent:4, latedef:true, newcap:true, noarg:true, noempty:true, nonew:true, plusplus:true, quotmark:single, undef:true, unused:true, strict:true, trailing:true, browser:true, devel:false, jquery:true, onevar:true */ ;(function ($, mw, dev) {    'use strict';     var conf = mw.config.get([ 'skin', 'stylepath', 'wgPageName', 'wgServer', 'wgUserName' ]),        testUser = false;     /**       * Calculate certain styles dynamically depending on the environment      */     function calcCss {         var chevCss,             h;         // styles for chevron colour         chevCss = '#wikimarks .chevron-right { border-left-color: ' +             $('#wikimarks .subnav-3a').css('color') +             '; }';         mw.util.addCSS(chevCss);         // set position of level 3 menus         // set border of level 4 menus (and remove weird padding)         $('#wikimarks').find('.subnav-3 > li').one('mouseover', function  { // we have to wait until one of the menus are open to calculate the // positioning, so wait until that happens, and then prevent it            // happening more than once // else debugging css becomes a nightmare if (h) { return; }            var $this = $(this), css, border, i,                index, top; h = $(this).height; border = $this.parent('ul').css('border'); css = '#wikimarks .subnav-4 { border: ' + border + '; padding: 0; }\n'; for (i = 0; i < 30; i += 1) { index = i + 1; top = Math.round(h * i); css += '#wikimarks .subnav-3 li:nth-child(' + index +                    ') ul { top: ' + top  + 'px !important; }\n'; }            mw.util.addCSS(css); });    }     /**      * Insert Wikimarks into the DOM and attach the relevant events      */     function addHtml($wikimarks) {         var $level2 = $wikimarks.find('.subnav-2-item'),             $wikinavLi = $('#WikiHeader').find('.nav-item');         $wikinavLi             // force wikimarks on top (easier than tracking which is currently open)             .removeClass('marked')             // and insert into the DOM             .first.replaceWith($wikimarks);         $wikimarks             .add($wikinavLi)             .off('mouseover')             .on('mouseover', function  { var $this = $(this), $siblings = $this.siblings; $siblings.removeClass('marked'); $this.addClass('marked'); $siblings .find('.marked2').removeClass('marked2') .find('.subnav-3').hide; });        $level2             .on('mouseover', function  { $('.marked2') .removeClass('marked2') .find('.subnav-3').hide; $(this).has('ul').addClass('marked2'); })            .on('mouseout', function  { $(this) .removeClass('marked2') .find('.subnav-3').hide; });        calcCss;         // everything is now done         // so fire an event so people can interact/extend it further         mw.hook('wikimarks.loaded').fire($wikimarks);     }     /**      * Prepare the parsed HTML and attach to the DOM      */     function prepareHtml(html) {         var $wikimarks = $('')                 .attr('id', 'wikimarks')                 // make sure to select the tab after loading                 .addClass('nav-item marked wikimarks')                 .append( $('') .attr(                            'href',                             'http://dev.wikia.com' +                                 mw.util.wikiGetlink('User:' + conf.wgUserName + '/Wikimarks')                         ) .addClass('wikimarks-logo') .append(                            'Wikim',                             $(' ')                                 .addClass('wikimarks-star'),                             'rks'                         ), html );        // add classes to elements         $wikimarks             .children('ul').addClass('subnav-2 accent')                 .children('li').addClass('subnav-2-item')                     .children('a').addClass('subnav-2a')                     .siblings('ul').addClass('subnav-3 subnav')                         .children('li').addClass('subnav-3-item')                             .children('a').addClass('subnav-3a')                             .siblings('ul').addClass('subnav-4')                                 .children('li').addClass('subnav-4-item')                                     .children('a').addClass('subnav-4a');         // add chevrons         $wikimarks.find('.subnav-3').siblings('.subnav-2a').each(function  { var $this = $(this), $chevron = $(' ') .addClass('chevron') .attr('src', 'data:image/gif;base64,R0lGODlhAQABAIABAAAAAP///yH5BAEAAAEALAAAAAABAAEAQAICTAEAOw%3D%3D'); $this.append($chevron); });        $wikimarks.find('.subnav-4').siblings('.subnav-3a').each(function  { var $this = $(this), $chevron = $(' ') .addClass('chevron-right') .attr('src', 'data:image/gif;base64,R0lGODlhAQABAIABAAAAAP///yH5BAEAAAEALAAAAAABAAEAQAICTAEAOw%3D%3D'); $this.append($chevron); });        // remove href from text converted to links         $wikimarks.find('a[href="/wiki/"]')             .removeAttr('href')             .css('cursor', 'pointer');         $wikimarks.find('a')             // titles don't add annything to the links             .removeAttr('title')             // remove external link class for ease of reading the source html             .removeClass('extiw');         // kill chat opening in a new window         $wikimarks.find('.WikiaChatLink').removeClass('WikiaChatLink');         if (testUser) {             return;         }         addHtml($wikimarks);     }     /**      * Pass the preprocess wikimarks to action=parse to be converted into wikitext      */     function parseWikimarks(data) {         var params = {             action: 'parse',             text: data         };         (new mw.Api)             .post(params)             .done(function (data) { var text = data.parse.text['*']; // remove preprocessor comment // should be able to hide it in api config // but that's broken in mw1.19 text = text.replace(//g, '').trim; if (testUser) { mw.log(text); }                prepareHtml(text); });    }     /**      * Preprocesses a wikimarks page to make it compatible with the wikitext parser      */     function preprocessData(data) {         data = data.trim.split(/\n+/);         var invalidLink = false,             parsed = [],             // handles /wiki/, index.php, api.php, wikia.php and /d (discussions)             relativeUrlRe = /\/(wiki\/|(?:index|api|wikia)\.php|d)/;         data.forEach(function (elem) { // ignore comments if (elem.indexOf('//') === 0 || elem.indexOf('#') === 0) { return; }            // handle external links elem = elem.replace(/^(\*+)\s*\[([^\s]+)\s+(.+?)\]\s*$/, function (_, p1, p2, p3) {                // handle query strings                 if (p2.indexOf('?') === 0) {                     return p1 + '[ ' + p3 + ']';                 }                 // allow appending to existing query strings as well                 if (p2.indexOf('&') === 0) {                     return p1 + '[' + location.href + p2 + ' ' + p3 + ']';                 }                 // handle relative URLs                 if (p2.search(relativeUrlRe) === 0) {                     p2 = conf.wgServer + p2;                 }                 // else just return it unchanged                 return p1 + ' [' + p2 + ' ' + p3 + ']';             }); // don't touch raw html // assumes that all html will begin with a tag, e.g. <span... if (!/^\*+\s*</.test(elem)) { // parse old style links to wikitext for backwards compatibility elem = elem.replace(/^(\*+)\s*([^\[]+?)\s*=\s*(.+?)\s*$/, function (_, p1, p2, p3) {                    // handle absolute URLs                     // 'http://' or 'https://' or '//'                     if (p3.search(/(?:https?:)?\/\//) === 0) {                         return p1 + ' [' + p3 + ' ' + p2 + ']';                     }                     // handle query strings                     if (p3.indexOf('?') === 0) {                         return p1 + '[ ' + p2 + ']';                     }                     // allow appending to existing query strings as well                     if (p2.indexOf('&') === 0) {                         return p1 + '[' + location.href + p2 + ' ' + p3 + ']';                     }                     // attempt to fix instances of Foo?bar=baz                     // domain added below                     if (p3.indexOf('?') > -1) {                         p3 = '/wiki/' + p3; }                    // handle relative URLs if (p3.search(relativeUrlRe) === 0) { p3 = conf.wgServer + p3; return p1 + ' [' + p3 + ' ' + p2 + ']'; }                    // ## BREAKING CHANGE ## // don't allow 'javascript:' urls // ridiculously difficult to parse these in js without using `eval` if (p3.search(/(?:javascript:)?(?:url|win)\(/) === 0) {                        p3 = '#invalidLink';                         invalidLink = true;                     }                     // else we expect a normal wikilink                     return p1 + ' ' + p2 + '';                 }); }            // remove css comment // caused by loading wikimarks config through RL and pretending it's CSS if (elem.search(/^\/\*.+?\*\/$/) === 0) { elem = ''; }            // substitute in global variables // syntax: {$VAR} where VAR is a global variable // @todo limit to stuff available in mw.config? elem = elem.replace(/\{\$(.+?)\}/g, function (_, p1) {                // fix for properties of globals                 var parts = p1.split('.'),                     test = window,                     prop,                     i;                 for (i = 0; i < parts.length; i += 1) {                     prop = parts[i];                     // @todo how secure is this?                     if (test.hasOwnProperty(prop)) {                         test = test[prop];                     } else {                         break;                     }                 }                 if (['string', 'number'].indexOf(typeof test) > -1) {                     return test;                 }             }); // make simple text strings into a null link so it doesn't break the styling elem = elem.replace(/^(\*+)\s*([A-Za-z0-9\s]+)\s*$/, '$1 #|$2'); parsed.push(elem); });        data = parsed.join('\n').trim;         mw.log(data);         if (invalidLink) {             // @todo do something         }         return data;     }     /**      * Load the users wikimarks      */     function loadWikimarks(username) {         var load = mw.util.wikiScript('load'),             params = {                 mode: 'articles',                 only: 'styles',                 debug: 'true',                 // don't encode anything in the username here, $.get does it anyway                 // otherwise stuff gets encoded twice and no results are returned                 articles: 'u:dev:User:' + (username || conf.wgUserName).replace(/ /g, '_') + '/Wikimarks'             };         if (username) {             testUser = true;         }         mw.log('params', params);         $.get(load, params).done(function (data) { if (!data) { // just in case there was an error in the api request mw.log(this); }            data = preprocessData(data); parseWikimarks(data); });    }     /**      * Calculates whether the body and wikinav are light or dark      * Used to determine which colour star to use in the wikimarks logo      */     function calcBrightness {         var $header = $('#WikiHeader'),             bgColor,             $navBg,             menuColor,             $pageHeader;         function isBright (color) {             var m = color.match(/(?:([\da-fA-F]{2})([\da-fA-F]{2})([\da-fA-F]{2}))|(?:(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3}))/),                 rgb;             if (!m) {                 return false;             }             rgb = m[1] ?                 { r: parseInt(m[1], 16), g: parseInt(m[2], 16), b: parseInt(m[3], 16) } :                 { r: parseInt(m[4], 10), g: parseInt(m[5], 10), b: parseInt(m[6], 10) };             return (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000 >= 128;         }         bgColor = $('#WikiaPageBackground').css('background-color'); if (bgColor === 'transparent') { bgColor = $('#WikiaPage').css('background-color'); }        $header.addClass(isBright(bgColor) ? 'bg-bright' : 'bg-dark'); $navBg = $header.find('.navbackground'); menuColor = bgColor; if ($navBg.length) { menuColor = $navBg.css('background-color'); } else { $pageHeader = $('#WikiaPageHeader'); if ($pageHeader.length) { menuColor = $pageHeader.css('border-bottom-color'); }        }         $header.addClass(isBright(menuColor) ? 'menu-bright' : 'menu-dark'); }    /**      * Shows loading status until the wikimarks have loaded */    function showLoading { $('.WikiNav .nav-item').first .css({                backgroundImage: 'url("' + conf.stylepath + '/common/images/ajax.gif")',                 backgroundPosition: 'center center',                 backgroundRepeat: 'no-repeat'             }) .children('a') .css('color', 'transparent'); }    /**      * Load stylesheets */    function loadStyles { importStylesheetURI('http://fonts.googleapis.com/css?family=Chela+One&text=Wikima*rks'); importStylesheetURI('http://dev.wikia.com/wiki/Wikimarks/code.css?action=raw&ctype=text/css&maxage=0&smaxage=0'); }    /**      * Checks for the correct environment before allowing the script to continue */    function init { // monobook isn't supported if (['oasis', 'wikia'].indexOf(conf.skin) === -1) { return; }        // prevent anyone trying to load this for anons if (!conf.wgUserName) { return; }        if (!$('#WikiHeader .WikiNav').length) { console.log('Wikimarks: wikinav not found, aborting...'); return; }        loadStyles; showLoading; calcBrightness; loadWikimarks; }    mw.loader.using(['mediawiki.api', 'mediawiki.util'], function  {         $(init);     }); dev.loadWikimarks = loadWikimarks; }(this.jQuery, this.mediaWiki, this.dev = this.dev || {}));