/*
Script: Core.js
MooTools - My Object Oriented JavaScript Tools.
License:
MIT-style license.
Copyright:
Copyright (c) 2006-2008 [Valerio Proietti](http://mad4milk.net/).
Code & Documentation:
[The MooTools production team](http://mootools.net/developers/).
Inspiration:
- Class implementation inspired by [Base.js](http://dean.edwards.name/weblog/2006/03/base/) Copyright (c) 2006 Dean Edwards, [GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)
- Some functionality inspired by [Prototype.js](http://prototypejs.org) Copyright (c) 2005-2007 Sam Stephenson, [MIT License](http://opensource.org/licenses/mit-license.php)
*/
var MooTools = {
'version': '1.2.1',
'build': '0d4845aab3d9a4fdee2f0d4a6dd59210e4b697cf'
};
var Native = function(options){
options = options || {};
var name = options.name;
var legacy = options.legacy;
var protect = options.protect;
var methods = options.implement;
var generics = options.generics;
var initialize = options.initialize;
var afterImplement = options.afterImplement || function(){};
var object = initialize || legacy;
generics = generics !== false;
object.constructor = Native;
object.$family = {name: 'native'};
if (legacy && initialize) object.prototype = legacy.prototype;
object.prototype.constructor = object;
if (name){
var family = name.toLowerCase();
object.prototype.$family = {name: family};
Native.typize(object, family);
}
var add = function(obj, name, method, force){
if (!protect || force || !obj.prototype[name]) obj.prototype[name] = method;
if (generics) Native.genericize(obj, name, protect);
afterImplement.call(obj, name, method);
return obj;
};
object.alias = function(a1, a2, a3){
if (typeof a1 == 'string'){
if ((a1 = this.prototype[a1])) return add(this, a2, a1, a3);
}
for (var a in a1) this.alias(a, a1[a], a2);
return this;
};
object.implement = function(a1, a2, a3){
if (typeof a1 == 'string') return add(this, a1, a2, a3);
for (var p in a1) add(this, p, a1[p], a2);
return this;
};
if (methods) object.implement(methods);
return object;
};
Native.genericize = function(object, property, check){
if ((!check || !object[property]) && typeof object.prototype[property] == 'function') object[property] = function(){
var args = Array.prototype.slice.call(arguments);
return object.prototype[property].apply(args.shift(), args);
};
};
Native.implement = function(objects, properties){
for (var i = 0, l = objects.length; i < l; i++) objects[i].implement(properties);
};
Native.typize = function(object, family){
if (!object.type) object.type = function(item){
return ($type(item) === family);
};
};
(function(){
var natives = {'Array': Array, 'Date': Date, 'Function': Function, 'Number': Number, 'RegExp': RegExp, 'String': String};
for (var n in natives) new Native({name: n, initialize: natives[n], protect: true});
var types = {'boolean': Boolean, 'native': Native, 'object': Object};
for (var t in types) Native.typize(types[t], t);
var generics = {
'Array': ["concat", "indexOf", "join", "lastIndexOf", "pop", "push", "reverse", "shift", "slice", "sort", "splice", "toString", "unshift", "valueOf"],
'String': ["charAt", "charCodeAt", "concat", "indexOf", "lastIndexOf", "match", "replace", "search", "slice", "split", "substr", "substring", "toLowerCase", "toUpperCase", "valueOf"]
};
for (var g in generics){
for (var i = generics[g].length; i--;) Native.genericize(window[g], generics[g][i], true);
};
})();
var Hash = new Native({
name: 'Hash',
initialize: function(object){
if ($type(object) == 'hash') object = $unlink(object.getClean());
for (var key in object) this[key] = object[key];
return this;
}
});
Hash.implement({
forEach: function(fn, bind){
for (var key in this){
if (this.hasOwnProperty(key)) fn.call(bind, this[key], key, this);
}
},
getClean: function(){
var clean = {};
for (var key in this){
if (this.hasOwnProperty(key)) clean[key] = this[key];
}
return clean;
},
getLength: function(){
var length = 0;
for (var key in this){
if (this.hasOwnProperty(key)) length++;
}
return length;
}
});
Hash.alias('forEach', 'each');
Array.implement({
forEach: function(fn, bind){
for (var i = 0, l = this.length; i < l; i++) fn.call(bind, this[i], i, this);
}
});
Array.alias('forEach', 'each');
function $A(iterable){
if (iterable.item){
var array = [];
for (var i = 0, l = iterable.length; i < l; i++) array[i] = iterable[i];
return array;
}
return Array.prototype.slice.call(iterable);
};
function $arguments(i){
return function(){
return arguments[i];
};
};
function $chk(obj){
return !!(obj || obj === 0);
};
function $clear(timer){
clearTimeout(timer);
clearInterval(timer);
return null;
};
function $defined(obj){
return (obj != undefined);
};
function $each(iterable, fn, bind){
var type = $type(iterable);
((type == 'arguments' || type == 'collection' || type == 'array') ? Array : Hash).each(iterable, fn, bind);
};
function $empty(){};
function $extend(original, extended){
for (var key in (extended || {})) original[key] = extended[key];
return original;
};
function $H(object){
return new Hash(object);
};
function $lambda(value){
return (typeof value == 'function') ? value : function(){
return value;
};
};
function $merge(){
var mix = {};
for (var i = 0, l = arguments.length; i < l; i++){
var object = arguments[i];
if ($type(object) != 'object') continue;
for (var key in object){
var op = object[key], mp = mix[key];
mix[key] = (mp && $type(op) == 'object' && $type(mp) == 'object') ? $merge(mp, op) : $unlink(op);
}
}
return mix;
};
function $pick(){
for (var i = 0, l = arguments.length; i < l; i++){
if (arguments[i] != undefined) return arguments[i];
}
return null;
};
function $random(min, max){
return Math.floor(Math.random() * (max - min + 1) + min);
};
function $splat(obj){
var type = $type(obj);
return (type) ? ((type != 'array' && type != 'arguments') ? [obj] : obj) : [];
};
var $time = Date.now || function(){
return +new Date;
};
function $try(){
for (var i = 0, l = arguments.length; i < l; i++){
try {
return arguments[i]();
} catch(e){}
}
return null;
};
function $type(obj){
if (obj == undefined) return false;
if (obj.$family) return (obj.$family.name == 'number' && !isFinite(obj)) ? false : obj.$family.name;
if (obj.nodeName){
switch (obj.nodeType){
case 1: return 'element';
case 3: return (/\S/).test(obj.nodeValue) ? 'textnode' : 'whitespace';
}
} else if (typeof obj.length == 'number'){
if (obj.callee) return 'arguments';
else if (obj.item) return 'collection';
}
return typeof obj;
};
function $unlink(object){
var unlinked;
switch ($type(object)){
case 'object':
unlinked = {};
for (var p in object) unlinked[p] = $unlink(object[p]);
break;
case 'hash':
unlinked = new Hash(object);
break;
case 'array':
unlinked = [];
for (var i = 0, l = object.length; i < l; i++) unlinked[i] = $unlink(object[i]);
break;
default: return object;
}
return unlinked;
};
/*
Script: Browser.js
The Browser Core. Contains Browser initialization, Window and Document, and the Browser Hash.
License:
MIT-style license.
*/
var Browser = $merge({
Engine: {name: 'unknown', version: 0},
Platform: {name: (window.orientation != undefined) ? 'ipod' : (navigator.platform.match(/mac|win|linux/i) || ['other'])[0].toLowerCase()},
Features: {xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector)},
Plugins: {},
Engines: {
presto: function(){
return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925));
},
trident: function(){
return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? 5 : 4);
},
webkit: function(){
return (navigator.taintEnabled) ? false : ((Browser.Features.xpath) ? ((Browser.Features.query) ? 525 : 420) : 419);
},
gecko: function(){
return (document.getBoxObjectFor == undefined) ? false : ((document.getElementsByClassName) ? 19 : 18);
}
}
}, Browser || {});
Browser.Platform[Browser.Platform.name] = true;
Browser.detect = function(){
for (var engine in this.Engines){
var version = this.Engines[engine]();
if (version){
this.Engine = {name: engine, version: version};
this.Engine[engine] = this.Engine[engine + version] = true;
break;
}
}
return {name: engine, version: version};
};
Browser.detect();
Browser.Request = function(){
return $try(function(){
return new XMLHttpRequest();
}, function(){
return new ActiveXObject('MSXML2.XMLHTTP');
});
};
Browser.Features.xhr = !!(Browser.Request());
Browser.Plugins.Flash = (function(){
var version = ($try(function(){
return navigator.plugins['Shockwave Flash'].description;
}, function(){
return new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
}) || '0 r0').match(/\d+/g);
return {version: parseInt(version[0] || 0 + '.' + version[1] || 0), build: parseInt(version[2] || 0)};
})();
function $exec(text){
if (!text) return text;
if (window.execScript){
window.execScript(text);
} else {
var script = document.createElement('script');
script.setAttribute('type', 'text/javascript');
script[(Browser.Engine.webkit && Browser.Engine.version < 420) ? 'innerText' : 'text'] = text;
document.head.appendChild(script);
document.head.removeChild(script);
}
return text;
};
Native.UID = 1;
var $uid = (Browser.Engine.trident) ? function(item){
return (item.uid || (item.uid = [Native.UID++]))[0];
} : function(item){
return item.uid || (item.uid = Native.UID++);
};
var Window = new Native({
name: 'Window',
legacy: (Browser.Engine.trident) ? null: window.Window,
initialize: function(win){
$uid(win);
if (!win.Element){
win.Element = $empty;
if (Browser.Engine.webkit) win.document.createElement("iframe"); //fixes safari 2
win.Element.prototype = (Browser.Engine.webkit) ? window["[[DOMElement.prototype]]"] : {};
}
win.document.window = win;
return $extend(win, Window.Prototype);
},
afterImplement: function(property, value){
window[property] = Window.Prototype[property] = value;
}
});
Window.Prototype = {$family: {name: 'window'}};
new Window(window);
var Document = new Native({
name: 'Document',
legacy: (Browser.Engine.trident) ? null: window.Document,
initialize: function(doc){
$uid(doc);
doc.head = doc.getElementsByTagName('head')[0];
doc.html = doc.getElementsByTagName('html')[0];
if (Browser.Engine.trident && Browser.Engine.version <= 4) $try(function(){
doc.execCommand("BackgroundImageCache", false, true);
});
if (Browser.Engine.trident) doc.window.attachEvent('onunload', function() {
doc.window.detachEvent('onunload', arguments.callee);
doc.head = doc.html = doc.window = null;
});
return $extend(doc, Document.Prototype);
},
afterImplement: function(property, value){
document[property] = Document.Prototype[property] = value;
}
});
Document.Prototype = {$family: {name: 'document'}};
new Document(document);
/*
Script: Array.js
Contains Array Prototypes like each, contains, and erase.
License:
MIT-style license.
*/
Array.implement({
every: function(fn, bind){
for (var i = 0, l = this.length; i < l; i++){
if (!fn.call(bind, this[i], i, this)) return false;
}
return true;
},
filter: function(fn, bind){
var results = [];
for (var i = 0, l = this.length; i < l; i++){
if (fn.call(bind, this[i], i, this)) results.push(this[i]);
}
return results;
},
clean: function() {
return this.filter($defined);
},
indexOf: function(item, from){
var len = this.length;
for (var i = (from < 0) ? Math.max(0, len + from) : from || 0; i < len; i++){
if (this[i] === item) return i;
}
return -1;
},
map: function(fn, bind){
var results = [];
for (var i = 0, l = this.length; i < l; i++) results[i] = fn.call(bind, this[i], i, this);
return results;
},
some: function(fn, bind){
for (var i = 0, l = this.length; i < l; i++){
if (fn.call(bind, this[i], i, this)) return true;
}
return false;
},
associate: function(keys){
var obj = {}, length = Math.min(this.length, keys.length);
for (var i = 0; i < length; i++) obj[keys[i]] = this[i];
return obj;
},
link: function(object){
var result = {};
for (var i = 0, l = this.length; i < l; i++){
for (var key in object){
if (object[key](this[i])){
result[key] = this[i];
delete object[key];
break;
}
}
}
return result;
},
contains: function(item, from){
return this.indexOf(item, from) != -1;
},
extend: function(array){
for (var i = 0, j = array.length; i < j; i++) this.push(array[i]);
return this;
},
getLast: function(){
return (this.length) ? this[this.length - 1] : null;
},
getRandom: function(){
return (this.length) ? this[$random(0, this.length - 1)] : null;
},
include: function(item){
if (!this.contains(item)) this.push(item);
return this;
},
combine: function(array){
for (var i = 0, l = array.length; i < l; i++) this.include(array[i]);
return this;
},
erase: function(item){
for (var i = this.length; i--; i){
if (this[i] === item) this.splice(i, 1);
}
return this;
},
empty: function(){
this.length = 0;
return this;
},
flatten: function(){
var array = [];
for (var i = 0, l = this.length; i < l; i++){
var type = $type(this[i]);
if (!type) continue;
array = array.concat((type == 'array' || type == 'collection' || type == 'arguments') ? Array.flatten(this[i]) : this[i]);
}
return array;
},
hexToRgb: function(array){
if (this.length != 3) return null;
var rgb = this.map(function(value){
if (value.length == 1) value += value;
return value.toInt(16);
});
return (array) ? rgb : 'rgb(' + rgb + ')';
},
rgbToHex: function(array){
if (this.length < 3) return null;
if (this.length == 4 && this[3] == 0 && !array) return 'transparent';
var hex = [];
for (var i = 0; i < 3; i++){
var bit = (this[i] - 0).toString(16);
hex.push((bit.length == 1) ? '0' + bit : bit);
}
return (array) ? hex : '#' + hex.join('');
}
});
/*
Script: Function.js
Contains Function Prototypes like create, bind, pass, and delay.
License:
MIT-style license.
*/
Function.implement({
extend: function(properties){
for (var property in properties) this[property] = properties[property];
return this;
},
create: function(options){
var self = this;
options = options || {};
return function(event){
var args = options.arguments;
args = (args != undefined) ? $splat(args) : Array.slice(arguments, (options.event) ? 1 : 0);
if (options.event) args = [event || window.event].extend(args);
var returns = function(){
return self.apply(options.bind || null, args);
};
if (options.delay) return setTimeout(returns, options.delay);
if (options.periodical) return setInterval(returns, options.periodical);
if (options.attempt) return $try(returns);
return returns();
};
},
run: function(args, bind){
return this.apply(bind, $splat(args));
},
pass: function(args, bind){
return this.create({bind: bind, arguments: args});
},
bind: function(bind, args){
return this.create({bind: bind, arguments: args});
},
bindWithEvent: function(bind, args){
return this.create({bind: bind, arguments: args, event: true});
},
attempt: function(args, bind){
return this.create({bind: bind, arguments: args, attempt: true})();
},
delay: function(delay, bind, args){
return this.create({bind: bind, arguments: args, delay: delay})();
},
periodical: function(periodical, bind, args){
return this.create({bind: bind, arguments: args, periodical: periodical})();
}
});
/*
Script: Number.js
Contains Number Prototypes like limit, round, times, and ceil.
License:
MIT-style license.
*/
Number.implement({
limit: function(min, max){
return Math.min(max, Math.max(min, this));
},
round: function(precision){
precision = Math.pow(10, precision || 0);
return Math.round(this * precision) / precision;
},
times: function(fn, bind){
for (var i = 0; i < this; i++) fn.call(bind, i, this);
},
toFloat: function(){
return parseFloat(this);
},
toInt: function(base){
return parseInt(this, base || 10);
}
});
Number.alias('times', 'each');
(function(math){
var methods = {};
math.each(function(name){
if (!Number[name]) methods[name] = function(){
return Math[name].apply(null, [this].concat($A(arguments)));
};
});
Number.implement(methods);
})(['abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp', 'floor', 'log', 'max', 'min', 'pow', 'sin', 'sqrt', 'tan']);
/*
Script: String.js
Contains String Prototypes like camelCase, capitalize, test, and toInt.
License:
MIT-style license.
*/
String.implement({
test: function(regex, params){
return ((typeof regex == 'string') ? new RegExp(regex, params) : regex).test(this);
},
contains: function(string, separator){
return (separator) ? (separator + this + separator).indexOf(separator + string + separator) > -1 : this.indexOf(string) > -1;
},
trim: function(){
return this.replace(/^\s+|\s+$/g, '');
},
clean: function(){
return this.replace(/\s+/g, ' ').trim();
},
camelCase: function(){
return this.replace(/-\D/g, function(match){
return match.charAt(1).toUpperCase();
});
},
hyphenate: function(){
return this.replace(/[A-Z]/g, function(match){
return ('-' + match.charAt(0).toLowerCase());
});
},
capitalize: function(){
return this.replace(/\b[a-z]/g, function(match){
return match.toUpperCase();
});
},
escapeRegExp: function(){
return this.replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1');
},
toInt: function(base){
return parseInt(this, base || 10);
},
toFloat: function(){
return parseFloat(this);
},
hexToRgb: function(array){
var hex = this.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);
return (hex) ? hex.slice(1).hexToRgb(array) : null;
},
rgbToHex: function(array){
var rgb = this.match(/\d{1,3}/g);
return (rgb) ? rgb.rgbToHex(array) : null;
},
stripScripts: function(option){
var scripts = '';
var text = this.replace(/
(end)
Note:
The title of the element will always be used as the tooltip body. If you put :: on your title, the text before :: will become the tooltip title.
If you put DOM:someElementID in your title, $('someElementID').innerHTML will be used as your tooltip contents (same syntax as above).
If you put AJAX:http://www.example.com/path/to/ajax_file.php in your title, the response text will be used as tooltip contents (same syntax as above). Either absolute or relative paths are ok.
*/
var Garbage = {
elements: [],
collect: function(el){
if (!el.$tmp){
Garbage.elements.push(el);
el.$tmp = {'opacity': 1};
}
return el;
},
trash: function(elements){
for (var i = 0, j = elements.length, el; i < j; i++){
if (!(el = elements[i]) || !el.$tmp) continue;
if (el.$events) el.fireEvent('trash').removeEvents();
for (var p in el.$tmp) el.$tmp[p] = null;
for (var p in Element.prototype) el[p] = null;
el.htmlElement = el.$tmp = el = null;
Garbage.elements.remove(el);
}
},
empty: function(){
Garbage.collect(window);
Garbage.collect(document);
Garbage.trash(Garbage.elements);
}
};
var TipsX3 = new Class({
options: { // modded for X3
onShow: function(tip){
//tip.fade('in');
tip.setStyle('visibility', 'visible');
},
onHide: function(tip){
//tip.fade('out');
tip.setStyle('visibility', 'hidden');
},
maxTitleChars: 8,
showDelay: 100,
hideDelay: 100,
className: 'armory-tooltip',
offsets: {'x': 8, 'y': 8},
fixed: false,
loadingText: 'Please wait...',
errTitle: 'Error',
errText: 'There was a problem retrieving the item from the WoW Armory',
maxWidth: 250,
idName: ''
},
initialize: function(elements, options){
this.setOptions(options);
this.toolTip = new Element('div', {
'class': this.options.className + '-tip',
'styles': {
'position': 'absolute',
'top': '0',
'left': '0',
'visibility': 'hidden',
'max-width' : this.options.maxWidth,
'z-index' : 5000
}
}).inject(document.body);
this.wrapper = new Element('div').inject(this.toolTip);
$$(elements).each(this.build, this);
if (this.options.initialize) this.options.initialize.call(this);
},
build: function(el){
if (!el)
return false;
if (el)
Garbage.collect( el );
if( !el.$tmp || el.$tmp == undefined )
return;
el.$tmp.myTitle = (el.href && el.getTag() == 'a') ? el.href.replace('http://', '') : (el.rel || false);
if (el.title){
// check if we need to extract contents from a DOM element
if (el.title.test('^DOM:', 'i')) {
el.title = $(el.title.split(':')[1].trim()).innerHTML;
}
// check for an URL to retrieve content from
if (el.title.test('^AJAX:', 'i')) {
el.title = this.options.loadingText + '::' + el.title;
}
// check for an URL to retrieve content from
if (el.title.test('^Item:', 'i')) {
el.title = this.options.loadingText + '::' + el.title;
}
// check for an URL to retrieve content from
if (el.title.test('^WoWArmory:', 'i')) {
el.title = this.options.loadingText + '::' + el.title;
}
// check for an URL to retrieve content from
if (el.title.test('^Character:', 'i')) {
el.title = this.options.loadingText + '::' + el.title;
}
var dual = el.title.split('::');
if (dual.length > 1) {
el.$tmp.myTitle = dual[0].trim();
el.$tmp.myText = dual[1].trim();
} else {
el.$tmp.myText = el.title;
}
el.removeAttribute('title');
} else {
el.$tmp.myText = false;
}
if (el.$tmp.myTitle && el.$tmp.myTitle.length > this.options.maxTitleChars) el.$tmp.myTitle = el.$tmp.myTitle.substr(0, this.options.maxTitleChars - 1) + "…";
el.addEvent('mouseenter', function(event){
this.start(el);
if (!this.options.fixed) this.locate(event);
else this.position(el);
}.bind(this));
if (!this.options.fixed) el.addEvent('mousemove', this.locate.bindWithEvent(this));
var end = this.end.bind(this);
el.addEvent('mouseleave', end);
el.addEvent('trash', end);
},
modify: function( element, newTitle, newText ){
element.$tmp.myTitle = newTitle;
element.$tmp.myText = newText;
this.startFinal( element, false );
},
modifyItem: function( element, newTitle, newText, itemIcon ){
element.$tmp.myTitle = newTitle;
element.$tmp.myText = newText;
element.$tmp.itemIcon = itemIcon;
this.startFinal( element, false );
},
modifyNotActive: function( element, newTitle, newText ){
element.$tmp.myTitle = newTitle;
element.$tmp.myText = newText;
},
start: function( el ) {
this.startFinal( el, true );
},
startFinal: function( el, show ) {
this.wrapper.empty();
// check if we have an AJAX Item request - if so, show a loading animation and launch the request
if (el.$tmp.myText && el.$tmp.myText.test('^Item:', 'i')) {
var linkObj = this;
this.ajax = new Request.JSON( { link: 'cancel', evalScripts: false, url: el.$tmp.myText.replace(/Item:/i,'/powered/items/'), onComplete: function( item ){
linkObj.modifyItem( el, '', item.html, item.icon )
}}).get();
el.$tmp.myText = '
';
}
// check if we have an AJAX Char request - if so, show a loading animation and launch the request
if (el.$tmp.myText && el.$tmp.myText.test('^Character:', 'i')) {
var linkObj = this;
this.ajax = new Request.HTML( { link: 'cancel', evalScripts: false, url: el.$tmp.myText.replace(/Character:/i,'/powered/characters/'), onSuccess: function( var1, var2, response, var3 ) {
linkObj.modify( el, '', response )
}}).get();
el.$tmp.myText = '';
}
// check if we have an AJAX wow armory item request - if so, show a loading animation and launch the request
if (el.$tmp.myText && el.$tmp.myText.test('^WoWArmory:', 'i')) {
var linkObj = this;
var myArmoryUrl = el.$tmp.myText.replace(/WoWArmory:/i,'');
myArmoryUrl = '/wowArmoryProxy.php?url=' + escape( myArmoryUrl );
this.ajax = new Request.HTML( { link: 'cancel', evalScripts: false, url: myArmoryUrl, onSuccess: function( var1, var2, response, var3 ) {
linkObj.modify( el, '', response )
}}).get();
el.$tmp.myText = '';
}
if( el.$tmp.itemIcon ) {
this.wrapper.set( 'html', '
' );
} else {
this.wrapper.set( 'html', '' );
}
this.theTip = $( this.options.idName );
if (el.$tmp.myTitle){
this.title = new Element('span').inject(
new Element('div', {'class': this.options.className + '-title'}).inject( this.theTip )
).set( 'html', el.$tmp.myTitle );
}
if (el.$tmp.myText){
this.text = new Element('span').inject(
new Element('div', {'class': this.options.className + '-text'}).inject( this.theTip )
).set( 'html', el.$tmp.myText );
}
if( show == true ) {
$clear(this.timer);
this.timer = this.show.delay( this.options.showDelay, this);
}
},
end: function(event){
$clear(this.timer);
this.timer = this.hide.delay( this.options.hideDelay, this);
},
position: function(element){
var pos = element.getPosition();
this.toolTip.setStyles({
'left': pos.x + this.options.offsets.x,
'top': pos.y + this.options.offsets.y
});
},
locate: function(event){
var win = {'x': window.getWidth(), 'y': window.getHeight()};
var scroll = {'x': window.getScrollLeft(), 'y': window.getScrollTop()};
var tip = {'x': this.toolTip.offsetWidth, 'y': this.toolTip.offsetHeight};
var prop = {'x': 'left', 'y': 'top'};
for (var z in prop){
var pos = event.page[z] + this.options.offsets[z];
if ((pos + tip[z] - scroll[z]) > win[z])
pos =event.page[z] - this.options.offsets[z] - tip[z];
if( z == 'y' && pos - scroll.y < 10 ) {
pos = scroll.y + 10;
}
this.toolTip.setStyle(prop[z], pos);
};
},
show: function(){
if (this.options.timeout) this.timer = this.hide.delay( this.options.timeout, this );
this.fireEvent( 'onShow', [this.toolTip] );
},
hide: function(){
this.fireEvent( 'onHide', [this.toolTip] );
}
});
TipsX3.implement( new Events, new Options );var MorphList = new Class({
Implements: [Events, Options],
options: {/*
onClick: $empty,
onMorph: $empty,*/
morph: { 'link': 'cancel' }
},
initialize: function(menu, options) {
var that = this;
this.setOptions(options);
this.menu = $(menu);
this.menuitems = this.menu.getChildren();
this.menuitems.addEvents({
mouseenter: function(){ that.morphTo(this); },
mouseleave: function(){ that.morphTo(that.current); },
click: function(ev){ that.click(ev, this); }
});
this.bg = new Element('li', {'class': 'background'}).adopt(new Element('div', {'class': 'left'}));
this.bg.inject(this.menu).set('morph', this.options.morph);
this.setCurrent(this.menu.getElement('.current'));
},
click: function(ev, item) {
this.setCurrent(item, true);
this.fireEvent('onClick', [ev, item]);
},
setCurrent: function(el, effect){
if(el && ! this.current) {
this.bg.set('styles', { left: el.offsetLeft, width: el.offsetWidth, height: el.offsetHeight, top: el.offsetTop });
(effect) ? this.bg.fade('hide').fade('in') : this.bg.fade('show');
}
if(this.current) this.current.removeClass('current');
if(el) this.current = el.addClass('current');
},
morphTo: function(to) {
if(! this.current) return;
this.bg.morph({
left: to.offsetLeft, top: to.offsetTop,
width: to.offsetWidth, height: to.offsetHeight
});
this.fireEvent('onMorph', to);
}
});
window.addEvent('domready', function() {
// attach fancy menu for browsers other than ie6
if( !Browser.Engine.trident4 ) {
new MorphList(
$('nav'), {
transition: Fx.Transitions.backOut,
duration: 700
}
);
}
// Check all links for item links or character links
var allLinks = $$( 'a' );
for (var i = allLinks.length - 1; i >= 0; i--){
var currentLink = allLinks[i];
if( !currentLink.href || currentLink.href == '' )
continue;
if( currentLink.rel && currentLink.rel == 'notooltip' )
continue;
if( currentLink.id && currentLink.id == 'fdbk_tab' )
continue;
var regExp1 = new RegExp( "^http://" + window.location.hostname + "\/items\/", "mi" );
var regExp2 = new RegExp( "^\/items\/", "mi" );
var regExp3 = new RegExp( "^http://" + window.location.hostname + "\/eu\/", "mi" );
var regExp4 = new RegExp( "^\/eu\/", "mi" );
var regExp5 = new RegExp( "^http://" + window.location.hostname + "\/us\/", "mi" );
var regExp6 = new RegExp( "^\/us\/", "mi" );
var regExp7 = new RegExp( "^http://" + window.location.hostname + "\/cn\/", "mi" );
var regExp8 = new RegExp( "^\/cn\/", "mi" );
var regExp9 = new RegExp( "^http://" + window.location.hostname + "\/kr\/", "mi" );
var regExp10 = new RegExp( "^\/kr\/", "mi" );
var regExp11 = new RegExp( "^http://" + window.location.hostname + "\/tw\/", "mi" );
var regExp12 = new RegExp( "^\/tw\/", "mi" );
// Check whether it starts with Armory Light
if( regExp1.test( currentLink.href ) == false &&
regExp2.test( currentLink.href ) == false &&
regExp3.test( currentLink.href ) == false &&
regExp4.test( currentLink.href ) == false &&
regExp5.test( currentLink.href ) == false &&
regExp6.test( currentLink.href ) == false &&
regExp7.test( currentLink.href ) == false &&
regExp8.test( currentLink.href ) == false &&
regExp9.test( currentLink.href ) == false &&
regExp10.test( currentLink.href ) == false &&
regExp11.test( currentLink.href ) == false &&
regExp12.test( currentLink.href ) == false )
continue;
var linkElements = currentLink.href.split( '/' );
var linkRel = currentLink.rel;
for (var j=linkElements.length; j <= 6; j++) {
linkElements[j] = '';
};
var LinkType = linkElements[3].toLowerCase();
var ItemId = linkElements[4].toLowerCase();
var ServerName = linkElements[4];
var CharacterName = linkElements[5];
var JSONRequestUrl = false;
if( LinkType == 'items' && ItemId != '' && ItemId > 0 && linkRel != 'notip' && linkRel.match('wowarmory') == null ) {
// Ok it's an item Link, create a span wrapper
var mySpan = new Element('span', {
'title': 'Item:' + ItemId,
'class': 'armoryitemtip' }
);
mySpan.wraps( currentLink );
} else if( LinkType == 'items' && ItemId != '' && ItemId > 0 && linkRel.match('wowarmory') == 'wowarmory' ) {
// Ok it's an item Link for the wow armory, create a span wrapper
var linkRelElements = currentLink.rel.split( ':' );
var armoryRegion = linkRelElements[1];
if( armoryRegion == 'us' )
armoryRegion = 'www';
var mySpan = new Element('span', {
'title': 'WoWArmory:http://' + armoryRegion + '.wowarmory.com/item-tooltip.xml?i=' + ItemId + '&s=' + linkRelElements[4] + '&r=' + linkRelElements[2] + '&n=' + linkRelElements[3],
'class': 'armoryitemtip' }
);
mySpan.wraps( currentLink );
} else if( ( LinkType == 'eu' || LinkType == 'us' || LinkType == 'kr' || LinkType == 'cn' || LinkType == 'tw' )&& ServerName != '' && CharacterName != '' ) {
// Ok it's an item Link, create a span wrapper
var mySpan = new Element('span', {
'title': 'Character:' + LinkType + '/' + ServerName + '/' + CharacterName,
'class': 'armoryitemtip' }
);
mySpan.wraps( currentLink );
} else {
continue;
}
};
// Preload tooltip background
new Element('img',{ src: 'http://static.armory-light.com/images/tooltip-trans.png' });
// Attach tooltips to all armorytip elements
var ArmoryLightTips = new TipsX3(
$$('.armorytip'), {
maxTitleChars: 100,
showDelay: 0,
maxWidth: 265,
idName: 'armoryGenericTipContent'
}
);
// Attach item tooltips to all armoryitemtip elements
var ArmoryLightTips = new TipsX3(
$$('.armoryitemtip'), {
maxTitleChars: 100,
maxWidth: 335,
showDelay: 0,
idName: 'armoryItemTipContent',
loadingText: 'Loading...'
}
);
// Set highslide options
if( undefined !== window.hs ) {
hs.graphicsDir = 'http://static.armory-light.com/images/highslide/';
hs.outlineType = 'rounded-black';
hs.wrapperClassName = 'draggable-header no-footer wide-border';
hs.allowSizeReduction = false;
hs.preserveContent = false;
hs.align = 'center';
hs.dimmingOpacity = 0.35;
hs.dragByHeading = false;
hs.fadeInOut = true;
hs.dimmingGeckoFix = true;
hs.dimmingDuration = 20;
hs.showCredits = false;
hs.objectLoadTime = 'after';
hs.transitions = ["expand"];
hs.Expander.prototype.onDrag = function (sender, e) {
return false;
};
hs.Expander.prototype.onBeforeClose = function (sender, e) {
window.location.hash = '#';
return true;
};
}
});
Array.prototype.inArray = function( value ) {
var i;
for (i=0; i < this.length; i++) {
// Matches identical (===), not just similar (==).
if (this[i] === value) {
return true;
}
}
return false;
};
var talent = new Array();
var rank = new Array();
var tree = new Array();
var treeStartStop = new Array();
var currentLevel = getLevel();
var numPointsAvailable = currentLevel - 9;
var windowTitle = '';
var CurrentSpec = '';
var maxTierArray = new Array();
var ArmoryLightTalentTips = null;
// Onload handler
window.addEvent( 'domready', function() {
// Get the window title
if( document.title ) {
windowTitle = document.title;
var firstArrowPos = windowTitle.indexOf( 'ยป' );
if( firstArrowPos > 0 ) {
windowTitle = windowTitle.substring( 0, firstArrowPos ) + '({t0}/{t1}/{t2}) ' + windowTitle.substring( firstArrowPos );
}
}
// initialise all talents
initTalents();
// Create/edit link to talent spec
$('resetalltalents').addEvent( 'click', function( e ) {
var pQuestion = confirm( 'Do you really want to reset all talent trees?' );
if( pQuestion ) {
CurrentSpec = '';
CheckSpec();
for (theTalentID = 0; talent[theTalentID]; theTalentID++) {
RenderOneTalent( theTalentID, false );
}
RenderTalents();
// prevent default link
e.stop();
return false;
}
});
// initialise tooltips
ArmoryLightTalentTips = new TipsX3(
$$('.armorytiptalents'), {
maxTitleChars: 100,
showDelay: 0,
maxWidth: 300,
idName: 'armoryTalentTipContent'
}
);
});
window.addEvent( 'load', function() {
var myFx = new Fx.Tween( 'overlay' );
myFx.start( 'opacity', '0.85', '0' );
});
function UpdateControls() {
// Display available points
var tPointsAvail = getAvailablePoints();
if( tPointsAvail == 0 )
tPointsAvail = 'none';
$('pointsAvailable').set( 'text', tPointsAvail );
// Display available points
var tRequiredLevel = 80 - ( getAvailablePoints() ) ;
if( tRequiredLevel < 10 )
tRequiredLevel = 10;
$('requiresLevel').set( 'text', tRequiredLevel );
// Update spent points
for( var i=1; i <=3; i++ ) {
var control = $( 'talentTreePointsSpent' + i );
control.set( 'text', getSpentPointsInTree( i-1 ) );
};
// Update window title
if( document.title ) {
var obj = { t0: getSpentPointsInTree( 0 ).toString(), t1: getSpentPointsInTree( 1 ).toString(), t2: getSpentPointsInTree( 2 ).toString() };
var title = windowTitle;
document.title = title.substitute( obj );
}
}
// Fill the trees with talents
function initTalents() {
// First, hide the table with an overlay
var tablesContainer = $('talentTrees');
var tablesContainerSize = tablesContainer.getSize();
$('overlay').style.width = tablesContainerSize.x + 'px';
$('overlay').style.height = tablesContainerSize.y + 'px';
$('overlay').style.display = 'block';
// Get current talent tree
CurrentSpec = getTalents();
CheckSpec();
// Create/edit link to talent spec
$('talentbuildlink').set( 'href', '/talents/' + playerClass.toLowerCase().stripSpaces() + '/' + CurrentSpec );
// Set the tree backgrounds
$( 'talentTree0' ).setStyle( 'background-image', 'url(http://static.armory-light.com/images/talentCalculator/' + playerClass + '/' + tree[0].toLowerCase().stripSpaces() + '/background.jpg)' );
$( 'talentTree1' ).setStyle( 'background-image', 'url(http://static.armory-light.com/images/talentCalculator/' + playerClass + '/' + tree[1].toLowerCase().stripSpaces() + '/background.jpg)' );
$( 'talentTree2' ).setStyle( 'background-image', 'url(http://static.armory-light.com/images/talentCalculator/' + playerClass + '/' + tree[2].toLowerCase().stripSpaces() + '/background.jpg)' );
// Set the max tier for each tree to 0
maxTierArray[0] = 0;
maxTierArray[1] = 0;
maxTierArray[2] = 0;
// Make text in talent trees not selectable
disableSelection( $( 'talentTrees' ) );
// Display the controls
for (var i=1; i <=3; i++) {
var controls = $( 'talentTreeControls' + i );
var controlHtml = '
';
controlHtml += tree[i-1];
controlHtml += ' ()';
controls.set( 'html', controlHtml );
};
// Attach tooltips
var ArmoryLightTipsGenerated = new TipsX3(
$$('.armorytipgenerated'), {
maxTitleChars: 100,
showDelay: 0,
maxWidth: 265,
idName: 'armoryGenericTipContentGenerated'
}
);
UpdateControls();
// Figure out which image we use
var cTalentIcons = 'http://static.armory-light.com/images/talentCalculator/' + playerClass.toLowerCase().stripSpaces() + '/' + playerClass.toLowerCase().stripSpaces();
// Loop over all talents
var iPosition = 0;
var cMyIconIndent = 0;
var cTreeTurnover = 0;
for (theTalentID = 0; talent[theTalentID]; theTalentID++) {
curTalent = talent[theTalentID];
var cTree = curTalent[0]; // Tree 0-2
var cColumn = curTalent[3]; // Column 1-4
var cTier = curTalent[4]; // Tier 1-11 (11 for the patch 3.0.2 talents)
var cName = curTalent[1]; // The talent name
var cMaxPoint = curTalent[2]; // Maximum number of points for that talent (1-5)
// Find out the ID of the element (for example s0t9c2)
var tId = 's' + cTree + 't' + cTier + 'c' + cColumn;
var tIdElement = $( tId );
// Find out how many points were spent for that talent
var talentPointsSet = getSpentPointsInTalent( iPosition );
// Get the talent description for the current Rank and for the next rank
if( talentPointsSet == 0 ) {
// Only get next rank
cRankDescription = "Rank " + talentPointsSet + "/" + cMaxPoint + "";
if( parseInt( getSpentPointsInTree( cTree ) ) < parseInt( ( cTier - 1 ) * 5 ) )
cRankDescription += "Requires " + parseInt( ( cTier - 1 ) * 5 ) + " points spent in " + tree[cTree] + " Talents";
if( !isDependencySatisfied( curTalent ) ) {
var dependency = curTalent[5][0];
var dependantPoints = curTalent[5][1];
cRankDescription += "Requires " + dependantPoints + " points spent in " + talent[dependency][1] + "";
}
cRankDescription += rank[theTalentID][0];
cRankDescription += " Click to learn";
} else if( talentPointsSet > 0 && talentPointsSet >= cMaxPoint ) {
// Only get current rank
cRankDescription = "Rank " + talentPointsSet + "/" + cMaxPoint + "";
cRankDescription += rank[theTalentID][talentPointsSet - 1];
// Different tooltip for Opera
if( Browser.Engine.presto && Browser.Platform.mac )
cRankDescription += " ⌘ + click to unlearn";
else if( Browser.Engine.presto )
cRankDescription += " Ctrl + click to unlearn";
else
cRankDescription += " Right click to unlearn";
} else {
// Get current rank
cRankDescription = "Rank " + talentPointsSet + "/" + cMaxPoint + "";
cRankDescription += rank[theTalentID][talentPointsSet - 1];
// And add next rank description
cRankDescription += " Next rank:";
cRankDescription += rank[theTalentID][talentPointsSet];
cRankDescription += " Click to learn";
}
// If we're in a new tree, reset the indent
if( cTreeTurnover != cTree ) {
cTreeTurnover = cTree;
cMyIconIndent = 0;
}
// If talent points are spent, update the maxTier array
if( talentPointsSet > 0 )
maxTierArray[cTree] = cTier;
// Fill the ID element with a clickable div and a span which holds the talent points
tIdElement.set( 'html', '' + cName + '::' + cRankDescription + ' ', talentPointsSet, '/', cMaxPoint, ' ' );
// Add the hasTalent class (this adds the background graphic for that talent slot)
tIdElement.addClass( 'hasTalent' );
var myTalentImage = $( 'talent' + tId );
myTalentImage.setStyle( 'background-image', 'url(' + cTalentIcons + '_' + (cTree + 1) + '.jpg)' );
if( talentPointsSet > 0 )
myTalentImage.setStyle( 'background-position', '-' + cMyIconIndent + 'px 0px' );
else
myTalentImage.setStyle( 'background-position', '-' + cMyIconIndent + 'px -36px' );
// Find out which other talents this talent depends on
if( curTalent[5] && curTalent[5][0] && typeof curTalent[5] == "object" ) {
var dependency = curTalent[5][0];
var depTalent = talent[dependency];
var stepsLeft = ( cColumn - depTalent[3] );
var stepsUp = ( cTier - depTalent[4] );
var imageFile = '';
if( stepsUp > 0 )
imageFile += "down-" + stepsUp;
if( stepsLeft > 0 )
imageFile += "-right-" + stepsLeft;
else if( stepsLeft < 0 )
imageFile += "-left-" + (stepsLeft*-1);
var arrowColor = 'grey';
if( getSpentPointsInTalent( iPosition ) == cMaxPoint )
arrowColor = 'yellow';
else if( getAvailablePoints() <= 0 )
arrowColor = 'grey';
else if( isDependencySatisfied( curTalent ) && getSpentPointsInTree( cTree ) >= ( cTier - 1 ) * 5 )
arrowColor = 'green';
var myArrow = new Element( 'div', {
'class' : arrowColor + ' dependency arrow-' + imageFile,
'id' : 'arrow' + tId
});
// Inject the arrow element
myArrow.inject( tIdElement, 'top' );
}
// Add hover and click events
var myButton = $( 'talentButton' + iPosition );
if( Browser.Engine.trident == true )
myButton.addEvent( 'dblclick', function( event ) { clickTalent( event, this.getProperty( 'rel' ) ) } );
myButton.addEvent( 'mousedown', function( event ) { clickTalent( event, this.getProperty( 'rel' ) ) } );
myButton.addEvent( 'contextmenu', function( event ) { event.stop(); return false; } );
// Add the correct class to the TD, remove the other classes
if( getSpentPointsInTalent( iPosition ) == 0 ) {
tIdElement.addClass( 'talentNoPoints' );
tIdElement.removeClass( 'talentAllPoints' );
tIdElement.removeClass( 'talentSomePoints' );
} else if( getSpentPointsInTalent( iPosition ) == cMaxPoint ) {
tIdElement.addClass( 'talentAllPoints' );
tIdElement.removeClass( 'talentNoPoints' );
tIdElement.removeClass( 'talentSomePoints' );
} else if( getSpentPointsInTalent( iPosition ) < cMaxPoint ) {
tIdElement.addClass( 'talentSomePoints' );
tIdElement.removeClass( 'talentAllPoints' );
tIdElement.removeClass( 'talentNoPoints' );
}
// Check whether the talent can be learned
if( getAvailablePoints() > 0 && parseInt( getSpentPointsInTree( cTree ) ) >= parseInt( ( cTier - 1 ) * 5 ) && isDependencySatisfied( curTalent ) )
tIdElement.addClass( 'talentCanLearn' );
else
tIdElement.removeClass( 'talentCanLearn' );
// Go to next position
iPosition++;
// Increase the indent by 36 (pixels) since each talent icon is offset by 36 pixels
cMyIconIndent += 36;
}
}
// This function re-renders the talent tree based on the current spec
function RenderOneTalent( theTalentID, callRenderTalents ) {
curTalent = talent[theTalentID];
var cTree = curTalent[0];
var cColumn = curTalent[3];
var cTier = curTalent[4];
var cName = curTalent[1];
var cMaxPoint = curTalent[2];
var cMyIconIndent = theTalentID * 36;
if( cTree > 0 )
cMyIconIndent = cMyIconIndent - ( parseInt( treeStartStop[0] + 1 ) * 36);
if( cTree > 1 )
cMyIconIndent = cMyIconIndent + ( parseInt( treeStartStop[0] + 1 ) * 36) - ( parseInt( treeStartStop[1] + 1 ) * 36 );
// Find out the ID of the | element (for example s0t9c2)
var tId = 's' + cTree + 't' + cTier + 'c' + cColumn;
var tIdElement = $( tId );
// Find out how many points were spent for that talent
var talentPointsSet = getSpentPointsInTalent( theTalentID );
var myTalentImage = $( 'talent' + tId );
var myTalentPoints = $( 'talentPoints' + theTalentID );
// Set the background position of the talent icon, greying it out or making it visible
if( talentPointsSet > 0 )
myTalentImage.setStyle( 'background-position', '-' + cMyIconIndent + 'px 0px' );
else
myTalentImage.setStyle( 'background-position', '-' + cMyIconIndent + 'px -36px' );
// Get the talent description for the current Rank and for the next rank
if( talentPointsSet == 0 ) {
// Only get next rank
cRankDescription = "Rank " + talentPointsSet + "/" + cMaxPoint + "";
if( parseInt( getSpentPointsInTree( cTree ) ) < parseInt( ( cTier - 1 ) * 5 ) )
cRankDescription += "Requires " + parseInt( ( cTier - 1 ) * 5 ) + " points spent in " + tree[cTree] + " Talents";
if( !isDependencySatisfied( curTalent ) ) {
var dependency = curTalent[5][0];
var dependantPoints = curTalent[5][1];
cRankDescription += "Requires " + dependantPoints + " points spent in " + talent[dependency][1] + "";
}
cRankDescription += rank[theTalentID][0];
cRankDescription += " Click to learn";
} else if( talentPointsSet > 0 && talentPointsSet >= cMaxPoint ) {
// Only get current rank
cRankDescription = "Rank " + talentPointsSet + "/" + cMaxPoint + "";
cRankDescription += rank[theTalentID][talentPointsSet - 1];
// Different tooltip for Opera
if( Browser.Engine.presto && Browser.Platform.mac )
cRankDescription += " ⌘ + click to unlearn";
else if( Browser.Engine.presto )
cRankDescription += " Ctrl + click to unlearn";
else
cRankDescription += " Right click to unlearn";
} else {
// Get current rank
cRankDescription = "Rank " + talentPointsSet + "/" + cMaxPoint + "";
cRankDescription += rank[theTalentID][talentPointsSet - 1];
// And add next rank description
cRankDescription += " Next rank:";
cRankDescription += rank[theTalentID][talentPointsSet];
cRankDescription += " Click to learn";
}
// Set the talent button title
ArmoryLightTalentTips.modify( $('talentButton' + theTalentID ), cName, cRankDescription );
// Update the points
myTalentPoints.set( 'text', talentPointsSet + '/' + cMaxPoint );
// Add the correct class to the TD
if( getSpentPointsInTalent( theTalentID ) == 0 ) {
tIdElement.addClass( 'talentNoPoints' );
tIdElement.removeClass( 'talentAllPoints' );
tIdElement.removeClass( 'talentSomePoints' );
} else if( getSpentPointsInTalent( theTalentID ) == cMaxPoint ) {
tIdElement.addClass( 'talentAllPoints' );
tIdElement.removeClass( 'talentNoPoints' );
tIdElement.removeClass( 'talentSomePoints' );
} else if( getSpentPointsInTalent( theTalentID ) < cMaxPoint ) {
tIdElement.addClass( 'talentSomePoints' );
tIdElement.removeClass( 'talentAllPoints' );
tIdElement.removeClass( 'talentNoPoints' );
}
if( callRenderTalents )
RenderTalents();
}
// This function re-renders the talent tree based on the current spec
function RenderTalents() {
UpdateControls();
// Create/edit link to talent spec
$('talentbuildlink').set( 'href', '/talents/' + playerClass.toLowerCase().stripSpaces() + '/' + CurrentSpec );
// Loop over all talents
var iPosition = 0;
for (theTalentID = 0; talent[theTalentID]; theTalentID++) {
curTalent = talent[theTalentID];
var cTree = curTalent[0];
var cColumn = curTalent[3];
var cTier = curTalent[4];
var cName = curTalent[1];
var cMaxPoint = curTalent[2];
// Find out how many points were spent for that talent
var talentPointsSet = getSpentPointsInTalent( theTalentID );
// If talent points are spent, update the maxTier array
if( talentPointsSet > 0 )
maxTierArray[cTree] = cTier;
// Find out the ID of the | element (for example s0t9c2)
var tId = 's' + cTree + 't' + cTier + 'c' + cColumn;
var tIdElement = $( tId );
// Check whether the talent can be learned
if( getAvailablePoints() > 0 && getSpentPointsInTree( cTree ) >= ( cTier - 1 ) * 5 && isDependencySatisfied( curTalent ) ) {
// CanLearn, check if the element already has the class
if( !tIdElement.hasClass( 'talentCanLearn' ) ) {
tIdElement.addClass( 'talentCanLearn' );
// Get the talent description for the current Rank and for the next rank
if( talentPointsSet == 0 ) {
// Only get next rank
cRankDescription = "Rank " + talentPointsSet + "/" + cMaxPoint + "";
if( parseInt( getSpentPointsInTree( cTree ) ) < parseInt( ( cTier - 1 ) * 5 ) )
cRankDescription += "Requires " + parseInt( ( cTier - 1 ) * 5 ) + " points spent in " + tree[cTree] + " Talents";
if( !isDependencySatisfied( curTalent ) ) {
var dependency = curTalent[5][0];
var dependantPoints = curTalent[5][1];
cRankDescription += "Requires " + dependantPoints + " points spent in " + talent[dependency][1] + "";
}
cRankDescription += rank[theTalentID][0];
cRankDescription += " Click to learn";
} else if( talentPointsSet > 0 && talentPointsSet >= cMaxPoint ) {
// Only get current rank
cRankDescription = "Rank " + talentPointsSet + "/" + cMaxPoint + "";
cRankDescription += rank[theTalentID][talentPointsSet - 1];
// Different tooltip for Opera
if( Browser.Engine.presto && Browser.Platform.mac )
cRankDescription += " ⌘ + click to unlearn";
else if( Browser.Engine.presto )
cRankDescription += " Ctrl + click to unlearn";
else
cRankDescription += " Right click to unlearn";
} else {
// Get current rank
cRankDescription = "Rank " + talentPointsSet + "/" + cMaxPoint + "";
cRankDescription += rank[theTalentID][talentPointsSet - 1];
// And add next rank description
cRankDescription += " Next rank:";
cRankDescription += rank[theTalentID][talentPointsSet];
cRankDescription += " Click to learn";
}
// Set the talent button title
ArmoryLightTalentTips.modifyNotActive( $('talentButton' + theTalentID ), cName, cRankDescription );
}
} else {
// CanNOTLearn, check if the element already has the class
if( tIdElement.hasClass( 'talentCanLearn' ) ) {
tIdElement.removeClass( 'talentCanLearn' );
// Get the talent description for the current Rank and for the next rank
if( talentPointsSet == 0 ) {
// Only get next rank
cRankDescription = "Rank " + talentPointsSet + "/" + cMaxPoint + "";
if( parseInt( getSpentPointsInTree( cTree ) ) < parseInt( ( cTier - 1 ) * 5 ) )
cRankDescription += "Requires " + parseInt( ( cTier - 1 ) * 5 ) + " points spent in " + tree[cTree] + " Talents";
if( !isDependencySatisfied( curTalent ) ) {
var dependency = curTalent[5][0];
var dependantPoints = curTalent[5][1];
cRankDescription += "Requires " + dependantPoints + " points spent in " + talent[dependency][1] + "";
}
cRankDescription += rank[theTalentID][0];
cRankDescription += " Click to learn";
} else if( talentPointsSet > 0 && talentPointsSet >= cMaxPoint ) {
// Only get current rank
cRankDescription = "Rank " + talentPointsSet + "/" + cMaxPoint + "";
cRankDescription += rank[theTalentID][talentPointsSet - 1];
// Different tooltip for Opera
if( Browser.Engine.presto && Browser.Platform.mac )
cRankDescription += " ⌘ + click to unlearn";
else if( Browser.Engine.presto )
cRankDescription += " Ctrl + click to unlearn";
else
cRankDescription += " Right click to unlearn";
} else {
// Get current rank
cRankDescription = "Rank " + talentPointsSet + "/" + cMaxPoint + "";
cRankDescription += rank[theTalentID][talentPointsSet - 1];
// And add next rank description
cRankDescription += " Next rank:";
cRankDescription += rank[theTalentID][talentPointsSet];
cRankDescription += " Click to learn";
}
// Set the talent button title
ArmoryLightTalentTips.modifyNotActive( $('talentButton' + theTalentID ), cName, cRankDescription );
}
}
// Find out which other talents this talent depends on
if( curTalent[5] && curTalent[5][0] && typeof curTalent[5] == "object" ) {
var arrowColor = 'grey';
if( getSpentPointsInTalent( iPosition ) == cMaxPoint )
arrowColor = 'yellow';
else if( getAvailablePoints() <= 0 )
arrowColor = 'grey';
else if( isDependencySatisfied( curTalent ) && getSpentPointsInTree( cTree ) >= ( cTier - 1 ) * 5 )
arrowColor = 'green';
// Try to find the arrow element
var myArrow = $( 'arrow' + tId );
// if the arrow element really exists...
if( myArrow ) {
if( arrowColor == 'grey' ) {
myArrow.removeClass( 'yellow' );
myArrow.removeClass( 'green' );
myArrow.addClass( 'grey' );
} else if( arrowColor == 'yellow' ) {
myArrow.removeClass( 'grey' );
myArrow.removeClass( 'green' );
myArrow.addClass( 'yellow' );
} else if( arrowColor == 'green' ) {
myArrow.removeClass( 'grey' );
myArrow.removeClass( 'yellow' );
myArrow.addClass( 'green' );
}
}
}
// Go to next position
iPosition++;
}
}
// Checks a talent tree for correct length
function CheckSpec() {
if( CurrentSpec.length != ( parseInt( treeStartStop[2] ) + 1 ) ) {
CurrentSpec = CurrentSpec.pad( parseInt( treeStartStop[2] ) + 1, "0", 1 );
}
}
// Strip all spaces from a string
String.prototype.stripSpaces = function() {
return this.replace( /\s/g, "" );
};
String.prototype.pad = function(l, s, t){
return s || (s = " "), (l -= this.length) > 0 ? (s = new Array(Math.ceil(l / s.length)
+ 1).join(s)).substr(0, t = !t ? l : t == 1 ? 0 : Math.ceil(l / 2))
+ this + s.substr(0, l - t) : this;
};
function getTalentID(talentName) {
var theTalentID;
for (theTalentID = 0; talent[theTalentID]; theTalentID++) {
if (talent[theTalentID][1] == talentName)
return theTalentID;
}
}
function getAvailablePoints() {
return parseInt( numPointsAvailable - getSpentPoints( CurrentSpec ) );
}
function getSpentPoints( TalentTree ) {
var PointsSpent = 0;
for( var i = TalentTree.length - 1; i >= 0; i-- ) {
PointsSpent += parseInt( TalentTree.charAt( i ) );
};
return PointsSpent;
}
function getSpentPointsInTalent( talentID ) {
var PointsSpent = CurrentSpec.charAt( talentID );
if( PointsSpent == "" )
PointsSpent = 0;
return parseInt( PointsSpent );
}
function getSpentPointsInTree( treeID ) {
var myTalentTree = new Array();
myTalentTree[0] = CurrentSpec.substring( 0, treeStartStop[0] + 1 );
myTalentTree[1] = CurrentSpec.substring( treeStartStop[0] + 1, treeStartStop[1] + 1 );
myTalentTree[2] = CurrentSpec.substring( treeStartStop[1] + 1, treeStartStop[2] + 1 );
var cSelectedTree = myTalentTree[treeID];
var PointsSpent = 0;
for( var i = cSelectedTree.length - 1; i >= 0; i-- ) {
PointsSpent += parseInt( cSelectedTree.charAt( i ) );
};
return PointsSpent;
}
function isDependencySatisfied( learnTalent ) {
// Does the talent have a dependency and is that dependency satisfied
if( learnTalent[5] && learnTalent[5][0] && typeof learnTalent[5] == "object" ) {
var dependency = learnTalent[5][0];
var dependantPoints = learnTalent[5][1];
var depTalent = talent[dependency];
var depTalentID = getTalentID( depTalent[1] );
if( getSpentPointsInTalent( depTalentID ) != dependantPoints )
return false;
}
return true;
}
function hasDependentTalentWithPoints( talentID ) {
var loopStart;
var loopStop;
var theTree = talent[talentID][0];
if ( talentID != 0 )
loopStart = talentID - 1;
else
loopStart = talentID;
loopStop = treeStartStop[theTree];
while( loopStart <= loopStop ){
if( talent[loopStart][5] && talent[loopStart][5][0] == talentID && getSpentPointsInTalent( loopStart ) != 0 )
return true;
loopStart++;
}
return false;
}
function getPointsSpentInTier( theTree, theTier ) {
var PointsSpentInTier = 0;
for (var i = talent.length - 1; i >= 0; i--){
if( talent[i][0] == theTree && talent[i][4] == theTier ) {
// This is a talent in the right tree and in the right tier!
PointsSpentInTier += parseInt( getSpentPointsInTalent( i ) );
}
};
return PointsSpentInTier;
}
function clickTalent( e, talentID ) {
if( e.shift || e.alt || e.control || e.meta || e.rightClick )
UnlearnTalent( talentID, 1 );
else
LearnTalent( talentID, 1 );
e.stop();
}
function LearnTalent( talentID, numPoints ) {
var learnTalent = talent[talentID];
var cTree = learnTalent[0];
var cColumn = learnTalent[3];
var cTier = learnTalent[4];
var cName = learnTalent[1];
var cMaxPoint = learnTalent[2];
// Check whether there are points left to learn
if( getAvailablePoints() <= 0 )
return;
// Check whether max number of points already spent in that talent
if( getSpentPointsInTalent( talentID ) >= cMaxPoint )
return;
// Depending on the tier, calculate whether enough points were spent to go deeper into the tree
if( parseInt( getSpentPointsInTree( cTree ) ) < parseInt( ( cTier - 1 ) * 5 ) )
return;
// Does the talent have a dependency and is that dependency satisfied
if( !isDependencySatisfied( learnTalent ) )
return;
// Ok, all dependencies are satisfied... build the new tree with one more point spent :)
CurrentSpec = (CurrentSpec.slice( 0, talentID )).toString() + ( parseInt( getSpentPointsInTalent( talentID ) ) + numPoints ).toString() + (CurrentSpec.slice( parseInt( talentID ) + 1 ) ).toString();
// Render the talent trees
RenderOneTalent( talentID, true );
}
function UnlearnTalent( talentID, numPoints ) {
// Check whether there are points spent in that talent
if( getSpentPointsInTalent( talentID ) < numPoints )
return;
if( hasDependentTalentWithPoints( talentID ) )
return;
var unLearnTalent = talent[talentID];
var cTree = unLearnTalent[0];
var cColumn = unLearnTalent[3];
var cTier = unLearnTalent[4];
var cName = unLearnTalent[1];
var cMaxPoint = unLearnTalent[2];
// Get the max tier for this tree
var maxTier = parseInt( maxTierArray[cTree] );
// Unlearning in the max tier is always allowed
if( cTier != maxTier ) {
var numPointsSpentInTier = new Array();
// For every tier up to the max tier, check if there are enough points left
for( var i = maxTier; i >= 1; i-- ) {
// Get the number of points spent in this tier
numPointsSpentInTier[i] = getPointsSpentInTier( cTree, i );
// If we are removing points from THIS tier, calculate this too
if( cTier == i )
numPointsSpentInTier[i] = numPointsSpentInTier[i] - parseInt( numPoints );
// Fix if below 0
if( numPointsSpentInTier[i] < 0 ) numPointsSpentInTier[i] = 0;
};
for( var i = maxTier; i >= 1; i-- ) {
// Calculate how many points we need for the talents in this tier
var pointsRequired = parseInt( ( i - 1 ) * 5 );
var numPointsSpentSoFar = 0;
var j = (i-1);
while( j >= 1 ) {
numPointsSpentSoFar += parseInt( numPointsSpentInTier[j] );
j--;
}
// Return if number of points not satisfied
if( numPointsSpentSoFar < pointsRequired )
return;
}
}
// Ok, all dependencies are satisfied... build the new tree with one less point spent :)
CurrentSpec = (CurrentSpec.slice( 0, talentID )).toString() + ( parseInt( getSpentPointsInTalent( talentID ) ) - numPoints ).toString() + (CurrentSpec.slice( parseInt( talentID ) + 1 ) ).toString();
// Render the talent trees
RenderOneTalent( talentID, true );
}
function disableSelection( element ) {
if( typeof element.onselectstart != "undefined" )
element.onselectstart = function() {
return false;
};
if( !element || !element.style )
return;
element.onmousedown = function(){
return false;
};
element.unselectable = 'on';
element.setStyle( 'MozUserSelect', 'none' );
element.setStyle( 'cursor', 'default' ) ;
}
var playerClass = "rogue";
var i = 0;
var t = 0;
var className = "Rogue Talents";
tree[i] = "Assassination"; i++;
tree[i] = "Combat"; i++;
tree[i] = "Subtlety"; i++;
i = 0;
talent[i] = [0, "Improved Eviscerate", 3, 1, 1]; i++;
talent[i] = [0, "Remorseless Attacks", 2, 2, 1]; i++;
talent[i] = [0, "Malice", 5, 3, 1]; i++;
talent[i] = [0, "Ruthlessness", 3, 1, 2]; i++;
talent[i] = [0, "Blood Spatter", 2, 2, 2]; i++;
talent[i] = [0, "Puncturing Wounds", 3, 4, 2]; i++;
talent[i] = [0, "Vigor", 1, 1, 3]; i++;
talent[i] = [0, "Improved Expose Armor", 2, 2, 3]; i++;
talent[i] = [0, "Lethality", 5, 3, 3, [getTalentID("Malice"),5]]; i++;
talent[i] = [0, "Vile Poisons", 3, 2, 4]; i++;
talent[i] = [0, "Improved Poisons", 5, 3, 4]; i++;
talent[i] = [0, "Fleet Footed", 2, 1, 5]; i++;
talent[i] = [0, "Cold Blood", 1, 2, 5]; i++;
talent[i] = [0, "Improved Kidney Shot", 3, 3, 5]; i++;
talent[i] = [0, "Quick Recovery", 2, 4, 5]; i++;
talent[i] = [0, "Seal Fate", 5, 2, 6, [getTalentID("Cold Blood"),1]]; i++;
talent[i] = [0, "Murder", 2, 3, 6]; i++;
talent[i] = [0, "Deadly Brew", 2, 1, 7]; i++;
talent[i] = [0, "Overkill", 1, 2, 7]; i++;
talent[i] = [0, "Deadened Nerves", 3, 3, 7]; i++;
talent[i] = [0, "Focused Attacks", 3, 1, 8]; i++;
talent[i] = [0, "Find Weakness", 3, 3, 8]; i++;
talent[i] = [0, "Master Poisoner", 3, 1, 9]; i++;
talent[i] = [0, "Mutilate", 1, 2, 9, [getTalentID("Overkill"),1]]; i++;
talent[i] = [0, "Turn the Tables", 3, 3, 9]; i++;
talent[i] = [0, "Cut to the Chase", 5, 2, 10]; i++;
talent[i] = [0, "Hunger For Blood", 1, 2, 11]; i++;
treeStartStop[t] = i - 1;
t++;
talent[i] = [1, "Improved Gouge", 3, 1, 1]; i++;
talent[i] = [1, "Improved Sinister Strike", 2, 2, 1]; i++;
talent[i] = [1, "Dual Wield Specialization", 5, 3, 1]; i++;
talent[i] = [1, "Improved Slice and Dice", 2, 1, 2]; i++;
talent[i] = [1, "Deflection", 3, 2, 2]; i++;
talent[i] = [1, "Precision", 5, 4, 2]; i++;
talent[i] = [1, "Endurance", 2, 1, 3]; i++;
talent[i] = [1, "Riposte", 1, 2, 3, [getTalentID("Deflection"),3]]; i++;
talent[i] = [1, "Close Quarters Combat", 5, 3, 3, [getTalentID("Dual Wield Specialization"),5]]; i++;
talent[i] = [1, "Improved Kick", 2, 1, 4]; i++;
talent[i] = [1, "Improved Sprint", 2, 2, 4]; i++;
talent[i] = [1, "Lightning Reflexes", 3, 3, 4]; i++;
talent[i] = [1, "Aggression", 5, 4, 4]; i++;
talent[i] = [1, "Mace Specialization", 5, 1, 5]; i++;
talent[i] = [1, "Blade Flurry", 1, 2, 5]; i++;
talent[i] = [1, "Sword Specialization", 5, 3, 5]; i++;
talent[i] = [1, "Weapon Expertise", 2, 2, 6, [getTalentID("Blade Flurry"),1]]; i++;
talent[i] = [1, "Blade Twisting", 2, 3, 6]; i++;
talent[i] = [1, "Vitality", 3, 1, 7]; i++;
talent[i] = [1, "Adrenaline Rush", 1, 2, 7]; i++;
talent[i] = [1, "Nerves of Steel", 2, 3, 7]; i++;
talent[i] = [1, "Throwing Specialization", 2, 1, 8]; i++;
talent[i] = [1, "Combat Potency", 5, 3, 8]; i++;
talent[i] = [1, "Unfair Advantage", 2, 1, 9]; i++;
talent[i] = [1, "Surprise Attacks", 1, 2, 9, [getTalentID("Adrenaline Rush"),1]]; i++;
talent[i] = [1, "Savage Combat", 2, 3, 9]; i++;
talent[i] = [1, "Prey on the Weak", 5, 2, 10]; i++;
talent[i] = [1, "Killing Spree", 1, 2, 11]; i++;
treeStartStop[t] = i - 1;
t++;
talent[i] = [2, "Relentless Strikes", 5, 1, 1]; i++;
talent[i] = [2, "Master of Deception", 3, 2, 1]; i++;
talent[i] = [2, "Opportunity", 2, 3, 1]; i++;
talent[i] = [2, "Sleight of Hand", 2, 1, 2]; i++;
talent[i] = [2, "Dirty Tricks", 2, 2, 2]; i++;
talent[i] = [2, "Camouflage", 3, 3, 2]; i++;
talent[i] = [2, "Elusiveness", 2, 1, 3]; i++;
talent[i] = [2, "Ghostly Strike", 1, 2, 3]; i++;
talent[i] = [2, "Serrated Blades", 3, 3, 3]; i++;
talent[i] = [2, "Setup", 3, 1, 4]; i++;
talent[i] = [2, "Initiative", 3, 2, 4]; i++;
talent[i] = [2, "Improved Ambush", 2, 3, 4]; i++;
talent[i] = [2, "Heightened Senses", 2, 1, 5]; i++;
talent[i] = [2, "Preparation", 1, 2, 5]; i++;
talent[i] = [2, "Dirty Deeds", 2, 3, 5]; i++;
talent[i] = [2, "Hemorrhage", 1, 4, 5, [getTalentID("Serrated Blades"),3]]; i++;
talent[i] = [2, "Master of Subtlety", 3, 1, 6]; i++;
talent[i] = [2, "Deadliness", 5, 3, 6]; i++;
talent[i] = [2, "Enveloping Shadows", 3, 1, 7]; i++;
talent[i] = [2, "Premeditation", 1, 2, 7, [getTalentID("Preparation"),1]]; i++;
talent[i] = [2, "Cheat Death", 3, 3, 7]; i++;
talent[i] = [2, "Sinister Calling", 5, 2, 8, [getTalentID("Premeditation"),1]]; i++;
talent[i] = [2, "Waylay", 2, 3, 8]; i++;
talent[i] = [2, "Honor Among Thieves", 3, 1, 9]; i++;
talent[i] = [2, "Shadowstep", 1, 2, 9]; i++;
talent[i] = [2, "Filthy Tricks", 2, 3, 9]; i++;
talent[i] = [2, "Slaughter from the Shadows", 5, 2, 10]; i++;
talent[i] = [2, "Shadow Dance", 1, 2, 11]; i++;
treeStartStop[t] = i - 1;
t++;
i = 0;
// Name: Improved Eviscerate
rank[i] = [
"Increases the damage done by your Eviscerate ability by 7%.",
"Increases the damage done by your Eviscerate ability by 14%.",
"Increases the damage done by your Eviscerate ability by 20%."
];
i++;
// Name: Remorseless Attacks
rank[i] = [
"After killing an opponent that yields experience or honor, gives you a 20% increased critical strike chance on your next Sinister Strike, Hemorrhage, Backstab, Mutilate, Ambush, or Ghostly Strike. Lasts 20 sec.",
"After killing an opponent that yields experience or honor, gives you a 40% increased critical strike chance on your next Sinister Strike, Hemorrhage, Backstab, Mutilate, Ambush, or Ghostly Strike. Lasts 20 sec."
];
i++;
// Name: Malice
rank[i] = [
"Increases your critical strike chance by 1%.",
"Increases your critical strike chance by 2%.",
"Increases your critical strike chance by 3%.",
"Increases your critical strike chance by 4%.",
"Increases your critical strike chance by 5%."
];
i++;
// Name: Ruthlessness
rank[i] = [
"Gives your melee finishing moves a 20% chance to add a combo point to your target.",
"Gives your melee finishing moves a 40% chance to add a combo point to your target.",
"Gives your melee finishing moves a 60% chance to add a combo point to your target."
];
i++;
// Name: Blood Spatter
rank[i] = [
"Increases the damage caused by your Garrote and Rupture abilities by 15%.",
"Increases the damage caused by your Garrote and Rupture abilities by 30%."
];
i++;
// Name: Puncturing Wounds
rank[i] = [
"Increases the critical strike chance of your Backstab ability by 10%, and the critical strike chance of your Mutilate ability by 5%.",
"Increases the critical strike chance of your Backstab ability by 20%, and the critical strike chance of your Mutilate ability by 10%.",
"Increases the critical strike chance of your Backstab ability by 30%, and the critical strike chance of your Mutilate ability by 15%."
];
i++;
// Name: Vigor
rank[i] = [
"Increases your maximum Energy by 10."
];
i++;
// Name: Improved Expose Armor
rank[i] = [
"Reduces the energy cost of your Expose Armor ability by 5.",
"Reduces the energy cost of your Expose Armor ability by 10."
];
i++;
// Name: Lethality
rank[i] = [
"Increases the critical strike damage bonus of all combo point-generating abilities that do not require stealth by 6%.",
"Increases the critical strike damage bonus of all combo point-generating abilities that do not require stealth by 12%..",
"Increases the critical strike damage bonus of all combo point-generating abilities that do not require stealth by 18%.",
"Increases the critical strike damage bonus of all combo point-generating abilities that do not require stealth by 24%.",
"Increases the critical strike damage bonus of all combo point-generating abilities that do not require stealth by 30%."
];
i++;
// Name: Vile Poisons
rank[i] = [
"Increases the damage dealt by your poisons and Envenom ability by 7% and gives your damage over time poisons an additional 10% chance to resist dispel effects.",
"Increases the damage dealt by your poisons and Envenom ability by 14% and gives your damage over time poisons an additional 20% chance to resist dispel effects.",
"Increases the damage dealt by your poisons and Envenom ability by 20% and gives your damage over time poisons an additional 30% chance to resist dispel effects."
];
i++;
// Name: Improved Poisons
rank[i] = [
"Increases the chance to apply Deadly Poison to your target by 2% and the frequency of applying Instant Poison to your target by 10%.",
"Increases the chance to apply Deadly Poison to your target by 4% and the frequency of applying Instant Poison to your target by 20%.",
"Increases the chance to apply Deadly Poison to your target by 6% and the frequency of applying Instant Poison to your target by 30%.",
"Increases the chance to apply Deadly Poison to your target by 8% and the frequency of applying Instant Poison to your target by 40%.",
"Increases the chance to apply Deadly Poison to your target by 10% and the frequency of applying Instant Poison to your target by 50%."
];
i++;
// Name: Fleet Footed
rank[i] = [
"Reduces the duration of all movement impairing effects by 15% and increases your movement speed by 8%. This does not stack with other movement speed increasing effects.",
"Reduces the duration of all movement impairing effects by 30% and increases your movement speed by 15%. This does not stack with other movement speed increasing effects."
];
i++;
// Name: Cold Blood
rank[i] = [
"When activated, increases the critical strike chance of your next offensive ability by 100%."
];
i++;
// Name: Improved Kidney Shot
rank[i] = [
"While affected by your Kidney Shot ability, the target receives an additional 3% damage from all sources.",
"While affected by your Kidney Shot ability, the target receives an additional 6% damage from all sources.",
"While affected by your Kidney Shot ability, the target receives an additional 9% damage from all sources."
];
i++;
// Name: Quick Recovery
rank[i] = [
"All healing effects on you are increased by 10%. In addition, your finishing moves refund 40% of their Energy cost when they fail to hit.",
"All healing effects on you are increased by 20%. In addition, your finishing moves refund 80% of their Energy cost when they fail to hit."
];
i++;
// Name: Seal Fate
rank[i] = [
"Your critical strikes from abilities that add combo points have a 20% chance to add an additional combo point.",
"Your critical strikes from abilities that add combo points have a 40% chance to add an additional combo point.",
"Your critical strikes from abilities that add combo points have a 60% chance to add an additional combo point.",
"Your critical strikes from abilities that add combo points have a 80% chance to add an additional combo point.",
"Your critical strikes from abilities that add combo points have a 100% chance to add an additional combo point."
];
i++;
// Name: Murder
rank[i] = [
"Increases all damage caused against Humanoid, Giant, Beast and Dragonkin targets by 2%.",
"Increases all damage caused against Humanoid, Giant, Beast and Dragonkin targets by 4%."
];
i++;
// Name: Deadly Brew
rank[i] = [
"When you apply Instant, Wound or Mind-Numbing poison to a target, you have a 50% chance to apply Crippling poison.",
"When you apply Instant, Wound or Mind-Numbing poison to a target, you have a 100% chance to apply Crippling poison."
];
i++;
// Name: Overkill
rank[i] = [
"While stealthed, and for 20 seconds after breaking stealth, you regenerate 30% additional energy."
];
i++;
// Name: Deadened Nerves
rank[i] = [
"Reduces all damage taken by 2%.",
"Reduces all damage taken by 4%.",
"Reduces all damage taken by 6%."
];
i++;
// Name: Focused Attacks
rank[i] = [
"Your melee critical strikes have a 33% chance to give you 2 energy.",
"Your melee critical strikes have a 66% chance to give you 2 energy.",
"Your melee critical strikes have a 100% chance to give you 2 energy."
];
i++;
// Name: Find Weakness
rank[i] = [
"Offensive ability damage increased by 2%.",
"Offensive ability damage increased by 4%.",
"Offensive ability damage increased by 6%."
];
i++;
// Name: Master Poisoner
rank[i] = [
"Increases the critical hit chance of all attacks made against any target you have poisoned by 1%, reduces the duration of all Poison effects applied to you by 17% and increases the bonus chance to apply Deadly Poison when Envenom is used by an additional 15%.",
"Increases the critical hit chance of all attacks made against any target you have poisoned by 2%, reduces the duration of all Poison effects applied to you by 34% and increases the bonus chance to apply Deadly Poison when Envenom is used by an additional 30%.",
"Increases the critical hit chance of all attacks made against any target you have poisoned by 3%, reduces the duration of all Poison effects applied to you by 50% and increases the bonus chance to apply Deadly Poison when Envenom is used by an additional 45%."
];
i++;
// Name: Mutilate
rank[i] = [
"Instantly attacks with both weapons for an additional 44 with each weapon. Damage is increased by 20% against Poisoned targets. Awards 2 combo points."
];
i++;
// Name: Turn the Tables
rank[i] = [
"Whenever anyone in your party or raid blocks, dodges, or parries an attack your chance to critically hit with all combo moves is increased by 2% for 8 sec.",
"Whenever anyone in your party or raid blocks, dodges, or parries an attack your chance to critically hit with all combo moves is increased by 4% for 8 sec.",
"Whenever anyone in your party or raid blocks, dodges, or parries an attack your chance to critically hit with all combo moves is increased by 6% for 8 sec."
];
i++;
// Name: Cut to the Chase
rank[i] = [
"Your Eviscerate and Envenom abilities have a 20% chance to refresh your Slice and Dice duration to its 5 combo point maximum.",
"Your Eviscerate and Envenom abilities have a 40% chance to refresh your Slice and Dice duration to its 5 combo point maximum.",
"Your Eviscerate and Envenom abilities have a 60% chance to refresh your Slice and Dice duration to its 5 combo point maximum.",
"Your Eviscerate and Envenom abilities have a 80% chance to refresh your Slice and Dice duration to its 5 combo point maximum.",
"Your Eviscerate and Envenom abilities have a 100% chance to refresh your Slice and Dice duration to its 5 combo point maximum."
];
i++;
// Name: Hunger For Blood
rank[i] = [
"Enrages you, increasing all damage caused by 15%. Requires a bleed effect to be active on the target. Lasts 1 min."
];
i++;
// Name: Improved Gouge
rank[i] = [
"Increases the effect duration of your Gouge ability by 0.5 sec.",
"Increases the effect duration of your Gouge ability by 1 sec.",
"Increases the effect duration of your Gouge ability by 1.5 sec."
];
i++;
// Name: Improved Sinister Strike
rank[i] = [
"Reduces the Energy cost of your Sinister Strike ability by 3.",
"Reduces the Energy cost of your Sinister Strike ability by 5."
];
i++;
// Name: Dual Wield Specialization
rank[i] = [
"Increases the damage done by your offhand weapon by 10%.",
"Increases the damage done by your offhand weapon by 20%.",
"Increases the damage done by your offhand weapon by 30%.",
"Increases the damage done by your offhand weapon by 40%.",
"Increases the damage done by your offhand weapon by 50%."
];
i++;
// Name: Improved Slice and Dice
rank[i] = [
"Increases the duration of your Slice and Dice ability by 25%.",
"Increases the duration of your Slice and Dice ability by 50%."
];
i++;
// Name: Deflection
rank[i] = [
"Increases your Parry chance by 2%.",
"Increases your Parry chance by 4%.",
"Increases your Parry chance by 6%."
];
i++;
// Name: Precision
rank[i] = [
"Increases your chance to hit with weapon and poison attacks by 1%.",
"Increases your chance to hit with weapon and poison attacks by 2%.",
"Increases your chance to hit with weapon and poison attacks by 3%.",
"Increases your chance to hit with weapon and poison attacks by 4%.",
"Increases your chance to hit with weapon and poison attacks by 5%."
];
i++;
// Name: Endurance
rank[i] = [
"Reduces the cooldown of your Sprint and Evasion abilities by 30 sec and increases your total Stamina by 2%.",
"Reduces the cooldown of your Sprint and Evasion abilities by 60 sec and increases your total Stamina by 4%."
];
i++;
// Name: Riposte
rank[i] = [
"A strike that becomes active after parrying an opponent\'s attack. This attack deals 150% weapon damage and slows their melee attack speed by 20% for 30 sec. Awards 1 combo point."
];
i++;
// Name: Close Quarters Combat
rank[i] = [
"Increases your chance to get a critical strike with Daggers and Fist Weapons by 1%.",
"Increases your chance to get a critical strike with Daggers and Fist Weapons by 2%.",
"Increases your chance to get a critical strike with Daggers and Fist Weapons by 3%.",
"Increases your chance to get a critical strike with Daggers and Fist Weapons by 4%.",
"Increases your chance to get a critical strike with Daggers and Fist Weapons by 5%."
];
i++;
// Name: Improved Kick
rank[i] = [
"Gives your Kick ability a 50% chance to silence the target for 2 sec.",
"Gives your Kick ability a 100% chance to silence the target for 2 sec."
];
i++;
// Name: Improved Sprint
rank[i] = [
"Gives a 50% chance to remove all Movement Impairing effects when you activate your Sprint ability.",
"Gives a 100% chance to remove all Movement Impairing effects when you activate your Sprint ability."
];
i++;
// Name: Lightning Reflexes
rank[i] = [
"Increases your Dodge chance by 2% and gives you 4% melee haste.",
"Increases your Dodge chance by 4% and gives you 7% melee haste.",
"Increases your Dodge chance by 6% and gives you 10% melee haste."
];
i++;
// Name: Aggression
rank[i] = [
"Increases the damage of your Sinister Strike, Backstab, and Eviscerate abilities by 3%.",
"Increases the damage of your Sinister Strike, Backstab, and Eviscerate abilities by 6%.",
"Increases the damage of your Sinister Strike, Backstab, and Eviscerate abilities by 9%.",
"Increases the damage of your Sinister Strike, Backstab, and Eviscerate abilities by 12%.",
"Increases the damage of your Sinister Strike, Backstab, and Eviscerate abilities by 15%."
];
i++;
// Name: Mace Specialization
rank[i] = [
"Your attacks with maces ignore up to 3% of your opponent\'s armor.",
"Your attacks with maces ignore up to 6% of your opponent\'s armor.",
"Your attacks with maces ignore up to 9% of your opponent\'s armor.",
"Your attacks with maces ignore up to 12% of your opponent\'s armor.",
"Your attacks with maces ignore up to 15% of your opponent\'s armor."
];
i++;
// Name: Blade Flurry
rank[i] = [
"Increases your attack speed by 20%. In addition, attacks strike an additional nearby opponent. Lasts 15 sec."
];
i++;
// Name: Sword Specialization
rank[i] = [
"Gives you a 1% chance to get an extra attack on the same target after hitting your target with your Sword or Axe.",
"Gives you a 2% chance to get an extra attack on the same target after hitting your target with your Sword or Axe.",
"Gives you a 3% chance to get an extra attack on the same target after hitting your target with your Sword or Axe.",
"Gives you a 4% chance to get an extra attack on the same target after hitting your target with your Sword or Axe.",
"Gives you a 5% chance to get an extra attack on the same target after hitting your target with your Sword or Axe."
];
i++;
// Name: Weapon Expertise
rank[i] = [
"Increases your expertise by 5.",
"Increases your expertise by 10."
];
i++;
// Name: Blade Twisting
rank[i] = [
"Increases the damage dealt by Sinister Strike and Backstab by 5%, and your damaging melee attacks have a 10% chance to Daze the target for 4 sec.",
"Increases the damage dealt by Sinister Strike and Backstab by 10%, and your damaging melee attacks have a 10% chance to Daze the target for 8 sec."
];
i++;
// Name: Vitality
rank[i] = [
"Increases your Energy regeneration rate by 8%.",
"Increases your Energy regeneration rate by 16%.",
"Increases your Energy regeneration rate by 25%."
];
i++;
// Name: Adrenaline Rush
rank[i] = [
"Increases your Energy regeneration rate by 100% for 15 sec."
];
i++;
// Name: Nerves of Steel
rank[i] = [
"Reduces damage taken while affected by Stun and Fear effects by 15%.",
"Reduces damage taken while affected by Stun and Fear effects by 30%."
];
i++;
// Name: Throwing Specialization
rank[i] = [
"Increases the range of Throw and Deadly Throw by 2 yards and gives your Deadly Throw and Fan of Knives abilities a 50% chance to interrupt the target for 3 sec.",
"Increases the range of Throw and Deadly Throw by 4 yards and gives your Deadly Throw and Fan of Knives abilities a 100% chance to interrupt the target for 3 sec."
];
i++;
// Name: Combat Potency
rank[i] = [
"Gives your successful off-hand melee attacks a 20% chance to generate 3 Energy.",
"Gives your successful off-hand melee attacks a 20% chance to generate 6 Energy.",
"Gives your successful off-hand melee attacks a 20% chance to generate 9 Energy.",
"Gives your successful off-hand melee attacks a 20% chance to generate 12 Energy.",
"Gives your successful off-hand melee attacks a 20% chance to generate 15 Energy."
];
i++;
// Name: Unfair Advantage
rank[i] = [
"Whenever you dodge an attack you gain an Unfair Advantage, striking back for 50% of your main hand weapon\'s damage. This cannot occur more than once per second.",
"Whenever you dodge an attack you gain an Unfair Advantage, striking back for 100% of your main hand weapon\'s damage. This cannot occur more than once per second."
];
i++;
// Name: Surprise Attacks
rank[i] = [
"Your finishing moves can no longer be dodged, and the damage dealt by your Sinister Strike, Backstab, Shiv, Hemorrhage and Gouge abilities is increased by 10%."
];
i++;
// Name: Savage Combat
rank[i] = [
"Increases your total attack power by 2% and all physical damage caused to enemies you have poisoned is increased by 2%.",
"Increases your total attack power by 4% and all physical damage caused to enemies you have poisoned is increased by 4%."
];
i++;
// Name: Prey on the Weak
rank[i] = [
"Your critical strike damage is increased by 4% when the target has less health than you (as a percentage of total health).",
"Your critical strike damage is increased by 8% when the target has less health than you (as a percentage of total health).",
"Your critical strike damage is increased by 12% when the target has less health than you (as a percentage of total health).",
"Your critical strike damage is increased by 16% when the target has less health than you (as a percentage of total health).",
"Your critical strike damage is increased by 20% when the target has less health than you (as a percentage of total health)."
];
i++;
// Name: Killing Spree
rank[i] = [
"Step through the shadows from enemy to enemy within 10 yards, attacking an enemy every .5 secs with both weapons until 5 assaults are made, and increasing all damage done by 20% for the duration. Can hit the same target multiple times. Cannot hit invisible or stealthed targets."
];
i++;
// Name: Relentless Strikes
rank[i] = [
"Your finishing moves have a 4% chance per combo point to restore 25 energy.",
"Your finishing moves have a 8% chance per combo point to restore 25 energy.",
"Your finishing moves have a 12% chance per combo point to restore 25 energy.",
"Your finishing moves have a 16% chance per combo point to restore 25 energy.",
"Your finishing moves have a 20% chance per combo point to restore 25 energy."
];
i++;
// Name: Master of Deception
rank[i] = [
"Reduces the chance enemies have to detect you while in Stealth mode.",
"Reduces the chance enemies have to detect you while in Stealth mode. More effective than Master of Deception (Rank 1).",
"Reduces the chance enemies have to detect you while in Stealth mode. More effective than Master of Deception (Rank 2)."
];
i++;
// Name: Opportunity
rank[i] = [
"Increases the damage dealt with your Backstab, Mutilate, Garrote and Ambush abilities by 10%.",
"Increases the damage dealt with your Backstab, Mutilate, Garrote and Ambush abilities by 20%."
];
i++;
// Name: Sleight of Hand
rank[i] = [
"Reduces the chance you are critically hit by melee and ranged attacks by 1% and increases the threat reduction of your Feint ability by 10%.",
"Reduces the chance you are critically hit by melee and ranged attacks by 2% and increases the threat reduction of your Feint ability by 20%."
];
i++;
// Name: Dirty Tricks
rank[i] = [
"Increases the range of your Blind and Sap abilities by 2 yards and reduces the energy cost of your Blind and Sap abilities by 25%.",
"Increases the range of your Blind and Sap abilities by 5 yards and reduces the energy cost of your Blind and Sap abilities by 50%."
];
i++;
// Name: Camouflage
rank[i] = [
"Increases your speed while stealthed by 5% and reduces the cooldown of your Stealth ability by 2 sec.",
"Increases your speed while stealthed by 10% and reduces the cooldown of your Stealth ability by 4 sec.",
"Increases your speed while stealthed by 15% and reduces the cooldown of your Stealth ability by 6 sec."
];
i++;
// Name: Elusiveness
rank[i] = [
"Reduces the cooldown of your Vanish and Blind abilities by 30 sec and your Cloak of Shadows ability by 15 sec.",
"Reduces the cooldown of your Vanish and Blind abilities by 60 sec and your Cloak of Shadows ability by 30 sec."
];
i++;
// Name: Ghostly Strike
rank[i] = [
"A strike that deals 125% weapon damage and increases your chance to dodge by 15% for 7 sec. Awards 1 combo point."
];
i++;
// Name: Serrated Blades
rank[i] = [
"Causes your attacks to ignore 2.67 of your target\'s Armor and increases the damage dealt by your Rupture ability by 10%. The amount of Armor reduced increases with your level.",
"Causes your attacks to ignore 5.34 of your target\'s Armor and increases the damage dealt by your Rupture ability by 20%. The amount of Armor reduced increases with your level.",
"Causes your attacks to ignore 8 of your target\'s Armor and increases the damage dealt by your Rupture ability by 30%. The amount of Armor reduced increases with your level."
];
i++;
// Name: Setup
rank[i] = [
"Gives you a 33% chance to add a combo point to your target after dodging their attack or fully resisting one of their spells. This cannot happen more than once per second.",
"Gives you a 66% chance to add a combo point to your target after dodging their attack or fully resisting one of their spells. This cannot happen more than once per second.",
"Gives you a 100% chance to add a combo point to your target after dodging their attack or fully resisting one of their spells. This cannot happen more than once per second."
];
i++;
// Name: Initiative
rank[i] = [
"Gives you a 33% chance to add an additional combo point to your target when using your Ambush, Garrote, or Cheap Shot ability.",
"Gives you a 66% chance to add an additional combo point to your target when using your Ambush, Garrote, or Cheap Shot ability.",
"Gives you a 100% chance to add an additional combo point to your target when using your Ambush, Garrote, or Cheap Shot ability."
];
i++;
// Name: Improved Ambush
rank[i] = [
"Increases the critical strike chance of your Ambush ability by 25%.",
"Increases the critical strike chance of your Ambush ability by 50%."
];
i++;
// Name: Heightened Senses
rank[i] = [
"Increases your Stealth detection and reduces the chance you are hit by spells and ranged attacks by 2%.",
"Increases your Stealth detection and reduces the chance you are hit by spells and ranged attacks by 4%. More effective than Heightened Senses (Rank 1)."
];
i++;
// Name: Preparation
rank[i] = [
"When activated, this ability immediately finishes the cooldown on your Evasion, Sprint, Vanish, Cold Blood and Shadowstep abilities."
];
i++;
// Name: Dirty Deeds
rank[i] = [
"Reduces the Energy cost of your Cheap Shot and Garrote abilities by 10. Additionally, your special abilities cause 10% more damage against targets below 35% health.",
"Reduces the Energy cost of your Cheap Shot and Garrote abilities by 20. Additionally, your special abilities cause 20% more damage against targets below 35% health."
];
i++;
// Name: Hemorrhage
rank[i] = [
"An instant strike that deals 110% weapon damage and causes the target to hemorrhage, increasing any Physical damage dealt to the target by up to 13. Lasts 10 charges or 15 sec. Awards 1 combo point."
];
i++;
// Name: Master of Subtlety
rank[i] = [
"Attacks made while stealthed and for 6 seconds after breaking stealth cause an additional 4% damage.",
"Attacks made while stealthed and for 6 seconds after breaking stealth cause an additional 7% damage.",
"Attacks made while stealthed and for 6 seconds after breaking stealth cause an additional 10% damage."
];
i++;
// Name: Deadliness
rank[i] = [
"Increases your attack power by 2%.",
"Increases your attack power by 4%.",
"Increases your attack power by 6%.",
"Increases your attack power by 8%.",
"Increases your attack power by 10%."
];
i++;
// Name: Enveloping Shadows
rank[i] = [
"Reduces the damage taken by area of effect attacks by 10%.",
"Reduces the damage taken by area of effect attacks by 20%.",
"Reduces the damage taken by area of effect attacks by 30%."
];
i++;
// Name: Premeditation
rank[i] = [
"When used, adds 2 combo points to your target. You must add to or use those combo points within 20 sec or the combo points are lost."
];
i++;
// Name: Cheat Death
rank[i] = [
"You have a 33% chance that an attack which would otherwise kill you will instead reduce you to 10% of your maximum health. In addition, all damage taken will be reduced by up to 90% for 3 sec (modified by resilience). This effect cannot occur more than once per minute.",
"You have a 66% chance that an attack which would otherwise kill you will instead reduce you to 10% of your maximum health. In addition, all damage taken will be reduced by up to 90% for 3 sec (modified by resilience). This effect cannot occur more than once per minute.",
"You have a 100% chance that an attack which would otherwise kill you will instead reduce you to 10% of your maximum health. In addition, all damage taken will be reduced by up to 90% for 3 sec (modified by resilience). This effect cannot occur more than once per minute."
];
i++;
// Name: Sinister Calling
rank[i] = [
"Increases your total Agility by 3% and increases the percentage damage bonus of Backstab and Hemorrhage by an additional 2%.",
"Increases your total Agility by 6% and increases the percentage damage bonus of Backstab and Hemorrhage by an additional 4%.",
"Increases your total Agility by 9% and increases the percentage damage bonus of Backstab and Hemorrhage by an additional 6%.",
"Increases your total Agility by 12% and increases the percentage damage bonus of Backstab and Hemorrhage by an additional 8%.",
"Increases your total Agility by 15% and increases the percentage damage bonus of Backstab and Hemorrhage by an additional 10%."
];
i++;
// Name: Waylay
rank[i] = [
"Your Ambush critical hits have a 50% chance to reduce the target\'s melee and ranged attack speed by 20%, movement speed by 70% for 8 sec.",
"Your Ambush critical hits have a 100% chance to reduce the target\'s melee and ranged attack speed by 20%, movement speed by 70% for 8 sec."
];
i++;
// Name: Honor Among Thieves
rank[i] = [
"When anyone in your group critically hits with a damage or healing spell or ability, you have a 33% chance to gain a combo point on your current target. This effect cannot occur more than once every second.",
"When anyone in your group critically hits with a damage or healing spell or ability, you have a 66% chance to gain a combo point on your current target. This effect cannot occur more than once every second.",
"When anyone in your group critically hits with a damage or healing spell or ability, you have a 100% chance to gain a combo point on your current target. This effect cannot occur more than once every second."
];
i++;
// Name: Shadowstep
rank[i] = [
"Attempts to step through the shadows and reappear behind your enemy and increases movement speed by 70% for 3 sec. The damage of your next ability is increased by 20% and the threat caused is reduced by 50%. Lasts 10 sec."
];
i++;
// Name: Filthy Tricks
rank[i] = [
"Reduces the cooldown of your Tricks of the Trade and Distract abilities by 5 secs and Preparation by 2.5 min.",
"Reduces the cooldown of your Tricks of the Trade and Distract abilities by 10 secs and Preparation by 5 min."
];
i++;
// Name: Slaughter from the Shadows
rank[i] = [
"Reduces the energy cost of your Backstab and Ambush abilities by 3 and the energy cost of your Hemorrhage by 1.",
"Reduces the energy cost of your Backstab and Ambush abilities by 6 and the energy cost of your Hemorrhage by 2.",
"Reduces the energy cost of your Backstab and Ambush abilities by 9 and the energy cost of your Hemorrhage by 3.",
"Reduces the energy cost of your Backstab and Ambush abilities by 12 and the energy cost of your Hemorrhage by 4.",
"Reduces the energy cost of your Backstab and Ambush abilities by 15 and the energy cost of your Hemorrhage by 5."
];
i++;
// Name: Shadow Dance
rank[i] = [
"Enter the Shadow Dance for 6 sec, allowing the use of Sap, Garrote, Ambush, Cheap Shot, Premeditation, Pickpocket and Disarm Trap regardless of being stealthed."
];
i++;
|