Fandom

Wikia Developers Wiki

Wikimarks/code

< Wikimarks

889pages on
this wiki
Add New Page
Talk0 Share
//
/**
 * Wikimarks v2
 * Replaces the original version following it being broken by verbatim being disabled
 *
 * @author Pecoes <http://c.wikia.com/wiki/User:Pecoes>
 * @author Cqm    <http://c.wikia.com/wiki/User: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();
    }
 
 
    /**
     * Prepare the parsed HTML and attach to the DOM
     */
    function prepareHtml(html) {
        var $wikimarks = $('<li>')
                .attr('id', 'wikimarks')
                // make sure to select the tab after loading
                .addClass('nav-item marked wikimarks')
                .append(
                    $('<a>')
                        .attr(
                            'href',
                            'http://dev.wikia.com' +
                                mw.util.wikiGetlink('User:' + conf.wgUserName + '/Wikimarks')
                        )
                        .addClass('wikimarks-logo')
                        .append(
                            'Wikim',
                            $('<span>')
                                .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 = $('<img>')
                    .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 = $('<img>')
                    .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(/<!--[\s\S]*?-->/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+/);
        parsed = [];
 
        var invalidLink = false;
 
        data.forEach(function (elem, index, array) {
            // 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 + '[{{fullurl:' + conf.wgPageName + '|' + p2.slice(1) + '}} ' + 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(/(\/wiki\/|\/(?:index|api|wikia)\.php)/) === 0) {
                    p2 = conf.wgServer + p2;
                }
 
                // else just return it unchanged
                return p1 + ' [' + p2 + ' ' + p3 + ']';
            });
 
            // 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 + '[{{fullurl:' + conf.wgPageName + '|' + p3.slice(1) + '}} ' + 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(/(\/wiki\/|\/(?:index|api|wikia)\.php)/) === 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 + ' [[' + p3 + '|' + p2 + ']]'; 
            });
 
            // remove css comment
            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 || {}));

Ad blocker interference detected!


Wikia is a free-to-use site that makes money from advertising. We have a modified experience for viewers using ad blockers

Wikia is not accessible if you’ve made further modifications. Remove the custom ad blocker rule(s) and the page will load as expected.