FANDOM


//
/**
 * @fileOverview 19 June 2012 -- This file defines the Railgun client, a user script which
 * provides a set of additional features for the Wikia Rail. It is designed for personal usage
 * on all .wikia.com wikis.
 * @author Jeff Bradford (User:Mathmagician)
 * @version 2.1.0
 */
 
/**
 * @namespace A wrapper object for all Railgun client methods and modules. In this context,
 * a "module" refers to a single section tag of the Wikia Rail, as well as all associated
 * states and behaviors of that entity. All modules are themselves objects in the Railgun
 * namespace. The Railgun.Storage namespace is not technically considered a module.
 */
Railgun = {
    /**
     * Specifies the debug mode of the Railgun client. In debug mode, the Railgun stylesheet
     * and Railgun server pages are non-cached, and some extra data is printed to the console
     * for testing purposes.
     * @field
     * @type Boolean
     * @default false
     */
    isDebug : false,
 
    /**
     * The version of the Railgun client source code.
     * @field
     * @type String
     */
    version : "2.1.0"
};
 
/**
 * @namespace A wrapper object defining Railgun's storage namespace, which is responsible for
 * handling communications with the Railgun server.
 */
Railgun.Storage = {
    /**
     * The domain where the Railgun server is located.
     * @constant
     * @type String
     * @default "http://railgunscript.wikia.com"
     */
    domain : "http://railgunscript.wikia.com",
 
    /**
     * A reference to the iframe HTML element which is appended to the body of the main page
     * when initialized in <code>Railgun.Storage.init()</code>. The client communicates with
     * the server by issuing postMessage requests to the iframe's contentWindow property.
     * @type Object
     * @see Railgun.Storage.init()
     */
    iframe : null,
 
    /**
     * A copy of all the properties of the RailgunServer object. This property is
     * initialized in <code>Railgun.initProcessServerResponse()</code>.
     * @type Object
     * @see Railgun.initProcessServerResponse()
     */
    serverState : null,
 
    /**
     * A copy of all the contents of localStorage provided by the server upon pageload. This
     * property is initialized in <code>Railgun.initProcessServerResponse()</code>.
     * @type Object
     * @see Railgun.initProcessServerResponse()
     */
    storageState : null,
 
    /**
     * The main line of communication from client to server. This method accepts a
     * request object as a parameter, converts it to JSON and sends it to the
     * server via iframe.contentWindow.postMessage(). Sample usage:<br>
     * <code>Railgun.Storage.sendRequest({<br>
     *     id : "showSiderail()",<br>
     *     instruction : "setItem",<br>
     *     key : "siderailHidden",<br>
     *     value : false
     * });</code>
     * @param {Object} request a request object to be sent to the server
     */
    sendRequest : function (request) {
        // console.log for debugging
        if (Railgun.isDebug) {
            console.log("Client issuing " + request.instruction + " request to the server:");
            console.log(request);
        }
        // issue request to the server
        this.iframe.contentWindow.postMessage(JSON.stringify(request), this.domain);
    },
 
    /**
     * The initialization method for the Storage object. This method loads the server into an
     * iframe and appends it to the body of the document.
     */
    init : function () {
        // generate HTML for the iframe
        var iframe = '<iframe id="railgun-server"'
                + 'style="display: none; position: absolute; left: -1px; width: 1px;"'
                + 'src="' + this.domain;
        if (Railgun.isDebug) {
            iframe += '/wiki/RailgunServerDebug?action=render"></iframe>';
        } else {
            iframe += '/wiki/RailgunServer?action=render"></iframe>';
        }
 
        // insert iframe into the page
        $(document.body).append(iframe);
 
        // set Storage.iframe property
        this.iframe = document.getElementById('railgun-server');
    }
}; // End definition of Railgun.Storage object
 
/**
 * @namespace A wrapper object containing functionality for the a feature of Railgun that
 * expands the content area. The <code>siderailHidden</code> property of this object
 * has a counterpart in localStorage.
 */
