/*
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 = "deathknight";
var i = 0;
var t = 0;
var className = "Death Knight Talents";
tree[i] = "Blood"; i++;
tree[i] = "Frost"; i++;
tree[i] = "Unholy"; i++;
i = 0;
talent[i] = [0, "Butchery", 2, 1, 1]; i++;
talent[i] = [0, "Subversion", 3, 2, 1]; i++;
talent[i] = [0, "Blade Barrier", 5, 3, 1]; i++;
talent[i] = [0, "Bladed Armor", 5, 1, 2]; i++;
talent[i] = [0, "Scent of Blood", 3, 2, 2]; i++;
talent[i] = [0, "Two-Handed Weapon Specialization", 2, 3, 2]; i++;
talent[i] = [0, "Rune Tap", 1, 1, 3]; i++;
talent[i] = [0, "Dark Conviction", 5, 2, 3]; i++;
talent[i] = [0, "Death Rune Mastery", 3, 3, 3]; i++;
talent[i] = [0, "Improved Rune Tap", 3, 1, 4, [getTalentID("Rune Tap"),1]]; i++;
talent[i] = [0, "Spell Deflection", 3, 3, 4]; i++;
talent[i] = [0, "Vendetta", 3, 4, 4]; i++;
talent[i] = [0, "Bloody Strikes", 3, 1, 5]; i++;
talent[i] = [0, "Veteran of the Third War", 3, 3, 5]; i++;
talent[i] = [0, "Mark of Blood", 1, 4, 5]; i++;
talent[i] = [0, "Bloody Vengeance", 3, 2, 6, [getTalentID("Dark Conviction"),5]]; i++;
talent[i] = [0, "Abomination's Might", 2, 3, 6]; i++;
talent[i] = [0, "Bloodworms", 3, 1, 7]; i++;
talent[i] = [0, "Hysteria", 1, 2, 7]; i++;
talent[i] = [0, "Improved Blood Presence", 2, 3, 7]; i++;
talent[i] = [0, "Improved Death Strike", 2, 1, 8]; i++;
talent[i] = [0, "Sudden Doom", 3, 2, 8]; i++;
talent[i] = [0, "Vampiric Blood", 1, 3, 8]; i++;
talent[i] = [0, "Will of the Necropolis", 3, 1, 9]; i++;
talent[i] = [0, "Heart Strike", 1, 2, 9]; i++;
talent[i] = [0, "Might of Mograine", 3, 3, 9]; i++;
talent[i] = [0, "Blood Gorged", 5, 2, 10]; i++;
talent[i] = [0, "Dancing Rune Weapon", 1, 2, 11]; i++;
treeStartStop[t] = i - 1;
t++;
talent[i] = [1, "Improved Icy Touch", 3, 1, 1]; i++;
talent[i] = [1, "Runic Power Mastery", 2, 2, 1]; i++;
talent[i] = [1, "Toughness", 5, 3, 1]; i++;
talent[i] = [1, "Icy Reach", 2, 2, 2]; i++;
talent[i] = [1, "Black Ice", 5, 3, 2]; i++;
talent[i] = [1, "Nerves of Cold Steel", 3, 4, 2]; i++;
talent[i] = [1, "Icy Talons", 5, 1, 3, [getTalentID("Improved Icy Touch"),3]]; i++;
talent[i] = [1, "Lichborne", 1, 2, 3]; i++;
talent[i] = [1, "Annihilation", 3, 3, 3]; i++;
talent[i] = [1, "Killing Machine", 5, 2, 4]; i++;
talent[i] = [1, "Chill of the Grave", 2, 3, 4]; i++;
talent[i] = [1, "Endless Winter", 2, 4, 4]; i++;
talent[i] = [1, "Frigid Dreadplate", 3, 2, 5]; i++;
talent[i] = [1, "Glacier Rot", 3, 3, 5]; i++;
talent[i] = [1, "Deathchill", 1, 4, 5]; i++;
talent[i] = [1, "Improved Icy Talons", 1, 1, 6, [getTalentID("Icy Talons"),5]]; i++;
talent[i] = [1, "Merciless Combat", 2, 2, 6, [getTalentID(""),1]]; i++;
talent[i] = [1, "Rime", 3, 3, 6]; i++;
talent[i] = [1, "Chilblains", 3, 1, 7]; i++;
talent[i] = [1, "Hungering Cold", 1, 2, 7]; i++;
talent[i] = [1, "Improved Frost Presence", 2, 3, 7]; i++;
talent[i] = [1, "", 3, 1, 8]; i++;
talent[i] = [1, "Blood of the North", 3, 2, 8]; i++;
talent[i] = [1, "Unbreakable Armor", 1, 3, 8]; i++;
talent[i] = [1, "Acclimation", 3, 1, 9]; i++;
talent[i] = [1, "Frost Strike", 1, 2, 9]; i++;
talent[i] = [1, "Guile of Gorefiend", 3, 3, 9]; i++;
talent[i] = [1, "Tundra Stalker", 5, 2, 10]; i++;
talent[i] = [1, "Howling Blast", 1, 2, 11]; i++;
treeStartStop[t] = i - 1;
t++;
talent[i] = [2, "Vicious Strikes", 2, 1, 1]; i++;
talent[i] = [2, "Virulence", 3, 2, 1]; i++;
talent[i] = [2, "Anticipation", 5, 3, 1]; i++;
talent[i] = [2, "Epidemic", 2, 1, 2]; i++;
talent[i] = [2, "Morbidity", 3, 2, 2]; i++;
talent[i] = [2, "Unholy Command", 2, 3, 2]; i++;
talent[i] = [2, "Ravenous Dead", 3, 4, 2]; i++;
talent[i] = [2, "Outbreak", 3, 1, 3]; i++;
talent[i] = [2, "Necrosis", 5, 2, 3]; i++;
talent[i] = [2, "Corpse Explosion", 1, 3, 3]; i++;
talent[i] = [2, "On a Pale Horse", 2, 2, 4]; i++;
talent[i] = [2, "Blood-Caked Blade", 3, 3, 4]; i++;
talent[i] = [2, "Night of the Dead", 2, 4, 4]; i++;
talent[i] = [2, "Unholy Blight", 1, 1, 5]; i++;
talent[i] = [2, "Impurity", 5, 2, 5]; i++;
talent[i] = [2, "Dirge", 2, 3, 5]; i++;
talent[i] = [2, "Desecration", 2, 1, 6]; i++;
talent[i] = [2, "Magic Suppression", 3, 2, 6]; i++;
talent[i] = [2, "Reaping", 3, 3, 6]; i++;
talent[i] = [2, "Master of Ghouls", 1, 4, 6, [getTalentID("Night of the Dead"),2]]; i++;
talent[i] = [2, "", 5, 1, 7]; i++;
talent[i] = [2, "Anti-Magic Zone", 1, 2, 7, [getTalentID("Magic Suppression"),3]]; i++;
talent[i] = [2, "Improved Unholy Presence", 2, 3, 7]; i++;
talent[i] = [2, "Ghoul Frenzy", 1, 4, 7, [getTalentID("Master of Ghouls"),1]]; i++;
talent[i] = [2, "Crypt Fever", 3, 2, 8]; i++;
talent[i] = [2, "Bone Shield", 1, 3, 8]; i++;
talent[i] = [2, "Wandering Plague", 3, 1, 9]; i++;
talent[i] = [2, "Ebon Plaguebringer", 3, 2, 9, [getTalentID("Crypt Fever"),3]]; i++;
talent[i] = [2, "Scourge Strike", 1, 3, 9]; i++;
talent[i] = [2, "Rage of Rivendare", 5, 2, 10]; i++;
talent[i] = [2, "Summon Gargoyle", 1, 2, 11]; i++;
treeStartStop[t] = i - 1;
t++;
i = 0;
// Name: Butchery
rank[i] = [
"Whenever you kill an enemy that grants experience or honor, you generate up to 10 runic power. In addition, you generate 1 runic power per 5 sec while in combat.",
"Whenever you kill an enemy that grants experience or honor, you generate up to 20 runic power. In addition, you generate 2 runic power per 5 sec while in combat."
];
i++;
// Name: Subversion
rank[i] = [
"Increases the critical strike chance of Blood Strike, Heart Strike and Obliterate by 3%, and reduces threat generated while in Blood or Unholy Presence by 8%.",
"Increases the critical strike chance of Blood Strike, Heart Strike and Obliterate by 6%, and reduces threat generated while in Blood or Unholy Presence by 16%.",
"Increases the critical strike chance of Blood Strike, Heart Strike and Obliterate by 9%, and reduces threat generated while in Blood or Unholy Presence by 25%."
];
i++;
// Name: Blade Barrier
rank[i] = [
"Whenever your Blood Runes are on cooldown, you gain the Blade Barrier effect, which decreases damage taken by 1% for the next 10 sec.",
"Whenever your Blood Runes are on cooldown, you gain the Blade Barrier effect, which decreases damage taken by 2% for the next 10 sec.",
"Whenever your Blood Runes are on cooldown, you gain the Blade Barrier effect, which decreases damage taken by 3% for the next 10 sec.",
"Whenever your Blood Runes are on cooldown, you gain the Blade Barrier effect, which decreases damage taken by 4% for the next 10 sec.",
"Whenever your Blood Runes are on cooldown, you gain the Blade Barrier effect, which decreases damage taken by 5% for the next 10 sec."
];
i++;
// Name: Bladed Armor
rank[i] = [
"Increases your attack power by 1 for every 180 armor value you have.",
"Increases your attack power by 2 for every 180 armor value you have.",
"Increases your attack power by 3 for every 180 armor value you have.",
"Increases your attack power by 4 for every 180 armor value you have.",
"Increases your attack power by 5 for every 180 armor value you have."
];
i++;
// Name: Scent of Blood
rank[i] = [
"You have a 15% chance after dodging, parrying or taking direct damage to gain the Scent of Blood effect, causing your next melee hit to generate 10 runic power.",
"You have a 15% chance after dodging, parrying or taking direct damage to gain the Scent of Blood effect, causing your next 2 melee hits to generate 10 runic power.",
"You have a 15% chance after dodging, parrying or taking direct damage to gain the Scent of Blood effect, causing your next 3 melee hits to generate 10 runic power."
];
i++;
// Name: Two-Handed Weapon Specialization
rank[i] = [
"Increases the damage you deal with two-handed melee weapons by 2%.",
"Increases the damage you deal with two-handed melee weapons by 4%."
];
i++;
// Name: Rune Tap
rank[i] = [
"Converts 1 Blood Rune into 10% of your maximum health."
];
i++;
// Name: Dark Conviction
rank[i] = [
"Increases your chance to critically hit with weapons, spells and abilities by 1%.",
"Increases your chance to critically hit with weapons, spells and abilities by 2%.",
"Increases your chance to critically hit with weapons, spells and abilities by 3%.",
"Increases your chance to critically hit with weapons, spells and abilities by 4%.",
"Increases your chance to critically hit with weapons, spells and abilities by 5%."
];
i++;
// Name: Death Rune Mastery
rank[i] = [
"Whenever you hit with Death Strike or Obliterate there is a 33% chance that the Frost and Unholy Runes will become Death Runes when they activate. Death Runes count as a Blood, Frost or Unholy Rune.",
"Whenever you hit with Death Strike or Obliterate there is a 66% chance that the Frost and Unholy Runes will become Death Runes when they activate. Death Runes count as a Blood, Frost or Unholy Rune.",
"Whenever you hit with Death Strike or Obliterate there is a 100% chance that the Frost and Unholy Runes will become Death Runes when they activate. Death Runes count as a Blood, Frost or Unholy Rune."
];
i++;
// Name: Improved Rune Tap
rank[i] = [
"Increases the health provided by Rune Tap by 33% and lowers its cooldown by 10 sec.",
"Increases the health provided by Rune Tap by 66% and lowers its cooldown by 20 sec.",
"Increases the health provided by Rune Tap by 100% and lowers its cooldown by 30 sec."
];
i++;
// Name: Spell Deflection
rank[i] = [
"You have a chance equal to your Parry chance of taking 15% less damage from a direct damage spell.",
"You have a chance equal to your Parry chance of taking 30% less damage from a direct damage spell.",
"You have a chance equal to your Parry chance of taking 45% less damage from a direct damage spell."
];
i++;
// Name: Vendetta
rank[i] = [
"Heals you for up to 2% of your maximum health whenever you kill a target that yields experience or honor.",
"Heals you for up to 4% of your maximum health whenever you kill a target that yields experience or honor.",
"Heals you for up to 6% of your maximum health whenever you kill a target that yields experience or honor."
];
i++;
// Name: Bloody Strikes
rank[i] = [
"Increases the damage of Blood Strike by 5% and Heart Strike by 15%, and increases the damage of Blood Boil by 10%.",
"Increases the damage of Blood Strike by 10% and Heart Strike by 30%, and increases the damage of Blood Boil by 20%.",
"Increases the damage of Blood Strike by 15% and Heart Strike by 45%, and increases the damage of Blood Boil by 30%."
];
i++;
// Name: Veteran of the Third War
rank[i] = [
"Increases your total Strength by 2%, your Stamina by 1%, and your expertise by 2.",
"Increases your total Strength by 4%, your Stamina by 2%, and your expertise by 4.",
"Increases your total Strength by 6%, your Stamina by 3%, and your expertise by 6."
];
i++;
// Name: Mark of Blood
rank[i] = [
"Place a Mark of Blood on an enemy. Whenever the marked enemy deals damage to a target, that target is healed for 4% of its maximum health. Lasts for 20 sec or up to 20 hits."
];
i++;
// Name: Bloody Vengeance
rank[i] = [
"Gives you a 1% bonus to physical damage you deal for 30 sec after dealing a critical strike from a weapon swing, spell, or ability. This effect stacks up to 3 times.",
"Gives you a 2% bonus to physical damage you deal for 30 sec after dealing a critical strike from a weapon swing, spell, or ability. This effect stacks up to 3 times.",
"Gives you a 3% bonus to physical damage you deal for 30 sec after dealing a critical strike from a weapon swing, spell, or ability. This effect stacks up to 3 times."
];
i++;
// Name: Abomination's Might
rank[i] = [
"Your Blood Strikes and Heart Strikes have a 25% chance and your Death Strikes and Obliterates have a 50% chance to increase the attack power by 10% of raid members within 45 yards for 10 sec. Also increases your total Strength by 1%.",
"Your Blood Strikes and Heart Strikes have a 50% chance and your Death Strikes and Obliterates have a 100% chance to increase the attack power by 10% of raid members within 45 yards for 10 sec. Also increases your total Strength by 2%."
];
i++;
// Name: Bloodworms
rank[i] = [
"Your weapon hits have a 3% chance to cause the target to spawn 2-4 Bloodworms. Bloodworms attack your enemies, healing you as they do damage for 20 sec or until killed.",
"Your weapon hits have a 6% chance to cause the target to spawn 2-4 Bloodworms. Bloodworms attack your enemies, healing you as they do damage for 20 sec or until killed.",
"Your weapon hits have a 9% chance to cause the target to spawn 2-4 Bloodworms. Bloodworms attack your enemies, healing you as they do damage for 20 sec or until killed."
];
i++;
// Name: Hysteria
rank[i] = [
"Induces a friendly unit into a killing frenzy for 30 sec. The target is Enraged, which increases their physical damage by 20%, but causes them to lose health equal to 1% of their maximum health every second."
];
i++;
// Name: Improved Blood Presence
rank[i] = [
"While in Frost Presence or Unholy Presence, you retain 2% healing from Blood Presence, and healing done to you is increased by 5% in Blood Presence.",
"While in Frost Presence or Unholy Presence, you retain 4% healing from Blood Presence, and healing done to you is increased by 10% in Blood Presence."
];
i++;
// Name: Improved Death Strike
rank[i] = [
"Increases the damage of your Death Strike by 15%, increases its critical strike chance by 3%, and increases the healing granted by 25%.",
"Increases the damage of your Death Strike by 30%, increases its critical strike chance by 6%, and increases the healing granted by 50%."
];
i++;
// Name: Sudden Doom
rank[i] = [
"Your Blood Strikes and Heart Strikes have a 5% chance to launch a free Death Coil at your target.",
"Your Blood Strikes and Heart Strikes have a 10% chance to launch a free Death Coil at your target.",
"Your Blood Strikes and Heart Strikes have a 15% chance to launch a free Death Coil at your target."
];
i++;
// Name: Vampiric Blood
rank[i] = [
"Temporarily grants the Death Knight 15% of maximum health and increases the amount of health generated through spells and effects by 35% for 20 sec. After the effect expires, the health is lost."
];
i++;
// Name: Will of the Necropolis
rank[i] = [
"Damage that would take you below 35% health or taken while you are at 35% health is reduced by 5%. This effect cannot occur more often than once every 15 sec and cannot be triggered by damage which deals less than 5% of your health.",
"Damage that would take you below 35% health or taken while you are at 35% health is reduced by 10%. This effect cannot occur more often than once every 15 sec and cannot be triggered by damage which deals less than 5% of your health.",
"Damage that would take you below 35% health or taken while you are at 35% health is reduced by 15%. This effect cannot occur more often than once every 15 sec and cannot be triggered by damage which deals less than 5% of your health."
];
i++;
// Name: Heart Strike
rank[i] = [
"Instantly strike the target and his nearest ally, causing 50% weapon damage plus 125, total damage increased by 10% for each of your diseases on the target."
];
i++;
// Name: Might of Mograine
rank[i] = [
"Increases the critical strike damage bonus of your Blood Boil, Blood Strike, Death Strike, and Heart Strike abilities by 15%.",
"Increases the critical strike damage bonus of your Blood Boil, Blood Strike, Death Strike, and Heart Strike abilities by 30%.",
"Increases the critical strike damage bonus of your Blood Boil, Blood Strike, Death Strike, and Heart Strike abilities by 45%."
];
i++;
// Name: Blood Gorged
rank[i] = [
"When you are above 75% health, you deal 2% more damage. In addition, your attacks ignore up to 2% of your opponent\'s armor at all times.",
"When you are above 75% health, you deal 4% more damage. In addition, your attacks ignore up to 4% of your opponent\'s armor at all times.",
"When you are above 75% health, you deal 6% more damage. In addition, your attacks ignore up to 6% of your opponent\'s armor at all times.",
"When you are above 75% health, you deal 8% more damage. In addition, your attacks ignore up to 8% of your opponent\'s armor at all times.",
"When you are above 75% health, you deal 10% more damage. In addition, your attacks ignore up to 10% of your opponent\'s armor at all times."
];
i++;
// Name: Dancing Rune Weapon
rank[i] = [
"Summons a second rune weapon that fights on its own for 12 sec, doing the same attacks as the Death Knight but for 50% reduced damage."
];
i++;
// Name: Improved Icy Touch
rank[i] = [
"Your Icy Touch does an additional 5% damage and your Frost Fever reduces melee and ranged attack speed by an additional 2%.",
"Your Icy Touch does an additional 10% damage and your Frost Fever reduces melee and ranged attack speed by an additional 4%.",
"Your Icy Touch does an additional 15% damage and your Frost Fever reduces melee and ranged attack speed by an additional 6%."
];
i++;
// Name: Runic Power Mastery
rank[i] = [
"Increases your maximum Runic Power by 15.",
"Increases your maximum Runic Power by 30."
];
i++;
// Name: Toughness
rank[i] = [
"Increases your armor value from items by 2% and reduces the duration of all movement slowing effects by 6%.",
"Increases your armor value from items by 4% and reduces the duration of all movement slowing effects by 12%.",
"Increases your armor value from items by 6% and reduces the duration of all movement slowing effects by 18%.",
"Increases your armor value from items by 8% and reduces the duration of all movement slowing effects by 24%.",
"Increases your armor value from items by 10% and reduces the duration of all movement slowing effects by 30%."
];
i++;
// Name: Icy Reach
rank[i] = [
"Increases the range of your Icy Touch, Chains of Ice and Howling Blast by 5 yards.",
"Increases the range of your Icy Touch, Chains of Ice and Howling Blast by 10 yards."
];
i++;
// Name: Black Ice
rank[i] = [
"Increases your Frost and Shadow damage by 2%.",
"Increases your Frost and Shadow damage by 4%.",
"Increases your Frost and Shadow damage by 6%.",
"Increases your Frost and Shadow damage by 8%.",
"Increases your Frost and Shadow damage by 10%."
];
i++;
// Name: Nerves of Cold Steel
rank[i] = [
"Increases your chance to hit with one-handed melee weapons by 1% and increases the damage done by your offhand weapon by 5%.",
"Increases your chance to hit with one-handed melee weapons by 2% and increases the damage done by your offhand weapon by 10%.",
"Increases your chance to hit with one-handed melee weapons by 3% and increases the damage done by your offhand weapon by 15%."
];
i++;
// Name: Icy Talons
rank[i] = [
"You leech heat from victims of your Frost Fever, so that when their melee attack speed is reduced, yours increases by 4% for the next 20 sec.",
"You leech heat from victims of your Frost Fever, so that when their melee attack speed is reduced, yours increases by 8% for the next 20 sec.",
"You leech heat from victims of your Frost Fever, so that when their melee attack speed is reduced, yours increases by 12% for the next 20 sec.",
"You leech heat from victims of your Frost Fever, so that when their melee attack speed is reduced, yours increases by 16% for the next 20 sec.",
"You leech heat from victims of your Frost Fever, so that when their melee attack speed is reduced, yours increases by 20% for the next 20 sec."
];
i++;
// Name: Lichborne
rank[i] = [
"Draw upon unholy energy to become undead for 10 sec. While undead, you are immune to Charm, Fear and Sleep effects."
];
i++;
// Name: Annihilation
rank[i] = [
"Increases the critical strike chance of your melee special abilities by 1%. In addition, there is a 33% chance that your Obliterate will do its damage without consuming diseases.",
"Increases the critical strike chance of your melee special abilities by 2%. In addition, there is a 66% chance that your Obliterate will do its damage without consuming diseases.",
"Increases the critical strike chance of your melee special abilities by 3%. In addition, there is a 100% chance that your Obliterate will do its damage without consuming diseases."
];
i++;
// Name: Killing Machine
rank[i] = [
"Your melee attacks have a chance to make your next Icy Touch, Howling Blast or Frost Strike a critical strike.",
"Your melee attacks have a chance to make your next Icy Touch, Howling Blast or Frost Strike a critical strike. Effect occurs more often than Killing Machine (Rank 1).",
"Your melee attacks have a chance to make your next Icy Touch, Howling Blast or Frost Strike a critical strike. Effect occurs more often than Killing Machine (Rank 2).",
"Your melee attacks have a chance to make your next Icy Touch, Howling Blast or Frost Strike a critical strike. Effect occurs more often than Killing Machine (Rank 3).",
"Your melee attacks have a chance to make your next Icy Touch, Howling Blast or Frost Strike a critical strike. Effect occurs more often than Killing Machine (Rank 4)."
];
i++;
// Name: Chill of the Grave
rank[i] = [
"Your Chains of Ice, Howling Blast, Icy Touch and Obliterate generate 2.5 additional runic power.",
"Your Chains of Ice, Howling Blast, Icy Touch and Obliterate generate 5 additional runic power."
];
i++;
// Name: Endless Winter
rank[i] = [
"Your Chains of Ice has a 50% chance to cause Frost Fever and the cost of your Mind Freeze is reduced to 10 runic power.",
"Your Chains of Ice has a 100% chance to cause Frost Fever and the cost of your Mind Freeze is reduced to no runic power."
];
i++;
// Name: Frigid Dreadplate
rank[i] = [
"Reduces the chance melee attacks will hit you by 1%.",
"Reduces the chance melee attacks will hit you by 2%.",
"Reduces the chance melee attacks will hit you by 3%."
];
i++;
// Name: Glacier Rot
rank[i] = [
"Diseased enemies take 7% more damage from your Icy Touch, Howling Blast and Frost Strike.",
"Diseased enemies take 13% more damage from your Icy Touch, Howling Blast and Frost Strike.",
"Diseased enemies take 20% more damage from your Icy Touch, Howling Blast and Frost Strike."
];
i++;
// Name: Deathchill
rank[i] = [
"When activated, makes your next Icy Touch, Howling Blast, Frost Strike or Obliterate a critical hit if used within 30 sec."
];
i++;
// Name: Improved Icy Talons
rank[i] = [
"Your Icy Talons effect increases the melee haste of your group or raid by 20% for the next 20 sec. In addition, increases your haste by 5% at all times."
];
i++;
// Name: Merciless Combat
rank[i] = [
"Your Icy Touch, Howling Blast, Obliterate and Frost Strike do an additional 6% damage when striking targets with less than 35% health.",
"Your Icy Touch, Howling Blast, Obliterate and Frost Strike do an additional 12% damage when striking targets with less than 35% health."
];
i++;
// Name: Rime
rank[i] = [
"Increases the critical strike chance of your Icy Touch and Obliterate by 5% and casting Obliterate has a 5% chance to reset the cooldown on Howling Blast and cause your next Howling Blast to consume no runes.",
"Increases the critical strike chance of your Icy Touch and Obliterate by 10% and casting Obliterate has a 10% chance to reset the cooldown on Howling Blast and cause your next Howling Blast to consume no runes.",
"Increases the critical strike chance of your Icy Touch and Obliterate by 15% and casting Obliterate has a 15% chance to reset the cooldown on Howling Blast and cause your next Howling Blast to consume no runes."
];
i++;
// Name: Chilblains
rank[i] = [
"Victims of your Frost Fever disease are Chilled, reducing movement speed by 15% for 10 sec.",
"Victims of your Frost Fever disease are Chilled, reducing movement speed by 30% for 10 sec.",
"Victims of your Frost Fever disease are Chilled, reducing movement speed by 50% for 10 sec."
];
i++;
// Name: Hungering Cold
rank[i] = [
"Purges the earth around the Death Knight of all heat. Enemies within 10 yards are trapped in ice, preventing them from performing any action for 10 sec and infecting them with Frost Fever. Enemies are considered Frozen, but any damage other than diseases will break the ice."
];
i++;
// Name: Improved Frost Presence
rank[i] = [
"While in Blood Presence or Unholy Presence, you retain 3% stamina from Frost Presence, and damage done to you is decreased by an additional 1% in Frost Presence.",
"While in Blood Presence or Unholy Presence, you retain 6% stamina from Frost Presence, and damage done to you is decreased by an additional 2% in Frost Presence."
];
i++;
// Name:
rank[i] = [
"When dual-wielding, your Death Strikes, Obliterates, Plague Strikes, Blood Strikes and Frost Strikes have a 30% chance to also deal damage with your off-hand weapon.",
"When dual-wielding, your Death Strikes, Obliterates, Plague Strikes, Blood Strikes and Frost Strikes have a 60% chance to also deal damage with your off-hand weapon.",
"When dual-wielding, your Death Strikes, Obliterates, Plague Strikes, Blood Strikes and Frost Strikes have a 100% chance to also deal damage with your off-hand weapon."
];
i++;
// Name: Blood of the North
rank[i] = [
"Increases Blood Strike and Frost Strike damage by 3%. In addition, whenever you hit with Blood Strike or Pestilence there is a 30% chance that the Blood Rune will become a Death Rune when it activates. Death Runes count as a Blood, Frost or Unholy Rune.",
"Increases Blood Strike and Frost Strike damage by 6%. In addition, whenever you hit with Blood Strike or Pestilence there is a 60% chance that the Blood Rune will become a Death Rune when it activates. Death Runes count as a Blood, Frost or Unholy Rune.",
"Increases Blood Strike and Frost Strike damage by 10%. In addition, whenever you hit with Blood Strike or Pestilence there is a 100% chance that the Blood Rune will become a Death Rune when it activates. Death Runes count as a Blood, Frost or Unholy Rune."
];
i++;
// Name: Unbreakable Armor
rank[i] = [
"Reinforces your armor with a thick coat of ice, reducing damage from all attacks by [5 * AR * 0.01] and increasing your Strength by 25% for 20 sec. The amount of damage reduced increases as your armor increases."
];
i++;
// Name: Acclimation
rank[i] = [
"When you are hit by a spell, you have a 10% chance to boost your resistance to that type of magic for 18 sec. Stacks up to 3 times.",
"When you are hit by a spell, you have a 20% chance to boost your resistance to that type of magic for 18 sec. Stacks up to 3 times.",
"When you are hit by a spell, you have a 30% chance to boost your resistance to that type of magic for 18 sec. Stacks up to 3 times."
];
i++;
// Name: Frost Strike
rank[i] = [
"Instantly strike the enemy, causing 55% weapon damage plus 47.85 as Frost damage."
];
i++;
// Name: Guile of Gorefiend
rank[i] = [
"Increases the critical strike damage bonus of your Blood Strike, Frost Strike, Howling Blast and Obliterate abilities by 15%, and increases the duration of your Icebound Fortitude by 2 secs.",
"Increases the critical strike damage bonus of your Blood Strike, Frost Strike, Howling Blast and Obliterate abilities by 30%, and increases the duration of your Icebound Fortitude by 4 secs.",
"Increases the critical strike damage bonus of your Blood Strike, Frost Strike, Howling Blast and Obliterate abilities by 45%, and increases the duration of your Icebound Fortitude by 6 secs."
];
i++;
// Name: Tundra Stalker
rank[i] = [
"Your spells and abilities deal 3% more damage to targets infected with Frost Fever. Also increases your expertise by 1.",
"Your spells and abilities deal 6% more damage to targets infected with Frost Fever. Also increases your expertise by 2.",
"Your spells and abilities deal 9% more damage to targets infected with Frost Fever. Also increases your expertise by 3.",
"Your spells and abilities deal 12% more damage to targets infected with Frost Fever. Also increases your expertise by 4.",
"Your spells and abilities deal 15% more damage to targets infected with Frost Fever. Also increases your expertise by 5."
];
i++;
// Name: Howling Blast
rank[i] = [
"Blast the target with a frigid wind dealing 198 to 214 Frost damage to all enemies within 10 yards."
];
i++;
// Name: Vicious Strikes
rank[i] = [
"Increases the critical strike chance by 3% and critical strike damage bonus by 15% of your Plague Strike and Scourge Strike.",
"Increases the critical strike chance by 6% and critical strike damage bonus by 30% of your Plague Strike and Scourge Strike."
];
i++;
// Name: Virulence
rank[i] = [
"Increases your chance to hit with your spells by 1% and reduces the chance that your damage over time diseases can be cured by 10%.",
"Increases your chance to hit with your spells by 2% and reduces the chance that your damage over time diseases can be cured by 20%.",
"Increases your chance to hit with your spells by 3% and reduces the chance that your damage over time diseases can be cured by 30%."
];
i++;
// Name: Anticipation
rank[i] = [
"Increases your Dodge chance by 1%.",
"Increases your Dodge chance by 2%.",
"Increases your Dodge chance by 3%.",
"Increases your Dodge chance by 4%.",
"Increases your Dodge chance by 5%."
];
i++;
// Name: Epidemic
rank[i] = [
"Increases the duration of Blood Plague and Frost Fever by 3 sec.",
"Increases the duration of Blood Plague and Frost Fever by 6 sec."
];
i++;
// Name: Morbidity
rank[i] = [
"Increases the damage and healing of Death Coil by 5% and reduces the cooldown on Death and Decay by 5 sec.",
"Increases the damage and healing of Death Coil by 10% and reduces the cooldown on Death and Decay by 10 sec.",
"Increases the damage and healing of Death Coil by 15% and reduces the cooldown on Death and Decay by 15 sec."
];
i++;
// Name: Unholy Command
rank[i] = [
"Reduces the cooldown of your Death Grip ability by 5 sec.",
"Reduces the cooldown of your Death Grip ability by 10 sec."
];
i++;
// Name: Ravenous Dead
rank[i] = [
"Increases your total Strength by 1% and the contribution your Ghouls get from your Strength and Stamina by 20%.",
"Increases your total Strength by 2% and the contribution your Ghouls get from your Strength and Stamina by 40%.",
"Increases your total Strength by 3% and the contribution your Ghouls get from your Strength and Stamina by 60%"
];
i++;
// Name: Outbreak
rank[i] = [
"Increases the damage of Plague Strike by 10% and Scourge Strike by 7%.",
"Increases the damage of Plague Strike by 20% and Scourge Strike by 13%.",
"Increases the damage of Plague Strike by 30% and Scourge Strike by 20%."
];
i++;
// Name: Necrosis
rank[i] = [
"Your auto attacks deal an additional 4% Shadow damage.",
"Your auto attacks deal an additional 8% Shadow damage.",
"Your auto attacks deal an additional 12% Shadow damage.",
"Your auto attacks deal an additional 16% Shadow damage.",
"Your auto attacks deal an additional 20% Shadow damage."
];
i++;
// Name: Corpse Explosion
rank[i] = [
"Cause a corpse to explode for 166 Shadow damage to all enemies within 10 yards. Will use a nearby corpse if the target is not a corpse. Does not affect mechanical or elemental corpses."
];
i++;
// Name: On a Pale Horse
rank[i] = [
"You become as hard to stop as death itself. The duration of all Stun and Fear effects used against you is reduced by 10%, and your mounted speed is increased by 10%. This does not stack with other movement speed increasing effects.",
"You become as hard to stop as death itself. The duration of all Stun and Fear effects used against you is reduced by 20%, and your mounted speed is increased by 20%. This does not stack with other movement speed increasing effects."
];
i++;
// Name: Blood-Caked Blade
rank[i] = [
"Your auto attacks have a 10% chance to cause a Blood-Caked Strike, which hits for 25% weapon damage plus 12.5% for each of your diseases on the target.",
"Your auto attacks have a 20% chance to cause a Blood-Caked Strike, which hits for 25% weapon damage plus 12.5% for each of your diseases on the target.",
"Your auto attacks have a 30% chance to cause a Blood-Caked Strike, which hits for 25% weapon damage plus 12.5% for each of your diseases on the target."
];
i++;
// Name: Night of the Dead
rank[i] = [
"Reduces the cooldown on Raise Dead by 45 sec. and the cooldown on Army of the Dead by 5 min. Also reduces the damage your pet takes from area of effect attacks by 40%.",
"Reduces the cooldown on Raise Dead by 90 sec. and the cooldown on Army of the Dead by 10 min. Also reduces the damage your pet takes from area of effect attacks by 70%."
];
i++;
// Name: Unholy Blight
rank[i] = [
"Causes the victims of your Death Coil to be surrounded by a vile swarm of unholy insects, taking 20% of the damage done by the Death Coil over 10 sec."
];
i++;
// Name: Impurity
rank[i] = [
"The attack power bonus of your spells is increased by 4%.",
"The attack power bonus of your spells is increased by 8%.",
"The attack power bonus of your spells is increased by 12%.",
"The attack power bonus of your spells is increased by 16%.",
"Your spells receive an additional 20% benefit from your attack power."
];
i++;
// Name: Dirge
rank[i] = [
"Your Death Strike, Obliterate, Plague Strike and Scourge Strike generate 2.5 additional runic power.",
"Your Death Strike, Obliterate, Plague Strike and Scourge Strike generate 5 additional runic power."
];
i++;
// Name: Desecration
rank[i] = [
"Your Plague Strikes and Scourge Strikes cause the Desecrated Ground effect. Targets in the area are slowed by 25% by the grasping arms of the dead while standing on the unholy ground. Lasts 12 sec.",
"Your Plague Strikes and Scourge Strikes cause the Desecrated Ground effect. Targets in the area are slowed by 50% by the grasping arms of the dead while standing on the unholy ground. Lasts 12 sec."
];
i++;
// Name: Magic Suppression
rank[i] = [
"You take 2% less damage from all magic. In addition, your Anti-Magic Shell absorbs an additional 8% of spell damage.",
"You take 4% less damage from all magic. In addition, your Anti-Magic Shell absorbs an additional 16% of spell damage.",
"You take 6% less damage from all magic. In addition, your Anti-Magic Shell absorbs an additional 25% of spell damage."
];
i++;
// Name: Reaping
rank[i] = [
"Whenever you hit with Blood Strike or Pestilence there is a 33% chance that the Blood Rune becomes a Death Rune when it activates. Death Runes count as a Blood, Frost or Unholy Rune.",
"Whenever you hit with Blood Strike or Pestilence there is a 66% chance that the Blood Rune becomes a Death Rune when it activates. Death Runes count as a Blood, Frost or Unholy Rune.",
"Whenever you hit with Blood Strike or Pestilence there is a 100% chance that the Blood Rune becomes a Death Rune when it activates. Death Runes count as a Blood, Frost or Unholy Rune."
];
i++;
// Name: Master of Ghouls
rank[i] = [
"Reduces the cooldown on Raise Dead by 60 sec., and the Ghoul summoned by your Raise Dead spell is considered a pet under your control. Unlike normal Death Knight Ghouls, your pet does not have a limited duration."
];
i++;
// Name:
rank[i] = [
"Your Blood Strikes cause you to deal 1% additional damage with all attacks for the next 20 sec.",
"Your Blood Strikes cause you to deal 2% additional damage with all attacks for the next 20 sec.",
"Your Blood Strikes cause you to deal 3% additional damage with all attacks for the next 20 sec.",
"Your Blood Strikes cause you to deal 4% additional damage with all attacks for the next 20 sec.",
"Your Blood Strikes cause you to deal 5% additional damage with all attacks for the next 20 sec."
];
i++;
// Name: Anti-Magic Zone
rank[i] = [
"Places a large, stationary Anti-Magic Zone that reduces spell damage done to party or raid members inside it by 75%. The Anti-Magic Zone lasts for 10 sec or until it absorbs [10000 + 2 * AP] spell damage."
];
i++;
// Name: Improved Unholy Presence
rank[i] = [
"While in Blood Presence or Frost Presence, you retain 8% increased movement speed from Unholy Presence, and your runes finish their cooldowns 5% faster in Unholy Presence.",
"While in Blood Presence or Frost Presence, you retain 15% increased movement speed from Unholy Presence, and your runes finish their cooldowns 10% faster in Unholy Presence."
];
i++;
// Name: Ghoul Frenzy
rank[i] = [
"Grants your pet 25% haste for 30 sec and heals it for 60% of its health over the duration."
];
i++;
// Name: Crypt Fever
rank[i] = [
"Your diseases also cause Crypt Fever, which increases disease damage taken by the target by 10%.",
"Your diseases also cause Crypt Fever, which increases disease damage taken by the target by 20%.",
"Your diseases also cause Crypt Fever, which increases disease damage taken by the target by 30%."
];
i++;
// Name: Bone Shield
rank[i] = [
"The Death Knight is surrounded by 4 whirling bones. While at least 1 bone remains, <he/she> takes 20% less damage from all sources and deals 2% more damage with all attacks, spells and abilities. Each damaging attack that lands consumes 1 bone. Lasts 5 min."
];
i++;
// Name: Wandering Plague
rank[i] = [
"When your diseases damage an enemy, there is a chance equal to your melee critical strike chance that they will cause 33% additional damage to the target and all enemies within 8 yards. Ignores any target under the effect of a spell that is cancelled by taking damage.",
"When your diseases damage an enemy, there is a chance equal to your melee critical strike chance that they will cause 66% additional damage to the target and all enemies within 8 yards. Ignores any target under the effect of a spell that is cancelled by taking damage.",
"When your diseases damage an enemy, there is a chance equal to your melee critical strike chance that they will cause 100% additional damage to the target and all enemies within 8 yards. Ignores any target under the effect of a spell that is cancelled by taking damage."
];
i++;
// Name: Ebon Plaguebringer
rank[i] = [
"Your Crypt Fever morphs into Ebon Plague, which increases magic damage taken by 4% in addition to increasing disease damage taken. Improves your critical strike chance with weapons and spells by 1% at all times.",
"Your Crypt Fever morphs into Ebon Plague, which increases magic damage taken by 9% in addition to increasing disease damage taken. Improves your critical strike chance with weapons and spells by 2% at all times.",
"Your Crypt Fever morphs into Ebon Plague, which increases magic damage taken by 13% in addition to increasing disease damage taken. Improves your critical strike chance with weapons and spells by 3% at all times."
];
i++;
// Name: Scourge Strike
rank[i] = [
"An unholy strike that deals 40% of weapon damage as Shadow damage plus 135, total damage increased 10% per each of your diseases on the target."
];
i++;
// Name: Rage of Rivendare
rank[i] = [
"Your spells and abilities deal 2% more damage to targets infected with Blood Plague. Also increases your expertise by 1.",
"Your spells and abilities deal 4% more damage to targets infected with Blood Plague. Also increases your expertise by 2.",
"Your spells and abilities deal 6% more damage to targets infected with Blood Plague. Also increases your expertise by 3.",
"Your spells and abilities deal 8% more damage to targets infected with Blood Plague. Also increases your expertise by 4.",
"Your spells and abilities deal 10% more damage to targets infected with Blood Plague. Also increases your expertise by 5."
];
i++;
// Name: Summon Gargoyle
rank[i] = [
"A Gargoyle flies into the area and bombards the target with Nature damage modified by the Death Knight\'s attack power. Persists for 30 sec."
];
i++;
|