User:PerfektesChaos/js/autoBackup/d.js
Appearance
Code that you insert on this page could contain malicious content capable of compromising your account. If you import a script from another page with "importScript", "mw.loader.load", "iusc", or "lusc", take note that this causes you to dynamically load a remote script, which could be changed by others. Editors are responsible for all edits and actions they perform, including by scripts. User scripts are not centrally supported and may malfunction or become inoperable due to software changes. User:PerfektesChaos/js/autoBackup/d. |
/// User:PerfektesChaos/js/autoBackup/d.js
/// 2021-05-21 [email protected]
// 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
/// <nowiki>
/* 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 [email protected]
"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 [email protected]
"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 [email protected]
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 [email protected]
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 [email protected]
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 [email protected]
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 [email protected]
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 [email protected]
var s;
if ( ! this.$div ) {
mw.util.addCSS( ".cn-fundraiser-banner,"
+ "#mw-js-message,"
+ "#siteNotice,"
+ "#fundraising\n"
+ "{display: none ! important;}" );
this.$div = $( "<div class='AutoBackupDiv' />" );
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 = $( "<div class='AutoBackupMsg' />" );
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 [email protected]
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 [email protected]
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 [email protected]
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 [email protected]
var r;
switch ( action ) {
case "ALL" :
case "SHOW" :
r = "<span"
+ " style='font-size: 200%;"
+ " font-weight: bold;"
+ " color: #008000;'>*</span>";
break;
case "DELETE" :
r = "<span"
+ " style='font-size: 200%;"
+ " font-weight: bold;"
+ " color: #FF0000;'>X</span>";
break;
default:
r = "";
} // switch action
if ( r ) {
r = "\n \n"
+ "<button type='button'"
+ " id='autoBackup" + action
+ ( add ? add : "" ) + "'>"
+ r
+ "</button>";
}
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 [email protected]
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 = $( "<div />" );
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 [email protected]
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 [email protected]
var s;
if ( ! BAK.cnf.self ) {
BAK.cnf.self = BAK.cnf.feature( "BAKself" );
}
s = BAK.cnf.feature( apply );
s = "<div id='autoBackup'"
+ " style='border: solid "
+ (action ? 2 : 5)
+ "px #FF0000;"
+ " padding: 0.5em;"
+ (action ? ""
: " color: #FF0000;")
+ "'>\n"
+ "<span style='font-size: larger; font-weight: bold;'>"
+ BAK.cnf.self + "</span> \n"
+ (action ? ""
: "<span class='error'>")
+ s
+ (action ? "\n"
: "</span>\n")
+ this.fiat( action )
+ "</div>\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 [email protected]
var e = access.split( " " ),
k = e[ 1 ],
s = "autoBackupR" + k,
i, w, $div, $textarea;
this.$box.append( "<div id='" + s + "' />" );
$div = this.$box.find( "#" + s );
if ( parseInt( k, 10 ) === BAK.revID ) {
s = k + " (" + BAK.cnf.feature( "thisPage" ) + ")";
} else {
s = mw.config.get( "wgArticlePath" );
s = "<a href='"
+ s.replace( /\$1/, "Special:PermanentLink/" ) + k
+ "' target='_blank'>" + k + "</a>";
}
s = "<h4>oldid=" + s + "</h4>";
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 = "<h5>" + BAK.util.focus( k[ 0 ] ) + "</h5>"
+ "<p>" + w.length + " bytes</p>";
$div.append( s );
$textarea = $( "<textarea rows='5' readonly />" );
$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 [email protected]
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 [email protected]
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 = "<h3>"
+ BAK.cnf.feature( "otherPages" )
+ "</h3>\n"
+ "<ul id='autoBackupPageList' />";
this.$div.append( s );
$x = this.$div.find( "#autoBackupPageList" );
u = "<a target='_blank'"
+ " href='" + mw.config.get( "wgScript" ) + "?";
g.sort();
for ( i = g.length - 1; i >= 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 + "</a> ";
} else {
s = "curid=" + u + "curid=" + s + "'>" + s + "</a> ";
if ( p[ 2 ] ) {
s = s + "(" + p[ 2 ].replace( /_/g, " " )
.replace( /</g, "<" )
+ ") ";
}
}
s = "<li>" + s + BAK.util.focus( p[ 0 ] )
+ "</li>";
$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 [email protected]
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 [email protected]
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 [email protected]
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 [email protected]
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 [email protected]
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 [email protected]
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 [email protected]
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 [email protected]
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 [email protected]
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 [email protected]
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 [email protected]
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 [email protected]
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 [email protected]
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 [email protected]
BAK.gui.face();
if ( ! BAK.gui.$msg ) {
if ( ! BAK.cnf.self ) {
BAK.cnf.self = BAK.cnf.feature( "BAKself" );
}
BAK.gui.$div.prepend( "<h2>" + BAK.cnf.self + "</h2>" );
}
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 [email protected]
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 [email protected]
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 [email protected]
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 [email protected]
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 [email protected]
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 [email protected]
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 [email protected]
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 [email protected]
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 </nowiki> autoBackup/d.js