Zeile 1: |
Zeile 1: |
| local JSONutil = { suite = "JSONutil", | | local JSONutil = { suite = "JSONutil", |
− | serial = "2019-05-17", | + | serial = "2019-07-18", |
| item = 63869449 } | | item = 63869449 } |
| --[=[ | | --[=[ |
| preprocess JSON data | | preprocess JSON data |
| ]=] | | ]=] |
| + | local Failsafe = JSONutil |
| | | |
| | | |
Zeile 12: |
Zeile 13: |
| | | |
| | | |
− | JSONutil.failsafe = function ( atleast )
| + | local Fallback = function () |
− | -- Retrieve versioning and check for compliance | + | -- Retrieve current default language code |
− | -- Precondition:
| + | -- Returns string |
− | -- atleast -- string, with required version or "wikidata"
| + | return mw.language.getContentLanguage():getCode() |
− | -- or false
| + | :lower() |
− | -- Postcondition:
| + | end -- Fallback() |
− | -- Returns string with appropriate version, or false | |
− | local since = atleast | |
− | local r
| |
− | if since == "wikidata" then
| |
− | local item = JSONutil.item
| |
− | since = false
| |
− | if type( item ) == "number" and item > 0 then
| |
− | local entity = mw.wikibase.getEntity( string.format( "Q%d",
| |
− | item ) )
| |
− | if type( entity ) == "table" then
| |
− | local vsn = entity:formatPropertyValues( "P348" )
| |
− | if type( vsn ) == "table" and
| |
− | type( vsn.value ) == "string" and
| |
− | vsn.value ~= "" then
| |
− | r = vsn.value
| |
− | end
| |
− | end
| |
− | end
| |
− | end
| |
− | if not r then
| |
− | if not since or since <= JSONutil.serial then
| |
− | r = JSONutil.serial
| |
− | else
| |
− | r = false
| |
− | end
| |
− | end
| |
− | return r
| |
− | end -- JSONutil.failsafe() | |
| | | |
| | | |
Zeile 54: |
Zeile 27: |
| -- apply -- string, with enhanced JSON | | -- apply -- string, with enhanced JSON |
| -- Returns: | | -- Returns: |
− | -- 1 -- string, or nil or false, with error keyword | + | -- 1 -- string|nil|false, with error keyword |
| -- 2 -- string, with JSON or context | | -- 2 -- string, with JSON or context |
| local m = 0 | | local m = 0 |
Zeile 60: |
Zeile 33: |
| local s = mw.text.trim( apply ) | | local s = mw.text.trim( apply ) |
| local sep = string.char( 10 ) | | local sep = string.char( 10 ) |
− | local i, j, r, scan, sep0, sep1, stab, start, stub, suffix | + | local i, j, last, r, scan, sep0, sep1, stab, start, stub, suffix |
| local framework = function ( a ) | | local framework = function ( a ) |
| -- syntax analysis outside strings | | -- syntax analysis outside strings |
Zeile 81: |
Zeile 54: |
| end | | end |
| end -- while k | | end -- while k |
− | end | + | end -- framework() |
| + | local free = function ( a, at, f ) |
| + | -- Throws: error if /* is not matched by */ |
| + | local s = a |
| + | local i = s:find( "//", at, true ) |
| + | local k = s:find( "/*", at, true ) |
| + | if i or k then |
| + | local m = s:find( sep0, at ) |
| + | if i and ( not m or i < m ) then |
| + | k = s:find( "\n", i + 2, true ) |
| + | if k then |
| + | if i == 1 then |
| + | s = s:sub( k + 1 ) |
| + | else |
| + | s = s:sub( 1, i - 1 ) .. |
| + | s:sub( k + 1 ) |
| + | end |
| + | elseif i > 1 then |
| + | s = s:sub( 1, i - 1 ) |
| + | else |
| + | s = "" |
| + | end |
| + | elseif k and ( not m or k < m ) then |
| + | i = s:find( "*/", k + 2, true ) |
| + | if i then |
| + | if k == 1 then |
| + | s = s:sub( i + 2 ) |
| + | else |
| + | s = s:sub( 1, k - 1 ) .. |
| + | s:sub( i + 2 ) |
| + | end |
| + | else |
| + | error( s:sub( k + 2 ), 0 ) |
| + | end |
| + | i = k |
| + | else |
| + | i = false |
| + | end |
| + | if i then |
| + | s = mw.text.trim( s ) |
| + | if s:find( "/", 1, true ) then |
| + | s = f( s, i, f ) |
| + | end |
| + | end |
| + | end |
| + | return s |
| + | end -- free() |
| if s:sub( 1, 1 ) == '{' then | | if s:sub( 1, 1 ) == '{' then |
| stab = string.char( 9 ) | | stab = string.char( 9 ) |
Zeile 87: |
Zeile 106: |
| :gsub( string.char( 13 ), sep ) | | :gsub( string.char( 13 ), sep ) |
| stub = s:gsub( sep, "" ):gsub( stab, "" ) | | stub = s:gsub( sep, "" ):gsub( stab, "" ) |
− | scan = string.char( 0x5B, 0x01, 0x2D, 0x1F, 0x5D ) | + | scan = string.char( 0x5B, 0x01, 0x2D, 0x1F, 0x5D ) -- [ \-\ ] |
| j = stub:find( scan ) | | j = stub:find( scan ) |
| if j then | | if j then |
| r = "ControlChar" | | r = "ControlChar" |
− | s = s:sub( j + 1, j + JSONutil.more ) | + | s = mw.text.trim( s:sub( j + 1 ) ) |
| + | s = mw.ustring.sub( s, 1, JSONutil.more ) |
| else | | else |
| i = true | | i = true |
| j = 1 | | j = 1 |
| + | last = ( stub:sub( -1 ) == "}" ) |
| sep0 = string.char( 0x5B, 0x22, 0x27, 0x5D ) -- [ " ' ] | | sep0 = string.char( 0x5B, 0x22, 0x27, 0x5D ) -- [ " ' ] |
| sep1 = string.char( 0x5B, 0x5C, 0x22, 0x5D ) -- [ \ " ] | | sep1 = string.char( 0x5B, 0x5C, 0x22, 0x5D ) -- [ \ " ] |
Zeile 100: |
Zeile 121: |
| else | | else |
| r = "Bracket0" | | r = "Bracket0" |
− | s = s:sub( 1, JSONutil.more ) | + | s = mw.ustring.sub( s, 1, JSONutil.more ) |
| end | | end |
| while i do | | while i do |
− | i = s:find( sep0, j ) | + | i, s = pcall( free, s, j, free ) |
| + | if i then |
| + | i = s:find( sep0, j ) |
| + | else |
| + | r = "CommentEnd" |
| + | s = mw.text.trim( s ) |
| + | s = mw.ustring.sub( s, 1, JSONutil.more ) |
| + | end |
| if i then | | if i then |
| if j == 1 then | | if j == 1 then |
Zeile 109: |
Zeile 137: |
| end | | end |
| if s:sub( i, i ) == '"' then | | if s:sub( i, i ) == '"' then |
− | stub = s:sub( j + 1, i - 1 ) | + | stub = s:sub( j, i - 1 ) |
| if stub:find( '[^"]*,%s*[%]}]' ) then | | if stub:find( '[^"]*,%s*[%]}]' ) then |
| r = "CommaEnd" | | r = "CommaEnd" |
− | s = stub:sub( 1, JSONutil.more ) | + | s = mw.text.trim( stub ) |
| + | s = mw.ustring.sub( s, 1, JSONutil.more ) |
| i = false | | i = false |
| j = false | | j = false |
Zeile 145: |
Zeile 174: |
| else | | else |
| r = "QouteEnd" | | r = "QouteEnd" |
− | s = s:sub( i, i + JSONutil.more ) | + | s = mw.text.trim( s:sub( i ) ) |
| + | s = mw.ustring.sub( s, 1, JSONutil.more ) |
| i = false | | i = false |
| end | | end |
Zeile 151: |
Zeile 181: |
| else | | else |
| r = "Qoute" | | r = "Qoute" |
− | s = s:sub( i, i + JSONutil.more ) | + | s = mw.text.trim( s:sub( i ) ) |
| + | s = mw.ustring.sub( s, 1, JSONutil.more ) |
| i = false | | i = false |
| end | | end |
− | else | + | elseif not r then |
| stub = s:sub( j ) | | stub = s:sub( j ) |
| if stub:find( '[^"]*,%s*[%]}]' ) then | | if stub:find( '[^"]*,%s*[%]}]' ) then |
| r = "CommaEnd" | | r = "CommaEnd" |
− | s = stub:sub( 1, JSONutil.more ) | + | s = mw.text.trim( stub ) |
| + | s = mw.ustring.sub( s, 1, JSONutil.more ) |
| else | | else |
| framework( stub ) | | framework( stub ) |
Zeile 186: |
Zeile 218: |
| if j > 1 then | | if j > 1 then |
| s = string.format( "%d %s", j, s ) | | s = string.format( "%d %s", j, s ) |
| + | end |
| + | elseif not ( r or last ) then |
| + | stub = suffix or apply or "" |
| + | j = stub:find( "/", 1, true ) |
| + | if j then |
| + | i, stub = pcall( free, stub, j, free ) |
| + | else |
| + | i = true |
| + | end |
| + | stub = mw.text.trim( stub ) |
| + | if i then |
| + | if stub:sub( - 1 ) ~= "}" then |
| + | r = "Trailing" |
| + | s = stub:match( "%}%s*(%S[^%}]*)$" ) |
| + | if s then |
| + | s = mw.ustring.sub( s, 1, JSONutil.more ) |
| + | else |
| + | s = mw.ustring.sub( stub, - JSONutil.more ) |
| + | end |
| + | end |
| + | else |
| + | r = "CommentEnd" |
| + | s = mw.ustring.sub( stub, 1, JSONutil.more ) |
| end | | end |
| end | | end |
Zeile 196: |
Zeile 251: |
| | | |
| | | |
− | JSONutil.fault = function ( alert, add, alien ) | + | JSONutil.fault = function ( alert, add, adapt ) |
| -- Retrieve formatted message | | -- Retrieve formatted message |
| -- Parameter: | | -- Parameter: |
| -- alert -- string, with error keyword, or other text | | -- alert -- string, with error keyword, or other text |
− | -- add -- string, or nil or false, with context | + | -- add -- string|nil|false, with context |
− | -- alien -- string, or nil or false, with language codes | + | -- adapt -- function|string|table|nil|false, for I18N |
| -- Returns string, with HTML span | | -- Returns string, with HTML span |
| local e = mw.html.create( "span" ) | | local e = mw.html.create( "span" ) |
| :addClass( "error" ) | | :addClass( "error" ) |
− | local f = function ( all, at )
| |
− | local slang
| |
− | if at == "*" then
| |
− | slang = mw.language.getContentLanguage():getCode()
| |
− | elseif at then
| |
− | slang = at
| |
− | end
| |
− | return all[ slang ]
| |
− | end -- f()
| |
| local s = alert | | local s = alert |
| if type( s ) == "string" then | | if type( s ) == "string" then |
Zeile 235: |
Zeile 281: |
| e = e[ 2 ] | | e = e[ 2 ] |
| if type( e ) == "table" then | | if type( e ) == "table" then |
− | local q | + | local q = type( adapt ) |
− | if type( alien ) == "string" then | + | if q == "function" then |
− | t = mw.text.split( alien, "%s+" )
| + | s = adapt( e, s ) |
| + | t = false |
| + | elseif q == "string" then |
| + | t = mw.text.split( adapt, "%s+" ) |
| + | elseif q == "table" then |
| + | t = adapt |
| else | | else |
− | t = { }
| + | t = { } |
| + | end |
| + | if t then |
| + | table.insert( t, Fallback() ) |
| + | table.insert( t, "en" ) |
| + | for k = 1, #t do |
| + | q = e[ t[ k ] ] |
| + | if type( q ) == "string" then |
| + | s = q |
| + | break -- for k |
| + | end |
| + | end -- for k |
| end | | end |
− | table.insert( t, "*" )
| |
− | table.insert( t, "en" )
| |
− | for k = 1, #t do
| |
− | q = f( e, t[ k ] )
| |
− | if type( q ) == "string" then
| |
− | s = q
| |
− | break -- for k
| |
− | end
| |
− | end -- for k
| |
| else | | else |
| s = "JSONutil.fault I18N bad #" .. | | s = "JSONutil.fault I18N bad #" .. |
Zeile 279: |
Zeile 332: |
| | | |
| | | |
− | JSONutil.fetch = function ( apply, always, alien ) | + | JSONutil.fetch = function ( apply, always, adapt ) |
| -- Retrieve JSON data, or error message | | -- Retrieve JSON data, or error message |
| -- Parameter: | | -- Parameter: |
| -- apply -- string, with presumable JSON text | | -- apply -- string, with presumable JSON text |
| -- always -- true, if apply is expected to need preprocessing | | -- always -- true, if apply is expected to need preprocessing |
− | -- alien -- string, or nil or false, with language codes | + | -- adapt -- function|string|table|nil|false, for I18N |
| -- Returns table, with data, or string, with error as HTML span | | -- Returns table, with data, or string, with error as HTML span |
| local lucky, r | | local lucky, r |
Zeile 293: |
Zeile 346: |
| lucky, r = JSONutil.fair( apply ) | | lucky, r = JSONutil.fair( apply ) |
| if lucky then | | if lucky then |
− | r = JSONutil.fault( lucky, r, alien ) | + | r = JSONutil.fault( lucky, r, adapt ) |
| else | | else |
| lucky, r = pcall( mw.text.jsonDecode, r ) | | lucky, r = pcall( mw.text.jsonDecode, r ) |
| if not lucky then | | if not lucky then |
− | r = JSONutil.fault( r, false, alien ) | + | r = JSONutil.fault( r, false, adapt ) |
| end | | end |
| end | | end |
Zeile 303: |
Zeile 356: |
| return r | | return r |
| end -- JSONutil.fetch() | | end -- JSONutil.fetch() |
| + | |
| + | |
| + | |
| + | Failsafe.failsafe = function ( atleast ) |
| + | -- Retrieve versioning and check for compliance |
| + | -- Precondition: |
| + | -- atleast -- string, with required version or "wikidata" or "~" |
| + | -- or false |
| + | -- Postcondition: |
| + | -- Returns string -- with queried version, also if problem |
| + | -- false -- if appropriate |
| + | local last = ( atleast == "~" ) |
| + | local since = atleast |
| + | local r |
| + | if last or since == "wikidata" then |
| + | local item = Failsafe.item |
| + | since = false |
| + | if type( item ) == "number" and item > 0 then |
| + | local entity = mw.wikibase.getEntity( string.format( "Q%d", |
| + | item ) ) |
| + | if type( entity ) == "table" then |
| + | local vsn = entity:formatPropertyValues( "P348" ) |
| + | if type( vsn ) == "table" and |
| + | type( vsn.value ) == "string" and |
| + | vsn.value ~= "" then |
| + | if last and vsn.value == Failsafe.serial then |
| + | r = false |
| + | else |
| + | r = vsn.value |
| + | end |
| + | end |
| + | end |
| + | end |
| + | end |
| + | if type( r ) == "nil" then |
| + | if not since or since <= Failsafe.serial then |
| + | r = Failsafe.serial |
| + | else |
| + | r = false |
| + | end |
| + | end |
| + | return r |
| + | end -- Failsafe.failsafe() |
| | | |
| | | |
Zeile 324: |
Zeile 420: |
| end | | end |
| end | | end |
− | return JSONutil.failsafe( since ) or "" | + | return Failsafe.failsafe( since ) or "" |
| end -- p.failsafe() | | end -- p.failsafe() |
| | | |