Railgun.ShowHideSiderail = {
    /**
     * Stores the current state of the siderail: <code>true</code> if hidden,
     * <code>false</code> otherwise.
     * @type Boolean
     * @default false
     */
    siderailHidden : false,
 
    /**
     * Constant which contains the HTML code for the left arrow in the user interface. The
     * arrow appears in the toolbar and has the railgun-siderail-left-arrow CSS class. When
     * clicked, this arrow should reveal the siderail.
     * @type String
     * @constant
     */
    leftArrow : '<img class="railgun-siderail-left-arrow" '
            + 'src="http://images3.wikia.nocookie.net/mathmagician/images/a/ab/ArrowLeft.png" '
            + 'onclick="Railgun.ShowHideSiderail.showSiderail();">',
 
    /**
     * Constant which contains the HTML code for the right arrow in the user interface. The
     * arrow appears in the toolbar and has the railgun-siderail-left-arrow CSS class. When
     * clicked, this arrow should collapse the siderail.
     * @type String
     * @constant
     */
    rightArrow : '<img class="railgun-siderail-right-arrow" '
            + 'src="http://images4.wikia.nocookie.net/mathmagician/images/9/93/ArrowRight.png" '
            + 'onclick="Railgun.ShowHideSiderail.hideSiderail();">',
 
    /**
     * Shows the siderail upon clicking the show siderail (leftArrow) button and issues
     * a request to store siderailHidden == false in storage.
     * @see Railgun.ShowHideSiderail.leftArrow
     */
    showSiderail : function () {
        // save state: siderailHidden == false in storage
        Railgun.Storage.sendRequest({
            id : "showSiderail()",
            instruction : "setItem",
            key : "siderailHidden",
            value : false
        });
 
        // apply CSS (see stylesheet) to show the siderail
        $('.WikiaRail, .WikiaMainContent, .catlinks').removeClass('railgun-no-siderail');
 
        // reveal the "hide siderail" arrow
        $('.railgun-siderail-left-arrow').css('display', 'none');
        $('.railgun-siderail-right-arrow').css('display', 'block');
    },
 
    /**
     * Shows the siderail upon clicking the hide siderail (rightArrow) button and issues
     * a request to store siderailHidden == true in storage.
     * @see Railgun.ShowHideSiderail.rightArrow
     */
    hideSiderail : function () {
        // save state: siderailHidden == true in storage
        Railgun.Storage.sendRequest({
            id : "hideSiderail()",
            instruction : "setItem",
            key : "siderailHidden",
            value : true
        });
 
        // apply CSS (see stylesheet) to hide the siderail
        $('.WikiaRail, .WikiaMainContent, .catlinks').addClass('railgun-no-siderail');
 
        // reveal the "show siderail" arrow
        $('.railgun-siderail-left-arrow').css('display', 'block');
        $('.railgun-siderail-right-arrow').css('display', 'none');
    },
 
    /**
     * Initialization method for the ShowHideSiderail module. Initializes the <code>
     * siderailHidden</code> property and modifies the DOM accordingly.
     */
    init : function () {
        // initialize siderailHidden property
        this.siderailHidden = Railgun.Storage.storageState.siderailHidden ? true : false;
 
        // add arrows to show and hide the siderail into the document
        $('.WikiaFooter .toolbar').prepend(this.leftArrow + this.rightArrow);
 
        if (true === this.siderailHidden) {
            // apply CSS (see stylesheet) to hide the siderail
            $('.WikiaRail, .WikiaMainContent, .catlinks').addClass('railgun-no-siderail');
 
            // reveal the "show siderail" arrow
            $('.railgun-siderail-left-arrow').css('display', 'block');
            $('.railgun-siderail-right-arrow').css('display', 'none');
        } else {
            // reveal the "hide siderail" arrow
            $('.railgun-siderail-left-arrow').css('display', 'none');
            $('.railgun-siderail-right-arrow').css('display', 'block');
        }
    }
};
 
/**
 * @namespace A wrapper object around all properties and methods that make up the Friend's
 * List module. The <code>friends</code> property of this object has a counterpart in
 * localStorage.
 */
