User:PerfektesChaos/js/paneMarker/d.js

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.
/// User:PerfektesChaos/js/paneMarker/d.js
/// 2022-08-28 PerfektesChaos@de.wikipedia
// Mark browser panes (tabbed or window) if particular action taken.
// Try to exchange wiki-favicon for a red one if editing a wiki page.
// Insert indicating character before document title.
// User defined changes of page title and favicon.
// ResourceLoader:  compatible;
//        dependencies: user, user.options, mediawiki.util, jquery.client
/// Fingerprint: #0#0#
/// License: CC-by-sa/4.0
/// <nowiki>
/* global window:false                                                 */
/* jshint forin:false,
          bitwise:true, curly:true, eqeqeq:true, latedef:true,
          laxbreak:true,
          nocomma:true, strict:true, undef:true, unused:true           */



( function ( mw, $ ) {
   "use strict";
   var Version  =  -3.6,
       PaneMk   =  "paneMarker";
   if ( typeof mw.libs[ PaneMk ]  !==  "object"
        ||   ! mw.libs[ PaneMk ] ) {
      mw.libs[ PaneMk ]  =  {  opt:  { }  };
   }
   mw.libs[ PaneMk ].vsn   =  Version;
   mw.libs[ PaneMk ].type  =  PaneMk;
   PaneMk                  =  mw.libs[ PaneMk ];
   if ( ! PaneMk.prefs ) {
      PaneMk.heads    =  "|commonswiki|dewiki|";
      PaneMk.rels     =  [ "icon", "shortcut icon" ];
      PaneMk.support  =  "User:PerfektesChaos/js/" + PaneMk.type;
      PaneMk.doc      =  "[[mw:" + PaneMk.support + "]]";
      PaneMk.config   =  { lazy:     false,
                           shortcut: true };
      PaneMk.prefs    =  { supply: "preferencesGadgetOptions" };
   }



   /*
    * This program is free software; you can redistribute it and/or
    * modify it under the terms of the GNU General Public License as
    * published by the Free Software Foundation; either version 2 of the
    * License, or (at your option) any later version.
    *
    * This program is distributed in the hope that it will be useful,
    * but WITHOUT ANY WARRANTY; without even the implied warranty of
    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    * GNU General Public License for more details.
    *
    * You should have received a copy of the GNU General Public License
    * along with this program;
    * if not, write to the Free Software Foundation, Inc.,
    * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    * http://www.gnu.org/copyleft/gpl.html
    */



   /*
   What links here: [[Special:GlobalUsage]]
   </nowiki>
   [[File:Apple-touch-icon-red-commons.png]]
   [[File:Apple-touch-icon-red-mediawiki.png]]
   [[File:Apple-touch-icon-red-wikinews.png]]
   [[File:Apple-touch-icon-red-wikipedia.png]]
   [[File:Apple-touch-icon-red-wiktionary.png]]
   [[File:Apple-touch-icon-red-wmf.png]]
   [[File:Favicon-red-commons.png]]
   [[File:Favicon-red-mediawiki.png]]
   [[File:Favicon-red-meta.png]]
   [[File:Favicon-red-testwiki.png]]
   [[File:Favicon-red-wikibooks.png]]
   [[File:Favicon-red-wikinews.png]]
   [[File:Favicon-red-wikipedia.png]]
   [[File:Favicon-red-wikiquote.png]]
   [[File:Favicon-red-wikisource.png]]
   [[File:Favicon-red-wikiversity.png]]
   [[File:Favicon-red-wmf.png]]
   <nowiki>
   */



   PaneMk.prefs.fetch  =  function () {
      // Retrieve preferences and overwrite presets
      // Uses:
      //    >  .type
      //     < .config.*
      //    mw.libs.preferencesGadgetOptions.fetch()
      // 2014-09-09 PerfektesChaos@de.wikipedia
      var s, v,
          scope  =  " appleIcon faviconICO faviconPNG lazy"
                    + " chars charDiff charHistory charLinks charVitally"
                    + " lowChar"
                    + " favicon leave shortcut ",
          vals   =  mw.libs[ this.supply ].fetch( PaneMk.type );
      for ( s in vals ) {
         if ( scope.indexOf( " " + s + " " )  >  0 ) {
            v  =  vals[ s ];
            if ( v !== "" ) {
               PaneMk.config[ s ]  =  v;
            }
         }
      }   // for s in vals
   };   // .prefs.fetch()



   PaneMk.prefs.fiat  =  function ( access, allow ) {
      // Convert object component user configuration into
      // Precondition:
      //    access  -- option ID
      //    allow   -- default if boolean, return boolean; else string
      // Uses:
      //    >  .config.*
      // 2014-09-09 PerfektesChaos@de.wikipedia
      var r;
      if ( typeof allow === "boolean" ) {
         if ( typeof PaneMk.config[ access ]  ===  "boolean" ) {
            r  =  PaneMk.config[ access ];
         } else {
            r  =  allow;
         }
      } else {
         if ( typeof PaneMk.config[ access ]  ===  "string" ) {
            r  =  PaneMk.config[ access ];
         } else {
            r  =  "";
         }
      }
      return  r;
   };   // .prefs.fiat()



   PaneMk.prefs.fire  =  function () {
      // Prepare preferencesGadgetOptions availability
      // Postcondition:
      //    loader.implement requested, if not yet defined
      //    Returns module ID
      // Uses:
      //    >  .prefs.supply
      //     < .prefs.suffix
      //    mw.loader.getState()
      //    mw.loader.state()
      //    mw.loader.load()
      //    mw.hook()
      //    (.prefs.fired)
      // Remark: May be used as event handler -- 'this' is not .prefs
      // 2022-08-28 PerfektesChaos@de.wikipedia
      var signature = "ext.gadget." + PaneMk.prefs.supply,
          rls;
      if ( ! mw.loader.getState( signature ) ) {
         rls = { };
         rls[ signature ] = "loading";
         mw.loader.state( rls );
      }
      PaneMk.prefs.suffix  =  "/r.js"
                              + "&bcache=1&maxage=604804"
                              + "&action=raw&ctype=text/javascript";
      mw.loader.load( "https://en.wikipedia.org/w/index.php?title="
                      + "User:PerfektesChaos/js/"
                      + PaneMk.prefs.supply + PaneMk.prefs.suffix,
                      "text/javascript" );
      mw.hook( PaneMk.prefs.supply + ".ready" )
        .add( PaneMk.prefs.fired );
   };   // .prefs.fire()



   PaneMk.prefs.fired  =  function () {
      // Prepare ResourceLoader availability
      // Postcondition:
      //    loader.implement requested, if not yet defined
      //    Returns module ID
      // Uses:
      //    .furnish()
      // Remark: May be used as event handler -- 'this' is not .prefs
      // 2018-08-24 PerfektesChaos@de.wikipedia
      PaneMk.furnish();
   };   // .prefs.fired()



   PaneMk.prefs.form  =  function () {
      // Trigger dialog form for Special:Gadgets
      // Postcondition:
      //    loader.implement requested, if not yet defined
      // Uses:
      //    >  .support
      //    >  .prefs.suffix
      //    mw.loader.load()
      // 2016-10-21 PerfektesChaos@de.wikipedia
      mw.loader.load( "https://www.mediawiki.org/w/index.php?title="
                      + PaneMk.support + "/dialog"
                      + this.suffix );
   };   // .prefs.form()



   PaneMk.facilitate  =  function () {
      // Set document title to relevant page name and site name
      // Postcondition:
      //    document.title has been set
      // Uses:
      //    >  this
      //    >  .env
      //     < document.title
      // Remark: wgRelevantPageName available since MW 1.20
      // 2022-08-25 PerfektesChaos@de.wikipedia
      var re  =  new RegExp( "_", "g" ),
          s   =  this.env.wgRelevantPageName;
      if ( ! s ) {
         s  =  this.env.wgPageName;
      }
      window.document.title  =  s.replace( re, " " )  +
                                " * "  +  this.env.wgSiteName;
   };   // .facilitate()



   PaneMk.favicon  =  function ( apply ) {
      // Try to exchange wiki-favicon
      // Precondition:
      //    apply  -- true:   red icon (page used in vulnerable mode)
      //              false:  show user defined default project icon
      // Uses:
      //    >  this
      //    >  .site
      //    >  .less
      //    >  .config.favicon
      //    >  .config.appleIcon
      //    >  .config.faviconICO
      //    >  .config.faviconPNG
      //    >  .rels
      //    .file()
      //    .filter()
      // 2022-08-28 PerfektesChaos@de.wikipedia
      var $apple,
          $head     =  $( window.document ).find( "head" ),
          $favicon,
          apple     =  false,
          i,
          k,
          light     =  apply,
          n         =  0,
          offer     =  false,
          png       =  false,
          s;
      if ( $head.length ) {
         if ( apply ) {
            png  =  [ "wmf", "f7" ];
            if ( this.site === "commonswiki" ) {
               apple  =  [ "commons", "70" ];
               png    =  [ "commons", "e7" ];
            } else if ( this.site === "metawiki" ) {
               png    =  [ "meta", "96" ];
            } else if ( this.site === "testwiki" ) {
               apple  =  [ "wikipedia", "f1" ];
               png    =  [ "testwiki", "b6" ];
            } else if ( this.site === "mediawikiwiki" ) {
               apple  =  [ "mediawiki", "b6" ];
               png    =  [ "mediawiki", "fa" ];
            } else if ( this.site.slice( -4 )  ===  "wiki" ) {
               apple  =  [ "wikipedia", "f1" ];
               if ( this.less ) {
                  png  =  [ "beta-wikipedia", "25" ];
               } else {
                  png  =  [ "wikipedia", "fb" ];
               }
            } else if ( this.site.slice( -9 )  ===  "wikibooks" ) {
               png    =  [ "wikibooks", "7e" ];
            } else if ( this.site.slice( -8 )  ===  "wikinews" ) {
               apple  =  [ "wikinews", "a9" ];
               png    =  [ "wikinews", "cc" ];
            } else if ( this.site.slice( -9 )  ===  "wikiquote" ) {
               png    =  [ "wikiquote", "cb" ];
            } else if ( this.site.slice( -10 )  ===  "wikisource" ) {
               png    =  [ "wikisource", "f4" ];
            } else if ( this.site.slice( -11 )  ===  "wikiversity" ) {
               png    =  [ "wikiversity", "95" ];
            } else if ( this.site.slice( -10 )  ===  "wiktionary" ) {
               apple  =  [ "wiktionary", "f8" ];
               png    =  [ "wiktionary", "c0" ];
            } else {
               apple  =  [ "wmf", "a9" ];
            }
         } else {
            if ( this.less  &&
                 typeof this.config.faviconPNG === "undefined" ) {
               this.config.favicon  =  this.file( "Favicon-",
                                                  "beta-wikipedia",
                                                  "14" );
            }
            light  =  this.config.favicon;
         }
         if ( apply ) {
            $apple  =  $head.find( "link" ).filter( function() {
                              return ( this.rel === "apple-touch-icon" );
                                                               } );
            if ( $apple.length === 1 ) {
               s  =  false;
               switch ( typeof this.config.appleIcon ) {
                  case "string" :
                     s  =  this.config.appleIcon;
                     break;
                  case "boolean" :
                     if ( ! this.config.appleIcon  ) {
                        break;
                     }   // fall through
                  default:
                     if ( apple ) {
                        s  =  this.file( "Apple-touch-icon-red-",
                                         apple[0],
                                         apple[1] );
                     }
               }   // switch   typeof .config.appleIcon
               if ( s ) {
                  $apple.detach();
                  $apple.attr( "href", s );
                  $head.append( $apple );
               }
            }
         }
         if ( light ) {
            $favicon  =  $head.find( "link" ).filter( PaneMk.filter );
            n  =  $favicon.length;
         }
         if ( n ) {
            if ( apply ) {
               s  =  false;
               switch ( typeof this.config.faviconPNG ) {
                  case "string" :
                     s  =  this.config.faviconPNG;
                     break;
                  case "boolean" :
                     if ( ! this.config.faviconPNG ) {
                        break;
                     }    // fall through
                  default:
                     s  =  this.file( "Favicon-red-",
                                      png[ 0 ],
                                      png[ 1 ] );
               }   // switch   typeof .config.faviconPNG
               offer  =  [  [ s, "image/png" ]  ];
               s      =  this.config.faviconICO;
               if ( s ) {
                  if ( typeof s === "string" ) {
                     offer.push( [ s, "image/x-icon" ] );
                  }
               }
            } else {
               s  =  this.config.favicon;
               if ( s ) {
                  if ( typeof s === "string" ) {
                     offer  =  [  [ s, null ]  ];
                     s  =  s.slice( -4 ).toLowerCase();
                     switch ( s ) {
                        case ".ico" :
                           offer[ 0 ][ 1 ]  =  "image/x-icon";
                           break;
                        case ".gif" :
                        case ".png" :
                           offer[ 0 ][ 1 ]  =  "image/" + s.substr(1);
                           break;
                     }   // switch   s.slice( -4 ).toLowerCase()
                  }
               }
            }
            if ( offer ) {
               for ( i = 0;  i < n;  i++ ) {
                  $favicon.eq( i ).detach();
               }   // for i
               if ( n > 1 ) {
                  $favicon  =  $favicon.eq( 0 );
               }
               for ( i = 0;  i < offer.length;  i++ ) {
                  $favicon.attr( "href",  offer[ i ][ 0 ] );
                  $favicon.attr( "type",  offer[ i ][ 1 ] );
                  for ( k = 0;  k < PaneMk.rels.length;  k++ ) {
                     $favicon.attr( "rel",  PaneMk.rels[ k ] );
                     $head.append( $favicon );
                     if ( k + 1  <  PaneMk.rels.length ) {
                        $favicon  =  $favicon.clone();
                     }
                  }   // for k
                  if ( i + 1  <  offer.length ) {
                     $favicon  =  $favicon.clone();
                  }
               }   // for i
            }
         }   // $favicon.length
      }   // $head
   };   // .favicon()



   PaneMk.file  =  function ( album, assign, access ) {
      // Retrieve PNG file URL at commons
      // Precondition:
      //    album   -- apple or favicon prefix
      //    assign  -- site
      //    access  -- hashcode octet
      // Postcondition:
      //    Returns URL
      // 2012-06-07 PerfektesChaos@de.wikipedia
      var r  =  "//upload.wikimedia.org/wikipedia/commons/"
                +  access.substr( 0, 1 )  +  "/"  +  access  +  "/"
                +  album  +  assign  +  ".png";
      return r;
   };   // .file()



   PaneMk.filter  =  function () {
      // Test <link> for appropriate rel=""
      // Precondition:
      //    this  -- <link>
      // Postcondition:
      //    Returns true if matching rel
      // Uses:
      //    this   (not PaneMk)
      //    >  .rels
      // 2022-08-28 PerfektesChaos@de.wikipedia
      var s = this.rel,
          i, r;
      for ( i = 0;  i < PaneMk.rels.length;  i++ ) {
         if ( PaneMk.rels[ i ] === s ) {
            r = true;
            break;   // for i
         }
      }   // for i
      return r;
   };   // .filter()



   PaneMk.fire  =  function () {
      // Startup
      // Uses:
      //    this
      //    >  .type
      //    >  .vsn
      //    >  .doc
      //    mw.loader.getState()
      //    mw.loader.state()
      //    mw.loader.using()
      //    mw.hook()
      //    (.prefs.fire)
      // 2018-08-24 PerfektesChaos@de.wikipedia
      var signature = "ext.gadget." + this.type,
          profile, rls;
      if ( mw.loader.getState( signature )  !==  "ready" ) {
         rls = { };
         rls[ signature ] = "ready";
         mw.loader.state( rls );
         mw.loader.using( [ "user",
                            "user.options",
                            "mediawiki.util",
                            "jquery.client" ],
                          this.prefs.fire );
         profile  =  { type: this.type,
                       vsn:  this.vsn,
                       doc:  this.doc };
         mw.hook( this.type ).fire( profile );
      }
   };   // .fire()



   PaneMk.flag  =  function ( action ) {
      // Put character together with document title
      // Precondition:
      //    action  -- "Diff", "History", "Links", "Vitally", "*"
      // Uses:
      //    this
      //    >  .config.leave
      //    >  .site
      //    >  .heads
      //    >  .config.charDiff
      //    >  .config.charHistory
      //    >  .config.charLinks
      //    >  .config.charVitally
      //    >  .config.chars
      //    >  .config.lowChar
      //    >  .config.lazy
      //    >  .config.rightleft
      //    >< document.title
      //     < .leave
      //    .facilitate()
      // Requires: JavaScript 1.3   String.fromCharCode()
      // 2013-01-01 PerfektesChaos@de.wikipedia
      var c       =  true,
          learn   =  true;
      if ( this.config.leave ) {
         this.facilitate();
      } else if ( this.heads.indexOf( this.site )  <  0 ) {
         c      =  this.config[ "char" + action ];
         learn  =  false;
      }
      if ( typeof this.config.chars === "boolean" ) {
         if ( c === true ) {
            c  =  this.config.chars;
         }
      }
      if ( c ) {
         if ( learn ) {
            c  =  this.config[ "char" + action ];
            if ( c === undefined ) {
               c  =  true;
            } else {
               learn  =  false;
            }
         }
         if ( learn ) {
            switch ( action ) {
               case "Diff" :
                  c  =  ( this.config.lowChar ? "±" :  916 );   // 'Δ'
                  break;
               case "History" :
                  c  =  ( this.config.lowChar ? "^" : 8595 );   // '↓'
                  break;
               case "Links" :
                  c  =  ( this.config.lowChar ? ">" : 8594 );   // '→'
                  break;
               case "Vitally" :
                  if ( c  &&  ! this.config.lazy ) {
                     c  =  false;
                     break;
                  }
                  c  =  "*";
                  break;
               default:
                  c  =  false;
            }   // switch action
         }
         if ( c ) {
            if ( ! this.config.leave ) {
               if ( action === "Links" ) {
                  this.facilitate();
               }
            }
            if ( typeof c === "number" ) {
               if ( c > 0 ) {
                  c  =  String.fromCharCode( c );
               }
            }
            if ( typeof c === "string" ) {
               if ( this.config.rightleft ) {
                  window.document.title  =  window.document.title
                                            + " " + c;
               } else {
                  c  =  c + " ";
                  if ( window.document.title.indexOf( c )  !==  0 ) {
                     window.document.title  =  c + window.document.title;
                  }
               }
            }
         }
      }
   };   // .flag()



   PaneMk.flip  =  function () {
      // Abbreviate document title by namespace shortcut, if any
      // Uses:
      //    >  this
      //    >  .nsN
      //    >  .env
      //    >< .config.shortcut
      //     < document.title
      // 2014-08-30 PerfektesChaos@de.wikipedia
      var s = false,
          e,
          o,
          $shortcut;
      if ( typeof this.config.shortcut !== "boolean" ) {
         this.config.shortcut  =  true;
      }
      if ( this.config.shortcut ) {
         $shortcut  =  $( ".shortcut" );
         if ( $shortcut.length ) {
            s  =  $shortcut.data( "shortcut" );
         }
      }
      if ( ! s  &&  this.nsN > 0 ) {
         o  =  this.env.wgNamespaceIds;
         for ( e in o ) {
            if ( e.length < 4 ) {   // file help talk user
               if ( o[ e ] === this.nsN ) {
                  s  =  e.toUpperCase() + ":" + this.env.wgTitle;
                  break;   // for e
               }   // first match
            }
         }   // for e
      }
      if ( s ) {
         window.document.title  =  s;
      }
   };   // .flip()



   PaneMk.fresh  =  function () {
      // Run paneMarker in this particular situation
      // Precondition:
      //    Page may be under loading, but not necessarily ready.
      //    .using( [ "user", "mediawiki.util" ] )
      // Uses:
      //    >  .opt.*
      //    >  .prefs.supply
      //    >  .jQuery.client
      //    >  .config.favicon
      //    >< this.*
      //     < .config.*
      //     < .env.*
      //     < .site
      //     < .nsN
      //     < .less
      //    mw.user.isAnon()
      //    .prefs.fetch()
      //    .flip()
      //    .flag()
      //    mw.config.get()
      //    mw.util.getParamValue()
      //    .favicon()
      //    .prefs.form()
      // 2016-10-08 PerfektesChaos@de.wikipedia
      var lenient  =  true,
          browser, s;
      if ( typeof this.opt === "object"  &&  this.opt ) {
         for ( s in this.opt ) {
            this.config[ s ]  =  this.opt[ s ];
         }   // for s in vals
      }
      if ( ! mw.user.isAnon()  &&
           mw.libs[ this.prefs.supply ] ) {
         this.prefs.fetch();
      }
      this.env   =  mw.config.get( [ "wgAction",
                                     "wgCanonicalSpecialPageName",
                                     "wgDBname",
                                     "wgNamespaceIds",
                                     "wgNamespaceNumber",
                                     "wgPageName",
                                     "wgRelevantPageName",
                                     "wgServer",
                                     "wgSiteName",
                                     "wgTitle" ] );
      this.site  =  this.env.wgDBname;
      this.nsN   =  this.env.wgNamespaceNumber;
      this.less  =  ( this.env.wgServer.indexOf( ".beta.wmflabs.org" )
                      > 0 );
      if ( ! this.config.lazy ) {
         if ( $.client ) {
            browser  =  $.client.profile();
            if ( browser.name === "msie" ) {
               this.config.lazy  =  ( browser.versionNumber < 10 );
            }
         }
      }
      this.flip();
      switch ( this.env.wgAction ) {
         case "edit" :
         case "submit" :
            if ( ! this.config.lazy ) {
               this.favicon( true );
               lenient  =  false;
            }
            this.flag( "Vitally" );
            break;
         case "history" :
            this.flag( "History" );
            break;
         case "view" :
            if ( mw.util.getParamValue( "diff" )  !==  null ) {
               this.flag( "Diff" );
            } else if ( this.nsN === -1 ) {
               switch ( this.env.wgCanonicalSpecialPageName ) {
                  case "Blankpage" :
                  case "Gadgets" :
                     if ( ! mw.user.isAnon()  &&
                          mw.libs[ this.prefs.supply ] ) {
                        this.prefs.form();
                     }
                     break;
                  case "Emailuser" :
                  case "Upload" :
                     if ( ! this.config.lazy ) {
                        this.favicon( true );
                        lenient  =  false;
                     }
                     this.flag( "Vitally" );
                     break;
                  case "Whatlinkshere" :
                     this.flag( "Links" );
                     break;
               }   // switch wgCanonicalSpecialPageName
            } else {
               this.flag( "*" );
            }
            break;
      }   // switch wgAction
      if ( lenient ) {
         if ( this.less  ||
              typeof this.config.favicon === "string" ) {
            this.favicon( false );
         }
      }
   };   // .fresh()



   PaneMk.furnish  =  function () {
      // Launch paneMarker from event queue
      // Uses:
      //    .fresh()
      // Remark: May be used as event handler -- 'this' is not accessed
      // 2012-06-07 PerfektesChaos@de.wikipedia
      PaneMk.fresh();
   };   // .furnish()



   PaneMk.fire();
}( window.mediaWiki, window.jQuery ) );



// Emacs
// Local Variables:
// coding: utf-8-dos
// fill-column: 80
// End:

/// EOF </nowiki>   paneMarker/d.js