MediaWiki:Gadget-FabStationUIExtender.js

MediaWiki interface page

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Opera: Press Ctrl-F5.
/**
 * Gadget, FabStationUIExtender, v0.33
 */

var FabStationUIExtender = {
  consts: {
    CLASSNAME_CANVAS: '.fab-station-ui__canvas',
    CLASSNAME_HUD_XHAIR: 'fab-station-ui__hud-crosshair',
    CLASSNAME_HUD_ZOOM: 'fab-station-ui__hud-zoom',
    CLASSNAME_SCALE_CTNR: '.fab-station-ui__scale-container',
    CLASSNAME_SCROLL_CTNR: '.fab-station-ui__scroll-container',
    CLASSNAME_VIEWPORT: '.fab-station-ui__viewport',
    CLASSNAME_ZOUT_FLAG: 'zoomed-out',
    DATA_KEY_STATE: 'fab-station-ui-state',
    DATA_KEY_STATE_INITIAL: 'fab-station-ui-initial-state',
    ZOOM_LOW: 'zoomLow',
    ZOOM_HIGH: 'zoomHigh',
  },

  cfg: {
    zoomBtnMargin: 100,
    zoomOutFactor: 0.5,
  },

  messages: {
    labelZoomIn: '+ZOOM',
    labelZoomOut: '-ZOOM',
  },

  utils: {
    extractInitialState: function(ctx, $viewport) {
      var $canvasScaler = $viewport.find(ctx.consts.CLASSNAME_SCALE_CTNR).eq(0);
      return {
        canvasFullWidth: $canvasScaler.width(),
        canvasFullHeight: $canvasScaler.height(),
      };
    },
    getFocusCoords: function(ctx, $viewport) {
      var $canvasScroller = $viewport.find(ctx.consts.CLASSNAME_SCROLL_CTNR).eq(0);
      return {
        left: $viewport.width() / 2 + $canvasScroller.scrollLeft(),
        top: $viewport.height() / 2 + $canvasScroller.scrollTop(),
      };
    },
  },

  init: function($content) {
    var ctx = this;
    var consts = ctx.consts;
    var utils = ctx.utils;
    var viewports = $content.find(consts.CLASSNAME_VIEWPORT);
    viewports.each(function() {
      var $viewport = $(this);
      // avoid repeat initialization
      if ($viewport.data(consts.DATA_KEY_STATE_INITIAL))
        return;

      $viewport.data(consts.DATA_KEY_STATE_INITIAL, utils.extractInitialState(ctx, $viewport));
      $viewport.data(consts.DATA_KEY_STATE, {
        zoomedOutAt: null,
      });
      ctx.setViewportZoom($viewport, consts.ZOOM_HIGH, true);

      var $zoomBtn = $('<div />').addClass(consts.CLASSNAME_HUD_ZOOM);
      ctx.setZoomBtnState($zoomBtn, false);
      $zoomBtn.on('click', function() {
        if ($viewport.data(consts.DATA_KEY_STATE).zoomedOutAt == null) {
          ctx.setViewportZoom($viewport, consts.ZOOM_LOW);
          ctx.setZoomBtnState($zoomBtn, true);
        } else {
          ctx.setViewportZoom($viewport, consts.ZOOM_HIGH);
          ctx.setZoomBtnState($zoomBtn, false);
        }
      });
      $viewport.append($zoomBtn);

      var $crosshair = $('<div />').addClass(consts.CLASSNAME_HUD_XHAIR);
      $viewport.append($crosshair);
    });
  },

  setViewportZoom: function($viewport, zoomLevel, initial) {
    var ctx = this;
    var consts = ctx.consts;
    var utils = ctx.utils;
    if (zoomLevel != consts.ZOOM_LOW && zoomLevel != consts.ZOOM_HIGH)
      throw new RangeError('setViewportZoom: incorrect zoom', zoomLevel);

    var state = $viewport.data(consts.DATA_KEY_STATE)
    var initialState = $viewport.data(consts.DATA_KEY_STATE_INITIAL);
    var $canvas = $viewport.find(consts.CLASSNAME_CANVAS).eq(0);
    var $canvasScroller = $viewport.find(consts.CLASSNAME_SCROLL_CTNR).eq(0);
    var $canvasScaler = $viewport.find(consts.CLASSNAME_SCALE_CTNR).eq(0);
    if (initial) {
      $viewport.css('width', initialState.canvasFullWidth + ctx.cfg.zoomBtnMargin + 10);
      $canvasScaler.css('width', initialState.canvasFullWidth + ctx.cfg.zoomBtnMargin);
    }

    if ((initial || state.zoomedOutAt == null) && zoomLevel === consts.ZOOM_LOW) {
      var focusCoords = utils.getFocusCoords(ctx, $viewport);

      $viewport
        .data(consts.DATA_KEY_STATE, Object.assign({}, state, { zoomedOutAt: focusCoords }))
        .addClass(consts.CLASSNAME_ZOUT_FLAG);
      $canvas.css({
        transformOrigin: focusCoords.left + 'px ' + focusCoords.top + 'px',
        transform: 'scale(' + ctx.cfg.zoomOutFactor + ')',
      });
    } else if ((initial || state.zoomedOutAt != null) && zoomLevel === consts.ZOOM_HIGH) {
      var focusCoords = utils.getFocusCoords(ctx, $viewport);
      $viewport
        .data(consts.DATA_KEY_STATE, Object.assign({}, state, { zoomedOutAt: null }))
        .removeClass(consts.CLASSNAME_ZOUT_FLAG);

      if (!initial) {
        var targetScroll = {
          left: $canvasScroller.scrollLeft() + focusCoords.left - state.zoomedOutAt.left,
          top: $canvasScroller.scrollTop() + focusCoords.top - state.zoomedOutAt.top,
        };
        $canvasScroller.scrollLeft(targetScroll.left).scrollTop(targetScroll.top);
      }

      $canvas.css({
        transformOrigin: '',
        transform: '',
      });
    }
  },

  setZoomBtnState: function($btn, zoomedOut) {
    var ctx = this;
    if (zoomedOut) {
      $btn.text(ctx.messages.labelZoomIn);
    } else {
      $btn.text(ctx.messages.labelZoomOut);
    }
  },
};

Object.freeze(FabStationUIExtender.utils);
Object.freeze(FabStationUIExtender);

mw.hook('wikipage.content').add(function($content) { FabStationUIExtender.init($content) });