Railgun.FriendsList = {
    /**
     * An array of objects representing all of the user's friends, initialized in
     * <code>Railgun.initProcessServerResponse()</code> and kept sorted by username in
     * the <code>addFriend()</code> method. Each element of the array is an object of
     * the form: <code>{ homewiki : "http://mathmagician.wikia.com", html : 'tr ... /tr',
     * username : "Mathmagician" }</code>
     * @type Array
     * @see Railgun.initProcessServerResponse
     */
    friends : null,
 
    /**
     * HTML code for the message which is displayed to the user when the Friend's List is empty.
     * This message has the railgun-no-friends-message CSS class, and is initialized in
     * the Railgun.FriendsList.init() method.
     * @type String
     * @constant
     * @see Railgun.FriendsList.init()
     */
    noFriendsMessage : '<p class="railgun-no-friends-message">'
            + 'It looks like you haven\'t made any friends yet. '
            + 'To add someone to your friend\'s list, simply click the icon '
            + 'on their profile masthead. '
            + 'There\'s no hurry, Fluttershy will keep you company in the meantime.'
            + '<img '
            + 'src="http://images3.wikia.nocookie.net/mathmagician/images/5/5a/Fus_Ro_Yay%21.gif">'
            + '</p>',
 
    /**
     * HTML code for the add friend button that appears on profile mastheads.
     * This message has the railgun-add-friend-image CSS class, and is initialized in
     * the Railgun.FriendsList.init() method.
     * @type String
     * @constant
     * @see Railgun.FriendsList.init()
     */
    addFriendProfileImage : '<img '
            + 'src="http://images1.wikia.nocookie.net/__cb20120418234625/'
            + 'mathmagician/images/0/00/Bomb_Omb_30px.png" '
            + 'onclick="Railgun.FriendsList.addFriend();" '
            + 'class="railgun-add-friend-image" '
            + 'title="This user is not your friend. Click to explodify!">',
 
    /**
     * HTML code for the remove friend button that appears on profile mastheads.
     * This message has the railgun-remove-friend-image CSS class, and is initialized in
     * the Railgun.FriendsList.init() method.
     * @type String
     * @constant
     * @see Railgun.FriendsList.init()
     */
    removeFriendProfileImage : '<img '
            + 'src="http://images1.wikia.nocookie.net/__cb20120418234525/'
            + 'mathmagician/images/7/79/Star_30px.png" '
            + 'onclick="Railgun.FriendsList.removeFriend();" '
            + 'class="railgun-remove-friend-image" '
            + 'title="Friendship is witchcraft. Click to steal your friend\'s star!">',
 
    /**
     * Shows friend's links on mouseover and hides them again on mouseout.
     * @param {Object} element a table row element containing HTML for a single friend
     * @param {Boolean} show <code>true</code> to show links, <code>false</code> to hide them
     */
    toggleLinks : function (element, show) {
        $(element)
        .find('.railgun-friend-td2-span2')
        .css('visibility', show ? 'visible' : 'hidden');
    },
 
    /**
     * Creates a table row element for a single friend to be added to the Friend's List.
     * @param {String} username name of a Wikia user, such as "Mathmagician"
     * @returns {String} a table row HTML element with the railgun-friend-tr CSS class.
     */
    createFriendHTML : function (username) {
        var encoded = encodeURIComponent(username.replace(/ /, '_'));
 
        var imagelink = '<a href="/wiki/User:' + encoded + '">';
        var image = '<img src="' + $('.masthead-avatar .avatar').attr('src')
        + '" class="railgun-friend-avatar">';
        var tr = '<tr class="railgun-friend-tr" data-user="' + username + '" '
                + 'onmouseover="Railgun.FriendsList.toggleLinks(this, true);" '
                + 'onmouseout="Railgun.FriendsList.toggleLinks(this, false);">';
        var td1 = '<td class="railgun-friend-td1">';
        var td2 = '<td class="railgun-friend-td2">';
        var td2span1 = '<span class="railgun-friend-td2-span1">';
        var profile = '<a href="/wiki/User:' + encoded + '">' + username + '</a>';
        var td2span2 = '<span class="railgun-friend-td2-span2">';
        var talk = '[<a href="/wiki/User_talk:' + encoded + '">talk</a>]';
        var blog = ' [<a href="/wiki/User_blog:' + encoded + '">blog</a>]';
        var contrib = ' [<a href="/wiki/Special:Contributions/' + encoded + '">contrib</a>]';
        var count = ' [<a href="/wiki/Special:Editcount/' + encoded + '">count</a>]';
        var log = ' [<a href="/wiki/Special:Log/' + encoded + '">log</a>]';
        var sub = ' [<a href="/wiki/Special:PrefixIndex/User:' + encoded + '">subpages</a>]';
 
        var html = tr + td1 + imagelink + image + '</a></td>' + td2 + td2span1 + profile
                + '</span><br />' + td2span2 + talk + blog + contrib + count + log + sub
                + '</span></td></tr>';
 
        return html;
    },
 
    /**
     * Adds a single friend to the Friend's List upon clicking the add friend button and
     * issues a request to save the updated <code>friends</code> array to localStorage.
     * This method sorts <code>Railgun.FriendsList.friends</code> alphabetically by
     * username and does not permit duplicates.
     * @see Railgun.FriendsList.addFriendProfileImage
     * @see Railgun.FriendsList.friends
     */
    addFriend : function () {
        var username = $('.masthead-info h1').text();
        var html = this.createFriendHTML(username);
        var index = -1;
 
        // remove this friend (don't allow duplicates)
        for (var i = 0; i < this.friends.length; i++) {
            if (this.friends[i].username === username) {
                index = i;
                break;
            }
        }
        if (-1 !== index) {
            this.friends.splice(index, 1);
        }
 
        // add friend object to friends array
        this.friends.push({
            username : username,
            html : html,
            homewiki : wgServer
        });
 
        // sort friends array
        this.friends.sort(function (a, b) {
            if (a.username < b.username)
                return -1;
            else if (a.username == b.username)
                return 0;
            else
                return 1;
        });
 
        // save friends in localStorage
        Railgun.Storage.sendRequest({
            id : "addFriend()",
            instruction : "setItem",
            key : "friends",
            value : this.friends
        });
 
        // remove no friends message
        $('.railgun-no-friends-message').remove();
 
        // add new friend into the document
        var friendsListed = $('.railgun-friend-tr');
        if (0 === friendsListed.length) {
            $('.railgun-friend-table').append(html);
        } else if (username < friendsListed.first().attr('data-user')) {
            $('.railgun-friend-table').prepend(html);
        } else if (username > friendsListed.last().attr('data-user')) {
            $('.railgun-friend-table').append(html);
        } else {
            for (var i = 0; i < friendsListed.length; i++) {
                if (username < $(friendsListed[i]).attr('data-user')) {
                    $(friendsListed[i]).before(html);
                    break;
                }
            }
        }
 
        // update profile masthead image
        $('.railgun-add-friend-image').replaceWith(this.removeFriendProfileImage);
    },
 
    /**
     * Removes a single friend from the Friend's List upon clicking the remove
     * friend button and issues a request to save the updated <code>friends</code>
     * array in localStorage.
     * @see Railgun.FriendsList.friends
     */
    removeFriend : function () {
        var username = $('.masthead-info h1').text();
        var friendsListed = $('.railgun-friend-tr');
        var index = -1;
 
        // remove friend
        for (var i = 0; i < this.friends.length; i++) {
            if (this.friends[i].username === username) {
                index = i;
                break;
            }
        }
        if (-1 !== index) {
            this.friends.splice(index, 1);
        }
 
        // save friends in localStorage
        Railgun.Storage.sendRequest({
            id : "removeFriend()",
            instruction : "setItem",
            key : "friends",
            value : this.friends
        });
 
        // remove friend from the document
        switch (friendsListed.length)
        {
        case 0 :
            break;
        case 1 :
            friendsListed.replaceWith(this.noFriendsMessage);
            break;
        default :
            for (var i = 0; i < friendsListed.length; i++) {
                if (username === $(friendsListed[i]).attr('data-user')) {
                    $(friendsListed[i]).remove();
                    break;
                }
            }
        }
 
        // update profile masthead image
        $('.railgun-remove-friend-image').replaceWith(this.addFriendProfileImage);
    },
 
    /**
     * Initialization method for the Friend's List module. This method initializes the
     * <code>friends</code> property and creates the visual representation of the module
     * on the siderail.
     */
    init : function () {
        var section = '<section class="railgun-friend-module module">';
        var header = '<h1>Friend\'s List</h1>';
        var table = '<table class="railgun-friend-table" cellspacing="3">';
 
        // initialize friends property
        this.friends = Railgun.Storage.storageState.friends || [];
 
        // set friendsHTML
        var friendsUsername = [];
        var friendsHTML = '';
        if (0 === this.friends.length) {
            friendsHTML = this.noFriendsMessage;
        } else {
            for (var i = 0; i < this.friends.length; i++) {
                friendsUsername[i] = this.friends[i].username;
                friendsHTML += this.friends[i].html;
            }
        }
 
        // add Friend's List module to the siderail
        var html = section + header + table + friendsHTML + '</table></section>';
        if ($('#WikiaSearch').parent().attr('id') === "WikiaRail") {
            $('#WikiaSearch').after(html);
        } else {
            $('#WikiaRail').prepend(html);
        }
 
        // add friend / defriend images to profile masthead
        var mastheadInfo = $('.masthead-info h1');
        if (0 !== mastheadInfo.length) {
            // append image to the profile masthead
            if (-1 === friendsUsername.indexOf(mastheadInfo.text())) {
                $('.masthead-info hgroup').append(Railgun.FriendsList.addFriendProfileImage);
            } else {
                $('.masthead-info hgroup').append(Railgun.FriendsList.removeFriendProfileImage);    
            }
        }
    }
}; // End definition of Railgun.FriendsList object
 
