Jump to content

User:PerfektesChaos/js/browserStorageManager/d.js

From Wikipedia, the free encyclopedia
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
/// User:PerfektesChaos/js/browserStorageManager/d.js
/// 2020-12-10 PerfektesChaos@de.wikipedia
// Show and manipulate web storage and cookies.
// ResourceLoader:  compatible;
//                  dependencies:
//                     user, user.options, mediawiki.user, mediawiki.util
/// Fingerprint: #0#0#
/// @license GPL [//www.mediawiki.org/w/COPYING] (+GFDL, LGPL, CC-BY-SA)
/// <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 vsn  =  -2.3,
       BSM  =  "browserStorageManager",
       Max  =  100,
       Bag  =  [ "localStorage", "sessionStorage", "cookies" ];



   // Requires: JavaScript  1.4             ("in")
   //           ECMA        262-3 § 11.8.5  (string comparison operators)



   // ///////////////////////////////////////////////////////////////////



   function bb_mw() {
      // Build functions for mediawiki environment, and execute
      // Precondition:
      // Uses:
      //    >  Bag
      //    >  Max
      //    >< BSM
      //     < domfuns
      // 2014-01-11
      var config, domfuns, gui, lang, mod, prego,
          Key  =  80;
      if ( typeof mw.libs[ BSM ]  !==  "object"
           ||   ! mw.libs[ BSM ] ) {
         mw.libs[ BSM ]  =  { };

      }
      mw.libs[ BSM ].type  =  BSM;
      BSM                  =  mw.libs[ BSM ];
      if ( BSM.vsn ) {
         return;
      }
      BSM.vsn  =  vsn;
      config   =  { support: "User:PerfektesChaos/js/" + BSM.type };
      gui      =  { };
      lang     =  { };
      mod      =  { };
      BSM.doc  =  "[[w:en:" + config.support + "]]";
      domfuns  =  [ "key", "length",
                    "clear",  "getItem",  "setItem",  "removeItem" ];



      lang.texts   =  {
         // 2014-01-04 PerfektesChaos@de.wikipedia
         "#bytes":    {"en": "bytes",
                       "de": "Bytes"},
         "#chars":    {"en": "chars",
                       "de": "Zeichen"},
         "#content":  {"en": "content...",
                       "de": "Inhalt..."},
         "#id":       {"en": "ID",
                       "de": "ID"},
         "!abort":    {"en": "abort",
                       "de": "Abbruch"},
         "!delete":   {"en": "delete entry",
                       "de": "Eintrag löschen"},
         "!edit":     {"en": "Modify text",
                       "de": "Text bearbeiten"},
         "!fresh":    {"en": "refresh table",
                       "de": "Tabelle aktualisieren"},
         "!new":      {"en": "new entry",
                       "de": "Neuer Eintrag"},
         "!save":     {"en": "save new text",
                       "de": "Text speichern"},
         "^show":     {"en": "Browser-Storage-Manager",
                       "de": "Browser-Storage-Manager"},
         "^suffix":   {"en": "&#8211; Show and manipulate"
                             + " web/DOM storage and cookies.",
                       "de": "&#8211; Zeige und bearbeite"
                             + " Web/DOM Storage und Cookies."},
         "^^key":     {"en": "Text height (default: "
                              + Key + " %)",
                       "de": "Schriftgröße (Vorgabe: "
                              + Key + " %)"},
         "^^max":     {"en": "Text length limit (default: "
                              + Max + ")",
                       "de": "Maximale Textlänge (Vorgabe: "
                              + Max + ")"},
         "^^portlet": {"en": "Create portlet link",
                       "de": "Portlet-Link einfügen"}
      };   // lang.texts



      lang.user  =  {
         "de" :   "de",
         "als" :  "de",
         "bar" :  "de",
         "dsb" :  "de",
         "frr" :  "de",
         "gsw" :  "de",
         "hsb" :  "de",
         "ksh" :  "de",
         "lb" :   "de",
         "nds" :  "de",
         "pdc" :  "de",
         "pdt" :  "de",
         "pfl" :  "de",
         "sli" :  "de",
         "stq" :  "de",
         "vmf" :  "de"
      };   // lang.user   2012-12-19 PerfektesChaos@de.wikipedia



      function fire() {
         // Complete initialization after code loading
         // Uses:
         //    >  config.signature
         //    >  config.rLoader
         //     < config.link
         //     < gui.ltr
         //    config.fetch()
         //    config.form()
         //    mw.loader.state()
         // Remark: May be used as event handler -- 'this' not accessed
         // 2020-12-10 PerfektesChaos@de.wikipedia
         config.fetch();
         if ( mw.config.get( "wgCanonicalSpecialPageName" )
                                                      === "Blankpage" ) {
            if ( prego ) {
               config.form();
            }
         } else {
            config.link  =  true;
         }
         gui.ltr  = ( $( "html" ).attr( "dir" )  !==  "rtl" );
         if ( typeof config.rLoader  !==  "object"
              ||   ! config.rLoader ) {
            config.rLoader = { };
         }
         config.rLoader[ config.signature ] = "ready";
         mw.loader.state( config.rLoader );
      }   // fire()



      function first() {
         // Start actions on current page
         // Uses:
         //    >  .key
         //    config.fence()
         //    config.font()
         //    config.fire()
         //    mw.loader.using()
         //    (config.firing)
         // 2015-09-29 PerfektesChaos@de.wikipedia
         var key  =  false;
         if ( typeof BSM.key  ===  "number" ) {
            key  =  BSM.key;
         }
         config.fence();
         config.font( key );
         config.fire();
         mw.loader.using( [ "user",
                            "user.options",
                            "mediawiki.user",
                            "mediawiki.util" ],
                          config.firing );
      }   // first()



      config.favorite  =  function () {
         // Detect support page in user language
         // Uses:
         //    this
         //    >  lang.slang
         //    >  config.support
         //    lang.favorite()
         // 2013-12-14 PerfektesChaos@de.wikipedia
         var r;
         lang.favorite();
         switch ( lang.slang ) {
            case "de" :
               r  =  "de.wikipedia.org";
               break;
            default:
               r  =  "en.wikipedia.org";
         }   // switch .slang
         return "//" + r + "/wiki/" + this.support;
      };   // config.favorite()



      config.fence  =  function ( apply ) {
         // Retrieve or check and memorize default text length
         // Precondition:
         //    apply  -- number with text length, to be stored, or nil
         // Postcondition:
         //    Returns valid number
         // Uses:
         //    this
         //    >  .max
         //    >  Max
         //    >< config.max
         // 2013-12-17 PerfektesChaos@de.wikipedia
         var n;
         if ( typeof config.max  !==  "number" ) {
            if ( typeof BSM.max  ===  "number" ) {
               if ( BSM.max >= 10  &&  BSM.max <= 1024 ) {
                  config.max  =  BSM.max;
               }
            }
            if ( typeof config.max  !==  "number" ) {
               config.max  =  Max;
            }
         }
         if ( apply ) {
            switch ( typeof apply ) {
               case "number" :
                  n  =  apply;
                  break;
               case "string" :
                  n  =  parseInt( apply, 10 );
                  break;
               default:
                  n  =  false;
            }   // switch typeof apply
            if ( n ) {
               if ( n >= 10  &&  n <= 1024 ) {
                  config.max  =  n;
               }
            }
         }
         return config.max;
      };   // config.fence()



      config.fetch  =  function () {
         // Retrieve preferences and overwrite presets
         // Uses:
         //    this
         //    >  config.supply
         //    >  .type
         //     < prego
         //     < gui.$link
         //    config.fence()
         //    config.font()
         //    gui.facility()
         //    mw.libs.preferencesGadgetOptions.fetch()
         // 2014-01-02 PerfektesChaos@de.wikipedia
         var link, vals;
         if ( typeof BSM.portlet  ===  "boolean" ) {
            link  =  BSM.portlet;
         } else {
            link  =  true;
         }
         if ( mw.user.isAnon() ) {
            prego  =  false;
         } else {
            prego  =  mw.libs[ this.supply ];
         }
         if ( prego ) {
            vals   =  prego.fetch( BSM.type );
            if ( vals   &&   typeof vals  ===  "object" ) {
               config.fence( vals.max );
               config.font( vals.key );
               if ( typeof vals.portlet  ===  "boolean" ) {
                  link  =  vals.portlet;
               }
            }
         }
         if ( link ) {
            gui.$link  =  true;
            gui.facility( true );
         }
      };   // config.fetch()



      config.filled  =  function ( assigned ) {
         // New preferences have been sent to server
         // Precondition:
         //    assigned  -- object with final form data, to be stored
         // Uses:
         //    config.fence()
         //    gui.facility()
         // Remark: May be used as event handler -- 'this' not accessed
         // 2013-12-17 PerfektesChaos@de.wikipedia
         if ( typeof assigned  ===  "object"   &&   assigned ) {
            config.fence( assigned.max );
            gui.facility( assigned.portlet );
         }
      };   // config.filled()



      config.fire  =  function () {
         // Prepare ResourceLoader availability
         // Postcondition:
         //    loader.load requested, if not yet defined
         // Uses:
         //    this
         //     < config.supply
         //     < config.signature
         //     < config.rLoader
         //    mw.loader.getState()
         //    mw.loader.state()
         //    mw.loader.load()
         // 2020-12-10 PerfektesChaos@de.wikipedia
         config.supply     =  "preferencesGadgetOptions";
         config.signature  =  "ext.gadget." + config.supply;
         if ( ! mw.loader.getState( config.signature ) ) {
            config.rLoader = { };
            config.rLoader[ config.signature ] = "loading";
            mw.loader.state( config.rLoader );
            mw.loader.load( "https://en.wikipedia.org"
                                   + "/w/index.php?title="
                            + "User:PerfektesChaos/js/"
                            + config.supply
                            + "/r.js"
                            + "&action=raw&bcache=1&maxage=604806"
                            + "&ctype=text/javascript",
                            "text/javascript" );
         }
      };   // config.fire()



      config.firing  =  function () {
         // Intermediate loading process step
         // Precondition:
         //    MW ressources are available
         // Uses:
         //    >  config.supply
         //    mw.hook()
         //    (fire)
         // Remark: Used as event handler -- 'this' is not config
         // 2015-09-29 PerfektesChaos@de.wikipedia
         mw.hook( config.supply + ".ready" ).add( fire );
      };   // config.firing()



      config.font  =  function ( apply ) {
         // Retrieve or check and memorize font size
         // Precondition:
         //    apply  -- number with font size, to be stored, or nil
         // Postcondition:
         //    Returns valid number
         // Uses:
         //    this
         //    >  Key
         //    >< config.key
         // 2013-12-17 PerfektesChaos@de.wikipedia
         var n;
         if ( typeof config.key  !==  "number" ) {
            config.key  =  Key;
         }
         if ( apply ) {
            switch ( typeof apply ) {
               case "number" :
                  n  =  apply;
                  break;
               case "string" :
                  n  =  parseInt( apply, 10 );
                  break;
               default:
                  n  =  false;
            }   // switch typeof apply
            if ( n ) {
               if ( n >= 65  &&  n <= 100 ) {
                  config.key  =  n;
               }
            }
         }
         return config.key;
      };   // config.font()



      config.form  =  function () {
         // Equip Special:Blankpage page with entry and form
         // Uses:
         //    >  .type
         //    >  lang.slang
         //    >  lang.texts
         //    >  .max
         //    >  gui.$link
         //    lang.favorite()
         //    config.favorite()
         //    prego.form()
         //    (.config.filled)
         //    mw.libs.preferencesGadgetOptions.form()
         // 2013-12-14 PerfektesChaos@de.wikipedia
         var dialog,
             sl,
             text    =  lang.texts;
         lang.favorite();
         sl  =  lang.slang;
         dialog  =  { script:  BSM.type,
                      show:    text[ "^show" ][ sl ],
                      support: this.favorite(),
                      suffix:  text[ "^suffix" ][ sl ],
                      filled:  this.filled,
                      opts:    [ { signature: "max",
                                   type:      "text",
                                   show:      text[ "^^max" ][ sl ],
                                   val:       config.fence()
                                 },
                                 { signature: "portlet",
                                   type:      "checkbox",
                                   show:      text[ "^^portlet" ][ sl ],
                                   val:       ( gui.$link ? true
                                                          : false )
                                 },
                                 { signature: "key",
                                   type:      "text",
                                   show:      text[ "^^key" ][ sl ],
                                   val:       config.font()
                                 }
                               ] };
         prego.form( dialog );
      };   // config.form()



      gui.facility  =  function ( add ) {
         // Remove previous portlet link, and create new one
         // Precondition:
         //    add  -- create new portlet link
         // Uses:
         //    this
         //    >< gui.$link
         //    gui.factory()
         // 2013-12-17 PerfektesChaos@de.wikipedia
         if ( typeof this.$link  ===  "object" ) {
            this.$link.hide();
            this.$link  =  false;
         }
         if ( add ) {
            $( this.factory );
         }
      };   // gui.facility()



      gui.factory  =  function () {
         // Insert portlet link
         // Uses:
         //    >  lang.slang
         //    >  lang.texts
         //    >  .portlet
         //            -- object with portlet requirements
         //               .scope     -- container id
         //               .show      -- label
         //               .shortcut  -- access key
         //               .stick     -- id to sort before in container
         //    >  .type
         //    >  .vsn
         //     < gui.$link
         //    lang.favorite()
         //    jQuery.trim()
         //    gui.facility()
         //    mw.util.addPortletLink()
         //    (gui.fiat)
         // Remark: May be used as event handler -- 'this' not accessed
         // 2013-12-17 PerfektesChaos@de.wikipedia
         var dom,
             s,
             show,
             signal,
             scope     =  "p-tb",
             shortcut  =  null,
             stick     =  null;
         lang.favorite();
         show  =  lang.texts[ "^show" ][ lang.slang ];
         if ( typeof BSM.portlet  ===  "object"   &&   BSM.portlet ) {
            s  =  BSM.portlet.scope;
            if ( typeof s  ===  "string" ) {
               s  =  $.trim( s );
               if ( s.length ) {
                  scope  =  s;
               }
            }
            s  =  BSM.portlet.shortcut;
            if ( typeof s  ===  "string" ) {
               s  =  $.trim( s );
               if ( s.length ) {
                  shortcut  =  s;
               }
            }
            s  =  BSM.portlet.show;
            if ( typeof s  ===  "string" ) {
               s  =  $.trim( s );
               if ( s.length ) {
                  show  =  s;
               }
            }
            s  =  BSM.portlet.stick;
            if ( typeof s  ===  "string" ) {
               s  =  $.trim( s );
               if ( s.length ) {
                  stick  =  s;
               }
            }
         }
         signal  =  show + "\n"
                    + BSM.type + " " + BSM.vsn;
         dom     =  mw.util.addPortletLink( scope,
                                            "#",
                                            show,
                                            "t-" + BSM.type,
                                            signal,
                                            shortcut,
                                            stick );
         gui.$link  =  $( dom ).find ( "a" );
         gui.$link.click( gui.fiat );
      };   // gui.factory()



      gui.fiat  =  function () {
         // Equip current page with tables
         // Precondition:
         //    document ready
         // Uses:
         //    >  Bag
         //    >  .type
         //    >  config.key
         //    >  config.link
         //    >  prego
         //    >< gui.$wrapper
         //     < gui.$table
         //     < mod.makes
         //    gui.fresh()
         //    lang.favorite()
         //    prego.$button()
         //    gui.folder()
         //    jQuery.tablesorter()
         //    gui.facility()
         //    (gui.features)
         // Remark: May be used as event handler -- 'this' not accessed
         // 2014-01-29 PerfektesChaos@de.wikipedia
         var i, $btn, selector;
         if ( gui.$wrapper ) {
            for ( i = 0;  i < 3;  i++ ) {
               gui.fresh( Bag[ i ] );
            }   // for i
         } else {
            selector  =  "#" + BSM.type;
            lang.favorite();
            gui.$wrapper  =  $( "<div />" );
            gui.$wrapper.attr( "id", selector );
            gui.$wrapper.css( { "border":        "solid 2px #006400",
                                "font-size":     config.key + "%",
                                "margin-bottom": "2em",
                                "padding":       "1em"
                              } );
            if ( config.link && prego ) {
               $btn  =  prego.$button( BSM.type );
               $btn.css( { "float"         : "right",
                           "vertical-align": "top" } );
               gui.$wrapper.prepend( $btn );
            }
            gui.$table  =  { };
            mod.makes   =  { };
            for ( i = 0;  i < 3;  i++ ) {
               gui.folder( Bag[ i ] );
            }   // for i
            $( "#firstHeading" ).before( gui.$wrapper );
            mw.loader.using( [ "jquery.tablesorter" ],
                             function () {
                                 for ( i = 0;  i < 3;  i++ ) {
                                    gui.$table[ Bag[ i ] ].tablesorter();
                                 }   // for i
                             } );
            gui.facility( false );
         }
      };   // gui.fiat()



      gui.folder  =  function ( about ) {
         // Create initial table
         // Uses:
         //    >  gui.$wrapper
         //    >  lang.slang
         //    >  lang.texts
         //    >< gui.$table
         //    gui.furnish()
         //    gui.fresh()
         //    (mod.flip)
         //    (gui.fresh)
         // 2013-12-17 PerfektesChaos@de.wikipedia
         var s         =  ( about === "cookies"  ?  "#bytes"
                                                 :  "#chars" ),
             $btn,
             $caption  =  $( "<caption />" ),
             $span     =  $( "<span />" ),
             $table    =  $( "<table />" ),
             $tbody    =  $( "<tbody />" ),
             $td       =  $( "<td />" ),
             $tfoot    =  $( "<tfoot />" ),
             $th       =  $( "<th />" ),
             $thead    =  $( "<thead />" ),
             $tr       =  $( "<tr />" ),
             $tf1      =  $td.clone(),
             $tf2      =  $th.clone(),
             $tf3      =  $td.clone(),
             $th1      =  $th.clone(),
             $th2      =  $th.clone(),
             $th3      =  $th.clone(),
             $trf      =  $tr.clone(),
             $trh      =  $tr.clone();
         $table.attr( "class", "wikitable sortable" );
         $caption.text( about );
         $table.append( $caption );
         $th1.text( lang.texts[ "#id" ][ lang.slang ] );
         $th2.text( lang.texts[ s ][ lang.slang ] );
         $th3.append( $span.clone() );
         $trh.append( $th1 );
         $trh.append( $th2 );
         $trh.append( $th3 );
         $thead.append( $trh );
         $table.append( $thead );
         $table.append( $tbody );
         $btn  =  gui.furnish( "$btnNew" );
         $btn.click( function () {  mod.flip( about, -1, true );  } );
         $tf1.append( $btn );
         $tf2.css( { "font-family": "monospace",
                     "text-align":  "right" } );
         $tf2.append( $span );
         $btn  =  gui.furnish( "$btnRefresh" );
         $btn.click( function () {   gui.fresh( about );   } );
         $tf3.append( $btn );
         $trf.append( $tf1 );
         $trf.append( $tf2 );
         $trf.append( $tf3 );
         $tfoot.append( $trf );
         $table.append( $tfoot );
         gui.$wrapper.append( $table );
         gui.$table[ about ]  =  $table;
         gui.fresh( about );
      };   // gui.folder()



      gui.fresh  =  function ( about ) {
         // Refill table body
         // Precondition:
         //    about  -- 1 of "localStorage", "sessionStorage", "cookies"
         // Postcondition:
         //    Returns Array with sorted assignments, or false
         // Uses:
         //    >  config.max
         //    >  lang.slang
         //    >  lang.texts
         //    >  gui.$table
         //    >  domfuns
         //    fresh()
         //    jQuery.inArray()
         //    gui.furnish()
         //    (gui.fresh_1)
         //    (gui.fresh_2)
         // 2014-01-20 PerfektesChaos@de.wikipedia
         var el, i, m, n, $btn, $td1d, $td2d, $td3d, $trd, $txt,
             light   =  ( about === "cookies" ),
             r       =  fresh( about, config.max ),
             s       =  lang.texts[ "#content" ][ lang.slang ]
                        + " (" + config.max + ")",
             $span   =  $( "<span />" ),
             $table  =  gui.$table[ about ],
             $tbody  =  $table.children( "tbody" ),
             $tfoot  =  $table.children( "tfoot" ),
             $thead  =  $table.children( "thead" ),
             $td     =  $( "<td />" ),
             $tr     =  $( "<tr />" ),
             $td1    =  $td.clone(),
             $td2    =  $td.clone(),
             $td3    =  $td.clone();
         $tbody.empty();
         $td1.css( { "font-family": "monospace" } );
         $td2.css( { "font-family": "monospace",
                     "text-align":  "right" } );
         if ( r ) {
            m  =  0;
            n  =  r.length;
            for ( i = 0;  i < n;  i++ ) {
               el  =  r[ i ];
               if ( light   ||
                    $.inArray( el.id, domfuns )  <  0 ) {
                  $td1d  =  $td1.clone();
                  $td2d  =  $td2.clone();
                  $td3d  =  $td3.clone();
                  $td1d.text( el.id );
                  $btn  =  gui.furnish( "$btnDelete" );
                  $btn.on( "click",
                           {  about: about,
                              id:    el.id },
                           gui.fresh_1 );
                  $td1d.append( $btn );
                  $td2d.text( el.n );
                  if ( el.n <= config.max ) {
                     $txt  =  $span.clone();
                     $txt.text( el.v );
                     $td3d.append( $txt );
                     $btn  =  gui.furnish( "$btnEdit" );
                     $btn.on( "click",
                              { about: about,
                                item: i,
                                id:   el.id,
                                val:  el.v },
                              gui.fresh_2 );
                     $td3d.append( $btn );
                  } else {
                     $td3d.text( el.v );
                  }
                  $trd  =  $tr.clone();
                  $trd.append( $td1d );
                  $trd.append( $td2d );
                  $trd.append( $td3d );
                  $tbody.append( $trd );
                  m  +=  el.n;
               }
            }   // for i
         } else {
            m  =  "";
         }
         $tr   =  $thead.children( "tr" );
         $txt  =  $tr.children( "th" ).eq( 2 ).children( "span" );
         $txt.text( s );
         $tr  =  $tfoot.children( "tr" );
         $tr.children( "td" ).children( "*" ).toggle( true );
         $txt  =  $tr.children( "th" ).children( "span" );
         $txt.text( m );
         mod.makes[ about ]  =  0;
         return  r;
      };   // gui.fresh()



      gui.fresh_1  =  function ( event ) {
         // [delete] button has been clicked.
         // Precondition:
         //    event
         // Uses:
         //    mod.flush()
         // 2013-12-17 PerfektesChaos@de.wikipedia
         mod.flush( event.data.about, event.data.id );
      };   // gui.fresh_1()



      gui.fresh_2  =  function ( event ) {
         // [edit] button has been clicked.
         // Precondition:
         //    event
         // Uses:
         //    mod.flip()
         // 2013-12-17 PerfektesChaos@de.wikipedia
         mod.flip( event.data.about,
                   event.data.item,
                   true,
                   event.data.id,
                   event.data.val );
      };   // gui.fresh_2()



      gui.furnish  =  function ( acquire ) {
         // Generate GUI elements.
         // Precondition:
         //    acquire  -- request
         //                "$btnAbort",
         //                "$btnDelete",
         //                "$btnEdit",
         //                "$btnNew",
         //                "$btnRefresh",
         //                "$btnStore"    -- button
         // Uses:
         //    this
         //    >  gui.ltr
         //    >  .type
         //    >  lang.slang
         //    lang.favorite()
         // 2013-12-17 PerfektesChaos@de.wikipedia
         var show, $e, $v;
         if ( ! this[ acquire ] ) {
            lang.favorite();
            $e  =  $( "<button>" );
            $e.attr( "type", "button" );
            switch ( acquire ) {
               case "$btnAbort" :
                  $e.css( { "color":       "#FF0000",
                            "font-weight": "bolder",
                            "float":       ( this.ltr ? "right"
                                                      : "left" )
                          } );
                  $e.attr( "class", BSM.type + "-tmp" );
                  $v  =  $( "<span />" );
                  $v.text( "X" );
                  show  =  "!abort";
                  break;
               case "$btnDelete" :
                  $e.css( { "color":       "#FF0000",
                            "font-family": "sans-serif",
                            "font-weight": "bolder",
                            "float":       ( this.ltr ? "right"
                                                      : "left" )
                          } );
                  $e.css( ( this.ltr ? "margin-left" : "margin-right" ),
                          "1em" );
                  $v  =  $( "<span />" );
                  $v.text( String.fromCharCode( 8722 ) );
                  show  =  "!delete";
                  break;
               case "$btnEdit" :
                  $e.css( { "color":       "#00A000",
                            "font-weight": "bolder",
                            "float":       ( this.ltr ? "right"
                                                      : "left" )
                          } );
                  $e.css( ( this.ltr ? "margin-left" : "margin-right" ),
                          "1em" );
                  $v  =  $( "<span />" );
                  $v.text( String.fromCharCode( 8660 ) );
                  show  =  "!edit";
                  break;
               case "$btnNew" :
                  $e.css( { "color":       "#00A000",
                            "font-size":   "150%",
                            "font-weight": "bolder"
                          } );
                  $v  =  $( "<span />" );
                  $v.text( "+" );
                  show  =  "!new";
                  break;
               case "$btnRefresh" :
                  $e.css( { "color":       "#00A000",
                            "font-size":   "150%",
                            "font-weight": "bolder",
                            "float":       ( this.ltr ? "left"
                                                      : "right" )
                          } );
                  $v  =  $( "<span />" );
                  $v.text( "*" );
                  show  =  "!fresh";
                  break;
               case "$btnSave" :
                  $e.css( { "color":       "#00A000",
                            "font-weight": "bolder",
                            "float":       ( this.ltr ? "right"
                                                      : "left" )
                          } );
                  $e.attr( "class", BSM.type + "-tmp" );
                  $v  =  $( "<span />" );
                  $v.text( "+" );
                  show  =  "!save";
                  break;
            }   // switch acquire
            $e.attr( "title",  lang.texts[ show ][ lang.slang ] );
            $e.append( $v );
            this[ acquire ]  =  $e;
         }
         return  this[ acquire ].clone();
      };   // gui.furnish()



      lang.favorite  =  function () {
         // Guess user language
         // Uses:
         //    this
         //    >  lang.user
         //    >< lang.slang
         //    mw.config.get()
         // 2012-12-19 PerfektesChaos@de.wikipedia
         var s;
         if ( ! this.slang ) {
            s  =  mw.config.get( "wgUserLanguage" ).toLowerCase();
            if ( s.length > 4 ) {   // Remove RFC 1766 subtag from code
               if ( s.charCodeAt( 2 )  ===  45 ) {   // '-'
                  s  =  s.substr( 0, 2 );
               }
            }
            s           =  this.user[ s ];
            this.slang  =  ( s ? s : "en" );
         }
      };   // lang.favorite()



      mod.flip  =  function ( about, access, active, assign, alter ) {
         // [edit] or [new] button has been clicked; or saved/canceled.
         // Precondition:
         //    about   -- 1 of "localStorage","sessionStorage","cookies"
         //    access  -- number of item; -1 for new
         //    active  -- true: edit;  false: saved/canceled, restore
         //    assign  -- identifier
         //    alter   -- text to be changed, if any
         // Uses:
         //    >  config.max
         //    >  .type
         //    >< mod.makes
         //    gui.furnish()
         //    (mod.flip)
         //    (mod.forward)
         // 2013-12-29 PerfektesChaos@de.wikipedia
         var start,
             $btnAbort, $btnSave, $eternity, $input, $lbl, $tbody, $td,
             $table  =  gui.$table[ about ],
             $tfoot  =  $table.children( "tfoot" ),
             $tr     =  $tfoot.children( "tr" ).children( "td" ),
             $new    =  $tr.eq( 0 ).children( "button" ),
             learn   =  ( access < 0 ),
             light   =  ( about === "cookies" ),
             slip    =  BSM.type + "-tmp";
         if ( learn ) {
            $td  =  $tr.eq( 1 );
            $td.children( "button" ).toggle( ! active );
            $td  =  $tr.eq( 0 );
            start  =  "";
         } else {
            $tbody  =  $table.children( "tbody" );
            $tr     =  $tbody.children( "tr" ).eq( access );
            $td     =  $tr.children( "td" ).eq( 2 );
            $td.children( "*" ).toggle( ! active );
            start  =  $td.children( "span" ).text();
         }
         if ( active ) {
            mod.makes[ about ]  =  mod.makes[ about ] + 1;
            $input  =  $( "<input />" );
            $input.attr( { size:  Math.ceil( config.max / 2 ),
                           type:  "text" } );
            $input.val( start );
            $td.append( $input );
            $btnAbort  =  gui.furnish( "$btnAbort" );
            $btnAbort.click( function () {
                                        mod.flip( about, access, false );
                                         } );
            $td.append( $btnAbort );
            $btnSave  =  gui.furnish( "$btnSave" );
            $btnSave.click( function () {   var life;
                                            if ( learn && light ) {
                                               life  =  $eternity.val();
                                            } else {
                                               life  =  false;
                                            }
                                            mod.forward( about,
                                                         assign,
                                                         $input.val(),
                                                         access,
                                                         life );
                                        } );
            $td.append( $btnSave );
            if ( learn && light ) {
               $eternity  =  $( "<input />" );
               $eternity.attr( { "class": slip,
                                 "float": "right",
                                 "id":    slip + "-eternity",
                                 "type":  "checkbox" } );
               $lbl  =  $( "<label />" );
               $lbl.attr( { "class": slip,
                            "float": "right"   ,
                            "for":   slip + "-eternity" } );
               $lbl.text( String.fromCharCode( 8734 ) );
               $td.append( $lbl );
               $td.append( $eternity );
            }
         } else {
            mod.makes[ about ]  =  mod.makes[ about ] - 1;
            $td.children( "input" ).remove();
            $td.children( "." + slip ).remove();
            if ( typeof alter  ===  "string" ) {
               $td.children( "span" ).text( alter );
               $td  =  $tr.children( "td" ).eq( 1 );
               $td.text( alter.length );
            }
         }
         $new.toggle( ! ( active  ||  mod.makes[ about ] ) );
      };   // mod.flip()



      mod.flush  =  function ( about, assign ) {
         // [delete] button has been clicked.
         // Precondition:
         //    about   -- 1 of "localStorage","sessionStorage","cookies"
         //    assign  -- identifier
         // Uses:
         //    jQuery.removeCookie()  :: mediawiki.cookie
         //    gui.fresh()
         // 2014-08-01 PerfektesChaos@de.wikipedia
         switch ( about ) {
            case "localStorage" :
            case "sessionStorage" :
               window[ about ].removeItem( assign );
               break;
            case "cookies" :
               $.removeCookie( assign,
                               { path: "/", expires: 0 } );
               break;
         }   // switch about
         gui.fresh( about );
      };   // mod.flush()



      mod.forward  =  function ( about, assign, alter, access, ages ) {
         // [save] button has been clicked.
         // Precondition:
         //    about   -- 1 of "localStorage","sessionStorage","cookies"
         //    assign  -- identifier
         //    alter   -- text to be stored
         //    access  -- number of item; -1 for new
         //    ages    -- make cookie persistent
         // Uses:
         //    >  domfuns
         //    jQuery.trim()
         //    jQuery.inArray()
         //    jQuery.cookie()
         //    gui.fresh()
         //    mod.flip()
         // 2014-01-20 PerfektesChaos@de.wikipedia
         var opts, story,
             subject  =  assign,
             learn    =  ( access < 0 ),
             storage  =  about;
         if ( learn ) {
            story    =  "";
            subject  =  $.trim( alter );
            if ( subject === "" ) {
               storage  =  "";
            } else {
               switch ( storage ) {
                  case "localStorage" :
                  case "sessionStorage" :
                     if ( window[ storage ].getItem( subject )   ||
                          $.inArray( assign, domfuns )  >=  0 ) {
                        storage  =  "";
                     }
                     break;
                  case "cookies" :
                     if ( mw.cookie.get( subject ) ) {
                        storage  =  "";
                     }
                     break;
               }   // switch storage
            }
         } else {
            story    =  alter;
         }
         if ( storage !== "" ) {
            switch ( storage ) {
               case "localStorage" :
               case "sessionStorage" :
                  window[ storage ].setItem( subject, story );
                  break;
               case "cookies" :
                  opts  =  { path: "/" };
                  if ( ages ) {
                     opts.expires  =  1000;
                  }
                  mw.cookie.set( subject, story, opts );
                  break;
            }   // switch storage
            mod.flip( storage, access, false, false, story );
            if ( learn ) {
               gui.fresh( storage );
            }
         }
      };   // mod.forward()



      BSM.fiat  =  function () {
         // Equip current page with tables
         // Uses:
         //    mw.loader.using()
         //    (gui.fiat)
         // 2013-12-17 PerfektesChaos@de.wikipedia
         mw.loader.using( [ "mediawiki.cookie" ],
                          function () {
                             $( gui.fiat );
                          } );
      };   // .fiat()



      BSM.fresh  =  function ( about ) {
         // Rebuild table content
         // Precondition:
         //    about  -- 1 of "localStorage", "sessionStorage", "cookies"
         // Postcondition:
         //    Returns Array with sorted assignments, or false
         // Uses:
         //    >  gui.$wrapper
         //    >  config.max
         //    gui.fresh()
         //    .fiat()
         // Remark: May be used as event handler -- 'this' not accessed
         // 2013-12-28 PerfektesChaos@de.wikipedia
         var r;
         if ( gui.$wrapper ) {
            r  =  gui.fresh( about );
         } else {
            BSM.fiat();
            r  =  fresh( about, config.max );
         }
         return r;
      };   // .fresh()



      first();



   }   // bb_mw()



   // ///////////////////////////////////////////////////////////////////



   function bb_nowiki() {
      // Build functions for non-mediawiki environment, and execute
      // Uses:
      //    >  Bag
      //    >  BSM
      //    >  Max
      // 2013-12-17



      function failure( all ) {
         // No console available
         // Precondition:
         //    all  -- object with all kinds of data
         // 2013-12-17
         var k, s,
             story  =  BSM + ": No console";
         for ( k = 0;  k < 2;  k++ ) {
            s      =  Bag[ k ];
            story  =  story + "\r\n" + s + ": " + figure( all[ s ] );
         }   // for k
         window.alert( story );
      }   // failure()



      function fancy( all ) {
         // Try console tables
         // Precondition:
         //    all  -- object with all kinds of data
         // Uses:
         //    >  BSM
         // Throws:
         //    Unavailable
         // 2013-12-17
         var k, o, s;
         window.console.info( "== " + BSM + " ==" );
         if ( window.opera ) {
            throw window.opera;
         }
         if ( typeof window.console.table  ===  "object" ) {
            for ( k = 0;  k < 3;  k++ ) {
               s  =  Bag[ k ];
               o  =  all[ s ];
               window.console.info( s + "   " + figure( o ) );
               if ( o ) {
                  window.console.table.call( window.console, o );
               }
            }   // for k
         } else {
            throw { };
         }
      }   // fancy()



      function figure( assembly ) {
         // Retrieve total item count and character length of web storage
         // Precondition:
         //    assembly  -- Array with web storage info, or false
         // Postcondition:
         //    Returns string with numbers
         // 2013-12-17 PerfektesChaos@de.wikipedia
         var i, m, n, r;
         if ( assembly ) {
            n  =  assembly.length;
            m  =  0;
            for ( i = 0;  i < n;  i++ ) {
               m  +=  assembly[ i ].n;
            }   // for i
            r  =  "#" + n + "   -> " + m;
         } else {
            r  =  "./.";
         }
         return  r;
      }   // figure()



      function fire() {
         // Run on load
         // Uses:
         //    >  Bag
         //    >  Max
         //    fresh()
         //    fancy()
         //    flat()
         //    failure()
         // 2013-12-17 PerfektesChaos@de.wikipedia
         var i, s,
             env  =  { };
         for ( i = 0;  i < 3;  i++ ) {
            s  =  Bag[ i ];
            env[ s ]  =  fresh( s, Max );
         }   // for i
         if ( window.console ) {
            try {
               fancy( env );
            } catch (e) {
               try {
                  window.console.log( env );
               } catch (ex) {
                  flat( env );
               }
            }
         } else {
            failure( env );
         }
      }   // fire()



      function flat( all ) {
         // console available, but no object log function
         // Precondition:
         //    all  -- object with all kinds of data
         // 2013-12-17
         var e, i, k, n, o, s;
         for ( k = 0;  k < 3;  k++ ) {
            s  =  Bag[ k ];
            o  =  all[ s ];
            n  =  o.length;
            window.console.info( "=== " + s + " ===" );
            for ( i = 0;  i < n;  i++ ) {
               e  =  o[ i ];
               window.console.info( e.id + " = " + e.v );
            }   // for i
         }   // for k
      }   // flat()



      fire();



   }   // bb_nowiki()



   // ///////////////////////////////////////////////////////////////////



   function fetch() {
      // Retrieve cookies
      // Postcondition:
      //    Returns Array with cookie assignments, or false
      // 2013-12-17
      var g, i, m, n, p, r, s, v;
      if ( typeof window.document.cookie  ===  "string" ) {
         r  =  [ ];
         g  =  window.document.cookie.split( "; " );
         n  =  g.length;
         for ( i = 0;  i < n;  i++ ) {
            p  =  g[ i ].split( "=" );
            s  =  decodeURIComponent( p[ 0 ] );
            v  =  p[ 1 ];
            if ( v ) {
               m  =  v.length;
               v  =  decodeURIComponent( v );
            } else {
               m  =  0;
               v  =  "";
            }
            r.push( { id: s,
                      n:  m,
                      v:  v } );
         }   // for i
      } else {
         r  =  false;
      }
      return  r;
   }   // fetch()



   function find( access, allow ) {
      // Retrieve object with web storage
      // Precondition:
      //    access  -- either "localStorage" or "sessionStorage"
      //    allow   -- maximum number of characters
      // Postcondition:
      //    Returns Array with storage assignments, or false
      // 2013-12-17 PerfektesChaos@de.wikipedia
      var n, r, s, store,
          o  =  window[ access ];
      if ( typeof o  ===  "object" ) {
         r  =  [ ];
         for ( s in o ) {
            store  =  o.getItem( s );
            if ( typeof store  ===  "string" ) {
               n  =  store.length;
               if ( n > allow ) {
                  store  =  store.substr( 0, allow );
               }
            } else {
               n      =  0;
               store  =  "";
            }
            r.push( { id: s,
                      n:  n,
                      v:  store } );
         }   // for s in o
      } else {
         r  =  false;
      }
      return  r;
   }   // find()



   function fresh( about, allow ) {
      // Rebuild content
      // Precondition:
      //    about   -- one of "localStorage", "sessionStorage", "cookies"
      //    allow   -- maximum number of characters
      // Uses:
      //    fetch()
      //    find()
      // Postcondition:
      //    Returns Array with sorted assignments, or false
      //            Each element is an object { id, n, v }
      //                                id  -- string
      //                                n   -- number of bytes/characters
      //                                v   -- truncated value
      // 2013-12-17 PerfektesChaos@de.wikipedia
      var r,
          f  =  function( a,  b ) {   return  (   a.id.toLowerCase()
                                                < b.id.toLowerCase()
                                                ?  -1
                                                :  +1 );
                                 };
      switch ( about ) {
         case "cookies" :
            r  =  fetch();
            break;
         case "localStorage" :
         case "sessionStorage" :
            r  =  find( about, allow );
            break;
         default:
            r  =  false;
      }   // switch about
      if ( r ) {
         r.sort( f );
      }
      return  r;
   }   // fresh()



   if ( typeof mw  ===  "object"   &&   $ ) {
      bb_mw();
   } else {
      bb_nowiki();
   }



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



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

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