1
0
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:
gnosygnu
2021-12-19 16:19:19 -05:00
parent 48559edffe
commit 0e80d7ef6d
7999 changed files with 1375876 additions and 1365947 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 = {
['>'] = '&gt;',
['<'] = '&lt;',
['&'] = '&amp;',
['"'] = '&quot;',
}
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 = {
['>'] = '&gt;',
['<'] = '&lt;',
['&'] = '&amp;',
['"'] = '&quot;',
}
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

View File

@@ -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 '&rlm;' or '&lrm;'
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 '&rlm;' or '&lrm;'
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

View File

@@ -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

View File

@@ -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

View File

@@ -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 = {
['>'] = '&gt;',
['<'] = '&lt;',
['&'] = '&amp;',
['"'] = '&quot;',
["'"] = '&#039;',
['\194\160'] = '&nbsp;',
}
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 = {
['"'] = '&#34;',
['&'] = '&#38;',
["'"] = '&#39;',
['<'] = '&#60;',
['='] = '&#61;',
['>'] = '&#62;',
['['] = '&#91;',
[']'] = '&#93;',
['{'] = '&#123;',
['|'] = '&#124;',
['}'] = '&#125;',
}
local nowikiRepl2 = {
["\n#"] = "\n&#35;", ["\r#"] = "\r&#35;",
["\n*"] = "\n&#42;", ["\r*"] = "\r&#42;",
["\n:"] = "\n&#58;", ["\r:"] = "\r&#58;",
["\n;"] = "\n&#59;", ["\r;"] = "\r&#59;",
["\n "] = "\n&#32;", ["\r "] = "\r&#32;",
["\n\n"] = "\n&#10;", ["\r\n"] = "&#13;\n",
["\n\r"] = "\n&#13;", ["\r\r"] = "\r&#13;",
["\n\t"] = "\n&#9;", ["\r\t"] = "\r&#9;",
}
local nowikiReplMagic = {}
for sp, esc in pairs( {
[' '] = '&#32;',
['\t'] = '&#9;',
['\r'] = '&#13;',
['\n'] = '&#10;',
['\f'] = '&#12;',
} ) 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&#45;---' )
s = string.sub( s, 2 )
s = string.gsub( s, '__', '_&#95;' )
s = string.gsub( s, '://', '&#58;//' )
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 = {
['>'] = '&gt;',
['<'] = '&lt;',
['&'] = '&amp;',
['"'] = '&quot;',
["'"] = '&#039;',
['\194\160'] = '&nbsp;',
}
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 = {
['"'] = '&#34;',
['&'] = '&#38;',
["'"] = '&#39;',
['<'] = '&#60;',
['='] = '&#61;',
['>'] = '&#62;',
['['] = '&#91;',
[']'] = '&#93;',
['{'] = '&#123;',
['|'] = '&#124;',
['}'] = '&#125;',
}
local nowikiRepl2 = {
["\n#"] = "\n&#35;", ["\r#"] = "\r&#35;",
["\n*"] = "\n&#42;", ["\r*"] = "\r&#42;",
["\n:"] = "\n&#58;", ["\r:"] = "\r&#58;",
["\n;"] = "\n&#59;", ["\r;"] = "\r&#59;",
["\n "] = "\n&#32;", ["\r "] = "\r&#32;",
["\n\n"] = "\n&#10;", ["\r\n"] = "&#13;\n",
["\n\r"] = "\n&#13;", ["\r\r"] = "\r&#13;",
["\n\t"] = "\n&#9;", ["\r\t"] = "\r&#9;",
}
local nowikiReplMagic = {}
for sp, esc in pairs( {
[' '] = '&#32;',
['\t'] = '&#9;',
['\r'] = '&#13;',
['\n'] = '&#10;',
['\f'] = '&#12;',
} ) 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&#45;---' )
s = string.sub( s, 2 )
s = string.gsub( s, '__', '_&#95;' )
s = string.gsub( s, '://', '&#58;//' )
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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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 );

View File

@@ -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-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-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 );

View File

@@ -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

View File

@@ -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

View File

@@ -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