/**
 * Loads all modules after all localStorage data has been copied to the Storage namespace.
 * This method un-registers <code>Railgun.initProcessServerResponse()</code> as a message event
 * listener for the window.
 * @see Railgun.initProcessServerResponse()
 */
Railgun.initLoadModules = function () {
    // un-register the Railgun.initProcessServerResponse() "message" event listener for this window
    if (window.removeEventListener) {
        window.removeEventListener("message", Railgun.initProcessServerResponse, false);
    } else if (window.detachEvent) {
        window.detachEvent("message", Railgun.initProcessServerResponse);
    }
 
    // load all modules
    Railgun.ShowHideSiderail.init();
    Railgun.FriendsList.init();
};
 
/**
 * Processes the initialization response from the Railgun server after the retrieve request
 * has been made by Railgun.initRequestServer(). This method initializes the
 * <code>Railgun.Storage.serverState</code> and <code>Railgun.Storage.storageState</code>
 * properties in the Storage namespace and then calls <code>Railgun.initLoadModules()</code>
 * @param {MessageEvent} event a message event representing the server's response
 * @see Railgun.Storage.serverState
 * @see Railgun.Storage.storageState
 * @see Railgun.initLoadModules()
 */
Railgun.initProcessServerResponse = function (event) {
    if (event.origin !== Railgun.Storage.domain)
        return; // only process requests from Railgun.Storage.domain
 
    // initialize data in Storage namespace to values received from the server
    var data = JSON.parse(event.data);
    Railgun.Storage.serverState = data.serverState;
    Railgun.Storage.storageState = data.storageState;
 
    // load the rest of the modules
    Railgun.initLoadModules();
};
 
