/// User:PerfektesChaos/js/jsonDebug/d.js
// Show JSON errors analysis live
/// 2018-08-24 [email protected]
// ResourceLoader: compatible;
// dependencies: user, mw.API
/// 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 Version = -1.2,
Signature = "jsonDebug",
JSOND = { site: "w:en",
store: "User:PerfektesChaos/js/" + Signature,
sel: "json-code-lint",
src: "div,pre",
using: null },
/*
PREGO = { app: false,
signature: "preferencesGadgetOptions",
site: "w:en",
store: "User:PerfektesChaos/js/" },
*/
REPOS = { requests: false },
UI = { bad: { "background-color": "#F0F080",
"border": "#FF0000 1px solid",
"color": "#FF0000",
"font-family": "monospace",
"font-size": "80%",
"font-weight": "normal",
"padding": "0.3em" },
code: { "margin-top": "1em",
"max-width": "95%",
"width": "95%" },
css: { "background-color": "#D0D0D0",
"border": "#808080 1px solid",
"clear": "both",
"margin-bottom": "1rem",
"max-width": "95%",
"padding": "1em" },
normal: 10,
$button: false },
VALID = { jsonlint: false,
reLineNum: "line ([0-9]+)" },
ZEBRA = { codemirror: false };
/*
function features( apply ) {
// Config hook has been fired
// Precondition:
// apply -- hook payload
// Uses:
// 2018-03-01 [email protected]
if ( typeof apply === "object"
&& apply ) {
}
} // features()
*/
JSOND.feed = function ( ask ) {
// External access interface
// Precondition:
// ask -- string with JSON source code
// Uses:
// > JSOND.using
// < JSOND.source
// mw.loader.using()
// JSOND.feeder()
// (JSOND.feeder)
// 2018-03-01 [email protected]
var s;
if ( typeof ask === "string" ) {
s = ask.trim();
if ( s ) {
JSOND.source = s;
if ( JSOND.using ) {
mw.loader.using( JSOND.using, JSOND.feeder );
} else {
JSOND.feeder();
}
}
}
}; // JSOND.feed()
JSOND.feeder = function () {
// External access, ensure DOM
// Precondition:
// Standard modules have been loaded
// Uses:
// (JSOND.feeding)
// 2018-03-01 [email protected]
$( JSOND.feeding );
}; // JSOND.feeder()
JSOND.feeding = function () {
// Perform external access
// Precondition:
// DOM ready
// Standard modules have been loaded
// Uses:
// > JSOND.source
// JSOND.flip(JSOND.flip)
// 2018-03-01 [email protected]
VALID.fire( JSOND.source );
JSOND.flip();
}; // JSOND.feeding()
JSOND.fire = function () {
// Start execution
// Precondition:
// all -- true, if entire content is JSON
// Standard modules have been loaded
// Uses:
// > Signature
// < JSOND.support
// mw.hook()
// PREGO.feed()
// (JSOND.fresh)
// 2018-03-18 [email protected]
/*
PREGO.feed();
mw.hook( Signature + ".config" ).add( JSOND.features );
*/
JSOND.support = Signature + "-button";
mw.hook( "wikipage.content" ).add( JSOND.fresh );
}; // JSOND.fire()
JSOND.fired = function ( event ) {
// Button has been pressed
// Precondition:
// event -- jQuery event object
// Uses:
// > JSOND.large
// > UI.live
// > UI.$source
// > JSOND.sel
// > JSOND.src
// < JSOND.source
// JSOND.flip()
// VALID.fire()
// 2018-03-18 [email protected]
var $button, $json;
event.stopPropagation();
JSOND.flip();
JSOND.source = false;
if ( JSOND.large ) {
if ( UI.live ) {
JSOND.source = UI.$source.val();
} else {
JSOND.source = UI.$source.text();
}
} else {
$button = $( event.target );
$json = $button.next();
$button.attr( { disabled: true } );
if ( $json.hasClass( JSOND.sel ) &&
$json.filter( JSOND.src ) ) {
JSOND.source = $json.text();
}
}
if ( JSOND.source ) {
JSOND.source = JSOND.source.trim();
VALID.fire( JSOND.source );
}
}; // JSOND.fired()
JSOND.first = function () {
// Autorun on loading
// Uses:
// > Signature
// > JSOND.using
// > JSOND.site
// > JSOND.store
// > Version
// < JSOND.signature
// < JSOND.large
// mw.loader.getState()
// mw.loader.state()
// mw.config.get()
// mw.loader.using()
// JSOND.fire()
// mw.hook()
// (JSOND.fire)
// (JSOND.feed)
// 2018-08-24 [email protected]
var env, pub, rls;
JSOND.signature = "ext.gadget." + Signature;
if ( mw.loader.getState( JSOND.signature ) !== "ready" ) {
env = mw.config.get( [ "wgAction",
"wgPageContentModel" ] );
rls = { };
rls[ JSOND.signature ] = "ready";
mw.loader.state( rls );
JSOND.large =
( env.wgPageContentModel.toLowerCase().indexOf( "json" )
>= 0 );
switch ( env.wgAction ) {
case "view":
case "edit":
case "submit":
case "parsermigration-edit":
if ( JSOND.using ) {
mw.loader.using( JSOND.using, JSOND.fire );
} else {
JSOND.fire();
}
break;
} // switch wgAction
pub = { doc: "[[" + JSOND.site + ":" + JSOND.store + "]]",
feed: JSOND.feed,
type: Signature,
vsn: Version };
mw.hook( Signature + ".ready" ).fire( pub );
}
}; // JSOND.first()
JSOND.flip = function () {
// Enable all buttons
// Uses:
// > UI.$area
// > JSOND.support
// 2018-03-01 [email protected]
UI.$area.find( "." + JSOND.support ).attr( { disabled: false } );
}; // JSOND.flip()
JSOND.fresh = function ( $area ) {
// Handler when content area has changed
// Precondition:
// $area -- jQuery element of content area
// (DOM ready)
// Uses:
// > JSOND.large
// > JSOND.sel
// > JSOND.src
// >< UI.$button
// < UI.$area
// < UI.live
// < UI.$source
// JSOND.furnish()
// (JSOND.furnish)
// 2018-03-18 [email protected]
var $e;
UI.$area = $area;
if ( JSOND.large ) {
$e = UI.$area.find( "#wpTextbox1" );
UI.live = $e.length;
if ( UI.live ) {
UI.$source = $e;
} else {
UI.$source = false;
$e = UI.$area.children(); // <div dir="ltr">
if ( $e.length === 1 ) {
$e = $e.children();
if ( $e.length === 1 &&
$e.hasClass( "mw-highlight" ) ) {
$e = $e.children(); // <pre>
if ( $e.length === 1 &&
! $e.children().length &&
$e.filter( "pre" ).length ) {
UI.$source = $e;
}
}
}
}
if ( UI.$source && ! JSOND.$button ) {
UI.$button = JSOND.furnish( 0, $area.get( 0 ) );
} else if ( JSOND.$button ) {
JSOND.$button.remove();
JSOND.$button = false;
}
} else {
UI.$area.find( "." + JSOND.support ).remove();
UI.$area.find( "." + JSOND.sel )
.filter( JSOND.src )
.each( JSOND.furnish );
}
}; // JSOND.fresh()
JSOND.furnish = function ( at, area ) {
// Create button before JSON content
// Precondition:
// at -- sequence number
// area -- DOM element of content
// Postcondition:
// Returns jQuery <button>
// Uses:
// > Signature
// > JSOND.support
// (JSOND.fired)
// 2018-03-18 [email protected]
var $r = $( "<button>" ).addClass( JSOND.support )
.click( JSOND.fired )
.css( { "margin-left": "0.5em",
"margin-right": "0.5em" } )
.text( Signature );
$( area ).before( $r );
return $r;
}; // JSOND.furnish()
/*
PREGO.feed = function () {
// Provide PREGO
// Precondition:
// mediawiki.Title has been loaded
// Uses:
// > PREGO.signature
// > PREGO.site
// > PREGO.store
// mw.loader.getState()
// REPOS.fire()
// 2018-03-01 [email protected]
var s = "ext.gadget." + PREGO.signature;
if ( mw.loader.getState( s ) !== "ready" ) {
REPOS.fire( PREGO.site,
PREGO.store + "/" + PREGO.signature,
"/r.js",
{ action: "raw",
ctype: "text/javascript",
bcache: 1,
maxage: 604813 } );
}
}; // PREGO.feed()
*/
REPOS.fire = function ( at, access, append, alter ) {
// Load from external URL
// Precondition:
// at -- Wikimedia Foundation site code, or not
// access -- string with basic page name
// append -- string with subpage, or not
// alter -- parameter object, or MIME string, or not
// Uses:
// >< REPOS.requests
// REPOS.foundation()
// mw.loader.load()
// 2018-03-21 [email protected]
var source, syntax;
if ( typeof REPOS.requests !== "object" ) {
REPOS.requests = { };
}
if ( typeof REPOS.requests[ access ] !== "boolean" ) {
REPOS.requests[ access ] = true;
if ( append ) {
source = access + append;
} else {
source = access;
}
if ( at ) {
source = REPOS.foundation( at, source, alter );
if ( typeof alter === "object"
&& alter &&
typeof alter.ctype === "string" ) {
syntax = alter.ctype;
}
} else {
syntax = alter;
}
mw.loader.load( source, syntax );
}
}; // REPOS.fire()
REPOS.foundation = function ( at, access, alter ) {
// Create URL within Wikimedia Foundation
// Precondition:
// at -- site code, or not
// access -- string with page name
// alter -- parameter object, or not
// Postcondition:
// Returns full URL
// 2018-03-21 [email protected]
var s = access,
r = encodeURI( s );
if ( typeof alter === "object"
&& alter ) {
r = "/w/index.php?title=" + r;
if ( access.substr( -3 ) === ".js" ) {
alter.ctype = "text/javascript";
} else if ( access.substr( -4 ) === ".css" ) {
alter.ctype = "text/css";
}
alter.action = "raw";
for ( s in alter ) {
r = r + "&" + s + "=" + encodeURI( alter[ s ] );
} // for s in alter
} else {
r = "/wiki/" + r;
}
if ( typeof at === "string"
&& at ) {
switch ( at ) {
case "meta":
r = "meta.wikimedia.org" + r;
break;
case "mw":
r = "www.mediawiki.org" + r;
break;
case "w:en":
r = "en.wikipedia.org" + r;
break;
default:
r = window.location.host + r;
} // switch at
r = "https://" + r;
}
return r;
}; // REPOS.foundation()
UI.factory = function () {
// Equip page with box
// Uses:
// > UI.signature
// > UI.css
// > JSOND.store
// > JSOND.site
// > Signature
// > Version
// > UI.bad
// > UI.$area
// < UI.$wrapper
// < UI.$warn
// < UI.$code
// REPOS.foundation()
// (UI.flip)
// 2018-03-21 [email protected]
var ltr = ( $( "html" ).attr( "dir" ) !== "rtl" ),
$head = $( "<div>" ),
$e;
UI.$wrapper = $( "<div>" ).addClass( UI.signature );
UI.$wrapper.css( { "clear": "both",
"font-size": "1rem",
"font-style": "normal",
"font-weight": "normal",
"max-width": "100%",
"width": "100%" } );
if ( typeof UI.css === "object"
&& UI.css ) {
UI.$wrapper.css( UI.css );
}
if ( typeof JSOND.store === "string"
&& JSOND.store ) {
$e = $( "<a>" ).attr( { href: REPOS.foundation( JSOND.site,
JSOND.store ),
target: Signature,
title: "Tool info / doc" } );
} else {
$e = $( "<span>" );
}
$e.css( { "font-family": "monospace",
"font-size": "1.6em",
"font-weight": "bold" } )
.text( Signature );
$head.append( $e );
$e = $( "<span>" ).css( { "font-size": "smaller",
"margin-left": "2em",
"margin-right": "2em" } )
.text( Version + "" );
$head.append( $e )
.css( { "float": ( ltr ? "left": "right" ) } );
UI.$wrapper.append( $head );
$e = $( "<button>" ).click( UI.flip )
.css( { "color": "#FF0000",
"float": ( ltr ? "right":
"left" ),
"font-weight": "bold",
"padding": "0.2em" } )
.text( "X" );
UI.$wrapper.append( $e );
UI.$area.prepend( UI.$wrapper );
$e = $( "<div>" ).css( { "clear": "both" } );
UI.$wrapper.append( $e );
UI.$warn = $( "<pre>" ).addClass( Signature + "-warn" )
.css( { "clear": "both",
"overflow": "auto" } )
.hide();
if ( typeof UI.bad === "object"
&& UI.bad ) {
UI.$warn.css( UI.bad );
}
UI.$wrapper.append( UI.$warn );
UI.$code = $( "<textarea>" ).addClass( Signature + "-code" )
.attr( { disabled: true } )
.css( { "overflow": "auto" } )
.hide();
if ( typeof UI.code === "object"
&& UI.code ) {
UI.$code.css( UI.code );
}
UI.$wrapper.append( UI.$code );
}; // UI.factory()
UI.fire = function ( apply, alert ) {
// Start presentation
// Precondition:
// apply -- string with JSON source code, or false
// alert -- string with error report, or false
// Uses:
// > Signature
// > UI.$warn
// > UI.$code
// > UI.$area
// > UI.normal
// >< VALID.reLineNum
// < UI.signature
// < UI.$wrapper
// UI.factory()
// ZEBRA.fire()
// 2018-03-01 [email protected]
var got, k, n;
UI.signature = Signature + "-wrapper";
UI.$wrapper = UI.$area.find( "." + UI.signature );
if ( ! UI.$wrapper.length ) {
UI.factory();
}
if ( ! apply || ! alert ) {
UI.$warn.hide();
if ( ! apply ) {
UI.$code.hide();
}
}
UI.$area.scrollTop( 0 );
UI.$wrapper.show();
if ( apply ) {
if ( alert ) {
UI.$warn.show()
.text( alert );
if ( typeof VALID.reLineNum === "string" ) {
VALID.reLineNum = new RegExp( VALID.reLineNum );
}
if ( typeof VALID.reLineNum === "object" ) {
got = alert.match( VALID.reLineNum );
if ( got ) {
k = parseInt( got[ 1 ], 10 );
if ( k > 1 ) {
k--;
}
}
}
}
n = apply.split( "\n" ).length;
if ( n > UI.normal ) {
n = UI.normal;
}
UI.$code.attr( "rows", n )
.show()
.val( apply );
ZEBRA.fire( apply, k );
}
}; // UI.fire()
UI.flip = function () {
// Hide box
// Uses:
// > UI.$wrapper
// JSOND.flip()
// 2018-03-01 [email protected]
UI.$wrapper.hide();
JSOND.flip();
}; // UI.flip()
VALID.fiat = function () {
// Execute validation
// Precondition:
// jsonlint has been loaded
// Uses:
// > VALID.jsonlint
// > VALID.source
// < VALID.scream
// VALID.jsonlint.parse()
// UI.fire()
// 2018-03-01 [email protected]
VALID.scream = false;
try {
VALID.jsonlint.parse( VALID.source );
} catch ( e ) {
VALID.scream = e.message;
}
UI.fire( VALID.source, VALID.scream );
}; // VALID.fiat()
VALID.fire = function ( apply ) {
// Start validation and presentation
// Precondition:
// apply -- string with JSON source code
// DOM ready
// mediawiki.Title has been loaded
// Uses:
// > JSOND.site
// > JSOND.store
// > Signature
// >< VALID.jsonlint
// < VALID.source
// UI.fire()
// ZEBRA.first()
// VALID.fiat()
// REPOS.fire()
// mw.hook()
// (VALID.furnish)
// 2018-03-21 [email protected]
UI.fire();
ZEBRA.first();
VALID.source = apply;
if ( VALID.jsonlint ) {
VALID.fiat();
} else if ( VALID.jsonlint === false ) {
VALID.jsonlint = null;
REPOS.fire( JSOND.site,
JSOND.store + "/jsonlint.js",
false,
{ action: "raw",
ctype: "text/javascript",
bcache: 1,
maxage: 604800 } );
mw.hook( Signature + ".jsonlint" ).add( VALID.furnish );
}
}; // VALID.fire()
VALID.furnish = function ( application ) {
// jsonlint has been loaded
// Precondition:
// application -- jsonlint object
// Uses:
// < VALID.jsonlint
// VALID.fiat()
// 2018-03-01 [email protected]
if ( application &&
typeof application === "object" &&
typeof application.parse === "function" ) {
VALID.jsonlint = application;
VALID.fiat();
}
}; // VALID.furnish()
ZEBRA.config = { autofocus: true,
gutters: [ "CodeMirror-linenumbers" ],
lineNumbers: true,
mode: "javascript",
readOnly: true,
tabSize: 2,
theme : Signature };
ZEBRA.fiat = function ( apply ) {
// Execute enhanced presentation
// Precondition:
// apply -- mode function for javascript, if not yet defined
// CodeMirror ready and initialized
// Uses:
// > ZEBRA.codemirror
// > UI.$code
// > ZEBRA.config
// > ZEBRA.source
// > ZEBRA.invalid
// >< ZEBRA.editor
// 2018-03-01 [email protected]
if ( typeof apply === "object"
&& apply &&
typeof apply.mode === "function" ) {
ZEBRA.codemirror.defineMode( "javascript", apply.mode );
}
if ( ZEBRA.editor ) {
ZEBRA.editor.toTextArea();
}
ZEBRA.editor = ZEBRA.codemirror.fromTextArea( UI.$code.get( 0 ),
ZEBRA.config );
ZEBRA.editor.setSize( null, UI.$code.height() );
ZEBRA.editor.setValue( ZEBRA.source );
if ( ZEBRA.invalid ) {
ZEBRA.focus( ZEBRA.invalid );
}
}; // ZEBRA.fiat()
ZEBRA.fire = function ( apply, at ) {
// Request presentation
// Precondition:
// apply -- string with JSON source code
// at -- line number with error, or false
// Uses:
// > ZEBRA.codemirror
// < ZEBRA.source
// < ZEBRA.invalid
// mw.loader.using()
// ZEBRA.fiat()
// (ZEBRA.furnish)
// 2018-03-01 [email protected]
ZEBRA.source = apply;
ZEBRA.invalid = at || false;
if ( ZEBRA.codemirror ) {
ZEBRA.fiat();
} else {
mw.loader.using( [ "ext.CodeMirror" ],
ZEBRA.furnish );
}
}; // ZEBRA.fire()
ZEBRA.first = function () {
// Pre-emptive loading of CodeMirror data
// Uses:
// > JSOND.site
// > JSOND.store
// >< ZEBRA.codemirror
// mw.loader.load()
// REPOS.fire()
// 2018-03-21 [email protected]
if ( ZEBRA.codemirror === false ) {
ZEBRA.codemirror = null;
mw.loader.load( [ "ext.CodeMirror" ] );
REPOS.fire( JSOND.site,
JSOND.store + ".css",
false,
{ action: "raw",
ctype: "text/css",
bcache: 1,
maxage: 604800 } );
REPOS.fire( JSOND.site,
JSOND.store + "/codemirrorJavascript.js",
false,
{ action: "raw",
ctype: "text/javascript",
bcache: 1,
maxage: 604800 } );
}
}; // ZEBRA.first()
ZEBRA.focus = function ( at ) {
// Highlight and focus particular line
// Precondition:
// at -- line number with error
// Uses:
// > Signature
// > ZEBRA.editor
// 2018-03-01 [email protected]
ZEBRA.editor.addLineClass( at,
"background",
Signature + "-line-error" );
ZEBRA.editor.setCursor( at );
}; // ZEBRA.focus()
ZEBRA.furnish = function () {
// Establish CodeMirror environment
// Precondition:
// CodeMirror supposed to have been loaded
// Uses:
// > window.CodeMirror
// >< ZEBRA.codemirror
// < ZEBRA.editor
// mw.hook()
// (ZEBRA.fiat)
// 2018-03-01 [email protected]
if ( ! ZEBRA.codemirror &&
typeof window.CodeMirror !== "undefined"
&& window.CodeMirror ) {
ZEBRA.codemirror = window.CodeMirror;
ZEBRA.editor = false;
}
if ( ZEBRA.codemirror ) {
mw.hook( Signature + ".cm-mode-js" ).add( ZEBRA.fiat );
}
}; // ZEBRA.furnish()
JSOND.first();
}( window.mediaWiki, window.jQuery ) );
// Emacs
// Local Variables:
// coding: utf-8-dos
// fill-column: 80
// End:
/// EOF </nowiki> jsonDebug/d.js