mirror of
https://github.com/gnosygnu/xowa.git
synced 2026-03-02 03:49:30 +00:00
Refactor: Pull more classes into baselib
This commit is contained in:
@@ -1,209 +1,209 @@
|
||||
---
|
||||
-- An implementation of the lua 5.2 bit32 library, in pure Lua
|
||||
|
||||
-- Note that in Lua, "x % n" is defined such that will always return a number
|
||||
-- between 0 and n-1 for positive n. We take advantage of that a lot here.
|
||||
|
||||
local bit32 = {}
|
||||
|
||||
local function checkint( name, argidx, x, level )
|
||||
local n = tonumber( x )
|
||||
if not n then
|
||||
error( string.format(
|
||||
"bad argument #%d to '%s' (number expected, got %s)",
|
||||
argidx, name, type( x )
|
||||
), level + 1 )
|
||||
end
|
||||
return math.floor( n )
|
||||
end
|
||||
|
||||
local function checkint32( name, argidx, x, level )
|
||||
local n = tonumber( x )
|
||||
if not n then
|
||||
error( string.format(
|
||||
"bad argument #%d to '%s' (number expected, got %s)",
|
||||
argidx, name, type( x )
|
||||
), level + 1 )
|
||||
end
|
||||
return math.floor( n ) % 0x100000000
|
||||
end
|
||||
|
||||
|
||||
function bit32.bnot( x )
|
||||
x = checkint32( 'bnot', 1, x, 2 )
|
||||
|
||||
-- In two's complement, -x = not(x) + 1
|
||||
-- So not(x) = -x - 1
|
||||
return ( -x - 1 ) % 0x100000000
|
||||
end
|
||||
|
||||
|
||||
---
|
||||
-- Logic tables for and/or/xor. We do pairs of bits here as a tradeoff between
|
||||
-- table space and speed. If you change the number of bits, also change the
|
||||
-- constants 2 and 4 in comb() below, and the initial value in bit32.band and
|
||||
-- bit32.btest
|
||||
local logic_and = {
|
||||
[0] = { [0] = 0, 0, 0, 0},
|
||||
[1] = { [0] = 0, 1, 0, 1},
|
||||
[2] = { [0] = 0, 0, 2, 2},
|
||||
[3] = { [0] = 0, 1, 2, 3},
|
||||
}
|
||||
local logic_or = {
|
||||
[0] = { [0] = 0, 1, 2, 3},
|
||||
[1] = { [0] = 1, 1, 3, 3},
|
||||
[2] = { [0] = 2, 3, 2, 3},
|
||||
[3] = { [0] = 3, 3, 3, 3},
|
||||
}
|
||||
local logic_xor = {
|
||||
[0] = { [0] = 0, 1, 2, 3},
|
||||
[1] = { [0] = 1, 0, 3, 2},
|
||||
[2] = { [0] = 2, 3, 0, 1},
|
||||
[3] = { [0] = 3, 2, 1, 0},
|
||||
}
|
||||
|
||||
---
|
||||
-- @param name string Function name
|
||||
-- @param args table Function args
|
||||
-- @param nargs number Arg count
|
||||
-- @param s number Start value, 0-3
|
||||
-- @param t table Logic table
|
||||
-- @return number result
|
||||
local function comb( name, args, nargs, s, t )
|
||||
for i = 1, nargs do
|
||||
args[i] = checkint32( name, i, args[i], 3 )
|
||||
end
|
||||
|
||||
local pow = 1
|
||||
local ret = 0
|
||||
for b = 0, 31, 2 do
|
||||
local c = s
|
||||
for i = 1, nargs do
|
||||
c = t[c][args[i] % 4]
|
||||
args[i] = math.floor( args[i] / 4 )
|
||||
end
|
||||
ret = ret + c * pow
|
||||
pow = pow * 4
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
function bit32.band( ... )
|
||||
return comb( 'band', { ... }, select( '#', ... ), 3, logic_and )
|
||||
end
|
||||
|
||||
function bit32.bor( ... )
|
||||
return comb( 'bor', { ... }, select( '#', ... ), 0, logic_or )
|
||||
end
|
||||
|
||||
function bit32.bxor( ... )
|
||||
return comb( 'bxor', { ... }, select( '#', ... ), 0, logic_xor )
|
||||
end
|
||||
|
||||
function bit32.btest( ... )
|
||||
return comb( 'btest', { ... }, select( '#', ... ), 3, logic_and ) ~= 0
|
||||
end
|
||||
|
||||
|
||||
function bit32.extract( n, field, width )
|
||||
n = checkint32( 'extract', 1, n, 2 )
|
||||
field = checkint( 'extract', 2, field, 2 )
|
||||
width = checkint( 'extract', 3, width or 1, 2 )
|
||||
if field < 0 then
|
||||
error( "bad argument #2 to 'extract' (field cannot be negative)", 2 )
|
||||
end
|
||||
if width <= 0 then
|
||||
error( "bad argument #3 to 'extract' (width must be positive)", 2 )
|
||||
end
|
||||
if field + width > 32 then
|
||||
error( 'trying to access non-existent bits', 2 )
|
||||
end
|
||||
|
||||
return math.floor( n / 2^field ) % 2^width
|
||||
end
|
||||
|
||||
function bit32.replace( n, v, field, width )
|
||||
n = checkint32( 'replace', 1, n, 2 )
|
||||
v = checkint32( 'replace', 2, v, 2 )
|
||||
field = checkint( 'replace', 3, field, 2 )
|
||||
width = checkint( 'replace', 4, width or 1, 2 )
|
||||
if field < 0 then
|
||||
error( "bad argument #3 to 'replace' (field cannot be negative)", 2 )
|
||||
end
|
||||
if width <= 0 then
|
||||
error( "bad argument #4 to 'replace' (width must be positive)", 2 )
|
||||
end
|
||||
if field + width > 32 then
|
||||
error( 'trying to access non-existent bits', 2 )
|
||||
end
|
||||
|
||||
local f = 2^field
|
||||
local w = 2^width
|
||||
local fw = f * w
|
||||
return ( n % f ) + ( v % w ) * f + math.floor( n / fw ) * fw
|
||||
end
|
||||
|
||||
|
||||
-- For the shifting functions, anything over 32 is the same as 32
|
||||
-- and limiting to 32 prevents overflow/underflow
|
||||
local function checkdisp( name, x )
|
||||
x = checkint( name, 2, x, 3 )
|
||||
return math.min( math.max( -32, x ), 32 )
|
||||
end
|
||||
|
||||
function bit32.lshift( x, disp )
|
||||
x = checkint32( 'lshift', 1, x, 2 )
|
||||
disp = checkdisp( 'lshift', disp )
|
||||
|
||||
return math.floor( x * 2^disp ) % 0x100000000
|
||||
end
|
||||
|
||||
function bit32.rshift( x, disp )
|
||||
x = checkint32( 'rshift', 1, x, 2 )
|
||||
disp = checkdisp( 'rshift', disp )
|
||||
|
||||
return math.floor( x / 2^disp ) % 0x100000000
|
||||
end
|
||||
|
||||
function bit32.arshift( x, disp )
|
||||
x = checkint32( 'arshift', 1, x, 2 )
|
||||
disp = checkdisp( 'arshift', disp )
|
||||
|
||||
if disp <= 0 then
|
||||
-- Non-positive displacement == left shift
|
||||
-- (since exponent is non-negative, the multipication can never result
|
||||
-- in a fractional part)
|
||||
return ( x * 2^-disp ) % 0x100000000
|
||||
elseif x < 0x80000000 then
|
||||
-- High bit is 0 == right shift
|
||||
-- (since exponent is positive, the division will never increase x)
|
||||
return math.floor( x / 2^disp )
|
||||
elseif disp > 31 then
|
||||
-- Shifting off all bits
|
||||
return 0xffffffff
|
||||
else
|
||||
-- 0x100000000 - 2 ^ ( 32 - disp ) creates a number with the high disp
|
||||
-- bits set. So shift right then add that number.
|
||||
return math.floor( x / 2^disp ) + ( 0x100000000 - 2 ^ ( 32 - disp ) )
|
||||
end
|
||||
end
|
||||
|
||||
-- For the rotation functions, disp works mod 32.
|
||||
-- Note that lrotate( x, disp ) == rrotate( x, -disp ).
|
||||
function bit32.lrotate( x, disp )
|
||||
x = checkint32( 'lrotate', 1, x, 2 )
|
||||
disp = checkint( 'lrotate', 2, disp, 2 ) % 32
|
||||
|
||||
local x = x * 2^disp
|
||||
return ( x % 0x100000000 ) + math.floor( x / 0x100000000 )
|
||||
end
|
||||
|
||||
function bit32.rrotate( x, disp )
|
||||
x = checkint32( 'rrotate', 1, x, 2 )
|
||||
disp = -checkint( 'rrotate', 2, disp, 2 ) % 32
|
||||
|
||||
local x = x * 2^disp
|
||||
return ( x % 0x100000000 ) + math.floor( x / 0x100000000 )
|
||||
end
|
||||
|
||||
return bit32
|
||||
---
|
||||
-- An implementation of the lua 5.2 bit32 library, in pure Lua
|
||||
|
||||
-- Note that in Lua, "x % n" is defined such that will always return a number
|
||||
-- between 0 and n-1 for positive n. We take advantage of that a lot here.
|
||||
|
||||
local bit32 = {}
|
||||
|
||||
local function checkint( name, argidx, x, level )
|
||||
local n = tonumber( x )
|
||||
if not n then
|
||||
error( string.format(
|
||||
"bad argument #%d to '%s' (number expected, got %s)",
|
||||
argidx, name, type( x )
|
||||
), level + 1 )
|
||||
end
|
||||
return math.floor( n )
|
||||
end
|
||||
|
||||
local function checkint32( name, argidx, x, level )
|
||||
local n = tonumber( x )
|
||||
if not n then
|
||||
error( string.format(
|
||||
"bad argument #%d to '%s' (number expected, got %s)",
|
||||
argidx, name, type( x )
|
||||
), level + 1 )
|
||||
end
|
||||
return math.floor( n ) % 0x100000000
|
||||
end
|
||||
|
||||
|
||||
function bit32.bnot( x )
|
||||
x = checkint32( 'bnot', 1, x, 2 )
|
||||
|
||||
-- In two's complement, -x = not(x) + 1
|
||||
-- So not(x) = -x - 1
|
||||
return ( -x - 1 ) % 0x100000000
|
||||
end
|
||||
|
||||
|
||||
---
|
||||
-- Logic tables for and/or/xor. We do pairs of bits here as a tradeoff between
|
||||
-- table space and speed. If you change the number of bits, also change the
|
||||
-- constants 2 and 4 in comb() below, and the initial value in bit32.band and
|
||||
-- bit32.btest
|
||||
local logic_and = {
|
||||
[0] = { [0] = 0, 0, 0, 0},
|
||||
[1] = { [0] = 0, 1, 0, 1},
|
||||
[2] = { [0] = 0, 0, 2, 2},
|
||||
[3] = { [0] = 0, 1, 2, 3},
|
||||
}
|
||||
local logic_or = {
|
||||
[0] = { [0] = 0, 1, 2, 3},
|
||||
[1] = { [0] = 1, 1, 3, 3},
|
||||
[2] = { [0] = 2, 3, 2, 3},
|
||||
[3] = { [0] = 3, 3, 3, 3},
|
||||
}
|
||||
local logic_xor = {
|
||||
[0] = { [0] = 0, 1, 2, 3},
|
||||
[1] = { [0] = 1, 0, 3, 2},
|
||||
[2] = { [0] = 2, 3, 0, 1},
|
||||
[3] = { [0] = 3, 2, 1, 0},
|
||||
}
|
||||
|
||||
---
|
||||
-- @param name string Function name
|
||||
-- @param args table Function args
|
||||
-- @param nargs number Arg count
|
||||
-- @param s number Start value, 0-3
|
||||
-- @param t table Logic table
|
||||
-- @return number result
|
||||
local function comb( name, args, nargs, s, t )
|
||||
for i = 1, nargs do
|
||||
args[i] = checkint32( name, i, args[i], 3 )
|
||||
end
|
||||
|
||||
local pow = 1
|
||||
local ret = 0
|
||||
for b = 0, 31, 2 do
|
||||
local c = s
|
||||
for i = 1, nargs do
|
||||
c = t[c][args[i] % 4]
|
||||
args[i] = math.floor( args[i] / 4 )
|
||||
end
|
||||
ret = ret + c * pow
|
||||
pow = pow * 4
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
function bit32.band( ... )
|
||||
return comb( 'band', { ... }, select( '#', ... ), 3, logic_and )
|
||||
end
|
||||
|
||||
function bit32.bor( ... )
|
||||
return comb( 'bor', { ... }, select( '#', ... ), 0, logic_or )
|
||||
end
|
||||
|
||||
function bit32.bxor( ... )
|
||||
return comb( 'bxor', { ... }, select( '#', ... ), 0, logic_xor )
|
||||
end
|
||||
|
||||
function bit32.btest( ... )
|
||||
return comb( 'btest', { ... }, select( '#', ... ), 3, logic_and ) ~= 0
|
||||
end
|
||||
|
||||
|
||||
function bit32.extract( n, field, width )
|
||||
n = checkint32( 'extract', 1, n, 2 )
|
||||
field = checkint( 'extract', 2, field, 2 )
|
||||
width = checkint( 'extract', 3, width or 1, 2 )
|
||||
if field < 0 then
|
||||
error( "bad argument #2 to 'extract' (field cannot be negative)", 2 )
|
||||
end
|
||||
if width <= 0 then
|
||||
error( "bad argument #3 to 'extract' (width must be positive)", 2 )
|
||||
end
|
||||
if field + width > 32 then
|
||||
error( 'trying to access non-existent bits', 2 )
|
||||
end
|
||||
|
||||
return math.floor( n / 2^field ) % 2^width
|
||||
end
|
||||
|
||||
function bit32.replace( n, v, field, width )
|
||||
n = checkint32( 'replace', 1, n, 2 )
|
||||
v = checkint32( 'replace', 2, v, 2 )
|
||||
field = checkint( 'replace', 3, field, 2 )
|
||||
width = checkint( 'replace', 4, width or 1, 2 )
|
||||
if field < 0 then
|
||||
error( "bad argument #3 to 'replace' (field cannot be negative)", 2 )
|
||||
end
|
||||
if width <= 0 then
|
||||
error( "bad argument #4 to 'replace' (width must be positive)", 2 )
|
||||
end
|
||||
if field + width > 32 then
|
||||
error( 'trying to access non-existent bits', 2 )
|
||||
end
|
||||
|
||||
local f = 2^field
|
||||
local w = 2^width
|
||||
local fw = f * w
|
||||
return ( n % f ) + ( v % w ) * f + math.floor( n / fw ) * fw
|
||||
end
|
||||
|
||||
|
||||
-- For the shifting functions, anything over 32 is the same as 32
|
||||
-- and limiting to 32 prevents overflow/underflow
|
||||
local function checkdisp( name, x )
|
||||
x = checkint( name, 2, x, 3 )
|
||||
return math.min( math.max( -32, x ), 32 )
|
||||
end
|
||||
|
||||
function bit32.lshift( x, disp )
|
||||
x = checkint32( 'lshift', 1, x, 2 )
|
||||
disp = checkdisp( 'lshift', disp )
|
||||
|
||||
return math.floor( x * 2^disp ) % 0x100000000
|
||||
end
|
||||
|
||||
function bit32.rshift( x, disp )
|
||||
x = checkint32( 'rshift', 1, x, 2 )
|
||||
disp = checkdisp( 'rshift', disp )
|
||||
|
||||
return math.floor( x / 2^disp ) % 0x100000000
|
||||
end
|
||||
|
||||
function bit32.arshift( x, disp )
|
||||
x = checkint32( 'arshift', 1, x, 2 )
|
||||
disp = checkdisp( 'arshift', disp )
|
||||
|
||||
if disp <= 0 then
|
||||
-- Non-positive displacement == left shift
|
||||
-- (since exponent is non-negative, the multipication can never result
|
||||
-- in a fractional part)
|
||||
return ( x * 2^-disp ) % 0x100000000
|
||||
elseif x < 0x80000000 then
|
||||
-- High bit is 0 == right shift
|
||||
-- (since exponent is positive, the division will never increase x)
|
||||
return math.floor( x / 2^disp )
|
||||
elseif disp > 31 then
|
||||
-- Shifting off all bits
|
||||
return 0xffffffff
|
||||
else
|
||||
-- 0x100000000 - 2 ^ ( 32 - disp ) creates a number with the high disp
|
||||
-- bits set. So shift right then add that number.
|
||||
return math.floor( x / 2^disp ) + ( 0x100000000 - 2 ^ ( 32 - disp ) )
|
||||
end
|
||||
end
|
||||
|
||||
-- For the rotation functions, disp works mod 32.
|
||||
-- Note that lrotate( x, disp ) == rrotate( x, -disp ).
|
||||
function bit32.lrotate( x, disp )
|
||||
x = checkint32( 'lrotate', 1, x, 2 )
|
||||
disp = checkint( 'lrotate', 2, disp, 2 ) % 32
|
||||
|
||||
local x = x * 2^disp
|
||||
return ( x % 0x100000000 ) + math.floor( x / 0x100000000 )
|
||||
end
|
||||
|
||||
function bit32.rrotate( x, disp )
|
||||
x = checkint32( 'rrotate', 1, x, 2 )
|
||||
disp = -checkint( 'rrotate', 2, disp, 2 ) % 32
|
||||
|
||||
local x = x * 2^disp
|
||||
return ( x % 0x100000000 ) + math.floor( x / 0x100000000 )
|
||||
end
|
||||
|
||||
return bit32
|
||||
|
||||
@@ -1,144 +1,144 @@
|
||||
--[[
|
||||
compat_env - see README for details.
|
||||
(c) 2012 David Manura. Licensed under Lua 5.1/5.2 terms (MIT license).
|
||||
--]]
|
||||
|
||||
local M = {_TYPE='module', _NAME='compat_env', _VERSION='0.2.2.20120406'}
|
||||
|
||||
local function check_chunk_type(s, mode)
|
||||
local nmode = mode or 'bt'
|
||||
local is_binary = s and #s > 0 and s:byte(1) == 27
|
||||
if is_binary and not nmode:match'b' then
|
||||
return nil, ("attempt to load a binary chunk (mode is '%s')"):format(mode)
|
||||
elseif not is_binary and not nmode:match't' then
|
||||
return nil, ("attempt to load a text chunk (mode is '%s')"):format(mode)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local IS_52_LOAD = pcall(load, '')
|
||||
if IS_52_LOAD then
|
||||
M.load = _G.load
|
||||
M.loadfile = _G.loadfile
|
||||
else
|
||||
-- 5.2 style `load` implemented in 5.1
|
||||
function M.load(ld, source, mode, env)
|
||||
local f
|
||||
if type(ld) == 'string' then
|
||||
local s = ld
|
||||
local ok, err = check_chunk_type(s, mode)
|
||||
if not ok then return ok, err end
|
||||
local err; f, err = loadstring(s, source)
|
||||
if not f then return f, err end
|
||||
elseif type(ld) == 'function' then
|
||||
local ld2 = ld
|
||||
if (mode or 'bt') ~= 'bt' then
|
||||
local first = ld()
|
||||
local ok, err = check_chunk_type(first, mode)
|
||||
if not ok then return ok, err end
|
||||
ld2 = function()
|
||||
if first then
|
||||
local chunk=first; first=nil; return chunk
|
||||
else return ld() end
|
||||
end
|
||||
end
|
||||
local err; f, err = load(ld2, source); if not f then return f, err end
|
||||
else
|
||||
error(("bad argument #1 to 'load' (function expected, got %s)")
|
||||
:format(type(ld)), 2)
|
||||
end
|
||||
if env then setfenv(f, env) end
|
||||
return f
|
||||
end
|
||||
|
||||
-- 5.2 style `loadfile` implemented in 5.1
|
||||
function M.loadfile(filename, mode, env)
|
||||
if (mode or 'bt') ~= 'bt' then
|
||||
local ioerr
|
||||
local fh, err = io.open(filename, 'rb'); if not fh then return fh,err end
|
||||
local function ld()
|
||||
local chunk; chunk,ioerr = fh:read(4096); return chunk
|
||||
end
|
||||
local f, err = M.load(ld, filename and '@'..filename, mode, env)
|
||||
fh:close()
|
||||
if not f then return f, err end
|
||||
if ioerr then return nil, ioerr end
|
||||
return f
|
||||
else
|
||||
local f, err = loadfile(filename); if not f then return f, err end
|
||||
if env then setfenv(f, env) end
|
||||
return f
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if _G.setfenv then -- Lua 5.1
|
||||
M.setfenv = _G.setfenv
|
||||
M.getfenv = _G.getfenv
|
||||
else -- >= Lua 5.2
|
||||
-- helper function for `getfenv`/`setfenv`
|
||||
local function envlookup(f)
|
||||
local name, val
|
||||
local up = 0
|
||||
local unknown
|
||||
repeat
|
||||
up=up+1; name, val = debug.getupvalue(f, up)
|
||||
if name == '' then unknown = true end
|
||||
until name == '_ENV' or name == nil
|
||||
if name ~= '_ENV' then
|
||||
up = nil
|
||||
if unknown then
|
||||
error("upvalues not readable in Lua 5.2 when debug info missing", 3)
|
||||
end
|
||||
end
|
||||
return (name == '_ENV') and up, val, unknown
|
||||
end
|
||||
|
||||
-- helper function for `getfenv`/`setfenv`
|
||||
local function envhelper(f, name)
|
||||
if type(f) == 'number' then
|
||||
if f < 0 then
|
||||
error(("bad argument #1 to '%s' (level must be non-negative)")
|
||||
:format(name), 3)
|
||||
elseif f < 1 then
|
||||
error("thread environments unsupported in Lua 5.2", 3) --[*]
|
||||
end
|
||||
f = debug.getinfo(f+2, 'f').func
|
||||
elseif type(f) ~= 'function' then
|
||||
error(("bad argument #1 to '%s' (number expected, got %s)")
|
||||
:format(type(name, f)), 2)
|
||||
end
|
||||
return f
|
||||
end
|
||||
-- [*] might simulate with table keyed by coroutine.running()
|
||||
|
||||
-- 5.1 style `setfenv` implemented in 5.2
|
||||
function M.setfenv(f, t)
|
||||
local f = envhelper(f, 'setfenv')
|
||||
local up, val, unknown = envlookup(f)
|
||||
if up then
|
||||
debug.upvaluejoin(f, up, function() return up end, 1) --unique upval[*]
|
||||
debug.setupvalue(f, up, t)
|
||||
else
|
||||
local what = debug.getinfo(f, 'S').what
|
||||
if what ~= 'Lua' and what ~= 'main' then -- not Lua func
|
||||
error("'setfenv' cannot change environment of given object", 2)
|
||||
end -- else ignore no _ENV upvalue (warning: incompatible with 5.1)
|
||||
end
|
||||
return f -- invariant: original f ~= 0
|
||||
end
|
||||
-- [*] http://lua-users.org/lists/lua-l/2010-06/msg00313.html
|
||||
|
||||
-- 5.1 style `getfenv` implemented in 5.2
|
||||
function M.getfenv(f)
|
||||
if f == 0 or f == nil then return _G end -- simulated behavior
|
||||
local f = envhelper(f, 'setfenv')
|
||||
local up, val = envlookup(f)
|
||||
if not up then return _G end -- simulated behavior [**]
|
||||
return val
|
||||
end
|
||||
-- [**] possible reasons: no _ENV upvalue, C function
|
||||
end
|
||||
|
||||
|
||||
return M
|
||||
--[[
|
||||
compat_env - see README for details.
|
||||
(c) 2012 David Manura. Licensed under Lua 5.1/5.2 terms (MIT license).
|
||||
--]]
|
||||
|
||||
local M = {_TYPE='module', _NAME='compat_env', _VERSION='0.2.2.20120406'}
|
||||
|
||||
local function check_chunk_type(s, mode)
|
||||
local nmode = mode or 'bt'
|
||||
local is_binary = s and #s > 0 and s:byte(1) == 27
|
||||
if is_binary and not nmode:match'b' then
|
||||
return nil, ("attempt to load a binary chunk (mode is '%s')"):format(mode)
|
||||
elseif not is_binary and not nmode:match't' then
|
||||
return nil, ("attempt to load a text chunk (mode is '%s')"):format(mode)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local IS_52_LOAD = pcall(load, '')
|
||||
if IS_52_LOAD then
|
||||
M.load = _G.load
|
||||
M.loadfile = _G.loadfile
|
||||
else
|
||||
-- 5.2 style `load` implemented in 5.1
|
||||
function M.load(ld, source, mode, env)
|
||||
local f
|
||||
if type(ld) == 'string' then
|
||||
local s = ld
|
||||
local ok, err = check_chunk_type(s, mode)
|
||||
if not ok then return ok, err end
|
||||
local err; f, err = loadstring(s, source)
|
||||
if not f then return f, err end
|
||||
elseif type(ld) == 'function' then
|
||||
local ld2 = ld
|
||||
if (mode or 'bt') ~= 'bt' then
|
||||
local first = ld()
|
||||
local ok, err = check_chunk_type(first, mode)
|
||||
if not ok then return ok, err end
|
||||
ld2 = function()
|
||||
if first then
|
||||
local chunk=first; first=nil; return chunk
|
||||
else return ld() end
|
||||
end
|
||||
end
|
||||
local err; f, err = load(ld2, source); if not f then return f, err end
|
||||
else
|
||||
error(("bad argument #1 to 'load' (function expected, got %s)")
|
||||
:format(type(ld)), 2)
|
||||
end
|
||||
if env then setfenv(f, env) end
|
||||
return f
|
||||
end
|
||||
|
||||
-- 5.2 style `loadfile` implemented in 5.1
|
||||
function M.loadfile(filename, mode, env)
|
||||
if (mode or 'bt') ~= 'bt' then
|
||||
local ioerr
|
||||
local fh, err = io.open(filename, 'rb'); if not fh then return fh,err end
|
||||
local function ld()
|
||||
local chunk; chunk,ioerr = fh:read(4096); return chunk
|
||||
end
|
||||
local f, err = M.load(ld, filename and '@'..filename, mode, env)
|
||||
fh:close()
|
||||
if not f then return f, err end
|
||||
if ioerr then return nil, ioerr end
|
||||
return f
|
||||
else
|
||||
local f, err = loadfile(filename); if not f then return f, err end
|
||||
if env then setfenv(f, env) end
|
||||
return f
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if _G.setfenv then -- Lua 5.1
|
||||
M.setfenv = _G.setfenv
|
||||
M.getfenv = _G.getfenv
|
||||
else -- >= Lua 5.2
|
||||
-- helper function for `getfenv`/`setfenv`
|
||||
local function envlookup(f)
|
||||
local name, val
|
||||
local up = 0
|
||||
local unknown
|
||||
repeat
|
||||
up=up+1; name, val = debug.getupvalue(f, up)
|
||||
if name == '' then unknown = true end
|
||||
until name == '_ENV' or name == nil
|
||||
if name ~= '_ENV' then
|
||||
up = nil
|
||||
if unknown then
|
||||
error("upvalues not readable in Lua 5.2 when debug info missing", 3)
|
||||
end
|
||||
end
|
||||
return (name == '_ENV') and up, val, unknown
|
||||
end
|
||||
|
||||
-- helper function for `getfenv`/`setfenv`
|
||||
local function envhelper(f, name)
|
||||
if type(f) == 'number' then
|
||||
if f < 0 then
|
||||
error(("bad argument #1 to '%s' (level must be non-negative)")
|
||||
:format(name), 3)
|
||||
elseif f < 1 then
|
||||
error("thread environments unsupported in Lua 5.2", 3) --[*]
|
||||
end
|
||||
f = debug.getinfo(f+2, 'f').func
|
||||
elseif type(f) ~= 'function' then
|
||||
error(("bad argument #1 to '%s' (number expected, got %s)")
|
||||
:format(type(name, f)), 2)
|
||||
end
|
||||
return f
|
||||
end
|
||||
-- [*] might simulate with table keyed by coroutine.running()
|
||||
|
||||
-- 5.1 style `setfenv` implemented in 5.2
|
||||
function M.setfenv(f, t)
|
||||
local f = envhelper(f, 'setfenv')
|
||||
local up, val, unknown = envlookup(f)
|
||||
if up then
|
||||
debug.upvaluejoin(f, up, function() return up end, 1) --unique upval[*]
|
||||
debug.setupvalue(f, up, t)
|
||||
else
|
||||
local what = debug.getinfo(f, 'S').what
|
||||
if what ~= 'Lua' and what ~= 'main' then -- not Lua func
|
||||
error("'setfenv' cannot change environment of given object", 2)
|
||||
end -- else ignore no _ENV upvalue (warning: incompatible with 5.1)
|
||||
end
|
||||
return f -- invariant: original f ~= 0
|
||||
end
|
||||
-- [*] http://lua-users.org/lists/lua-l/2010-06/msg00313.html
|
||||
|
||||
-- 5.1 style `getfenv` implemented in 5.2
|
||||
function M.getfenv(f)
|
||||
if f == 0 or f == nil then return _G end -- simulated behavior
|
||||
local f = envhelper(f, 'setfenv')
|
||||
local up, val = envlookup(f)
|
||||
if not up then return _G end -- simulated behavior [**]
|
||||
return val
|
||||
end
|
||||
-- [**] possible reasons: no _ENV upvalue, C function
|
||||
end
|
||||
|
||||
|
||||
return M
|
||||
|
||||
@@ -1,71 +1,71 @@
|
||||
local libraryUtil = {}
|
||||
|
||||
function libraryUtil.checkType( name, argIdx, arg, expectType, nilOk )
|
||||
if arg == nil and nilOk then
|
||||
return
|
||||
end
|
||||
if type( arg ) ~= expectType then
|
||||
local msg = string.format( "bad argument #%d to '%s' (%s expected, got %s)",
|
||||
argIdx, name, expectType, type( arg )
|
||||
)
|
||||
error( msg, 3 )
|
||||
end
|
||||
end
|
||||
|
||||
function libraryUtil.checkTypeMulti( name, argIdx, arg, expectTypes )
|
||||
local argType = type( arg )
|
||||
for _, expectType in ipairs( expectTypes ) do
|
||||
if argType == expectType then
|
||||
return
|
||||
end
|
||||
end
|
||||
local n = #expectTypes
|
||||
local typeList
|
||||
if n > 1 then
|
||||
typeList = table.concat( expectTypes, ', ', 1, n - 1 ) .. ' or ' .. expectTypes[n]
|
||||
else
|
||||
typeList = expectTypes[1]
|
||||
end
|
||||
local msg = string.format( "bad argument #%d to '%s' (%s expected, got %s)",
|
||||
argIdx,
|
||||
name,
|
||||
typeList,
|
||||
type( arg )
|
||||
)
|
||||
error( msg, 3 )
|
||||
end
|
||||
|
||||
function libraryUtil.checkTypeForIndex( index, value, expectType )
|
||||
if type( value ) ~= expectType then
|
||||
local msg = string.format( "value for index '%s' must be %s, %s given",
|
||||
index, expectType, type( value )
|
||||
)
|
||||
error( msg, 3 )
|
||||
end
|
||||
end
|
||||
|
||||
function libraryUtil.checkTypeForNamedArg( name, argName, arg, expectType, nilOk )
|
||||
if arg == nil and nilOk then
|
||||
return
|
||||
end
|
||||
if type( arg ) ~= expectType then
|
||||
local msg = string.format( "bad named argument %s to '%s' (%s expected, got %s)",
|
||||
argName, name, expectType, type( arg )
|
||||
)
|
||||
error( msg, 3 )
|
||||
end
|
||||
end
|
||||
|
||||
function libraryUtil.makeCheckSelfFunction( libraryName, varName, selfObj, selfObjDesc )
|
||||
return function ( self, method )
|
||||
if self ~= selfObj then
|
||||
error( string.format(
|
||||
"%s: invalid %s. Did you call %s with a dot instead of a colon, i.e. " ..
|
||||
"%s.%s() instead of %s:%s()?",
|
||||
libraryName, selfObjDesc, method, varName, method, varName, method
|
||||
), 3 )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return libraryUtil
|
||||
local libraryUtil = {}
|
||||
|
||||
function libraryUtil.checkType( name, argIdx, arg, expectType, nilOk )
|
||||
if arg == nil and nilOk then
|
||||
return
|
||||
end
|
||||
if type( arg ) ~= expectType then
|
||||
local msg = string.format( "bad argument #%d to '%s' (%s expected, got %s)",
|
||||
argIdx, name, expectType, type( arg )
|
||||
)
|
||||
error( msg, 3 )
|
||||
end
|
||||
end
|
||||
|
||||
function libraryUtil.checkTypeMulti( name, argIdx, arg, expectTypes )
|
||||
local argType = type( arg )
|
||||
for _, expectType in ipairs( expectTypes ) do
|
||||
if argType == expectType then
|
||||
return
|
||||
end
|
||||
end
|
||||
local n = #expectTypes
|
||||
local typeList
|
||||
if n > 1 then
|
||||
typeList = table.concat( expectTypes, ', ', 1, n - 1 ) .. ' or ' .. expectTypes[n]
|
||||
else
|
||||
typeList = expectTypes[1]
|
||||
end
|
||||
local msg = string.format( "bad argument #%d to '%s' (%s expected, got %s)",
|
||||
argIdx,
|
||||
name,
|
||||
typeList,
|
||||
type( arg )
|
||||
)
|
||||
error( msg, 3 )
|
||||
end
|
||||
|
||||
function libraryUtil.checkTypeForIndex( index, value, expectType )
|
||||
if type( value ) ~= expectType then
|
||||
local msg = string.format( "value for index '%s' must be %s, %s given",
|
||||
index, expectType, type( value )
|
||||
)
|
||||
error( msg, 3 )
|
||||
end
|
||||
end
|
||||
|
||||
function libraryUtil.checkTypeForNamedArg( name, argName, arg, expectType, nilOk )
|
||||
if arg == nil and nilOk then
|
||||
return
|
||||
end
|
||||
if type( arg ) ~= expectType then
|
||||
local msg = string.format( "bad named argument %s to '%s' (%s expected, got %s)",
|
||||
argName, name, expectType, type( arg )
|
||||
)
|
||||
error( msg, 3 )
|
||||
end
|
||||
end
|
||||
|
||||
function libraryUtil.makeCheckSelfFunction( libraryName, varName, selfObj, selfObjDesc )
|
||||
return function ( self, method )
|
||||
if self ~= selfObj then
|
||||
error( string.format(
|
||||
"%s: invalid %s. Did you call %s with a dot instead of a colon, i.e. " ..
|
||||
"%s.%s() instead of %s:%s()?",
|
||||
libraryName, selfObjDesc, method, varName, method, varName, method
|
||||
), 3 )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return libraryUtil
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
local hash = {}
|
||||
local php
|
||||
|
||||
local util = require 'libraryUtil'
|
||||
local checkType = util.checkType
|
||||
|
||||
function hash.listAlgorithms()
|
||||
return php.listAlgorithms()
|
||||
end
|
||||
|
||||
function hash.hashValue( algo, value )
|
||||
checkType( 'hashValue', 1, algo, 'string' )
|
||||
checkType( 'hashValue', 2, value, 'string' )
|
||||
|
||||
return php.hashValue( algo, value )
|
||||
end
|
||||
|
||||
function hash.setupInterface()
|
||||
-- Boilerplate
|
||||
php = mw_interface
|
||||
mw_interface = nil
|
||||
|
||||
-- Register this library in the "mw" global
|
||||
mw = mw or {}
|
||||
mw.hash = hash
|
||||
|
||||
package.loaded['mw.hash'] = hash
|
||||
end
|
||||
|
||||
return hash
|
||||
local hash = {}
|
||||
local php
|
||||
|
||||
local util = require 'libraryUtil'
|
||||
local checkType = util.checkType
|
||||
|
||||
function hash.listAlgorithms()
|
||||
return php.listAlgorithms()
|
||||
end
|
||||
|
||||
function hash.hashValue( algo, value )
|
||||
checkType( 'hashValue', 1, algo, 'string' )
|
||||
checkType( 'hashValue', 2, value, 'string' )
|
||||
|
||||
return php.hashValue( algo, value )
|
||||
end
|
||||
|
||||
function hash.setupInterface()
|
||||
-- Boilerplate
|
||||
php = mw_interface
|
||||
mw_interface = nil
|
||||
|
||||
-- Register this library in the "mw" global
|
||||
mw = mw or {}
|
||||
mw.hash = hash
|
||||
|
||||
package.loaded['mw.hash'] = hash
|
||||
end
|
||||
|
||||
return hash
|
||||
|
||||
@@ -1,422 +1,422 @@
|
||||
--[[
|
||||
A module for building complex HTML from Lua using a
|
||||
fluent interface.
|
||||
|
||||
Originally written on the English Wikipedia by
|
||||
Toohool and Mr. Stradivarius.
|
||||
|
||||
Code released under the GPL v2+ as per:
|
||||
https://en.wikipedia.org/w/index.php?diff=next&oldid=581399786
|
||||
https://en.wikipedia.org/w/index.php?diff=next&oldid=581403025
|
||||
|
||||
@license GNU GPL v2+
|
||||
@author Marius Hoch < hoo@online.de >
|
||||
]]
|
||||
|
||||
local HtmlBuilder = {}
|
||||
|
||||
local util = require 'libraryUtil'
|
||||
local checkType = util.checkType
|
||||
local checkTypeMulti = util.checkTypeMulti
|
||||
|
||||
local metatable = {}
|
||||
local methodtable = {}
|
||||
|
||||
local selfClosingTags = {
|
||||
area = true,
|
||||
base = true,
|
||||
br = true,
|
||||
col = true,
|
||||
command = true,
|
||||
embed = true,
|
||||
hr = true,
|
||||
img = true,
|
||||
input = true,
|
||||
keygen = true,
|
||||
link = true,
|
||||
meta = true,
|
||||
param = true,
|
||||
source = true,
|
||||
track = true,
|
||||
wbr = true,
|
||||
}
|
||||
|
||||
local htmlencodeMap = {
|
||||
['>'] = '>',
|
||||
['<'] = '<',
|
||||
['&'] = '&',
|
||||
['"'] = '"',
|
||||
}
|
||||
|
||||
metatable.__index = methodtable
|
||||
|
||||
metatable.__tostring = function( t )
|
||||
local ret = {}
|
||||
t:_build( ret )
|
||||
return table.concat( ret )
|
||||
end
|
||||
|
||||
-- Get an attribute table (name, value) and its index
|
||||
--
|
||||
-- @param name
|
||||
local function getAttr( t, name )
|
||||
for i, attr in ipairs( t.attributes ) do
|
||||
if attr.name == name then
|
||||
return attr, i
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Is this a valid attribute name?
|
||||
--
|
||||
-- @param s
|
||||
local function isValidAttributeName( s )
|
||||
-- Good estimate: http://www.w3.org/TR/2000/REC-xml-20001006#NT-Name
|
||||
return s:match( '^[a-zA-Z_:][a-zA-Z0-9_.:-]*$' )
|
||||
end
|
||||
|
||||
-- Is this a valid tag name?
|
||||
--
|
||||
-- @param s
|
||||
local function isValidTag( s )
|
||||
return s:match( '^[a-zA-Z0-9]+$' )
|
||||
end
|
||||
|
||||
-- Escape a value, for use in HTML
|
||||
--
|
||||
-- @param s
|
||||
local function htmlEncode( s )
|
||||
-- The parentheses ensure that there is only one return value
|
||||
return ( string.gsub( s, '[<>&"]', htmlencodeMap ) )
|
||||
end
|
||||
|
||||
local function cssEncode( s )
|
||||
-- XXX: I'm not sure this character set is complete.
|
||||
-- bug #68011: allow delete character (\127)
|
||||
return mw.ustring.gsub( s, '[^\32-\57\60-\127]', function ( m )
|
||||
return string.format( '\\%X ', mw.ustring.codepoint( m ) )
|
||||
end )
|
||||
end
|
||||
|
||||
-- Create a builder object. This is a separate function so that we can show the
|
||||
-- correct error levels in both HtmlBuilder.create and metatable.tag.
|
||||
--
|
||||
-- @param tagName
|
||||
-- @param args
|
||||
local function createBuilder( tagName, args )
|
||||
if tagName ~= nil and tagName ~= '' and not isValidTag( tagName ) then
|
||||
error( string.format( "invalid tag name '%s'", tagName ), 3 )
|
||||
end
|
||||
|
||||
args = args or {}
|
||||
local builder = {}
|
||||
setmetatable( builder, metatable )
|
||||
builder.nodes = {}
|
||||
builder.attributes = {}
|
||||
builder.styles = {}
|
||||
|
||||
if tagName ~= '' then
|
||||
builder.tagName = tagName
|
||||
end
|
||||
|
||||
builder.parent = args.parent
|
||||
builder.selfClosing = selfClosingTags[tagName] or args.selfClosing or false
|
||||
return builder
|
||||
end
|
||||
|
||||
-- Append a builder to the current node. This is separate from methodtable.node
|
||||
-- so that we can show the correct error level in both methodtable.node and
|
||||
-- methodtable.wikitext.
|
||||
--
|
||||
-- @param builder
|
||||
local function appendBuilder( t, builder )
|
||||
if t.selfClosing then
|
||||
error( "self-closing tags can't have child nodes", 3 )
|
||||
end
|
||||
|
||||
if builder then
|
||||
table.insert( t.nodes, builder )
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
methodtable._build = function( t, ret )
|
||||
if t.tagName then
|
||||
table.insert( ret, '<' .. t.tagName )
|
||||
for i, attr in ipairs( t.attributes ) do
|
||||
table.insert(
|
||||
ret,
|
||||
-- Note: Attribute names have already been validated
|
||||
' ' .. attr.name .. '="' .. htmlEncode( attr.val ) .. '"'
|
||||
)
|
||||
end
|
||||
if #t.styles > 0 then
|
||||
table.insert( ret, ' style="' )
|
||||
local css = {}
|
||||
for i, prop in ipairs( t.styles ) do
|
||||
if type( prop ) ~= 'table' then -- added with cssText()
|
||||
table.insert( css, htmlEncode( prop ) )
|
||||
else -- added with css()
|
||||
table.insert(
|
||||
css,
|
||||
htmlEncode( cssEncode( prop.name ) .. ':' .. cssEncode( prop.val ) )
|
||||
)
|
||||
end
|
||||
end
|
||||
table.insert( ret, table.concat( css, ';' ) )
|
||||
table.insert( ret, '"' )
|
||||
end
|
||||
if t.selfClosing then
|
||||
table.insert( ret, ' />' )
|
||||
return
|
||||
end
|
||||
table.insert( ret, '>' )
|
||||
end
|
||||
for i, node in ipairs( t.nodes ) do
|
||||
if node then
|
||||
if type( node ) == 'table' then
|
||||
node:_build( ret )
|
||||
else
|
||||
table.insert( ret, tostring( node ) )
|
||||
end
|
||||
end
|
||||
end
|
||||
if t.tagName then
|
||||
table.insert( ret, '</' .. t.tagName .. '>' )
|
||||
end
|
||||
end
|
||||
|
||||
-- Append a builder to the current node
|
||||
--
|
||||
-- @param builder
|
||||
methodtable.node = function( t, builder )
|
||||
return appendBuilder( t, builder )
|
||||
end
|
||||
|
||||
-- Appends some markup to the node. This will be treated as wikitext.
|
||||
methodtable.wikitext = function( t, ... )
|
||||
local vals = {...}
|
||||
for i = 1, #vals do
|
||||
checkTypeMulti( 'wikitext', i, vals[i], { 'string', 'number' } )
|
||||
appendBuilder( t, vals[i] )
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
-- Appends a newline character to the node.
|
||||
methodtable.newline = function( t )
|
||||
t:wikitext( '\n' )
|
||||
return t
|
||||
end
|
||||
|
||||
-- Appends a new child node to the builder, and returns an HtmlBuilder instance
|
||||
-- representing that new node.
|
||||
--
|
||||
-- @param tagName
|
||||
-- @param args
|
||||
methodtable.tag = function( t, tagName, args )
|
||||
checkType( 'tag', 1, tagName, 'string' )
|
||||
checkType( 'tag', 2, args, 'table', true )
|
||||
args = args or {}
|
||||
|
||||
args.parent = t
|
||||
local builder = createBuilder( tagName, args )
|
||||
t:node( builder )
|
||||
return builder
|
||||
end
|
||||
|
||||
-- Get the value of an html attribute
|
||||
--
|
||||
-- @param name
|
||||
methodtable.getAttr = function( t, name )
|
||||
checkType( 'getAttr', 1, name, 'string' )
|
||||
|
||||
local attr = getAttr( t, name )
|
||||
if attr then
|
||||
return attr.val
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Set an HTML attribute on the node.
|
||||
--
|
||||
-- @param name Attribute to set, alternative table of name-value pairs
|
||||
-- @param val Value of the attribute. Nil causes the attribute to be unset
|
||||
methodtable.attr = function( t, name, val )
|
||||
if type( name ) == 'table' then
|
||||
if val ~= nil then
|
||||
error(
|
||||
"bad argument #2 to 'attr' " ..
|
||||
'(if argument #1 is a table, argument #2 must be left empty)',
|
||||
2
|
||||
)
|
||||
end
|
||||
|
||||
local callForTable = function()
|
||||
for attrName, attrValue in pairs( name ) do
|
||||
t:attr( attrName, attrValue )
|
||||
end
|
||||
end
|
||||
|
||||
if not pcall( callForTable ) then
|
||||
error(
|
||||
"bad argument #1 to 'attr' " ..
|
||||
'(table keys must be strings, and values must be strings or numbers)',
|
||||
2
|
||||
)
|
||||
end
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
checkType( 'attr', 1, name, 'string' )
|
||||
checkTypeMulti( 'attr', 2, val, { 'string', 'number', 'nil' } )
|
||||
|
||||
-- if caller sets the style attribute explicitly, then replace all styles
|
||||
-- previously added with css() and cssText()
|
||||
if name == 'style' then
|
||||
t.styles = { val }
|
||||
return t
|
||||
end
|
||||
|
||||
if not isValidAttributeName( name ) then
|
||||
error( string.format(
|
||||
"bad argument #1 to 'attr' (invalid attribute name '%s')",
|
||||
name
|
||||
), 2 )
|
||||
end
|
||||
|
||||
local attr, i = getAttr( t, name )
|
||||
if attr then
|
||||
if val ~= nil then
|
||||
attr.val = val
|
||||
else
|
||||
table.remove( t.attributes, i )
|
||||
end
|
||||
elseif val ~= nil then
|
||||
table.insert( t.attributes, { name = name, val = val } )
|
||||
end
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
-- Adds a class name to the node's class attribute. Spaces will be
|
||||
-- automatically added to delimit each added class name.
|
||||
--
|
||||
-- @param class
|
||||
methodtable.addClass = function( t, class )
|
||||
checkTypeMulti( 'addClass', 1, class, { 'string', 'number', 'nil' } )
|
||||
|
||||
if class == nil then
|
||||
return t
|
||||
end
|
||||
|
||||
local attr = getAttr( t, 'class' )
|
||||
if attr then
|
||||
attr.val = attr.val .. ' ' .. class
|
||||
else
|
||||
t:attr( 'class', class )
|
||||
end
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
-- Set a CSS property to be added to the node's style attribute.
|
||||
--
|
||||
-- @param name CSS attribute to set, alternative table of name-value pairs
|
||||
-- @param val The value to set. Nil causes it to be unset
|
||||
methodtable.css = function( t, name, val )
|
||||
if type( name ) == 'table' then
|
||||
if val ~= nil then
|
||||
error(
|
||||
"bad argument #2 to 'css' " ..
|
||||
'(if argument #1 is a table, argument #2 must be left empty)',
|
||||
2
|
||||
)
|
||||
end
|
||||
|
||||
local callForTable = function()
|
||||
for attrName, attrValue in pairs( name ) do
|
||||
t:css( attrName, attrValue )
|
||||
end
|
||||
end
|
||||
|
||||
if not pcall( callForTable ) then
|
||||
error(
|
||||
"bad argument #1 to 'css' " ..
|
||||
'(table keys and values must be strings or numbers)',
|
||||
2
|
||||
)
|
||||
end
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
checkTypeMulti( 'css', 1, name, { 'string', 'number' } )
|
||||
checkTypeMulti( 'css', 2, val, { 'string', 'number', 'nil' } )
|
||||
|
||||
for i, prop in ipairs( t.styles ) do
|
||||
if prop.name == name then
|
||||
if val ~= nil then
|
||||
prop.val = val
|
||||
else
|
||||
table.remove( t.styles, i )
|
||||
end
|
||||
return t
|
||||
end
|
||||
end
|
||||
|
||||
if val ~= nil then
|
||||
table.insert( t.styles, { name = name, val = val } )
|
||||
end
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
-- Add some raw CSS to the node's style attribute. This is typically used
|
||||
-- when a template allows some CSS to be passed in as a parameter
|
||||
--
|
||||
-- @param css
|
||||
methodtable.cssText = function( t, css )
|
||||
checkTypeMulti( 'cssText', 1, css, { 'string', 'number', 'nil' } )
|
||||
if css ~= nil then
|
||||
table.insert( t.styles, css )
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
-- Returns the parent node under which the current node was created. Like
|
||||
-- jQuery.end, this is a convenience function to allow the construction of
|
||||
-- several child nodes to be chained together into a single statement.
|
||||
methodtable.done = function( t )
|
||||
return t.parent or t
|
||||
end
|
||||
|
||||
-- Like .done(), but traverses all the way to the root node of the tree and
|
||||
-- returns it.
|
||||
methodtable.allDone = function( t )
|
||||
while t.parent do
|
||||
t = t.parent
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
-- Create a new instance
|
||||
--
|
||||
-- @param tagName
|
||||
-- @param args
|
||||
function HtmlBuilder.create( tagName, args )
|
||||
checkType( 'mw.html.create', 1, tagName, 'string', true )
|
||||
checkType( 'mw.html.create', 2, args, 'table', true )
|
||||
return createBuilder( tagName, args )
|
||||
end
|
||||
|
||||
mw_interface = nil
|
||||
|
||||
-- Register this library in the "mw" global
|
||||
mw = mw or {}
|
||||
mw.html = HtmlBuilder
|
||||
|
||||
package.loaded['mw.html'] = HtmlBuilder
|
||||
|
||||
return HtmlBuilder
|
||||
--[[
|
||||
A module for building complex HTML from Lua using a
|
||||
fluent interface.
|
||||
|
||||
Originally written on the English Wikipedia by
|
||||
Toohool and Mr. Stradivarius.
|
||||
|
||||
Code released under the GPL v2+ as per:
|
||||
https://en.wikipedia.org/w/index.php?diff=next&oldid=581399786
|
||||
https://en.wikipedia.org/w/index.php?diff=next&oldid=581403025
|
||||
|
||||
@license GNU GPL v2+
|
||||
@author Marius Hoch < hoo@online.de >
|
||||
]]
|
||||
|
||||
local HtmlBuilder = {}
|
||||
|
||||
local util = require 'libraryUtil'
|
||||
local checkType = util.checkType
|
||||
local checkTypeMulti = util.checkTypeMulti
|
||||
|
||||
local metatable = {}
|
||||
local methodtable = {}
|
||||
|
||||
local selfClosingTags = {
|
||||
area = true,
|
||||
base = true,
|
||||
br = true,
|
||||
col = true,
|
||||
command = true,
|
||||
embed = true,
|
||||
hr = true,
|
||||
img = true,
|
||||
input = true,
|
||||
keygen = true,
|
||||
link = true,
|
||||
meta = true,
|
||||
param = true,
|
||||
source = true,
|
||||
track = true,
|
||||
wbr = true,
|
||||
}
|
||||
|
||||
local htmlencodeMap = {
|
||||
['>'] = '>',
|
||||
['<'] = '<',
|
||||
['&'] = '&',
|
||||
['"'] = '"',
|
||||
}
|
||||
|
||||
metatable.__index = methodtable
|
||||
|
||||
metatable.__tostring = function( t )
|
||||
local ret = {}
|
||||
t:_build( ret )
|
||||
return table.concat( ret )
|
||||
end
|
||||
|
||||
-- Get an attribute table (name, value) and its index
|
||||
--
|
||||
-- @param name
|
||||
local function getAttr( t, name )
|
||||
for i, attr in ipairs( t.attributes ) do
|
||||
if attr.name == name then
|
||||
return attr, i
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Is this a valid attribute name?
|
||||
--
|
||||
-- @param s
|
||||
local function isValidAttributeName( s )
|
||||
-- Good estimate: http://www.w3.org/TR/2000/REC-xml-20001006#NT-Name
|
||||
return s:match( '^[a-zA-Z_:][a-zA-Z0-9_.:-]*$' )
|
||||
end
|
||||
|
||||
-- Is this a valid tag name?
|
||||
--
|
||||
-- @param s
|
||||
local function isValidTag( s )
|
||||
return s:match( '^[a-zA-Z0-9]+$' )
|
||||
end
|
||||
|
||||
-- Escape a value, for use in HTML
|
||||
--
|
||||
-- @param s
|
||||
local function htmlEncode( s )
|
||||
-- The parentheses ensure that there is only one return value
|
||||
return ( string.gsub( s, '[<>&"]', htmlencodeMap ) )
|
||||
end
|
||||
|
||||
local function cssEncode( s )
|
||||
-- XXX: I'm not sure this character set is complete.
|
||||
-- bug #68011: allow delete character (\127)
|
||||
return mw.ustring.gsub( s, '[^\32-\57\60-\127]', function ( m )
|
||||
return string.format( '\\%X ', mw.ustring.codepoint( m ) )
|
||||
end )
|
||||
end
|
||||
|
||||
-- Create a builder object. This is a separate function so that we can show the
|
||||
-- correct error levels in both HtmlBuilder.create and metatable.tag.
|
||||
--
|
||||
-- @param tagName
|
||||
-- @param args
|
||||
local function createBuilder( tagName, args )
|
||||
if tagName ~= nil and tagName ~= '' and not isValidTag( tagName ) then
|
||||
error( string.format( "invalid tag name '%s'", tagName ), 3 )
|
||||
end
|
||||
|
||||
args = args or {}
|
||||
local builder = {}
|
||||
setmetatable( builder, metatable )
|
||||
builder.nodes = {}
|
||||
builder.attributes = {}
|
||||
builder.styles = {}
|
||||
|
||||
if tagName ~= '' then
|
||||
builder.tagName = tagName
|
||||
end
|
||||
|
||||
builder.parent = args.parent
|
||||
builder.selfClosing = selfClosingTags[tagName] or args.selfClosing or false
|
||||
return builder
|
||||
end
|
||||
|
||||
-- Append a builder to the current node. This is separate from methodtable.node
|
||||
-- so that we can show the correct error level in both methodtable.node and
|
||||
-- methodtable.wikitext.
|
||||
--
|
||||
-- @param builder
|
||||
local function appendBuilder( t, builder )
|
||||
if t.selfClosing then
|
||||
error( "self-closing tags can't have child nodes", 3 )
|
||||
end
|
||||
|
||||
if builder then
|
||||
table.insert( t.nodes, builder )
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
methodtable._build = function( t, ret )
|
||||
if t.tagName then
|
||||
table.insert( ret, '<' .. t.tagName )
|
||||
for i, attr in ipairs( t.attributes ) do
|
||||
table.insert(
|
||||
ret,
|
||||
-- Note: Attribute names have already been validated
|
||||
' ' .. attr.name .. '="' .. htmlEncode( attr.val ) .. '"'
|
||||
)
|
||||
end
|
||||
if #t.styles > 0 then
|
||||
table.insert( ret, ' style="' )
|
||||
local css = {}
|
||||
for i, prop in ipairs( t.styles ) do
|
||||
if type( prop ) ~= 'table' then -- added with cssText()
|
||||
table.insert( css, htmlEncode( prop ) )
|
||||
else -- added with css()
|
||||
table.insert(
|
||||
css,
|
||||
htmlEncode( cssEncode( prop.name ) .. ':' .. cssEncode( prop.val ) )
|
||||
)
|
||||
end
|
||||
end
|
||||
table.insert( ret, table.concat( css, ';' ) )
|
||||
table.insert( ret, '"' )
|
||||
end
|
||||
if t.selfClosing then
|
||||
table.insert( ret, ' />' )
|
||||
return
|
||||
end
|
||||
table.insert( ret, '>' )
|
||||
end
|
||||
for i, node in ipairs( t.nodes ) do
|
||||
if node then
|
||||
if type( node ) == 'table' then
|
||||
node:_build( ret )
|
||||
else
|
||||
table.insert( ret, tostring( node ) )
|
||||
end
|
||||
end
|
||||
end
|
||||
if t.tagName then
|
||||
table.insert( ret, '</' .. t.tagName .. '>' )
|
||||
end
|
||||
end
|
||||
|
||||
-- Append a builder to the current node
|
||||
--
|
||||
-- @param builder
|
||||
methodtable.node = function( t, builder )
|
||||
return appendBuilder( t, builder )
|
||||
end
|
||||
|
||||
-- Appends some markup to the node. This will be treated as wikitext.
|
||||
methodtable.wikitext = function( t, ... )
|
||||
local vals = {...}
|
||||
for i = 1, #vals do
|
||||
checkTypeMulti( 'wikitext', i, vals[i], { 'string', 'number' } )
|
||||
appendBuilder( t, vals[i] )
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
-- Appends a newline character to the node.
|
||||
methodtable.newline = function( t )
|
||||
t:wikitext( '\n' )
|
||||
return t
|
||||
end
|
||||
|
||||
-- Appends a new child node to the builder, and returns an HtmlBuilder instance
|
||||
-- representing that new node.
|
||||
--
|
||||
-- @param tagName
|
||||
-- @param args
|
||||
methodtable.tag = function( t, tagName, args )
|
||||
checkType( 'tag', 1, tagName, 'string' )
|
||||
checkType( 'tag', 2, args, 'table', true )
|
||||
args = args or {}
|
||||
|
||||
args.parent = t
|
||||
local builder = createBuilder( tagName, args )
|
||||
t:node( builder )
|
||||
return builder
|
||||
end
|
||||
|
||||
-- Get the value of an html attribute
|
||||
--
|
||||
-- @param name
|
||||
methodtable.getAttr = function( t, name )
|
||||
checkType( 'getAttr', 1, name, 'string' )
|
||||
|
||||
local attr = getAttr( t, name )
|
||||
if attr then
|
||||
return attr.val
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Set an HTML attribute on the node.
|
||||
--
|
||||
-- @param name Attribute to set, alternative table of name-value pairs
|
||||
-- @param val Value of the attribute. Nil causes the attribute to be unset
|
||||
methodtable.attr = function( t, name, val )
|
||||
if type( name ) == 'table' then
|
||||
if val ~= nil then
|
||||
error(
|
||||
"bad argument #2 to 'attr' " ..
|
||||
'(if argument #1 is a table, argument #2 must be left empty)',
|
||||
2
|
||||
)
|
||||
end
|
||||
|
||||
local callForTable = function()
|
||||
for attrName, attrValue in pairs( name ) do
|
||||
t:attr( attrName, attrValue )
|
||||
end
|
||||
end
|
||||
|
||||
if not pcall( callForTable ) then
|
||||
error(
|
||||
"bad argument #1 to 'attr' " ..
|
||||
'(table keys must be strings, and values must be strings or numbers)',
|
||||
2
|
||||
)
|
||||
end
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
checkType( 'attr', 1, name, 'string' )
|
||||
checkTypeMulti( 'attr', 2, val, { 'string', 'number', 'nil' } )
|
||||
|
||||
-- if caller sets the style attribute explicitly, then replace all styles
|
||||
-- previously added with css() and cssText()
|
||||
if name == 'style' then
|
||||
t.styles = { val }
|
||||
return t
|
||||
end
|
||||
|
||||
if not isValidAttributeName( name ) then
|
||||
error( string.format(
|
||||
"bad argument #1 to 'attr' (invalid attribute name '%s')",
|
||||
name
|
||||
), 2 )
|
||||
end
|
||||
|
||||
local attr, i = getAttr( t, name )
|
||||
if attr then
|
||||
if val ~= nil then
|
||||
attr.val = val
|
||||
else
|
||||
table.remove( t.attributes, i )
|
||||
end
|
||||
elseif val ~= nil then
|
||||
table.insert( t.attributes, { name = name, val = val } )
|
||||
end
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
-- Adds a class name to the node's class attribute. Spaces will be
|
||||
-- automatically added to delimit each added class name.
|
||||
--
|
||||
-- @param class
|
||||
methodtable.addClass = function( t, class )
|
||||
checkTypeMulti( 'addClass', 1, class, { 'string', 'number', 'nil' } )
|
||||
|
||||
if class == nil then
|
||||
return t
|
||||
end
|
||||
|
||||
local attr = getAttr( t, 'class' )
|
||||
if attr then
|
||||
attr.val = attr.val .. ' ' .. class
|
||||
else
|
||||
t:attr( 'class', class )
|
||||
end
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
-- Set a CSS property to be added to the node's style attribute.
|
||||
--
|
||||
-- @param name CSS attribute to set, alternative table of name-value pairs
|
||||
-- @param val The value to set. Nil causes it to be unset
|
||||
methodtable.css = function( t, name, val )
|
||||
if type( name ) == 'table' then
|
||||
if val ~= nil then
|
||||
error(
|
||||
"bad argument #2 to 'css' " ..
|
||||
'(if argument #1 is a table, argument #2 must be left empty)',
|
||||
2
|
||||
)
|
||||
end
|
||||
|
||||
local callForTable = function()
|
||||
for attrName, attrValue in pairs( name ) do
|
||||
t:css( attrName, attrValue )
|
||||
end
|
||||
end
|
||||
|
||||
if not pcall( callForTable ) then
|
||||
error(
|
||||
"bad argument #1 to 'css' " ..
|
||||
'(table keys and values must be strings or numbers)',
|
||||
2
|
||||
)
|
||||
end
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
checkTypeMulti( 'css', 1, name, { 'string', 'number' } )
|
||||
checkTypeMulti( 'css', 2, val, { 'string', 'number', 'nil' } )
|
||||
|
||||
for i, prop in ipairs( t.styles ) do
|
||||
if prop.name == name then
|
||||
if val ~= nil then
|
||||
prop.val = val
|
||||
else
|
||||
table.remove( t.styles, i )
|
||||
end
|
||||
return t
|
||||
end
|
||||
end
|
||||
|
||||
if val ~= nil then
|
||||
table.insert( t.styles, { name = name, val = val } )
|
||||
end
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
-- Add some raw CSS to the node's style attribute. This is typically used
|
||||
-- when a template allows some CSS to be passed in as a parameter
|
||||
--
|
||||
-- @param css
|
||||
methodtable.cssText = function( t, css )
|
||||
checkTypeMulti( 'cssText', 1, css, { 'string', 'number', 'nil' } )
|
||||
if css ~= nil then
|
||||
table.insert( t.styles, css )
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
-- Returns the parent node under which the current node was created. Like
|
||||
-- jQuery.end, this is a convenience function to allow the construction of
|
||||
-- several child nodes to be chained together into a single statement.
|
||||
methodtable.done = function( t )
|
||||
return t.parent or t
|
||||
end
|
||||
|
||||
-- Like .done(), but traverses all the way to the root node of the tree and
|
||||
-- returns it.
|
||||
methodtable.allDone = function( t )
|
||||
while t.parent do
|
||||
t = t.parent
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
-- Create a new instance
|
||||
--
|
||||
-- @param tagName
|
||||
-- @param args
|
||||
function HtmlBuilder.create( tagName, args )
|
||||
checkType( 'mw.html.create', 1, tagName, 'string', true )
|
||||
checkType( 'mw.html.create', 2, args, 'table', true )
|
||||
return createBuilder( tagName, args )
|
||||
end
|
||||
|
||||
mw_interface = nil
|
||||
|
||||
-- Register this library in the "mw" global
|
||||
mw = mw or {}
|
||||
mw.html = HtmlBuilder
|
||||
|
||||
package.loaded['mw.html'] = HtmlBuilder
|
||||
|
||||
return HtmlBuilder
|
||||
|
||||
@@ -1,197 +1,197 @@
|
||||
local language = {}
|
||||
local php
|
||||
local util = require 'libraryUtil'
|
||||
local init_site_for_lang_needed = true -- xowa
|
||||
|
||||
function language.setupInterface()
|
||||
-- Boilerplate
|
||||
language.setupInterface = nil
|
||||
php = mw_interface
|
||||
mw_interface = nil
|
||||
|
||||
-- Register this library in the "mw" global
|
||||
mw = mw or {}
|
||||
mw.language = language
|
||||
mw.getContentLanguage = language.getContentLanguage
|
||||
mw.getLanguage = mw.language.new
|
||||
|
||||
local lang = mw.getContentLanguage();
|
||||
|
||||
-- Extend ustring
|
||||
if mw.ustring then
|
||||
mw.ustring.upper = function ( s )
|
||||
return lang:uc( s )
|
||||
end
|
||||
mw.ustring.lower = function ( s )
|
||||
return lang:lc( s )
|
||||
end
|
||||
string.uupper = mw.ustring.upper
|
||||
string.ulower = mw.ustring.lower
|
||||
end
|
||||
|
||||
package.loaded['mw.language'] = language
|
||||
end
|
||||
|
||||
-- xowa:bgn
|
||||
function language.notify_lang_changed()
|
||||
init_site_for_lang_needed = true
|
||||
end
|
||||
-- xowa:end
|
||||
|
||||
function language.isSupportedLanguage( code )
|
||||
return php.isSupportedLanguage( code )
|
||||
end
|
||||
|
||||
function language.isKnownLanguageTag( code )
|
||||
return php.isKnownLanguageTag( code )
|
||||
end
|
||||
|
||||
function language.isValidCode( code )
|
||||
return php.isValidCode( code )
|
||||
end
|
||||
|
||||
function language.isValidBuiltInCode( code )
|
||||
return php.isValidBuiltInCode( code )
|
||||
end
|
||||
|
||||
function language.fetchLanguageName( code, inLanguage )
|
||||
return php.fetchLanguageName( code, inLanguage )
|
||||
end
|
||||
|
||||
function language.fetchLanguageNames( inLanguage, include )
|
||||
return php.fetchLanguageNames( inLanguage, include )
|
||||
end
|
||||
|
||||
function language.getFallbacksFor( code )
|
||||
return php.getFallbacksFor( code )
|
||||
end
|
||||
|
||||
function language.new( code )
|
||||
if code == nil then
|
||||
error( "too few arguments to mw.language.new()", 2 )
|
||||
end
|
||||
|
||||
local lang = { code = code }
|
||||
|
||||
local checkSelf = util.makeCheckSelfFunction( 'mw.language', 'lang', lang, 'language object' )
|
||||
|
||||
local wrappers = {
|
||||
lcfirst = 1,
|
||||
ucfirst = 1,
|
||||
lc = 1,
|
||||
uc = 1,
|
||||
caseFold = 1,
|
||||
formatNum = 1,
|
||||
formatDate = 1,
|
||||
formatDuration = 1,
|
||||
getDurationIntervals = 1,
|
||||
convertPlural = 2,
|
||||
convertGrammar = 2,
|
||||
gender = 2,
|
||||
}
|
||||
|
||||
for name, numArgs in pairs( wrappers ) do
|
||||
lang[name] = function ( self, ... )
|
||||
checkSelf( self, name )
|
||||
if select( '#', ... ) < numArgs then
|
||||
error( "too few arguments to mw.language:" .. name, 2 )
|
||||
end
|
||||
return php[name]( self.code, ... )
|
||||
end
|
||||
end
|
||||
|
||||
-- This one could use caching
|
||||
function lang:isRTL()
|
||||
checkSelf( self, 'isRTL' )
|
||||
local rtl = php.isRTL( self.code )
|
||||
self.isRTL = function ()
|
||||
return rtl
|
||||
end
|
||||
return rtl
|
||||
end
|
||||
|
||||
-- Fix semantics
|
||||
function lang:parseFormattedNumber( ... )
|
||||
checkSelf( self, 'parseFormattedNumber' )
|
||||
if select( '#', ... ) < 1 then
|
||||
error( "too few arguments to mw.language:parseFormattedNumber", 2 )
|
||||
end
|
||||
return tonumber( php.parseFormattedNumber( self.code, ... ) )
|
||||
end
|
||||
|
||||
-- Alias
|
||||
lang.plural = lang.convertPlural
|
||||
|
||||
-- Parser function compat
|
||||
function lang:grammar( case, word )
|
||||
checkSelf( self, name )
|
||||
return self:convertGrammar( word, case )
|
||||
end
|
||||
|
||||
-- Other functions
|
||||
function lang:getCode()
|
||||
checkSelf( self, 'getCode' )
|
||||
return self.code
|
||||
end
|
||||
|
||||
function lang:getDir()
|
||||
checkSelf( self, 'getDir' )
|
||||
return self:isRTL() and 'rtl' or 'ltr'
|
||||
end
|
||||
|
||||
function lang:getDirMark( opposite )
|
||||
checkSelf( self, 'getDirMark' )
|
||||
local b = self:isRTL()
|
||||
if opposite then
|
||||
b = not b
|
||||
end
|
||||
return b and '\226\128\143' or '\226\128\142'
|
||||
end
|
||||
|
||||
function lang:getDirMarkEntity( opposite )
|
||||
checkSelf( self, 'getDirMarkEntity' )
|
||||
local b = self:isRTL()
|
||||
if opposite then
|
||||
b = not b
|
||||
end
|
||||
return b and '‏' or '‎'
|
||||
end
|
||||
|
||||
function lang:getArrow( direction )
|
||||
checkSelf( self, 'getArrow' )
|
||||
direction = direction or 'forwards'
|
||||
util.checkType( 'getArrow', 1, direction, 'string' )
|
||||
if direction == 'forwards' then
|
||||
return self:isRTL() and '←' or '→'
|
||||
elseif direction == 'backwards' then
|
||||
return self:isRTL() and '→' or '←'
|
||||
elseif direction == 'left' then
|
||||
return '←'
|
||||
elseif direction == 'right' then
|
||||
return '→'
|
||||
elseif direction == 'up' then
|
||||
return '↑'
|
||||
elseif direction == 'down' then
|
||||
return '↓'
|
||||
end
|
||||
end
|
||||
|
||||
function lang:getFallbackLanguages()
|
||||
checkSelf( self, 'getFallbackLanguages' )
|
||||
return language.getFallbacksFor( self.code )
|
||||
end
|
||||
|
||||
return lang
|
||||
end
|
||||
|
||||
local contLangCode
|
||||
|
||||
function language.getContentLanguage()
|
||||
if init_site_for_lang_needed then -- xowa; was contLangCode == nil
|
||||
contLangCode = php.getContLangCode()
|
||||
init_site_for_lang_needed = false -- xowa
|
||||
end
|
||||
return language.new( contLangCode )
|
||||
end
|
||||
|
||||
return language
|
||||
local language = {}
|
||||
local php
|
||||
local util = require 'libraryUtil'
|
||||
local init_site_for_lang_needed = true -- xowa
|
||||
|
||||
function language.setupInterface()
|
||||
-- Boilerplate
|
||||
language.setupInterface = nil
|
||||
php = mw_interface
|
||||
mw_interface = nil
|
||||
|
||||
-- Register this library in the "mw" global
|
||||
mw = mw or {}
|
||||
mw.language = language
|
||||
mw.getContentLanguage = language.getContentLanguage
|
||||
mw.getLanguage = mw.language.new
|
||||
|
||||
local lang = mw.getContentLanguage();
|
||||
|
||||
-- Extend ustring
|
||||
if mw.ustring then
|
||||
mw.ustring.upper = function ( s )
|
||||
return lang:uc( s )
|
||||
end
|
||||
mw.ustring.lower = function ( s )
|
||||
return lang:lc( s )
|
||||
end
|
||||
string.uupper = mw.ustring.upper
|
||||
string.ulower = mw.ustring.lower
|
||||
end
|
||||
|
||||
package.loaded['mw.language'] = language
|
||||
end
|
||||
|
||||
-- xowa:bgn
|
||||
function language.notify_lang_changed()
|
||||
init_site_for_lang_needed = true
|
||||
end
|
||||
-- xowa:end
|
||||
|
||||
function language.isSupportedLanguage( code )
|
||||
return php.isSupportedLanguage( code )
|
||||
end
|
||||
|
||||
function language.isKnownLanguageTag( code )
|
||||
return php.isKnownLanguageTag( code )
|
||||
end
|
||||
|
||||
function language.isValidCode( code )
|
||||
return php.isValidCode( code )
|
||||
end
|
||||
|
||||
function language.isValidBuiltInCode( code )
|
||||
return php.isValidBuiltInCode( code )
|
||||
end
|
||||
|
||||
function language.fetchLanguageName( code, inLanguage )
|
||||
return php.fetchLanguageName( code, inLanguage )
|
||||
end
|
||||
|
||||
function language.fetchLanguageNames( inLanguage, include )
|
||||
return php.fetchLanguageNames( inLanguage, include )
|
||||
end
|
||||
|
||||
function language.getFallbacksFor( code )
|
||||
return php.getFallbacksFor( code )
|
||||
end
|
||||
|
||||
function language.new( code )
|
||||
if code == nil then
|
||||
error( "too few arguments to mw.language.new()", 2 )
|
||||
end
|
||||
|
||||
local lang = { code = code }
|
||||
|
||||
local checkSelf = util.makeCheckSelfFunction( 'mw.language', 'lang', lang, 'language object' )
|
||||
|
||||
local wrappers = {
|
||||
lcfirst = 1,
|
||||
ucfirst = 1,
|
||||
lc = 1,
|
||||
uc = 1,
|
||||
caseFold = 1,
|
||||
formatNum = 1,
|
||||
formatDate = 1,
|
||||
formatDuration = 1,
|
||||
getDurationIntervals = 1,
|
||||
convertPlural = 2,
|
||||
convertGrammar = 2,
|
||||
gender = 2,
|
||||
}
|
||||
|
||||
for name, numArgs in pairs( wrappers ) do
|
||||
lang[name] = function ( self, ... )
|
||||
checkSelf( self, name )
|
||||
if select( '#', ... ) < numArgs then
|
||||
error( "too few arguments to mw.language:" .. name, 2 )
|
||||
end
|
||||
return php[name]( self.code, ... )
|
||||
end
|
||||
end
|
||||
|
||||
-- This one could use caching
|
||||
function lang:isRTL()
|
||||
checkSelf( self, 'isRTL' )
|
||||
local rtl = php.isRTL( self.code )
|
||||
self.isRTL = function ()
|
||||
return rtl
|
||||
end
|
||||
return rtl
|
||||
end
|
||||
|
||||
-- Fix semantics
|
||||
function lang:parseFormattedNumber( ... )
|
||||
checkSelf( self, 'parseFormattedNumber' )
|
||||
if select( '#', ... ) < 1 then
|
||||
error( "too few arguments to mw.language:parseFormattedNumber", 2 )
|
||||
end
|
||||
return tonumber( php.parseFormattedNumber( self.code, ... ) )
|
||||
end
|
||||
|
||||
-- Alias
|
||||
lang.plural = lang.convertPlural
|
||||
|
||||
-- Parser function compat
|
||||
function lang:grammar( case, word )
|
||||
checkSelf( self, name )
|
||||
return self:convertGrammar( word, case )
|
||||
end
|
||||
|
||||
-- Other functions
|
||||
function lang:getCode()
|
||||
checkSelf( self, 'getCode' )
|
||||
return self.code
|
||||
end
|
||||
|
||||
function lang:getDir()
|
||||
checkSelf( self, 'getDir' )
|
||||
return self:isRTL() and 'rtl' or 'ltr'
|
||||
end
|
||||
|
||||
function lang:getDirMark( opposite )
|
||||
checkSelf( self, 'getDirMark' )
|
||||
local b = self:isRTL()
|
||||
if opposite then
|
||||
b = not b
|
||||
end
|
||||
return b and '\226\128\143' or '\226\128\142'
|
||||
end
|
||||
|
||||
function lang:getDirMarkEntity( opposite )
|
||||
checkSelf( self, 'getDirMarkEntity' )
|
||||
local b = self:isRTL()
|
||||
if opposite then
|
||||
b = not b
|
||||
end
|
||||
return b and '‏' or '‎'
|
||||
end
|
||||
|
||||
function lang:getArrow( direction )
|
||||
checkSelf( self, 'getArrow' )
|
||||
direction = direction or 'forwards'
|
||||
util.checkType( 'getArrow', 1, direction, 'string' )
|
||||
if direction == 'forwards' then
|
||||
return self:isRTL() and '←' or '→'
|
||||
elseif direction == 'backwards' then
|
||||
return self:isRTL() and '→' or '←'
|
||||
elseif direction == 'left' then
|
||||
return '←'
|
||||
elseif direction == 'right' then
|
||||
return '→'
|
||||
elseif direction == 'up' then
|
||||
return '↑'
|
||||
elseif direction == 'down' then
|
||||
return '↓'
|
||||
end
|
||||
end
|
||||
|
||||
function lang:getFallbackLanguages()
|
||||
checkSelf( self, 'getFallbackLanguages' )
|
||||
return language.getFallbacksFor( self.code )
|
||||
end
|
||||
|
||||
return lang
|
||||
end
|
||||
|
||||
local contLangCode
|
||||
|
||||
function language.getContentLanguage()
|
||||
if init_site_for_lang_needed then -- xowa; was contLangCode == nil
|
||||
contLangCode = php.getContLangCode()
|
||||
init_site_for_lang_needed = false -- xowa
|
||||
end
|
||||
return language.new( contLangCode )
|
||||
end
|
||||
|
||||
return language
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,220 +1,220 @@
|
||||
local message = {}
|
||||
local php
|
||||
|
||||
local util = require 'libraryUtil'
|
||||
local checkType = util.checkType
|
||||
local init_message_for_lang_needed = true -- xowa
|
||||
|
||||
local valuemt = {
|
||||
__tostring = function ( t )
|
||||
return tostring( t.raw or t.num )
|
||||
end
|
||||
}
|
||||
|
||||
local function checkScalar( name, argIdx, arg, level, valuemtOk )
|
||||
local tp = type( arg )
|
||||
|
||||
-- If special params are ok, detect them
|
||||
if valuemtOk and tp == 'table' and getmetatable( arg ) == valuemt then
|
||||
return arg
|
||||
end
|
||||
|
||||
-- If it's a table with a custom __tostring function, use that string
|
||||
if tp == 'table' and getmetatable( arg ) and getmetatable( arg ).__tostring then
|
||||
return tostring( arg )
|
||||
end
|
||||
|
||||
if tp ~= 'string' and tp ~= 'number' then
|
||||
error( string.format(
|
||||
"bad argument #%d to '%s' (string or number expected, got %s)",
|
||||
argIdx, name, tp
|
||||
), level + 1 )
|
||||
end
|
||||
|
||||
return arg
|
||||
end
|
||||
|
||||
local function checkParams( name, valueOk, ... )
|
||||
-- Accept an array of params, or params as individual command line arguments
|
||||
local params, nparams
|
||||
local first = select( 1, ... )
|
||||
if type( first ) == 'table' and
|
||||
not ( getmetatable( first ) and getmetatable( first ).__tostring )
|
||||
then
|
||||
if select( '#', ... ) == 1 then
|
||||
params = first
|
||||
nparams = table.maxn( params )
|
||||
else
|
||||
error(
|
||||
"bad arguments to '" .. name .. "' (pass either a table of params or params as individual arguments)",
|
||||
3
|
||||
)
|
||||
end
|
||||
else
|
||||
params = { ... }
|
||||
nparams = select( '#', ... )
|
||||
end
|
||||
for i = 1, nparams do
|
||||
params[i] = checkScalar( 'params', i, params[i], 3, valueOk )
|
||||
end
|
||||
return params
|
||||
end
|
||||
|
||||
function message.setupInterface( options )
|
||||
-- Boilerplate
|
||||
message.setupInterface = nil
|
||||
php = mw_interface
|
||||
mw_interface = nil
|
||||
php.options = message.make_proxy(options) -- xowa
|
||||
|
||||
-- Register this library in the "mw" global
|
||||
mw = mw or {}
|
||||
mw.message = message
|
||||
|
||||
package.loaded['mw.message'] = message
|
||||
end
|
||||
|
||||
local function makeMessage( options )
|
||||
local obj = {}
|
||||
local checkSelf = util.makeCheckSelfFunction( 'mw.message', 'msg', obj, 'message object' )
|
||||
|
||||
local data = {
|
||||
keys = options.keys,
|
||||
rawMessage = options.rawMessage,
|
||||
params = {},
|
||||
lang = php.options.lang,
|
||||
useDB = true,
|
||||
}
|
||||
|
||||
function obj:params( ... )
|
||||
checkSelf( self, 'params' )
|
||||
local params = checkParams( 'params', true, ... )
|
||||
local j = #data.params
|
||||
for i = 1, #params do
|
||||
data.params[j + i] = params[i]
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
function obj:rawParams( ... )
|
||||
checkSelf( self, 'rawParams' )
|
||||
local params = checkParams( 'rawParams', false, ... )
|
||||
local j = #data.params
|
||||
for i = 1, #params do
|
||||
data.params[j + i] = setmetatable( { raw = params[i] }, valuemt )
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
function obj:numParams( ... )
|
||||
checkSelf( self, 'numParams' )
|
||||
local params = checkParams( 'numParams', false, ... )
|
||||
local j = #data.params
|
||||
for i = 1, #params do
|
||||
data.params[j + i] = setmetatable( { num = params[i] }, valuemt )
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
function obj:inLanguage( lang )
|
||||
checkSelf( self, 'inLanguage' )
|
||||
if type( lang ) == 'table' and lang.getCode then
|
||||
-- probably a mw.language object
|
||||
lang = lang:getCode()
|
||||
end
|
||||
checkType( 'inLanguage', 1, lang, 'string' )
|
||||
data.lang = lang
|
||||
return self
|
||||
end
|
||||
|
||||
function obj:useDatabase( value )
|
||||
checkSelf( self, 'useDatabase' )
|
||||
checkType( 'useDatabase', 1, value, 'boolean' )
|
||||
data.useDB = value
|
||||
return self
|
||||
end
|
||||
|
||||
function obj:plain()
|
||||
checkSelf( self, 'plain' )
|
||||
return php.plain( data )
|
||||
end
|
||||
|
||||
function obj:exists()
|
||||
checkSelf( self, 'exists' )
|
||||
return php.check( 'exists', data )
|
||||
end
|
||||
|
||||
function obj:isBlank()
|
||||
checkSelf( self, 'isBlank' )
|
||||
return php.check( 'isBlank', data )
|
||||
end
|
||||
|
||||
function obj:isDisabled()
|
||||
checkSelf( self, 'isDisabled' )
|
||||
return php.check( 'isDisabled', data )
|
||||
end
|
||||
|
||||
return setmetatable( obj, {
|
||||
__tostring = function ( t )
|
||||
return t:plain()
|
||||
end
|
||||
} )
|
||||
end
|
||||
|
||||
function message.new( key, ... )
|
||||
checkType( 'message.new', 1, key, 'string' )
|
||||
return makeMessage{ keys = { key } }:params( ... )
|
||||
end
|
||||
|
||||
function message.newFallbackSequence( ... )
|
||||
for i = 1, math.max( 1, select( '#', ... ) ) do
|
||||
checkType( 'message.newFallbackSequence', i, select( i, ... ), 'string' )
|
||||
end
|
||||
return makeMessage{ keys = { ... } }
|
||||
end
|
||||
|
||||
function message.newRawMessage( msg, ... )
|
||||
checkType( 'message.newRawMessage', 1, msg, 'string' )
|
||||
return makeMessage{ rawMessage = msg }:params( ... )
|
||||
end
|
||||
|
||||
function message.rawParam( value )
|
||||
value = checkScalar( 'message.rawParam', 1, value )
|
||||
return setmetatable( { raw = value }, valuemt )
|
||||
end
|
||||
|
||||
function message.numParam( value )
|
||||
value = checkScalar( 'message.numParam', 1, value )
|
||||
return setmetatable( { num = value }, valuemt )
|
||||
end
|
||||
|
||||
function message.getDefaultLanguage()
|
||||
if mw.language then
|
||||
return mw.language.new( php.options.lang )
|
||||
else
|
||||
return php.options.lang
|
||||
end
|
||||
end
|
||||
|
||||
-- xowa:bgn
|
||||
function message.notify_lang_changed()
|
||||
init_message_for_lang_needed = true
|
||||
end
|
||||
|
||||
function message.make_proxy(real)
|
||||
local proxy = {}
|
||||
setmetatable(proxy, {__index =
|
||||
function(tbl, key)
|
||||
if key == "lang" and init_message_for_lang_needed then
|
||||
real[key] = php.init_message_for_lang().lang
|
||||
init_message_for_lang_needed = false
|
||||
end
|
||||
return real[key]
|
||||
end
|
||||
}
|
||||
)
|
||||
return proxy
|
||||
end
|
||||
-- xowa:end
|
||||
|
||||
return message
|
||||
local message = {}
|
||||
local php
|
||||
|
||||
local util = require 'libraryUtil'
|
||||
local checkType = util.checkType
|
||||
local init_message_for_lang_needed = true -- xowa
|
||||
|
||||
local valuemt = {
|
||||
__tostring = function ( t )
|
||||
return tostring( t.raw or t.num )
|
||||
end
|
||||
}
|
||||
|
||||
local function checkScalar( name, argIdx, arg, level, valuemtOk )
|
||||
local tp = type( arg )
|
||||
|
||||
-- If special params are ok, detect them
|
||||
if valuemtOk and tp == 'table' and getmetatable( arg ) == valuemt then
|
||||
return arg
|
||||
end
|
||||
|
||||
-- If it's a table with a custom __tostring function, use that string
|
||||
if tp == 'table' and getmetatable( arg ) and getmetatable( arg ).__tostring then
|
||||
return tostring( arg )
|
||||
end
|
||||
|
||||
if tp ~= 'string' and tp ~= 'number' then
|
||||
error( string.format(
|
||||
"bad argument #%d to '%s' (string or number expected, got %s)",
|
||||
argIdx, name, tp
|
||||
), level + 1 )
|
||||
end
|
||||
|
||||
return arg
|
||||
end
|
||||
|
||||
local function checkParams( name, valueOk, ... )
|
||||
-- Accept an array of params, or params as individual command line arguments
|
||||
local params, nparams
|
||||
local first = select( 1, ... )
|
||||
if type( first ) == 'table' and
|
||||
not ( getmetatable( first ) and getmetatable( first ).__tostring )
|
||||
then
|
||||
if select( '#', ... ) == 1 then
|
||||
params = first
|
||||
nparams = table.maxn( params )
|
||||
else
|
||||
error(
|
||||
"bad arguments to '" .. name .. "' (pass either a table of params or params as individual arguments)",
|
||||
3
|
||||
)
|
||||
end
|
||||
else
|
||||
params = { ... }
|
||||
nparams = select( '#', ... )
|
||||
end
|
||||
for i = 1, nparams do
|
||||
params[i] = checkScalar( 'params', i, params[i], 3, valueOk )
|
||||
end
|
||||
return params
|
||||
end
|
||||
|
||||
function message.setupInterface( options )
|
||||
-- Boilerplate
|
||||
message.setupInterface = nil
|
||||
php = mw_interface
|
||||
mw_interface = nil
|
||||
php.options = message.make_proxy(options) -- xowa
|
||||
|
||||
-- Register this library in the "mw" global
|
||||
mw = mw or {}
|
||||
mw.message = message
|
||||
|
||||
package.loaded['mw.message'] = message
|
||||
end
|
||||
|
||||
local function makeMessage( options )
|
||||
local obj = {}
|
||||
local checkSelf = util.makeCheckSelfFunction( 'mw.message', 'msg', obj, 'message object' )
|
||||
|
||||
local data = {
|
||||
keys = options.keys,
|
||||
rawMessage = options.rawMessage,
|
||||
params = {},
|
||||
lang = php.options.lang,
|
||||
useDB = true,
|
||||
}
|
||||
|
||||
function obj:params( ... )
|
||||
checkSelf( self, 'params' )
|
||||
local params = checkParams( 'params', true, ... )
|
||||
local j = #data.params
|
||||
for i = 1, #params do
|
||||
data.params[j + i] = params[i]
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
function obj:rawParams( ... )
|
||||
checkSelf( self, 'rawParams' )
|
||||
local params = checkParams( 'rawParams', false, ... )
|
||||
local j = #data.params
|
||||
for i = 1, #params do
|
||||
data.params[j + i] = setmetatable( { raw = params[i] }, valuemt )
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
function obj:numParams( ... )
|
||||
checkSelf( self, 'numParams' )
|
||||
local params = checkParams( 'numParams', false, ... )
|
||||
local j = #data.params
|
||||
for i = 1, #params do
|
||||
data.params[j + i] = setmetatable( { num = params[i] }, valuemt )
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
function obj:inLanguage( lang )
|
||||
checkSelf( self, 'inLanguage' )
|
||||
if type( lang ) == 'table' and lang.getCode then
|
||||
-- probably a mw.language object
|
||||
lang = lang:getCode()
|
||||
end
|
||||
checkType( 'inLanguage', 1, lang, 'string' )
|
||||
data.lang = lang
|
||||
return self
|
||||
end
|
||||
|
||||
function obj:useDatabase( value )
|
||||
checkSelf( self, 'useDatabase' )
|
||||
checkType( 'useDatabase', 1, value, 'boolean' )
|
||||
data.useDB = value
|
||||
return self
|
||||
end
|
||||
|
||||
function obj:plain()
|
||||
checkSelf( self, 'plain' )
|
||||
return php.plain( data )
|
||||
end
|
||||
|
||||
function obj:exists()
|
||||
checkSelf( self, 'exists' )
|
||||
return php.check( 'exists', data )
|
||||
end
|
||||
|
||||
function obj:isBlank()
|
||||
checkSelf( self, 'isBlank' )
|
||||
return php.check( 'isBlank', data )
|
||||
end
|
||||
|
||||
function obj:isDisabled()
|
||||
checkSelf( self, 'isDisabled' )
|
||||
return php.check( 'isDisabled', data )
|
||||
end
|
||||
|
||||
return setmetatable( obj, {
|
||||
__tostring = function ( t )
|
||||
return t:plain()
|
||||
end
|
||||
} )
|
||||
end
|
||||
|
||||
function message.new( key, ... )
|
||||
checkType( 'message.new', 1, key, 'string' )
|
||||
return makeMessage{ keys = { key } }:params( ... )
|
||||
end
|
||||
|
||||
function message.newFallbackSequence( ... )
|
||||
for i = 1, math.max( 1, select( '#', ... ) ) do
|
||||
checkType( 'message.newFallbackSequence', i, select( i, ... ), 'string' )
|
||||
end
|
||||
return makeMessage{ keys = { ... } }
|
||||
end
|
||||
|
||||
function message.newRawMessage( msg, ... )
|
||||
checkType( 'message.newRawMessage', 1, msg, 'string' )
|
||||
return makeMessage{ rawMessage = msg }:params( ... )
|
||||
end
|
||||
|
||||
function message.rawParam( value )
|
||||
value = checkScalar( 'message.rawParam', 1, value )
|
||||
return setmetatable( { raw = value }, valuemt )
|
||||
end
|
||||
|
||||
function message.numParam( value )
|
||||
value = checkScalar( 'message.numParam', 1, value )
|
||||
return setmetatable( { num = value }, valuemt )
|
||||
end
|
||||
|
||||
function message.getDefaultLanguage()
|
||||
if mw.language then
|
||||
return mw.language.new( php.options.lang )
|
||||
else
|
||||
return php.options.lang
|
||||
end
|
||||
end
|
||||
|
||||
-- xowa:bgn
|
||||
function message.notify_lang_changed()
|
||||
init_message_for_lang_needed = true
|
||||
end
|
||||
|
||||
function message.make_proxy(real)
|
||||
local proxy = {}
|
||||
setmetatable(proxy, {__index =
|
||||
function(tbl, key)
|
||||
if key == "lang" and init_message_for_lang_needed then
|
||||
real[key] = php.init_message_for_lang().lang
|
||||
init_message_for_lang_needed = false
|
||||
end
|
||||
return real[key]
|
||||
end
|
||||
}
|
||||
)
|
||||
return proxy
|
||||
end
|
||||
-- xowa:end
|
||||
|
||||
return message
|
||||
|
||||
@@ -1,141 +1,141 @@
|
||||
local site = {}
|
||||
local init_site_for_wiki_needed = true -- xowa
|
||||
local php
|
||||
|
||||
function site.setupInterface( info )
|
||||
-- Boilerplate
|
||||
site.setupInterface = nil
|
||||
php = mw_interface
|
||||
mw_interface = nil
|
||||
|
||||
-- site.setupSite(info) -- xowa
|
||||
|
||||
-- Register this library in the "mw" global
|
||||
mw = mw or {}
|
||||
mw.site = site.make_proxy(site) -- xowa
|
||||
|
||||
package.loaded['mw.site'] = site
|
||||
end
|
||||
|
||||
-- xowa:bgn
|
||||
function site.notify_wiki_changed()
|
||||
init_site_for_wiki_needed = true
|
||||
end
|
||||
|
||||
function site.make_proxy(real)
|
||||
local proxy = {}
|
||||
setmetatable(proxy, {__index =
|
||||
function(tbl, key)
|
||||
if init_site_for_wiki_needed then
|
||||
info = php.init_site_for_wiki() -- more performant than calling setupInterface for every page load
|
||||
site.setupSite(info)
|
||||
init_site_for_wiki_needed = false
|
||||
end
|
||||
return real[key]
|
||||
end
|
||||
}
|
||||
)
|
||||
return proxy
|
||||
end
|
||||
function site.setupSite( info )
|
||||
site.siteName = info.siteName
|
||||
site.server = info.server
|
||||
site.scriptPath = info.scriptPath
|
||||
site.stylePath = info.stylePath
|
||||
site.currentVersion = info.currentVersion
|
||||
site.stats = info.stats
|
||||
site.stats.pagesInCategory = php.pagesInCategory
|
||||
site.stats.pagesInNamespace = php.pagesInNamespace
|
||||
site.stats.usersInGroup = php.usersInGroup
|
||||
site.interwikiMap = php.interwikiMap
|
||||
|
||||
-- Process namespace list into more useful tables
|
||||
site.namespaces = {}
|
||||
local namespacesByName = {}
|
||||
site.subjectNamespaces = {}
|
||||
site.talkNamespaces = {}
|
||||
site.contentNamespaces = {}
|
||||
for ns, data in pairs( info.namespaces ) do
|
||||
data.subject = info.namespaces[data.subject]
|
||||
data.talk = info.namespaces[data.talk]
|
||||
data.associated = info.namespaces[data.associated]
|
||||
|
||||
site.namespaces[ns] = data
|
||||
|
||||
namespacesByName[data.name] = data
|
||||
if data.canonicalName then
|
||||
namespacesByName[data.canonicalName] = data
|
||||
end
|
||||
for i = 1, #data.aliases do
|
||||
namespacesByName[data.aliases[i]] = data
|
||||
end
|
||||
|
||||
if data.isSubject then
|
||||
site.subjectNamespaces[ns] = data
|
||||
end
|
||||
if data.isTalk then
|
||||
site.talkNamespaces[ns] = data
|
||||
end
|
||||
if data.isContent then
|
||||
site.contentNamespaces[ns] = data
|
||||
end
|
||||
end
|
||||
|
||||
-- Set __index for namespacesByName to handle names-with-underscores
|
||||
-- and non-standard case
|
||||
local getNsIndex = php.getNsIndex
|
||||
setmetatable( namespacesByName, {
|
||||
__index = function ( t, k )
|
||||
if type( k ) == 'string' then
|
||||
-- Try with fixed underscores
|
||||
k = string.gsub( k, '_', ' ' )
|
||||
if rawget( t, k ) then
|
||||
return rawget( t, k )
|
||||
end
|
||||
|
||||
-- Ask PHP, because names are case-insensitive
|
||||
local ns = getNsIndex( k )
|
||||
if ns then
|
||||
rawset( t, k, site.namespaces[ns] )
|
||||
end
|
||||
end
|
||||
return rawget( t, k )
|
||||
end
|
||||
} )
|
||||
|
||||
-- Set namespacesByName as the lookup table for site.namespaces, so
|
||||
-- something like site.namespaces.Wikipedia works without having
|
||||
-- pairs( site.namespaces ) iterate all those names.
|
||||
setmetatable( site.namespaces, { __index = namespacesByName } )
|
||||
|
||||
-- Copy the site stats, and set up the metatable to load them if necessary.
|
||||
local loadSiteStats = php.loadSiteStats
|
||||
site.stats.x = php.loadSiteStats
|
||||
if info.stats then
|
||||
loadSiteStats = nil
|
||||
for k, v in pairs( info.stats ) do
|
||||
site.stats[k] = v
|
||||
end
|
||||
end
|
||||
setmetatable( site.stats, {
|
||||
__index = function ( t, k )
|
||||
if t ~= site.stats then -- cloned
|
||||
return site.stats[k]
|
||||
end
|
||||
|
||||
if k == 'admins' then
|
||||
t.admins = t.usersInGroup( 'sysop' )
|
||||
elseif loadSiteStats then
|
||||
for k, v in pairs( loadSiteStats() ) do
|
||||
t[k] = v
|
||||
end
|
||||
loadSiteStats = nil
|
||||
end
|
||||
|
||||
return rawget( t, k )
|
||||
end
|
||||
} )
|
||||
end
|
||||
-- xowa:end
|
||||
|
||||
return site
|
||||
local site = {}
|
||||
local init_site_for_wiki_needed = true -- xowa
|
||||
local php
|
||||
|
||||
function site.setupInterface( info )
|
||||
-- Boilerplate
|
||||
site.setupInterface = nil
|
||||
php = mw_interface
|
||||
mw_interface = nil
|
||||
|
||||
-- site.setupSite(info) -- xowa
|
||||
|
||||
-- Register this library in the "mw" global
|
||||
mw = mw or {}
|
||||
mw.site = site.make_proxy(site) -- xowa
|
||||
|
||||
package.loaded['mw.site'] = site
|
||||
end
|
||||
|
||||
-- xowa:bgn
|
||||
function site.notify_wiki_changed()
|
||||
init_site_for_wiki_needed = true
|
||||
end
|
||||
|
||||
function site.make_proxy(real)
|
||||
local proxy = {}
|
||||
setmetatable(proxy, {__index =
|
||||
function(tbl, key)
|
||||
if init_site_for_wiki_needed then
|
||||
info = php.init_site_for_wiki() -- more performant than calling setupInterface for every page load
|
||||
site.setupSite(info)
|
||||
init_site_for_wiki_needed = false
|
||||
end
|
||||
return real[key]
|
||||
end
|
||||
}
|
||||
)
|
||||
return proxy
|
||||
end
|
||||
function site.setupSite( info )
|
||||
site.siteName = info.siteName
|
||||
site.server = info.server
|
||||
site.scriptPath = info.scriptPath
|
||||
site.stylePath = info.stylePath
|
||||
site.currentVersion = info.currentVersion
|
||||
site.stats = info.stats
|
||||
site.stats.pagesInCategory = php.pagesInCategory
|
||||
site.stats.pagesInNamespace = php.pagesInNamespace
|
||||
site.stats.usersInGroup = php.usersInGroup
|
||||
site.interwikiMap = php.interwikiMap
|
||||
|
||||
-- Process namespace list into more useful tables
|
||||
site.namespaces = {}
|
||||
local namespacesByName = {}
|
||||
site.subjectNamespaces = {}
|
||||
site.talkNamespaces = {}
|
||||
site.contentNamespaces = {}
|
||||
for ns, data in pairs( info.namespaces ) do
|
||||
data.subject = info.namespaces[data.subject]
|
||||
data.talk = info.namespaces[data.talk]
|
||||
data.associated = info.namespaces[data.associated]
|
||||
|
||||
site.namespaces[ns] = data
|
||||
|
||||
namespacesByName[data.name] = data
|
||||
if data.canonicalName then
|
||||
namespacesByName[data.canonicalName] = data
|
||||
end
|
||||
for i = 1, #data.aliases do
|
||||
namespacesByName[data.aliases[i]] = data
|
||||
end
|
||||
|
||||
if data.isSubject then
|
||||
site.subjectNamespaces[ns] = data
|
||||
end
|
||||
if data.isTalk then
|
||||
site.talkNamespaces[ns] = data
|
||||
end
|
||||
if data.isContent then
|
||||
site.contentNamespaces[ns] = data
|
||||
end
|
||||
end
|
||||
|
||||
-- Set __index for namespacesByName to handle names-with-underscores
|
||||
-- and non-standard case
|
||||
local getNsIndex = php.getNsIndex
|
||||
setmetatable( namespacesByName, {
|
||||
__index = function ( t, k )
|
||||
if type( k ) == 'string' then
|
||||
-- Try with fixed underscores
|
||||
k = string.gsub( k, '_', ' ' )
|
||||
if rawget( t, k ) then
|
||||
return rawget( t, k )
|
||||
end
|
||||
|
||||
-- Ask PHP, because names are case-insensitive
|
||||
local ns = getNsIndex( k )
|
||||
if ns then
|
||||
rawset( t, k, site.namespaces[ns] )
|
||||
end
|
||||
end
|
||||
return rawget( t, k )
|
||||
end
|
||||
} )
|
||||
|
||||
-- Set namespacesByName as the lookup table for site.namespaces, so
|
||||
-- something like site.namespaces.Wikipedia works without having
|
||||
-- pairs( site.namespaces ) iterate all those names.
|
||||
setmetatable( site.namespaces, { __index = namespacesByName } )
|
||||
|
||||
-- Copy the site stats, and set up the metatable to load them if necessary.
|
||||
local loadSiteStats = php.loadSiteStats
|
||||
site.stats.x = php.loadSiteStats
|
||||
if info.stats then
|
||||
loadSiteStats = nil
|
||||
for k, v in pairs( info.stats ) do
|
||||
site.stats[k] = v
|
||||
end
|
||||
end
|
||||
setmetatable( site.stats, {
|
||||
__index = function ( t, k )
|
||||
if t ~= site.stats then -- cloned
|
||||
return site.stats[k]
|
||||
end
|
||||
|
||||
if k == 'admins' then
|
||||
t.admins = t.usersInGroup( 'sysop' )
|
||||
elseif loadSiteStats then
|
||||
for k, v in pairs( loadSiteStats() ) do
|
||||
t[k] = v
|
||||
end
|
||||
loadSiteStats = nil
|
||||
end
|
||||
|
||||
return rawget( t, k )
|
||||
end
|
||||
} )
|
||||
end
|
||||
-- xowa:end
|
||||
|
||||
return site
|
||||
|
||||
@@ -1,352 +1,352 @@
|
||||
local mwtext = {}
|
||||
local php
|
||||
local options
|
||||
local init_text_for_wiki_needed = true -- xowa
|
||||
|
||||
local util = require 'libraryUtil'
|
||||
local checkType = util.checkType
|
||||
local checkTypeForNamedArg = util.checkTypeForNamedArg
|
||||
|
||||
function mwtext.setupInterface( opts )
|
||||
-- Boilerplate
|
||||
mwtext.setupInterface = nil
|
||||
php = mw_interface
|
||||
mw_interface = nil
|
||||
-- options = opts -- xowa
|
||||
|
||||
-- Register this library in the "mw" global
|
||||
mw = mw or {}
|
||||
mw.text = mwtext.make_proxy(mwtext) -- xowa
|
||||
|
||||
package.loaded['mw.text'] = mwtext
|
||||
end
|
||||
|
||||
function mwtext.trim( s, charset )
|
||||
charset = charset or '\t\r\n\f '
|
||||
s = mw.ustring.gsub( s, '^[' .. charset .. ']*(.-)[' .. charset .. ']*$', '%1' )
|
||||
return s
|
||||
end
|
||||
|
||||
local htmlencode_map = {
|
||||
['>'] = '>',
|
||||
['<'] = '<',
|
||||
['&'] = '&',
|
||||
['"'] = '"',
|
||||
["'"] = ''',
|
||||
['\194\160'] = ' ',
|
||||
}
|
||||
local htmldecode_map = {}
|
||||
for k, v in pairs( htmlencode_map ) do
|
||||
htmldecode_map[v] = k
|
||||
end
|
||||
local decode_named_entities = nil
|
||||
|
||||
function mwtext.encode( s, charset )
|
||||
charset = charset or '<>&"\'\194\160'
|
||||
s = mw.ustring.gsub( s, '[' .. charset .. ']', function ( m )
|
||||
if not htmlencode_map[m] then
|
||||
local e = string.format( '&#%d;', mw.ustring.codepoint( m ) )
|
||||
htmlencode_map[m] = e
|
||||
htmldecode_map[e] = m
|
||||
end
|
||||
return htmlencode_map[m]
|
||||
end )
|
||||
return s
|
||||
end
|
||||
|
||||
function mwtext.decode( s, decodeNamedEntities )
|
||||
local dec
|
||||
if decodeNamedEntities then
|
||||
if decode_named_entities == nil then
|
||||
decode_named_entities = php.getEntityTable()
|
||||
setmetatable( decode_named_entities, { __index = htmldecode_map } )
|
||||
end
|
||||
dec = decode_named_entities
|
||||
else
|
||||
dec = htmldecode_map
|
||||
end
|
||||
-- string.gsub is safe here, because only ASCII chars are in the pattern
|
||||
s = string.gsub( s, '(&(#?x?)([a-zA-Z0-9]+);)', function ( m, flg, name )
|
||||
if not dec[m] then
|
||||
local n = nil
|
||||
if flg == '#' then
|
||||
n = tonumber( name, 10 )
|
||||
elseif flg == '#x' then
|
||||
n = tonumber( name, 16 )
|
||||
end
|
||||
if n and n <= 0x10ffff then
|
||||
n = mw.ustring.char( n )
|
||||
if n then
|
||||
htmldecode_map[m] = n
|
||||
htmlencode_map[n] = m
|
||||
end
|
||||
end
|
||||
end
|
||||
return dec[m]
|
||||
end )
|
||||
return s
|
||||
end
|
||||
|
||||
local nowikiRepl1 = {
|
||||
['"'] = '"',
|
||||
['&'] = '&',
|
||||
["'"] = ''',
|
||||
['<'] = '<',
|
||||
['='] = '=',
|
||||
['>'] = '>',
|
||||
['['] = '[',
|
||||
[']'] = ']',
|
||||
['{'] = '{',
|
||||
['|'] = '|',
|
||||
['}'] = '}',
|
||||
}
|
||||
|
||||
local nowikiRepl2 = {
|
||||
["\n#"] = "\n#", ["\r#"] = "\r#",
|
||||
["\n*"] = "\n*", ["\r*"] = "\r*",
|
||||
["\n:"] = "\n:", ["\r:"] = "\r:",
|
||||
["\n;"] = "\n;", ["\r;"] = "\r;",
|
||||
["\n "] = "\n ", ["\r "] = "\r ",
|
||||
["\n\n"] = "\n ", ["\r\n"] = " \n",
|
||||
["\n\r"] = "\n ", ["\r\r"] = "\r ",
|
||||
["\n\t"] = "\n	", ["\r\t"] = "\r	",
|
||||
}
|
||||
|
||||
local nowikiReplMagic = {}
|
||||
for sp, esc in pairs( {
|
||||
[' '] = ' ',
|
||||
['\t'] = '	',
|
||||
['\r'] = ' ',
|
||||
['\n'] = ' ',
|
||||
['\f'] = '',
|
||||
} ) do
|
||||
nowikiReplMagic['ISBN' .. sp] = 'ISBN' .. esc
|
||||
nowikiReplMagic['RFC' .. sp] = 'RFC' .. esc
|
||||
nowikiReplMagic['PMID' .. sp] = 'PMID' .. esc
|
||||
end
|
||||
|
||||
function mwtext.nowiki( s )
|
||||
-- string.gsub is safe here, because we're only caring about ASCII chars
|
||||
s = string.gsub( s, '["&\'<=>%[%]{|}]', nowikiRepl1 )
|
||||
s = '\n' .. s
|
||||
s = string.gsub( s, '[\r\n][#*:; \n\r\t]', nowikiRepl2 )
|
||||
s = string.gsub( s, '([\r\n])%-%-%-%-', '%1----' )
|
||||
s = string.sub( s, 2 )
|
||||
s = string.gsub( s, '__', '__' )
|
||||
s = string.gsub( s, '://', '://' )
|
||||
s = string.gsub( s, 'ISBN%s', nowikiReplMagic )
|
||||
s = string.gsub( s, 'RFC%s', nowikiReplMagic )
|
||||
s = string.gsub( s, 'PMID%s', nowikiReplMagic )
|
||||
for k, v in pairs( options.nowiki_protocols ) do
|
||||
s = string.gsub( s, k, v )
|
||||
end
|
||||
|
||||
return s
|
||||
end
|
||||
|
||||
function mwtext.tag( name, attrs, content )
|
||||
local named = false
|
||||
if type( name ) == 'table' then
|
||||
named = true
|
||||
name, attrs, content = name.name, name.attrs, name.content
|
||||
checkTypeForNamedArg( 'tag', 'name', name, 'string' )
|
||||
checkTypeForNamedArg( 'tag', 'attrs', attrs, 'table', true )
|
||||
else
|
||||
checkType( 'tag', 1, name, 'string' )
|
||||
checkType( 'tag', 2, attrs, 'table', true )
|
||||
end
|
||||
|
||||
local ret = { '<' .. name }
|
||||
for k, v in pairs( attrs or {} ) do
|
||||
if type( k ) ~= 'string' then
|
||||
error( "bad named argument attrs to 'tag' (keys must be strings, found " .. type( k ) .. ")", 2 )
|
||||
end
|
||||
if string.match( k, '[\t\r\n\f /<>"\'=]' ) then
|
||||
error( "bad named argument attrs to 'tag' (invalid key '" .. k .. "')", 2 )
|
||||
end
|
||||
local tp = type( v )
|
||||
if tp == 'boolean' then
|
||||
if v then
|
||||
ret[#ret+1] = ' ' .. k
|
||||
end
|
||||
elseif tp == 'string' or tp == 'number' then
|
||||
ret[#ret+1] = string.format( ' %s="%s"', k, mwtext.encode( tostring( v ) ) )
|
||||
else
|
||||
error( "bad named argument attrs to 'tag' (value for key '" .. k .. "' may not be " .. tp .. ")", 2 )
|
||||
end
|
||||
end
|
||||
|
||||
local tp = type( content )
|
||||
if content == nil then
|
||||
ret[#ret+1] = '>'
|
||||
elseif content == false then
|
||||
ret[#ret+1] = ' />'
|
||||
elseif tp == 'string' or tp == 'number' then
|
||||
ret[#ret+1] = '>'
|
||||
ret[#ret+1] = content
|
||||
ret[#ret+1] = '</' .. name .. '>'
|
||||
else
|
||||
if named then
|
||||
checkTypeForNamedArg( 'tag', 'content', content, 'string, number, nil, or false' )
|
||||
else
|
||||
checkType( 'tag', 3, content, 'string, number, nil, or false' )
|
||||
end
|
||||
end
|
||||
|
||||
return table.concat( ret )
|
||||
end
|
||||
|
||||
function mwtext.unstrip( s )
|
||||
return php.unstrip( s )
|
||||
end
|
||||
|
||||
function mwtext.unstripNoWiki( s )
|
||||
return php.unstripNoWiki( s )
|
||||
end
|
||||
|
||||
function mwtext.killMarkers( s )
|
||||
return php.killMarkers( s )
|
||||
end
|
||||
|
||||
function mwtext.split( text, pattern, plain )
|
||||
local ret = {}
|
||||
for m in mwtext.gsplit( text, pattern, plain ) do
|
||||
ret[#ret+1] = m
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
function mwtext.gsplit( text, pattern, plain )
|
||||
local s, l = 1, mw.ustring.len( text )
|
||||
return function ()
|
||||
if s then
|
||||
local e, n = mw.ustring.find( text, pattern, s, plain )
|
||||
local ret
|
||||
if not e then
|
||||
ret = mw.ustring.sub( text, s )
|
||||
s = nil
|
||||
elseif n < e then
|
||||
-- Empty separator!
|
||||
ret = mw.ustring.sub( text, s, e )
|
||||
if e < l then
|
||||
s = e + 1
|
||||
else
|
||||
s = nil
|
||||
end
|
||||
else
|
||||
ret = e > s and mw.ustring.sub( text, s, e - 1 ) or ''
|
||||
s = n + 1
|
||||
end
|
||||
return ret
|
||||
end
|
||||
end, nil, nil
|
||||
end
|
||||
|
||||
function mwtext.listToText( list, separator, conjunction )
|
||||
separator = separator or options.comma
|
||||
conjunction = conjunction or options['and']
|
||||
local n = #list
|
||||
|
||||
local ret
|
||||
if n > 1 then
|
||||
ret = table.concat( list, separator, 1, n - 1 ) .. conjunction .. list[n]
|
||||
else
|
||||
ret = tostring( list[1] or '' )
|
||||
end
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
function mwtext.truncate( text, length, ellipsis, adjustLength )
|
||||
local l = mw.ustring.len( text )
|
||||
if l <= math.abs( length ) then
|
||||
return text
|
||||
end
|
||||
|
||||
ellipsis = ellipsis or options.ellipsis
|
||||
local elen = 0
|
||||
if adjustLength then
|
||||
elen = mw.ustring.len( ellipsis )
|
||||
end
|
||||
|
||||
local ret
|
||||
if math.abs( length ) <= elen then
|
||||
ret = ellipsis
|
||||
elseif length > 0 then
|
||||
ret = mw.ustring.sub( text, 1, length - elen ) .. ellipsis
|
||||
else
|
||||
ret = ellipsis .. mw.ustring.sub( text, length + elen )
|
||||
end
|
||||
|
||||
if mw.ustring.len( ret ) < l then
|
||||
return ret
|
||||
else
|
||||
return text
|
||||
end
|
||||
end
|
||||
|
||||
-- Check for stuff that can't even be passed to PHP properly and other stuff
|
||||
-- that gives different error messages in different versions of PHP
|
||||
local function checkForJsonEncode( t, seen, lvl )
|
||||
local tp = type( t )
|
||||
if tp == 'table' then
|
||||
if seen[t] then
|
||||
error( "mw.text.jsonEncode: Cannot use recursive tables", lvl )
|
||||
end
|
||||
seen[t] = 1
|
||||
for k, v in pairs( t ) do
|
||||
if type( k ) == 'number' then
|
||||
if k >= math.huge or k <= -math.huge then
|
||||
error( string.format( "mw.text.jsonEncode: Cannot use 'inf' as a table key", type( k ) ), lvl )
|
||||
end
|
||||
elseif type( k ) ~= 'string' then
|
||||
error( string.format( "mw.text.jsonEncode: Cannot use type '%s' as a table key", type( k ) ), lvl )
|
||||
end
|
||||
checkForJsonEncode( v, seen, lvl + 1 )
|
||||
end
|
||||
seen[t] = nil
|
||||
elseif tp == 'number' then
|
||||
if t ~= t or t >= math.huge or t <= -math.huge then
|
||||
error( "mw.text.jsonEncode: Cannot encode non-finite numbers", lvl )
|
||||
end
|
||||
elseif tp ~= 'boolean' and tp ~= 'string' and tp ~= 'nil' then
|
||||
error( string.format( "mw.text.jsonEncode: Cannot encode type '%s'", tp ), lvl )
|
||||
end
|
||||
end
|
||||
|
||||
function mwtext.jsonEncode( value, flags )
|
||||
checkForJsonEncode( value, {}, 3 )
|
||||
return php.jsonEncode( value, flags )
|
||||
end
|
||||
|
||||
function mwtext.jsonDecode( json, flags )
|
||||
return php.jsonDecode( json, flags )
|
||||
end
|
||||
|
||||
-- Matches PHP Scribunto_LuaTextLibrary constants
|
||||
mwtext.JSON_PRESERVE_KEYS = 1
|
||||
mwtext.JSON_TRY_FIXING = 2
|
||||
mwtext.JSON_PRETTY = 4
|
||||
|
||||
-- xowa:bgn
|
||||
function mwtext.notify_wiki_changed()
|
||||
init_text_for_wiki_needed = true
|
||||
end
|
||||
|
||||
function mwtext.make_proxy(real)
|
||||
local proxy = {}
|
||||
setmetatable(proxy, {__index =
|
||||
function(tbl, key)
|
||||
if init_text_for_wiki_needed then
|
||||
options = php.init_text_for_wiki() -- more performant than calling setupInterface for every page load
|
||||
init_text_for_wiki_needed = false
|
||||
end
|
||||
return real[key]
|
||||
end
|
||||
}
|
||||
)
|
||||
return proxy
|
||||
end
|
||||
-- xowa:end
|
||||
|
||||
return mwtext
|
||||
local mwtext = {}
|
||||
local php
|
||||
local options
|
||||
local init_text_for_wiki_needed = true -- xowa
|
||||
|
||||
local util = require 'libraryUtil'
|
||||
local checkType = util.checkType
|
||||
local checkTypeForNamedArg = util.checkTypeForNamedArg
|
||||
|
||||
function mwtext.setupInterface( opts )
|
||||
-- Boilerplate
|
||||
mwtext.setupInterface = nil
|
||||
php = mw_interface
|
||||
mw_interface = nil
|
||||
-- options = opts -- xowa
|
||||
|
||||
-- Register this library in the "mw" global
|
||||
mw = mw or {}
|
||||
mw.text = mwtext.make_proxy(mwtext) -- xowa
|
||||
|
||||
package.loaded['mw.text'] = mwtext
|
||||
end
|
||||
|
||||
function mwtext.trim( s, charset )
|
||||
charset = charset or '\t\r\n\f '
|
||||
s = mw.ustring.gsub( s, '^[' .. charset .. ']*(.-)[' .. charset .. ']*$', '%1' )
|
||||
return s
|
||||
end
|
||||
|
||||
local htmlencode_map = {
|
||||
['>'] = '>',
|
||||
['<'] = '<',
|
||||
['&'] = '&',
|
||||
['"'] = '"',
|
||||
["'"] = ''',
|
||||
['\194\160'] = ' ',
|
||||
}
|
||||
local htmldecode_map = {}
|
||||
for k, v in pairs( htmlencode_map ) do
|
||||
htmldecode_map[v] = k
|
||||
end
|
||||
local decode_named_entities = nil
|
||||
|
||||
function mwtext.encode( s, charset )
|
||||
charset = charset or '<>&"\'\194\160'
|
||||
s = mw.ustring.gsub( s, '[' .. charset .. ']', function ( m )
|
||||
if not htmlencode_map[m] then
|
||||
local e = string.format( '&#%d;', mw.ustring.codepoint( m ) )
|
||||
htmlencode_map[m] = e
|
||||
htmldecode_map[e] = m
|
||||
end
|
||||
return htmlencode_map[m]
|
||||
end )
|
||||
return s
|
||||
end
|
||||
|
||||
function mwtext.decode( s, decodeNamedEntities )
|
||||
local dec
|
||||
if decodeNamedEntities then
|
||||
if decode_named_entities == nil then
|
||||
decode_named_entities = php.getEntityTable()
|
||||
setmetatable( decode_named_entities, { __index = htmldecode_map } )
|
||||
end
|
||||
dec = decode_named_entities
|
||||
else
|
||||
dec = htmldecode_map
|
||||
end
|
||||
-- string.gsub is safe here, because only ASCII chars are in the pattern
|
||||
s = string.gsub( s, '(&(#?x?)([a-zA-Z0-9]+);)', function ( m, flg, name )
|
||||
if not dec[m] then
|
||||
local n = nil
|
||||
if flg == '#' then
|
||||
n = tonumber( name, 10 )
|
||||
elseif flg == '#x' then
|
||||
n = tonumber( name, 16 )
|
||||
end
|
||||
if n and n <= 0x10ffff then
|
||||
n = mw.ustring.char( n )
|
||||
if n then
|
||||
htmldecode_map[m] = n
|
||||
htmlencode_map[n] = m
|
||||
end
|
||||
end
|
||||
end
|
||||
return dec[m]
|
||||
end )
|
||||
return s
|
||||
end
|
||||
|
||||
local nowikiRepl1 = {
|
||||
['"'] = '"',
|
||||
['&'] = '&',
|
||||
["'"] = ''',
|
||||
['<'] = '<',
|
||||
['='] = '=',
|
||||
['>'] = '>',
|
||||
['['] = '[',
|
||||
[']'] = ']',
|
||||
['{'] = '{',
|
||||
['|'] = '|',
|
||||
['}'] = '}',
|
||||
}
|
||||
|
||||
local nowikiRepl2 = {
|
||||
["\n#"] = "\n#", ["\r#"] = "\r#",
|
||||
["\n*"] = "\n*", ["\r*"] = "\r*",
|
||||
["\n:"] = "\n:", ["\r:"] = "\r:",
|
||||
["\n;"] = "\n;", ["\r;"] = "\r;",
|
||||
["\n "] = "\n ", ["\r "] = "\r ",
|
||||
["\n\n"] = "\n ", ["\r\n"] = " \n",
|
||||
["\n\r"] = "\n ", ["\r\r"] = "\r ",
|
||||
["\n\t"] = "\n	", ["\r\t"] = "\r	",
|
||||
}
|
||||
|
||||
local nowikiReplMagic = {}
|
||||
for sp, esc in pairs( {
|
||||
[' '] = ' ',
|
||||
['\t'] = '	',
|
||||
['\r'] = ' ',
|
||||
['\n'] = ' ',
|
||||
['\f'] = '',
|
||||
} ) do
|
||||
nowikiReplMagic['ISBN' .. sp] = 'ISBN' .. esc
|
||||
nowikiReplMagic['RFC' .. sp] = 'RFC' .. esc
|
||||
nowikiReplMagic['PMID' .. sp] = 'PMID' .. esc
|
||||
end
|
||||
|
||||
function mwtext.nowiki( s )
|
||||
-- string.gsub is safe here, because we're only caring about ASCII chars
|
||||
s = string.gsub( s, '["&\'<=>%[%]{|}]', nowikiRepl1 )
|
||||
s = '\n' .. s
|
||||
s = string.gsub( s, '[\r\n][#*:; \n\r\t]', nowikiRepl2 )
|
||||
s = string.gsub( s, '([\r\n])%-%-%-%-', '%1----' )
|
||||
s = string.sub( s, 2 )
|
||||
s = string.gsub( s, '__', '__' )
|
||||
s = string.gsub( s, '://', '://' )
|
||||
s = string.gsub( s, 'ISBN%s', nowikiReplMagic )
|
||||
s = string.gsub( s, 'RFC%s', nowikiReplMagic )
|
||||
s = string.gsub( s, 'PMID%s', nowikiReplMagic )
|
||||
for k, v in pairs( options.nowiki_protocols ) do
|
||||
s = string.gsub( s, k, v )
|
||||
end
|
||||
|
||||
return s
|
||||
end
|
||||
|
||||
function mwtext.tag( name, attrs, content )
|
||||
local named = false
|
||||
if type( name ) == 'table' then
|
||||
named = true
|
||||
name, attrs, content = name.name, name.attrs, name.content
|
||||
checkTypeForNamedArg( 'tag', 'name', name, 'string' )
|
||||
checkTypeForNamedArg( 'tag', 'attrs', attrs, 'table', true )
|
||||
else
|
||||
checkType( 'tag', 1, name, 'string' )
|
||||
checkType( 'tag', 2, attrs, 'table', true )
|
||||
end
|
||||
|
||||
local ret = { '<' .. name }
|
||||
for k, v in pairs( attrs or {} ) do
|
||||
if type( k ) ~= 'string' then
|
||||
error( "bad named argument attrs to 'tag' (keys must be strings, found " .. type( k ) .. ")", 2 )
|
||||
end
|
||||
if string.match( k, '[\t\r\n\f /<>"\'=]' ) then
|
||||
error( "bad named argument attrs to 'tag' (invalid key '" .. k .. "')", 2 )
|
||||
end
|
||||
local tp = type( v )
|
||||
if tp == 'boolean' then
|
||||
if v then
|
||||
ret[#ret+1] = ' ' .. k
|
||||
end
|
||||
elseif tp == 'string' or tp == 'number' then
|
||||
ret[#ret+1] = string.format( ' %s="%s"', k, mwtext.encode( tostring( v ) ) )
|
||||
else
|
||||
error( "bad named argument attrs to 'tag' (value for key '" .. k .. "' may not be " .. tp .. ")", 2 )
|
||||
end
|
||||
end
|
||||
|
||||
local tp = type( content )
|
||||
if content == nil then
|
||||
ret[#ret+1] = '>'
|
||||
elseif content == false then
|
||||
ret[#ret+1] = ' />'
|
||||
elseif tp == 'string' or tp == 'number' then
|
||||
ret[#ret+1] = '>'
|
||||
ret[#ret+1] = content
|
||||
ret[#ret+1] = '</' .. name .. '>'
|
||||
else
|
||||
if named then
|
||||
checkTypeForNamedArg( 'tag', 'content', content, 'string, number, nil, or false' )
|
||||
else
|
||||
checkType( 'tag', 3, content, 'string, number, nil, or false' )
|
||||
end
|
||||
end
|
||||
|
||||
return table.concat( ret )
|
||||
end
|
||||
|
||||
function mwtext.unstrip( s )
|
||||
return php.unstrip( s )
|
||||
end
|
||||
|
||||
function mwtext.unstripNoWiki( s )
|
||||
return php.unstripNoWiki( s )
|
||||
end
|
||||
|
||||
function mwtext.killMarkers( s )
|
||||
return php.killMarkers( s )
|
||||
end
|
||||
|
||||
function mwtext.split( text, pattern, plain )
|
||||
local ret = {}
|
||||
for m in mwtext.gsplit( text, pattern, plain ) do
|
||||
ret[#ret+1] = m
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
function mwtext.gsplit( text, pattern, plain )
|
||||
local s, l = 1, mw.ustring.len( text )
|
||||
return function ()
|
||||
if s then
|
||||
local e, n = mw.ustring.find( text, pattern, s, plain )
|
||||
local ret
|
||||
if not e then
|
||||
ret = mw.ustring.sub( text, s )
|
||||
s = nil
|
||||
elseif n < e then
|
||||
-- Empty separator!
|
||||
ret = mw.ustring.sub( text, s, e )
|
||||
if e < l then
|
||||
s = e + 1
|
||||
else
|
||||
s = nil
|
||||
end
|
||||
else
|
||||
ret = e > s and mw.ustring.sub( text, s, e - 1 ) or ''
|
||||
s = n + 1
|
||||
end
|
||||
return ret
|
||||
end
|
||||
end, nil, nil
|
||||
end
|
||||
|
||||
function mwtext.listToText( list, separator, conjunction )
|
||||
separator = separator or options.comma
|
||||
conjunction = conjunction or options['and']
|
||||
local n = #list
|
||||
|
||||
local ret
|
||||
if n > 1 then
|
||||
ret = table.concat( list, separator, 1, n - 1 ) .. conjunction .. list[n]
|
||||
else
|
||||
ret = tostring( list[1] or '' )
|
||||
end
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
function mwtext.truncate( text, length, ellipsis, adjustLength )
|
||||
local l = mw.ustring.len( text )
|
||||
if l <= math.abs( length ) then
|
||||
return text
|
||||
end
|
||||
|
||||
ellipsis = ellipsis or options.ellipsis
|
||||
local elen = 0
|
||||
if adjustLength then
|
||||
elen = mw.ustring.len( ellipsis )
|
||||
end
|
||||
|
||||
local ret
|
||||
if math.abs( length ) <= elen then
|
||||
ret = ellipsis
|
||||
elseif length > 0 then
|
||||
ret = mw.ustring.sub( text, 1, length - elen ) .. ellipsis
|
||||
else
|
||||
ret = ellipsis .. mw.ustring.sub( text, length + elen )
|
||||
end
|
||||
|
||||
if mw.ustring.len( ret ) < l then
|
||||
return ret
|
||||
else
|
||||
return text
|
||||
end
|
||||
end
|
||||
|
||||
-- Check for stuff that can't even be passed to PHP properly and other stuff
|
||||
-- that gives different error messages in different versions of PHP
|
||||
local function checkForJsonEncode( t, seen, lvl )
|
||||
local tp = type( t )
|
||||
if tp == 'table' then
|
||||
if seen[t] then
|
||||
error( "mw.text.jsonEncode: Cannot use recursive tables", lvl )
|
||||
end
|
||||
seen[t] = 1
|
||||
for k, v in pairs( t ) do
|
||||
if type( k ) == 'number' then
|
||||
if k >= math.huge or k <= -math.huge then
|
||||
error( string.format( "mw.text.jsonEncode: Cannot use 'inf' as a table key", type( k ) ), lvl )
|
||||
end
|
||||
elseif type( k ) ~= 'string' then
|
||||
error( string.format( "mw.text.jsonEncode: Cannot use type '%s' as a table key", type( k ) ), lvl )
|
||||
end
|
||||
checkForJsonEncode( v, seen, lvl + 1 )
|
||||
end
|
||||
seen[t] = nil
|
||||
elseif tp == 'number' then
|
||||
if t ~= t or t >= math.huge or t <= -math.huge then
|
||||
error( "mw.text.jsonEncode: Cannot encode non-finite numbers", lvl )
|
||||
end
|
||||
elseif tp ~= 'boolean' and tp ~= 'string' and tp ~= 'nil' then
|
||||
error( string.format( "mw.text.jsonEncode: Cannot encode type '%s'", tp ), lvl )
|
||||
end
|
||||
end
|
||||
|
||||
function mwtext.jsonEncode( value, flags )
|
||||
checkForJsonEncode( value, {}, 3 )
|
||||
return php.jsonEncode( value, flags )
|
||||
end
|
||||
|
||||
function mwtext.jsonDecode( json, flags )
|
||||
return php.jsonDecode( json, flags )
|
||||
end
|
||||
|
||||
-- Matches PHP Scribunto_LuaTextLibrary constants
|
||||
mwtext.JSON_PRESERVE_KEYS = 1
|
||||
mwtext.JSON_TRY_FIXING = 2
|
||||
mwtext.JSON_PRETTY = 4
|
||||
|
||||
-- xowa:bgn
|
||||
function mwtext.notify_wiki_changed()
|
||||
init_text_for_wiki_needed = true
|
||||
end
|
||||
|
||||
function mwtext.make_proxy(real)
|
||||
local proxy = {}
|
||||
setmetatable(proxy, {__index =
|
||||
function(tbl, key)
|
||||
if init_text_for_wiki_needed then
|
||||
options = php.init_text_for_wiki() -- more performant than calling setupInterface for every page load
|
||||
init_text_for_wiki_needed = false
|
||||
end
|
||||
return real[key]
|
||||
end
|
||||
}
|
||||
)
|
||||
return proxy
|
||||
end
|
||||
-- xowa:end
|
||||
|
||||
return mwtext
|
||||
|
||||
@@ -1,392 +1,392 @@
|
||||
local title = {}
|
||||
local php
|
||||
local NS_MEDIA = -2
|
||||
local init_title_for_page_needed = true -- xowa
|
||||
|
||||
local util = require 'libraryUtil'
|
||||
local checkType = util.checkType
|
||||
local checkTypeForIndex = util.checkTypeForIndex
|
||||
|
||||
local function checkNamespace( name, argIdx, arg )
|
||||
if type( arg ) == 'string' and tostring( tonumber( arg ) ) == arg then
|
||||
arg = tonumber( arg )
|
||||
end
|
||||
if type( arg ) == 'number' then
|
||||
arg = math.floor( arg + 0.5 )
|
||||
if not mw.site.namespaces[arg] then
|
||||
local msg = string.format( "bad argument #%d to '%s' (unrecognized namespace number '%s')",
|
||||
argIdx, name, arg
|
||||
)
|
||||
error( msg, 3 )
|
||||
end
|
||||
elseif type( arg ) == 'string' then
|
||||
local ns = mw.site.namespaces[arg]
|
||||
if not ns then
|
||||
local msg = string.format( "bad argument #%d to '%s' (unrecognized namespace name '%s')",
|
||||
argIdx, name, arg
|
||||
)
|
||||
error( msg, 3 )
|
||||
end
|
||||
arg = ns.id
|
||||
else
|
||||
local msg = string.format( "bad argument #%d to '%s' (string or number expected, got %s)",
|
||||
argIdx, name, type( arg )
|
||||
)
|
||||
error( msg, 3 )
|
||||
end
|
||||
return arg
|
||||
end
|
||||
|
||||
|
||||
local function lt( a, b )
|
||||
if a.interwiki ~= b.interwiki then
|
||||
return a.interwiki < b.interwiki
|
||||
end
|
||||
if a.namespace ~= b.namespace then
|
||||
return a.namespace < b.namespace
|
||||
end
|
||||
return a.text < b.text
|
||||
end
|
||||
|
||||
local function makeTitleObject( data )
|
||||
if not data then
|
||||
return nil
|
||||
end
|
||||
|
||||
local obj = {}
|
||||
local checkSelf = util.makeCheckSelfFunction( 'mw.title', 'title', obj, 'title object' );
|
||||
local ns = mw.site.namespaces[data.namespace]
|
||||
|
||||
data.isContentPage = ns.isContent
|
||||
data.isExternal = data.interwiki ~= ''
|
||||
data.isSpecialPage = data.namespace == mw.site.namespaces.Special.id
|
||||
data.isTalkPage = ns.isTalk
|
||||
data.subjectNsText = ns.subject.name
|
||||
data.canTalk = ns.talk ~= nil
|
||||
|
||||
data.prefixedText = data.text
|
||||
if data.nsText ~= '' then
|
||||
data.prefixedText = string.gsub( data.nsText .. ':' .. data.prefixedText, '_', ' ' )
|
||||
end
|
||||
if data.interwiki ~= '' then
|
||||
data.prefixedText = data.interwiki .. ':' .. data.prefixedText
|
||||
end
|
||||
|
||||
local firstSlash, lastSlash
|
||||
if ns.hasSubpages then
|
||||
firstSlash, lastSlash = string.match( data.text, '^[^/]*().*()/[^/]*$' )
|
||||
end
|
||||
if firstSlash then
|
||||
data.isSubpage = true
|
||||
data.rootText = string.sub( data.text, 1, firstSlash - 1 )
|
||||
data.baseText = string.sub( data.text, 1, lastSlash - 1 )
|
||||
data.subpageText = string.sub( data.text, lastSlash + 1 )
|
||||
else
|
||||
data.isSubpage = false
|
||||
data.rootText = data.text
|
||||
data.baseText = data.text
|
||||
data.subpageText = data.text
|
||||
end
|
||||
|
||||
function data:inNamespace( ns )
|
||||
checkSelf( self, 'inNamespace' )
|
||||
ns = checkNamespace( 'inNamespace', 1, ns )
|
||||
return ns == self.namespace
|
||||
end
|
||||
|
||||
function data:inNamespaces( ... )
|
||||
checkSelf( self, 'inNamespaces' )
|
||||
for i = 1, select( '#', ... ) do
|
||||
local ns = checkNamespace( 'inNamespaces', i, select( i, ... ) )
|
||||
if ns == self.namespace then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function data:hasSubjectNamespace( ns )
|
||||
checkSelf( self, 'hasSubjectNamespace' )
|
||||
ns = checkNamespace( 'hasSubjectNamespace', 1, ns )
|
||||
return ns == mw.site.namespaces[self.namespace].subject.id
|
||||
end
|
||||
|
||||
function data:isSubpageOf( title )
|
||||
checkSelf( self, 'isSubpageOf' )
|
||||
checkType( 'isSubpageOf', 1, title, 'table' )
|
||||
return self.interwiki == title.interwiki and
|
||||
self.namespace == title.namespace and
|
||||
title.text .. '/' == string.sub( self.text, 1, #title.text + 1 )
|
||||
end
|
||||
|
||||
function data:subPageTitle( text )
|
||||
checkSelf( self, 'subpageTitle' )
|
||||
checkType( 'subpageTitle', 1, text, 'string' )
|
||||
return title.makeTitle( data.namespace, data.text .. '/' .. text )
|
||||
end
|
||||
|
||||
function data:partialUrl()
|
||||
checkSelf( self, 'partialUrl' )
|
||||
return data.thePartialUrl
|
||||
end
|
||||
|
||||
function data:fullUrl( query, proto )
|
||||
checkSelf( self, 'fullUrl' )
|
||||
return php.getUrl( self.fullText, 'fullUrl', query, proto )
|
||||
end
|
||||
|
||||
function data:localUrl( query )
|
||||
checkSelf( self, 'localUrl' )
|
||||
return php.getUrl( self.fullText, 'localUrl', query )
|
||||
end
|
||||
|
||||
function data:canonicalUrl( query )
|
||||
checkSelf( self, 'canonicalUrl' )
|
||||
return php.getUrl( self.fullText, 'canonicalUrl', query )
|
||||
end
|
||||
|
||||
function data:getContent()
|
||||
checkSelf( self, 'getContent' )
|
||||
local content = php.getContent( self.fullText )
|
||||
data.getContent = function ( self )
|
||||
checkSelf( self, 'getContent' )
|
||||
return content
|
||||
end
|
||||
return content
|
||||
end
|
||||
|
||||
-- Known fields, both those defined above and any dynamically handled in
|
||||
-- __index. Truthy values represent read-only, and falsey values represent
|
||||
-- read-write. If the value is the string 'e', expensive data will be loaded
|
||||
-- if the field is read.
|
||||
local readOnlyFields = {
|
||||
fragment = false,
|
||||
fullText = true,
|
||||
rootPageTitle = true,
|
||||
basePageTitle = true,
|
||||
talkPageTitle = true,
|
||||
subjectPageTitle = true,
|
||||
fileExists = true,
|
||||
file = true,
|
||||
protectionLevels = true,
|
||||
cascadingProtection = true,
|
||||
exists = 'e',
|
||||
isRedirect = 'e',
|
||||
contentModel = 'e',
|
||||
id = 'e',
|
||||
redirectTarget = true,
|
||||
}
|
||||
for k in pairs( data ) do
|
||||
readOnlyFields[k] = true
|
||||
end
|
||||
|
||||
local function pairsfunc( t, k )
|
||||
local v
|
||||
repeat
|
||||
k = next( readOnlyFields, k )
|
||||
if k == nil then
|
||||
return nil
|
||||
end
|
||||
v = t[k]
|
||||
until v ~= nil
|
||||
return k, v
|
||||
end
|
||||
|
||||
return setmetatable( obj, {
|
||||
__eq = title.equals,
|
||||
__lt = lt,
|
||||
__pairs = function ( t )
|
||||
return pairsfunc, t, nil
|
||||
end,
|
||||
__index = function ( t, k )
|
||||
if k == 'exists' and data.namespace == NS_MEDIA then
|
||||
k = 'fileExists'
|
||||
end
|
||||
|
||||
if readOnlyFields[k] == 'e' and data[k] == nil then
|
||||
for k,v in pairs( php.getExpensiveData( t.fullText ) ) do
|
||||
data[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
if k == 'fullText' then
|
||||
if data.fragment ~= '' then
|
||||
return data.prefixedText .. '#' .. data.fragment
|
||||
else
|
||||
return data.prefixedText
|
||||
end
|
||||
end
|
||||
|
||||
if k == 'rootPageTitle' then
|
||||
return title.makeTitle( data.namespace, data.rootText )
|
||||
end
|
||||
if k == 'basePageTitle' then
|
||||
return title.makeTitle( data.namespace, data.baseText )
|
||||
end
|
||||
if k == 'talkPageTitle' then
|
||||
local ns = mw.site.namespaces[data.namespace].talk
|
||||
if not ns then
|
||||
return nil
|
||||
end
|
||||
if ns.id == data.namespace then
|
||||
return obj
|
||||
end
|
||||
return title.makeTitle( ns.id, data.text )
|
||||
end
|
||||
if k == 'subjectPageTitle' then
|
||||
local ns = mw.site.namespaces[data.namespace].subject
|
||||
if ns.id == data.namespace then
|
||||
return obj
|
||||
end
|
||||
return title.makeTitle( ns.id, data.text )
|
||||
end
|
||||
if k == 'file' then
|
||||
if data.file == nil then
|
||||
data.file = php.getFileInfo( data.prefixedText )
|
||||
end
|
||||
return data.file or nil
|
||||
end
|
||||
if k == 'fileExists' then -- Kept for backward compatibility. Since 1.25, file.exists is preferred over this
|
||||
return t.file and t.file.exists
|
||||
end
|
||||
if k == 'protectionLevels' then
|
||||
if data.protectionLevels == nil then
|
||||
data.protectionLevels = php.protectionLevels( data.prefixedText )
|
||||
end
|
||||
return data.protectionLevels
|
||||
end
|
||||
if k == 'cascadingProtection' then
|
||||
if data.cascadingProtection == nil then
|
||||
data.cascadingProtection = php.cascadingProtection( data.prefixedText )
|
||||
end
|
||||
return data.cascadingProtection
|
||||
end
|
||||
if k == 'redirectTarget' then
|
||||
if data.redirectTarget == nil then
|
||||
data.redirectTarget = makeTitleObject( php.redirectTarget( data.prefixedText ) ) or false
|
||||
end
|
||||
return data.redirectTarget
|
||||
end
|
||||
|
||||
return data[k]
|
||||
end,
|
||||
__newindex = function ( t, k, v )
|
||||
if k == 'fragment' then
|
||||
checkTypeForIndex( k, v, 'string' )
|
||||
v = string.gsub( v, '[%s_]+', ' ' )
|
||||
v = string.gsub( v, '^(.-) ?$', '%1' )
|
||||
data[k] = v
|
||||
elseif readOnlyFields[k] then
|
||||
error( "index '" .. k .. "' is read only", 2 )
|
||||
else
|
||||
readOnlyFields[k] = v and false -- assigns nil if v == nil, false otherwise
|
||||
rawset( t, k, v )
|
||||
end
|
||||
end,
|
||||
__tostring = function ( t )
|
||||
return t.prefixedText
|
||||
end
|
||||
} )
|
||||
end
|
||||
|
||||
function title.setupInterface( options )
|
||||
-- Boilerplate
|
||||
title.setupInterface = nil
|
||||
php = mw_interface
|
||||
mw_interface = nil
|
||||
NS_MEDIA = options.NS_MEDIA
|
||||
|
||||
-- xowa:bgn
|
||||
-- Set current title
|
||||
--title.getCurrentTitle = function ()
|
||||
-- return makeTitleObject( options.thisTitle )
|
||||
--end
|
||||
-- xowa:end
|
||||
|
||||
-- Register this library in the "mw" global
|
||||
mw = mw or {}
|
||||
mw.title = title
|
||||
title.notify_page_changed()
|
||||
|
||||
package.loaded['mw.title'] = title
|
||||
end
|
||||
|
||||
function title.new( text_or_id, defaultNamespace )
|
||||
return makeTitleObject( php.newTitle( text_or_id, defaultNamespace ) )
|
||||
end
|
||||
|
||||
function title.makeTitle( ns, title, fragment, interwiki )
|
||||
return makeTitleObject( php.makeTitle( ns, title, fragment, interwiki ) )
|
||||
end
|
||||
|
||||
function title.equals( a, b )
|
||||
return a.interwiki == b.interwiki and
|
||||
a.namespace == b.namespace and
|
||||
a.text == b.text
|
||||
end
|
||||
|
||||
function title.compare( a, b )
|
||||
if a.interwiki ~= b.interwiki then
|
||||
return a.interwiki < b.interwiki and -1 or 1
|
||||
end
|
||||
if a.namespace ~= b.namespace then
|
||||
return a.namespace < b.namespace and -1 or 1
|
||||
end
|
||||
if a.text ~= b.text then
|
||||
return a.text < b.text and -1 or 1
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
-- xowa:bgn
|
||||
local system_globals =
|
||||
{ ["string"] = true
|
||||
, ["xpcall"] = true
|
||||
, ["package"] = true
|
||||
, ["rawset"] = true
|
||||
, ["_VERSION"] = true
|
||||
, ["error"] = true
|
||||
, ["pcall"] = true
|
||||
, ["os"] = true
|
||||
, ["unpack"] = true
|
||||
, ["math"] = true
|
||||
, ["ipairs"] = true
|
||||
, ["require"] = true
|
||||
, ["mw"] = true
|
||||
, ["tostring"] = true
|
||||
, ["type"] = true
|
||||
, ["pairs"] = true
|
||||
, ["next"] = true
|
||||
, ["select"] = true
|
||||
, ["assert"] = true
|
||||
, ["rawget"] = true
|
||||
, ["tonumber"] = true
|
||||
, ["table"] = true
|
||||
, ["_G"] = true
|
||||
, ["rawequal"] = true
|
||||
, ["setfenv"] = true
|
||||
, ["setmetatable"] = true
|
||||
, ["getfenv"] = true
|
||||
, ["getmetatable"] = true
|
||||
, ["debug"] = true
|
||||
}
|
||||
|
||||
function title.notify_page_changed()
|
||||
local php_data = php.getCurrentTitle()
|
||||
title.getCurrentTitle = function ()
|
||||
return makeTitleObject(php_data)
|
||||
end
|
||||
local delete_list = {}
|
||||
for k,v in pairs(_G) do
|
||||
local type_name = type(v)
|
||||
if not system_globals[k] and type_name ~= 'function' and type_name ~= 'table' then
|
||||
delete_list[k] = k
|
||||
end
|
||||
end
|
||||
for k,v in pairs(delete_list) do
|
||||
_G[k] = nil
|
||||
end
|
||||
end
|
||||
-- xowa:end
|
||||
|
||||
return title
|
||||
local title = {}
|
||||
local php
|
||||
local NS_MEDIA = -2
|
||||
local init_title_for_page_needed = true -- xowa
|
||||
|
||||
local util = require 'libraryUtil'
|
||||
local checkType = util.checkType
|
||||
local checkTypeForIndex = util.checkTypeForIndex
|
||||
|
||||
local function checkNamespace( name, argIdx, arg )
|
||||
if type( arg ) == 'string' and tostring( tonumber( arg ) ) == arg then
|
||||
arg = tonumber( arg )
|
||||
end
|
||||
if type( arg ) == 'number' then
|
||||
arg = math.floor( arg + 0.5 )
|
||||
if not mw.site.namespaces[arg] then
|
||||
local msg = string.format( "bad argument #%d to '%s' (unrecognized namespace number '%s')",
|
||||
argIdx, name, arg
|
||||
)
|
||||
error( msg, 3 )
|
||||
end
|
||||
elseif type( arg ) == 'string' then
|
||||
local ns = mw.site.namespaces[arg]
|
||||
if not ns then
|
||||
local msg = string.format( "bad argument #%d to '%s' (unrecognized namespace name '%s')",
|
||||
argIdx, name, arg
|
||||
)
|
||||
error( msg, 3 )
|
||||
end
|
||||
arg = ns.id
|
||||
else
|
||||
local msg = string.format( "bad argument #%d to '%s' (string or number expected, got %s)",
|
||||
argIdx, name, type( arg )
|
||||
)
|
||||
error( msg, 3 )
|
||||
end
|
||||
return arg
|
||||
end
|
||||
|
||||
|
||||
local function lt( a, b )
|
||||
if a.interwiki ~= b.interwiki then
|
||||
return a.interwiki < b.interwiki
|
||||
end
|
||||
if a.namespace ~= b.namespace then
|
||||
return a.namespace < b.namespace
|
||||
end
|
||||
return a.text < b.text
|
||||
end
|
||||
|
||||
local function makeTitleObject( data )
|
||||
if not data then
|
||||
return nil
|
||||
end
|
||||
|
||||
local obj = {}
|
||||
local checkSelf = util.makeCheckSelfFunction( 'mw.title', 'title', obj, 'title object' );
|
||||
local ns = mw.site.namespaces[data.namespace]
|
||||
|
||||
data.isContentPage = ns.isContent
|
||||
data.isExternal = data.interwiki ~= ''
|
||||
data.isSpecialPage = data.namespace == mw.site.namespaces.Special.id
|
||||
data.isTalkPage = ns.isTalk
|
||||
data.subjectNsText = ns.subject.name
|
||||
data.canTalk = ns.talk ~= nil
|
||||
|
||||
data.prefixedText = data.text
|
||||
if data.nsText ~= '' then
|
||||
data.prefixedText = string.gsub( data.nsText .. ':' .. data.prefixedText, '_', ' ' )
|
||||
end
|
||||
if data.interwiki ~= '' then
|
||||
data.prefixedText = data.interwiki .. ':' .. data.prefixedText
|
||||
end
|
||||
|
||||
local firstSlash, lastSlash
|
||||
if ns.hasSubpages then
|
||||
firstSlash, lastSlash = string.match( data.text, '^[^/]*().*()/[^/]*$' )
|
||||
end
|
||||
if firstSlash then
|
||||
data.isSubpage = true
|
||||
data.rootText = string.sub( data.text, 1, firstSlash - 1 )
|
||||
data.baseText = string.sub( data.text, 1, lastSlash - 1 )
|
||||
data.subpageText = string.sub( data.text, lastSlash + 1 )
|
||||
else
|
||||
data.isSubpage = false
|
||||
data.rootText = data.text
|
||||
data.baseText = data.text
|
||||
data.subpageText = data.text
|
||||
end
|
||||
|
||||
function data:inNamespace( ns )
|
||||
checkSelf( self, 'inNamespace' )
|
||||
ns = checkNamespace( 'inNamespace', 1, ns )
|
||||
return ns == self.namespace
|
||||
end
|
||||
|
||||
function data:inNamespaces( ... )
|
||||
checkSelf( self, 'inNamespaces' )
|
||||
for i = 1, select( '#', ... ) do
|
||||
local ns = checkNamespace( 'inNamespaces', i, select( i, ... ) )
|
||||
if ns == self.namespace then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function data:hasSubjectNamespace( ns )
|
||||
checkSelf( self, 'hasSubjectNamespace' )
|
||||
ns = checkNamespace( 'hasSubjectNamespace', 1, ns )
|
||||
return ns == mw.site.namespaces[self.namespace].subject.id
|
||||
end
|
||||
|
||||
function data:isSubpageOf( title )
|
||||
checkSelf( self, 'isSubpageOf' )
|
||||
checkType( 'isSubpageOf', 1, title, 'table' )
|
||||
return self.interwiki == title.interwiki and
|
||||
self.namespace == title.namespace and
|
||||
title.text .. '/' == string.sub( self.text, 1, #title.text + 1 )
|
||||
end
|
||||
|
||||
function data:subPageTitle( text )
|
||||
checkSelf( self, 'subpageTitle' )
|
||||
checkType( 'subpageTitle', 1, text, 'string' )
|
||||
return title.makeTitle( data.namespace, data.text .. '/' .. text )
|
||||
end
|
||||
|
||||
function data:partialUrl()
|
||||
checkSelf( self, 'partialUrl' )
|
||||
return data.thePartialUrl
|
||||
end
|
||||
|
||||
function data:fullUrl( query, proto )
|
||||
checkSelf( self, 'fullUrl' )
|
||||
return php.getUrl( self.fullText, 'fullUrl', query, proto )
|
||||
end
|
||||
|
||||
function data:localUrl( query )
|
||||
checkSelf( self, 'localUrl' )
|
||||
return php.getUrl( self.fullText, 'localUrl', query )
|
||||
end
|
||||
|
||||
function data:canonicalUrl( query )
|
||||
checkSelf( self, 'canonicalUrl' )
|
||||
return php.getUrl( self.fullText, 'canonicalUrl', query )
|
||||
end
|
||||
|
||||
function data:getContent()
|
||||
checkSelf( self, 'getContent' )
|
||||
local content = php.getContent( self.fullText )
|
||||
data.getContent = function ( self )
|
||||
checkSelf( self, 'getContent' )
|
||||
return content
|
||||
end
|
||||
return content
|
||||
end
|
||||
|
||||
-- Known fields, both those defined above and any dynamically handled in
|
||||
-- __index. Truthy values represent read-only, and falsey values represent
|
||||
-- read-write. If the value is the string 'e', expensive data will be loaded
|
||||
-- if the field is read.
|
||||
local readOnlyFields = {
|
||||
fragment = false,
|
||||
fullText = true,
|
||||
rootPageTitle = true,
|
||||
basePageTitle = true,
|
||||
talkPageTitle = true,
|
||||
subjectPageTitle = true,
|
||||
fileExists = true,
|
||||
file = true,
|
||||
protectionLevels = true,
|
||||
cascadingProtection = true,
|
||||
exists = 'e',
|
||||
isRedirect = 'e',
|
||||
contentModel = 'e',
|
||||
id = 'e',
|
||||
redirectTarget = true,
|
||||
}
|
||||
for k in pairs( data ) do
|
||||
readOnlyFields[k] = true
|
||||
end
|
||||
|
||||
local function pairsfunc( t, k )
|
||||
local v
|
||||
repeat
|
||||
k = next( readOnlyFields, k )
|
||||
if k == nil then
|
||||
return nil
|
||||
end
|
||||
v = t[k]
|
||||
until v ~= nil
|
||||
return k, v
|
||||
end
|
||||
|
||||
return setmetatable( obj, {
|
||||
__eq = title.equals,
|
||||
__lt = lt,
|
||||
__pairs = function ( t )
|
||||
return pairsfunc, t, nil
|
||||
end,
|
||||
__index = function ( t, k )
|
||||
if k == 'exists' and data.namespace == NS_MEDIA then
|
||||
k = 'fileExists'
|
||||
end
|
||||
|
||||
if readOnlyFields[k] == 'e' and data[k] == nil then
|
||||
for k,v in pairs( php.getExpensiveData( t.fullText ) ) do
|
||||
data[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
if k == 'fullText' then
|
||||
if data.fragment ~= '' then
|
||||
return data.prefixedText .. '#' .. data.fragment
|
||||
else
|
||||
return data.prefixedText
|
||||
end
|
||||
end
|
||||
|
||||
if k == 'rootPageTitle' then
|
||||
return title.makeTitle( data.namespace, data.rootText )
|
||||
end
|
||||
if k == 'basePageTitle' then
|
||||
return title.makeTitle( data.namespace, data.baseText )
|
||||
end
|
||||
if k == 'talkPageTitle' then
|
||||
local ns = mw.site.namespaces[data.namespace].talk
|
||||
if not ns then
|
||||
return nil
|
||||
end
|
||||
if ns.id == data.namespace then
|
||||
return obj
|
||||
end
|
||||
return title.makeTitle( ns.id, data.text )
|
||||
end
|
||||
if k == 'subjectPageTitle' then
|
||||
local ns = mw.site.namespaces[data.namespace].subject
|
||||
if ns.id == data.namespace then
|
||||
return obj
|
||||
end
|
||||
return title.makeTitle( ns.id, data.text )
|
||||
end
|
||||
if k == 'file' then
|
||||
if data.file == nil then
|
||||
data.file = php.getFileInfo( data.prefixedText )
|
||||
end
|
||||
return data.file or nil
|
||||
end
|
||||
if k == 'fileExists' then -- Kept for backward compatibility. Since 1.25, file.exists is preferred over this
|
||||
return t.file and t.file.exists
|
||||
end
|
||||
if k == 'protectionLevels' then
|
||||
if data.protectionLevels == nil then
|
||||
data.protectionLevels = php.protectionLevels( data.prefixedText )
|
||||
end
|
||||
return data.protectionLevels
|
||||
end
|
||||
if k == 'cascadingProtection' then
|
||||
if data.cascadingProtection == nil then
|
||||
data.cascadingProtection = php.cascadingProtection( data.prefixedText )
|
||||
end
|
||||
return data.cascadingProtection
|
||||
end
|
||||
if k == 'redirectTarget' then
|
||||
if data.redirectTarget == nil then
|
||||
data.redirectTarget = makeTitleObject( php.redirectTarget( data.prefixedText ) ) or false
|
||||
end
|
||||
return data.redirectTarget
|
||||
end
|
||||
|
||||
return data[k]
|
||||
end,
|
||||
__newindex = function ( t, k, v )
|
||||
if k == 'fragment' then
|
||||
checkTypeForIndex( k, v, 'string' )
|
||||
v = string.gsub( v, '[%s_]+', ' ' )
|
||||
v = string.gsub( v, '^(.-) ?$', '%1' )
|
||||
data[k] = v
|
||||
elseif readOnlyFields[k] then
|
||||
error( "index '" .. k .. "' is read only", 2 )
|
||||
else
|
||||
readOnlyFields[k] = v and false -- assigns nil if v == nil, false otherwise
|
||||
rawset( t, k, v )
|
||||
end
|
||||
end,
|
||||
__tostring = function ( t )
|
||||
return t.prefixedText
|
||||
end
|
||||
} )
|
||||
end
|
||||
|
||||
function title.setupInterface( options )
|
||||
-- Boilerplate
|
||||
title.setupInterface = nil
|
||||
php = mw_interface
|
||||
mw_interface = nil
|
||||
NS_MEDIA = options.NS_MEDIA
|
||||
|
||||
-- xowa:bgn
|
||||
-- Set current title
|
||||
--title.getCurrentTitle = function ()
|
||||
-- return makeTitleObject( options.thisTitle )
|
||||
--end
|
||||
-- xowa:end
|
||||
|
||||
-- Register this library in the "mw" global
|
||||
mw = mw or {}
|
||||
mw.title = title
|
||||
title.notify_page_changed()
|
||||
|
||||
package.loaded['mw.title'] = title
|
||||
end
|
||||
|
||||
function title.new( text_or_id, defaultNamespace )
|
||||
return makeTitleObject( php.newTitle( text_or_id, defaultNamespace ) )
|
||||
end
|
||||
|
||||
function title.makeTitle( ns, title, fragment, interwiki )
|
||||
return makeTitleObject( php.makeTitle( ns, title, fragment, interwiki ) )
|
||||
end
|
||||
|
||||
function title.equals( a, b )
|
||||
return a.interwiki == b.interwiki and
|
||||
a.namespace == b.namespace and
|
||||
a.text == b.text
|
||||
end
|
||||
|
||||
function title.compare( a, b )
|
||||
if a.interwiki ~= b.interwiki then
|
||||
return a.interwiki < b.interwiki and -1 or 1
|
||||
end
|
||||
if a.namespace ~= b.namespace then
|
||||
return a.namespace < b.namespace and -1 or 1
|
||||
end
|
||||
if a.text ~= b.text then
|
||||
return a.text < b.text and -1 or 1
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
-- xowa:bgn
|
||||
local system_globals =
|
||||
{ ["string"] = true
|
||||
, ["xpcall"] = true
|
||||
, ["package"] = true
|
||||
, ["rawset"] = true
|
||||
, ["_VERSION"] = true
|
||||
, ["error"] = true
|
||||
, ["pcall"] = true
|
||||
, ["os"] = true
|
||||
, ["unpack"] = true
|
||||
, ["math"] = true
|
||||
, ["ipairs"] = true
|
||||
, ["require"] = true
|
||||
, ["mw"] = true
|
||||
, ["tostring"] = true
|
||||
, ["type"] = true
|
||||
, ["pairs"] = true
|
||||
, ["next"] = true
|
||||
, ["select"] = true
|
||||
, ["assert"] = true
|
||||
, ["rawget"] = true
|
||||
, ["tonumber"] = true
|
||||
, ["table"] = true
|
||||
, ["_G"] = true
|
||||
, ["rawequal"] = true
|
||||
, ["setfenv"] = true
|
||||
, ["setmetatable"] = true
|
||||
, ["getfenv"] = true
|
||||
, ["getmetatable"] = true
|
||||
, ["debug"] = true
|
||||
}
|
||||
|
||||
function title.notify_page_changed()
|
||||
local php_data = php.getCurrentTitle()
|
||||
title.getCurrentTitle = function ()
|
||||
return makeTitleObject(php_data)
|
||||
end
|
||||
local delete_list = {}
|
||||
for k,v in pairs(_G) do
|
||||
local type_name = type(v)
|
||||
if not system_globals[k] and type_name ~= 'function' and type_name ~= 'table' then
|
||||
delete_list[k] = k
|
||||
end
|
||||
end
|
||||
for k,v in pairs(delete_list) do
|
||||
_G[k] = nil
|
||||
end
|
||||
end
|
||||
-- xowa:end
|
||||
|
||||
return title
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,85 +1,85 @@
|
||||
-- Get a fresh copy of the basic ustring
|
||||
local old_ustring = package.loaded.ustring
|
||||
package.loaded.ustring = nil
|
||||
local ustring = require( 'ustring' )
|
||||
package.loaded.ustring = old_ustring
|
||||
old_ustring = nil
|
||||
|
||||
local util = require 'libraryUtil'
|
||||
local checkType = util.checkType
|
||||
|
||||
local gmatch_init = nil
|
||||
local gmatch_callback = nil
|
||||
local function php_gmatch( s, pattern )
|
||||
checkType( 'gmatch', 1, s, 'string' )
|
||||
checkType( 'gmatch', 2, pattern, 'string' )
|
||||
|
||||
local re, capt = gmatch_init( s, pattern )
|
||||
local pos = 0
|
||||
return function()
|
||||
local ret
|
||||
pos, ret = gmatch_callback( s, re, capt, pos )
|
||||
return unpack( ret )
|
||||
end, nil, nil
|
||||
end
|
||||
|
||||
function ustring.setupInterface( opt )
|
||||
-- Boilerplate
|
||||
ustring.setupInterface = nil
|
||||
|
||||
-- Set string limits
|
||||
ustring.maxStringLength = opt.stringLengthLimit
|
||||
ustring.maxPatternLength = opt.patternLengthLimit
|
||||
|
||||
-- Gmatch
|
||||
if mw_interface.gmatch_callback and mw_interface.gmatch_init then
|
||||
gmatch_init = mw_interface.gmatch_init
|
||||
gmatch_callback = mw_interface.gmatch_callback
|
||||
ustring.gmatch = php_gmatch
|
||||
end
|
||||
mw_interface.gmatch_init = nil
|
||||
mw_interface.gmatch_callback = nil
|
||||
|
||||
-- Replace pure-lua implementation with php callbacks
|
||||
local nargs = {
|
||||
char = 0,
|
||||
find = 2,
|
||||
match = 2,
|
||||
gsub = 3,
|
||||
}
|
||||
for k, v in pairs( mw_interface ) do
|
||||
local n = nargs[k] or 1
|
||||
if n == 0 then
|
||||
ustring[k] = v
|
||||
else
|
||||
-- Avoid PHP warnings for missing arguments by checking before
|
||||
-- calling PHP.
|
||||
ustring[k] = function ( ... )
|
||||
if select( '#', ... ) < n then
|
||||
error( "too few arguments to mw.ustring." .. k, 2 )
|
||||
end
|
||||
return v( ... )
|
||||
end
|
||||
end
|
||||
end
|
||||
mw_interface = nil
|
||||
|
||||
-- Replace upper/lower with mw.language versions if available
|
||||
if mw and mw.language then
|
||||
local lang = mw.language.getContentLanguage()
|
||||
ustring.upper = function ( s )
|
||||
return lang:uc( s )
|
||||
end
|
||||
ustring.lower = function ( s )
|
||||
return lang:lc( s )
|
||||
end
|
||||
end
|
||||
|
||||
-- Register this library in the "mw" global
|
||||
mw = mw or {}
|
||||
mw.ustring = ustring
|
||||
|
||||
package.loaded['mw.ustring'] = ustring
|
||||
end
|
||||
|
||||
return ustring
|
||||
-- Get a fresh copy of the basic ustring
|
||||
local old_ustring = package.loaded.ustring
|
||||
package.loaded.ustring = nil
|
||||
local ustring = require( 'ustring' )
|
||||
package.loaded.ustring = old_ustring
|
||||
old_ustring = nil
|
||||
|
||||
local util = require 'libraryUtil'
|
||||
local checkType = util.checkType
|
||||
|
||||
local gmatch_init = nil
|
||||
local gmatch_callback = nil
|
||||
local function php_gmatch( s, pattern )
|
||||
checkType( 'gmatch', 1, s, 'string' )
|
||||
checkType( 'gmatch', 2, pattern, 'string' )
|
||||
|
||||
local re, capt = gmatch_init( s, pattern )
|
||||
local pos = 0
|
||||
return function()
|
||||
local ret
|
||||
pos, ret = gmatch_callback( s, re, capt, pos )
|
||||
return unpack( ret )
|
||||
end, nil, nil
|
||||
end
|
||||
|
||||
function ustring.setupInterface( opt )
|
||||
-- Boilerplate
|
||||
ustring.setupInterface = nil
|
||||
|
||||
-- Set string limits
|
||||
ustring.maxStringLength = opt.stringLengthLimit
|
||||
ustring.maxPatternLength = opt.patternLengthLimit
|
||||
|
||||
-- Gmatch
|
||||
if mw_interface.gmatch_callback and mw_interface.gmatch_init then
|
||||
gmatch_init = mw_interface.gmatch_init
|
||||
gmatch_callback = mw_interface.gmatch_callback
|
||||
ustring.gmatch = php_gmatch
|
||||
end
|
||||
mw_interface.gmatch_init = nil
|
||||
mw_interface.gmatch_callback = nil
|
||||
|
||||
-- Replace pure-lua implementation with php callbacks
|
||||
local nargs = {
|
||||
char = 0,
|
||||
find = 2,
|
||||
match = 2,
|
||||
gsub = 3,
|
||||
}
|
||||
for k, v in pairs( mw_interface ) do
|
||||
local n = nargs[k] or 1
|
||||
if n == 0 then
|
||||
ustring[k] = v
|
||||
else
|
||||
-- Avoid PHP warnings for missing arguments by checking before
|
||||
-- calling PHP.
|
||||
ustring[k] = function ( ... )
|
||||
if select( '#', ... ) < n then
|
||||
error( "too few arguments to mw.ustring." .. k, 2 )
|
||||
end
|
||||
return v( ... )
|
||||
end
|
||||
end
|
||||
end
|
||||
mw_interface = nil
|
||||
|
||||
-- Replace upper/lower with mw.language versions if available
|
||||
if mw and mw.language then
|
||||
local lang = mw.language.getContentLanguage()
|
||||
ustring.upper = function ( s )
|
||||
return lang:uc( s )
|
||||
end
|
||||
ustring.lower = function ( s )
|
||||
return lang:lc( s )
|
||||
end
|
||||
end
|
||||
|
||||
-- Register this library in the "mw" global
|
||||
mw = mw or {}
|
||||
mw.ustring = ustring
|
||||
|
||||
package.loaded['mw.ustring'] = ustring
|
||||
end
|
||||
|
||||
return ustring
|
||||
|
||||
@@ -1,386 +1,386 @@
|
||||
--XOWA:updated 2019-08-19
|
||||
--[[
|
||||
Registers and defines functions to handle Wikibase Entities through the Scribunto extension.
|
||||
|
||||
@since 0.5
|
||||
|
||||
@license GNU GPL v2+
|
||||
@author Marius Hoch < hoo@online.de >
|
||||
@author Bene* < benestar.wikimedia@gmail.com >
|
||||
]]
|
||||
|
||||
local php = mw_interface
|
||||
mw_interface = nil
|
||||
local Entity = {}
|
||||
local metatable = {}
|
||||
local methodtable = {}
|
||||
local util = require 'libraryUtil'
|
||||
local checkType = util.checkType
|
||||
local checkTypeMulti = util.checkTypeMulti
|
||||
|
||||
metatable.__index = methodtable
|
||||
|
||||
-- Claim ranks (Claim::RANK_* in PHP)
|
||||
Entity.claimRanks = {
|
||||
RANK_TRUTH = 3,
|
||||
RANK_PREFERRED = 2,
|
||||
RANK_NORMAL = 1,
|
||||
RANK_DEPRECATED = 0
|
||||
}
|
||||
|
||||
-- Is this a valid property id (Pnnn)?
|
||||
--
|
||||
-- @param {string} propertyId
|
||||
local isValidPropertyId = function( propertyId )
|
||||
return type( propertyId ) == 'string' and propertyId:match( '^P[1-9]%d*$' )
|
||||
end
|
||||
|
||||
-- Log access to claims of entity
|
||||
--
|
||||
-- @param {string} entityId
|
||||
-- @param {string} propertyId
|
||||
local addStatementUsage = function( entityId, propertyId )
|
||||
if isValidPropertyId( propertyId ) then
|
||||
-- Only attempt to track the usage if we have a valid property id.
|
||||
php.addStatementUsage( entityId, propertyId )
|
||||
end
|
||||
end
|
||||
|
||||
-- Function to mask an entity's subtables in order to log access
|
||||
-- Code for logging based on: http://www.lua.org/pil/13.4.4.html
|
||||
--
|
||||
-- @param {table} entity
|
||||
-- @param {string} tableName
|
||||
-- @param {function} usageFunc
|
||||
local maskEntityTable = function( entity, tableName, usageFunc )
|
||||
if entity[tableName] == nil then
|
||||
return
|
||||
end
|
||||
local actualEntityTable = entity[tableName]
|
||||
entity[tableName] = {}
|
||||
|
||||
local pseudoTableMetatable = {}
|
||||
pseudoTableMetatable.__index = function( _, key )
|
||||
usageFunc( entity.id, key )
|
||||
return actualEntityTable[key]
|
||||
end
|
||||
|
||||
pseudoTableMetatable.__newindex = function( _, _, _ )
|
||||
error( 'Entity cannot be modified', 2 )
|
||||
end
|
||||
|
||||
local logNext = function( _, key )
|
||||
local k, v = next( actualEntityTable, key )
|
||||
if k ~= nil then
|
||||
usageFunc( entity.id, k )
|
||||
end
|
||||
return k, v
|
||||
end
|
||||
|
||||
pseudoTableMetatable.__pairs = function( _ )
|
||||
return logNext, {}, nil
|
||||
end
|
||||
|
||||
setmetatable( entity[tableName], pseudoTableMetatable )
|
||||
end
|
||||
|
||||
local noUsageTracking = function()
|
||||
end
|
||||
|
||||
-- Function to mask an entity's subtables in order to log access and prevent modifications
|
||||
--
|
||||
-- @param {table} entity
|
||||
-- @param {bool} fineGrainedTracking
|
||||
local maskEntityTables = function ( entity, fineGrainedTracking )
|
||||
if fineGrainedTracking then
|
||||
maskEntityTable( entity, 'claims', addStatementUsage )
|
||||
maskEntityTable( entity, 'labels', php.addLabelUsage )
|
||||
maskEntityTable( entity, 'sitelinks', php.addSiteLinksUsage )
|
||||
maskEntityTable( entity, 'descriptions', php.addDescriptionUsage )
|
||||
maskEntityTable( entity, 'aliases', php.addOtherUsage )
|
||||
else
|
||||
maskEntityTable( entity, 'claims', noUsageTracking )
|
||||
maskEntityTable( entity, 'labels', noUsageTracking )
|
||||
maskEntityTable( entity, 'sitelinks', noUsageTracking )
|
||||
maskEntityTable( entity, 'descriptions', noUsageTracking )
|
||||
maskEntityTable( entity, 'aliases', noUsageTracking )
|
||||
end
|
||||
end
|
||||
|
||||
-- Create new entity object from given data
|
||||
--
|
||||
-- @param {table} data
|
||||
Entity.create = function( data )
|
||||
if type( data ) ~= 'table' then
|
||||
error( 'Expected a table obtained via mw.wikibase.getEntityObject, got ' .. type( data ) .. ' instead' )
|
||||
end
|
||||
if next( data ) == nil then
|
||||
error( 'Expected a non-empty table obtained via mw.wikibase.getEntityObject' )
|
||||
end
|
||||
if type( data.schemaVersion ) ~= 'number' then
|
||||
error( 'data.schemaVersion must be a number, got ' .. type( data.schemaVersion ) .. ' instead' )
|
||||
end
|
||||
if data.schemaVersion < 2 then
|
||||
error( 'mw.wikibase.entity must not be constructed using legacy data' )
|
||||
end
|
||||
if type( data.id ) ~= 'string' then
|
||||
error( 'data.id must be a string, got ' .. type( data.id ) .. ' instead' )
|
||||
end
|
||||
|
||||
local entity = data
|
||||
maskEntityTables( entity, php.getSetting( 'fineGrainedLuaTracking' ) )
|
||||
|
||||
setmetatable( entity, metatable )
|
||||
return entity
|
||||
end
|
||||
|
||||
-- Get the id serialization from this entity.
|
||||
methodtable.getId = function( entity )
|
||||
return entity.id
|
||||
end
|
||||
|
||||
-- Get a term of a given type for a given language code or the content language (on monolingual wikis)
|
||||
-- or the user's language (on multilingual wikis).
|
||||
-- Second return parameter is the language the term is in.
|
||||
--
|
||||
-- @param {table} entity
|
||||
-- @param {string} termType A valid key in the entity table (either labels, descriptions or aliases)
|
||||
-- @param {string|number} langCode
|
||||
local getTermAndLang = function( entity, termType, langCode )
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.entity.getTermAndLang.call' )
|
||||
|
||||
langCode = langCode or php.getLanguageCode()
|
||||
|
||||
if langCode == nil then
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
if entity[termType] == nil then
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
local term = entity[termType][langCode]
|
||||
|
||||
if term == nil then
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
local actualLang = term.language or langCode
|
||||
return term.value, actualLang
|
||||
end
|
||||
|
||||
-- Get the label for a given language code or the content language (on monolingual wikis)
|
||||
-- or the user's language (on multilingual wikis).
|
||||
--
|
||||
-- @param {string|number} [langCode]
|
||||
methodtable.getLabel = function( entity, langCode )
|
||||
checkTypeMulti( 'getLabel', 1, langCode, { 'string', 'number', 'nil' } )
|
||||
|
||||
local label = getTermAndLang( entity, 'labels', langCode )
|
||||
return label
|
||||
end
|
||||
|
||||
-- Get the description for a given language code or the content language (on monolingual wikis)
|
||||
-- or the user's language (on multilingual wikis).
|
||||
--
|
||||
-- @param {string|number} [langCode]
|
||||
methodtable.getDescription = function( entity, langCode )
|
||||
checkTypeMulti( 'getDescription', 1, langCode, { 'string', 'number', 'nil' } )
|
||||
|
||||
local description = getTermAndLang( entity, 'descriptions', langCode )
|
||||
return description
|
||||
end
|
||||
|
||||
-- Get the label for a given language code or the content language (on monolingual wikis)
|
||||
-- or the user's language (on multilingual wikis).
|
||||
-- Has the language the returned label is in as an additional second return parameter.
|
||||
--
|
||||
-- @param {string|number} [langCode]
|
||||
methodtable.getLabelWithLang = function( entity, langCode )
|
||||
checkTypeMulti( 'getLabelWithLang', 1, langCode, { 'string', 'number', 'nil' } )
|
||||
|
||||
return getTermAndLang( entity, 'labels', langCode )
|
||||
end
|
||||
|
||||
-- Get the description for a given language code or the content language (on monolingual wikis)
|
||||
-- or the user's language (on multilingual wikis).
|
||||
-- Has the language the returned description is in as an additional second return parameter.
|
||||
--
|
||||
-- @param {string|number} [langCode]
|
||||
methodtable.getDescriptionWithLang = function( entity, langCode )
|
||||
checkTypeMulti( 'getDescriptionWithLang', 1, langCode, { 'string', 'number', 'nil' } )
|
||||
|
||||
return getTermAndLang( entity, 'descriptions', langCode )
|
||||
end
|
||||
|
||||
-- Get the sitelink title linking to the given site id
|
||||
--
|
||||
-- @param {string|number} [globalSiteId]
|
||||
methodtable.getSitelink = function( entity, globalSiteId )
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.entity.getSitelink.call' )
|
||||
|
||||
checkTypeMulti( 'getSitelink', 1, globalSiteId, { 'string', 'number', 'nil' } )
|
||||
|
||||
if entity.sitelinks == nil then
|
||||
return nil
|
||||
end
|
||||
|
||||
globalSiteId = globalSiteId or php.getGlobalSiteId()
|
||||
|
||||
if globalSiteId == nil then
|
||||
return nil
|
||||
end
|
||||
|
||||
local sitelink = entity.sitelinks[globalSiteId]
|
||||
|
||||
if sitelink == nil then
|
||||
return nil
|
||||
end
|
||||
|
||||
return sitelink.title
|
||||
end
|
||||
|
||||
-- @param {table} entity
|
||||
-- @param {string} propertyLabelOrId
|
||||
-- @param {string} funcName for error logging
|
||||
local getEntityStatements = function( entity, propertyLabelOrId, funcName )
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.entity.getEntityStatements.call' )
|
||||
|
||||
checkType( funcName, 1, propertyLabelOrId, 'string' )
|
||||
|
||||
if not entity.claims then
|
||||
return {}
|
||||
end
|
||||
|
||||
local propertyId = propertyLabelOrId
|
||||
if not isValidPropertyId( propertyId ) then
|
||||
propertyId = mw.wikibase.resolvePropertyId( propertyId )
|
||||
end
|
||||
|
||||
if propertyId and entity.claims[propertyId] then
|
||||
return entity.claims[propertyId]
|
||||
end
|
||||
|
||||
return {}
|
||||
end
|
||||
|
||||
-- Get the best statements with the given property id or label
|
||||
--
|
||||
-- @param {string} propertyLabelOrId
|
||||
methodtable.getBestStatements = function( entity, propertyLabelOrId )
|
||||
local entityStatements = getEntityStatements( entity, propertyLabelOrId, 'getBestStatements' )
|
||||
local statements = {}
|
||||
local bestRank = 'normal'
|
||||
|
||||
for _, statement in pairs( entityStatements ) do
|
||||
if statement.rank == bestRank then
|
||||
statements[#statements + 1] = statement
|
||||
elseif statement.rank == 'preferred' then
|
||||
statements = { statement }
|
||||
bestRank = 'preferred'
|
||||
end
|
||||
end
|
||||
|
||||
return statements
|
||||
end
|
||||
|
||||
-- Get all statements with the given property id or label
|
||||
--
|
||||
-- @param {string} propertyLabelOrId
|
||||
methodtable.getAllStatements = function( entity, propertyLabelOrId )
|
||||
return getEntityStatements( entity, propertyLabelOrId, 'getAllStatements' )
|
||||
end
|
||||
|
||||
-- Get a table with all property ids attached to the entity.
|
||||
methodtable.getProperties = function( entity )
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.entity.getProperties.call' )
|
||||
|
||||
if entity.claims == nil then
|
||||
return {}
|
||||
end
|
||||
|
||||
-- Get the keys (property ids)
|
||||
local properties = {}
|
||||
|
||||
local n = 0
|
||||
for k, _ in pairs( entity.claims ) do
|
||||
n = n + 1
|
||||
properties[n] = k
|
||||
end
|
||||
|
||||
return properties
|
||||
end
|
||||
|
||||
-- Get the formatted value of the claims with the given property id
|
||||
--
|
||||
-- @param {table} entity
|
||||
-- @param {string} phpFormatterFunction
|
||||
-- @param {string} propertyLabelOrId
|
||||
-- @param {table} [acceptableRanks]
|
||||
local formatValuesByPropertyId = function( entity, phpFormatterFunction, propertyLabelOrId, acceptableRanks )
|
||||
acceptableRanks = acceptableRanks or nil
|
||||
|
||||
local formatted = php[phpFormatterFunction](
|
||||
entity.id,
|
||||
propertyLabelOrId,
|
||||
acceptableRanks
|
||||
)
|
||||
|
||||
local label
|
||||
if isValidPropertyId( propertyLabelOrId ) then
|
||||
label = mw.wikibase.getLabel( propertyLabelOrId )
|
||||
end
|
||||
|
||||
if label == nil then
|
||||
-- Make the label fallback on the entity id for convenience/ consistency
|
||||
label = propertyLabelOrId
|
||||
end
|
||||
|
||||
return {
|
||||
value = formatted,
|
||||
label = label
|
||||
}
|
||||
end
|
||||
|
||||
-- Format the main Snaks belonging to a Statement (which is identified by a PropertyId
|
||||
-- or the label of a Property) as wikitext escaped plain text.
|
||||
--
|
||||
-- @param {string} propertyLabelOrId
|
||||
-- @param {table} [acceptableRanks]
|
||||
methodtable.formatPropertyValues = function( entity, propertyLabelOrId, acceptableRanks )
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.entity.formatPropertyValues.call' )
|
||||
|
||||
checkType( 'formatPropertyValues', 1, propertyLabelOrId, 'string' )
|
||||
checkTypeMulti( 'formatPropertyValues', 2, acceptableRanks, { 'table', 'nil' } )
|
||||
|
||||
return formatValuesByPropertyId(
|
||||
entity,
|
||||
'formatPropertyValues',
|
||||
propertyLabelOrId,
|
||||
acceptableRanks
|
||||
);
|
||||
end
|
||||
|
||||
-- Format the main Snaks belonging to a Statement (which is identified by a PropertyId
|
||||
-- or the label of a Property) as rich wikitext.
|
||||
--
|
||||
-- @param {string} propertyLabelOrId
|
||||
-- @param {table} [acceptableRanks]
|
||||
methodtable.formatStatements = function( entity, propertyLabelOrId, acceptableRanks )
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.entity.formatStatements.call' )
|
||||
|
||||
checkType( 'formatStatements', 1, propertyLabelOrId, 'string' )
|
||||
checkTypeMulti( 'formatStatements', 2, acceptableRanks, { 'table', 'nil' } )
|
||||
|
||||
return formatValuesByPropertyId(
|
||||
entity,
|
||||
'formatStatements',
|
||||
propertyLabelOrId,
|
||||
acceptableRanks
|
||||
);
|
||||
end
|
||||
|
||||
mw.wikibase.entity = Entity
|
||||
package.loaded['mw.wikibase.entity'] = Entity
|
||||
|
||||
return Entity
|
||||
--XOWA:updated 2019-08-19
|
||||
--[[
|
||||
Registers and defines functions to handle Wikibase Entities through the Scribunto extension.
|
||||
|
||||
@since 0.5
|
||||
|
||||
@license GNU GPL v2+
|
||||
@author Marius Hoch < hoo@online.de >
|
||||
@author Bene* < benestar.wikimedia@gmail.com >
|
||||
]]
|
||||
|
||||
local php = mw_interface
|
||||
mw_interface = nil
|
||||
local Entity = {}
|
||||
local metatable = {}
|
||||
local methodtable = {}
|
||||
local util = require 'libraryUtil'
|
||||
local checkType = util.checkType
|
||||
local checkTypeMulti = util.checkTypeMulti
|
||||
|
||||
metatable.__index = methodtable
|
||||
|
||||
-- Claim ranks (Claim::RANK_* in PHP)
|
||||
Entity.claimRanks = {
|
||||
RANK_TRUTH = 3,
|
||||
RANK_PREFERRED = 2,
|
||||
RANK_NORMAL = 1,
|
||||
RANK_DEPRECATED = 0
|
||||
}
|
||||
|
||||
-- Is this a valid property id (Pnnn)?
|
||||
--
|
||||
-- @param {string} propertyId
|
||||
local isValidPropertyId = function( propertyId )
|
||||
return type( propertyId ) == 'string' and propertyId:match( '^P[1-9]%d*$' )
|
||||
end
|
||||
|
||||
-- Log access to claims of entity
|
||||
--
|
||||
-- @param {string} entityId
|
||||
-- @param {string} propertyId
|
||||
local addStatementUsage = function( entityId, propertyId )
|
||||
if isValidPropertyId( propertyId ) then
|
||||
-- Only attempt to track the usage if we have a valid property id.
|
||||
php.addStatementUsage( entityId, propertyId )
|
||||
end
|
||||
end
|
||||
|
||||
-- Function to mask an entity's subtables in order to log access
|
||||
-- Code for logging based on: http://www.lua.org/pil/13.4.4.html
|
||||
--
|
||||
-- @param {table} entity
|
||||
-- @param {string} tableName
|
||||
-- @param {function} usageFunc
|
||||
local maskEntityTable = function( entity, tableName, usageFunc )
|
||||
if entity[tableName] == nil then
|
||||
return
|
||||
end
|
||||
local actualEntityTable = entity[tableName]
|
||||
entity[tableName] = {}
|
||||
|
||||
local pseudoTableMetatable = {}
|
||||
pseudoTableMetatable.__index = function( _, key )
|
||||
usageFunc( entity.id, key )
|
||||
return actualEntityTable[key]
|
||||
end
|
||||
|
||||
pseudoTableMetatable.__newindex = function( _, _, _ )
|
||||
error( 'Entity cannot be modified', 2 )
|
||||
end
|
||||
|
||||
local logNext = function( _, key )
|
||||
local k, v = next( actualEntityTable, key )
|
||||
if k ~= nil then
|
||||
usageFunc( entity.id, k )
|
||||
end
|
||||
return k, v
|
||||
end
|
||||
|
||||
pseudoTableMetatable.__pairs = function( _ )
|
||||
return logNext, {}, nil
|
||||
end
|
||||
|
||||
setmetatable( entity[tableName], pseudoTableMetatable )
|
||||
end
|
||||
|
||||
local noUsageTracking = function()
|
||||
end
|
||||
|
||||
-- Function to mask an entity's subtables in order to log access and prevent modifications
|
||||
--
|
||||
-- @param {table} entity
|
||||
-- @param {bool} fineGrainedTracking
|
||||
local maskEntityTables = function ( entity, fineGrainedTracking )
|
||||
if fineGrainedTracking then
|
||||
maskEntityTable( entity, 'claims', addStatementUsage )
|
||||
maskEntityTable( entity, 'labels', php.addLabelUsage )
|
||||
maskEntityTable( entity, 'sitelinks', php.addSiteLinksUsage )
|
||||
maskEntityTable( entity, 'descriptions', php.addDescriptionUsage )
|
||||
maskEntityTable( entity, 'aliases', php.addOtherUsage )
|
||||
else
|
||||
maskEntityTable( entity, 'claims', noUsageTracking )
|
||||
maskEntityTable( entity, 'labels', noUsageTracking )
|
||||
maskEntityTable( entity, 'sitelinks', noUsageTracking )
|
||||
maskEntityTable( entity, 'descriptions', noUsageTracking )
|
||||
maskEntityTable( entity, 'aliases', noUsageTracking )
|
||||
end
|
||||
end
|
||||
|
||||
-- Create new entity object from given data
|
||||
--
|
||||
-- @param {table} data
|
||||
Entity.create = function( data )
|
||||
if type( data ) ~= 'table' then
|
||||
error( 'Expected a table obtained via mw.wikibase.getEntityObject, got ' .. type( data ) .. ' instead' )
|
||||
end
|
||||
if next( data ) == nil then
|
||||
error( 'Expected a non-empty table obtained via mw.wikibase.getEntityObject' )
|
||||
end
|
||||
if type( data.schemaVersion ) ~= 'number' then
|
||||
error( 'data.schemaVersion must be a number, got ' .. type( data.schemaVersion ) .. ' instead' )
|
||||
end
|
||||
if data.schemaVersion < 2 then
|
||||
error( 'mw.wikibase.entity must not be constructed using legacy data' )
|
||||
end
|
||||
if type( data.id ) ~= 'string' then
|
||||
error( 'data.id must be a string, got ' .. type( data.id ) .. ' instead' )
|
||||
end
|
||||
|
||||
local entity = data
|
||||
maskEntityTables( entity, php.getSetting( 'fineGrainedLuaTracking' ) )
|
||||
|
||||
setmetatable( entity, metatable )
|
||||
return entity
|
||||
end
|
||||
|
||||
-- Get the id serialization from this entity.
|
||||
methodtable.getId = function( entity )
|
||||
return entity.id
|
||||
end
|
||||
|
||||
-- Get a term of a given type for a given language code or the content language (on monolingual wikis)
|
||||
-- or the user's language (on multilingual wikis).
|
||||
-- Second return parameter is the language the term is in.
|
||||
--
|
||||
-- @param {table} entity
|
||||
-- @param {string} termType A valid key in the entity table (either labels, descriptions or aliases)
|
||||
-- @param {string|number} langCode
|
||||
local getTermAndLang = function( entity, termType, langCode )
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.entity.getTermAndLang.call' )
|
||||
|
||||
langCode = langCode or php.getLanguageCode()
|
||||
|
||||
if langCode == nil then
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
if entity[termType] == nil then
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
local term = entity[termType][langCode]
|
||||
|
||||
if term == nil then
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
local actualLang = term.language or langCode
|
||||
return term.value, actualLang
|
||||
end
|
||||
|
||||
-- Get the label for a given language code or the content language (on monolingual wikis)
|
||||
-- or the user's language (on multilingual wikis).
|
||||
--
|
||||
-- @param {string|number} [langCode]
|
||||
methodtable.getLabel = function( entity, langCode )
|
||||
checkTypeMulti( 'getLabel', 1, langCode, { 'string', 'number', 'nil' } )
|
||||
|
||||
local label = getTermAndLang( entity, 'labels', langCode )
|
||||
return label
|
||||
end
|
||||
|
||||
-- Get the description for a given language code or the content language (on monolingual wikis)
|
||||
-- or the user's language (on multilingual wikis).
|
||||
--
|
||||
-- @param {string|number} [langCode]
|
||||
methodtable.getDescription = function( entity, langCode )
|
||||
checkTypeMulti( 'getDescription', 1, langCode, { 'string', 'number', 'nil' } )
|
||||
|
||||
local description = getTermAndLang( entity, 'descriptions', langCode )
|
||||
return description
|
||||
end
|
||||
|
||||
-- Get the label for a given language code or the content language (on monolingual wikis)
|
||||
-- or the user's language (on multilingual wikis).
|
||||
-- Has the language the returned label is in as an additional second return parameter.
|
||||
--
|
||||
-- @param {string|number} [langCode]
|
||||
methodtable.getLabelWithLang = function( entity, langCode )
|
||||
checkTypeMulti( 'getLabelWithLang', 1, langCode, { 'string', 'number', 'nil' } )
|
||||
|
||||
return getTermAndLang( entity, 'labels', langCode )
|
||||
end
|
||||
|
||||
-- Get the description for a given language code or the content language (on monolingual wikis)
|
||||
-- or the user's language (on multilingual wikis).
|
||||
-- Has the language the returned description is in as an additional second return parameter.
|
||||
--
|
||||
-- @param {string|number} [langCode]
|
||||
methodtable.getDescriptionWithLang = function( entity, langCode )
|
||||
checkTypeMulti( 'getDescriptionWithLang', 1, langCode, { 'string', 'number', 'nil' } )
|
||||
|
||||
return getTermAndLang( entity, 'descriptions', langCode )
|
||||
end
|
||||
|
||||
-- Get the sitelink title linking to the given site id
|
||||
--
|
||||
-- @param {string|number} [globalSiteId]
|
||||
methodtable.getSitelink = function( entity, globalSiteId )
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.entity.getSitelink.call' )
|
||||
|
||||
checkTypeMulti( 'getSitelink', 1, globalSiteId, { 'string', 'number', 'nil' } )
|
||||
|
||||
if entity.sitelinks == nil then
|
||||
return nil
|
||||
end
|
||||
|
||||
globalSiteId = globalSiteId or php.getGlobalSiteId()
|
||||
|
||||
if globalSiteId == nil then
|
||||
return nil
|
||||
end
|
||||
|
||||
local sitelink = entity.sitelinks[globalSiteId]
|
||||
|
||||
if sitelink == nil then
|
||||
return nil
|
||||
end
|
||||
|
||||
return sitelink.title
|
||||
end
|
||||
|
||||
-- @param {table} entity
|
||||
-- @param {string} propertyLabelOrId
|
||||
-- @param {string} funcName for error logging
|
||||
local getEntityStatements = function( entity, propertyLabelOrId, funcName )
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.entity.getEntityStatements.call' )
|
||||
|
||||
checkType( funcName, 1, propertyLabelOrId, 'string' )
|
||||
|
||||
if not entity.claims then
|
||||
return {}
|
||||
end
|
||||
|
||||
local propertyId = propertyLabelOrId
|
||||
if not isValidPropertyId( propertyId ) then
|
||||
propertyId = mw.wikibase.resolvePropertyId( propertyId )
|
||||
end
|
||||
|
||||
if propertyId and entity.claims[propertyId] then
|
||||
return entity.claims[propertyId]
|
||||
end
|
||||
|
||||
return {}
|
||||
end
|
||||
|
||||
-- Get the best statements with the given property id or label
|
||||
--
|
||||
-- @param {string} propertyLabelOrId
|
||||
methodtable.getBestStatements = function( entity, propertyLabelOrId )
|
||||
local entityStatements = getEntityStatements( entity, propertyLabelOrId, 'getBestStatements' )
|
||||
local statements = {}
|
||||
local bestRank = 'normal'
|
||||
|
||||
for _, statement in pairs( entityStatements ) do
|
||||
if statement.rank == bestRank then
|
||||
statements[#statements + 1] = statement
|
||||
elseif statement.rank == 'preferred' then
|
||||
statements = { statement }
|
||||
bestRank = 'preferred'
|
||||
end
|
||||
end
|
||||
|
||||
return statements
|
||||
end
|
||||
|
||||
-- Get all statements with the given property id or label
|
||||
--
|
||||
-- @param {string} propertyLabelOrId
|
||||
methodtable.getAllStatements = function( entity, propertyLabelOrId )
|
||||
return getEntityStatements( entity, propertyLabelOrId, 'getAllStatements' )
|
||||
end
|
||||
|
||||
-- Get a table with all property ids attached to the entity.
|
||||
methodtable.getProperties = function( entity )
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.entity.getProperties.call' )
|
||||
|
||||
if entity.claims == nil then
|
||||
return {}
|
||||
end
|
||||
|
||||
-- Get the keys (property ids)
|
||||
local properties = {}
|
||||
|
||||
local n = 0
|
||||
for k, _ in pairs( entity.claims ) do
|
||||
n = n + 1
|
||||
properties[n] = k
|
||||
end
|
||||
|
||||
return properties
|
||||
end
|
||||
|
||||
-- Get the formatted value of the claims with the given property id
|
||||
--
|
||||
-- @param {table} entity
|
||||
-- @param {string} phpFormatterFunction
|
||||
-- @param {string} propertyLabelOrId
|
||||
-- @param {table} [acceptableRanks]
|
||||
local formatValuesByPropertyId = function( entity, phpFormatterFunction, propertyLabelOrId, acceptableRanks )
|
||||
acceptableRanks = acceptableRanks or nil
|
||||
|
||||
local formatted = php[phpFormatterFunction](
|
||||
entity.id,
|
||||
propertyLabelOrId,
|
||||
acceptableRanks
|
||||
)
|
||||
|
||||
local label
|
||||
if isValidPropertyId( propertyLabelOrId ) then
|
||||
label = mw.wikibase.getLabel( propertyLabelOrId )
|
||||
end
|
||||
|
||||
if label == nil then
|
||||
-- Make the label fallback on the entity id for convenience/ consistency
|
||||
label = propertyLabelOrId
|
||||
end
|
||||
|
||||
return {
|
||||
value = formatted,
|
||||
label = label
|
||||
}
|
||||
end
|
||||
|
||||
-- Format the main Snaks belonging to a Statement (which is identified by a PropertyId
|
||||
-- or the label of a Property) as wikitext escaped plain text.
|
||||
--
|
||||
-- @param {string} propertyLabelOrId
|
||||
-- @param {table} [acceptableRanks]
|
||||
methodtable.formatPropertyValues = function( entity, propertyLabelOrId, acceptableRanks )
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.entity.formatPropertyValues.call' )
|
||||
|
||||
checkType( 'formatPropertyValues', 1, propertyLabelOrId, 'string' )
|
||||
checkTypeMulti( 'formatPropertyValues', 2, acceptableRanks, { 'table', 'nil' } )
|
||||
|
||||
return formatValuesByPropertyId(
|
||||
entity,
|
||||
'formatPropertyValues',
|
||||
propertyLabelOrId,
|
||||
acceptableRanks
|
||||
);
|
||||
end
|
||||
|
||||
-- Format the main Snaks belonging to a Statement (which is identified by a PropertyId
|
||||
-- or the label of a Property) as rich wikitext.
|
||||
--
|
||||
-- @param {string} propertyLabelOrId
|
||||
-- @param {table} [acceptableRanks]
|
||||
methodtable.formatStatements = function( entity, propertyLabelOrId, acceptableRanks )
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.entity.formatStatements.call' )
|
||||
|
||||
checkType( 'formatStatements', 1, propertyLabelOrId, 'string' )
|
||||
checkTypeMulti( 'formatStatements', 2, acceptableRanks, { 'table', 'nil' } )
|
||||
|
||||
return formatValuesByPropertyId(
|
||||
entity,
|
||||
'formatStatements',
|
||||
propertyLabelOrId,
|
||||
acceptableRanks
|
||||
);
|
||||
end
|
||||
|
||||
mw.wikibase.entity = Entity
|
||||
package.loaded['mw.wikibase.entity'] = Entity
|
||||
|
||||
return Entity
|
||||
|
||||
@@ -1,485 +1,485 @@
|
||||
--XOWA:updated 2019-11-22
|
||||
--REF:https://github.com/wikimedia/mediawiki-extensions-Wikibase/blob/master/client/includes/DataAccess/Scribunto/mw.wikibase.lua
|
||||
--[[
|
||||
Registers and defines functions to access Wikibase through the Scribunto extension
|
||||
Provides Lua setupInterface
|
||||
|
||||
@since 0.4
|
||||
|
||||
@license GNU GPL v2+
|
||||
@author Jens Ohlig < jens.ohlig@wikimedia.de >
|
||||
@author Marius Hoch < hoo@online.de >
|
||||
@author Bene* < benestar.wikimedia@gmail.com >
|
||||
]]
|
||||
|
||||
local wikibase = {}
|
||||
local util = require 'libraryUtil'
|
||||
local checkType = util.checkType
|
||||
local checkTypeMulti = util.checkTypeMulti
|
||||
|
||||
-- Get a cache data structure to use with addToCache and getFromCache
|
||||
-- with a given maximum size.
|
||||
--
|
||||
-- @param maxCacheSize
|
||||
local function initCache( maxCacheSize )
|
||||
return {
|
||||
data = {},
|
||||
order = {},
|
||||
size = 0,
|
||||
maxCacheSize = maxCacheSize,
|
||||
}
|
||||
end
|
||||
|
||||
-- Cache a given value (can also be false, in case it doesn't exist).
|
||||
--
|
||||
-- @param cache
|
||||
-- @param key
|
||||
-- @param value
|
||||
local function addToCache( cache, key, value )
|
||||
if cache.size == cache.maxCacheSize then
|
||||
local toRemove = table.remove( cache.order, cache.maxCacheSize )
|
||||
cache.data[ toRemove ] = nil
|
||||
else
|
||||
cache.size = cache.size + 1
|
||||
end
|
||||
|
||||
table.insert( cache.order, 1, key )
|
||||
cache.data[ key ] = value
|
||||
end
|
||||
|
||||
-- Retrieve a value from a cache. Will return nil in case of a cache miss.
|
||||
--
|
||||
-- @param cache
|
||||
-- @param key
|
||||
local function getFromCache( cache, key )
|
||||
if cache.data[ key ] ~= nil then
|
||||
for cacheOrderId, cacheOrderKey in pairs( cache.order ) do
|
||||
if cacheOrderKey == key then
|
||||
table.remove( cache.order, cacheOrderId )
|
||||
break
|
||||
end
|
||||
end
|
||||
table.insert( cache.order, 1, key )
|
||||
end
|
||||
|
||||
return cache.data[ key ]
|
||||
end
|
||||
|
||||
-- 15 slot cache for entities
|
||||
local entityCache = initCache( 15 )
|
||||
|
||||
-- 50 slot cache for statements
|
||||
local statementCache = initCache( 50 )
|
||||
|
||||
-- xowa:bgn
|
||||
-- NOTE: moved from inside wikibase.setupInterface; ISSUE#:638; DATE:2020-03-07
|
||||
-- Caching variable for the entity id string belonging to the current page (nil if page is not linked to an entity)
|
||||
local pageEntityId = false
|
||||
-- xowa:end
|
||||
|
||||
function wikibase.setupInterface()
|
||||
local php = mw_interface
|
||||
mw_interface = nil
|
||||
|
||||
-- Get the entity id of the connected item, if id is nil. Cached.
|
||||
local function getIdOfConnectedItemIfNil( id )
|
||||
if id == nil then
|
||||
return wikibase.getEntityIdForCurrentPage()
|
||||
end
|
||||
|
||||
return id
|
||||
end
|
||||
|
||||
-- Get the mw.wikibase.entity object for a given id. Cached.
|
||||
local function getEntityObject( id )
|
||||
local entity = getFromCache( entityCache, id )
|
||||
|
||||
if entity == nil then
|
||||
entity = php.getEntity( id )
|
||||
|
||||
if id ~= wikibase.getEntityIdForCurrentPage() then
|
||||
-- Accessing an arbitrary entity is supposed to increment the expensive function count
|
||||
php.incrementExpensiveFunctionCount()
|
||||
end
|
||||
|
||||
if type( entity ) ~= 'table' then
|
||||
entity = false
|
||||
end
|
||||
|
||||
addToCache( entityCache, id, entity )
|
||||
end
|
||||
|
||||
if type( entity ) ~= 'table' then
|
||||
return nil
|
||||
end
|
||||
|
||||
local entityModule = require( php.getEntityModuleName( id ) )
|
||||
|
||||
-- Use a deep clone here, so that people can't modify the entity
|
||||
return entityModule.create( mw.clone( entity ) )
|
||||
end
|
||||
|
||||
-- Get the entity id for the current page. Cached.
|
||||
-- Nil if not linked to an entity.
|
||||
function wikibase.getEntityIdForCurrentPage()
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.wikibase.getEntityIdForCurrentPage.call' )
|
||||
|
||||
if pageEntityId == false then
|
||||
pageEntityId = php.getEntityId( tostring( mw.title.getCurrentTitle().prefixedText ) )
|
||||
end
|
||||
|
||||
return pageEntityId
|
||||
end
|
||||
|
||||
-- Takes a page title string either in the local wiki or another wiki on the same cluster
|
||||
-- specified by the global site identifier, and returns the item ID connected via a sitelink, if
|
||||
-- one exists. Returns nil if there's no linked item.
|
||||
--
|
||||
-- @param {string} pageTitle
|
||||
-- @param {string} [globalSiteId]
|
||||
function wikibase.getEntityIdForTitle( pageTitle, globalSiteId )
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.wikibase.getEntityIdForTitle.call' )
|
||||
|
||||
checkType( 'getEntityIdForTitle', 1, pageTitle, 'string' )
|
||||
checkTypeMulti( 'getEntityIdForTitle', 2, globalSiteId, { 'string', 'nil' } )
|
||||
|
||||
return php.getEntityId( pageTitle, globalSiteId )
|
||||
end
|
||||
|
||||
-- Get the mw.wikibase.entity object for the current page or for the
|
||||
-- specified id.
|
||||
--
|
||||
-- @param {string} [id]
|
||||
function wikibase.getEntity( id )
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.wikibase.getEntity.call' )
|
||||
|
||||
checkTypeMulti( 'getEntity', 1, id, { 'string', 'nil' } )
|
||||
|
||||
id = getIdOfConnectedItemIfNil( id )
|
||||
|
||||
if id == nil then
|
||||
return nil
|
||||
end
|
||||
|
||||
if not php.getSetting( 'allowArbitraryDataAccess' ) and id ~= wikibase.getEntityIdForCurrentPage() then
|
||||
error( 'Access to arbitrary entities has been disabled.', 2 )
|
||||
end
|
||||
|
||||
return getEntityObject( id )
|
||||
end
|
||||
|
||||
-- getEntityObject is an alias for getEntity as these used to be different.
|
||||
wikibase.getEntityObject = wikibase.getEntity
|
||||
|
||||
-- @param {string} entityId
|
||||
-- @param {string} propertyId
|
||||
-- @param {string} funcName for error logging
|
||||
-- @param {string} rank Which statements to include. Either "best" or "all".
|
||||
local function getEntityStatements( entityId, propertyId, funcName, rank )
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.wikibase.getEntityStatements.call' )
|
||||
|
||||
if not php.getSetting( 'allowArbitraryDataAccess' ) and entityId ~= wikibase.getEntityIdForCurrentPage() then
|
||||
error( 'Access to arbitrary entities has been disabled.', 2 )
|
||||
end
|
||||
|
||||
checkType( funcName, 1, entityId, 'string' )
|
||||
checkType( funcName, 2, propertyId, 'string' )
|
||||
|
||||
local cacheKey = entityId .. '-' .. propertyId .. '-' .. rank
|
||||
local statements = getFromCache( statementCache, cacheKey )
|
||||
if statements == nil then
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.wikibase.getEntityStatements.cache_miss' )
|
||||
|
||||
statements = php.getEntityStatements( entityId, propertyId, rank )
|
||||
addToCache( statementCache, cacheKey, statements )
|
||||
end
|
||||
|
||||
if statements and statements[propertyId] then
|
||||
-- Use a clone here, so that users can't modify the cached statement
|
||||
return mw.clone( statements[propertyId] )
|
||||
end
|
||||
|
||||
return {}
|
||||
end
|
||||
|
||||
-- Returns a table with the "best" statements matching the given property ID on the given entity
|
||||
-- ID. The definition of "best" is that the function will return "preferred" statements, if
|
||||
-- there are any, otherwise "normal" ranked statements. It will never return "deprecated"
|
||||
-- statements. This is what you usually want when surfacing values to an ordinary reader.
|
||||
--
|
||||
-- @param {string} entityId
|
||||
-- @param {string} propertyId
|
||||
function wikibase.getBestStatements( entityId, propertyId )
|
||||
return getEntityStatements( entityId, propertyId, 'getBestStatements', 'best' )
|
||||
end
|
||||
|
||||
-- Returns a table with all statements (including all ranks, even "deprecated") matching the
|
||||
-- given property ID on the given entity ID.
|
||||
--
|
||||
-- @param {string} entityId
|
||||
-- @param {string} propertyId
|
||||
function wikibase.getAllStatements( entityId, propertyId )
|
||||
return getEntityStatements( entityId, propertyId, 'getAllStatements', 'all' )
|
||||
end
|
||||
|
||||
-- Get the URL for the given entity id, if specified, or of the
|
||||
-- connected entity, if exists.
|
||||
--
|
||||
-- @param {string} [id]
|
||||
function wikibase.getEntityUrl( id )
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.wikibase.getEntityUrl.call' )
|
||||
|
||||
checkTypeMulti( 'getEntityUrl', 1, id, { 'string', 'nil' } )
|
||||
|
||||
id = getIdOfConnectedItemIfNil( id )
|
||||
|
||||
if id == nil then
|
||||
return nil
|
||||
end
|
||||
|
||||
return php.getEntityUrl( id )
|
||||
end
|
||||
|
||||
-- Get the label, label language for the given entity id, if specified,
|
||||
-- or of the connected entity, if exists.
|
||||
--
|
||||
-- @param {string} [id]
|
||||
function wikibase.getLabelWithLang( id )
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.wikibase.getLabelWithLang.call' )
|
||||
|
||||
checkTypeMulti( 'getLabelWithLang', 1, id, { 'string', 'nil' } )
|
||||
|
||||
id = getIdOfConnectedItemIfNil( id )
|
||||
|
||||
if id == nil then
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
return php.getLabel( id )
|
||||
end
|
||||
|
||||
-- Like wikibase.getLabelWithLang, but only returns the plain label.
|
||||
--
|
||||
-- @param {string} [id]
|
||||
function wikibase.getLabel( id )
|
||||
checkTypeMulti( 'getLabel', 1, id, { 'string', 'nil' } )
|
||||
local label = wikibase.getLabelWithLang( id )
|
||||
|
||||
return label
|
||||
end
|
||||
|
||||
-- Legacy alias for getLabel
|
||||
wikibase.label = wikibase.getLabel
|
||||
|
||||
-- Get the label in languageCode for the given entity id.
|
||||
--
|
||||
-- @param {string} id
|
||||
-- @param {string} languageCode
|
||||
function wikibase.getLabelByLang( id, languageCode )
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.wikibase.getLabelByLang.call' )
|
||||
|
||||
checkType( 'getLabelByLang', 1, id, 'string' )
|
||||
checkType( 'getLabelByLang', 2, languageCode, 'string' )
|
||||
|
||||
return php.getLabelByLanguage( id, languageCode )
|
||||
end
|
||||
|
||||
-- Get the description, description language for the given entity id, if specified,
|
||||
-- or of the connected entity, if exists.
|
||||
--
|
||||
-- @param {string} [id]
|
||||
function wikibase.getDescriptionWithLang( id )
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.wikibase.getDescriptionWithLang.call' )
|
||||
|
||||
checkTypeMulti( 'getDescriptionWithLang', 1, id, { 'string', 'nil' } )
|
||||
|
||||
id = getIdOfConnectedItemIfNil( id )
|
||||
|
||||
if id == nil then
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
return php.getDescription( id )
|
||||
end
|
||||
|
||||
-- Like wikibase.getDescriptionWithLang, but only returns the plain description.
|
||||
--
|
||||
-- @param {string} [id]
|
||||
function wikibase.getDescription( id )
|
||||
checkTypeMulti( 'getDescription', 1, id, { 'string', 'nil' } )
|
||||
local description = wikibase.getDescriptionWithLang( id )
|
||||
|
||||
return description
|
||||
end
|
||||
|
||||
-- Legacy alias for getDescription
|
||||
wikibase.description = wikibase.getDescription
|
||||
|
||||
-- Get the local sitelink title for the given entity id.
|
||||
--
|
||||
-- @param {string} itemId
|
||||
-- @param {string} [globalSiteId]
|
||||
function wikibase.getSitelink( itemId, globalSiteId )
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.wikibase.getSitelink.call' )
|
||||
|
||||
checkType( 'getSitelink', 1, itemId, 'string' )
|
||||
checkTypeMulti( 'getSitelink', 2, globalSiteId, { 'string', 'nil' } )
|
||||
|
||||
return php.getSiteLinkPageName( itemId, globalSiteId )
|
||||
end
|
||||
|
||||
-- Legacy alias for getSitelink
|
||||
wikibase.sitelink = wikibase.getSitelink
|
||||
|
||||
-- Is this a valid (parseable) entity id?
|
||||
--
|
||||
-- @param {string} entityIdSerialization
|
||||
function wikibase.isValidEntityId( entityIdSerialization )
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.wikibase.isValidEntityId.call' )
|
||||
|
||||
checkType( 'isValidEntityId', 1, entityIdSerialization, 'string' )
|
||||
|
||||
return php.isValidEntityId( entityIdSerialization )
|
||||
end
|
||||
|
||||
-- Does the entity in question exist?
|
||||
--
|
||||
-- @param {string} entityId
|
||||
function wikibase.entityExists( entityId )
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.wikibase.entityExists.call' )
|
||||
|
||||
checkType( 'entityExists', 1, entityId, 'string' )
|
||||
|
||||
if not php.getSetting( 'allowArbitraryDataAccess' ) and entityId ~= wikibase.getEntityIdForCurrentPage() then
|
||||
error( 'Access to arbitrary entities has been disabled.', 2 )
|
||||
end
|
||||
|
||||
return php.entityExists( entityId )
|
||||
end
|
||||
|
||||
-- Render a Snak value from its serialization as wikitext escaped plain text.
|
||||
--
|
||||
-- @param {table} snakSerialization
|
||||
function wikibase.renderSnak( snakSerialization )
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.wikibase.renderSnak.call' )
|
||||
|
||||
checkType( 'renderSnak', 1, snakSerialization, 'table' )
|
||||
|
||||
return php.renderSnak( snakSerialization )
|
||||
end
|
||||
|
||||
-- Render a Snak value from its serialization as rich wikitext.
|
||||
--
|
||||
-- @param {table} snakSerialization
|
||||
function wikibase.formatValue( snakSerialization )
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.wikibase.formatValue.call' )
|
||||
|
||||
checkType( 'formatValue', 1, snakSerialization, 'table' )
|
||||
|
||||
return php.formatValue( snakSerialization )
|
||||
end
|
||||
|
||||
-- Render a list of Snak values from their serialization as wikitext escaped plain text.
|
||||
--
|
||||
-- @param {table} snaksSerialization
|
||||
function wikibase.renderSnaks( snaksSerialization )
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.wikibase.renderSnaks.call' )
|
||||
|
||||
checkType( 'renderSnaks', 1, snaksSerialization, 'table' )
|
||||
|
||||
return php.renderSnaks( snaksSerialization )
|
||||
end
|
||||
|
||||
-- Render a list of Snak values from their serialization as rich wikitext.
|
||||
--
|
||||
-- @param {table} snaksSerialization
|
||||
function wikibase.formatValues( snaksSerialization )
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.wikibase.formatValues.call' )
|
||||
|
||||
checkType( 'formatValues', 1, snaksSerialization, 'table' )
|
||||
|
||||
return php.formatValues( snaksSerialization )
|
||||
end
|
||||
|
||||
-- Returns a property id for the given label or id
|
||||
--
|
||||
-- @param {string} propertyLabelOrId
|
||||
function wikibase.resolvePropertyId( propertyLabelOrId )
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.wikibase.resolvePropertyId.call' )
|
||||
|
||||
checkType( 'resolvePropertyId', 1, propertyLabelOrId, 'string' )
|
||||
|
||||
return php.resolvePropertyId( propertyLabelOrId )
|
||||
end
|
||||
|
||||
-- Returns a table of the given property IDs ordered
|
||||
--
|
||||
-- @param {table} propertyIds
|
||||
function wikibase.orderProperties( propertyIds )
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.wikibase.orderProperties.call' )
|
||||
|
||||
checkType( 'orderProperties', 1, propertyIds, 'table' )
|
||||
return php.orderProperties( propertyIds )
|
||||
end
|
||||
|
||||
-- Returns an ordered table of serialized property IDs
|
||||
function wikibase.getPropertyOrder()
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.wikibase.getPropertyOrder.call' )
|
||||
|
||||
return php.getPropertyOrder()
|
||||
end
|
||||
|
||||
-- Get the closest referenced entity (out of toIds), from a given entity.
|
||||
--
|
||||
-- @param {string} fromEntityId
|
||||
-- @param {string} propertyId
|
||||
-- @param {table} toIds
|
||||
function wikibase.getReferencedEntityId( fromEntityId, propertyId, toIds )
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.wikibase.getReferencedEntityId.call' )
|
||||
|
||||
checkType( 'getReferencedEntityId', 1, fromEntityId, 'string' )
|
||||
checkType( 'getReferencedEntityId', 2, propertyId, 'string' )
|
||||
checkType( 'getReferencedEntityId', 3, toIds, 'table' )
|
||||
|
||||
-- Check the type of all toId values, Scribunto has no function for this yet (T196048)
|
||||
for i, toId in ipairs( toIds ) do
|
||||
if type( toId ) ~= 'string' then
|
||||
error(
|
||||
'toIds value at index ' .. i .. ' must be string, ' .. type( toId ) .. ' given.',
|
||||
1
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
if not php.getSetting( 'allowArbitraryDataAccess' ) then
|
||||
error( 'Access to arbitrary entities has been disabled.', 2 )
|
||||
end
|
||||
|
||||
return php.getReferencedEntityId( fromEntityId, propertyId, toIds )
|
||||
end
|
||||
|
||||
-- Returns the current site's global id
|
||||
function wikibase.getGlobalSiteId()
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.wikibase.getGlobalSiteId.call' )
|
||||
|
||||
return php.getSetting( 'siteGlobalID' )
|
||||
end
|
||||
|
||||
mw = mw or {}
|
||||
mw.wikibase = wikibase
|
||||
package.loaded['mw.wikibase'] = wikibase
|
||||
wikibase.setupInterface = nil
|
||||
end
|
||||
|
||||
-- xowa:bgn
|
||||
function wikibase.notify_page_changed()
|
||||
-- reset cache everytime page changes
|
||||
entityCache = initCache( 15 )
|
||||
statementCache = initCache( 50 )
|
||||
|
||||
-- reset pageEntityId on every page; ISSUE#:638; PAGE:en.s:Author:Victor_Hugo, en.s:Author:Charles_Dickens DATE:2020-03-07
|
||||
pageEntityId = false
|
||||
end
|
||||
-- xowa:end
|
||||
|
||||
return wikibase
|
||||
--XOWA:updated 2019-11-22
|
||||
--REF:https://github.com/wikimedia/mediawiki-extensions-Wikibase/blob/master/client/includes/DataAccess/Scribunto/mw.wikibase.lua
|
||||
--[[
|
||||
Registers and defines functions to access Wikibase through the Scribunto extension
|
||||
Provides Lua setupInterface
|
||||
|
||||
@since 0.4
|
||||
|
||||
@license GNU GPL v2+
|
||||
@author Jens Ohlig < jens.ohlig@wikimedia.de >
|
||||
@author Marius Hoch < hoo@online.de >
|
||||
@author Bene* < benestar.wikimedia@gmail.com >
|
||||
]]
|
||||
|
||||
local wikibase = {}
|
||||
local util = require 'libraryUtil'
|
||||
local checkType = util.checkType
|
||||
local checkTypeMulti = util.checkTypeMulti
|
||||
|
||||
-- Get a cache data structure to use with addToCache and getFromCache
|
||||
-- with a given maximum size.
|
||||
--
|
||||
-- @param maxCacheSize
|
||||
local function initCache( maxCacheSize )
|
||||
return {
|
||||
data = {},
|
||||
order = {},
|
||||
size = 0,
|
||||
maxCacheSize = maxCacheSize,
|
||||
}
|
||||
end
|
||||
|
||||
-- Cache a given value (can also be false, in case it doesn't exist).
|
||||
--
|
||||
-- @param cache
|
||||
-- @param key
|
||||
-- @param value
|
||||
local function addToCache( cache, key, value )
|
||||
if cache.size == cache.maxCacheSize then
|
||||
local toRemove = table.remove( cache.order, cache.maxCacheSize )
|
||||
cache.data[ toRemove ] = nil
|
||||
else
|
||||
cache.size = cache.size + 1
|
||||
end
|
||||
|
||||
table.insert( cache.order, 1, key )
|
||||
cache.data[ key ] = value
|
||||
end
|
||||
|
||||
-- Retrieve a value from a cache. Will return nil in case of a cache miss.
|
||||
--
|
||||
-- @param cache
|
||||
-- @param key
|
||||
local function getFromCache( cache, key )
|
||||
if cache.data[ key ] ~= nil then
|
||||
for cacheOrderId, cacheOrderKey in pairs( cache.order ) do
|
||||
if cacheOrderKey == key then
|
||||
table.remove( cache.order, cacheOrderId )
|
||||
break
|
||||
end
|
||||
end
|
||||
table.insert( cache.order, 1, key )
|
||||
end
|
||||
|
||||
return cache.data[ key ]
|
||||
end
|
||||
|
||||
-- 15 slot cache for entities
|
||||
local entityCache = initCache( 15 )
|
||||
|
||||
-- 50 slot cache for statements
|
||||
local statementCache = initCache( 50 )
|
||||
|
||||
-- xowa:bgn
|
||||
-- NOTE: moved from inside wikibase.setupInterface; ISSUE#:638; DATE:2020-03-07
|
||||
-- Caching variable for the entity id string belonging to the current page (nil if page is not linked to an entity)
|
||||
local pageEntityId = false
|
||||
-- xowa:end
|
||||
|
||||
function wikibase.setupInterface()
|
||||
local php = mw_interface
|
||||
mw_interface = nil
|
||||
|
||||
-- Get the entity id of the connected item, if id is nil. Cached.
|
||||
local function getIdOfConnectedItemIfNil( id )
|
||||
if id == nil then
|
||||
return wikibase.getEntityIdForCurrentPage()
|
||||
end
|
||||
|
||||
return id
|
||||
end
|
||||
|
||||
-- Get the mw.wikibase.entity object for a given id. Cached.
|
||||
local function getEntityObject( id )
|
||||
local entity = getFromCache( entityCache, id )
|
||||
|
||||
if entity == nil then
|
||||
entity = php.getEntity( id )
|
||||
|
||||
if id ~= wikibase.getEntityIdForCurrentPage() then
|
||||
-- Accessing an arbitrary entity is supposed to increment the expensive function count
|
||||
php.incrementExpensiveFunctionCount()
|
||||
end
|
||||
|
||||
if type( entity ) ~= 'table' then
|
||||
entity = false
|
||||
end
|
||||
|
||||
addToCache( entityCache, id, entity )
|
||||
end
|
||||
|
||||
if type( entity ) ~= 'table' then
|
||||
return nil
|
||||
end
|
||||
|
||||
local entityModule = require( php.getEntityModuleName( id ) )
|
||||
|
||||
-- Use a deep clone here, so that people can't modify the entity
|
||||
return entityModule.create( mw.clone( entity ) )
|
||||
end
|
||||
|
||||
-- Get the entity id for the current page. Cached.
|
||||
-- Nil if not linked to an entity.
|
||||
function wikibase.getEntityIdForCurrentPage()
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.wikibase.getEntityIdForCurrentPage.call' )
|
||||
|
||||
if pageEntityId == false then
|
||||
pageEntityId = php.getEntityId( tostring( mw.title.getCurrentTitle().prefixedText ) )
|
||||
end
|
||||
|
||||
return pageEntityId
|
||||
end
|
||||
|
||||
-- Takes a page title string either in the local wiki or another wiki on the same cluster
|
||||
-- specified by the global site identifier, and returns the item ID connected via a sitelink, if
|
||||
-- one exists. Returns nil if there's no linked item.
|
||||
--
|
||||
-- @param {string} pageTitle
|
||||
-- @param {string} [globalSiteId]
|
||||
function wikibase.getEntityIdForTitle( pageTitle, globalSiteId )
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.wikibase.getEntityIdForTitle.call' )
|
||||
|
||||
checkType( 'getEntityIdForTitle', 1, pageTitle, 'string' )
|
||||
checkTypeMulti( 'getEntityIdForTitle', 2, globalSiteId, { 'string', 'nil' } )
|
||||
|
||||
return php.getEntityId( pageTitle, globalSiteId )
|
||||
end
|
||||
|
||||
-- Get the mw.wikibase.entity object for the current page or for the
|
||||
-- specified id.
|
||||
--
|
||||
-- @param {string} [id]
|
||||
function wikibase.getEntity( id )
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.wikibase.getEntity.call' )
|
||||
|
||||
checkTypeMulti( 'getEntity', 1, id, { 'string', 'nil' } )
|
||||
|
||||
id = getIdOfConnectedItemIfNil( id )
|
||||
|
||||
if id == nil then
|
||||
return nil
|
||||
end
|
||||
|
||||
if not php.getSetting( 'allowArbitraryDataAccess' ) and id ~= wikibase.getEntityIdForCurrentPage() then
|
||||
error( 'Access to arbitrary entities has been disabled.', 2 )
|
||||
end
|
||||
|
||||
return getEntityObject( id )
|
||||
end
|
||||
|
||||
-- getEntityObject is an alias for getEntity as these used to be different.
|
||||
wikibase.getEntityObject = wikibase.getEntity
|
||||
|
||||
-- @param {string} entityId
|
||||
-- @param {string} propertyId
|
||||
-- @param {string} funcName for error logging
|
||||
-- @param {string} rank Which statements to include. Either "best" or "all".
|
||||
local function getEntityStatements( entityId, propertyId, funcName, rank )
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.wikibase.getEntityStatements.call' )
|
||||
|
||||
if not php.getSetting( 'allowArbitraryDataAccess' ) and entityId ~= wikibase.getEntityIdForCurrentPage() then
|
||||
error( 'Access to arbitrary entities has been disabled.', 2 )
|
||||
end
|
||||
|
||||
checkType( funcName, 1, entityId, 'string' )
|
||||
checkType( funcName, 2, propertyId, 'string' )
|
||||
|
||||
local cacheKey = entityId .. '-' .. propertyId .. '-' .. rank
|
||||
local statements = getFromCache( statementCache, cacheKey )
|
||||
if statements == nil then
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.wikibase.getEntityStatements.cache_miss' )
|
||||
|
||||
statements = php.getEntityStatements( entityId, propertyId, rank )
|
||||
addToCache( statementCache, cacheKey, statements )
|
||||
end
|
||||
|
||||
if statements and statements[propertyId] then
|
||||
-- Use a clone here, so that users can't modify the cached statement
|
||||
return mw.clone( statements[propertyId] )
|
||||
end
|
||||
|
||||
return {}
|
||||
end
|
||||
|
||||
-- Returns a table with the "best" statements matching the given property ID on the given entity
|
||||
-- ID. The definition of "best" is that the function will return "preferred" statements, if
|
||||
-- there are any, otherwise "normal" ranked statements. It will never return "deprecated"
|
||||
-- statements. This is what you usually want when surfacing values to an ordinary reader.
|
||||
--
|
||||
-- @param {string} entityId
|
||||
-- @param {string} propertyId
|
||||
function wikibase.getBestStatements( entityId, propertyId )
|
||||
return getEntityStatements( entityId, propertyId, 'getBestStatements', 'best' )
|
||||
end
|
||||
|
||||
-- Returns a table with all statements (including all ranks, even "deprecated") matching the
|
||||
-- given property ID on the given entity ID.
|
||||
--
|
||||
-- @param {string} entityId
|
||||
-- @param {string} propertyId
|
||||
function wikibase.getAllStatements( entityId, propertyId )
|
||||
return getEntityStatements( entityId, propertyId, 'getAllStatements', 'all' )
|
||||
end
|
||||
|
||||
-- Get the URL for the given entity id, if specified, or of the
|
||||
-- connected entity, if exists.
|
||||
--
|
||||
-- @param {string} [id]
|
||||
function wikibase.getEntityUrl( id )
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.wikibase.getEntityUrl.call' )
|
||||
|
||||
checkTypeMulti( 'getEntityUrl', 1, id, { 'string', 'nil' } )
|
||||
|
||||
id = getIdOfConnectedItemIfNil( id )
|
||||
|
||||
if id == nil then
|
||||
return nil
|
||||
end
|
||||
|
||||
return php.getEntityUrl( id )
|
||||
end
|
||||
|
||||
-- Get the label, label language for the given entity id, if specified,
|
||||
-- or of the connected entity, if exists.
|
||||
--
|
||||
-- @param {string} [id]
|
||||
function wikibase.getLabelWithLang( id )
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.wikibase.getLabelWithLang.call' )
|
||||
|
||||
checkTypeMulti( 'getLabelWithLang', 1, id, { 'string', 'nil' } )
|
||||
|
||||
id = getIdOfConnectedItemIfNil( id )
|
||||
|
||||
if id == nil then
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
return php.getLabel( id )
|
||||
end
|
||||
|
||||
-- Like wikibase.getLabelWithLang, but only returns the plain label.
|
||||
--
|
||||
-- @param {string} [id]
|
||||
function wikibase.getLabel( id )
|
||||
checkTypeMulti( 'getLabel', 1, id, { 'string', 'nil' } )
|
||||
local label = wikibase.getLabelWithLang( id )
|
||||
|
||||
return label
|
||||
end
|
||||
|
||||
-- Legacy alias for getLabel
|
||||
wikibase.label = wikibase.getLabel
|
||||
|
||||
-- Get the label in languageCode for the given entity id.
|
||||
--
|
||||
-- @param {string} id
|
||||
-- @param {string} languageCode
|
||||
function wikibase.getLabelByLang( id, languageCode )
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.wikibase.getLabelByLang.call' )
|
||||
|
||||
checkType( 'getLabelByLang', 1, id, 'string' )
|
||||
checkType( 'getLabelByLang', 2, languageCode, 'string' )
|
||||
|
||||
return php.getLabelByLanguage( id, languageCode )
|
||||
end
|
||||
|
||||
-- Get the description, description language for the given entity id, if specified,
|
||||
-- or of the connected entity, if exists.
|
||||
--
|
||||
-- @param {string} [id]
|
||||
function wikibase.getDescriptionWithLang( id )
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.wikibase.getDescriptionWithLang.call' )
|
||||
|
||||
checkTypeMulti( 'getDescriptionWithLang', 1, id, { 'string', 'nil' } )
|
||||
|
||||
id = getIdOfConnectedItemIfNil( id )
|
||||
|
||||
if id == nil then
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
return php.getDescription( id )
|
||||
end
|
||||
|
||||
-- Like wikibase.getDescriptionWithLang, but only returns the plain description.
|
||||
--
|
||||
-- @param {string} [id]
|
||||
function wikibase.getDescription( id )
|
||||
checkTypeMulti( 'getDescription', 1, id, { 'string', 'nil' } )
|
||||
local description = wikibase.getDescriptionWithLang( id )
|
||||
|
||||
return description
|
||||
end
|
||||
|
||||
-- Legacy alias for getDescription
|
||||
wikibase.description = wikibase.getDescription
|
||||
|
||||
-- Get the local sitelink title for the given entity id.
|
||||
--
|
||||
-- @param {string} itemId
|
||||
-- @param {string} [globalSiteId]
|
||||
function wikibase.getSitelink( itemId, globalSiteId )
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.wikibase.getSitelink.call' )
|
||||
|
||||
checkType( 'getSitelink', 1, itemId, 'string' )
|
||||
checkTypeMulti( 'getSitelink', 2, globalSiteId, { 'string', 'nil' } )
|
||||
|
||||
return php.getSiteLinkPageName( itemId, globalSiteId )
|
||||
end
|
||||
|
||||
-- Legacy alias for getSitelink
|
||||
wikibase.sitelink = wikibase.getSitelink
|
||||
|
||||
-- Is this a valid (parseable) entity id?
|
||||
--
|
||||
-- @param {string} entityIdSerialization
|
||||
function wikibase.isValidEntityId( entityIdSerialization )
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.wikibase.isValidEntityId.call' )
|
||||
|
||||
checkType( 'isValidEntityId', 1, entityIdSerialization, 'string' )
|
||||
|
||||
return php.isValidEntityId( entityIdSerialization )
|
||||
end
|
||||
|
||||
-- Does the entity in question exist?
|
||||
--
|
||||
-- @param {string} entityId
|
||||
function wikibase.entityExists( entityId )
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.wikibase.entityExists.call' )
|
||||
|
||||
checkType( 'entityExists', 1, entityId, 'string' )
|
||||
|
||||
if not php.getSetting( 'allowArbitraryDataAccess' ) and entityId ~= wikibase.getEntityIdForCurrentPage() then
|
||||
error( 'Access to arbitrary entities has been disabled.', 2 )
|
||||
end
|
||||
|
||||
return php.entityExists( entityId )
|
||||
end
|
||||
|
||||
-- Render a Snak value from its serialization as wikitext escaped plain text.
|
||||
--
|
||||
-- @param {table} snakSerialization
|
||||
function wikibase.renderSnak( snakSerialization )
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.wikibase.renderSnak.call' )
|
||||
|
||||
checkType( 'renderSnak', 1, snakSerialization, 'table' )
|
||||
|
||||
return php.renderSnak( snakSerialization )
|
||||
end
|
||||
|
||||
-- Render a Snak value from its serialization as rich wikitext.
|
||||
--
|
||||
-- @param {table} snakSerialization
|
||||
function wikibase.formatValue( snakSerialization )
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.wikibase.formatValue.call' )
|
||||
|
||||
checkType( 'formatValue', 1, snakSerialization, 'table' )
|
||||
|
||||
return php.formatValue( snakSerialization )
|
||||
end
|
||||
|
||||
-- Render a list of Snak values from their serialization as wikitext escaped plain text.
|
||||
--
|
||||
-- @param {table} snaksSerialization
|
||||
function wikibase.renderSnaks( snaksSerialization )
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.wikibase.renderSnaks.call' )
|
||||
|
||||
checkType( 'renderSnaks', 1, snaksSerialization, 'table' )
|
||||
|
||||
return php.renderSnaks( snaksSerialization )
|
||||
end
|
||||
|
||||
-- Render a list of Snak values from their serialization as rich wikitext.
|
||||
--
|
||||
-- @param {table} snaksSerialization
|
||||
function wikibase.formatValues( snaksSerialization )
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.wikibase.formatValues.call' )
|
||||
|
||||
checkType( 'formatValues', 1, snaksSerialization, 'table' )
|
||||
|
||||
return php.formatValues( snaksSerialization )
|
||||
end
|
||||
|
||||
-- Returns a property id for the given label or id
|
||||
--
|
||||
-- @param {string} propertyLabelOrId
|
||||
function wikibase.resolvePropertyId( propertyLabelOrId )
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.wikibase.resolvePropertyId.call' )
|
||||
|
||||
checkType( 'resolvePropertyId', 1, propertyLabelOrId, 'string' )
|
||||
|
||||
return php.resolvePropertyId( propertyLabelOrId )
|
||||
end
|
||||
|
||||
-- Returns a table of the given property IDs ordered
|
||||
--
|
||||
-- @param {table} propertyIds
|
||||
function wikibase.orderProperties( propertyIds )
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.wikibase.orderProperties.call' )
|
||||
|
||||
checkType( 'orderProperties', 1, propertyIds, 'table' )
|
||||
return php.orderProperties( propertyIds )
|
||||
end
|
||||
|
||||
-- Returns an ordered table of serialized property IDs
|
||||
function wikibase.getPropertyOrder()
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.wikibase.getPropertyOrder.call' )
|
||||
|
||||
return php.getPropertyOrder()
|
||||
end
|
||||
|
||||
-- Get the closest referenced entity (out of toIds), from a given entity.
|
||||
--
|
||||
-- @param {string} fromEntityId
|
||||
-- @param {string} propertyId
|
||||
-- @param {table} toIds
|
||||
function wikibase.getReferencedEntityId( fromEntityId, propertyId, toIds )
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.wikibase.getReferencedEntityId.call' )
|
||||
|
||||
checkType( 'getReferencedEntityId', 1, fromEntityId, 'string' )
|
||||
checkType( 'getReferencedEntityId', 2, propertyId, 'string' )
|
||||
checkType( 'getReferencedEntityId', 3, toIds, 'table' )
|
||||
|
||||
-- Check the type of all toId values, Scribunto has no function for this yet (T196048)
|
||||
for i, toId in ipairs( toIds ) do
|
||||
if type( toId ) ~= 'string' then
|
||||
error(
|
||||
'toIds value at index ' .. i .. ' must be string, ' .. type( toId ) .. ' given.',
|
||||
1
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
if not php.getSetting( 'allowArbitraryDataAccess' ) then
|
||||
error( 'Access to arbitrary entities has been disabled.', 2 )
|
||||
end
|
||||
|
||||
return php.getReferencedEntityId( fromEntityId, propertyId, toIds )
|
||||
end
|
||||
|
||||
-- Returns the current site's global id
|
||||
function wikibase.getGlobalSiteId()
|
||||
php.incrementStatsKey( 'wikibase.client.scribunto.wikibase.getGlobalSiteId.call' )
|
||||
|
||||
return php.getSetting( 'siteGlobalID' )
|
||||
end
|
||||
|
||||
mw = mw or {}
|
||||
mw.wikibase = wikibase
|
||||
package.loaded['mw.wikibase'] = wikibase
|
||||
wikibase.setupInterface = nil
|
||||
end
|
||||
|
||||
-- xowa:bgn
|
||||
function wikibase.notify_page_changed()
|
||||
-- reset cache everytime page changes
|
||||
entityCache = initCache( 15 )
|
||||
statementCache = initCache( 50 )
|
||||
|
||||
-- reset pageEntityId on every page; ISSUE#:638; PAGE:en.s:Author:Victor_Hugo, en.s:Author:Charles_Dickens DATE:2020-03-07
|
||||
pageEntityId = false
|
||||
end
|
||||
-- xowa:end
|
||||
|
||||
return wikibase
|
||||
|
||||
@@ -1,134 +1,134 @@
|
||||
-- This file is for anything that needs to be set up before a Lua engine can
|
||||
-- start. Things in this file may run more than once, so avoid putting anything
|
||||
-- other than function definitions in it. Also, because this can run before
|
||||
-- PHP can do anything, mw_interface is unavailable here.
|
||||
|
||||
mw = mw or {}
|
||||
|
||||
-- Extend pairs and ipairs to recognize __pairs and __ipairs, if they don't already
|
||||
do
|
||||
local t = {}
|
||||
setmetatable( t, { __pairs = function() return 1, 2, 3 end } )
|
||||
local f = pairs( t )
|
||||
if f ~= 1 then
|
||||
local old_pairs = pairs
|
||||
pairs = function ( t )
|
||||
local mt = getmetatable( t )
|
||||
local f, s, var = ( mt and mt.__pairs or old_pairs )( t )
|
||||
return f, s, var
|
||||
end
|
||||
local old_ipairs = ipairs
|
||||
ipairs = function ( t )
|
||||
local mt = getmetatable( t )
|
||||
local f, s, var = ( mt and mt.__ipairs or old_ipairs )( t )
|
||||
return f, s, var
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Do a "deep copy" of a table or other value.
|
||||
function mw.clone( val )
|
||||
local tableRefs = {}
|
||||
local function recursiveClone( val )
|
||||
if type( val ) == 'table' then
|
||||
-- Encode circular references correctly
|
||||
if tableRefs[val] ~= nil then
|
||||
return tableRefs[val]
|
||||
end
|
||||
|
||||
local retVal
|
||||
retVal = {}
|
||||
tableRefs[val] = retVal
|
||||
|
||||
-- Copy metatable
|
||||
if getmetatable( val ) then
|
||||
setmetatable( retVal, recursiveClone( getmetatable( val ) ) )
|
||||
end
|
||||
|
||||
for key, elt in pairs( val ) do
|
||||
retVal[key] = recursiveClone( elt )
|
||||
end
|
||||
return retVal
|
||||
else
|
||||
return val
|
||||
end
|
||||
end
|
||||
return recursiveClone( val )
|
||||
end
|
||||
|
||||
--- Make isolation-safe setfenv and getfenv functions
|
||||
--
|
||||
-- @param protectedEnvironments A table where the keys are protected environment
|
||||
-- tables. These environments cannot be accessed with getfenv(), and
|
||||
-- functions with these environments cannot be modified or accessed using
|
||||
-- integer indexes to setfenv(). However, functions with these environments
|
||||
-- can have their environment set with setfenv() with a function value
|
||||
-- argument.
|
||||
--
|
||||
-- @param protectedFunctions A table where the keys are protected functions,
|
||||
-- which cannot have their environments set by setfenv() with a function
|
||||
-- value argument.
|
||||
--
|
||||
-- @return setfenv
|
||||
-- @return getfenv
|
||||
function mw.makeProtectedEnvFuncs( protectedEnvironments, protectedFunctions )
|
||||
local old_setfenv = setfenv
|
||||
local old_getfenv = getfenv
|
||||
|
||||
local function my_setfenv( func, newEnv )
|
||||
if type( func ) == 'number' then
|
||||
local stackIndex = math.floor( func )
|
||||
if stackIndex <= 0 then
|
||||
error( "'setfenv' cannot set the global environment, it is protected", 2 )
|
||||
end
|
||||
if stackIndex > 10 then
|
||||
error( "'setfenv' cannot set an environment at a level greater than 10", 2 )
|
||||
end
|
||||
|
||||
-- Add one because we are still in Lua and 1 is right here
|
||||
stackIndex = stackIndex + 1
|
||||
|
||||
local env = old_getfenv( stackIndex )
|
||||
if env == nil or protectedEnvironments[ env ] then
|
||||
error( "'setfenv' cannot set the requested environment, it is protected", 2 )
|
||||
end
|
||||
func = old_setfenv( stackIndex, newEnv )
|
||||
elseif type( func ) == 'function' then
|
||||
if protectedFunctions[func] then
|
||||
error( "'setfenv' cannot be called on a protected function", 2 )
|
||||
end
|
||||
local env = old_getfenv( func )
|
||||
if env == nil or protectedEnvironments[ env ] then
|
||||
error( "'setfenv' cannot set the requested environment, it is protected", 2 )
|
||||
end
|
||||
old_setfenv( func, newEnv )
|
||||
else
|
||||
error( "'setfenv' can only be called with a function or integer as the first argument", 2 )
|
||||
end
|
||||
return func
|
||||
end
|
||||
|
||||
local function my_getfenv( func )
|
||||
local env
|
||||
if type( func ) == 'number' then
|
||||
if func <= 0 then
|
||||
error( "'getfenv' cannot get the global environment" )
|
||||
end
|
||||
env = old_getfenv( func + 1 )
|
||||
elseif type( func ) == 'function' then
|
||||
env = old_getfenv( func )
|
||||
else
|
||||
error( "'getfenv' cannot get the global environment" )
|
||||
end
|
||||
|
||||
if protectedEnvironments[env] then
|
||||
return nil
|
||||
else
|
||||
return env
|
||||
end
|
||||
end
|
||||
|
||||
return my_setfenv, my_getfenv
|
||||
end
|
||||
|
||||
return mw
|
||||
-- This file is for anything that needs to be set up before a Lua engine can
|
||||
-- start. Things in this file may run more than once, so avoid putting anything
|
||||
-- other than function definitions in it. Also, because this can run before
|
||||
-- PHP can do anything, mw_interface is unavailable here.
|
||||
|
||||
mw = mw or {}
|
||||
|
||||
-- Extend pairs and ipairs to recognize __pairs and __ipairs, if they don't already
|
||||
do
|
||||
local t = {}
|
||||
setmetatable( t, { __pairs = function() return 1, 2, 3 end } )
|
||||
local f = pairs( t )
|
||||
if f ~= 1 then
|
||||
local old_pairs = pairs
|
||||
pairs = function ( t )
|
||||
local mt = getmetatable( t )
|
||||
local f, s, var = ( mt and mt.__pairs or old_pairs )( t )
|
||||
return f, s, var
|
||||
end
|
||||
local old_ipairs = ipairs
|
||||
ipairs = function ( t )
|
||||
local mt = getmetatable( t )
|
||||
local f, s, var = ( mt and mt.__ipairs or old_ipairs )( t )
|
||||
return f, s, var
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Do a "deep copy" of a table or other value.
|
||||
function mw.clone( val )
|
||||
local tableRefs = {}
|
||||
local function recursiveClone( val )
|
||||
if type( val ) == 'table' then
|
||||
-- Encode circular references correctly
|
||||
if tableRefs[val] ~= nil then
|
||||
return tableRefs[val]
|
||||
end
|
||||
|
||||
local retVal
|
||||
retVal = {}
|
||||
tableRefs[val] = retVal
|
||||
|
||||
-- Copy metatable
|
||||
if getmetatable( val ) then
|
||||
setmetatable( retVal, recursiveClone( getmetatable( val ) ) )
|
||||
end
|
||||
|
||||
for key, elt in pairs( val ) do
|
||||
retVal[key] = recursiveClone( elt )
|
||||
end
|
||||
return retVal
|
||||
else
|
||||
return val
|
||||
end
|
||||
end
|
||||
return recursiveClone( val )
|
||||
end
|
||||
|
||||
--- Make isolation-safe setfenv and getfenv functions
|
||||
--
|
||||
-- @param protectedEnvironments A table where the keys are protected environment
|
||||
-- tables. These environments cannot be accessed with getfenv(), and
|
||||
-- functions with these environments cannot be modified or accessed using
|
||||
-- integer indexes to setfenv(). However, functions with these environments
|
||||
-- can have their environment set with setfenv() with a function value
|
||||
-- argument.
|
||||
--
|
||||
-- @param protectedFunctions A table where the keys are protected functions,
|
||||
-- which cannot have their environments set by setfenv() with a function
|
||||
-- value argument.
|
||||
--
|
||||
-- @return setfenv
|
||||
-- @return getfenv
|
||||
function mw.makeProtectedEnvFuncs( protectedEnvironments, protectedFunctions )
|
||||
local old_setfenv = setfenv
|
||||
local old_getfenv = getfenv
|
||||
|
||||
local function my_setfenv( func, newEnv )
|
||||
if type( func ) == 'number' then
|
||||
local stackIndex = math.floor( func )
|
||||
if stackIndex <= 0 then
|
||||
error( "'setfenv' cannot set the global environment, it is protected", 2 )
|
||||
end
|
||||
if stackIndex > 10 then
|
||||
error( "'setfenv' cannot set an environment at a level greater than 10", 2 )
|
||||
end
|
||||
|
||||
-- Add one because we are still in Lua and 1 is right here
|
||||
stackIndex = stackIndex + 1
|
||||
|
||||
local env = old_getfenv( stackIndex )
|
||||
if env == nil or protectedEnvironments[ env ] then
|
||||
error( "'setfenv' cannot set the requested environment, it is protected", 2 )
|
||||
end
|
||||
func = old_setfenv( stackIndex, newEnv )
|
||||
elseif type( func ) == 'function' then
|
||||
if protectedFunctions[func] then
|
||||
error( "'setfenv' cannot be called on a protected function", 2 )
|
||||
end
|
||||
local env = old_getfenv( func )
|
||||
if env == nil or protectedEnvironments[ env ] then
|
||||
error( "'setfenv' cannot set the requested environment, it is protected", 2 )
|
||||
end
|
||||
old_setfenv( func, newEnv )
|
||||
else
|
||||
error( "'setfenv' can only be called with a function or integer as the first argument", 2 )
|
||||
end
|
||||
return func
|
||||
end
|
||||
|
||||
local function my_getfenv( func )
|
||||
local env
|
||||
if type( func ) == 'number' then
|
||||
if func <= 0 then
|
||||
error( "'getfenv' cannot get the global environment" )
|
||||
end
|
||||
env = old_getfenv( func + 1 )
|
||||
elseif type( func ) == 'function' then
|
||||
env = old_getfenv( func )
|
||||
else
|
||||
error( "'getfenv' cannot get the global environment" )
|
||||
end
|
||||
|
||||
if protectedEnvironments[env] then
|
||||
return nil
|
||||
else
|
||||
return env
|
||||
end
|
||||
end
|
||||
|
||||
return my_setfenv, my_getfenv
|
||||
end
|
||||
|
||||
return mw
|
||||
|
||||
@@ -1,127 +1,127 @@
|
||||
--[[
|
||||
-- A package library similar to the one that comes with Lua 5.1, but without
|
||||
-- the local filesystem access. Based on Compat-5.1 which comes with the
|
||||
-- following license notice:
|
||||
--
|
||||
-- Copyright © 2004-2006 The Kepler Project.
|
||||
--
|
||||
-- Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
-- this software and associated documentation files (the "Software"), to deal in
|
||||
-- the Software without restriction, including without limitation the rights to
|
||||
-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
-- the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
-- subject to the following conditions:
|
||||
--
|
||||
-- The above copyright notice and this permission notice shall be included in all
|
||||
-- copies or substantial portions of the Software.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
-- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
-- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
-- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
-- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
--]]
|
||||
|
||||
|
||||
local assert, error, ipairs, setmetatable, type = assert, error, ipairs, setmetatable, type
|
||||
local format = string.format
|
||||
|
||||
--
|
||||
-- avoid overwriting the package table if it's already there
|
||||
--
|
||||
package = package or {}
|
||||
local _PACKAGE = package
|
||||
|
||||
package.loaded = package.loaded or {}
|
||||
package.loaded.debug = debug
|
||||
package.loaded.string = string
|
||||
package.loaded.math = math
|
||||
package.loaded.io = io
|
||||
package.loaded.os = os
|
||||
package.loaded.table = table
|
||||
package.loaded._G = _G
|
||||
package.loaded.coroutine = coroutine
|
||||
package.loaded.package = package
|
||||
local _LOADED = package.loaded
|
||||
|
||||
--
|
||||
-- avoid overwriting the package.preload table if it's already there
|
||||
--
|
||||
package.preload = package.preload or {}
|
||||
local _PRELOAD = package.preload
|
||||
|
||||
--
|
||||
-- check whether library is already loaded
|
||||
--
|
||||
local function loader_preload (name)
|
||||
assert (type(name) == "string", format (
|
||||
"bad argument #1 to `require' (string expected, got %s)", type(name)))
|
||||
assert (type(_PRELOAD) == "table", "`package.preload' must be a table")
|
||||
return _PRELOAD[name]
|
||||
end
|
||||
|
||||
-- create `loaders' table
|
||||
package.loaders = package.loaders or { loader_preload }
|
||||
local _LOADERS = package.loaders
|
||||
|
||||
--
|
||||
-- iterate over available loaders
|
||||
--
|
||||
local function load (name, loaders)
|
||||
-- iterate over available loaders
|
||||
assert (type (loaders) == "table", "`package.loaders' must be a table")
|
||||
for i, loader in ipairs (loaders) do
|
||||
local f = loader (name)
|
||||
if f then
|
||||
return f
|
||||
end
|
||||
end
|
||||
error (format ("module `%s' not found", name))
|
||||
end
|
||||
|
||||
-- sentinel
|
||||
local sentinel = function () end
|
||||
|
||||
--
|
||||
-- require
|
||||
--
|
||||
function _G.require (modname)
|
||||
assert (type(modname) == "string", format (
|
||||
"bad argument #1 to `require' (string expected, got %s)", type(modname)))
|
||||
local p = _LOADED[modname]
|
||||
if p then -- is it there?
|
||||
if p == sentinel then
|
||||
error (format ("loop or previous error loading module '%s'", modname))
|
||||
end
|
||||
return p -- package is already loaded
|
||||
end
|
||||
local init = load (modname, _LOADERS)
|
||||
_LOADED[modname] = sentinel
|
||||
local actual_arg = _G.arg
|
||||
_G.arg = { modname }
|
||||
local res = init (modname)
|
||||
if res then
|
||||
_LOADED[modname] = res
|
||||
end
|
||||
_G.arg = actual_arg
|
||||
if _LOADED[modname] == sentinel then
|
||||
_LOADED[modname] = true
|
||||
end
|
||||
return _LOADED[modname]
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- package.seeall function
|
||||
--
|
||||
function _PACKAGE.seeall (module)
|
||||
local t = type(module)
|
||||
assert (t == "table", "bad argument #1 to package.seeall (table expected, got "..t..")")
|
||||
local meta = getmetatable (module)
|
||||
if not meta then
|
||||
meta = {}
|
||||
setmetatable (module, meta)
|
||||
end
|
||||
meta.__index = _G
|
||||
end
|
||||
--[[
|
||||
-- A package library similar to the one that comes with Lua 5.1, but without
|
||||
-- the local filesystem access. Based on Compat-5.1 which comes with the
|
||||
-- following license notice:
|
||||
--
|
||||
-- Copyright © 2004-2006 The Kepler Project.
|
||||
--
|
||||
-- Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
-- this software and associated documentation files (the "Software"), to deal in
|
||||
-- the Software without restriction, including without limitation the rights to
|
||||
-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
-- the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
-- subject to the following conditions:
|
||||
--
|
||||
-- The above copyright notice and this permission notice shall be included in all
|
||||
-- copies or substantial portions of the Software.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
-- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
-- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
-- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
-- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
--]]
|
||||
|
||||
|
||||
local assert, error, ipairs, setmetatable, type = assert, error, ipairs, setmetatable, type
|
||||
local format = string.format
|
||||
|
||||
--
|
||||
-- avoid overwriting the package table if it's already there
|
||||
--
|
||||
package = package or {}
|
||||
local _PACKAGE = package
|
||||
|
||||
package.loaded = package.loaded or {}
|
||||
package.loaded.debug = debug
|
||||
package.loaded.string = string
|
||||
package.loaded.math = math
|
||||
package.loaded.io = io
|
||||
package.loaded.os = os
|
||||
package.loaded.table = table
|
||||
package.loaded._G = _G
|
||||
package.loaded.coroutine = coroutine
|
||||
package.loaded.package = package
|
||||
local _LOADED = package.loaded
|
||||
|
||||
--
|
||||
-- avoid overwriting the package.preload table if it's already there
|
||||
--
|
||||
package.preload = package.preload or {}
|
||||
local _PRELOAD = package.preload
|
||||
|
||||
--
|
||||
-- check whether library is already loaded
|
||||
--
|
||||
local function loader_preload (name)
|
||||
assert (type(name) == "string", format (
|
||||
"bad argument #1 to `require' (string expected, got %s)", type(name)))
|
||||
assert (type(_PRELOAD) == "table", "`package.preload' must be a table")
|
||||
return _PRELOAD[name]
|
||||
end
|
||||
|
||||
-- create `loaders' table
|
||||
package.loaders = package.loaders or { loader_preload }
|
||||
local _LOADERS = package.loaders
|
||||
|
||||
--
|
||||
-- iterate over available loaders
|
||||
--
|
||||
local function load (name, loaders)
|
||||
-- iterate over available loaders
|
||||
assert (type (loaders) == "table", "`package.loaders' must be a table")
|
||||
for i, loader in ipairs (loaders) do
|
||||
local f = loader (name)
|
||||
if f then
|
||||
return f
|
||||
end
|
||||
end
|
||||
error (format ("module `%s' not found", name))
|
||||
end
|
||||
|
||||
-- sentinel
|
||||
local sentinel = function () end
|
||||
|
||||
--
|
||||
-- require
|
||||
--
|
||||
function _G.require (modname)
|
||||
assert (type(modname) == "string", format (
|
||||
"bad argument #1 to `require' (string expected, got %s)", type(modname)))
|
||||
local p = _LOADED[modname]
|
||||
if p then -- is it there?
|
||||
if p == sentinel then
|
||||
error (format ("loop or previous error loading module '%s'", modname))
|
||||
end
|
||||
return p -- package is already loaded
|
||||
end
|
||||
local init = load (modname, _LOADERS)
|
||||
_LOADED[modname] = sentinel
|
||||
local actual_arg = _G.arg
|
||||
_G.arg = { modname }
|
||||
local res = init (modname)
|
||||
if res then
|
||||
_LOADED[modname] = res
|
||||
end
|
||||
_G.arg = actual_arg
|
||||
if _LOADED[modname] == sentinel then
|
||||
_LOADED[modname] = true
|
||||
end
|
||||
return _LOADED[modname]
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- package.seeall function
|
||||
--
|
||||
function _PACKAGE.seeall (module)
|
||||
local t = type(module)
|
||||
assert (t == "table", "bad argument #1 to package.seeall (table expected, got "..t..")")
|
||||
local meta = getmetatable (module)
|
||||
if not meta then
|
||||
meta = {}
|
||||
setmetatable (module, meta)
|
||||
end
|
||||
meta.__index = _G
|
||||
end
|
||||
|
||||
@@ -1,51 +1,51 @@
|
||||
This is ustring, a pure-Lua library to handle UTF-8 strings.
|
||||
|
||||
It implements generally the same interface as the standard string library, with
|
||||
the following differences:
|
||||
* Most functions work on codepoints rather than bytes or characters. Yes, this
|
||||
means that even though "á" and "á" should appear identical and represent the
|
||||
same character, the former is one codepoint (U+00E1) while the latter is two
|
||||
(U+0061 U+0301).
|
||||
* Added functions isutf8, byteoffset, codepoint, gcodepoint, toNFC, toNFD.
|
||||
* No workalike for string.reverse is provided.
|
||||
|
||||
Contents:
|
||||
* README - This file.
|
||||
* ustring.lua - The main file for the library.
|
||||
* string.lua - Extend the string metatable with methods from this library.
|
||||
* upper.lua - Data table for ustring.upper.
|
||||
* lower.lua - Data table for ustring.lower.
|
||||
* charsets.lua - Data tables for pattern matching functions.
|
||||
* make-tables.php - Regenerate upper.lua and lower.lua using PHP's multibyte
|
||||
string library, and charsets.lua using PCRE.
|
||||
* normalization-data.lua - Data tables for toNFC and toNFD.
|
||||
* make-normalization-table.php - Regenerate normalization-data.lua based on the
|
||||
file includes/normal/UtfNormalData.inc from MediaWiki core.
|
||||
|
||||
|
||||
This library (consisting of the files described above) is released under the MIT
|
||||
License:
|
||||
|
||||
Copyright (C) 2012 Brad Jorsch <bjorsch@wikimedia.org>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
This is ustring, a pure-Lua library to handle UTF-8 strings.
|
||||
|
||||
It implements generally the same interface as the standard string library, with
|
||||
the following differences:
|
||||
* Most functions work on codepoints rather than bytes or characters. Yes, this
|
||||
means that even though "á" and "á" should appear identical and represent the
|
||||
same character, the former is one codepoint (U+00E1) while the latter is two
|
||||
(U+0061 U+0301).
|
||||
* Added functions isutf8, byteoffset, codepoint, gcodepoint, toNFC, toNFD.
|
||||
* No workalike for string.reverse is provided.
|
||||
|
||||
Contents:
|
||||
* README - This file.
|
||||
* ustring.lua - The main file for the library.
|
||||
* string.lua - Extend the string metatable with methods from this library.
|
||||
* upper.lua - Data table for ustring.upper.
|
||||
* lower.lua - Data table for ustring.lower.
|
||||
* charsets.lua - Data tables for pattern matching functions.
|
||||
* make-tables.php - Regenerate upper.lua and lower.lua using PHP's multibyte
|
||||
string library, and charsets.lua using PCRE.
|
||||
* normalization-data.lua - Data tables for toNFC and toNFD.
|
||||
* make-normalization-table.php - Regenerate normalization-data.lua based on the
|
||||
file includes/normal/UtfNormalData.inc from MediaWiki core.
|
||||
|
||||
|
||||
This library (consisting of the files described above) is released under the MIT
|
||||
License:
|
||||
|
||||
Copyright (C) 2012 Brad Jorsch <bjorsch@wikimedia.org>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,185 +1,185 @@
|
||||
#!/usr/bin/php
|
||||
<?
|
||||
|
||||
if ( PHP_SAPI !== 'cli' ) {
|
||||
die( "This script may only be executed from the command line.\n" );
|
||||
}
|
||||
|
||||
$datafile = null;
|
||||
if ( count( $argv ) > 1 ) {
|
||||
$datafile = $argv[1];
|
||||
if ( !file_exists( $datafile ) ) {
|
||||
die( "The specified file '$datafile' does not exist\n" );
|
||||
}
|
||||
} else {
|
||||
foreach( array(
|
||||
__DIR__ . '/../../../../../core/includes/normal/UtfNormalData.inc',
|
||||
__DIR__ . '/../../../../../includes/normal/UtfNormalData.inc',
|
||||
) as $tryfile ) {
|
||||
$tryfile = realpath( $tryfile );
|
||||
if ( file_exists( $tryfile ) ) {
|
||||
$datafile = $tryfile;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( !$datafile ) {
|
||||
die( "Cannot find UtfNormalData.inc. Please specify the path explicitly.\n" );
|
||||
}
|
||||
}
|
||||
|
||||
echo "Loading data file $datafile...\n";
|
||||
class UtfNormal {
|
||||
static $utfCheckNFC = null;
|
||||
static $utfCombiningClass = null;
|
||||
static $utfCanonicalDecomp = null;
|
||||
static $utfCanonicalComp = null;
|
||||
}
|
||||
require_once( $datafile );
|
||||
|
||||
if ( !UtfNormal::$utfCheckNFC ||
|
||||
!UtfNormal::$utfCombiningClass ||
|
||||
!UtfNormal::$utfCanonicalDecomp ||
|
||||
!UtfNormal::$utfCanonicalComp
|
||||
) {
|
||||
die( "Data file $datafile did not contain needed data.\n" );
|
||||
}
|
||||
|
||||
function uord( $c, $firstOnly ) {
|
||||
$ret = unpack( 'N*', mb_convert_encoding( $c, 'UTF-32BE', 'UTF-8' ) );
|
||||
return $firstOnly ? $ret[1] : $ret;
|
||||
}
|
||||
|
||||
echo "Creating normalization table...\n";
|
||||
$X = fopen( __DIR__ . '/normalization-data.lua', 'w' );
|
||||
if ( !$X ) {
|
||||
die( "Failed to open normalization-data.lua\n" );
|
||||
}
|
||||
fprintf( $X, "-- This file is automatically generated by make-normalization-table.php\n" );
|
||||
fprintf( $X, "local normal = {\n" );
|
||||
fprintf( $X, "\t-- Characters that might change depending on the following combiner\n" );
|
||||
fprintf( $X, "\t-- (minus any that are themselves combiners, those are added later)\n" );
|
||||
fprintf( $X, "\tcheck = {\n" );
|
||||
foreach ( UtfNormal::$utfCheckNFC as $k => $v ) {
|
||||
if ( isset( UtfNormal::$utfCombiningClass[$k] ) ) {
|
||||
// Skip, because it's in the other table already
|
||||
continue;
|
||||
}
|
||||
fprintf( $X, "\t\t[0x%06x] = 1,\n", uord( $k, true ) );
|
||||
}
|
||||
fprintf( $X, "\t},\n\n" );
|
||||
fprintf( $X, "\t-- Combining characters, mapped to combining class\n" );
|
||||
fprintf( $X, "\tcombclass = {\n" );
|
||||
$comb = array();
|
||||
foreach ( UtfNormal::$utfCombiningClass as $k => $v ) {
|
||||
$cp = uord( $k, true );
|
||||
$comb[$cp] = 1;
|
||||
fprintf( $X, "\t\t[0x%06x] = %d,\n", $cp, $v );
|
||||
}
|
||||
fprintf( $X, "\t},\n\n" );
|
||||
fprintf( $X, "\t-- Characters mapped to what they decompose to\n" );
|
||||
fprintf( $X, "\t-- Note Hangul to Jamo is done separately below\n" );
|
||||
fprintf( $X, "\tdecomp = {\n" );
|
||||
foreach ( UtfNormal::$utfCanonicalDecomp as $k => $v ) {
|
||||
fprintf( $X, "\t\t[0x%06x] = { ", uord( $k, true ) );
|
||||
$fmt = "0x%06x";
|
||||
foreach ( uord( $v, false ) as $c ) {
|
||||
fprintf( $X, $fmt, $c );
|
||||
$fmt = ", 0x%06x";
|
||||
}
|
||||
fprintf( $X, " },\n" );
|
||||
}
|
||||
fprintf( $X, "\t},\n\n" );
|
||||
|
||||
fprintf( $X, "\t-- Character-pairs mapped to what they compose to\n" );
|
||||
fprintf( $X, "\t-- Note Jamo to Hangul is done separately below\n" );
|
||||
$t = array();
|
||||
foreach ( UtfNormal::$utfCanonicalComp as $k => $v ) {
|
||||
$k = uord( $k, false );
|
||||
if ( count( $k ) == 1 ) {
|
||||
// No idea why these are in the file
|
||||
continue;
|
||||
}
|
||||
if ( isset( $comb[$k[1]] ) ) {
|
||||
// Non-starter, no idea why these are in the file either
|
||||
continue;
|
||||
}
|
||||
$t[$k[1]][$k[2]] = uord( $v, true );
|
||||
}
|
||||
fprintf( $X, "\tcomp = {\n" );
|
||||
ksort( $t );
|
||||
foreach ( $t as $k1 => $v1 ) {
|
||||
fprintf( $X, "\t\t[0x%06x] = {\n", $k1 );
|
||||
ksort( $v1 );
|
||||
foreach ( $v1 as $k2 => $v2 ) {
|
||||
if ( $k2 < 0 ) {
|
||||
fprintf( $X, "\t\t\t[-1] = 0x%06x,\n", $v2 );
|
||||
} else {
|
||||
fprintf( $X, "\t\t\t[0x%06x] = 0x%06x,\n", $k2, $v2 );
|
||||
}
|
||||
}
|
||||
fprintf( $X, "\t\t},\n" );
|
||||
}
|
||||
fprintf( $X, "\t},\n" );
|
||||
|
||||
fprintf( $X, "}\n" );
|
||||
|
||||
fprintf( $X, "\n%s\n", <<<LUA
|
||||
-- All combining characters need to be checked, so just do that
|
||||
setmetatable( normal.check, { __index = normal.combclass } )
|
||||
|
||||
-- Handle Hangul to Jamo decomposition
|
||||
setmetatable( normal.decomp, { __index = function ( _, k )
|
||||
if k >= 0xac00 and k <= 0xd7a3 then
|
||||
-- Decompose a Hangul syllable into Jamo
|
||||
k = k - 0xac00
|
||||
local ret = {
|
||||
0x1100 + math.floor( k / 588 ),
|
||||
0x1161 + math.floor( ( k % 588 ) / 28 )
|
||||
}
|
||||
if k % 28 ~= 0 then
|
||||
ret[3] = 0x11a7 + ( k % 28 )
|
||||
end
|
||||
return ret
|
||||
end
|
||||
return nil
|
||||
end } )
|
||||
|
||||
-- Handle Jamo to Hangul composition
|
||||
local jamo_l_v_mt = { __index = function ( t, k )
|
||||
if k >= 0x1161 and k <= 0x1175 then
|
||||
-- Jamo leading + Jamo vowel
|
||||
return t.base + 28 * ( k - 0x1161 )
|
||||
end
|
||||
return nil
|
||||
end }
|
||||
local hangul_jamo_mt = { __index = function ( t, k )
|
||||
if k >= 0x11a7 and k <= 0x11c2 then
|
||||
-- Hangul + jamo final
|
||||
return t.base + k - 0x11a7
|
||||
end
|
||||
return nil
|
||||
end }
|
||||
setmetatable( normal.comp, { __index = function ( t, k )
|
||||
if k >= 0x1100 and k <= 0x1112 then
|
||||
-- Jamo leading, return a second table that combines with a Jamo vowel
|
||||
local t2 = { base = 0xac00 + 588 * ( k - 0x1100 ) }
|
||||
setmetatable( t2, jamo_l_v_mt )
|
||||
t[k] = t2 -- cache it
|
||||
return t2
|
||||
elseif k >= 0xac00 and k <= 0xd7a3 and k % 28 == 16 then
|
||||
-- Hangul. "k % 28 == 16" picks out just the ones that are
|
||||
-- Jamo leading + vowel, no final. Return a second table that combines
|
||||
-- with a Jamo final.
|
||||
local t2 = { base = k }
|
||||
setmetatable( t2, hangul_jamo_mt )
|
||||
t[k] = t2 -- cache it
|
||||
return t2
|
||||
end
|
||||
return nil
|
||||
end } )
|
||||
|
||||
return normal
|
||||
LUA
|
||||
);
|
||||
|
||||
fclose( $X );
|
||||
#!/usr/bin/php
|
||||
<?
|
||||
|
||||
if ( PHP_SAPI !== 'cli' ) {
|
||||
die( "This script may only be executed from the command line.\n" );
|
||||
}
|
||||
|
||||
$datafile = null;
|
||||
if ( count( $argv ) > 1 ) {
|
||||
$datafile = $argv[1];
|
||||
if ( !file_exists( $datafile ) ) {
|
||||
die( "The specified file '$datafile' does not exist\n" );
|
||||
}
|
||||
} else {
|
||||
foreach( array(
|
||||
__DIR__ . '/../../../../../core/includes/normal/UtfNormalData.inc',
|
||||
__DIR__ . '/../../../../../includes/normal/UtfNormalData.inc',
|
||||
) as $tryfile ) {
|
||||
$tryfile = realpath( $tryfile );
|
||||
if ( file_exists( $tryfile ) ) {
|
||||
$datafile = $tryfile;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( !$datafile ) {
|
||||
die( "Cannot find UtfNormalData.inc. Please specify the path explicitly.\n" );
|
||||
}
|
||||
}
|
||||
|
||||
echo "Loading data file $datafile...\n";
|
||||
class UtfNormal {
|
||||
static $utfCheckNFC = null;
|
||||
static $utfCombiningClass = null;
|
||||
static $utfCanonicalDecomp = null;
|
||||
static $utfCanonicalComp = null;
|
||||
}
|
||||
require_once( $datafile );
|
||||
|
||||
if ( !UtfNormal::$utfCheckNFC ||
|
||||
!UtfNormal::$utfCombiningClass ||
|
||||
!UtfNormal::$utfCanonicalDecomp ||
|
||||
!UtfNormal::$utfCanonicalComp
|
||||
) {
|
||||
die( "Data file $datafile did not contain needed data.\n" );
|
||||
}
|
||||
|
||||
function uord( $c, $firstOnly ) {
|
||||
$ret = unpack( 'N*', mb_convert_encoding( $c, 'UTF-32BE', 'UTF-8' ) );
|
||||
return $firstOnly ? $ret[1] : $ret;
|
||||
}
|
||||
|
||||
echo "Creating normalization table...\n";
|
||||
$X = fopen( __DIR__ . '/normalization-data.lua', 'w' );
|
||||
if ( !$X ) {
|
||||
die( "Failed to open normalization-data.lua\n" );
|
||||
}
|
||||
fprintf( $X, "-- This file is automatically generated by make-normalization-table.php\n" );
|
||||
fprintf( $X, "local normal = {\n" );
|
||||
fprintf( $X, "\t-- Characters that might change depending on the following combiner\n" );
|
||||
fprintf( $X, "\t-- (minus any that are themselves combiners, those are added later)\n" );
|
||||
fprintf( $X, "\tcheck = {\n" );
|
||||
foreach ( UtfNormal::$utfCheckNFC as $k => $v ) {
|
||||
if ( isset( UtfNormal::$utfCombiningClass[$k] ) ) {
|
||||
// Skip, because it's in the other table already
|
||||
continue;
|
||||
}
|
||||
fprintf( $X, "\t\t[0x%06x] = 1,\n", uord( $k, true ) );
|
||||
}
|
||||
fprintf( $X, "\t},\n\n" );
|
||||
fprintf( $X, "\t-- Combining characters, mapped to combining class\n" );
|
||||
fprintf( $X, "\tcombclass = {\n" );
|
||||
$comb = array();
|
||||
foreach ( UtfNormal::$utfCombiningClass as $k => $v ) {
|
||||
$cp = uord( $k, true );
|
||||
$comb[$cp] = 1;
|
||||
fprintf( $X, "\t\t[0x%06x] = %d,\n", $cp, $v );
|
||||
}
|
||||
fprintf( $X, "\t},\n\n" );
|
||||
fprintf( $X, "\t-- Characters mapped to what they decompose to\n" );
|
||||
fprintf( $X, "\t-- Note Hangul to Jamo is done separately below\n" );
|
||||
fprintf( $X, "\tdecomp = {\n" );
|
||||
foreach ( UtfNormal::$utfCanonicalDecomp as $k => $v ) {
|
||||
fprintf( $X, "\t\t[0x%06x] = { ", uord( $k, true ) );
|
||||
$fmt = "0x%06x";
|
||||
foreach ( uord( $v, false ) as $c ) {
|
||||
fprintf( $X, $fmt, $c );
|
||||
$fmt = ", 0x%06x";
|
||||
}
|
||||
fprintf( $X, " },\n" );
|
||||
}
|
||||
fprintf( $X, "\t},\n\n" );
|
||||
|
||||
fprintf( $X, "\t-- Character-pairs mapped to what they compose to\n" );
|
||||
fprintf( $X, "\t-- Note Jamo to Hangul is done separately below\n" );
|
||||
$t = array();
|
||||
foreach ( UtfNormal::$utfCanonicalComp as $k => $v ) {
|
||||
$k = uord( $k, false );
|
||||
if ( count( $k ) == 1 ) {
|
||||
// No idea why these are in the file
|
||||
continue;
|
||||
}
|
||||
if ( isset( $comb[$k[1]] ) ) {
|
||||
// Non-starter, no idea why these are in the file either
|
||||
continue;
|
||||
}
|
||||
$t[$k[1]][$k[2]] = uord( $v, true );
|
||||
}
|
||||
fprintf( $X, "\tcomp = {\n" );
|
||||
ksort( $t );
|
||||
foreach ( $t as $k1 => $v1 ) {
|
||||
fprintf( $X, "\t\t[0x%06x] = {\n", $k1 );
|
||||
ksort( $v1 );
|
||||
foreach ( $v1 as $k2 => $v2 ) {
|
||||
if ( $k2 < 0 ) {
|
||||
fprintf( $X, "\t\t\t[-1] = 0x%06x,\n", $v2 );
|
||||
} else {
|
||||
fprintf( $X, "\t\t\t[0x%06x] = 0x%06x,\n", $k2, $v2 );
|
||||
}
|
||||
}
|
||||
fprintf( $X, "\t\t},\n" );
|
||||
}
|
||||
fprintf( $X, "\t},\n" );
|
||||
|
||||
fprintf( $X, "}\n" );
|
||||
|
||||
fprintf( $X, "\n%s\n", <<<LUA
|
||||
-- All combining characters need to be checked, so just do that
|
||||
setmetatable( normal.check, { __index = normal.combclass } )
|
||||
|
||||
-- Handle Hangul to Jamo decomposition
|
||||
setmetatable( normal.decomp, { __index = function ( _, k )
|
||||
if k >= 0xac00 and k <= 0xd7a3 then
|
||||
-- Decompose a Hangul syllable into Jamo
|
||||
k = k - 0xac00
|
||||
local ret = {
|
||||
0x1100 + math.floor( k / 588 ),
|
||||
0x1161 + math.floor( ( k % 588 ) / 28 )
|
||||
}
|
||||
if k % 28 ~= 0 then
|
||||
ret[3] = 0x11a7 + ( k % 28 )
|
||||
end
|
||||
return ret
|
||||
end
|
||||
return nil
|
||||
end } )
|
||||
|
||||
-- Handle Jamo to Hangul composition
|
||||
local jamo_l_v_mt = { __index = function ( t, k )
|
||||
if k >= 0x1161 and k <= 0x1175 then
|
||||
-- Jamo leading + Jamo vowel
|
||||
return t.base + 28 * ( k - 0x1161 )
|
||||
end
|
||||
return nil
|
||||
end }
|
||||
local hangul_jamo_mt = { __index = function ( t, k )
|
||||
if k >= 0x11a7 and k <= 0x11c2 then
|
||||
-- Hangul + jamo final
|
||||
return t.base + k - 0x11a7
|
||||
end
|
||||
return nil
|
||||
end }
|
||||
setmetatable( normal.comp, { __index = function ( t, k )
|
||||
if k >= 0x1100 and k <= 0x1112 then
|
||||
-- Jamo leading, return a second table that combines with a Jamo vowel
|
||||
local t2 = { base = 0xac00 + 588 * ( k - 0x1100 ) }
|
||||
setmetatable( t2, jamo_l_v_mt )
|
||||
t[k] = t2 -- cache it
|
||||
return t2
|
||||
elseif k >= 0xac00 and k <= 0xd7a3 and k % 28 == 16 then
|
||||
-- Hangul. "k % 28 == 16" picks out just the ones that are
|
||||
-- Jamo leading + vowel, no final. Return a second table that combines
|
||||
-- with a Jamo final.
|
||||
local t2 = { base = k }
|
||||
setmetatable( t2, hangul_jamo_mt )
|
||||
t[k] = t2 -- cache it
|
||||
return t2
|
||||
end
|
||||
return nil
|
||||
end } )
|
||||
|
||||
return normal
|
||||
LUA
|
||||
);
|
||||
|
||||
fclose( $X );
|
||||
|
||||
@@ -1,151 +1,151 @@
|
||||
#!/usr/bin/php
|
||||
<?
|
||||
|
||||
if ( PHP_SAPI !== 'cli' ) {
|
||||
die( "This script may only be executed from the command line.\n" );
|
||||
}
|
||||
|
||||
$chars=array();
|
||||
for ( $i = 0; $i <= 0x10ffff; $i++ ) {
|
||||
if ( $i < 0xd800 || $i > 0xdfff ) { // Skip UTF-16 surrogates
|
||||
$chars[$i] = mb_convert_encoding( pack( 'N', $i ), 'UTF-8', 'UTF-32BE' );
|
||||
}
|
||||
}
|
||||
|
||||
### Uppercase and Lowercase mappings
|
||||
echo "Creating upper and lower tables...\n";
|
||||
$L = fopen( __DIR__ . '/lower.lua', 'w' );
|
||||
if ( !$L ) {
|
||||
die( "Failed to open lower.lua\n" );
|
||||
}
|
||||
$U = fopen( __DIR__ . '/upper.lua', 'w' );
|
||||
if ( !$U ) {
|
||||
die( "Failed to open upper.lua\n" );
|
||||
}
|
||||
fprintf( $L, "-- This file is automatically generated by make-tables.php\n" );
|
||||
fprintf( $L, "return {\n" );
|
||||
fprintf( $U, "-- This file is automatically generated by make-tables.php\n" );
|
||||
fprintf( $U, "return {\n" );
|
||||
foreach ( $chars as $i => $c ) {
|
||||
$l = mb_strtolower( $c, 'UTF-8' );
|
||||
$u = mb_strtoupper( $c, 'UTF-8' );
|
||||
if ( $c !== $l ) {
|
||||
fprintf( $L, "\t[\"%s\"] = \"%s\",\n", $c, $l );
|
||||
}
|
||||
if ( $c !== $u ) {
|
||||
fprintf( $U, "\t[\"%s\"] = \"%s\",\n", $c, $u );
|
||||
}
|
||||
}
|
||||
fprintf( $L, "}\n" );
|
||||
fprintf( $U, "}\n" );
|
||||
fclose( $L );
|
||||
fclose( $U );
|
||||
|
||||
### Pattern code mappings
|
||||
echo "Creating charsets table...\n";
|
||||
$X = fopen( __DIR__ . '/charsets.lua', 'w' );
|
||||
if ( !$X ) {
|
||||
die( "Failed to open charsets.lua\n" );
|
||||
}
|
||||
$pats = array(
|
||||
// These should match the expressions in UstringLibrary::patternToRegex()
|
||||
'a' => array( '\p{L}', 'lu' ),
|
||||
'c' => array( '\p{Cc}', null ),
|
||||
'd' => array( '\p{Nd}', null ),
|
||||
'l' => array( '\p{Ll}', null ),
|
||||
'p' => array( '\p{P}', null ),
|
||||
's' => array( '\p{Xps}', null ),
|
||||
'u' => array( '\p{Lu}', null ),
|
||||
'w' => array( null, 'da' ), # '[\p{L}\p{Nd}]' exactly matches 'a' + 'd'
|
||||
'x' => array( '[0-9A-Fa-f0-9A-Fa-f]', null ),
|
||||
'z' => array( '\0', null ),
|
||||
);
|
||||
|
||||
$ranges = array();
|
||||
function addRange( $k, $start, $end ) {
|
||||
global $X, $ranges;
|
||||
// Speed/memory tradeoff
|
||||
if ( !( $start >= 0x20 && $start < 0x7f ) && $end - $start >= 10 ) {
|
||||
$ranges[$k][] = sprintf( "c >= 0x%06x and c < 0x%06x", $start, $end );
|
||||
} else {
|
||||
for ( $i = $start; $i < $end; $i++ ) {
|
||||
fprintf( $X, "\t\t[0x%06x] = 1,\n", $i );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fprintf( $X, "-- This file is automatically generated by make-tables.php\n" );
|
||||
fprintf( $X, "local pats = {\n" );
|
||||
foreach ( $pats as $k => $pp ) {
|
||||
$ranges[$k] = array();
|
||||
$re=$pp[0];
|
||||
if ( !$re ) {
|
||||
fprintf( $X, "\t[0x%02x] = {},\n", ord( $k ) );
|
||||
continue;
|
||||
}
|
||||
|
||||
$re2='fail';
|
||||
if ( $pp[1] ) {
|
||||
$re2 = array();
|
||||
foreach ( str_split( $pp[1] ) as $p ) {
|
||||
$re2[] = $pats[$p][0];
|
||||
}
|
||||
$re2=join( '|', $re2 );
|
||||
}
|
||||
|
||||
fprintf( $X, "\t[0x%02x] = {\n", ord( $k ) );
|
||||
$rstart = null;
|
||||
foreach ( $chars as $i => $c ) {
|
||||
if ( preg_match( "/^$re$/u", $c ) && !preg_match( "/^$re2$/u", $c ) ) {
|
||||
if ( $rstart === null ) {
|
||||
$rstart = $i;
|
||||
}
|
||||
} else {
|
||||
if ( $rstart !== null ) {
|
||||
addRange( $k, $rstart, $i );
|
||||
$rstart = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( $rstart !== null ) {
|
||||
addRange( $k, $rstart, 0x110000 );
|
||||
}
|
||||
fprintf( $X, "\t},\n" );
|
||||
}
|
||||
foreach ( $pats as $k => $pp ) {
|
||||
$kk = strtoupper( $k );
|
||||
fprintf( $X, "\t[0x%02x] = {},\n", ord( $kk ) );
|
||||
}
|
||||
fprintf( $X, "}\n" );
|
||||
foreach ( $pats as $k => $pp ) {
|
||||
$body = '';
|
||||
$check = array();
|
||||
if ( $pp[1] ) {
|
||||
foreach ( str_split( $pp[1] ) as $p ) {
|
||||
$check[] = sprintf( "pats[0x%02x][k]", ord( $p ) );
|
||||
}
|
||||
}
|
||||
if ( $ranges[$k] ) {
|
||||
$body = "\tlocal c = tonumber( k ) or 0/0;\n";
|
||||
$check = array_merge( $check, $ranges[$k] );
|
||||
}
|
||||
if ( $check ) {
|
||||
$body .= "\treturn " . join( " or\n\t\t", $check );
|
||||
fprintf( $X, "setmetatable( pats[0x%02x], { __index = function ( t, k )\n%s\nend } )\n",
|
||||
ord( $k ), $body );
|
||||
}
|
||||
}
|
||||
foreach ( $pats as $k => $pp ) {
|
||||
fprintf( $X, "setmetatable( pats[0x%02x], { __index = function ( t, k ) return k and not pats[0x%02x][k] end } )\n",
|
||||
ord( strtoupper( $k ) ), ord( $k ) );
|
||||
}
|
||||
fprintf( $X, "\n-- For speed, cache printable ASCII characters in main tables\n" );
|
||||
fprintf( $X, "for k, t in pairs( pats ) do\n" );
|
||||
fprintf( $X, "\tif k >= 0x61 then\n" );
|
||||
fprintf( $X, "\t\tfor i = 0x20, 0x7e do\n" );
|
||||
fprintf( $X, "\t\t\tt[i] = t[i] or false\n" );
|
||||
fprintf( $X, "\t\tend\n" );
|
||||
fprintf( $X, "\tend\n" );
|
||||
fprintf( $X, "end\n" );
|
||||
fprintf( $X, "\nreturn pats\n" );
|
||||
fclose( $X );
|
||||
#!/usr/bin/php
|
||||
<?
|
||||
|
||||
if ( PHP_SAPI !== 'cli' ) {
|
||||
die( "This script may only be executed from the command line.\n" );
|
||||
}
|
||||
|
||||
$chars=array();
|
||||
for ( $i = 0; $i <= 0x10ffff; $i++ ) {
|
||||
if ( $i < 0xd800 || $i > 0xdfff ) { // Skip UTF-16 surrogates
|
||||
$chars[$i] = mb_convert_encoding( pack( 'N', $i ), 'UTF-8', 'UTF-32BE' );
|
||||
}
|
||||
}
|
||||
|
||||
### Uppercase and Lowercase mappings
|
||||
echo "Creating upper and lower tables...\n";
|
||||
$L = fopen( __DIR__ . '/lower.lua', 'w' );
|
||||
if ( !$L ) {
|
||||
die( "Failed to open lower.lua\n" );
|
||||
}
|
||||
$U = fopen( __DIR__ . '/upper.lua', 'w' );
|
||||
if ( !$U ) {
|
||||
die( "Failed to open upper.lua\n" );
|
||||
}
|
||||
fprintf( $L, "-- This file is automatically generated by make-tables.php\n" );
|
||||
fprintf( $L, "return {\n" );
|
||||
fprintf( $U, "-- This file is automatically generated by make-tables.php\n" );
|
||||
fprintf( $U, "return {\n" );
|
||||
foreach ( $chars as $i => $c ) {
|
||||
$l = mb_strtolower( $c, 'UTF-8' );
|
||||
$u = mb_strtoupper( $c, 'UTF-8' );
|
||||
if ( $c !== $l ) {
|
||||
fprintf( $L, "\t[\"%s\"] = \"%s\",\n", $c, $l );
|
||||
}
|
||||
if ( $c !== $u ) {
|
||||
fprintf( $U, "\t[\"%s\"] = \"%s\",\n", $c, $u );
|
||||
}
|
||||
}
|
||||
fprintf( $L, "}\n" );
|
||||
fprintf( $U, "}\n" );
|
||||
fclose( $L );
|
||||
fclose( $U );
|
||||
|
||||
### Pattern code mappings
|
||||
echo "Creating charsets table...\n";
|
||||
$X = fopen( __DIR__ . '/charsets.lua', 'w' );
|
||||
if ( !$X ) {
|
||||
die( "Failed to open charsets.lua\n" );
|
||||
}
|
||||
$pats = array(
|
||||
// These should match the expressions in UstringLibrary::patternToRegex()
|
||||
'a' => array( '\p{L}', 'lu' ),
|
||||
'c' => array( '\p{Cc}', null ),
|
||||
'd' => array( '\p{Nd}', null ),
|
||||
'l' => array( '\p{Ll}', null ),
|
||||
'p' => array( '\p{P}', null ),
|
||||
's' => array( '\p{Xps}', null ),
|
||||
'u' => array( '\p{Lu}', null ),
|
||||
'w' => array( null, 'da' ), # '[\p{L}\p{Nd}]' exactly matches 'a' + 'd'
|
||||
'x' => array( '[0-9A-Fa-f0-9A-Fa-f]', null ),
|
||||
'z' => array( '\0', null ),
|
||||
);
|
||||
|
||||
$ranges = array();
|
||||
function addRange( $k, $start, $end ) {
|
||||
global $X, $ranges;
|
||||
// Speed/memory tradeoff
|
||||
if ( !( $start >= 0x20 && $start < 0x7f ) && $end - $start >= 10 ) {
|
||||
$ranges[$k][] = sprintf( "c >= 0x%06x and c < 0x%06x", $start, $end );
|
||||
} else {
|
||||
for ( $i = $start; $i < $end; $i++ ) {
|
||||
fprintf( $X, "\t\t[0x%06x] = 1,\n", $i );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fprintf( $X, "-- This file is automatically generated by make-tables.php\n" );
|
||||
fprintf( $X, "local pats = {\n" );
|
||||
foreach ( $pats as $k => $pp ) {
|
||||
$ranges[$k] = array();
|
||||
$re=$pp[0];
|
||||
if ( !$re ) {
|
||||
fprintf( $X, "\t[0x%02x] = {},\n", ord( $k ) );
|
||||
continue;
|
||||
}
|
||||
|
||||
$re2='fail';
|
||||
if ( $pp[1] ) {
|
||||
$re2 = array();
|
||||
foreach ( str_split( $pp[1] ) as $p ) {
|
||||
$re2[] = $pats[$p][0];
|
||||
}
|
||||
$re2=join( '|', $re2 );
|
||||
}
|
||||
|
||||
fprintf( $X, "\t[0x%02x] = {\n", ord( $k ) );
|
||||
$rstart = null;
|
||||
foreach ( $chars as $i => $c ) {
|
||||
if ( preg_match( "/^$re$/u", $c ) && !preg_match( "/^$re2$/u", $c ) ) {
|
||||
if ( $rstart === null ) {
|
||||
$rstart = $i;
|
||||
}
|
||||
} else {
|
||||
if ( $rstart !== null ) {
|
||||
addRange( $k, $rstart, $i );
|
||||
$rstart = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( $rstart !== null ) {
|
||||
addRange( $k, $rstart, 0x110000 );
|
||||
}
|
||||
fprintf( $X, "\t},\n" );
|
||||
}
|
||||
foreach ( $pats as $k => $pp ) {
|
||||
$kk = strtoupper( $k );
|
||||
fprintf( $X, "\t[0x%02x] = {},\n", ord( $kk ) );
|
||||
}
|
||||
fprintf( $X, "}\n" );
|
||||
foreach ( $pats as $k => $pp ) {
|
||||
$body = '';
|
||||
$check = array();
|
||||
if ( $pp[1] ) {
|
||||
foreach ( str_split( $pp[1] ) as $p ) {
|
||||
$check[] = sprintf( "pats[0x%02x][k]", ord( $p ) );
|
||||
}
|
||||
}
|
||||
if ( $ranges[$k] ) {
|
||||
$body = "\tlocal c = tonumber( k ) or 0/0;\n";
|
||||
$check = array_merge( $check, $ranges[$k] );
|
||||
}
|
||||
if ( $check ) {
|
||||
$body .= "\treturn " . join( " or\n\t\t", $check );
|
||||
fprintf( $X, "setmetatable( pats[0x%02x], { __index = function ( t, k )\n%s\nend } )\n",
|
||||
ord( $k ), $body );
|
||||
}
|
||||
}
|
||||
foreach ( $pats as $k => $pp ) {
|
||||
fprintf( $X, "setmetatable( pats[0x%02x], { __index = function ( t, k ) return k and not pats[0x%02x][k] end } )\n",
|
||||
ord( strtoupper( $k ) ), ord( $k ) );
|
||||
}
|
||||
fprintf( $X, "\n-- For speed, cache printable ASCII characters in main tables\n" );
|
||||
fprintf( $X, "for k, t in pairs( pats ) do\n" );
|
||||
fprintf( $X, "\tif k >= 0x61 then\n" );
|
||||
fprintf( $X, "\t\tfor i = 0x20, 0x7e do\n" );
|
||||
fprintf( $X, "\t\t\tt[i] = t[i] or false\n" );
|
||||
fprintf( $X, "\t\tend\n" );
|
||||
fprintf( $X, "\tend\n" );
|
||||
fprintf( $X, "end\n" );
|
||||
fprintf( $X, "\nreturn pats\n" );
|
||||
fclose( $X );
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,15 +1,15 @@
|
||||
string.isutf8 = ustring.isutf8
|
||||
string.byteoffset = ustring.byteoffset
|
||||
string.codepoint = ustring.codepoint
|
||||
string.gcodepoint = ustring.gcodepoint
|
||||
string.toNFC = ustring.toNFC
|
||||
string.toNFD = ustring.toNFD
|
||||
string.uchar = ustring.char
|
||||
string.ulen = ustring.len
|
||||
string.usub = ustring.sub
|
||||
string.uupper = ustring.upper
|
||||
string.ulower = ustring.lower
|
||||
string.ufind = ustring.find
|
||||
string.umatch = ustring.match
|
||||
string.ugmatch = ustring.gmatch
|
||||
string.ugsub = ustring.gsub
|
||||
string.isutf8 = ustring.isutf8
|
||||
string.byteoffset = ustring.byteoffset
|
||||
string.codepoint = ustring.codepoint
|
||||
string.gcodepoint = ustring.gcodepoint
|
||||
string.toNFC = ustring.toNFC
|
||||
string.toNFD = ustring.toNFD
|
||||
string.uchar = ustring.char
|
||||
string.ulen = ustring.len
|
||||
string.usub = ustring.sub
|
||||
string.uupper = ustring.upper
|
||||
string.ulower = ustring.lower
|
||||
string.ufind = ustring.find
|
||||
string.umatch = ustring.match
|
||||
string.ugmatch = ustring.gmatch
|
||||
string.ugsub = ustring.gsub
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,8 @@
|
||||
package.path = arg[1] .. '/engines/LuaStandalone/?.lua;' ..
|
||||
arg[1] .. '/engines/LuaCommon/lualib/?.lua'
|
||||
|
||||
require('MWServer')
|
||||
require('mwInit')
|
||||
server = MWServer:new()
|
||||
server:execute()
|
||||
|
||||
package.path = arg[1] .. '/engines/LuaStandalone/?.lua;' ..
|
||||
arg[1] .. '/engines/LuaCommon/lualib/?.lua'
|
||||
|
||||
require('MWServer')
|
||||
require('mwInit')
|
||||
server = MWServer:new()
|
||||
server:execute()
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,9 @@
|
||||
require('MWServer')
|
||||
require('mwInit')
|
||||
_G.getfenv = require 'compat_env'.getfenv
|
||||
_G.setfenv = require 'compat_env'.setfenv
|
||||
_G.loadstring = load
|
||||
_G.unpack = table.unpack
|
||||
|
||||
server = MWServer:new()
|
||||
return server
|
||||
require('MWServer')
|
||||
require('mwInit')
|
||||
_G.getfenv = require 'compat_env'.getfenv
|
||||
_G.setfenv = require 'compat_env'.setfenv
|
||||
_G.loadstring = load
|
||||
_G.unpack = table.unpack
|
||||
|
||||
server = MWServer:new()
|
||||
return server
|
||||
|
||||
Reference in New Issue
Block a user