/**
 * The client's iframe onload event listener that sends a retrieve request to the server to
 * get all data from localStorage. Execution passes to <code>
 * Railgun.initProcessServerResponse()</code> after the server has handled the request.
 * @see Railgun.initProcessServerResponse()
 */
Railgun.initRequestServer = function () {
    // register Railgun.initProcessServerResponse() as a "message" event listener for this window
    if (window.addEventListener) {
        window.addEventListener("message", Railgun.initProcessServerResponse, false);
    } else if (window.attachEvent) {
        window.attachEvent("message", Railgun.initProcessServerResponse);
    }
 
    // send initialization info to the server
    Railgun.Storage.sendRequest({
        instruction : "retrieve",
        isDebug : Railgun.isDebug
    });
};
 
/**
 * Railgun's main initialization method which is called upon pageload. This method is
 * responsible for preventing Railgun from loading if the usage preconditions are not
 * met. It also attaches the Railgun stylesheet to the head of the document,
 * loads the iframe into the document by initializing the Storage object, and
 * passes execution to <code>Railgun.initRequestServer()</code> when the iframe
 * has loaded.
 * @see Railgun.initRequestServer()
 * @see Railgun.Storage.iframe
 */
Railgun.init = function () {
    if (null === wgUserName) {
        if (Railgun.isDebug)
            console.log("Railgun didn't initialize because: not logged in");
        return; // Don't run if user is not signed in
    } else if ("oasis" !== skin) {
        if (Railgun.isDebug)
            console.log("Railgun didn't initialize because: not using Oasis");
        return; // Don't run if user is not using Oasis skin
    } else if (0 === $('.WikiaRail').length) {
        if (Railgun.isDebug)
            console.log("Railgun didn't initialize because: siderail absent");
        return; // Don't run if there's no siderail
    } else if ("http://wikimarks.wikia.com" === wgServer) {
        if (Railgun.isDebug)
            console.log("Railgun didn't initialize because: domain is wikimarks");
        return; // Prevent interference with wikimarks
    }
 
    // attach CSS stylesheet
    $('#railgun-stylesheet').remove();
    var href = Railgun.Storage.domain;
    if (Railgun.isDebug) {
        href += "/wiki/MediaWiki:RailgunStylesheetDebug.css?action=raw&ctype=text/css"
                + "&maxage=0&smaxage=0";
    } else {
        href += "/wiki/MediaWiki:RailgunStylesheet.css?action=raw&ctype=text/css";
    }
    $(document.head).append('<link id="railgun-stylesheet" rel="stylesheet" type="text/css" ' 
            + 'href="' + href + '">');
 
    // initialize the Storage module first
    Railgun.Storage.init();
 
    // register Railgun.initRequestServer() as the iframe onload event listener
    if (Railgun.Storage.iframe.addEventListener) {
        Railgun.Storage.iframe.addEventListener("load", Railgun.initRequestServer, false);
    } else if (Railgun.Storage.iframe.attachEvent) {
        Railgun.Storage.iframe.attachEvent("onload", Railgun.initRequestServer);
    }
};
 
// $(document).ready()
// Issues the command to initialize Railgun on pageload
$(function () {
    Railgun.init();
});

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.