598 lines
20 KiB
JavaScript
598 lines
20 KiB
JavaScript
"use strict";
|
|
|
|
|
|
/**
|
|
* Tooltip.
|
|
*
|
|
* @class
|
|
*/
|
|
var GW2Tooltip = (function()
|
|
{
|
|
/**
|
|
* Private functions for DOM interaction.
|
|
*
|
|
* @type {object}
|
|
*/
|
|
var Element =
|
|
{
|
|
/**
|
|
* Converts a string to a DOM element and returns this. If the string
|
|
* contains more than one element on the highest level, only the
|
|
* first node is returned.
|
|
*
|
|
* @method stringToNode
|
|
* @param {string} string the string.
|
|
* @return {object} a DOM element.
|
|
*/
|
|
stringToNode : function(string)
|
|
{
|
|
var parent = document.createElement("div");
|
|
parent.innerHTML = string;
|
|
return parent.firstChild;
|
|
},
|
|
|
|
/**
|
|
* Inserts an element in the DOM after another element.
|
|
*
|
|
* @method insertAfter
|
|
* @param {object} e1 the element after which is to be
|
|
* inserted.
|
|
* @param {object} e2 the element to be inserted.
|
|
*/
|
|
insertAfter : function(e1, e2)
|
|
{
|
|
e1.parentNode.insertBefore(e2, e1.nextSibling);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* API access.
|
|
*
|
|
* @class
|
|
*/
|
|
var APIAccess = (function()
|
|
{
|
|
var queue = {};
|
|
|
|
/**
|
|
* Returns a cross-domain request object, or undefined if the browser
|
|
* sucks.
|
|
*
|
|
* @method getCORSRequest
|
|
* @return {(XMLHttpRequest|ActiveXObject)} a cross-domain request
|
|
* object, or undefined
|
|
* if the browser
|
|
* sucks.
|
|
*/
|
|
var getCORSRequest = (function()
|
|
{
|
|
var validFactory = -1,
|
|
factories = [
|
|
//function() { return new XDomainRequest(); },
|
|
function() { return new XMLHttpRequest(); },
|
|
function() { return new ActiveXObject("Msxml2.XMLHTTP"); },
|
|
function() { return new ActiveXObject("Msxml3.XMLHTTP"); },
|
|
function()
|
|
{
|
|
return new ActiveXObject("Microsoft.XMLHTTP");
|
|
}
|
|
];
|
|
|
|
// Try out all factories and save index of first factory that works.
|
|
var factory,
|
|
i, length = factories.length;
|
|
for(i = 0; i < length; i++)
|
|
{
|
|
try
|
|
{
|
|
factory = factories[i]();
|
|
validFactory = i;
|
|
break;
|
|
}
|
|
catch(e)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Return a function that returns the cross-domain request object.
|
|
return function()
|
|
{
|
|
if(validFactory >= 0)
|
|
return factories[validFactory]();
|
|
else
|
|
return undefined;
|
|
};
|
|
})();
|
|
|
|
/**
|
|
* Reads JSON from the given URL and passes this to the callback.
|
|
*
|
|
* @method getJSONP
|
|
* @param {string} url the URL of the JSON.
|
|
* @param {Function} callback the function to pass the JSON to.
|
|
*/
|
|
var getJSONP = function(url, callback)
|
|
{
|
|
var _baseURL = "https://api.guildwars2.com/";
|
|
var xmlhttp = getCORSRequest();
|
|
|
|
xmlhttp.onreadystatechange = function()
|
|
{
|
|
if(xmlhttp.readyState == 4 && xmlhttp.status == 200)
|
|
if(typeof callback === "function")
|
|
callback(Polyfill.JSONParse(xmlhttp.response));
|
|
};
|
|
|
|
xmlhttp.open("GET", _baseURL + url, true);
|
|
xmlhttp.send();
|
|
};
|
|
|
|
/**
|
|
* Public functions.
|
|
*
|
|
* @type {object}
|
|
*/
|
|
var exports = {};
|
|
|
|
/**
|
|
* Requests data for a single tooltip from the API right away.
|
|
*
|
|
* @method requestData
|
|
* @param {number} id the id of the data.
|
|
* @param {class} generator the generator to use for the
|
|
* tooltip.
|
|
* @param {Function} callback the function to pass the JSON to.
|
|
*/
|
|
exports.requestData = function(id, generator, callback)
|
|
{
|
|
getJSONP("/v2/" + generator.type + "/" + id, callback);
|
|
};
|
|
|
|
/**
|
|
* Adds the id for a single tooltip to the request queue.
|
|
*
|
|
* @method queueData
|
|
* @param {object} e the element to add the tooltip to
|
|
* after completing the queue.
|
|
* @param {number} id the id of the data.
|
|
* @param {class} generator the generator to use for the
|
|
* tooltip.
|
|
*/
|
|
exports.queueData = function(e, id, generator)
|
|
{
|
|
var type = generator.type;
|
|
|
|
if(queue[type] === undefined)
|
|
queue[type] = [];
|
|
queue[type].push([e, id]);
|
|
};
|
|
|
|
/**
|
|
* Executes the queue and adds all tooltips to the right elements, and
|
|
* then empties the queue.
|
|
*
|
|
* @method executeQueue
|
|
* @param {class} generator the generator to use for the
|
|
* tooltip.
|
|
* @param {Function} callback the function to pass the JSONs to.
|
|
*/
|
|
exports.executeQueue = function(generator, options, callback)
|
|
{
|
|
var type = generator.type;
|
|
|
|
if(queue.length === 0)
|
|
return;
|
|
if(queue[type] === undefined)
|
|
return;
|
|
if(queue[type].length === 0)
|
|
return;
|
|
|
|
// Build URL
|
|
var url = [],
|
|
i, length = queue[type].length;
|
|
for(i = 0; i < length; i++)
|
|
url.push(queue[type][i][1]);
|
|
|
|
// Get JSON and add respective tooltips
|
|
getJSONP("/v2/" + type + "?ids=" + url.join(","), function(json)
|
|
{
|
|
var i, length1 = queue[type].length,
|
|
j, length2 = json.length;
|
|
|
|
for(i = 0; i < length1; i++)
|
|
for(j = 0; j < length2; j++)
|
|
if(json[j].id == queue[type][i][1])
|
|
callback(queue[type][i][0], json[j], generator,
|
|
options);
|
|
|
|
queue[type] = [];
|
|
});
|
|
};
|
|
|
|
return exports;
|
|
})();
|
|
|
|
|
|
var exports = {};
|
|
|
|
/**
|
|
* Adds a tooltip to the given element.
|
|
*
|
|
* @method addDirect
|
|
* @param {object} e the element to add the tooltip to.
|
|
* @param {object} json the data. Should have the structure as
|
|
* specified in the Guild Wars 2 API
|
|
* version 2 documentation.
|
|
* @param {class} generator the generator to use for the tooltip.
|
|
* @param {class} options additional parameters for the tooltip.
|
|
* Options starting with "link" are
|
|
* used to manipulate the contents of
|
|
* the tooltip link, which is the
|
|
* element to which the tooltip is
|
|
* added. These options can be applied
|
|
* together. Use option "linkReplace"
|
|
* to indicate whether the contents of
|
|
* the link should be replaced with the
|
|
* new information or should be
|
|
* prepended instead.
|
|
* The following options are
|
|
* recognised:
|
|
* "linkIcon" :
|
|
* true if the link info should
|
|
* contain an <img />
|
|
* element with the
|
|
* tooltip's icon; should
|
|
* be false or undefined
|
|
* otherwise.
|
|
* Use option
|
|
* "linkIconClass" to
|
|
* optionally set the class
|
|
* of these images.
|
|
* "linkIconClass":
|
|
* if "linkIcon" is true, the
|
|
* added icons will have
|
|
* the class defined in
|
|
* this option.
|
|
* "linkInfo" :
|
|
* false or undefined if no
|
|
* info should be added to
|
|
* the link; should be a
|
|
* string to add info from
|
|
* the tooltip (e.g.
|
|
* "name", "id",
|
|
* "chat_link" etc.). See
|
|
* the Guild Wars 2 API for
|
|
* all possible values.
|
|
* "linkReplace" :
|
|
* true to replace the link's
|
|
* contents with the
|
|
* content defined with
|
|
* other options starting
|
|
* with "link"; false to
|
|
* prepend this content to
|
|
* the link instead. If no
|
|
* new content is defined,
|
|
* an empty string will
|
|
* replace or prepend the
|
|
* link's contents.
|
|
*/
|
|
exports.addDirect = function(e, json, generator, options)
|
|
{
|
|
// Default parameter values
|
|
options = (options || {});
|
|
options.linkIcon = (options.linkIcon || false);
|
|
options.linkIconClass = (options.linkIconClass || "");
|
|
options.linkInfo = (options.linkInfo || false);
|
|
// If no link contents are generated, prepend with empty string
|
|
options.linkReplace = (options.linkReplace || false);
|
|
|
|
// Parse JSON if needed
|
|
if(typeof json === "string")
|
|
json = Polyfill.JSONParse(json);
|
|
|
|
|
|
Polyfill.addClass(e, "GW2TooltipLink");
|
|
var tooltip = new generator(json);
|
|
|
|
// Remove current tooltip if it is there
|
|
if(e.nextSibling !== null &&
|
|
Polyfill.hasClass(e.nextSibling, "GW2Tooltip"))
|
|
e.parentNode.removeChild(e.nextSibling);
|
|
|
|
|
|
// Change tooltip link if needed
|
|
var innerHTML = (options.linkReplace ? "" : e.innerHTML);
|
|
|
|
// Addition should be in reversed order of appearance
|
|
if(options.linkInfo !== false)
|
|
{
|
|
innerHTML = json[options.linkInfo] + innerHTML;
|
|
}
|
|
if(options.linkIcon === true)
|
|
{
|
|
innerHTML = "" +
|
|
"<img src=\"" +
|
|
json.icon +
|
|
"\" class=\"" +
|
|
options.linkIconClass +
|
|
"\" /> " +
|
|
innerHTML;
|
|
}
|
|
|
|
e.innerHTML = innerHTML;
|
|
|
|
|
|
// Insert tooltip
|
|
Element.insertAfter(e,
|
|
Element.stringToNode(tooltip.getTooltip()));
|
|
};
|
|
|
|
/**
|
|
* Adds a tooltip to the given element.
|
|
*
|
|
* @method addNow
|
|
* @param {object} e the element to add the tooltip to.
|
|
* @param {number} id the id of the data.
|
|
* @param {class} generator the generator to use for the tooltip.
|
|
*/
|
|
exports.addNow = function(e, id, generator, options)
|
|
{
|
|
APIAccess.requestData(id, generator, function(json)
|
|
{
|
|
exports.addDirect(e, json, generator, options);
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Adds a tooltip to the given element as soon as the user hovers over the
|
|
* link. Decreases page load time and increases tooltip appear time.
|
|
*
|
|
* @method addLater
|
|
* @param {object} e the element to add the tooltip to.
|
|
* @param {number} id the id of the data.
|
|
* @param {class} generator the generator to use for the tooltip.
|
|
*/
|
|
exports.addLater = function(e, dataId, generator, options)
|
|
{
|
|
var curEvent = e.onmouseover;
|
|
|
|
e.onmouseover = function()
|
|
{
|
|
if(typeof curEvent === "function")
|
|
curEvent();
|
|
|
|
APIAccess.requestData(dataId, generator, function(json)
|
|
{
|
|
exports.addDirect(e, json, generator, options);
|
|
});
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Adds tooltips for all elements with the data-[type]id tag. This is
|
|
* usually done directly after loading the page.
|
|
*
|
|
* @method addAllNow
|
|
* @param {class} generator the generator to use for the tooltip.
|
|
*/
|
|
exports.addAllNow = function(generator, options)
|
|
{
|
|
var eList,
|
|
i, length;
|
|
|
|
// Get all elements with proper data attribute.
|
|
if(document.querySelectorAll)
|
|
{
|
|
eList = document.querySelectorAll("[data-" + generator.attr + "]");
|
|
}
|
|
else
|
|
{
|
|
// Custom polyfill
|
|
var e, eAll = document.getElementsByTagName("*");
|
|
length = eAll.length;
|
|
|
|
for(i = 0; i < length; i++)
|
|
{
|
|
e = eAll[i];
|
|
if(e.dataset !== undefined &&
|
|
e.dataset[generator.attr] !== undefined)
|
|
{
|
|
eList.push(e);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Queue tooltips
|
|
length = eList.length;
|
|
for(i = 0; i < length; i++)
|
|
{
|
|
APIAccess.queueData(
|
|
eList[i],
|
|
eList[i].dataset[generator.attr],
|
|
generator
|
|
);
|
|
}
|
|
|
|
// Add tooltips
|
|
APIAccess.executeQueue(generator, options, exports.addDirect);
|
|
};
|
|
|
|
|
|
/**
|
|
* Static tooltip data. Used by other Tooltip classes.
|
|
*
|
|
* @type {object}
|
|
*/
|
|
exports.TooltipData =
|
|
{
|
|
// List of icons.
|
|
icons :
|
|
{
|
|
base :
|
|
"https://render.guildwars2.com/file/",
|
|
ui_coin_gold :
|
|
"090A980A96D39FD36FBB004903644C6DBEFB1FFB/156904.png",
|
|
ui_coin_silver :
|
|
"E5A2197D78ECE4AE0349C8B3710D033D22DB0DA6/156907.png",
|
|
ui_coin_copper :
|
|
"6CF8F96A3299CFC75D5CC90617C3C70331A1EF0E/156902.png",
|
|
ui_infusion_slot_offensive :
|
|
"FD212A4A36BEA799E3BDCDFFDC55DEC2A50FE19D/517203.png",
|
|
ui_infusion_slot_defensive :
|
|
"3D941C5F3C0BE9FFAFE27A2C4AE70ABC94AAE724/517202.png",
|
|
ui_infusion_slot_utility :
|
|
"0B5F38D94AAA1539EC6009F4CF37DF5673C81DA0/517204.png",
|
|
ui_infusion_slot_agony :
|
|
"F54BC4283FFAB7531073FF08F470A9730E47FB4D/683590.png"
|
|
},
|
|
|
|
// List of attribute names.
|
|
attributes :
|
|
{
|
|
BoonDuration : "Concentration",
|
|
ConditionDamage : "Condition Damage",
|
|
ConditionDutation : "Expertise",
|
|
CritDamage : "Ferocity",
|
|
Healing : "Healing Power",
|
|
Power : "Power",
|
|
Precision : "Precision",
|
|
Toughness : "Toughness",
|
|
Vitality : "Vitality"
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Polyfill functions for browser compatibility. Used by other Tooltip
|
|
* classes.
|
|
*
|
|
* @type {object}
|
|
*/
|
|
var Polyfill =
|
|
{
|
|
/**
|
|
* Sets the JSON parser to JSON4 if present, or falls back to JSON3,
|
|
* then to JSON2, then to default JSON and then to eval.
|
|
*
|
|
* @class
|
|
* @return {method} sets the JSON parser.
|
|
*/
|
|
JSONParse : function()
|
|
{
|
|
if(typeof JSON4 !== "undefined")
|
|
return JSON4.parse;
|
|
else if(typeof JSON3 !== "undefined")
|
|
return JSON3.parse;
|
|
else if(typeof JSON2 !== "undefined")
|
|
return JSON2.parse;
|
|
else if(typeof JSON !== "undefined")
|
|
return JSON.parse;
|
|
else
|
|
return function(sJSON)
|
|
{
|
|
// Provide a JSON library to prevent this eval from
|
|
// executing.
|
|
return eval("(" + sJSON + ")");
|
|
};
|
|
}(),
|
|
|
|
/**
|
|
* Returns the first index at which a given element can be found in the
|
|
* object, or -1 if it is not present.
|
|
*
|
|
* @method indexOf
|
|
* @param {object} object the object to traverse.
|
|
* @param {object} searchElement the element to locate in the
|
|
* object.
|
|
* @param {number} fromIndex the index to start the search
|
|
* at.
|
|
* @return {number} the first index at which a given element can be
|
|
* found in the object, or -1 if it is not
|
|
* present.
|
|
*/
|
|
indexOf : function(object, searchElement, fromIndex)
|
|
{
|
|
var k;
|
|
|
|
//if(object == null)
|
|
if(object === undefined || object === null)
|
|
throw new TypeError("\"this\" is null or not defined");
|
|
|
|
var o = Object(object),
|
|
len = o.length >>> 0;
|
|
|
|
if(len === 0)
|
|
return -1;
|
|
|
|
var n = +fromIndex || 0;
|
|
if(Math.abs(n) === Infinity)
|
|
n = 0;
|
|
if(n >= len)
|
|
return -1;
|
|
|
|
k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
|
|
|
|
while(k < len)
|
|
{
|
|
if(k in o && o[k] === searchElement)
|
|
return k;
|
|
k++;
|
|
}
|
|
|
|
return -1;
|
|
},
|
|
|
|
/**
|
|
* Returns true if the element has the given class.
|
|
*
|
|
* @method hasClass
|
|
* @param {object} element the element.
|
|
* @param {string} cls the class.
|
|
* @return {string} true if the element has the given class.
|
|
*/
|
|
hasClass : function(element, cls)
|
|
{
|
|
return (' ' + element.className + ' ').indexOf(' ' + cls + ' ') > -1;
|
|
},
|
|
|
|
/**
|
|
* Adds a class to the given element.
|
|
*
|
|
* @method addClass
|
|
* @param {object} element the element.
|
|
* @param {string} cls the class.
|
|
*/
|
|
addClass : function(element, cls)
|
|
{
|
|
if(!Polyfill.hasClass(element, cls))
|
|
element.className += (" " + cls);
|
|
},
|
|
|
|
/**
|
|
* Returns the comma-separated string of a number.
|
|
*
|
|
* @method commaSeparate
|
|
* @param {number} val the number to separate.
|
|
* @return {string} the comma-separated string of a number.
|
|
*/
|
|
commaSeparate : function(val)
|
|
{
|
|
val = val.toString();
|
|
var str = val,
|
|
dif = str.length - (3 * Math.floor(str.length / 3));
|
|
|
|
if(dif !== 0)
|
|
str = str.substr(dif, str.length);
|
|
|
|
var arr = str.match(/.{3}/g);
|
|
if(dif !== 0)
|
|
arr.unshift(val.substr(0, dif));
|
|
|
|
return arr.join(",");
|
|
}
|
|
};
|
|
exports.Polyfill = Polyfill;
|
|
|
|
return exports;
|
|
})();
|