User:Bagatelle/gm
From A KoL Wiki
// ==UserScript== // @name KoL Wiki Helper // @description Provides assistance with editing the KoL Wiki // @include *kingdomofloathing.com/desc_item.php?whichitem=* // @include *127.0.0.1:600*/desc_item.php?whichitem=* // ==/UserScript== /* Current Version: 0.0 Version History: 0.1 Initial release: *Parses item pop-ups into wiki format To Do: *Clan Trophies *Effects *Skills *Automatically output table rows for equipment/mechanics lists *Try to automatically glean ItemID from HTTP request to store/closet *Automatically populate adventure/location text on "page not created" */ var ScriptURL = "http://userscripts.org/scripts/show/51010"; var CurrentVersion = 0.0; /******************************************************************************/ // utility function that needs to be defined prior to pattern definitions // return a lowercase abbreviation of class given a title-cased plural of it // return "1" if not in dictionary (all classes) function ClassAbbrev() { if (arguments.length >= 2) { switch (arguments[1]) { case "Seal Clubbers": return "sc"; case "Turtle Tamers": return "tt"; case "Pastamancers": return "pm"; case "Saucerors": return "s"; case "Disco Bandits": return "db"; case "Accordion Thieves": return "at"; } } else { return "1"; } } // duplicated nuisance text FooterText = ( "(?:Type:|Selling Price:|Cannot be discarded|Cannot be traded|" + "Free pull from Hagnk's|</blockquote>|" + "\\(Meat Pasting component\\)|\\(Meatsmithing component\\)|" + "\\(Cocktailcrafting ingredient\\)|\\(Cooking ingredient\\)|" + "\\(Jewelrymaking component\\))" ); // pop-up parsing patterns // see Parser() for documentation PatsKoL = [ ["haiku", "<img [^>]+?><blockquote>"], [ "image", "(?:<blockquote>)?<img src=\"http://images\.kingdomofloathing\.com/" + "([\\s\\S]*?/[^/]*?\.gif)" ], ["name", "<center><img [\\s\\S]+?><br><b>([\\s\\S]+?)</b></center><p>"], ["desc", "<p><blockquote>([\\s\\S]+?)<br>(?:<br>)?" + FooterText], ["haikuname", "<img [\\s\\S]+?><blockquote><b>([\\s\\S]+?)</b><br>"], [ "haikudesc", "(?!<p>)<blockquote><b>(?:[\\s\\S]+?)</b><br>([\\s\\S]+?)<br><br>" + FooterText ], ["paste", "<br>\\(Meat Pasting component\\)"], ["smith", "<br>\\(Meatsmithing component\\)"], ["cocktail", "<br>\\(Cocktailcrafting ingredient\\)"], ["cook", "<br>\\(Cooking ingredient\\)"], ["jewelry", "<br>\\(Jewelrymaking component\\)"], ["type", "<br>Type: <b>([\\s\\S]+?)</b>"], [ "alsocombat", "<br>Type: <b>[\\s\\S]+?</b><br>\\(can also be used in combat\\)" ], [ "power", "<br>(?:Power|Damage Reduction|Capacity): <b>(\\d+?(?: per level)?)</b>" ], [ "powertype", "<br>(Damage Reduction|Capacity): <b>\\d+?(?: per level)?</b>" ], ["stat", "<br>(Muscle|Mysticality|Moxie) Required: <b>\\d+?</b>"], ["statreq", "<br>(?:Muscle|Mysticality|Moxie) Required: <b>(\\d+?)</b>"], ["level", "<br>Level required: <b>(\\d+)</b>"], ["outfit", "<br>Outfit: <b>([\\s\\S]+?)</b>\\."], ["autosell", "<br>Selling Price: <b>(\\d+) Meat\\.</b>"], ["cost", "<br>Cost: <b>(\\d+?) Meat</b>"], ["notrade", "<br>Cannot be traded"], ["nodiscard", "<br>Cannot be discarded"], ["gift", "<br><b>Gift Item</b>"], ["quest", "<br><b>Quest Item</b>"], [ "enchantment", "<center>Enchantment:<br><b><font color=\"?blue\"?>([\\s\\S]+?)" + "(?:(?:<br>)+\\(Bonus for (?:Seal Clubbers|Turtle Tamers|Pastamancers|" + "Saucerors|Disco Bandits|Accordion Thieves) only\\))?(?:<br>)*" + "</font></b></center>" ], [ "enchclass", "(?:<br\\s*/?><br\\s*/?>)" + "\\(Bonus for (Seal Clubbers|Turtle Tamers|Pastamancers|" + "Saucerors|Disco Bandits|Accordion Thieves) only\\)</font></b></center>", ClassAbbrev ], [ "critical", "<br><b>NOTE:</b> If you wear multiple items that increase your Critical " + "Hit multiplier, only the highest multiplier applies\\." ], [ "nohardcore", "<br><b>NOTE:</b> This item cannot be equipped while in Hardcore\\." ], [ "mpreduce", "<br><b>NOTE:</b> Items that reduce the MP cost of skills will not do so" + " by more than 3 points, in total\\." ], [ "limit", "<br><b>NOTE:</b> You may not equip more than one of this item at a time\\." ], ["hagnk", "<br>Free pull from Hagnk's"], [ "spelltype", "<br><b>NOTE:</b> This item only works for ([\\s\\S]+?) Spells\\." ], [ "class", "<p><font color=\"?blue\"?><b>Only " + "(Seal Clubbers|Turtle Tamers|Pastamancers|" + "Saucerors|Disco Bandits|Accordion Thieves) " + "may use this item\\.</b></font>", ClassAbbrev ], [ "grantskill", "<br><b>NOTE:</b> This item grants a skill(?: that can only be used by " + "(Seal Clubbers|Turtle Tamers|Pastamancers|" + "Saucerors|Disco Bandits|Accordion Thieves))?\\.", ClassAbbrev ], [ "lounge", "<br><b>NOTE:</b> When used, this item will be installed in your " + "Clan Hall's VIP Lounge, and will be usable by anybody in your Clan " + "with VIP Lounge access\\." ], [ "bluenote", "<p><font color=\"?blue\"?><b>" + "(?!Only " + "(?:Seal Clubbers|Turtle Tamers|Pastamancers|" + "Saucerors|Disco Bandits|Accordion Thieves) " + "may use this item\\.)" + "([\\s\\S]+?)</b></font>" ] ]; // patterns to tidy enchantment text into Wiki format PatsEnchantmentClean = [ [ "eltdmg", "^\\+(\\d+) (?:<font color=\"?(?:blue|red|blueviolet|gray|green)\"?>)" + "(Cold|Hot|Sleaze|Spooky|Stench) Damage(?:</font>)$", "+$1 {{element|$2}}" ], [ "eltspelldmg", "^\\+(\\d+(?: Damage)?) to " + "(?:<font color=\"?(?:blue|red|blueviolet|gray|green)\"?>)" + "(Cold|Hot|Sleaze|Spooky|Stench) Spells(?:</font>)$", "+$1 to {{element|$2|Spells}}" ], [ "eltpassive", "Deals (\\d+(?:\-\\d+)) " + "(?:<font color=\"?(?:blue|red|blueviolet|gray|green)\"?>)" + "(Cold|Hot|Sleaze|Spooky|Stench) Damage</font> to attackers$", "Deals $1 {{element|$2}} to attackers" ], ["intrinsic", "^Intrinsic effect: ([\\s\\S]+)$", "Intrinsic effect: [[$1]]"] ]; // in a RE match return, return the first matched subgroup function ReturnFirstGroup() { for (var N = 1; N < arguments.length; N++) { if (arguments[N]) { return arguments[N]; } } return ""; } // pattern order matters for stuff like "underwater only" tags PatsEnchantmentParse = [ [ "mus", "^(?:((?:\\+|\\-)\\s*\\d+)\\s+)Muscle$|" + "^Muscle(?:\\s+((?:\\+|\\-)\\s*\\d+))$", ReturnFirstGroup ], [ "muspct", "^(?:((?:\\+|\\-)\\s*\\d+%)\\s+)Muscle$|" + "^Muscle(?:\\s+((?:\\+|\\-)\\s*\\d+%))$", ReturnFirstGroup ], [ "mys", "^(?:((?:\\+|\\-)\\s*\\d+)\\s+)Mysticality$|" + "^Mysticality(?:\\s+((?:\\+|\\-)\\s*\\d+))$", ReturnFirstGroup ], [ "myspct", "^(?:((?:\\+|\\-)\\s*\\d+%)\\s+)Mysticality$|" + "^Mysticality(?:\\s+((?:\\+|\\-)\\s*\\d+%))$", ReturnFirstGroup ], [ "mox", "^(?:((?:\\+|\\-)\\s*\\d+)\\s+)Moxie$|" + "^Moxie(?:\\s+((?:\\+|\\-)\\s*\\d+))$", ReturnFirstGroup ], [ "moxpct", "^(?:((?:\\+|\\-)\\s*\\d+%)\\s+)Moxie$|" + "^Moxie(?:\\s+((?:\\+|\\-)\\s*\\d+%))$", ReturnFirstGroup ], ["hp", "^Maximum HP(?:\\s+((?:\\+|\\-)\\s*\\d+))$"], ["mp", "^Maximum MP(?:\\s+((?:\\+|\\-)\\s*\\d+))$"], ["hpmp", "^Maximum HP/MP(?:\\s+((?:\\+|\\-)\\s*\\d+))$"], [ "weapon", "^(?:((?:\\+|\\-)\\s*\\d+)\\s+)Weapon Damage$|" + "^Weapon Damage(?:\\s+((?:\\+|\\-)\\s*\\d+))$", ReturnFirstGroup ], ["weaponpct", "^Weapon Damage(?:\\s+((?:\\+|\\-)\\s*\\d+%))$"], [ "ranged", "^(?:((?:\\+|\\-)\\s*\\d+)\\s+)Ranged Damage$|" + "^Ranged Damage(?:\\s+((?:\\+|\\-)\\s*\\d+))$", ReturnFirstGroup ], ["rangedpct", "^Ranged Damage(?:\\s+((?:\\+|\\-)\\s*\\d+%))$"], [ "spell", "^(?:((?:\\+|\\-)\\s*\\d+)\\s+)Spell Damage$|" + "^Spell Damage(?:\\s+((?:\\+|\\-)\\s*\\d+))$", ReturnFirstGroup ], [ "spellpct", "^(?:((?:\\+|\\-)\\s*\\d+%)\\s+)Spell Damage$|" + "^Spell Damage(?:\\s+((?:\\+|\\-)\\s*\\d+%))$", ReturnFirstGroup ], [ "init", "^(?:((?:\\+|\\-)\\s*\\d+%)\\s+)Combat Initiative$|" + "^Combat Initiative(?:\\s+((?:\\+|\\-)\\s*\\d+%))$", ReturnFirstGroup ], ["da", "^Damage Absorption(?:\\s+((?:\\+|\\-)\\s*\\d+))$"], ["dr", "^Damage Reduction:(?:\\s+((?:\\+|\\-)?\\s*\\d+))$"], [ "fumble", "^(Never) Fumble$|^(Reduced chance) of fumbling$|" + "^([\\d.]+x) chance of Fumble$", ReturnFirstGroup ], ["meat", "^(?:((?:\\+|\\-)\\s*\\d+%)\\s*)Meat from Monsters$"], ["ml", "^(?:((?:\\+|\\-)\\s*\\d+)\\s*)to Monster Level$"], ["crit", "^(?:((?:\\+|\\-)?\\s*\\d+(?:x|%))\\s*)chance of Critical Hit$"], ["mpreduce", "^(?:((?:\\+|\\-)?\\s*\\d+)\\s*)MP to use Skills$"], ["hobo", "^(?:((?:\\+|\\-)?\\s*\\d+)\\s*)Hobo Power$"], [ "rollover", "^(?:((?:\\+|\\-)?\\s*\\d+)\\s*)Adventure\\(s\\) per day when equipped\\.?$" ], [ "pvp", "^(?:((?:\\+|\\-)?\\s*\\d+)\\s*)PvP fight\\(s\\) per day when equipped\\.?$" ], [ "items", "^(?:((?:\\+|\\-)\\s*\\d+%)\\s*)([\\s\\S]+)\\s*Drops? from " + "([\\s\\S]+)( \\(.+\\))?$", function () { return Strip(arguments[1] + " " + arguments[2] + (arguments[3] || " ") + (arguments[3] || "") + (arguments[4] || " ") + (arguments[4] || "") ); } ], [ "allattributes", "^All Attributes(?:\\s+((?:\\+|\\-)\\s*\\d+))$", ReturnFirstGroup ], [ "allattributespct", "^All Attributes(?:\\s+((?:\\+|\\-)\\s*\\d+%))$", ReturnFirstGroup ], ["fam", "^(?:((?:\\+|\\-)\\s*\\d+)\\s*)(?:to )?Familiar Weight$"], ["songs", "^Allows you to keep (\\d+) songs in your head instead of 3\\.$"], [ "mpregen", "^Regenerate ([\\d-]*) MP per adventure( \\(.+\\))?$", function () { return Strip(arguments[1] + " " + (arguments[2] || "")); } ], ["hpregen", "^Regenerate ([\\d-]*) HP per adventure$"], ["hpmpregen", "^Regenerate ([\\d-]*) HP and(?: $1)? MP per adventure$"], [ "stats", "^(?:((?:\\+|\\-)?\\s*.+)\\s*)(Muscle|Mysticality|Moxie)?" + "\\s*Stat(?:s|\\(s\\)) Per Fight$", "$1" + (" $2" || "") ], ["combatfreq", "^Monsters will be (more|less) attracted to you\\.?$"], ["intrinsic", "Intrinsic effect: \\[\\[([\\s\\S]*)\\]\\]"], [ "res", "^(Slight|So-So|Serious|Stupendous|Superhuman|Sublime) " + "(?:Resistance to (All Elements)|" + "(Cold|Hot|Sleaze|Spooky|Stench) Resistance)$", function () { return arguments[1] + " " + (arguments[2] || arguments[3]); } ], [ "slimeres", "^(Slight|So-So|Serious|Stupendous|Superhuman|Sublime) Slime Resistance$" ], [ "elt", "^(?:((?:\\+|\\-)?\\s*\\d+)\\s*)\\{\\{element\\|" + "(Cold|Hot|Sleaze|Spooky|Stench)\\}\\}$", "$1 $2" ], [ "eltspell", "^(?:((?:\\+|\\-)?\\s*\\d+)\\s*)(?:Damage )?to \\{\\{element\\|" + "(Cold|Hot|Sleaze|Spooky|Stench)\\|Spells\\}\\}$", "$1 $2" ], [ "tunespell", "^All Spells Cast Are (Cold|Hot|Sleazy|Spooky|Stinky)$", function () { switch (arguments[1]) { case "Cold": return "Cold"; case "Hot": return "Hot"; case "Sleazy": return "Sleaze"; case "Spooky": return "Spooky"; case "Stinky": return "Stench"; } } ], [ "damagevs", "^(?:((?:\\+|\\-)?\\s*\\d+)\\s*)Damage vs\\. ([\\s\\S]+)$", "$1 $2" ], [ "passive", "^Deals ([\\d-]*) \\{\\{element\\|(Cold|Hot|Sleaze|Spooky|Stench)\\}\\} " + "to attackers$|" + "^Deals ([\\d-]*) damage to attackers$|" + "^Damages Attacking Opponent\\s*(\\(.+\\))?$", function () { return (arguments[1] && arguments[1] + " " + arguments[2]) || arguments[3] || arguments[4] || "some"; } ], [ "weaken", "Successful hit weakens opponent\\.", ], [ "dbcombat", "(?:((?:\\+|\\-)?\\s*\\d+)\\s*) damage to Disco Bandit Combat Skills" ], ["diver", "Makes you a better diver"], [ "oncritical", "^On Critical: ([\\s\\S]+)$|^([\\s\\S]+) on Critical Hit$|" + "^Critical Hits ([\\s\\S]+)$", ReturnFirstGroup ] ]; //songs has no mech page //intrinsic has no mech page //dbcombat has no mech page MechanicsLinks = [ [["mus", "muspct", "allattributes", "allattributespct"], "Muscle Modifiers"], [ ["mys", "myspct", "allattributes", "allattributespct"], "Mysticality Modifiers" ], [["mox", "moxpct", "allattributes", "allattributespct"], "Moxie Modifiers"], [["hp", "hpmp"], "HP Increasers"], [["mp", "hpmp"], "MP Increasers"], [["weapon", "weaponpct", "elt", "damagevs"], "Bonus Weapon Damage"], [["ranged", "rangedpct"], "Bonus Ranged Damage"], [["spell", "spellpct", "eltspell"], "Bonus Spell Damage"], [["init"], "Combat Initiative"], [["da"], "Damage Absorption"], [["dr"], "Damage Reduction"], [["fumble"], "Fumble Chance"], [["meat"], "Meat from Monsters"], [["items"], "Items from Monsters"], [["ml", "weaken"], "Monster Level"], [["crit"], "Critical Hit Chance"], [["mpreduce"], "Skill MP Cost Modifiers"], [["hobo"], "Hobo Power"], [["rollover"], "Extra Rollover Adventures"], [["pvp"], "Extra PvP Fights"], [["fam"], "Familiar Weight"], [["hpregen", "hpmpregen"], "HP Restorers"], [["mpregen", "hpmpregen"], "MP Restorers"], [["stats"], "Stat Gains from Fights"], [["combatfreq"], "Combat Frequency"], [["res"], "Elemental Resistance"], [["slimeres"], "Slime Resistance"], [["tunespell"], "Elemental Spell Damage"], [["passive"], "Passive Damage"], [["oncritical"], "Critical Hit"], [["diver"], "Underwater adventuring"] ]; // plurals of weapons > 1 instance, for use in categories var WeaponPlurals = []; WeaponPlurals["blowgun"] = "blowguns"; WeaponPlurals["rifle"] = "rifles"; WeaponPlurals["yoyo"] = "yoyos"; WeaponPlurals["banjo"] = "banjos"; WeaponPlurals["bow"] = "bows"; WeaponPlurals["horn"] = "horns"; WeaponPlurals["saucepan"] = "saucepans"; WeaponPlurals["whistle"] = "whistles"; WeaponPlurals["accordion"] = "accordions"; WeaponPlurals["boomerang"] = "boomerangs"; WeaponPlurals["flute"] = "flutes"; WeaponPlurals["umbrella"] = "umbrellas"; WeaponPlurals["drum"] = "drums"; WeaponPlurals["guitar"] = "guitars"; WeaponPlurals["pistol"] = "pistols"; WeaponPlurals["axe"] = "axes"; WeaponPlurals["chefstaff"] = "chefstaves"; WeaponPlurals["knife"] = "knives"; WeaponPlurals["spear"] = "spears"; WeaponPlurals["flail"] = "flails"; WeaponPlurals["polearm"] = "polearms"; WeaponPlurals["whip"] = "whips"; WeaponPlurals["utensil"] = "utensils"; WeaponPlurals["crossbow"] = "crossbows"; WeaponPlurals["staff"] = "staves"; WeaponPlurals["club"] = "clubs"; WeaponPlurals["sword"] = "swords"; // style of the replacement text nudge var NudgeStyle = "font-size:200%; font-weight:bold;"; /******************************************************************************/ // extract descid function GetDescID() { Pat = Compile( "http://(?:127\\.0\\.0\\.1\\:600\d+|" + "(?:www\\d*\\.kingdomofloathing\\.com))" + "/desc_item\\.php\\?whichitem=([\\w\\-]+)" ); Match = Pat.exec(document.location) if (Match) { return Match[1]; } else { return ""; } } // string repeater function Repeat(InStr, Freq) { var OutStr = ""; for (var N = 0; N < Freq; N++) { OutStr += InStr; } return OutStr; } // count the number of times a substring appears in a string function Count(InString, SubString) { // don't want case insensitivity this time var Pat = new RegExp(Escape(SubString), "g"); var NumMatches = 0; var Match; var TempString = InString; do { Match = Pat.exec(TempString); NumMatches += (Match != null); } while (Match != null); return NumMatches; } // prettyprint arrays for debugging function PrintArray(InArr) { var OutStr = ""; if (typeof(InArr) != "number") { OutStr += "["; for (var N = 0; N < InArr.length; N++) { OutStr += PrintArray(InArr[N]); } OutStr += "]"; } else { OutStr += " " + InArr + " "; } return OutStr; } // strip leading/trailing whitespace function Strip(InStr) { return InStr.replace(/^\s*([\s\S]*?)\s*$/, "$1"); } // entity converter function Entity2Unicode(InStr) { var Element = document.createElement("textarea"); Element.innerHTML = InStr; return Element.value; } // for lazy typists function Compile(Pat) { return new RegExp(Pat, "ig"); } // escapes special RE characters function Escape(InString) { // it's the backslash plague! var Pat = Compile("([.\\\\[\\](){}+\\-*?:!^$,|])"); return InString.replace(Pat, "\\$1"); } // find next place in given text where "parens" are matched function FindBalancePos(InStr, Open, Close) { var PatOpen = Compile(Escape(Open)); var PatClose = Compile(Escape(Close)); var Pat = Compile("(" + Escape(Open) + ")|(" + Escape(Close) + ")"); var NumOpen = NumClose = 0; var Match, GroupNum, NumOpen, NumClose; do { Match = Pat(InStr); if (Match[1]) { GroupNum = 1; NumOpen += 1; } if (Match[2]) { GroupNum = 2; NumClose += 1; } if (NumOpen == NumClose) { return Pat.lastIndex; } } while (Match != null); } // function to combine string indices to determine how much has been parsed // e.g., using slice-style notation, if [[1, 5], [10, 12], [15, 19]] of a // string has been "used" and the next parsing operation uses [11, 14], // this function returns the combined range [[1, 5], [10, 14], [15, 19]] function CombineRanges(Old, Add) { if (Old.length == 0) { return [Add]; } else { var OutList = new Array; var Existing, First, Pos, Current, TempRange; // find first existing range that comes after the add for (var N = 0; N < Old.length; N++) { Existing = Old[N]; if (Add[0] <= Existing[1]) { First = N; break; } if (N == Old.length - 1) { First = Old.length; } } // ranges before this one do not overlap; output if (First != 0) {OutList = OutList.concat(Old.slice(0, First));} TempRange = [Add[0], Add[1]]; if (First == Old.length) { // the add is past all existing ranges OutList.push(TempRange); } else { // the difficult case: the add is between extremes, and possibly // overlaps existing ranges Pos = First; while (Pos < Old.length) { Current = Old[Pos]; if ( (Current[0] <= TempRange[0] && TempRange[0] <= Current[1]) || (Current[0] <= TempRange[1] && TempRange[1] <= Current[1]) || (TempRange[0] <= Current[0] && Current[0] <= TempRange[1]) || (TempRange[0] <= Current[1] && Current[0] <= TempRange[1]) ) { // condense add with current if overlapped TempRange = [ Math.min(TempRange[0], Current[0]), Math.max(TempRange[1], Current[1]) ]; // output if last in loop if (Pos == Old.length - 1) { OutList.push(TempRange); } } else { // not overlapped, so append and quit loop OutList.push(TempRange); break; } Pos += 1; } // tack on rest of list if (Pos <= Old.length) { for (var N = Pos; N < Old.length; N++) { OutList.push(Old[N]) } } } } return OutList; } // calculate what parts of a string remain unparsed by flipping the parsed bits // R is a list of 2-long lists, which are the slice delimiters of the parsed // areas; Length is the total length of the input text // returns a list of 2-long lists function ComplementRange(R, Length) { if (R.length == 0) { return [[0, Length]]; } else { var OutList = new Array; var Current = new Array; var Next = new Array; for (var Pos = 0; Pos < R.length; Pos++) { // check if first range starts at first character if (Pos == 0 && R[0][0] > 0) { OutList.push([0, R[0][0]]); } Current = R[Pos]; if (Pos == R.length - 1) { // last range--output from end if applicable if (Current[1] < Length) { OutList.push([Current[1], Length]); } } else { // there is a next range--compute the unused space between Next = R[Pos + 1]; OutList.push([Current[1], Next[0]]); } } } return OutList; } // function that returns a list with two components // the second list component is a list of strings from the input text that // weren't parsed // the first is a dictionary of parsed fields from the HTML source // call: // *InText is the HTML source. // *Fields is a list of lists instructing how each field is extracted from the // source. // **The first component of each inner list is the field's name (text); the // output dictionary uses this as a key. // **The second is a RE, possibly with groups, identifying how to extract // from the HTML. // ***If it has groups, it will return the first group as the key value on // a match as a default; if it has no groups, it returns "1" on a match; on // a non-match, it returns a blank string. The defaults can be overridden // by the third component. // **The third list component is optional, to override the default extraction // of field values. It performs RE replacement according to the RE pattern // (input previously) and the substitution string/function (the third list // component). // e.g., Fields could be: // [ // [ // "req", // "<br>(Muscle|Mysticality|Moxie) Required: <b>(\\d+?)</b>", // "$1 $2" // ], // [ // "class", // "<p><font color=blue><b>Only " // "(Seal Clubbers|Turtle Tamers|Pastamancers|" // "Saucerors|Disco Bandits|Accordion Thieves) " // "may use this item.</b></font>", // Y // where Y is some function that takes a match object as input // ] // ] // *SingleParse is true/false, indicating whether the parser should stop once // the first pattern matches. Used for parsing enchantments one at a time; // no need to try every pattern once a match is found. function Parser(InText, Fields, SingleParse) { if (SingleParse == null) {SingleParse = true;} var Item = []; var Parsed = new Array(0); var Unparsed, UnparsedStrings, SubStr; var Field, Pat, Match, Name, Extract; for (var N = 0; N < Fields.length; N++) { Field = Fields[N]; Pat = Compile(Field[1]); Match = Pat.exec(InText); Name = Field[0]; if (Field.length > 2) { Extract = Field[2]; } else { // default is 1 or group 1 if (Match != null && Match.length > 1) { Extract = "$1"; } else { Extract = "1"; } } if (Match) { Parsed = CombineRanges(Parsed, [Match.index, Pat.lastIndex]); Item[Name] = Strip(Match[0].replace(Pat, Extract)); } else { if (! SingleParse) {Item[Name] = "";} } if (Match && SingleParse) {break;} } // done parsing requested fields; see which bits of the input weren't parsed Unparsed = ComplementRange(Parsed, InText.length); // can safely strip out remaning HTML tags and leading/trailing spaces PatTag = Compile("<(?!\!--)/?[\\s\\S]+?>"); UnparsedStrings = []; for (N = 0; N < Unparsed.length; N++) { SubStr = Unparsed[N]; TempStr = Strip(InText.slice(SubStr[0], SubStr[1]).replace(PatTag, "")); if (TempStr != "") { UnparsedStrings.push(TempStr); } } return [Item, UnparsedStrings]; } // clean desc field function CleanDescKoL(Body) { // save to retain comments Body = Strip(Body); var OrigBody = Body; PatPlan = Compile( "This detailed set of plans will teach you how to smith a fancy new " + "item:\n" + "<p><center><table style='border: 1px solid black;' cellpadding=5>" + "<tr><td align=center><img style='vertical-align: middle' " + "class=hand src='http://images\.kingdomofloathing\.com/itemimages/" + ".+?\.gif' onclick='descitem\(.+?\)'><br><b>" + "(.+?)</b></td></tr></table>" ) Body = Body.replace(PatPlan, "{{plans|$1}}"); Body = Body.replace(/\s+/ig, " "); // multispace -> space Body = Body.replace(/\s*<\s*br\s*\/?>\s*/ig, "<br />"); // standard <br /> Body = Body.replace(/\s*<\/p>\s*/ig, ""); // strip closing </p> Body = Body.replace(/(<\/?)super>/ig, "$1sup>"); // <super> -> <sup> // small fonts: these can be nested, but none so far, so let's ignore it... Body = Body. replace( /<font size\s*=\s*\"?1\"?>([\s\S]*?)<\/font>/ig, "<small>$1</small>" ); // handle whitespace/tag weirdness var PatOpen = Compile("(<(?!/)[^>]*?>)\\s+"); var PatClose = Compile("\\s+(</[^>]*?>)"); var Temp; while (true) { Temp = Body.replace(PatOpen, " $1"); Temp = Temp.replace(PatClose, "$1 "); if (Temp == Body) {break;} Body = Temp; } // swap order of open/close <i/b> with <br />s var PatIB = Compile("(.*?)(<(?:b|i)>)(<br />)(.*?)"); var PatIB2 = Compile("(.*?)(<br />)(</(?:b|i)>)(.*?)"); while (true) { Temp = Body; Body = Body.replace(PatIB, "$1$3$2$4"); Body = Body.replace(PatIB2, "$1$3$2$4"); if (Temp == Body) {break;} } Body = Body.replace(/<br \/>\s*<p>/ig, "<p>"); // break-p renders to p Body = Body.replace(/(\s*<p>\s*){1,}/ig, "<p>"); // multiple p to single Body = Body.replace(/\s*<p>\s*/ig, "<br /><br />"); // <p> -> two breaks // break-comment -> comment-break PatBreakComment = Compile("(<br />)\\s*(<!--[\\s\\S]*?-->)"); while (true) { Temp = Body; Body = Body.replace(PatBreakComment, "$2$1"); if (Temp == Body) {break;} } // strip breaks from beginning/end while (true) { Temp = Body; Body = Body.replace(/^<br \/>|<br \/>$/ig, ""); if (Temp == Body) {break;} } // collapse multiple <br />s into linebreaks Body = Body.replace( /((?:<br \/>){2,})/ig, function Break2Newline() { var Breaks = arguments[1].length / 6; return Repeat("\n", Breaks); } ); Body = Body.replace(/<br \/>/ig, "<br />\n"); Body = Body.replace(/ *\n */ig, "\n"); // balance <i/b/small> for breaks var NewBody = ""; var NumI = 0; var NumB = 0; var NumS = 0; var Line; var Lines = Body.split("\n"); var PatNowiki = /^([\[\]*#;:{}])(.*)/i; for (var N = 0; N < Lines.length; N++) { Line = Lines[N]; if ((NumB || NumI || NumS) && Line != "") { Line = Repeat("<b>", NumB) + Repeat("<i>", NumI) + Repeat("<small>", NumS) + Line NumI = NumB = NumS = 0 } var TagArray = ["i", "b", "small"]; var TagOpen, TagClose; for (var M = 0; M < TagArray.length; M++) { Tag = TagArray[M]; TagOpen = "<" + Tag + ">"; TagClose = "</" + Tag + ">"; if (Count(Line, TagOpen) > Count(Line, TagClose)) { BR = (Line.lastIndexOf("<br />") == Line.length - 6) Line = Line.slice(0, -6 * BR) + TagClose + Repeat("<br />", BR); NumB += (Tag == "b"); NumI += (Tag == "i"); NumS += (Tag == "small"); } } Line = Line.replace(PatNowiki, "$1$2"); NewBody += Line + "\n"; } Body = Strip(NewBody); // swap syntax Body = Body.replace(/<\/?b>/ig, "'''"); Body = Body.replace(/<\/?i>/ig, "''"); // reset comments var PatComment = Compile("(<!--[\\s\\S]*?-->)") var PatComment2 = Compile("(\\n*<!--[\\s\\S]*?-->)") var NewBody = ""; var Prev = 0; var New, Orig; do { Match = PatComment.exec(Body); Orig = PatComment2.exec(OrigBody); if (Match) { NewBody += Body.slice(Prev, Match.index) + Orig[1]; Prev = Match.index + Match[0].length; } } while (Match != null); Body = NewBody + Body.slice(Prev); return Body; } // clean up enchantment text for {{item}} // this is not a complete enchantment parser function StandardiseEnchantmentText(InText) { if (! InText) { return InText; } else { var CleanedList = [], Parsed; // process each line separately Enchantments = InText.split("<br>"); for (var N = 0; N < Enchantments.length; N++) { Enchantment = Enchantments[N]; var Parsed = Parser(Enchantment, PatsEnchantmentClean); if (Parsed[1].length) { // if unparsed components, just use the text as-is CleanedList.push(Parsed[1][0]); } else { for (var Key in Parsed[0]) { // should be only one if (Parsed[0][Key]) { CleanedList.push(Parsed[0][Key]); } else { CleanedList.push(""); } } } } return CleanedList.join("<br />"); } } // parse enchantments into types so we know what See Also links are needed // not everything is caught, as there are many exceptional cases function ParseEnchantment(Item) { var Enchantments = Item["enchantment"]; if (Enchantments == "") { return null; } else { var PatParen = Compile("^\\([^()]+\\)$"); Enchantments = Enchantments.split("<br />"); // remove blank entries // have to handle N manually, as array is being edited for (var N = 0; N < Enchantments.length;) { if (Enchantments[N] == "") { Enchantments.splice(N, 1); } else { N += 1; } } var EnchantmentDict = []; // if next is parenthetical, append to previous var NewEnchantments = []; var Total = Enchantments.length; var Used = false; while (Enchantments.length != 0) { if (Enchantments.length == 1) { NewEnchantments.push(Enchantments[0]); Enchantments.splice(0, 1); } else { if (PatParen.exec(Enchantments[1])) { NewEnchantments.push(Enchantments[0] + " " + Enchantments[1]); Enchantments.splice(0, 2); } else { NewEnchantments.push(Enchantments[0]); Enchantments.splice(0, 1); } } } // parsing var Unparsed = []; for (var N = 0; N < NewEnchantments.length; N++) { Enchantment = NewEnchantments[N]; Parsed = Parser(Enchantment, PatsEnchantmentParse); if (Parsed[1].length) { Unparsed.push(Parsed[1][0]); } else { for (var Key in Parsed[0]) { // should be only one if (Key in EnchantmentDict) { EnchantmentDict[Key].push(Parsed[0][Key]); } else { EnchantmentDict[Key] = [Parsed[0][Key]]; } } } } return [EnchantmentDict, Unparsed]; } } // clean up parser output function GeneralCleanupKoL(Item) { // switch in haiku format if applicable if (Item["haiku"]) { Item["name"] = Item["haikuname"]; Item["desc"] = Item["haikudesc"]; delete(Item["haikuname"]); delete(Item["haikudesc"]); } Item["desc"] = CleanDescKoL(Item["desc"]); Item["enchantment"] = StandardiseEnchantmentText(Item["enchantment"]); Item["parsedenchantment"] = ParseEnchantment(Item); // suppressed level requirements if (! Item["level"]) { var Type = Item["type"]; if (Type == "food" || Type == "beverage" || Type == "booze") { Item["level"] = "1"; } } // weapon types and subtypes var PatWeapon = Compile("^(ranged )?weapon \\((\\d)-handed (.*)\\)$"); var Weapon = PatWeapon.exec(Item["type"]); Item["weapon"] = Repeat("1", (Weapon != null)); Item["weaponrange"] = ((Weapon && Weapon[1] && String(Weapon[1])) || ""); Item["weaponhands"] = ((Weapon && String(Weapon[2])) || ""); var WeaponType = ((Weapon && String(Weapon[3])) || ""); Item["weapontype"] = WeaponType; if (Weapon && ! Item["weaponrange"]) { if ( WeaponType == "saucepan" || WeaponType == "utensil" || WeaponType == "chefstaff" || Item["stat"] == "Mysticality" ) { Item["weaponrange"] = "mysticality"; } else { Item["weaponrange"] = "melee"; } } } // convert parsed item into a Wiki article // <GM_ItemParser_Nudge></GM_ItemParser_Nudge> surround text // which is to be formatted specially to remind users not to blindly // copy & paste function ConvertItem( Item, ItemID, Desc, IotM, NeedsContent, NeedsSpading, Recipe ) { // helper to make looping through consecutive {{item}} parameters // less painful to read function ListLooper(InArr) { var X; for (N = 0; N < InArr.length; N++) { X = InArr[N]; if (Item[X]) {OutStr += "|\n" + X + "=" + Item[X];} } } var OutStr = ""; var ItemType = Item["type"]; var Usable = (ItemType.indexOf("usable") > -1); var Potion = (ItemType == "potion"); var Combat = Boolean(ItemType.indexOf("combat") > -1 || Item["alsocombat"]); var Familiar = (ItemType == "familiar"); var Food = (ItemType == "food" || ItemType == "beverage"); var Booze = (ItemType == "booze"); var Spleen = Boolean(Usable && Item["level"] && ! Item["class"]); // calculate a range of dates to automatically input Mr. Store dates var DateVar = new Date(); var Year = DateVar.getUTCFullYear(); var Month = DateVar.getUTCMonth(); var Day = DateVar.getUTCDate(); if (Day >= 20) { Month = (Month + 1) % 12; Year += (Month == 0); } Year = Year.toString(); Month = Month.toString(); Month = [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ][Month]; var DateString = Month + " " + Year; if (ItemID == null) {ItemID = "";} if (Desc == null) {Desc = "";} if (NeedsContent) { OutStr += "{{NeedsContent|<GM_ItemParser_Nudge>wut content</GM_ItemParser_Nudge>}}"; "</GM_ItemParser_Nudge>}}"; } if (NeedsSpading) { if (OutStr != "") {OutStr += "\n";} OutStr += "{{NeedsSpading|<GM_ItemParser_Nudge>spade pls</GM_ItemParser_Nudge>}}"; } // {{item}} if (OutStr != "") {OutStr += "\n";} OutStr += "{{item|\nitemid=" + ItemID + "|\ndescid=" + Desc; ListLooper([ "desc", "paste", "smith", "cocktail", "cook", "jewelry", "type", "power", "powertype" ]); if (Item["stat"] || Item["statreq"]) { OutStr += "|\nstat=" + Item["stat"] + "|\nstatreq=" + Item["statreq"]; } ListLooper(["level", "alsocombat", "outfit"]); if (Item["nodiscard"]) {OutStr += "|\nautosell=0";} ListLooper(["autosell", "cost", "notrade", "gift", "quest", "enchantment"]); ListLooper([ "critical", "nohardcore", "mpreduce", "limit", "hagnk", "class", "spelltype", "grantskill", "lounge", "bluenote", "haiku" ]); OutStr += "}}"; // other standard sections if (Recipe) { OutStr += "\n\n==Recipe=="; OutStr += "<GM_ItemParser_Nudge>\ntailor to suit</GM_ItemParser_Nudge>" + "\n{| class=\"recipe\"" + "\n|- class=\"row1\"" + "\n! {{cocktail}}" + "\n| [[ingredient 1]]" + "\n| [[ingredient 2]]" + "\n|- class=\"row2\"" + "\n! {{equals}}" + "\n| colspan=\"2\" | [[product]]" + "\n|}" } else { if (IotM) { OutStr += "\n\n==Obtained From==\n;Stores\n:[[Mr. Store]] " + "(<GM_ItemParser_Nudge>1 [[Mr. Accessory]]</GM_ItemParser_Nudge>)"; } else { OutStr += "\n\n==Obtained From==" + "<GM_ItemParser_Nudge>" + "\n;[[Location]]\n:[[Monster]]\n:''[[Non-Combat]]''" + "\n;Items\n:[[Item]] (0-1) (''sometimes'')" + "\n;Stores\n:[[Store]] (0 Meat)" + "</GM_ItemParser_Nudge>"; }} if (Potion || Usable || Combat || Familiar || Food || Booze || Spleen) { if (Food || Booze || Spleen) { if (Spleen) { OutStr += "\n\n==When Used=="; } else { OutStr += "\n\n==When Consumed=="; } OutStr += "\n{{useitem|\ntext=<GM_ItemParser_Nudge>?" + "</GM_ItemParser_Nudge>"; OutStr += "|\nadv=|\nmus=gain |\nmys=gain |\nmox=gain |"; OutStr += "\n<GM_ItemParser_Nudge>posteffect={{acquireEffect|effect=?|" + "duration=?}}|</GM_ItemParser_Nudge>\ntype="; if (Food) {OutStr += "food";} if (Booze) {OutStr += "booze";} if (Spleen) {OutStr += "spleen";} OutStr += "|\nlimiter=<GM_ItemParser_Nudge>?</GM_ItemParser_Nudge>" + "<!-- when the limiter is found, the " + "value in the category tag needs to be filled out too -->}}"; } if (Potion || (! Spleen && Usable) || Combat || Familiar) { OutStr += "\n\n==When Used=="; var MultiUses = ((Usable + Potion + Combat + Familiar) > 1); if (Familiar) { OutStr += Repeat("\n*''From inventory:''", MultiUses) + "\n{{useitem|\ntext=<GM_ItemParser_Nudge>?</GM_ItemParser_Nudge>" + "|\ntype=familiar}}"; } if (Combat) { OutStr += Repeat("\n*''In combat:''", MultiUses) + "\n{{useitem|\ntext=<GM_ItemParser_Nudge>?</GM_ItemParser_Nudge>" + "|\ntype=combat}}"; } if (Usable || Potion) { OutStr += Repeat("\n*''From inventory:''", MultiUses) + "\n{{useitem|\ntext=<GM_ItemParser_Nudge>?" + "|\neffect={{acquireEffect|effect=?|duration=?}}" + "</GM_ItemParser_Nudge>}}"; } } } if ( Item["paste"] || Item["smith"] || Item["cook"] || Item["cocktail"] || Item["jewelry"] ) { OutStr += "\n\n==Uses==\n*[[]]"; } var Notes = false; if (Item["type"] == "familiar") { OutStr += "\n\n==Notes==\n*Becomes a [[<GM_ItemParser_Nudge>" + "Some Familiar</GM_ItemParser_Nudge>]]."; Notes = true; } if (IotM) { if (! Notes) {OutStr += "\n\n==Notes==";} OutStr += "\n*" + DateString + "'s " + "special of the month from [[Mr. Store]]." + "\n*Its [[Mr. Store]] description was:" + "\n*:<GM_ItemParser_Nudge>Description</GM_ItemParser_Nudge>" } // See Also ParsedEnchantments = Item["parsedenchantment"]; if (ParsedEnchantments) { ParsedEnchantments = ParsedEnchantments[0]; // clumsy, but it works var HasEnchantments = false; for (var Props in ParsedEnchantments) { HasEnchantments = true; break; } if (HasEnchantments) { OutStr += "\n\n==See Also=="; for (var N = 0; N < MechanicsLinks.length; N++) { for (var M = 0; M < MechanicsLinks[N][0].length; M++) { if ( MechanicsLinks[N][0][M] in ParsedEnchantments || ( // handle shields specially MechanicsLinks[N][0][M] == "dr" && ItemType == "off-hand item (shield)" && Item["power"] && Item["powertype"] == "Damage Reduction" ) ) { OutStr += "\n*[[" + MechanicsLinks[N][1] + "]]"; break; } } } if ("elt" in ParsedEnchantments) { var Prismatic = true; var Elements = ["Cold", "Hot", "Sleaze", "Spooky", "Stench"]; for (var N = 0; N <= Elements.length; N++) { var Element = Elements[N]; var PatElt = Compile("\\b" + Element + "\\b"); var Matched = []; for (var M = 0; M <= ParsedEnchantments["elt"].length; M++) { if (PatElt.test(ParsedEnchantments["elt"])) { Matched.push(1); break; } } if (! Matched.length) { Prismatic = false; break; } } if (Prismatic) {OutStr += "\n*[[Prismatic Damage]]";} } } } // collection/category if (! Item["quest"]) { OutStr += "\n\n==Collection==\n<collection>" + ItemID + "</collection>"; } if (IotM) { OutStr += "\n\n{{iotm|duration=" + "<GM_ItemParser_Nudge>" + DateString + "</GM_ItemParser_Nudge>|before=<GM_ItemParser_Nudge>" + "sum nub 4got 2 update dis txt lol</GM_ItemParser_Nudge>|after=}}" } if (Food) { OutStr += "\n\n[[Category:Food (By Fullness)|?, " + Item["name"].toLowerCase() + "]]"; } if (Booze) { OutStr += "\n\n[[Category:Booze (By Drunkenness)|?, " + Item["name"].toLowerCase() + "]]"; } if (Spleen) { OutStr += "\n\n[[Category:Spleentacular Items (By Spleen Damage)|?, " + Item["name"].toLowerCase() + "]]"; } if (Item["weapon"]) { var RangeType = Item["weaponrange"]; RangeType = RangeType.slice(0, 1).toUpperCase() + RangeType.slice(1) + " Weapons"; OutStr += "\n\n[[Category:" + RangeType + "]]"; OutStr += "\n[[Category:" + Item["weaponhands"] + "-Handed Weapons]]"; var OtherType; try { OtherType = WeaponPlurals[Item["weapontype"]]; OtherType = OtherType.slice(0, 1).toUpperCase() + OtherType.slice(1); } catch (Exception) { OtherType = "Other Weapons"; } OutStr += "\n[[Category:" + OtherType + "]]"; } return OutStr; } /******************************************************************************/ // convert text to be displayed in a web browser function Text2Web(InStr) { return InStr.replace(/</ig, "<").replace(/>/ig, ">") .replace(/\n/ig, "<br />") } // user interface helper function CreateOption(Parent, Name, Type, Label, OtherAttributes) { var PatEvent = /^on[\w]*$/i; if (Parent.innerHTML != "") { Parent.appendChild(document.createElement("br")); } var NewNode = document.createElement("span"); var NewOption = document.createElement("input"); NewOption.setAttribute("type", Type); NewOption.setAttribute("name", Name); if (OtherAttributes) { var CurrAttr; for (var N = 0; N < OtherAttributes.length; N++) { CurrAttr = OtherAttributes[N]; if (PatEvent.test(CurrAttr[0])) { NewOption.addEventListener( CurrAttr[0].slice(2).toLowerCase(), CurrAttr[1], true ); } else { NewOption.setAttribute(CurrAttr[0], CurrAttr[1]); } } } NewNode.appendChild(NewOption); NewNode.appendChild(document.createTextNode(" " + Label)); Parent.appendChild(NewNode); } // parse item from pop-up/user options and return a string to display // for copy-paste // Doc is the document node of the window being parsed (either the // original pop-up or the replicated tab); the rest are the user checkboxes function Item2String(Doc, ItemID, IotM, NeedsContent, NeedsSpading, Recipe) { var Desc = Doc.getElementById("description"); var DescID = Desc.getAttribute("GM_ItemParser_DescID"); PageText = Entity2Unicode( document.getElementById("description").innerHTML.replace(/<\/p>/ig, "") ).replace(/<script[^>]*?>[\s\S]+?<\/script>/ig, ""); var Item = Parser(PageText, PatsKoL, false); GeneralCleanupKoL(Item[0]); var OutString = ConvertItem( Item[0], ItemID, DescID, IotM, NeedsContent, NeedsSpading, Recipe ); // convert tags to display as text; style nudge notes OutString = Text2Web(OutString).replace(/<(\/?GM_ItemParser_Nudge)>/ig, "<$1>"); OutString = "<span style=\"font-family:Courier New;\">" + OutString.replace( /<GM_ItemParser_Nudge>/ig, "<span style=\"" + NudgeStyle + "\">" ).replace(/<\/GM_ItemParser_Nudge>/ig, "</span>") + "</span>"; // print unparsed bits var EnchantmentArray = Item[0]["parsedenchantment"]; if (EnchantmentArray && EnchantmentArray[1].length) { var UnparsedString = "<span style=\"color:red\">" + "Unparsed strings in enchantment (See Also " + "may need manual links):<ul>"; for (var N = 0; N < EnchantmentArray[1].length; N++) { UnparsedString += "<li>" + Text2Web(EnchantmentArray[1][N]) + "</li>"; } UnparsedString += "</ul><hr /></span>"; OutString = UnparsedString + OutString; } if (Item[1].length) { var UnparsedString = "<span style=\"color:red\">" + "Unparsed portions of pop-up:<ul>"; for (var N = 0; N < Item[1].length; N++) { UnparsedString += "<li>" + Text2Web(Item[1][N]) + "</li>"; } UnparsedString += "</ul><hr /></span>"; OutString = UnparsedString + OutString; } if ("image" in Item[0]) { var PatImage = /^([\s\S]*)\/([^/]*\.gif)$/i; var KoLImage = Item[0]["image"].replace( PatImage, "<span style=\"font-family:Courier New;\">" + "{{kolimage|$1|$2<span style=\"" + NudgeStyle + "\">" + "|renamed=1</span>}}</span>" ) OutString = "Image copyright code:<br />" + KoLImage + "<br /><hr />" + OutString; } return OutString; } // refresh output on checkbox click function RefreshOutput(Event) { // haxx0r document node of new tab var Doc; if (Event) { var Curr = Event.target, Prev = Event.target; while (true) { Curr = Curr.parentNode; if (Curr == null) { Doc = Prev; break; } else { Prev = Curr; } } } else { Doc = document; } var Form = Doc.forms.namedItem("GM_ItemParser_Options"); var Input, ItemID, IotM, NeedsSpading, NeedsContent, Recipe if (Form) { Input = Form.elements.namedItem("GM_ItemParser_ID"); ItemID = Input.value; ItemID = Strip(ItemID.replace(/[^\d\-]/ig, "")); Input = Form.elements.namedItem("GM_ItemParser_IotM"); IotM = Input.checked; Input = Form.elements.namedItem("GM_ItemParser_NeedsContent"); NeedsContent = Input.checked; Input = Form.elements.namedItem("GM_ItemParser_NeedsSpading"); NeedsSpading = Input.checked; Input = Form.elements.namedItem("GM_ItemParser_Recipe"); Recipe = Input.checked; } else { ItemID = ""; IotM = false; NeedsSpading = false; NeedsContent = false; Recipe = false; } var Output = Doc.getElementById("GM_ItemParser_Output"); Output.innerHTML = Item2String(Doc, ItemID, IotM, NeedsContent, NeedsSpading, Recipe); } // replicate pop-up in tab/window with scrollbars enabled on button press function Main() { var Window; // have to act differently depending on whether browser window has scrollbars // if no scrollbars (default FF interface), the pop-up can't have scrollbars // added, so copy data into a new window with scrollbars enabled // if scrollbars (e.g., Tab Mix Plus), just put everything onto the // pop-up itself if (! window.scrollbars.visible) { var Node = document.getElementById("description"); Node.setAttribute("GM_ItemParser_DescID", GetDescID()); var OrigSource = document.body.innerHTML; Window = window.open( "", "GM_ItemParser_Window", "width = 640, height = 480, scrollbars = yes" ); Window.document.body.innerHTML = OrigSource; Node = Window.document.getElementById("GM_ItemParser_Activator"); Node.parentNode.removeChild(Node); } else { Window = window; } // add more options to page Window.document.body.appendChild(document.createElement("hr")); var Options = document.createElement("form"); Options.setAttribute("name", "GM_ItemParser_Options"); Options.setAttribute( "style", "float:left; border-style:solid; border-width:1px; " + "margin-right:20px; padding:5px" ); CreateOption( Options, "GM_ItemParser_IotM", "checkbox", "IotM", [["onClick", RefreshOutput]] ); CreateOption( Options, "GM_ItemParser_NeedsContent", "checkbox", "NeedsContent", [["onClick", RefreshOutput]] ); CreateOption( Options, "GM_ItemParser_NeedsSpading", "checkbox", "NeedsSpading", [["onClick", RefreshOutput]] ); CreateOption( Options, "GM_ItemParser_Recipe", "checkbox", "Recipe", [["onClick", RefreshOutput]] ); CreateOption( Options, "GM_ItemParser_ID", "text", "ItemID", [["size", "6"], ["onChange", RefreshOutput]] ); Window.document.body.appendChild(Options); var Instructions = document.createElement("div"); Instructions.setAttribute("id", "GM_ItemParser_Instructions"); Instructions.setAttribute("style", "display:inline"); Instructions.innerHTML = "<h2>Instructions</h2>" + "<p>Copy the item name under the image above. Paste that into the " + "KoLWiki's search box. If the article already exists, ur2slow :( " + "It may be necessary to disambiguate with parentheses (e.g., " + "<a href=\"http://kol.coldfront.net/thekolwiki/index.php/" + "Golden_ring_(item)\">golden ring</a>).</p>" + "<table><tr><td><ul><li>Check IotM to preload IotM skeleton</li>" + "<li>Check NeedsContent/Spading to preload tag</li>" + "<li>Check Recipe to preload a recipe template</li>" + "<li>Find the ItemID and input into the textbox " + "(<a href=\"http://kol.coldfront.net/thekolwiki/index.php/" + "Items_by_number\">how?</a>)</li>" + "<li>Copy and paste the output into the Wiki edit box</li>" + "<li>Anything in <span style=\"" + NudgeStyle + "\">this irritating " + "text style</span> will need to be edited, but " + "<b>preview everything</b> to make sure nothing went wrong</li>" + "<li><a href=\"http://kol.coldfront.net/thekolwiki/index.php/" + "Special:Upload\">Upload</a> the image and put the copyright code " + "in the summary box (remove \"|renamed=1\" for images uploaded " + "with their original names)</li>" + "<li>Create item metadata by clicking on the floating red link on the " + "item page and following the text hints</li></ul></td></tr></table><hr />" ; Window.document.body.appendChild(Instructions); var Output = document.createElement("div"); Output.setAttribute("id", "GM_ItemParser_Output"); Output.innerHTML = Item2String(document); Instructions.appendChild(Output); // asynch request should be done now; note if new version is available var WebVer = parseFloat(GM_getValue("WebVersion", "Error")); if (! isNaN(WebVer) && WebVer > CurrentVersion) { var NewElement = document.createElement('p'); NewElement.setAttribute("style", "text-align: center"); NewElement.style.fontSize = "small"; NewElement.innerHTML = "New KoL Wiki Helper version " + WebVer + " available: " + "<a href=\"" + ScriptURL + "\">" + ScriptURL + "</a><hr />"; document.body.insertBefore(NewElement, document.body.firstChild); } } /******************************************************************************/ // autoupdate code "adapted" from antimarty's fortune cookie script, // in turn "adapted" from someone else's script // such banditry function GM_get(Target, Callback) { GM_xmlhttpRequest({ method: 'GET', url: Target, onload: function(Details) { if (typeof Callback == "function") {Callback(Details.responseText);} } }); } // Check version number of script on the web function CheckScriptVersion(Data) { // Preemptively set error, in case request fails... GM_setValue("WebVersion", "Error") var M = Data.match( /<p>Version (\d+\.\d+)<\/p>/i ); if (M) {GM_setValue("WebVersion", M[1]);} } var Today = new Date(); var Year = String(Today.getUTCFullYear()); var Month = String(Today.getUTCMonth()); Month = Repeat("0", Month.length == 1) + Month; var Day = String(Today.getUTCDate()); Day = Repeat("0", Day.length == 1) + Day; Today = Year + Month + Day; var LastCheck = GM_getValue("LastCheck", "00000000"); if (Today > LastCheck) { GM_get(ScriptURL, CheckScriptVersion); GM_setValue("LastCheck", Today); } /******************************************************************************/ // attach activation button to page var Activator = document.createElement("div"); Activator.setAttribute("id", "GM_ItemParser_Activator"); Activator.setAttribute("style", "text-align:center"); CreateOption( Activator, "GM_ItemParser_Button", "button", "", [["value", "I Can Has Wiki"], ["onClick", Main]] ); Activator.appendChild(document.createElement("hr")); document.body.insertBefore(Activator, document.body.firstChild);