/*
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 = "warlock";
var i = 0;
var t = 0;
var className = "Warlock Talents";
tree[i] = "Affliction"; i++;
tree[i] = "Demonology"; i++;
tree[i] = "Destruction"; i++;
i = 0;
talent[i] = [0, "Improved Curse of Agony", 2, 1, 1]; i++;
talent[i] = [0, "Suppression", 3, 2, 1]; i++;
talent[i] = [0, "Improved Corruption", 5, 3, 1]; i++;
talent[i] = [0, "Improved Curse of Weakness", 2, 1, 2]; i++;
talent[i] = [0, "Improved Drain Soul", 2, 2, 2]; i++;
talent[i] = [0, "Improved Life Tap", 2, 3, 2]; i++;
talent[i] = [0, "Soul Siphon", 2, 4, 2]; i++;
talent[i] = [0, "Improved Fear", 2, 1, 3]; i++;
talent[i] = [0, "Fel Concentration", 3, 2, 3]; i++;
talent[i] = [0, "Amplify Curse", 1, 3, 3]; i++;
talent[i] = [0, "Grim Reach", 2, 1, 4]; i++;
talent[i] = [0, "Nightfall", 2, 2, 4]; i++;
talent[i] = [0, "Empowered Corruption", 3, 4, 4]; i++;
talent[i] = [0, "Shadow Embrace", 5, 1, 5]; i++;
talent[i] = [0, "Siphon Life", 1, 2, 5]; i++;
talent[i] = [0, "Curse of Exhaustion", 1, 3, 5, [getTalentID("Amplify Curse"),1]]; i++;
talent[i] = [0, "Improved Felhunter", 2, 1, 6]; i++;
talent[i] = [0, "Shadow Mastery", 5, 2, 6, [getTalentID("Siphon Life"),1]]; i++;
talent[i] = [0, "Eradication", 3, 1, 7]; i++;
talent[i] = [0, "Contagion", 5, 2, 7]; i++;
talent[i] = [0, "Dark Pact", 1, 3, 7]; i++;
talent[i] = [0, "Improved Howl of Terror", 2, 1, 8]; i++;
talent[i] = [0, "Malediction", 3, 3, 8]; i++;
talent[i] = [0, "Death's Embrace", 3, 1, 9]; i++;
talent[i] = [0, "Unstable Affliction", 1, 2, 9, [getTalentID("Contagion"),5]]; i++;
talent[i] = [0, "Pandemic", 1, 3, 9, [getTalentID("Unstable Affliction"),1]]; i++;
talent[i] = [0, "Everlasting Affliction", 5, 2, 10]; i++;
talent[i] = [0, "Haunt", 1, 2, 11]; i++;
treeStartStop[t] = i - 1;
t++;
talent[i] = [1, "Improved Healthstone", 2, 1, 1]; i++;
talent[i] = [1, "Improved Imp", 3, 2, 1]; i++;
talent[i] = [1, "Demonic Embrace", 3, 3, 1]; i++;
talent[i] = [1, "Fel Synergy", 2, 4, 1]; i++;
talent[i] = [1, "Improved Health Funnel", 2, 1, 2]; i++;
talent[i] = [1, "Demonic Brutality", 3, 2, 2]; i++;
talent[i] = [1, "Fel Vitality", 3, 3, 2]; i++;
talent[i] = [1, "Improved Succubus", 3, 1, 3]; i++;
talent[i] = [1, "Soul Link", 1, 2, 3]; i++;
talent[i] = [1, "Fel Domination", 1, 3, 3]; i++;
talent[i] = [1, "Demonic Aegis", 3, 4, 3]; i++;
talent[i] = [1, "Unholy Power", 5, 2, 4, [getTalentID("Soul Link"),1]]; i++;
talent[i] = [1, "Master Summoner", 2, 3, 4, [getTalentID("Fel Domination"),1]]; i++;
talent[i] = [1, "Mana Feed", 1, 1, 5, [getTalentID("Unholy Power"),5]]; i++;
talent[i] = [1, "Master Conjuror", 2, 3, 5]; i++;
talent[i] = [1, "Master Demonologist", 5, 2, 6, [getTalentID("Unholy Power"),5]]; i++;
talent[i] = [1, "Molten Core", 3, 3, 6]; i++;
talent[i] = [1, "Demonic Resilience", 3, 1, 7]; i++;
talent[i] = [1, "Demonic Empowerment", 1, 2, 7, [getTalentID("Master Demonologist"),5]]; i++;
talent[i] = [1, "Demonic Knowledge", 3, 3, 7]; i++;
talent[i] = [1, "Demonic Tactics", 5, 2, 8]; i++;
talent[i] = [1, "Decimation", 2, 3, 8]; i++;
talent[i] = [1, "Improved Demonic Tactics", 3, 1, 9, [getTalentID("Demonic Tactics"),5]]; i++;
talent[i] = [1, "Summon Felguard", 1, 2, 9]; i++;
talent[i] = [1, "Nemesis", 3, 3, 9]; i++;
talent[i] = [1, "Demonic Pact", 5, 2, 10]; i++;
talent[i] = [1, "Metamorphosis", 1, 2, 11]; i++;
treeStartStop[t] = i - 1;
t++;
talent[i] = [2, "Improved Shadow Bolt", 5, 2, 1]; i++;
talent[i] = [2, "Bane", 5, 3, 1]; i++;
talent[i] = [2, "Aftermath", 2, 1, 2]; i++;
talent[i] = [2, "Molten Skin", 3, 2, 2]; i++;
talent[i] = [2, "Cataclysm", 3, 3, 2]; i++;
talent[i] = [2, "Demonic Power", 2, 1, 3]; i++;
talent[i] = [2, "Shadowburn", 1, 2, 3]; i++;
talent[i] = [2, "Ruin", 5, 3, 3]; i++;
talent[i] = [2, "Intensity", 2, 1, 4]; i++;
talent[i] = [2, "Destructive Reach", 2, 2, 4]; i++;
talent[i] = [2, "Improved Searing Pain", 3, 4, 4]; i++;
talent[i] = [2, "Backlash", 3, 1, 5, [getTalentID("Intensity"),2]]; i++;
talent[i] = [2, "Improved Immolate", 3, 2, 5]; i++;
talent[i] = [2, "Devastation", 1, 3, 5, [getTalentID("Ruin"),5]]; i++;
talent[i] = [2, "Nether Protection", 3, 1, 6]; i++;
talent[i] = [2, "Emberstorm", 5, 3, 6]; i++;
talent[i] = [2, "Conflagrate", 1, 2, 7, [getTalentID("Improved Immolate"),3]]; i++;
talent[i] = [2, "Soul Leech", 3, 3, 7]; i++;
talent[i] = [2, "Pyroclasm", 3, 4, 7]; i++;
talent[i] = [2, "Shadow and Flame", 5, 2, 8]; i++;
talent[i] = [2, "Improved Soul Leech", 2, 3, 8, [getTalentID("Soul Leech"),3]]; i++;
talent[i] = [2, "Backdraft", 3, 1, 9, [getTalentID("Conflagrate"),1]]; i++;
talent[i] = [2, "Shadowfury", 1, 2, 9]; i++;
talent[i] = [2, "Empowered Imp", 3, 3, 9]; i++;
talent[i] = [2, "Fire and Brimstone", 5, 2, 10]; i++;
talent[i] = [2, "Chaos Bolt", 1, 2, 11]; i++;
treeStartStop[t] = i - 1;
t++;
i = 0;
// Name: Improved Curse of Agony
rank[i] = [
"Increases the damage done by your Curse of Agony by 5%.",
"Increases the damage done by your Curse of Agony by 10%."
];
i++;
// Name: Suppression
rank[i] = [
"Increases your chance to hit with spells by 1%, and reduces the mana cost of your Affliction spells by 2%.",
"Increases your chance to hit with spells by 2%, and reduces the mana cost of your Affliction spells by 4%.",
"Increases your chance to hit with spells by 3%, and reduces the mana cost of your Affliction spells by 6%."
];
i++;
// Name: Improved Corruption
rank[i] = [
"Increases the damage done by your Corruption by 2%, and increases the critical strike chance of your Seed of Corruption by 1%.",
"Increases the damage done by your Corruption by 4%, and increases the critical strike chance of your Seed of Corruption by 2%.",
"Increases the damage done by your Corruption by 6%, and increases the critical strike chance of your Seed of Corruption by 3%.",
"Increases the damage done by your Corruption by 8%, and increases the critical strike chance of your Seed of Corruption by 4%.",
"Increases the damage done by your Corruption by 10%, and increases the critical strike chance of your Seed of Corruption by 5%."
];
i++;
// Name: Improved Curse of Weakness
rank[i] = [
"Increases the amount of attack power reduced by your Curse of Weakness by 10%.",
"Increases the amount of attack power reduced by your Curse of Weakness by 20%."
];
i++;
// Name: Improved Drain Soul
rank[i] = [
"Returns 7% of your maximum mana if the target is killed by you while you drain its soul. In addition, your Affliction spells generate 5% less threat.",
"Returns 15% of your maximum mana if the target is killed by you while you drain its soul. In addition, your Affliction spells generate 10% less threat."
];
i++;
// Name: Improved Life Tap
rank[i] = [
"Increases the amount of Mana awarded by your Life Tap spell by 10%.",
"Increases the amount of Mana awarded by your Life Tap spell by 20%."
];
i++;
// Name: Soul Siphon
rank[i] = [
"Increases the amount drained by your Drain Life and Drain Soul spells by an additional 3% for each of your Affliction effects on the target, up to a maximum of 9% additional effect.",
"Increases the amount drained by your Drain Life and Drain Soul spells by an additional 6% for each of your Affliction effects on the target, up to a maximum of 18% additional effect."
];
i++;
// Name: Improved Fear
rank[i] = [
"Causes your Fear spell to inflict a Nightmare on the target when the fear effect ends. The Nightmare effect reduces the target\'s movement speed by 15% for 5 sec.",
"Causes your Fear spell to inflict a Nightmare on the target when the fear effect ends. The Nightmare effect reduces the target\'s movement speed by 30% for 5 sec."
];
i++;
// Name: Fel Concentration
rank[i] = [
"Reduces the pushback suffered from damaging attacks while casting Drain Life, Drain Mana, Drain Soul, Unstable Affliction, and Haunt by 23%.",
"Reduces the pushback suffered from damaging attacks while casting Drain Life, Drain Mana, Drain Soul, Unstable Affliction, and Haunt spells by 46%.",
"Reduces the pushback suffered from damaging attacks while casting Drain Life, Drain Mana, Drain Soul, Unstable Affliction, and Haunt spells by 70%."
];
i++;
// Name: Amplify Curse
rank[i] = [
"Reduces the global cooldown of your Curses by 0.5 sec."
];
i++;
// Name: Grim Reach
rank[i] = [
"Increases the range of your Affliction spells by 10%.",
"Increases the range of your Affliction spells by 20%."
];
i++;
// Name: Nightfall
rank[i] = [
"Gives your Corruption and Drain Life spells a 2% chance to cause you to enter a Shadow Trance state after damaging the opponent. The Shadow Trance state reduces the casting time of your next Shadow Bolt spell by 100%.",
"Gives your Corruption and Drain Life spells a 4% chance to cause you to enter a Shadow Trance state after damaging the opponent. The Shadow Trance state reduces the casting time of your next Shadow Bolt spell by 100%."
];
i++;
// Name: Empowered Corruption
rank[i] = [
"Increases the damage of your Corruption spell by an amount equal to 12% of your spell power.",
"Increases the damage of your Corruption spell by an amount equal to 24% of your spell power.",
"Increases the damage of your Corruption spell by an amount equal to 36% of your spell power."
];
i++;
// Name: Shadow Embrace
rank[i] = [
"Your Shadow Bolt and Haunt spells apply the Shadow Embrace effect, increasing all shadow periodic damage dealt to the target by you by 1%, and reduces all periodic healing done to the target by 3%. Lasts for 12 sec. Stacks up to 2 times.",
"Your Shadow Bolt and Haunt spells apply the Shadow Embrace effect, increasing all shadow periodic damage dealt to the target by you by 2%, and reduces all periodic healing done to the target by 6%. Lasts for 12 sec. Stacks up to 2 times.",
"Your Shadow Bolt and Haunt spells apply the Shadow Embrace effect, increasing all shadow periodic damage dealt to the target by you by 3%, and reduces all periodic healing done to the target by 9%. Lasts for 12 sec. Stacks up to 2 times.",
"Your Shadow Bolt and Haunt spells apply the Shadow Embrace effect, increasing all shadow periodic damage dealt to the target by you by 4%, and reduces all periodic healing done to the target by 12%. Lasts for 12 sec. Stacks up to 2 times.",
"Your Shadow Bolt and Haunt spells apply the Shadow Embrace effect, increasing all shadow periodic damage dealt to the target by you by 5%, and reduces all periodic healing done to the target by 15%. Lasts for 12 sec. Stacks up to 2 times."
];
i++;
// Name: Siphon Life
rank[i] = [
"When you deal damage with your Corruption spell, you are instantly healed for 40% of the damage done. In addition, the damage done by your Corruption, Seed of Corruption and Unstable Affliction damage over time effects is increased by 5%."
];
i++;
// Name: Curse of Exhaustion
rank[i] = [
"Reduces the target\'s movement speed by 30% for 12 sec. Only one Curse per Warlock can be active on any one target."
];
i++;
// Name: Improved Felhunter
rank[i] = [
"Your Felhunter regains 4% of is maximum mana each time it hits with its Shadow Bite ability, and increases the effect of your Felhunter\'s Fel Intelligence by 5%.",
"Your Felhunter regains 8% of is maximum mana each time it hits with its Shadow Bite ability, and increases the effect of your Felhunter\'s Fel Intelligence by 10%."
];
i++;
// Name: Shadow Mastery
rank[i] = [
"Increases the damage dealt or life drained by your Shadow spells by 3%.",
"Increases the damage dealt or life drained by your Shadow spells by 6%.",
"Increases the damage dealt or life drained by your Shadow spells by 9%.",
"Increases the damage dealt or life drained by your Shadow spells by 12%.",
"Increases the damage dealt or life drained by your Shadow spells by 15%."
];
i++;
// Name: Eradication
rank[i] = [
"When you deal damage with Corruption, you have 6% chance to increase your spell casting speed by 6% for 10 sec.",
"When you deal damage with Corruption, you have 6% chance to increase your spell casting speed by 12% for 10 sec.",
"When you deal damage with Corruption, you have 6% chance to increase your spell casting speed by 20% for 10 sec."
];
i++;
// Name: Contagion
rank[i] = [
"Increases the damage of Curse of Agony, Corruption and Seed of Corruption by 1% and reduces the chance your helpful Affliction spells and damage over time effects will be dispelled by an additional 6%.",
"Increases the damage of Curse of Agony, Corruption and Seed of Corruption by 2% and reduces the chance your helpful Affliction spells and damage over time effects will be dispelled by an additional 12%.",
"Increases the damage of Curse of Agony, Corruption and Seed of Corruption by 3% and reduces the chance your helpful Affliction spells and damage over time effects will be dispelled by an additional 18%.",
"Increases the damage of Curse of Agony, Corruption and Seed of Corruption by 4% and reduces the chance your helpful Affliction spells and damage over time effects will be dispelled by an additional 24%.",
"Increases the damage of Curse of Agony, Corruption and Seed of Corruption by 5% and reduces the chance your helpful Affliction spells and damage over time effects will be dispelled by an additional 30%."
];
i++;
// Name: Dark Pact
rank[i] = [
"Drains 305 of your summoned demon\'s Mana, returning 100% to you."
];
i++;
// Name: Improved Howl of Terror
rank[i] = [
"Reduces the casting time of your Howl of Terror spell by 0.8 sec.",
"Reduces the casting time of your Howl of Terror spell by 1.5 sec."
];
i++;
// Name: Malediction
rank[i] = [
"Increases your spell damage by 1%, and increases the periodic critical strike chance of your Corruption and Unstable Affliction spells by 3%.",
"Increases your spell damage by 2%, and increases the periodic critical strike chance of your Corruption and Unstable Affliction spells by 6%.",
"Increases your spell damage by 3%, and increases the periodic critical strike chance of your Corruption and Unstable Affliction spells by 9%."
];
i++;
// Name: Death's Embrace
rank[i] = [
"Increases the amount drained by your Drain Life by 10% while your health is at or below 20% health, and increases the damage done by your Shadow spells by 4% when your target is at or below 35% health.",
"Increases the amount drained by your Drain Life by 20% while your health is at or below 20% health, and increases the damage done by your Shadow spells by 8% when your target is at or below 35% health.",
"Increases the amount drained by your Drain Life by 30% while your health is at or below 20% health, and increases the damage done by your Shadow spells by 12% when your target is at or below 35% health."
];
i++;
// Name: Unstable Affliction
rank[i] = [
"Shadow energy slowly destroys the target, causing 550 damage over 15 sec. In addition, if the Unstable Affliction is dispelled it will cause 990 damage to the dispeller and silence them for 5 sec."
];
i++;
// Name: Pandemic
rank[i] = [
"Grants the periodic damage from your Corruption and Unstable Affliction spells the ability to critically hit for 100% increased damage, and increases the critical strike damage bonus of your Haunt spell by 100%."
];
i++;
// Name: Everlasting Affliction
rank[i] = [
"Your Corruption and Unstable Affliction spells gain an additional 1% of your bonus spell damage, and your Drain Life and Haunt spells have a 20% chance to reset the duration of your Corruption spell on the target.",
"Your Corruption and Unstable Affliction spells gain an additional 2% of your bonus spell damage, and your Drain Life and Haunt spells have a 40% chance to reset the duration of your Corruption spell on the target.",
"Your Corruption and Unstable Affliction spells gain an additional 3% of your bonus spell damage, and your Drain Life and Haunt spells have a 60% chance to reset the duration of your Corruption spell on the target.",
"Your Corruption and Unstable Affliction spells gain an additional 4% of your bonus spell damage, and your Drain Life and Haunt spells have a 80% chance to reset the duration of your Corruption spell on the target.",
"Your Corruption and Unstable Affliction spells gain an additional 5% of your bonus spell damage, and your Drain Life and Haunt spells have a 100% chance to reset the duration of your Corruption spell on the target."
];
i++;
// Name: Haunt
rank[i] = [
"You send a ghostly soul into the target, dealing 405 to 473 Shadow damage and increasing all damage done by your Shadow damage-over-time effects on the target by 20% for 12 sec. When the Haunt spell ends or is dispelled, the soul returns to you, healing you for 100% of the damage it did to the target."
];
i++;
// Name: Improved Healthstone
rank[i] = [
"Increases the amount of Health restored by your Healthstone by 10%.",
"Increases the amount of Health restored by your Healthstone by 20%."
];
i++;
// Name: Improved Imp
rank[i] = [
"Increases the effect of your Imp\'s Firebolt, Fire Shield, and Blood Pact spells by 10%.",
"Increases the effect of your Imp\'s Firebolt, Fire Shield, and Blood Pact spells by 20%.",
"Increases the effect of your Imp\'s Firebolt, Fire Shield, and Blood Pact spells by 30%."
];
i++;
// Name: Demonic Embrace
rank[i] = [
"Increases your total Stamina by 4%.",
"Increases your total Stamina by 7%.",
"Increases your total Stamina by 10%."
];
i++;
// Name: Fel Synergy
rank[i] = [
"You have a 50% chance to heal your pet for 15% of the amount of spell damage done by you.",
"You have a 100% chance to heal your pet for 15% of the amount of spell damage done by you."
];
i++;
// Name: Improved Health Funnel
rank[i] = [
"Increases the amount of Health transferred by your Health Funnel spell by 10% and reduces the initial health cost by 10%. In addition, your summoned Demon takes 15% less damage while under the effect of your Health Funnel.",
"Increases the amount of Health transferred by your Health Funnel spell by 20% and reduces the initial health cost by 20%. In addition, your summoned Demon takes 30% less damage while under the effect of your Health Funnel."
];
i++;
// Name: Demonic Brutality
rank[i] = [
"Increases the effectiveness of your Voidwalker\'s Torment, Consume Shadows, Sacrifice and Suffering spells by 10%, and increases the attack power bonus on your Felguard\'s Demonic Frenzy effect by 1%.",
"Increases the effectiveness of your Voidwalker\'s Torment, Consume Shadows, Sacrifice and Suffering spells by 20%, and increases the attack power bonus on your Felguard\'s Demonic Frenzy effect by 2%.",
"Increases the effectiveness of your Voidwalker\'s Torment, Consume Shadows, Sacrifice and Suffering spells by 30%, and increases the attack power bonus on your Felguard\'s Demonic Frenzy effect by 3%."
];
i++;
// Name: Fel Vitality
rank[i] = [
"Increases the Stamina and Intellect of your Imp, Voidwalker, Succubus, Felhunter and Felguard by 5% and increases your maximum health and mana by 1%.",
"Increases the Stamina and Intellect of your Imp, Voidwalker, Succubus, Felhunter and Felguard by 10% and increases your maximum health and mana by 2%.",
"Increases the Stamina and Intellect of your Imp, Voidwalker, Succubus, Felhunter and Felguard by 15% and increases your maximum health and mana by 3%."
];
i++;
// Name: Improved Succubus
rank[i] = [
"Reduces the cast time of your Succubus\' Seduction by 22%, and increases the duration of your Succubus\' Seduction and Lesser Invisibility spells by 10%.",
"Reduces the cast time of your Succubus\' Seduction by 44%, and increases the duration of your Succubus\' Seduction and Lesser Invisibility spells by 20%.",
"Reduces the cast time of your Succubus\' Seduction by 66%, and increases the duration of your Succubus\' Seduction and Lesser Invisibility spells by 30%."
];
i++;
// Name: Soul Link
rank[i] = [
"When active, 20% of all damage taken by the caster is taken by your Imp, Voidwalker, Succubus, Felhunter, Felguard, or enslaved demon instead. That damage cannot be prevented. Lasts as long as the demon is active and controlled."
];
i++;
// Name: Fel Domination
rank[i] = [
"Your next Imp, Voidwalker, Succubus, Felhunter or Felguard Summon spell has its casting time reduced by 5.5 sec and its Mana cost reduced by 50%."
];
i++;
// Name: Demonic Aegis
rank[i] = [
"Increases the effectiveness of your Demon Armor and Fel Armor spells by 10%.",
"Increases the effectiveness of your Demon Armor and Fel Armor spells by 20%.",
"Increases the effectiveness of your Demon Armor and Fel Armor spells by 30%."
];
i++;
// Name: Unholy Power
rank[i] = [
"Increases the damage done by your Voidwalker, Succubus, Felhunter and Felguard\'s melee attacks and your Imp\'s Firebolt by 4%.",
"Increases the damage done by your Voidwalker, Succubus, Felhunter and Felguard\'s melee attacks and your Imp\'s Firebolt by 8%.",
"Increases the damage done by your Voidwalker, Succubus, Felhunter and Felguard\'s melee attacks and your Imp\'s Firebolt by 12%.",
"Increases the damage done by your Voidwalker, Succubus, Felhunter and Felguard\'s melee attacks and your Imp\'s Firebolt by 16%.",
"Increases the damage done by your Voidwalker, Succubus, Felhunter and Felguard\'s melee attacks and your Imp\'s Firebolt by 20%."
];
i++;
// Name: Master Summoner
rank[i] = [
"Reduces the casting time of your Imp, Voidwalker, Succubus, Felhunter and Fel Guard Summoning spells by 2 sec and the Mana cost by 20%.",
"Reduces the casting time of your Imp, Voidwalker, Succubus, Felhunter and Fel Guard Summoning spells by 4 sec and the Mana cost by 40%."
];
i++;
// Name: Mana Feed
rank[i] = [
"When you gain mana from Drain Mana or Life Tap spells, your summoned demon gains 100% of the mana you gain."
];
i++;
// Name: Master Conjuror
rank[i] = [
"Increases the combat ratings gained from your conjured Firestone and Spellstone by 150%.",
"Increases the combat ratings gained from your conjured Firestone and Spellstone by 300%."
];
i++;
// Name: Master Demonologist
rank[i] = [
"Grants both the Warlock and the summoned demon an effect as long as that demon is active.
Imp - Increases your Fire damage by 1%, and increases the critical effect chance of your Fire spells by 1%.
Voidwalker - Reduces Physical damage taken by 2%.
Succubus - Increases your Shadow damage by 1%, and increases the critical effect chance of your Shadow spells by 1%.
Felhunter - Reduces all spell damage taken by 2%.
Felguard - Increases all damage done by 1%, and reduces all damage taken by 1%.",
"Grants both the Warlock and the summoned demon an effect as long as that demon is active.
Imp - Increases your Fire damage by 2%, and increases the critical effect chance of your Fire spells by 2%.
Voidwalker - Reduces Physical damage taken by 4%.
Succubus - Increases your Shadow damage by 2%, and increases the critical effect chance of your Shadow spells by 2%.
Felhunter - Reduces all spell damage taken by 4%.
Felguard - Increases all damage done by 2%, and reduces all damage taken by 2%.",
"Grants both the Warlock and the summoned demon an effect as long as that demon is active.
Imp - Increases your Fire damage by 3%, and increases the critical effect chance of your Fire spells by 3%.
Voidwalker - Reduces Physical damage taken by 6%.
Succubus - Increases your Shadow damage by 3%, and increases the critical effect chance of your Shadow spells by 3%.
Felhunter - Reduces all spell damage taken by 6%.
Felguard - Increases all damage done by 3%, and reduces all damage taken by 3%.",
"Grants both the Warlock and the summoned demon an effect as long as that demon is active.
Imp - Increases your Fire damage by 4%, and increases the critical effect chance of your Fire spells by 4%.
Voidwalker - Reduces Physical damage taken by 8%.
Succubus - Increases your Shadow damage by 4%, and increases the critical effect chance of your Shadow spells by 4%.
Felhunter - Reduces all spell damage taken by 8%.
Felguard - Increases all damage done by 4%, and reduces all damage taken by 4%.",
"Grants both the Warlock and the summoned demon an effect as long as that demon is active.
Imp - Increases your Fire damage by 5%, and increases the critical effect chance of your Fire spells by 5%.
Voidwalker - Reduces Physical damage taken by 10%.
Succubus - Increases your Shadow damage by 5%, and increases the critical effect chance of your Shadow spells by 5%.
Felhunter - Reduces all spell damage taken by 10%.
Felguard - Increases all damage done by 5%, and reduces all damage taken by 5%."
];
i++;
// Name: Molten Core
rank[i] = [
"Your Shadow spells and damage over time effects have a 5% chance to increase the damage of your Fire spells by 10% for 12 sec.",
"Your Shadow spells and damage over time effects have a 10% chance to increase the damage of your Fire spells by 10% for 12 sec.",
"Your Shadow spells and damage over time effects have a 15% chance to increase the damage of your Fire spells by 10% for 12 sec."
];
i++;
// Name: Demonic Resilience
rank[i] = [
"Reduces the chance you\'ll be critically hit by melee and spells by 1% and reduces all damage your summoned demon takes by 5%.",
"Reduces the chance you\'ll be critically hit by melee and spells by 2% and reduces all damage your summoned demon takes by 10%.",
"Reduces the chance you\'ll be critically hit by melee and spells by 3% and reduces all damage your summoned demon takes by 15%."
];
i++;
// Name: Demonic Empowerment
rank[i] = [
"Grants the Warlock\'s summoned demon Empowerment.
Imp - Increases the Imp\'s spell critical strike chance by 20% for 30 sec.
Voidwalker - Increases the Voidwalker\'s health by 20%, and its threat generated from spells and attacks by 20% for 20 sec.
Succubus - Instantly vanishes, causing the Succubus to go into an improved Invisibility state. The vanish effect removes all stuns, snares and movement impairing effects from the Succubus.
Felhunter - Dispels all magical effects from the Felhunter.
Felguard - Increases the Felguard\'s attack speed by 20% and breaks all stun, snare and movement impairing effects and makes your Felguard immune to them for 15 sec."
];
i++;
// Name: Demonic Knowledge
rank[i] = [
"Increases your spell damage by an amount equal to 4% of the total of your active demon\'s Stamina plus Intellect.",
"Increases your spell damage by an amount equal to 8% of the total of your active demon\'s Stamina plus Intellect.",
"Increases your spell damage by an amount equal to 12% of the total of your active demon\'s Stamina plus Intellect."
];
i++;
// Name: Demonic Tactics
rank[i] = [
"Increases melee and spell critical strike chance for you and your summoned demon by 2%.",
"Increases melee and spell critical strike chance for you and your summoned demon by 4%.",
"Increases melee and spell critical strike chance for you and your summoned demon by 6%.",
"Increases melee and spell critical strike chance for you and your summoned demon by 8%.",
"Increases melee and spell critical strike chance for you and your summoned demon by 10%."
];
i++;
// Name: Decimation
rank[i] = [
"When you Shadowbolt or Incinerate a target that is at or below 35% health, the cast time of your next Soulfire is reduced by 30%. Soulfires cast under the effect of Decimation cost no shard. Lasts 10 sec.",
"When you Shadowbolt or Incinerate a target that is at or below 35% health, the cast time of your next Soulfire is reduced by 60%. Soulfires cast under the effect of Decimation cost no shard. Lasts 10 sec."
];
i++;
// Name: Improved Demonic Tactics
rank[i] = [
"Increases your summoned demon\'s critical strike chance equal to 10% of your critical strike chance.",
"Increases your summoned demon\'s critical strike chance equal to 20% of your critical strike chance.",
"Increases your summoned demon\'s critical strike chance equal to 30% of your critical strike chance."
];
i++;
// Name: Summon Felguard
rank[i] = [
"Summons a Felguard under the command of the Warlock."
];
i++;
// Name: Nemesis
rank[i] = [
"Reduces the cooldown of your Demonic Empowerment, Metamorphosis, and Fel Domination spells by 10%.",
"Reduces the cooldown of your Demonic Empowerment, Metamorphosis, and Fel Domination spells by 20%.",
"Reduces the cooldown of your Demonic Empowerment, Metamorphosis, and Fel Domination spells by 30%."
];
i++;
// Name: Demonic Pact
rank[i] = [
"Your pet\'s criticals apply the Demonic Pact effect to your party or raid members. Demonic Pact increases spell power by 2% of your Spell Damage for 12 sec. Does not work on Enslaved demons.",
"Your pet\'s criticals apply the Demonic Pact effect to your party or raid members. Demonic Pact increases spell power by 4% of your Spell Damage for 12 sec. Does not work on Enslaved demons.",
"Your pet\'s criticals apply the Demonic Pact effect to your party or raid members. Demonic Pact increases spell power by 6% of your Spell Damage for 12 sec. Does not work on Enslaved demons.",
"Your pet\'s criticals apply the Demonic Pact effect to your party or raid members. Demonic Pact increases spell power by 8% of your Spell Damage for 12 sec. Does not work on Enslaved demons.",
"Your pet\'s criticals apply the Demonic Pact effect to your party or raid members. Demonic Pact increases spell power by 10% of your Spell Damage for 12 sec. Does not work on Enslaved demons."
];
i++;
// Name: Metamorphosis
rank[i] = [
"You transform into a Demon for 30 sec. This form increases your armor by 600%, damage by 20%, reduces the chance you\'ll be critically hit by melee attacks by 6% and reduces the duration of stun and snare effects by 50%. You gain some unique demon abilities in addition to your normal abilities. 3 minute cooldown."
];
i++;
// Name: Improved Shadow Bolt
rank[i] = [
"Increases the damage done by your Shadow Bolt spell by 1%, and your Shadow Bolt has a 20% chance to cause your target to be vulnerable to spell damage, increasing spell critical strike chance against that target by 5%. Effect lasts 30 sec.",
"Increases the damage done by your Shadow Bolt spell by 2%, and your Shadow Bolt has a 40% chance to cause your target to be vulnerable to spell damage, increasing spell critical strike chance against that target by 5%. Effect lasts 30 sec.",
"Increases the damage done by your Shadow Bolt spell by 3%, and your Shadow Bolt has a 60% chance to cause your target to be vulnerable to spell damage, increasing spell critical strike chance against that target by 5%. Effect lasts 30 sec.",
"Increases the damage done by your Shadow Bolt spell by 4%, and your Shadow Bolt has a 80% chance to cause your target to be vulnerable to spell damage, increasing spell critical strike chance against that target by 5%. Effect lasts 30 sec.",
"Increases the damage done by your Shadow Bolt spell by 5%, and your Shadow Bolt has a 100% chance to cause your target to be vulnerable to spell damage, increasing spell critical strike chance against that target by 5%. Effect lasts 30 sec."
];
i++;
// Name: Bane
rank[i] = [
"Reduces the casting time of your Shadow Bolt. Chaos Bolt and Immolate spells by 0.1 sec and your Soul Fire spell by 0.4 sec.",
"Reduces the casting time of your Shadow Bolt. Chaos Bolt and Immolate spells by 0.2 sec and your Soul Fire spell by 0.8 sec.",
"Reduces the casting time of your Shadow Bolt. Chaos Bolt and Immolate spells by 0.3 sec and your Soul Fire spell by 1.2 sec.",
"Reduces the casting time of your Shadow Bolt. Chaos Bolt and Immolate spells by 0.4 sec and your Soul Fire spell by 1.6 sec.",
"Reduces the casting time of your Shadow Bolt. Chaos Bolt and Immolate spells by 0.5 sec and your Soul Fire spell by 2 sec."
];
i++;
// Name: Aftermath
rank[i] = [
"Increases the periodic damage done by your Immolate by 3%, and your Conflagrate has a 50% chance to daze the target for 5 sec.",
"Increases the periodic damage done by your Immolate by 6%, and your Conflagrate has a 100% chance to daze the target for 5 sec."
];
i++;
// Name: Molten Skin
rank[i] = [
"Reduces all damage taken by 2%.",
"Reduces all damage taken by 4%.",
"Reduces all damage taken by 6%."
];
i++;
// Name: Cataclysm
rank[i] = [
"Reduces the Mana cost of your Destruction spells by 4%.",
"Reduces the Mana cost of your Destruction spells by 7%.",
"Reduces the Mana cost of your Destruction spells by 10%."
];
i++;
// Name: Demonic Power
rank[i] = [
"Reduces the cooldown of your Succubus\' Lash of Pain spell by 3 sec. and reduces the casting time of your Imp\'s Firebolt spell by 0.25 sec.",
"Reduces the cooldown of your Succubus\' Lash of Pain spell by 6 sec. and reduces the casting time of your Imp\'s Firebolt spell by 0.5 sec."
];
i++;
// Name: Shadowburn
rank[i] = [
"Instantly blasts the target for 87 to 99 Shadow damage. If the target dies within 5 sec of Shadowburn, and yields experience or honor, the caster gains a Soul Shard."
];
i++;
// Name: Ruin
rank[i] = [
"Increases the critical strike damage bonus of your Destruction spells by 20%.",
"Increases the critical strike damage bonus of your Destruction spells by 40%.",
"Increases the critical strike damage bonus of your Destruction spells by 60%.",
"Increases the critical strike damage bonus of your Destruction spells by 80%.",
"Increases the critical strike damage bonus of your Destruction spells by 100%."
];
i++;
// Name: Intensity
rank[i] = [
"Reduces the pushback suffered from damaging attacks while casting or channeling any Destruction spell by 35%.",
"Reduces the pushback suffered from damaging attacks while casting or channeling any Destruction spell by 70%."
];
i++;
// Name: Destructive Reach
rank[i] = [
"Increases the range of your Destruction spells by 10% and reduces threat caused by Destruction spells by 5%.",
"Increases the range of your Destruction spells by 20% and reduces threat caused by Destruction spells by 10%."
];
i++;
// Name: Improved Searing Pain
rank[i] = [
"Increases the critical strike chance of your Searing Pain spell by 4%.",
"Increases the critical strike chance of your Searing Pain spell by 7%.",
"Increases the critical strike chance of your Searing Pain spell by 10%."
];
i++;
// Name: Backlash
rank[i] = [
"Increases your critical strike chance with spells by an additional 1% and gives you a 8% chance when hit by a physical attack to reduce the cast time of your next Shadow Bolt or Incinerate spell by 100%. This effect lasts 8 sec and will not occur more than once every 8 seconds.",
"Increases your critical strike chance with spells by an additional 2% and gives you a 16% chance when hit by a physical attack to reduce the cast time of your next Shadow Bolt or Incinerate spell by 100%. This effect lasts 8 sec and will not occur more than once every 8 seconds.",
"Increases your critical strike chance with spells by an additional 3% and gives you a 25% chance when hit by a physical attack to reduce the cast time of your next Shadow Bolt or Incinerate spell by 100%. This effect lasts 8 sec and will not occur more than once every 8 seconds."
];
i++;
// Name: Improved Immolate
rank[i] = [
"Increases the damage done by your Immolate spell by 10%.",
"Increases the damage done by your Immolate spell by 20%.",
"Increases the damage done by your Immolate spell by 30%."
];
i++;
// Name: Devastation
rank[i] = [
"Increases the critical strike chance of your Destruction spells by 5%."
];
i++;
// Name: Nether Protection
rank[i] = [
"After being hit with a spell, you have a 10% chance to gain Nether Protection, reducing all damage by that spell school by 30% for 8 sec.",
"After being hit with a spell, you have a 20% chance to gain Nether Protection, reducing all damage by that spell school by 30% for 8 sec.",
"After being hit with a spell, you have a 30% chance to gain Nether Protection, reducing all damage by that spell school by 30% for 8 sec."
];
i++;
// Name: Emberstorm
rank[i] = [
"Increases the damage done by your Fire spells by 3% and reduces the cast time of your Incinerate spell by 0.05 sec.",
"Increases the damage done by your Fire spells by 6% and reduces the cast time of your Incinerate spell by 0.1 sec.",
"Increases the damage done by your Fire spells by 9% and reduces the cast time of your Incinerate spell by 0.15 sec.",
"Increases the damage done by your Fire spells by 12% and reduces the cast time of your Incinerate spell by 0.2 sec.",
"Increases the damage done by your Fire spells by 15% and reduces the cast time of your Incinerate spell by 0.25 sec."
];
i++;
// Name: Conflagrate
rank[i] = [
"Consumes an Immolate or Shadowflame effect on the enemy target to instantly deal damage equal to 12 sec of your Immolate or 8 sec of your Shadowflame."
];
i++;
// Name: Soul Leech
rank[i] = [
"Gives your Shadow Bolt, Shadowburn, Chaos Bolt, Soul Fire, Incinerate, Searing Pain and Conflagrate spells a 10% chance to return health equal to 20% of the damage caused.",
"Gives your Shadow Bolt, Shadowburn, Chaos Bolt, Soul Fire, Incinerate, Searing Pain and Conflagrate spells a 20% chance to return health equal to 20% of the damage caused.",
"Gives your Shadow Bolt, Shadowburn, Chaos Bolt, Soul Fire, Incinerate, Searing Pain and Conflagrate spells a 30% chance to return health equal to 20% of the damage caused."
];
i++;
// Name: Pyroclasm
rank[i] = [
"When you critically strike with Searing Pain or Conflagrate, your Fire and Shadow spell damage is increased by 2% for 10 sec.",
"When you critically strike with Searing Pain or Conflagrate, your Fire and Shadow spell damage is increased by 4% for 10 sec.",
"When you critically strike with Searing Pain or Conflagrate, your Fire and Shadow spell damage is increased by 6% for 10 sec."
];
i++;
// Name: Shadow and Flame
rank[i] = [
"Your Shadow Bolt, Shadowburn, Chaos Bolt and Incinerate spells gain an additional 4% of your bonus spell damage effects.",
"Your Shadow Bolt, Shadowburn, Chaos Bolt and Incinerate spells gain an additional 8% of your bonus spell damage effects.",
"Your Shadow Bolt, Shadowburn, Chaos Bolt and Incinerate spells gain an additional 12% of your bonus spell damage effects.",
"Your Shadow Bolt, Shadowburn, Chaos Bolt and Incinerate spells gain an additional 16% of your bonus spell damage effects.",
"Your Shadow Bolt, Shadowburn, Chaos Bolt and Incinerate spells gain an additional 20% of your bonus spell damage effects."
];
i++;
// Name: Improved Soul Leech
rank[i] = [
"Your Soul Leech effect also restores mana to you and your summoned demon equal to 1% of maximum mana, and has a 50% chance to grant up to 10 party or raid members mana regeneration equal to 1% of maximum mana per 5 sec. Lasts for 15 sec.",
"Your Soul Leech effect also restores mana to you and your summoned demon equal to 2% of maximum mana, and has a 100% chance to grant up to 10 party or raid members mana regeneration equal to 1% of maximum mana per 5 sec. Lasts for 15 sec."
];
i++;
// Name: Backdraft
rank[i] = [
"When you cast Conflagrate, the cast time and global cooldown of your next three Destruction spells is reduced by 10%. Lasts 15 sec.",
"When you cast Conflagrate, the cast time and global cooldown of your next three Destruction spells is reduced by 20%. Lasts 15 sec.",
"When you cast Conflagrate, the cast time and global cooldown of your next three Destruction spells is reduced by 30%. Lasts 15 sec."
];
i++;
// Name: Shadowfury
rank[i] = [
"Shadowfury is unleashed, causing 343 to 407 Shadow damage and stunning all enemies within 8 yds for 3 sec."
];
i++;
// Name: Empowered Imp
rank[i] = [
"Increases the damage done by your Imp by 5%, and all critical hits done by your Imp have a 33% chance to increase your spell critical hit chance for your next spell by 100%. This effect lasts 8 sec.",
"Increases the damage done by your Imp by 10%, and all critical hits done by your Imp have a 66% chance to increase your spell critical hit chance for your next spell by 100%. This effect lasts 8 sec.",
"Increases the damage done by your Imp by 15%, and all critical hits done by your Imp have a 100% chance to increase your spell critical hit chance for your next spell by 100%. This effect lasts 8 sec."
];
i++;
// Name: Fire and Brimstone
rank[i] = [
"Increases the damage done by your Incinerate and Chaos Bolt spells to targets afflicted by your Immolate by 2%, and the critical strike chance of your Conflagrate spell is increased by 5%.",
"Increases the damage done by your Incinerate and Chaos Bolt spells to targets afflicted by your Immolate by 4%, and the critical strike chance of your Conflagrate spell is increased by 10%.",
"Increases the damage done by your Incinerate and Chaos Bolt spells to targets afflicted by your Immolate by 6%, and the critical strike chance of your Conflagrate spell is increased by 15%.",
"Increases the damage done by your Incinerate and Chaos Bolt spells to targets afflicted by your Immolate by 8%, and the critical strike chance of your Conflagrate spell is increased by 20%.",
"Increases the damage done by your Incinerate and Chaos Bolt spells to targets afflicted by your Immolate by 10%, and the critical strike chance of your Conflagrate spell is increased by 25%."
];
i++;
// Name: Chaos Bolt
rank[i] = [
"Sends a bolt of chaotic fire at the enemy, dealing 837 to 1061 Fire damage. Chaos Bolt cannot be resisted, and pierces through all absorption effects."
];
i++;
|