Advertisement
RenzXVI

Hide Side Rail

Aug 10th, 2016
175
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ==UserScript==
  2. // @name         Hide Rail
  3. // @namespace    http://tampermonkey.net/
  4. // @version      0.1
  5. // @description  Hide Side Rail
  6. // @author       http://dev.wikia.com/wiki/HideRail
  7. // @match        http://*/*
  8. // @grant        none
  9. // @include      wikia.com
  10. // @include      http://elderscrolls.wikia.com/
  11. // @include      http://terraria.wikia.com/
  12. // @include      http://kallenbearrp.wikia.com/
  13. // @include      http://metal.wikia.com/
  14. // @include      http://tl.elderscrolls.wikia.com/
  15. // ==/UserScript==
  16.  
  17. (function() {
  18.     'use strict';
  19.  
  20.     // Your code here...
  21.  
  22. //__NOWYSIWYG__ <syntaxhighlight lang="javascript">
  23. /*! Copyright (C) 2012 Lunarity
  24.  *
  25.  * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
  26.  * and associated documentation files (the "Software"), to deal in the Software without
  27.  * restriction, including without limitation the rights to use, copy, modify, merge, publish,
  28.  * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
  29.  * Software is furnished to do so, subject to the following conditions:
  30.  *
  31.  * The above copyright notice and this permission notice shall be included in all copies or
  32.  * substantial portions of the Software.
  33.  *
  34.  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
  35.  * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  36.  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
  37.  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  38.  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  39.  */
  40. /*jshint smarttabs:true laxbreak:true laxcomma:true curly:false
  41.   browser:true jquery:true es5:true
  42.  */
  43. /*global mediaWiki */
  44.  
  45. //
  46. // Facepalm fix for Wikia's code.
  47. // Observable is the base class for most of Wikia's complex JS code.
  48. //
  49. window.Observable.prototype.off = window.Observable.prototype.un;
  50.  
  51.  
  52. /**
  53.  * Hide Rail script.
  54.  *
  55.  * This script mainly exists to hide the Wikia Rail on Article pages to reduce the
  56.  * amount of scrolling needed to read a long article. It also provides a collection
  57.  * of related hiding features that can be optionally enabled as well.
  58.  */
  59. if (({oasis:1, wikia:1})[mediaWiki.config.get('skin')] === 1)
  60. (function(window, $, mw) {
  61.     "use strict";
  62.  
  63.     // JavaScript does not have block scoping, all variables exist even before the 'var'
  64.     var document, jStorage,
  65.         settings, i18n,
  66.         ArticleClass, EditPlugin, EditClass, OasisWideMode, StorageManager;
  67.  
  68.     // Language translations
  69.     i18n = {
  70.         // English
  71.         en: {
  72.             expand: 'Expand Content',
  73.             shrink: 'Shrink Content',
  74.             widemode: 'Enable Wide Mode'
  75.         },
  76.         // Catalan [[User:Espartannoble6]]
  77.         ca: {
  78.             expand: 'Expandeix',
  79.             shrink: 'Contrau',
  80.             widemode: 'Mode Ampli'
  81.         },
  82.         // German [[User:Arkondi]]
  83.         de: {
  84.             expand: 'Seitenleiste ausblenden',
  85.             shrink: 'Seitenleiste einblenden'
  86.         },
  87.         // Spanish [[User:JLSilver]]
  88.         es: {
  89.             expand: 'Expandir',
  90.             shrink: 'Contraer',
  91.             widemode: 'Modo Amplio'
  92.         },
  93.         // Hungarian [[User:TK-999]]
  94.         hu: {
  95.             expand: 'Tartalomtér növelése',
  96.             shrink: 'Tartalomtér csökkentése',
  97.             widemode: 'Széles mód'
  98.         },
  99.         // Dutch [[User:Jens Ingels]]
  100.         nl: {
  101.             expand: 'Inhoud Uitbreiden',
  102.             shrink: 'Inhoud Slinken'
  103.         },
  104.         // Polish [[User:Vuh]]
  105.         pl: {
  106.             expand: 'Rozwiń treść',
  107.             shrink: 'Zwiń treść',
  108.             widemode: 'Włącz szeroki tryb'
  109.         },
  110.         // Russian [[User:Unaited17]]
  111.         ru: {
  112.             expand: 'Развернуть',
  113.             shrink: 'Сжать',
  114.             widemode: 'Широкоэкранный режим'
  115.         }
  116.     };
  117.  
  118.     //
  119.     // Logger Singleton
  120.     //
  121.     var logger = { // IE8 only gains window.console when the console is open in the UI
  122.         _console: window.console || { log: $.noop, warn: $.noop, info: $.noop, error: $.noop },
  123.         _slice: [].slice,
  124.         _log: function(fn, args) {
  125.             args = this._slice.call(args);
  126.             args[0] = 'HIDERAIL' + (args[0] ? '(' + args[0] + '):' : ':');
  127.             return fn.apply(this._console, args);
  128.         },
  129.         log: function(/*component, ...*/) {
  130.             return this._log(this._console.log, arguments);
  131.         },
  132.         inf: function(/*component, ...*/) {
  133.             return this._log(this._console.info, arguments);
  134.         },
  135.         wrn: function(/*component, ...*/) {
  136.             return this._log(this._console.warn, arguments);
  137.         },
  138.         err: function(/*component, ...*/) {
  139.             return this._log(this._console.error, arguments);
  140.         }
  141.     };
  142.  
  143.     //
  144.     // Execution context
  145.     //
  146.     document = window.document;
  147.     jStorage = $.storage || { get: $.noop, set: $.noop, del: $.noop };
  148.  
  149.     function getBooleanStorageWithDefault(name, defaultValue) {
  150.         var value = jStorage.get(name);
  151.         return typeof(value) === 'boolean' ? value : !!defaultValue;
  152.     }
  153.  
  154.     //
  155.     // User configuration
  156.     // We use mw.user.options for our settings, this allows a clean migration to
  157.     // MW1.20's Options component for storing these on the server. Need a UI to
  158.     // perform setting actions though.
  159.     // TODO: Pecoes' preferences framework should be integrated. Performance
  160.     //  considerations dictate that wideMode+defaultWideModwe MUST come from global
  161.     //  conf so that we can calculate and apply the stylesheets immediately. Other
  162.     //  options will just delay the DOM ready processing which is not so bad.
  163.     //
  164.     settings = $.extend({
  165.         wideVisualEditor: true, // Enable edit page function?
  166.         wideMode: 1, // 0=disabled, others are HD values (1-3)
  167.         defaultWideMode: false, // Default OasisWideMode to on?
  168.         defaultExpand: false, // Default Rail to hidden?
  169.         // When expanding/widening, affect ALL tabs at once
  170.         syncTabs: false,
  171.         debug: false    // Internal Development flag.
  172.     }, (function(opts) {
  173.         var map = {
  174.             HideRailWideVisualEditor: 'wideVisualEditor',
  175.             HideRailWideMode: 'wideMode',
  176.             HideRailStartWide: 'defaultWideMode',
  177.             HideRailStartExpanded: 'defaultExpand',
  178.             HideRailSyncAcrossTabs: 'syncTabs',
  179.             HideRailDebug: 'debug'
  180.         }, types = {
  181.             HideRailWideMode: Number
  182.         }, out = {}, Bool = Boolean, val;
  183.         for (var key in map) {
  184.             if (map.hasOwnProperty(key)) {
  185.                 val = opts.get(key);
  186.                 if (val === null) continue; // Non-existent
  187.                 out[map[key]] = (types[key] || Bool)(val);
  188.             }
  189.         }
  190.         return out;
  191.     })(mw.user.options));
  192.     // Expose the internal state for inspection during debugging
  193.     if (settings.debug) {
  194.         window.HideRailJS = settings;
  195.     }
  196.  
  197.     //
  198.     // Collapse the resources so we only keep the ones we are actually going to use.
  199.     // The others will be garbage collected.
  200.     //
  201.     i18n = (function(i18n) {
  202.         // This is reverse order, last is most important, first is least
  203.         var langs = [{}, i18n.en],
  204.             langCode,
  205.             i = 1,
  206.             l = arguments.length;
  207.  
  208.         for ( ; i < l ; ++i) {
  209.             langCode = arguments[i] + '';
  210.             if (i18n.hasOwnProperty(langCode)) langs.push(i18n[langCode]);
  211.             else logger.wrn('i18n', 'No translation for language "' + langCode + '"');
  212.         }
  213.  
  214.         // Result: user, wiki, english
  215.         return $.extend.apply($, langs);
  216.     })(i18n, mw.config.get('wgContentLanguage'), mw.config.get('wgUserLanguage'));
  217.  
  218.     //
  219.     // Import the CSS style sheet that provides the default link appearance
  220.     //
  221.     mw.loader.load('http://dev.wikia.com/index.php?title=HideRail/code.css&action=raw&ctype=text/css&maxage=300&smaxage=300', 'text/css');
  222.  
  223.     //
  224.     // Code for managing a toolbar button on Wikia's Oasis Toolbar.
  225.     // This is more complex than it seems because we have to deal with hooking
  226.     // Wikia's code for when the toolbar is reconstructed via Customize and
  227.     // deal with the weirdness for creating a popup menu.
  228.     // id = HTML ID to stick on the LI
  229.     // overflowable = Mutex with popup. Can this be added to the overflow spill menu?
  230.     //
  231.     function ToolbarMenuItem(id, overflowable) {
  232.         this._$item = $(document.createElement('li'));
  233.         this.$link = $(document.createElement('a'))
  234.             .prop('href', '#')
  235.             .appendTo(this._$item)
  236.             ;
  237.         this._toolbarHook = null;
  238.  
  239.         // Attach ID if we have one
  240.         if (id) this._$item.prop('id', id);
  241.  
  242.         // "We were hit with a .remove()" event
  243.         // TODO: This is unnecessary now, remove
  244.         this.eventsDestroyed = $.Callbacks()
  245.             .add($.proxy(this._onEventsDestroyed, this))
  246.             .fire()
  247.             ;
  248.  
  249.         // We need the Wikia Toolbar script to do this properly, although it only really
  250.         // matters if we're using a popup
  251.         this._findWikiaToolbarApp();
  252.         if (overflowable) {
  253.             this._$item.addClass('overflow');
  254.         }
  255.  
  256.         return this;
  257.     }
  258.     $.extend(ToolbarMenuItem.prototype, {
  259.         _onEventsDestroyed: function() {
  260.             this.$link.click(false);
  261.         },
  262.         _findWikiaToolbarApp: function() {
  263.             var toolbarObj;
  264.             // Ugh. Wikia has an API but it's lame, it does not offer a
  265.             // addCustomButton function or anything. (I found this reading the source)
  266.             if (!(toolbarObj = window.WikiaFooterApp)|| !(toolbarObj = toolbarObj.tcToolbar)|| !toolbarObj.handleOverflowMenu|| !toolbarObj.el|| !toolbarObj.el.jquery|| !toolbarObj.el.length
  267.                ) {
  268.                 logger.err('ToolbarMenuItem', 'Wikia API Failure, WikiaFooterApp is unusable');
  269.                 toolbarObj = {
  270.                     el: $('#WikiaBarWrapper .toolbar > ul.tools'),
  271.                     handleOverflowMenu: $.noop
  272.                 };
  273.                 if (!toolbarObj.el.length) throw new Error("Direct search for Footer toolbar failed, it's missing from the DOM");
  274.             }
  275.  
  276.             this._toolbarApp = toolbarObj;
  277.         },
  278.         _constructMenu: function() {
  279.             if (!this._toolbarApp.menuGroup) throw new Error('Wikia TCToolbar not compatible');
  280.             if (this._$submenu) throw new Error('Submenu already exists! Destroy it first');
  281.  
  282.             this._$submenu = $(document.createElement('ul'))
  283.                 .addClass('tools-menu')
  284.                 ;
  285.             this._$item
  286.                 .prepend('<span class="arrow-icon-ctr"><span class="arrow-icon arrow-icon-single"></span></span>')
  287.                 .append(this._$submenu)
  288.                 .removeClass('overflow')
  289.                 ;
  290.             if (this._$item.parent().length) {
  291.                 // Reattach the item in case it spilled and was moved to a spill menu
  292.                 this.attach();
  293.                 // If the button is already attached then we need to activate now
  294.                 this._toolbarApp.menuGroup.add(this._$item);
  295.             }
  296.         },
  297.         destroyMenu: function() {
  298.             // Detach the submenu if there is one
  299.             if (this._$submenu) {
  300.                 // Menu is being destroyed whilst still attached so we need
  301.                 // to deregister the events
  302.                 if (this._$item.parent().length) {
  303.                     this._toolbarApp.menuGroup.remove(this._$item);
  304.                 }
  305.                 this._$submenu.remove();
  306.                 this._$item.find('> span.arrow-icon-ctr').remove();
  307.                 delete this._$submenu;
  308.             }
  309.             return this;
  310.         },
  311.         // Attaches the link-button to the Oasis toolbar
  312.         attach: function() {
  313.             var reattach = this._$item.parent().length !== 0;
  314.             // el is a jQuery object
  315.             this._toolbarApp.el.append(this._$item);
  316.  
  317.             // We invoke Wikia's overflow menu builder in case spillage happens
  318.             // We also deal with a Wikia bug where every menu item spills into the overflow for no
  319.             // reason. This is somewhat of a hack but is a nice convenience.
  320.             var $overflow = this._toolbarApp.el.find('.overflow-menu');
  321.             if ($overflow.is(':visible')) {
  322.                 $overflow.hide().find('> .tools-menu > li').each(function() {
  323.                     $overflow[0].parentNode.insertBefore(this, $overflow[0].parentNode.firstChild);
  324.                 });
  325.             }
  326.             // Respill if needed
  327.             this._toolbarApp.handleOverflowMenu();
  328.  
  329.             // Attach the submenu if there is one
  330.             if (this._$submenu && !reattach) {
  331.                 this._toolbarApp.menuGroup.add(this._$item);
  332.             }
  333.  
  334.             // Avoid losing the button if the user reconfigures their toolbar.
  335.             // There should be an event to hook but there doesn't seem to be one.
  336.             //
  337.             // After clicking Customize, pressing Save in the popup will
  338.             // invoke load(html) with the new toolbar configuration received via
  339.             // AJAX. We hook load, detach ourself from the toolbar, wait for load()
  340.             // to complete then reattach ourself afterwards.
  341.             if (this._toolbarHook === null) {
  342.                 var toolbarObj = this._toolbarApp, realFunc = toolbarObj.load, hookDisabled = false, self = this;
  343.                 this._toolbarHook = toolbarObj.load = function() {
  344.                     // Detach ourself first so that we won't get .remove()-ed which
  345.                     // kills our event handlers'
  346.                     var safe = !hookDisabled;
  347.                     if (safe) self.detach();
  348.  
  349.                     try {
  350.                         var r = realFunc.apply(this, arguments);
  351.                     } finally {
  352.                         // Now the toolbar is ready again, so we can just reattach to it.
  353.                         if (safe) self.attach();
  354.                     }
  355.                     return r;
  356.                 };
  357.                 this._toolbarHook.disable = function() {
  358.                     // We can only detach properly if there hasn't been a double
  359.                     // monkey patching.
  360.                     hookDisabled = true;
  361.                     if (toolbarObj.load === self._toolbarHook) {
  362.                         toolbarObj.load = realFunc;
  363.                         self._toolbarHook = null;
  364.                     }
  365.                 };
  366.                 this._toolbarHook.enable = function() {
  367.                     hookDisabled = false;
  368.                 };
  369.             } else {
  370.                 this._toolbarHook.enable();
  371.             }
  372.             return this;
  373.         },
  374.         detach: function() {
  375.             if (this._toolbarHook) this._toolbarHook.disable();
  376.             if (this._$submenu) {
  377.                 this._toolbarApp.menuGroup.remove(this._$item);
  378.             }
  379.             this._$item.detach();
  380.             return this;
  381.         },
  382.         destroy: function() {
  383.             this.destroyMenu();
  384.             this._$item.remove();
  385.             this._$item = this.$link = null;
  386.             return this;
  387.         },
  388.         // Add LIs to the submenu
  389.         appendToSubmenu: function($nodes) {
  390.             if (!this._$submenu) this._constructMenu();
  391.             this._$submenu.append($nodes);
  392.             return this;
  393.         },
  394.         prependToSubmenu: function($nodes) {
  395.             if (!this._$submenu) this._constructMenu();
  396.             this._$submenu.prepend($nodes);
  397.             return this;
  398.         },
  399.         removeFromSubmenu: function($nodes, asDetach) {
  400.             if (this._$submenu) {
  401.                 // Only remove stuff that is actually a child rather than random nodes
  402.                 if (this._$submenu.find($nodes)[asDetach ? 'detach' : 'remove']().length) {
  403.                     // If the menu is empty now, kill it
  404.                     if (!this._$submenu.find('> li').length) {
  405.                         this.destroyMenu();
  406.                     }
  407.                 }
  408.             }
  409.             return this;
  410.         }
  411.     });
  412.  
  413.     //
  414.     // Class that implements the User Interface.
  415.     // Does not contain any actual business logic, just manages the state of the buttons
  416.     // and generates events when they change.
  417.     //
  418.     function ExpandButtons(isExpanded, isWide) {
  419.         // Anti-badness defence
  420.         if (!settings.debug && document.getElementById('HideRailJS_Menu')) {
  421.             throw new Error('HideRail is already running');
  422.         }
  423.  
  424.         // Create the base menu item
  425.         this._toolbarItem = new ToolbarMenuItem('HideRailJS_Menu');
  426.         this._toolbarItem.eventsDestroyed.add($.proxy(this._onEventsDestroyed, this));
  427.  
  428.         // Click handler for the base menu item, not in the popup
  429.         this._$link = this._toolbarItem.$link;
  430.  
  431.         // Wide mode checkbox
  432.         this._$wideCheckbox = $(document.createElement('input'))
  433.             .prop('type', 'checkbox')
  434.             ;
  435.         // NOTE: We wrap in an anchor as the menu expects anchors so won't style properly
  436.         //  without one.
  437.         this._$wideModeLi = $(document.createElement('li'))
  438.             .append($(document.createElement('a'))
  439.                 .append(this._$wideCheckbox)
  440.                 .append(i18n.widemode)
  441.             );
  442.  
  443.         this._onEventsDestroyed();
  444.  
  445.         // State change events
  446.         this.wideStateChanged = $.Callbacks('memory');
  447.         this.expandStateChanged = $.Callbacks('memory');
  448.  
  449.         // Configure initial state
  450.         this.updateExpandButton(isExpanded);
  451.         this.updateWideButton(isWide);
  452.  
  453.         // Delegate functions
  454.         this.attachButton = $.proxy(this._toolbarItem.attach, this._toolbarItem);
  455.         this.detachButton = $.proxy(this._toolbarItem.detach, this._toolbarItem);
  456.         this.destroyButton = $.proxy(this._toolbarItem.destroy, this._toolbarItem);
  457.  
  458.         return this;
  459.     }
  460.     $.extend(ExpandButtons.prototype, {
  461.         // Second stage init to enable the wide mode checkbox or not
  462.         commitInit: function(aWideEnabled) {
  463.             if (aWideEnabled) {
  464.                 this._toolbarItem.appendToSubmenu(this._$wideModeLi);
  465.             } else {
  466.                 this._$wideModeLi.remove();
  467.                 delete this._$wideModeLi;
  468.                 delete this._$wideCheckbox;
  469.                 this.updateWideButton = $.noop;
  470.             }
  471.             return this;
  472.         },
  473.  
  474.         // When Wikia's code tries to kill us, we have to rise from the ashes by
  475.         // reattaching all the event handlers.
  476.         _onEventsDestroyed: function() {
  477.             this._$link.on('click.HideRailExpand', $.proxy(this._onExpandClicked, this));
  478.             if (this._$wideCheckbox) {
  479.                 var $check = this._$wideCheckbox
  480.                     .change($.proxy(this._onWideCheckChanged, this))
  481.                     ;
  482.                 $check.parent() // The anchor tag
  483.                     .click(function(e) {
  484.                         // If the checkbox was clicked directly then ignore it
  485.                         if (e.target === $check[0]) return;
  486.                         e.preventDefault();
  487.                         $check[0].checked = !$check[0].checked;
  488.                         $check.change(); // Signal manually
  489.                     })
  490.                     ;
  491.             }
  492.         },
  493.  
  494.         // Event handler for link interaction
  495.         _onExpandClicked: function(e) {
  496.             e.preventDefault();
  497.             this.updateExpandButton(!this._isExpanded, true);
  498.         },
  499.         _onWideCheckChanged: function() {
  500.             this.updateWideButton(this._$wideCheckbox.prop('checked'), true);
  501.         },
  502.  
  503.         // Updates the text on the button and fires the notification event that the state
  504.         // has been changed
  505.         updateExpandButton: function(aIsExpanded, aByEvent) {
  506.             // NOTE: This equality test relies on this._isExpanded being undefined to start
  507.             //  with so that the first call is always unconditional
  508.             aIsExpanded = !!aIsExpanded;
  509.             if (this._isExpanded === aIsExpanded) return this;
  510.             var mode = aIsExpanded ? 'shrink' : 'expand',
  511.                 $link = this._$link,
  512.                 link = $link[0];
  513.  
  514.             link.className = link.className.replace(
  515.                 /(?:^|\s+)hiderail-link-[^\s]*\b/g, ''
  516.             ) + ' hiderail-link-' + mode;
  517.             $link.text(i18n[mode]);
  518.             this.expandStateChanged.fire( (this._isExpanded = aIsExpanded), !!aByEvent );
  519.             return this;
  520.         },
  521.         // Flicks the checkbox to reflect the internal state and fires the change event
  522.         updateWideButton: function(aIsWide, aByEvent) {
  523.             aIsWide = !!aIsWide;
  524.             if (aIsWide === this._isWide) return this;
  525.             this._$wideCheckbox.prop('checked', aIsWide);
  526.             this.wideStateChanged.fire( (this._isWide = aIsWide), !!aByEvent );
  527.             return this;
  528.         },
  529.  
  530.         // Switches the expand button to "dead" mode when it won't do anything
  531.         // (i.e. no-one is going to listen for the events)
  532.         killExpandButton: function() {
  533.             this._$link
  534.                 .off('click.HideRailExpand')
  535.                 [0].className = 'hiderail-link-generic'
  536.                 ;
  537.             this.updateExpandButton = $.noop;
  538.             return this;
  539.         },
  540.  
  541.         // Query if the page is already expanded or not
  542.         isExpanded: function() {
  543.             return this._isExpanded;
  544.         },
  545.         isWide: function() {
  546.             return this._isWide;
  547.         }
  548.     });
  549.  
  550.  
  551.     // Class for handling Article pages.
  552.     // Uses oasis-one-column to properly format the page in the absence of the rail
  553.     // Handles the search box to keep it from disappearing with the rail by attaching
  554.     // it to the Page Header (this keeps it in roughly the same pixel position).
  555.     ArticleClass = function(buttonObj) {
  556.         var header;
  557.  
  558.         this._$rail = $('#WikiaRail');
  559.         this._$content = $('#WikiaMainContent');
  560.         if (!this._$rail.length || !this._$content.length) throw new Error('No rail/content?');
  561.         if (this._$rail.is(':hidden')) throw new Error('Rail is being hidden by something else?');
  562.  
  563.         header = document.getElementById('WikiaPageHeader');
  564.         if (header === null) {
  565.             // Blog post pages have a weird structure, keeping the search is harder
  566.             header = $('#WikiaUserPagesHeader.WikiaBlogPostHeader');
  567.             if (header.length) {
  568.                 header = $(document.createElement('div'))
  569.                     .css({
  570.                         'float': 'right',
  571.                         marginLeft: '10px'
  572.                     })
  573.                     .prependTo(header)
  574.                     [0]; // Extract DIV back out of jQuery
  575.             } else {
  576.                 header = null;
  577.             }
  578.         }
  579.         this._header = header;
  580.         this._search = header && document.getElementById('WikiaSearch');
  581.  
  582.         this._button = buttonObj;
  583.         buttonObj.expandStateChanged.add($.proxy(this._onStateChanged, this));
  584.  
  585.         return this;
  586.     };
  587.     $.extend(ArticleClass.prototype, {
  588.         _onStateChanged: function(isExpanded) {
  589.             var _search = this._search, $rail = this._$rail;
  590.             if (isExpanded) {
  591.                 $rail.css('display', 'none');
  592.                 document.body.className += ' oasis-one-column';
  593.                 if (_search) this._header.appendChild(_search);
  594.                 // Grid layout uses CSS classes which make this a pain in the ass
  595.                 this._$content.removeClass('grid-4').addClass('grid-6');
  596.             } else {
  597.                 if (_search) $rail.prepend(_search);
  598.                 $(document.body).removeClass('oasis-one-column');
  599.                 $rail.css('display', ''); // This ensures that CSS display none is honored
  600.                 this._$content.removeClass('grid-6').addClass('grid-4');
  601.             }
  602.         }
  603.     });
  604.  
  605.  
  606.     // Class for handling Edit pages.
  607.     // Replaces Wikia's expand/contract feature in source mode and adds the ability
  608.     // to hide the rail to the visual editor. This functionality replaces Wikia's
  609.     // entirely, this is helpful because Wikia's expander phones home with AJAX
  610.     // everytime you use it which causes unnecessary lag.
  611.     // ASSUMPTION: Editor is not in wide page mode (wgEditPageIsWidePage != true)
  612.     //
  613.     // NOTES:
  614.     // Toolbars are controlled by WikiaEditor.plugins.toolbarspaces, point of action
  615.     // is "renderToolbars()". Data is sourced from config.toolbars which is a map of
  616.     // "spaces" as per WikiaEditor.plugins.spaces, points of interest are are "toolbar"
  617.     // and "rail" which contain modules from the module directory. There is no way to
  618.     // purge toolbars but we don't really need to: just wipe out config.toolbars and
  619.     // replace it with { toolbar: ['ToolbarWidescreen'] } then call renderToolbars()
  620.     // to add the widescreen toolbar. >>>>We can then acquire the toolbar using
  621.     // modules.ToolbarWidescreen.element which is a jQuery we can just wack with
  622.     // .hide()/.show() when switching back and forth between wide and not-wide mode.<<<<
  623.     // !!Module provides .show/.hide on the base class. Just wack the class itself instead.!!
  624.     //
  625.     // CAUTION: renderToolbars resets the .toolbars member which means that the existing
  626.     //  toolbars are lost which is very bad (especially since we may want the .rail member
  627.     //  of that later), have to capture that property before rendering then manual
  628.     //  merge new and old.
  629.     //
  630.     // Editor comes from (x = WikiaEditor.getInstance())
  631.     // x.config.toolbars, x.plugins
  632.     // <<
  633.     // Scratch that: The logic block is ugly as hell. We can do better by reimplementing
  634.     // the renderChildren function:
  635.     // toolbarspaces.toolbars.toolbar.module.modules.push(x = editorInst.modules.create('ToolbarWidescreen')); // May fail
  636.     // toolbarspaces.toolbars.toolbar.module.moduleEls.push(y = x.render()); // x may fail
  637.     // z = toolbarspaces.toolbars.toolbar.module.afterRenderChild('ToolbarWidescreen', x, y);
  638.     // toolbarspaces.toolbars.toolbar.module.childEls.push(z);
  639.     // toolbarspaces.toolbars.toolbar.module.el.append(z);
  640.     // The x object can be used to .show/.hide, the y object can be used to query for the
  641.     // categories module as a descendant of it.
  642.     //
  643.     // Key problem: Categories module refuses to duplicate, it MOVES from rail to
  644.     // wide toolbar which is a problem, I'm not sure how to put it back in the rail
  645.     // yet. I can manually dance it around with jQuery which works fine although,
  646.     // since it's obviously self-aware, I should make it dance itself.
  647.     // $('.cke_panel_dropdown .module.module_categories > .module_content').contents().appendTo(
  648.     //   $('#EditPageRail .module.module_categories > .module_content')
  649.     // );
  650.     // The categories module has a #id which is what is causing the jump glitch. This
  651.     // is not good as it'll probably cause double categories on saving. We may have to
  652.     // strip the categories module from the wide toolbar then shift the category module
  653.     // back and forth manually. This is another problem since we won't have the dropdown
  654.     // panel if I do that; need to monkey patch the categories module so that it loads
  655.     // but does not do anything important then do manual switching between real and fake
  656.     // so that we only have one "real" instance that receives the saving event.
  657.     // <<
  658.     // Scratch that. Turns out #csMainModule is actually the same thing as the category
  659.     // editor on article pages. It's referring to a global node/object pair window.cs*,
  660.     // this is disgusting but it's simple enough: window.csType="module" followed by
  661.     // window.initCatSelectForEdit(). This solves this problem nicely, we can just jQuery
  662.     // the #csMainModule around freely without side-effects.
  663.     //
  664.     // Misc: x.plugins.autoresizer needs to have its rightrail property unset to avoid
  665.     // screwing up when in wide mode [it'll keep trying to make the rail taller to match
  666.     // the page height]. This needs to be reset, either by calling initDom
  667.     // or manually when switching back to Rail mode. Makes sure to clear the height
  668.     // style as well.
  669.     // ----: Figure out what is responsible for .rail-auto-height
  670.     //  > railminimumheight. Just hit it with display:none
  671.     //
  672.     // Architecture:
  673.     // Plugin installed manually via initPlugin (CAUTION: Editor may not be ready,
  674.     // where getInstance() returns null; TODO: Look for more events or just edit
  675.     // wikiacore to depend on us and avoid the issue; check config.isMiniEditor is
  676.     // false for thouroughness).
  677.     // Plugin will need to depend on toolbarspaces and autoresizer.
  678.     //
  679.     // On init do the required manip [get categories .parent() for rail restore first],
  680.     // then find the toolbar by digging in the .toolbars prop and call the module's hide().
  681.     // Once the toolbar is ready, use jQuery to shift
  682.     // the categories module back to the rail. [remember the .parent() as well]
  683.     //
  684.     // When widemode happens, shift categories to main toolbar, call .show on it,
  685.     // poke the autoresizer to remove its rightrail property (set "false") then
  686.     // remove the .style.height from the rail (can use the rightrail prop to do this).
  687.     // Hide the rail-auto-height with display:none.
  688.     // We need to partially reimplement mainpagewidemode which isn't hard since it
  689.     // only calculates a min-height like we're already doing anyway.
  690.     //
  691.     // When switching back to normal, .hide the toolbar, shift the categories back,
  692.     // clear the min-height style from the toolbar space, show the rail-auto-height,
  693.     // call autoresizer.initDom().
  694.     //
  695.     // CAUTION: editpage-visualwidemode is mutex with sourcewidemode, make sure you strip
  696.     //  those styles out to avoid glitches.
  697.     //
  698.     // Editor plugin will expose .wide() function as a getter/setter that toggles
  699.     // this processing. EditClass will just attach the plugin and try to init it.
  700.     //
  701.     // TODO: // this.editor.initialized === true means we have to call initDom manually as all
  702.             // events were already fired
  703.     //
  704.     if (typeof($.createClass) === 'function' && window.WikiaEditor) {
  705.         EditPlugin = $.createClass(window.WikiaEditor.plugin, {
  706.             // Dependencies
  707.             // TODO: Drop the dep on sourcewidemode and test conditionally
  708.             requires: ['spaces', 'toolbarspaces', 'autoresizer', 'sourcewidemode', 'ui'],
  709.            
  710.             // Don't ask, Firefox fails to execute $.createClass correctly, apparently
  711.             // ({}).constructor !== Object.prototype.constructor even though it does.
  712.             // Setting constructor to false bypasses the miscompilation/execution crap
  713.             // and forces $.createClass to work.
  714.             constructor: false,
  715.            
  716.             _initialised: false,
  717.             _isWide: false,
  718.             _wideCategories: $(),
  719.             _railCategories: $(),
  720.            
  721.             // beforeInit
  722.             init: function() {
  723.                 if (this.editor.ui.uiReadyFired) {
  724.                     return this._doInit();
  725.                 }
  726.                 var self = this;
  727.                 this.editor.on('uiReady', function callback() {
  728.                     self.editor.off('uiReady', callback);
  729.                     self._doInit();
  730.                 });
  731.             },
  732.             // initEditor
  733.             // initDom
  734.             // afterShow
  735.            
  736.             _doInit: function() {
  737.                 var editor = this.editor;
  738.                 // TODO: Better error handling
  739.                
  740.                 // Disable self for mini-editors
  741.                 // NOTE: There should never be mini-editors on the Edit page.
  742.                 if (editor.config.isMiniEditor || this._initialised) {
  743.                     logger.log('Init Abort:', editor.config.isMiniEditor ? 'MiniEditor' : 'Double Init');
  744.                     return;
  745.                 }
  746.                
  747. //Deleted content here
  748.                
  749.                 // Step 4: Grab the autoresizer since we need to alter its configuration
  750.                 // whenever we switch.
  751.                 this._resizer = editor.plugins.autoresizer;
  752.                
  753.                 // Now we're done. We just need to wait for the edit class to direct
  754.                 // us to expand or shrink
  755.                 this._initialised = true;
  756.                 EditPlugin.ready.fire(this);
  757.             },
  758.             wide: function(toWide) {
  759.                 // Getter
  760.                 if (toWide === void 0) {
  761.                     return this._isWide;
  762.                 }
  763.                 toWide = !!toWide;
  764.                 // Same, do nothing
  765.                 if (toWide === this._isWide) return;
  766.                 if (!this._initialised) {
  767.                     logger.wrn('EditPlugin', 'Attempted to change wide mode before initialised');
  768.                     return;
  769.                 }
  770.                
  771.                 var $cats = $('#CategorySelect');
  772.                 if (toWide) {
  773.                     this._wideToolbar.show();
  774.                     if (!$cats.parent().is(this._wideCategories)) {
  775.                         this._railCategories = $cats.parent();
  776.                         this._wideCategories.append($cats);
  777.                     }
  778.                     this._rail.css('height', '')
  779.                         .find('.rail-auto-height').css('display', 'none');
  780.                     this._resizer.rightrail = false;
  781.                     this.editor.element.addClass('editpage-visualwidemode');
  782.                     // TODO: toolbar should be fetched in init and checked for fail
  783.                     // TODO: This is too simple, need to handle content-box calc
  784.                     //  (innerHeight() - height()) = padding; rail.outerHeight - padding = minHeight
  785.                     this.editor.getSpace('toolbar').css('minHeight', this._rail.outerHeight() + 'px');
  786.                     // NOTE: Copied from sourcewidemode, supposedly fixes a problem with IE
  787.                     if (this.editor.ck) this.editor.ck.fire('resize');
  788.                     // Get the resizer to fix the textbox height
  789.                     this._resizer.resize();
  790.                     // HACK: Transitions screw us here, we have to listen for the transition to end
  791.                     //  and fire the resize event, otherwise the editor will be left with a large
  792.                     //  whitespace area at the bottom
  793.                     // NOTE: Problem is caused by wide toolbar pushing down the content during transition,
  794.                     //  that happens because of the 300px right padding and the fact that it starts 300px
  795.                     //  from the right edge at the start of the transition (=600px from right edge, 66%).
  796.                     // TODO: This should be registered in the init instead, that's important because
  797.                     //  browsers without transitions or with transitions disabled will pile up more
  798.                     //  and more copies of this event handler since it never gets triggered
  799.                     $('#EditPageMain').on('transitionend.HACKHACKHACK', function() {
  800.                         $(this).off('.HACKHACKHACK');
  801.                         $(window).resize();
  802.                     });
  803.                 } else {
  804.                     this._wideToolbar.hide();
  805.                     // Migration happens the first time the toolbar drop down is expanded by the
  806.                     // user. We need to notice that and take notes on where it ended up.
  807.                     if (!$cats.parent().is(this._railCategories)) {
  808.                         this._wideCategories = $cats.parent();
  809.                         this._railCategories.append($cats);
  810.                     }
  811.                     this.editor.getSpace('toolbar').css('minHeight', '');
  812.                     this.editor.element.removeClass('editpage-visualwidemode');
  813.                     this._rail.find('.rail-auto-height').css('display', '');
  814.                     if (this.editor.ck) this.editor.ck.fire('resize');
  815.                     this._resizer.initDom();
  816.                 }
  817.                 $(window).resize();
  818.                
  819.                 this._isWide = toWide;
  820.             }
  821.         });
  822.         EditPlugin.ready = $.Callbacks('memory once');
  823.     }
  824.     EditClass = function(buttonObj, storeObj) {    
  825.         if (!EditPlugin) throw new Error('No EditPlugin?');
  826.        
  827.         this._store = storeObj;
  828.         this._button = buttonObj;
  829.        
  830.         // Wait for the plugin to become ready before proceeding
  831.         EditPlugin.ready.add($.proxy(this._onPluginReady, this));
  832.        
  833.         // Install plugin
  834.         var WE = window.WikiaEditor, plugName = 'HideRailEditorIntegration';
  835.         logger.log('EditClass', 'Starting');
  836.         window.GlobalTriggers.on('wikiaeditoraddons', function callback() {
  837.             logger.log('EditClass', 'Addon Event Fired');
  838.             window.GlobalTriggers.off('wikiaeditoraddons', callback);
  839.             WE.plugins[plugName] = EditPlugin;
  840.             WE.plugins.wikiacore.prototype.requires.push(plugName); // Register as required always
  841.            
  842.             // If the editor is already started then we need to do this the hard way
  843.             // wpTextbox1 is the name of the primary Editor textbox for an article, so we
  844.             // don't try to connect to a mini-editor or whatever.
  845.             var WEI = WE.getInstance('wpTextbox1');
  846.             if (WEI) {
  847.                 var plug = WEI.initPlugin(plugName);
  848.                 logger.log('EditClass', 'Instance exists, initialised:', WEI.initialized, 'State:', WEI.state);
  849.                 if (WEI.initialized) {
  850.                     plug.init();
  851.                 }
  852.             }
  853.         });
  854.  
  855.         return this;
  856.     };
  857.     $.extend(EditClass.prototype, {
  858.         // Second stage init when the editor has come up
  859.         _onPluginReady: function(plugin) {
  860.             this._plugin = plugin;
  861.             logger.log('EditClass', 'Plugin Ready');
  862.            
  863.             // We need to decide the initial editor mode (Flush first, to align with default)
  864.             // Flushing is required since it glitches with the Rail hidden state
  865.             this._button.updateExpandButton(false, false);
  866.             this._button.updateExpandButton(this._isExpanded = !!jStorage.get(plugin.storageKey), true);
  867.            
  868.             // Register the event handlers (Remember: Fires immediately via memory)
  869.             this._button.expandStateChanged.add($.proxy(this._onStateChanged, this));
  870.             this._store.storageChanged.add($.proxy(this._onStorageChanged, this));
  871.         },
  872.         _onStateChanged: function(aIsExpanded, aByEvent) {
  873.             logger.log('EditPlugin', "Expand:", aIsExpanded, "Event:", aByEvent);
  874.             if (!aByEvent) {
  875.                 // If it was a programmatic change then revert it
  876.                 // CAUTION: This is an implicit recursion
  877.                 return this._button.updateExpandButton(this._isExpanded);
  878.             }
  879.             this._plugin.wide((this._isExpanded = aIsExpanded));
  880.             jStorage.set(this._plugin.storageKey, aIsExpanded);
  881.         },
  882.         // We have our own local storage so we don't want to bother with the script's
  883.         _onStorageChanged: function(evt) {
  884.             if (evt.key !== this._plugin.storageKey) return;
  885.             this._button.updateExpandButton(jStorage.get(this._plugin.storageKey), true);
  886.         },
  887.         // Internal storage, do not remember or set us
  888.         hasOwnMemory: true
  889.     });
  890.  
  891.  
  892.  
  893.     //
  894.     // **************************************************************************
  895.     // OasisWideMode
  896.     // Rebuilds the document stylesheets to be wide if possible
  897.     //
  898.     OasisWideMode = function(buttonObj) {
  899.         this._button = buttonObj;
  900.         buttonObj.wideStateChanged.add($.proxy(this._onWideChanged, this));
  901.         return this;
  902.     };
  903.     $.extend(OasisWideMode.prototype, {
  904.         _onWideChanged: function(aIsWide) {
  905.             // In the event of additional sheets added by AJAX or something
  906.             this.SheetManager.processSASS(this.SheetManager.activeValue);
  907.             // Toggle state
  908.             $('#WikiaPage')[aIsWide ? 'addClass' : 'removeClass']('hiderail-wide-grid');
  909.             this.SheetManager[aIsWide ? 'applySheets' : 'removeSheets']();
  910.         }
  911.     });
  912.  
  913.     //
  914.     // Stylesheet Manager Singleton
  915.     //
  916.     OasisWideMode.prototype.SheetManager = {
  917.         // HD is a reference to old name, was changed to 'widthType'
  918.         _regexGetHD: /^(.*?\/sass\/(?:[^\/]+?%26)?widthType%3[dD])(\d+)([^\/]*\/.*)$/,
  919.         sheets: [],
  920.         applied: false,
  921.  
  922.         // Add/Change the 'widthType' SASS parameter
  923.         // Values:
  924.         // 0/Out-of-Range = Old (1000px)
  925.         // 1 = WOWWiki (1200px)
  926.         // 2 = Auto-width/Fluid (100%) [glitchy]
  927.         // 3 = Wikia Grid Layout (1030px)
  928.         //
  929.         // Algorithm Note:
  930.         // Altering the existing link node causes the page to unstyle and restyle.
  931.         // As such, we add new nodes immediately after the existing ones which
  932.         // causes the cascade to override properly without causing a FOUC.
  933.         _buildSheets: function($links, value) {
  934.             var linktag, text, match, newlink, links = [],
  935.                 i = $links.length,
  936.                 regexNoHD = /^(.*?\/sass\/)(.*)/,
  937.                 regexGetHD = this._regexGetHD,
  938.                 head = document.getElementsByTagName('head')[0];
  939.             while (i--) {
  940.                 linktag = $links[i];
  941.                 text = linktag.href;
  942.                 match = regexGetHD.exec(text);
  943.                 if (match) {    // Param already exists?
  944.                     text = match[1] + value + match[3];
  945.                 } else {
  946.                     // We must add the HD parameter before the wordmark-font part
  947.                     // everything after that is interpreted as a file path.
  948.                     match = regexNoHD.exec(text);
  949.                     if (match) {
  950.                         text = match[1]+ 'widthType%3D' + value + '%26'+ match[2]
  951.                              ;
  952.                     }
  953.                 }
  954.                 if (!match) continue;
  955.  
  956.                 newlink = linktag.cloneNode();
  957.                 newlink.href = text;
  958.                 newlink.removeAttribute('id');
  959.                 links[links.length] = {
  960.                     wide: newlink,
  961.                     old: linktag
  962.                 };
  963.                 // Try to prevent FOUC by forcing loading/parsing to start now
  964.                 // We use insertBefore because Chrome will cascade the sheet IMMEDIATELY
  965.                 // then uncascade it which triggers transitions. Inserting the CSS at the
  966.                 // start of the head ensures the cascade has no effect.
  967.                 head.insertBefore(newlink, head.firstChild);
  968.                 head.removeChild(newlink);
  969.             }
  970.             return links;
  971.         },
  972.         processSASS: function(value) {
  973.             var $links = $('link[href*="/sass/"][rel*="stylesheet"]');
  974.             if (!$links.length) throw new Error('No SASS Sheets?');
  975.  
  976.             // Only need to do this once. It won't change once the page is loaded.
  977.             if (this.originalValue === void 0) {
  978.                 var defaultHD = this._regexGetHD.exec($links.prop('href'));
  979.                 if (defaultHD) { // HD is already being used
  980.                     defaultHD = +defaultHD[2];
  981.                 } else { // HD is not being used
  982.                     defaultHD = 0;
  983.                 }
  984.                 this.originalValue = defaultHD;
  985.             }
  986.             var applied = this.applied;
  987.             if (value !== this.activeValue) {
  988.                 this.activeValue = value;
  989.                 this.removeSheets();
  990.                 this.sheets = [];
  991.             }
  992.  
  993.             var oldsheets = this.sheets;
  994.             $links = $links.filter(function() { // Set complement, only new sheets (if any)
  995.                 var i = oldsheets.length;
  996.                 while (i--) { // Since our sheets are applied, don't match ourself
  997.                     if (oldsheets[i].old  === this || oldsheets[i].wide === this) return false;
  998.                 }
  999.                 return true;
  1000.             });
  1001.  
  1002.             this.sheets = oldsheets.concat(this._buildSheets($links, value));
  1003.  
  1004.             // Apply the new sheets, existing ones will just be reinserted in the same spot
  1005.             if (applied) this.applySheets();
  1006.  
  1007.             return this.originalValue;
  1008.         },
  1009.  
  1010.         applySheets: function() {
  1011.             var sheets = this.sheets, i = sheets.length, link, oldlink;
  1012.             while (i--) {
  1013.                 link = sheets[i];
  1014.                 oldlink = link.old;
  1015.                 if (!oldlink.parentNode) {
  1016.                     // <LINK> has been detached somehow. Throw it away.
  1017.                     logger.info('SheetManager', 'Sheet lost:', oldlink.href);
  1018.                     sheets.splice(i, 1);
  1019.                     continue;
  1020.                 }
  1021.                 oldlink.parentNode.insertBefore(link.wide, oldlink.nextSibling);
  1022.             }
  1023.             this.applied = true;
  1024.             // Get Wikia JS to fix Editor
  1025.             // Since we're persisting the links, we can just do this right now since
  1026.             // there won't be a loading delay the 2nd+ time
  1027.             $(window).resize();
  1028.         },
  1029.         removeSheets: function() {
  1030.             var sheets = this.sheets, i = sheets.length, link;
  1031.             while (i--) {
  1032.                 link = sheets[i].wide;
  1033.                 if (!link.parentNode) continue;
  1034.                 link.parentNode.removeChild(link);
  1035.             }
  1036.             this.applied = false;
  1037.             // No need to wait here. Styles should kick in immediately (removal)
  1038.             $(window).resize();
  1039.         }
  1040.     };
  1041.  
  1042.     //
  1043.     // Storage Management Object
  1044.     // This exists to contain all this crap in one place.
  1045.     //
  1046.     StorageManager = function() {
  1047.         this.storageChanged = $.Callbacks();
  1048.         return this;
  1049.     };
  1050.     $.extend(StorageManager.prototype, {
  1051.         expanded: 'HideRailJS_Expanded',
  1052.         widened: 'HideRailJS_Widened',
  1053.  
  1054.         // Functions to attach memory event catchers
  1055.         // So we can store the state to local storage
  1056.         attachForStateMemory: function(buttonObj, noSave) {
  1057.             if (this._buttonObj) throw new Error('Already attached');
  1058.             this._buttonObj = buttonObj;
  1059.             this._callbacks = [
  1060.                 this._makeSaveHandler(this.expanded, 'defaultExpand'),
  1061.                 this._makeSaveHandler(this.widened, 'defaultWideMode'),
  1062.                 $.proxy(this._onStorage, this)
  1063.             ];
  1064.             logger.log('StorageManager', 'noSave:', noSave);
  1065.             if (!noSave) {
  1066.                 buttonObj.expandStateChanged.add(this._callbacks[0]);
  1067.             }
  1068.             this._noSave = noSave;
  1069.             buttonObj.wideStateChanged.add(this._callbacks[1]);
  1070.  
  1071.             // Try to catch storage change events so we can keep tabs
  1072.             // consistent with each other
  1073.             if (settings.syncTabs) {
  1074.                 $(window).on('storage', this._callbacks[2]);
  1075.             }
  1076.             return this;
  1077.         },
  1078.         detachStateMemory: function() {
  1079.             $(window).off('storage', this._callbacks[2]);
  1080.             this._buttonObj.expandStateChanged.remove(this._callbacks[0]);
  1081.             this._buttonObj.wideStateChanged.remove(this._callbacks[1]);
  1082.             delete this._callbacks;
  1083.             delete this._buttonObj;
  1084.             return this;
  1085.         },
  1086.  
  1087.         // When localstorage is changed by a different tab, this event fires
  1088.         // NOTE: Some browsers are buggy and fire in same tab as well so beware the
  1089.         //  recursion devil.
  1090.         _onStorage: function(evt) {
  1091.             // evt has .newValue and .oldValue but it's the raw JSON string, plus we
  1092.             // we have to deal with the default or not-default algorithm
  1093.             switch (evt.originalEvent.key) {
  1094.             case this.expanded:
  1095.                 if (this._noSave) break;
  1096.                 this._buttonObj.updateExpandButton(this.getIsExpanded());
  1097.                 break;
  1098.  
  1099.             case this.widened:
  1100.                 this._buttonObj.updateWideButton(this.getIsWide());
  1101.                 break;
  1102.  
  1103.             default:
  1104.                 this.storageChanged.fire(evt.originalEvent);
  1105.             }
  1106.         },
  1107.  
  1108.         _makeSaveHandler: function(key, defaultKey) {
  1109.             return function(aIsWide) {
  1110.                 jStorage[aIsWide !== settings[defaultKey] ? 'set' : 'del'](key, aIsWide);
  1111.             };
  1112.         },
  1113.  
  1114.         getIsWide: function() {
  1115.             return getBooleanStorageWithDefault(this.widened, settings.defaultWideMode);
  1116.         },
  1117.         getIsExpanded: function() {
  1118.             return getBooleanStorageWithDefault(this.expanded, settings.defaultExpand);
  1119.         }
  1120.     });
  1121.  
  1122.     // OASISWIDEMODE START
  1123.     // We need to start doing the Wide mode work ASAP to avoid weirdness as much as possible.
  1124.     // We don't want the UI to pop wider only after the DOM is fully displayed...
  1125.     // "hiderail-transitions-on" prevents the page from widening when the wide sheets load,
  1126.     // otherwise EVERY page will slowly transition wide when it loads which sucks. We add
  1127.     // the enable switch class on "onload" so that won't happen.
  1128.     if (settings.wideMode) (function(man) {
  1129.         /*jshint bitwise:false */
  1130.         var mode = Math.max(Math.min(settings.wideMode | 0, 3), 1), Oasis = OasisWideMode;
  1131.         OasisWideMode = null; // Clear so that it stays null on an error
  1132.         // (Hopefully enough of the DOM is loaded that Oasis.scss is accessible,
  1133.         // this assumption SHOULD hold as global.js and global.css should come after Oasis)
  1134.         try {
  1135.             if (man.processSASS(mode) === mode) {
  1136.                 return logger.log('OasisWideMode', 'Page is already wide. [Disabled]');
  1137.             }
  1138.             if (man.sheets.length === 0) {
  1139.                 return logger.err('OasisWideMode', 'Internal error, incompatible SASS paths');
  1140.             }
  1141.         } catch(e) {
  1142.             return logger.err('OasisWideMode', 'Crash:', e, e.stack || e.stacktrace);
  1143.         }
  1144.  
  1145.         if (new StorageManager().getIsWide()) {
  1146.             // Apply the wide sheets right now before the page begins to load fully
  1147.             man.applySheets();
  1148.  
  1149.             // Requery after DOM Ready to catch any added sheets
  1150.             $($.proxy(man.processSASS, man, mode));
  1151.             // Try waiting for the document's load event to refresh the editor and toolbar
  1152.             // position (once the CSS has all loaded)
  1153.             $(window).on('load.OasisWideMode', function() {
  1154.                 $(window).off('load.OasisWideMode').resize();
  1155.                 man.processSASS(mode);
  1156.                 // This is a pain, the transitions will enable instantly which means the
  1157.                 // page will still transition unless all the stylesheets are completely
  1158.                 // done. Firefox at least seems to lag on this with onload firing whilst
  1159.                 // the last bit of processing is still pending, so we need to delay until
  1160.                 // Fx gets its crap together.
  1161.                 window.setTimeout(function() {
  1162.                     $(document.body).addClass('hiderail-transitions-on');
  1163.                 }, 500);
  1164.             });
  1165.         } else {
  1166.             $(document.body).addClass('hiderail-transitions-on');
  1167.         }
  1168.  
  1169.         // Put it back if we got here without an error
  1170.         OasisWideMode = Oasis;
  1171.     })(OasisWideMode.prototype.SheetManager); else {
  1172.         $(document.body).addClass('hiderail-transitions-on');
  1173.     }
  1174.  
  1175.  
  1176.     //
  1177.     // Instantiate the appropriate class (if any) and clean everything else up
  1178.     // once the DOM becomes available.
  1179.     //
  1180.     $(function(/*$*/) {
  1181.         try {
  1182.             var storeObj = new StorageManager(), buttonObj = new ExpandButtons(storeObj.getIsExpanded(), storeObj.getIsWide()), pageObj, oasisObj
  1183.               ;
  1184.  
  1185.             try {
  1186.                 // Message Walls claim to be edit pages but aren't (Wut?). Fortunately,
  1187.                 // we can catch them just by checking for oasis-one-column since REAL
  1188.                 // edit pages have that, Message Walls don't.
  1189.                 if (!/\boasis-one-column\b/.test(document.body.className)) {
  1190.                     logger.inf(0, 'Article Page');
  1191.                     pageObj = new ArticleClass(buttonObj);
  1192.                 } else if (({edit:1,editredlink:1,submit:1})[mw.config.get('wgAction')] === 1) {
  1193.                     // Some Edit pages are wider than normal like for the main page. We
  1194.                     // don't do anything on those pages.
  1195.                     if (settings.wideVisualEditor && !mw.config.get('wgEditPageIsWidePage')) {
  1196.                         logger.inf(0, 'Edit Page');
  1197.                         pageObj = new EditClass(buttonObj, storeObj);
  1198.                     }
  1199.                 }
  1200.             } catch(e) {
  1201.                 logger.err(0, 'Page Expander failed to initialise', e, e.stack || e.stacktrace);
  1202.             }
  1203.             // Create the OasisWideMode manager now
  1204.             if (OasisWideMode !== null) {
  1205.                 oasisObj = new OasisWideMode(buttonObj);
  1206.             }
  1207.             buttonObj.commitInit(oasisObj);
  1208.             // If there is no page expander then we'll disable the button entirely since it's useless.
  1209.             if (!pageObj) {
  1210.                 buttonObj.killExpandButton();
  1211.             }
  1212.             // Attach button to the page
  1213.             ArticleClass = EditClass = OasisWideMode = null;
  1214.             if (pageObj || oasisObj) {
  1215.                 storeObj.attachForStateMemory(buttonObj, !pageObj || pageObj.hasOwnMemory);
  1216.                 buttonObj.attachButton();
  1217.  
  1218.                 settings.buttonObj = buttonObj;
  1219.                 settings.pageObj = pageObj;
  1220.                 settings.oasisObj = oasisObj;
  1221.                 settings.storeObj = storeObj;
  1222.                 settings.detachCleanly = function() {
  1223.                     storeObj.detachStateMemory();
  1224.                     buttonObj.detachButton();
  1225.                     // Remove wide stylesheets from OasisWideMode
  1226.                     buttonObj
  1227.                         .updateWideButton(false, true)
  1228.                         .destroyButton()
  1229.                         ;
  1230.                 };
  1231.             } else if (settings.debug) {
  1232.                 logger.inf(0, 'Nothing to do here');
  1233.             }
  1234.         } catch(e) {
  1235.             logger.err(0, 'Initialisation failed:', e, e.stack || e.stacktrace);
  1236.         }
  1237.     });
  1238. })(window, jQuery, mediaWiki);
  1239.  
  1240. //</syntaxhighlight>
  1241.  
  1242. })();
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement