Created
May 22, 2009 17:56
-
-
Save stephenjudkins/116269 to your computer and use it in GitHub Desktop.
the most beautiful ruby code ever produced.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
MW_PARSER_VERSION = "1.6.1" | |
RLH_FOR_UPDATE = 1 | |
OT_HTML = 1 | |
OT_WIKI = 2 | |
OT_MSG = 3 | |
OT_PREPROCESS = 4 | |
SFH_NO_HASH = 1 | |
STRIP_COMMENTS = "HTMLCommentStrip" | |
HTTP_PROTOCOLS = "http:\\/\\/|https:\\/\\/" | |
EXT_LINK_URL_CLASS = "[^][<>\"\\x00-\\x20\\x7F]" | |
EXT_LINK_TEXT_CLASS = "[^\\]\\x0a\\x0d]" | |
EXT_IMAGE_FNAME_CLASS = "[A-Za-z0-9_.,~%\\-+&;#*?!=()@\\x80-\\xFF]" | |
EXT_IMAGE_EXTENSIONS = "gif|png|jpg|jpeg" | |
EXT_LINK_BRACKETED = ("/\\[(\\b(" + (wfUrlProtocols + (")" + (EXT_LINK_URL_CLASS + ("+) *(" + (EXT_LINK_TEXT_CLASS + "*?)\\]/S")))))) | |
EXT_IMAGE_REGEX = ("/^(" + (HTTP_PROTOCOLS + (")(" + (EXT_LINK_URL_CLASS + ("+)\\/(" + (EXT_IMAGE_FNAME_CLASS + ("+)\\.((?i)" + (EXT_IMAGE_EXTENSIONS + ")$/S")))))))) | |
MW_COLON_STATE_TEXT = 0 | |
MW_COLON_STATE_TAG = 1 | |
MW_COLON_STATE_TAGSTART = 2 | |
MW_COLON_STATE_CLOSETAG = 3 | |
MW_COLON_STATE_TAGSLASH = 4 | |
MW_COLON_STATE_COMMENT = 5 | |
MW_COLON_STATE_COMMENTDASH = 6 | |
MW_COLON_STATE_COMMENTDASHDASH = 7 | |
class Parser | |
attr_accessor(:mTagHooks) | |
attr_accessor(:mFunctionHooks) | |
attr_accessor(:mFunctionSynonyms) | |
attr_accessor(:mVariables) | |
attr_accessor(:mOutput) | |
attr_accessor(:mAutonumber) | |
attr_accessor(:mDTopen) | |
attr_accessor(:mStripState) | |
attr_accessor(:mIncludeCount) | |
attr_accessor(:mArgStack) | |
attr_accessor(:mLastSection) | |
attr_accessor(:mInPre) | |
attr_accessor(:mInterwikiLinkHolders) | |
attr_accessor(:mLinkHolders) | |
attr_accessor(:mUniqPrefix) | |
attr_accessor(:mIncludeSizes) | |
attr_accessor(:mDefaultSort) | |
attr_accessor(:mTemplates) | |
attr_accessor(:mTemplatePath) | |
attr_accessor(:mOptions) | |
attr_accessor(:mTitle) | |
attr_accessor(:mOutputType) | |
attr_accessor(:ot) | |
attr_accessor(:mRevisionId) | |
attr_accessor(:mRevisionTimestamp) | |
attr_accessor(:mRevIdForTs) | |
def Parser | |
@mTagHooks = [] | |
@mFunctionHooks = [] | |
@mFunctionSynonyms = { 0 => ([]), 1 => ([]) } | |
@mFirstCall = true | |
end | |
def firstCallInit | |
return nil if (not @mFirstCall) | |
wfProfileIn("Parser::firstCallInit") | |
(php_global(:wgAllowDisplayTitle) | |
php_global(:wgAllowSlowParserFunctions)) | |
self.setHook("pre", [self, "renderPreTag"]) | |
self.setFunctionHook("int", ["CoreParserFunctions", "intFunction"], SFH_NO_HASH) | |
self.setFunctionHook("ns", ["CoreParserFunctions", "ns"], SFH_NO_HASH) | |
self.setFunctionHook("urlencode", ["CoreParserFunctions", "urlencode"], SFH_NO_HASH) | |
self.setFunctionHook("lcfirst", ["CoreParserFunctions", "lcfirst"], SFH_NO_HASH) | |
self.setFunctionHook("ucfirst", ["CoreParserFunctions", "ucfirst"], SFH_NO_HASH) | |
self.setFunctionHook("lc", ["CoreParserFunctions", "lc"], SFH_NO_HASH) | |
self.setFunctionHook("uc", ["CoreParserFunctions", "uc"], SFH_NO_HASH) | |
self.setFunctionHook("localurl", ["CoreParserFunctions", "localurl"], SFH_NO_HASH) | |
self.setFunctionHook("localurle", ["CoreParserFunctions", "localurle"], SFH_NO_HASH) | |
self.setFunctionHook("fullurl", ["CoreParserFunctions", "fullurl"], SFH_NO_HASH) | |
self.setFunctionHook("fullurle", ["CoreParserFunctions", "fullurle"], SFH_NO_HASH) | |
self.setFunctionHook("formatnum", ["CoreParserFunctions", "formatnum"], SFH_NO_HASH) | |
self.setFunctionHook("grammar", ["CoreParserFunctions", "grammar"], SFH_NO_HASH) | |
self.setFunctionHook("plural", ["CoreParserFunctions", "plural"], SFH_NO_HASH) | |
self.setFunctionHook("numberofpages", ["CoreParserFunctions", "numberofpages"], SFH_NO_HASH) | |
self.setFunctionHook("numberofusers", ["CoreParserFunctions", "numberofusers"], SFH_NO_HASH) | |
self.setFunctionHook("numberofarticles", ["CoreParserFunctions", "numberofarticles"], SFH_NO_HASH) | |
self.setFunctionHook("numberoffiles", ["CoreParserFunctions", "numberoffiles"], SFH_NO_HASH) | |
self.setFunctionHook("numberofadmins", ["CoreParserFunctions", "numberofadmins"], SFH_NO_HASH) | |
self.setFunctionHook("numberofedits", ["CoreParserFunctions", "numberofedits"], SFH_NO_HASH) | |
self.setFunctionHook("language", ["CoreParserFunctions", "language"], SFH_NO_HASH) | |
self.setFunctionHook("padleft", ["CoreParserFunctions", "padleft"], SFH_NO_HASH) | |
self.setFunctionHook("padright", ["CoreParserFunctions", "padright"], SFH_NO_HASH) | |
self.setFunctionHook("anchorencode", ["CoreParserFunctions", "anchorencode"], SFH_NO_HASH) | |
self.setFunctionHook("special", ["CoreParserFunctions", "special"]) | |
self.setFunctionHook("defaultsort", ["CoreParserFunctions", "defaultsort"], SFH_NO_HASH) | |
if wgAllowDisplayTitle then | |
self.setFunctionHook("displaytitle", ["CoreParserFunctions", "displaytitle"], SFH_NO_HASH) | |
end | |
if wgAllowSlowParserFunctions then | |
self.setFunctionHook("pagesinnamespace", ["CoreParserFunctions", "pagesinnamespace"], SFH_NO_HASH) | |
end | |
self.initialiseVariables | |
@mFirstCall = false | |
wfProfileOut("Parser::firstCallInit") | |
end | |
def clearState | |
wfProfileIn("Parser::clearState") | |
self.firstCallInit if @mFirstCall | |
@mOutput = ParserOutput.new | |
@mAutonumber = 0 | |
@mLastSection = "" | |
@mDTopen = false | |
@mIncludeCount = [] | |
@mStripState = StripState.new | |
@mArgStack = [] | |
@mInPre = false | |
@mInterwikiLinkHolders = { "texts" => ([]), "titles" => ([]) } | |
@mLinkHolders = { "namespaces" => ([]), "dbkeys" => ([]), "queries" => ([]), "texts" => ([]), "titles" => ([]) } | |
@mRevisionTimestamp = @mRevisionId = nil | |
@mUniqPrefix = ("\aUNIQ" + Parser.getRandomString) | |
@mTemplates = [] | |
@mTemplatePath = [] | |
@mShowToc = true | |
@mForceTocPosition = false | |
@mIncludeSizes = { "pre-expand" => 0, "post-expand" => 0, "arg" => 0 } | |
@mDefaultSort = false | |
wfRunHooks("ParserClearState", [self]) | |
wfProfileOut("Parser::clearState") | |
end | |
def setOutputType(ot) | |
@mOutputType = ot | |
@ot = { "html" => ((ot == OT_HTML)), "wiki" => ((ot == OT_WIKI)), "msg" => ((ot == OT_MSG)), "pre" => ((ot == OT_PREPROCESS)) } | |
end | |
def uniqPrefix | |
return @mUniqPrefix | |
end | |
def parse(text, title, options, linestart = true, clearState = true, revid = nil) | |
(php_global(:wgUseTidy) | |
php_global(:wgAlwaysUseTidy) | |
php_global(:wgContLang)) | |
fname = ("Parser::parse-" + wfGetCaller) | |
wfProfileIn("Parser::parse") | |
wfProfileIn(fname) | |
self.clearState if clearState | |
@mOptions = options | |
@mTitle = title | |
oldRevisionId = @mRevisionId | |
oldRevisionTimestamp = @mRevisionTimestamp | |
if (not (revid == nil)) then | |
@mRevisionId = revid | |
@mRevisionTimestamp = nil | |
end | |
self.setOutputType(OT_HTML) | |
wfRunHooks("ParserBeforeStrip", [self, text, @mStripState]) | |
text = self.strip(text, @mStripState) | |
wfRunHooks("ParserAfterStrip", [self, text, @mStripState]) | |
text = self.internalParse(text) | |
text = @mStripState.unstripGeneral(text) | |
fixtags = { "/(.) (?=\\?|:|;|!|\\302\\273)/" => "\\1 \\2", "/(\\302\\253) /" => "\\1 " } | |
text = preg_replace(array_keys(fixtags), array_values(fixtags), text) | |
text = self.doBlockLevels(text, linestart) | |
self.replaceLinkHolders(text) | |
text = wgContLang.parserConvert(text, self) | |
text = @mStripState.unstripNoWiki(text) | |
wfRunHooks("ParserBeforeTidy", [self, text]) | |
text = Sanitizer.normalizeCharReferences(text) | |
if ((wgUseTidy and @mOptions.mTidy) or wgAlwaysUseTidy) then | |
text = Parser.tidy(text) | |
else | |
tidyregs = { "/(<([bi])>)(<([bi])>)?([^<]*)(<\\/?a[^<]*>)([^<]*)(<\\/\\4>)?(<\\/\\2>)/" => "\\1\\3\\5\\8\\9\\6\\1\\3\\7\\8\\9", "/(<a[^>]+>)([^<]*)(<a[^>]+>[^<]*)<\\/a>(.*)<\\/a>/" => "\\1\\2</a>\\3</a>\\1\\4</a>", "/(<([aib]) [^>]+>)([^<]*)(<div([^>]*)>)(.*)(<\\/div>)([^<]*)(<\\/\\2>)/" => "\\1\\3<div\\5>\\6</div>\\8\\9", "/<([bi])><\\/\\1>/" => "" } | |
text = preg_replace(array_keys(tidyregs), array_values(tidyregs), text) | |
end | |
wfRunHooks("ParserAfterTidy", [self, text]) | |
if (max(@mIncludeSizes) > 1500) then | |
max = @mOptions.getMaxIncludeSize | |
text = (text + ("<!-- \nPre-expand include size: " + (@mIncludeSizes["pre-expand"] + (" bytes\nPost-expand include size: " + (@mIncludeSizes["post-expand"] + (" bytes\nTemplate argument size: " + (@mIncludeSizes["arg"] + (" bytes\nMaximum: " + (max + " bytes\n-->\n"))))))))) | |
end | |
@mOutput.setText(text) | |
@mRevisionId = oldRevisionId | |
@mRevisionTimestamp = oldRevisionTimestamp | |
wfProfileOut(fname) | |
wfProfileOut("Parser::parse") | |
return @mOutput | |
end | |
def recursiveTagParse(text) | |
wfProfileIn("Parser::recursiveTagParse") | |
wfRunHooks("ParserBeforeStrip", [self, text, @mStripState]) | |
text = self.strip(text, @mStripState) | |
wfRunHooks("ParserAfterStrip", [self, text, @mStripState]) | |
text = self.internalParse(text) | |
wfProfileOut("Parser::recursiveTagParse") | |
return text | |
end | |
def preprocess(text, title, options) | |
wfProfileIn("Parser::preprocess") | |
self.clearState | |
self.setOutputType(OT_PREPROCESS) | |
@mOptions = options | |
@mTitle = title | |
wfRunHooks("ParserBeforeStrip", [self, text, @mStripState]) | |
text = self.strip(text, @mStripState) | |
wfRunHooks("ParserAfterStrip", [self, text, @mStripState]) | |
text = Sanitizer.removeHTMLcomments(text) if @mOptions.getRemoveComments | |
text = self.replaceVariables(text) | |
text = @mStripState.unstripBoth(text) | |
wfProfileOut("Parser::preprocess") | |
return text | |
end | |
def getRandomString | |
return (dechex(mt_rand(0, 2147483647)) + dechex(mt_rand(0, 2147483647))) | |
end | |
def getTitle | |
return @mTitle | |
end | |
def getOptions | |
return @mOptions | |
end | |
def getFunctionLang | |
(php_global(:wgLang) | |
php_global(:wgContLang)) | |
return @mOptions.getInterfaceMessage ? (wgLang) : (wgContLang) | |
end | |
def extractTagsAndParams(elements, text, matches, uniq_prefix = "") | |
php_static_var(:static => (true)) | |
stripped = "" | |
matches = [] | |
taglist = implode("|", elements) | |
start = ("/<(" + (taglist + ")(\\s+[^>]*?|\\s*?)(\\/?>)|<(!--)/i")) | |
while (not ("" == text)) do | |
p = preg_split(start, text, 2, PREG_SPLIT_DELIM_CAPTURE) | |
stripped = (stripped + p[0]) | |
break if (count(p) < 5) | |
if (count(p) > 5) then | |
element = p[4] | |
attributes = "" | |
close = "" | |
inside = p[5] | |
else | |
element = p[1] | |
attributes = p[2] | |
close = p[3] | |
inside = p[4] | |
end | |
marker = ("" + (uniq_prefix + ("-" + (element + ("-" + (sprintf("%08X", n = (n + 1)) + "-QINU")))))) | |
stripped = (stripped + marker) | |
if (close == "/>") then | |
content = nil | |
text = inside | |
tail = nil | |
else | |
if (element == "!--") then | |
_end = "/(-->)/" | |
else | |
_end = ("/(<\\/" + (element + "\\s*>)/i")) | |
end | |
q = preg_split(_end, inside, 2, PREG_SPLIT_DELIM_CAPTURE) | |
content = q[0] | |
if (count(q) < 3) then | |
tail = "" | |
text = "" | |
else | |
tail = q[1] | |
text = q[2] | |
end | |
end | |
matches[marker] = [element, content, Sanitizer.decodeTagAttributes(attributes), ("<" + (element + (attributes + (close + (content + tail)))))] | |
end | |
return stripped | |
end | |
def strip(text, state, stripcomments = false, dontstrip = []) | |
php_global(:wgContLang) | |
wfProfileIn("Parser::strip") | |
render = (@mOutputType == OT_HTML) | |
uniq_prefix = @mUniqPrefix | |
commentState = ReplacementArray.new | |
nowikiItems = [] | |
generalItems = [] | |
elements = array_merge(["nowiki", "gallery"], array_keys(@mTagHooks)) | |
php_global(:wgRawHtml) | |
(elements << "html") if wgRawHtml | |
(elements << "math") if @mOptions.getUseTeX | |
elements.each do |v| | |
next if (not in_array(v, dontstrip)) | |
elements.delete(k) | |
end | |
matches = [] | |
text = Parser.extractTagsAndParams(elements, text, matches, uniq_prefix) | |
matches.each do |data| | |
element, content, params, tag = *data | |
if render then | |
tagName = strtolower(element) | |
wfProfileIn(("Parser::strip-render-" + tagName)) | |
case tagName | |
when "!--" then | |
if (substr(tag, -3) == "-->") then | |
output = tag | |
else | |
output = ("" + (tag + "-->")) | |
end | |
when "html" then | |
if wgRawHtml then | |
output = content | |
break | |
end | |
output = Xml.escapeTagsOnly(content) | |
when "nowiki" then | |
output = Xml.escapeTagsOnly(content) | |
when "math" then | |
output = wgContLang.armourMath(MathRenderer.renderMath(content)) | |
when "gallery" then | |
output = self.renderImageGallery(content, params) | |
else | |
(if defined?(@mTagHooks[tagName]) then | |
output = call_user_func_array(@mTagHooks[tagName], [content, params, self]) | |
else | |
raise(MWException.new(("Invalid call hook " + element))) | |
end) | |
end | |
wfProfileOut(("Parser::strip-render-" + tagName)) | |
else | |
output = tag | |
end | |
output = state.unstripBoth(output) | |
if ((not stripcomments) and (element == "!--")) then | |
commentState.setPair(marker, output) | |
else | |
if ((element == "html") or (element == "nowiki")) then | |
nowikiItems[marker] = output | |
else | |
generalItems[marker] = output | |
end | |
end | |
end | |
state.nowiki.mergeArray(nowikiItems) | |
state.general.mergeArray(generalItems) | |
text = commentState.replace(text) if (not stripcomments) | |
wfProfileOut("Parser::strip") | |
return text | |
end | |
def unstrip(text, state) | |
return state.unstripGeneral(text) | |
end | |
def unstripNoWiki(text, state) | |
return state.unstripNoWiki(text) | |
end | |
def unstripForHTML(text) | |
return @mStripState.unstripBoth(text) | |
end | |
def insertStripItem(text, state) | |
rnd = (@mUniqPrefix + ("-item" + Parser.getRandomString)) | |
state.general.setPair(rnd, text) | |
return rnd | |
end | |
def tidy(text) | |
php_global(:wgTidyInternal) | |
wrappedtext = ("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"><html><head><title>test</title></head><body>" + (text + "</body></html>")) | |
if wgTidyInternal then | |
correctedtext = Parser.internalTidy(wrappedtext) | |
else | |
correctedtext = Parser.externalTidy(wrappedtext) | |
end | |
if is_null(correctedtext) then | |
wfDebug("Tidy error detected!\n") | |
return (text + "\n<!-- Tidy found serious XHTML errors -->\n") | |
end | |
return correctedtext | |
end | |
def externalTidy(text) | |
(php_global(:wgTidyConf) | |
php_global(:wgTidyBin) | |
php_global(:wgTidyOpts)) | |
fname = "Parser::externalTidy" | |
wfProfileIn(fname) | |
cleansource = "" | |
opts = " -utf8" | |
descriptorspec = { 0 => (["pipe", "r"]), 1 => (["pipe", "w"]), 2 => (["file", "/dev/null", "a"]) } | |
pipes = [] | |
process = proc_open(("" + (wgTidyBin + (" -config " + (wgTidyConf + (" " + (wgTidyOpts + opts)))))), descriptorspec, pipes) | |
if is_resource(process) then | |
fwrite(pipes[0], text) | |
fclose(pipes[0]) | |
while (not feof(pipes[1])) do | |
cleansource = (cleansource + fgets(pipes[1], 1024)) | |
end | |
fclose(pipes[1]) | |
proc_close(process) | |
end | |
wfProfileOut(fname) | |
if ((cleansource == "") and (not (text == ""))) then | |
return nil | |
else | |
return cleansource | |
end | |
end | |
def internalTidy(text) | |
php_global(:wgTidyConf) | |
fname = "Parser::internalTidy" | |
wfProfileIn(fname) | |
tidy_load_config(wgTidyConf) | |
tidy_set_encoding("utf8") | |
tidy_parse_string(text) | |
tidy_clean_repair | |
if (tidy_get_status == 2) then | |
cleansource = nil | |
else | |
cleansource = tidy_get_output | |
end | |
wfProfileOut(fname) | |
return cleansource | |
end | |
def doTableStuff(text) | |
fname = "Parser::doTableStuff" | |
wfProfileIn(fname) | |
lines = explode("\n", text) | |
td_history = [] | |
last_tag_history = [] | |
tr_history = [] | |
tr_attributes = [] | |
has_opened_tr = [] | |
indent_level = 0 | |
lines.each do |line| | |
line = trim(line) | |
next if (line == "") | |
first_character = line[(0..0)] | |
matches = [] | |
if preg_match("/^(:*)\\{\\|(.*)$/", line, matches) then | |
indent_level = strlen(matches[1]) | |
attributes = @mStripState.unstripBoth(matches[2]) | |
attributes = Sanitizer.fixTagAttributes(attributes, "table") | |
lines[key] = (str_repeat("<dl><dd>", indent_level) + ("<table" + (attributes + ">"))) | |
array_push(td_history, false) | |
array_push(last_tag_history, "") | |
array_push(tr_history, false) | |
array_push(tr_attributes, "") | |
array_push(has_opened_tr, false) | |
else | |
if (count(td_history) == 0) then | |
next | |
else | |
if (substr(line, 0, 2) == "|}") then | |
line = ("</table>" + substr(line, 2)) | |
last_tag = array_pop(last_tag_history) | |
line = ("<tr><td></td></tr>" + line) if (not array_pop(has_opened_tr)) | |
line = ("</tr>" + line) if array_pop(tr_history) | |
line = ("</" + (last_tag + (">" + line))) if array_pop(td_history) | |
array_pop(tr_attributes) | |
lines[key] = (line + str_repeat("</dd></dl>", indent_level)) | |
else | |
if (substr(line, 0, 2) == "|-") then | |
line = preg_replace("#^\\|-+#", "", line) | |
attributes = @mStripState.unstripBoth(line) | |
attributes = Sanitizer.fixTagAttributes(attributes, "tr") | |
array_pop(tr_attributes) | |
array_push(tr_attributes, attributes) | |
line = "" | |
last_tag = array_pop(last_tag_history) | |
array_pop(has_opened_tr) | |
array_push(has_opened_tr, true) | |
line = "</tr>" if array_pop(tr_history) | |
line = ("</" + (last_tag + (">" + line))) if array_pop(td_history) | |
lines[key] = line | |
array_push(tr_history, false) | |
array_push(td_history, false) | |
array_push(last_tag_history, "") | |
else | |
if (((first_character == "|") or (first_character == "!")) or (substr(line, 0, 2) == "|+")) then | |
if (substr(line, 0, 2) == "|+") then | |
first_character = "+" | |
line = substr(line, 1) | |
end | |
line = substr(line, 1) | |
line = str_replace("!!", "||", line) if (first_character == "!") | |
cells = StringUtils.explodeMarkup("||", line) | |
lines[key] = "" | |
cells.each do |cell| | |
previous = "" | |
if (not (first_character == "+")) then | |
tr_after = array_pop(tr_attributes) | |
previous = ("<tr" + (tr_after + ">\n")) if (not array_pop(tr_history)) | |
array_push(tr_history, true) | |
array_push(tr_attributes, "") | |
array_pop(has_opened_tr) | |
array_push(has_opened_tr, true) | |
end | |
last_tag = array_pop(last_tag_history) | |
previous = ("</" + (last_tag + (">" + previous))) if array_pop(td_history) | |
if (first_character == "|") then | |
last_tag = "td" | |
else | |
if (first_character == "!") then | |
last_tag = "th" | |
else | |
(first_character == "+") ? (last_tag = "caption") : (last_tag = "") | |
end | |
end | |
array_push(last_tag_history, last_tag) | |
cell_data = explode("|", cell, 2) | |
if (not (strpos(cell_data[0], "[[") == false)) then | |
cell = ("" + (previous + ("<" + (last_tag + (">" + cell))))) | |
else | |
if (count(cell_data) == 1) then | |
cell = ("" + (previous + ("<" + (last_tag + (">" + cell_data[0]))))) | |
else | |
attributes = @mStripState.unstripBoth(cell_data[0]) | |
attributes = Sanitizer.fixTagAttributes(attributes, last_tag) | |
cell = ("" + (previous + ("<" + (last_tag + (attributes + (">" + cell_data[1])))))) | |
end | |
end | |
lines[key] = (lines[key] + cell) | |
array_push(td_history, true) | |
end | |
end | |
end | |
end | |
end | |
end | |
end | |
while (count(td_history) > 0) do | |
(lines << "</td>") if array_pop(td_history) | |
(lines << "</tr>") if array_pop(tr_history) | |
(lines << "<tr><td></td></tr>") if (not array_pop(has_opened_tr)) | |
(lines << "</table>") | |
end | |
output = implode("\n", lines) | |
output = "" if (output == "<table>\n<tr><td></td></tr>\n</table>") | |
wfProfileOut(fname) | |
return output | |
end | |
def internalParse(text) | |
args = [] | |
isMain = true | |
fname = "Parser::internalParse" | |
wfProfileIn(fname) | |
if (not wfRunHooks("ParserBeforeInternalParse", [self, text, @mStripState])) then | |
wfProfileOut(fname) | |
return text | |
end | |
text = strtr(text, "<onlyinclude>" => "", "</onlyinclude>" => "") | |
text = strtr(text, "<noinclude>" => "", "</noinclude>" => "") | |
text = StringUtils.delimiterReplace("<includeonly>", "</includeonly>", "", text) | |
text = Sanitizer.removeHTMLtags(text, [self, "attributeStripCallback"]) | |
text = self.replaceVariables(text, args) | |
wfRunHooks("InternalParseBeforeLinks", [self, text, @mStripState]) | |
text = self.doTableStuff(text) | |
text = preg_replace("/(^|\\n)-----*/", "\\1<hr />", text) | |
text = self.stripToc(text) | |
self.stripNoGallery(text) | |
text = self.doHeadings(text) | |
if @mOptions.getUseDynamicDates then | |
df = DateFormatter.getInstance | |
text = df.reformat(@mOptions.getDateFormat, text) | |
end | |
text = self.doAllQuotes(text) | |
text = self.replaceInternalLinks(text) | |
text = self.replaceExternalLinks(text) | |
text = str_replace((@mUniqPrefix + "NOPARSE"), "", text) | |
text = self.doMagicLinks(text) | |
text = self.formatHeadings(text, isMain) | |
wfProfileOut(fname) | |
return text | |
end | |
def doMagicLinks(text) | |
wfProfileIn("Parser::doMagicLinks") | |
text = preg_replace_callback("!(?: # Start cases\n\t\t\t <a.*?</a> | # Skip link text\n\t\t\t <.*?> | # Skip stuff inside HTML elements\n\t\t\t (?:RFC|PMID)\\s+([0-9]+) | # RFC or PMID, capture number as m[1]\n\t\t\t ISBN\\s+(\\b # ISBN, capture number as m[2]\n (?: 97[89] [\\ \\-]? )? # optional 13-digit ISBN prefix\n (?: [0-9] [\\ \\-]? ){9} # 9 digits with opt. delimiters\n [0-9Xx] # check digit\n \\b)\n\t\t\t)!x", [self, "magicLinkCallback"], text) | |
wfProfileOut("Parser::doMagicLinks") | |
return text | |
end | |
def magicLinkCallback(m) | |
if (substr(m[0], 0, 1) == "<") then | |
return m[0] | |
else | |
if (substr(m[0], 0, 4) == "ISBN") then | |
isbn = m[2] | |
num = strtr(isbn, "-" => "", " " => "", "x" => "X") | |
titleObj = SpecialPage.getTitleFor("Booksources") | |
text = ("<a href=\"" + (titleObj.escapeLocalUrl(("isbn=" + num)) + ("\" class=\"internal\">ISBN " + (isbn + "</a>")))) | |
else | |
if (substr(m[0], 0, 3) == "RFC") then | |
keyword = "RFC" | |
urlmsg = "rfcurl" | |
id = m[1] | |
else | |
if (substr(m[0], 0, 4) == "PMID") then | |
keyword = "PMID" | |
urlmsg = "pubmedurl" | |
id = m[1] | |
else | |
raise(MWException.new(("Parser::magicLinkCallback: unrecognised match type \"" + (substr(m[0], 0, 20) + "\"")))) | |
end | |
end | |
url = wfMsg(urlmsg, id) | |
sk = @mOptions.getSkin | |
la = sk.getExternalLinkAttributes(url, (keyword + id)) | |
text = ("<a href=\"" + (url + ("\"" + (la + (">" + (keyword + (" " + (id + "</a>")))))))) | |
end | |
end | |
return text | |
end | |
def doHeadings(text) | |
fname = "Parser::doHeadings" | |
wfProfileIn(fname) | |
(i = 6 | |
while (i >= 1) do | |
(h = str_repeat("=", i) | |
text = preg_replace(("/^" + (h + ("(.+)" + (h + "\\s*$/m")))), ("<h" + (i + (">\\1</h" + (i + ">\\2")))), text)) | |
i = (i - 1) | |
end) | |
wfProfileOut(fname) | |
return text | |
end | |
def doAllQuotes(text) | |
fname = "Parser::doAllQuotes" | |
wfProfileIn(fname) | |
outtext = "" | |
lines = explode("\n", text) | |
lines.each { |line| outtext = (outtext + (self.doQuotes(line) + "\n")) } | |
outtext = substr(outtext, 0, -1) | |
wfProfileOut(fname) | |
return outtext | |
end | |
def doQuotes(text) | |
arr = preg_split("/(''+)/", text, -1, PREG_SPLIT_DELIM_CAPTURE) | |
if (count(arr) == 1) then | |
return text | |
else | |
i = 0 | |
numbold = 0 | |
numitalics = 0 | |
arr.each do |r| | |
if ((i % 2) == 1) then | |
if (strlen(arr[i]) == 4) then | |
arr[(i - 1)] = (arr[(i - 1)] + "'") | |
arr[i] = "'''" | |
else | |
if (strlen(arr[i]) > 5) then | |
arr[(i - 1)] = (arr[(i - 1)] + str_repeat("'", (strlen(arr[i]) - 5))) | |
arr[i] = "'''''" | |
end | |
end | |
if (strlen(arr[i]) == 2) then | |
numitalics = (numitalics + 1) | |
else | |
if (strlen(arr[i]) == 3) then | |
numbold = (numbold + 1) | |
else | |
if (strlen(arr[i]) == 5) then | |
numitalics = (numitalics + 1) | |
numbold = (numbold + 1) | |
end | |
end | |
end | |
end | |
i = (i + 1) | |
end | |
if (((numbold % 2) == 1) and ((numitalics % 2) == 1)) then | |
i = 0 | |
firstsingleletterword = -1 | |
firstmultiletterword = -1 | |
firstspace = -1 | |
arr.each do |r| | |
if (((i % 2) == 1) and (strlen(r) == 3)) then | |
x1 = substr(arr[(i - 1)], -1) | |
x2 = substr(arr[(i - 1)], -2, 1) | |
if (x1 == " ") then | |
firstspace = i if (firstspace == -1) | |
else | |
if (x2 == " ") then | |
firstsingleletterword = i if (firstsingleletterword == -1) | |
else | |
firstmultiletterword = i if (firstmultiletterword == -1) | |
end | |
end | |
end | |
i = (i + 1) | |
end | |
if (firstsingleletterword > -1) then | |
arr[firstsingleletterword] = "''" | |
arr[(firstsingleletterword - 1)] = (arr[(firstsingleletterword - 1)] + "'") | |
else | |
if (firstmultiletterword > -1) then | |
arr[firstmultiletterword] = "''" | |
arr[(firstmultiletterword - 1)] = (arr[(firstmultiletterword - 1)] + "'") | |
else | |
if (firstspace > -1) then | |
arr[firstspace] = "''" | |
arr[(firstspace - 1)] = (arr[(firstspace - 1)] + "'") | |
end | |
end | |
end | |
end | |
output = "" | |
buffer = "" | |
state = "" | |
i = 0 | |
arr.each do |r| | |
if ((i % 2) == 0) then | |
(state == "both") ? (buffer = (buffer + r)) : (output = (output + r)) | |
else | |
if (strlen(r) == 2) then | |
if (state == "i") then | |
output = (output + "</i>") | |
state = "" | |
else | |
if (state == "bi") then | |
output = (output + "</i>") | |
state = "b" | |
else | |
if (state == "ib") then | |
output = (output + "</b></i><b>") | |
state = "b" | |
else | |
if (state == "both") then | |
output = (output + ("<b><i>" + (buffer + "</i>"))) | |
state = "b" | |
else | |
output = (output + "<i>") | |
state = (state + "i") | |
end | |
end | |
end | |
end | |
else | |
if (strlen(r) == 3) then | |
if (state == "b") then | |
output = (output + "</b>") | |
state = "" | |
else | |
if (state == "bi") then | |
output = (output + "</i></b><i>") | |
state = "i" | |
else | |
if (state == "ib") then | |
output = (output + "</b>") | |
state = "i" | |
else | |
if (state == "both") then | |
output = (output + ("<i><b>" + (buffer + "</b>"))) | |
state = "i" | |
else | |
output = (output + "<b>") | |
state = (state + "b") | |
end | |
end | |
end | |
end | |
else | |
if (strlen(r) == 5) then | |
if (state == "b") then | |
output = (output + "</b><i>") | |
state = "i" | |
else | |
if (state == "i") then | |
output = (output + "</i><b>") | |
state = "b" | |
else | |
if (state == "bi") then | |
output = (output + "</i></b>") | |
state = "" | |
else | |
if (state == "ib") then | |
output = (output + "</b></i>") | |
state = "" | |
else | |
if (state == "both") then | |
output = (output + ("<i><b>" + (buffer + "</b></i>"))) | |
state = "" | |
else | |
buffer = "" | |
state = "both" | |
end | |
end | |
end | |
end | |
end | |
end | |
end | |
end | |
end | |
i = (i + 1) | |
end | |
output = (output + "</b>") if ((state == "b") or (state == "ib")) | |
if (((state == "i") or (state == "bi")) or (state == "ib")) then | |
output = (output + "</i>") | |
end | |
output = (output + "</b>") if (state == "bi") | |
if ((state == "both") and buffer) then | |
output = (output + ("<b><i>" + (buffer + "</i></b>"))) | |
end | |
return output | |
end | |
end | |
def replaceExternalLinks(text) | |
php_global(:wgContLang) | |
fname = "Parser::replaceExternalLinks" | |
wfProfileIn(fname) | |
sk = @mOptions.getSkin | |
bits = preg_split(EXT_LINK_BRACKETED, text, -1, PREG_SPLIT_DELIM_CAPTURE) | |
s = self.replaceFreeExternalLinks(array_shift(bits)) | |
i = 0 | |
while (i < count(bits)) do | |
url = bits[i = (i + 1)] | |
protocol = bits[i = (i + 1)] | |
text = bits[i = (i + 1)] | |
trail = bits[i = (i + 1)] | |
m2 = [] | |
if preg_match("/&(lt|gt);/", url, m2, PREG_OFFSET_CAPTURE) then | |
text = (substr(url, m2[0][1]) + (" " + text)) | |
url = substr(url, 0, m2[0][1]) | |
end | |
img = self.maybeMakeExternalImage(text) | |
text = img if (not (img == false)) | |
dtrail = "" | |
linktype = (text == url) ? ("free") : ("text") | |
if (text == "") then | |
if (not (strpos(wfUrlProtocols, substr(protocol, 0, strpos(protocol, ":"))) == false)) then | |
text = ("[" + ((@mAutonumber = (@mAutonumber + 1)) + "]")) | |
linktype = "autonumber" | |
else | |
text = htmlspecialchars(url) | |
linktype = "free" | |
end | |
else | |
dtrail, trail = *Linker.splitTrail(trail) | |
end | |
text = wgContLang.markNoConversion(text) | |
url = Sanitizer.cleanUrl(url) | |
trail = self.replaceFreeExternalLinks(trail) | |
s = (s + (sk.makeExternalLink(url, text, false, linktype, @mTitle.getNamespace) + (dtrail + trail))) | |
pasteurized = Parser.replaceUnusualEscapes(url) | |
@mOutput.addExternalLink(pasteurized) | |
end | |
wfProfileOut(fname) | |
return s | |
end | |
def replaceFreeExternalLinks(text) | |
php_global(:wgContLang) | |
fname = "Parser::replaceFreeExternalLinks" | |
wfProfileIn(fname) | |
bits = preg_split(("/(\\b(?:" + (wfUrlProtocols + "))/S")), text, -1, PREG_SPLIT_DELIM_CAPTURE) | |
s = array_shift(bits) | |
i = 0 | |
sk = @mOptions.getSkin | |
while (i < count(bits)) do | |
protocol = bits[i = (i + 1)] | |
remainder = bits[i = (i + 1)] | |
m = [] | |
if preg_match(("/^(" + (EXT_LINK_URL_CLASS + "+)(.*)$/s")), remainder, m) then | |
url = (protocol + m[1]) | |
trail = m[2] | |
if ((((strlen(trail) == 0) and defined?(bits[i])) and preg_match(("/^" + (wfUrlProtocols + "$/S")), bits[i])) and preg_match(("/^(" + (EXT_LINK_URL_CLASS + "+)(.*)$/s")), bits[(i + 1)], m)) then | |
url = (url + (bits[i] + m[1])) | |
i = (i + 2) | |
trail = m[2] | |
end | |
m2 = [] | |
if preg_match("/&(lt|gt);/", url, m2, PREG_OFFSET_CAPTURE) then | |
trail = (substr(url, m2[0][1]) + trail) | |
url = substr(url, 0, m2[0][1]) | |
end | |
sep = ",;\\.:!?" | |
sep = (sep + ")") if (strpos(url, "(") == false) | |
numSepChars = strspn(strrev(url), sep) | |
if numSepChars then | |
trail = (substr(url, -numSepChars) + trail) | |
url = substr(url, 0, -numSepChars) | |
end | |
url = Sanitizer.cleanUrl(url) | |
text = self.maybeMakeExternalImage(url) | |
if (text == false) then | |
text = sk.makeExternalLink(url, wgContLang.markNoConversion(url), true, "free", @mTitle.getNamespace) | |
pasteurized = Parser.replaceUnusualEscapes(url) | |
@mOutput.addExternalLink(pasteurized) | |
else | |
pasteurized = Parser.replaceUnusualEscapes(url) | |
@mOutput.addExternalLink(pasteurized) | |
end | |
s = (s + (text + trail)) | |
else | |
s = (s + (protocol + remainder)) | |
end | |
end | |
wfProfileOut(fname) | |
return s | |
end | |
def replaceUnusualEscapes(url) | |
return preg_replace_callback("/%[0-9A-Fa-f]{2}/", ["Parser", "replaceUnusualEscapesCallback"], url) | |
end | |
def replaceUnusualEscapesCallback(matches) | |
char = urldecode(matches[0]) | |
ord = ord(char) | |
if (((ord > 32) and (ord < 127)) and (strpos("<>\"\#{}|\\^~[]`;/?", char) == false)) then | |
return char | |
else | |
return matches[0] | |
end | |
end | |
def maybeMakeExternalImage(url) | |
sk = @mOptions.getSkin | |
imagesfrom = @mOptions.getAllowExternalImagesFrom | |
imagesexception = (not empty(imagesfrom)) | |
text = false | |
if (@mOptions.getAllowExternalImages or (imagesexception and (strpos(url, imagesfrom) == 0))) then | |
if preg_match(EXT_IMAGE_REGEX, url) then | |
text = sk.makeExternalImage(htmlspecialchars(url)) | |
end | |
end | |
return text | |
end | |
def replaceInternalLinks(s) | |
php_global(:wgContLang) | |
php_static_var(:static => (true)) | |
wfProfileIn(fname) | |
wfProfileIn((fname + "-setup")) | |
php_static_var(:static => (true)) | |
tc = (Title.legalChars + "#%") if (not tc) | |
sk = @mOptions.getSkin | |
a = explode("[[", (" " + s)) | |
s = array_shift(a) | |
s = substr(s, 1) | |
php_static_var(:static => (true)) | |
e1 = ("/^([" + (tc + "]+)(?:\\|(.+?))?]](.*)$/sD")) if (not e1) | |
php_static_var(:static => (true)) | |
e1_img = ("/^([" + (tc + "]+)\\|(.*)$/sD")) if (not e1_img) | |
e2 = wfMsgForContent("linkprefix") | |
useLinkPrefixExtension = wgContLang.linkPrefixExtension | |
if is_null(@mTitle) then | |
raise(MWException.new("Parser::replaceInternalLinks: $this->mTitle is null\n")) | |
end | |
nottalk = (not @mTitle.isTalkPage) | |
if useLinkPrefixExtension then | |
m = [] | |
preg_match(e2, s, m) ? (first_prefix = m[2]) : (first_prefix = false) | |
else | |
prefix = "" | |
end | |
if wgContLang.hasVariants then | |
selflink = wgContLang.convertLinkToAllVariants(@mTitle.getPrefixedText) | |
else | |
selflink = [@mTitle.getPrefixedText] | |
end | |
useSubpages = self.areSubpagesAllowed | |
wfProfileOut((fname + "-setup")) | |
(k = 0 | |
while defined?(a[k]) do | |
(line = a[k] | |
if useLinkPrefixExtension then | |
wfProfileIn((fname + "-prefixhandling")) | |
if preg_match(e2, s, m) then | |
prefix = m[2] | |
s = m[1] | |
else | |
prefix = "" | |
end | |
if first_prefix then | |
prefix = first_prefix | |
first_prefix = false | |
end | |
wfProfileOut((fname + "-prefixhandling")) | |
end | |
might_be_img = false | |
wfProfileIn(("" + (fname + "-e1"))) | |
if preg_match(e1, line, m) then | |
text = m[2] | |
if (((not (text == "")) and (substr(m[3], 0, 1) == "]")) and (not (strpos(text, "[") == false))) then | |
text = (text + "]") | |
m[3] = substr(m[3], 1) | |
end | |
if (not (strpos(m[1], "%") == false)) then | |
m[1] = str_replace(["<", ">"], ["<", ">"], urldecode(m[1])) | |
end | |
trail = m[3] | |
else | |
if preg_match(e1_img, line, m) then | |
might_be_img = true | |
text = m[2] | |
m[1] = urldecode(m[1]) if (not (strpos(m[1], "%") == false)) | |
trail = "" | |
else | |
s = (s + (prefix + ("[[" + line))) | |
wfProfileOut(("" + (fname + "-e1"))) | |
next | |
end | |
end | |
wfProfileOut(("" + (fname + "-e1"))) | |
wfProfileIn(("" + (fname + "-misc"))) | |
if preg_match(("/^\\b(?:" + (wfUrlProtocols + ")/")), m[1]) then | |
s = (s + (prefix + ("[[" + line))) | |
next | |
end | |
useSubpages ? (link = self.maybeDoSubpageLink(m[1], text)) : (link = m[1]) | |
noforce = (not (substr(m[1], 0, 1) == ":")) | |
link = substr(link, 1) if (not noforce) | |
wfProfileOut(("" + (fname + "-misc"))) | |
wfProfileIn(("" + (fname + "-title"))) | |
nt = Title.newFromText(@mStripState.unstripNoWiki(link)) | |
if (not nt) then | |
s = (s + (prefix + ("[[" + line))) | |
wfProfileOut(("" + (fname + "-title"))) | |
next | |
end | |
ns = nt.getNamespace | |
iw = nt.getInterWiki | |
wfProfileOut(("" + (fname + "-title"))) | |
if might_be_img then | |
wfProfileIn(("" + (fname + "-might_be_img"))) | |
if ((ns == NS_IMAGE) and noforce) then | |
found = false | |
while defined?(a[(k + 1)]) do | |
spliced = array_splice(a, (k + 1), 1) | |
next_line = array_shift(spliced) | |
m = explode("]]", next_line, 3) | |
if (count(m) == 3) then | |
found = true | |
text = (text + ("[[" + (m[0] + ("]]" + m[1])))) | |
trail = m[2] | |
break | |
else | |
if (count(m) == 2) then | |
text = (text + ("[[" + (m[0] + ("]]" + m[1])))) | |
else | |
text = (text + ("[[" + next_line)) | |
break | |
end | |
end | |
end | |
if (not found) then | |
text = self.replaceInternalLinks(text) | |
s = (s + ("" + (prefix + ("[[" + (link + ("|" + text)))))) | |
wfProfileOut(("" + (fname + "-might_be_img"))) | |
next | |
end | |
else | |
s = (s + ("" + (prefix + ("[[" + (link + ("|" + text)))))) | |
wfProfileOut(("" + (fname + "-might_be_img"))) | |
next | |
end | |
wfProfileOut(("" + (fname + "-might_be_img"))) | |
end | |
wasblank = ("" == text) | |
text = link if wasblank | |
if noforce then | |
wfProfileIn(("" + (fname + "-interwiki"))) | |
if (((iw and @mOptions.getInterwikiMagic) and nottalk) and wgContLang.getLanguageName(iw)) then | |
@mOutput.addLanguageLink(nt.getFullText) | |
s = rtrim((s + prefix)) | |
s = (s + (trim(trail, "\n") == "") ? ("") : ((prefix + trail))) | |
wfProfileOut(("" + (fname + "-interwiki"))) | |
next | |
end | |
wfProfileOut(("" + (fname + "-interwiki"))) | |
if (ns == NS_IMAGE) then | |
wfProfileIn(("" + (fname + "-image"))) | |
if (not wfIsBadImage(nt.getDBkey, @mTitle)) then | |
text = self.replaceExternalLinks(text) | |
text = self.replaceInternalLinks(text) | |
s = (s + (prefix + (self.armorLinks(self.makeImage(nt, text)) + trail))) | |
@mOutput.addImage(nt.getDBkey) | |
wfProfileOut(("" + (fname + "-image"))) | |
next | |
else | |
@mOutput.addImage(nt.getDBkey) | |
end | |
wfProfileOut(("" + (fname + "-image"))) | |
end | |
if (ns == NS_CATEGORY) then | |
wfProfileIn(("" + (fname + "-category"))) | |
s = rtrim((s + "\n")) | |
wasblank ? (sortkey = self.getDefaultSort) : (sortkey = text) | |
sortkey = Sanitizer.decodeCharReferences(sortkey) | |
sortkey = str_replace("\n", "", sortkey) | |
sortkey = wgContLang.convertCategoryKey(sortkey) | |
@mOutput.addCategory(nt.getDBkey, sortkey) | |
s = (s + (trim((prefix + trail), "\n") == "") ? ("") : ((prefix + trail))) | |
wfProfileOut(("" + (fname + "-category"))) | |
next | |
end | |
end | |
if (nt.getFragment == "") then | |
if in_array(nt.getPrefixedText, selflink, true) then | |
s = (s + (prefix + sk.makeSelfLinkObj(nt, text, "", trail))) | |
next | |
end | |
end | |
if (ns == NS_MEDIA) then | |
link = sk.makeMediaLinkObj(nt, text) | |
s = (s + (prefix + (self.armorLinks(link) + trail))) | |
@mOutput.addImage(nt.getDBkey) | |
next | |
else | |
if (ns == NS_SPECIAL) then | |
s = (s + self.makeKnownLinkHolder(nt, text, "", trail, prefix)) | |
next | |
else | |
if (ns == NS_IMAGE) then | |
img = Image.new(nt) | |
if img.exists then | |
s = (s + self.makeKnownLinkHolder(nt, text, "", trail, prefix)) | |
@mOutput.addLink(nt) | |
next | |
end | |
end | |
end | |
end | |
s = (s + self.makeLinkHolder(nt, text, "", trail, prefix))) | |
k = (k + 1) | |
end) | |
wfProfileOut(fname) | |
return s | |
end | |
def makeLinkHolder(nt, text = "", query = "", trail = "", prefix = "") | |
wfProfileIn("Parser::makeLinkHolder") | |
if (not is_object(nt)) then | |
retVal = ("<!-- ERROR -->" + (prefix + (text + trail))) | |
else | |
inside, trail = *Linker.splitTrail(trail) | |
if nt.isExternal then | |
nr = array_push(@mInterwikiLinkHolders["texts"], (prefix + (text + inside))) | |
(@mInterwikiLinkHolders["titles"] << nt) | |
retVal = ("<!--IWLINK " + ((nr - 1) + ("-->" + trail))) | |
else | |
nr = array_push(@mLinkHolders["namespaces"], nt.getNamespace) | |
(@mLinkHolders["dbkeys"] << nt.getDBkey) | |
(@mLinkHolders["queries"] << query) | |
(@mLinkHolders["texts"] << (prefix + (text + inside))) | |
(@mLinkHolders["titles"] << nt) | |
retVal = ("<!--LINK " + ((nr - 1) + ("-->" + trail))) | |
end | |
end | |
wfProfileOut("Parser::makeLinkHolder") | |
return retVal | |
end | |
def makeKnownLinkHolder(nt, text = "", query = "", trail = "", prefix = "") | |
inside, trail = *Linker.splitTrail(trail) | |
sk = @mOptions.getSkin | |
link = sk.makeKnownLinkObj(nt, text, query, inside, prefix) | |
return (self.armorLinks(link) + trail) | |
end | |
def armorLinks(text) | |
return preg_replace(("/\\b(" + (wfUrlProtocols + ")/")), ("" + (@mUniqPrefix + "NOPARSE$1")), text) | |
end | |
def areSubpagesAllowed | |
php_global(:wgNamespacesWithSubpages) | |
return (not empty(wgNamespacesWithSubpages[@mTitle.getNamespace])) | |
end | |
def maybeDoSubpageLink(target, text) | |
fname = "Parser::maybeDoSubpageLink" | |
wfProfileIn(fname) | |
ret = target | |
target = trim(target) | |
if self.areSubpagesAllowed then | |
if ((not (target == "")) and (target[(0..0)] == "/")) then | |
trailingSlashes = preg_match_all("%(/+)$%", target, m) | |
if trailingSlashes then | |
noslash = target = substr(target, 1, -strlen(m[0][0])) | |
else | |
noslash = substr(target, 1) | |
end | |
ret = (@mTitle.getPrefixedText + ("/" + trim(noslash))) | |
text = target if ("" == text) | |
else | |
dotdotcount = 0 | |
nodotdot = target | |
while (strncmp(nodotdot, "../", 3) == 0) do | |
dotdotcount = (dotdotcount + 1) | |
nodotdot = substr(nodotdot, 3) | |
end | |
if (dotdotcount > 0) then | |
exploded = explode("/", @mTitle.GetPrefixedText) | |
if (count(exploded) > dotdotcount) then | |
ret = implode("/", array_slice(exploded, 0, -dotdotcount)) | |
if (substr(nodotdot, -1, 1) == "/") then | |
nodotdot = substr(nodotdot, 0, -1) | |
text = nodotdot if ("" == text) | |
end | |
nodotdot = trim(nodotdot) | |
ret = (ret + ("/" + nodotdot)) if (not (nodotdot == "")) | |
end | |
end | |
end | |
end | |
wfProfileOut(fname) | |
return ret | |
end | |
def closeParagraph | |
result = "" | |
result = ("</" + (@mLastSection + ">\n")) if (not ("" == @mLastSection)) | |
@mInPre = false | |
@mLastSection = "" | |
return result | |
end | |
def getCommon(st1, st2) | |
fl = strlen(st1) | |
shorter = strlen(st2) | |
shorter = fl if (fl < shorter) | |
(i = 0 | |
while (i < shorter) do | |
break if (not (st1[(i..i)] == st2[(i..i)])) | |
i = (i + 1) | |
end) | |
return i | |
end | |
def openList(char) | |
result = self.closeParagraph | |
if ("*" == char) then | |
result = (result + "<ul><li>") | |
else | |
if ("#" == char) then | |
result = (result + "<ol><li>") | |
else | |
if (":" == char) then | |
result = (result + "<dl><dd>") | |
else | |
if (";" == char) then | |
result = (result + "<dl><dt>") | |
@mDTopen = true | |
else | |
result = "<!-- ERR 1 -->" | |
end | |
end | |
end | |
end | |
return result | |
end | |
def nextItem(char) | |
if (("*" == char) or ("#" == char)) then | |
return "</li><li>" | |
else | |
if ((":" == char) or (";" == char)) then | |
close = "</dd>" | |
close = "</dt>" if @mDTopen | |
if (";" == char) then | |
@mDTopen = true | |
return (close + "<dt>") | |
else | |
@mDTopen = false | |
return (close + "<dd>") | |
end | |
end | |
end | |
return "<!-- ERR 2 -->" | |
end | |
def closeList(char) | |
if ("*" == char) then | |
text = "</li></ul>" | |
else | |
if ("#" == char) then | |
text = "</li></ol>" | |
else | |
if (":" == char) then | |
if @mDTopen then | |
@mDTopen = false | |
text = "</dt></dl>" | |
else | |
text = "</dd></dl>" | |
end | |
else | |
return "<!-- ERR 3 -->" | |
end | |
end | |
end | |
return (text + "\n") | |
end | |
def doBlockLevels(text, linestart) | |
fname = "Parser::doBlockLevels" | |
wfProfileIn(fname) | |
textLines = explode("\n", text) | |
lastPrefix = output = "" | |
@mDTopen = inBlockElem = false | |
prefixLength = 0 | |
paragraphStack = false | |
output = (output + array_shift(textLines)) if (not linestart) | |
textLines.each do |oLine| | |
lastPrefixLength = strlen(lastPrefix) | |
preCloseMatch = preg_match("/<\\/pre/i", oLine) | |
preOpenMatch = preg_match("/<pre/i", oLine) | |
if (not @mInPre) then | |
prefixLength = strspn(oLine, "*#:;") | |
pref = substr(oLine, 0, prefixLength) | |
pref2 = str_replace(";", ":", pref) | |
t = substr(oLine, prefixLength) | |
@mInPre = (not empty(preOpenMatch)) | |
else | |
prefixLength = 0 | |
pref = pref2 = "" | |
t = oLine | |
end | |
if (prefixLength and (0 == strcmp(lastPrefix, pref2))) then | |
output = (output + self.nextItem(substr(pref, -1))) | |
paragraphStack = false | |
if (substr(pref, -1) == ";") then | |
term = t2 = "" | |
if (not (self.findColonNoLinks(t, term, t2) == false)) then | |
t = t2 | |
output = (output + (term + self.nextItem(":"))) | |
end | |
end | |
else | |
if (prefixLength or lastPrefixLength) then | |
commonPrefixLength = self.getCommon(pref, lastPrefix) | |
paragraphStack = false | |
while (commonPrefixLength < lastPrefixLength) do | |
output = (output + self.closeList(lastPrefix[((lastPrefixLength - 1)..(lastPrefixLength - 1))])) | |
lastPrefixLength = (lastPrefixLength - 1) | |
end | |
if ((prefixLength <= commonPrefixLength) and (commonPrefixLength > 0)) then | |
output = (output + self.nextItem(pref[((commonPrefixLength - 1)..(commonPrefixLength - 1))])) | |
end | |
while (prefixLength > commonPrefixLength) do | |
char = substr(pref, commonPrefixLength, 1) | |
output = (output + self.openList(char)) | |
if (";" == char) then | |
if (not (self.findColonNoLinks(t, term, t2) == false)) then | |
t = t2 | |
output = (output + (term + self.nextItem(":"))) | |
end | |
end | |
commonPrefixLength = (commonPrefixLength + 1) | |
end | |
lastPrefix = pref2 | |
end | |
end | |
if (0 == prefixLength) then | |
wfProfileIn(("" + (fname + "-paragraph"))) | |
openmatch = preg_match("/(?:<table|<blockquote|<h1|<h2|<h3|<h4|<h5|<h6|<pre|<tr|<p|<ul|<ol|<li|<\\/tr|<\\/td|<\\/th)/iS", t) | |
closematch = preg_match(("/(?:<\\/table|<\\/blockquote|<\\/h1|<\\/h2|<\\/h3|<\\/h4|<\\/h5|<\\/h6|<td|<th|<\\/?div|<hr|<\\/pre|<\\/p|" + (@mUniqPrefix + "-pre|<\\/li|<\\/ul|<\\/ol|<\\/?center)/iS")), t) | |
if (openmatch or closematch) then | |
paragraphStack = false | |
output = (output + self.closeParagraph) | |
@mInPre = true if (preOpenMatch and (not preCloseMatch)) | |
closematch ? (inBlockElem = false) : (inBlockElem = true) | |
else | |
if ((not inBlockElem) and (not @mInPre)) then | |
if ((" " == t[(0..0)]) and ((@mLastSection == "pre") or (not (trim(t) == "")))) then | |
if (not (@mLastSection == "pre")) then | |
paragraphStack = false | |
output = (output + (self.closeParagraph + "<pre>")) | |
@mLastSection = "pre" | |
end | |
t = substr(t, 1) | |
else | |
if ("" == trim(t)) then | |
if paragraphStack then | |
output = (output + (paragraphStack + "<br />")) | |
paragraphStack = false | |
@mLastSection = "p" | |
else | |
if (not (@mLastSection == "p")) then | |
output = (output + self.closeParagraph) | |
@mLastSection = "" | |
paragraphStack = "<p>" | |
else | |
paragraphStack = "</p><p>" | |
end | |
end | |
else | |
if paragraphStack then | |
output = (output + paragraphStack) | |
paragraphStack = false | |
@mLastSection = "p" | |
else | |
if (not (@mLastSection == "p")) then | |
output = (output + (self.closeParagraph + "<p>")) | |
@mLastSection = "p" | |
end | |
end | |
end | |
end | |
end | |
end | |
wfProfileOut(("" + (fname + "-paragraph"))) | |
end | |
@mInPre = false if (preCloseMatch and @mInPre) | |
output = (output + (t + "\n")) if (paragraphStack == false) | |
end | |
while prefixLength do | |
output = (output + self.closeList(pref2[((prefixLength - 1)..(prefixLength - 1))])) | |
prefixLength = (prefixLength - 1) | |
end | |
if (not ("" == @mLastSection)) then | |
output = (output + ("</" + (@mLastSection + ">"))) | |
@mLastSection = "" | |
end | |
wfProfileOut(fname) | |
return output | |
end | |
def findColonNoLinks(str, before, after) | |
fname = "Parser::findColonNoLinks" | |
wfProfileIn(fname) | |
pos = strpos(str, ":") | |
if (pos == false) then | |
wfProfileOut(fname) | |
return false | |
end | |
lt = strpos(str, "<") | |
if ((lt == false) or (lt > pos)) then | |
before = substr(str, 0, pos) | |
after = substr(str, (pos + 1)) | |
wfProfileOut(fname) | |
return pos | |
end | |
state = MW_COLON_STATE_TEXT | |
stack = 0 | |
len = strlen(str) | |
(i = 0 | |
while (i < len) do | |
(c = str[(i..i)] | |
case state | |
when 0 then | |
case c | |
when "<" then | |
state = MW_COLON_STATE_TAGSTART | |
when ":" then | |
if (stack == 0) then | |
before = substr(str, 0, i) | |
after = substr(str, (i + 1)) | |
wfProfileOut(fname) | |
return i | |
end | |
else | |
(colon = strpos(str, ":", i) | |
if (colon == false) then | |
wfProfileOut(fname) | |
return false | |
end | |
lt = strpos(str, "<", i) | |
if (stack == 0) then | |
if ((lt == false) or (colon < lt)) then | |
before = substr(str, 0, colon) | |
after = substr(str, (colon + 1)) | |
wfProfileOut(fname) | |
return i | |
end | |
end | |
Php2Rb.break(2) if (lt == false) | |
i = lt | |
state = MW_COLON_STATE_TAGSTART) | |
end | |
when 1 then | |
case c | |
when ">" then | |
stack = (stack + 1) | |
state = MW_COLON_STATE_TEXT | |
when "/" then | |
state = MW_COLON_STATE_TAGSLASH | |
else | |
# do nothing | |
end | |
when 2 then | |
case c | |
when "/" then | |
state = MW_COLON_STATE_CLOSETAG | |
when "!" then | |
state = MW_COLON_STATE_COMMENT | |
when ">" then | |
state = MW_COLON_STATE_TEXT | |
else | |
(state = MW_COLON_STATE_TAG) | |
end | |
when 3 then | |
if (c == ">") then | |
stack = (stack - 1) | |
if (stack < 0) then | |
wfDebug(("Invalid input in " + (fname + "; too many close tags\n"))) | |
wfProfileOut(fname) | |
return false | |
end | |
state = MW_COLON_STATE_TEXT | |
end | |
when MW_COLON_STATE_TAGSLASH then | |
(c == ">") ? (state = MW_COLON_STATE_TEXT) : (state = MW_COLON_STATE_TAG) | |
when 5 then | |
state = MW_COLON_STATE_COMMENTDASH if (c == "-") | |
when MW_COLON_STATE_COMMENTDASH then | |
if (c == "-") then | |
state = MW_COLON_STATE_COMMENTDASHDASH | |
else | |
state = MW_COLON_STATE_COMMENT | |
end | |
when MW_COLON_STATE_COMMENTDASHDASH then | |
if (c == ">") then | |
state = MW_COLON_STATE_TEXT | |
else | |
state = MW_COLON_STATE_COMMENT | |
end | |
else | |
(raise(MWException.new(("State machine error in " + fname)))) | |
end) | |
i = (i + 1) | |
end) | |
if (stack > 0) then | |
wfDebug(("Invalid input in " + (fname + ("; not enough close tags (stack " + (stack + (", state " + (state + ")\n"))))))) | |
return false | |
end | |
wfProfileOut(fname) | |
return false | |
end | |
def getVariableValue(index) | |
(php_global(:wgContLang) | |
php_global(:wgSitename) | |
php_global(:wgServer) | |
php_global(:wgServerName) | |
php_global(:wgScriptPath)) | |
php_static_var(:static => (true)) | |
if wfRunHooks("ParserGetVariableValueVarCache", [self, varCache]) then | |
return varCache[index] if defined?(varCache[index]) | |
end | |
ts = time | |
wfRunHooks("ParserGetVariableValueTs", [self, ts]) | |
php_global(:wgLocaltimezone) | |
if defined?(wgLocaltimezone) then | |
oldtz = getenv("TZ") | |
putenv(("TZ=" + wgLocaltimezone)) | |
end | |
localTimestamp = date("YmdHis", ts) | |
localMonth = date("m", ts) | |
localMonthName = date("n", ts) | |
localDay = date("j", ts) | |
localDay2 = date("d", ts) | |
localDayOfWeek = date("w", ts) | |
localWeek = date("W", ts) | |
localYear = date("Y", ts) | |
localHour = date("H", ts) | |
putenv(("TZ=" + oldtz)) if defined?(wgLocaltimezone) | |
case index | |
when "currentmonth" then | |
return varCache[index] = wgContLang.formatNum(date("m", ts)) | |
when "currentmonthname" then | |
return varCache[index] = wgContLang.getMonthName(date("n", ts)) | |
when "currentmonthnamegen" then | |
return varCache[index] = wgContLang.getMonthNameGen(date("n", ts)) | |
when "currentmonthabbrev" then | |
return varCache[index] = wgContLang.getMonthAbbreviation(date("n", ts)) | |
when "currentday" then | |
return varCache[index] = wgContLang.formatNum(date("j", ts)) | |
when "currentday2" then | |
return varCache[index] = wgContLang.formatNum(date("d", ts)) | |
when "localmonth" then | |
return varCache[index] = wgContLang.formatNum(localMonth) | |
when "localmonthname" then | |
return varCache[index] = wgContLang.getMonthName(localMonthName) | |
when "localmonthnamegen" then | |
return varCache[index] = wgContLang.getMonthNameGen(localMonthName) | |
when "localmonthabbrev" then | |
return varCache[index] = wgContLang.getMonthAbbreviation(localMonthName) | |
when "localday" then | |
return varCache[index] = wgContLang.formatNum(localDay) | |
when "localday2" then | |
return varCache[index] = wgContLang.formatNum(localDay2) | |
when "pagename" then | |
return @mTitle.getText | |
when "pagenamee" then | |
return @mTitle.getPartialURL | |
when "fullpagename" then | |
return @mTitle.getPrefixedText | |
when "fullpagenamee" then | |
return @mTitle.getPrefixedURL | |
when "subpagename" then | |
return @mTitle.getSubpageText | |
when "subpagenamee" then | |
return @mTitle.getSubpageUrlForm | |
when "basepagename" then | |
return @mTitle.getBaseText | |
when "basepagenamee" then | |
return wfUrlEncode(str_replace(" ", "_", @mTitle.getBaseText)) | |
when "talkpagename" then | |
if @mTitle.canTalk then | |
talkPage = @mTitle.getTalkPage | |
return talkPage.getPrefixedText | |
else | |
return "" | |
end | |
if @mTitle.canTalk then | |
talkPage = @mTitle.getTalkPage | |
return talkPage.getPrefixedUrl | |
else | |
return "" | |
end | |
subjPage = @mTitle.getSubjectPage | |
return subjPage.getPrefixedText | |
when "talkpagenamee" then | |
if @mTitle.canTalk then | |
talkPage = @mTitle.getTalkPage | |
return talkPage.getPrefixedUrl | |
else | |
return "" | |
end | |
subjPage = @mTitle.getSubjectPage | |
return subjPage.getPrefixedText | |
when "subjectpagename" then | |
subjPage = @mTitle.getSubjectPage | |
return subjPage.getPrefixedText | |
when "subjectpagenamee" then | |
subjPage = @mTitle.getSubjectPage | |
return subjPage.getPrefixedUrl | |
when "revisionid" then | |
return @mRevisionId | |
when "revisionday" then | |
return intval(substr(self.getRevisionTimestamp, 6, 2)) | |
when "revisionday2" then | |
return substr(self.getRevisionTimestamp, 6, 2) | |
when "revisionmonth" then | |
return intval(substr(self.getRevisionTimestamp, 4, 2)) | |
when "revisionyear" then | |
return substr(self.getRevisionTimestamp, 0, 4) | |
when "revisiontimestamp" then | |
return self.getRevisionTimestamp | |
when "namespace" then | |
return str_replace("_", " ", wgContLang.getNsText(@mTitle.getNamespace)) | |
when "namespacee" then | |
return wfUrlencode(wgContLang.getNsText(@mTitle.getNamespace)) | |
when "talkspace" then | |
return @mTitle.canTalk ? (str_replace("_", " ", @mTitle.getTalkNsText)) : ("") | |
when "talkspacee" then | |
return @mTitle.canTalk ? (wfUrlencode(@mTitle.getTalkNsText)) : ("") | |
when "subjectspace" then | |
return @mTitle.getSubjectNsText | |
when "subjectspacee" then | |
return wfUrlencode(@mTitle.getSubjectNsText) | |
when "currentdayname" then | |
return varCache[index] = wgContLang.getWeekdayName((date("w", ts) + 1)) | |
when "currentyear" then | |
return varCache[index] = wgContLang.formatNum(date("Y", ts), true) | |
when "currenttime" then | |
return varCache[index] = wgContLang.time(wfTimestamp(TS_MW, ts), false, false) | |
when "currenthour" then | |
return varCache[index] = wgContLang.formatNum(date("H", ts), true) | |
when "currentweek" then | |
return varCache[index] = wgContLang.formatNum(date("W", ts).to_i) | |
when "currentdow" then | |
return varCache[index] = wgContLang.formatNum(date("w", ts)) | |
when "localdayname" then | |
return varCache[index] = wgContLang.getWeekdayName((localDayOfWeek + 1)) | |
when "localyear" then | |
return varCache[index] = wgContLang.formatNum(localYear, true) | |
when "localtime" then | |
return varCache[index] = wgContLang.time(localTimestamp, false, false) | |
when "localhour" then | |
return varCache[index] = wgContLang.formatNum(localHour, true) | |
when "localweek" then | |
return varCache[index] = wgContLang.formatNum(localWeek.to_i) | |
when "localdow" then | |
return varCache[index] = wgContLang.formatNum(localDayOfWeek) | |
when "numberofarticles" then | |
return varCache[index] = wgContLang.formatNum(SiteStats.articles) | |
when "numberoffiles" then | |
return varCache[index] = wgContLang.formatNum(SiteStats.images) | |
when "numberofusers" then | |
return varCache[index] = wgContLang.formatNum(SiteStats.users) | |
when "numberofpages" then | |
return varCache[index] = wgContLang.formatNum(SiteStats.pages) | |
when "numberofadmins" then | |
return varCache[index] = wgContLang.formatNum(SiteStats.admins) | |
when "numberofedits" then | |
return varCache[index] = wgContLang.formatNum(SiteStats.edits) | |
when "currenttimestamp" then | |
return varCache[index] = wfTimestampNow | |
when "localtimestamp" then | |
return varCache[index] = localTimestamp | |
when "currentversion" then | |
return varCache[index] = SpecialVersion.getVersion | |
when "sitename" then | |
return wgSitename | |
when "server" then | |
return wgServer | |
when "servername" then | |
return wgServerName | |
when "scriptpath" then | |
return wgScriptPath | |
when "directionmark" then | |
return wgContLang.getDirMark | |
when "contentlanguage" then | |
php_global(:wgContLanguageCode) | |
return wgContLanguageCode | |
else | |
(ret = nil | |
if wfRunHooks("ParserGetVariableValueSwitch", [self, varCache, index, ret]) then | |
return ret | |
else | |
return nil | |
end) | |
end | |
end | |
def initialiseVariables | |
fname = "Parser::initialiseVariables" | |
wfProfileIn(fname) | |
variableIDs = MagicWord.getVariableIDs | |
@mVariables = [] | |
variableIDs.each do |id| | |
mw = MagicWord.get(id) | |
mw.addToArray(@mVariables, id) | |
end | |
wfProfileOut(fname) | |
end | |
def replace_callback(text, callbacks) | |
wfProfileIn("Parser::replace_callback") | |
openingBraceStack = [] | |
lastOpeningBrace = -1 | |
validOpeningBraces = implode("", array_keys(callbacks)) | |
i = 0 | |
while (i < strlen(text)) do | |
if (lastOpeningBrace == -1) then | |
currentClosing = "" | |
search = validOpeningBraces | |
else | |
currentClosing = openingBraceStack[lastOpeningBrace]["braceEnd"] | |
search = (validOpeningBraces + ("|" + currentClosing)) | |
end | |
rule = nil | |
i = (i + strcspn(text, search, i)) | |
if (i < strlen(text)) then | |
if (text[i] == "|") then | |
found = "pipe" | |
else | |
if (text[i] == currentClosing) then | |
found = "close" | |
else | |
if defined?(callbacks[text[i]]) then | |
found = "open" | |
rule = callbacks[text[i]] | |
else | |
i = (i + 1) | |
next | |
end | |
end | |
end | |
else | |
break | |
end | |
if (found == "open") then | |
piece = { "brace" => (text[i]), "braceEnd" => (rule["end"]), "title" => "", "parts" => (nil) } | |
piece["count"] = strspn(text, piece["brace"], i) | |
piece["startAt"] = piece["partStart"] = (i + piece["count"]) | |
i = (i + piece["count"]) | |
if (piece["count"] >= rule["min"]) then | |
lastOpeningBrace = (lastOpeningBrace + 1) | |
openingBraceStack[lastOpeningBrace] = piece | |
end | |
else | |
if (found == "close") then | |
maxCount = openingBraceStack[lastOpeningBrace]["count"] | |
count = strspn(text, text[i], i, maxCount) | |
matchingCount = 0 | |
matchingCallback = nil | |
cbType = callbacks[openingBraceStack[lastOpeningBrace]["brace"]] | |
if (count > cbType["max"]) then | |
matchingCount = cbType["max"] | |
else | |
matchingCount = count | |
while ((matchingCount > 0) and (not array_key_exists(matchingCount, cbType["cb"]))) do | |
matchingCount = (matchingCount - 1) | |
end | |
end | |
if (matchingCount <= 0) then | |
i = (i + count) | |
next | |
end | |
matchingCallback = cbType["cb"][matchingCount] | |
if (nil == openingBraceStack[lastOpeningBrace]["parts"]) then | |
openingBraceStack[lastOpeningBrace]["title"] = substr(text, openingBraceStack[lastOpeningBrace]["partStart"], (i - openingBraceStack[lastOpeningBrace]["partStart"])) | |
else | |
(openingBraceStack[lastOpeningBrace]["parts"] << substr(text, openingBraceStack[lastOpeningBrace]["partStart"], (i - openingBraceStack[lastOpeningBrace]["partStart"]))) | |
end | |
pieceStart = (openingBraceStack[lastOpeningBrace]["startAt"] - matchingCount) | |
pieceEnd = (i + matchingCount) | |
if is_callable(matchingCallback) then | |
cbArgs = { "text" => (substr(text, pieceStart, (pieceEnd - pieceStart))), "title" => (trim(openingBraceStack[lastOpeningBrace]["title"])), "parts" => (openingBraceStack[lastOpeningBrace]["parts"]), "lineStart" => (((pieceStart > 0) and (text[(pieceStart - 1)] == "\n"))) } | |
replaceWith = call_user_func(matchingCallback, cbArgs) | |
text = (substr(text, 0, pieceStart) + (replaceWith + substr(text, pieceEnd))) | |
i = (pieceStart + strlen(replaceWith)) | |
else | |
i = (i + matchingCount) | |
end | |
piece = { "brace" => (openingBraceStack[lastOpeningBrace]["brace"]), "braceEnd" => (openingBraceStack[lastOpeningBrace]["braceEnd"]), "count" => (openingBraceStack[lastOpeningBrace]["count"]), "title" => "", "parts" => (nil), "startAt" => (openingBraceStack[lastOpeningBrace]["startAt"]) } | |
openingBraceStack[lastOpeningBrace = (lastOpeningBrace - 1)] = nil | |
if (matchingCount < piece["count"]) then | |
piece["count"] = (piece["count"] - matchingCount) | |
piece["startAt"] = (piece["startAt"] - matchingCount) | |
piece["partStart"] = piece["startAt"] | |
currentCbList = callbacks[piece["brace"]]["cb"] | |
while piece["count"] do | |
if array_key_exists(piece["count"], currentCbList) then | |
lastOpeningBrace = (lastOpeningBrace + 1) | |
openingBraceStack[lastOpeningBrace] = piece | |
break | |
end | |
piece["count"] -= 1 | |
end | |
end | |
else | |
if (found == "pipe") then | |
if (nil == openingBraceStack[lastOpeningBrace]["parts"]) then | |
openingBraceStack[lastOpeningBrace]["title"] = substr(text, openingBraceStack[lastOpeningBrace]["partStart"], (i - openingBraceStack[lastOpeningBrace]["partStart"])) | |
openingBraceStack[lastOpeningBrace]["parts"] = [] | |
else | |
(openingBraceStack[lastOpeningBrace]["parts"] << substr(text, openingBraceStack[lastOpeningBrace]["partStart"], (i - openingBraceStack[lastOpeningBrace]["partStart"]))) | |
end | |
openingBraceStack[lastOpeningBrace]["partStart"] = i = (i + 1) | |
end | |
end | |
end | |
end | |
wfProfileOut("Parser::replace_callback") | |
return text | |
end | |
def replaceVariables(text, args = [], argsOnly = false) | |
return text if (strlen(text) > @mOptions.getMaxIncludeSize) | |
fname = "Parser::replaceVariables" | |
wfProfileIn(fname) | |
array_push(@mArgStack, args) | |
braceCallbacks = [] | |
braceCallbacks[2] = [self, "braceSubstitution"] if (not argsOnly) | |
if (not (@mOutputType == OT_MSG)) then | |
braceCallbacks[3] = [self, "argSubstitution"] | |
end | |
if braceCallbacks then | |
callbacks = { "{" => ({ "end" => "}", "cb" => (braceCallbacks), "min" => (argsOnly ? (3) : (2)), "max" => (defined?(braceCallbacks[3]) ? (3) : (2)) }), "[" => ({ "end" => "]", "cb" => ({ 2 => (nil) }), "min" => 2, "max" => 2 }) } | |
text = self.replace_callback(text, callbacks) | |
array_pop(@mArgStack) | |
end | |
wfProfileOut(fname) | |
return text | |
end | |
def variableSubstitution(matches) | |
php_global(:wgContLang) | |
fname = "Parser::variableSubstitution" | |
varname = wgContLang.lc(matches[1]) | |
wfProfileIn(fname) | |
skip = false | |
if (@mOutputType == OT_WIKI) then | |
mwSubst = MagicWord.get("subst") | |
skip = true if (not mwSubst.matchStartAndRemove(varname)) | |
end | |
if ((not skip) and array_key_exists(varname, @mVariables)) then | |
id = @mVariables[varname] | |
mw = MagicWord.get(id) | |
if mw.match(matches[1]) then | |
text = self.getVariableValue(id) | |
@mOutput.mContainsOldMagic=(true) | |
else | |
text = matches[0] | |
end | |
else | |
text = matches[0] | |
end | |
wfProfileOut(fname) | |
return text | |
end | |
def createAssocArgs(args) | |
assocArgs = [] | |
index = 1 | |
args.each do |arg| | |
eqpos = strpos(arg, "=") | |
if (eqpos == false) then | |
assocArgs[index = (index + 1)] = arg | |
else | |
name = trim(substr(arg, 0, eqpos)) | |
value = trim(substr(arg, (eqpos + 1))) | |
value = "" if (value == false) | |
assocArgs[name] = value if (not (name == false)) | |
end | |
end | |
return assocArgs | |
end | |
def braceSubstitution(piece) | |
(php_global(:wgContLang) | |
php_global(:wgLang) | |
php_global(:wgAllowDisplayTitle) | |
php_global(:wgNonincludableNamespaces)) | |
fname = "Parser::braceSubstitution" | |
wfProfileIn(fname) | |
wfProfileIn("Parser::braceSubstitution-setup") | |
found = false | |
nowiki = false | |
noparse = false | |
noargs = false | |
replaceHeadings = false | |
headingOffset = 0 | |
isHTML = false | |
forceRawInterwiki = false | |
title = nil | |
linestart = "" | |
titleText = part1 = piece["title"] | |
if (nil == piece["parts"]) then | |
replaceWith = self.variableSubstitution([piece["text"], piece["title"]]) | |
if (not (replaceWith == piece["text"])) then | |
text = replaceWith | |
found = true | |
noparse = true | |
noargs = true | |
end | |
end | |
args = (nil == piece["parts"]) ? ([]) : (piece["parts"]) | |
wfProfileOut("Parser::braceSubstitution-setup") | |
wfProfileIn("Parser::braceSubstitution-modifiers") | |
if (not found) then | |
mwSubst = MagicWord.get("subst") | |
if mwSubst.matchStartAndRemove(part1).^(@ot["wiki"]) then | |
text = piece["text"] | |
found = true | |
noparse = true | |
noargs = true | |
end | |
end | |
if (not found) then | |
mwMsgnw = MagicWord.get("msgnw") | |
if mwMsgnw.matchStartAndRemove(part1) then | |
nowiki = true | |
else | |
mwMsg = MagicWord.get("msg") | |
mwMsg.matchStartAndRemove(part1) | |
end | |
mwRaw = MagicWord.get("raw") | |
forceRawInterwiki = true if mwRaw.matchStartAndRemove(part1) | |
end | |
wfProfileOut("Parser::braceSubstitution-modifiers") | |
lastPathLevel = @mTemplatePath | |
if (not found) then | |
wfProfileIn("Parser::braceSubstitution-pfunc") | |
colonPos = strpos(part1, ":") | |
if (not (colonPos == false)) then | |
function = substr(part1, 0, colonPos) | |
if defined?(@mFunctionSynonyms[1][function]) then | |
function = @mFunctionSynonyms[1][function] | |
else | |
function = strtolower(function) | |
if defined?(@mFunctionSynonyms[0][function]) then | |
function = @mFunctionSynonyms[0][function] | |
else | |
function = false | |
end | |
end | |
if function then | |
funcArgs = array_map("trim", args) | |
funcArgs = array_merge([self, trim(substr(part1, (colonPos + 1)))], funcArgs) | |
result = call_user_func_array(@mFunctionHooks[function], funcArgs) | |
found = true | |
if is_array(result) then | |
if defined?(result[0]) then | |
text = (linestart + result[0]) | |
result.delete(0) | |
end | |
extract(result) | |
else | |
text = (linestart + result) | |
end | |
end | |
end | |
wfProfileOut("Parser::braceSubstitution-pfunc") | |
end | |
if ((not found) and defined?(@mTemplates[piece["title"]])) then | |
found = true | |
if defined?(@mTemplatePath[part1]) then | |
noparse = true | |
noargs = true | |
found = true | |
text = (linestart + ("[[" + (part1 + "]]<!-- WARNING: template loop detected -->"))) | |
wfDebug(("Parser::braceSubstitution: template loop broken at '" + (part1 + "'\n"))) | |
else | |
text = (linestart + @mTemplates[piece["title"]]) | |
ns = NS_TEMPLATE | |
subpage = "" | |
part1 = self.maybeDoSubpageLink(part1, subpage) | |
ns = @mTitle.getNamespace if (not (subpage == "")) | |
title = Title.newFromText(part1, ns) | |
titleText = title.getPrefixedText | |
replaceHeadings = true | |
end | |
end | |
if (not found) then | |
wfProfileIn("Parser::braceSubstitution-loadtpl") | |
ns = NS_TEMPLATE | |
subpage = "" | |
part1 = self.maybeDoSubpageLink(part1, subpage) | |
ns = @mTitle.getNamespace if (not (subpage == "")) | |
title = Title.newFromText(part1, ns) | |
if (not is_null(title)) then | |
titleText = title.getPrefixedText | |
if (wgContLang.hasVariants and (title.getArticleID == 0)) then | |
wgContLang.findVariantLink(part1, title) | |
end | |
if (not title.isExternal) then | |
if (((title.getNamespace == NS_SPECIAL) and @mOptions.getAllowSpecialInclusion) and @ot["html"]) then | |
text = SpecialPage.capturePath(title) | |
if is_string(text) then | |
found = true | |
noparse = true | |
noargs = true | |
isHTML = true | |
self.disableCache | |
end | |
else | |
if (wgNonincludableNamespaces and in_array(title.getNamespace, wgNonincludableNamespaces)) then | |
found = false | |
wfDebug(("" + (fname + (": template inclusion denied for " + title.getPrefixedDBkey)))) | |
else | |
articleContent = self.fetchTemplate(title) | |
if (not (articleContent == false)) then | |
found = true | |
text = articleContent | |
replaceHeadings = true | |
end | |
end | |
end | |
if ((not found) and (@ot["html"] or @ot["pre"])) then | |
text = ("[[:" + (titleText + "]]")) | |
found = true | |
end | |
else | |
if title.isTrans then | |
if (@ot["html"] and (not forceRawInterwiki)) then | |
text = self.interwikiTransclude(title, "render") | |
isHTML = true | |
noparse = true | |
else | |
text = self.interwikiTransclude(title, "raw") | |
replaceHeadings = true | |
end | |
found = true | |
end | |
end | |
if found then | |
@mTemplates[piece["title"]] = text unless isHTML | |
text = (linestart + text) | |
end | |
end | |
wfProfileOut("Parser::braceSubstitution-loadtpl") | |
end | |
if (found and (not self.incrementIncludeSize("pre-expand", strlen(text)))) then | |
text = (linestart + ("[[" + (titleText + "]]<!-- WARNING: template omitted, pre-expand include size too large -->"))) | |
noparse = true | |
noargs = true | |
end | |
if ((nowiki and found) and (@ot["html"] or @ot["pre"])) then | |
text = wfEscapeWikiText(text) | |
else | |
if ((not @ot["msg"]) and found) then | |
if noargs then | |
assocArgs = [] | |
else | |
assocArgs = self.class.createAssocArgs(args) | |
@mTemplatePath[part1] = 1 | |
end | |
if (not noparse) then | |
if (in_string("<onlyinclude>", text) and in_string("</onlyinclude>", text)) then | |
replacer = OnlyIncludeReplacer.new | |
StringUtils.delimiterReplaceCallback("<onlyinclude>", "</onlyinclude>", [replacer, "replace"], text) | |
text = replacer.output | |
end | |
text = StringUtils.delimiterReplace("<noinclude>", "</noinclude>", "", text) | |
text = strtr(text, "<includeonly>" => "", "</includeonly>" => "") | |
if (@ot["html"] or @ot["pre"]) then | |
text = self.strip(text, @mStripState) | |
if @ot["html"] then | |
text = Sanitizer.removeHTMLtags(text, [self, "replaceVariables"], assocArgs) | |
else | |
if (@ot["pre"] and @mOptions.getRemoveComments) then | |
text = Sanitizer.removeHTMLcomments(text) | |
end | |
end | |
end | |
text = self.replaceVariables(text, assocArgs) | |
if ((not piece["lineStart"]) and preg_match("/^(?:{\\||:|;|#|\\*)/", text)) then | |
text = ("\n" + text) | |
end | |
else | |
text = self.replaceVariables(text, assocArgs, true) if (not noargs) | |
end | |
end | |
end | |
@mTemplatePath = lastPathLevel | |
if (found and (not self.incrementIncludeSize("post-expand", strlen(text)))) then | |
text = (linestart + ("[[" + (titleText + "]]<!-- WARNING: template omitted, post-expand include size too large -->"))) | |
noparse = true | |
noargs = true | |
end | |
if (not found) then | |
wfProfileOut(fname) | |
return piece["text"] | |
else | |
wfProfileIn("Parser::braceSubstitution-placeholders") | |
if isHTML then | |
text = ("\n\n" + self.insertStripItem(text, @mStripState)) | |
else | |
if (((not @ot["wiki"]) and (not @ot["pre"])) and replaceHeadings) then | |
if (not is_null(title)) then | |
encodedname = base64_encode(title.getPrefixedDBkey) | |
else | |
encodedname = base64_encode("") | |
end | |
m = preg_split("/(^={1,6}.*?={1,6}\\s*?$)/m", text, -1, PREG_SPLIT_DELIM_CAPTURE) | |
text = "" | |
nsec = headingOffset | |
(i = 0 | |
while (i < count(m)) do | |
(text = (text + m[i]) | |
next if ((not defined?(m[(i + 1)])) or (m[(i + 1)] == "")) | |
hl = m[(i + 1)] | |
if strstr(hl, "<!--MWTEMPLATESECTION") then | |
text = (text + hl) | |
next | |
end | |
m2 = [] | |
preg_match("/^(={1,6})(.*?)(={1,6})\\s*?$/m", hl, m2) | |
text = (text + (m2[1] + (m2[2] + ("<!--MWTEMPLATESECTION=" + (encodedname + ("&" + (base64_encode(("" + nsec)) + ("-->" + m2[3])))))))) | |
nsec = (nsec + 1)) | |
i = (i + 2) | |
end) | |
end | |
end | |
wfProfileOut("Parser::braceSubstitution-placeholders") | |
end | |
@mTemplatePath = lastPathLevel | |
if (not found) then | |
wfProfileOut(fname) | |
return piece["text"] | |
else | |
wfProfileOut(fname) | |
return text | |
end | |
end | |
def fetchTemplate(title) | |
wfRunHooks("FixTemplateTitle", [title]) | |
text = false | |
(i = 0 | |
while ((i < 2) and is_object(title)) do | |
(rev = Revision.newFromTitle(title) | |
@mOutput.addTemplate(title, title.getArticleID) | |
if rev then | |
text = rev.getText | |
else | |
if (title.getNamespace == NS_MEDIAWIKI) then | |
php_global(:wgLang) | |
message = wgLang.lcfirst(title.getText) | |
text = wfMsgForContentNoTrans(message) | |
if wfEmptyMsg(message, text) then | |
text = false | |
break | |
end | |
else | |
break | |
end | |
end | |
break if (text == false) | |
title = Title.newFromRedirect(text)) | |
i = (i + 1) | |
end) | |
return text | |
end | |
def interwikiTransclude(title, action) | |
php_global(:wgEnableScaryTranscluding) | |
return wfMsg("scarytranscludedisabled") if (not wgEnableScaryTranscluding) | |
url = title.getFullUrl(("action=" + action)) | |
return wfMsg("scarytranscludetoolong") if (strlen(url) > 255) | |
return self.fetchScaryTemplateMaybeFromCache(url) | |
end | |
def fetchScaryTemplateMaybeFromCache(url) | |
php_global(:wgTranscludeCacheExpiry) | |
dbr = wfGetDB(DB_SLAVE) | |
obj = dbr.selectRow("transcache", ["tc_time", "tc_contents"], "tc_url" => (url)) | |
if obj then | |
time = obj.tc_time | |
text = obj.tc_contents | |
return text if (time and (time < (time + wgTranscludeCacheExpiry))) | |
end | |
text = Http.get(url) | |
return wfMsg("scarytranscludefailed", url) if (not text) | |
dbw = wfGetDB(DB_MASTER) | |
dbw.replace("transcache", ["tc_url"], "tc_url" => (url), "tc_time" => (time), "tc_contents" => (text)) | |
return text | |
end | |
def argSubstitution(matches) | |
arg = trim(matches["title"]) | |
text = matches["text"] | |
inputArgs = end(@mArgStack) | |
if array_key_exists(arg, inputArgs) then | |
text = inputArgs[arg] | |
else | |
if ((((@mOutputType == OT_HTML) or (@mOutputType == OT_PREPROCESS)) and (not (nil == matches["parts"]))) and (count(matches["parts"]) > 0)) then | |
text = matches["parts"][0] | |
end | |
end | |
if (not self.incrementIncludeSize("arg", strlen(text))) then | |
text = (matches["text"] + "<!-- WARNING: argument omitted, expansion size too large -->") | |
end | |
return text | |
end | |
def incrementIncludeSize(type, size) | |
if ((@mIncludeSizes[type] + size) > @mOptions.getMaxIncludeSize) then | |
return false | |
else | |
@mIncludeSizes[type] = (@mIncludeSizes[type] + size) | |
return true | |
end | |
end | |
def stripNoGallery(text) | |
mw = MagicWord.get("nogallery") | |
@mOutput.mNoGallery=(mw.matchAndRemove(text)) | |
end | |
def stripToc(text) | |
mw = MagicWord.get("notoc") | |
@mShowToc = false if mw.matchAndRemove(text) | |
mw = MagicWord.get("toc") | |
if mw.match(text) then | |
@mShowToc = true | |
@mForceTocPosition = true | |
text = mw.replace("<!--MWTOC-->", text, 1) | |
text = mw.replace("", text) | |
end | |
return text | |
end | |
def formatHeadings(text, isMain = true) | |
(php_global(:wgMaxTocLevel) | |
php_global(:wgContLang)) | |
doNumberHeadings = @mOptions.getNumberHeadings | |
if (not @mTitle.quickUserCan("edit")) then | |
showEditLink = 0 | |
else | |
showEditLink = @mOptions.getEditSection | |
end | |
esw = MagicWord.get("noeditsection") | |
showEditLink = 0 if esw.matchAndRemove(text) | |
matches = [] | |
numMatches = preg_match_all("/<H(?P<level>[1-6])(?P<attrib>.*?>)(?P<header>.*?)<\\/H[1-6] *>/i", text, matches) | |
enoughToc = (@mShowToc and ((numMatches >= 4) or @mForceTocPosition)) | |
mw = MagicWord.get("newsectionlink") | |
@mOutput.setNewSection(true) if mw.matchAndRemove(text) | |
mw = MagicWord.get("forcetoc") | |
if mw.matchAndRemove(text) then | |
@mShowToc = true | |
enoughToc = true | |
end | |
enoughToc = false if (numMatches < 1) | |
sk = @mOptions.getSkin | |
headlineCount = 0 | |
sectionCount = 0 | |
toc = "" | |
full = "" | |
head = [] | |
sublevelCount = [] | |
levelCount = [] | |
toclevel = 0 | |
level = 0 | |
prevlevel = 0 | |
toclevel = 0 | |
prevtoclevel = 0 | |
matches[3].each do |headline| | |
istemplate = 0 | |
templatetitle = "" | |
templatesection = 0 | |
numbering = "" | |
mat = [] | |
if preg_match("/<!--MWTEMPLATESECTION=([^&]+)&([^_]+)-->/", headline, mat) then | |
istemplate = 1 | |
templatetitle = base64_decode(mat[1]) | |
templatesection = (1 + base64_decode(mat[2]).to_i) | |
headline = preg_replace("/<!--MWTEMPLATESECTION=([^&]+)&([^_]+)-->/", "", headline) | |
end | |
if toclevel then | |
prevlevel = level | |
prevtoclevel = toclevel | |
end | |
level = matches[1][headlineCount] | |
if (doNumberHeadings or enoughToc) then | |
if (level > prevlevel) then | |
toclevel = (toclevel + 1) | |
sublevelCount[toclevel] = 0 | |
toc = (toc + sk.tocIndent) if (toclevel < wgMaxTocLevel) | |
else | |
if ((level < prevlevel) and (toclevel > 1)) then | |
if ((toclevel == 2) and (level <= levelCount[1])) then | |
toclevel = 1 | |
else | |
i = toclevel | |
while (i > 0) do | |
if (levelCount[i] == level) then | |
toclevel = i | |
break | |
else | |
if (levelCount[i] < level) then | |
toclevel = (i + 1) | |
break | |
end | |
end | |
i = (i - 1) | |
end | |
end | |
if (toclevel < wgMaxTocLevel) then | |
toc = (toc + sk.tocUnindent((prevtoclevel - toclevel))) | |
end | |
else | |
toc = (toc + sk.tocLineEnd) if (toclevel < wgMaxTocLevel) | |
end | |
end | |
levelCount[toclevel] = level | |
begin | |
sublevelCount[toclevel] += 1 | |
rescue Exception | |
# do nothing | |
end | |
dot = 0 | |
(i = 1 | |
while (i <= toclevel) do | |
if (not empty(sublevelCount[i])) then | |
numbering = (numbering + ".") if dot | |
numbering = (numbering + wgContLang.formatNum(sublevelCount[i])) | |
dot = 1 | |
end | |
i = (i + 1) | |
end) | |
end | |
canonized_headline = @mStripState.unstripBoth(headline) | |
canonized_headline = preg_replace("/<!--LINK ([0-9]*)-->/e", "$this->mLinkHolders['texts'][$1]", canonized_headline) | |
canonized_headline = preg_replace("/<!--IWLINK ([0-9]*)-->/e", "$this->mInterwikiLinkHolders['texts'][$1]", canonized_headline) | |
canonized_headline = preg_replace("/<.*?>/", "", canonized_headline) | |
tocline = trim(canonized_headline) | |
headline_hint = trim(canonized_headline) | |
canonized_headline = Sanitizer.escapeId(tocline) | |
refers[headlineCount] = canonized_headline | |
if defined?(refers[canonized_headline]) then | |
refers[canonized_headline] += 1 | |
else | |
refers[canonized_headline] = 1 | |
end | |
refcount[headlineCount] = refers[canonized_headline] | |
if (doNumberHeadings and (count(matches[3]) > 1)) then | |
headline = (numbering + (" " + headline)) | |
end | |
anchor = canonized_headline | |
if (refcount[headlineCount] > 1) then | |
anchor = (anchor + ("_" + refcount[headlineCount])) | |
end | |
if (enoughToc and ((not defined?(wgMaxTocLevel)) or (toclevel < wgMaxTocLevel))) then | |
toc = (toc + sk.tocLine(anchor, tocline, numbering, toclevel)) | |
end | |
if (showEditLink and ((not istemplate) or (not (templatetitle == "")))) then | |
if istemplate then | |
editlink = sk.editSectionLinkForOther(templatetitle, templatesection) | |
else | |
editlink = sk.editSectionLink(@mTitle, (sectionCount + 1), headline_hint) | |
end | |
else | |
editlink = "" | |
end | |
head[headlineCount] = sk.makeHeadline(level, matches["attrib"][headlineCount], anchor, headline, editlink) | |
headlineCount = (headlineCount + 1) | |
sectionCount = (sectionCount + 1) if (not istemplate) | |
end | |
if enoughToc then | |
toc = (toc + sk.tocUnindent((toclevel - 1))) if (toclevel < wgMaxTocLevel) | |
toc = sk.tocList(toc) | |
end | |
blocks = preg_split("/<H[1-6].*?>.*?<\\/H[1-6]>/i", text) | |
i = 0 | |
blocks.each do |block| | |
unless (((showEditLink and (headlineCount > 0)) and (i == 0)) and (not (block == "\n"))) then | |
end | |
full = (full + block) | |
if (((enoughToc and (not i)) and isMain) and (not @mForceTocPosition)) then | |
full = (full + toc) | |
end | |
full = (full + head[i]) if (not empty(head[i])) | |
i = (i + 1) | |
end | |
if @mForceTocPosition then | |
return str_replace("<!--MWTOC-->", toc, full) | |
else | |
return full | |
end | |
end | |
def preSaveTransform(text, title, user, options, clearState = true) | |
@mOptions = options | |
@mTitle = title | |
self.setOutputType(OT_WIKI) | |
self.clearState if clearState | |
stripState = StripState.new | |
pairs = { "\r\n" => "\n" } | |
text = str_replace(array_keys(pairs), array_values(pairs), text) | |
text = self.strip(text, stripState, true, ["gallery"]) | |
text = self.pstPass2(text, stripState, user) | |
text = stripState.unstripBoth(text) | |
return text | |
end | |
def pstPass2(text, stripState, user) | |
(php_global(:wgContLang) | |
php_global(:wgLocaltimezone)) | |
if defined?(wgLocaltimezone) then | |
oldtz = getenv("TZ") | |
putenv(("TZ=" + wgLocaltimezone)) | |
end | |
d = (wgContLang.timeanddate(date("YmdHis"), false, false) + (" (" + (date("T") + ")"))) | |
putenv(("TZ=" + oldtz)) if defined?(wgLocaltimezone) | |
text = self.replaceVariables(text) | |
text = self.strip(text, stripState, false, ["gallery"]) | |
sigText = self.getUserSig(user) | |
text = strtr(text, "~~~~~" => (d), "~~~~" => (("" + (sigText + (" " + d)))), "~~~" => (sigText)) | |
php_global(:wgLegalTitleChars) | |
tc = ("[" + (wgLegalTitleChars + "]")) | |
nc = "[ _0-9A-Za-z\\x80-\\xff]" | |
p1 = ("/\\[\\[(:?" + (nc + ("+:|:|)(" + (tc + ("+?)( \\(" + (tc + "+\\))\\|]]/")))))) | |
p3 = ("/\\[\\[(:?" + (nc + ("+:|:|)(" + (tc + ("+?)( \\(" + (tc + ("+\\)|)(, " + (tc + "+|)\\|]]/")))))))) | |
p2 = ("/\\[\\[\\|(" + (tc + "+)]]/")) | |
text = preg_replace(p1, "[[\\1\\2\\3|\\2]]", text) | |
text = preg_replace(p3, "[[\\1\\2\\3\\4|\\2]]", text) | |
t = @mTitle.getText | |
m = [] | |
if preg_match(("/^(" + (nc + ("+:|)" + (tc + ("+?( \\(" + (tc + "+\\))$/")))))), t, m) then | |
text = preg_replace(p2, ("[[" + (m[1] + ("\\1" + (m[2] + "|\\1]]")))), text) | |
else | |
if (preg_match(("/^(" + (nc + ("+:|)" + (tc + ("+?(, " + (tc + "+|)$/")))))), t, m) and (not ("" == ("" + (m[1] + m[2]))))) then | |
text = preg_replace(p2, ("[[" + (m[1] + ("\\1" + (m[2] + "|\\1]]")))), text) | |
else | |
text = preg_replace(p2, "[[\\1]]", text) | |
end | |
end | |
text = rtrim(text) | |
return text | |
end | |
def getUserSig(user) | |
username = user.getName | |
nickname = user.getOption("nickname") | |
nickname = (nickname == "") ? (username) : (nickname) | |
if (not (user.getBoolOption("fancysig") == false)) then | |
if (not (self.validateSig(nickname) == false)) then | |
return self.cleanSig(nickname, true) | |
else | |
nickname = username | |
wfDebug(("Parser::getUserSig: " + (username + " has bad XML tags in signature.\n"))) | |
end | |
end | |
nickname = self.cleanSigInSig(nickname) | |
userpage = user.getUserPage | |
return ("[[" + (userpage.getPrefixedText + ("|" + (wfEscapeWikiText(nickname) + "]]")))) | |
end | |
def validateSig(text) | |
return wfIsWellFormedXmlFragment(text) ? (text) : (false) | |
end | |
def cleanSig(text, parsing = false) | |
php_global(:wgTitle) | |
self.startExternalParse(wgTitle, ParserOptions.new, parsing ? (OT_WIKI) : (OT_MSG)) | |
substWord = MagicWord.get("subst") | |
substRegex = ("/\\{\\{(?!(?:" + (substWord.getBaseRegex + ("))/x" + substWord.getRegexCase))) | |
substText = ("{{" + substWord.getSynonym(0)) | |
text = preg_replace(substRegex, substText, text) | |
text = self.cleanSigInSig(text) | |
text = self.replaceVariables(text) | |
self.clearState | |
return text | |
end | |
def cleanSigInSig(text) | |
text = preg_replace("/~{3,5}/", "", text) | |
return text | |
end | |
def startExternalParse(title, options, outputType, clearState = true) | |
@mTitle = title | |
@mOptions = options | |
self.setOutputType(outputType) | |
self.clearState if clearState | |
end | |
def transformMsg(text, options) | |
php_global(:wgTitle) | |
php_static_var(:static => (true)) | |
fname = "Parser::transformMsg" | |
return text if executing | |
executing = true | |
wfProfileIn(fname) | |
if (wgTitle and (not wgTitle.is_a?(FakeTitle))) then | |
@mTitle = wgTitle | |
else | |
@mTitle = Title.newFromText("msg") | |
end | |
@mOptions = options | |
self.setOutputType(OT_MSG) | |
self.clearState | |
text = self.replaceVariables(text) | |
executing = false | |
wfProfileOut(fname) | |
return text | |
end | |
def setHook(tag, callback) | |
tag = strtolower(tag) | |
oldVal = defined?(@mTagHooks[tag]) ? (@mTagHooks[tag]) : (nil) | |
@mTagHooks[tag] = callback | |
return oldVal | |
end | |
def setFunctionHook(id, callback, flags = 0) | |
oldVal = defined?(@mFunctionHooks[id]) ? (@mFunctionHooks[id]) : (nil) | |
@mFunctionHooks[id] = callback | |
mw = MagicWord.get(id) | |
if (not mw) then | |
raise(MWException.new("Parser::setFunctionHook() expecting a magic word identifier.")) | |
end | |
synonyms = mw.getSynonyms | |
sensitive = intval(mw.isCaseSensitive) | |
synonyms.each do |syn| | |
syn = strtolower(syn) if (not sensitive) | |
syn = ("#" + syn) if (not flags.&(SFH_NO_HASH)) | |
syn = substr(syn, 0, -1) if (substr(syn, -1, 1) == ":") | |
@mFunctionSynonyms[sensitive][syn] = id | |
end | |
return oldVal | |
end | |
def getFunctionHooks | |
return array_keys(@mFunctionHooks) | |
end | |
def replaceLinkHolders(text, options = 0) | |
php_global(:wgUser) | |
php_global(:wgContLang) | |
fname = "Parser::replaceLinkHolders" | |
wfProfileIn(fname) | |
pdbks = [] | |
colours = [] | |
sk = @mOptions.getSkin | |
linkCache = LinkCache.singleton | |
if (not empty(@mLinkHolders["namespaces"])) then | |
wfProfileIn((fname + "-check")) | |
dbr = wfGetDB(DB_SLAVE) | |
page = dbr.tableName("page") | |
threshold = wgUser.getOption("stubthreshold") | |
asort(@mLinkHolders["namespaces"]) | |
query = false | |
current = nil | |
@mLinkHolders["namespaces"].each do |ns| | |
title = @mLinkHolders["titles"][key] | |
next if is_null(title) | |
pdbk = pdbks[key] = title.getPrefixedDBkey | |
if title.isAlwaysKnown then | |
colours[pdbk] = 1 | |
else | |
if (not ((id = linkCache.getGoodLinkID(pdbk)) == 0)) then | |
colours[pdbk] = 1 | |
@mOutput.addLink(title, id) | |
else | |
if linkCache.isBadLink(pdbk) then | |
colours[pdbk] = 0 | |
else | |
if (not defined?(current)) then | |
current = ns | |
query = "SELECT page_id, page_namespace, page_title" | |
query = (query + ", page_len, page_is_redirect") if (threshold > 0) | |
query = (query + (" FROM " + (page + (" WHERE (page_namespace=" + (ns + " AND page_title IN("))))) | |
else | |
if (not (current == ns)) then | |
current = ns | |
query = (query + (")) OR (page_namespace=" + (ns + " AND page_title IN("))) | |
else | |
query = (query + ", ") | |
end | |
end | |
query = (query + dbr.addQuotes(@mLinkHolders["dbkeys"][key])) | |
end | |
end | |
end | |
end | |
if query then | |
query = (query + "))") | |
query = (query + " FOR UPDATE") if options.&(RLH_FOR_UPDATE) | |
res = dbr.query(query, fname) | |
while s = dbr.fetchObject(res) do | |
title = Title.makeTitle(s.page_namespace, s.page_title) | |
pdbk = title.getPrefixedDBkey | |
linkCache.addGoodLinkObj(s.page_id, title) | |
@mOutput.addLink(title, s.page_id) | |
if (threshold > 0) then | |
size = s.page_len | |
if ((s.page_is_redirect or (not (s.page_namespace == 0))) or (size >= threshold)) then | |
colours[pdbk] = 1 | |
else | |
colours[pdbk] = 2 | |
end | |
else | |
colours[pdbk] = 1 | |
end | |
end | |
end | |
wfProfileOut((fname + "-check")) | |
if wgContLang.hasVariants then | |
linkBatch = LinkBatch.new | |
variantMap = [] | |
categoryMap = [] | |
varCategories = [] | |
categories = @mOutput.getCategoryLinks | |
@mLinkHolders["namespaces"].each do |ns| | |
title = @mLinkHolders["titles"][key] | |
next if is_null(title) | |
pdbk = title.getPrefixedDBkey | |
titleText = title.getText | |
allTextVariants = wgContLang.convertLinkToAllVariants(titleText) | |
if (not defined?(colours[pdbk])) then | |
allTextVariants.each do |textVariant| | |
if (not (textVariant == titleText)) then | |
variantTitle = Title.makeTitle(ns, textVariant) | |
next if is_null(variantTitle) | |
linkBatch.addObj(variantTitle) | |
(variantMap[variantTitle.getPrefixedDBkey] << key) | |
end | |
end | |
end | |
end | |
categories.each do |category| | |
variants = wgContLang.convertLinkToAllVariants(category) | |
variants.each do |variant| | |
if (not (variant == category)) then | |
variantTitle = Title.newFromDBkey(Title.makeName(NS_CATEGORY, variant)) | |
next if is_null(variantTitle) | |
linkBatch.addObj(variantTitle) | |
categoryMap[variant] = category | |
end | |
end | |
end | |
if (not linkBatch.isEmpty) then | |
titleClause = linkBatch.constructSet("page", dbr) | |
variantQuery = "SELECT page_id, page_namespace, page_title" | |
if (threshold > 0) then | |
variantQuery = (variantQuery + ", page_len, page_is_redirect") | |
end | |
variantQuery = (variantQuery + (" FROM " + (page + (" WHERE " + titleClause)))) | |
variantQuery = (variantQuery + " FOR UPDATE") if options.&(RLH_FOR_UPDATE) | |
varRes = dbr.query(variantQuery, fname) | |
while s = dbr.fetchObject(varRes) do | |
variantTitle = Title.makeTitle(s.page_namespace, s.page_title) | |
varPdbk = variantTitle.getPrefixedDBkey | |
vardbk = variantTitle.getDBkey | |
holderKeys = [] | |
if defined?(variantMap[varPdbk]) then | |
holderKeys = variantMap[varPdbk] | |
linkCache.addGoodLinkObj(s.page_id, variantTitle) | |
@mOutput.addLink(variantTitle, s.page_id) | |
end | |
holderKeys.each do |key| | |
title = @mLinkHolders["titles"][key] | |
next if is_null(title) | |
pdbk = title.getPrefixedDBkey | |
if (not defined?(colours[pdbk])) then | |
@mLinkHolders["titles"][key] = variantTitle | |
@mLinkHolders["dbkeys"][key] = variantTitle.getDBkey | |
pdbks[key] = varPdbk | |
if (threshold > 0) then | |
size = s.page_len | |
if ((s.page_is_redirect or (not (s.page_namespace == 0))) or (size >= threshold)) then | |
colours[varPdbk] = 1 | |
else | |
colours[varPdbk] = 2 | |
end | |
else | |
colours[varPdbk] = 1 | |
end | |
end | |
end | |
if defined?(categoryMap[vardbk]) then | |
oldkey = categoryMap[vardbk] | |
varCategories[oldkey] = vardbk if (not (oldkey == vardbk)) | |
end | |
end | |
if (count(varCategories) > 0) then | |
newCats = [] | |
originalCats = @mOutput.getCategories | |
originalCats.each do |sortkey| | |
if array_key_exists(cat, varCategories) then | |
newCats[varCategories[cat]] = sortkey | |
else | |
newCats[cat] = sortkey | |
end | |
end | |
@mOutput.setCategoryLinks(newCats) | |
end | |
end | |
end | |
wfProfileIn((fname + "-construct")) | |
replacePairs = [] | |
@mLinkHolders["namespaces"].each do |ns| | |
pdbk = pdbks[key] | |
searchkey = ("<!--LINK " + (key + "-->")) | |
title = @mLinkHolders["titles"][key] | |
if empty(colours[pdbk]) then | |
linkCache.addBadLinkObj(title) | |
colours[pdbk] = 0 | |
@mOutput.addLink(title, 0) | |
replacePairs[searchkey] = sk.makeBrokenLinkObj(title, @mLinkHolders["texts"][key], @mLinkHolders["queries"][key]) | |
else | |
if (colours[pdbk] == 1) then | |
replacePairs[searchkey] = sk.makeKnownLinkObj(title, @mLinkHolders["texts"][key], @mLinkHolders["queries"][key]) | |
else | |
if (colours[pdbk] == 2) then | |
replacePairs[searchkey] = sk.makeStubLinkObj(title, @mLinkHolders["texts"][key], @mLinkHolders["queries"][key]) | |
end | |
end | |
end | |
end | |
replacer = HashtableReplacer.new(replacePairs, 1) | |
wfProfileOut((fname + "-construct")) | |
wfProfileIn((fname + "-replace")) | |
text = preg_replace_callback("/(<!--LINK .*?-->)/", replacer.cb, text) | |
wfProfileOut((fname + "-replace")) | |
end | |
if (not empty(@mInterwikiLinkHolders["texts"])) then | |
wfProfileIn((fname + "-interwiki")) | |
replacePairs = [] | |
@mInterwikiLinkHolders["texts"].each do |link| | |
title = @mInterwikiLinkHolders["titles"][key] | |
replacePairs[key] = sk.makeLinkObj(title, link) | |
end | |
replacer = HashtableReplacer.new(replacePairs, 1) | |
text = preg_replace_callback("/<!--IWLINK (.*?)-->/", replacer.cb, text) | |
wfProfileOut((fname + "-interwiki")) | |
end | |
wfProfileOut(fname) | |
return colours | |
end | |
def replaceLinkHoldersText(text) | |
fname = "Parser::replaceLinkHoldersText" | |
wfProfileIn(fname) | |
text = preg_replace_callback("/<!--(LINK|IWLINK) (.*?)-->/", [self, "replaceLinkHoldersTextCallback"], text) | |
wfProfileOut(fname) | |
return text | |
end | |
def replaceLinkHoldersTextCallback(matches) | |
type = matches[1] | |
key = matches[2] | |
if (type == "LINK") then | |
return @mLinkHolders["texts"][key] if defined?(@mLinkHolders["texts"][key]) | |
else | |
if (type == "IWLINK") then | |
if defined?(@mInterwikiLinkHolders["texts"][key]) then | |
return @mInterwikiLinkHolders["texts"][key] | |
end | |
end | |
end | |
return matches[0] | |
end | |
def renderPreTag(text, attribs) | |
content = StringUtils.delimiterReplace("<nowiki>", "</nowiki>", "$1", text, "i") | |
attribs = Sanitizer.validateTagAttributes(attribs, "pre") | |
return (wfOpenElement("pre", attribs) + (Xml.escapeTagsOnly(content) + "</pre>")) | |
end | |
def renderImageGallery(text, params) | |
ig = ImageGallery.new | |
ig.setContextTitle(@mTitle) | |
ig.setShowBytes(false) | |
ig.setShowFilename(false) | |
ig.setParsing | |
ig.useSkin(@mOptions.getSkin) | |
if defined?(params["caption"]) then | |
caption = params["caption"] | |
caption = htmlspecialchars(caption) | |
caption = self.replaceInternalLinks(caption) | |
ig.setCaptionHtml(caption) | |
end | |
ig.setPerRow(params["perrow"]) if defined?(params["perrow"]) | |
ig.setWidths(params["widths"]) if defined?(params["widths"]) | |
ig.setHeights(params["heights"]) if defined?(params["heights"]) | |
lines = explode("\n", text) | |
lines.each do |line| | |
matches = [] | |
preg_match("/^([^|]+)(\\|(.*))?$/", line, matches) | |
next if (count(matches) == 0) | |
tp = Title.newFromText(matches[1]) | |
nt = tp | |
next if is_null(nt) | |
defined?(matches[3]) ? (label = matches[3]) : (label = "") | |
pout = self.parse(label, @mTitle, @mOptions, false, false) | |
html = pout.getText | |
ig.add(Image.new(nt), html) | |
@mOutput.addImage(nt.getDBkey) if (nt.getNamespace == NS_IMAGE) | |
end | |
return ig.toHTML | |
end | |
def makeImage(nt, options) | |
part = array_map("trim", explode("|", options)) | |
mwAlign = [] | |
alignments = ["left", "right", "center", "none", "baseline", "sub", "super", "top", "text-top", "middle", "bottom", "text-bottom"] | |
alignments.each do |alignment| | |
mwAlign[alignment] = MagicWord.get(("img_" + alignment)) | |
end | |
mwThumb = MagicWord.get("img_thumbnail") | |
mwManualThumb = MagicWord.get("img_manualthumb") | |
mwWidth = MagicWord.get("img_width") | |
mwFramed = MagicWord.get("img_framed") | |
mwPage = MagicWord.get("img_page") | |
caption = "" | |
params = [] | |
framed = thumb = false | |
manual_thumb = "" | |
align = valign = "" | |
sk = @mOptions.getSkin | |
Php2Rb.foreach(part) do |val| | |
if (not is_null(mwThumb.matchVariableStartToEnd(val))) then | |
thumb = true | |
else | |
if (not is_null(match = mwManualThumb.matchVariableStartToEnd(val))) then | |
thumb = true | |
manual_thumb = match | |
else | |
Php2Rb.foreach(alignments) do |alignment| | |
if (not is_null(mwAlign[alignment].matchVariableStartToEnd(val))) then | |
case alignment | |
when "left", "right", "center", "none" then | |
align = alignment | |
else | |
(valign = alignment) | |
end | |
Php2Rb.continue(2) | |
end | |
end | |
if (not is_null(match = mwPage.matchVariableStartToEnd(val))) then | |
params["page"] = match | |
else | |
if ((not defined?(params["width"])) and (not is_null(match = mwWidth.matchVariableStartToEnd(val)))) then | |
wfDebug(("img_width match: " + (match + "\n"))) | |
m = [] | |
if preg_match("/^([0-9]*)x([0-9]*)$/", match, m) then | |
params["width"] = intval(m[1]) | |
params["height"] = intval(m[2]) | |
else | |
params["width"] = intval(match) | |
end | |
else | |
if (not is_null(mwFramed.matchVariableStartToEnd(val))) then | |
framed = true | |
else | |
caption = val | |
end | |
end | |
end | |
end | |
end | |
end | |
alt = self.replaceLinkHoldersText(caption) | |
alt = @mStripState.unstripBoth(alt) | |
alt = Sanitizer.stripAllTags(alt) | |
return sk.makeImageLinkObj(nt, caption, alt, align, params, framed, thumb, manual_thumb, valign) | |
end | |
def disableCache | |
wfDebug("Parser output marked as uncacheable.\n") | |
@mOutput.mCacheTime=(-1) | |
end | |
def attributeStripCallback(text, args) | |
text = self.replaceVariables(text, args) | |
text = @mStripState.unstripBoth(text) | |
return text | |
end | |
def Title(x = nil) | |
return wfSetVar(@mTitle, x) | |
end | |
def Options(x = nil) | |
return wfSetVar(@mOptions, x) | |
end | |
def OutputType(x = nil) | |
return wfSetVar(@mOutputType, x) | |
end | |
def getTags | |
return array_keys(@mTagHooks) | |
end | |
def extractSections(text, section, mode, newtext = "") | |
stripState = StripState.new | |
oldOutputType = @mOutputType | |
oldOptions = @mOptions | |
@mOptions = ParserOptions.new | |
self.setOutputType(OT_WIKI) | |
striptext = self.strip(text, stripState, true) | |
self.setOutputType(oldOutputType) | |
@mOptions = oldOptions | |
uniq = preg_quote(self.uniqPrefix, "/") | |
comment = ("(?:" + (uniq + "-!--.*?QINU)")) | |
secs = preg_split(("/\n\t\t\t(\n\t\t\t\t^\n\t\t\t\t(?:" + (comment + ("|<\\/?noinclude>)* # Initial comments will be stripped\n\t\t\t\t(=+) # Should this be limited to 6?\n\t\t\t\t.+? # Section title...\n\t\t\t\t\\2 # Ending = count must match start\n\t\t\t\t(?:" + (comment + "|<\\/?noinclude>|[ \\t]+)* # Trailing whitespace ok\n\t\t\t\t$\n\t\t\t|\n\t\t\t\t<h([1-6])\\b.*?>\n\t\t\t\t.*?\n\t\t\t\t<\\/h\\3\\s*>\n\t\t\t)\n\t\t\t/mix")))), striptext, -1, PREG_SPLIT_DELIM_CAPTURE) | |
if (mode == "get") then | |
(section == 0) ? (rv = secs[0]) : (rv = newtext) | |
else | |
if (mode == "replace") then | |
if (section == 0) then | |
rv = (newtext + "\n\n") | |
remainder = true | |
else | |
rv = secs[0] | |
remainder = false | |
end | |
end | |
end | |
count = 0 | |
sectionLevel = 0 | |
(index = 1 | |
while (index < count(secs)) do | |
(headerLine = secs[index = (index + 1)] | |
if secs[index] then | |
headerLevel = strlen(secs[index = (index + 1)]) | |
else | |
index = (index + 1) | |
headerLevel = intval(secs[index = (index + 1)]) | |
end | |
content = secs[index = (index + 1)] | |
count = (count + 1) | |
if (mode == "get") then | |
if (count == section) then | |
rv = (headerLine + content) | |
sectionLevel = headerLevel | |
else | |
if (count > section) then | |
if (sectionLevel and (headerLevel > sectionLevel)) then | |
rv = (rv + (headerLine + content)) | |
else | |
break | |
end | |
end | |
end | |
else | |
if (mode == "replace") then | |
if (count < section) then | |
rv = (rv + (headerLine + content)) | |
else | |
if (count == section) then | |
rv = (rv + (newtext + "\n\n")) | |
sectionLevel = headerLevel | |
else | |
if (count > section) then | |
remainder = true if (headerLevel <= sectionLevel) | |
rv = (rv + (headerLine + content)) if remainder | |
end | |
end | |
end | |
end | |
end) | |
# do nothing | |
end) | |
rv = trim(stripState.unstripBoth(rv)) if is_string(rv) | |
return rv | |
end | |
def getSection(text, section, deftext = "") | |
return self.extractSections(text, section, "get", deftext) | |
end | |
def replaceSection(oldtext, section, text) | |
return self.extractSections(oldtext, section, "replace", text) | |
end | |
def getRevisionTimestamp | |
if is_null(@mRevisionTimestamp) then | |
wfProfileIn("Parser::getRevisionTimestamp") | |
php_global(:wgContLang) | |
dbr = wfGetDB(DB_SLAVE) | |
timestamp = dbr.selectField("revision", "rev_timestamp", "rev_id" => (@mRevisionId), "Parser::getRevisionTimestamp") | |
timestamp = wfTimestamp(TS_MW, timestamp) | |
@mRevisionTimestamp = wgContLang.userAdjust(timestamp, "") | |
wfProfileOut("Parser::getRevisionTimestamp") | |
end | |
return @mRevisionTimestamp | |
end | |
def setDefaultSort(sort) | |
@mDefaultSort = sort | |
end | |
def getDefaultSort | |
if (not (@mDefaultSort == false)) then | |
return @mDefaultSort | |
else | |
return if (@mTitle.getNamespace == NS_CATEGORY) then | |
@mTitle.getText | |
else | |
@mTitle.getPrefixedText | |
end | |
end | |
end | |
end | |
class OnlyIncludeReplacer | |
attr_accessor(:output) | |
def replace(matches) | |
if (substr(matches[1], -1) == "\n") then | |
@output = (@output + substr(matches[1], 0, -1)) | |
else | |
@output = (@output + matches[1]) | |
end | |
end | |
end | |
class StripState | |
attr_accessor(:general) | |
attr_accessor(:nowiki) | |
def __construct | |
@general = ReplacementArray.new | |
@nowiki = ReplacementArray.new | |
end | |
def unstripGeneral(text) | |
wfProfileIn("StripState::unstripGeneral") | |
text = @general.replace(text) | |
wfProfileOut("StripState::unstripGeneral") | |
return text | |
end | |
def unstripNoWiki(text) | |
wfProfileIn("StripState::unstripNoWiki") | |
text = @nowiki.replace(text) | |
wfProfileOut("StripState::unstripNoWiki") | |
return text | |
end | |
def unstripBoth(text) | |
wfProfileIn("StripState::unstripBoth") | |
text = @general.replace(text) | |
text = @nowiki.replace(text) | |
wfProfileOut("StripState::unstripBoth") | |
return text | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment