/*
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 = "mage";
var i = 0;
var t = 0;
var className = "Mage Talents";
tree[i] = "Arcane"; i++;
tree[i] = "Fire"; i++;
tree[i] = "Frost"; i++;
i = 0;
talent[i] = [0, "Arcane Subtlety", 2, 1, 1]; i++;
talent[i] = [0, "Arcane Focus", 3, 2, 1]; i++;
talent[i] = [0, "Arcane Stability", 5, 3, 1]; i++;
talent[i] = [0, "Arcane Fortitude", 3, 1, 2]; i++;
talent[i] = [0, "Magic Absorption", 2, 2, 2]; i++;
talent[i] = [0, "Arcane Concentration", 5, 3, 2]; i++;
talent[i] = [0, "Magic Attunement", 2, 1, 3]; i++;
talent[i] = [0, "Spell Impact", 3, 2, 3]; i++;
talent[i] = [0, "Student of the Mind", 3, 3, 3]; i++;
talent[i] = [0, "Focus Magic", 1, 4, 3]; i++;
talent[i] = [0, "Arcane Shielding", 2, 1, 4]; i++;
talent[i] = [0, "Improved Counterspell", 2, 2, 4]; i++;
talent[i] = [0, "Arcane Meditation", 3, 3, 4]; i++;
talent[i] = [0, "Torment the Weak", 3, 4, 4]; i++;
talent[i] = [0, "Improved Blink", 2, 1, 5]; i++;
talent[i] = [0, "Presence of Mind", 1, 2, 5]; i++;
talent[i] = [0, "Arcane Mind", 5, 4, 5]; i++;
talent[i] = [0, "Prismatic Cloak", 3, 1, 6]; i++;
talent[i] = [0, "Arcane Instability", 3, 2, 6, [getTalentID("Presence of Mind"),1]]; i++;
talent[i] = [0, "Arcane Potency", 2, 3, 6, [getTalentID("Presence of Mind"),1]]; i++;
talent[i] = [0, "Arcane Empowerment", 3, 1, 7]; i++;
talent[i] = [0, "Arcane Power", 1, 2, 7, [getTalentID("Arcane Instability"),3]]; i++;
talent[i] = [0, "Incanter's Absorption", 3, 3, 7]; i++;
talent[i] = [0, "Arcane Flows", 2, 2, 8, [getTalentID("Arcane Power"),1]]; i++;
talent[i] = [0, "Mind Mastery", 5, 3, 8]; i++;
talent[i] = [0, "Slow", 1, 2, 9]; i++;
talent[i] = [0, "Missile Barrage", 5, 3, 9]; i++;
talent[i] = [0, "Netherwind Presence", 3, 2, 10]; i++;
talent[i] = [0, "Spell Power", 2, 3, 10]; i++;
talent[i] = [0, "Arcane Barrage", 1, 2, 11]; i++;
treeStartStop[t] = i - 1;
t++;
talent[i] = [1, "Improved Fire Blast", 2, 1, 1]; i++;
talent[i] = [1, "Incineration", 3, 2, 1]; i++;
talent[i] = [1, "Improved Fireball", 5, 3, 1]; i++;
talent[i] = [1, "Ignite", 5, 1, 2]; i++;
talent[i] = [1, "Burning Determination", 2, 2, 2]; i++;
talent[i] = [1, "World in Flames", 3, 3, 2]; i++;
talent[i] = [1, "Flame Throwing", 2, 1, 3]; i++;
talent[i] = [1, "Impact", 3, 2, 3]; i++;
talent[i] = [1, "Pyroblast", 1, 3, 3]; i++;
talent[i] = [1, "Burning Soul", 2, 4, 3]; i++;
talent[i] = [1, "Improved Scorch", 3, 1, 4]; i++;
talent[i] = [1, "Molten Shields", 2, 2, 4]; i++;
talent[i] = [1, "Master of Elements", 3, 4, 4]; i++;
talent[i] = [1, "Playing with Fire", 3, 1, 5]; i++;
talent[i] = [1, "Critical Mass", 3, 2, 5]; i++;
talent[i] = [1, "Blast Wave", 1, 3, 5, [getTalentID("Pyroblast"),1]]; i++;
talent[i] = [1, "Blazing Speed", 2, 1, 6]; i++;
talent[i] = [1, "Fire Power", 5, 3, 6]; i++;
talent[i] = [1, "Pyromaniac", 3, 1, 7]; i++;
talent[i] = [1, "Combustion", 1, 2, 7, [getTalentID("Critical Mass"),3]]; i++;
talent[i] = [1, "Molten Fury", 2, 3, 7]; i++;
talent[i] = [1, "Fiery Payback", 2, 1, 8]; i++;
talent[i] = [1, "Empowered Fire", 3, 3, 8]; i++;
talent[i] = [1, "Firestarter", 2, 1, 9, [getTalentID("Dragon's Breath"),1]]; i++;
talent[i] = [1, "Dragon's Breath", 1, 2, 9, [getTalentID("Combustion"),1]]; i++;
talent[i] = [1, "Hot Streak", 3, 3, 9]; i++;
talent[i] = [1, "Burnout", 5, 2, 10]; i++;
talent[i] = [1, "Living Bomb", 1, 2, 11]; i++;
treeStartStop[t] = i - 1;
t++;
talent[i] = [2, "Frostbite", 3, 1, 1]; i++;
talent[i] = [2, "Improved Frostbolt", 5, 2, 1]; i++;
talent[i] = [2, "Ice Floes", 3, 3, 1]; i++;
talent[i] = [2, "Ice Shards", 3, 1, 2]; i++;
talent[i] = [2, "Frost Warding", 2, 2, 2]; i++;
talent[i] = [2, "Precision", 3, 3, 2]; i++;
talent[i] = [2, "Permafrost", 3, 4, 2]; i++;
talent[i] = [2, "Piercing Ice", 3, 1, 3]; i++;
talent[i] = [2, "Icy Veins", 1, 2, 3]; i++;
talent[i] = [2, "Improved Blizzard", 3, 3, 3]; i++;
talent[i] = [2, "Arctic Reach", 2, 1, 4]; i++;
talent[i] = [2, "Frost Channeling", 3, 2, 4]; i++;
talent[i] = [2, "Shatter", 3, 3, 4]; i++;
talent[i] = [2, "Cold Snap", 1, 2, 5]; i++;
talent[i] = [2, "Improved Cone of Cold", 3, 3, 5]; i++;
talent[i] = [2, "Frozen Core", 3, 4, 5]; i++;
talent[i] = [2, "Cold as Ice", 2, 1, 6, [getTalentID("Cold Snap"),1]]; i++;
talent[i] = [2, "Winter's Chill", 3, 3, 6]; i++;
talent[i] = [2, "Shattered Barrier", 2, 1, 7, [getTalentID("Ice Barrier"),1]]; i++;
talent[i] = [2, "Ice Barrier", 1, 2, 7, [getTalentID("Cold Snap"),1]]; i++;
talent[i] = [2, "Arctic Winds", 5, 3, 7]; i++;
talent[i] = [2, "Empowered Frostbolt", 2, 2, 8]; i++;
talent[i] = [2, "Fingers of Frost", 2, 3, 8]; i++;
talent[i] = [2, "Brain Freeze", 3, 1, 9]; i++;
talent[i] = [2, "Summon Water Elemental", 1, 2, 9]; i++;
talent[i] = [2, "Enduring Winter", 3, 3, 9, [getTalentID("Summon Water Elemental"),1]]; i++;
talent[i] = [2, "Chilled to the Bone", 5, 2, 10]; i++;
talent[i] = [2, "Deep Freeze", 1, 2, 11]; i++;
treeStartStop[t] = i - 1;
t++;
i = 0;
// Name: Arcane Subtlety
rank[i] = [
"Reduces the chance your helpful spells and damage over time effects will be dispelled by 15% and reduces the threat caused by your Arcane spells by 20%.",
"Reduces the chance your helpful spells and damage over time effects will be dispelled by 30% and reduces the threat caused by your Arcane spells by 40%."
];
i++;
// Name: Arcane Focus
rank[i] = [
"Increases your chance to hit and reduces the mana cost of your Arcane spells by 1%.",
"Increases your chance to hit and reduces the mana cost of your Arcane spells by 2%.",
"Increases your chance to hit and reduces the mana cost of your Arcane spells by 3%."
];
i++;
// Name: Arcane Stability
rank[i] = [
"Reduces the pushback suffered from damaging attacks while casting Arcane Missiles and Arcane Blast by 20%.",
"Reduces the pushback suffered from damaging attacks while casting Arcane Missiles and Arcane Blast by 40%.",
"Reduces the pushback suffered from damaging attacks while casting Arcane Missiles and Arcane Blast by 60%.",
"Reduces the pushback suffered from damaging attacks while casting Arcane Missiles and Arcane Blast by 80%.",
"Reduces the pushback suffered from damaging attacks while casting Arcane Missiles and Arcane Blast by 100%."
];
i++;
// Name: Arcane Fortitude
rank[i] = [
"Increases your armor by an amount equal to 50% of your Intellect.",
"Increases your armor by an amount equal to 100% of your Intellect.",
"Increases your armor by an amount equal to 150% of your Intellect."
];
i++;
// Name: Magic Absorption
rank[i] = [
"Increases all resistances by .5 per level and causes all spells you fully resist to restore 1% of your total mana. 1 sec cooldown.",
"Increases all resistances by 1 per level and causes all spells you fully resist to restore 2% of your total mana. 1 sec cooldown."
];
i++;
// Name: Arcane Concentration
rank[i] = [
"Gives you a 2% chance of entering a Clearcasting state after any damage spell hits a target. The Clearcasting state reduces the mana cost of your next damage spell by 100%.",
"Gives you a 4% chance of entering a Clearcasting state after any damage spell hits a target. The Clearcasting state reduces the mana cost of your next damage spell by 100%.",
"Gives you a 6% chance of entering a Clearcasting state after any damage spell hits a target. The Clearcasting state reduces the mana cost of your next damage spell by 100%.",
"Gives you a 8% chance of entering a Clearcasting state after any damage spell hits a target. The Clearcasting state reduces the mana cost of your next damage spell by 100%.",
"Gives you a 10% chance of entering a Clearcasting state after any damage spell hits a target. The Clearcasting state reduces the mana cost of your next damage spell by 100%."
];
i++;
// Name: Magic Attunement
rank[i] = [
"Increases the range of your Arcane spells by 3 yards and the effect of your Amplify Magic and Dampen Magic spells by 25%.",
"Increases the range of your Arcane spells by 6 yards and the effect of your Amplify Magic and Dampen Magic spells by 50%."
];
i++;
// Name: Spell Impact
rank[i] = [
"Increases the damage of your Arcane Explosion, Arcane Blast, Blast Wave, Fire Blast, Scorch, Fireball, Ice Lance and Cone of Cold spells by an additional 2%.",
"Increases the damage of your Arcane Explosion, Arcane Blast, Blast Wave, Fire Blast, Scorch, Fireball, Ice Lance and Cone of Cold spells by an additional 4%.",
"Increases the damage of your Arcane Explosion, Arcane Blast, Blast Wave, Fire Blast, Scorch, Fireball, Ice Lance and Cone of Cold spells by an additional 6%."
];
i++;
// Name: Student of the Mind
rank[i] = [
"Increases your total Spirit by 4%.",
"Increases your total Spirit by 7%.",
"Increases your total Spirit by 10%."
];
i++;
// Name: Focus Magic
rank[i] = [
"Increases the target\'s chance to critically hit with spells by 3%. When the target critically hits the caster\'s chance to critically hit with spells is increased by 3% for 10 sec. Cannot be cast on self."
];
i++;
// Name: Arcane Shielding
rank[i] = [
"Decreases the mana lost per point of damage taken when Mana Shield is active by 17% and increases the resistances granted by Mage Armor by 25%.",
"Decreases the mana lost per point of damage taken when Mana Shield is active by 33% and increases the resistances granted by Mage Armor by 50%."
];
i++;
// Name: Improved Counterspell
rank[i] = [
"Your Counterspell also silences the target for 2 sec.",
"Your Counterspell also silences the target for 4 sec."
];
i++;
// Name: Arcane Meditation
rank[i] = [
"Allows 17% of your mana regeneration to continue while casting.",
"Allows 33% of your mana regeneration to continue while casting.",
"Allows 50% of your mana regeneration to continue while casting."
];
i++;
// Name: Torment the Weak
rank[i] = [
"Your Frostbolt, Fireball, Frostfire Bolt, Arcane Missiles, Arcane Blast, and Arcane Barrage abilities deal 4% more damage to snared or slowed targets.",
"Your Frostbolt, Fireball, Frostfire Bolt, Arcane Missiles, Arcane Blast, and Arcane Barrage abilities deal 8% more damage to snared or slowed targets.",
"Your Frostbolt, Fireball, Frostfire Bolt, Arcane Missiles, Arcane Blast, and Arcane Barrage abilities deal 12% more damage to snared or slowed targets."
];
i++;
// Name: Improved Blink
rank[i] = [
"Reduces the mana cost of Blink by 25% and for 4 sec after casting your chance to be hit by all attacks and spells is reduced by 15%.",
"Reduces the mana cost of Blink by 50% and for 4 sec after casting your chance to be hit by all attacks and spells is reduced by 30%."
];
i++;
// Name: Presence of Mind
rank[i] = [
"When activated, your next Mage spell with a casting time less than 10 sec becomes an instant cast spell."
];
i++;
// Name: Arcane Mind
rank[i] = [
"Increases your total Intellect by 3%.",
"Increases your total Intellect by 6%.",
"Increases your total Intellect by 9%.",
"Increases your total Intellect by 12%.",
"Increases your total Intellect by 15%."
];
i++;
// Name: Prismatic Cloak
rank[i] = [
"Reduces all damage taken by 2% and reduces the fade time of your Invisibility spell by 1 sec.",
"Reduces all damage taken by 4% and reduces the fade time of your Invisibility spell by 2 sec.",
"Reduces all damage taken by 6% and reduces the fade time of your Invisibility spell by 3 sec."
];
i++;
// Name: Arcane Instability
rank[i] = [
"Increases the damage done by your spells and your critical strike chance by 1%.",
"Increases the damage done by your spells and your critical strike chance by 2%.",
"Increases the damage done by your spells and your critical strike chance by 3%."
];
i++;
// Name: Arcane Potency
rank[i] = [
"Increases the critical strike chance of your next damaging spell by 15% after gaining Clearcasting or Presence of Mind.",
"Increases the critical strike chance of your next damaging spell by 30% after gaining Clearcasting or Presence of Mind."
];
i++;
// Name: Arcane Empowerment
rank[i] = [
"Increases the damage of your Arcane Missiles spell by an amount equal to 15% of your spell power and the damage of your Arcane Blast by 3% of your spell power.",
"Increases the damage of your Arcane Missiles spell by an amount equal to 30% of your spell power and the damage of your Arcane Blast by 6% of your spell power.",
"Increases the damage of your Arcane Missiles spell by an amount equal to 45% of your spell power and the damage of your Arcane Blast by 9% of your spell power."
];
i++;
// Name: Arcane Power
rank[i] = [
"When activated, your spells deal 20% more damage while costing 20% more mana to cast. This effect lasts 15 sec."
];
i++;
// Name: Incanter's Absorption
rank[i] = [
"When you absorb damage your spell damage is increased by 5% of the amount absorbed for 10 sec. Total spell damage increase cannot exceed 5% of your health.",
"When you absorb damage your spell damage is increased by 10% of the amount absorbed for 10 sec. Total spell damage increase cannot exceed 5% of your health.",
"When you absorb damage your spell damage is increased by 15% of the amount absorbed for 10 sec. Total spell damage increase cannot exceed 5% of your health."
];
i++;
// Name: Arcane Flows
rank[i] = [
"Reduces the cooldown of your Presence of Mind, Arcane Power and Invisibility spells by 15% and the cooldown of your Evocation spell by 1 min.",
"Reduces the cooldown of your Presence of Mind, Arcane Power and Invisibility spells by 30% and the cooldown of your Evocation spell by 2 min."
];
i++;
// Name: Mind Mastery
rank[i] = [
"Increases spell power by 3% of your total Intellect.",
"Increases spell power by 6% of your total Intellect.",
"Increases spell power by 9% of your total Intellect.",
"Increases spell power by 12% of your total Intellect.",
"Increases spell power by 15% of your total Intellect."
];
i++;
// Name: Slow
rank[i] = [
"Reduces target\'s movement speed by 60%, increases the time between ranged attacks by 60% and increases casting time by 30%. Lasts 15 sec. Slow can only affect one target at a time."
];
i++;
// Name: Missile Barrage
rank[i] = [
"Gives your Arcane Blast, Arcane Barrage, Fireball, Frostbolt and Frostfire Bolt spells a 4% chance to reduce the channeled duration of the next Arcane Missiles spell by 2.5 secs and missiles will fire every .5 secs.",
"Gives your Arcane Blast, Arcane Barrage, Fireball, Frostbolt and Frostfire Bolt spells a 8% chance to reduce the channeled duration of the next Arcane Missiles spell by 2.5 secs and missiles will fire every .5 secs.",
"Gives your Arcane Blast, Arcane Barrage, Fireball, Frostbolt and Frostfire Bolt spells a 12% chance to reduce the channeled duration of the next Arcane Missiles spell by 2.5 secs and missiles will fire every .5 secs.",
"Gives your Arcane Blast, Arcane Barrage, Fireball, Frostbolt and Frostfire Bolt spells a 16% chance to reduce the channeled duration of the next Arcane Missiles spell by 2.5 secs and missiles will fire every .5 secs.",
"Gives your Arcane Blast, Arcane Barrage, Fireball, Frostbolt and Frostfire Bolt spells a 20% chance to reduce the channeled duration of the next Arcane Missiles spell by 2.5 secs and missiles will fire every .5 secs."
];
i++;
// Name: Netherwind Presence
rank[i] = [
"Increases your spell haste by 2%.",
"Increases your spell haste by 4%.",
"Increases your spell haste by 6%."
];
i++;
// Name: Spell Power
rank[i] = [
"Increases critical strike damage bonus of all spells by 25%.",
"Increases critical strike damage bonus of all spells by 50%."
];
i++;
// Name: Arcane Barrage
rank[i] = [
"Launches several missiles at the enemy target, causing 386 to 470 Arcane damage."
];
i++;
// Name: Improved Fire Blast
rank[i] = [
"Reduces the cooldown of your Fire Blast spell by 1 sec.",
"Reduces the cooldown of your Fire Blast spell by 2 sec."
];
i++;
// Name: Incineration
rank[i] = [
"Increases the critical strike chance of your Fire Blast, Scorch, Arcane Blast and Cone of Cold spells by 2%.",
"Increases the critical strike chance of your Fire Blast, Scorch, Arcane Blast and Cone of Cold spells by 4%.",
"Increases the critical strike chance of your Fire Blast, Scorch, Arcane Blast and Cone of Cold spells by 6%."
];
i++;
// Name: Improved Fireball
rank[i] = [
"Reduces the casting time of your Fireball spell by 0.1 sec.",
"Reduces the casting time of your Fireball spell by 0.2 sec.",
"Reduces the casting time of your Fireball spell by 0.3 sec.",
"Reduces the casting time of your Fireball spell by 0.4 sec.",
"Reduces the casting time of your Fireball spell by 0.5 sec."
];
i++;
// Name: Ignite
rank[i] = [
"Your critical strikes from Fire damage spells cause the target to burn for an additional 8% of your spell\'s damage over 4 sec.",
"Your critical strikes from Fire damage spells cause the target to burn for an additional 16% of your spell\'s damage over 4 sec.",
"Your critical strikes from Fire damage spells cause the target to burn for an additional 24% of your spell\'s damage over 4 sec.",
"Your critical strikes from Fire damage spells cause the target to burn for an additional 32% of your spell\'s damage over 4 sec.",
"Your critical strikes from Fire damage spells cause the target to burn for an additional 40% of your spell\'s damage over 4 sec."
];
i++;
// Name: Burning Determination
rank[i] = [
"When Interrupted or Silenced you have a 50% chance to become immune to both mechanics for 10 sec.",
"When Interrupted or Silenced you have a 100% chance to become immune to both mechanics for 10 sec."
];
i++;
// Name: World in Flames
rank[i] = [
"Increases the critical strike chance of your Flamestrike, Pyroblast, Blast Wave, Dragon\'s Breath, Living Bomb, Blizzard and Arcane Explosion spells by 2%.",
"Increases the critical strike chance of your Flamestrike, Pyroblast, Blast Wave, Dragon\'s Breath, Living Bomb, Blizzard and Arcane Explosion spells by 4%.",
"Increases the critical strike chance of your Flamestrike, Pyroblast, Blast Wave, Dragon\'s Breath, Living Bomb, Blizzard and Arcane Explosion spells by 6%."
];
i++;
// Name: Flame Throwing
rank[i] = [
"Increases the range of all Fire spells except Frostfire Bolt by 3 yards.",
"Increases the range of all Fire spells except Frostfire Bolt by 6 yards."
];
i++;
// Name: Impact
rank[i] = [
"Gives your damaging spells a 4% chance to cause the next Fire Blast you cast to stun the target for 2 sec.",
"Gives your damaging spells a 7% chance to cause the next Fire Blast you cast to stun the target for 2 sec.",
"Gives your damaging spells a 10% chance to cause the next Fire Blast you cast to stun the target for 2 sec."
];
i++;
// Name: Pyroblast
rank[i] = [
"Hurls an immense fiery boulder that causes 141 to 187 Fire damage and an additional 56 Fire damage over 12 sec."
];
i++;
// Name: Burning Soul
rank[i] = [
"Reduces the pushback suffered from damaging attacks while casting Fire spells by 35% and reduces the threat caused by your Fire spells by 5%.",
"Reduces the pushback suffered from damaging attacks while casting Fire spells by 70% and reduces the threat caused by your Fire spells by 10%."
];
i++;
// Name: Improved Scorch
rank[i] = [
"Increases your chance to critically hit with Scorch, Fireball and Frostfire Bolt by an additional 1% and your damaging Scorch spells have a 33% chance to cause your target to be vulnerable to spell damage, increasing spell critical strike chance against that target by 1% and lasts 30 sec. Stacks up to 5 times.",
"Increases your chance to critically hit with Scorch, Fireball and Frostfire Bolt by an additional 2% and your damaging Scorch spells have a 66% chance to cause your target to be vulnerable to spell damage, increasing spell critical strike chance against that target by 1% and lasts 30 sec. Stacks up to 5 times.",
"Increases your chance to critically hit with Scorch, Fireball and Frostfire Bolt by an additional 3% and your damaging Scorch spells have a 100% chance to cause your target to be vulnerable to spell damage, increasing spell critical strike chance against that target by 1% and lasts 30 sec. Stacks up to 5 times."
];
i++;
// Name: Molten Shields
rank[i] = [
"Causes your Fire Ward and Frost Ward spells to have a 15% chance to reflect the warded spell while active. In addition, your Molten Armor has a 50% chance to affect ranged and spell attacks.",
"Causes your Fire Ward and Frost Ward spells to have a 30% chance to reflect the warded spell while active. In addition, your Molten Armor has a 100% chance to affect ranged and spell attacks."
];
i++;
// Name: Master of Elements
rank[i] = [
"Your spell criticals will refund 10% of their base mana cost.",
"Your spell criticals will refund 20% of their base mana cost.",
"Your spell criticals will refund 30% of their base mana cost."
];
i++;
// Name: Playing with Fire
rank[i] = [
"Increases all spell damage caused by 1% and all spell damage taken by 1%.",
"Increases all spell damage caused by 2% and all spell damage taken by 2%.",
"Increases all spell damage caused by 3% and all spell damage taken by 3%."
];
i++;
// Name: Critical Mass
rank[i] = [
"Increases the critical strike chance of your Fire spells by 2%.",
"Increases the critical strike chance of your Fire spells by 4%.",
"Increases the critical strike chance of your Fire spells by 6%."
];
i++;
// Name: Blast Wave
rank[i] = [
"A wave of flame radiates outward from the caster, damaging all enemies caught within the blast for 154 to 186 Fire damage, knocking them back and Dazing them for 6 sec."
];
i++;
// Name: Blazing Speed
rank[i] = [
"Gives you a 5% chance when hit by a melee or ranged attack to increase your movement speed by 50% and dispel all movement impairing effects. This effect lasts 8 sec.",
"Gives you a 10% chance when hit by a melee or ranged attack to increase your movement speed by 50% and dispel all movement impairing effects. This effect lasts 8 sec."
];
i++;
// Name: Fire Power
rank[i] = [
"Increases the damage done by your Fire spells by 2%.",
"Increases the damage done by your Fire spells by 4%.",
"Increases the damage done by your Fire spells by 6%.",
"Increases the damage done by your Fire spells by 8%.",
"Increases the damage done by your Fire spells by 10%."
];
i++;
// Name: Pyromaniac
rank[i] = [
"Increases chance to critically hit by 1% and allows 17% of your mana regeneration to continue while casting.",
"Increases chance to critically hit by 2% and allows 33% of your mana regeneration to continue while casting.",
"Increases chance to critically hit by 3% and allows 50% of your mana regeneration to continue while casting."
];
i++;
// Name: Combustion
rank[i] = [
"When activated, this spell causes each of your Fire damage spell hits to increase your critical strike chance with Fire damage spells by 10%. This effect lasts until you have caused 3 critical strikes with Fire spells."
];
i++;
// Name: Molten Fury
rank[i] = [
"Increases damage of all spells against targets with less than 35% health by 6%.",
"Increases damage of all spells against targets with less than 35% health by 12%."
];
i++;
// Name: Fiery Payback
rank[i] = [
"When below 35% health all damage taken is reduced by 10% and your Pyroblast spell\'s cast time is reduced by 1.75 secs while the cooldown is increased by 2.5 secs. In addition, melee and ranged attacks made against you have a 5% chance to disarm your attacker\'s main hand and ranged weapons.",
"When below 35% health all damage taken is reduced by 20% and your Pyroblast spell\'s cast time is reduced by 3.5 secs while the cooldown is increased by 5 secs. In addition, melee and ranged attacks made against you have a 10% chance to disarm your attacker\'s main hand and ranged weapons."
];
i++;
// Name: Empowered Fire
rank[i] = [
"Increases the damage of your Fireball and Frostfire Bolt spells by an amount equal to 5% of your spell power. In addition, each time your Ignite talent causes damage, you have a 33% chance to regain 2% of your base mana.",
"Increases the damage of your Fireball and Frostfire Bolt spells by an amount equal to 10% of your spell power. In addition, each time your Ignite talent causes damage, you have a 67% chance to regain 2% of your base mana.",
"Increases the damage of your Fireball and Frostfire Bolt spells by an amount equal to 15% of your spell power. In addition, each time your Ignite talent causes damage, you have a 100% chance to regain 2% of your base mana."
];
i++;
// Name: Firestarter
rank[i] = [
"Your damaging Blast Wave and Dragon\'s Breath spells have a 50% chance to make your next Flamestrike spell instant cast. Lasts 10 sec.",
"Your damaging Blast Wave and Dragon\'s Breath spells have a 100% chance to make your next Flamestrike spell instant cast. Lasts 10 sec."
];
i++;
// Name: Dragon's Breath
rank[i] = [
"Targets in a cone in front of the caster take 370 to 430 Fire damage and are Disoriented for 5 sec. Any direct damaging attack will revive targets. Turns off your attack when used."
];
i++;
// Name: Hot Streak
rank[i] = [
"Any time you score 2 non-periodic spell criticals in a row using Fireball, Fire Blast, Scorch, Living Bomb, or Frostfire Bolt, you have a 33% chance the next Pyroblast spell cast within 10 sec will be instant cast.",
"Any time you score 2 non-periodic spell criticals in a row using Fireball, Fire Blast, Scorch, Living Bomb, or Frostfire Bolt, you have a 66% chance the next Pyroblast spell cast within 10 sec will be instant cast.",
"Any time you score 2 non-periodic spell criticals in a row using Fireball, Fire Blast, Scorch, Living Bomb, or Frostfire Bolt, you have a 100% chance the next Pyroblast spell cast within 10 sec will be instant cast."
];
i++;
// Name: Burnout
rank[i] = [
"Increases your spell critical damage bonus with all spells by 10% but your non-periodic spell criticals cost an additional 1% of the spell\'s cost.",
"Increases your spell critical damage bonus with all spells by 20% but your non-periodic spell criticals cost an additional 2% of the spell\'s cost.",
"Increases your spell critical damage bonus with all spells by 30% but your non-periodic spell criticals cost an additional 3% of the spell\'s cost.",
"Increases your spell critical damage bonus with all spells by 40% but your non-periodic spell criticals cost an additional 4% of the spell\'s cost.",
"Increases your spell critical damage bonus with all spells by 50% but your non-periodic spell criticals cost an additional 5% of the spell\'s cost."
];
i++;
// Name: Living Bomb
rank[i] = [
"The target becomes a Living Bomb, taking 612 Fire damage over 12 sec. After 12 sec or when the spell is dispelled, the target explodes dealing 306 Fire damage to all enemies within 10 yards."
];
i++;
// Name: Frostbite
rank[i] = [
"Gives your Chill effects a 5% chance to freeze the target for 5 sec.",
"Gives your Chill effects a 10% chance to freeze the target for 5 sec.",
"Gives your Chill effects a 15% chance to freeze the target for 5 sec."
];
i++;
// Name: Improved Frostbolt
rank[i] = [
"Reduces the casting time of your Frostbolt spell by 0.1 sec.",
"Reduces the casting time of your Frostbolt spell by 0.2 sec.",
"Reduces the casting time of your Frostbolt spell by 0.3 sec.",
"Reduces the casting time of your Frostbolt spell by 0.4 sec.",
"Reduces the casting time of your Frostbolt spell by 0.5 sec."
];
i++;
// Name: Ice Floes
rank[i] = [
"Reduces the cooldown of your Frost Nova, Cone of Cold, Ice Block and Icy Veins spells by 7%.",
"Reduces the cooldown of your Frost Nova, Cone of Cold, Ice Block and Icy Veins spells by 14%.",
"Reduces the cooldown of your Frost Nova, Cone of Cold, Ice Block and Icy Veins spells by 20%."
];
i++;
// Name: Ice Shards
rank[i] = [
"Increases the critical strike damage bonus of your Frost spells by 33%.",
"Increases the critical strike damage bonus of your Frost spells by 66%.",
"Increases the critical strike damage bonus of your Frost spells by 100%."
];
i++;
// Name: Frost Warding
rank[i] = [
"Increases the armor and resistances given by your Frost Armor and Ice Armor spells by 25%. In addition, gives your Frost Ward and Fire Ward a 15% chance to negate the warded damage spell and restore mana equal to the damage caused.",
"Increases the armor and resistances given by your Frost Armor and Ice Armor spells by 50%. In addition, gives your Frost Ward and Fire Ward a 30% chance to negate the warded damage spell and restore mana equal to the damage caused."
];
i++;
// Name: Precision
rank[i] = [
"Reduces the mana cost and increases your chance to hit with spells by 1%.",
"Reduces the mana cost and increases your chance to hit with spells by 2%.",
"Reduces the mana cost and increases your chance to hit with spells by 3%."
];
i++;
// Name: Permafrost
rank[i] = [
"Increases the duration of your Chill effects by 1 sec, reduces the target\'s speed by an additional 4%, and reduces the target\'s healing received by 7%.",
"Increases the duration of your Chill effects by 2 secs, reduces the target\'s speed by an additional 7%, and reduces the target\'s healing received by 13%.",
"Increases the duration of your Chill effects by 3 secs, reduces the target\'s speed by an additional 10%, and reduces the target\'s healing received by 20%."
];
i++;
// Name: Piercing Ice
rank[i] = [
"Increases the damage done by your Frost spells by 2%.",
"Increases the damage done by your Frost spells by 4%.",
"Increases the damage done by your Frost spells by 6%."
];
i++;
// Name: Icy Veins
rank[i] = [
"Hastens your spellcasting, increasing spell casting speed by 20% and reduces the pushback suffered from damaging attacks while casting by 100%. Lasts 20 sec."
];
i++;
// Name: Improved Blizzard
rank[i] = [
"Adds a chill effect to your Blizzard spell. This effect lowers the target\'s movement speed by 25%. Lasts 1.50 sec.",
"Adds a chill effect to your Blizzard spell. This effect lowers the target\'s movement speed by 40%. Lasts 1.50 sec.",
"Adds a chill effect to your Blizzard spell. This effect lowers the target\'s movement speed by 50%. Lasts 1.50 sec."
];
i++;
// Name: Arctic Reach
rank[i] = [
"Increases the range of your Frostbolt, Ice Lance, Deep Freeze and Blizzard spells and the radius of your Frost Nova and Cone of Cold spells by 10%.",
"Increases the range of your Frostbolt, Ice Lance, Deep Freeze and Blizzard spells and the radius of your Frost Nova and Cone of Cold spells by 20%."
];
i++;
// Name: Frost Channeling
rank[i] = [
"Reduces the mana cost of all spells by 4% and reduces the threat caused by your Frost spells by 4%.",
"Reduces the mana cost of all spells by 7% and reduces the threat caused by your Frost spells by 7%.",
"Reduces the mana cost of all spells by 10% and reduces the threat caused by your Frost spells by 10%."
];
i++;
// Name: Shatter
rank[i] = [
"Increases the critical strike chance of all your spells against frozen targets by 17%.",
"Increases the critical strike chance of all your spells against frozen targets by 34%.",
"Increases the critical strike chance of all your spells against frozen targets by 50%."
];
i++;
// Name: Cold Snap
rank[i] = [
"When activated, this spell finishes the cooldown on all Frost spells you recently cast."
];
i++;
// Name: Improved Cone of Cold
rank[i] = [
"Increases the damage dealt by your Cone of Cold spell by 15%.",
"Increases the damage dealt by your Cone of Cold spell by 25%.",
"Increases the damage dealt by your Cone of Cold spell by 35%."
];
i++;
// Name: Frozen Core
rank[i] = [
"Reduces the damage taken from all spells by 2%.",
"Reduces the damage taken from all spells by 4%.",
"Reduces the damage taken from all spells by 6%."
];
i++;
// Name: Cold as Ice
rank[i] = [
"Reduces the cooldown of your Cold Snap, Ice Barrier and Summon Water Elemental spells by 10%.",
"Reduces the cooldown of your Cold Snap, Ice Barrier and Summon Water Elemental spells by 20%."
];
i++;
// Name: Winter's Chill
rank[i] = [
"Increases your chance to critically hit with Frostbolt by an additional 1% and gives your Frost damage spells a 33% chance to apply the Winter\'s Chill effect, which increases the chance spells will critically hit the target by 1% for 15 sec. Stacks up to 5 times.",
"Increases your chance to critically hit with Frostbolt by an additional 2% and gives your Frost damage spells a 66% chance to apply the Winter\'s Chill effect, which increases the chance spells will critically hit the target by 1% for 15 sec. Stacks up to 5 times.",
"Increases your chance to critically hit with Frostbolt by an additional 3% and gives your Frost damage spells a 100% chance to apply the Winter\'s Chill effect, which increases the chance spells will critically hit the target by 1% for 15 sec. Stacks up to 5 times."
];
i++;
// Name: Shattered Barrier
rank[i] = [
"Gives your Ice Barrier spell a 50% chance to freeze all enemies within 10 yds for 8 sec when it is destroyed.",
"Gives your Ice Barrier spell a 100% chance to freeze all enemies within 10 yds for 8 sec when it is destroyed."
];
i++;
// Name: Ice Barrier
rank[i] = [
"Instantly shields you, absorbing 438 damage. Lasts 1 min. While the shield holds, spellcasting will not be delayed by damage."
];
i++;
// Name: Arctic Winds
rank[i] = [
"Increases all Frost damage you cause by 1% and reduces the chance melee and ranged attacks will hit you by 1%.",
"Increases all Frost damage you cause by 2% and reduces the chance melee and ranged attacks will hit you by 2%.",
"Increases all Frost damage you cause by 3% and reduces the chance melee and ranged attacks will hit you by 3%.",
"Increases all Frost damage you cause by 4% and reduces the chance melee and ranged attacks will hit you by 4%.",
"Increases all Frost damage you cause by 5% and reduces the chance melee and ranged attacks will hit you by 5%."
];
i++;
// Name: Empowered Frostbolt
rank[i] = [
"Increases the damage of your Frostbolt spell by an amount equal to 5% of your spell power and reduces the cast time by 0.1 sec.",
"Increases the damage of your Frostbolt spell by an amount equal to 10% of your spell power and reduces the cast time by 0.2 sec."
];
i++;
// Name: Fingers of Frost
rank[i] = [
"Gives your Chill effects a 7% chance to grant you the Fingers of Frost effect, which treats your next 2 spells cast as if the target were Frozen. Lasts 15 sec.",
"Gives your Chill effects a 15% chance to grant you the Fingers of Frost effect, which treats your next 2 spells cast as if the target were Frozen. Lasts 15 sec."
];
i++;
// Name: Brain Freeze
rank[i] = [
"Your Frost damage spells with chilling effects have a 5% chance to cause your next Fireball spell to be instant cast and cost no mana.",
"Your Frost damage spells with chilling effects have a 10% chance to cause your next Fireball spell to be instant cast and cost no mana.",
"Your Frost damage spells with chilling effects have a 15% chance to cause your next Fireball spell to be instant cast and cost no mana."
];
i++;
// Name: Summon Water Elemental
rank[i] = [
"Summon a Water Elemental to fight for the caster for 45 sec."
];
i++;
// Name: Enduring Winter
rank[i] = [
"Increases the duration of your Summon Water Elemental spell by 5 sec and your Frostbolt spell has a 33% chance to grant the Replenishment effect to up to 10 party or raid members mana regeneration equal to 0.25% of their maximum mana per second for 15 sec. This effect cannot occur more often than once every 6 sec.",
"Increases the duration of your Summon Water Elemental spell by 10 sec and your Frostbolt spell has a 66% chance to grant the Replenishment effect to up to 10 party or raid members mana regeneration equal to 0.25% of their maximum mana per second for 15 sec. This effect cannot occur more often than once every 6 sec.",
"Increases the duration of your Summon Water Elemental spell by 15 sec and your Frostbolt spell has a 100% chance to grant the Replenishment effect to up to 10 party or raid members mana regeneration equal to 0.25% of their maximum mana per second for 15 sec. This effect cannot occur more often than once every 6 sec."
];
i++;
// Name: Chilled to the Bone
rank[i] = [
"Increases the damage caused by your Frostbolt, Frostfire Bolt and Ice Lance spells by 1% and reduces the movement speed of all chilled targets by an additional 2%.",
"Increases the damage caused by your Frostbolt, Frostfire Bolt and Ice Lance spells by 2% and reduces the movement speed of all chilled targets by an additional 4%.",
"Increases the damage caused by your Frostbolt, Frostfire Bolt and Ice Lance spells by 3% and reduces the movement speed of all chilled targets by an additional 6%.",
"Increases the damage caused by your Frostbolt, Frostfire Bolt and Ice Lance spells by 4% and reduces the movement speed of all chilled targets by an additional 8%.",
"Increases the damage caused by your Frostbolt, Frostfire Bolt and Ice Lance spells by 5% and reduces the movement speed of all chilled targets by an additional 10%."
];
i++;
// Name: Deep Freeze
rank[i] = [
"Stuns the target for 5 sec. Only usable on Frozen targets."
];
i++;
|