MediaWiki:ChatLinkSearch.js
From Guild Wars 2 Wiki
Jump to navigationJump to search
Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
- Opera: Press Ctrl-F5.
/* <nowiki> */
/**
* GW2W Chat link search
*
* Decodes Guild Wars 2 chat links in the search panel, and tries to find the
* corresponding article using the SMW property "Has game id".
*
* Original by Patrick Westerhoff [User:Poke]. 2022 modifications by Chieftain Alex.
*/
/**
* Since square brackets are considered illegal characters for native interwiki search redirects,
* this JS is required such that interwiki's can be respected when searching from the ingame
* /wiki command with a chatlink.
*
* "de:" redirects to the german wiki,
* "fr:" redirects to the french wiki,
* "es:" redirects to the spanish wiki.
*
* Based on a suggestion at [[MediaWiki talk:ChatLinkSearch.js]] by [[de:Benutzer:Olertu]]
*
* To disable this functionality by setting a cookie, visit [[Widget:No interwiki search]].
*/
(function checkForInterWiki() {
var searchBar = document.querySelector('#searchText input');
if (!searchBar) {
return;
}
// Check for a cookie which, if set, prevents the wiki redirecting on chat links
var stopCookie = getCookie('ignoreInterwikiSearchRedirect');
// Check for an interwiki prefix
var match = searchBar.value.match(/^(de|fr|es):(.*?)$/i);
if (match && stopCookie === null) {
console.log('Redirecting from ',window.location.href,' to another language wiki.');
window.location.href = 'https://wiki-' + match[1] + '.guildwars2.com/index.php?title=Special:Search&search=' + encodeURIComponent(match[2]);
} else {
chatLinkSearch(searchBar)
}
})();
function getCookie(k) {
var v = document.cookie.match('(^|;) ?' + k + '=([^;]*)(;|$)');
return v ? v[2] : null
}
function chatLinkSearch(searchBar) {
var mwApi;
function convertWikiMarkupToHTML(data, element_id) {
// Remove the header from the parsed payload, cleanup rest
var headertype = data.headername;
delete data.headername;
delete data.header;
delete data.extendedoutput;
delete data.searchflag;
var parse_payload = 'WIDGETSEPARATOR' + '{{Template:ChatLinkSearch ' + headertype + '|' + $.map(data, function(v,k){
return k + '=' + v;
}).join('|') + '}}' + 'WIDGETSEPARATOR';
mw.loader.using('mediawiki.api', function () {
var api = new mw.Api();
api.parse(parse_payload)
.done(function (result) {
var parsed_payload = result.split('WIDGETSEPARATOR');
// Remove first two elements where the header and footer of the parsed data will be (div open, div closed+pp limit report)
parsed_payload.pop();
parsed_payload.shift();
// Distribute result back to source
$('#' + element_id).html(parsed_payload[0]);
})
.fail(function(d, textStatus, error) {
console.log('[[MediaWiki:ChatLinkSearch.js]]: Mediawiki API failed to parse text.');
console.error('[[MediaWiki:ChatLinkSearch.js]]: GW2W API Parse operation failed, status: ' + textStatus + ', error: '+error);
});
});
}
// Helper function: Convert item mask into options (upgrades, sigils/runes, skins)
function itemChoices(mask){
var option = {};
// Bitmask meanings: 0 = no upgrades, 64 (or 32) = 1 sigil, 96 = 2 sigils, 128 = skinned, 192 (or 160) = skinned + 1 sigil, 224 = skinned + 2 sigils
switch (mask) {
case 0: option.name = 'no upgrades'; option.arr = ['','','']; break;
case 32:
case 64: option.name = 'one sigil/rune'; option.arr = ['item','','']; break;
case 96: option.name = 'two sigils/runes'; option.arr = ['item','item','']; break;
case 128: option.name = 'skin applied'; option.arr = ['skin','','']; break;
case 160:
case 192: option.name = 'one sigil/rune and a skin applied'; option.arr = ['skin','item','']; break;
case 224: option.name = 'two sigils/runes and a skin applied'; option.arr = ['skin','item','item']; break;
default: option.name = 'unknown'; option.arr = ['','','']; break;
}
return option;
}
// Helper function: Convert specialization mask into options (unallocated 0/top 1/middle 2/bottom 3)
function specializationChoices(mask8){
// Convert to binary
var binary = mask8.toString(2).padStart(8,'0');
// Split into pairs
var binary_pairs = binary.match(/../g);
// Remove the useless 1st pair
binary_pairs.shift();
// Reverse the order and convert back into decimals
var positions = $.map(binary_pairs.reverse(), function(v) {
return parseInt(v,2);
});
return positions;
}
// Helper function: Convert item visibility mask (length 16 binary bits) into slots. Status is 1 if visible, 0 if hidden.
function itemVisibility(mask16){
var bitmask = mask16.toString(2).padStart(16,'0').match(/./g).reverse();
var slots = ['aquabreather', 'back', 'coat', 'boots', 'gloves', 'helm', 'legs', 'shoulders', 'outfit', 'aquaweapon1', 'aquaweapon2', 'weapon1', 'weapon2', 'weapon3', 'weapon4'];
// Only return data on hidden slots
var hiddenslots = [];
$.map(slots, function(v,k){ if (bitmask[k] == 0) { hiddenslots.push(v); } });
return hiddenslots;
}
// Helper function: Convert travel visibility mask ??
function travelVisibility(mask16){
var bitmask = mask16.toString(2).padStart(16,'0').match(/./g).reverse();
return bitmask;
}
// Helper function: Convert weapon type numbers into type names
function weaponTypeNames(id){
var name;
switch (id) {
case 5: name = "Axe"; break;
case 35: name = "Longbow"; break;
case 47: name = "Dagger"; break;
case 49: name = "Focus"; break;
case 50: name = "Greatsword"; break;
case 51: name = "Hammer"; break;
case 53: name = "Mace"; break;
case 54: name = "Pistol"; break;
case 85: name = "Rifle"; break;
case 86: name = "Scepter"; break;
case 87: name = "Shield"; break;
case 89: name = "Staff"; break;
case 90: name = "Sword"; break;
case 102: name = "Torch"; break;
case 103: name = "Warhorn"; break;
case 107: name = "Shortbow"; break;
case 265: name = "Spear"; break;
default: "(unknown weapon type " + id + ")";
}
return name;
}
// Helper function: Reads a string like AAB60000, break into pairs AA-B6-00-00, reverses pairs 00-00-B6-AA, joins, and converts HEX (radix 16) to DECIMAL (radix 10).
// Note: prefixing a number with 0x would allow you to skip specifying the 16 bit.
// https://www.binaryhexconverter.com/hex-to-decimal-converter
function parseHexLittleEndian(text){
if (text == undefined || !(text.match(/../g)) ){
return '';
}
return parseInt(text.match(/../g).reverse().join(''),16);
}
function decodeChatLink3(input) {
/** Example usage: decodeChatLink('[&AdsnAAA=]')
*
* Some examples that can be decoded:
* '[&AdsnAAA=]'; // Coin - 1g 02s 03c
* '[&AgGqtgAA]'; // Item - Zojja's Claymore
* '[&AgGqtgDgfQ4AAP9fAAAnYAAA]'; // Item - Zojja's Claymore (item 46762), bitmask 224 (skin + two upgrades) skinned as Dreamthistle Greatsword (skin 3709), with Superior Bloodlust (item 24575), Superior Force (24615)
* '[&AxcnAAA=]'; // Text: "Fight what cannot be fought" - id 10007
* '[&DGYAAABOBAAA]'; // WvW objective: [[Y'lan Academy]] --> map id 1102, objective 102
* '[&DAYAAAAmAAAA]'; // WvW objective: [[Speldan Clearcut]] --> map id 38, objective 6
* '[&DQQIByEANzZ5AHgAqwEAALUApQEAALwA7QDtABg9AAEAAAAAAAAAAAAAAAA=]'; // Ranger build
* '[&DQEqHhAaPj1LFwAAFRcAAEgBAAAxAQAANwEAAAAAAAAAAAAAAAAAAAAAAAA=]'; // Burn firebrand
*/
// Input cleanup - remove "[&" and rear "]"
var code = input.replace(/^\[\&+|\]+$/g, '');
// Also remove base64 padding from end (= sign)
code = code.replace(/=+$/, '');
// Split characters into array
var textArray = code.split('');
// Convert from Text to an Array of decimal numbers (0, 1, 2, 3, 4, 5, 6, 7, 8, 9).
var AtoB_lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
var decimals = new Array(textArray.length);
for (var i = 0; i < code.length; i++) {
decimals[i] = AtoB_lookup.indexOf(textArray[i]);
}
// Convert from numbers to blocks of 6 binary bits [aka digits]
// Needs 6 digits 32-16-8-4-2-1 to represent 0-63 for a base 64 number
var binaries = new Array(code.length);
for (var i = 0; i < code.length; i++) {
binaries[i] = decimals[i].toString(2).padStart(6,'0');
}
// Join
var binary_stream = binaries.join('');
// Split into blocks, but this time in groups of 8 binary bits (1 byte)
var binary_octets = binary_stream.match(/......../g);
// Interpret as HEX
var hex_octets = new Array(binary_octets.length);
for (var i = 0; i < binary_octets.length; i++) {
hex_octets[i] = parseInt(binary_octets[i], 2).toString(16).padStart(2,'0').toUpperCase();
}
// Join
var hex_stream = hex_octets.join('');
// Note: Two hex characters together are called a "byte" (because it maps back to 8 binary bits).
// Layout specifications change depending on the header number
var specification = {
1: {
name: 'coin',
searchflag: 'n',
extendedoutput: 'n',
format: [
{ name: 'header', bytes: 1 },
{ name: 'copper_qty', bytes: 4 }
]
},
2: {
name: 'item',
searchflag: 'y',
extendedoutput: 'n',
format: [
{ name: 'header', bytes: 1 },
{ name: 'quantity', bytes: 1 },
{ name: 'id', bytes: 3 },
{ name: 'bitmask', bytes: 1 }, // Bitmask meanings: 0 = no upgrades, 64 (or 32) = 1 sigil, 96 = 2 sigils, 128 = skinned, 192 (or 160) = skinned + 1 sigil, 224 = skinned + 2 sigils
{ name: 'upgrade1', bytes: 3 },
{ name: 'padding1', bytes: 1 },
{ name: 'upgrade2', bytes: 3 },
{ name: 'padding2', bytes: 1 },
{ name: 'upgrade3', bytes: 3 }
]
},
3: {
name: 'text',
searchflag: 'n',
extendedoutput: 'n',
format: [
{ name: 'header', bytes: 1 },
{ name: 'id', bytes: 3 }
]
},
4: {
name: 'location',
searchflag: 'y',
extendedoutput: 'n',
format: [
{ name: 'header', bytes: 1 },
{ name: 'id', bytes: 3 }
]
},
6: {
name: 'skill',
searchflag: 'y',
extendedoutput: 'n',
format: [
{ name: 'header', bytes: 1 },
{ name: 'id', bytes: 3 }
]
},
7: {
name: 'trait',
searchflag: 'y',
extendedoutput: 'n',
format: [
{ name: 'header', bytes: 1 },
{ name: 'id', bytes: 3 }
]
},
9: {
name: 'recipe',
searchflag: 'y',
extendedoutput: 'n',
format: [
{ name: 'header', bytes: 1 },
{ name: 'id', bytes: 3 }
]
},
10: {
name: 'skin',
searchflag: 'y',
extendedoutput: 'n',
format: [
{ name: 'header', bytes: 1 },
{ name: 'id', bytes: 3 }
]
},
11: {
name: 'outfit',
searchflag: 'y',
extendedoutput: 'n',
format: [
{ name: 'header', bytes: 1 },
{ name: 'id', bytes: 3 }
]
},
12: {
name: 'wvw objective',
searchflag: 'y',
extendedoutput: 'n',
format: [
{ name: 'header', bytes: 1 },
{ name: 'id', bytes: 3 },
{ name: 'padding1', bytes: 1 },
{ name: 'map_id', bytes: 3 },
{ name: 'padding2', bytes: 1 }
]
},
13: {
name: 'build template',
searchflag: 'n',
extendedoutput: 'y',
format: [
{ name: 'header', bytes: 1 },
{ name: 'prof', bytes: 1 },
{ name: 'spec1', bytes: 1 }, // 1oo6 bytes
{ name: 'spec1_choices', bytes: 1 }, // 2oo6
{ name: 'spec2', bytes: 1 }, // 3oo6
{ name: 'spec2_choices', bytes: 1 }, // 4oo6
{ name: 'spec3', bytes: 1 }, // 5oo6
{ name: 'spec3_choices', bytes: 1 }, // 6oo6
{ name: 'heal', bytes: 2 }, // 2oo20 bytes
{ name: 'aquatic_heal', bytes: 2 }, // 4oo20
{ name: 'utility1', bytes: 2 }, // 6oo20
{ name: 'aquatic_utility1', bytes: 2 }, // 8oo20
{ name: 'utility2', bytes: 2 }, // 10oo20
{ name: 'aquatic_utility2', bytes: 2 }, // 12oo20
{ name: 'utility3', bytes: 2 }, // 14oo20
{ name: 'aquatic_utility3', bytes: 2 }, // 16oo20
{ name: 'elite', bytes: 2 }, // 18oo20
{ name: 'aquatic_elite', bytes: 2 }, // 20oo20
{ name: 'pet1ORrevlegend1', bytes: 1 }, // 1oo4 bytes
{ name: 'pet2ORrevlegend2', bytes: 1 }, // 2oo4
{ name: 'aquatic_pet1ORrevlegend1', bytes: 1 }, // 3oo4
{ name: 'aquatic_pet2ORrevlegend2', bytes: 1 }, // 4oo4
{ name: 'inactive_rev_utility1' , bytes: 2 }, // 2oo6
{ name: 'inactive_rev_utility2' , bytes: 2 }, // 4oo6
{ name: 'inactive_rev_utility3' , bytes: 2 }, // 6oo6
{ name: 'inactive_aquatic_rev_utility1', bytes: 2 }, // 2oo6
{ name: 'inactive_aquatic_rev_utility2', bytes: 2 }, // 4oo6
{ name: 'inactive_aquatic_rev_utility3', bytes: 2 } // 6oo6
]
},
14: {
name: 'achievement',
searchflag: 'y',
extendedoutput: 'n',
format: [
{ name: 'header', bytes: 1 },
{ name: 'id', bytes: 3 }
]
},
15: {
name: 'fashion template',
searchflag: 'n',
extendedoutput: 'y',
format: [
{ name: 'header', bytes: 1 },
{ name: 'aquabreather', bytes: 2 }, // aquabreather
{ name: 'armor1', bytes: 2 }, // 2oo10 bytes - back
{ name: 'armor1dye1', bytes: 2 }, // 4oo10
{ name: 'armor1dye2', bytes: 2 }, // 6oo10
{ name: 'armor1dye3', bytes: 2 }, // 8oo10
{ name: 'armor1dye4', bytes: 2 }, // 10oo10
{ name: 'armor2', bytes: 2 }, // 2oo10 bytes - coat
{ name: 'armor2dye1', bytes: 2 }, // 4oo10
{ name: 'armor2dye2', bytes: 2 }, // 6oo10
{ name: 'armor2dye3', bytes: 2 }, // 8oo10
{ name: 'armor2dye4', bytes: 2 }, // 10oo10
{ name: 'armor3', bytes: 2 }, // 2oo10 bytes - boots
{ name: 'armor3dye1', bytes: 2 }, // 4oo10
{ name: 'armor3dye2', bytes: 2 }, // 6oo10
{ name: 'armor3dye3', bytes: 2 }, // 8oo10
{ name: 'armor3dye4', bytes: 2 }, // 10oo10
{ name: 'armor4', bytes: 2 }, // 2oo10 bytes - gloves
{ name: 'armor4dye1', bytes: 2 }, // 4oo10
{ name: 'armor4dye2', bytes: 2 }, // 6oo10
{ name: 'armor4dye3', bytes: 2 }, // 8oo10
{ name: 'armor4dye4', bytes: 2 }, // 10oo10
{ name: 'armor5', bytes: 2 }, // 2oo10 bytes - helm
{ name: 'armor5dye1', bytes: 2 }, // 4oo10
{ name: 'armor5dye2', bytes: 2 }, // 6oo10
{ name: 'armor5dye3', bytes: 2 }, // 8oo10
{ name: 'armor5dye4', bytes: 2 }, // 10oo10
{ name: 'armor6', bytes: 2 }, // 2oo10 bytes - legs
{ name: 'armor6dye1', bytes: 2 }, // 4oo10
{ name: 'armor6dye2', bytes: 2 }, // 6oo10
{ name: 'armor6dye3', bytes: 2 }, // 8oo10
{ name: 'armor6dye4', bytes: 2 }, // 10oo10
{ name: 'armor7', bytes: 2 }, // 2oo10 bytes - shoulders
{ name: 'armor7dye1', bytes: 2 }, // 4oo10
{ name: 'armor7dye2', bytes: 2 }, // 6oo10
{ name: 'armor7dye3', bytes: 2 }, // 8oo10
{ name: 'armor7dye4', bytes: 2 }, // 10oo10
{ name: 'outfit', bytes: 2 }, // 2oo10 bytes
{ name: 'outfitdye1', bytes: 2 }, // 4oo10
{ name: 'outfitdye2', bytes: 2 }, // 6oo10
{ name: 'outfitdye3', bytes: 2 }, // 8oo10
{ name: 'outfitdye4', bytes: 2 }, // 10oo10
{ name: 'aquaweapon1', bytes: 2 }, // 2oo4 bytes
{ name: 'aquaweapon2', bytes: 2 }, // 4oo4
{ name: 'weapon1', bytes: 2 }, // 2oo8 bytes
{ name: 'weapon2', bytes: 2 }, // 4oo8
{ name: 'weapon3', bytes: 2 }, // 6oo8
{ name: 'weapon4', bytes: 2 }, // 8oo8
{ name: 'hiddenslots', bytes: 2 } // Bitmask meanings: Each digit represents, in sequence, each of the above slots being visible (e.g. first is aquabreather, last is weapon4)
]
},
16: {
name: 'travel template',
searchflag: 'n',
extendedoutput: 'y',
format: [
{ name: 'header', bytes: 1 },
{ name: 'glider-skin', bytes: 2 }, // glider
{ name: 'glider-dye1', bytes: 2 },
{ name: 'glider-dye2', bytes: 2 },
{ name: 'glider-dye3', bytes: 2 },
{ name: 'glider-dye4', bytes: 2 },
{ name: 'door-skin', bytes: 2 }, // conjured doorway
{ name: 'door-dye1', bytes: 2 },
{ name: 'door-dye2', bytes: 2 },
{ name: 'door-dye3', bytes: 2 },
{ name: 'door-dye4', bytes: 2 },
{ name: 'mount-jackal-skin', bytes: 2 }, // jackal
{ name: 'mount-jackal-dye1', bytes: 2 },
{ name: 'mount-jackal-dye2', bytes: 2 },
{ name: 'mount-jackal-dye3', bytes: 2 },
{ name: 'mount-jackal-dye4', bytes: 2 },
{ name: 'mount-griffon-skin', bytes: 2 }, // griffon
{ name: 'mount-griffon-dye1', bytes: 2 },
{ name: 'mount-griffon-dye2', bytes: 2 },
{ name: 'mount-griffon-dye3', bytes: 2 },
{ name: 'mount-griffon-dye4', bytes: 2 },
{ name: 'mount-springer-skin', bytes: 2 }, // springer
{ name: 'mount-springer-dye1', bytes: 2 },
{ name: 'mount-springer-dye2', bytes: 2 },
{ name: 'mount-springer-dye3', bytes: 2 },
{ name: 'mount-springer-dye4', bytes: 2 },
{ name: 'mount-skimmer-skin', bytes: 2 }, // skimmer
{ name: 'mount-skimmer-dye1', bytes: 2 },
{ name: 'mount-skimmer-dye2', bytes: 2 },
{ name: 'mount-skimmer-dye3', bytes: 2 },
{ name: 'mount-skimmer-dye4', bytes: 2 },
{ name: 'mount-raptor-skin', bytes: 2 }, // raptor
{ name: 'mount-raptor-dye1', bytes: 2 },
{ name: 'mount-raptor-dye2', bytes: 2 },
{ name: 'mount-raptor-dye3', bytes: 2 },
{ name: 'mount-raptor-dye4', bytes: 2 },
{ name: 'mount-beetle-skin', bytes: 2 }, // roller beetle
{ name: 'mount-beetle-dye1', bytes: 2 },
{ name: 'mount-beetle-dye2', bytes: 2 },
{ name: 'mount-beetle-dye3', bytes: 2 },
{ name: 'mount-beetle-dye4', bytes: 2 },
{ name: 'mount-warclaw-skin', bytes: 2 }, // warclaw
{ name: 'mount-warclaw-dye1', bytes: 2 },
{ name: 'mount-warclaw-dye2', bytes: 2 },
{ name: 'mount-warclaw-dye3', bytes: 2 },
{ name: 'mount-warclaw-dye4', bytes: 2 },
{ name: 'mount-skyscale-skin', bytes: 2 }, // skyscale
{ name: 'mount-skyscale-dye1', bytes: 2 },
{ name: 'mount-skyscale-dye2', bytes: 2 },
{ name: 'mount-skyscale-dye3', bytes: 2 },
{ name: 'mount-skyscale-dye4', bytes: 2 },
{ name: 'skiff-skin', bytes: 2 }, // skiff
{ name: 'skiff-dye1', bytes: 2 },
{ name: 'skiff-dye2', bytes: 2 },
{ name: 'skiff-dye3', bytes: 2 },
{ name: 'skiff-dye4', bytes: 2 },
{ name: 'mount-turtle-skin', bytes: 2 }, // siege turtle
{ name: 'mount-turtle-dye1', bytes: 2 },
{ name: 'mount-turtle-dye2', bytes: 2 },
{ name: 'mount-turtle-dye3', bytes: 2 },
{ name: 'mount-turtle-dye4', bytes: 2 },
{ name: 'hiddenmask', bytes: 2 } //
]
}
};
// Examine the header - this informs the structure of the rest of the chatlink
var headerTypeNum = parseHexLittleEndian( hex_stream.slice(0,2) );
if (!(headerTypeNum in specification)){
// Chatlink header type not supported
return {
headername: 'unsupported',
header: headerTypeNum,
searchflag: 'n'
};
}
// Convert hex stream into blocks of decimals
var hex_spec = {}, dec_spec = { 'headername': '' }, offset = 0;
$.each( specification[headerTypeNum].format, function(i,v){
hex_spec[v.name] = hex_stream.slice(offset, offset + v.bytes*2);
dec_spec[v.name] = parseHexLittleEndian( hex_spec[v.name] );
offset += v.bytes*2;
});
// Push the header name and wiki seach true/false flag
dec_spec.headername = specification[headerTypeNum].name;
dec_spec.searchflag = specification[headerTypeNum].searchflag;
dec_spec.extendedoutput = specification[headerTypeNum].extendedoutput;
// Extra sanitization due to printing the json blob
if (dec_spec.headername == 'item') {
// Upgrades
var i_temp = itemChoices(dec_spec.bitmask);
// Name
dec_spec.enhancements = i_temp.name;
// Rename remaining variables
var i_temp_upgrades_array = [dec_spec.upgrade1,dec_spec.upgrade2,dec_spec.upgrade3];
var i_count = 0;
$.each(i_temp.arr, function(i,v) {
if (v !== '') {
if (v == 'skin') {
dec_spec.skin = i_temp_upgrades_array[i];
} else {
i_count += 1;
dec_spec['item_upgrade_id' + i_count] = i_temp_upgrades_array[i];
}
}
});
delete dec_spec.bitmask;
delete dec_spec.upgrade1;
delete dec_spec.upgrade2;
delete dec_spec.upgrade3;
delete dec_spec.padding1;
delete dec_spec.padding2;
delete dec_spec.padding3;
}
if (dec_spec.headername == 'build template') {
// Specializations
dec_spec.spec1_choices = specializationChoices(dec_spec.spec1_choices).join('-');
dec_spec.spec2_choices = specializationChoices(dec_spec.spec2_choices).join('-');
dec_spec.spec3_choices = specializationChoices(dec_spec.spec3_choices).join('-');
// Ranger and Revenant specific
// Ranger
if (dec_spec.prof == 4) {
dec_spec.pet1 = dec_spec.pet1ORrevlegend1;
dec_spec.pet2 = dec_spec.pet2ORrevlegend2;
dec_spec.aquatic_pet1 = dec_spec.aquatic_pet1ORrevlegend1;
dec_spec.aquatic_pet2 = dec_spec.aquatic_pet2ORrevlegend2;
}
// Revenant
if (dec_spec.prof == 9) {
dec_spec.revlegend1 = dec_spec.pet1ORrevlegend1;
dec_spec.revlegend2 = dec_spec.pet2ORrevlegend2;
dec_spec.aquatic_revlegend1 = dec_spec.aquatic_pet1ORrevlegend1;
dec_spec.aquatic_revlegend2 = dec_spec.aquatic_pet2ORrevlegend2;
} else {
delete dec_spec.inactive_rev_utility1;
delete dec_spec.inactive_rev_utility2;
delete dec_spec.inactive_rev_utility3;
delete dec_spec.inactive_aquatic_rev_utility1;
delete dec_spec.inactive_aquatic_rev_utility2;
delete dec_spec.inactive_aquatic_rev_utility3;
}
delete dec_spec.pet1ORrevlegend1;
delete dec_spec.pet2ORrevlegend2;
delete dec_spec.aquatic_pet1ORrevlegend1;
delete dec_spec.aquatic_pet2ORrevlegend2;
// Check for any remaining hex (potential for build templates)
if (hex_stream.length - offset > 0) {
// First byte is a header identifying the length of the equipped (land) weapon information
var bytes = 1;
var weapon_count = parseHexLittleEndian( hex_stream.slice(offset, offset + bytes*2) );
offset += bytes*2;
// If there are weapons, each one is two bytes each
bytes = 2;
if (weapon_count > 0) {
dec_spec.weapons = [];
for (var i = 0; i < weapon_count; i++) {
dec_spec.weapons.push( weaponTypeNames(parseHexLittleEndian( hex_stream.slice(offset, offset + bytes*2) )) );
offset += bytes*2;
}
dec_spec.weapons = dec_spec.weapons.join('-');
}
// Check the next byte
bytes = 1;
var override_count = parseHexLittleEndian( hex_stream.slice(offset, offset + bytes*2) );
offset += bytes*2;
// If there are overrides, each one is four bytes each
bytes = 4;
if (override_count > 0) {
dec_spec.skill_overrides = [];
for (var i = 0; i < override_count; i++) {
dec_spec.overrides.push( parseHexLittleEndian( hex_stream.slice(offset, offset + bytes*2) ) );
offset += bytes*2;
}
dec_spec.skill_overrides = dec_spec.skill_overrides.join('-');
}
}
}
if (dec_spec.headername == 'fashion template') {
// Masks
dec_spec.hiddenslots = itemVisibility(dec_spec.hiddenslots).join('-');
}
if (dec_spec.headername == 'travel template') {
// Masks
dec_spec.hiddenmask = travelVisibility(dec_spec.hiddenmask).join('-');
}
return dec_spec;
}
function smwAskArticle (data, callback) {
var apiData = { action: 'ask', query: '?Has canonical name|?Has context|limit=1|' };
var query = '[[:+]] [[Has game id::' + data.id + ']]';
switch (data.headername) {
case 'item':
query += '[[Has context::Item]]';
break;
case 'location':
query += '[[Has context::Location]]';
break;
case 'skill':
query = query + '[[Has context::Skill]] OR ' + query + '[[Has context::Effect]]';
break;
case 'trait':
query += '[[Has context::Trait]]';
break;
case 'skin':
query += '[[Has context::Skin]]';
break;
case 'recipe':
query = '[[:+]] [[Has recipe id::' + data.id + ']]';
break;
case 'outfit':
query = '[[:+]] [[Has outfit id::' + data.id + ']]';
break;
case 'wvw objective':
query = '[[:+]] [[Has wvw objective id::' + data.map_id + '-' + data.id + ']]';
break;
case 'achievement':
query += '[[Has context::Achievement]]';
break;
}
apiData.query += query;
mwApi.get(apiData)
.done(function (apidata) {
if (apidata.query.results.length === 0) {
callback(null);
}
else {
for (var title in apidata.query.results) {
var canonicalName = apidata.query.results[title].printouts['Has canonical name'][0];
var gameContexts = apidata.query.results[title].printouts['Has context']
callback(title, canonicalName, gameContexts.length ? gameContexts[0] : null);
return;
}
}
})
.fail(function (apidata) {
callback(null);
});
}
function capitalizeFirstLetter(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
function sanitizeForTitleAttr(obj) {
delete obj.searchflag;
// Remove empty key-value pairs
$.each(obj, function(i,v){
if (v === "") {
delete obj[i];
}
});
// Replace quote marks, newlines and double spaces
return JSON.stringify(obj, null, 2)
.replace(/"/g,"")
.replace(/\n/g,"")
.replace(/ /g," ");
}
function display (code, listItem) {
var data = decodeChatLink3(code);
var type = data.headername;
var searchflag = data.searchflag;
var extendedoutput = data.extendedoutput;
if (searchflag == 'n') {
if (type == 'unsupported') {
var span = document.createElement('span');
span.innerHTML = 'This type of chat link is not recognized and has not been decoded. (Chat link header #' + data.header + ')';
span.title = sanitizeForTitleAttr(data);
$(span).fadeIn(1000).appendTo(listItem);
return;
} else {
if (extendedoutput == 'y') {
var span = document.createElement('span');
span.innerHTML = capitalizeFirstLetter(type) + ' chat link. Searching for this type of chat link is not currently supported, but it has been decoded, see the extended output below'
if ('id' in data ) {
span.innerHTML += ' (' + type + ' #' + data.id + ')';
}
span.title = sanitizeForTitleAttr(data);
var div = document.createElement('div');
var rid = 'R' + Math.floor(Math.random()*100000);
div.id = rid;
div.style.border = '1px solid #AAA';
$(span).append(div);
$(span).fadeIn(1000).appendTo(listItem);
// Async conversion of data into wiki output
convertWikiMarkupToHTML(data, rid);
return;
} else {
var span = document.createElement('span');
span.innerHTML = capitalizeFirstLetter(type) + ' chat link. Searching for this type of chat link is not currently supported, but it has been decoded, hover over this line for details.'
if ('id' in data ) {
span.innerHTML += ' (' + type + ' #' + data.id + ')';
}
span.title = sanitizeForTitleAttr(data);
$(span).fadeIn(1000).appendTo(listItem);
return;
}
}
} else {
smwAskArticle(data, function (title, canonicalName, gameContext) {
var span = document.createElement('span');
span.title = sanitizeForTitleAttr(data);
if (title) {
// If a single chatlink returns a single result (single li element), redirect to that page
// but don't redirect if it contains anything except a chatlink, e.g. interwiki prefix or text following
if (searchBar.value.match(/^\[&[A-Za-z0-9+/=]+\]$/)) {
// Redirect only once for the current browsing session for that precise result
var key = 'searchredirected-' + searchBar.value;
try {
if (!sessionStorage.getItem(key)) {
sessionStorage.setItem(key, 'true');
document.location = '/index.php?title=' + encodeURIComponent(title.replace(/ /g, '_'));
}
} catch(e) {
// This might throw if session storage is disabled or unsupported. Just don't redirect if so.
}
}
var link = document.createElement('a');
link.href = '/wiki/' + $.map(title.split('/'), function(v){
return encodeURIComponent(v.replace(/ /g, '_'));
}).join('/');
link.title = title;
link.innerHTML = canonicalName || title;
span.appendChild(link);
if (type == 'skill' && gameContext == 'Effect') {
type = 'effect';
}
span.appendChild(document.createTextNode(' (' + type + ' #' + data.id + ')'));
}
else {
var msg = 'There is no article linked with this ID (' + data.id + ') yet.';
msg += ' If you know what <i>' + (type == 'skill' ? 'skill or effect' : type) + '</i> this chat link links to, please add the ID to the article or create it if it does not exist yet.';
span.innerHTML = msg;
}
$(span).fadeIn(1000).appendTo(listItem);
$(listItem).attr('data-gameid', data.id)
});
}
}
window.mw.loader.using('mediawiki.api', function() {
mwApi = new window.mw.Api();
// Find chat links
var ul = document.createElement('ul');
var expr = /\[&([A-Za-z0-9+/]+=*)\]/g;
var match;
while ((match = expr.exec(searchBar.value))) {
var li = document.createElement('li');
li.innerHTML = '<tt>' + match[0] + '</tt>';
ul.appendChild(li);
display(match[1], li);
}
// Display results
if (ul.children.length) {
var div = document.createElement('div');
div.className = 'gw2w-chat-link-search';
div.innerHTML = 'The following <a href="/wiki/Chat_link_format" title="Chat link format">chat links</a> were included in your search query:';
div.appendChild(ul);
var topTable = document.getElementById('mw-search-top-table');
$(div).hide().insertAfter(topTable).show('fast');
}
});
}
/* </nowiki> */