User:PerfektesChaos/js/autoBackup/d.js

/// User:PerfektesChaos/js/autoBackup/d.js /// 2021-05-21 PerfektesChaos@de.wikipedia // Backup Wiki TEXTAREA in regular intervals // ResourceLoader: compatible; //   dependencies: user, //                 mediawiki.api, mediawiki.user, mediawiki.util /// Fingerprint: #0#0# /// @license: CC-by-sa/4.0 /// /* jshint forin:false                                                 */ /* global window:false, JSON:false                                    */ /* jshint bitwise:true, curly:true, eqeqeq:true, latedef:true, laxbreak:true, nocomma:true, strict:true, undef:true, unused:true           */

( function ( mw, $ ) {  "use strict";   var VERSION = -2.4,       BAK     = "autoBackup";   if ( typeof mw.libs[ BAK ]  !==  "object" ||  ! mw.libs[ BAK ] ) {      mw.libs[ BAK ] = { };   }   mw.libs[ BAK ].type  =  BAK;   BAK       = mw.libs[ BAK ];   BAK.doc   =  "w:en:User:PerfektesChaos/js/" + BAK + "";   BAK.vsn   =  VERSION;   BAK.cnf   =  { maxAge:    72,                  maxHist:    5,                  maxPages:  10,                  maxRev:     3,                  mid:        5,                  msec:     300000 };   BAK.disk  =  { self:  "AutoBackupPerfectChaos",                  stick: "newest|subject" };   BAK.gui   =  { };   BAK.util  =  { };   if ( ! typeof BAK.opt ||  typeof BAK.opt  !==  "object" ) {      BAK.opt  =  { };   }

// User options: //  .maxAge     number of full hours to remember //  .maxHist    number of history entries per revision to keep //  .maxPages   number of pages to handle simultaneously //  .maxRev     number of revisions to keep //  .mid        number of minutes for scheduled snapshots //  .portlet    add portlet link

/*   * 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 */

// Requires: JavaScript 1.3   (String.charCodeAt String.replace) //          ECMA        262-3 § 11.8.5  (string comparison operators) //          MediaWiki   1.18 (mw.libs, jQuery core)

/*  .pages wgArticleId  { } newest: timestamp subject: last known page name wgCurRevisionId  { } newest: timestamp history: [ [ timestamp-new,  snapshot ], [ timestamp-old,  snapshot ], [ timestamp-older, snapshot ], ] */

BAK.cnf.text =  { // 2014-09-23 PerfektesChaos@de.wikipedia "BAKself":      {"en": "AutoBackup", "de": "AutoBackup"}, "IntJSONparse": {"en": "Internal ERROR -- JSON.parse", "de": "Interner FEHLER -- JSON.parse"}, "NoLSavail":    {"en": "No localStorage available", "de": "Kein localStorage zugreifbar"}, "otherPages":   {"en": "Other Pages", "de": "Andere Seiten"}, "pending":      {"en": "Recover from pending abort?", "de": "Wiederherstellung nach Abbruch?"}, "setItemExcept": {"en": "ERROR setting localStorage", "de": "FEHLER beim Setzen im localStorage"}, "thisPage":     {"en": "this page",       //oldid "de": "diese Seite"}, "VanishedLS":   {"en": "ERROR: localStorage vanished", "de": "FEHLER: localStorage verschwunden"}, "WriteCrash":   {"en": "Crash on localStorage write attempt", "de": "Schreibversuch auf localStorage versagt"} };  // .cnf.text

BAK.cnf.translang =  { // 2012-11-30 PerfektesChaos@de.wikipedia "de" :       "de", "de-at" :    "de", "de-ch" :    "de", "de-formal" : "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" };  // .cnf.translang

BAK.cnf.favorite =  function  { // Guess user language // Uses: //   this //   >  .translang //   >< .slang //   mw.config.get // 2012-11-21 PerfektesChaos@de.wikipedia var s;     if ( ! this.slang ) { s =  mw.config.get( "wgUserLanguage" ).toLowerCase; s =  this.translang[ s ]; if ( s ) { this.slang =  s;         } else { this.slang =  "en"; }     }   };   // .cnf.favorite

BAK.cnf.feature =  function ( apply ) { // Wrap message text access for user language // Precondition: //   apply  -- text keyword // Postcondition: //   Return text closest to user language // Uses: //   this //   >  .cnf.text //   .cnf.favorite // Remark: To be replaced //        if one day ResourceLoader3 gives access to      //         gadget@translatewiki // 2012-11-04 PerfektesChaos@de.wikipedia var e, r;     if ( ! this.slang ) { this.favorite; }     e  =  this.text[ apply ]; if ( e ) { r =  e[ this.slang ]; if ( ! r ) { r =  e.en; if ( ! r ) { r =  "???" + apply + "???"; }        }      } else { r =  "***" + apply + "***"; }     return r;   };   // .cnf.feature

BAK.cnf.fetch =  function  { // Consider user options // Uses: //   this //   >  .opt //   >  .learn //   >< .cnf.load //    < .cnf.maxAge //    < .cnf.maxHist //    < .cnf.maxPages //    < .cnf.maxRev //    < .cnf.mid //    < .cnf.msec //   .gui.facility // 2012-11-30 PerfektesChaos@de.wikipedia if ( ! this.load ) { if ( typeof BAK.opt === "object" ) { if ( typeof BAK.opt.maxAge ===  "number" ) { if ( BAK.opt.maxAge >= 0 ) { this.maxAge =  Math.floor( BAK.opt.maxAge ); }           }            if ( typeof BAK.opt.maxHist  ===  "number" ) { if ( BAK.opt.maxHist >= 1 ) { this.maxHist =  Math.ceil( BAK.opt.maxHist ); }           }            if ( typeof BAK.opt.maxPages  ===  "number" ) { if ( BAK.opt.maxPages >= 1 ) { this.maxPages =  Math.ceil( BAK.opt.maxPages ); }           }            if ( typeof BAK.opt.maxRev  ===  "number" ) { if ( BAK.opt.maxRev >= 1 ) { this.maxRev =  Math.ceil( BAK.opt.maxRev ); }           }            if ( BAK.learn ) { if ( typeof BAK.opt.mid ===  "number" ) { if ( BAK.opt.mid > 0 ) { this.mid =  BAK.opt.mid; } else { this.mid =  false; }              }               if ( this.mid ) { this.msec =  this.mid * 60000; } else { this.msec =  false; }              if ( BAK.opt.portlet ) { BAK.gui.facility; }           }         }         this.load  =  true; }  };   // .cnf.fetch

BAK.disk.fetch =  function  { // Retrieve pages object from localStorage data // Postcondition: //   .pages is set to collection object, or false // Uses: //   this //   >  .disk.storage //   >< window.localStorage //    < .pages //    < .disk.lock //   .family //   JSON.parse //   .gui.flag // 2014-09-23 PerfektesChaos@de.wikipedia var s;     BAK.pages  =  false; if ( typeof window.localStorage ===  "object" ) { BAK.family; s =  window.localStorage.getItem( this.storage ); if ( s ) { try { BAK.pages =  JSON.parse( s ); } catch ( e ) { BAK.gui.flag( "IntJSONparse", false ); window.localStorage.setItem( this.storage, "" ); }        }      } else { BAK.gui.flag( "NoLSavail", false ); this.lock =  true; }  };   // .disk.fetch

BAK.disk.flush =  function  { // Put .pages object into localStorage // Postcondition: //   Clear empty .pages object // Uses: //   this //   >  .disk.storage //   >< window.localStorage //   >< .pages //   jQuery.toJSON //   .gui.flag // 2014-09-23 PerfektesChaos@de.wikipedia var store =  false, s;     if ( typeof BAK.pages === "object" ) { for ( s in BAK.pages ) { store =  true; break;  //  for s         }   //  for s      } if ( ! store ) { this.pages =  false; }     if ( typeof window.localStorage  ===  "object" ) { if ( BAK.pages ) { store =  JSON.stringify( BAK.pages ); if ( store === "{}") { store =  ""; }        } else { store =  ""; }        try { window.localStorage.setItem( this.storage, store ); s =  window.localStorage.getItem( this.storage ); if ( s !== store ) { BAK.gui.flag( "WriteCrash", false ); }        } catch ( e ) { BAK.gui.flag( "setItemExcept", false ); }        window.localStorage.setItem( this.storage, store ); } else { BAK.gui.flag( "VanishedLS", false ); }  };   // .disk.flush

BAK.gui.face =  function ( apply ) { // Prepare GUI message area for content // Precondition: //   apply  -- HTML message box content // Uses: //   this //   >  .$page //   >< .gui.$top //   >< .gui.$div //   >< .gui.$msg //   mw.util.addCSS //   jQuery //   jQuery.prepend //   jQuery.before //   jQuery.empty //   jQuery.attr //   jQuery.append // 2021-05-21 PerfektesChaos@de.wikipedia var s;     if ( ! this.$div ) { mw.util.addCSS( ".cn-fundraiser-banner,"                        + "#mw-js-message,"                         + "#siteNotice,"                         + "#fundraising\n"                         + "{display: none ! important;}" ); this.$div =  $( " " ); if ( ! this.$top ) { this.$top =  $( "#mw-content-text" ); if ( this.$top.length ) { this.$top.prepend( this.$div ); } else { this.$top =  $( ".mw-body-content" ).eq( 0 ); if ( ! this.$top.length ) { this.$top =  $( "#article" ); if ( ! this.$top.length ) { this.$top =  $( "#content" ); if ( ! this.$top.length ) { this.$top =  BAK.$page; }                 }               }               this.$top.before( this.$div ); }        }      }      if ( apply ) { if ( this.$msg ) { this.$msg.empty; } else { this.$msg =  $( " " ); this.$div.prepend( this.$msg ); }        if ( this.$msg ) { s =    "display:   block;" + "font-size: 120%;"; this.$msg.attr( "style", s ); this.$msg.append( apply ); }     } else { if ( this.$msg ) { this.$msg.attr( "style", "display: none;" ); }     }   };   // .gui.face

BAK.gui.facility =  function  { // Equip portlet with link to trigger // Precondition: //   document.ready // Uses: //   >< .cnf.self //   >  .type //   >  .vsn //   .cnf.feature //   mw.util.addPortletLink //   (.fresh) // 2017-01-04 PerfektesChaos@de.wikipedia var portlet, $portlet; if ( ! BAK.cnf.self ) { BAK.cnf.self =  BAK.cnf.feature( "BAKself" ); }     portlet   =  mw.util.addPortletLink( "p-cactions",                                           "#",                                           BAK.cnf.self,                                           "ca-" + BAK.type ); $portlet =  $( portlet ); $portlet.click( BAK.fresh ); $portlet.attr( { title: BAK.type + " " + BAK.vsn } ); };  // .gui.facility

BAK.gui.feed =  function  { // Retrieve content of text area; wpTextbox2 if edit conflict // Precondition: //   document.ready // Postcondition: //   Return textarea string; or false // Uses: //   this //   >  .$page //   >  .gui.$textarea //   >  wikEd //   >< .gui.leading //    < .gui.$editform //    < .gui.$textarea //   jQuery.find //   wikEd.UpdateTextarea // 2019-07-01 PerfektesChaos@de.wikipedia var r     =  false, $form, $ta; if ( this.leading ) {  // opening this.leading   =  false; this.$editform =  false; this.$textarea =  false; $form          =  BAK.$page.find( "#editform" ); if ( $form ) { $ta =  BAK.$page.find( "#wpTextbox2" ); if ( $ta.length ) {  // edit conflict r =  $ta.val; }           $ta  =  $form.find( "#wpTextbox1" ); if ( $ta.length ) {  // textarea if ( ! $ta.attr( "readonly" ) ) {  // modifiable this.$editform =  $form; this.$textarea =  $ta; }           }   // textarea }     }      if ( this.$textarea  &&  ! r ) {   // editing if ( window.wikEd             &&  typeof window.wikEd  ===  "object"              &&  ! window.wikEd.disabled              &&  window.wikEd.turnedOn              &&  window.wikEd.useWikEd              &&  window.wikEd.UpdateTextarea ) { window.wikEd.UpdateTextarea; }        r  =  this.$textarea.val; }  // editing return r;  };   // .gui.feed

BAK.gui.fence =  function ( apply ) { // Draw box around entire autoBAK rectangle //   apply  -- true if to be shown, or false to remove // Uses: //   this //   >  .gui.$div //   jQuery.attr // 2012-10-29 PerfektesChaos@de.wikipedia this.$div.attr( "style",                     ( apply  ? "border: solid 1px #606060;" + " border-bottom: solid 5px #606060;" + " padding: 0.5em;" + " margin-bottom: 2em;" : null ) ); };  // .gui.fence

BAK.gui.fiat =  function ( action, add ) { // Create button // Precondition: //   action  -- identifier //   add     -- data identifier // Postcondition: //   Return html string; or "" // 2012-10-29 PerfektesChaos@de.wikipedia var r;     switch ( action ) { case "ALL" : case "SHOW" : r =  "* "; break; case "DELETE" : r =  "X "; break; default: r =  ""; }  // switch action if ( r ) { r =  "\n   \n" + "" + r              + " "; }     return r;   };   // .gui.fiat

BAK.gui.fill =  function  { // Fill current page with offers // Uses: //   >  .pages //   >  .pageID //   >  .gui.$msg //   >  .disk.stick //   >< .gui.$box //   >< .revID //   jQuery.find //   jQuery.remove //   mw.config.get //   .gui.fence //   .gui.face // Remark: Used as event handler -- 'this' is not BAK // 2012-11-04 PerfektesChaos@de.wikipedia var page =  BAK.pages[ BAK.pageID ], revs =  false, i, n, s;     BAK.gui.$msg.find( "#autoBackupALL" ).remove; if ( BAK.gui.$box ) { BAK.gui.$box.remove; }     if ( page ) { BAK.gui.$box =  $( " " ); BAK.gui.$msg.after( BAK.gui.$box ); if ( ! BAK.revID ) { BAK.revID =  mw.config.get( "wgCurRevisionId" ); }        for ( i in page ) { if ( BAK.disk.stick.indexOf( i ) <  0 ) { s =  page[ i ].newest + " " + i;               if ( revs ) { revs.push( s ); } else { revs =  [ s ]; }           }         }   // for i in page n =  revs.length; revs.sort; for ( i = revs.length-1; i >= 0;  i-- ) { BAK.gui.folder( revs[ i ], page ); }  // for i-- BAK.gui.fence( true ); } else { BAK.gui.face( false ); } };   // .gui.fill

BAK.gui.finish =  function ( access ) { // Define additional hook on standard editform button // Precondition: //   access  -- button ID      // Uses: //   this //   >  .gui.$editform //   jQuery.find //   jQuery.click //   (.fresh) // 2012-10-29 PerfektesChaos@de.wikipedia var $btn =  this.$editform.find( "#" + access ); if ( $btn.length ) { $btn.click( BAK.fresh ); }  };   // .gui.finish

BAK.gui.flag =  function ( apply, action ) { // Display error message similar to mw.util.jsMessage MW 1.19 // Precondition: //   apply   -- text keyword //   action  -- identifier for button, or false if error case // Uses: //   this //   >  .cnf.slang //   >< .cnf.self //   .cnf.feature //   .gui.fiat //   .gui.face //   .gui.form // 2012-11-03 PerfektesChaos@de.wikipedia var s;     if ( ! BAK.cnf.self ) { BAK.cnf.self =  BAK.cnf.feature( "BAKself" ); }     s  =  BAK.cnf.feature( apply ); s =  "\n" + " "            +  BAK.cnf.self + "  \n" + (action ? ""                       : " ") + s            +  (action ? "\n"                       : " \n") + this.fiat( action ) + " \n"; this.face( s ); if ( action ) { this.form( action, "" ); }  };   // .gui.flag

BAK.gui.folder =  function ( access, album ) { // Insert single revision data into current page // Precondition: //   access  -- string  sortkey_space_symbol //   album   -- page revisions // Uses: //   this //   >  .gui.$box //   >  .revID //   jQuery.append //   jQuery.find //   .cnf.feature //   mw.config.get //   .gui.fiat //   .gui.form //   .util.focus //   .jQuery //   .gui.full // 2012-11-06 PerfektesChaos@de.wikipedia var e =  access.split( " " ), k =  e[ 1 ], s =  "autoBackupR" + k,          i, w, $div, $textarea; this.$box.append( "" ); $div =  this.$box.find( "#" + s ); if ( parseInt( k, 10 ) ===  BAK.revID ) { s =  k  +  " ("  +  BAK.cnf.feature( "thisPage" )  +  ")"; } else { s =  mw.config.get( "wgArticlePath" ); s =  "" + k + ""; }     s  =  " oldid=" + s + " "; s =  s  +  this.fiat( "DELETE", k ); //     $div.append( s ); this.form( "DELETE", k ); e =  album[ k ].history; for ( i = 0; i < e.length;  i++ ) { k =  e[ i ]; w =  k[ 1 ]; s =  " "  +  BAK.util.focus( k[ 0 ] )  +  " " + " " + w.length + " bytes "; $div.append( s ); $textarea = $( "" ); $textarea.val( w ); $div.append( $textarea ); }  // for i++ this.full; };  // .gui.folder

BAK.gui.form =  function ( action, add ) { // Provide click action for button // Precondition: //   action  -- identifier //   add     -- data distinguisher, or "" // Uses: //   this //   >  .gui.$top //   jQuery.find //   jQuery.click //   (.fixed) // 2012-11-04 PerfektesChaos@de.wikipedia var fun =  false, sign, $btn; switch ( action ) { case "ALL" : fun =  this.fill; break; case "DELETE" : sign        =  "DELETE" + add; fun         =  function  {  BAK.fixed( add );  }; BAK[ sign ] =  fun; break; case "SHOW" : break; }  // switch action if ( fun ) { $btn =  this.$top.find( "#autoBackup" + action + add ); if ( $btn.length ) { $btn.click( fun ); }     }   };   // .gui.form

BAK.gui.full =  function  { // Display otherPages offers on GUI // Uses: //   this //   >  .pages //   >  .pageID //   >  .gui.$div //   >< .gui.reSpace //   jQuery.empty //   .cnf.feature //   jQuery.append //   jQuery.find //   mw.config.get //   mw.util.wikiUrlencode //   .util.focus //   .gui.fence // 2012-11-06 PerfektesChaos@de.wikipedia var e, g, i, p, s, u, $x; if ( BAK.pages ) { g =  false; s =  "" + BAK.pageID; for ( i in BAK.pages ) { if ( i !== s ) { p =  BAK.pages[ i ]; if ( p.subject ) { if ( ! this.reSpace ) { this.reSpace =  new RegExp(" +", "g"); }                 e  =  " "  +  p.subject.replace( this.reSpace, "_" ); } else { e =  ""; }              e  =  p.newest + " " + i + e;               if ( g ) { g.push( e ); } else { g =  [ e ]; }           }         }   // for i         if ( g ) { s =  " " + BAK.cnf.feature( "otherPages" ) + " \n" + ""; this.$div.append( s ); $x =  this.$div.find( "#autoBackupPageList" ); u  =  "= 0;  i-- ) { e =  g[ i ]; p =  e.split( " " ); s =  p[ 1 ]; if ( s.substr( 0, 2 ) ===  "0_" ) { s =  s.substr( 2 ); s =  u + "title=" + mw.util.wikiUrlencode( s ) + "&redirect=no'>" + s + " "; } else { s =  "curid=" + u + "curid=" + s + "'>" + s + " "; if ( p[ 2 ] ) { s =  s  +  "("  +  p[ 2 ].replace( /_/g, " " )                                               .replace( /"  +  s  +  BAK.util.focus( p[ 0 ] ) + ""; $x.append( s ); }  // for i-- }        this.fence( true ); }  };   // .gui.full

BAK.gui.further =  function  { // Try to define additional hooks on buttons // Uses: //   this //   >  .gui.$editform //   .gui.finish //   (.fresh) // 2012-11-03 PerfektesChaos@de.wikipedia if ( this.$editform ) {  // editing this.finish( "wpDiff" ); this.finish( "wpPreview" ); this.finish( "wpSave" ); this.$editform.find( ".mw-summary" ).focusin( BAK.fresh ); }  };   // .gui.further

BAK.util.figure =  function ( ask ) { // Is ask a positive number or zero? // Precondition: //   ask  -- string // Postcondition: //   Return true iff ask contains only digits // 2012-10-29 PerfektesChaos@de.wikipedia return (/^[0-9]+$/).test( ask ); };  // .util.figure

BAK.util.flat =  function ( album, amount, arrange ) { // Reduce number of entries in object; remove lowest sorted // Precondition: //   album    -- object to be limited //   amount   -- maximum number of components within album //   arrange  -- Array with sort keys and IDs, or false //               every entry is string  sortkey_space_symbol // 2012-11-07 PerfektesChaos@de.wikipedia var e, i, k, s;     if ( arrange ) { k =  arrange.length - amount; if ( k > 0 ) { arrange.sort; for ( i = 0; i <= k;  i++ ) { e =  arrange[ i ]; s =  e.split( " " ); delete album[ s[ 1 ] ]; }  // for i         } }  };   // .util.flat

BAK.util.focus =  function ( appoint ) { // Format ISO 8601 UTC timestamp according to local time // Precondition: //   appoint  -- Date object // Postcondition: //   Return ISO 8601 string in local time zone // Uses: //   this //   >  mw.user //   >  mw.user.options //   >< .cnf.justify //  .format // 2014-03-28 PerfektesChaos@de.wikipedia var r =  appoint + " UTC", s =  typeof BAK.cnf.justify, g;     if ( s !== "boolean"  &&  s !== "number" ) { BAK.cnf.justify =  false; if ( typeof mw.user ===  "object" ) { if ( mw.user.options ) { s =  mw.user.options.get( "timecorrection" ); if ( s ) {  // "System|120" g =  /\|?([0-9]+)$/.exec( s ); if ( g ) { BAK.cnf.justify =  parseInt( g[1], 10 ); }              }            }         }      }      if ( BAK.cnf.justify === 0 ) { r =  appoint; } else if ( typeof BAK.cnf.justify ===  "number" ) { g =  /^([0-9]+)-([01][0-9])-([0-3][0-9])T([0-2][0-9]):([0-6][0-9]):([0-6][0-9])$/.exec( appoint ); if ( g ) { r =  Date.UTC( parseInt( g[1], 10 ),                            parseInt( g[2], 10 )  -  1,                            parseInt( g[3], 10 ),                            parseInt( g[4], 10 ),                            parseInt( g[5], 10 )  +  BAK.cnf.justify,                            parseInt( g[6], 10 ) ); r =  this.format( new Date( r ) ); }     }		return  r;   };   // .util.focus

BAK.util.format =  function ( appoint ) { // Format timestamp according to ISO 8601 // Precondition: //   appoint  -- Date object // Postcondition: //   Return ISO 8601 string in local time zone // 2012-10-29 PerfektesChaos@de.wikipedia var r =  appoint.getUTCFullYear + "-", i =  appoint.getUTCMonth + 1; if ( i < 10 ) { i	= "0" + i;      } r =  r + i + "-"; i =  appoint.getUTCDate; if ( i < 10 ) { i	= "0" + i;      } r =  r + i + "T"; i =  appoint.getUTCHours; if ( i < 10 ) { i	= "0" + i;      } r =  r + i + ":"; i =  appoint.getUTCMinutes; if ( i < 10 ) { i	= "0" + i;      } r =  r + i + ":"; i =  appoint.getUTCSeconds; if ( i < 10 ) { i	= "0" + i;      } return r + i;   };   // .util.format

BAK.family =  function  { // Establish distinguishing page identifier // Postcondition: //   .pageID is available // Uses: //   >< .pageID //    < .pageName //   mw.config.get // 2014-07-05 PerfektesChaos@de.wikipedia var env; if ( ! this.pageID ) { env =  mw.config.get( [ "wgArticleId",                                  "wgPageName" ] ); this.pageID   =  env.wgArticleId; this.pageName =  env.wgPageName; if ( this.pageID === 0 ) { this.pageID   =  "0_" + this.pageName.replace( /~/, "%7E" ); }     }   };   // .family

BAK.fast =  function  { // Check sessionStorage for quick page id test on view // Postcondition: //   Return true iff localStorage should be evaluated // Uses: //   this //   >  .disk.storage //   >  window.sessionStorage //   >< .pageID //   >< .pageName //    < .later //   mw.config.get // 2013-08-23 PerfektesChaos@de.wikipedia var r =  true, s =  window.sessionStorage.getItem( this.disk.storage ), q;     if ( s ) { r =  ( s.length > 1 ); if ( r ) { if ( ! this.pageID ) { this.pageID =  mw.config.get( "wgArticleId" ); }           r  =  ( s.indexOf( "~" + this.pageID + "~" )  >=  0 ); if ( ! r ) { if ( s.indexOf( "~0_" ) >=  0  ) { if ( ! BAK.pageName ) { BAK.pageName =  mw.config.get( "wgPageName" ); }                 q  =  "0_" + BAK.pageName.replace( /~/, "%7E" ); if ( s.indexOf( "~" + q + "~" ) >=  0 ) { r          =  true; this.later =  true; }              }            }         }      }		return  r;   };   // .fast

BAK.filter =  function  { // Cleanup repository, discard old and supernumerous entries // Precondition: //   .pages is an object // Uses: //   this //   >  .disk.stick //   >  .cnf.maxAge //   >  .cnf.maxRev //   >  .cnf.maxPages //   >  .gui.$div //   >  .disk.storage //   >< .pages //   >< .now //   >< window.sessionStorage //   .cnf.fetch //   .util.format //   .util.flat //   .gui.fence // Requires: ECMA 262-3 § 11.8.5 (string comparison operators) // 2013-08-23 PerfektesChaos@de.wikipedia var lazy  =  true, store =  "~", hist, page, pid, revs, rid, sift, stamp; this.cnf.fetch; if ( this.cnf.maxAge ) { if ( ! this.now ) { this.now =  new Date; }        sift  =  Date.UTC( this.now.getUTCFullYear,                            this.now.getUTCMonth,                            this.now.getUTCDate,                            this.now.getUTCHours - this.cnf.maxAge,                            0,                            0 ); sift =  this.util.format( new Date( sift ) ); } else { sift =  "1970-01-01T00:00:00"; }     for ( pid in this.pages ) { page =  this.pages[ pid ]; if ( page.newest < sift ) { delete this.pages[ pid ]; } else { revs =  false; for ( rid in page ) { if ( this.disk.stick.indexOf( rid ) <  0 ) { stamp =  page[ rid ].newest; if ( stamp > sift ) { stamp =  stamp + " " + rid; if ( revs ) { revs.push( stamp ); } else { revs =  [ stamp ]; }                 } else { delete page[ rid ]; }              }            }   // for rid in page if ( revs ) { page =  page.newest + " " + pid; if ( hist ) { hist.push( page ); } else { hist =  [ page ]; }              this.util.flat( page, this.cnf.maxRev, revs ); store =  store + pid + "~"; lazy  =  false; } else { delete this.pages[ pid ]; }        }      }   // for pid in .pages this.util.flat( this.pages, this.cnf.maxPages, hist ); window.sessionStorage.setItem( this.disk.storage, store ); if ( lazy &&  this.gui.$div ) { this.gui.fence; }  };   // .filter

BAK.finalize =  function ( already, add ) { // Remove trailing whitespace and compare to recent text // Precondition: //   already  -- old text, or empty if new //   add      -- new text // Postcondition: //   Return new text iff significant change, or false // Requires: JavaScript 1.3  String.charCodeAt // 2012-10-29 PerfektesChaos@de.wikipedia var k =  add.length, r =  add, i;     for ( i = k;  i > 0;  i-- ) { if ( add.charCodeAt( i - 1 ) >  32 ) { break;  // for i-- }     }   // for i-- if ( i > 1 ) { if ( i < k ) { r =  add.substr( 0, i ); }        if ( already ) { if ( i === already.length ) { if ( r === already ) { r =  false; }           }         }      } else { r =  false; }     return r;   };   // .finalize

BAK.find =  function  { // Launch API query for revisions of the same user // Uses: //   this //   >  .pageID //   >  .later //   >  .pageName //   mw.Api //   mw.util.wikiUrlencode //   mw.config.get //   (.founder) //   (.found) // 2015-07-07 PerfektesChaos@de.wikipedia var q =  new mw.Api, w =  { action:     "query", "continue": "",           // TEMP ?? pageids:   this.pageID, prop:      "revisions", rvlimit:   3, rvuser:    mw.util.wikiUrlencode(                                          mw.config.get( "wgUserName" ) ) };     if ( this.later ) { delete w.pageids; w.titles =  this.pageName; q.get( w ).done( this.founder ); } else { q.get( w ).done( this.found ); }  };   // .find

BAK.first =  function  { // Opening edit page; check for pending attempts // Precondition: //   Page in edit::edit mode // Uses: //   this //   >  .pages //   >  mw.user //   >  mw.user.options //   >  .pageID //    < .gui.leading //    < .livePreview //   .disk.fetch //   .find //   jQuery.bind //   mw.hook //   (.fresh) // 2019-06-17 PerfektesChaos@de.wikipedia this.$editform =  false; this.disk.fetch; if ( this.pages ) { if ( this.pages[ this.pageID ] ) { this.find; }     }      if ( typeof mw.user  ===  "object" ) { if ( mw.user.options &&              mw.user.options.get( "uselivepreview" ) ) { this.livePreview =  true; $( mw ).bind( "LivePreviewDone", this.fresh ); }     }      this.gui.leading  =  true; mw.hook( "wikipage.content" ).add( this.fresh ); };  // .first

BAK.fixed =  function ( apply ) { // Remove current page revision from storage and GUI // Precondition: //   apply  -- revision ID      // Uses: //   this //   >  .gui.$top //   >  .pageID //   >< .pages //    < .disk.launch //   .flush //   .gui.fence //   .gui.face // 2012-11-04 PerfektesChaos@de.wikipedia var p;     this.gui.$top.find( "#autoBackupR" + apply ).remove; if ( this.pages ) { p =  this.pages[ BAK.pageID ]; if ( p ) { if ( p[ apply ] ) { delete p[ apply ]; this.disk.launch =  true; this.flush; if ( ! this.pages ) { this.gui.fence( false ); } else if ( ! this.pages[ BAK.pageID ] ) { this.gui.face( false ); }           }         }      }   };   // .fixed

BAK.flush =  function  { // Write repository // Uses: //   this //   >  .disk.launch //   >  .pages //   .filter //   .disk.flush // 2012-10-29 PerfektesChaos@de.wikipedia if ( this.disk.launch ) { if ( this.pages ) { this.filter; }        this.disk.flush; }  };   // .flush

BAK.folder =  function  { // API: Display list of links to all other pages on current page // Uses: //   >  .gui.$div //   >  .gui.$msg //   >< .cnf.self //   .cnf.feature //   jQuery.prepend //   .gui.face //   .gui.fill //   .gui.filter //   .gui.full // Remark: May be used as event handler -- 'this' is not BAK // 2012-11-30 PerfektesChaos@de.wikipedia BAK.gui.face; if ( ! BAK.gui.$msg ) { if ( ! BAK.cnf.self ) { BAK.cnf.self =   BAK.cnf.feature( "BAKself" ); }        BAK.gui.$div.prepend( " " + BAK.cnf.self + " " ); }     BAK.gui.fill; BAK.gui.filter; BAK.gui.full; };  // .folder

BAK.follow =  function  { // Check whether current page displays saved edit // Precondition: //   Page in view mode // Uses: //   this //   >  .pages //   >  .later //   >  .pageID //    < .revID //   .disk.fetch //   .find //   mw.config.get //   .gui.flag // 2012-11-30 PerfektesChaos@de.wikipedia var h, i, p;     this.disk.fetch; if ( this.later ) { this.find; } else if ( this.pages ) { p =  this.pages[ this.pageID ]; if ( p ) { this.revID =  mw.config.get( "wgCurRevisionId" ); for ( h in p ) { i =  parseInt( h, 10 ); if ( i ) { if ( this.revID > i ) { this.find; break;  // for h                  } else if ( this.revID === i ) { this.gui.flag( "pending", "ALL" ); break;  // for h                  } }           }   // for h in p         } }  };   // .follow

BAK.found =  function ( arrived ) { // Postprocess in page view after ajax request // Precondition: //   arrived  -- JSON result of ajax query //   Previously it has been detected that revID is open snapshot. // Uses: //   >  .pageID //   >  .revID //   >< .pages //    < .disk.launch //   .flush // Remark: Used as event handler -- 'this' is not BAK // 2012-10-29 PerfektesChaos@de.wikipedia var query  =  ( typeof arrived   ===  "object" ), learnt =  false, i;     if ( query ) { query =  arrived.query; if ( query ) { query =  query.pages[ BAK.pageID ]; if ( query ) { query =  query.revisions; if ( query ) { for ( i = 0; i < query.length;  i++ ) { if ( query[i].revid === BAK.revID ) { learnt =  true; delete BAK.pages[ BAK.pageID ]; BAK.disk.launch =  true; break; //  for i                     } }  //  for i               } }        }      }      BAK.flush; if ( ! learnt ) { BAK.gui.flag( "pending", "ALL" ); }  };   // .found

BAK.founder =  function ( arrived ) { // Postprocess a created page in page view after ajax request // Precondition: //   arrived  -- JSON result of ajax query //   Previously it has been detected revID=0 was open snapshot. // Uses: //   >  .pageName //   >< .pages //    < .pageID //    < .disk.launch //   .flush // Remark: Used as event handler -- 'this' is not BAK // 2012-12-02 PerfektesChaos@de.wikipedia var query  =  ( typeof arrived   ===  "object" ), learnt =  false; if ( query ) { query =  arrived.query; if ( query ) { query =  query.pages[ BAK.pageID ]; if ( query ) { BAK.pageID =  "0_" + BAK.pageName.replace( /~/, "%7E" ); delete BAK.pages[ BAK.pageID ]; BAK.disk.launch =  true; learnt =  true; }        }      }      BAK.flush; if ( ! learnt ) { BAK.gui.flag( "pending", "ALL" ); }  };   // .founder

BAK.fresh =  function  { // API: Add snapshot to storage if content changed // Precondition: //   Start of preview/diff mode, or time-triggered // Uses: //   >  .pageID //   >  .pageName; //   >  .cnf.maxHist //   >  .cnf.msec //   >< .timeoutID //    < .revID //    < .now //    < .disk.launch //   .gui.feed //   .family //   mw.config.get //   .disk.fetch //   .finalize //   .util.format //   .cnf.fetch //   .flush // Remark: Used as event handler -- 'this' is not BAK // 2013-02-17 PerfektesChaos@de.wikipedia var shot  =  BAK.gui.feed, page, revs, stamp; if ( shot ) { BAK.family; BAK.revID =  mw.config.get( "wgCurRevisionId" ); if ( ! BAK.pages ) { BAK.disk.fetch; }        if ( ! BAK.pages ) { BAK.pages =  { }; }        if ( ! BAK.pages[ BAK.pageID ] ) { BAK.pages[ BAK.pageID ] =  { }; }        page  =  BAK.pages[ BAK.pageID ]; if ( ! page[ BAK.revID ] ) { page[ BAK.revID ] =  { }; }        revs  =  page[ BAK.revID ].history; if ( revs ) { shot =  BAK.finalize( revs[0][1], shot ); if ( shot ) { BAK.now =  new Date; stamp   =  BAK.util.format( BAK.now ); BAK.cnf.fetch; if ( revs.length >= BAK.cnf.maxHist - 1 ) { revs.pop; }              revs.unshift( [ stamp, shot ] ); }        } else { shot =  BAK.finalize( false, shot ); if ( shot ) { BAK.now =  new Date; stamp   =  BAK.util.format( BAK.now ); revs    =  [ [ stamp, shot ] ]; }        }         if ( shot ) { page[ BAK.revID ].history =  revs; page[ BAK.revID ].newest  =  stamp; page.newest               =  stamp; page.subject              =  BAK.pageName; BAK.pages[ BAK.pageID ]   =  page; BAK.disk.launch           =  true; BAK.flush; }        if ( BAK.cnf.msec ) { if ( BAK.timeoutID ) { window.clearTimeout( BAK.timeoutID ); }           BAK.timeoutID  =  window.setTimeout( BAK.fresh,                                                 BAK.cnf.msec ); }     }   };   // .fresh

BAK.further =  function  { // Additional triggers in edit mode, to be called only once // Uses: //   this //   >  .gui.$editform //   >  .cnf.msec //   >< .learnt //   .gui.further //   .cnf.fetch //   (.fresh) // 2012-10-29 PerfektesChaos@de.wikipedia if ( ! this.learnt ) {  // only once this.learnt  =  true; if ( this.gui.$editform ) {  // editing this.gui.further; this.cnf.fetch; if ( this.cnf.msec ) { window.setTimeout( this.fresh, this.cnf.msec ); }        }      }   };   // .further

BAK.fire =  function  { // Start page processing; check whether situation is interesting // Uses: //   >  .disk.self //    < .disk.storage //    < .learn //    < .gui.leading //   mw.config.get //   .follow //   mw.util.getParamValue //   mw.hook //   (.firing) // Remark: Used as event handler -- 'this' is not BAK // 2017-01-04 PerfektesChaos@de.wikipedia var env =  mw.config.get( [ "wgAction",                                   "wgIsArticle",                                   "wgUserName" ] ); BAK.disk.storage =  BAK.disk.self + " " + env.wgUserName; if ( env.wgIsArticle ) { if ( env.wgAction === "view" &&              BAK.fast ) { BAK.follow; }     } else { BAK.start =  mw.util.getParamValue( "action" ); if ( BAK.start ) { BAK.learn =  true; if ( "|edit|submit|".indexOf( BAK.start ) >  0 ) { mw.hook( "wikipage.content" ).add( BAK.firing ); }        }      }   };   // .fire

BAK.firing =  function ( $all ) { // Start content processing // Precondition: //   $all  -- mw.util.$content // Uses: //   >  .start //    < .gui.leading //   .fast //   .fresh //   .further //   .first // Remark: Used as event handler -- 'this' is not BAK // 2019-07-01 PerfektesChaos@de.wikipedia BAK.$page = $all; switch ( BAK.start ) { case "submit" : BAK.gui.leading =  true; BAK.fresh; BAK.further; break; case "edit" : BAK.first; BAK.further; }  // switch .start };  // .firing

function first { // Initiate resource loading // Uses: //   >  .type //    < .error //   mw.loader.getState //   mw.loader.using //   mw.loader.state //   (.fire) // 2018-08-24 PerfektesChaos@de.wikipedia var signature = "ext.gadget." + BAK.type, rls; if ( mw.loader.getState( signature ) !==  "ready" ) { rls = { }; rls[ signature ] = "ready"; mw.loader.state( rls ); if ( mw.config.get( "wgNamespaceNumber" ) >=  0 ) { mw.loader.using( [ "user",                              "mediawiki.api",                               "mediawiki.user",                               "mediawiki.util" ],                             BAK.fire ); }     }   }   // first

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

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

/// EOF  autoBackup/d.js