利用者:Hatukanezumi/Anansi.js
表示
お知らせ: 保存した後、ブラウザのキャッシュをクリアしてページを再読み込みする必要があります。
多くの Windows や Linux のブラウザ
- Ctrl を押しながら F5 を押す。
Mac における Safari
Mac における Chrome や Firefox
- ⌘ Cmd と ⇧ Shift を押しながら R を押す。
詳細についてはWikipedia:キャッシュを消すをご覧ください。
/*
----
[[Image:Disambig.svg|25px]]
If you wish to know about Anansi in the real world, a spider, human or
both of them, see [[w:en:Anansi]].
----
Anansi - proofreading support script for Mediawiki.
Version: 0.00-alpha; it may not work as smart as you expect. :-/
== Requirements ==
* Mediawiki 1.13alpha (wikibits js 160) or later
* JavaScript 1.5 or later (apparently compliant with ECMA-262 3rd ed.)
* CSS level 2 (optionally level 2 rev. 1 features are used)
* DOM level 2
* Optional features:
** Transparency effect: Internet Explorer >=6?, CSS2 (Firefox, Safari, Opera)
** Pointing lines: SVG Tiny (Firefox >= 2?, Opera >= 9?, Safari >=3) or VML (IE >=6?)
== How to use Anansi ==
1. Add following line to your user script, [[User:YourName/monobook.js]] etc.:
importScript("User:Hatukanezumi/Anansi.js");
2. Enjoy!
== Configuring Anansi ==
Edit your user page [[User:YourName/AnansiConfig.js]]
(you might create this page at first time).
''Description of configuration options will be written on
[[User:Hatukanezumi/Help:Anansi]]...''
== Code ==
*/
/*
* Anansi
*
* Main Processes.
*
* Properties:
* config: AnansiConfig object.
* util: AnansiUtil object.
*
* Methods:
* onloadHook(): body.onload hook function.
* registerRule(definition)
* disableRule(id)
* enableRule(id)
* register/disable/enable rules.
*
* Files:
* Style Sheet: Configured by anansi.config.stylesheet.
* Default styles are defined by "User:Hatukanezumi/Anansi.css".
* Config file: "User:''YourUserName''/AnansiConfig.js".
* Note that camelcase is used for this page name.
* Default Rules: "User:Hatukanezumi/AnansiRules.js".
*
*/
function Anansi()
{
/****
**** Configurations and object-global variables.
****/
/***
*** Constants
***/
var DEFAULT_STYLESHEET = "User:Hatukanezumi/Anansi.css";
var VMLNS = "urn:schemas-microsoft-com:vml";
var VMLRULE = "behavior: url(#default#VML); display: inline-block;";
var SVGNS = "http://www.w3.org/2000/svg";
var SVGVER = 1.1;
var ICON_QUESTION = "http://upload.wikimedia.org/wikipedia/commons/thumb" +
"/2/24/Gtk-dialog-question.svg/16px-Gtk-dialog-question.svg.png";
var ICON_CLOSE = "http://upload.wikimedia.org/wikipedia/commons/thumb" +
"/3/35/Button_normal.svg/14px-Button_normal.svg.png";
var FONTWEIGHT_EMPH = "bold";
var STROKEWIDTH_NORMAL = "1";
var STROKEWIDTH_EMPH = "3";
var LOGO_ENABLED = "http://upload.wikimedia.org/wikipedia/ja" +
"/2/2d/Anansi_logo_spider_enabled_small.png";
var LOGO_ENABLED_ALT = "アナンシ有効";
var LOGO_ENABLED_TITLE = "アナンシ有効。クリックすると無効にします。";
var LOGO_DISABLED = "http://upload.wikimedia.org/wikipedia/ja" +
"/9/9a/Anansi_logo_spider_disabled_small.png";
var LOGO_DISABLED_ALT = "アナンシ無効";
var LOGO_DISABLED_TITLE = "アナンシ無効。クリックすると有効にします。";
/***
*** AnansiConfig
***
*
* Configuration variables.
*/
function AnansiConfig()
{
this.debuglevel = 1; // debug level.
this.disabledpages = null; // disable Anansi on specific page(s).
this.enabled = true; // enable Anansi.
this.lineshape = "polyline"; // line style.
this.namespaces = [0, 1, // Namespaces where Anansi is available.
2, 3,
4, 5,
6, 7,
8, 9,
10, 11,
12, 13,
14, 15
];
this.stylesheet = DEFAULT_STYLESHEET; // location of stylesheet.
}
/**
** config
**/
this.config = new AnansiConfig();
var config = this.config;
/***
*** AnansiUtil
***
*
* Placeholder for user-side utilities.
*/
function AnansiUtil()
{
}
/**
** util
**/
this.util = new AnansiUtil();
/***
*** blocks
***/
var blocks = new Array();
/***
*** index & seq
***/
var index = 0;
var seq = 0;
/***
*** rules
***/
var rules = new Array();
/***
*** logo
***/
var anansiLogo = null;
/***
*** AnansiPlatform
***
*
* Detect platform of client.
*/
function AnansiPlatform()
{
this.domLevel = 0;
this.graphicFormat = null;
this.minVersion = 0;
this.name = null;
this.supported = false;
this.ua = null;
// get name of user-agent.
if (navigator && navigator.userAgent)
this.ua = navigator.userAgent;
// check features.
if (document.createElementNS)
this.domLevel = 2;
else if (document.getElementById)
this.domLevel = 1;
// detect user-agent and its version.
if (window.opera) { // Opera
this.name = "Opera";
// cf. [http://www.howtocreate.co.uk/operaStuff/operaObject.html The mysterious Opera object].
if (typeof window.opera.version == "function") { // >= 7.6
var g = /^[0-9]+(\.[0-9]+)?/.exec(window.opera.version());
if (g)
this.minVersion = eval(g[0]);
else
this.minVersion = 7.6;
}
else if (typeof window.opera.buildNumber == "function") {
var bn = window.opera.buildNumber('inconspicuous');
if (0 < bn.indexOf("as smart as"))
this.minVersion = 6.0;
else if (bn)
this.minVersion = 7.0;
}
}
else if (document.all) { // Trident (or Tasman)
this.name = "MSIE";
var vernum = null;
/*@cc_on
@if (@_jscript)
if (5.7 <= @_jscript_version)
vernum = 7.0;
else if (5.6 <= @_jscript_version)
vernum = 6.0;
else if (5.5 <= @_jscript_version)
vernum = 5.5;
else if (5.1 <= @_jscript_version)
vernum = 5.01;
else if (5.0 <= @_jscript_version)
vernum = 5.0;
else
vernum = 4.0;
@end @*/
if (vernum)
this.minVersion = vernum;
}
else if (this.ua && 0 < this.ua.indexOf(" AppleWebKit/")) { // WebKit
this.name = "WebKit";
var g;
if (g = / AppleWebKit\/([0-9]+)(\.([0-9]+))?/.exec(this.ua)) {
this.minVersion = eval(g[1]);
if (g[3])
this.minVersion += eval(g[3]) / 100.0;
}
}
else if (this.ua && 0 < this.ua.indexOf("Gecko/")) { // Gecko
this.name = "Gecko";
var g;
if (g = /rv:([01]\.[0-9]+)/.exec(this.ua))
this.minVersion = eval(g[1]);
}
// determin graphic format
if (2 <= this.domLevel)
this.graphicFormat = "SVG";
else if (this.name == "MSIE")
this.graphicFormat = "VML";
// check if this platform is supported.
if (!this.domLevel)
this.supported = false;
else if (this.name == "Opera" && this.minVersion < 7.5)
this.supported = false;
else if (this.name == "MSIE" && this.minVersion < 5.5) // guess.
this.supported = false;
else if (this.name == "WebKit" && this.minVersion < 522.11) // Safari < 3
this.supported = false;
else if (this.name == "Gecko" && this.minVersion < 1.0) // guess.
this.supported = false;
else
this.supported = true;
}
/**
** platform
**/
this.platform = new AnansiPlatform();
var platform = this.platform;
/****
**** Main Function
****/
/***
*** Anansi.onloadHook()
***
*
* Event handler for Load event.
*/
this.onloadHook = function()
{
// When Anansi is disabled, do nothing.
if (!config.enabled)
return;
// When Anansi is disabled by personal toolbar button (via cookie), do nothing.
if (!initLogo())
return;
if (!enableMe())
return;
// Initialize, parse and construct.
try {
init();
parsePreview();
markBlocks();
addHandler(window, "resize", drawAllLines);
if (window.setTimeout)
window.setTimeout(drawAllLines, 5000); // FIXME: Case of long rendering process.
} catch (e) { // Crashed.
debug(e.name + ": " + e.message +
" (" + e.fileName + ":" + e.lineNumber + ")", 3);
}
};
/***
*** enableMe()
***/
var metaPageRe = new RegExp("\\.(js|css)$", "i");
function enableMe()
{
// Will work only on preview mode.
if (mw.config.get('wgAction') != "submit")
return false;
// Won't work on scripts and stylesheets.
if (mw.config.get('wgNamespaceNumber') == 2 || mw.config.get('wgNamespaceNumber') == 8)
if (metaPageRe.test(mw.config.get('wgPageName')))
return false;
// Won't work on specified pages.
if (config.disabledpages && config.disabledpages.length)
for (var i = 0; i < config.disabledpages.length; i++)
if (config.disabledpages[i][0] == mw.config.get('wgNamespaceNumber') &&
config.disabledpages[i][1] == wgTitle)
return false;
// Otherwise, if configured, will work on limited namespaces.
if (config.namespaces && config.namespaces.length) {
for (var i = 0; i < config.namespaces.length; i++)
if (config.namespaces[i] == mw.config.get('wgNamespaceNumber') ||
(config.namespaces[i] + "").toLowerCase() ==
wgCanonicalNamespace.toLowerCase())
return true;
return false;
}
// OK
return true;
}
/***
*** init()
***
*
* Initialize Anansi:
*/
function init()
{
// Set stylesheet.
importStylesheet(DEFAULT_STYLESHEET);
if (config.stylesheet != DEFAULT_STYLESHEET)
importStylesheet(config.stylesheet);
// Add a namespace and styles for MSIE VML.
if (platform.name == "MSIE" && !document.namespaces.v) {
document.namespaces.add("v", VMLNS);
document.createStyleSheet().addRule("v\\:*", VMLRULE);
}
}
/***
*** initLogo()
***
*
* Initialize Anansi Logo:
*/
function initLogo()
{
// Initialize logo.
anansiLogo = document.createElement("img");
anansiLogo.setAttribute("id", "anansiLogoImage");
anansiLogo.setAttribute("class", "anansiLogo");
var logoList = document.getElementById("p-personal");
if (logoList) { // monobook, simple, myskin, modern
var logoItem = document.createElement("li");
logoItem.setAttribute("id", "anansiLogoItem");
logoItem.appendChild(anansiLogo);
logoList.getElementsByTagName("ul")[0].appendChild(logoItem);
}
else { // standard, nostalgia, cologneblue
logoList = document.getElementById("quickbar");
if (!logoList)
logoList = document.getElementById("topbar");
if (logoList)
logoList.appendChild(anansiLogo);
}
if (!logoList) // Unknown skin.
return true; // fallback to be always enabled.
else
addHandler(anansiLogo, 'click', toggleAnansi);
// Get cookie.
var enabled = getCookie("anansi");
if (enabled && enabled == "disabled") {
anansiLogo.setAttribute("src", LOGO_DISABLED);
anansiLogo.setAttribute("alt", LOGO_DISABLED_ALT);
anansiLogo.setAttribute("title", LOGO_DISABLED_TITLE);
return false;
}
else {
anansiLogo.setAttribute("src", LOGO_ENABLED);
anansiLogo.setAttribute("alt", LOGO_ENABLED_ALT);
anansiLogo.setAttribute("title", LOGO_ENABLED_TITLE);
}
return true;
}
/***
*** toggleAnansi()
***
*
* Event handler: toggle Anansi.
*/
function toggleAnansi() {
if (!anansi.config.enabled)
return;
var enabled = getCookie("anansi");
if (enabled && enabled == "disabled") {
document.cookie = 'anansi=enabled; path=/';
anansiLogo.setAttribute("src", LOGO_ENABLED);
anansiLogo.setAttribute("alt", LOGO_ENABLED_ALT);
anansiLogo.setAttribute("title", LOGO_ENABLED_TITLE);
return true;
}
else {
document.cookie = 'anansi=disabled; path=/';
anansiLogo.setAttribute("src", LOGO_DISABLED);
anansiLogo.setAttribute("alt", LOGO_DISABLED_ALT);
anansiLogo.setAttribute("title", LOGO_DISABLED_TITLE);
return false;
}
}
/****
**** Parser Functions.
****/
/***
*** Block(wtag, texts)
***
*
* Parsed “block” object.
*
* Properties:
* index: ''0-based block index'',
* wtag: ''wiki tag name'',
* texts: ''Texts object'',
*
* where ''wiki tag name'' will be either of:
* - paragraph - "\n"
* - preformatted - IGNORED
* - headdings - "=", "==", ... or "======"
* - list items - ";", ":", "#", "*" or possible combination of them.
* - hairline - "-
* - table [unimplemented yet]:
* - caption - "|+"
* - data - "|"
* - headding - "!"
* - division - "D" [experimental]
* - blockquote - "Q"
* - center - "C" [experimental]
*
* Methods:
* None.
*
*/
function Block(wtag, texts)
{
this.index = index;
this.wtag = wtag;
if (texts)
this.texts = texts;
var blen = blocks.length;
if (blen) {
this.prev = blocks[blen - 1];
blocks[blen - 1].next = this;
}
else
this.prev = null;
this.next = null;
blocks.push(this);
}
/***
*** Texts()
***
*
* Parsed text fragments in a paragraph (block).
*
* Properties:
* ''n'': a text node object of ''n''-th fragment.
* length: current number of text fragments.
* indexes[''n'']: 0-based index of ''n''-th fragment in paragraph.
* lengths[''n'']: length of ''n''-th fragment.
* plain: concatenated all fragments in paragraph.
*
* Methods:
* append(n): add a text node to Texts object.
* concat(t): append other Texts object to Texts object.
*
*/
function Texts()
{
this.length = 0;
this.indexes = new Array();
this.lengths = new Array();
this.plain = "";
this.append = function(n)
{
var text;
/*
if (n.nodeType == 1)
switch (n.nodeName.toLowerCase()) {
case "img":
case "object":
case "iframe":
text = "\uFFFC"; // OBJECT REPLACEMENT CHARACTER
break;
default:
debug("Unexpected element (1) " + n.nodeName, 2);
break;
}
else
*/
text = n.nodeValue;
if (typeof text != "string")
text = "";
this.plain += text;
this.lengths.push(text.length);
if (this.length)
this.indexes.push(this.indexes[this.length - 1] + this.lengths[this.length - 1]);
else
this.indexes.push(0);
this[this.length] = n;
this.length++;
};
this.concat = function(t)
{
for (var i = 0; i < t.length; i++)
this.append(t[i]);
};
}
/***
*** parsePreview()
***
*
* Parse Preview.
*
* Get “blocks” from the preview then accumlate into an array
* blocks.
*/
function parsePreview()
{
// get preview.
var preview = document.getElementById("wikiPreview");
if (!preview ||
!preview.hasChildNodes || !preview.childNodes.length ||
preview.childNodes.length == 1 && preview.firstChild.nodeType != 1)
return false;
// parse block-level nodes.
var afterNote = false;
var node = preview.firstChild;
while (node) {
// skip script or style elements.
if (isMeta(node))
;
// skip anchors.
else if (isAutoAnchor(node))
;
// skip non-block.
else if (isIgnorable(node))
;
else if (!isBlock(node))
debug("a top-level non-empty non-block is found: " +
index + " (" + node.nodeType + ") (" + node.nodeName + ") " +
node.nodeValue, 2);
// skip previewnote.
else if (!afterNote &&
(node.getAttribute("class") == "previewnote" ||
node.getAttribute("className") == "previewnote"))
afterNote = true;
else if (afterNote) {
// skip last empty paragraph.
if (!node.nextSibling && isEmptyParagraph(node))
break;
// otherwise, insert candidate areas,
var sideNote = document.createElement("div");
sideNote.id = "AnansiSideNote-" + index;
sideNote.setAttribute("class", "AnansiSideNote");
sideNote.setAttribute("className", "AnansiSideNote");
sideNote.style.display = "none";
// XXX sideNote.appendChild(document.createElement("ul"));
preview.insertBefore(sideNote, node);
// ...then do parse.
parseBlock(node);
index++;
}
node = node.nextSibling;
}
if (!afterNote) {
debug("no previewnote were found.", 2);
return false;
}
else
return true;
}
/***
*** parse one block-level node.
***/
function parseBlock(node, wtag)
{
if (!wtag)
wtag = "";
var name = node.nodeName.toLowerCase();
switch (name) {
case "blockquote":
parseBCDBlock(node, wtag, "Q");
break;
case "center":
parseBCDBlock(node, wtag, "C"); // experimental
break;
case "div":
parseBCDBlock(node, wtag, "D"); // experimental
break;
case "h1":
case "h2":
case "h3":
case "h4":
case "h5":
case "h6":
case "p":
var wt;
if (name == "p")
wt = "\n";
else {
wt = "";
for (var i = 0; i < eval(name.charAt(1)); i++)
wt = "=";
}
var texts = new Texts();
var n = node.firstChild;
while (n) {
texts.concat(parseInline(n));
n = n.nextSibling;
}
new Block(wtag + wt, texts);
break;
case "pre":
/* IGNORE preformatted block */
break;
case "hr":
new Block(wtag + "-");
break;
case "dl":
case "ol":
case "ul":
parseList(node, wtag);
break;
case "table":
if (node.id == "toc")
break;
// TODO: parse table recursively.
break;
default:
debug("unknown block element: index=" + index +
"; name=" + node.nodeName, 2);
break;
}
}
/***
*** parse blockquote, center or div node recursively.
***/
function parseBCDBlock(node, wtag, mywtag)
{
if (!wtag)
wtag = "";
var texts = new Texts();
function flushTexts(wtag)
{
if (texts.length) {
new Block(wtag, texts);
texts = new Texts();
}
}
/* start here. */
var n = node.firstChild;
while (n) {
// skip script or style elements.
if (isMeta(n))
;
// skip anchors.
else if (isAutoAnchor(n))
;
// skip non-block.
else if (isIgnorable(n))
;
// parse another sub-level block.
else if (isBlock(n)) {
flushTexts(wtag + mywtag);
parseBlock(n, wtag + mywtag);
}
// otherwise, child will be (a part of) anonymous block.
else
texts.concat(parseInline(n));
n = n.nextSibling;
}
flushTexts(wtag + mywtag);
}
/***
*** parse list node recursively.
***/
function parseList(node, wtag)
{
if (!wtag)
wtag = "";
var texts = new Texts();
function flushTexts(wtag)
{
if (texts.length) {
new Block(wtag, texts);
texts = new Texts();
}
}
function parseListItem(node, wtag)
{
var n = node.firstChild;
while (n) {
// skip script or style elements.
if (isMeta(n))
;
// skip anchors.
else if (isAutoAnchor(n))
;
// skip non-block
if (isIgnorable(n))
;
// parse sub-level list.
else if (isList(n)) {
flushTexts(wtag);
parseList(n, wtag);
}
// parse another sub-level block (maybe division).
else if (isBlock(n)) {
flushTexts(wtag);
parseBlock(n, wtag);
}
// otherwise, child will be (a part of) anonymous block.
else
texts.concat(parseInline(n));
n = n.nextSibling;
}
flushTexts(wtag);
}
/* start here. */
var n = node.firstChild;
while (n) {
if (isIgnorable(n))
;
else if (n.nodeType == 1) { // elements
switch (n.nodeName.toLowerCase()) {
case "dd":
flushTexts(wtag);
parseListItem(n, wtag + ":");
break;
case "dt":
flushTexts(wtag);
parseListItem(n, wtag + ";");
break;
case "li":
switch (node.nodeName.toLowerCase()) {
case "ol":
flushTexts(wtag);
parseListItem(n, wtag + "#");
break;
case "ul":
flushTexts(wtag);
parseListItem(n, wtag + "*");
break;
default:
debug("Unknown parent node of list item: index=" + index +
" name=" + node.nodeName, 2);
break;
}
break;
case "dl":
case "ol":
case "ul":
flushTexts(wtag);
parseList(n, wtag);
break;
default:
debug("Unknown list item: index=" + index +
" name=" + n.nodeName, 2);
break;
}
}
n = n.nextSibling;
}
flushTexts(wtag);
}
/***
*** parseInline(node)
***
*
* parse one node containing inline node(s) only.
*/
function parseInline(node)
{
var texts = new Texts();
switch (node.nodeType) {
case 1: // element node (assumed to be inline-level).
if (isBlock(node))
debug("unexpected block element: name=" + node.nodeName, 2);
else if (isIgnorable(node))
;
else if (isOmittable(node))
;
else
switch (node.nodeName.toLowerCase()) {
/* replacements. */
case "img":
case "object":
// case "iframe":
texts.append(node);
break;
default:
var n = node.firstChild;
while (n) {
texts.concat(arguments.callee(n));
n = n.nextSibling;
}
break;
}
break;
case 3: // text node.
texts.append(node);
break;
case 8: // comment node.
break;
default:
debug("unknown nodetype: " + node.nodeType, 2);
break;
}
return texts;
}
/****
**** Proofreader Functions.
****/
/***
*** Result(rule, block, index, length)
***
*
* Result object.
*
* Properties:
* id: Rule ID.
* rule: Rule object.
* block: Current block.
* index:
* length:
* string: Substring matched by rule.match.
* seq: 0-based (global) sequencial number of match.
* subindex:
* sublength:
* substring: A fragment of string above.
* subseq: 0-based sequencial number of fragments.
* candidate: candidate(s): given by user-defined Rule.replace() callback via apply().
* instruction: instruction: given by user-defined Rule.replace() callback via apply().
*
* Methods:
* Constructor: Create object based on rule-string (see above).
* shift(length): Create object based on substring of ''text''.
* apply(): Apply Rule.replace() with ''this'' context as this object.
*/
function Result(rule, block, index, length)
{
// update seq
this.seq = seq++;
//
this.rule = rule;
this.id = rule.id;
this.text = block.texts.plain;
this.subindex = this.index = index;
this.sublength = this.length = length;
this.substring = this.string = this.text.slice(index, index + length);
// init subseq
this.subseq = 0;
// init results.
this.candidate = new Array();
this.instruction = new Array();
/**
** shift(sublength)
**
*
* bite off a substring.
*/
this.shift = function(sublength) {
var Res = function(){};
Res.prototype = this;
var res = new Res;
res.subindex = this.subindex;
res.sublength = sublength;
res.substring = this.text.slice(this.subindex, this.subindex + sublength);
res.subcandidate = null;
res.subinstruction = null;
this.subindex += sublength;
this.sublength = (this.index + this.length) - this.subindex;
this.substring = this.text.slice(this.subindex, this.subindex + this.sublength);
// update subseq
res.subseq = this.subseq++;
return res;
};
/**
** apply()
**
*
* apply rule.
*/
this.apply = function() {
var rule = this.rule;
var replace = rule.replace;
if (typeof replace == "undefined" || replace === null)
;
else if (typeof replace == "function") {
delete this.rule; // user should not access to internal object.
try {
replace.call(this);
if (this.hasOwnProperty("instruction")) {
debug(this.id + ": don't assign values directly to instructioin. Use push().", 1);
delete this.instruction;
}
if (this.hasOwnProperty("candidate")) {
debug(this.id + ": don't assign values directly to candidate. Use push().", 1);
delete this.candidate;
}
} catch (e) {
debug(e.name + ": " + e.message + " (" + e.fileName + ":" + e.lineNumber + ")", 1);
while (this.candidate.length)
this.candidate.pop();
while (this.instruction.length)
this.instruction.pop();
}
this.rule = rule;
}
else if (typeof replace == "string")
this.instruction.push(replace);
else // should be an Array.
for (var i = 0; i < replace.length; i++)
this.candidate.push(replace[i]);
};
}
/***
*** markBlocks()
***
*
* Mark all parsed blocks.
*/
function markBlocks()
{
var block = blocks[0];
while (block) {
markBlock(block);
block = block.next;
}
}
/***
*** markBlock(block)
***
*
* Mark one parsed block.
*/
function markBlock(block)
{
/*
* Check if two text ranges are overlapped.
* Return value:
* 0: overlapped.
* 1/-1: not overlapped.
*/
function compareRanges(xindex, xlength, yindex, ylength)
{
if (xindex == yindex && xlength == 0 && ylength == 0)
// zero-width ranges
return 0;
else if (xindex <= yindex) {
if (xindex + xlength <= yindex)
return -1;
else
return 0;
}
else if (yindex + ylength <= xindex)
return 1;
else
return 0;
}
// debugging output
var s = block.index;
s += " (" + block.wtag + ")";
if (block.texts)
s += " " + block.texts.plain;
s += "\n";
debug(s);
if (!block.texts)
return;
if (!rules || !rules.length)
return;
var plain = block.texts.plain;
/**
** do match.
**/
var results = new Array();
var len = rules.length;
for (var i = 0; i < len; i++) {
var rule = rules[i];
// skip disabled rule.
if (!rule.enabled)
continue;
// skip rules without regexps.
var match = rule.match;
if (!match || !match.length)
continue;
var mlen = match.length;
for (var j = 0; j < mlen; j++) {
var re = match[j];
if (!re) // skip disabled RegExp
next;
try {
re.lastIndex = 0;
var groups;
while (groups = re.exec(plain)) {
var index = groups.index + groups[1].length;
var length = groups[2].length;
// insert matched range if it doesn't overlap with other ranges.
var k;
for (k = results.length; k; k--) {
var c = compareRanges(index, length,
results[k-1].index, results[k-1].length);
if (c == 0) { // overlapped
k = -1;
break;
}
else if (c == 1)
break;
}
if (0 <= k) {
var result = new Result(rule, block, index, length);
results.splice(k, 0, result);
}
}
} catch (e) { // catch RegExp error
// report error.
debug(e.name + ": " + e.message +
" (" + e.fileName + ":" + e.lineNumber + ") " +
" (" + rules[i].id + ") “" +
rules[i].match[j].source + "”", 1);
rules[i].match[j] = null; // disable errorneous RegExp.
}
}
}
if (!results.length)
return;
/**
** split matched substrings then replace with decorated ones.
**/
var plain = block.texts.plain;
var rlen = results.length;
var tlen = block.texts.length;
var previndex;
var ti, tindex, tlength, ri, rindex, rlength, res, frag;
ti = ri = 0;
for ( ; ti < tlen; ti++) {
tindex = block.texts.indexes[ti];
tlength = block.texts.lengths[ti];
res = new Array();
previndex = tindex;
for ( ; ri < rlen; ri++, seq++) {
rindex = results[ri].subindex;
rlength = results[ri].sublength;
if (tindex + tlength <= rindex)
break;
if (previndex < rindex)
res.push(plain.slice(previndex, rindex));
else if (rindex < previndex)
debug("unknown situation: [" + index + "] reindex=" + rindex +
" previndex=" + previndex, 2);
previndex = rindex;
if (rindex + rlength <= tindex + tlength) {
res.push(results[ri].shift(rlength));
previndex = rindex + rlength;
if (results[ri].sublength != 0)
debug("unknown situation: [" + index + "] results[" + ri +
"].sublength=" + results[ri].sublength, 2);
continue; // step over next text fragment.
}
else {
res.push(results[ri].shift(tindex + tlength - rindex));
previndex = tindex + tlength;
break; // continue with remainder of current text fragment.
}
} /* for ( ; ri < rlen; ri++) */
if (previndex < tindex + tlength)
res.push(plain.slice(previndex, tindex + tlength));
else if (tindex + tlength < previndex)
debug("unknown situation: [" + index + "] previndex=" + previndex, 2);
/*
* replace text fragment...
*/
if (!res.length)
;
else if (res.length == 1 && typeof res[0] == "stirng")
;
else
replaceBlockText(block.texts[ti], res, plain, block.index);
} /* for ( ; ti < tlen; ti++) */
/**
** append remainders.
**/
if (ri < rlen) {
var last;
if (block.texts.length)
last = block.texts[block.texts.length - 1];
else
return; // FIXME: how about the block without children?
previndex = plain.length;
for ( ; ri < rlen; ri++, seq++) {
rindex = results[ri].subindex;
rlength = results[ri].sublength;
if (rindex == previndex) {
appendBlockText(last, results[ri], plain, block.index);
previndex += rlength;
}
else
debug("Beyond the text boundary: "+ rindex + "+" + rlength, 2);
}
}
}
/***
*** replaceBlockText(node, frags, plain, blockIndex)
***
*
* replace fragment with marked nodes.
*/
function replaceBlockText(node, frags, plain, blockIndex)
{
var sideNote = document.getElementById("AnansiSideNote-" + blockIndex);
// incorrect logic?
if (!sideNote) {
debug("Unknown sidenote for candidates: " + blockIndex, 2);
return;
}
var parent = node.parentNode;
var len = frags.length;
for (var i = 0; i < len; i++) {
var n = createReplacement(sideNote, frags[i], plain, blockIndex);
// FIXME: might be inserted into upper-most ancester node.
parent.insertBefore(n, node);
}
parent.removeChild(node);
}
/***
*** appendBlockText(node, frags, plain, blockIndex)
***
*
* Special case: append marked nodes at end of paragraph (block).
*/
function appendBlockText(node, frags, plain, blockIndex)
{
var sideNote = document.getElementById("AnansiSideNote-" + blockIndex);
// incorrect logic?
if (!sideNote) {
debug("Unknown sidenote for candidates: " + blockIndex, 2);
return;
}
var next = node.nextSibling;
var parent = node.parentNode;
for (var i = 0; i < frags.length; i++) {
var n = createReplacement(sideNote, frags[i], plain, blockIndex);
if (next)
parent.insertBefore(n, next);
else if (parent)
parent.appendChild(n);
else {
debug("Unknown fragment node", 2);
return;
}
}
}
/***
*** createReplacement(sideNote, frag, plain, blockIndex)
***
*
* decorate declined fragment then insert replacement candidates to side note.
*/
var lineSpaceRe = new RegExp("^[ \r\n]*$");
function createReplacement(sideNote, frag, plain, blockIndex)
{
var klass, replId, repl;
// raw text won't be replaced.
if (typeof frag == "string")
return document.createTextNode(frag);
// compute candidate(s).
frag.apply();
var candidate = frag.candidate;
var instruction = frag.instruction;
if (!candidate.length && !instruction.length) // no fixes needed.
return document.createTextNode(frag.substring);
// text decoration.
if (!frag.subseq)
replId = "AnansiDeclined-" + frag.seq;
else
replId = "AnansiDeclined-" + frag.seq + "-" + frag.subseq;
klass = "AnansiFixS" + frag.id;
if (frag.length == 0) {
repl = document.createElement("ins");
repl.id = replId;
if (candidate.length == 1 && candidate[0] == " ") { // narrow space
repl.setAttribute("class", klass + " AnansiInsertSPNa");
repl.setAttribute("className", klass + " AnansiInsertSPNa");
repl.appendChild(document.createTextNode("\u2002"));
candidate = new Array();
if (!instruction.length)
instruction = new Array("空ける");
}
else if (candidate.length == 1 && candidate[0] == " ") { // wide space
repl.setAttribute("class", klass + " AnansiInsertSP");
repl.setAttribute("className", klass + " AnansiInsertSP");
repl.appendChild(document.createTextNode("\u2003"));
candidate = new Array();
if (!instruction.length)
instruction = new Array("空ける");
}
else {
repl.setAttribute("class", klass + " AnansiInsert");
repl.setAttribute("className", klass + " AnansiInsert");
repl.appendChild(document.createTextNode("\u2003"));
}
}
else if (candidate.length == 1 && candidate[0] == "") {
repl = document.createElement("del");
repl.id = replId;
switch (frag.substring) {
case " ": // narrow spaces
case "\u00A0":
case "\u2000":
case "\u2002":
case "\u2004":
case "\u2005":
case "\u2006":
case "\u2007":
case "\u2008":
case "\u2009":
case "\u200A":
repl.setAttribute("class", klass + " AnansiRemoveSPNa");
repl.setAttribute("className", klass + " AnansiRemoveSPNa");
candidate = new Array();
if (!instruction.length)
instruction = new Array("詰める");
repl.appendChild(document.createTextNode("\u2002"));
break;
case "\u2001": // wide spaces
case "\u2003":
case " ":
repl.setAttribute("class", klass + " AnansiRemoveSP");
repl.setAttribute("className", klass + " AnansiRemoveSP");
candidate = new Array();
if (!instruction.length)
instruction = new Array("詰める");
repl.appendChild(document.createTextNode("\u2003"));
break;
default:
if (lineSpaceRe.test(plain) && frag.substring == plain) { // line space
repl.setAttribute("class", klass + " AnansiRemoveLS");
repl.setAttribute("className", klass + " AnansiRemoveLS");
candidate = new Array();
if (!instruction.length)
instruction = new Array("行を詰める");
repl.appendChild(document.createTextNode("\u2003"));
}
else { // others; remove texts
repl.setAttribute("class", klass + " AnansiDeclined");
repl.setAttribute("className", klass + " AnansiDeclined");
candidate = new Array();
if (!instruction.length)
instruction = new Array("取る詰め");
repl.appendChild(document.createTextNode(frag.substring));
}
break;
}
}
else {
repl = document.createElement("del");
repl.id = replId;
repl.setAttribute("class", klass + " AnansiDeclined");
repl.setAttribute("className", klass + " AnansiDeclined");
repl.appendChild(document.createTextNode(frag.substring));
}
// Set event handler.
addHandler(repl, "resize", drawLine);
addHandler(repl, "mouseover", emphLine);
addHandler(repl, "mouseout", unemphLine);
// append candidate(s) and instructions to side note.
var list = sideNote.firstChild;
if (!list) {
list = document.createElement("ul");
sideNote.appendChild(list);
}
var g = document.createElement("li");
klass = "AnansiFixD" + frag.id;
if (instruction.length) {
g.id = "AnansiInstruction-" + frag.seq;
g.setAttribute("class", klass + " AnansiInstruction");
g.setAttribute("className", klass + " AnansiInstruction");
for (var j = 0; j < instruction.length; j++) {
if (j)
g.appendChild(document.createTextNode(" "));
g.appendChild(document.createTextNode(instruction));
}
}
if (candidate.length) {
g.id = "AnansiCandidateGroup-" + frag.seq;
g.setAttribute("class", "AnansiCandidateGroup");
g.setAttribute("className", "AnansiCandidateGroup");
for (var j = 0; j < candidate.length; j++) {
if (j)
g.appendChild(document.createTextNode(" "));
var c = document.createElement("ins");
if (j)
c.id = "AnansiCandidate-" + frag.seq + "-" + j;
else
c.id = "AnansiCandidate-" + frag.seq;
c.setAttribute("class", klass + " AnansiCandidate");
c.setAttribute("className", klass + " AnansiCandidate");
c.appendChild(document.createTextNode(candidate[j]));
g.appendChild(c);
}
}
// add link to description page(s).
var desc = null;
for (var i = 0; i < rules.length; i++) {
if (rules[i].id == frag.id) {
desc = rules[i].description;
}
}
if (desc) {
g.appendChild(document.createTextNode(" "));
for (var di = 0; di < desc.length; di++) {
var link = document.createElement("a");
link.setAttribute("href",
wgArticlePath.replace("$1",
encodeURIComponent(desc[di].replace(/ /g, "_"))));
link.setAttribute("target", "_blank");
var img = document.createElement("img");
img.setAttribute("width", "16");
img.setAttribute("height", "16");
img.setAttribute("border", "0");
img.setAttribute("alt", "説明");
img.setAttribute("src", ICON_QUESTION);
link.appendChild(img);
g.appendChild(link);
}
}
// Set event handler.
addHandler(g, "resize", drawLine);
addHandler(g, "mouseover", emphLine);
addHandler(g, "mouseout", unemphLine);
list.appendChild(g);
sideNote.style.display = "";
return repl;
}
/****
**** Event Handler: Line Drawer.
****/
/***
*** drawLine()
***
*
*/
function drawLine()
{
if (!platform.graphicFormat)
return;
if (config.lineshape.toLowerCase() == "none")
return;
var elm;
if (platform.name == "MSIE" && !this.id) // MSIE kludge.
elm = event.srcElement;
else
elm = this;
var parent = elm.offsetParent;
if (!parent)
return;
var container = document.getElementById("AnansiLines-" + parent.id);
var root;
if (!container) {
container = document.createElement("div");
container.id = "AnansiLines-" + parent.id;
container.style.position = "absolute";
container.style.left = 0;
container.style.top = 0;
switch (platform.graphicFormat) {
case "SVG":
root = document.createElementNS(SVGNS, "svg");
root.setAttribute("xmlns", SVGNS);
root.setAttribute("version", SVGVER);
break;
case "VML":
root = document.createElement("v:group");
var rect = document.createElement("v:rect");
root.appendChild(rect);
break;
}
container.appendChild(root);
parent.appendChild(container);
// set background-color.
container.style.zIndex = -1;
var bgColor = getComputedElementStyle(parent).backgroundColor || "white";
switch (platform.graphicFormat) {
case "SVG":
container.style.backgroundColor = bgColor;
break;
case "VML":
container.style.backgroundColor = bgColor;
var rect = root.firstChild;
rect.setAttribute("strokecolor", bgColor);
rect.setAttribute("fillcolor", bgColor);
/* rect.setAttribute("opacity", 0); */
break;
}
parent.style.backgroundColor = "transparent";
}
else
root = container.firstChild;
// fix size of viewbox.
switch (platform.graphicFormat) {
case "SVG":
root.setAttribute("width", parent.offsetWidth);
root.setAttribute("height", parent.offsetHeight);
root.setAttribute("viewBox", "0 0 " + parent.offsetWidth + " " + parent.offsetHeight);
break;
case "VML":
root.style.width = parent.offsetWidth;
root.style.height = parent.offsetHeight;
root.setAttribute("coordorigin", "0 0");
root.setAttribute("coordsize", parent.offsetWidth + " " + parent.offsetHeight);
var rect = root.firstChild;
rect.style.width = parent.offsetWidth;
rect.style.height = parent.offsetHeight;
break;
}
// Get (or create if it wasn't exist) elements for line drawing.
getLineElements(elm, root);
}
/***
*** drawAllLines()
***
*
*/
function drawAllLines()
{
var elms = document.getElementsByTagName("*");
var elen = elms.length;
for (var i = 0; i < elen; i++)
if (elms[i].id.indexOf("AnansiDeclined-") == 0)
drawLine.call(elms[i]);
}
/***
*** emphLine()
***
*
*/
function emphLine()
{
if (!platform.graphicFormat)
return;
if (config.lineshape.toLowerCase() == "none")
return;
var elm;
if (platform.name == "MSIE" && !this.id) // MSIE kludge.
elm = event.srcElement;
else
elm = this;
drawLine.call(elm);
var elms = getLineElements(elm);
if (!elms[2])
return;
elms[1].style.fontWeight = FONTWEIGHT_EMPH;
switch (platform.graphicFormat) {
case "SVG":
elms[2].parentNode.setAttribute("stroke-width", STROKEWIDTH_EMPH);
break;
case "VML":
elms[2].parentNode.setAttribute("strokeweight", STROKEWIDTH_EMPH);
break;
}
}
/***
*** unemphLine()
***
*
*/
function unemphLine()
{
if (!platform.graphicFormat)
return;
if (config.lineshape.toLowerCase() == "none")
return;
var elm;
if (platform.name == "MSIE" && !this.id) // MSIE kludge.
elm = event.srcElement;
else
elm = this;
drawLine.call(elm);
var elms = getLineElements(elm);
if (!elms[2])
return;
elms[1].style.fontWeight = "";
switch (platform.graphicFormat) {
case "SVG":
elms[2].parentNode.setAttribute("stroke-width", STROKEWIDTH_NORMAL);
break;
case "VML":
elms[2].parentNode.setAttribute("strokeweight", STROKEWIDTH_NORMAL);
break;
}
}
/***
*** getLineElements(elm, root)
***
*
* utility function.
*/
function getLineElements(elm, root)
{
if (!platform.graphicFormat)
return;
if (config.lineshape.toLowerCase() == "none")
return;
var pixelRe = new RegExp("^([0-9]+(\\.[0-9]+)?)(px)?$", "i");
function pixelToNumber(s)
{
var m = pixelRe.exec(s);
if (m)
return eval(m[1]);
else
return null;
}
/* start here. */
// get decorated element on preview.
var seq, sElm, dElm, g, path;
seq = elm.id.split("-")[1];
sElm = document.getElementById("AnansiDeclined-" + seq);
dElm = document.getElementById("AnansiInstruction-" + seq) ||
document.getElementById("AnansiCandidateGroup-" + seq);
if (!sElm || !dElm)
return new Array();
g = document.getElementById("AnansiLine-" + seq);
if (!g) {
switch (platform.graphicFormat) {
case "SVG":
g = document.createElementNS(SVGNS, "g");
g.setAttribute("stroke", "red");
g.setAttribute("stroke-width", STROKEWIDTH_NORMAL);
g.setAttribute("fill", "none");
path = document.createElementNS(SVGNS, "path");
break;
case "VML":
g = document.createElement("v:shape");
g.style.position = "absolute";
g.style.left = 0;
g.style.top = 0;
g.style.width = 1;
g.style.height = 1;
g.setAttribute("coordorigin", "0 0");
g.setAttribute("coordsize", "1 1");
g.setAttribute("strokecolor", "red");
g.setAttribute("strokeweight", STROKEWIDTH_NORMAL);
path = document.createElement("v:path");
break;
}
g.id = "AnansiLine-" + seq;
g.appendChild(path);
if (platform.graphicFormat == "VML") {
var fill = document.createElement("v:fill");
fill.setAttribute("opacity", "0");
g.appendChild(fill);
}
if (root)
root.appendChild(g);
}
else
path = g.firstChild;
var sLeft = pixelToNumber(sElm.offsetLeft);
var sTop = pixelToNumber(sElm.offsetTop);
var dLeft = pixelToNumber(dElm.offsetLeft);
var dTop = pixelToNumber(dElm.offsetTop);
// on MSIE, offset parent of nodes is side note etc, not content division.
var sParent = sElm.offsetParent;
while (sParent && sParent.id != "content") {
sLeft += pixelToNumber(sParent.offsetLeft);
sTop += pixelToNumber(sParent.offsetTop);
sParent = sParent.offsetParent;
}
var dParent = dElm.offsetParent;
while (dParent && dParent.id != "content") {
dLeft += pixelToNumber(dParent.offsetLeft);
dTop += pixelToNumber(dParent.offsetTop);
dParent = dParent.offsetParent;
}
if (!sLeft || !sTop || !dLeft || !dTop) {
debug("couldn't get offset geometries of source/dest element(s).", 2);
return new Array();
}
/*
if (platform.name == "MSIE") debug("s.parent="+sElm.offsetParent.id+";sLeft="+sLeft+";sTop="+sTop+";d.parent="+dElm.offsetParent.id+";dLeft="+dLeft+";dTop="+dTop+";", 1);
*/
var sStyle = getComputedElementStyle(sElm);
var dStyle = getComputedElementStyle(dElm);
var sFontSize = pixelToNumber(sStyle.fontSize) || 12;
var dFontSize = pixelToNumber(dStyle.fontSize) || 12;
var sLineHeight = pixelToNumber(sStyle.lineHeight) || 16;
var dLineHeight = pixelToNumber(dStyle.lineHeight) || 16;
var pathExpr;
if (sTop < dTop)
pathExpr = "M " + parseInt(sLeft + sFontSize / 2) + "," +
parseInt(sTop + sFontSize) +
" L " + parseInt(sLeft + sFontSize / 2) + "," +
parseInt(sTop + (sLineHeight + sFontSize) / 2) +
" L " + parseInt(dLeft - dFontSize * 3 / 2) + "," +
parseInt(dTop + dLineHeight / 2) +
" L " + parseInt(dLeft - dFontSize / 2) + "," +
parseInt(dTop + dLineHeight / 2);
else if (dLeft < sLeft)
pathExpr = "M " + parseInt(sLeft + sFontSize / 2) + "," +
parseInt(sTop) +
" L " + parseInt(sLeft + sFontSize / 2) + "," +
parseInt(sTop - (sLineHeight - sFontSize) / 2) +
" L " + parseInt(dLeft - dFontSize / 2) + "," +
parseInt(dTop + dLineHeight) +
" L " + parseInt(dLeft - dFontSize / 2) + "," +
parseInt(dTop + dLineHeight / 2);
else
pathExpr = "M " + parseInt(sLeft + sFontSize / 2) + "," +
parseInt(sTop) +
" L " + parseInt(sLeft + sFontSize / 2) + "," +
parseInt(sTop - (sLineHeight - sFontSize) / 2) +
" L " + parseInt(dLeft - dFontSize * 3 / 2) + "," +
parseInt(dTop + dLineHeight / 2) +
" L " + parseInt(dLeft - dFontSize / 2) + "," +
parseInt(dTop + dLineHeight / 2);
switch (platform.graphicFormat) {
case "SVG":
path.setAttribute("d", pathExpr);
break;
case "VML":
path.setAttribute("v", pathExpr.toLowerCase() + " e");
break;
}
return new Array(sElm, dElm, path);
}
/****
**** Rule Manipulater functions.
****/
/***
*** Rule object
***
*
* Definitions for prototype of proofreading rule.
*
* Properties:
*
* id: unique id (name of this rule).
* match: array of (one or more) RegExp object(s).
* pattern: array of triplets of pattern.
* replace: replacement candidate(s).
* desc:
* enabled: enable this rule;
*
* Methods:
* register(): register this rule.
*
* where the source of ''RegExp object'' should be formatted as:
*
* (''previous'')(''to be replaced'')(?=''following'')
*
* - ''previous'' pattern is assumed not to contain grouping “(...)”.
* Instead, use “(?:...)”.
* - ''to be replaced'' pattern will be matched with that may be replaced.
* - ''following'' pattern is applied as “lookahead assersion”.
*
* replacement candidate(s) may be an array of static strings,
* or may be a Function object (see function Result()):
*
*/
var idRe = new RegExp("^[A-Z][0-9A-Z]*$", "i");
function Rule()
{
// this.id;
// this.replace;
// this.description;
// this.pattern;
this.match = null;
this.register = function() {
function safeRegExp(pattern) {
if (!pattern)
return "";
else if (typeof pattern == "string")
return pattern;
else if (pattern.source != undefined)
return pattern.source;
else // Unknown object.
return pattern.toString();
}
if (!this.id)
this.id = "Rule" + (rules.length + 1);
else if (!idRe.test(this.id)) {
debug(this.id + ": Bad rule ID", 1);
return;
}
for (var i = 0; i < rules.length; i++)
if (rules[i].id.toLowerCase() == this.id.toLowerCase()) {
debug(this.id + ": Already registered.", 1);
return;
}
if (typeof this.replace == "string")
this.replace = new Array(this.replace);
else if (typeof this.replace == "undefined" || this.replace === null) {
debug(this.id + ": Replacement string(s) should be given for the rule.", 1);
return;
}
if (typeof this.description == "string")
this.description = [this.description];
else if (!this.description || !this.description.length)
this.description = new Array();
var regexp;
this.match = new Array();
try {
for (var i = 0; i + 2 < this.pattern.length; i += 3) {
// Avoid grouping (...) in preceding pattern.
regexp = "(" + safeRegExp(this.pattern[i]).replace(
/(\\.|\[\^?\]?(\\.|[^\\\]])*\]|\(\?.|\()/g,
function(m) {
if (m == "(")
return "(?:";
else
return m;
}
) + ")";
regexp += "(" + safeRegExp(this.pattern[i+1]) + ")";
// Optional succeeding pattern.
if (this.pattern[i+2])
regexp += "(?=" + safeRegExp(this.pattern[i+2]) + ")";
this.match.push(new RegExp(regexp, "g"));
}
} catch (e) {
debug(this.id + ": " + e.name + ": " + e.message + " (" + regexp + ")",
1);
this.match = null;
return;
}
if (!this.match || !this.match.length) {
debug(this.id + ": No matching rules", 1);
return;
}
if (typeof this.enabled == "undefined" || this.enabled === null)
this.enabled = true;
delete this.register;
rules.push(this);
}
}
/***
*** Anansi.registerRule(definition)
***
*
*/
this.registerRule = function(definition) {
if (!enableMe())
return;
definition.prototype = new Rule;
try {
(new definition).register();
} catch (e) {
debug(e.name + ": " + e.message + " (" + e.fileName + ":" + e.lineNumber + ")", 1);
}
};
/***
*** Anansi.enableRule(id)
***
*
*/
this.enableRule = function(id) {
if (!enableMe())
return;
if (!idRe.test(id)) {
debug(id + ": Bad rule ID: ", 1);
return;
}
for (var i = 0; i < rules.length; i++)
if (rules[i].id.toLowerCase() == id.toLowerCase())
rules[i].enabled = true;
}
/***
*** Anansi.disableRule(id)
***
*
*/
this.disableRule = function(id) {
if (!enableMe())
return;
if (!idRe.test(id)) {
debug(id + ": Bad rule ID: ", 1);
return;
}
for (var i = 0; i < rules.length; i++)
if (rules[i].id.toLowerCase() == id.toLowerCase())
rules[i].enabled = false;
}
/****
**** Debugger Functions.
****/
/***
*** debug(message, severity)
***
*
* show debug messages.
*
* severity:
* - 0: for the purpose of internal debugging.
* - 1: data format error (registered rules, config variables, ...).
* - 2: failures or overlookings in programmed logic.
* - 3: crash (syntax error, undefined object etc.).
*/
var debugArea = null;
function debug(message, severity)
{
if (!severity || severity < 0)
severity = 0;
if (severity < config.debuglevel)
return;
if (severity > 3)
severity = 3;
if (platform.name == "MSIE" &&
platform.minVersion && platform.minVersion < 7) {
// MSIE <=6 crashes when “fixed” position is set to element.
// So we won't support internal debug message (severity = 0)
// and will use alert box for other higher level messages.
if (0 < severity)
alert("<" + severity + "> " + message);
return;
}
else if (!debugArea) {
var node = document.createElement("div");
node.id = "AnansiDebug";
if (platform.name == "MSIE")
node.setAttribute("className", "AnansiDebug");
else
node.setAttribute("class", "AnansiDebug");
var buttonarea = document.createElement("div");
buttonarea.style.textAlign = "right";
var button = document.createElement("img");
button.setAttribute("src", ICON_CLOSE);
button.style.width = "14px";
button.style.height = "14px";
addHandler(button, "click",
function() {
document.getElementById("AnansiDebug").style.display = "none";
});
buttonarea.appendChild(button);
node.appendChild(buttonarea);
debugArea = document.createElement("ul");
debugArea.id = "AnansiDebugArea";
debugArea.setAttribute("class", "AnansiDebugArea");
debugArea.setAttribute("className", "AnansiDebugArea");
if (platform.ua)
debugArea.appendChild(document.createTextNode("; " + platform.ua.replace(/:/g, ":")));
else
debugArea.appendChild(document.createTextNode("; (name and version of your browser)"));
debugArea.appendChild(document.createTextNode(": [{{fullurl:" + mw.config.get('wgPageName') +
"|oldid=" + wgCurRevisionId + "}} " +
mw.config.get('wgPageName') + "]"));
debugArea.appendChild(document.createTextNode(": Detected: " + platform.name + " " +
platform.minVersion));
debugArea.appendChild(document.createElement("br"));
node.appendChild(debugArea);
if (platform.name == "MSIE") {
var preview = document.getElementById("wikiPreview");
if (preview)
preview.insertBefore(node, preview.firstChild);
}
else
document.getElementsByTagName("body")[0].appendChild(node);
}
var msgnode = document.createElement("li");
var klass = "AnansiDebugMessage";
if (severity > 0)
klass += " AnansiSeverity" + severity;
msgnode.setAttribute("class", klass);
msgnode.setAttribute("className", klass);
msgnode.appendChild(document.createTextNode(message));
debugArea.appendChild(msgnode);
debugArea.parentNode.style.display = "";
}
/****
**** Utilities
****/
/***
*** isAutoAnchor(node) - Anchor for headdings: “a” or an paragraph
*** having just only one “a”.
*** isBlock(node) - Block-level elements excluding list items.
*** isEmptytParagraph(node) - Empty paragraph.
*** isIgnorable(node) - Ignorable text node.
*** isOmittable(node) - Omittable inline nodes.
*** isList(node) - Block-level elements especially the lists.
*** isListItem(node) - List items; “dd”, “dt” or “li”.
*** isMeta(node) - “script” or “style” element.
***
*
* Classify nodes.
*/
function isBlock(node)
{
if (!node)
return false;
if (node.nodeType != 1)
return false;
switch (node.nodeName.toLowerCase()) {
case "blockquote":
case "center":
case "div":
case "dl":
case "h1":
case "h2":
case "h3":
case "h4":
case "h5":
case "h6":
case "hr":
case "ol":
case "p":
case "pre":
case "table":
case "ul":
return true;
default:
return false;
}
}
function isList(node)
{
if (!node)
return false;
if (node.nodeType != 1)
return false;
switch (node.nodeName.toLowerCase()) {
case "dl":
case "ol":
case "ul":
return true;
default:
return false;
}
}
function isListItem(node)
{
if (!node)
return false;
if (node.nodeType != 1)
return false;
switch (node.nodeName.toLowerCase()) {
case "dd":
case "dt":
case "li":
return true;
default:
return false;
}
}
function isMeta(node)
{
if (!node)
return false;
if (node.nodeType != 1)
return false;
switch (node.nodeName.toLowerCase()) {
case "script":
case "style":
return true;
default:
return false;
}
}
function isIgnorable(node)
{
switch (node.nodeType) {
case 3: // A text node that...
var n;
// 1. contains whitespaces only (or empty), and...
if (node.nodeValue.match(/[^\r\n ]/))
return false;
// 2.1. succeeds to: (a) no nodes (as a child of block-level node),
// (b) block-level node, (c) script or style (d) or comment node.
// or...
n = node.previousSibling;
if (!n && (isBlock(node.parentNode) || isListItem(node.parentNode)) ||
n && (isBlock(n) || isListItem(n) || isMeta(n) || n.nodeType == 8))
return true;
// 2.2. preceeds to (a) no nodes (as a child of block-level node),
// (b) block-level node, (c) script or style (d) or comment node.
n = node.nextSibling;
if (!n && (isBlock(node.parentNode) || isListItem(node.parentNode)) ||
n && (isBlock(n) || isListItem(n) || isMeta(n) || n.nodeType == 8))
return true;
// ...is ignorable. Otherwise, text nodes aren't ignorable.
return false;
case 8: // Comment nodes are ignorable.
return true;
default: // The others aren't ignorable.
return false;
}
}
/*
* Following inline nodes are omittable.
* * <sub> & <sup>
* * The nodes with "noprint" class.
* * Reference marks; the nodes with "reference" class etc.
*
* See [[User:Hatukanezumi/JIS X 4051の字間空き量#その他の処理]].
*
*/
var OMITTABLE_CLASSES = new Array("noprint", "reference", "external autonumber");
function isOmittable(node)
{
if (node.nodeType != 1)
return false;
switch (node.nodeName.toLowerCase()) {
case "sub":
case "sup":
return true;
default:
var klass = node.getAttribute("class") || node.getAttribute("className");
for (var i = 0; i < OMITTABLE_CLASSES.length; i++)
if (0 <= (" " + klass + " ").indexOf(" " + OMITTABLE_CLASSES[i] + " "))
return true;
}
return false;
}
function isEmptyParagraph(node)
{
if (node.nodeType != 1 || node.nodeName.toLowerCase() != "p")
return false;
var nodes = new Array();
var n = node.firstChild;
while (n) {
if (!isIgnorable(n))
nodes.push(n);
n = n.nextSibling;
}
if (nodes.length == 1 &&
nodes[0].nodeType == 1 &&
nodes[0].nodeName.toLowerCase() == "br")
return true;
else
return false;
}
var headdingRe = new RegExp("^h[1-6]$", "i");
function isAutoAnchor(node)
{
if (node.nodeType != 1)
return false;
else if (node.nodeName.toLowerCase() == "a" &&
node.getAttribute("name")) {
var n = node.nextSibling;
while (n && isIgnorable(n))
n = n.nextSibling;
if (n && n.nodeType == 1 && headdingRe.test(n.nodeName))
return true;
return false;
}
else if (node.nodeName.toLowerCase() != "p")
return false;
var nodes = new Array();
var n = node.firstChild;
while (n) {
if (!isIgnorable(n))
nodes.push(n);
n = n.nextSibling;
}
if (nodes.length == 1 &&
nodes[0].nodeType == 1 &&
nodes[0].nodeName.toLowerCase() == "a" &&
nodes[0].getAttribute("name")) {
var n = node.nextSibling;
while (n && isIgnorable(n))
n = n.nextSibling;
if (n && n.nodeType == 1 && headdingRe.test(n.nodeName))
return true;
}
return false;
}
/***
*** getComputedElementStyle(element)
***
*
* Get actual style object of element.
*/
function getComputedElementStyle(element)
{
if (element.currentStyle) // IE
return element.currentStyle;
else if (window.getComputedStyle) // Firefox, Opera
return window.getComputedStyle(element, "");
else if (document.defaultView) // Safari
return document.defaultView.getComputedStyle(element, "");
else if (element.style)
return element.style;
else
return {};
}
/***
*** getCookie(name)
***
*
* Get cookie value.
*/
function getCookie(name) {
var cookie = ' ' + document.cookie;
var search = ' ' + name + '=';
var value = null;
var off;
var end;
off = cookie.indexOf(search);
if (off != -1) {
off += search.length;
end = cookie.indexOf(';', off)
if (end == -1)
end = cookie.length;
value = decodeURIComponent(cookie.substring(off, end).replace(/\+/g, ' '));
}
return(value);
}
}
/****
**** Startup.
****/
anansi = new Anansi();
if (anansi.platform.supported) {
addOnloadHook(anansi.onloadHook);
importScript("User:Hatukanezumi/AnansiRules.js");
importScript("User:" + wgUserName + "/AnansiConfig.js");
}
else if (mw.config.get('wgAction') == "submit")
alert("Anansi does not support browser that you are using now.");
/* end of script */