/*
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 = "paladin";
var i = 0;
var t = 0;
var className = "Paladin Talents";
tree[i] = "Holy"; i++;
tree[i] = "Protection"; i++;
tree[i] = "Retribution"; i++;
i = 0;
talent[i] = [0, "Spiritual Focus", 5, 2, 1]; i++;
talent[i] = [0, "Seals of the Pure", 5, 3, 1]; i++;
talent[i] = [0, "Healing Light", 3, 1, 2]; i++;
talent[i] = [0, "Divine Intellect", 5, 2, 2]; i++;
talent[i] = [0, "Unyielding Faith", 2, 3, 2]; i++;
talent[i] = [0, "Aura Mastery", 1, 1, 3]; i++;
talent[i] = [0, "Illumination", 5, 2, 3]; i++;
talent[i] = [0, "Improved Lay on Hands", 2, 3, 3]; i++;
talent[i] = [0, "Improved Concentration Aura", 3, 1, 4]; i++;
talent[i] = [0, "Improved Blessing of Wisdom", 2, 3, 4]; i++;
talent[i] = [0, "Blessed Hands", 2, 4, 4]; i++;
talent[i] = [0, "Pure of Heart", 2, 1, 5]; i++;
talent[i] = [0, "Divine Favor", 1, 2, 5, [getTalentID("Illumination"),5]]; i++;
talent[i] = [0, "Sanctified Light", 3, 3, 5]; i++;
talent[i] = [0, "Purifying Power", 2, 1, 6]; i++;
talent[i] = [0, "Holy Power", 5, 3, 6]; i++;
talent[i] = [0, "Light's Grace", 3, 1, 7]; i++;
talent[i] = [0, "Holy Shock", 1, 2, 7, [getTalentID("Divine Favor"),1]]; i++;
talent[i] = [0, "Blessed Life", 3, 3, 7]; i++;
talent[i] = [0, "Sacred Cleansing", 3, 1, 8]; i++;
talent[i] = [0, "Holy Guidance", 5, 3, 8]; i++;
talent[i] = [0, "Divine Illumination", 1, 1, 9]; i++;
talent[i] = [0, "Judgements of the Pure", 5, 3, 9]; i++;
talent[i] = [0, "Infusion of Light", 2, 2, 10, [getTalentID("Holy Shock"),1]]; i++;
talent[i] = [0, "Enlightened Judgements", 2, 3, 10]; i++;
talent[i] = [0, "Beacon of Light", 1, 2, 11]; i++;
treeStartStop[t] = i - 1;
t++;
talent[i] = [1, "Divinity", 5, 2, 1]; i++;
talent[i] = [1, "Divine Strength", 5, 3, 1]; i++;
talent[i] = [1, "Stoicism", 3, 1, 2]; i++;
talent[i] = [1, "Guardian's Favor", 2, 2, 2]; i++;
talent[i] = [1, "Anticipation", 5, 3, 2]; i++;
talent[i] = [1, "Divine Sacrifice", 1, 1, 3]; i++;
talent[i] = [1, "Improved Righteous Fury", 3, 2, 3]; i++;
talent[i] = [1, "Toughness", 5, 3, 3]; i++;
talent[i] = [1, "Divine Guardian", 2, 1, 4, [getTalentID("Divine Sacrifice"),1]]; i++;
talent[i] = [1, "Improved Hammer of Justice", 2, 2, 4]; i++;
talent[i] = [1, "Improved Devotion Aura", 3, 3, 4]; i++;
talent[i] = [1, "Blessing of Sanctuary", 1, 2, 5]; i++;
talent[i] = [1, "Reckoning", 5, 3, 5]; i++;
talent[i] = [1, "Sacred Duty", 2, 1, 6]; i++;
talent[i] = [1, "One-Handed Weapon Specialization", 3, 3, 6]; i++;
talent[i] = [1, "Spiritual Attunement", 2, 1, 7]; i++;
talent[i] = [1, "Holy Shield", 1, 2, 7, [getTalentID("Blessing of Sanctuary"),1]]; i++;
talent[i] = [1, "Ardent Defender", 3, 3, 7]; i++;
talent[i] = [1, "Redoubt", 3, 1, 8]; i++;
talent[i] = [1, "Combat Expertise", 3, 3, 8]; i++;
talent[i] = [1, "Touched by the Light", 3, 1, 9]; i++;
talent[i] = [1, "Avenger's Shield", 1, 2, 9, [getTalentID("Holy Shield"),1]]; i++;
talent[i] = [1, "Guarded by the Light", 2, 3, 9]; i++;
talent[i] = [1, "Shield of the Templar", 3, 2, 10, [getTalentID("Avenger's Shield"),1]]; i++;
talent[i] = [1, "Judgements of the Just", 2, 3, 10]; i++;
talent[i] = [1, "Hammer of the Righteous", 1, 2, 11]; i++;
treeStartStop[t] = i - 1;
t++;
talent[i] = [2, "Deflection", 5, 2, 1]; i++;
talent[i] = [2, "Benediction", 5, 3, 1]; i++;
talent[i] = [2, "Improved Judgements", 2, 1, 2]; i++;
talent[i] = [2, "Heart of the Crusader", 3, 2, 2]; i++;
talent[i] = [2, "Improved Blessing of Might", 2, 3, 2]; i++;
talent[i] = [2, "Vindication", 2, 1, 3]; i++;
talent[i] = [2, "Conviction", 5, 2, 3]; i++;
talent[i] = [2, "Seal of Command", 1, 3, 3]; i++;
talent[i] = [2, "Pursuit of Justice", 2, 4, 3]; i++;
talent[i] = [2, "Eye for an Eye", 2, 1, 4]; i++;
talent[i] = [2, "Sanctity of Battle", 3, 3, 4]; i++;
talent[i] = [2, "Crusade", 3, 4, 4]; i++;
talent[i] = [2, "Two-Handed Weapon Specialization", 3, 1, 5]; i++;
talent[i] = [2, "Sanctified Retribution", 1, 3, 5, [getTalentID(""),1]]; i++;
talent[i] = [2, "Vengeance", 3, 2, 6, [getTalentID("Conviction"),5]]; i++;
talent[i] = [2, "Divine Purpose", 2, 3, 6]; i++;
talent[i] = [2, "The Art of War", 2, 1, 7]; i++;
talent[i] = [2, "Repentance", 1, 2, 7]; i++;
talent[i] = [2, "Judgements of the Wise", 3, 3, 7]; i++;
talent[i] = [2, "Fanaticism", 3, 2, 8, [getTalentID("Repentance"),1]]; i++;
talent[i] = [2, "Sanctified Wrath", 2, 3, 8]; i++;
talent[i] = [2, "Swift Retribution", 3, 1, 9]; i++;
talent[i] = [2, "Crusader Strike", 1, 2, 9]; i++;
talent[i] = [2, "Sheath of Light", 3, 3, 9]; i++;
talent[i] = [2, "Righteous Vengeance", 3, 2, 10]; i++;
talent[i] = [2, "Divine Storm", 1, 2, 11]; i++;
treeStartStop[t] = i - 1;
t++;
i = 0;
// Name: Spiritual Focus
rank[i] = [
"Reduces the pushback suffered from damaging attacks while casting Flash of Light and Holy Light by 14%.",
"Reduces the pushback suffered from damaging attacks while casting Flash of Light and Holy Light by 28%.",
"Reduces the pushback suffered from damaging attacks while casting Flash of Light and Holy Light by 42%.",
"Reduces the pushback suffered from damaging attacks while casting Flash of Light and Holy Light by 56%.",
"Reduces the pushback suffered from damaging attacks while casting Flash of Light and Holy Light by 70%."
];
i++;
// Name: Seals of the Pure
rank[i] = [
"Increases the damage done by your Seal of Righteousness, Seal of Vengeance and Seal of Corruption and their Judgement effects by 3%.",
"Increases the damage done by your Seal of Righteousness, Seal of Vengeance and Seal of Corruption and their Judgement effects by 6%.",
"Increases the damage done by your Seal of Righteousness, Seal of Vengeance and Seal of Corruption and their Judgement effects by 9%.",
"Increases the damage done by your Seal of Righteousness, Seal of Vengeance and Seal of Corruption and their Judgement effects by 12%.",
"Increases the damage done by your Seal of Righteousness, Seal of Vengeance and Seal of Corruption and their Judgement effects by 15%."
];
i++;
// Name: Healing Light
rank[i] = [
"Increases the amount healed by your Holy Light, Flash of Light and the effectiveness of Holy Shock spells by 4%.",
"Increases the amount healed by your Holy Light, Flash of Light and the effectiveness of Holy Shock spells by 8%.",
"Increases the amount healed by your Holy Light, Flash of Light and the effectiveness of Holy Shock spells by 12%."
];
i++;
// Name: Divine Intellect
rank[i] = [
"Increases your total Intellect by 2%.",
"Increases your total Intellect by 4%.",
"Increases your total Intellect by 6%.",
"Increases your total Intellect by 8%.",
"Increases your total Intellect by 10%."
];
i++;
// Name: Unyielding Faith
rank[i] = [
"Reduces the duration of all Fear and Disorient effects by 15%.",
"Reduces the duration of all Fear and Disorient effects by 30%."
];
i++;
// Name: Aura Mastery
rank[i] = [
"Causes your Concentration Aura to make all affected targets immune to Silence and Interrupt effects and improve the effect of all other auras by 100%. Lasts 10 sec."
];
i++;
// Name: Illumination
rank[i] = [
"After getting a critical effect from your Flash of Light, Holy Light, or Holy Shock heal spell you have a 20% chance to gain mana equal to 30% of the base cost of the spell.",
"After getting a critical effect from your Flash of Light, Holy Light, or Holy Shock heal spell you have a 40% chance to gain mana equal to 30% of the base cost of the spell.",
"After getting a critical effect from your Flash of Light, Holy Light, or Holy Shock heal spell you have a 60% chance to gain mana equal to 30% of the base cost of the spell.",
"After getting a critical effect from your Flash of Light, Holy Light, or Holy Shock heal spell you have a 80% chance to gain mana equal to 30% of the base cost of the spell.",
"After getting a critical effect from your Flash of Light, Holy Light, or Holy Shock heal spell you have a 100% chance to gain mana equal to 30% of the base cost of the spell."
];
i++;
// Name: Improved Lay on Hands
rank[i] = [
"Grants the target of your Lay on Hands spell 10% reduced physical damage taken for 15 sec. In addition, the cooldown for your Lay on Hands spell is reduced by 2 min.",
"Grants the target of your Lay on Hands spell 20% reduced physical damage taken for 15 sec. In addition, the cooldown for your Lay on Hands spell is reduced by 4 min."
];
i++;
// Name: Improved Concentration Aura
rank[i] = [
"Increases the effect of your Concentration Aura by an additional 5% and while any Aura is active reduces the duration of any Silence or Interrupt effect used against an affected group member by 10%. The duration reduction does not stack with any other effects.",
"Increases the effect of your Concentration Aura by an additional 10% and while any Aura is active reduces the duration of any Silence or Interrupt effect used against an affected group member by 20%. The duration reduction does not stack with any other effects.",
"Increases the effect of your Concentration Aura by an additional 15% and while any Aura is active reduces the duration of any Silence or Interrupt effect used against an affected group member by 30%. The duration reduction does not stack with any other effects."
];
i++;
// Name: Improved Blessing of Wisdom
rank[i] = [
"Increases the effect of your Blessing of Wisdom spell by 10%.",
"Increases the effect of your Blessing of Wisdom spell by 20%."
];
i++;
// Name: Blessed Hands
rank[i] = [
"Reduces the mana cost of Hand of Freedom, Hand of Sacrifice and Hand of Salvation by 15%, increases the effectiveness of Hand of Salvation by 50% and the effectiveness of Hand of Sacrifice by an additional 5%.",
"Reduces the mana cost of Hand of Freedom, Hand of Sacrifice and Hand of Salvation by 30%, increases the effectiveness of Hand of Salvation by 100% and the effectiveness of Hand of Sacrifice by an additional 10%."
];
i++;
// Name: Pure of Heart
rank[i] = [
"Reduces the duration of Curse, Disease and Poison effects by 15%.",
"Reduces the duration of Curse, Disease and Poison effects by 30%."
];
i++;
// Name: Divine Favor
rank[i] = [
"When activated, gives your next Flash of Light, Holy Light, or Holy Shock spell a 100% critical effect chance."
];
i++;
// Name: Sanctified Light
rank[i] = [
"Increases the critical effect chance of your Holy Light and Holy Shock spells by 2%.",
"Increases the critical effect chance of your Holy Light and Holy Shock spells by 4%.",
"Increases the critical effect chance of your Holy Light and Holy Shock spells by 6%."
];
i++;
// Name: Purifying Power
rank[i] = [
"Reduces the mana cost of your Cleanse, Purify and Consecration spells by 5% and reduces the cooldown of your Exorcism and Holy Wrath spells by 17%.",
"Reduces the mana cost of your Cleanse, Purify and Consecration spells by 10% and reduces the cooldown of your Exorcism and Holy Wrath spells by 33%."
];
i++;
// Name: Holy Power
rank[i] = [
"Increases the critical effect chance of your Holy spells by 1%.",
"Increases the critical effect chance of your Holy spells by 2%.",
"Increases the critical effect chance of your Holy spells by 3%.",
"Increases the critical effect chance of your Holy spells by 4%.",
"Increases the critical effect chance of your Holy spells by 5%."
];
i++;
// Name: Light's Grace
rank[i] = [
"Gives your Holy Light spell a 33% chance to reduce the cast time of your next Holy Light spell by 0.5 sec. This effect lasts 15 sec.",
"Gives your Holy Light spell a 66% chance to reduce the cast time of your next Holy Light spell by 0.5 sec. This effect lasts 15 sec.",
"Gives your Holy Light spell a 100% chance to reduce the cast time of your next Holy Light spell by 0.5 sec. This effect lasts 15 sec."
];
i++;
// Name: Holy Shock
rank[i] = [
"Blasts the target with Holy energy, causing 314 to 340 Holy damage to an enemy, or 481 to 519 healing to an ally."
];
i++;
// Name: Blessed Life
rank[i] = [
"All attacks against you have a 4% chance to cause half damage.",
"All attacks against you have a 7% chance to cause half damage.",
"All attacks against you have a 10% chance to cause half damage."
];
i++;
// Name: Sacred Cleansing
rank[i] = [
"Your Cleanse spell has a 10% chance to increase the target\'s resistance to Disease, Magic and Poison by 30% for 10 sec.",
"Your Cleanse spell has a 20% chance to increase the target\'s resistance to Disease, Magic and Poison by 30% for 10 sec.",
"Your Cleanse spell has a 30% chance to increase the target\'s resistance to Disease, Magic and Poison by 30% for 10 sec."
];
i++;
// Name: Holy Guidance
rank[i] = [
"Increases your spell power by 4% of your total Intellect.",
"Increases your spell power by 8% of your total Intellect.",
"Increases your spell power by 12% of your total Intellect.",
"Increases your spell power by 16% of your total Intellect.",
"Increases your spell power by 20% of your total Intellect."
];
i++;
// Name: Divine Illumination
rank[i] = [
"Reduces the mana cost of all spells by 50% for 15 sec."
];
i++;
// Name: Judgements of the Pure
rank[i] = [
"Increases the damage done by your Seal and Judgement spells by 5%, and your Judgement spells increase your casting and melee haste by 3% for 1 min.",
"Increases the damage done by your Seal and Judgement spells by 10%, and your Judgement spells increase your casting and melee haste by 6% for 1 min.",
"Increases the damage done by your Seal and Judgement spells by 15%, and your Judgement spells increase your casting and melee haste by 9% for 1 min.",
"Increases the damage done by your Seal and Judgement spells by 20%, and your Judgement spells increase your casting and melee haste by 12% for 1 min.",
"Increases the damage done by your Seal and Judgement spells by 25%, and your Judgement spells increase your casting and melee haste by 15% for 1 min."
];
i++;
// Name: Infusion of Light
rank[i] = [
"Your Holy Shock critical hits reduce the cast time of your next Flash of Light by 0.75 sec or increase the critical chance of your next Holy Light by 10%.",
"Your Holy Shock critical hits reduce the cast time of your next Flash of Light by 1.5 sec or increase the critical chance of your next Holy Light by 20%."
];
i++;
// Name: Enlightened Judgements
rank[i] = [
"Increases the range of your Judgement of Light and Judgement of Wisdom spells by 15 yards and increases your chance to hit by 2%.",
"Increases the range of your Judgement of Light and Judgement of Wisdom spells by 30 yards and increases your chance to hit by 4%."
];
i++;
// Name: Beacon of Light
rank[i] = [
"The target becomes a Beacon of Light to all targets within a 60 yard radius. Any heals you cast on those targets will also heal the Beacon for 100% of the amount healed. Only one target can be the Beacon of Light at a time. Lasts 1 min."
];
i++;
// Name: Divinity
rank[i] = [
"Increases all healing done by you and all healing effects on you by 1%.",
"Increases all healing done by you and all healing effects on you by 2%.",
"Increases all healing done by you and all healing effects on you by 3%.",
"Increases all healing done by you and all healing effects on you by 4%.",
"Increases all healing done by you and all healing effects on you by 5%."
];
i++;
// Name: Divine Strength
rank[i] = [
"Increases your total Strength by 3%.",
"Increases your total Strength by 6%.",
"Increases your total Strength by 9%.",
"Increases your total Strength by 12%.",
"Increases your total Strength by 15%."
];
i++;
// Name: Stoicism
rank[i] = [
"Reduces the duration of all Stun effects by an additional 10% and reduces the chance your helpful spells and damage over time effects will be dispelled by an additional 10%.",
"Reduces the duration of all Stun effects by an additional 20% and reduces the chance your helpful spells and damage over time effects will be dispelled by an additional 20%.",
"Reduces the duration of all Stun effects by an additional 30% and reduces the chance your helpful spells and damage over time effects will be dispelled by an additional 30%."
];
i++;
// Name: Guardian's Favor
rank[i] = [
"Reduces the cooldown of your Hand of Protection by 60 sec and increases the duration of your Hand of Freedom by 0.0333333 min.",
"Reduces the cooldown of your Hand of Protection by 2 min and increases the duration of your Hand of Freedom by 4 sec."
];
i++;
// Name: Anticipation
rank[i] = [
"Increases your chance to dodge by 1%.",
"Increases your chance to dodge by 2%.",
"Increases your chance to dodge by 3%.",
"Increases your chance to dodge by 4%.",
"Increases your chance to dodge by 5%."
];
i++;
// Name: Divine Sacrifice
rank[i] = [
"$s1% of all damage taken by party or raid members within $a1 yards is redirected to the Paladin (up to a maximum of $s3% of the Paladin's health). Lasts $d."
];
i++;
// Name: Improved Righteous Fury
rank[i] = [
"While Righteous Fury is active, all damage taken is reduced by 2%.",
"While Righteous Fury is active, all damage taken is reduced by 4%.",
"While Righteous Fury is active, all damage taken is reduced by 6%."
];
i++;
// Name: Toughness
rank[i] = [
"Increases your armor value from items by 2% and reduces the duration of all movement slowing effects by 6%.",
"Increases your armor value from items by 4% and reduces the duration of all movement slowing effects by 12%.",
"Increases your armor value from items by 6% and reduces the duration of all movement slowing effects by 18%.",
"Increases your armor value from items by 8% and reduces the duration of all movement slowing effects by 24%.",
"Increases your armor value from items by 10% and reduces the duration of all movement slowing effects by 30%."
];
i++;
// Name: Divine Guardian
rank[i] = [
"Improves the effectiveness of your Divine Sacrifice spell by an additional 5% and increases the duration of your Sacred Shield by 50% and the amount absorbed by 10%.",
"Improves the effectiveness of your Divine Sacrifice spell by an additional 10% and increases the duration of your Sacred Shield by 100% and the amount absorbed by 20%."
];
i++;
// Name: Improved Hammer of Justice
rank[i] = [
"Decreases the cooldown of your Hammer of Justice spell by 10 sec.",
"Decreases the cooldown of your Hammer of Justice spell by 20 sec."
];
i++;
// Name: Improved Devotion Aura
rank[i] = [
"Increases the armor bonus of your Devotion Aura by 17% and increases the amount healed on any target affected by any of your Auras by 2%.",
"Increases the armor bonus of your Devotion Aura by 34% and increases the amount healed on any target affected by any of your Auras by 4%.",
"Increases the armor bonus of your Devotion Aura by 50% and increases the amount healed on any target affected by any of your Auras by 6%."
];
i++;
// Name: Blessing of Sanctuary
rank[i] = [
"Places a Blessing on the friendly target, reducing damage taken from all sources by 3% for 10 min and increasing stamina by 10%. In addition, when the target blocks, parries, or dodges a melee attack the target will gain 2% of maximum displayed mana. Players may only have one Blessing on them per Paladin at any one time."
];
i++;
// Name: Reckoning
rank[i] = [
"Gives you a 2% chance after being hit by any damaging attack that the next 4 weapon swings within 8 sec will generate an additional attack.",
"Gives you a 4% chance after being hit by any damaging attack that the next 4 weapon swings within 8 sec will generate an additional attack.",
"Gives you a 6% chance after being hit by any damaging attack that the next 4 weapon swings within 8 sec will generate an additional attack.",
"Gives you a 8% chance after being hit by any damaging attack that the next 4 weapon swings within 8 sec will generate an additional attack.",
"Gives you a 10% chance after blocking or being hit by any damaging attack that the next 4 weapon swings within 8 sec will generate an additional attack."
];
i++;
// Name: Sacred Duty
rank[i] = [
"Increases your total Stamina by 4%, reduces the cooldown of your Divine Shield and Divine Protection spells by 30 sec.",
"Increases your total Stamina by 8%, reduces the cooldown of your Divine Shield and Divine Protection spells by 60 sec."
];
i++;
// Name: One-Handed Weapon Specialization
rank[i] = [
"Increases all damage you deal when a one-handed melee weapon is equipped by 4%.",
"Increases all damage you deal when a one-handed melee weapon is equipped by 7%.",
"Increases all damage you deal when a one-handed melee weapon is equipped by 10%."
];
i++;
// Name: Spiritual Attunement
rank[i] = [
"A passive ability that gives the Paladin mana when healed by other friendly targets\' spells. The amount of mana gained is equal to 5% of the amount healed.",
"A passive ability that gives the Paladin mana when healed by other friendly targets\' spells. The amount of mana gained is equal to 10% of the amount healed."
];
i++;
// Name: Holy Shield
rank[i] = [
"Increases chance to block by 30% for 10 sec and deals 79 Holy damage for each attack blocked while active. Each block expends a charge. 8 charges."
];
i++;
// Name: Ardent Defender
rank[i] = [
"Damage that takes you below 35% health is reduced by 10%. In addition, attacks which would otherwise kill you cause you to be healed by up to 10% of your maximum health (amount healed based on defense). This healing effect cannot occur more often than once every 2 min.",
"Damage that takes you below 35% health is reduced by 20%. In addition, attacks which would otherwise kill you cause you to be healed by up to 20% of your maximum health (amount healed based on defense). This healing effect cannot occur more often than once every 2 min.",
"Damage that takes you below 35% health is reduced by 30%. In addition, attacks which would otherwise kill you cause you to be healed by up to 30% of your maximum health (amount healed based on defense). This healing effect cannot occur more often than once every 2 min."
];
i++;
// Name: Redoubt
rank[i] = [
"Increases your block value by 10% and damaging melee and ranged attacks against you have a 10% chance to increase your chance to block by 10%. Lasts 10 sec or 5 blocks.",
"Increases your block value by 20% and damaging melee and ranged attacks against you have a 10% chance to increase your chance to block by 20%. Lasts 10 sec or 5 blocks.",
"Increases your block value by 30% and damaging melee and ranged attacks against you have a 10% chance to increase your chance to block by 30%. Lasts 10 sec or 5 blocks."
];
i++;
// Name: Combat Expertise
rank[i] = [
"Increases your expertise by 2, total Stamina and chance to critically hit by 2%.",
"Increases your expertise by 4, total Stamina and chance to critically hit by 4%.",
"Increases your expertise by 6, total Stamina and chance to critically hit by 6%."
];
i++;
// Name: Touched by the Light
rank[i] = [
"Increases your spell power by an amount equal to 10% of your Stamina and increases the amount healed by your critical heals by 10%.",
"Increases your spell power by an amount equal to 20% of your Stamina and increases the amount healed by your critical heals by 20%.",
"Increases your spell power by an amount equal to 30% of your Stamina and increases the amount healed by your critical heals by 30%."
];
i++;
// Name: Avenger's Shield
rank[i] = [
"Hurls a holy shield at the enemy, dealing [440 + 0.07 * SPH + 0.07 * AP] to [536 + 0.07 * SPH + 0.07 * AP] Holy damage, Dazing them and then jumping to additional nearby enemies. Affects 3 total targets. Lasts 10 sec."
];
i++;
// Name: Guarded by the Light
rank[i] = [
"Reduces spell damage taken by 3% and gives a 50% chance to refresh the duration of your Divine Plea when you hit an enemy. In addition, your Divine Plea spell is 50% less likely to be dispelled.",
"Reduces spell damage taken by 6% and gives a 100% chance to refresh the duration of your Divine Plea when you hit an enemy. In addition, your Divine Plea spell is 100% less likely to be dispelled."
];
i++;
// Name: Shield of the Templar
rank[i] = [
"Reduces all damage taken by 1% and grants your Avenger\'s Shield a 33% chance to silence your targets for 3 sec.",
"Reduces all damage taken by 2% and grants your Avenger\'s Shield a 66% chance to silence your targets for 3 sec.",
"Reduces all damage taken by 3% and grants your Avenger\'s Shield a 100% chance to silence your targets for 3 sec."
];
i++;
// Name: Judgements of the Just
rank[i] = [
"Reduces the cooldown of your Hammer of Justice by 10 sec, increases the duration of your Seal of Justice effect by 0.5 sec and your Judgement spells also reduce the melee attack speed of the target by 10%.",
"Reduces the cooldown of your Hammer of Justice by 20 sec, increases the duration of your Seal of Justice effect by 1 sec and your Judgement spells also reduce the melee attack speed of the target by 20%."
];
i++;
// Name: Hammer of the Righteous
rank[i] = [
"Hammer the current target and up to 2 additional nearby targets, causing 4 times your main hand damage per second as Holy damage."
];
i++;
// Name: Deflection
rank[i] = [
"Increases your Parry chance by 1%.",
"Increases your Parry chance by 2%.",
"Increases your Parry chance by 3%.",
"Increases your Parry chance by 4%.",
"Increases your Parry chance by 5%."
];
i++;
// Name: Benediction
rank[i] = [
"Reduces the mana cost of all instant cast spells by 2%.",
"Reduces the mana cost of all instant cast spells by 4%.",
"Reduces the mana cost of all instant cast spells by 6%.",
"Reduces the mana cost of all instant cast spells by 8%.",
"Reduces the mana cost of all instant cast spells by 10%."
];
i++;
// Name: Improved Judgements
rank[i] = [
"Decreases the cooldown of your Judgement spells by 1 sec.",
"Decreases the cooldown of your Judgement spells by 2 sec."
];
i++;
// Name: Heart of the Crusader
rank[i] = [
"In addition to the normal effect, your Judgement spells will also increase the critical strike chance of all attacks made against that target by an additional 1%.",
"In addition to the normal effect, your Judgement spells will also increase the critical strike chance of all attacks made against that target by an additional 2%.",
"In addition to the normal effect, your Judgement spells will also increase the critical strike chance of all attacks made against that target by an additional 3%."
];
i++;
// Name: Improved Blessing of Might
rank[i] = [
"Increases the attack power bonus of your Blessing of Might by 12%.",
"Increases the attack power bonus of your Blessing of Might by 25%."
];
i++;
// Name: Vindication
rank[i] = [
"Gives the Paladin\'s damaging attacks a chance to reduce the target\'s attack power by 23 for 10 sec.",
"Gives the Paladin\'s damaging attacks a chance to reduce the target\'s attack power by 46 for 10 sec."
];
i++;
// Name: Conviction
rank[i] = [
"Increases your chance to get a critical strike with all spells and attacks by 1%.",
"Increases your chance to get a critical strike with all spells and attacks by 2%.",
"Increases your chance to get a critical strike with all spells and attacks by 3%.",
"Increases your chance to get a critical strike with all spells and attacks by 4%.",
"Increases your chance to get a critical strike with all spells and attacks by 5%."
];
i++;
// Name: Seal of Command
rank[i] = [
"All melee attacks deal [0.36 * mw] to [0.36 * MW] additional Holy damage. Lasts 30 min.
Unleashing this Seal\'s energy will judge an enemy, instantly causing [0.19 * mw + 0.08 * AP + 0.13 * SPH] to [0.19 * MW + 0.08 * AP + 0.13 * SPH] Holy damage."
];
i++;
// Name: Pursuit of Justice
rank[i] = [
"Reduces the duration of all Disarm effects by 25% and increases movement and mounted movement speed by 8%. This does not stack with other movement speed increasing effects.",
"Reduces the duration of all Disarm effects by 50% and increases movement and mounted movement speed by 15%. This does not stack with other movement speed increasing effects."
];
i++;
// Name: Eye for an Eye
rank[i] = [
"All criticals against you cause 5% of the damage taken to the attacker as well. The damage caused by Eye for an Eye will not exceed 50% of the Paladin\'s total health.",
"All criticals against you cause 10% of the damage taken to the attacker as well. The damage caused by Eye for an Eye will not exceed 50% of the Paladin\'s total health."
];
i++;
// Name: Sanctity of Battle
rank[i] = [
"Increases your chance to critically hit with all spells and attacks by 1% and increases the damage caused by Exorcism and Crusader Strike by 5%.",
"Increases your chance to critically hit with all spells and attacks by 2% and increases the damage caused by Exorcism and Crusader Strike by 10%.",
"Increases your chance to critically hit with all spells and attacks by 3% and increases the damage caused by Exorcism and Crusader Strike by 15%."
];
i++;
// Name: Crusade
rank[i] = [
"Increases all damage caused by 1% and all damage caused against Humanoids, Demons, Undead and Elementals by an additional 1%.",
"Increases all damage caused by 2% and all damage caused against Humanoids, Demons, Undead and Elementals by an additional 2%.",
"Increases all damage caused by 3% and all damage caused against Humanoids, Demons, Undead and Elementals by an additional 3%."
];
i++;
// Name: Two-Handed Weapon Specialization
rank[i] = [
"Increases the damage you deal with two-handed melee weapons by 2%.",
"Increases the damage you deal with two-handed melee weapons by 4%.",
"Increases the damage you deal with two-handed melee weapons by 6%."
];
i++;
// Name: Sanctified Retribution
rank[i] = [
"Increases the damage caused by Retribution Aura by 50% and all damage caused by friendly targets affected by any of your Auras is increased by 3%."
];
i++;
// Name: Vengeance
rank[i] = [
"Gives you a 1% bonus to Physical and Holy damage you deal for 30 sec after dealing a critical strike from a weapon swing, spell, or ability. This effect stacks up to 3 times.",
"Gives you a 2% bonus to Physical and Holy damage you deal for 30 sec after dealing a critical strike from a weapon swing, spell, or ability. This effect stacks up to 3 times.",
"Gives you a 3% bonus to Physical and Holy damage you deal for 30 sec after dealing a critical strike from a weapon swing, spell, or ability. This effect stacks up to 3 times."
];
i++;
// Name: Divine Purpose
rank[i] = [
"Reduces your chance to be hit by spells and ranged attacks by 2% and gives your Hand of Freedom spell a 50% chance to remove any Stun effects on the target.",
"Reduces your chance to be hit by spells and ranged attacks by 4% and gives your Hand of Freedom spell a 100% chance to remove any Stun effects on the target."
];
i++;
// Name: The Art of War
rank[i] = [
"Increases the damage of your Judgement, Crusader Strike and Divine Storm abilities by 5% and when your melee attacks critically hit the cast time of your next Flash of Light or Exorcism is reduced by 0.75 sec.",
"Increases the damage of your Judgement, Crusader Strike and Divine Storm abilities by 10% and when your melee attacks critically hit your next Flash of Light or Exorcism spell becomes instant cast."
];
i++;
// Name: Repentance
rank[i] = [
"Puts the enemy target in a state of meditation, incapacitating them for up to 1 min. Any damage caused will awaken the target. Usable against Demons, Dragonkin, Giants, Humanoids and Undead."
];
i++;
// Name: Judgements of the Wise
rank[i] = [
"Your damaging Judgement spells have a 33% chance to grant the Replenishment effect to up to 10 party or raid members mana regeneration equal to 1% of their maximum mana per 5 sec for 15 sec, and to immediately grant you 25% of your base mana.",
"Your damaging Judgement spells have a 66% chance to grant the Replenishment effect to up to 10 party or raid members mana regeneration equal to 1% of their maximum mana per 5 sec for 15 sec, and to immediately grant you 25% of your base mana.",
"Your damaging Judgement spells have a 100% chance to grant the Replenishment effect to up to 10 party or raid members mana regeneration equal to 1% of their maximum mana per 5 sec for 15 sec, and to immediately grant you 25% of your base mana."
];
i++;
// Name: Fanaticism
rank[i] = [
"Increases the critical strike chance of all Judgements capable of a critical hit by 6% and reduces threat caused by all actions by 10% except when under the effects of Righteous Fury.",
"Increases the critical strike chance of all Judgements capable of a critical hit by 12% and reduces threat caused by all actions by 20% except when under the effects of Righteous Fury.",
"Increases the critical strike chance of all Judgements capable of a critical hit by 18% and reduces threat caused by all actions by 30% except when under the effects of Righteous Fury."
];
i++;
// Name: Sanctified Wrath
rank[i] = [
"Increases the critical strike chance of Hammer of Wrath by 25%, reduces the cooldown of Avenging Wrath by 30 secs and while affected by Avenging Wrath 25% of all damage caused bypasses damage reduction effects.",
"Increases the critical strike chance of Hammer of Wrath by 50%, reduces the cooldown of Avenging Wrath by 60 secs and while affected by Avenging Wrath 50% of all damage caused bypasses damage reduction effects."
];
i++;
// Name: Swift Retribution
rank[i] = [
"Your auras also increase casting, ranged and melee attack speeds by 1%.",
"Your auras also increase casting, ranged and melee attack speeds by 2%.",
"Your auras also increase casting, ranged and melee attack speeds by 3%."
];
i++;
// Name: Crusader Strike
rank[i] = [
"An instant strike that causes 75% weapon damage."
];
i++;
// Name: Sheath of Light
rank[i] = [
"Increases your spell power by an amount equal to 10% of your attack power and your critical healing spells heal the target for 20% of the healed amount over 12 seconds.",
"Increases your spell power by an amount equal to 20% of your attack power and your critical healing spells heal the target for 40% of the healed amount over 12 seconds.",
"Increases your spell power by an amount equal to 30% of your attack power and your critical healing spells heal the target for 60% of the healed amount over 12 seconds."
];
i++;
// Name: Righteous Vengeance
rank[i] = [
"When your Judgement, Crusader Strike and Divine Storm spells deal a critical strike, your target will take 10% additional damage over 8 sec.",
"When your Judgement, Crusader Strike and Divine Storm spells deal a critical strike, your target will take 20% additional damage over 8 sec.",
"When your Judgement, Crusader Strike and Divine Storm spells deal a critical strike, your target will take 30% additional damage over 8 sec."
];
i++;
// Name: Divine Storm
rank[i] = [
"An instant weapon attack that causes 110% of weapon damage to up to 4 enemies within 8 yards. The Divine Storm heals up to 3 party or raid members totaling 25% of the damage caused."
];
i++;
|