Large

FB MafiaWars Addon

By dakam Last update Jun 11, 2012 — Installed 1,360,814 times.

There are 301 previous versions of this script.

the source is over 100KB, syntax highlighting in the browser is too slow

// ==UserScript==
// @name FB MafiaWars Addon
// @namespace http://userscripts.org/scripts/show/90615
// @copyright (C) David Cabrera 2010 - 2012.
// @author David Cabrera http://www.facebook.com/edacmo
// @version 0.6.7.2
// @description http://userscripts.org/topics/101797
// @contributionURL https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=CQVWPSUMDKW5N
// @include http://facebook.mafiawars*/mwfb/remote/html_server.php*
// @include https://facebook.mafiawars*/mwfb/remote/html_server.php*
// ==/UserScript==

var AppInfo = {
    id       : 'app_10979261223',
    fanpage  : '183999591658872',
    version  : '0.6.7.2',
    name     : 'FB MafiaWars Addon',
    tag      : 'FBMWAddon',
    prefix   : 'FBMafiaWarsAddon_',
    url      : 'http://userscripts.org/scripts/show/90615',
    meta     : 'http://userscripts.org/scripts/source/90615.meta.js',
    chmext   : 'http://dascript.bravehost.com/MafiaWars/chrome/MWAddonChromeExt.crx',
    chmextID : 'llfmkjppmncfcgdebajkjnopgodlcaoe',
    contributionURL: 'https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=CQVWPSUMDKW5N'
};
// ==Script==
// @id        Logger.js
// @author    Dakam
// @memberOf  MWAddon.js
// ==Script==

var Logger = {    
    log: function(type, e) {
        var now = (new Date()).toLocaleTimeString();
        if (Util.isObject(e)) {
            e = ( e.message ? e.message : Util.toJSON(e) );
        }
        (console||unsafeWindow.console)
        .log(AppInfo.tag + ' ' + type + ' ('+now+'): '+ e);
    },
    
    debug: function(e) {
        if (UserConfig.main.debugMode !== true) {
            return;
        }
        Logger.log('DEBUG', e);
    },
    
    error: function(e) {
        Logger.log('ERROR', e);
    },
    
    object: function(o) {
        (console||unsafeWindow.console).log(o);
    }
};
// ==Script==
// @id        Global.js
// @author    Dakam
// @memberOf  MWAddon.js
// ==Script==
/**
 * Create a new City.
 * @constructor
 * @param {String} name
 * @param {Boolean} enabled
 * @param {Number} ratio
 * @param {String} currency
 * @param {String} cashIcon
 * @return {MWAddonCity}
 */
function MWAddonCity(name, enabled, ratio, currency, cashIcon) {
    this.name     = name     || 'Unknow City';
    this.ratio    = ratio    || 1;
    this.currency = currency || '$';
    this.enabled  = enabled === true;
    this.cashIcon = cashIcon;
    return this;
};
MWAddonCity.prototype.toString = function() {
    return this.name;
};

/**
 * @namespace global
 */
var global = {
    /** @type Object  */ scope            : this,
    /** @type String  */ USER_ID          : '',
    /** @type String  */ PERSON_ID        : '',
    /** @type String  */ mw_locale        : 'en_US',
    /** @type String  */ zGraphicsURL     : 'http://mwfb.static.zynga.com/mwfb/graphics/',
    /** @type Object  */ location         : null,
    /** @type Object  */ uri              : new Object(),
    /** @type Boolean */ is_unframed      : false,
    /** @type Boolean */ xd_support       : false,
    /** @type Boolean */ is_chrome        : false,
    /** @type Boolean */ has_filesystem   : false,
    
    /** Handle Ajax Requests. */
    AjaxRequests: new Object(),
    
    /** Executed popups. */
    Popups: new Object(),

    pages: {
        'story_controller'      : PageJob,
        'job_controller'        : PageJob,
        'map_controller'        : PageJobMap,
        'collection_controller' : PageCollection,
        'clan_controller'       : PageClan,
        'stats_controller'      : PageProfile 
    },
    /**
     * All available cities.
     */
    cities: {        
        each: function(callback) {
            Util.each(global.cities, function(id, city) {
                if (city.name && city.enabled === true) {
                    return callback(id, city);
                }
            });
        }
    },
    cityNames: null,
    /**
     * Get a city (safe, bugs-free)
     * @param {Object} id
     */
    city: function(id) {
        return (global.cities[id] || new MWAddonCity('Unknow City', false, 1, '$'));        
    },
    /**
     * Add a new City. Used for plugins.
     * @param {Number} id
     * @param {Object} city_data
     */
    addCity: function(id, a,b,c,d,e) {
        if (Util.isNumber(id) && Util.isString(a)) {
            global.cities[id] = new MWAddonCity(a,b,c,d,e);
        }
    },
    
    User: {
        'cash_by_city': function() {
            return unsafeWindow.User.cash_by_city;
        },
        'locale': function() {
            return unsafeWindow.User.locale;
        },
        'level': function() {
            return unsafeWindow.User.user_level||parseInt($('#user_level').text());
        },
        'health': function() {
            return unsafeWindow.User.health;
        },
        'max_health': function() {
            return unsafeWindow.User.max_health;
        },
        'energy': function() {
            return unsafeWindow.User.energy;
        },
        'max_energy': function() {
            return unsafeWindow.User.max_energy;
        },
        'stamina': function() {
            return unsafeWindow.User.stamina;
        },
        'max_stamina': function() {
            return unsafeWindow.User.max_stamina;
        },
        'expToNextLevel': function() {
            return unsafeWindow.User['exp_to_next_level'];
        },
        'expForThisLevel': function() {
            return unsafeWindow.User['exp_for_this_level'];
        },
        'cash': function() {
            return unsafeWindow.User.cash;
        },
        'reward_points': function() {
            return unsafeWindow.User.favor;
        },
        'mafia_size': function() {
            return unsafeWindow.User.mafia_size;
        }
    },
    /**
     * All groups that the user is join in.
     */
    Groups: {
        /**
         * @param {Function} callback
         */
        refresh: function(callback) {
            facebook.getGroups(function(groups) {
                UserConfig.main.groups = groups;
                UserConfig.main.save(callback);
            });
        },
        /**
         * @param {Object} selector
         * @param {Number} default_group_id
         */
        addToSelect: function(selector, default_group_id) {
            var elt = $(selector).empty();
            Util.each(UserConfig.main.groups, function(id, name) {
                elt.append(c$('option', 'value:'+id).text(name));
            });
            if ( default_group_id ) {
                $('option[value='+default_group_id+']', elt).attr('selected','selected');
            }
        }
    },
    /**
     * Load the specified controller for the current page.
     * @param {String} name Controller name
     * @param {Object} responseText Response HTML of page.
     */
    loadPage: function(name, responseText) {
        if (typeof global.pages[name] == 'function') {
            Logger.debug('loading: ' + name);
            setTimeout(function() {
                global.pages[name](responseText);
            }, 500);
        }
        else 
            Logger.debug('No handled: ' + name);
    },
    /**
     * Used to register onPageLoad event functions.
     */
    onPageLoadArray: new Object(),
    /**
     * Set a new onPageLoad event.
     * @param {Object} func
     */
    onPageLoad: function(id, func) {
        if (id && Util.isFunc(func)) {
            global.onPageLoadArray[id] = func;
        }
    },
    /**
     * Create a new scope to execute user defined scripts.
     * @param {String} id
     * @param {String} callee
     * @param {String} scope_type
     */
    createMWAddonScope: function(id, callee, scope_type) {
        return {
            'id'                    : id,
            'name'                  : callee, 
            'scope'                 : scope_type,  
            'Version'               : AppInfo.version,
            'Battlefield'           : Battlefieldv2,
            'FightClubBuyer'        : FightClubBuyer,
            'CollectAllCities'      : CollectAllCities,
            'Configuration'         : Configuration,
            'CraftManager'          : CraftManager,
            'FreeGiftsCenter'       : FreeGiftsCenter,
            'HomeFeedCenter'        : HomeFeedCenter,
            'InventoryAnalyzer'     : InventoryAnalyzer,
            'MafiaWiper'            : MafiaWiper,
            'MultiGifter'           : MultiGifter,
            'OperationsCenter'      : OperationsCenter,
            'UserLinks'             : UserLinks,
            //--
            'isGameUnframed'        : global.is_unframed,
            'CrossDomainSupported'  : global.xd_support,
            'onPageLoad'            : global.onPageLoad,
            'addMenu'               : MainMenu.add,
            'addCity'               : global.addCity,
            'execPluginByName'      : Plugins.execByName,
            'Util'                  : Util,
            'MW'                    : MW,
            'Facebook'              : facebook,
            'Ajax'                  : httpAjaxRequest,
            'httpRequest'           : httpXDRequest,
            'Base64'                : Base64,
            'Config'                : Config,
            'CreatePopup'           : PopupObject,
            'CreateTab'             : TabObject,
            'CreateDropDown'        : DropDownObject,
            'CreateConfigSettings'  : UserConfig.create,
            'ShowHelpPopup'         : showHelpPopup,
            'ShowPromptPopup'       : showPromptPopup,
            'ShowTextPopup'         : showTextPopup,
            'ShowAskPopup'          : showAskPopup,
            'ShowGroupSelection'    : showGroupSelection,
            'addFreeGift'           : UserFreeGifts.add,
            'addStream'             : UserStreams.add,
            'Collection'            : Collection,
            'TimerMessage'          : TimerMessage,
            'Countdown'             : Countdown,
            'LoadingScreen'         : loadingScreen,
            'ShortURL'              : getShortURL,
            'UnshortUrl'            : getUnshortUrl,
            'addURLService'         : URLServices.add,
            'Notification'          : MWAddonNotify.Show,
            'Toolbar': {
                'LinkWatcher'       : MWAddonToolbar.LinkWatcher
            },
            'Tooltips': {
                'applyTo'              : Tooltips.applyTo,
                'addResource'          : Tooltips.addResource,
                'addResourceContent'   : Tooltips.addResourceContent,
                'addResourceString'    : Tooltips.addResourceString,
                'getLocalizedResource' : Tooltips.getLocalizedResource
            }
        };
    }
};
// ==Script==
// @id        Config.js
// @author    Dakam
// @memberOf  MWAddon.js
// ==Script==

/** 
 * @namespace
 */
var UserConfig = {
    /**
     * Create a new configuration Object 
     * @param {Object} id the id for this configuration.
     * @param {Object} def default configuration " setting_name: value ".
     * @return {Config}
     */
    create: function(id, def) {
        return (UserConfig[id] = new Config(id, def));
    },
    /**
     * Create a new setting object from an object/array.
     * @param {Object} obj
     * @param {Object} def_value
     */
    getSettingFrom:  function(obj, def_value) {
        var new_obj = new Object();
        Util.each(obj, function(n,v) {
            if (Util.isObject(def_value)) {
                new_obj[n] = Util.clone(def_value); 
            } else {
                new_obj[n] = def_value;
            }
        });
        return new_obj;
    },
    /**
     * Return true if the key element is a valid config. 
     * @param {String} key
     * @param {Object} value
     */
    isValidProperty: function(key, value) {
        return ( String(key).charAt(0) !== '_' && !Util.isFunc(value) );
    },
    /**
     * Convert a value to the type of "def", otherwise return "def".
     * @param {Object} value
     * @param {Object} def
     */
    toType: function(value, def) {
        if (!Util.isSet(value)) {
            return def;
        }
        switch (typeof(def)) {
            case 'string'  : return String(value);
            case 'number'  : return isNaN(value=parseInt(value)) ? def : value;
            case 'boolean' : return value === true;
        }
        return (typeof(value) === typeof(def) ? value : def);
    },
    /**
     * Loops through all settings.
     * @param {Function} callback
     */
    each: function(callback) {
        Util.each(UserConfig, function(name, obj) {
            if ( obj._isConfigObject === true ) {
                callback(name, obj);  
            }
        });
    },
    /**
     * Create an Array with all Config objects.
     * @return {Array}
     */
    toArray: function() {
        var oArray = new Array();
        UserConfig.each(function(n, obj) { 
            oArray.push(obj); 
        });
        return oArray;
    },
    /**
     * Save a json settings object
     * @param {Function} callback
     */
    save: function(callback) {
        UserConfig.each(function(name, obj) {
            obj.save();
        });
    },
    /**
     * Load a saved json settings object
     * @param {Function} callback
     */
    load: function(callback) {
        UserConfig.each(function(name, obj) {
            obj.load();
        });
    },
    /**
     * Set a checkbox value.
     * @param {Object} elem
     * @param {Boolean} value
     */
    setCheckboxValue: function(elem, value) {
        if (!elem.is) {
            elem = $(elem);
        }
        if (elem.is('input:checkbox')) {
            elem[0].checked = value;
        }
        else {
            elem.toggleClass('checked', value);
        }
    },
    /**
     * Get a checkbox value
     * @param {Object} elem
     */
    getCheckboxValue: function(elem) {
        if (!elem.is) {
            elem = $(elem);
        }
        if (elem.is('input:checkbox')) {
            return elem[0].checked;
        }
        else {
            return elem.hasClass('checked');
        }
    },
    /**
     * Apply settings to elements
     */
    toDomElements: function(obj, key) {
        Util.each( obj, function(n, v) {         
            if (UserConfig.isValidProperty(n, v)) {
                var subKey = key+'_'+n;
                
                if (!UserConfig.setElementValue(subKey, v)) {
                    if (Util.isObject(v)) {
                        UserConfig.toDomElements(v, subKey); 
                    }
                }
            }
        });
    },
    /**
     * Set settings from elements
     */
    fromDomElements: function(obj, key) {
        Util.each( obj, function(n, v) {
            if (UserConfig.isValidProperty(n, v)) {
                var subKey = key+'_'+n;
                var value = UserConfig.getElementValue(subKey);
                
                if (Util.isSet(value)) {
                    obj[n] = UserConfig.toType(value, v);
                } 
                else if (Util.isObject(v)) {
                    UserConfig.fromDomElements(v, subKey);
                }
            }
        });
    },
    /**
     * Set an element value
     * @param {String} key
     * @param {Object} value
     * @return {Boolean} 
     */
    setElementValue: function(key, value) {
        var elem = e$('#' + key) || e$('#' + key.toLowerCase());
        if (elem === null) return false;
            
        if (elem.is('input:checkbox') || elem.attr('name') === 'checkbox') {
            UserConfig.setCheckboxValue(elem, value);
        }
        else if (elem.is('select[multiple]')) {
            elem.empty();
            Util.each(value, function(n, v) {
                elem.append(c$('option', 'value:'+n).text(v));
            });
        }
        else if (elem.attr('name') === 'checkboxlist') {
            Util.each(value, function(n, v) {
                UserConfig.setCheckboxValue($('*[value='+n+']', elem), v);
            });
        }
        else if (elem.is('input, select, textarea')) {
            elem.val(value);
        }
        else {
            return false;
        }
        return true;
    },
    /**
     * Get an element value
     * @param {String} key
     * @return {Object} 
     */
    getElementValue: function(key) {
        var elem = e$('#' + key) || e$('#' + key.toLowerCase());
        if (elem === null) return;

        if (elem.is('input:checkbox') || elem.attr('name') === 'checkbox') {
            return UserConfig.getCheckboxValue(elem);
        }
        else if (elem.is('select[multiple]')) {
            var opts = new Object();
            $('option',elem).each(function(index,opt) {
                opts[opt.value] = opt.text;
            });
            return opts;
        }
        else if (elem.attr('name') === 'checkboxlist') {
            var chks = new Object();
            $('.checkbox, input:checkbox', elem).each(function(i, e) {
                var name = e.getAttribute('value');
                if (name) {
                    chks[name] = UserConfig.getCheckboxValue(e);
                }
            });
            return chks;
        }
        else if (elem.is('input, select, textarea')) {
            return elem.val();
        }
        return;
    }
};

/**
 * Save/load data in json string format.<br>
 * Member with "_" at begin are considerated as private and will not be visible in "Config.each".<br>
 * @param {String} key Used for set/get from elements.
 * @param {Array} def An array object of key->value pairs.
 * @param {Bollean} allusers set true to make setting available to all users.
 * @return {Config}
 */
function Config(key, def, allusers) {
    var me = this;
    /** @private */
    this._isConfigObject = true;
    /** @private */
    this._allusers = allusers||false;
    /** @private */
    this._key = key||'';
    /** @private */
    this._saving = null;
    
    if (Util.isSet(def) && def !== null) {
        Util.each(def, function(key, value) {
            if (UserConfig.isValidProperty(key, value)) {
                me[key] = value;
            } 
        });
    }
    return this;
};
/**
 * Get an option value
 * @return {Object}
 */
Config.prototype.get = function(key) {
    return this[key];
};
/**
 * Set an option value
 * @param {String} key
 * @param {Object} new_value
 */
Config.prototype.set = function(key, new_value) {
    this[key] = UserConfig.toType(new_value, this[key]);
};
/**
 * Add a new option.
 * @param {String} name
 * @param {Object} value
 */
Config.prototype.add = function(key, value) {
    this[key] = value;
};
/**
 * Set options from the specified object recursively.
 * @param {Object} obj
 */
Config.prototype.fromObject = function(obj) {
    if (!Util.isSet(obj)) {
        return;
    }
    function apply(apply_from, apply_to) {
        Util.each(apply_from, function(name, value) {
            
            if ( UserConfig.isValidProperty(name, value) ) {
                var def = apply_to[name];
                
                if (!Util.isSet(def)) {
                    apply_to[name] = value;
                }
                else if (Util.isArray(def)) {
                    if (Util.isArray(value)) {
                        apply_to[name] = value;
                    }
                }
                else if (Util.isObject(def)) {
                    if (Util.isObject(value)) {
                        apply(value, apply_to[name]);
                    }
                }
                else {
                    apply_to[name] = UserConfig.toType(value, def);
                }                    
            }
        });
    }
    apply(obj, this);
};
/**
 * Load a json string object to set options
 * @param {String} json_string
 */
Config.prototype.fromJSON = function(json_string) {
    if (Util.isString(json_string)) {
        try {
            this.fromObject(Util.parseJSON(json_string));
        } catch (e) {
            Logger.error('fromJSON: Error parsing json configuration.');
        }
    }
};
/**
 * Loops through all settings.
 * @param {Function} callback
 */
Config.prototype.each = function(callback) {
    Util.each(this, function(n, v) {
        if ( UserConfig.isValidProperty(n, v) ) {
            callback(n, v);  
        }
    });
};
/**
 * Create an Array with all valid settings.
 * @return {Array}
 */
Config.prototype.toArray = function() {
    var oArray = new Array();
    this.each(function(n, value) {
        oArray.push(value); 
    });
    return oArray;
};
/**
 * Save a json settings object
 * @param {Function} callback
 */
Config.prototype.save = function(callback) {
    var me = this;
    var path = AppInfo[me._allusers?'prefix':'unique_prefix'] + me._key;
    var toSave = new Object();
    me.each(function(n, v) {
        toSave[n] = v;
    });
    if (me._saving && me._saving.timeout) {
        clearTimeout(me._saving.timeout);
    }
    function save() {
        var data = me._saving.data;
        me._saving = null;
        if (global.has_filesystem === true) {
            GM_setValue(path, data, callback);
        } 
        else {
            GM_setValue(path, data);
            callback&&callback();
        }
    }
    me._saving = {
        data: Util.toJSON(toSave, true),
        timeout: setTimeout(save, 100)
    };
};
/**
 * Load a saved json settings object
 * @param {Function} callback
 */
Config.prototype.load = function(callback) {
    var me = this;
    var path = AppInfo[me._allusers?'prefix':'unique_prefix'] + me._key;
    if (global.has_filesystem === true) {
        GM_getValue(path, function(data) {
            if (typeof data == 'string') {
                me.fromJSON(data);
            }
            if (typeof callback == 'function')
                callback.apply(me);
        });
    } 
    else {
        setTimeout(function() {
            me.fromJSON(GM_getValue(path, null));
            if (typeof callback == 'function')
                callback.apply(me);
        }, 0);
    }
};
/**
 * Apply settings to elements recursively.
 */
Config.prototype.toDomElements = function() {
    return UserConfig.toDomElements( this, this._key );
};
/**
 * Set settings from elements recursively.
 */
Config.prototype.fromDomElements = function() {
    return UserConfig.fromDomElements( this, this._key );
};
// ==Script==
// @id        Base64.js
// @memberOf  MWAddon.js
// ==Script==

/**
 * Base64 encode/decode class
 * @return {Base64}
 */
var Base64 = {
    _keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
    /**
     * private method for UTF-8 encoding
     *
     * @private
     * @param {String} string
     * @return {String}
     */
    _utf8_encode: function(string) {
        string = string.replace(/\r\n/g, "\n");
        var utftext = "";

        for (var n = 0; n < string.length; n++) {

            var c = string.charCodeAt(n);

            if (c < 128) {
                utftext += String.fromCharCode(c);
            }
            else if ((c > 127) && (c < 2048)) {
                utftext += String.fromCharCode((c >> 6) | 192);
                utftext += String.fromCharCode((c & 63) | 128);
            }
            else {
                utftext += String.fromCharCode((c >> 12) | 224);
                utftext += String.fromCharCode(((c >> 6) & 63) | 128);
                utftext += String.fromCharCode((c & 63) | 128);
            }
        }
        return utftext;
    },
    /**
     * private method for UTF-8 decoding
     *
     * @private
     * @param {String} utftext
     * @return {String}
     */
    _utf8_decode: function(utftext) {
        var string = "";
        var i = 0;
        var c = 0, c2 = 0, c3 = 0;

        while (i < utftext.length) {

            c = utftext.charCodeAt(i);

            if (c < 128) {
                string += String.fromCharCode(c);
                i++;
            }
            else if ((c > 191) && (c < 224)) {
                c2 = utftext.charCodeAt(i + 1);
                string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
                i += 2;
            }
            else {
                c2 = utftext.charCodeAt(i + 1);
                c3 = utftext.charCodeAt(i + 2);
                string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
                i += 3;
            }
        }
        return string;
    },
    /**
     * Public method for encode
     *
     * @param {String} input
     * @return {String}
     */
    encode: function(input) {
        var output = "";
        var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
        var i = 0;

        input = Base64._utf8_encode(input);

        while (i < input.length) {

            chr1 = input.charCodeAt(i++);
            chr2 = input.charCodeAt(i++);
            chr3 = input.charCodeAt(i++);

            enc1 = chr1 >> 2;
            enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
            enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
            enc4 = chr3 & 63;

            if (isNaN(chr2)) {
                enc3 = enc4 = 64;
            }
            else if (isNaN(chr3)) {
                enc4 = 64;
            }

            output = output +
            Base64._keyStr.charAt(enc1) +
            Base64._keyStr.charAt(enc2) +
            Base64._keyStr.charAt(enc3) +
            Base64._keyStr.charAt(enc4);

        }
        return output;
    },
    /**
     * Public method for decode
     *
     * @param {String} input
     * @return {String}
     */
    decode: function(input) {
        var output = "";
        var chr1, chr2, chr3;
        var enc1, enc2, enc3, enc4;
        var i = 0;
        
        input = input.replace(/^data:\w+\/\w+;base64,/, "");
        input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

        while (i < input.length) {

            enc1 = Base64._keyStr.indexOf(input.charAt(i++));
            enc2 = Base64._keyStr.indexOf(input.charAt(i++));
            enc3 = Base64._keyStr.indexOf(input.charAt(i++));
            enc4 = Base64._keyStr.indexOf(input.charAt(i++));

            chr1 = (enc1 << 2) | (enc2 >> 4);
            chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
            chr3 = ((enc3 & 3) << 6) | enc4;

            output = output + String.fromCharCode(chr1);

            if (enc3 != 64) {
                output = output + String.fromCharCode(chr2);
            }
            if (enc4 != 64) {
                output = output + String.fromCharCode(chr3);
            }

        }
        output = Base64._utf8_decode(output);
        return output;
    }
};
// ==Script==
// @id        Collection.js
// @author    Dakam
// @memberOf  MWAddon.js
// ==Script==

/**
 * Create a new Collection class.
 * @constructor
 * @param {Object, Array} obj
 * @return {Collection}
 */
function Collection(obj) {
    var me = this;
    var position = 0;
    var Arrs = new Object();
    var Func = new Object();
    
    function MoveEvent() {
        if (Util.isFunc(Func.Move)) {
            me.Current = Arrs.Items[position];
            Func.Move.apply(me, [position, Arrs.Keys[position], Arrs.Items[position]]);
        }
    }
    function EndEvent() {
        if (Util.isFunc(Func.End)) {
            Func.End.apply(me);
        }
    }
    /**
     * callback( index, key, item )
     */
    this.onMove = function(callback) {
        if (Util.isFunc(callback)) {
            Func.Move = callback;
        }
    };
    /**
     * callback()
     */
    this.onEnd = function(callback) {
        if (Util.isFunc(callback)) {
            Func.End = callback;
        }
    };
    // Move Actions
    this.MoveFirst = function() {
        position = 0;
        MoveEvent();
    };
    this.MoveNext = function() {
        position++;
        if ( position < Arrs.Items.length ) {
            MoveEvent();
        } else {
            EndEvent();
        }
        return null;
    };
    this.MovePrevious = function() {
        position--;
        if ( position > 0 ) {
            MoveEvent();
        }
        return null;
    };
    this.MoveLast = function() {
        position = Items.length-1;
        MoveEvent();
        EndEvent();
    };
    this.clear = function() {
        Arrs = new Object();
        Func = new Object();
    };
    this.setArray = function(arr) {
        Arrs.Keys = new Array();
        Arrs.Items = new Array();
        for (var i in arr) {
            if ( !Util.isFunc(arr[i]) ) {
                Arrs.Keys.push(i);
                Arrs.Items.push(arr[i]); 
            }
        }
        position = 0;
    };

    if ( obj ) { 
        me.setArray(obj); 
        return me;
    } else {
        return false;
    }
}
// ==Script==
// @id        Timers.js
// @author    Dakam
// @memberOf  MWAddon.js
// ==Script==

/**
 * Create a Countdown timer.<br><br> 
 * {Number}   args.delay                                                                  <br>
 * {Function} args.success                                                                <br>
 * {Function} args.stop                                                                   <br>
 * ------------ Step Event ---------------                                                <br>
 * {Function} args.step foo(delay, time)                                                  <br>
 * ---------------  OR  ------------------                                                <br>
 * {String}   args.selector 
 * Create a default step event adding the countdown text to this HTML element.            <br>
 * {String}   args.text 
 * Text to add to this default step event.                                                <br>
 * 
 * @constructor
 * @param {Object} args
 * @return {Countdown}
 */
function Countdown( args ) {
    var intId, me = this;
    
    this.started = false;
    this.arguments = args;
    
    if (!Util.isNumber(args.delay)) {
        args.delay = 10;
    }
    if (args.selector) {
        args.step = function(n, t) {
            var $s = $(args.selector);
            if ($s.length > 0) {
                $s.html((args.text ? args.text+' ':'')+t);
            }
        };
    }
    /**
     * Start this countdown.
     * @param {Number} delay Seconds
     */
    this.start = function(delay) {
        if (me.started === true) {
            if (!Util.isSet(delay)) {
                return;
            }
            if (intId) {
                clearInterval(intId);
                intId = null;
            }
        } else {
            me.started = true;
        }
        if (!delay || isNaN(delay = parseInt(delay))) {
            delay = args.delay;
        } else {
            args.delay = delay;
        }
        args.step.apply(me, [delay, Util.toDateString(delay*1000, args.format)]);
        intId = setInterval(function() {
            delay--;
            if (Util.isFunc(args.step)) {
                args.step.apply(me, [delay, Util.toDateString(delay*1000, args.format)]);
            }
            if (delay < 0) {
                me.clear();
                if (Util.isFunc(args.success)) {
                    args.success.apply(me, [args.id]);
                }
            }
        }, 1000);
    };
    /**
     * clear countdown
     */
    this.clear = function() {
        if (Util.isFunc(args.stop)) {
            args.stop.apply(me);
        }
        if ( intId ) {
            clearInterval(intId);
            intId = null;
        }
        me.started = false;
    };
    
    me.clear();
    return this;
}
/**
 * Set a timerInterval to an element.
 * @constructor
 * @param {String, Element, jQuery} selector The dom element which show the messages.
 * @return {TimerMessage}
 */
function TimerMessage(selector) {
    var me = this;
    var timerId;
    /**
     * Start the timer with the specified options.<br> - text must contain %N%
     * tag to be replaced by the countwown delay
     * 
     * @param {String} text Text to apply.
     * @param {Number} delay Countdown in seconds.
     * @param {Function} callback function to be executed when delay is 0
     * @param {Array} [params] Optional array of parameters for callback.
     */
    this.start = function(text, delay, callback, params) {
        if (e$(selector) === null) {
            return 0;
        }
        me.clear();
        
        $(selector).html(text.replace('%N%', delay)).show();
        
        timerId = setInterval(function() {
            
            $(selector).html(text.replace('%N%', delay--));
            
            if (delay < 0) {
                me.clear();
                callback.apply(me, params);
            }
            
        }, 1000);
        
        return timerId;
    };
    /**
     * Clear the timer.
     */
    this.clear = function() {
        clearInterval(timerId);
    };
    
    return this;
}
// ==Script==
// @id        MafiaMemberDB.js
// @author    Dakam
// @memberOf  MWAddon.js
// ==Script==

/**
 * MafiaMember class.
 * @param {Object} data
 * @return {MafiaMember}
 */
function MafiaMember(data) {
    this.uid          = data.uid;
    this.name         = data.name ? Base64.decode(data.name) : '';
    this.level        = data.level;
    this.first_name   = data.first_name ? Base64.decode(data.first_name) : '';
    this.profile_pic  = data.profile_pic;
    
    data = null;
    return this;
}
MafiaMember.prototype.profile = function() {
    if (this.uid) {
        return MW.getProfileLink(this.uid);
    }
    return '#';
};
/**
 * MafiaMemberCollection class.
 * @constructor
 * @param {Array} data
 * @return {MafiaMemberCollection}
 */
function MafiaMemberCollection(data) {
    var me = this;
    if (Util.isArray(data) && data.length > 0) {
        Util.each(data, function(index, member) {
            me.add(member);
        });
    }
    data = null;
    return this;
}
/**
 * @param {Number, String} pid
 * @return {Boolean}
 */
MafiaMemberCollection.prototype.exists = function(pid) {
    return Util.isSet(this[pid]);
};
/**
 * @param {Number, String} pid
 * @return {MafiaMember}
 */
MafiaMemberCollection.prototype.get = function(pid) {
    return this[pid];
};
/**
 * @return {Number}
 */
MafiaMemberCollection.prototype.length = function() {
    return Util.length(this);
};
/**
 * Parse Mafia Members data.
 * @param {MafiaMember} data
 * @return {MafiaMember, undefined}
 */
MafiaMemberCollection.prototype.add = function(member) {
    if (member && member.uid && member.name) {
        return (this[member.uid] = new MafiaMember(member));
    }
    return;
};
/**
 * @param {Function} callback function(pid, member)
 */
MafiaMemberCollection.prototype.each = function(callback) {
    Util.each(this, callback); 
};
// ==Script==
// @id        Updater.js
// @author    Dakam
// @memberOf  MWAddon.js
// ==Script==

/**
 * @namespace Updater
 */
var Updater = {
    /**
     * @private
     * @param {Object} callback
     */
    get: function(callback, bSilent) {
        httpXDRequest({
            method : 'GET', 
            url    : AppInfo.meta + '?' + (new Date()).getTime(),
            onload : function(m) { callback(m.responseText); }
        }, bSilent);
    },
    /**
     * @private
     * @param {Object} meta
     * @param {Function} callback
     */
    parse: function(meta, callback) {
        var ver = {
            version : Util.doRgx(/@version\s*(.+)/, meta).$1,
            name    : Util.doRgx(/@name\s*(.+)/,    meta).$1,
            history : Util.doRgx(/@description\s*(.+)/, meta).$1
        };
        ver.isNew = (AppInfo.name === ver.name && AppInfo.version < ver.version);
        if (ver.isNew === true) {
            ver.message = ver.name + ' has been updated to version '+ver.version+'.';
        } else {
            ver.message = 'You have already the latest version of '+ver.name+'.';
        }
        callback(ver);
    },
    /**
     * Check for a new update.
     * @param {Boolean} bForce
     */
    check: function(bForce) {
		showVerInfoPopup({
			isnew   : true,
			message : 'This verion is very old.',
			history : '<p>All MWAddon updates are now at <a href="http://mwaddon.com/">mwaddon.com</a></p><br><p>Please, Visit the page and download the MWAddon Client to get the latest updates!.</p>',
			url     : 'http://mwaddon.com/'
		});
	/*
        if (global.xd_support !== true) {
            return;
        }
        // check 
        Updater.get(function(m){
            Updater.parse(m, function(data){
                Logger.debug('Updater.check( isNew:'+data.isNew+', bForce:'+bForce+' )');
                if ( data.isNew === true || bForce === true ) {
                    showVerInfoPopup({
                        isnew   : data.isNew,
                        message : data.message,
                        history : data.history,
                        url     : AppInfo.url
                    });
                }
            });
        }, bForce !== true);
	*/	
    }
	
};
// ==Script==
// @id        Tooltips.js
// @author    Dakam
// @memberOf  MWAddon.js
// ==Script==

/**
 * @namespace
 */
var Tooltips = {
    Initialized: false,
    EditMode: false,
    timeoutID: null,
    
    saveAsPlugin: function() {
        Tooltips.EditMode = false;
        var loc = Tooltips.getLocalizedResource();
        var cn = {}, message='No data modified to save.';
        Util.each(loc.content, function(id, data) {
            if (data.edited === true) {
                if (!Util.isSet(cn[id])) cn[id] = {edited: true};
                Util.each(data, function(i,e) {
                    if (Util.isArray(e) && e[2]) {
                        cn[id][i] = e;
                    }
                });
            }
        });
        if (Util.length(cn) > 0) {
            message = 'The plugin "User Tooltips" has been saved.';
            var data = Util.toJSON(cn);
            Plugins.addNew({
                active: true,
                name  : 'User Tooltips',
                runat : Plugins.RUNAT_STARTUP,
                code  : 'MWAddon.Tooltips.addResourceContent("'+global.mw_locale+'",'+data+');'
            });
        }
        showHelpPopup({
            icon: 'info',
            message: message+'<br>Tooltip Editor has been disabled.'
        });
        Tooltips.$box.mouseleave(Tooltips.LeaveEvent);
    },
    /**
     * Get the resource and id of the tooltop from an element.
     * @param {Element, jQuery} e
     */
    getResourceFromElement: function(e) {
        if (e.attr('tooltipData')) {
            var a = Util.parseJSON(e.attr('tooltipData'));
            if (a[0] && a[1]) {
                return {
                    id: a[1],
                    content: Tooltips.getLocalizedResourceContent(a[0])
                };
            }
        } else {
            return false;
        }
    },
    /**
     * Gt a valid Tooltip object.
     * @param {Array} a
     */
    parseTooltip: function(a) {
        var tooltip = {
            title: 'Help',
            message: ''
        };
        if (a && a[0] && a[1]) {
            tooltip.title = String(a[0]);
            tooltip.message = String(a[1]);
        }
        return tooltip;
    },
    /**
     * Fix the position of the tooltip box on the top of the specified element.
     * @param {Element, jQuery} element
     */
    FixToolTipPosition: function(element) {
        var $box = Tooltips.$box;
        var offset = $(element).offset();
        var outer = {
            w: $box.outerWidth(),
            h: $box.outerHeight()
        };
		var view = {
            x: $(unsafeWindow).scrollLeft() + $(unsafeWindow).width(),
            y: $(unsafeWindow).scrollTop() + $(unsafeWindow).height()
        };
		if (view.x < offset.left + outer.w) {
            $box.css({'left':'', 'right':2});
		} else {
            $box.css({'left':offset.left, 'right':''});
        }
		$box.css('top', Math.max(offset.top - outer.h, 0));
    },
    /**
     * @private
     * Used when editing tooltips.
     */
    EditEnterEvent: function(event) {
        if (!event) {
            return;
        }
        if (Tooltips.EditMode !== true) {
            $(this).unbind('mouseenter', Tooltips.EditEnterEvent);
            return;
        }
        var me = $(event.target), data;
        if (me.is('option')) {
            me = $(event.target.parentNode);
        }
        if (!(data=Tooltips.getResourceFromElement(me))) {
            return;
        }
        var $box = Tooltips.$box;
        var $prev = $box.find('#save_tooltip');
        if ($prev.length > 0) {
            $prev.click();
        }
        $box.hide().empty().css({
            'top': 0,
            'left': 0
        });
        var autoSelect = 'a=this;setTimeout(function(){a.select();},5);';
        var tooltip = Tooltips.parseTooltip(data.content[data.id]);
        var $btn = c$('div').appendTo($box);
        var $ttl = c$('input:text').css('width',250).attr('onfocus',autoSelect);
        var $msg = c$('textarea').css({'width':375,'height':90}).attr('onfocus',autoSelect);
        
        $ttl.appendTo($btn).val(tooltip.title);
        c$('a','class:ui-button,id:save_tooltip').text('SAVE').appendTo($btn).click(function() {
            var title = $ttl.val()||'Help', text = ($msg.val()||'').replace(/\n/g,'<br>');
            if (title+text === tooltip.title+tooltip.message) {
                return;
            }
            if (Util.isString(text) && text.length > 0) {
                data.content[data.id] = [title, text, true];
                data.content.edited = true;
            }
        });
        c$('a','class:ui-button').text('Finish').appendTo($btn).click(function() {
            $box.hide().empty();
            Tooltips.saveAsPlugin();
        });
        c$('a','class:ui-button').text('CLOSE').appendTo($btn).click(function() {
            $box.hide().empty();
        });
        $msg.appendTo($box).val(tooltip.message.replace(/<br>/g,'\n'));
        $box.show();
        $ttl.focus();
    },
    /**
     * @private
     * @param {Object} e
     */
    BoxEnterEvent: function(e) {
        if (Tooltips.timeoutID) {
            clearTimeout(Tooltips.timeoutID);
            Tooltips.timeoutID = null;
        }
    },
    /**
     * @private
     * @param {Object} e
     */
    LeaveEvent: function(event) {
        if (!event || $(event.target).is('option')) {
            return;
        }
        if (Tooltips.$box) {
            if (Tooltips.timeoutID) {
                clearTimeout(Tooltips.timeoutID);
            }
            Tooltips.timeoutID = setTimeout(function() {
                Tooltips.$box.hide();
                Tooltips.timeoutID = null;
            }, 200);
        }
    },
    /**
     * Event to show the tooltip of this control. 
     * @private
     * @param {Object} e
     */
    EnterEvent: function(event) {
        if (!event) {
            return;
        }
        if (Tooltips.timeoutID) {
            clearTimeout(Tooltips.timeoutID);
            Tooltips.timeoutID = null;
        }
        var me = $(event.target), data;
        if (me.is('option')) {
            me = $(event.target.parentNode);
        }
        if (!(data=Tooltips.getResourceFromElement(me))) {
            return;
        }
        var tooltip = Tooltips.parseTooltip(data.content[data.id]);
        var $box = Tooltips.$box.hide().empty().css({
            'top': 0,
            'left': 0
        });
        c$('div','class:title mwa_res_info_icon').text(tooltip.title).appendTo($box);
        c$('div').html(tooltip.message).appendTo($box);
        $box.find('a').attr('target', '_black');
        Tooltips.FixToolTipPosition(me);
        Tooltips.timeoutID = setTimeout(function() {
            $box.show();
        }, 2000);
    },
    /**
     * Return true if the localized resource exists.
     * @param {Object} lanID
     */
    resourceExists: function(lanID) {
        return Util.isSet( Tooltips._Resources[lanID] );
    },
    /**
     * Get a resource.
     * @param {Object} lanID
     * @return {Object}
     */
    getResource: function(lanID) {
        var loc = Tooltips._Resources[lanID];
        if ( !(Util.isSet(loc) && Util.isSet(loc.content)) ) {
            return;
        }
        return loc;
    },
    /**
     * Get a resource content.
     * @param {Object} lanID
     * @param {Object} contentID
     * @return {Object}
     */
    getResourceContent: function(lanID, contentID) {
        var loc = Tooltips.getResource(lanID);
        if (loc) {
            return loc.content[contentID];
        }
        return;
    },
    /**
     * Loops through all resources.
     * @param {Object} func function(lanID, resourceObject) {...}
     */
    each: function(callback) {
        Util.each(Tooltips._Resources, callback);
    },
    /**
     * Return a new Object with lanID -> Name pairs. 
     * @return {Object}
     */
    toArray: function() {
        var oArray = new Object();
        Util.each(Tooltips._Resources, function(id, res) {
            oArray[id] = res.name;
        });
        return oArray;
    },
    /**
     * Get the localized resource.
     * @return {Object}
     */
    getLocalizedResource: function() {
        var lanID = UserConfig.main.get('toolTipLanguage');        
        if ( !Util.isSet(Tooltips._Resources[lanID]) ) {
            lanID = global.mw_locale;
        }
        return Tooltips.getResource(lanID);
    },
    /**
     * Get the localized resource content.
     * @param {Object} resID
     * @return {Object}
     */
    getLocalizedResourceContent: function(contentID) {
        var res = Tooltips.getLocalizedResource();
        if (res) {
            return res.content[contentID];
        }
        return;
    },
    /**
     * Create a new resource.
     * <pre>
     * Tooltips.addResource('lanID', {
     *     name: 'the name for this resource',
     *     description: 'a description of this resource',
     *     content: {}
     * });
     * </pre>
     * @param {String} lanID
     * @param {Object} obj
     */
    addResource: function(lanID, obj) {
        if (!Util.isObject(obj) || !(obj.name && obj.description && obj.content)) {
            return;
        }
        var res = Tooltips._Resources[lanID];
        if (!Util.isSet( res )) {
            res = (Tooltips._Resources[lanID] = new Object());
        }
        res.name = obj.name;
        res.description = obj.description;
        res.content = obj.content;
        return res;
    },
    /**
     * Add new content to an existing resource.
     * @param {Object} lanID
     * @param {Object} content
     */
    addResourceContent: function(lanID, content) {
        var loc = Tooltips._Resources[lanID];
        if ( !Util.isSet(loc) || !Util.isSet(loc.content) ) {
            loc = Tooltips.addResource(lanID, Tooltips._Resources['en_US']);
            loc.name = lanID;
        }
        Util.each(content, function(n, o) {
            if (Util.isSet(loc.content[n])) {
                Util.merge(loc.content[n], o);
            } else {
                loc.content[n] = o;
            }
        });
    },
    /**
     * Add a new string to an existing resource.
     * @param {String} lanID
     * @param {String} contentID
     * @param {String} elementID
     * @param {String} text
     */
    addResourceString: function(lanID, contentID, elementID, text) {
        var loc = Tooltips._Resources[lanID];
        if ( Util.isSet(loc) && Util.isSet(loc.content) ) {
            if (!Util.isSet(loc.content[contentID])) {
                loc.content[contentID] = new Object();
            }
            loc.content[contentID][elementID] = text;
        }
    },
    /**
     * Apply the specified localized resource content.
     * @param {Object} contentID
     */
    applyTo: function(contentID) {
        if (UserConfig.main.toolTips !== true) {
            return;
        }
        var res = Tooltips.getLocalizedResourceContent(contentID);
        
        if (Tooltips.EditMode === true) {
            if (!Util.isSet(res)) {
                res = (Tooltips.getLocalizedResource().content[contentID] = {});
            }
            Util.each(res, function(id, text) {
                if (e$('#'+contentID+' #'+id) === null) {
                    res[id] = 'UNUSED';
                }
            });
            Logger.debug('Editing tooltips for '+contentID+'.');
            $('a, input, select, textarea, .checkbox', '#'+contentID).each(function(i,e) {
                if (e.id) {
                    $(e).tooltip(contentID, e.id, true);
                }
            });
            Tooltips.$box.unbind('mouseleave', Tooltips.LeaveEvent);
            return;
        }
        if ( Util.isSet(res) ) {
            Logger.debug('Applying help tooltips to '+contentID+'.');
            Util.each(res, function(id, data) {
                if (Util.isArray(data)) {
                    var $tp = e$('#'+contentID+' #'+id);
                    if ($tp) {
                        $tp.tooltip(contentID, id);
                    }
                }
            });
        }
    },
    init: function() {
        if (Tooltips.Initialized===true) {
            return;
        }
        $.fn.extend({
            tooltip: function(content, id, edit){
                this.each(function() {
                    $(this).attr('tooltipData',Util.toJSON([content, id]))
                    .removeAttr('title').removeAttr('alt');
                });
                if (edit===true) {
                    return this.mouseover(Tooltips.EditEnterEvent);
                }
                return this
                .mouseover(Tooltips.EnterEvent)
                .mouseout(Tooltips.LeaveEvent)
                .click(Tooltips.LeaveEvent);
            }
        });
        if (!Tooltips.$box) {
            var $div = c$('div').prependTo('body');
            c$('style').append(Base64.decode(
            'I3Rvb2x0aXBfYm94IHsNCiAgICBwb3NpdGlvbjogYWJzb2x1dGU7DQogICAgd2lkdGg6IGF1dG87DQogICAgei1pbmRleDogMTAw'+
            'MDsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KICAgIHBhZGRpbmc6IDAuM2VtIDAuOGVtOw0KICAgIG1heC13aWR0aDogNDAwcHg7'+
            'DQogICAgbWluLXdpZHRoOiAxMDBweDsNCiAgICBtaW4taGVpZ2h0OiAyMHB4Ow0KICAgIGJhY2tncm91bmQtY29sb3I6ICMxQTFB'+
            'MUE7DQogICAgZm9udC1zaXplOiA5MiU7DQogICAgb3BhY2l0eTogMC44Ow0KICAgIC13ZWJraXQtYm94LXNoYWRvdzogcmdiKDE1'+
            'MywgMTUzLCAxNTMpIDBweCAwcHggMTJweDsNCiAgICAtbW96LWJveC1zaGFkb3c6IHJnYigxNTMsIDE1MywgMTUzKSAwcHggMHB4'+
            'IDEycHg7DQogICAgYm94LXNoYWRvdzogcmdiKDE1MywgMTUzLCAxNTMpIDBweCAwcHggMTJweDsNCiAgICAtd2Via2l0LWJvcmRl'+
            'ci1yYWRpdXM6IDJweDsNCiAgICAtbW96LWJvcmRlci1yYWRpdXM6IDJweDsNCiAgICBib3JkZXItcmFkaXVzOiAycHg7DQogICAg'+
            'Ym9yZGVyOiAxcHggc29saWQgd2hpdGU7DQogICAgY29sb3I6IHdoaXRlOw0KfQ0KI3Rvb2x0aXBfYm94OmhvdmVyIHsNCiAgICBi'+
            'b3gtc2hhZG93OiByZ2IoMCwgMCwgMCkgMHB4IDBweCAxMnB4Ow0KICAgIG9wYWNpdHk6IDE7DQp9DQojdG9vbHRpcF9ib3ggLnRp'+
            'dGxlIHsNCiAgICBwYWRkaW5nLWxlZnQ6IDIycHggIWltcG9ydGFudDsNCiAgICBiYWNrZ3JvdW5kLXNpemU6IDE4cHggIWltcG9y'+
            'dGFudDsNCiAgICAtbW96LWJhY2tncm91bmQtc2l6ZTogMThweCAhaW1wb3J0YW50Ow0KICAgIG1pbi1oZWlnaHQ6IDIycHggIWlt'+
            'cG9ydGFudDsNCiAgICB3aWR0aDogYXV0byAhaW1wb3J0YW50Ow0KICAgIGZvbnQtd2VpZ2h0OiBib2xkOw0KICAgIGZvbnQtc3R5'+
            'bGU6IGl0YWxpYzsNCiAgICBmb250LXNpemU6IDE1cHg7DQp9DQojdG9vbHRpcF9ib3ggaW5wdXQsICN0b29sdGlwX2JveCB0ZXh0'+
            'YXJlYSB7DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogICAgY29sb3I6ICNEMEQwRDA7DQogICAgYm9yZGVyOiAxcHggc29saWQg'+
            'Izk5OTsNCiAgICBiYWNrZ3JvdW5kLWNvbG9yOiBibGFjazsNCiAgICAtbW96LWJvcmRlci1yYWRpdXM6IDNweDsNCiAgICBib3Jk'+
            'ZXItcmFkaXVzOiAzcHg7DQogICAgLXdlYmtpdC1ib3JkZXItcmFkaXVzOiAzcHg7DQogICAgZm9udC1zaXplOiAxM3B4Ow0KICAg'+
            'IGxpbmUtaGVpZ2h0OiAxNHB4Ow0KICAgIG1pbi1oZWlnaHQ6IDEwcHg7DQp9DQojdG9vbHRpcF9ib3ggYSB7DQogICAgbWFyZ2lu'+
            'LWxlZnQ6IDRweDsNCiAgICBib3JkZXItdG9wOiAxcHggc29saWQgIzcwNzA3MDsNCiAgICBiYWNrZ3JvdW5kOiAjMWExYTFhOw0K'+
            'ICAgIGJhY2tncm91bmQ6IC13ZWJraXQtZ3JhZGllbnQobGluZWFyLCBsZWZ0IHRvcCwgbGVmdCBib3R0b20sIGZyb20oIzcwNzA3'+
            'MCksIHRvKCMyYTJhMmEpKTsNCiAgICBiYWNrZ3JvdW5kOiAtd2Via2l0LWxpbmVhci1ncmFkaWVudCh0b3AsICM3MDcwNzAsICMy'+
            'YTJhMmEpOw0KICAgIGJhY2tncm91bmQ6IC1tb3otbGluZWFyLWdyYWRpZW50KHRvcCwgIzcwNzA3MCwgIzJhMmEyYSk7DQogICAg'+
            'YmFja2dyb3VuZDogLW1zLWxpbmVhci1ncmFkaWVudCh0b3AsICM3MDcwNzAsICMyYTJhMmEpOw0KICAgIGJhY2tncm91bmQ6IC1v'+
            'LWxpbmVhci1ncmFkaWVudCh0b3AsICM3MDcwNzAsICMyYTJhMmEpOw0KICAgIHBhZGRpbmc6IDJweCA2cHg7DQogICAgbWFyZ2lu'+
            'LXJpZ2h0OiAxcHg7DQogICAgLXdlYmtpdC1ib3JkZXItcmFkaXVzOiAzcHg7DQogICAgLW1vei1ib3JkZXItcmFkaXVzOiAzcHg7'+
            'DQogICAgYm9yZGVyLXJhZGl1czogM3B4Ow0KICAgIGNvbG9yOiB3aGl0ZTsNCiAgICBmb250LXNpemU6IDExcHg7DQogICAgdGV4'+
            'dC1kZWNvcmF0aW9uOiBub25lOw0KICAgIHRleHQtdHJhbnNmb3JtOiB1cHBlcmNhc2U7DQogICAgZGlzcGxheTogaW5saW5lLWJs'+
            'b2NrOw0KICAgIGxpbmUtaGVpZ2h0OiAxNHB4Ow0KfQ0KI3Rvb2x0aXBfYm94IGE6aG92ZXIgeyAgIA0KICAgIGJhY2tncm91bmQ6'+
            'ICMyYTJhMmE7DQogICAgYmFja2dyb3VuZDogLXdlYmtpdC1ncmFkaWVudChsaW5lYXIsIGxlZnQgdG9wLCBsZWZ0IGJvdHRvbSwg'+
            'ZnJvbSgjODA4MDgwKSwgdG8oIzJhMmEyYSkpOw0KICAgIGJhY2tncm91bmQ6IC13ZWJraXQtbGluZWFyLWdyYWRpZW50KHRvcCwg'+
            'IzgwODA4MCwgIzJhMmEyYSk7DQogICAgYmFja2dyb3VuZDogLW1vei1saW5lYXItZ3JhZGllbnQodG9wLCAjODA4MDgwLCAjMmEy'+
            'YTJhKTsNCiAgICBiYWNrZ3JvdW5kOiAtbXMtbGluZWFyLWdyYWRpZW50KHRvcCwgIzgwODA4MCwgIzJhMmEyYSk7DQogICAgYmFj'+
            'a2dyb3VuZDogLW8tbGluZWFyLWdyYWRpZW50KHRvcCwgIzgwODA4MCwgIzJhMmEyYSk7DQogICAgY29sb3I6ICNkMGQwZDA7DQp9'+
            'DQojdG9vbHRpcF9ib3ggYTphY3RpdmUgew0KICAgIGNvbG9yOiB3aGl0ZTsNCn0='
            )).appendTo($div);
            $div.prependTo('body');
            Tooltips.$box = c$('div', 'id:tooltip_box').css('display','none').appendTo($div);
            Tooltips.$box.mouseover(Tooltips.BoxEnterEvent).mouseout(Tooltips.LeaveEvent);
        }
        Tooltips.Initialized = true;
    },
    /*
     * To add resurces
     * 'lang_ID': {
     *     name: 'Name of this resource.',
     *     description: 'A description of this resource.',
     *     content: {
     *         'context': {
     *             'element': {title,message}
     *         }
     *     }
     * }    
     */
    _Resources: {
        'en_US': {
            name: 'English (United States)',
            description: 'English Default Tooltip.<br><br>You can edit this Tooltip to create new ones in another language or to fix some typos.<br>Click on "Tooltip Editor" to change Tooltips to this mode.',
            content: {
                "config_popup": {
                    "give_all_permissions": ["Permissions", "Click to give all required permissions for MWAddon."],
                    "main_debugMode": ["General", "You play, keep it off and your browser will be a bit faster."],
                    "main_opt_JobRates": ["Features", "Modify the game job pages to show  energy/experince ratios that will help you to know what the best job to do if you've enabled the MWAddon Toolbar and you've access to the energy ratio needed to level up."],
                    "main_opt_CollectionPage": ["Features", "Add a anew event on all gift buttons to send collection items to use MultiGifter instead of goinf to the game gift page."],
                    "main_opt_ProfilePage": ["Features", "Add new buttons to the user's profiles like \"Promote\", \"Facebook Profile\", etc..."],
                    "main_opt_FamilyPage": ["Features", "Add two new buttons in family pages which you can use to add all family members to your Battlefield blacklist or whitelist."],
                    "main_autoheal": ["Autoheal", "When active will heal you automatically if your health goes below the specified amount.<br><br>This works only with page reload."],
                    "main_autohealwhen": ["Autoheal", "Set the health amount to activate auto-heal."],
                    "main_autohealin": ["Autoheal", "Select which City's currency you want to use to be healed."],
                    "main_shortserviceid": ["Shortening", "Select the shortener service to use.<br><br>[JSON] tagged Services will work in Firefox and Chrome (1).<br><br><span style=\"font-size:10px\">(1) Requires MWAddon Extended to be installed.</span>"],
                    "main_unshortserviceid": ["Unshortening", "Select the unshortener service to use.<br><br>[JSON] tagged Services will work in Firefox and Chrome (1).<br><br><span style=\"font-size:10px\">(1) Requires MWAddon Extended to be installed.</span>"],
                    "privacy": ["Privacy", "Select the privacy configuration you want to use when publishing."],
                    "main_publishPreview": ["General", "Use the Facebook connection used by the game to send publications.<br>It is very useful when you're getting error messages from facebook."],
                    "main_checkForUpdates": ["General", "MWAddon will search for new updates."],
                    "main_fbtimeout": ["General", "Set the timeout you want to wait until MWAddon cancel a Facebook request."],
                    "main_rqtimeout": ["General", "Set the timeout you want to wait until MWAddon cancel an internal request."],
                    "main_jstimeout": ["General", "Set the timeout you want to wait until MWAddon cancel an external request."],
                    "main_opt_Toolbar": ["Features", "Show the MWAddon Toolbar which offers you a new look for your stats and a great bookmark feature!"],
                    "main_opt_ChromeBM": ["Features", "If you're using Google Chrome:<br><br>Load your Browser bookmarks (where you add bookmarklets) and show them in your MWAddon Toolbar Bookmarks menu.<br><br><span style=\"font-size:8px\">REQUIRES MWADDON TOOLBAR ENABLED.</span>"],
                    "main_opt_Community": ["Features", "Show a bar in your top screen scrolling the most recent news from MWAddon Fan Page."],
                    "main_opt_CommunityLimit": ["Features", "Set the limit of news that MWAddon Community News bar will load."],
                    "main_autodeposit_1_active": ["Autodeposit", "When your cash goes above the specified amount will be automatically deposited in your bank."],
                    "main_autodeposit_1_amount": ["Autodeposit", "Set the amount of cash you'll keep in hand before autodeposit it."],
                    "main_autodeposit_5_amount": ["Autodeposit", "Set the amount of cash you'll keep in hand before autodeposit it."],
                    "main_autodeposit_7_amount": ["Autodeposit", "Set the amount of cash you'll keep in hand before autodeposit it."],
                    "main_autodeposit_8_amount": ["Autodeposit", "Set the amount of cash you'll keep in hand before autodeposit it."],
                    "main_autodeposit_9_amount": ["Autodeposit", "Set the amount of cash you'll keep in hand before autodeposit it."],
                    "main_autodeposit_5_active": ["Autodeposit", "When your cash goes above the specified amount will be automatically deposited in your bank."],
                    "main_autodeposit_7_active": ["Autodeposit", "When your cash goes above the specified amount will be automatically deposited in your bank."],
                    "main_autodeposit_8_active": ["Autodeposit", "When your cash goes above the specified amount will be automatically deposited in your bank."],
                    "main_autodeposit_9_active": ["Autodeposit", "When your cash goes above the specified amount will be automatically deposited in your bank."],
                    "groups_list": ["Groups", "Remove the groups you want to not be showed in your group list when you're selecting them."],
                    "main_helpNotes": ["General", "Help Notes is a new menu which contains some articles and helps about MWAddon."]
                },
                "battlefield_popup": {
                    "bfopt_traveltostartcity": ["AutoTravel", "Enable AutoTravel.<br><br>This option should be check if you want to use all other features of this same line."],
                    "bfopt_startcity": ["AutoTravel", "Select the Started city where will travel at first time."],
                    "bfopt_timercityactive": ["AutoTravel", "Enable AutoTravel Cowntdown."],
                    "bfopt_timercitymins": ["AutoTravel", "Set a timer cowntdown to travel to a different city of the selected cities in \"Travel To\""],
                    "bfopt_travelautomatic": ["Autotravel", "Check if you want to enable the Automatic mode.<br>Autotravel will travel to a different city if:<br>- You lost 5 times in a row.<br>- You lost 5 ices in a row.<br>- You fight 15 times with dead bodies.<br>- Your fightlist has no valid targets for 3 times.<br>- Your health fall down very quickly."],
                    "bfopt_healactive": ["Autoheal", "Autoheal feature.<br><br>This option should be checked if you want to use all other features of this same line."],
                    "bfopt_healcity": ["Autoheal", "Select the city you want to use for Autoheal.<br>The selected city's currency will be used."],
                    "bfopt_healbelow": ["Autoheal", "Autoheal will heal you when your health goes below the specified amount."],
                    "bfopt_healminsta": ["Autoheal", "Autoheal will not heal you if your stamina goes below the specified amount."],
                    "bfopt_healattacking": ["Autoheal", "Check if you want to use Autoheal even when attacking the same player.<br>Disable it for \"brute\" mode."],
                    "bfopt_healtimer": ["Autoheal", "Set the time you need to wait before you heal again after you've been healed.<br>Set It to heal you just after healing is available, saving time instead of trying to heal every few seconds.<br>Normally it's 60 seconds for all players, you may want to set it to 61 sec's."],
                    "bfopt_rapidfireactive": ["Rapid Fire", "Rapid Fire feature.<br>Power Attack must be checked to enable Rapid Fire.<br><br>This feature allow you to send multiples attacks in a short time giving you more chance to knock down your opponent."],
                    "bfopt_rapidfirehealth": ["Rapid Fire", "Select when you want to start Rapid Fire.<br>Normally you want to start Radpi Fire when your opponent has a low health, but you can set it at maximum for a fast stamina burn."],
                    "bfopt_rapidfireattacks": ["Rapid Fire", "This value will stop Rapid Fire, forcing to start a new one if the opponent was not iced.<br>Low values are recommended for slow connections."],
                    "bfopt_rapidfiretiming": ["Rapid Fire", "Set the Rapid Fire Timing.<br>Lowering this value attack faster. but will give you more errors.<br>A good success rate will give you more possibilites to get an ice. So, faster don't mean better."],
                    "bfopt_rapidfireautotiming": ["Rapid Fire", "Rapid Fire Autotiming.<br>This feature will ensure you to get a really good success ratio in your attacks, that means that you'll have more chances to ice the opponent.<br>Good success rates will also burn your stamina faster because it will need to send less requests.<br><br>"],
                    "bfopt_attackusemax": ["Attacks", "Check to use Maximum Attacks.<br>Maximum Attacks will be calculated after some attacks, then if the required attacks to ice your enemy are more than the specified value, the foe will be skipped."],
                    "bfopt_attackmax": ["Attacks", "Set the Maximum Attacks you would use on the same opponent.<br>Opponents that requires more than this value will be skipped."],
                    "bfopt_attackretries": ["Attacks", "Set the Maximum Retries you want to perform before skip an opponent when server return an invalid response."],
                    "bfopt_attackpwr": ["Attacks", "Check to enable Power Attack.<br>Use it if you want to use Rapid Fire."],
                    "bfopt_attacktonpc": ["Attacks", "Check it if you want to fight non-player opponents added by Mafia Wars when you have an active mission availible.<br>You must also configure \"Start City\" to required city where they are hideing."],
                    "bfopt_attackuseperfoe": ["Attacks", "Check to enable a limit of attacks per foe.<br>The difference between Maximum Attacks is that this will skip the foe just after you've reached the specified limit."],
                    "bfopt_attackperfoe": ["Attacks", "Set the limit of attacks per opponent."],
                    "bfopt_attkdelayusea": ["Attacks", "A delay between attacks to same player.<br>Uncheck it if you're going for ices."],
                    "bfopt_attkdelaya": ["Attacks", "Set the attack delay for same player."],
                    "bfopt_attkdelayuseb": ["Attacks", "A delay when you're ready to attack a new player.<br>This has low effect when going for ices, recommended to be checked."],
                    "bfopt_attkdelayb": ["Attacks", "Set the ready to attack delay.<br>Recomended to use at least 1 second."],
                    "bfopt_rediceactive": ["Red ices", "Keep attacking the player when you had lost.<br>The Red Ice term means that you can get an ice while losing the fights.<br>You will lose experience points, health and loot when attacking in this way."],
                    "bfopt_redicemaxattk": ["Red ices", "Set maximum attacks to enable red ice.<br>It only works if \"RedIce\" is checked."],
                    "bfopt_rediceafterwon": ["Red ices", "Sometimes you could lose a fight after you have won, your opponent suddenly got stronger by a boost or leveling up.<br>Check this to allow Red Ice mode for the current opponent after winning at least the first attack.<br>\"This function can work Individually\"."],
                    "bfopt_attackrevenge": ["Revenges", "Take your revenge from thieves.<br>This feature allows you to AutoAttack the thief that has stolen your ice."],
                    "bfopt_attackrevengefilter": ["Revenges", "Select the revenge filter.<br>- \"Attack everyone\": attack any thief without use any filter (skip options still used).<br>- \"Attack any mafia/level\": attack any thief using filters but not the mafia/level ones.<br>- \"Use all filters\": attack the thief only if it passed all filters."],
                    "bfopt_timerrivalsactive": ["Rivals", "Enable to load your rivals list after some time and attack lived targets."],
                    "bfopt_timerrivalsmins": ["Rivals", "Set the minutes you want to wait before load your rival list again."],
                    "bfopt_levelrangeactive": ["Filtering by level", "Check to use a level range filter players."],
                    "bfopt_levelrangemin": ["Filtering by level", "Set the minimal level range."],
                    "bfopt_levelrangemax": ["Filtering by level", "Set the maximum level range."],
                    "bfopt_levelrangemethod": ["Filtering by level", "Select if players IN level range are Attacked or Skipped."],
                    "bfopt_mafiarangeactive": ["Filtering by mafia", "Check to use a mafia range to filter players."],
                    "bfopt_mafiarangemin": ["Filtering by mafia", "Set the minimal mafia range."],
                    "bfopt_mafiarangemax": ["Filtering by mafia", "Set the maximum mafia range."],
                    "bfopt_mafiarangemethod": ["Filtering by mafia", "Select if players IN mafia range are Attacked or Skipped."],
                    "bfopt_publishactive": ["Autopublish", "Check to enable Autopublish feature.<br>It only works if you using the external Facebook api.<br>By default MWaddon uses this api, to make sure it'senabled open Configuration and uncheck \"Use Facebook user interface (UI)\"."],
                    "bfopt_publishafter": ["Autopublish", "Select the amount of ices you need to archive before you publish it."],
                    "bfopt_publishto": ["Autopublish", "Set the group or page id where you want to Autopublish your ices or leave it blank for your wall.<br>When you're in the group/page, look in profile picture link, look for group_id=XXX, copy the XXX number. Add to box"],
                    "bfopt_usestampack": ["PowerPack", "Check to use a powerpack to refill your stamina."],
                    "bfopt_usestampackwhen": ["PowerPack", "Set to use StaminaPack only if your required experience points to level up are more than this."],
                    "bfopt_usehealpack": ["PowerPack", "Check to use a powerpack to refill your health."],
                    "bfopt_usehealpackwhen": ["PowerPack", "Set to use HealthPack only if your stamina points are more than this."],
                    "bfopt_maxloglength": ["Log events", "Select the maximum amount of log events that will be displayed."],
                    "bfopt_showsocialevents": ["Log events", "Check to enable showing social events in general log.<br>Uncheck it if you are going for long sessions to save Memory usage."],
                    "bfopt_showlootevents": ["Log events", "Check to enable showing loot events in general log.<br>Uncheck it if you are going for long sessions to save Memory usage."],
                    "bfopt_namefilteractive": ["Filtering", "Check to skip players by their names."],
                    "bfopt_namefilterexpr": ["Filtering", "Used when \"Skip Names\" is checked.<br>Click \"EDIT\" to set the filter.<br>Put every name/characters you want to skip by lines."],
                    "bfopt_badgefilteractive": ["Filtering", "Check to skip players by their badges."],
                    "bfopt_badgefilterexpr": ["Filtering", "Click \"EDIT\" to set the filter.<br>Set here those badge tiers you want to SKIP by lines."],
                    "bfopt_skipiced": ["Filtering", "Filter/Skip player by an iced state."],
                    "bfopt_skipicedbyme": ["Filtering", "Filter/Skip players that were iced by you."],
                    "bfopt_skipusehealth": ["Skipping", "Skip a player if the health percentage is more than specified."],
                    "bfopt_skiphealth": ["Skipping", "Select the maximum health percentage allowed before you will attack an opponent."],
                    "bfopt_skipusemincash": ["Skipping", "Skip players based the cash gained for each fight.<br>This feature ensures you gain at least the specified value as minimum per attack after a fight."],
                    "bfopt_skipbymincash": ["Skipping", "Set the minimal cash per fight you should collect."],
                    "bfopt_skipwrongcash": ["Skipping", "Skip the current opponent when cash gained is from a different city.<br>This works very well if you're going for x5 loot in Brazil."],
                    "bfopt_skipunderattk": ["Skipping", "Check if you want to skip a player that is under attack by other players."],
                    "bfopt_skipunderattkpct": ["Skipping", "Set the percentage of damage does by others to the current opponent needed for skip."],
                    "bfopt_stopbyices": ["Autostop", "Stop when you archive a specified amount of session ices."],
                    "bfopt_stopiceamount": ["Autostop", "Set the amount of session ices to archive to stop."],
                    "bfopt_stopkeepstaon": ["Autostop", "Check to stop when your stamina goes below a specified number."],
                    "bfopt_stopkeepsta": ["Autostop", "Set the amount of stamina you want to keep."],
                    "bfopt_stopkeepexpon": ["Autostop", "Stop before you raise a new level."],
                    "bfopt_stopkeepexp": ["Autostop", "Set the amount of experience that you want to keep before level up."],
                    "bfopt_stopbylevelup": ["Autostop", "Check to stop after you raise a new level.<br>It will stop and show the level up popup."],
                    "bfopt_stopresume": ["Autoresume", "Resume AutoFight when it was stopped under some circumstances."],
                    "bfopt_stopresumedelay": ["Autoresume", "Set the amount of minutes to wait before you resume Battlefield after it was stopped."],
                    "bfopt_timerfightactive": ["Autopause", "Pause Battlefield after a specified time."],
                    "bfopt_timerfightmins": ["Autopause", "Set the amount of time in minutes that you want to keep fighting before AutoFight is paused."],
                    "bfopt_timerfightresume": ["Autopause", "Set the amount of time in minutes to wait before you resume Battlefield when it was stopped by the Autopause feature."],
                    "bfopt_useblacklist": ["Blacklist", "Add players who defeat you to blacklist."],
                    "bfopt_blacklist": ["Blacklist", "The balcklist is used to filter/skip players.<br>If you have an allience with a \"player\" add them here.<br>By default battlefield does not attack the Blacklist."],
                    "bfopt_whitelistcountactive": ["Whitelist", "Check if you want to attack the whole whitelist X times.<br>After attack the whole whitelist X times, you'll continue with Random BF Attacks."],
                    "bfopt_whitelistcount": ["Whitelist", "If \"Attack whitelist only\" is checked, set here the amount of times that you want to attack the whole whitelist.<br>It will not attack the same Target again until the full list has circulated, Either listed or Randomly."],
                    "bfopt_randomizewhitelist": ["Whitelist", "Check to attack the whole whitelist in any random order."],
                    "bfopt_whitelist": ["Whitelist", "You can use the \"Attack witelist\" button to attack these players, it works like Rival list but allows a greater amount of players."],
                    "bfopt_frdclanlist": ["Family", "The Friend Clan list is used to add friendly families.<br>If you just want to skip \"freindly allies\", add their families using the buttons on the right side.<br>Just add their profile link (located in the family profile page, copy and paste here), or the families IDs."],
                    "bfopt_enmclanlist": ["Family", "The Enemy Clan list is used to add enemy families.<br>If you just want to FIGHT againts all members of a family, add the family using the buttons on the right side.<br>Just add their profile link (located in the family profile page, copy and paste here), or the families IDs.<br>Click Attack Families to attack them!."],
                    "bfopt_refreshon": ["Refreshing", "This feature will limit the specified amount of opponents loaded per fightlist.<br>A low value will ensures you that you attack more fresh players."],
                    "bfopt_skiphealed": ["Skipping", "It will save the health of your opponents when you start attacking them and will skip them if the health raises the starting amount at any point of the fight.<br><br>This mean the player was Healed."],
                    "bfopt_collectclanfightxp": ["Experience", "Collect your Family fight progresion when available."],
                    "bfopt_stopautomatic": ["AutoStop", "If your health fall down in a very short time, normally is because you was attacked by others.<br>Enable it to Stop AutoFight when this happens.<br>You can enable the AutoResume option (next to this one) if you want to resume the fight after the specified time."],
                    "bfopt_timerstartactive": ["AutoStart", "AutoFight will start after the specified time."],
                    "bfopt_timerstartmins": ["AutoStart", "Set the time to wait in minutes before start AutoFight."],
                    "bfopt_rediceepicbattle": ["Red ices", "Used on family Epic Battles to keep fighting against stronged opponents that beat you."],
                    "bfopt_epicbattle": ["Epic Battle", "Enable the Epic Battle Mode.<br><br>When you're in a family epic battle (or family war) the current mode used (random, whitelist or family) is temporary disabled.<br>Then Battlefield will concentre all attacks to the enemy family until the battle end."],
                    "bfopt_epicbattlerefresh": ["Epic Battle", "When you're in a family war, set here the time delay to refresh the battle."]
                },
                "freegiftscenter_popup": {
                    "filter_text": ["Searching", "Add a text word to search for.<br>You can add searches separated by the \"|\" character.<br>Example: join|build|mystery."],
                    "category_select": ["Searching", "Select the category of the requests you want to show."],
                    "mss_action": ["Collector", "Select what action you want to perform for this gift."],
                    "mss_amount": ["Collector", "Set the amount of this gift you want to collect using the selected action."],
                    "friendlists": ["Friend Lists", "Select a list to fill your Friends box.<br>Sender will send gifts to all friends showed in your friend box.<br>Use \"EDIT\" to add/remove friends.<br>Select Custom list and click \"SAVE\" to save a new friendlist."],
                    "fgopt_sendback": ["Send back", "Check to send thanks gifts."],
                    "fgopt_sendinternally": ["Send back", "Check send thanks gifts only internally.<br>Faster."],
                    "fgopt_activefrmin": ["Active Friends", "Set how many gifts should have sent your friends to be considerated as \"Active friends\"."],
                    "fgopt_excludedpattern": ["Skipping", "Set the text pattern used to add gifts to your exclude list.<br>You can add different texts separated by \"|\" character.<br>Example: reached your limit|gold mastery"]
                },
                "remindereditor_popup": {
                    "reminder_name": ["Reminder Editor", "Set the reminder name."],
                    "reminder_description": ["Reminder Editor", "Set a description to remember the task."],
                    "reminder_checktype": ["Reminder Editor", "Select if you want to check every X hours, or after X time clock every day."],
                    "reminder_every": ["Reminder Editor", "Set the time in hours or the time clock.<br>You can use floating numbers like \"0.5\" to use a half of a hour."],
                    "reminder_gotocity": ["Reminder Editor", "Check if you want to go to a specified city."],
                    "reminder_gotocityid": ["Reminder Editor", "Select the city to travel."],
                    "reminder_gotopage": ["Reminder Editor", "Check if you want to go to a specified page."],
                    "reminder_gotopageurl": ["Reminder Editor", "Set the page url to go.<br>You can paste here the full url link copied from a link in Mafia Wars that directs you to the wanted page."],
                    "reminder_runplugin": ["Reminder Editor", "Check to execute a pre-installed plugin."],
                    "reminder_runpluginid": ["Reminder Editor", "Select the plugin to execute."],
                    "reminder_resetonload": ["Reminder Editor", "Check if this reminder will be reset after a page load."],
                    "reminder_resetonloadurl": ["Reminder Editor", "Set the page that will reset this reminder.<br>Same as \"Go To Page\", you can paste here a full url link of a Mafia Wars page."],
                    "reminder_jstest": ["Reminder Editor", "This code is executed before show the message to the user.<br>Return true will ask to the user to run it."],
                    "reminder_jsexec": ["Reminder Editor", "This code is executed if the user has clicked \"yes\" after show the message.<br>You can use the MWAddon object to run MWAddon's tools."],
                    "bookmark_gotopageurl": ["Bookmarks", "Select a previous saved bookmark with your MWAddon Toolbar to set the \"Go to page\" field."],
                    "bookmark_resetonloadurl": ["Bookmarks", "Select a previous saved bookmark with your MWAddon Toolbar to set the \"ResetOnLoad\" field."]
                },
                "pluginmanager_popup": {
                    "app_name": ["Plugin Manager", "Set the plugin name."],
                    "app_runat": ["Plugin Manager", "Set how is this plgin executed.<br>\"User Click\" means it will appear in the user plugin menu and will be executed if the user click on it.<br>\"Startup\" means that this plugin will be executed once after MWAddon is initialized."],
                    "app_src": ["Plugin Manager", "Set the source of the code.<br>\"Plain Text\" means the text in the code box is used as the plugin code.<br>\"Remote JS File\" means the code should be loaded from a remote site, and the text in the code box contains the remote JS file url."],
                    "app_code": ["Plugin Manager", "Paste the script code to be executed here.<br>You can use the MWAddon object to run MWAddon's tools."]
                },
                "multigifter_popup": {
                    "mgopt_giftpages": ["Pagination", "Check to enable pagination for items."],
                    "mgopt_userpages": ["Pagination", "Check to enable pagination for users."],
                    "mgopt_hidezeroamount": ["Item quantity", "Check to hide items with zero quantity."],
                    "mgopt_delay": ["Delay", "Set the time in seconds to wait to send a new gift."],
                    "gift_page": ["Pagination", "Select the page you want to go to."],
                    "user_page": ["Pagination", "Select the page you want to go to."],
                    "gift_category": ["Gift category", "Select the item group category to show."],
                    "user_category": ["User category", "Select the user group category to show."],
                    "collection_filter_type": ["Filtering", "Select a collection type to filter."],
                    "collection_filter": ["Filtering", "Select a collection name to filter."],
                    "search_gift": ["Searching", "Set a search to find gifts.<br>You can set here different searches separating every one by the \"|\" character."],
                    "saved_search": ["Searching", "Use a previously saved search."],
                    "search_user": ["Searching", "Set a search to find users.<br>You can set here different searches separating every one by the \"|\" character."],
                    "attack_more_less": ["Attack Filter", "Select if an item Attack should be More or Less than the specified amount."],
                    "active_filter": ["Active Loot", "Check it to show all active loot.<br><br>Active loot is those that you use in your fights."],
                    "inactive_filter": ["Help", "Check it to show all inactive loot.<br><br>Inactive loot is those that you use don't in your fights."],
                    "defense_more_less": ["Defense Filter", "Select if an item Defense should be More or Less than the specified amount."],
                    "defense_amount": ["Defense Filter", "Set the Defense number to check."],
                    "attack_amount": ["Attack Filter", "Set the Attack number to check."],
                    "user_fav_clear": ["Clearing", "Clear your Favourites."],
                    "gift_fav_clear": ["Clearing", "Clear your Favourites."]
                },
                "homefeedcenter_popup": {
                    "hfopt_filter": ["Filtering", "Put a search text to filter your posts.<br>You can use \"|\" character to set diferent searches.<br>Example:Build|operation|mission."],
                    "hfopt_grouping": ["Filtering", "Select a group of posts to show."],
                    "hfopt_dogotowar": ["Helping", "Check it to help in published wars.<br>Click to open more options."],
                    "hfopt_dogivesocialhelp": ["Helping", "Check it to help in published jobs.<br>Click to open selection menu."],
                    "hfopt_dosocialmissions": ["Helping", "Check it to join in operations.<br>Click to open more options."],
                    "hfopt_doclaimbonusandreward": ["Helping", "Check it to claim/collect all bonuses and rewards published.<br>Click to open selection Menu."],
                    "hfopt_docollectloot": ["Helping", "Check it to collect published loots (actually this option is unnecesary).<br>Click to open search filter. One entry per line."],
                    "hfopt_dopropertyhelp": ["Helping", "Check it to help sending property parts.<br>Click to open selection menu."],
                    "hfopt_dogetboost": ["Helping", "Check it to help in levelup bonuses."],
                    "hfopt_dosendenergyandphones": ["Helping", "Check it to help sending rob phones and energy packs."],
                    "hfopt_doacceptgiftevent": ["Helping", "Check it to help in gift events."],
                    "hfopt_dohitlistbounty": ["Helping", "Check it to help in hitlist bounties.<br>Click to open selection by filter options."],
                    "hfopt_dosecretstash": ["Helping", "Check it to help in secret stashes.<br>Click to open selection by filter options."],
                    "hfopt_docitycrew": ["Helping", "Check it to help your friends by joining their city crew.<br>Click to open selection by filter options."],
                    "skiptext_givesocialhelp": ["Edit Skip Action", "Set the text returned by the server after collect the selected feed when you reached the limit for this feed to skip it."],
                    "skiptext_claimbonusandreward": ["Edit Skip Action", "Set the text returned by the server after collect the selected feed when you reached the limit for this feed to skip it."],
                    "skiptext_propertyhelp": ["Edit Skip Action", "Set the text returned by the server after collect the selected feed when you reached the limit for this feed to skip it."],
                    "hfopt_autoclose": ["General", "If checked, the help options you click \"accept\" the link will be autoclosed after a few seconds."],
                    "hfopt_feedslimit": ["General", "Set here the limit of posts you want to load when using \"AutoHelp\"."],
                    "hfopt_maxloglength": ["General", "Set here the maximum number of log entries when using \"AutoHelp\"."],
                    "hfopt_helpdelay": ["General", "Set here how much time you want to wait between helping."],
                    "hfopt_refreshdelay": ["General", "Set here how much time you want to wait before refreshing your home posts."],
                    "hfopt_useuserfilter": ["User Filter", "Enable/disable user filter."],
                    "hfopt_userfilteraction": ["User Filter", "Select how you want to use the filter."],
                    "hfopt_userfilter": ["Filters", "Click \"EDIT\" to add/remove friends to filter the feeds you want to see adn help in Home Feed Center."],
                    "hfopt_waruseminattack": ["War help", "Check it if you want to collect only those items with more than the specified attack."],
                    "hfopt_warminattack": ["War help", "Set the minimum war reward item attack need to help in a war."],
                    "hfopt_warusemindefense": ["War help", "Check it if you want to collect only those items with more than the specified defense."],
                    "hfopt_warmindefense": ["War help", "Set the minimum war reward item defense need to help in a war."],
                    "hfopt_warusenamefilter": ["War help", "Check it to use war reward item's name to filter wars. If you check it, you should uncheck minimal attack/defense options."],
                    "hfopt_warnamefilter": ["War help", "Add war reward item's names separated by commas or \"|\" character.<br>Example:Paraglider|Sharksaw|emu Or Paraglider,Sharksaw,Emu."],
                    "warloot_data": ["War Loot", "Secundary loot is used to filter those wars that you would like to help for your mafia but giving you no loot.<br><br>Click \"TOGGLE\" to enable/disable all war secundary loot."],
                    "warlootlimited_data": ["War Loot", "It's your desired war loot.<br>You will help in wars for this loot (if enabled) until you've reached the 5/5 limit.<br>If you've reached to the 5/5 limit or you disables it, will check your secundary war loot to determinate if you should help or not."],
                    "warloot_name": ["War Loot", "Set the name of the loot you want to collect.<br>(Asterisk) \"*\" means anything."],
                    "warloot_att": ["War Loot", "Set the minimal attack a loot should have to help in the war.<br>\"0\" means anything."],
                    "warloot_def": ["War Loot", "Set the minimal defense a loot should have to help in the war.<br>\"0\" means anything."],
                    "warloot_enabled": ["War Loot", "Enable/Disable this loot."],
                    "hfopt_joinmission": ["Operations", "Select the slot type where you want to join."],
                    "hfopt_joinmissionafter": ["Operations", "If the previous slot type are not available, select a second slot type to join."],
                    "hfopt_maxfreeslots": ["Operations", "Select the maximum amount of free slots to join in an operation."],
                    "hfopt_opusenamefilter": ["Operations", "Check to filter operations by name.<br>You will try to join only the operations that match the filter name."],
                    "hfopt_opnamefilter": ["Operations", "Add operatons names separated by \"|\" character.<br>Example:<br>Fix The Tripple Crown|Steal Government Research"],
                    "hfopt_dtlootpriority": ["Daily Take", "Enter the EXACT name of the items you want ADDON to retrieve.<br>Put one item on each line in the order you want to retrieve them (the most important item first).<br>HINT: You can get the name of the item by hovering over it in the Daily Take window."],
                    "hfopt_dtcheckminatkdef": ["Daily Take", "Check to retrieve fight loot if there is nothing that is on your Priority List available.<br>You can specify a minimum attack and defense.<br>If there is nothing that meets your specifications, the daily take will be ignored."],
                    "hfopt_dtminattack": ["Daily Take", "Set the minimal attack value for the fight loot."],
                    "hfopt_dtmindefense": ["Daily Take", "Set the minimal defense value for the fight loot."],
                    "hfopt_dodailytake": ["Helping", "Check it to help in Daily Takes.<br>Click to open more options."],
                    "hfopt_fblistfilteron": ["Filters", "Enable or Disable Facebook Friends list Filter.<br><br>Click \"EDIT\" to show a list of your Facebook friends list which you can select to filter the feeds you want to see adn help in Home Feed Center."],
                    "hfopt_fblistfilter": ["Filters", "Click \"EDIT\" to show a list of your Facebook friends list which you can select to filter the feeds you want to see adn help in Home Feed Center."],
                    "hfopt_userfilteron": ["Filters", "Enable or Disable Friends Filter.<br><br>Click \"EDIT\" to add/remove friends to filter the feeds you want to see adn help in Home Feed Center."],
                    "hfopt_warlootsecundarylimit": ["War Loot", "Used to colelct all secundary llot one time.<br><br>Once the loot has been collected, it will turn in OFF."],
                    "action_skipif": ["Skip Check", "Used to Skip a feed when the result text from the server match the criteria."],
                    "action_fail": ["Fail Check", "Used to send a feed to the fail log when you was not able to collect it when the result text from the server match the criteria."]
                },
                "bountyhunter_popup": {
                    "bhopt_userblackliston": ["Blacklist", "All bounties where you lost will be added in this list."],
                    "bhopt_clanblackliston": ["Blacklist", "You can skip bounties if the player is in one of your friendly families."],
                    "bhopt_userblacklist": ["Blacklist", "You can import your Battlefield Blacklist."],
                    "bhopt_clanblacklist": ["Blacklist", "You can import your Battlefield FriendClanlist."],
                    "bhopt_deposit": ["AutoDeposit", "This option uses the General Config to deposit your cash.<br><br>Open \"Configuration\" to set it."],
                    "bhopt_healon": ["AutoHeal", "If your heal goes below the specified amount, you will be healed."],
                    "bhopt_healmin": ["AutoHeal", "Set the how low should be your health before heal you."],
                    "bhopt_redbounty": ["Blood Hunting", "You will continue hitting a bounty even if you lost."],
                    "bhopt_delay": ["Delay", "The time to wait before reload the hitlist."]
                },
                "operationscenter_popup": {
                    "select_fbgroups": ["Publish", "Select the group where you want to publish your operations."]
                }
            }
        }
        
        //----
    }
};

// ==Script==
// @id        FileSystem.js
// @author    Dakam
// @memberOf  MWAddon.js
// ==Script==

var FileSystem = {
    /**
     * Read the file and return the text to callback.
     * @param {Object} files
     * @param {function} callback
     */
    fileReader: function(files, callback) {
        var file = files && files[0];
        
        if (file) {
            
        	var reader = new FileReader();
        	reader.onload = function(e) {
        		callback(e && e.target && e.target.result);
                Logger.debug('fileReader completed, loaded '+file.size+'bytes.');
        	};
        	reader.readAsText(file);
            
        } else {
            
            callback(false);
            
        }
    },
    /**
     * Launch the Save dialog box.
     * @param {Object} data
     */
    fileSave: function(data) {
        unsafeWindow.open(FileSystem.createObjectURL(data, 'application/octet-stream'), '_top');
    },
    /**
     * Create a new blob object url.
     * @param {Object} data
     * @param {String} mime
     */
    createObjectURL: function(data, mime) {
        var blobBuilder = unsafeWindow.WebKitBlobBuilder || unsafeWindow.MozBlobBuilder;
        var cURL = unsafeWindow.webkitURL || unsafeWindow.URL;
        if (blobBuilder) {
            var bb = new blobBuilder();
            bb.append(data);
            if (FileSystem.lastObjectURL) {
                FileSystem.revokeObjectURL(FileSystem.lastObjectURL);
            }
            return ( FileSystem.lastObjectURL = cURL.createObjectURL(bb.getBlob(mime)) );
        }
        
    },
    /**
     * Revoke a blob object url (saves memory usage).
     */
    revokeObjectURL: function(objectURL) {
        var cURL = unsafeWindow.webkitURL || unsafeWindow.URL;
        if (cURL && objectURL) {
            cURL.revokeObjectURL(objectURL);
        }
    }
    
    
};
// ==Script==
// @id        Controllers.js
// @author    Dakam
// @memberOf  MWAddon.js
// ==Script==
/**
 * Get a jQuery with at least 1 element, otherwise null.
 *
 * @param {Object} selector
 * @param {Object} context
 * @return {jQuery, null}
 */
function e$(selector, context) {
    var $elem = $(selector, context);
    if ($elem.length > 0) {
        return $elem;
    }
    return null;
}
/**
 * Create a jQuery from a HTML text.
 *
 * @param {String} htmlText
 * @return {jQuery}
 */
function h$(htmlText) {
    var elem;
    if (typeof(htmlText) == 'string') {
        elem = c$('div');
        elem[0].innerHTML = '<div>' + htmlText + '</div>';
    } else {
        elem = $(htmlText);
    }

    return elem;
}
/**
 * Create a new element.<br><br>
 * Script and style tag are supported.<br>
 * Input elements can set the type in format "input:text" for example.<br><br>
 *
 * @param {String, jQuery, Element} name The new element to create.
 * @param {Object, String} attr Attributes to set to the new jquery element
 * @return {jQuery}
 */
function c$(name, attr) {
    var inputRegex = /^input:(\w+)$/i;
    var obj, r, a;
    // create the object
    if (typeof(name) !== "string") {
        obj = $(name);
    }
    else if ((r = inputRegex.exec(name))) {
        obj = $('<input type="' + r[1] + '">');
    }
    else {
        if (name == 'style') {
            obj = $('<' + name + ' type="text/css">');
        }
        else if (name == 'script') {
            obj = $('<' + name + ' type="text/javascript">');
        }
        else {
            obj = $('<' + name + '>');
        }
    }
    // return undefined if no object is created
    if (obj.length < 1)
        return null;

    // apply attributes
    if (typeof(attr) === "string")
    {
        if ((r = attr.split(',')).length > 1) {
            for (var i = 0; i < r.length; i++) {
                if ((a = r[i].split(':')).length == 2) {
                    obj.attr($.trim(a[0]), $.trim(a[1]));
                }
            }
        }
        else if ((a = attr.split(':')).length == 2) {
            obj.attr($.trim(a[0]), $.trim(a[1]));
        }
        else {
            obj.attr('id', $.trim(attr));
        }
    }
    else if (typeof(attr) === "object") {
        obj.attr(attr);
    }

    return obj;
}
/**
 * Create a new checkbox element.
 * @param {String} id
 * @param {String} label
 * @return {jQuery}
 */
function x$(id, label, elementType, margin_right) {
    if (!elementType) {
        elementType = 'span';
    }
    if (!margin_right) {
        margin_right = 5;
    }
    return c$(elementType).html(label||'').css({
        'cursor':'pointer',
        'margin-right': margin_right
    }).attr({
        'id':id,
        'name':'checkbox',
        'class':'checkbox',
        'onclick':'$(this).toggleClass(\'checked\');return false;'
    });
}
/**
 * Create a new select element.
 * @param {String} id
 * @param {String} [text]
 * @param {Number} [width]
 * @param {Object} [events]
 * @return {jQuery}
 */
function s$(id) {
    var text, width = 40, events;
    Util.each(arguments, function(i, a) {
        if (i > 0) {
            switch( typeof(a) ) {
                case 'string': text = a;   break;
                case 'number': width = a;  break;
                case 'object': events = a; break; 
            }
        }
    });
    var s = c$('select','id:'+id).css({'width': width, 'margin':'0px 5px 0px 5px'});
    if ( events ) {
        Util.each(events, function(e,fn) {
            if (Util.isFunc(s[e])) s[e](fn);
        });
    }
    if ( Util.isString(text) ) {
        s = c$('label', 'for:'+id).css('margin-right', 5).text(text).append(s);
    }
    return s;
}
/**
 * Create a new numeric input element.
 * @param {String} id
 * @param {String} [text]
 * @param {Number} [width]
 * @return {jQuery}
 */
function n$(id, text, width) {
    return t$(id, text, width, {
        'onchange' : 'if(isNaN(parseFloat(this.value))){this.value=0;}',
        'onclick'  : '$(this).select();'
    });
}
/**
 * Create a new text input element.
 * @param {String} id
 * @param {String} [text]
 * @param {Number} [width]
 * @param {Object} [events]
 * @return {jQuery}
 */
function t$(id) {
    var text, width = 40, events;
    Util.each(arguments, function(i, a) {
        if (i > 0) {
            switch( typeof(a) ) {
                case 'string': text = a;   break;
                case 'number': width = a;  break;
                case 'object': events = a; break; 
            }
        }
    });
    var t = c$('input:text','id:'+id).css({'width':width, 'margin':'0px 5px 0px 5px'});
    if ( events ) {
        Util.each(events, function(e, fn) {
            if (Util.isFunc(t[e]) && Util.isFunc(fn)) {
                t[e](fn);
            }
            else {
                t.attr(e, fn);
            }
        });
    }
    if ( Util.isString(text) ) {
        t = c$('label', 'for:'+id).css('margin-right', 5).text(text).append(t);
    }
    return t;
}
/**
 * Return a new jQuery with the new sexy style button.
 * @param {String} label
 * @param {String} attr
 * @return {jQuery}
 */
function b$(label, attr) {
    var spanElt = c$('span').append(c$('span').html(label));
    return c$('a', attr).addClass('sexy_button_new').append(spanElt);
}


// ==Script==
// @id        Util.js
// @author    Dakam
// @memberOf  MWAddon.js
// ==Script==

/**
 * @namespace Util
 */
var Util = {
    /**
     * @param {Object} obj
     * @return {Boolean}
     */
    isSet: function(obj) {
        return typeof obj !== 'undefined' && obj !== null;
    },
    /**
     * @param {Object} obj
     * @return {Boolean}
     */
    isString: function(obj) {
        return typeof obj === 'string';
    },
    /**
     * @param {Object} obj
     * @return {Boolean}
     */
    isBoolean: function(obj) {
        return typeof obj === 'boolean';
    },
    /**
     * @param {Object} obj
     * @return {Boolean}
     */
    isFunc: function(obj) {
        return typeof obj === 'function';
    },
    /**
     * @param {Object} obj
     * @return {Boolean}
     */
    isObject: function(obj) {
        if (typeof obj === 'object' && obj !== null) {
            return /Object/.test(obj.constructor);
        } else {
            return false;
        }
    },
    /**
     * @param {Object} obj
     * @return {Boolean}
     */
    isArray: function(obj) {
        if (typeof obj === 'object' && obj !== null) {
            return /Array/.test(obj.constructor);
        } else {
            return false;
        }
    },
    /**
     * @param {Object} obj
     * @return {Boolean}
     */
    isDate: function() {
        if (typeof obj === 'object' && obj !== null) {
            return /Date/.test(obj.constructor);
        } else {
            return false;
        }
    },
    /**
     * @param {Object} obj
     * @return {Boolean}
     */
    isNumber: function(obj) {
        return Math.round(obj, 0) === obj;
    },
    /**
     * Compare two strings
     * @param {String} name
     * @param {String} compare
     */
    isSameString: function(str, compare) {
        if (Util.isString(str) && Util.isString(compare)) {
            return str.toLowerCase() === compare.toLowerCase();
        } else {
            return false;
        }
    },
    /**
     * Return a json string (support base64 encode) of the object.
     * @param {Object} obj
     * @param {Boolean} [encode]         set true to encode to base64
     * @param {String} [encode_type]     by default "application/json"
     * @return {String}
     */
    toJSON: function(obj, encode, encode_type) {
        if (typeof($) !== 'undefined') {
            obj = $.toJSON(obj);
        } else {
            obj = JSON.stringify(obj); 
        }
        if (!Util.isString(obj) || obj.length < 1) {
            return '';
        }
        if (encode === true) {
            return 'data:'+(encode_type||'application/json')+';base64,'+Base64.encode(obj);
        }
        return obj;
    },
    /** 
     * Parse a json string (support base64 encode) to an object.
     * @param {String} text
     * @return {Object}
     */
    parseJSON: function(text) {
        if (!Util.isString(text)) {
            return new Object(); 
        }
        if (/^data:\w+\/\w+;base64,/.test(text)) {
            text = Base64.decode(text);
        }
        if (typeof($) !== 'undefined') {
            return $.parseJSON(text);
        } else {
            return JSON.parse(text); 
        }
    },
    /**
     * @param {Object} obj
     * @return {Number}
     */
    length: function(obj) {
        var count = 0;
        for (var e in obj) {
            if (typeof e !== 'undefined'){count++;}
        }
        return count;
    },
    /**
     * @param {String} text
     * @return {Object}
     */
    parseParam: function(text) {
        text = String(text).replace(/\+/g, '').replace('%ITEM_ID%','""');
        try {
            return Util.parseJSON(text);
        } catch (err) {
            Logger.error('parseParam: ' + text);
            return {};
        }
    },
    /**
     * @param {Number} num
     * @param {Number} digits
     * @return {String}
     */
    padNum: function(num, digits) {
        if (!this.isNumber(num)) {
            return num;
        }
        if (!this.isNumber(digits)) {
            digits = 2;
        }
        num = num.toString();
        while (digits > num.length) {
            num = '0' + num;
        }
        return num;
    },
    /**
     * @param {Number, String} num
     * @return {String}
     */
    formatNum: function(num) {
        var expr = /(\d+)(\d{3})/, fixed = String(num);

        while (expr.test(fixed)) {
            fixed = fixed.replace(expr, '$1,$2');
        }
        return fixed;
    },
    /**
     * Return the Date String from the giving milliseconds Number.
     * @param {Number} ms
     * @param {Array} format     by default ['h ','m ', 's']
     * @return {String}
     */
    toDateString: function(ms, format) {
        var dateText = '', s, m, h, d;
        if ((ms=parseInt(ms)) < 0){ms = 0;}
        if (!Util.isArray(format)||format.length!==3) { format = ['h ','m ', 's']; }
        s = Math.floor(ms / 1000);
        m = Math.floor(s / 60);
        h = Math.floor(m / 60);
        d = Math.floor(h / 24);

        dateText += d > 0 ? d + 'd ' : '';
        dateText += (h = h % 24) > 0 ? this.padNum(h) + (format[0]||':') : '';
        dateText += ((m = m % 60) > 0 ? this.padNum(m) : '00') + (format[1]||':');
        dateText += ((s = s % 60) > 0 ? this.padNum(s) : '00') + (format[2]||'');

        return dateText;
    },
    /**
     * Return a delay String from the giving milliseconds Number.
     * @param {Number} ms
     * @return {String}
     */
    toDelayString: function(ms) {
        var dateText = '', s, m, h, d;
        if (ms < 0){ms = 0;}

        s = Math.floor(ms / 1000);
        m = Math.floor(s / 60);
        h = Math.floor(m / 60);
        d = Math.floor(h / 24);

        dateText += d > 0 ? d + 'd ' : '';
        dateText += ( (h = h % 24) > 0     ? Util.padNum(h) + 'h : ' : '');
        dateText += ( (m = m % 60) > 0     ? Util.padNum(m) + 'm : ' : '');
        dateText += ( (s = s % 60) > 0     ? Util.padNum(s) + 's : ' : '');
        dateText += ( (ms = ms % 1000) > 0 ? Util.padNum(ms,3)    : '000') + 'ms';

        return dateText;
    },
    /**
     * Return an new object with all url parameters.
     * @param {String} url
     * @return {Object}
     */
    uSplit: function(url) {
        var cUrl = new Object();
        if (!Util.isString(url)) {
            return cUrl;
        }
        try {
            url = url.replace(/&amp;/g, '&');
            url = url.replace(/&quot;/g, '"');

            if (url.indexOf('?') !== -1) {
                url = String(url.split('?')[1]);
            }
            this.each(url.split('&'), function(p, param) {
                if ((p = param.split('=')).length === 2) {
                    cUrl[p[0]] = unescape(p[1]);
                }
            });
        } catch (err) {
            Logger.error(err);
        }
        return cUrl;
    },
    /**
     * Return a new object with all url parts.
     * @param {String} url
     */
    uParse: function(url) {
        var rgx = /^(([^:\/?#]+):)(\/\/([^\/?#]*))([^?#]*)(\?([^#]*))?(#(.*))?/gi;
        var url_params = this.doRgx(rgx, url);

        return {
            href        : url,
            protocol    : url_params.$2,
            host        : url_params.$4,
            path        : url_params.$5,
            query       : url_params.$7,
            hash        : url_params.$9
        };
    },
    /**
     * Loops through the properties of the object.
     * @param {Object} obj
     * @param {Function} callback
     */
    each: function(obj, callback) {
      if (typeof($) !== 'undefined') {
          $.each(obj, callback);
      } else {
          for (var key in obj) {
              if (obj.hasOwnProperty(key)) {
                  if (callback(key, obj[key]) === false) {
                      break;
                  }
              }
          }
      }
    },
    /**
     * Execute a Regular expresion and return an object.
     * @param {RegExp} rgx
     * @param {String} text
     * @return {Object}
     */
    doRgx: function(expr, text) {
        var r, retult = new Object();
        if (Util.isSet(expr) && Util.isSet(text)) {
            if (Util.isString(expr)) {
                expr = new RegExp(expr, 'i');
            }
            if (expr.exec && (r = expr.exec(text))) {
                for (var i=0; i < r.length; i++) {
                    retult['$'+i] = r[i];
                }
            }
        }
        return retult;
    },
    /**
     * Execute a Regular expresion and return the result of the test.
     * @param {RegExp} rgx
     * @param {String} text
     * @return {Boolean}
     */
    doRgxTest: function(expr, text) {
        if (Util.isSet(expr) && Util.isSet(text)) {
            if (Util.isString(expr)) {
                expr = new RegExp(expr, 'i');
            }
            if (expr.test) {
                return expr.test(text);
            }
        }
        return false;
    },
    /**
     * @param {String} text
     * @return {String}
     */
    htmlDecode: function(text) {
        if (!Util.isString(text)) {
            return '';
        }
        try {
            return $.trim(c$('textarea').html(text).val());
        } catch (err) {
            Logger.error(err);
            return $.trim(unescape(text));
        }
    },
    /**
     * @param {String} text
     * @param {String} color
     * @param {String} [id]
     * @return {String}
     */
    setColor: function(text, color, id) {
        return '<span ' + (id ? 'id="' + id + '" ' : '') + 'style="color:'
                + color + ';">' + text + '</span>';
    },
    /**
     * @param {String} url
     * @param {String} text
     * @return {String}
     */
    setAnchor: function(url, text) {
        return ' <a href="' + url + '" target="_black">' + text + '</a>';
    },
    /**
     * Return a Hex color string.
     *
     * @param {String} color
     * @return {String}
     */
    rgbToHex: function(color) {
        if (typeof color !== 'string' || color.length < 1) {
            return color;
        }

        if (color.charAt(0) !== '#') {
            var rgx, rgb;
            if ((rgx = /rgba?\((\d+)[,\s]*(\d+)[,\s]*(\d+)\)/.exec(color))) {

                rgb = 1 << 24 | parseInt(rgx[1]) << 16 | parseInt(rgx[2]) << 8 | parseInt(rgx[3]);
                return '#' + rgb.toString(16).substr(1);
            }
        }
        while (color.length < 7) {
            color += '0';
        }
        return String(color).toLowerCase();
    },
    /**
     * @param {String} string
     * @return {Number}
     */
    parseNum: function(string) {
        if (!Util.isSet(string)) {
            return 0;
        }
        if (!Util.isString(string)) {
            return parseInt(string);
        }
        var number_part = string.replace(/\D+/g, '');
        return number_part.length > 0 ? parseInt(number_part) : 0;
    },
    /**
     * @param {Element, jQuery} elem
     * @return {String}
     */
    textNodes: function(elem) {
        return $(elem).contents().filter(function() {
            return this.nodeType === 3;
        }).text();
    },
    /**
     * Render the specified HTML content as a template, using the specified data
     * @param {Object} template
     * @param {Object} data
     */
    render: function(template, data) {
        Util.each(data, function(id, text) {
            var pattern = new RegExp('\\$\\{'+id+'\\}', 'gi');
            template = template.replace(pattern, text);
        });
        return template;
    },
    /**
     * @param {String} string
     * @param {String} sStart
     * @param {String} sEnd
     * @param {Number} [nStartPos]
     * @param {Number} [nEndPos]
     * @param {Number} [nStartIndex]
     * @param {Boolean} [bUseLastIndex]
     * @return {String}
     */
    substr: function(string, sStart, sEnd, nStartPos, nEndPos, nStartIndex, bUseLastIndex) {
        if (!(Util.isString(string) && Util.isString(sStart) && Util.isString(sEnd))) {
            return string;
        }

        nStartPos = Util.isNumber(nStartPos) ? nStartPos : 0;
        nEndPos = Util.isNumber(nEndPos) ? nEndPos : 0;

        var bIndex, aIndex = string.indexOf(sStart, nStartIndex);
        if (aIndex === -1) {
            return false;
        }
        if (bUseLastIndex !== true) {
            bIndex = string.indexOf(sEnd, aIndex + Math.max(nStartPos,1));
        } else {
            bIndex = string.lastIndexOf(sEnd);
        }
        if (bIndex === -1) {
            return false;
        }
        aIndex += nStartPos;
        bIndex += nEndPos;
        return string.substr(aIndex, bIndex - aIndex);
    },

    /**
     * @param {jQuery} elem
     * @return {String}
     */
    toUrl: function(elem) {
        if (!elem.attr) {
            elem = $(elem);
        }
        var url = elem.attr('href');
        if (Util.isString(url) && /html_server/i.test(url)) {
            return url;
        }
        return Util.doRgx(/['"](remote[^'"]*)/, elem.attr('onclick')).$1;
    },
    /**
     * Trim all spaces.
     * @param {Object} text
     * @return {String}
     */
    trim: function(text) {
        if (!Util.isString(text)) {
            return '';
        }
        if ( typeof($) !== undefined ) {
            return $.trim(text);
        } else {
            return text.replace(/^\s*|\s*$/, '');
        }
    },
    /**
     * Return a new Array from an Object.
     * @param {Object} obj
     */
    toArray: function(obj) {
        if (Util.isArray(obj)) {
            return obj;
        }
        var oArray = new Array();
        Util.each(obj, function(i,v) {
            oArray.push(v);
        });
        return oArray;
    },
    /**
     * Format a text, uppercase every first letter
     *
     * @param {String} string text to format
     * @return {String}
     */
    formatText: function(string) {
        var words = String(string).replace(/_/g, ' ').split(/\s/g);
        var outText = '';
        for ( var i = 0; i < words.length; i++) {
            outText += words[i].charAt(0).toUpperCase()
                    + words[i].substr(1).toLowerCase() + ' ';
        }
        return outText.replace(/^\s+|\s+$/, '');
    },
    /**
     * @param {Element, jQuery} element
     * @return {String}
     */
    getInputSelectedValue: function(element) {
        element = $(element)[0];
        if (element.options) {
            return element.options[element.selectedIndex].value;
        }
    },
    /**
     * Get a picture url from a background css string.
     * @param {String} s_background
     * @return {String}
     */
    getPicture: function(s_background) {
        if (Util.isString(s_background)) {
            var url = Util.doRgx(/url\(([^\)]+)\)/, s_background).$1;
            if (Util.isSet(url)) {
                return String(url).replace(/["']/g,'');
            }
        }
        return '';
    },
    /**
     * Search for a javascript code that apply html to the popup_fodder element,
     * return the HTML code.
     *
     * @param {String} htmlText
     * @return {jQuery}
     */
    parsePopup: function(htmlText) {
        try {
            var sHtml = Util.doRgx(/\$\('#popup_fodder'\).html\("(.*?)"\);\s*?MW.Popup.show/i, htmlText).$1;
            if (sHtml) {
                eval('sHtml = "' + sHtml + '";');
                return sHtml;
            }
        } catch (err) {
            Logger.error('parsePopup: ' + err.message);
        }
        return;
    },
    /**
     * Calculate the percentage of current relative to max. 
     * @param {Number} current
     * @param {Number} max
     * @return {Number}
     */
    percent: function(current, max) {
        current = parseInt(current);
        max = parseInt(max);
        if (isNaN(current) || isNaN(max) || current < 1 || max < 1) {
            return 0;
        } else {
            return parseFloat((current * 100) / max); 
        }
    },
    /**
     * Move an item in the Array the steps specified by move.
     *
     * @param {Array} the_array array where item will be moved
     * @param {Number} index element to move
     * @param {Number} move + / - values
     * @return {Number}
     */
    moveArrayItem: function(the_array, index, move) {
        var to_index = index + move, item;

        if (index < 0 || index >= the_array.length) {
            return index;
        }
        if (to_index < 0 || to_index >= the_array.length || to_index === index) {
            return index;
        }

        item = the_array[to_index];
        the_array[to_index] = the_array[index];
        the_array[index] = item;

        return to_index;
    },
    /**
     * Merge the contents of two arrays/objects together into the first array.
     * @param {Object} first
     * @param {Object} second
     */
    merge: function(first, second) {
        if (!Util.isSet(second)) {
            return first;
        }
        var isArray = Util.isArray(first);
        Util.each(second, function(i, v) {
            if (isArray) {
                first.push(v);
            } else {
                first[i] = v;
            }
        });
        return first;
    },
    /**
     * Return an empty function.
     */
    noop: function() {
        return false;
    },
    /**
     * Clear all regex metatags.
     *
     * @param {String} text
     * @return {String}
     */
    clearRegExpMeta: function(text) {
        var clearPattern = /([\^\$\.\+\?\*\{\}\(\)\\\/\|\[\]])/g;
        if (String(text).length > 0 && clearPattern.test(text)) {
            return String(text).replace(clearPattern, '\\$1');
        } else {
            return text;
        }
    },
    /**
     * Create a new copy of the object.
     * @param {Object} obj
     * @return {Object}
     */
    clone: function(obj) {
        var cloned = new Object();
        Util.each(obj, function(a,b) {
            if (!Util.isFunc(b)) {
                cloned[a] = b;
            }
        });
        return cloned;
    },
    /**
     * Wait until test return true.<br><br>
     * Params:<br>
     * test: function(){...}
     * delay: Number
     * retries: Number
     * success: function(){...} }
     * 
     * @param {Object} p
     */
    until: function(p) {
        if ( !Util.isNumber(p.retries) ) { p.retries = 50; }
        if ( !Util.isNumber(p.delay)   ) { p.delay = 100;  }
        var interval_id = setInterval(function() {
            if (p.test() === true || p.retries < 0) {
                clearInterval(interval_id);
                p.success();
            }
            p.retries--;
        }, p.delay);
    }
};
// ==Script==
// @id        MW.js
// @author    Dakam
// @memberOf  MWAddon.js
// ==Script==

/**
 * @namespace MW
 */
var MW = {
    /**
     * Update gift data.
     * @param {String} [htmlText] optional server response.
     * @return {Object}
     */
    parseGiftData: function(htmlText) {
        var groups_levels, item_amounts, item_names, item_imgs, gifts_daily_left;
        try {
	        eval( Util.substr(htmlText, 'var groups_levels', 'var friends_names') );
        } catch (e) { 
            Logger.error('parseGiftData: '+e.message); 
        }
        return (global.giftData = {
            'gifts_daily_left' : gifts_daily_left,
            'groups_levels'    : groups_levels,
            'item_amounts'     : item_amounts,
            'item_names'       : item_names,
            'item_imgs'        : item_imgs
        });
    },
    /**
     * Get gift data from server. 
     * @param {Function} callback usage function(data).
     * @param {Boolean} bForceUpdate true to force update from server.
     */
    getGiftData: function(callback, bForceUpdate) {
        if (!Util.isFunc(callback)) {
            return;
        }
        var w = unsafeWindow, $g = $('#gift_send input#gift_key');
        if ($g.length > 0) {
            global.gift_key = $g.val();
        }
        if ( bForceUpdate !== true && global.gift_key  ) {
            if (global.giftData) {
                callback(global.giftData);
                return;
            }
            if (w.groups_levels && w.item_amounts && w.item_names && w.item_imgs && w.gifts_daily_left) {
                callback(global.giftData = {
                    'gifts_daily_left' : w.gifts_daily_left,
                    'groups_levels'    : w.groups_levels,
                    'item_amounts'     : w.item_amounts,
                    'item_names'       : w.item_names,
                    'item_imgs'        : w.item_imgs
                });
                return;
            }
        }
        httpAjaxRequest({
            url: 'remote/' + MW.getIntURL('gift'),
            timeout: 60000,
            message: 'Loading friends...',
            success: function(htmlText) {
                if (MW.update(htmlText) !== true) {
                    showBadResponse();
                    callback();
                    return;
                }
                global.gift_key = Util.doRgx(/gift_key['"]\s*value=['"]([^'"]+)/, htmlText).$1;
                callback( MW.parseGiftData(htmlText) );
            }
        });
    },
    /**
     * Update Inventory data.
     * @param {String} [htmlText] optional server response.
     * @return {Object}
     */
    parseInventoryData: function(htmlText) {
        var Items, Item, div = document.createElement('div');
        try {
            eval( Util.substr(htmlText, 'var Items', '</script>') );
        } catch (e) { 
            Logger.error('parseInventoryData: '+e); 
        }
        try {
            if (!(Item = unsafeWindow.Item)) {
                div.setAttribute('onclick', "return Item;");
                Item = div.onclick();
            }
        } catch(e) { 
            Logger.error('parseInventoryData'+ e);
        }
        return (global.inventoryData = {
            'Item': Item,
            'Items': Items
        });
    },
    /**
     * Get Inventory data from server. 
     * @param {Function} callback usage function(data).
     * @param {Boolean} bForceUpdate true to force update from server.
     */
    getInventoryData: function(callback, bForceUpdate) {
        if (!Util.isFunc(callback)) {
            return;
        }
        if ( bForceUpdate !== true ) {
            if (global.inventoryData) {
                callback(global.inventoryData);
                return;
            }
            if (unsafeWindow.Item && unsafeWindow.Items) {
                callback(global.inventoryData = {
                    'Item': unsafeWindow.Item,
                    'Items': unsafeWindow.Items
                });
                return;
            }           
        }
        httpAjaxRequest({
            url: 'remote/' + MW.getIntURL('inventory'),
            timeout: 60000,
            message: 'Loading inventory data...',
            success: function(htmlText) {
                if (MW.update(htmlText) !== true) {
                    showBadResponse();
                    callback();
                    return;
                }
                callback( MW.parseInventoryData(htmlText) );
            }
        });
    },
    /**
     * Get the specified loot name.
     * @param {Number} item_id
     * @param {Object} callback
     */
    getItemInfo: function(item_id, callback) {
        httpAjaxRequest({
            'url': 'remote/' + MW.getIntURL('item', 'get_item_data') + '&item_id=' + item_id,
            'success': function(jsonData) {
                var n, t, p;
                if ( jsonData.popup ) {
                    p = c$('div').html(jsonData.popup).find('#'+jsonData.popup_id);
                    n = Util.textNodes(p.find('h3'));
                    t = Util.trim(p.find('.info dd:eq(1)').text());
                    callback( Util.trim(n), t );
                } else {
                    callback( 'Unknow' );
                }
            }
        });
    },
    /**
     * Get the SNAPI authentication key.
     */
    getSNAPIauth: function() {
        if (unsafeWindow && unsafeWindow.SNAPI) {
            return unsafeWindow.SNAPI.getAuthInformation();
        } else {
            var elt = document.createElement("div");
            elt.setAttribute("onclick", "return SNAPI.getAuthInformation();");
            return elt.onclick();
        }
    },
    /**
     * Decode a base64 MW url params
     * @param {String} encodedText
     * @return {Object}
     */
    decodeURL: function(encodedText) {
        try {
            var rgx, text = Base64.decode(encodedText);
            if ( (rgx = /i:\d;s:\d+:"([^"]+)";i:\d;s:\d+:"([^"]+)";i:\d;s:\d+:"([^"]+)";/.exec(text)) ) {
                return {
                    'next_controller' : rgx[1],
                    'next_action'     : rgx[2],
                    'next_params'     : Util.uSplit('?'+rgx[3])
                };
            }
            return {};
        }
        catch(err) {
            Logger.error('decodeURL: '+encodedText);
            return {};
        }
    },
    /**
     * Get user ID from script code.
     * @return {Number}
     */
    getUserID: function() {
        if (unsafeWindow.User && unsafeWindow.User.id) {
            return unsafeWindow.User.id;
        }
        var match = /'sf_xw_user_id':\s?'([^']+)'/.exec($('body').html());
        if (match) {
            return unescape(match[1]);
        }
        return null;
    },
    /**
     * Update Mafia Wars from a HTML text response.
     * 
     * @param {String} htmlText
     * @return {Boolean}
     */
    update: function(htmlText) 
    {
        try {
            var script = h$(htmlText).find('script:regex(text,local_xw_sig =|var user_fields)');
            
            if (script.length > 0) {
                $('#sf_updater').append(script);
                return true;
            }
        }
        catch(err) {
            Logger.error(err);
        }
        return false;
    },
    /** 
     * @param {String} url
     */
    updateUri: function(url) {
        global.uri.cb  = (Util.doRgx(/cb=([a-fA-F0-9]+)/,  url).$1 || global.uri.cb  || '');
        global.uri.tmp = (Util.doRgx(/tmp=([a-fA-F0-9]+)/, url).$1 || global.uri.tmp || '');
    },
    /**
     * Get the current city ID.
     * 
     * @return {Number}
     */
    currentCity: function() {
        try {
            var cityId = String($('#mw_city_wrapper').attr('class')).substring(7);
            if (parseInt(cityId) < 1) {
                throw Error('unexpected cityId value.');
            }
            return parseInt(cityId);
        } 
        catch (err) {
            Logger.error(err);
            return 1;
        }
    },
    /**
     * get the current Page name.
     * 
     * @return {String}
     */
    currentPageName: function() {
        return $('#inner_page').attr('class');
    },
    /**
     * Return a valid internal Mafia wars url.
     * 
     * @param {String} controller Default is "index".
     * @param {String} action Default is "view".
     * @param {Number} city To force city ID, otherwise the current city is used.
     * @return {String}
     */
    getIntURL: function(controller, action, city, person) {
        var params = [
            'xw_controller='   + (controller || 'index'),
            'xw_action='       + (action || 'view'),
            'xw_city='         + (Util.isSet(city) ? city : MW.currentCity()),
            'xw_person='       + (Util.isSet(person) ? person : global.PERSON_ID),
            'cb='              + global.uri.cb  || ((new Date()).getTime() / 1000),
            'tmp='             + global.uri.tmp || ((new Date()).getTime() / 1000)
        ];
        return 'html_server.php?' + params.join('&');
    },
    /**
     * Return a valid external Mafia Wars url.
     * 
     * @param {String} controller Default is "index".
     * @param {String} action Default is "view".
     * @param {Object}  params {Name => Value} pairs.
     * @return {String}
     */
    getExtURL: function(controller, action, params) {
        var url = 'http://apps.facebook.com/inthemafia/track.php?';
        
        if (typeof(params) !== 'object') {
            params = {};
        }
        
        url += 'next_controller=' + (controller || 'index');
        url += '&next_action=' + (action || 'view');
        
        Util.each(params, function(name, value) {
            if (name == 'next_params') {
                value = escape( $.toJSON(value) ); 
            }
            if (name != 'next_controller' && name != 'next_action') {
                url += ('&' + name + '=' + value);
            }
        });
        
        return url;
    },
    /**
     * Return the profile url of the user
     * 
     * @param {String} user_id
     * @return {String}
     */
    getProfileLink: function(user_id) {
        if (!Util.isSet(user_id)) {
            user_id = global.USER_ID;
        }
        if ((user_id=String(user_id)).charAt(0) !== 'p') {
            user_id = 'p|'+user_id;
        }
        var id = Base64.encode(user_id);
        return 'http://apps.facebook.com/inthemafia/profile.php?id='+escape('{"user":"'+id+'"}');
    },
    /**
     * Return the profile url of the family
     * 
     * @param {String} family_id
     * @return {String}
     */
    getFamilyLink: function(family_id) {
        family_id = Base64.encode(String(family_id));
        return 'http://apps.facebook.com/inthemafia/family.php?id='+escape('{"id":"'+family_id+'"}');
    },
    /**
     * Return a valid gift link.<br><br>
     * Usage: <br>
     * <b>message:</b> {String} show overlay message.<br><br>
     * 
     * <b>req_type:<b> {String} set request type.<br>
     * <b>req_name:<b> {String} used with req_type to set request name.<br>
     * 
     * <b>giftId:</b> {Number} to set the gift ID.<br>
     * <b>giftCat:</b> {Number} to set the gift Category.<br><br>
     * 
     * @param {Object} options
     */
    getGiftLink: function(options) {
        try {
            if (typeof(options) !== 'object') {
                throw ReferenceError('options is not defined.');
            }
            if (typeof(options.success) !== 'function') {
                throw ReferenceError('callback is not defined.');
            }
            var sUrl = '';
            if (options.giftId) options.req_type = 'gift';
            
            switch(options.req_type) {
                case 'gift':
                    sUrl = MW.getIntURL('requests', 'friend_selector') +
                        '&free_gift_id='+(options.giftId || 0) + 
                        '&free_gift_cat='+(options.giftCat || 1);
                    break;
                    
                case 'simple':
                    sUrl = MW.getIntURL('requests', 'friend_selector_simple', options.city) +
                        '&req_type=' + options.req_type +
                        '&req_name='+options.req_name;
                    break;       
                           
                case 'safehouse':
                    sUrl = MW.getIntURL('safehouse', 'safehouse_request');
                    break;
                    
                case 'masteryboost':
                    sUrl = MW.getIntURL('requests', 'friend_selector') +
                        '&req_controller=AsnSocialJob';
                    break;
            }
            
            if (sUrl.length < 1) {
                Logger.error('getGiftLink: '+Util.toJSON(options));
                return;
            };
            // send request
            httpAjaxRequest({
                url      : 'remote/' + sUrl + '&fbml_iframe=1',
                data     : options.params,
                liteLoad : 1,
                message  : options.message,
                success  : function(htmlText) {
                    var request1 = Util.doRgx(/<fb:req-choice url='([^']+)'/, htmlText).$1;
                    var request2 = Util.doRgx(/MW.Request.setData\('([^']+)/, htmlText).$1;
                    if (request1) {
                        options.success(request1);
                    } else if (request2) {
                        options.success(MW.getExtURL('freegifts','accept_gift')+'&next_params='+escape(request2));
                    } else {
                        options.success();
                    }
                }
            });
        }
        catch(err) {
            Logger.error(err);
        }
    },
    /**
     * @param {Number} giftId
     * @param {Number} giftCat
     * @param {Function} callback function(data, msg, title)
     */
    getGiftRequest: function(giftId, giftCat, callback, params) {
        var url = MW.getIntURL('requests', 'friend_selector');
        giftId = '&free_gift_id='+(giftId || 0);
        giftCat = '&free_gift_cat='+(giftCat || 1);
        httpAjaxRequest({
            url      : 'remote/' + url + giftId + giftCat + '&fbml_iframe=1',
            liteLoad : 1,
			data     : params,
            success  : function(htmlText) {
                var $container = h$(htmlText).find('div.request_item_container');
                callback(
                    Util.doRgx(/MW.Request.setData\('([^']+)/,  htmlText).$1,
                    Util.doRgx(/MW.Request.setMsg\('([^']+)/,   htmlText).$1,
                    Util.doRgx(/MW.Request.setTitle\('([^']+)/, htmlText).$1,
                    Util.formatText(Util.trim($('div:first', $container).text())),
                    Util.doRgx(/mwfb\/graphics\/(.+)/, $('img', $container).attr('src')).$1
                );
            }
        });
    },
    /**
     * @param {Number} req_id
     * @param {Function} callback function(data, msg, title)
     */
    getCityRequest: function(req_id, callback) {
        httpAjaxRequest({
            url      : 'remote/'+MW.getIntURL('requests','pop_mfs')+'&request_id='+req_id,
            liteLoad : 1,
            success  : function(htmlText) {
                callback(
                    Util.doRgx(/MW.Request.setData\('([^']+)/,  htmlText).$1,
                    Util.doRgx(/MW.Request.setMsg\('([^']+)/,   htmlText).$1,
                    Util.doRgx(/MW.Request.setTitle\('([^']+)/, htmlText).$1
                );
            }
        });
    },
    /**
     * @param {Number} giftId
     * @param {Number} giftCat
     * @param {Function} callback function(item_id)
     */
    getGiftItemId: function(giftId, giftCat, callback) {
        MW.getGiftRequest(giftId, giftCat, function(data) {
            var item_id;
            if (data) {
                try {
                   item_id = Util.parseJSON(unescape(data).replace(/\\"/g, '"')).item_id;
                }
                catch(e) {
                    Logger.error('getItemId: '+e.message);
                }
            }
            callback(item_id);
        });
    },
    /**
     * Deposit the amount of cash specified.
     * 
     * @param {Number} city city id where to deposit
     * @param {Number, String} amount cash to deposit
     * @param {Function} callback return server response or an error message.
     */ 
    deposit: function(city, amount, callback) {
        var url = 'remote/'; 
        if (parseInt(amount) < 11) {
            callback('Can\'t deposit.');
            return;
        }
        if (parseInt(city) === 5) {
            url += MW.getIntURL('propertyV2', 'doaction', city);
            url += '&doaction=ActionBankDeposit&building_type=6&city='+city;
        }
        else if (global.city(city).enabled===true) {
            url += MW.getIntURL('bank', 'deposit_all', city) 
                + '&city=' + global.city(city).name.toLowerCase().replace(/\s/g,'_');
        } 
        else {
            callback('Error depositing cash: '+Util.setColor('unknow city id.','red'));
            return;
        }
        httpAjaxRequest({'url': url + '&amount=' + amount, 'success': function(jsonData) {
                var result;
                try {
                    result = jsonData.deposit_message;
                    if (typeof(result) == 'undefined') {
                        result = Util.parseJSON(jsonData.data).success_message;
                        if (/You cannot deposit that much/i.test(result)) {
                            if (UserConfig.main.autoDeposit && UserConfig.main.autoDeposit[city]) {
                                UserConfig.main.autoDeposit[city].active = false;
                            }
                            result = 'You can\'t deposit in this city, autodeposit has been disabled.';
                        }
                    }
                }
                catch(err) {
                    result = 'Error depositing your cash of: ' + amount;
                    Logger.error(err);
                }
                callback(result, jsonData);
            }
        });
    },
    evaluateUserFields: function(htmlText) {
        var user_fields;
        try {
            if (Util.isSet(htmlText.user_fields)) {
                user_fields = htmlText.user_fields;
            } else {
                user_fields = Util.parseJSON(Util.trim(htmlText)).user_fields;
            }
        }
        catch(err) {
            var data = Util.substr(htmlText,'var user_fields', 'user_fields_update');
            if (data) {
                eval(Util.substr(htmlText,'var user_fields', 'user_fields_update'));
            }
        }
        finally {
            return user_fields;
        }
    },
    /**
     * Collect clan experience.
     * @param {String} type
     * @param {Function} callback
     */
    collectClanXP: function(type, callback) {
        httpAjaxRequest({
            'url': 'remote/'+MW.getIntURL('clan','collectProgress')+'&exp_type='+type, 
            'success': function(data) {
                callback&&callback(data.msg);
            }
        });
    },
    /**
     * Collect properties.
     * @param {Number} city
     * @param {String} [type] optional
     * @param {Function} callback
     */
    collect: function(city, type, callback) {
        var url = MW.getIntURL('propertyV2', 'collectAll', city)
        var s_one = MW.getIntURL('propertyV2', 'collect', city);
         
        if (Util.isFunc(type)) {
            callback = type;
        } 
        else if (!Util.isFunc(callback)) {
            return;
        }
        
        if (isNaN( city = parseInt(city) )) {
            city = 1;
        }
        switch( city ) {
            case 1: url += '&cityCode=nyc';      break;
            case 2: url += '&cityCode=cuba';     break;
            case 3: url += '&cityCode=moscow';   break;
            case 4: url += '&cityCode=bangkok';  break;
            case 5: url += '&cityCode=vegas';    break;
            case 6: url += '&cityCode=italy';    break;
            case 7:
                switch(String(type).toLowerCase()) {
                    case 'cash'     : url = s_one + '&cityCode=brazil_Cash&building_type=1';     break;
                    case 'refinery' : url = s_one + '&cityCode=brazil_Refinery&building_type=4'; break;
                    case 'barracks' : url = s_one + '&cityCode=brazil_Barracks&building_type=5'; break;
                    default         : url += '&cityCode=brazil_Properties';                      break;
                }
                break;
            case 8: url += '&cityCode=chicago';  break;
            case 9: url += '&cityCode=london';  break;
        }
        httpAjaxRequest({
            url: 'remote/' + url + '&requesttype=json',
            success: function(jsonData) {
                try {
                    var data = Util.parseJSON(jsonData.data);
                    var message = (data.description||data.success_message);
                    callback({
                        success: true,
                        message: message.replace('%ENERGY%','0').replace('%STAMINA%','0'),
                        cash: parseInt(data.cash)
                    });
                }
                catch(err) {
                    callback({
                        success: false,
                        message: 'You can\'t collect.',
                        cash: 0                  
                    });
                }
            }
        });
    },
    /**
     * Collect stamina PowerPack
     * @param {Function} callback
     * @param {String} [type]
     */
    usePack: function(callback, type) {
        var url = 'remote/'+MW.getIntURL('module','usePowerPack');
        httpAjaxRequest({
            'url': url + (type=='hospital'?'&responseType=hospital&packType=1':''),
            'success': function(data) {
                var result = false;
                if (data && data.user_fields) {
                    if (type=='hospital') {
                        result = {
                            'health'  : parseInt(data.user_fields.user_health),
                            'success' : (data.heal_success == 0),
                            'timer'   : 600
                        };
                        result.message = 'PowerPack:<br>Health boosted to '+result.health+'.';
                    }
                    else  {
                        var success = false;
                        Util.each(data.responses, function(i, response) {
                            if (response.func=='usedPower' && response.data) {
                                success = (response.data.success == 0);
                            }
                        });
                        result = {
                            'stamina' : parseInt(data.user_fields.user_stamina),
                            'success' : success,
                            'timer'   : 28800
                        };
                        result.success = (result.stamina > parseInt(data.user_fields.user_max_stamina));
                        result.message = 'PowerPack:<br>Stamina boosted to '+result.stamina+'.';
                    }
                    result.user_fields = data.user_fields;
                }
                callback&&callback(result);
            }
        });
    },
    /**
     * Heal in specified city.
     * @param {Number} to_city 0 current city, 1 force new york.
     * @param {Function} callback
     */
    heal: function(to_city, callback) {
        var url = 'remote/' + MW.getIntURL('hospital', 'heal', to_city);
        if (to_city === 1) {
            url = 'remote/' + MW.getIntURL('hospital', 'heal') + '&xcity=1';
        }
        httpAjaxRequest({'url':url, 'success': function(jsonData) {
            try {
                callback(jsonData.hospital_message);
            }
            catch(err) {
                callback('Error healing at '+(to_city===1 ? 'New York' : 'Current city'));
            }
        }});
    },
    /**
     * Get the Wait heal timer.
     * @param {Function} callback
     */
    getWaitHealTimer: function(callback) {
        httpAjaxRequest({
            'url': 'remote/' + MW.getIntURL('hospital', 'view'), 
            'success': function(htmlText){
                var waitHealTimer = 0;
                var $script = h$(htmlText).find('script:regex(text,var PowerPacks)');
                if ($script.length > 0) {
                    waitHealTimer = Util.doRgx(/waitHealTimer:\s*(-?\d+),/i,$script.text()).$1;
                }
                callback&&callback(parseInt(waitHealTimer));
            }
        }); 
    },
    /**
     * Travel to the specified city.
     * @param {Number} destination
     * @param {Function} callback
     * @param {String} [selector]
     * @param {String} [from]
     */
    travel: function(destination, callback, selector, from) {
        var cityRegex = /current_city_id'.\s*=\s*parseInt."(\d)".;/i;
        var message = selector ? 'Traveling to '+global.city(destination).name+'...' : null;
        
        httpAjaxRequest({
            url: 'remote/' + MW.getIntURL('travel', 'travel'),
            liteLoad: 1,
            message:  message,
            selector: selector,
            data: {
                'destination' : destination || 1,
                'from'        : from || 'index',
                'zone'        : 1,
                'nextParams'  : ''
            },
            success: function(htmlText) {
                var cityId = Util.doRgx(cityRegex, htmlText).$1 || 0;
                Logger.debug('MW.travel: '+ cityId);
                callback && callback(parseInt(cityId), htmlText);
            }
        });
    },
    /**
     * Load specified url
     * @param {String} url
     * @param {String} selector
     * @param {Function} callback
     */
    goPage: function(url, selector, callback) {
        var param = Util.uSplit(url);
        httpAjaxRequest({
            url: url,
            liteLoad: 1,
            message:  'Loading page '+ (param&&param.xw_controller||'...'),
            selector: selector,
            success: callback
        });
    },
    /**
     * Returns all mafia members
     * @param {Array} users
     * @param {Function} callback
     */
    getMyMafia: function(users, callback) {
        if ( !Util.isFunc(callback) ) {
            return;
        }
        if (!Util.isArray(users)) {
            callback&&callback();
            return;
        }
        httpAjaxRequest({
            'url': 'remote/' + MW.getIntURL('friendladder','friend_actions') + '&friends='+Util.toJSON(users), 
            'success': function(jsonData) {
                try {
                    callback(Util.parseJSON(jsonData.data).json_data);
                }
                catch(err) {
                    Logger.error(err);
                    callback();
                }
            }
        });
    }
};
// ==Script==
// @id        Facebook.js
// @author    Dakam
// @memberOf  MWAddon.js
// ==Script==

/**
 * @namespace facebook
 */
var facebook = {
    user: { 'first_name': 'Unknow' },
    initialized: false,
    session: { 'access_token' : null, uid: 0 },
    
    updateSession: function(callback) {
        var fb = facebook;
        var bTimeout = false;
        var timeout_id = setTimeout(function() {
            bTimeout = true;
            Logger.error('Request to "updateSession" failed "timeout".');
            callback({
                error: {message: 'timeout'}
            });
        }, Math.max(UserConfig.main.fbTimeout||0,30)*1000);
        
        MWFB.getLoginStatus(function(response) {
            if (bTimeout !== false) {
                return
            }
            clearTimeout(timeout_id);
            if (response && response.authResponse) {
               fb.session.access_token = response.authResponse.accessToken;
               fb.session.uid = response.authResponse.userID;
            } else {
                try {
                    var s_info = unsafeWindow.SNAPI.sessionInformation['1'].session;
                    fb.session.access_token = s_info.access_token;
                    fb.session.uid = s_info.user_id;
                } catch (e) {
                	Logger.error('Unable to load facebook session.');
                }
            }
            callback && callback(fb.session);
        }, true);
    },
    
    init: function(callback){
        if (facebook.initialized) {
            callback&&callback(true);
            return;
        }
        if ((MWFB = unsafeWindow.FB) == 'undefined') {
            callback&&callback(false);
            return;
        }
        console.log('init');
        /*
        MWFB.init({
          appId      : '10979261223',
          status     : true,
          cookie     : true,
          oauth      : true
        });
        */
        facebook.updateSession(function() {
            if (facebook.session.access_token !== null) {
                facebook.initialized = true;
            }
            MWFB.api('/me', function(user) {
                if (user && user.first_name) {
                    facebook.user = user;
                } else {
                    try {
                    	facebook.user.first_name = unsafeWindow.SNAPI.getCurrentUserInfo().name;
                    } catch (e) {
                    	Logger.error('Unable to load facebook user info.');
                    }
                }
                callback && callback(true);
            });
        });
    },
     
    // Graph API Request
    api: function(name, path, callback, data) {
        if (facebook.session.access_token == null) {
            callback&&callback({ error: {message: 'Invalid OAuth access token.'} });
            return;
        }        
        var bTimeout = false,
            url = 'https://graph.facebook.com',
            start_at = parseInt((new Date()).getTime()),    
            timeout_id = setTimeout(function() {
                bTimeout = true;
                Logger.error('Request to "'+name+'" failed "timeout".');
                callback({
                    error: {message: 'timeout'}
                });
            }, Math.max(UserConfig.main.fbTimeout||0,30)*1000);
        
        data = data || new Object();
        
        data['format'] = 'json';
        data['access_token'] = facebook.session.access_token;
        
        if (path === 'stream.publish') {
            url = 'https://api.facebook.com/method';
        }
        
        $.ajax({
            url      : url + (path.charAt(0)==='/'?'':'/') + path + '?callback=?',
            dataType : 'jsonp',
            global   : false,
            data     : data,
            success  : function (r) {
                clearTimeout(timeout_id);
                if (bTimeout === false) {
                    var delay = parseInt((new Date()).getTime()) - start_at;
                    if (r.error_code) {
                        r.error = {'code': r.error_code, 'message': r.error_msg};
                    }
                    if (r && r.error) {
                        Logger.error('Request to "'+name+'" failed "'+r.error.message+'".');
                    } else {
                        Logger.debug('Request to "'+name+'" completed. ('+Util.toDelayString(delay)+').');
                    }
                    callback(r);
                }
            }
        });        
    },
        
    // PERMISSIONS
    getAppPermissions: function(callback) {
        facebook.api('getAppPermissions', 'me/permissions', callback);
    },
    
    requestPermission: function(permissions, callback) {
        if (!Util.isFunc(callback)) {
            return;
        }
        MWFB.login(function(response) {
            if (response && response.authResponse) {
                callback( true );
            } else {
                callback( false );
            }
        }, {
            scope: permissions
        });
    },
    
    needAppPermission: function(perms, callback) {
        if (!Util.isFunc(callback)) {
            return;
        }
        var bNeedAsk = false;
        facebook.getAppPermissions(function(result) {
            var values = (result && result.data && result.data[0]) ? result.data[0] : null;
            if (!values) {
                facebook.requestPermission(perms, callback);
                return;
            }
            Util.each(perms.split(','), function(i, name) {
                return (bNeedAsk = (parseInt(values[name]) !== 1)) === false;
            });
            if (bNeedAsk) {
                facebook.requestPermission(perms, callback);
            }
            else {
                callback(true);
            }
        });
    },
    
    // REQUESTS
    sendRequest: function(args, callback, bOnlyInternally) {
        var data = args.data;
        args.data = '';
        args.message = String(args.message).replace(/&#39;/gi, "'");
        if (bOnlyInternally !== true) {
            facebook.updateSession(function(session) {
                args['method'] = 'apprequests';
                if (Util.isArray(args.to)) {
                    args.to = Util.toJSON(args.to);
                }
                MWFB.ui(args, postSend);
            });
        } else {
            postSend({request: parseInt((new Date()).getTime()/1000)});
        }
        var retries = 3;
        function postSend(response) {
            if ( !(response && response.request) ) {
                callback(false);
                return;
            }
            var url = 'remote/' + MW.getIntURL('requests', 'postSend');
            url += '&rid=' + response.request;
            url += '&data=' + data;
            url += '&to=' + args.to;
            httpAjaxRequest({
                'url': url,
                'success': function(r) {
					if (r && r.data && r.data.reqsentmsg) {
						callback($(r.data.reqsentmsg).find('div:first').html());
					} else {
						callback(false);
					}
				},
                'error': function(msg){
                    if (retries > 0) {
                        retries--;
                        postSend(response);
                    }
                    else {
                        callback(msg);
                    }
                }
            });
        }
    },
    
    // PUBLISH METHODS
    streamPublish: function(args, callback) {
        var target, privacy, attach, actionLinks = args.actionLinks;
        
        function fixText(text) {
            return text.replace(/&#39/gi, "'").replace(/&#34/gi, '"')
                       .replace(/\{\*actor\*\}/gi, facebook.user.first_name);
        }
        
        if (!Util.isSet(actionLinks) && (args.actionText && args.link)) {
            actionLinks = new Array({
                'name': args.actionText,
                'link': args.link
            });
        }
        if (args.caption) {
            args.caption = fixText(args.caption);
        }
        if (args.description) {
            args.description = fixText(args.description);
        }
        if (args.name) {
            args.name = fixText(args.name);
        }
        if (!Util.isSet(args.target) || parseInt(args.target) === 0) {
            privacy = Util.toJSON(UserConfig.main.privacy);
        } else {
            target = args.target;
        }
        var feed = {
            'message'     : args.message,
            'link'        : args.link,
            'picture'     : args.picture,
            'source'      : args.source,
            'name'        : args.name,
            'caption'     : args.caption,
            'description' : args.description,
            'actions'     : actionLinks
        };
        if (Util.isSet(args.properties)) {
            attach = {
                'name'       : args.name,
                'properties' : args.properties
            }
            if (args.picture && args.link) {
                attach['media'] = [{
                    'type' : 'image',
                    'src'  : args.picture,
                    'href' : args.link
                }];
                attach['href'] = args.link;
            }
        }
        if (Util.isSet(actionLinks)) {
            actionLinks = Util.toJSON(actionLinks);
        }
        facebook.needAppPermission('publish_stream', function(success) {
            if (success !== true) {
                return;
            }
            if (UserConfig.main.publishPreview === true && !Util.isSet(target)) {
                if (Util.isSet(attach)) {
                    MWFB.ui({
                        'method'       : 'stream.publish',
                        'message'      : args.message,
                        'attachment'   : attach,
                        'action_links' : actionLinks
                    }, callback);
                } else {
                    feed['method'] = 'feed';
                    MWFB.ui(feed, callback);
                }
            }
            else {
                if (Util.isSet(attach)) {
                    facebook.api('streamPublish', 'stream.publish', callback, {
                        'message'      : args.message,
                        'target_id'    : args.target,
                        'privacy'      : privacy,
                        'attachment'   : Util.toJSON(attach),
                        'action_links' : actionLinks
                    });
                } else {
                    feed['method'] = 'post';
                    feed['privacy'] = privacy;
                    feed['actions'] = actionLinks;
                    facebook.api('streamPublish', (target?target:'me')+'/feed', callback, feed);
                }
            }    
        });
    },
    
    notesCreate: function(title, content, callback) {
        facebook.needAppPermission('create_note', function(success) {
            if (success === true) {
                facebook.api('notesCreate', 'me/notes', callback, {
                    'method'  : 'post', 
                    'subject' : title,
                    'message' : content
                }); 
            }
        });
    },
    
    notesGet: function(id, callback) {
        facebook.api('notesGet', (id||'me')+'/notes', callback); 
    },
    
    likeAdd: function(id, callback) {
        facebook.api('likeAdd', id+'/likes', callback, {
            'method'  : 'post'
        });
    },
    
    commentAdd: function(id, comment, callback) {
        facebook.api('commentAdd', id+'/comments', callback, {
            'method'  : 'post',
            'message' : comment
        });
    },
    
    // FRIENDLIST
    friendlist: function(callback) {
        facebook.api('friendlist', '/me/friendlists', callback);
    },
    
    friendlistGet: function(listId, callback) {
        facebook.api('friendlistGet', '/'+listId+'/members', callback);
    },
    
    friendlistCreate: function(name, callback) {
        facebook.api('friendlistCreate', '/me/friendlists', callback, {
            'method' : 'post', 
            'name'   : name
        });
    },
    
    friendlistAdd: function(listId, userId, callback) {
        facebook.api('friendlistAdd', '/'+listId+'/members/'+userId, callback, {
            'method': 'post'
        });
    },
    
    // RETRIEVAL METHODS
    /**
     * @param {String, Number} from
     * @param {Function} callback
     * @param {Object} data
     */
    getFeeds: function(from, callback, data) {
        data = data || {};
        data['date_format'] = 'U';
        facebook.api('getFeeds', from+'/feed', callback, data);
    },
    /**
     * @param {String, Number} from
     * @param {Function} callback
     * @param {Object} data
     */
    getLinks: function(from, callback, data) {
        data = data || {};
        data['date_format'] = 'U';
        facebook.api('getLinks', from+'/links', callback, data);
    },
    /**
     * 
     * @param {Object} ids
     * @param {Function} callback
     */
    getAppUsers: function(ids, callback) {
        facebook.api('getAppUsers', '', callback, {
            'ids': ids.join(','), 
            'fields': 'id,installed,name'
        });
    },
    /**
     * 
     * @param {Object} ids
     * @param {Function} callback
     */
    getUsers: function(ids, callback) {
        facebook.api('getUsers', '', callback, {
            'ids': ids.join(','), 
            'fields': 'id,first_name,name,picture,link'
        });
    },
    /**
     * 
     * @param {Function} callback
     */
    getAppFriends: function(callback) {
        var s = 'SELECT uid, name FROM user WHERE is_app_user AND uid IN (SELECT uid2 FROM friend WHERE uid1 = me())';
        facebook.updateSession(function() {
            facebook.api('getAppFriends', 'fql', function(r){callback(r&&r.data)}, {'q': s});
        });
    },
    /**
     * 
     * @param {Function} callback
     */
    getGroups: function(callback) {
        facebook.needAppPermission('user_groups', function(success) {
            var groups = {'0': 'My Wall'};
            if (!success) {
                callback(groups);
                return;
            }            
            facebook.api('getGroups', 'me/groups', function(r) {
                if (r && r.data) {
                    Util.each(r.data, function(i, g) {
                        groups[g.id] = g.name;
                    });
                }
                callback(groups);
            });
        });
    },
    /**
     * Get all members of a group.
     * @param {Object} id
     * @param {Object} callback
     */
    getGroupsMembers: function(id, callback) {
        facebook.needAppPermission('user_groups', function(success) {
            facebook.api('getGroupsMembers', id + '/members', callback);
        });
    },
    /**
     * @param {Array} users
     * @param {Function} callback
     */
    queryUsersFeed: function(users, callback) {
        facebook.updateSession(function() {
            facebook.api('queryUsersFeed', 'fql', function(r){callback(r&&r.data)}, {
                'q': 'SELECT post_id,source_id,created_time,permalink FROM stream '
                +    'WHERE source_id IN ('+users.join(',')+') AND filter_key="'+AppInfo.id+'" '
                +    'AND created_time>0 order by created_time desc limit 300' 
            });
        });
    },
    /**
     * @param {Function} callback
     * @param {Object} limit
     */
    queryFeed: function(callback, args) {
        var source = 'source_id IN (SELECT uid2 FROM friend WHERE uid1 = me()';
        if (!Util.isObject(args)) {
            args = {}
        }
        if (!Util.isNumber(args.limit)) {
            args.limit = 150;
        }
        if (Util.isArray(args.flid) && args.flid.length > 0) {
            source += ' AND uid2 IN (SELECT uid FROM friendlist_member WHERE flid IN ('+args.flid.join(',')+'))'
            if (Util.isArray(args.uid) && args.uid.length > 0) {
                source += ' OR uid2 IN ('+args.uid.join(',')+')'
            }
        }
        else if (Util.isArray(args.uid) && args.uid.length > 0) {
            source += ' AND uid2 IN ('+args.uid.join(',')+')'
        }
        facebook.updateSession(function() {
            facebook.api('queryFeed', 'fql', function(r){callback(r&&r.data)}, {
                'q': 'SELECT strip_tags(attachment),post_id,source_id,target_id,created_time,permalink FROM stream '
                +    'WHERE ' + source + ') AND filter_key="' + AppInfo.id + '" '
                +    'AND created_time>0 order by created_time desc limit '+args.limit
            });
        });
    }
};
// ==Script==
// @id        Ajax.js
// @author    Dakam
// @memberOf  MWAddon.js
// ==Script==

/**
 * Cross-domain request
 * @param {Object} args
 */
function httpXDRequest(args, bSilent) {
    var errorCallback = Util.isSet(args.onerror) ? args.onerror : args.onload;
    
    if (global.is_chrome === true) {
        args.id = 'FBMWAddon';
        chrome.extension.sendRequest(AppInfo.chmextID, args, function(r) {
            if ( !Util.isSet(r) ) {
                if (bSilent !== true) {
                    showHelpPopup({
                        icon: 'info',
                        title: 'MWAddon Chrome Plug-in',
                        message: 'You\'ve request an action that require MWAddon Chrome Extension.'
                        +        '<br>You can install it clicking ' 
                        +        Util.setAnchor(AppInfo.chmext, 'here')
                    });
                }
                errorCallback(false);
            }
            else if (Util.isSet(args.onload)) { 
                args.onload(r);
            }
        });
    }
    else if (global.xd_support === true) {
        setTimeout(function() {
            try {
            	GM_xmlhttpRequest(args); 
            } catch (e) {
                Logger.error('httpXDRequest: '+e);
            }
        }, 0);
    } else {
        if (bSilent !== true) {
            showHelpPopup({
                icon: 'error',
                title: 'not supported',
                message: 'This feature is not supported by your browser.'
            });
        }
        errorCallback('Not supported.');
    }
}

/**
 * Use "XMLHttpRequest()" to do an ajax request.
 * 
 * @param {Object} args
 */
function httpRequest(args) {
    try {
        var requestId = String((new Date()).getTime());
        var xmlHttp = new XMLHttpRequest();
        
        if (!xmlHttp) {
            throw Error('Can\'t create XMLHttpRequest object.');
        }
        if (typeof(args.success) !== 'function') {
            throw ReferenceError('success is not defined');
        }
        if (typeof(args.url) !== 'string') {
            throw ReferenceError('url is not defined');
        }
        if (typeof(args.timeout) !== 'number') {
            args.timeout = Math.max((UserConfig.main.rqTimeout||0),30)*1000;
        }
        if (args.liteLoad !== 1) {
            args.liteLoad = 0;
        }
        if (typeof(args.error) !== 'function') {
            args.error = args.success;
        }
        
        // define url and params
        var connector = (args.url.indexOf('?') == -1) ? '?' : '&';
        var url = (/^https?/.test(args.url) ? '' : 
        global.location.protocol + '://' + global.location.host + '/mwfb/');
        
        var body = {
            'ajax': 1,
            'liteload': args.liteLoad,
            'sf_xw_user_id': global.USER_ID,
            'sf_xw_sig': unsafeWindow.local_xw_sig
        };       
        
        // add optional parameters.
        if (typeof(args.data) == 'object') {
            Util.each(args.data, function(name, value) {
                body[name] = value;
            });
        };
        
        global.AjaxRequests[requestId] = xmlHttp;
        // set timeout
        var nTimeout = setTimeout(function() {
            if (Util.isSet(global.AjaxRequests[requestId])) {
                httpAjaxStopRequests(requestId,'Timeout');
                args.error && args.error('timeout.');
            }
        }, args.timeout);
        
        xmlHttp.onreadystatechange = function() {
            if(xmlHttp.readyState == 4) {
                clearTimeout(nTimeout);
                delete global.AjaxRequests[requestId];
                if (xmlHttp.status == 200) {
                    args.success(xmlHttp.responseText);
                }
                else {
                    args.error(xmlHttp.statusText);
                }
            }
        };
        
        // send request
        xmlHttp.open('POST', url + args.url + connector + 'xw_client_id=8', true);
        xmlHttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
        xmlHttp.send($.param(body));
        
        return xmlHttp;
    }
    catch(err) {
        Logger.error(err);
        args && args.error && args.error();
    }
}
/**
 * Do an ajax request.
 * @param {Object} args
 * @param {Function} [callback]
 */
function httpAjaxRequest(args, callback) 
{
    var user_clicks = 1;
    var showOverlay = Util.isString(args.message);
    try {
        if (Util.isString(args) && Util.isFunc(callback)) {
            var sUrl = args;
            args = {'url':sUrl, 'success':callback};
        }
        if (!Util.isObject(args)) {
            throw ReferenceError('args is not defined.');
        }
        if (!Util.isFunc(args.success)) {
            if (Util.isFunc(callback)) { 
                args.success = callback; 
            } else { 
                args.success = Util.noop; 
            }
        }
        if (!Util.isFunc(args.error)) {
            args.error = args.success;
        }
        if (!Util.isSet(args.data)) { 
            args.data = new Object(); 
        }
        if (args.update !== false) {
            args.update = true;
        }
        try { user_clicks = ++unsafeWindow.User.clicks; } catch(err) {Logger.error(err);}
        args.data['clicks'] = user_clicks;
        
        if (showOverlay) { loadingScreen(args.message); }
        
        function updateFromJSON(data) {
            data = Util.parseJSON(data);
            if (args.update !== false) {
                try {
                    if (Util.isSet(data.questData)) {
                        unsafeWindow.MW.QuestBar.update(data.questData);
                    }
                    if (Util.isSet(data.user_fields)) {
                        unsafeWindow.user_fields_update(data.user_fields);
                        unsafeWindow.user_info_update(data.user_fields, data.user_info);
                    }
                    if (data.sk_update) {
                        unsafeWindow.SK.update();
                    }
                }
                catch(err) {
                    Logger.error(err);
                }
            }
            args.success(data);
        }
        function updateFromHTML(response) {
            MW.updateUri(response);
            if (args.selector === '#inner_page') {
                var $selector = $(args.selector);
                $selector.html(response);
                try {
                    var metadata = Util.substr(response, '!-- Current Page: ', '--\>', 1, 18);
                    if (Util.isString(metadata)) {
                        var controller = Util.doRgx(/([a-zA-Z]*)?(?:_controller) ([0-9]+)/, metadata);
                        if (controller.$1) {
                            $selector.attr('class', controller.$1 + '_controller');
                            unsafeWindow.User.page = controller.$1;
                        }
                        if (controller.$2 && unsafeWindow.User.bt !== parseInt(controller.$2, 10)) {
                            unsafeWindow.User.bt = parseInt(controller.$2, 10);
                            unsafeWindow.MW.reloadJS();
                        }
                        if (metadata.indexOf('sk_update') != -1) {
                            unsafeWindow.SK.update();
                        }
                    }
                } 
                catch (err) {
                    Logger.error(err);
                }
            } else if (Util.isString(args.selector)) {
                $(args.selector).html(response);
            }
            args.success(response);
        }
        function handleErrorResponse(success) {
            return (function(htmlText) {
                if (showOverlay) { 
                    loadingScreen(); 
                }
                if (success === true) {
                    if (( htmlText = Util.trim(htmlText) ).charAt(0) === '{') {
                        updateFromJSON(htmlText);
                    } else {
                        updateFromHTML(htmlText);
                    }
                } else { 
                    args.error(htmlText);
                }
            });
        }
        // send request
        httpRequest({
            url       : args.url,
            data      : args.data,
            liteLoad  : args.liteLoad,
            timeout   : args.timeout,
            success   : handleErrorResponse(true),
            error     : handleErrorResponse(false)
        });
    }
    catch(err) {
        Logger.error(err);
        loadingScreen();
        args && args.error && args.error(err.message);
    }
}

/**
 * Do an ajax request.
 */
function httpAjaxStopRequests(id, reason) {
    function abort(id, xmlHttp) {
        xmlHttp.onreadystatechange = function(){};
        xmlHttp.abort();
        delete global.AjaxRequests[id];
        Logger.debug('Aborted Request id: '+id+' '+(reason?reason:''));
    }
    if (id) {
        abort(id, global.AjaxRequests[id]);
    } else {
        Util.each(global.AjaxRequests, abort);
    }
}
/**
 * Show an error message about server http request response.
 * @param {String} msg
 */
function showBadResponse(msg) {
    showHelpPopup({
        icon: 'error',
        title: 'Bad server response',
        message: msg||'There is an error in the server response. Try again later.'
    });
}
// ==Script==
// @id        Resources.js
// @author    Dakam
// @memberOf  MWAddon.js
// ==Script==

var Resources = {
    // RAW data
    content : new Object(),
    // load image size
    _getImageSize: function(id, data) {
        var pic = document.createElement('img');
        var code = 'if(!window.MWAddonImageSizes) window.MWAddonImageSizes={};\n'
        +          'window.MWAddonImageSizes[this.id] = '
        +          '{id:this.id,width:this.width,height:this.height};';
        pic.setAttribute('id', id);
        pic.setAttribute('onload', code);
        pic.setAttribute('src', data);
    },
    /**
     * Add a new resource.
     * @param {Object} id
     * @param {Object} data
     */
    add: function(id, data) {
        if (Util.isString(id) && Util.isString(data)) {
            Resources.content[id] = data;
            if (data.indexOf('data:image') > -1) {
                Resources._getImageSize(id, data);
            }
        }
    },
    /**
     * Return the specified resurce.
     * @param {Object} id
     * @return {Object}
     */
    get: function(id) {
        return Resources.content[id];
    },
    /**
     * Return a TAG element with a picture in background.
     * @param {String} id
     * @param {String} [tag]
     * @return {jQuery}
     */
    getPicture: function(id, tag) {
        return c$(tag||'div').addClass('mwa_res_'+ id);
    },
    /**
     * Get the class name for the specified ID.
     * @return {String}
     */
    className: function(id) {
        return 'mwa_res_'+ id;
    },
    /**
     * Inject a CSS object into HTML.
     */
    insertCSS: function() {
        if (e$('#mwaddon_resources')) {
            $('#mwaddon_resources').remove();
        }
        var cssText = '', template = Base64.decode(
        'Lm13YV9yZXNfJHtpZH0gew0KYmFja2dyb3VuZDogdXJsKCR7c3JjfSkgdG9wIGxlZnQgbm8tcmVwZWF0'+
        'Ow0KYmFja2dyb3VuZC1zaXplOiAke3dpZHRofXB4ICR7aGVpZ2h0fXB4Ow0KYmFja2dyb3VuZC1wb3Np'+
        'dGlvbjogMHB4IDUwJTsNCm1pbi1oZWlnaHQ6ICR7aGVpZ2h0fXB4Ow0KbWluLXdpZHRoOiAke3dpZHRo'+
        'fXB4Ow0Kd2lkdGg6ICR7d2lkdGh9cHg7DQp9');
        Util.each(unsafeWindow.MWAddonImageSizes, function(id, pic) {
            pic.src = Resources.content[id];
            cssText += Util.render(template, pic) + '\n';
        });
        
        // ui-button
        cssText += Base64.decode(
        'LnVpLWJ1dHRvbiB7DQogICBib3JkZXItdG9wOiAxcHggc29saWQgIzcwNzA3MDsNCiAgIGJhY2tncm91bmQ6ICMxYTFhMWE7DQog'+
        'ICBiYWNrZ3JvdW5kOiAtd2Via2l0LWdyYWRpZW50KGxpbmVhciwgbGVmdCB0b3AsIGxlZnQgYm90dG9tLCBmcm9tKCM3MDcwNzAp'+
        'LCB0bygjMmEyYTJhKSk7DQogICBiYWNrZ3JvdW5kOiAtd2Via2l0LWxpbmVhci1ncmFkaWVudCh0b3AsICM3MDcwNzAsICMyYTJh'+
        'MmEpOw0KICAgYmFja2dyb3VuZDogLW1vei1saW5lYXItZ3JhZGllbnQodG9wLCAjNzA3MDcwLCAjMmEyYTJhKTsNCiAgIGJhY2tn'+
        'cm91bmQ6IC1tcy1saW5lYXItZ3JhZGllbnQodG9wLCAjNzA3MDcwLCAjMmEyYTJhKTsNCiAgIGJhY2tncm91bmQ6IC1vLWxpbmVh'+
        'ci1ncmFkaWVudCh0b3AsICM3MDcwNzAsICMyYTJhMmEpOw0KICAgcGFkZGluZzogMnB4IDZweDsNCiAgIG1hcmdpbi1yaWdodDog'+
        'MXB4Ow0KICAgLXdlYmtpdC1ib3JkZXItcmFkaXVzOiAzcHg7DQogICAtbW96LWJvcmRlci1yYWRpdXM6IDNweDsNCiAgIGJvcmRl'+
        'ci1yYWRpdXM6IDNweDsNCiAgIC13ZWJraXQtYm94LXNoYWRvdzogcmdiYSgwLDAsMCwxKSAwIDFweCAwOw0KICAgLW1vei1ib3gt'+
        'c2hhZG93OiByZ2JhKDAsMCwwLDEpIDAgMXB4IDA7DQogICBib3gtc2hhZG93OiByZ2JhKDAsMCwwLDEpIDAgMXB4IDA7DQogICB0'+
        'ZXh0LXNoYWRvdzogcmdiYSgwLDAsMCwuNCkgMCAxcHggMDsNCiAgIGNvbG9yOiB3aGl0ZTsNCiAgIGZvbnQtc2l6ZTogMTBweDsN'+
        'CiAgIHRleHQtZGVjb3JhdGlvbjogbm9uZTsNCiAgIHRleHQtdHJhbnNmb3JtOiB1cHBlcmNhc2U7DQogICB2ZXJ0aWNhbC1hbGln'+
        'bjogbWlkZGxlOw0KICAgZGlzcGxheTogaW5saW5lLWJsb2NrOw0KICAgbGluZS1oZWlnaHQ6IDE0cHg7DQp9DQoudWktYnV0dG9u'+
        'OmhvdmVyIHsgICANCiAgIGJhY2tncm91bmQ6ICMyYTJhMmE7DQogICBiYWNrZ3JvdW5kOiAtd2Via2l0LWdyYWRpZW50KGxpbmVh'+
        'ciwgbGVmdCB0b3AsIGxlZnQgYm90dG9tLCBmcm9tKCM4MDgwODApLCB0bygjMmEyYTJhKSk7DQogICBiYWNrZ3JvdW5kOiAtd2Vi'+
        'a2l0LWxpbmVhci1ncmFkaWVudCh0b3AsICM4MDgwODAsICMyYTJhMmEpOw0KICAgYmFja2dyb3VuZDogLW1vei1saW5lYXItZ3Jh'+
        'ZGllbnQodG9wLCAjODA4MDgwLCAjMmEyYTJhKTsNCiAgIGJhY2tncm91bmQ6IC1tcy1saW5lYXItZ3JhZGllbnQodG9wLCAjODA4'+
        'MDgwLCAjMmEyYTJhKTsNCiAgIGJhY2tncm91bmQ6IC1vLWxpbmVhci1ncmFkaWVudCh0b3AsICM4MDgwODAsICMyYTJhMmEpOw0K'+
        'ICAgY29sb3I6ICNkMGQwZDA7DQogICB0ZXh0LWRlY29yYXRpb246IG5vbmU7DQp9DQoudWktYnV0dG9uOmFjdGl2ZSB7DQogICBj'+
        'b2xvcjogd2hpdGU7DQp9DQoudWktbGVmdCB7DQogICAgZmxvYXQ6IGxlZnQ7DQp9DQoudWktcmlnaHQgew0KICAgIGZsb2F0OiBy'+
        'aWdodDsNCn0=');
        
        // insert the global css 
        c$('style', 'id:mwaddon_resources').text(cssText).appendTo('head');
        //$('<link href="http://fonts.googleapis.com/css?family=Crushed" rel="stylesheet" type="text/css">').appendTo('head');
    }    
}

Resources.add('ok_icon', 'data:image/png;base64,'
    + 'iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAALMUlEQVRogc2aa5Ac1XXHf/fe7p7Xzs6+d/YhrVZCoJJVKLIxxgkg5BiDZSJsqEROyXZCVb6lyo5SpEiIDSTOh2Aq4JQfFWMLOUUp/uAPSdkujKjYIkhIQhjQMvvelfY5+5zdefbM7Mz0dD50zz4shB5e'
    + 'aX2qbt2era0753/O/57zv7dH8Hto2g+0x6QQfymkvFchk8pW3SInjqW+mn4JKALlyv+KjXPzUpMvyhphi9d31e3e/cmmu+kIbGUmO8uYeZHuxHsk8rFJ64L1pfi/JM/gALHVRjtdsYrzB7d+afdn2j7HXHaRt+fOE8st4BE+tgVvI2TUVierFh8VQf53qWspBlhyox2v'
    + 'mEQc3lW3e/cdDZ/g9NRbdMV6MIsZUoUUUXOK8cwkDZ4w26p3BgOf9P8rUANovxcAtP/Q9vj1wFMHOw8xkBgmmp1GCkCAECCEIFfKM5ePsdl/C3qNvheoBzzaBvsOgFTihQObHkETGj2LfUjheL8mugIs20IKgUd5AKoAY8MBGD/UD2+rvnXvHQ2f4NT0WddJ6UQfAIFw'
    + 'S42UAikFZdsCUIDYUACeHxk12OLpA5sfYT6/wHRuBimF47hYKZCVzyEjSMHOQ5E53FK6oXtA2OLfP7Pps6Gwv4XfzL+LFBIpJVKq5WflftalRo03xPxSlNK0VSmj1oZlwPdD7746X/1X7gnvYzA5TL6cR0rpRhuEy6FKHloDLVgUmcqNEf9Z/KeACRQ2DIAQ4ujBrYfA'
    + 'hr7EgOu8w/e1zguq9AAhI8h7i6fJDGT+K3kyfRFIbhiAwBH/P+2qu73jltB23pg+jSYVQohLIi+EQCJprQoTNUdYTMQujjw5fgRYAFJsBIWqfhDo9Om+pw50PMJ4JspiIY5Uq6IvxJpMtPibKdkFRhKD5vSLs88Ai0AMyAH2Td/EQhdHH2jfT7URoifRi5ICTUo0JdGU'
    + 'QpNqea72BKk2gvTGz5PqSR1LnkoNAXNAGrcK3dQMBI9UfaGtqn3vPS330RWPUBYWSmpIN9pCuANQUtESaGY8PUJsbjYy8sz4T3AinwBKlTVvGoDqo8EaAUe/eMshFgtxJszJZe5Lscp54dAn7G3GsgtcWOwzp1+aew6HOgtAHrAr6940CgnEM/e27gu1BzbTl+xHkwql'
    + 'FJqS7rwy6j21VHuCvB97h0QkdSxxKjmEE/0Mq84CcJMyUHOkep9fD3ztgU37GU4PY1oZlJJus1qbAV3qNPjqGUkOMzczE7n4zOgHUuemAhBKvvDF7YcQUjCWHUMpBZQpY+HUTLlMpUZfPflyjoH5XjN6ZPay1LlpAOp+XHN4S2jr7q2hWzg1f5qp/DSqIhGEO9vOXGvU'
    + 'Yosy70y/RSKSOBY/lbgsdS4F8G06UfwNgj3YCCBBgV9wgf/h+8xfboEPdf5ITadfCzz9+c5HiWajzOSn0ZXmAlBrgHiUQZOvkcnkGPMz85Ghp0c+lDprAXyHx3y676W9LZ9iW+hW0sUMqWKS3kT3Q4P+3m/xLM/xBP8GLF0LEKnkt+9q+cNQ0BPk7dl3XOfdjVsBIBRS'
    + 'Slp9LZTtEhfmB83okakrUmf5O/gO+3y676W/3nmYP2rex3RmgflsEsoe7mq4j0e3fLm6rqnhmzzPy0AQR4df0RqP1u+r9dcd2Nf+KQbSgxTsJXRNw9A0dKWjKw1D6RiaRoO3nmojSGT2PPFI8tjiyStTp2KK/fznX2z/q44mX5jjEyeILyUxS1nSxQzpYopaby3bq3cQ'
    + 'LY3vzN2dvY3XOI57I3BZ51+urxFCvPrntx6qUUrRlexCUxp6ZWjOrCkNr+alxRdmNHGRoZHhSO/XBp4DZoB5oHClQEkU9+6qu53zsR4KZcevSjcs2iVGU+PkrQIPtf8prU2bvyC+y2nxoOgA9MsuasvDe5o+1tEZ2kYk1e04r6113nCz0eILkyuaRKJd5uSPoldNnYo5'
    + 'e0BAspBy/+SK2GVRJeiN91OlB9hV9zGUVDtmHpp8pVws/4n1K2vUzcayNf+4cY9P9z31YMfnGDVHSVspN9pOk9Kl86ykotaoIWgEeG30TTI92WMLJ+NXTZ2VDDi6FRvbAVzR4cKp1GOZceZzMSbMSWayc9xefwc7w7tv8/yZ8brvb733Ah5Wzh1IJV94oOOzGEpn2BxG'
    + 'kxq6Usucr2TAr3tp8NbTN9fHwkws0v+NoauqOpcCKBOJxM/TWb0ZKdSyDl+ylhhKXCBXck5KUgjihThT2Rm2Vm9nd/jjrcYu/b+rvhrYWwHR8nLzY52hzr0fbbqDrlQXtigvO796GJpGs7cJcylDdzRijl8HdSqmuIfkHLOPfrzxLoQQpAsZFgtxJjNTIEApp1Yr5ZS9'
    + 'ol2kaBdpD26iKRD2xEOxzxt79GnvDs+0sVn/+cHbDnmLFBkyB5c36orzDphao5YaI8TrF15n+tTs9yaOTb0BTLvRt67WeQfArxhM353a0Z/r3bk9uAMhBH2LAyBwD9VqZbg63aJEvpynraqN1mCbZ96YOaBa5P77Ov64/SP1uziXOAeStZHXnOHTvTR5m+iZ6WVwYOhs'
    + '99/1f49rqDqXAgCb13g1f2e2eoKxOwOqhjpPDaliGiGEG/0VIBX1iIClcp5GfxNtVe1ITTQ+2LGfsdwYsWLskpJpuHPY20y2kOXk4Cmz9xsDjxfjxUkXQJZroM5aAFDi17y59JFcMhGIfbrN10Gdt5Z0KY0Uwo2+dCSwdE9NUoEE0zJp9DfSWbMNKQV96V50pdaUTkNz'
    + 'Gletp4YaPcSvh04wfnzy2dlfzr8DTOEc0K+JOqsB4IIocJLzpduXEgu+2fsbfWEafY2ki2mkFGuiv6zdXWD5co6gUcVkfhIkl9Z9TSOg+Wj2NhOZ7magf+Bs95P9L+LwPsZ1UOe3AVRAlHiD8/YflJOLvtn7w742wv4weSuHLexVGZDu5lwBkbVMl/dqecNq2gqQsDdM'
    + 'ZinDGwMnzZ6vDzxeiBejwCzu4fx6Afz2iawMZEv/XPq+NVZ6oif5G0r2EltDnfg137Jmd0SY81y5PVtRlytzhWq1Ri1e5eX0yBnmT8Sez1wwozib1uQ6VO5q+yBhZgOl0v9Z57WPqmTcO39/g7eZZn8zeSvnltaV24NKZ9UqkZdrS6Zf89HkaaJ7upuB/sGz7/9D37pQ'
    + '58MALIMonCie13dpE0lv7L4Gf7PR7G8mV86BSyfNlQi60tDkpQ1LV4qwN4xZMDk5eMqMfL3v8cLi+lCnYh92qC8D2eQ3Uy9n38oe7I29m8lbWbYEN+PTvAi3O0shl2klVw0lJbV6LV7l4ezIWeZOxJ7PDK8fdSp2JW1vA1b+7aUJbZM6l66LP+wz/J72wCaWynmEsB1h'
    + 'plbkcaXz+nSHOr1uw+r6+551pc7VAlgGkXsrP2ls1s+ZdcmHA0aVp62q3XlrQmnVHnBnTdHiaSFbMDlz4YzZ9Y+9jxcWC+tKnWsBUDHLPJud9G7xnsvWpx6u8lR5WqvaKAvndmE1iDqjDq/ycnb0DGOvTjwbfWXmd25Y6wEAwEqfyUz6tnjPmXWZh/16wNMaaMMWNmVh'
    + 'oSuFX3caVv9sL6PDY2fffSJyQ6hzvQAArNTp9KR/i++cWZW8M+AP1LcGWpfFXqO3kVQuxfsTXeZ7T0ZuGHUqdr0vuq3Em8lJK2kdL21f+rTURENzIEytp5Z8Mcf7U+8xcTz6bPSV6RtGnYr9Lm/qrezFXLKcLf+y3FbsiGYn2kYTF43x2dG52dNzL3Z/q//nOCpzgRtA'
    + 'nYqtx28ldCAENLBy7bIExHFqfo51qvkfZOv1Yw8N51hpuGtaOCAK3EDnAYRtr/u+uqn2/+06dvy3qQOnAAAAAElFTkSuQmCC'
);
Resources.add('mwaddon_icon', 'data:image/jpeg;base64,'
    + '/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAUDBAQEAwUEBAQFBQUGBwwIBwcHBw8LCwkMEQ8SEhEPERETFhwXExQaFRERGCEYGh0dHx8fExciJCIeJBweHx7/2wBDAQUFBQcGBw4ICA4eFBEUHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4e'
    + 'Hh4eHh4eHh7/wAARCABAAEADASIAAhEBAxEB/8QAHAAAAQQDAQAAAAAAAAAAAAAABwAFBggBAgME/8QANRAAAgEEAQMCBAIJBQEAAAAAAQIDBAUGERIAByETMQgUIlFhcRUWIyRBQlKCkRcyM2KBof/EABkBAQADAQEAAAAAAAAAAAAAAAABAgMFBP/EACIRAAICAgIA'
    + 'BwAAAAAAAAAAAAABAhEDMRIhBBMiQWGh4f/aAAwDAQACEQMRAD8Apl1YS1/DFc66/wCP0Yyb07bd8W/WB682/YgYBd0/H1PqIMkf1ch4Ynj46r31aa0fEFAI7Ji9VdVp7PS0MMMtSqMeKpRvE0XgctGRIW8ePcnoAOdp+0+Q5xklioqmludos96eeOmuzW9pYXeKGSXg'
    + 'myocn0mGg33+3TTesEvMeYVOP4/bb5eDEW9JmtbwzSBNCQmIFioVtr7n286PgGXGu6mI43jOI2Wx1NNTC2VPzlTIYZOZmegnhlZmO+R5yaGvAAUDwOmntZmuJW+XHay7XCNq61W9IITJHIwp/wBs7SHQHk8SNHzrfQDT2O7Ezd0LVS10GRm2lrxLbayM0HqmlVaZpll/'
    + '5F5BmAj4+NE72fbpo7h9mMkxae30lDDcL7WSWcXa5RUtvbVvjLuo5sC2xqMnZC/l1JuxPd6Pt/Q5vAlYsb3KZJaFtHy3JwxHjx9JU+ft1Obj3mwy55lleS1FypluVbSfo2jneCVv3X5VgQgA0hMrEFiN68ex6Ar5Q9te4VctK1Hg+R1C1cDVFMY7bKRNEvHbr9PlfrTy'
    + 'P6h9+otIjxyNHIrI6khlYaII9wR1Yyl7yQVXde61lZlksON19tgo5oHSVkkj9CJJeBQh4pCYlPNfJ4+d+Oq9XSSOW51UsMkkkTzOyPISXYFjosT7n79AebpdLpdAZUbOt66lmJQYvFL6l5rqhCVI3Frxvx7EdRWBxHPHIVDBGDcT/HR9urR9jbFg15pJqbIO31DXWinp'
    + 'AbjcXWoWVHD8gTIraG10NKUI9yCpOoeRY+2jfFgeZOvYrbJb0rb3HQ2mRZ/XmWKEH6dljob30Wbf2swevqkwqHKqtc1alaqjkNP+5SMYhKsJbeweA5b1r6vJ2OHUVyK5WUdxqmvxi0JaqWBZEpUA8LKGYggHewAQvnewuyNnQnFryKnihrbnSxwfpyksjCKv9WFnecD0'
    + '4gq8TIWKlDonj5Ka0AvVl6lZlKPGXEAnS63mSSOVo5UZHU6ZWXRB/L+HWnUFRdLpdLoAmdjMHpskvlXeLqxNjsFHJcrkAgJcIP2cK72CzycVHIa1yJB1oyy/ZfOaE2i01KVFwuc7110r/l0ScM/HVOrgeEQKGLDzyd1GgNM24fkEmP8AZf8AVa2QlrvlFaa6saNSZDRU'
    + 'xAhjGvI5TCRt/ZCD79MFPiHcOWmeW24LlFZJKCfVhtU7gD+1etEldsvdQ62xszn5JXpKWghARIdy1AHIs3Jt+f5dHY8e+h0TfhBwaPI+4NHcnpZqpKVw6zcA0cMikk8ixA3x4ka2wJ/268gdS4HnVHYKl7vh2RUpjmXh8za50OmDFvdfbYH+f8nD4Sr1SY/SVlBNT3aa'
    + 'mZlmlFPH6a+qQoYM+gQo1xIL6I34PXkzSpP5Ol4eHmyUo1aX3r9BX8W9tFm79X61K0LpSpTIjR+5X5eMjn/20QPyA6E/RT+LC6wXjv8A5RUwUkFMYp46aX0TtZJIokjd/wD0qf8A5vzvoWdbR0qOZNtybezaJHlkWONGd3IVVUbLE+wA6NOKYJi9gyy14nfaNcnzeulS'
    + 'OW2tUNDbbPtSzGqkjIeZ4xpnRGVVAYFiRroTYrdWsWT2q9pEsrW+thqhGw2HMbh9H8Drp6ospWjvOSXfT1VVd454A7sVYJO25W2P5mTkh/B26kqGrKrx3dx3uDkHbyy6pJ6SjY0lHi1sFKsqGIcHT01MraD72WYhgfPjqO9ysK7l0NrxGpo8izvIbtkNPUzzWueCoFVR'
    + 'tAyK4ZBI7Hy58kDQA+/j2z/EC1fTVVdV03pX2sxuSxVFWF2xQ74ty1veyST+I+3XK3d9jTdrI8LY8pUx+ezpV8DyRZABoHW+OgB+SjqW7JZjKMZ7lYQ+KJjGRZ3dq2/UElXLa4o6mnqqT02AdCkcjMCCTs+NfxHTxJ3G72mkNq/03yC6x04FPXi9W2orJuTgcU9VEjkj'
    + '2GUj6ix2CD5101v3zgftVJhboJJ2xxLMtUUPJVUEFQdb4nYH9o663HvvTz9s5MVhjEdWcXgsTVYVg0gReJBOt60W9/6z1D7Ck1ojGV4FbL4LhJjtjvOJ5TQ07Vtbid35s8sA2zS0kjhXfiv1GNxy0GKs+uhF0Z7n3Vpq3uHgWWyScmxazU9HKADyqHhD/T5HkOX0fwJ6'
    + 'DHQg/9k='
);
Resources.add('info_icon', 'data:image/png;base64,'
    + 'iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAABKZJREFUSImdll1oHFUUx3/3zp2Z3Wyaj23SbNI2S02rrU1t+oUUjBYRqgj6UKHgx4NWCpZqEVTEBxV8UhFUKGJb0CIVK1ifKj4pioKVftivB61JWzHZZNPsJruT3dmZnbk+'
    + 'zG6yaVJBDxzuzNxz/v9zz7nn3hH8i2itASSgAKs2CsBv0FAIcUuMRWdqwAaQKJS87h8ujWwplPyNQoplIvLJtsTNC/fc2X26NWFnAAcIFiNa8KUGbp8fnkh/+dOVfU0x+7GVnS09cdMSShlR+EFAqeLpv28URl3PP7Fr+20fDfQtuwq4N5OIm8HDMIy9dOTHBwtu8MGm'
    + 'vp6VhmGK6RmfiLfBUUBrwiQMq/rCcGak2ZYvvrNn8KSUstxIMo+g7HrqkTe+eqqro/3gwOreeLFcvUVm50tLk+LS1RF3NJt//uvXHz3aFLf9+pxsjH5g7+FBLdTBvp7ueHaqQrkSzCpas3NTip2bukHreXPj+Qrprq6YocwPt+77ZIfvz+Kj6g/x+15LtKdSh+5anY7n'
    + 'HW9+XdDs6E+xfW0nAIUZl5NnMoiGBLhelQ19K+OXh0c/bt/51gBQaFjBLlxt7N+4Jt3nuAGuX52nFa9KwXFnwaYdF9erLrDLOz7b1q1a5QTsp2tXwwrWp6yW5viero6louwtnvdvzmYoewFCwHcXx9HCWNQu2dZKsqXp2Vxn53uMU4kIEnZ/qjPZV6osBNdas3Z5K6AZ'
    + 'Gp8BoMlSTDr+ol3ketDb3ZHOZSc2AKcjAqE3x2Jx6S4SfRCEvPnEAIac3Q8c+/5Pvvj5LwSaBfsXWJJISARb5gi07hbSYH56BEhJGApeOXqW+/uX8fC2XgC8QOMGtfBDDWEARESGlEhDgQ57oF4DraXrBcyuQEiQBmgB0uDUNYfVqSWz1NVQUw4FQgsMIGZKPK9Cvlhm'
    + 'LD/DWHYSdCjnCCA7li9S1iaWaWLZNsqSGEqgTIkUBlWtCYIArTWuHzDtS3y/ilNymcg7eK4L1QrosKaMzxGE+sK0UwqnfVNiWKA8UDYoC2FaIBWDfQ6FQgGATL7EpTEPwioEAWgFhhkBVz3QoSbU56HeB2X/PG5xBGWDUTNWJigLbVhow47SNlseAaYdqbIiW8OMfE0b'
    + 'KoUMvv/bHMHkiEPFOUZQ0UgjMpQNTlLeRFB7lwoMqzaqaAw8cIufMz5ahOjMh5nfoWPrZfAfp23FEqRRi8yCMOTE0ysYvL2VtiYTgBXtNg/dEef42XwEXM+7NCD7xzgl5xmGPis2FhkuT40xYBwgN3yMVL81F6VAKcVoIWC0GDUaWqNMhTAUmpqdsiFz0ac0dYCh6ZGG'
    + 'zd4ga/ZK2ppeYGn6bZYPWPU6YFi1nMei/PsV8F0I/KioVQ9GzvncuPYqufL7DB0K65DzD5TcGU1i/a8EpSuUJu4l1pIgkZwrvFG/ksPZxqKQgeunJsiPPsdU+QhDh8NGyMVv6/QDkFzXi6VeprljN8l0By1dAqs58qg4UMxqctcmKU4eF57/rp66cp2r3y6AuvXvAED6'
    + 'SSGTbUls826N2KylSAEIrceE1ueoVH8Jc8Uc1z9deCD9L0nthu7d/8nlH/ZrI5qfAOMrAAAAAElFTkSuQmCC'
);
Resources.add('ajax_loader', 'data:image/gif;base64,'
    + 'R0lGODlhEAAQAPYAAAAAAP///yoqKmpqap6enr6+vrq6upCQkFxcXCIiIlpaWtra2tbW1s7OzsjIyMDAwJSUlEREROLi4oyMjBISEhAQEDw8PHR0dK6urqCgoEBAQC4uLsTExOjo6HJyclRUVKKiooKCghwcHHh4ePDw8JaWlmJiYpiYmEhISLi4uPT09E5OTmhoaObm'
    + '5vj4+BYWFgoKCoaGhnp6eggICHx8fFZWVgQEBAICAj4+PjQ0NAYGBigoKFBQUA4ODiwsLBoaGiAgIDAwMDg4OEJCQh4eHiYmJgwMDCQkJISEhEpKSkxMTLKysqysrKSkpJycnLy8vMLCwjo6OoiIiMzMzBQUFNTU1HBwcKamptLS0uDg4F5eXrCwsOzs7HZ2dpqamsrK'
    + 'yjY2NjIyMhgYGEZGRoCAgGxsbGBgYKioqG5ubrS0tLa2ttzc3FhYWO7u7vLy8lJSUvr6+mRkZNjY2Orq6sbGxoqKitDQ0Pb29o6Ojt7e3qqqqpKSkn5+fgAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJ'
    + 'CgAAACwAAAAAEAAQAAAHjYAAgoOEhYUbIykthoUIHCQqLoI2OjeFCgsdJSsvgjcwPTaDAgYSHoY2FBSWAAMLE4wAPT89ggQMEbEzQD+CBQ0UsQA7RYIGDhWxN0E+ggcPFrEUQjuCCAYXsT5DRIIJEBgfhjsrFkaDERkgJhswMwk4CDzdhBohJwcxNB4sPAmMIlCwkOGh'
    + 'Ro5gwhIGAgAh+QQJCgAAACwAAAAAEAAQAAAHjIAAgoOEhYU7A1dYDFtdG4YAPBhVC1ktXCRfJoVKT1NIERRUSl4qXIRHBFCbhTKFCgYjkII3g0hLUbMAOjaCBEw9ukZGgidNxLMUFYIXTkGzOmLLAEkQCLNUQMEAPxdSGoYvAkS9gjkyNEkJOjovRWAb04NBJlYsWh9K'
    + 'Q2FUkFQ5SWqsEJIAhq6DAAIBACH5BAkKAAAALAAAAAAQABAAAAeJgACCg4SFhQkKE2kGXiwChgBDB0sGDw4NDGpshTheZ2hRFRVDUmsMCIMiZE48hmgtUBuCYxBmkAAQbV2CLBM+t0puaoIySDC3VC4tgh40M7eFNRdH0IRgZUO3NjqDFB9mv4U6Pc+DRzUfQVQ3NzAU'
    + 'LxU2hUBDKENCQTtAL9yGRgkbcvggEq9atUAAIfkECQoAAAAsAAAAABAAEAAAB4+AAIKDhIWFPygeEE4hbEeGADkXBycZZ1tqTkqFQSNIbBtGPUJdD088g1QmMjiGZl9MO4I5ViiQAEgMA4JKLAm3EWtXgmxmOrcUElWCb2zHkFQdcoIWPGK3Sm1LgkcoPrdOKiOCRmA4'
    + 'IpBwDUGDL2A5IjCCN/QAcYUURQIJIlQ9MzZu6aAgRgwFGAFvKRwUCAAh+QQJCgAAACwAAAAAEAAQAAAHjIAAgoOEhYUUYW9lHiYRP4YACStxZRc0SBMyFoVEPAoWQDMzAgolEBqDRjg8O4ZKIBNAgkBjG5AAZVtsgj44VLdCanWCYUI3txUPS7xBx5AVDgazAjC3Q3Ze'
    + 'ghUJv5B1cgOCNmI/1YUeWSkCgzNUFDODKydzCwqFNkYwOoIubnQIt244MzDC1q2DggIBACH5BAkKAAAALAAAAAAQABAAAAeJgACCg4SFhTBAOSgrEUEUhgBUQThjSh8IcQo+hRUbYEdUNjoiGlZWQYM2QD4vhkI0ZWKCPQmtkG9SEYJURDOQAD4HaLuyv0ZeB4IVj8ZN'
    + 'J4IwRje/QkxkgjYz05BdamyDN9uFJg9OR4YEK1RUYzFTT0qGdnduXC1Zchg8kEEjaQsMzpTZ8avgoEAAIfkECQoAAAAsAAAAABAAEAAAB4iAAIKDhIWFNz0/Oz47IjCGADpURAkCQUI4USKFNhUvFTMANxU7KElAhDA9OoZHH0oVgjczrJBRZkGyNpCCRCw8vIUzHmXB'
    + 'hDM0HoIGLsCQAjEmgjIqXrxaBxGCGw5cF4Y8TnybglprLXhjFBUWVnpeOIUIT3lydg4PantDz2UZDwYOIEhgzFggACH5BAkKAAAALAAAAAAQABAAAAeLgACCg4SFhjc6RhUVRjaGgzYzRhRiREQ9hSaGOhRFOxSDQQ0uj1RBPjOCIypOjwAJFkSCSyQrrhRDOYILXFSu'
    + 'Nkpjggwtvo86H7YAZ1korkRaEYJlC3WuESxBggJLWHGGFhcIxgBvUHQyUT1GQWwhFxuFKyBPakxNXgceYY9HCDEZTlxA8cOVwUGBAAA7AAAAAAAAAAAA'
);
Resources.add('ajax_error', 'data:image/png;base64,'
    + 'iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAACYQAAAmEBwTBV+gAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAQHSURBVDiNtZVtbxRVFMd/596ZnS1L210otMB2t62gmKA2gVLtC5Ug'
    + 'aGi0Eg2JfgY+AdAWWq0fgKJGE33ZqCQF5EFqCIoJ0RoTXpRATHjY0hYa2i6d7XZ2u7tzfbHttlgKiYn/5M5N7j33N+fMOfeMGGP4P2Q9yyCxSloUxBAioCKIyviW1R9/5N152jlZyeORkOwz6A5VVdWsa2uxamOAonB/hPzoCH4yOahE922acHsxJv9M8L2QbETbp/Sm'
    + 'aNOqtv1Yz20BgeJD5ichf+c23oWzFMbHB4IZ82HVw4epFcET66R8Nu9cKdv9diNbnsefm6Ns46YSbCk4OzmF0gpz+xaZ3369ZgK6tfbm8NgCSy2+QixPVp8MNr/WmF+zlqnECNlwFVODg/gpF991i3PKJTV0HXdiisnbCfIVYZztOxoV1qmlHpfAo9Xhg1a8fm+hJorr'
    + 'ptnyzbfE2ttRL71C8q9r+KkUJpVi5ubfeNqm4Xgvm7/6GncySWFdNToWbxp5+YV9y8DGtj+26hvw3WnKtr6ICgYBiB1pR2/fwaOh68zcukUmVM7mE58jWqMch+DWrRSmXaxYHB1wOh4DJ+pq6iUcaUIUAccmd+ki944dLYUV6zyKtfNVsmvWsfmLL0HrYqK7jpH/7Rec'
    + 'UBkoBRWVzWMtO1tgvo4tJ7BftMafngbbYnWkEvfsGRKFAvGu7iK8qxt8vwgAhjs7mLv0MxWxKH46Db6PaI2xVAy4WrwglhM0+Rz5sVEktAoVClERqcQ908/dQoG6T3vm4ytC7x4+TG7gAhXRDZjZWUw2g8lmAUE5gUjJYyydFKVAFORy+G6qCMnlyD94AIVCKfxiQgwm'
    + 'mynaLZSiZYEojKhFsAScZNFAFY2UYnY6hX5zNw3He0ueLqiup4eEVqR+PEV5TTUoQUQWwJnFqrDVMHYAbBuxbdKpNNa+d2noPbEY/qFD3D18qASPd39CsO0DZianEMsGuziU2P2w5Obdf6Pld5Q0Z9Oz8NY71PV8VoIk2o+QPXcGEJzW94h3dy/udXTA1Ss4kTCIDNZ8'
    + '39/8WB2LY3dh20gwSObGDXzPm89+J9mfzlFRG6UiFiU7cJ7ho50A+J6Hd30IFQwiWiNK9ZV4S3vFg9Y9g4hqyqZm8Naup2zbNuYuXqA8uuGx7z8zeh979x68oSHK0i7BcCVgBqrHk61cvpxfBp5o27uxoALnjEjjnOdh8gWcSBj5V/IAMo+mUZZFYHUIkGvay71edfp0'
    + 'qcMta5sTbW3lBUefRMneZbQn608t+fer+k6PLV18cqPftcsar15zEOEjDDtXAP5hjHTVfHfy/JM2V/yDLGj8wIF6Uf5+IyYoRpIGPymih9f3/XD1aeeeCf6v+gfLYZXwkd5f2QAAAABJRU5ErkJggg=='
);
Resources.add('menu_arrow', 'data:image/png;base64,'
    + 'iVBORw0KGgoAAAANSUhEUgAAACcAAAAyCAYAAADfuMIdAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABdRJREFUeNrsmWtoHFUUx+feOzu7yWZNmoeNu6liC9ZqNFAfKEitSbGPTyp+1E8KQkUpgoJSFaz6oT4+iFYFQW1FUFuaNgVtMWkiWGNN'
    + 'LFWhJGKa127SPDabTbLPeXjvcm84ezuTTTITUfDCYSbZmZ3f/s8995xzR1H+xQOt8Bq76y2H8zWHQ5Ip0rkFgCzJ1hROQGBuSDpCIIMfTenoaqglPmcQBByhKRzC5HDQFC8A1WUoR/h1PnD08c8EjE4tD455yd1rAicUYzB+agFqGj/H/MEMJMctK00Vww0gKfGZj8Mw'
    + 'qDJqFQ1blQ17DmpvJ2PGX7PRwkMD/DoC5qRpEzCeKIfAfFO5SgwsxEwLonCwFt9z95MaGfst90E+rWTo/9PUFqjNcUgZaFUKEgc3Eq5YQS1q11CrolatVSj1m3f5dgYqcaS+EYcHuoyoaRR+gMbvQyBQXClIJDAMAoCBBTnYOgZGrc4XQNc1PqRtQwgpofUkUrsJXTt8'
    + '3ogb+cI9qhSprgCJTVSKCV8OwGoZGLV61Y/CTY9qWxkcs6oGUl8VVupGeowEBYTrn2tAFcwvLAVABXdljQBjpmeVeoRx0Zds3B64ybSwv+NgptPIFb6j1FjWHCRANR+YZ0EOxhRbz8HC1CLUKu96LHCDUE5YzY1qVVUDrrv8oz5jmYtquVJQBfNNuLScR+Y6oBgDa2DQ'
    + 'VDlTlZQT4+bmQISiNp9+PdVumcvy3JIKqtJ8E3Otkrnzia8rX80tKCSXsjQjbyFTt0xVQwRj55S8ZUcgrFiouf2tVFc+Y7kCVEFACDgRodVVYd/1q1k8b91ZFqGuvr/jnQUlu7B6QFVK8EXBgB3ct5zRuKucAW779sCcCVxsrQRQdcihTL0QwUhxM27fXd6gqbj51IFk'
    + 'h56zYK0HAwYGjWGnnBwUTL1yTIjrgvGWXUEaTOiB1pcTulTFwKMdaFFuJZJ6ZcSFW+G4bU9FhIZRy8lX4t8ZeqF6yQATFQ2Wi19VKsNh+tLcuhWOpt3BDSpGLW1vzLRl5swkLxJYsZDiz8s5ZQhkl2O9cGuxgqGNaoA82Pba9Mx83JjmHtI4B7FTzrHL8sqtRVG8o2JL'
    + 'qFp9+Itnxt9PzZoymHCtpYBGxT59YLImtunOYFPL3uo7pAIVrajBoZN4TZrlwd70xfZDM702keoId1VzrK6BW//4fv7SN/tjx1OzxjwPAt2mtSyCs6Q6rNDuEY+Vu3BqduCr/WNn0kljikdp1gawSDl55RaLYw5j76K192Ri5Mhz0XZDt67QPxN8KUkBuCXdKlbvPP9F'
    + 'aa+itad1JvrZs8Pt9DRKbYJaXIIz7JpwVXKnzi9mK3eKeLDO9ZyIx448P3KWno5QGwNwSd61Zbkght2cg+4UqhXaPIzcKdd9bHr0032DnaZhDXLVxqmx+TZLbZ7DQTBbtwo4oRq7MeHGrT8dnYp++dLQDxxslINNUpvhqgmX5kEwOAaEDlRjN8fjsfxwZs4gmXlDy+cs'
    + 'RKth0+dHZPO9lTVLg01GD79wuSubMgcA2MRKwGAu9YEthxCvhOtAUxMRPQS76fDUfdsdXXl8MvbhU32shxhyAybPOREMKVC2+6SEHNbKcKWTu7tbJ6Mf7+0/6wWYHK26VFNhkJQXE7K/jCC78v3csYmhj57u66SuH/UCTA4IxG8UsAiALXbyVLmrstrPJyb7P9nX15HP'
    + 'mOMcyjUYdKsiJWADwMGdTMsfJBoCS8yvp6cvUsXOpOcKKSnOI9I1mN0ijCQl4bZr4f+hai0o3HrpXOL8u4//fjSXNuf4ip/gUK7B7KoSeDPiSwsMCKM24q/GCCmxP1O/vPnIhUMUTN6fS3oB5lTPQTeLtW9RuVRSjyWu5Lo/f7H/PerKCX6NAExzKNdgpbb64ZaYtoI9'
    + '4YwXYKUqYTnnKiDFZZexm+4KbDlw8gsQ8fBS7yFML97klHpJYtqo+I+9wfnPvvty7GcdoltRPH5r+P9Y7fhbgAEAMJrdIgjV5tQAAAAASUVORK5CYII='
);
Resources.add('up_arrow', 'data:image/png;base64,'
    + 'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAJOgAACToB8GSSSgAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAARfSURBVFiFvVZdbBRVFP7OvXOnLchPKpRNLS1LKRQUywapiqQRYkqB'
    + 'grYQChIbtMSGJvpk4oMxgQQTIhqjxlQjJlAxGIwkxkZ9MD6ZoCIhhaZgKy1UbYAtpD/bn53dudeHmVnuTreyLYs3OTl3ZjPn+875zrl3SSmF6S4jf/HjABDv6/512jGm+6FYuHTdQ5V1xwiAKFy2N9b7x8//GwFRuGxDwca6o00NLwYBoFmIFrOodJ917fJPU41FU5VA'
    + 'FC2vKqja/Ulj/Z7CudkCCsBgNI5PP/+i96/vTzZaVzt+uG8ExKIV1Qs372lueL6uYHaWAYAAAqCAIcvGsZOn/u797sT+aHd7a8YJiOAjtUXVL3xYv7M2f6YwXGyXgLtGYhInvjrdd/Xb469Er1w8nU7ctHpALF65a9G2ve/tqt0WMBlDTDqkSQMnIpicsHt7Tf4pw/go'
    + 'p6TMHOtq+/KeCZjFj9YHn3vpyI6tm/MEI1hSJYAJKsGCCFBQMA2GupqtgW+yxPsPlIbMyOXzLdMmYC4pawjW7Dv87KbKeQYjxKUDkpy5p4ICY05DGIzw8NqKvIgVPTJnxWox2HHusykTMEtC+4PbXz605ZkNuYwIlu0CEwDpZE5Aogc4A6RSuDYwjs6BEcSkjQdDT+Zl'
    + 'Z4nDuSvXmLcvnm1Om4BZEno1uKPxYOXTFXOJaKLmbud7mXNGuBGxcCE8DEtKmBwQ3OmJmavK5+VkGYfml60R4bazH9yVgFkSeq14Z9Ob69etnQ0AMVsm6ax7BYATcKk/gp7BsQSocAl4tqDssdycbHEwECo3r5//7R0dL2kMzaWr31hS1/T6U0+UzzIYS4CQV24X3Xu2'
    + 'pERbeBiRWByCeYDJ4Dqhwc72obaWj9/+5/df3ppAgM2YVS0KSw/w/OLRO01GIAIWLF9Vsn5TdYDcZxDBsm2cuzEECaUBwgUk9J/5sS/S03mFM4ARwIjAGDDQ3TXj5qX2A+NDg61JEsjR4VYAKU+wOVX1Z2IbtwSIAFJAXElcCDvgJk+d+Ujvn71dXx+vSBVPX+ldRkSI'
    + 'J+ZfoeNWBLbSwZMJmJzA9Vm9ZwIAbAVwUui8PQJLSp/OydkL7pQ7YwSInBm/NWph3LYnzVx/x9MrQHoEOBFiysbNMQvCoLt0vEuAZVACRgrXR6PgzA/oPjMtewMQjJAmfpoSQCFqy7RK7+0zWgHO6D873kjRiBmtACM/IHyEJlbkPlZg8tLrpDI6BYzB6X5f06W6eDxC'
    + 'GT0HOKU+bBLZa1PgSTTtk5Ao6dYHAJpfVt7TcfTdYUbkXiyOLN6eMafkRATv8gn39PQTEXdjKN0r7QompZQOylxg5jPy7XXzAygAUvO66e+UUkrpgRgA7pqRwrjmuY+QHjgOwPZ5v9muSU8CPUOdgPCZoXmPiJe5FzQOIOZaXCPprxABIEP7QeLO0gPGXCAxSRX07/WM'
    + '/VXQvSeHSvwjcvvAL4m+n6wXvDVB4xRe30Mppf4F67SXpvDAfoAAAAAASUVORK5CYII='
);
Resources.add('down_arrow', 'data:image/png;base64,'
    + 'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAJOgAACToB8GSSSgAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAARDSURBVFiFtZddaBxVFMf/Z2Z2Nk1KEkuihSII1WJDW6yIVattLX60'
    + 'aX0piIK+iQULgtQXadAkJkR88lHqo1AffFCTbj4aslmiRZBaaZtufVAMKA1pXOtmE2d2Z+fe48PO3dxMZncnEoc9zJ3L5fzO1z33LjEzGj3mtu1HYdnNAEBUaxWpHwCA/fKffm7+h0a6rYZ0ANbuA+d3Hn/lQRDAvGoBEYE1KgeGCMmYH79wGcAzm2KAsbXNe/6xfQAR'
    + 'JDOEBCQYggHJgETwDsaFko+FTLMXy7k4i4AKGEBDuJQMyYBhxNMcywACIBgAN4YLAKYJmLWLZc0Tz05SkMZww6jAzc2NAEEC4BhwgwIjjHgRiFcDFKQAjeGuEMg5Hsx4/I0UIVZTEQH3hMSiW4LrCyQtgrGZESCq5B9MkfBFx0POLSFhEpIWwbbof4oA1sKJgD8KLgpl'
    + 'H7YGt03a3BqgIPSgVTiD8VveRUmKdXDbos3tAwh2AQI4CPj1bwc+yypQh9sWwdpoCsz2zqeMto49yuPVYwVo6jqQlFzp9YZRCbsOT4bgSZMgik77fV17TxNVzgwj0EsE5ObmvneXl28CAKnT0Ny2fae9a3+q49SZhztbmsDVTkZo39qMztYWGAaQczzccUt14bZFMPwS'
    + 'TPYrcwYhYRFIlDA6NHQlm04/x8yFNQYAgHnPvQ8k9x4cfvzN9/bd39YMCUBIhgw8d32BuSUHiQbwqDnySxjp7/322tj4CWZeqSY3fB8w2zt2NO05ePHR0+f272jdAhmEzjSAuSUHnpQbhsNzMdzfm7kxcekkMzs6b12tinzudvHaTPdP5wev/r7kVOGuL6LhZn04F//B'
    + 'N70fTN2YuNQdhkcaAABiJb/gXk0fm/1s8MqdZQeGAfxV9CLhUTtAwaWzguG+3onZycmTzFyMYtXchrLo5IjoheuQYzjT82SZjEh4rVSIlQJGBvpSN6fSp5i5XItTtw8wc56IjmVJju5+69zTtpmMBfeX8zwy0D+cTU+/zMx+PUbDRsTMBSI6/gtx6pG3ew7byS114V7+'
    + 'rkwNDXyVTU+/ysyikf5YnZCZV4ioexby4hNn3z+abGqJLMLS3ZxIDQ1+eSuTeZ2ZZRzdMTs2wMzO7ctTJ3785MNJKjnrCs/NLfqpocELtzKZ1+LCgdhnQdWIIhG99B3x1y/29HXb7a2wLYKzuOCPf/zR59lM5o2N6FNKNywAEg8dfnb43ekZeXZ0zOs6cuTT/6KHmdd3'
    + 'QgAgWnOlpdBbPYldhw59YRLN/zwz847yR/ctPOYIWNWAAKqLUeMdljCIUbm36GMZMQ/myv8sHajERKU+LG1samO1To+KAomQ+JqoObWOVREqAxQkUUd0w9QuYk2pD6BcRyhYwwCkFVKgoiFC3unhVBBTWx/2vhzyXH3rEWAATMwczr+e83Ba9G+1Xk8Bq9BqoLBUa4GZ'
    + '+V/8vLMP/1VGiAAAAABJRU5ErkJggg=='
);
Resources.add('thief_icon', 'data:image/png;base64,'
    + 'iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAORSURBVDhPldRrTJNnFAdwEudcdMucOmEKsnIL0NELpaVMoZcoiFYUitB2VlKtguIlJi5WU+WyoMZCGUwNIqLG'
    + 'Qp1IHZStA6sCBaxZoJS5LoWUljgDFAeB8QGC9m/5oDGD4vbh/fC+z3l/Oc85z3O8XC6Xl6dncnJy6fDw8OrR0dHPxsbGPp2amvpwofh5ocHBQZ+uzi66Xq9/Wq2qctbcqemr+6mux/jYeN9kMtGHhoZ85kPnYDabLfCM/DRStu8AmURycVhsFy+R5+KwOS9plMhXGd+I'
    + 'cam0FP39/YH/BudgP5SWyklh4QgOCED8xngI04WQSvdBKBC53zchwM8fzCg6VLduHfOIzczMLHLXZbm6qrp4rbcPSMSvQCaSQCVTQYuMApVCAyWCDFI4Ed4rP0exsrjZarWGvAu+zWxgYOBLxYXCv8JCw8COY2FzwmakpqRALBRCIt6F3SIhdvL54G3ZCnYsC0wGExXl'
    + 'FTZ3g7zfgG8xg6Et4ejBA2BEEMFwZ0IhEsHjbkC2OBmyTBEyBdsQvyEa0ZFUxMXEgBsTDX1TU/lsl+dgTqdz1ZVLFxVcJgPE0HAczDoAflISwgMJCPH3RTSFhMy9eyDcmY51a9ZAmiGeMHV1Mefd5uzHe5p7AkYkDcoiJdxnDE16PYIDQ7DI6wPsSErGsHMEL178jfTU'
    + 'NMStj3W1trbyPGLqarWERo2aLS46jE/A56eB4O5eAicOQYQgyOU5+MPyJ8Tu48GOZUOrbZB4zqz2riSRw8KKFavh+4UvIoIJuFF0Av84DDiVvQvB6/zgt9YfHy1ZhsOZ+9D4q07qEdPV3ZUUZm0H72sSju/m4eHVk7DqLqL3QSXs+svQKI9gbzIXPHoQ1AoZWpsfijxi'
    + 'DZqa/S0KKTpK9sBWfx4OnRLq84eRny2AUZUL+y9F6K8/h8cKIR6V5z5zOBwEj1h7m2Fr3dksqA6xYLx2AuY7+ei8nY/7V2T4vaYA5h/zYCg7Bq1sC/p+az6z4HUaHx//pKqs+Kb2ZBK0eekwXpfBfDsXFk0BOqtz0FHxLbQ5KdDIBWj8uT7jvXezr7c3tKWyAI3fCdCQ'
    + 'm4pHJfthuJyNB0opdHlpqM0R4Wl7k2lkZGTVe7HZALvdHvDc2t3SpirEE1UB2ipPw1z7PdqrlDC2G7gTExMf/6cR9CZoenp6scViIfeYu+nmbhO9x2ymu8dO0P8ejgv9sNDaa6ZGr5SeoeYHAAAAAElFTkSuQmCC'
);
Resources.add('config_menu_icon', 'data:image/png;base64,'
    + 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAC/ElEQVR42mNkAAJpaWnup0+ffmXAA/j4+Fjd3d3DVVVVFc+fP793+/btx0DijHZ2dv7zFyxYlJeXl7p1y5ZV2DQzMTEx5ufnz66srEj+/v3H/w0b1t+urKxy+vbt21NGoMkGy5Yv3/H371/W/Lx8pwcP'
    + '7l9EN4CRkZFv/4H9j02MTfgKCgpmzZkzpwEo/BKI/zGC5IE2aKelp/fFxsbqJycn+9y4fv00sgFAL9quXLnikKKiEgPQ9gPVNTWBCvIKGi9fvnzEiKROLDExcWpWVrYz0JCAS5cuHoJJ+Pr5tubm5lYBNTAoKSky3L93/92Xr1+58nLz9BjRXCsUGRnZV1xcEpSRkR52'
    + '5syZHUJCQhIrVq44efr0KTmgXxgkxCUY3r9//3/Z0mVTz507V4RuAAjwBwQGtlVXVyeUlZYVBwT4Z/z89VP/zes3P27dvn31zu3b54ExdgxoyDqg2o/YDAABnqCgoIk1NbVJM2ZMZ+Dl5X05a9bM5M+fvxwGyoGi+y88gLHp1tDQMExKStoCDAep7JxchiunVt08sWXm'
    + 'zLm7vvZjxBC6gKGhoV1CQsL65SuWCRnoG/5U5jn1PCfqnsKT65/fB1T/tb36gOEqTgNsbGx8gkOCly9btpRHT1f/76lTp2pYv1z+URvLUOoayiD1l1Hkx8SODzP61v0p/fCN4Q+KAR4eHtEuLi5zly1fyq6irPr/9evXk/fv318BlPrFxMSgtrKRYYtXmLLSz8//GGZM'
    + 'vb+oZxVD6ruvDL/ABgQEBKTExcVNb2puZJGUkGLg5+dfs2LFihRQKMNcqiLOELW4nmGmupEINwsnE8O8ma/WtSxmiGGUlJRU9/Hxad2xY8fR9PT0XqDNhydOnBgO1PQCLXhY5IQZwpY2MUzTMuDkZ+ViZCir/FbHKCwsbAgMtPLjx49vcHJyKm1rawv59+/ffRzRyyzB'
    + 'z+C7qJphjoQUI7N3wf9AkBfYgSGfC0xxKgcOHOgGZqq7DPgBExcbg+G//wz/f/xmuAAANAw4i9ZGbqAAAAAASUVORK5CYII='
);
Resources.add('freegifts_menu_icon', 'data:image/png;base64,'
    + 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAADA0lEQVR42n2TW0jTURzHv3/nnE7d1C3nLd3cbJuXTDfTckVpYiRSElgZ+LCHKAjxwSKhkL3Ug/UaROJL4gW7MKpZYFhSiWV4DVJDnTh1/r39ne5m2zr7g6Jo/d7OOd/zOd/f5VD4T/h8Pqp1aPTxz3k6'
    + 'O5lyf1/wUK/54yMfq6qqXFsaaueFe89aKwN4Aa2G8vJNhmEUoaGhug9Tlsq+ielTnvU1OB0OD3d6/JqhtrZxG9DX1yfWaDQrdrv9SKPxtfHrwCC1HBJxoEKbzl33AmaKB8H6KiyWGczMzuFmSfFTbVZmjUgkWmMB7e3t55VKZZNcLg8bGBlBo/EN+n9P4pgsAQuLS4g5'
    + 'rIEwWoKzKhnihEKIxSLw+fw/JL7QNH2VamhoSKcoqjsoKChywsNBsNuOxRUG0ugo6LI1kEqTMG/bQI/FCm8wH1lCPhSiCDidTo/ZbBZQdXV1gQqF4j5J4dbo3AJylApotVqEhYVBSF7k8XggD8DtdqOpqxtuH4VLudkgDowSieQCW8S2trZiInjnDAiEzTrHikldkJ+f'
    + 'D6/Xy6798aTzE9IS4nD8kBwul6siPj6+hQU0NzeLuVwu7eWFYGl2Bmurq34BCgoKoFKpWAd+kOFVB/Qnc3FQFAWbzaYkdRvbbqPRaJxa3LAnrS4vAy6nP0fodDqkpqaygGWy/7CrB3dLCsHlcJiY2NhIctm3DTCZTM9naPriFGlVdEgw6yAnJwdpaWns+bzViubBX6g+'
    + 'cwKbm5tdiYmJBbsGqaOj4w5jsz3oGhxGRqwEDocDmZmZyMjIYM/ppSW8H53Eldwsf03qk5OTb+8C6PX6cJKzqXNwWJcnl4F0hbW/BbDQNKaZDajFEXNjY2PnysrKBvaMsh+yES7sPa1Wqv0ppKSkbAOGJqZgZWzfzD96ywwGw+y+f8Efl69XS/NU0jYOh3NUJpOxAPKp'
    + 'fJ8Hhl+87R+90WKoWdyp3wPYckKKZyIDplOr1QyZOH1RUdHL/bT7ArYghYWFjwQCQX1paen4v3R/AeSaS1ILC2JNAAAAAElFTkSuQmCC'
);
Resources.add('inventory_menu_icon', 'data:image/png;base64,'   
    + 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAANkE3LLaAgAAAoFJREFUeJx1kstL1FEYhp9zzsw45VxSs5Fuk8NITldqMCWKIDCQiogW0UKoDCKCWrUOoj+gVYsocNGiRWBEi6A2CQXahJV2WdSgdNGiJh3NnzO/c2mhjZL2rc7ifR/e872f'
    + 'YIlJH01f3X9sf6ee0VMCgQqryOO7j2/l7+Wv/KsNLAVINTZmujpOrdeeDwJUOMhwLt+UJ79IuyRg2vzSH6cGMZ7BAQGtmBbjZimtWvCWbeeyNzP70scbdsR2xxpCcc+N4zHOpCkw7Sbiq1NrW+q31x38nBt9ALhFCULN5fa9h7PrjO9RcB8IBiUAvnaksisbk7tk45MH'
    + 'uZG/5kWATi8z3P+qt97f4sLxQIyIWIF1mh+M8b00SmSw2jtd3vPpGe8Xf+FEe/Ptg61Nh6rfI/WMJyKp1axZniYarAXrk+iTtLxLy6aGug2ZVCL1qO9DTwVw+dKZqx3ZVR0DAy9qvn7+JnhTxo0Itmw+QKxUz5cbz7G9RX4WCvLr6BfasptSieTWxNP+1w8DAL8nJ5sS'
    + '1SI1EqlhWbQWJSR+0THWk8M5qJuKUJWO4pxFOkNUlKLC2UxlB8boeFAb9rVuxvkeQkhAYLSPEALZkATncA4sIEtFgk7GKgCrjRZYVm5s5VH3NRz2n7YFAF7ZcqTrIt9z95kpq1IF4CQ+QoKqAhViZ3brAnOlMQZeDEEoPJfEzgOEFT5qtnNjDK8GhnAVo6gAjJk9RhkI'
    + 'YayeBxhEGTl7EkoptjUnsdYgpcLaOZNSvHw7AoDWGmdZkEBILQWApf3kBf437S0OEKhQECtMqZIvHCZ59vjh60gZcDgnhQQxF93N78ACdfFIdaFQLHbfuX9+osTwHy4bB5h5T+YrAAAAAElFTkSuQmCC'
);
Resources.add('stamina_menu_icon', 'data:image/png;base64,'   
    + 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAiRJREFUeNqUk/9qE0EQx7+7uVxySbSpv9LEai+toGiFKIhIoQYUVEQNgv+IkLyB7RO0PkEewb6B+ADS9G/xF4ptFUsba0tDrBd7aa65'
    + 'vV0ntWkaY4oOzN2xM9/Pzs7tQCmFhnezcv+psW6xho5jH/txa+Bp8H4tu19OV8D322beuCly9YXA8/8GrF5KZkI3xJhY0lF9wwqNtWJiML+UGIz+E0A/K/NiVoe7wiB2+nPgjpeKXPemF/6AdAA+x5Mp/TAzZQXwxSS8HYDzic/ovTwVHpHPugLm4slJfz8yap2WG7oK'
    + 'R/C8ys7Gk+ngsMp6XzmMoyxdHDVzHYAPfaZpnFb3tBDglRj0i1vb754hljvxENP8JzeVQzGCaAfxuANw6Aom9BCz1udVAZsUMFwER2vwvnC4ryjNVgg/sMGoNJ9iqaZO263fZhlGeYE+ZFVYwZ0LInTXhj5ch5IMTJPkdHGiEn464uvYQJpUhRagwqIBIsSOqZxcJMEZ'
    + 'QNr+1q0TfLstLEDPZQ4Por0CuUbnk/RBv85/QUI7JwmgdV7fDY5qmWIKb9sATl0tBhLSjDwStAslOr93bBPTBs5HhpqUhculoqX2NrEivSebq4BY4ZBV9lffesexUVZYE2K8qWPNSWSM4WXs5GRvxDfRQz02hlp4WQdq84D1HlbZccdHSstTzWlsAzTsxZHjZojznMH4'
    + '1d3Soay6UjO2lFPXyt+sveP8S4ABAGGU75AoIqjnAAAAAElFTkSuQmCC'
);
Resources.add('list_menu_icon', 'data:image/png;base64,'  
    + 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAC5ElEQVR42m1TSWjUUBj+XiaZySxtR51aXMBaa2m1FEVEVHA7ePKkCIL0VNFj8eDuyYMo9qAggicRBPHkQXFH8eClKFgUN0a0tbYVu02TTpKXt/i/uMBQXwgvhOTb/u+xyemKdDNpxsDwd2m6lFQQUmI2'
    + 'CAWt3cuWLr6L/yxWDQKlpWS24yQvUqkUGMFprSEJhAuBMIyquay7M5/LvpgDEAShEiJmnuchnU7TxwEaGxcmQGbxOEYYcdQX8pN+NdharK97OwcgCKosqFaJUSY/CimQslJQSkGREqGAfCEPzsX3ahhu7mhdPlgDQIJZGEUQxGZUGAApJByyxSwLnucjkzMAMSqed3TV'
    + 'ypYLNQBRFDLf9+HYNiSxmjgNuwEibEScI53NwnYy8P3ZI+2tzX01AJwAIh4RewYxqUjmQUFS+rRZMAHHFGhMqqa86SNdbW19NVPgUZRk4JB8ij+ZggGSSie5cMR4OvoKQzNjKGUaxtaUVl7jIj67s2uLz2a8WaVkzCqVym//xGobK8QW0/MXPopj/Vcg6UpTwDFZs3QK'
    + 've17XpfSDVvY5FRFQSumNCVObEYyJ88mC03V6H5+HMUMUHA0TQZYvWAN9rbtw/77J3Cms+ciG/s5oaIwYDFNIaLb2DAlMiBl6z0eDN9Cg1sE0yHyTgGH153Dw6EXuFO+js3zt02woZEfZEEwmQTGEs8mQE0KXkf3MBk+w56Oy3j85QY6SxtQ567ApZe9aKnnmJfaDvZ5'
    + '8LsKqUgUJDErKpKdsBtLU4WPGBg/j7WLurGjuRcWAV/o7wWPHsFJZdFSODTN3pe/kgLJ4pj/OwNmpwbBzQG3hw/SCIexfskBuJmNeFrugUNZKJSwa0nfVTbwoayogex3gHQmLQaXSqNphKbGVn4cT76dwkz0FQI52KjCdRZj66KTn1xd3MT6B95J6n9Cij9H2qgwt1I6'
    + 'sVVXb2Nav4EvR1jebqo0ZTpuCiFPty9bO/ELz+W0wwUdlkUAAAAASUVORK5CYII='
);
Resources.add('weapon_menu_icon', 'data:image/png;base64,'  
    + 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACE0lEQVR42q2Tz2sTQRTH3/zI7maboIbsph48VJLquU2JIEoPCh5aLKIgBS9eelChB8HiD0IVT4I/qij00H9B/EU9KIWCx4jQg1LbgiTZ0mZjutlNt5v91bc56KlQbR8Mb+YN7zNv5n2HwB6N7Asg13uc'
    + '/Fz8Ef4X4OnUq3zgBx7jzEMD1/PBRx/NgyCAMAzRh+D7HliWlZEkqSEI4vrDB3crHUDp68JmrabH264L7XYbh4vJLjiO20mKQBHExT0P1z7CRFGsLC0tFl8+fzxDpl5MrxnGhholFwoFaDYNKFeqoOs64GnAGQfTtIDgZaOKfKzQRVBaSecn702UyLUbN0dM00wxxgIu'
    + '8H48NbHlOBCE4ZFkIpm1W5sbRrOZrdfWulZWloFSCrlcL/TlB5aR+mjHLoxdH+/LKGrifvH2/OXRK0+kuDw+++EtCGIcFEWBVFo98+nju887Am7dKfJEl3yVUdLCdxhuGFb/+zevs6bV6lTRc7Rn7Mv83PSudXDh0ujgqlaZq1Y1sO0t6D6cKS18K+V3DRg+f1Gs1/Xf'
    + 'mqbJtm1DLBaDVOrQwD8p8fTg2dly+dc5Ve0GTVuFZFKe+QM4cWqIUQ6MhAEHCBkOjgJAjzFCMEYY5/CsUV8fiVrp+QF2I/f9L+DkEEVhxwgFAYMCZUSkBER8MBlbfIASctBx7GMIyciyxFAToecFjf35THuxbaMI8dyQO+T6AAAAAElFTkSuQmCC'
);
Resources.add('link_menu_icon', 'data:image/png;base64,'  
    + 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAwZJREFUeNp0U39IU1EU/vZckhJZQolhg0SWWqbgqEEF1d+NJDMESc0KN8hS26aSmSSas2Z/lNPIWoZm6WZh/oiIkERo/gg1JajQENLE'
    + 'uaGtua29d7v3mWFCDw7n3ffO+e75vnOOxDYwiP89EokEUqkUk5OTpuHhEQ0RBPh5HjHR0XUjo6MajuMgXQkmhIgJawE4TtJssw2k6nWXMD09A4fTgbZnz9UUhP3X/AVgaAEBAaJfBVDb1PQ49WpJMcorDCC8r9K56CpUqVTo6uhQ0/hlAHYTS7ZYrGLJEgZCK+J5Xl2g'
    + '16GisgrXy68paOiU0Wjc7PN6sxkVMIr/8l3HSgargdCzQP3MzHf88vmgOX9h8KfLJXk/PJodIYsE7/eLl3CrAZY5c6DYolg8Fc3c0IDSK5dhn50VpUpKOo7urk5sCgm1yrbvWAZYEY8JyYwhCzSZUkBZaQnSM85Qy4LqaBK62rsQH6d8aTRW5uNXEKSM+x8QkyDwGkIE'
    + 'EcTv5+nNxcjMzIZ92onu9h54l7yIikyoy8s/W5XRiKkvyAFarW0Y+TDWrNMXEvu8g4yPfyS6giLidrvJyZRTtBwkMjtmQG3aQ5DU+yBp1DLMMJ1uAKSBgYEW2qpknS4fd2pMoIk4cviQWPbct3lktWBI8OBW6Eaod0cBe8P16J+pwtgnaBwLtGG2/oFkvU6LGtNdlJYU'
    + 'KxYXf8D8wIyMzEzE7VGAULF5P3LlEcCCC9gVahC9XEa/C1BzAhWKTZjH7TI0NTYO1ZluK7ZFRGDJ7YHH4wWVBAIloggvQO4+IurFPDszvTkf7bHdYadc1hfQHmhzLuYNxsTGorPjBeQ75S0smY4Gej4bUPZ6uVvMszObNy4xMdH6pPkplEolgjeE3Nh/4CD6ensRn5Dw'
    + 'Sl+UrQUFWHLCOjFNBaPvfV8LRc/OSw5YGaTsXr252mZ7l8zQ/XTCtoaFvamqrDhXX2eZeBt8Ao/SIUu5ieqgUCSzstnYsORWLfJX1o9Kgi2rFtFJbWLNdq+NmWO78VuAAQCvqWY4vpJZwQAAAABJRU5ErkJggg=='
);
Resources.add('reminder_menu_icon', 'data:image/png;base64,' 
    + 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAm9JREFUOI2N0klIlFEAwPH/e986M6gzOpOayVRoJUYejBKqS4lLHYLoVBDZdujkIY8J3bPoZIZCCEHXoEPbKbKCFujQXoiag5WOM04647e9DmalIviHB+/w+PE2wbLyfcRV'
    + 'qKYBYQi83LvQ0b6UKDq0fNnfxOIk10OlqGi5pm/ef0SraNAQkmDyY6DSbx6Sf9tpH37+YVUg10O1LN83ZO06US1LKhG6DcJEIVH5abxPt9Pkx1qsg49fLQckgLATA9bGsmppzyOYA+UABUSQQRoORtW2UpyfN/3xAWsFMNdLvZ4obZZyFFH4Ct4EMAMqByoL3jcEKbT4'
    + 'uu3e6xsHVgDCEE16Ii6EGQUcEAo0DaT3B8mADjK2HnS3eTmgC0PYQnpgVi3ciAgWjqBcoACaAsMAZSOLw00rdqDmgw/B7DRoEQhc8HPg/gQ/A8IBTYBpIiyJjBY3ei+bSpYA0uSxn5kaU/kJQAdnCtxJCLIgCqADlgmWjohETGHZx5cA1klclc+e93989lU+Bb4DziR4'
    + 'aQh+geaCbf5BDDCilwpDB+JLnjF02rvr/xg/7g2/zgZT71HOLMqdBTe9MLws4KNUwJxbF++4Yl7v6rrw7yMtlrssymRRyTEZi7XIaHSPVl4Rk7EYwtIJAo/vXxyefm7CKKuhu/viYDgc6VgC/F/2apE0k4nd+obyc7I4emI8ZcpbD+Kk3WJOnTnL/fv36O8f2LMq8H/D'
    + 'dxr2dvYWDW6tb9xkmhaBEox+G1eWZdWtCQAoLY0mE/GyR+1trTWjYyna2ttHWlvbduhrBdLpzIht2/ufDD3bWVu7JYiEQy+SyeTMb4VU55rpNi9QAAAAAElFTkSuQmCC'
);
Resources.add('plugin_menu_icon', 'data:image/png;base64,' 
    + 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAChUlEQVR42o2TX0hTURzHf8c7N53NaWYroWJZONB0IiIjsKUvTgpHRG8VPfXSw14qQ2hOzR4yhaSwQIleknooIpKkECVzqYQoBAWJUjiU4f7cu3t3d++5p3Ov29qqQQd+/P7w+37O75xzL4Icq8VrNdU5'
    + 'bSMNltbTCDGG9ejXLysBv+epe2k6sw/lArgfV3suNfuGjpU1ACuxsC1uw4OFG4vP3XONOQEneg5bEYMqCAEJ5aHm4yddd9yV5ykgCsvBz/BqZmxCjiuDskgSikKQGJVXsgDtY/aprrYBJy/HAKE82GUohUgiDJzMgSDzgBUJQvEgxGQWImIY3k9MdqcBerMOtfTVzw5f'
    + 'eOKISqqI1UxtjkmsBtHipE9gESZHZ/rSAGOFAbX1OpY7O3w1rBzJaubxTsyrNcxBQhFBkHiYerh8CxUdKkBNndU+xDEVtfXV51w17aaUiMc7Is3TXBVy2jQRkJQEfBj+0Y8szt2ugUc9b/YV7wWZnlGAiCYQkoA45oHDYToBnQrTy/SH1zaX2AVzpdG2Oh7wogNnLd7h'
    + 'ez3dDEMomZ4dh6gPaSJ1VxWECdaOiTGBl7fXBzfGA9dpqqMmov0dFt9FX9PNOAnvXAbJdCidawAF4MX97yPBj6FrunJ9YWw2tIWMtqJ215D9dYlRh0RJgeLCfMhEEAJZa21DZpV4gcARQe+/Om9VX0Ff7Ci9kl+uP3LUdfBMVZXRkikgGUEqZpAONjkB3nWtnEo9oway'
    + '9zd+stoNdX+J05OQdE2g007f/ebJ/BKZWq9jrqyWNP5rV5IFApDohS6OrvZmAUouV7411ZtaUwJCfvs0JFlTaDH47Kf3z79xDzUz/N9SOVu/AItPaIZBhlrUAAAAAElFTkSuQmCC'
);
Resources.add('update_menu_icon', 'data:image/png;base64,' 
    + 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAuJJREFUeNqkU8lPE1EY/6ad6dAOSwttLcUolIrAASPVWEncIulBjVESl+jZg4l/ggdvnkz06EU8FA9iNMaIYvRgmhASUw2NQJQuLBZZ'
    + 'Oixtp515s/k9iojRxIOT/ObNm2/9fQtjmib8z8PS14n+pft4hP4ijyNuIzhEEYOJhq4TTdNAU9VfDnRdj4Fphi5F/CFFqWTEsgw8eTcP54837hbXiSebk8nsQlkydJhA8fefESz0hd6i6HV4YGg2ziAlqaCCIutAI6nEYATO6gv6HLvPHPZ02lgzgsECvzlQFcUPhtZz'
    + 'OuwN5ZYkqKoCIIqKxgQG32YKo+OL1VJZCa6KcmvkgHs/A/pF0zCcSGczA0L6mr18ML9SwtRNePRyKm5D1pqiACrF57Lrb17FZsqySgLikhTc5ea70OaIjnXYqAFRlJ6GaraeRk1kxBTeRx8+m6jkiE4sFuYjMvMlpnLdHf7ane4aVkGdYyh9UaEgy21WC+PQTANSmeU1'
    + 'XdNuYPS7iHi5WITC6hrNciidWeZRpwl1A2hzELGVgVTWjLyqmRvfRgmpcFwU+xFFCrTFMSmfj1pZVpA0s5YDg+oJW20ksvxhuSDX6gzLe9yCkp3LncLIQxzPw+Z8UIR9vjqnKBtgBbWENl+3d+H97DdxUgQu7WppwlqRozg03TJmgjI42dcTwjPs7mxtRR2Yy4oreB+h'
    + 'sooDQmLTX2YSVU4hmbUK6UBv2F6zwxXBWoQwVUiVGNh3oTeULFvAEOyQHJ9Oos1TOo0bDpDnmlwsPY4PDn9y1NlT83xd0jwULjZePleDxYN1Kw9jBSu46h0wFn0eJ5JEo88XX9+q1KAwcAUs3va0svds+fODUqezLSBAS7NN8ftMOkwGDoXdYsKCAdBx/Wooca+fmk1q'
    + 'ixNRhm4jwzD0B62qh7G7GsDTLkC1n2NI3gBv1zVo2NP2x5qJU5PGyJ2b2x3YEA6EHcH8Y4vpxmm0fD8EGADFhK0+biwMlwAAAABJRU5ErkJggg=='
);
Resources.add('run_menu_icon', 'data:image/png;base64,' 
    + 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAYJJREFUeNpi/P//PwMlgImBQsASGhqKIsDIyKgEpCyAmJcI/WdZ/v79iyzACzTAsqSkpFJUVET716/fDL9+/WQA0X/+/GX4/58RaAEr'
    + 'AwsLBwMzMxtDW1tFJsufP3+QbTczNzeP+vLli/aHDx8YQHIwDLIIGW8Bgo0bNz5gYWNjg9uuoKBgXlVV5UWM369evXrc1tb2NMvPnz9htpsGBgb6IrsIFzgJBHfu3DkhLCz8luXHjx8gMT5FRUU7ExMTi9+/f+OPNiYmhoULF+4AWnwGZDnLt2/fQIKWISEhXlDDCNl+'
    + '5ubNm8eBLv4EjkagAfxKSkoOZmZmpr9+/cKrmZWVlWHmzJl7vn//fgKeDoAhbh8dHe3+9etXgrYfOnTo/LVr144BDfj45s0bBpAels+fP5toaWkpsbCw4NX8/v37jz09PUeePn16GKgHEfWcnJyZwDCQIybqgPF/GRhOV4HMd0D8Foi/MQIJLiDmAXmHyOT/D4i/gzQD'
    + '8W/GAc+NAAEGAH6Du6zbRlpcAAAAAElFTkSuQmCC'
);
Resources.add('timer_icon', 'data:image/png;base64,' 
    + 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAANhSURBVDiNZZNvaBt1HMaf7+/u8stdclnb2W2ZY223adratHPNdMkyq8hWRaivHPjCPygdVCbKhm8EQV/o'
    + 'Br4QGfSldfO9RTu1IlWnNmTUZXNWdGOJTtc/4XLJpXfJNZfrnS9KR8UHnncPH3geeMj3fWzWp1MX929taxtTFTkZDqvbQ4qMWr2u2auNbLlSGX/skSO5zXnaAIy+doqSBxNnImHlpc8nJ7foJV3q6ekGAOTzeWzfscN95vjxlXqjcWE2O3f67Ntv+XcBI88+R4+mD/+8'
    + '+Pftzl+v5VqHhobAOUc6fYSICJnMrO84DrLZLB5OpoyOrj13pr/9buDj8XO+CADx3p738jf/6LSMSms6nUYgEIAkSbh6NeczxiBJEhhjSKVSKBQKLY2mg0OJwQ8AvC7c1sr97W1t5zI/fN8aj8fBOb/rwUSCotEo6boOIoLneZBlGddyuWBvX7x78svpGRYOhcYuzXyj'
    + '7h8YABFBEARIkgTO+fpIRJBlGZxzSJIEQRAQi8Uw8/V0WA2HTzLGKKXrJXFjVcYYRFEE5xzECMQIiqKAcw5RFMEYAxFhpWoIPCAmGQ8Edu7t6kIylSLP8+D7PjZ6EwhKKIR9sfupb6CfBEGAbdvYF7uPjGoVwUBwm8iIrVMZwXEcWJYFIgIAOE0HYkOE67pwXReFQgF3'
    + 'Fhawc9cu+J4HABCbbnPxVr6w9ZPzF3zP86CqKqLRKHZ3dlL+Vt6XJAlVw4CmabAsC3a9jvMTE77nA07TKbI1z5tta2935+fnoes6bNtGrLcHN2/c8OWQQnJIIU3TUK1W1wG2jdXVVXR3d6813bXLzLRq40ePPWE6joNarYbEQwdxZW4OfxYKKJfLWFxY8JeXl1EqlWAY'
    + 'BkzTRLPZxOPDw7UV0/yQfN/HO2ffP2MZlROZny61ch7EmutCVVWoqgrGGBzHgW3bME0TlUoFL748WlVbtkyMvvD8KQYAV365/mbH3j1/DT/5VCWfz0PTNBSLRTzQH6f+Aw/S0tISisUiKoaBV06+Wt3d0fHP7OW50/8701Dq0LstkciJi1NT4d9/mw+4ay4YExDkHAcG'
    + 'E87I0yN1w7Q++jGTfeM/Z9qsz774qm9b+z1jIVlORyKRexU5CMuyFuuNRkbXy+ND6cPXN+f/BU+TggAryQj/AAAAAElFTkSuQmCC'
);
Resources.add('snapshot_icon', 'data:image/png;base64,' 
    + 'iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA7BJREFUeNrsVc1LW1kU/72Xl7x8mxg1ammLzggGygyM4mpAprS29EvEqouuhGLpykUXBdttF/4JFdcOKJ0ZKHQ5ji2UVrQftJB+WGpN'
    + 'rJo0+p7RfGgSe85J4mg3HWjdzYXDve/ee875nd/93fuUnZ0dHGRTccDtwBNoPT09UBRl79z5iYkJ0Pzdbw3O9GulcSvZLzwYGBi4TYaRkZEr3xj/CdmM0t3dzR9XhoeHbzkcjsB/QfW1tVQqlRgaGrpBw9taOBzmuet9fX2BmpoaxGIxtLW1YXBwEFNTU2hvbxenQqGA'
    + 'zc1Ndsb29rbMWa1WOJ1OuFwuqOq/x2m32wO0X3gvJ3jS39/fwI51dXWYnp7G1tYWOjo6kEwmkcvlEI/H2REVFRWw2WwSiPeYponV1VVUV1dD04qMWywW5PP5YoKymmhiX/nsnEgkJPjKygr8fj8Kqgv3H7/G09mHeP/mOUKhEDo7O6WKSCSCYDAoSdioAnVvAks2m5UF'
    + 'VpTb7QZ/c2N0TA8HeP3BxIW+q1AJoZJLCvrR0VGcO9fJJCKTyQoDXAEBU/beA405XFtbE443NjakAqZnbu4d1teTGBsbw6YZw707o/hj4nf8+dddLESiQtvs7Ax03YVw+BWdoUmJ00IRs6L19vZygpNMD9NRPsBPnxIwDJMc4qitrcfS0hL1EUz+8wg//NyFE0c78Obp'
    + 'HfExzTWq2kdVb9FhW2kOnKBI0fj4OPcPurq6jvPmTCYDj8dDVaSILk1M06xiTNXhxp/Q0tIiIDLGWwr0obRuk+BsiiIUFROwJKmFyhUwf8Xz0GlcoNLtFARoaGgUmbocHsy9mindUkMoqq09JJU7HG6Srk1A7Z4Bl05mMt8sQXZk/nmjrjvh9fqJrlWcPn2BEqRQyMaQ'
    + '/DiJ9cW/ceLUGVKQG62tv2Jx8SOqqoLkp1NYRSjiM1AXFhZAZnJgvmTpdLp0iXQyu/BvGOsyvnTpMiorg4RQJbROROeeUeJuQc8Cqa8/IiossaEyI7syZYnyzeSsPp9PKFJVC10qOxobmzA/P08IAzh79qLonlFmMmlEo1GqMI6mppAkLT4Z2KWonEBhaTL/TFVz87FS'
    + 'AlUQBQJV4ry8vIwXL17ueyqY1lDoGD0X7n1vEjEhsTVd1xnxvWvXbv7o8fgrik+3gi97r9dG6vJK8Hw+JygZECvoi+ee7o3JTTcMA7ziIyX8RpvbvuePhiT9iKqY5AR2MkepV75TfBI2WC1Z5f+f/tfaZwEGAF5e0kpGJ78XAAAAAElFTkSuQmCC'
);
Resources.add('createtable_icon', 'data:image/png;base64,' 
    + 'iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QAAAAAAAD5Q7t/AAAACXBIWXMAAAuHAAALhwGTQIdTAAAACXZwQWcAAAAYAAAAGAB4TKWmAAAC0klEQVRIx7WVT2sTURTFf9GaOEVtcSGoO9NEMMmkTXcVpRVciOJSwQ/gp1BcCeJesFIQEVwE'
    + 'lxUEcW1RaSapYGe6L5RiWpoE0+jc62Le/EltbFB88Jj73sA595577gz855VKHh49fngXmP8boJ+7P6fu3X/g7L0f2XOev37tJtlsFsuy9gXq9XqAAtBsbuH7PhsbG7x7/7Z248b14uLim6+ADCIgm83i+z7tdju6U9Vog6KiKMrS0gdmZi5SqVRYX18H+DI6ap2vVl97'
    + 'Awksy2JhYSEARkHh1u1boKAqoMqy41C2bTqdDplMBoC5uTm63S6AW62+Tg0kSGZtIjAZh1VM2jaK8r3b5emzJ3/szb4E+Xw+JlBhubbcJ5Uph6nJSb5tbvLx8ydSwOkzZ9n8tnEwQaR1mHG5jEKgvUpUTbO5RSY9wuyxWZrNJiI6HMHq6mrIhKKUbRsJSVGcmoNtlxgf'
    + 'H0PE56hl8aO3y9j4Sdy1r31YhwZ0AEWiCkSDzEUUFaFk26hpevpImvTICNlz58jl8sP2IIeIaTCK49RQCRwlxPIFTyOpCpcunxqOIAQPKyiXbQOifVUFEooxmu4H9btErVYLz1vFdV1c18XzvBhUBFGhXq+DBrFKcN9oNIa3qYoZsrDRIsY9gRzFYjG2rKmmWCgMT5DL'
    + 'TURgIDh1J5ZBQ/LwkxFkpKrMXbk6ZAXGnmFcKtkRiCqICuGwicaTflAPothb8/C8NTw3eKoYrTWQaqWxYu6Cs6A0Gl8OlCiOI4cQSRLPBRSKFyJ7RneFC3Q67WSyspcgA9DptMlOTMSWRGnU6wnAQJ6IXCV6N3PxUoh1FOgCkiQ4ErvI+NtUUywVjRwkvkUxeDQP8RoF'
    + 'ensJ/DCoTE/zjyvqZ5Jgt7XTevXi5fM7/4K8vbVdNdkr9P/0U0AaOA6cMGVmgMNGvsMmoZTJUBO7C7SBncT2Q5L/un4B6tEruiWSAgYAAAAASUVORK5CYII='
);
Resources.add('refresh_icon', 'data:image/png;base64,' 
    + 'iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAABIAAAASABGyWs+AAAGLElEQVRIx62Va2wcVxXHz70zszM7s7PeXe/L2Zft3dhev+omsV23VGmaAiKqmkpRVdQSITVUFSAoCIl8KSDxBaQiVIlIpR9AqloQ'
    + 'lFRNaUuFWrt1CU6CXcd27Gzs2vFjs3Z2vE/vY3bncS8f3JgEJ6EfOB/vvef8dP7ncRGlFL6oEUKAUAoMxoAQ+kI+7P96UNd0tHxNCS+tpfuVXKlN0w3eaZdSLUH3eDwanLOJgnY3f3S3DJavKf4Pxy49ZyLuab/f1yLaZNYEBKWySjcVJVMvF94+PNDxYm9HZOFOGd0R'
    + 'MJVY6f3H5Gcv9Xa3P+R2NyKDYtAJAlUnoBkUNJNAMV+AxcTcxaHOwLcfHuq+cANSq2vMSmoz0ORxKrcFzMyvxkfH51+LhJp6iluVRKlcWUUYO+0OZ08wEnGYmIOaTkAzAWpqFeanJj89dqj7qVjEvzg5t3zv2PTVZ64rWf7ks499f1cN0plCw5t//9cvrVZBokb9Wwf3'
    + 'x95vkMWCpun8zMLafTOTF38Wi3c+yAgSgEEAcwKEO7r2vzUy9SurwF/PEetjDOfytbUKJ91OuboL8NYH40+uKznxua8/cmxfZ+tljHe0NQ4P9Qz73cmrw+Nzv4t13XMIYw7ApGARZfBEux5VqhQ4iw3VUgv5nu49HyOEAN8cPJXOeWYX1nq/+9RXfnCgO3pz8B3r2hta'
    + 'ju1x/jQxO7vJYQQYAVAAYCUHwrwNUUMHRtu6FAv7E7vadGZ+NXr08IHX+zpb5m5X+MJWhb8wvXh/YiX9bKOvSUKIAkYIMKKAEQWBQ1ArlcBvt3zkcsilWwC6YQLPcYqzQeJyhZJkl8UKyzA7d8NjlwY/mUj8sLk5cuTA4ICMeStU6iZgZAJGABghsFoYyJRype540wjD'
    + '4FsHjWMZKFVr+z6YWPq1U7YuyQJzIeRznN8b8c+4HPI6b2EreyNN53VTI4m5xD1WmxyWGhwSJ4iIYzgwCAWeGMAb5UQ82jm7a5JrdQ1fWVW+1tzVFxIlMQRa9aGNUlFbnrh6ner1WYdkGQv4nOeavM4zQCnN5EvNKSW5f71UG9SB7eVEe8hEWHJLzKi30ZHbBUhuZAM5'
    + 'Fb4ks1bQCQKraAevw2kRWBRmqBE26uqRjVRKe/fjkSuRPY2/OfHEw68euk8cLVfrrJIpeNY2su2LSWVfMOQ5S+E/s7UDmP0sOUhFZ7NqAPAsACEUDNMEA2EAasKmslkuFgqjB/vjv7//3raRBpuoAyCwiYJhC/s3WsP+je620MTwudkOXTcZlmHMHUC9rqMrq5tftTjb'
    + 'LKpOQOYxEEqBEAQIAcxOzWz5ZPZ73zz6wF/sNqt6u72j6Qbzh7+efbq9ZU/CKljMG+d4W55MIKvCg4iXwCQUMBCggIBQAIMgCITDUlnV2jFCxh2Co9fOjH7j0vzqA/290U9vvsPb8lwbYGVXC9IqUF27rCTnJtOYGEAAwCQEnB4fY3V6nz8zPPGjzVxRvOFMKYV0piD/'
    + '6b2x50fOX/7J418e+GOjQ67csk2rtTr+xStvv1zHwqNe2XJ6qKf51VQ6176UM3/bGu+2sxiAZzFYOQyb68natZXlv7UEGt8UBcv1ak1vzhYrT2xVagcFDr/4zLFDP79ZHgAAlM4U5DMfjj8+0BubjEcDCd7CkapaZ19/558nVdb+QiS6V2AZBDyzPUhUr0E+mzVMQzdc'
    + 'DZIlmy2YqfWNl588MvSCt7Gh9N/yIZMQAEoB41vWEuQKW8Ib75//TkFnf9zR2eWzyzawcBh4FgPPAFTLWzA1PZcEs/7S0cP9rzR5nRWA3fVBlFJQFAWmp6fRwsICWllZwaqq4mQyicvlCruUyg+1xPcd790/2O/z+V3ENMxUci19cfzc2fTalT9Hg+6LgsCbwWCQiqJI'
    + 'gsEgicVipK+vj4ZCoW3A6dOn4dSpU8zi4iKbz+dZVVU5Sin7eRsjAOCBsTSyvOSgxCRmvZoDauQBwADY7gWEkG61Wg2Hw2G0trYaJ06cMI8fP04RpRRM04RyuQyZTAZyuRxKpVJI13VULBZRKpVCAICAUqDbm/nzTkVAKQWv10s9Hg/hOI4GAgHqcrmo2+0GWZYBY3z3'
    + 'T///Yf8GfBnc0hL3m9sAAAAielRYdFNvZnR3YXJlAAB42isvL9fLzMsuTk4sSNXLL0oHADbYBlgQU8pcAAAAAElFTkSuQmCC'
);
Resources.add('chrome_bookmark_icon', 'data:image/png;base64,' 
    + 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2dJREFUeNpcU21oG2Uc/93lkkvv0iSEyuwSujRsa2kaGyWKDmFRsM4vUtTpXroRma/MT51OUBC/+PLRr9YWKps4FG1BN1BcOHUUv8xm'
    + 'q6vbuqZNmiZb3l/vrte71P8FlM37cDw8z//t9/Jn8L/vuUNH99t5Pub17ozyPO/Xt3QYhlHdyOUkVVU/+/7cV7/eHc/8e3j+8LjLarHMHHh6dCzQ70erJcPG26DrBmRZRrPZxI3lZawkVyVN08a++/pszcyzmL8XjhxzCXZeyqVT0cFgELzdjt77d8Dp6Iaz2wGXywlR'
    + 'EGHhOOzs7fVnc7kDA0PBc0uLVzdZs4Aoih+ePjUR7g8EUCoUsMvnQ/ziL3j39Nt46+RJTH0xCVWRMTQ4AIZhEB55IExpcx0IB48eH3lm9KkEZ7Nhd38/OAuD2dlZfPv7AqyPj2OwpwuV5CK2/4rjo08+haKqkH67BN3QkUyuRllBEGKh0DBu3VpBrV7HMEGYn59H9dGX'
    + '8ewT+/DBkVGcePUNbHlDmJmeQuH2HRCZcDgcsFksMdbn80bv5Atot9twOrvRkhXUajUYnj647YS524b7RCusuyOoNxpIp1ahNetwiSJcTkeUEwUhXK83CAyDttEmyXSwdG5Q8I83y6ioOjL1Tawnl/Gg14sXDx3G+QsXIMXj0Iy2nzMoiWUZkDQoVyrYIt0jkQhuX/kG'
    + 'f+AgLuda6MoswHsjjidfer/DQaFcwQ5fH7RNFVyj2VxjGNavaVtIpdchdtlRLZUx0m4h//N72CZoPlJl/J0JDIdCWPr7OjKZDSLRgCCKVa5cLktW3hYjKMgRQT+d/wH+XX58fGoCbrfbRNaZSlYUrK6lcHkhgfV0GkEqls/nJY4eZlKpdKynpwfXlq5BI+ftGQpieSUJ'
    + 'NxnIRvJWqzWQlTsTJhIJbJP+LJmKqs9ZFhN/pvoCgWggEPDLrVaHvDbDolgsYSObQ5K6rqXSSGcyuE7jl0olPBR5GKViMXF2evJNznSToqhjVFnaOzAYzmazneRKpQqOupjLYuJtkEdAvR95bB+9lau0YLF7lunYiddcRMqcx+OJFovFjqxut6eTZC6UIArYJNaL+XzC'
    + '0PXYmenJK/cU+K/QK6/vZ1k2RuxHBYfDb96RP6qKIkum/89Mff7l3fH/CDAA6OOVPZK/40YAAAAASUVORK5CYII='
);
Resources.add('victory_coin_icon', 'data:image/jpeg;base64,' 
    + 'R0lGODlhFQAVAPepAK0BAa6urwEBAbS1tZ6en7y9vdnZ2dXV1eTk5JeYmKytrcPExLa2t6wLC7u8vJ+foMvLzKamp5iZmUxMTMTFxYmKixISEnl6e3h5eeHh4ZGRkhsbG9LS0iUlJujo6IKCgygpKYODgw0NDZSVljExMcTExHp7fEZHR8fIyLi4uUdHR7JcXMzMzX5+'
    + 'f52dna0MDLy8vB8fINvb29DR0YuLjKROTtra2oCBgoGCg7e3t7GHh+Pj48/Pz69ZWX+Aga9aWioqK5aLjKkzM8LDxC0tLUREROXl5tTU1Lh3eL1oaF9fX5SUlT09PcCVlYOEhYKDg6+vsKenqK+vr0JCQn1+f7E7O9K8vJqQkXd4eXR1dVRUVCQkJIeIiTMzM5WVlbit'
    + 'rrGys4uMjdzc3N7e3szMzI2Oj8vLyxoaG4yNjkZGR0ZGRt/f35GRkawKCqFLS9jY2LycnXx8fQkJCb29vsrKy3t8fLNoaLY1NYqLjLImJs3NzqpUVIWGh4SFhsLCw6wWFsWwsLq6uwICAqRjZJOTlK4NDScnKJ6fn9PT04KDhMPDxOLi4q5YWLW2ttzd3X5/gIiJiqip'
    + 'qS8vMIWFhrOztIuLi+np6dfBwQcHBxcXGEhJSVtbXJCGhwQEBIaHiMFra0NERDU1NpKSk5V1dggICG9vb8XGxr6+vqusrAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
    + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
    + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAKkALAAAAAAVABUAQAj/AFMJHEiwoEGBncBU0CBBCAAAduDkuWNlDIJQBwUWyZAjxYBGBNh0yChQwKQnDhZAQGQjg5EWQDJKWXLBh6cybgD8UTCgAAVKJEgKHZpKDggmaiRlEiSUSAgCEQIwOGVKzwEx'
    + 'OyxtwlRQBAoqfcIQqgHgxY8qfuhwMJDmYKVDGOrggDQqwYMoULJ0Icq3L18tGOaQMXBgyIQYQkuh4umTxREZizx42XAwwAi7exitQNIkyadLCHgYKmhBkRM8oq40ANDjSyFAM96sOSGAoABHFx7xQRNkNQAdKSGYAWVwSwksJm5w4dRmUFQGMJSQyugiEI04iRZKiPRB'
    + 'E98zE1RMBbHgt29AADs='
);// ==Script==
// @id        URLServices.js
// @author    Dakam
// @memberOf  MWAddon.js
// ==Script==
/**
 * @namespace
 */
var URLServices = {
    /**
     * Get an array of service names for the specified type.
     * @param {Object} id
     */    
    toArray: function(type) {
        var oArray = new Object();
        if (Util.isSet(URLServices[type])) {
            Util.each(URLServices[type], function(id, api) {
                oArray[id] = api.name;
            });
        }
        return oArray;
    },
    /**
     * Add a new service.
     * @param {String} type
     * @param {String} id
     * @param {Object} service
     */
    add: function(type, id, service) {
        var addTo = URLServices[type];
        if (Util.isSet(addTo) && Util.isSet(service)) {
            addTo[id] = service;
        }
    },
    /**
     * Shortening Services.
     * name: the name of this service. 
     * url: the short service url. The long url will be added to the end.
     * format: json, jsonp, text
     * parser: function to parse the response object, return false if not success.
     * login: use name for the user name string and key for the user key string.
     */
    shorten: {
        
        'bitly': {
            name: 'bit.ly',
            url: 'http://api.bit.ly/v3/shorten?callback=?&format=json&longUrl=',
            format: 'jsonp',
            description: '<span style="color:green;">Work with any browser.</span>',
            login: {
                name: 'login',
                key: 'apiKey'
            },
            parser: function(data){
                if (data.status_code === 200) {
                    return data.data.url;
                }
                return false;
            }
        },        
        'JSON-TinyUrl': {
            name: '[JSON] TinyUrl',
            url: 'http://json-tinyurl.appspot.com/?callback=?&url=',
            format: 'jsonp',
            description: '<span style="color:green;">Work with any browser.</span>',
            parser: function(data){
                if (data.ok) {
                    return data.tinyurl;
                }
                return false;
            }
            
        },        
        'TinyUrl': {
            name: 'TinyUrl',
            url: 'http://tinyurl.com/api-create.php?url=',
            description: '<span style="color:yellow;">Need cross domain capabilities to work.</span>',
            format: 'text',
            parser: function(data){
                return Util.doRgx(/http:\/\/tinyurl\.com\/[\w\d]+/,data).$0;
            }
        },        
        'Google': {
            name: 'Google URL Shortener',
            url: 'http://goo.gl/api/shorten?security_token=&url=',
            format: 'json',
            description: '<span style="color:yellow;">Need cross domain capabilities to work.</span>',
            parser: function(data){
                if (data.short_url) {
                    return data.short_url;
                }
                return false;
            }
        },        
        'IsGD': {
            name: 'is.gd',
            url: 'http://is.gd/create.php?opt=0&shorturl=&url=',
            format: 'text',
            description: '<span style="color:yellow;">Need cross domain capabilities to work.</span>',
            parser: function(data){
                if (typeof($) !== 'undefined') {
                    return h$(data).find('#short_url').val();
                }
                else {
                    return Util.doRgx(/value="(http:\/\/is\.gd\/[0-9a-zA-Z]+)"/, data).$1;
                }
            }
            
        }
        
    },
    /**
     * Unshortening Services.
     * url: the unshort service url. The short url will be added to the end.
     * format: json, jsonp, text
     * parser: function to parse the response object, return false if not success.
     */
    unshorten: {
    
        'unshort.me': {
            name: 'unshort.me',
            url: 'http://unshort.me/index.php?r=',
            format: 'text',
            description: '<span style="color:yellow;">Need cross domain capabilities to work.</span>',
            parser: function(data){
                if (typeof($) !== 'undefined') {
                    return h$(data).find('.result a').attr('href');
                }
                else {
                    return Util.doRgx(/class="result".*?href="(.+?)"/, data).$1;
                }
            }
            
        },
    
        'LongURL': {
            name: 'LongURL',
            url: 'http://longurl.org/expand?url=',
            format: 'text',
            description: '<span style="color:yellow;">Need cross domain capabilities to work.</span>',
            parser: function(data){
                if (typeof($) !== 'undefined') {
                    return h$(data).find('#content dl:first > dd:last a').attr('href');
                }
                else {
                    return false;
                }
            }
            
        },
    
        'JSON-LongURL': {
            name: '[JSON] LongURL',
            url: 'http://api.longurl.org/v2/expand?format=json&url=',
            format: 'jsonp',
            description: '<span style="color:green;">Work with any browser.</span>',
            parser: function(data){
                if (data['long-url']) {
                    return data['long-url'];
                }
                return false;
            }
            
        }
    }
    
};

/**
 * Convert a long url to a new short url.
 * @param {String} longURL The long url to be shorter.
 * @param {Function} success The function to be executed after finish -> success(shortURL).
 * @param {Function} error The function to be executed if an error happen -> error().
 */
function getShortURL(longURL, success, error)
{
    try {
        if (!Util.isFunc(success)) {
            throw ReferenceError('Success callback is not defined.');
        }
        if (!Util.isString(longURL) || !/http/.test(longURL)) {
            throw ReferenceError('longURL is not defined.');
        }
         
        var api = URLServices.shorten[UserConfig.main.get('shortServiceID')];
        
        if (global.xd_support !== true && api.format !== 'jsonp') {
            api = URLServices.shorten['JSON-TinyUrl'];
        }
        
        var login, login_text = '';
        if (api.login) {
            login = UserConfig.main.get('shortServiceLogin');
            login_text += '&' + api.login.name + '=' + login[api.login.name];
            login_text += '&' + api.login.key  + '=' + login[api.login.key];
        }
        
        function parserAndCallback(response) {
            var r = api.parser(response);
            if ( r ) {
                success( r );
            } else if ( error ) {
                error('Error parsing short service callback.');
            }
        }
        
        if ( api.format !== 'jsonp' ) {
            
            httpXDRequest({
                method: 'POST',
                url : api.url + escape(longURL) + login_text,
                onerror: error,
                onload: function(responseDetails) {
                    var response = responseDetails.responseText;
                    if (api.format !== 'text') {
                        response = Util.parseJSON(response);
                    }
                    if (api.parser) {
                        parserAndCallback( response );
                    } else {
                        success( response );
                    }
                }
            }, true);
            
        } else if (typeof($) !== 'undefined') {
            
            var bAborted = false;
            var nTimeout = setTimeout(function() {
                bAborted = true;
                error && error(Util.setColor('Timeout while unshorting url', 'red'));
            }, Math.max(UserConfig.main.jsTimeout,5)*1000);
        
            // send request
            $.ajax({
                dataType: 'jsonp',
                url: api.url + escape(longURL) + login_text,
                global: false,
                success: function(jsonData) {
                    if (bAborted === false) {
                        clearTimeout(nTimeout);
                        parserAndCallback( jsonData );
                    }
                }
            });
            
        } else {
            
            error && error('jQuery not loaded.');
            
        }
    }
    catch(err) {
        Logger.error(err);
        error && error(err.message);
    }
}

/**
 * Convert a short url to a long url.
 *
 * @param {String} shortURL The short url to unshort.
 * @param {Function} success The function to be executed after finish -> success(longURL).
 * @param {Function} error The function to be executed if an error happen -> error().
 */
function getUnshortUrl(shortURL, success, error) {
    var errorHandler = function(err) {
        Logger.error(err);
        error && error(err.message);
    };
    try {
        if (!Util.isFunc(success)) {
            throw ReferenceError('callback is not defined.');
        }
        if (!Util.isString(shortURL) || !/http/.test(shortURL)) {
            throw ReferenceError('shortURL is not defined.');
        }
         
        var api = URLServices.unshorten[UserConfig.main.get('unshortServiceID')];
        
        if (global.xd_support !== true && api.format !== 'jsonp') {
            api = URLServices.unshorten['JSON-LongURL'];
        }
                
        function parserAndCallback(response) {
            var r = api.parser(response);
            if ( r ) {
                success( r );
            } else if ( error ) {
                error('Error parsing unshort service callback.');
            }
        }
        
        if ( api.format !== 'jsonp' ) {
            
            httpXDRequest({
                method: 'POST',
                url : api.url + escape(shortURL),
                onerror: error,
                onload: function(responseDetails) {
                    var response = responseDetails.responseText;
                    if (api.format !== 'text') {
                        response = Util.parseJSON(response);
                    }
                    if (api.parser) {
                        parserAndCallback( response );
                    } else {
                        success( response );
                    }
                }
            }, true);
            
        } else if (typeof($) !== 'undefined') {
            
            var bAborted = false;
            var nTimeout = setTimeout(function() {
                bAborted = true;
                error && error(Util.setColor('Timeout while unshorting url', 'red'));
            }, Math.max(UserConfig.main.jsTimeout,5)*1000);
        
            // send request
            $.ajax({
                dataType: 'jsonp',
                url: api.url + escape(shortURL),
                global: false,
                success: function(jsonData) {
                    if (bAborted === false) {
                        clearTimeout(nTimeout);
                        parserAndCallback( jsonData );
                    }
                }
            });
            
        } else {
            
            error && error('jQuery not loaded.');
            
        }
    }
    catch(err) {
        errorHandler(err);
    }
}
// ==Script==
// @id        PopupObject.js
// @author    Dakam
// @memberOf  MWAddon.js
// ==Script==

/**
 * Create a popup with the specified options.<br>
 * Options:<br>
 * <br>
 * <b>type:</b> {String} set popup type.<br>
 * <b>appendTo:</b> {Element, jQuery} append popup in.<br>
 * <b>title:</b> {String} set popup title.<br>
 * <b>top:</b> {Number} set popup top.<br>
 * <b>center:</b> {Boolean} set default text-align.<br>
 * <b>background:</b> {String} set background style sheet.<br>
 * <b>autoOpen:</b> {Boolean} show it up.<br>
 * <b>onclose:</b> {Function} onclose event.<br>
 * <b>closeAfterClick:</b> {Boolean} close after click any of popup buttons.<br>
 * <b>buttons:</b> {Array} of buttons options<br>
 *
 * @constructor
 * @param {String} popId
 * @param {Object} args
 * @return {PopupObject}
 */
function PopupObject(popId, args) {
    var me = this;
    var pId = Math.round(new Date().getTime() / 100);

    if (Util.isObject(popId)) {
        args = popId;
        popId = 'mwaddon_popup_'+pId;
    }
    if (!Util.isObject(args)) {
        args = new Object();
    }
    if (!Util.isString(args.title)) {
        if (args.title && args.title.html) {
            args.title = c$('div').append(args.title).html();
        } else {
            args.title = '';
        }
    }
    if (args.autoOpen !== true) {
        args.autoOpen = false;
    }
    if (!Util.isBoolean(args.center)) {
        args.center = true;
    }
    if (!Util.isString(args.type)) {
        args.type = 'normal';
    }
    if (!Util.isSet(args.appendTo)) {
        args.appendTo = e$('#fbmw_addon_popup');
    }
    if (!Util.isSet(args.zIndex)) {
        args.zIndex = 100 + Util.length(global.Popups);
    }
    var $fodder = c$('div','pop_'+pId).appendTo((args.appendTo||c$('div','fbmw_addon_popup').prependTo('#final_wrapper')));
    var $bg = c$('div','class:pop_bg,pop_bg_'+pId).width('100%').appendTo($fodder);
    var $pop = c$('div','class:pop_box,pop_box_'+pId).appendTo($fodder).css('top', args.top||80);
    var $hdr = c$('div').appendTo($pop);
    var $body = c$('div', popId).appendTo($pop).css('clear','both');
    var $btn = c$('div').css('padding', '10px 5px').appendTo($pop);
    var cssMain = {
        'width'       : 730,
        'border'      : '4px solid #A99E9E',
        'text-align'  : 'left',
        'top'         : args.top             || 15,
        'z-index'     : 501
    };
    var $title = c$('center').html(args.title).css({
        'text-transform': 'uppercase',
        'font-size'     : 32,
        'font-weight'   : 'bold',
        'position'      : 'absolute',
        'width'         : '100%'
    });
    // popup type
    switch(args.type) {
        case 'main'   : $pop.css(cssMain);                      break;
        case 'reqs'   : $fodder.addClass('request_iframe_pop'); break;
        case 'rob'    : $fodder.addClass('pop_rob');            break;
        case 'help'   : $fodder.addClass('cash_help_box');      break;
        case 'simple' : $fodder.addClass('simpleWithTitle');    break;
    }
    if (args.width) {
        $pop.width(args.width);
    }
    // fix to center
    $pop.css({
        'background': args.background ? args.background : '#121212',
        'left': '50%',
        'margin': '0px -'+ String($pop.outerWidth()/2) + 'px'
    });
    // z-index
    $pop.css('z-index', args.zIndex + 1);
    $bg.css('z-index', args.zIndex);

    // close button
    if (args.hideCloseButton !== true) {
        c$('a').appendTo($pop).addClass('pop_close').bind('click', closePopup);
    }
    
    if (args.type == 'main') {
        $hdr.css({
            'background'    : 'black url('+global.zGraphicsURL+'zmc/brick_bg_700x73_01.jpg) no-repeat 0px 0px',
            'border-bottom' : '2px solid #2f2f2f',
            'height'        : 70,
            'opacity'       : 0.6
        })
        .append($(Resources.getPicture('mwaddon_icon')).css({'float':'left','margin':2}))
        .append($title.html(args.title).css({
            'font-family' : 'Helvetica, sans-serif',
            'font-size'   : 32,
            'top'         : 15
        }));
    }
    else if (args.type == 'normal') {
        $hdr.append($title.html(args.title)).css({
            'border-bottom' : '2px solid #A99E9E',
            'margin-bottom' : 5,
            'height'        : 45
        });
    }
    else {
        $hdr.css('height', 15);
    }

    if (args.content) {
        $body.append(args.content);
    }
    if (args.center) {
        $body.css('text-align', 'center');
    }
    // Add buttons
    if (Util.isArray(args.buttons)) {
        $btn.css({
            'border-top': '2px solid #2F2F2F',
            'background-color': 'transparent',
            'text-align': 'center',
            'max-height': 40,
            'height': 40
        });
        Util.each(args.buttons, function(i, b) {
            if (!Util.isObject(b)) {
                b = new Object();
            }
            if (!Util.isString(b.label)) {
                b.label = 'undefined';
            }
            if (!Util.isFunc(b.onclick)) {
                b.onclick = closePopup;
            }
            b$(b.label).appendTo($btn).css('margin-left', 5)
            .click(closeAfterClick).click(b.onclick).addClass(b.addClass);
        });
    }
    // -------
    // METHODS
    // -------
    function closePopup() {
        $bg.hide();
        $pop.fadeOut(200, function() {
            if (typeof(args.onclose) == 'function') {
                if (args.onclose.apply(this) !== false) {
                    me.destroy();
                };
            } else {
                me.destroy();
            }
        });
        $('#content_row, #inner_page').height('auto');
        return false;
    }
    function closeAfterClick() {
        if (args.closeAfterClick === true) {
          closePopup();
        }
        return false;
    }
    function openPopup(bMoveToTop) {
        if (me.is_closed === true) {
            return;
        }
        if ( bMoveToTop === true ) {
            $('#TopField').focus();
        }
        if ( args.type === 'main' ) {
           Tooltips.applyTo(popId, $fodder);
        }
        global.Popups['pop_'+pId] = {
            type: args.type,
            popup: me
        }
        if (MWAddonNotify.Initialized === true) {
            MWAddonNotify.hideAll();
        }
        $bg.show();
        $pop.show();
    }
    function applySelectOptions(options) {
        Util.each(options, function(id, op) {
            var $elt = $('#' + id, $pop);
            Util.each(op, function(value, name) {
                $elt.append(c$('option', 'value:'+value).text(name));
            });
        });
    }
    function applyBlackStyle() {
        var text = Util.render(Base64.decode(
        'IyR7aWR9IGlucHV0LCAjJHtpZH0gdGV4dGFyZWEsICMke2lkfSBzZWxlY3Qgew0KZm9udC13ZWlnaHQ6'+
        'IGJvbGQ7DQpjb2xvcjogI0QwRDBEMDsNCmJvcmRlcjogMXB4IHNvbGlkICM5OTk7DQpiYWNrZ3JvdW5k'+
        'LWNvbG9yOiBibGFjazsNCi1tb3otYm9yZGVyLXJhZGl1czogM3B4Ow0KYm9yZGVyLXJhZGl1czogM3B4'+
        'Ow0KLXdlYmtpdC1ib3JkZXItcmFkaXVzOiAzcHg7DQpmb250LXNpemU6IDEzcHg7DQpsaW5lLWhlaWdo'+
        'dDogMTRweDsNCm1pbi1oZWlnaHQ6IDEwcHg7DQp9'
        ), {id: popId});
        /*
        var text = '#'+popId+' .black_box {font-weight: bold;color: rgb(208, 208, 208);'
	    + 'border: 1px solid rgb(153, 153, 153);background-color: black;font-size: 14px;}';
	    */
        c$('style').prependTo($pop).append(text);
    }
    
    /**
     * Add Style element to popup from a base64 encoded string.
     * @param {String} base64String An String encoded in base64
     * @param {Object} [render] An String encoded in base64
     */
    this.addBase64Style = function(base64String, render) {
        base64String = Base64.decode(base64String);
        if (Util.isObject(render)) {
            c$('style').append(Util.render(base64String, render)).prependTo($pop);
        } else {
            c$('style').append(base64String).prependTo($pop);
        }
    };
    /**
     * Add Script element to popup from a base64 encoded string.
     * @param {String} base64String An String encoded in base64
     */
    this.addBase64Script = function(base64String) {
        c$('script').append(Base64.decode(base64String)).prependTo($pop);
    };
    /**
     * Destroy this popup
     */
    this.destroy = function() {
        $fodder.remove();
        me.is_closed = true;
        global.Popups['pop_'+pId] = null;
        delete global.Popups['pop_'+pId];
    };
    /**
     * Apply the specified options to all select elements.
     * @type {function}
     * @param {Object} options
     */
    this.applyOptions = applySelectOptions;
    /**
     * Add black style sheet
     * @type {function}
     */
    this.applyCustomClass = applyBlackStyle;
    /** 
     * Close the popup, fires onclose event.
     * @type {function}
     */
    this.close = closePopup;
    /** 
     * Show the popup.
     * @type {function}
     * @param {Boolean} [focus]
     */
    this.show = openPopup;
    /** 
     * @type {jQuery}
     */
    this.header = $hdr;
    /**
     * @type {jQuery}
     */
    this.content = $body;
    // ---------
    // AUTO-OPEN
    // ---------
    if (args.autoOpen) {
        openPopup();
    }
    
    return this;
}
// ==Script==
// @id        TabObject.js
// @author    Dakam
// @memberOf  MWAddon.js
// ==Script==

/**
 * Create a new DOM Tabs.<br><br>
 * args.id::String<br>
 * args.tabs::Array --><br>
 *     label::String<br>
 *     onclick::Function<br>
 *     
 * @constructor
 * @param {Object} args.
 * @return {TabObject}
 */
function TabObject(args) {
    var me = this;
    var $main = (me.main = c$('div', args.id));
    var $header = c$('div', 'class:tab_header').appendTo($main);
    
    this.header = c$('div','class:tab_right').appendTo($header);
    this.buttons = new Array();
    this.content = new Array();
    
    if (!Util.isSet(args.id)) {
        args.id = 'new_tab';
    }
    if (!Util.isSet(args.height)) {
        args.height = 'auto';
    }
    if (!Util.isSet(args.width)) {
        args.width = 'auto';
    }
    if (!Util.isSet(args.padding)) {
        args.padding = 0;
    }
    if (!Util.isArray(args.tabs)) {
        args.tabs = ['Unknow Tab Name'];
    }
    function pixelCoor(value) {
        if (Util.isNumber(value)) {
            return value + 'px'
        }
        return value || 'auto';
    }
    $main.prepend(c$('style').text(Util.render(Base64.decode(
        'IyR7aWR9IHsNCiAgICB3aWR0aDogJHt3aWR0aH07DQogICAgaGVpZ2h0OiBhdXRvOw0KICAgIG92ZXJmbG93OiBoaWRkZW47DQp9'+
        'DQojJHtpZH0gLnRhYl9oZWFkZXIgew0KICAgIGhlaWdodDogMzVweDsNCiAgICBvdmVyZmxvdzogaGlkZGVuOw0KfQ0KIyR7aWR9'+
        'IC50YWJfaGVhZGVyIC50YWJfcmlnaHQgew0KICAgIGZsb2F0OiByaWdodDsNCiAgICBmb250LXNpemU6IDEycHg7DQogICAgbGlu'+
        'ZS1oZWlnaHQ6IDM2cHg7DQogICAgcGFkZGluZy1sZWZ0OiAxMHB4Ow0KfQ0KIyR7aWR9IC50YWJfaGVhZGVyID4gLnRhYl9idXR0'+
        'b24gew0KICAgIGZsb2F0OiBsZWZ0Ow0KICAgIG1hcmdpbi1yaWdodDogM3B4Ow0KICAgIGN1cnNvcjogcG9pbnRlcjsNCiAgICBw'+
        'YWRkaW5nLXRvcDogOHB4Ow0KICAgIGZpbHRlcjogYWxwaGEob3BhY2l0eT01MCk7DQogICAgLW1vei1vcGFjaXR5OiAwLjU7DQog'+
        'ICAgb3BhY2l0eTogMC41Ow0KfQ0KIyR7aWR9IC50YWJfaGVhZGVyID4gLnRhYl9idXR0b24uYWN0aXZlIHsNCiAgICBmaWx0ZXI6'+
        'IGFscGhhKG9wYWNpdHk9MTAwKTsNCiAgICAtbW96LW9wYWNpdHk6IDEuMDsNCiAgICBvcGFjaXR5OiAxLjA7DQp9DQojJHtpZH0g'+
        'LnRhYl9oZWFkZXIgPiAudGFiX2J1dHRvbi5kaXNhYmxlZCB7DQogICAgZmlsdGVyOiBhbHBoYShvcGFjaXR5PTEwKTsNCiAgICAt'+
        'bW96LW9wYWNpdHk6IDAuMTsNCiAgICBvcGFjaXR5OiAwLjE7DQogICAgY3Vyc29yOiBkZWZhdWx0Ow0KfQ0KIyR7aWR9IC50YWJf'+
        'aGVhZGVyID4gLnRhYl9idXR0b24gLmJ1dHRvbl9zdGFydCB7DQogICAgd2lkdGg6IDNweDsNCiAgICBoZWlnaHQ6IDI4cHg7DQog'+
        'ICAgZmxvYXQ6IGxlZnQ7DQogICAgYmFja2dyb3VuZDogdXJsKCR7YmFja2dyb3VuZF9iYXNlfWJsYWNrX3RhYnNfbGVmdC5wbmcp'+
        'IG5vLXJlcGVhdDsNCn0NCiMke2lkfSAudGFiX2hlYWRlciA+IC50YWJfYnV0dG9uIC5idXR0b25fbWlkZGxlIHsNCiAgICBmbG9h'+
        'dDogbGVmdDsNCiAgICBoZWlnaHQ6IDI4cHg7DQogICAgcGFkZGluZy1sZWZ0OiA1cHg7DQogICAgcGFkZGluZy1yaWdodDogMTBw'+
        'eDsNCiAgICBwYWRkaW5nLXRvcDogNXB4Ow0KICAgIGJhY2tncm91bmQ6IHVybCgke2JhY2tncm91bmRfYmFzZX1ibGFja190YWJz'+
        'X3NsaWNlLnBuZyk7DQogICAgY29sb3I6IHdoaXRlOw0KICAgIGZvbnQtc2l6ZTogMTRweDsNCn0NCiMke2lkfSAudGFiX2hlYWRl'+
        'ciA+IC50YWJfYnV0dG9uIC5idXR0b25fZW5kIHsNCiAgICB3aWR0aDogM3B4Ow0KICAgIGhlaWdodDogMjhweDsNCiAgICBmbG9h'+
        'dDogbGVmdDsNCiAgICBiYWNrZ3JvdW5kOiB1cmwoJHtiYWNrZ3JvdW5kX2Jhc2V9YmxhY2tfdGFic19yaWdodC5wbmcpIG5vLXJl'+
        'cGVhdDsNCn0NCiMke2lkfSAudGFiX2xheWVyIHsNCiAgICBoZWlnaHQ6ICR7aGVpZ2h0fTsNCiAgICB3aWR0aDogYXV0bzsNCiAg'+
        'ICBwYWRkaW5nOiAke3BhZGRpbmd9Ow0KICAgIG1hcmdpbjogMHB4Ow0KICAgIGJhY2tncm91bmQ6ICR7YmFja2dyb3VuZH07DQog'+
        'ICAgYm9yZGVyOiBzb2xpZCAxcHggIzY2NjsNCiAgICBvdmVyZmxvdzogaGlkZGVuOw0KICAgIGRpc3BsYXk6IG5vbmU7DQp9'
    ), {
        id: args.id,
        width: pixelCoor(args.width),
        height: pixelCoor(args.height),
        padding: pixelCoor(args.padding),
        background_base: global.zGraphicsURL,
        background: args.background || '#111'
    })));
    
    if (args.appendTo) {
        $main.appendTo(args.appendTo);
    }
    
    Util.each(args.tabs, function(index, tab) {
        var $content = c$('div', 'class:tab_layer').appendTo($main);
        var $button =  c$('div', 'class:tab_button').appendTo($header);
        
        if (Util.isString(tab)) {
            tab = { 'label': tab };
        }
        c$('div', 'class:button_start').html('&nbsp;').appendTo($button);
        c$('div', 'class:button_middle').html(tab.label).appendTo($button);
        c$('div', 'class:button_end').html('&nbsp;').appendTo($button);
        if (args.overflow) {
            $content.css('overflow-y','auto');
        }
        if (args.border) {
            $content.css('border',args.border);
        }
        me.content.push($content);
        me.buttons.push($button.click(function() {
            if ($button.hasClass('disabled') || $button.hasClass('active')) {
                return false;
            }
            $('.tab_layer', $main).hide();
            $('.tab_button', $header).removeClass('active');
            $content.show();
            $button.addClass('active');
            if (Util.isFunc(tab.onclick)) {
                tab.onclick();
            }
            return false;
        }));
    });
    
    // show up first tab without firing events.
    me.content[0].css('display', 'block');
    me.buttons[0].addClass('active');
    return this;
}
TabObject.prototype = {
    /**
     * Show a tab.
     * @param {Number} id
     */
    showTab: function(id, event) {
        if (this.buttons && this.buttons[id] && !this.buttons[id].hasClass('disabled')) {
            if (event === false) {
                Util.each(this.buttons,function(i,b){
                    $(b).toggleClass('active', i==id);
                });
                Util.each(this.content,function(i,c){
                    $(c)[i==id?'show':'hide']();
                });
            } else {
                this.buttons[id].click();
            }
        }
    },
    /**
     * Get the specified button.
     * @param {Number} id
     * @return {jQuery}
     */
    getButton: function(id) {
        if (this.buttons && this.buttons[id]) {
            return this.buttons[id];
        }
    },
    /**
     * Enable/Disable the specified button.
     * @param {Number} id
     * @param {Boolean} enabled
     */
    buttonEnabled: function(id, enabled) {
        var b = this.getButton(id);
        if (b) b.toggleClass('disabled', enabled===false);
    },
    /**
     * Show/Hide the specified button.
     * @param {Number} id
     * @param {Boolean} display
     */
    buttonDisplay: function(id, display) {
        var b = this.getButton(id);
        if (b) b[display?'show':'hide']();
    },
    /**
     * Get the specified layout.
     * @param {Number} id
     * @return {jQuery}
     */
    getLayout: function(id) {
        return this.content[id];
    }
};
// ==Script==
// @id        DropDownObject.js
// @author    Dakam
// @memberOf  MWAddon.js
// ==Script==

/**
 * Create a new dropdown select with checkboxes
 * @param {String} id
 * @param {String} label
 * @param {Object} elements
 * @return {jQuery}
 */
function DropDownObject(id, label, elements) {
    var containerElt = c$('span', 'id:'+id).css('margin', '0px 2px 0px 2px').attr({
        'name': 'checkboxlist',
        'onmouseover': "var c=$('#sort_menu_button',this),p=c.position();p.top+=c.outerHeight();$('#sort_menu',this).css({'left':p.left+1,'top':p.top,'width':c.width()-3}).show();",
        'onmouseout': "$('#sort_menu', this).hide();"
    });
    
    b$('', 'id:sort_menu_button,class:short black').appendTo(containerElt).width(140).find('span:last')
    .append(c$('span').text(label||'Select').css({
       'max-width': 100,
       'text-overflow': 'ellipsis',
       'float': 'left',
       'overflow': 'hidden' 
    }))
    .append(c$('img').attr('src', global.zGraphicsURL+'dropdown_travel_arrow.gif').css({
        'float': 'right',
        'margin-top': 5
    }))
    .append(c$('div').css('clear','both'));
    
    var menuElt = c$('div', 'id:sort_menu').appendTo(containerElt);
    
    Util.each(elements, function(check_id, name) {
        c$('div').appendTo(menuElt).text(String(name)).css({
            'cursor': 'pointer',
            'margin': 1
        }).attr({
            'value': check_id,
            'name':'checkbox',
            'class':'checkbox',
            'onclick':"$(this).toggleClass('checked');",
            'onmouseover': "$(this).css('background-color','#444')",
            'onmouseout': "$(this).css('background-color','transparent')"
        });
    });
    return containerElt;
}
// ==Script==
// @id        TableObject.js
// @author    Dakam
// @memberOf  MWAddon.js
// ==Script==

/**
 * Create a new DOM table object.
 *
 * @constructor
 * @param {Element} appendTo
 * @param {Number} rows
 * @param {Number} columns
 * @param {Object} styleCSS
 * @return {TableObject}
 */
var TableObject = function(appendTo, bHeader) {
    var $head, me = this;
    var $table = c$('table', 'cellspacing:0').appendTo(appendTo);
    var $body = c$('tbody').appendTo($table);
    
    if (bHeader === true) {
        $head = c$('tr', 'class:table_header').appendTo($body);
    }    
    
    this.table = $table;
    this.tbody = $body;
    this.head = $head;
    
    /**
     * @param {Number} x
     * @return {jQuery}
     */
    this.row = function(x) {
        var $r = $('tr:eq('+x+')', $body);
        if ($r.length !== 1) {
            return c$('tr').appendTo($body);
        }
        return $r;
    };
    /**
     * @param {Number} x
     * @param {Number} y
     * @return {jQuery}
     */
    this.cell = function(x, y) {
        var $r = me.row(x);
        var sT = $r.hasClass('table_header') ? 'th' : 'td';
        var $cell = $(sT, $r).eq(y);
        
        if ($cell.length !== 1) {
            return c$(sT).appendTo($r);
        }
        return $cell;
    };
    return this;
};



// ==Script==
// @id        ModalPopups.js
// @author    Dakam
// @memberOf  MWAddon.js
// ==Script==

/**
 * @param {function} callback
 * @param {Object} selectedUsers
 */
function showFriendsSelector(callback, selectedUsers) {
    
    var FbFriendListCache = new Object();

    var Popup = new PopupObject('friend_selection_popup', {
        type       : 'normal',
        title      : 'Friends Selector',
        top        : 50,
        width      : 710,
        background : 'black url(\''+global.zGraphicsURL+'requests/pop_back.jpg\') repeat-x',
        zIndex     : 999
    });
    
    var Events = {
        skip: function() {
            Popup.close();
            return false;
        },
        save: function() {
            Popup.close();
            
            var uSelected = new Array();
            Util.each(global.appFriends, function(i, user) {
                if (e$('.mfs_selected #mfs_user_'+user.uid, Popup.content)) {
                    uSelected.push({
                        uid: user.uid,
                        name: user.name
                    });
                }
            });
            callback(uSelected);
            
            return false;
        },
        select: function() {
            var $elt = $(this).clone().removeClass().addClass('mfs_remove');
            $('.mfs_selected ul', Popup.content).append($elt.click(Events.unselect));
            $(this).addClass('is_added');
            return false;
        },
        unselect: function() {
            $('.mfs_selectable #'+this.id, Popup.content).removeClass('is_added');
            $(this).unbind().remove();
            return false;
        },
        filter: function() {
            var $lst = $('.mfs_selectable', Popup.content);
            var text = String($(this).val());
            if (text.length < 1) {
                $('li', $lst).removeClass('is_hidden');
            } else {
                var RExp = new RegExp(text, 'i');
                
                Util.each(global.appFriends, function(i, user) {
                    var $elt = $('#mfs_user_'+user.uid, $lst);
                    if (RExp.test(user.name)) {
                        $elt.removeClass('is_hidden');
                    } else {
                        $elt.addClass('is_hidden');
                    }
                });
            }
            return false;
        },
        focusout: function() {
            var text = String($(this).val());
            if (text.length < 1) {
                $(this).val('Type a name...');
            }
        },
        focusin: function() {
            var text = String($(this).val());
            if (text === 'Type a name...') {
                $(this).val('');
            }
        },
        fbfriendlist_change: function() {
            var list_id = $(this).val();
            var curr_id = $(this).attr('curfl');
            $(this).attr('curfl', list_id);
            
            if (list_id !== 'none' && curr_id !== list_id) {
                selectFriendsFromList(list_id);
            }
        },
        fbgrouplist_change: function() {
            var list_id = $(this).val();
            var curr_id = $(this).attr('curfl');
            $(this).attr('curfl', list_id);
            
            if (list_id !== '0' && curr_id !== list_id) {
                selectFriendsFromList(list_id);
            }
        },
        select_max: function() {
            var count = 50;
            var selectable = $('.mfs_selectable li:not(.is_added)', Popup.content);
            if (selectable.length > 0) {
                count = Math.min(count, selectable.length);
                for (; count>0; count--) {
                    selectable.eq(Math.floor(Math.random()*selectable.length)).click();
                    selectable = selectable.filter(':not(.is_added)');
                }
            }
            return false;
        },
        clear: function() {
            $('.mfs_selected .mfs_remove', Popup.content).click();
            return false;
        }
    };
    
    function genFriendList() {
        var $lst = $('.mfs_selectable ul', Popup.content);
        Util.each(global.appFriends, function(i,user) {
            c$('li', 'class:mfs_add,id:mfs_user_'+user.uid).appendTo($lst).text(user.name);
        });
        $('li', $lst).click(Events.select);
    }
    
    function getFacebookFriendlists() {
        var $fl = $('#fb_friendlists', Popup.content).empty();
        c$('option', 'value:none').appendTo($fl).text('Select a Facebook Friendlist');
        $fl.attr('curfl', 'none');
        facebook.friendlist(function(response) {
            Util.each(response.data,function(index, list) {
                if (list.list_type === 'user_created') {
                    c$('option', 'value:'+list.id).appendTo($fl).text(list.name);
                }
            });
        });
    }
    
    function getFacebookGroups() {
        var $fl = $('#fb_grouplists', Popup.content).empty();
        facebook.getGroups(function(groups) {
            groups[0] = 'Select a Group';
            $fl.attr('curfl', '0');
            Util.each(groups, function(id, name) {
                c$('option', 'value:'+id).appendTo($fl).text(name);
            });
        });
    }

    function selectFriendsFromList(list_id) {
        if (Util.isSet(FbFriendListCache[list_id])) {
            addFriends(FbFriendListCache[list_id]);
        }
        else {
            facebook.friendlistGet(list_id, function(result) {
                if (result.data) {
                    addFriends(FbFriendListCache[list_id] = result.data);
                }
            });
        }
        function addFriends(data) {
            Util.each(data, function(index, user) {
                $('.mfs_selectable li#mfs_user_'+user.id, Popup.content).click();
            });
        }
    }
        
    function genMainDom() {
        var $mfs = c$('div', 'id:mfs_container').appendTo(Popup.content);
        
        c$('div', 'class:mfs_outer').appendTo($mfs);
        $mfs = c$('div', 'class:mfs_inner').appendTo($mfs);
        
        var $elt =  c$('div', 'class:mfs').css('float','left').appendTo($mfs);
        c$('div', 'class:mfs_name_filter_container').appendTo($elt);
        c$('div', 'class:line').appendTo($elt);
        c$('div', 'class:mfs_selectable').appendTo($elt).append(c$('ul'));
        c$('div', 'class:line').appendTo($elt);
        c$('div').appendTo($elt).append(s$('fb_friendlists', 250, {click:Events.fbfriendlist_change}));
        
        c$('input:text','id:mfs_name_filter,class:mfs_name_filter_box')
        .appendTo('.mfs_name_filter_container').focusin(Events.focusin).focusout(Events.focusout);
        
        $elt =  c$('div', 'class:mfs').css('float','left').appendTo($mfs);
        c$('div', 'class:mfs_name_title_container').appendTo($elt).text('Selected Friends:');
        c$('div', 'class:line').appendTo($elt);
        c$('div', 'class:mfs_selected').appendTo($elt).append(c$('ul'));
        c$('div', 'class:line').appendTo($elt);
        c$('div').appendTo($elt).append(s$('fb_grouplists', 250, {click:Events.fbgrouplist_change}));
        
        c$('div').css('clear','both').appendTo($mfs)
        $elt =  c$('div', 'class:mfs_buttons').appendTo($mfs);
        
        
        b$('SKIP','class:medium white').appendTo($elt).css({'min-width':80,'margin-right':10}).click(Events.skip);
        b$('CLEAR','class:medium white').appendTo($elt).css({'min-width':80,'margin-right':10}).click(Events.clear);
        b$('RANDOM FIFTY','class:medium green').appendTo($elt).css({'min-width':80,'margin-right':10}).click(Events.select_max);
        b$('SAVE AND CLOSE','class:medium orange').appendTo($elt).css({'min-width':80,'margin-right':10}).click(Events.save);
        
        $('#mfs_name_filter', Popup.content).keyup(Events.filter);
        
        Popup.header.css({
            'font-size': 22,
            'font-weight': 'bold',
            'color': '#FC0',
            'margin-bottom': 5,
            'padding': 4,
            'text-align': 'center'
        });
    }
    
    function showPopup() {
        genMainDom();
        genFriendList();
        getFacebookFriendlists();
        getFacebookGroups();
        if (selectedUsers) {
            Util.each(selectedUsers, function(i,uid) {
                $('.mfs_selectable #mfs_user_'+uid, Popup.content).click();
            });
        }
        Popup.applyCustomClass();
        Popup.show();
        $('#mfs_name_filter', Popup.content).focus();
    }
    
    Popup.addBase64Style(
        'I2ZyaWVuZF9zZWxlY3Rpb25fcG9wdXAgI21mc19jb250YWluZXIgew0KICAgIGJhY2tncm91bmQ6IHRyYW5zcGFyZW50Ow0KICAg'+
        'IHdpZHRoOiBhdXRvOw0KICAgIGhlaWdodDogNTQycHg7DQogICAgbWFyZ2luOiAxMHB4IGF1dG87DQp9DQojZnJpZW5kX3NlbGVj'+
        'dGlvbl9wb3B1cCAjbWZzX2NvbnRhaW5lciAubWZzX291dGVyIHsNCiAgICBib3JkZXI6IDJweCBncm9vdmUgIzQ0NDsNCiAgICBw'+
        'b3NpdGlvbjogYWJzb2x1dGU7DQogICAgbWFyZ2luOiAwcHggMHB4IDBweCAxMnB4Ow0KICAgIHdpZHRoOiA2NzVweDsNCiAgICBo'+
        'ZWlnaHQ6IDUyMHB4Ow0KICAgIGJhY2tncm91bmQ6IGJsYWNrOw0KICAgIG9wYWNpdHk6IDAuNjsNCiAgICB0b3A6IDYwcHg7DQog'+
        'ICAgLW1vei1ib3JkZXItcmFkaXVzOiA4cHg7DQogICAgYm9yZGVyLXJhZGl1czogOHB4Ow0KICAgIC13ZWJraXQtYm9yZGVyLXJh'+
        'ZGl1czogOHB4Ow0KfQ0KI2ZyaWVuZF9zZWxlY3Rpb25fcG9wdXAgI21mc19jb250YWluZXIgLm1mc19pbm5lciB7DQogICAgcG9z'+
        'aXRpb246IGFic29sdXRlOw0KICAgIHdpZHRoOiAxMDAlOw0KICAgIGhlaWdodDogNTYwcHg7DQp9DQojZnJpZW5kX3NlbGVjdGlv'+
        'bl9wb3B1cCAjbWZzX2NvbnRhaW5lciBkaXYubWZzIHsNCiAgICB3aWR0aDogMzExcHg7DQogICAgaGVpZ2h0OiA0MDlweDsNCiAg'+
        'ICBtYXJnaW4tbGVmdDogMTVweDsNCiAgICBwYWRkaW5nOiAxMHB4Ow0KICAgIGJhY2tncm91bmQ6IHVybCgnJHtiYXNlX3VybH0v'+
        'cmVxdWVzdHMvTUZTX2JhY2sucG5nJykgbGVmdCB0b3Agbm8tcmVwZWF0Ow0KfQ0KI2ZyaWVuZF9zZWxlY3Rpb25fcG9wdXAgI21m'+
        'c19jb250YWluZXIgZGl2Lm1mc19uYW1lX2ZpbHRlcl9jb250YWluZXIgew0KICAgIGJhY2tncm91bmQ6IHVybCgnJHtiYXNlX3Vy'+
        'bH0vcmVxdWVzdHMvYXJyb3cucG5nJykgMTVweCA4cHggbm8tcmVwZWF0Ow0KICAgIHBhZGRpbmc6IDVweCAxMHB4IDVweCA0MHB4'+
        'Ow0KfQ0KI2ZyaWVuZF9zZWxlY3Rpb25fcG9wdXAgI21mc19jb250YWluZXIgZGl2Lm1mc19uYW1lX3RpdGxlX2NvbnRhaW5lciB7'+
        'DQogICAgYmFja2dyb3VuZDogdXJsKCcke2Jhc2VfdXJsfS9yZXF1ZXN0cy9hcnJvdy5wbmcnKSAxNXB4IDhweCBuby1yZXBlYXQ7'+
        'DQogICAgcGFkZGluZzogMTNweCAxMHB4IDVweCAxMDBweDsNCiAgICBjb2xvcjogI0ZDMDsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0'+
        'Ow0KfQ0KI2ZyaWVuZF9zZWxlY3Rpb25fcG9wdXAgI21mc19jb250YWluZXIgLm1mc19uYW1lX2ZpbHRlcl9ib3ggew0KICAgIGJh'+
        'Y2tncm91bmQ6IHRyYW5zcGFyZW50Ow0KICAgIGNvbG9yOiB3aGl0ZTsNCiAgICBmb250LXNpemU6IDEzcHg7DQogICAgaGVpZ2h0'+
        'OiAyNXB4Ow0KICAgIHdpZHRoOiAyNjBweDsNCiAgICBib3JkZXI6IG5vbmU7DQp9DQojZnJpZW5kX3NlbGVjdGlvbl9wb3B1cCAj'+
        'bWZzX2NvbnRhaW5lciBkaXYubGluZSB7DQogICAgaGVpZ2h0OiAycHg7DQogICAgYmFja2dyb3VuZDogdXJsKCcke2Jhc2VfdXJs'+
        'fS9yZXF1ZXN0cy9saW5lLnBuZycpIDUwJSA1MCUgbm8tcmVwZWF0Ow0KICAgIGJvcmRlcjogbm9uZTsNCn0NCiNmcmllbmRfc2Vs'+
        'ZWN0aW9uX3BvcHVwICNtZnNfY29udGFpbmVyIGRpdi5tZnNfc2VsZWN0YWJsZSwNCiNmcmllbmRfc2VsZWN0aW9uX3BvcHVwICNt'+
        'ZnNfY29udGFpbmVyIGRpdi5tZnNfc2VsZWN0ZWQgew0KICAgIG92ZXJmbG93OiBhdXRvOw0KICAgIG1hcmdpbjogMTBweCA1cHg7'+
        'DQogICAgaGVpZ2h0OiAzMDBweDsNCn0NCiNmcmllbmRfc2VsZWN0aW9uX3BvcHVwIGRpdi5tZnNfc2VsZWN0ZWQgdWwsICNmcmll'+
        'bmRfc2VsZWN0aW9uX3BvcHVwIGRpdi5tZnNfc2VsZWN0YWJsZSB1bCB7DQogICAgbWFyZ2luOiAwOw0KICAgIHBhZGRpbmctbGVm'+
        'dDogMjBweDsNCn0NCiNmcmllbmRfc2VsZWN0aW9uX3BvcHVwIGRpdi5tZnNfc2VsZWN0YWJsZSAubWZzX2FkZCB7DQogICAgY3Vy'+
        'c29yOiBwb2ludGVyOw0KICAgIGJhY2tncm91bmQ6IHVybCgnJHtiYXNlX3VybH0vcmVxdWVzdHMvcGx1cy5wbmcnKSAxcHggMnB4'+
        'IG5vLXJlcGVhdDsNCiAgICBwYWRkaW5nLWxlZnQ6IDIwcHg7DQogICAgbGlzdC1zdHlsZS10eXBlOiBub25lOw0KICAgIHRleHQt'+
        'YWxpZ246IGxlZnQ7DQp9DQojZnJpZW5kX3NlbGVjdGlvbl9wb3B1cCBkaXYubWZzX3NlbGVjdGFibGUgLm1mc19hZGQuaXNfYWRk'+
        'ZWQsDQojZnJpZW5kX3NlbGVjdGlvbl9wb3B1cCBkaXYubWZzX3NlbGVjdGFibGUgLm1mc19hZGQuaXNfaGlkZGVuIHsNCiAgICBk'+
        'aXNwbGF5OiBub25lOw0KfQ0KI2ZyaWVuZF9zZWxlY3Rpb25fcG9wdXAgZGl2Lm1mc19zZWxlY3RhYmxlIC5tZnNfYWRkOmhvdmVy'+
        'IHsNCiAgICBiYWNrZ3JvdW5kLWNvbG9yOiAjMjQyNDI0Ow0KfQ0KI2ZyaWVuZF9zZWxlY3Rpb25fcG9wdXAgZGl2Lm1mc19zZWxl'+
        'Y3RlZCAubWZzX3JlbW92ZSB7DQogICAgY3Vyc29yOiBwb2ludGVyOw0KICAgIGJhY2tncm91bmQ6IHVybCgnJHtiYXNlX3VybH0v'+
        'cmVxdWVzdHMvY3Jvc3MucG5nJykgMXB4IDJweCBuby1yZXBlYXQ7DQogICAgcGFkZGluZy1sZWZ0OiAyMHB4Ow0KICAgIGxpc3Qt'+
        'c3R5bGUtdHlwZTogbm9uZTsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KI2ZyaWVuZF9zZWxlY3Rpb25fcG9wdXAgZGl2Lm1m'+
        'c19zZWxlY3RlZCAubWZzX3JlbW92ZTpob3ZlciB7DQogICAgYmFja2dyb3VuZC1jb2xvcjogIzI0MjQyNDsNCn0NCiNmcmllbmRf'+
        'c2VsZWN0aW9uX3BvcHVwIGRpdi5tZnNfYnV0dG9ucyB7DQogICAgdGV4dC1hbGlnbjogcmlnaHQ7DQogICAgbWFyZ2luOiAyMHB4'+
        'IDQwcHggMCAwOw0KfQ==',
        {base_url: global.zGraphicsURL}
    );
    
    Logger.debug('Loading showUserSelection...');
    
    if (global.appFriends) {
        showPopup();
    } else {
        loadingScreen('Loading friends...');
        facebook.getAppFriends(function(users) {
            loadingScreen();
            if ((global.appFriends = users)) {
                showPopup();
            } else {
                Popup.destroy();
                showHelpPopup({
                    icon: 'error',
                    message: 'Cant load your Mafia Wars Friends.'
                });
            }
        });
    }
}

/**
 * Display a modal message popup.<br>
 * Usage:<br> <br>
 * <b>icon</b>: {String} Can be info or error.<br>
 * <b>title</b>: {String} the title of the new popup.<br>
 * <b>message</b>: {String} message sent to user.<br>
 * <b>buttons</b>: {Array} optional buttons.
 * 
 * <pre>
 * foo({
 *     icon: '',
 *     title: '',
 *     message: ''
 * });
 * </pre>
 * 
 * @param {Object} args
 */
function showHelpPopup(args) {
    if (typeof($)==='undefined') {
        return;
    }
    var title = (this.scope==='MWAddonPlugin' ? this.name : 'MWAddon') + ' says:';
    var $icon = {
        'info': Resources.getPicture('info_icon'),
        'error': Resources.getPicture('ajax_error'),
        'caution': c$('img').attr('src', global.zGraphicsURL+'cautionTopIcon.png')
    };
    if (!Util.isSet(args.message)) {
        args = {
            icon: 'info',
            title: title,
            message: String(args)
        };
    }
    if (!Util.isSet(args.icon)) {
        args.icon = 'info';
    }
    if (!Util.isSet(args.title)) {
        args.title = title;
    }
    var helpPopup = new PopupObject({
        type: 'help',
        top: args.top,
        autoOpen: true,
        zIndex: 99999,
        closeAfterClick: true,
        hideCloseButton: args.hideCloseButton,
        onclose: args.onclose,
        buttons: args.buttons,
        width: args.width,
        content: c$('div').css({'text-align': 'left','margin': '5px 30px'})
            .append($icon[args.icon].css('float', 'left'))
            .append(c$('h4').css('padding', '3px 28px').html(args.title))
            .append(c$('div').css('margin-top', 10).html(args.message))
    });
    if (!Util.isSet(args.top)) {
        $('#TopField').focus();
    }
    if (Util.isSet(args.autoclose) && parseInt(args.autoclose) > 0) {
        setTimeout(helpPopup.close, args.autoclose * 1000);
    }
    return helpPopup;
}
/**
 * Display a modal popup asking to the user to input text.
 * @param {String} message
 * @param {String} defaultText
 * @param {Function} callback
 * @param {Number} [height]
 */
function showPromptPopup(message, defaultText, callback, height) {
    if (!Util.isString(message)) {
        return;
    }
    if (!Util.isFunc(callback)) {
        return prompt(message, defaultText);
    }
    var $in = Util.isNumber(height)
	? c$('textarea').css({'width':600,'height':height})
	: c$('input:text').width(600);
	
    var Popup = new PopupObject({
        type     : 'simple',
        zIndex   : 99999,
        buttons  : [{ 
		    label: 'Accept', 
			onclick: function () {
		        Popup.close();
		        callback && callback(String($in.val()));
		        return false;
		    } 
		}, { 
			label: 'Cancel' 
		}]
    });
	
	c$('div').css('margin', '10px 0px 5px 0px').appendTo(Popup.content)
    .append(c$('h4').css('margin', 0).text(message||'write a text:')).append($in);
	
	$in.val(defaultText||'');
	
	Popup.applyCustomClass();
	Popup.show(true);
}
/**
 * Display a modal popup showing text to the user.
 * @param {String} message
 * @param {String} text
 */
function showTextPopup(message, text) {
    if (!message || !text) {
        return;
    }
    var $in = c$('textarea','readonly:readonly').css({'width':600,'height':250}).val(text);
    var Popup = new PopupObject({
        type     : 'simple',
        zIndex   : 99999,
        buttons  : [{
            label    : 'Select All',
            addClass : 'short white',
            onclick  : function() {$in.select(); return false;}
        }]
    });
    
    c$('div').css('margin', '10px 0px 5px 0px')
	.append(c$('h4').css('margin', 0).text(message))
    .append($in).appendTo(Popup.content);
    
	Popup.applyCustomClass();
    Popup.show(true);
}
/**
 * Display a modal popup asking to the user.
 * @param {String} title
 * @param {String} message
 * @param {Function} accept_callback
 * @param {Function} cancel_callback
 */
function showAskPopup(title, message, accept_callback, cancel_callback) {
    if (!Util.isFunc(accept_callback)) {
        return confirm(title);
    }
    showHelpPopup({
        icon: 'info',
        title: title,
        message: message,
        hideCloseButton: true,
        buttons: [{
            label: 'Accept',
            addClass: 'short green',
            onclick: accept_callback
        }, {
            label: 'Cancel',
            addClass: 'short white',
            onclick: cancel_callback
        }]
    });
}
/**
 * Show a publishing message that autoclose after 3 seconds.
 */
function showPublishMessage() {
    if (UserConfig.main.publishPreview === true) {
        return;
    }
    showHelpPopup({
        icon: 'info',
        title: 'Post Published!',
        message: 'You\'ve published to selected target.',
        autoclose: 3
    });
}
/**
 * Show a graphical loading screen.<br><br>
 * - Use loadingScreen('text') to ahow the overlay.<br>
 * - Use loadingScreen() to hide the overlay.
 * @param {String} [message] Optional message. 
 * @return {jQuery}
 */
function loadingScreen(message) {
    $('#fbmw_menu').mouseleave();
    
    if (!Util.isSet(message) || message == 'hide') {
        return $('#overlay_pop').hide();
    }
    
    if (e$('#fbmw_addon_popup #overlay_pop') !== null) {
        $('#loaderText', '#overlay_pop').text(message);
        return $('#overlay_pop').show();
    }
    
    var $div = c$('div', 'id:overlay_pop').hide()
    .appendTo(e$('#fbmw_addon_popup') || c$('div', 'fbmw_addon_popup').prependTo('#final_wrapper'));
    
    c$('div', 'id:overlay_pop_bg,class:pop_bg').css('z-index', 20000).appendTo($div).show();
    c$('div', 'id:overlay_pop_box,class:socialMissionTryAgain').css('z-index', 20001)
    .appendTo($div).css('top', 100).show()
    .append(c$('div', 'loaderText').text(message))
    .append(c$('div').append(c$('img').attr('src', 
    'http://mwfb.static.zgncdn.com/mwfb/graphics/socialmissions/ajax-loader.gif')));
    
    return $div.show();
}
/**
 * Show a popup for group selection.
 * @param {Number} defaultGroup
 * @param {Function} callback
 */
function showGroupSelection(defaultGroup, callback, defaultMessage) {

    var Popup = new PopupObject('target_selection_popup', {
        type       : 'simple',
        top        : 150,
        width      : 560,
        background : 'black url(\''+global.zGraphicsURL+'Fight-Explain_bg.jpg\') repeat-x',
        zIndex     : 99999
    });
    
    var Events = {
        select_click: function() {
            $(this).unbind().addClass('disabled').find('span > span').text('Target Selected!');
            setTimeout(function() {
                Popup.close();
                callback(Util.getInputSelectedValue($('#select_fbgroups', Popup.content)), 
                         $('#message_post',Popup.content).val());
            }, 1000);
            return false;
        },
        refresh_click: function() {
            var $btn = $(this);
            if ($btn.hasClass('disabled')) {
                return false;
            }
            else {
                $btn.addClass('disabled');
            }
            global.Groups.refresh(function() {
                global.Groups.addToSelect('#select_fbgroups',defaultGroup);
                $btn.removeClass('disabled');
            });
            return false;
        }
    };
    
    Popup.addBase64Style(
        'I3RhcmdldF9zZWxlY3Rpb25fcG9wdXAgLmJsYWNrIHsNCglmb250LXdlaWdodDogYm9sZDsNCgljb2xvcjogcmdiKDIwOCwgMjA4'+
        'LCAyMDgpOyANCglib3JkZXI6IDFweCBzb2xpZCByZ2IoMTUzLCAxNTMsIDE1Myk7IA0KCWJhY2tncm91bmQtY29sb3I6IGJsYWNr'+
        'OyANCglmb250LXNpemU6IDE0cHg7DQp9DQojdGFyZ2V0X3NlbGVjdGlvbl9wb3B1cCAucG9wdXBfYm9keSB7DQoJbWFyZ2luLXRv'+
        'cDogMjBweDsNCgltYXJnaW4tcmlnaHQ6IDEwcHg7DQoJbWFyZ2luLWJvdHRvbTogMjBweDsNCgltYXJnaW4tbGVmdDogNTBweDsN'+
        'CgloZWlnaHQ6IDIwMHB4Ow0KCXRleHQtYWxpZ246IGxlZnQ7DQp9DQojdGFyZ2V0X3NlbGVjdGlvbl9wb3B1cCBhIHsNCgl0ZXh0'+
        'LWRlY29yYXRpb246IG5vbmU7DQoJZm9udC1zaXplOiAxNHB4Ow0KfQ0KI3RhcmdldF9zZWxlY3Rpb25fcG9wdXAgcCB7DQogICAg'+
        'bWFyZ2luOiAxcHg7DQoJcGFkZGluZzogMHB4Ow0KfQ=='
    );
    
    c$('div', 'class:popup_body').appendTo(Popup.content)
    .append(c$('p').text('Write a Message:').css('margin-bottom',1))
    .append(c$('textarea', 'id:message_post').width(450).height(80))
    .append('<br /><br />')
    .append(c$('p').text('Select Destination:').css('margin-bottom',1))
    .append(c$('select', 'id:select_fbgroups').css('margin-right',5).width(400))
    .append(c$('a', 'href:#').text('Refresh').click(Events.refresh_click));
    
    b$('Publish', 'class:medium white').appendTo(Popup.content).click(Events.select_click);
    
    if (Util.isString(defaultMessage)) {
        $('#message_post', Popup.content).val(defaultMessage);
    }
    global.Groups.addToSelect('#select_fbgroups',defaultGroup);
    Popup.applyCustomClass();
    Popup.show();
}

/**
 * Show new version update. 
 * @param {Object} info
 */
function showVerInfoPopup(info) {
    var Popup = new PopupObject('version_info_popup', {
        type       : 'simple',
        top        : 100,
        width      : 645,
        background : '#161616 url(\''+global.zGraphicsURL+'clan_chat/settings_popup_bgrnd_1.png\') repeat-x',
        zIndex     : 9999
    });
    
    c$('div','class:info_header').html(info.message).appendTo(Popup.content);
    
    var $body = c$('div', 'class:info_body').appendTo(Popup.content);
    
    c$('div').appendTo(Popup.content).css('margin','12px 10px 5px 10px').append(c$('span')
    .text('MWAddon requires some effort, if you think this work deserves a reward, you can donate at:'));
    c$('a', 'target:_black').appendTo(Popup.content).attr('href', 'https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=CQVWPSUMDKW5N')
    .append(c$('img').attr('src', 'https://www.paypal.com/en_US/i/btn/btn_donate_SM.gif'));
    
    c$('center').css({'padding-top':12,'border-top':'1px dotted #333'}).appendTo(Popup.content)
    .append(b$(info.isnew?'Update Now!':'Go to Page', 'class:medium white')
              .attr('onclick',"window.open('"+info.url+"', '_blank');return false;"));
    
    $('#popup_fodder_zmc').empty();
    
    $body.append(c$('p').text('Loading version history...'));
    $body.append(c$('span').text('Wait a moment while history is loaded, '));
    $body.append(c$('a', 'target:_black').attr('href', info.history).text('or click here.'));
    
    httpXDRequest({  
        method : 'GET',
        url    : info.history,
        onload : function(s) {
            $body.html(h$(s.responseText).find('table tr.by-author:first td.body').html());
            //$body.find('br').remove();
        }
    });
    Popup.addBase64Style(
        'I3ZlcnNpb25faW5mb19wb3B1cCAuaW5mb19oZWFkZXIgew0KICAgIGhlaWdodDogNTVweDsNCiAgICBmb250LXNpemU6IDE2cHg7'+
        'DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQp9DQojdmVyc2lvbl9pbmZvX3BvcHVwIC5pbmZvX2JvZHkgew0KICAgIHRleHQtYWxp'+
        'Z246IGxlZnQ7DQogICAgb3ZlcmZsb3cteTogYXV0bzsNCiAgICBtYXJnaW46IDBweCAyNXB4IDBweCAzNXB4Ow0KICAgIGhlaWdo'+
        'dDogMzIwcHg7DQogICAgcGFkZGluZy1yaWdodDogMTBweDsNCiAgICBmb250LXNpemU6IDEycHg7DQp9DQojdmVyc2lvbl9pbmZv'+
        'X3BvcHVwIC5pbmZvX2JvZHkgcHJlIHsNCiAgICBtYXJnaW46IDBweCAwcHggNXB4IDBweDsNCiAgICBmb250LXNpemU6IDEycHg7'+
        'DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogICAgYmFja2dyb3VuZC1jb2xvcjogIzMzMzsNCiAgICBib3JkZXI6IDFweCBzb2xp'+
        'ZCAjNDQ0Ow0KICAgIHBhZGRpbmc6IDFweCA1cHggMXB4IDVweDsNCn0NCiN2ZXJzaW9uX2luZm9fcG9wdXAgLmluZm9fYm9keSB1'+
        'bCB7DQogICAgbWFyZ2luOiAwcHggMHB4IDVweCAwcHg7DQogICAgYm9yZGVyLWJvdHRvbTogMXB4IGRvdHRlZCAjMzMzOw0KICAg'+
        'IHBhZGRpbmctYm90dG9tOiAxMHB4Ow0KfQ=='
    );
    Popup.show();
}
/**
 * Edit a link to add to the user custom links.
 * @param {Function} callback
 * @param {Object} data
 */
function showBookmarkEditor(callback, data) {
    
    var excluded_params = /cb|tmp|xw_person/i;
    var Popup = new PopupObject('edit_link_popup', {
        type       : 'simple',
        top        : 150,
        width      : 560,
        background : 'black url(\''+global.zGraphicsURL+'Fight-Explain_bg.jpg\') repeat-x',
        zIndex     : 99999,
        buttons: [{
            label: 'Save',
            addClass: 'short white',
            onclick: function() {
                callback&&callback({
                    name   : $('#link_name', Popup.content).val(),
                    url    : $('#link_url', Popup.content).val()
                });
                Popup.close();
                return false;
            }
        }]
    });
    if (Util.isSet(data) && data.url) {
        data.params = new Object();
        Util.each(Util.uSplit(data.url), function(name, value) {
            if (!excluded_params.test(name)) {
                data.params[name] = value;
            }
        });
        if (data.params.xw_action!=='travel') {
            data.params.xw_action = 'view';
        }
        data.city = global.city(data.params.xw_city||1).name;
        data.url = $.param(data.params);
    } else {
        data = {};
    }
    Popup.content.css({
        'font-size': 14,
        'padding': '10px 0px 15px 20px',
        'text-align': 'left',
        'color': '#FFD927'
    })
    .append(c$('div').text('Bookmark Name:').css('margin-bottom',1))
    .append(c$('input:text', 'id:link_name').width(500).val(data.name||''))
    .append(c$('div').text('Destination:').css('margin-bottom',1))
    .append(c$('input:text', 'id:link_url').width(500).val(data.url||''))
    .append(c$('div').css({'margin-top':5,'clear':'both'}));
    if (Util.isSet(data.city)) {
        x$('force_travel', 'Does this link requires to travel to '+data.city+'?', 'div')
        .appendTo(Popup.content).click(function(){
            var new_url = 'xw_controller=travel&xw_action=travel';
            var url = Util.uSplit(data.url);
            if ($(this).hasClass('checked')) {
                new_url += '&from=' +url.xw_controller+'&destination='+url.xw_city;
                Util.each(url, function(name, value) {
                    if (!/from|destination|xw_controller|xw_action|xw_city/.test(name)) {
                        new_url += '&'+name+'='+value;
                    }
                });
            } else {
                new_url = data.url;
            }
            $('#link_url', Popup.content).val(new_url);
        });
           
    }
    Popup.applyCustomClass();
    Popup.show();
}// ==Script==
// @id        Plugins.js
// @author    Dakam
// @memberOf  MWAddon.js
// ==Script==

UserConfig.create('plugins', {all: new Array()});

/**
 * @namespace
 */
var Plugins = {
    // constants
    SRC_PLAIN_TEXT: 0,
    SRC_REMOTE: 1,
    RUNAT_USER_CLICK: 0,
    RUNAT_STARTUP: 1,
    CODE_EMPTY: '',
    // functions
    sources: new Array(),
    collection: new Array(),
    /**
     * Get a menu object style Array.
     */
    getMenu: function() {
        var oArray = new Array();
        Plugins.each(function(i,m) {
            if (m.active===true && parseInt(m.runat) === Plugins.RUNAT_USER_CLICK) {
                oArray.push({'name':m.name, 'click':{'func':Plugins.exec,'params':[i]}});
            }
        });
        return oArray;
    },
    /**
     * Reload plugins, this don't initializate the plugins. 
     * @param {Function} callback
     */
    reload: function(callback) {
        UserConfig.plugins.load(function() {
            Plugins.collection = new Array();
            Util.each(UserConfig.plugins.all, function(index, plugin) {
                Plugins.collection.push(new MWAddonPlugin(plugin));
            });
            callback&&callback();
        });
    },
    /**
     * Load and initializate all plugins. 
     * @param {Function} callback
     */
    init: function(callback) {
        var total_requests = 0;
        Plugins.sources = new Array();
        
        UserConfig.plugins.load(function() {
            Plugins.collection = new Array();
            Util.each(UserConfig.plugins.all, function(index, plugin) {
                Plugins.collection.push(plugin = new MWAddonPlugin(plugin));
                
                if (plugin.src === Plugins.SRC_REMOTE) {
                    if (/http/.test(plugin.code)) {
                        Logger.debug('Downloading Remote JS File: '+plugin.code);
                        if (plugin.runat === Plugins.RUNAT_STARTUP) { total_requests++; }
                        Plugins.getRemoteJS(plugin.code, function(jsText) {
                            Plugins.addFunc(index, jsText);
                            if (plugin.runat === Plugins.RUNAT_STARTUP) { total_requests--; }
                        });
                    }
                } else {
                    if (/^javascript:/.test(plugin.code)) {
                        plugin.code = 'return '+plugin.code.substring(11);
                    }
                    Plugins.addFunc(index, plugin.code);
                }
            });
            if (Util.isFunc(callback)) {
                Util.until({test: function(){ return (total_requests < 1); }, success: callback});
            }
        });
    },
    /**
     * Save all plugins
     * @param {Function} callback
     */
    save: function(callback) {
        UserConfig.plugins.all = Plugins.collection;
        UserConfig.plugins.save(callback);
    },
    /**
     * Change a Plugin position.
     * @param {Number} old_index
     * @param {Number} step
     */
    newPosition: function(old_index, step) {
        return Util.moveArrayItem( Plugins.collection, old_index, step );
    },
    /**
     * Add a new plugin.
     * @param {Object} index
     * @param {Object} code
     */
    addFunc: function(index, code) {
        try {
            var f = new Function('unsafeWindow','jQuery','User','Util','MW','FB',
            'alert','confirm','prompt','c$','e$','x$','n$','b$','s$','t$','h$','MWAddon',code);
            if (Util.isFunc(f)) {
                Plugins.sources[index] = f;
            } else {
                throw 'Unexpected function string';    
            }            
        } catch (e) {
            var name = Plugins.get(index).name;
            showHelpPopup({
                icon: 'error',
                title: 'Plugin Code Error',
                message: 'The code in "'+name+'" contains errors and cant be processed.'
            });
        }
    },
    /**
     * Get a remote javascript.
     * @param {MWAddonPlugin} plugin
     * @param {Function} callback
     */
    getRemoteJS: function(url, callback) {
        httpXDRequest({
            method: 'GET',
            headers: {'Content-Type': 'text/javascript'},
            url: Util.trim(url)+'?'+Math.random(),
            onload: function(r) {
                callback&&callback(Util.trim(r.responseText));
            }
        });    
    },
    /**
     * Get a plugin.
     * @param {Object} key
     * @return {MWAddonPlugin}
     */
    get: function(key) {
        if (Util.isString(key)) {
            var found;
            Plugins.each(function(index, p) {
                if (Util.isSameString(key, p.name)) {
                    found = p;
                    return false;
                }
            });
            return found;
        }
        if (Util.isNumber(key)) {
            return Plugins.collection[key];
        }
        return;
    },
    /**
     * Return true if the specified Plugin id exists.
     * @param {Object} key
     */
    exists: function(key) {
        return Util.isSet(Plugins.get(key));
    },
    /**
     * Loops through all plugins.
     * @param {Function} callback
     */
    each: function(callback) {
        if (Util.isFunc(callback)) {
            Util.each(Plugins.collection, callback);
        }
    },
    /**
     * Remove the specified plugin.
     * @param {String} key
     * @param {Function} callback
     */
    remove: function(key, callback) {
        if (Util.isString(key)) {
            Plugins.each(function(i, p) {
                if (Util.isSameString(key, p.name)) {
                    key = i;
                    return false;
                }
            });
        }
        if (Util.isNumber(key)) {
            Plugins.collection.splice(key, 1);
            Plugins.save(callback);
        } else {
            callback&&callback();
        }
    },
    /**
     * Execute a plugin.<br>
     * This method call the plugin function with a MWAddon object context (to use with this).
     * @param {Number, String} index
     */
    exec: function(index) {
        var thisApp = Plugins.get(index);
        if (!Util.isFunc(Plugins.sources[index]) || !thisApp || !thisApp.active) {
            return;
        }
        Logger.debug('Executed: "'+thisApp.name+'" Plugin.');
        var scope = global.createMWAddonScope(index, thisApp.name, 'MWAddonPlugin');
        Plugins.sources[index].apply(scope, [ unsafeWindow,
            unsafeWindow.jQuery, unsafeWindow.User, unsafeWindow.Util, unsafeWindow.MW, 
            unsafeWindow.FB, function(){return showHelpPopup.apply(scope, arguments)}, 
            showAskPopup, showPromptPopup, c$, e$, x$, n$, b$, s$, t$, h$, scope
        ]);
    },
    /**
     * execute a plugin by name.
     * @param {String} key
     */
    execByName: function(key) {
        Plugins.each(function(i, p) {
            if (Util.isSameString(p.name, key)) {
                Plugins.exec(i);
                return false;
            }
        });
    },
    /**
     * Add a new plugin.
     * @param {Object} obj
     * @param {Function} callback
     */
    addNew: function(obj, callback) {
        if (!obj || !Util.isString(obj.name)) {
            callback&&callback();
            return;
        }
        var index = Plugins.collection.length;
        var plugin = new MWAddonPlugin(obj);
        
        Plugins.each(function(i, p) {
            if (Util.isSameString(obj.name, p.name)) {
                index = i;
                plugin.active = p.active;
                return false;
            }
        });
        
        Plugins.collection[index] = plugin;
        
        Plugins.save(function() {
            callback&&callback(index, plugin);
        });
    }
};
/**
 * Create a new Reminder class.
 * @constructor
 * @param {MWAddonPlugin} from
 * @return {MWAddonPlugin}
 */
function MWAddonPlugin(from) {
    var me = this;
    me.active    = false;
    me.name      = 'New Plugin';
    me.src       = Plugins.SRC_PLAIN_TEXT;
    me.runat     = Plugins.RUNAT_USER_CLICK;
    me.code      = Plugins.CODE_EMPTY;
    if (from) {
        if (from.click) {from.code = from.click;}
        Util.each(me, function(name, value) {
            if (!Util.isFunc(value)) {
                me[name] = UserConfig.toType(from[name], value);
            }
        });
        from = null;
    }
    return me;
};
// constructor and identifier
MWAddonPlugin.prototype.constructor = MWAddonPlugin;
/**
 * Update this reminder data.
 * @param {Object} from
 */
MWAddonPlugin.prototype.update = function(from) {
    var me = this;
    if (Util.isString(from)) {
        from = Util.parseJSON(from);
    }
    if (from.name && Plugins.exists(from.name)) {
        delete from.name;
    }
    Util.each(from, function(name, value) {
        if (name !== 'active' && !Util.isFunc(value)) {
            me[name] = value;
        }
    });
};
/**
 * Update this Plugin data.
 * @param {Object} from
 */
MWAddonPlugin.prototype.values = function(from) {
    var values = new Object(), me = this;
    Util.each(me, function(name, value) {
        if (name !== 'active' && !Util.isFunc(value)) {
            values[name] = value;
        }
    });
    return values;
};
/**
 * Get an install link for this plugin.
 */
MWAddonPlugin.prototype.getLink = function() {
    var data = Util.toJSON(this.values());
    return MW.getExtURL('index', 'view', {
        'next_params': {'mwaddon_plugin': Base64.encode(data)}
    });
};
/**
 * Return a json encoded plugin.
 */
MWAddonPlugin.prototype.encode = function() {
    return Util.toJSON(this.values(), true);
};
// ==Script==
// @id        Reminder.js
// @author    Dakam
// @requires  MWAddon.js
// ==Script==

UserConfig.create('reminder', {all: new Array(), fixed: new Object()});
/**
 * @namespace Reminders
 */
var Reminders = {
    isDisabled: false,
    collection: new Array(),
    /**
     * Init the config object.
     */
    init: function(callback) {
        
        // load up user reminders.
        UserConfig.reminder.load(function() {
            Reminders.collection = new Array();
            
            Util.each(UserConfig.reminder.all, function(index, rem) {
                Reminders.collection.push(new MWAddonReminder(rem));
            });
            
            callback&&callback();
        });
    },
    /**
     * Save all reminders
     * @param {Function} callback
     */
    save: function(callback) {
        UserConfig.reminder.all = Reminders.collection;
        UserConfig.reminder.save(callback);
    },
    /**
     * Get the milliseconds of the specified time increase in days, hours, minutes, seconds.
     * @param {Number} d
     * @param {Number} h
     * @param {Number} m
     * @param {Number} s
     */
    timeIncrease: function(d,h,m,s) {
        if (d&&d>0) h=(h||0)+(d*24);
        if (h&&h>0) m=(m||0)+(h*60);
        if (m&&m>0) s=(s||0)+(m*60);
        var ms = s*1000;
        return ms;
    },
    /**
     * Get a menu object style Array.
     */
    getMenu: function() {
        var oArray = new Array();
        Reminders.each(function(i, rem) {
            if (rem.active !== true || rem.fixed === true || !Util.isFunc(rem.remain)) {
                return;
            }
            oArray.push({
                'name': rem.name, 
                'important': rem.important, 
                'click': {'func':Reminders.exec,'params':[i]},
                'remain': parseInt(rem.remain()/1000)
            });
        });
        return oArray;
    },
    /**
     * Get a Reminder.
     * @param {Object} key
     * @return {MWAddonReminder}
     */
    get: function(key) {
        if (Util.isString(key)) {
            var found;
            Reminders.each(function(i, rem) {
                if (Util.isSameString(key, rem.name)) {
                    found = rem;
                    return false;
                }
            });
            return found;
        }
        if (Util.isNumber(key)) {
            return Reminders.collection[key];
        }
        return;
    },
    /**
     * Return true if the specified reminder id exists.
     * @param {Object} key
     */
    exists: function(key) {
        return Util.isSet(Reminders.get(key));
    },
    /**
     * Loops through all reminders.
     * @param {Function} callback
     */
    each: function(callback) {
        if (Util.isFunc(callback)) {
            Util.each(Reminders.collection, callback);
        }
    },
    /**
     * Remove the specified reminder.
     * @param {Object} key
     * @param {Function} callback
     */
    remove: function(key, callback) {
        if (Util.isString(key)) {
            Reminders.each(function(i, rem) {
                if (Util.isSameString(key, rem.name)) {
                    key = i;
                    return false;
                }
            });
        }
        if (Util.isNumber(key)) {
            Reminders.collection.splice(key, 1);
            Reminders.save(callback);
        } else {
            callback&&callback();
        }
    },
    /**
     * Execute a reminder.
     * @param {Object} key
     */
    exec: function(key) {
        var rem = key;
        if (!Util.isFunc(rem.reset)) {
            rem = Reminders.get(key);
        }
        if (!Util.isFunc(rem.reset)) {
            return;
        }
        var zpopups = $('#content_row > div[id^=popup_] div:has(.pop_box:visible)');
        if (zpopups.length > 0 || Util.length(global.Popups) > 0) {
            var me = this;
            showAskPopup('Popups Executing', 
            'There is popups open, would you like to continue anyway?', 
            function() {
                zpopups.css('display', 'none');
                Util.each(global.Popups, function(id, data) {
                    data&&data.popup&&data.popup.close();
                });
                setTimeout(function() {
                    rem.reset(doReminderStep1);
                }, 1000);
            });
            return;
        }
        function doReminderStep1() {
            rem._loc = false;
            if (rem.v2) {
                rem.exec();
                return;
            }
            if ( rem.gotocity === true ) {
                if (MW.currentCity() !== parseInt(rem.gotocityid)) {
                    MW.travel(rem.gotocityid, doReminderStep2, '#inner_page');
                    return;
                }
            }
            doReminderStep2();
        }
        function doReminderStep2() {
            var rgx, url = String(rem.gotopageurl);
            if ( rem.gotopage === true ) {
                if ( (rgx=/^run:(.+)/.exec(url)) ) {
                    setTimeout(function() {
                        eval(rgx[1]);
                    }, 1000);
                    return;
                } else if ( (rgx=/^tab:(.+)/.exec(url)) ) {
                    unsafeWindow.open(rgx[1]);
                    return;
                } else if ( (url = rem.getUrlLink()) ) {
                    MW.goPage(url,'#inner_page',doReminderStep3);
                    return;
                }
            }
            doReminderStep3();
        }
        function doReminderStep3() {
            if ( rem.runplugin === true ) {
                Plugins.exec(rem.runpluginid);
            }
        }
        rem.reset(doReminderStep1);
    },
    /**
     * Check all reminders
     * @param {String} url
     */
    checker: function(url) {
        if (Reminders.isDisabled === true) {
            return;
        }
        Reminders.each(check);
        
        function check(index, rem) {
            if (rem.active !== true) {
                return;
            }
            if ( Util.isString(url) && rem.testUrl(url) ) {
                Logger.debug('Reset on page load to "'+rem.name+'":\n'+url);
                rem.reset();
            }
            else if (rem.isReady() && MWAddonNotify.Showing() == 0) {
                Reminders.Show(rem);
                return false
            }
        }
    },
    /**
     * Add a new reminder.
     * @param {Object} rem
     * @param {Function} callback
     * @param {Boolean} [active]
     */
    addNew: function(rem, callback, active) {
        if (!rem || !Util.isString(rem.name)) {
            callback&&callback();
            return;
        }
        var index = Reminders.collection.length;
        var reminder = new MWAddonReminder(rem);
        
        Reminders.each(function(i, r) {
            if (Util.isSameString(rem.name, r.name)) {
                index = i;
                reminder.active = r.active;
                return false;
            }
        });
        
        Reminders.collection[index] = reminder;
        
        Reminders.save(function() {
            callback&&callback(index, reminder);
        });
    },
    
    Show: function(reminder) {
        if (!MWAddonNotify.Initialized !== true) {
            MWAddonNotify.init();
        }
        if (Util.length(global.Popups) > 0) {
            return;
        }
        reminder._loc = true;
        MWAddonNotify.Show(reminder.name+' says:', reminder.description, function(success) {
            if (success===true) {
                Reminders.exec(reminder);
            } else {
                reminder.wait(15*60*1000);
            }
            reminder._loc = false;
        }, reminder.timeout||15000, reminder.id);
    }

};
/**
 * Create a new Reminder class.
 * @constructor
 * @param {MWAddonReminder} from
 * @return {MWAddonReminder}
 */
function MWAddonReminder(from) {
    var me = this;
    me.id             = (from&&from.id||Reminders.collection.length);
    me.active         = false;
    me.important      = false;
    me.name           = 'New Reminder';
    me.description    = 'Type here a description';
    me.lastcheck      = 0;
    me.checktype      = 0;
    me.every          = 0;
    me.resetonload    = false;
    me.resetonloadurl = '';
    if (from && from.v2) {
        me.v2         = true;
        me.jstest     = 'return true;';
        me.jsexec     = '';
    } else {
        me.gotocity       = false;
        me.gotocityid     = 1;
        me.gotopage       = false;
        me.gotopageurl    = '';
        me.runplugin      = false;
        me.runpluginid    = 0;
    }
    if (from) {
        Util.each(me, function(name, value) {
            if (!Util.isFunc(value)) {
                me[name] = UserConfig.toType(from[name], value);
            }
        });
        if (from.fixed === true) {
            me.fixed = true;
        }
        from = null;
    }
    return me;
};
// constructor and identifier
MWAddonReminder.prototype.constructor = MWAddonReminder;
/**
 * Test a url ro check ResetOnLoad.
 * @param {String} url
 */
MWAddonReminder.prototype.testUrl = function(url) {
    var bValid = false;
    if (this.resetonload === true) {
        url = String(url);
        var p = Util.uSplit(this.resetonloadurl);
        if ( p.xw_controller && p.xw_action ) {
            Util.each(p, function(name, value) {
                return (bValid = (url.indexOf(name+'='+value) > -1 ));
            });
        }
    }
    return bValid;
};
/**
 * Get the remaining time for this reminder.
 */
MWAddonReminder.prototype.remain = function() {
    var now = (new Date()).getTime();
    var me = this;
    if (me.delay && me.delay > 0) {
        return me.delay - now;
    }
    if (!(me.every > 0)) {
        me.every = 0.1;
    }
    function getFixedDate(day) {
        var a = new Date();
        if (Util.isSet(me.lastcheck)) {
            a = new Date(me.lastcheck);
            a.setDate(a.getDate()+1);
        }
        if (parseInt(me.every)>23) {
            me.every = 0;
        }
        a.setHours(me.every);
        a.setMinutes(0);
        a.setSeconds(0);
        return a;
    }
    switch(parseInt(me.checktype)) {
        case 0: return Math.max( 0, (me.lastcheck+(me.every*60*60*1000)) - now );
        case 1: return Math.max( 0, getFixedDate().getTime() - now );
    } 
};
/**
 * Check if reminder was timeout.
 */
MWAddonReminder.prototype.isReady = function() {
    var me = this;
    if (me._loc === true) {
        return false;
    }
    if (!Util.isSet(me.lastcheck)) {
        if (parseInt(me.checktype) === 0) {
            return true;
        }
    }
    if (me.v2) {
        //console.log('checking '+this.name+': '+me.remain()+'  &  '+me.test());
        return (me.remain() < 1) && me.test();
    } else {
        //console.log('checking '+this.name+': '+me.remain());
        return me.remain() < 1; 
    }
};
/**
 * Reset the timer of this reminder.
 */
MWAddonReminder.prototype.reset = function(callback, important) {
    this.important = false;
    this.lastcheck = (new Date()).getTime();
    this.delay = null;
    if (this.fixed === true) {
        UserConfig.reminder.fixed[this.name] = this.lastcheck;
    }
    Reminders.save(callback);
};
/**
 * Reset the timer of this reminder.
 */
MWAddonReminder.prototype.wait = function(delay) {
    if (this.fixed === true) {
        this.reset();
    } else {
        this.important = true;
        this.delay = (new Date()).getTime() + delay;
    }
    Reminders.save();
};
/**
 * Set this reminder to show.
 */
MWAddonReminder.prototype.start = function(callback) {
    this.important = false;
    this.lastcheck = 0;
    Reminders.save(callback);
};
/**
 * Build a valid url.
 * @param {Object} p
 */
MWAddonReminder.prototype.getUrlLink = function(p) {
    var p = Util.uSplit(this.gotopageurl);
    if (!p.xw_controller) {
        return false;
    }
    var url = 'remote/'+MW.getIntURL(p.xw_controller,p.xw_action||'view');
    Util.each(p, function(name,value) {
        if ( !/xw_/.test(name) ) {
            url += ('&'+name+'='+value);
        }
    });
    return url;
};
/**
 * Update this reminder data.
 * @param {Object} from
 */
MWAddonReminder.prototype.update = function(from) {
    var excluded = ['active','important','lastcheck'];
    var me = this;
    if (Util.isString(from)) {
        from = Util.parseJSON(from);
    }
    if (from.name && Reminders.exists(from.name)) {
        delete from.name;
    }
    Util.each(me.values(), function(name) {
        if (Util.isSet(from[name]) && !Util.isFunc(from[name])) {
            me[name] = from[name];
        }
    });
};
/**
 * Update this reminder data.
 * @param {Object} from
 */
MWAddonReminder.prototype.values = function(from) {
    var excluded = ['active','important','lastcheck'];
    var values = new Object(), me = this;
    Util.each(me, function(name, value) {
        if (!Util.isFunc(value) && excluded.indexOf(name) < 0) {
            values[name] = value;
        }
    });
    return values;
};
/**
 * Execute this reminder js-test code.
 */
MWAddonReminder.prototype.test = function() {
    var now = (new Date()).getTime();
    var fn = new Function('MWAddon', 'unsafeWindow', 'now', 'timeIncrease', this.jstest);
    var scope = global.createMWAddonScope(null, this.name, 'MWAddonReminder');
    var result = fn.apply(this, [scope, unsafeWindow, now, Reminders.timeIncrease]);
    return result;
};
/**
 * Execute this reminder js-exec code.
 */
MWAddonReminder.prototype.exec = function() {
    var fn = new Function('MWAddon', 'unsafeWindow', 'openURL', this.jsexec);
    var scope = global.createMWAddonScope(null, this.name, 'MWAddonReminder');
    fn.apply(this, [scope, unsafeWindow, unsafeWindow.open]);
};
/**
 * Get an install link for this reminder.
 */
MWAddonReminder.prototype.getLink = function() {
    var data = Util.toJSON(this.values());
    return MW.getExtURL('index', 'view', {
        'next_params': {'mwaddon_reminder': Base64.encode(data)}
    });
};
/**
 * Return a json encoded reminder.
 */
MWAddonReminder.prototype.encode = function() {
    return Util.toJSON(this.values(), true);
};
// ==Script==
// @id        UserFreeGifts.js
// @author    Dakam
// @requires  MWAddon.js
// ==Script==

/**
 * @namespace
 */
var UserFreeGifts = {
    content: {
        'ccb': { request_id:'city_crew_brazil',name:'City Crew', img:'buy_hiredguns_75x75_01.gif'             },
        'enp': { request_id:'energy',name:'Energy Pack'      , img:'energy_request_bg.gif'                    },
        503  : { item_id: 8035,  name:'Power Pack'           , img:'huge_item_powerpack_01.png'               },
		441  : { item_id: 3000,  name:'Mission Crew'         , img:'buy_hiredguns_75x75_01.gif'               },
        
        1014 : { item_id: 2297,  name:'Poker Card'           , img:'huge_item_poker_card_02.png'              },
        1015 : { item_id: 12218, name:'English Brick'        , img:'huge_item_englishbrick_01.png'            },
        1016 : { item_id: 12219, name:'Clay Tiles'           , img:'huge_item_claytiles_01.png'               },
        1017 : { item_id: 12220, name:'Sledge Hammer'        , img:'huge_item_sledgehammer_01.png'            },
        1018 : { item_id: 12221, name:'Plumb Line'           , img:'huge_item_plumbline_01.png'               },
        1019 : { item_id: 12222, name:'Iron Girder'          , img:'huge_item_irongirder_01.png'              },
        
        1000 : { item_id: 11051, name:'Union Worker'         , img:'huge_item_union_worker_01.png'            },
        1001 : { item_id: 11052, name:'Carpenter Nails'      , img:'huge_item_carpenter_nails_01.png'         },
        1002 : { item_id: 11053, name:'Lath Strips'          , img:'huge_item_lath_strips_01.png'             },
        1003 : { item_id: 11054, name:'Iron Cast'            , img:'huge_item_iron_cast_01.png'               },
        1004 : { item_id: 11055, name:'Douglas Fir Beams'    , img:'huge_item_douglas_fir_beams_01.png'       },
        100  : { item_id: 527,   name:'Blue Mystery Bag'     , img:'item_mysterybag_blue_1.png'               },
        400  : { item_id: 4400,  name:'Secret Drop'          , img:'icon_atk_75x75_01.png'                    },
        401  : { item_id: 2600,  name:'Italian Hardwood'     , img:'huge_item_italianhardwood_01.png'         },
        402  : { item_id: 2601,  name:'Marble Slab'          , img:'huge_item_marbleslab_01.png'              },
        422  : { item_id: 4604,  name:'Exotic Animal Feed'   , img:'huge_item_exoticanimalfeed_01.png'        },
        444  : { item_id: 2610,  name:'Set of Hidden Charges', img:'huge_item_hiddencharges_01.png'           },
        445  : { item_id: 2614,  name:'Cooked Book'          , img:'huge_item_cookedbook_01.png'              },
        189  : { item_id: 2319,  name:'Special Part'         , img:'huge_item_parts_01.png'                   },
        417  : { item_id: 4605,  name:'Aquarium'             , img:'huge_item_aquarium_01.png'                },
        418  : { item_id: 4606,  name:'Big Cage'             , img:'huge_item_bigcage_01.png'                 },
        419  : { item_id: 4607,  name:'Bird Cage'            , img:'huge_item_birdcage_01.png'                },
        420  : { item_id: 4608,  name:'Feeding Trough'       , img:'huge_item_feedingtrough_01.png'           },
        421  : { item_id: 4609,  name:'Terrarium'            , img:'huge_item_terrarium_01.png'               },
        181  : { item_id: 2196,  name:'Hammer'               , img:'huge_item_hammer_01.png'                  },
        182  : { item_id: 2197,  name:'Rivet'                , img:'huge_item_rivets_01.png'                  },
        183  : { item_id: 2183,  name:'Furnace'              , img:'huge_item_furnace_01.png'                 },
        184  : { item_id: 2184,  name:'Vice'                 , img:'huge_item_vice_01.png'                    },
        185  : { item_id: 2185,  name:'Anvil'                , img:'huge_item_anvil_01.png'                   },
        75   : { item_id: 656,   name:'Arc Welder'           , img:'huge_item_arcwelder_01.gif'               },
        76   : { item_id: 657,   name:'Buzzsaw'              , img:'huge_item_electronicwhetstone_01.gif'     },
        77   : { item_id: 658,   name:'Gunpowder'            , img:'huge_item_gunpowder_01.gif'               },
        78   : { item_id: 659,   name:'Gun Drill'            , img:'huge_item_gundrill_01.gif'                },
        79   : { item_id: 660,   name:'Forge'                , img:'huge_item_forge_01.gif'                   },
        70   : { item_id: 532,   name:'Cement Block'         , img:'huge_item_cinderblock_01.png'             },
        71   : { item_id: 533,   name:'Power Tool'           , img:'huge_item_powertools_01.png'              },
        72   : { item_id: 534,   name:'Car Lift'             , img:'huge_item_carlift_01.png'                 },
        73   : { item_id: 535,   name:'Acetylene Torch'      , img:'huge_item_acetylenetorches_01.png'        },
        74   : { item_id: 536,   name:'Shipping Container'   , img:'huge_item_shippingcontainers_01.png'      },
        161  : { item_id: 1575,  name:'Cinder Block'         , img:'huge_item_Cinderblock_01.gif'             },
        162  : { item_id: 1576,  name:'Steel Girder'         , img:'huge_item_buildcasino_steelgirders_01.gif'},
        163  : { item_id: 1577,  name:'Concrete'             , img:'huge_item_Concrete_01.gif'                },
        164  : { item_id: 1578,  name:'Construction Tool'    , img:'huge_item_constructiontools_01.png'       },
        169  : { item_id: 762,   name:'Security Camera'      , img:'huge_item_securitycamera_01.png'          },
        170  : { item_id: 763,   name:'Reinforced Steel'     , img:'huge_item_reinforcedsteel_01.png'         },
        171  : { item_id: 764,   name:'Deposit Box'          , img:'item_depositbox_01.png'                   },
        172  : { item_id: 765,   name:'Motion Sensor'        , img:'item_motionsensor_01.png'                 },
        173  : { item_id: 766,   name:'Magnetic Lock'        , img:'huge_item_magneticlock_01.png'            }
    },
	/**
	 * Return a gift from item_id and item_cat
	 * @param {Number} item_id
	 * @param {Number} item_cat
	 * @return {Object}
	 */
	getByItemId: function(item_id, item_cat) {
		var found_gift = {
			item_id  : item_id,
			item_cat : item_cat,
			name     : 'Unknow Gift',
			img      : 'item_mysterybag_blue_1.png'
		};
		item_id = parseInt(item_id);
		item_cat = parseInt(item_cat||1);
		Util.each(UserFreeGifts.content, function(id, gift) {
			if (parseInt(gift.item_id) === item_id && parseInt(gift.item_cat) === item_cat) {
				found_gift = gift;
				return false;
			}
		});
		return found_gift;
	},
    /**
     * Return a gift by id
     * @param {Number} id
     * @return {Object}
     */
    get: function(id) {
        return UserFreeGifts.content[id];
    },
    /**
     * Add a new gift
     * @param {Number} id
     * @param {Object} data
     */
    add: function(id, data) {
        if (id) {
            UserFreeGifts.content[id] = data;
        }
    },
    /**
     * @param {Object} callback
     * @param {Boolean} [sorted]
     */
    each: function(callback, sorted) {
        if (sorted === true) {
            var content = new Array();
            Util.each(UserFreeGifts.content, function(id, gift) {
                content.push({
                    'id': id,
                    'gt': gift
                });
            });
            content.sort(function(a, b) {
                if (a.gt.name > b.gt.name) { return 1; }
                if (a.gt.name < b.gt.name) { return -1; }
                return 0;
            });
            Util.each(content, function(index, sortedItem) {
                return callback(sortedItem.id, sortedItem.gt);
            });
        } else {
            Util.each(UserFreeGifts.content, callback);
        }
    },
    /**
     * Return true if the gift exists.
     * @param {Object} id
     * @return {Boolean}
     */
    exists: function(id) {
        return Util.isSet(UserFreeGifts.content[id]);
    },
    /**
     * Get the specified gift image url. 
     * @param {String} url
     * @return {String}
     */
    getImageUrl: function(url) {
        if (/http/.test(url)) {
            return url;
        }
        else {
            return global.zGraphicsURL + url;
        }
    },
    
    update: function() {
        httpAjaxRequest({url:'remote/'+MW.getIntURL('freegifts'), success:parseResponse});
        function parseResponse(htmlText) {
            var today = (new Date()).getDate();
            var allCats = Util.doRgx(/var allCats = (.+?);/, htmlText).$1; 
            var n_added = 0, n_reqs = 0, userGiftsIds = UserConfig.main.userGiftsIds;
            if (allCats) {
                allCats = Util.parseJSON(allCats);
            } else {
                allCats = new Object();
            }
            h$(htmlText).find('.freegift_grid').children().each(function(index, elem) {
                try {
                    if (!(elem.id && e$('.freegift_box_stats', elem) !== null)) {
                        return;
                    }
                    var id = parseInt(elem.id.substring(13));
                    if (UserFreeGifts.exists(id)) {
                        return;
                    }
                    var name = $('.freegift_box_itemname',elem).text();
                    var icon = $('.freegift_box_image img',elem).attr('src');
                    UserFreeGifts.add(id, {'name': name, 'img': icon });
                    n_added++;
                }
                catch(err) {
                    Logger.error(err);
                }
            });
            if (!Util.isSet(userGiftsIds.today) || userGiftsIds.today != today) {
                userGiftsIds = (UserConfig.main.userGiftsIds = {
                    'today': today
                });
            }
            UserFreeGifts.each(function(id, gift) {
                gift.name = Util.formatText(gift.name);
                if (Util.isSet(gift.request_id)) {
                    return;
                }
                gift.item_cat = allCats[id] || 1;
				if (!Util.isSet(gift.item_id)) {
					if (Util.isSet(userGiftsIds[id])) {
						gift.item_id = userGiftsIds[id];
					} else {
						n_reqs++;
						MW.getGiftItemId(id, gift.item_cat, function(item_id) {
							n_reqs--;
							userGiftsIds[id] = parseInt(gift.item_id = item_id);
							if (n_reqs < 1) {
								UserConfig.main.save();
							}
						});
					}
				}
            });
            Logger.debug('Added '+n_added+' new free gifts.');
        }
    }
};
// ==Script==
// @id        Toolbar.js
// @author    Dakam
// @memberOf  MWAddon.js
// ==Script==
/**
 * The Toolbar Object.
 */
var MWAddonToolbar = {
    fanPageLinks: new Array(),
    initialized: false,
    isWatching: false,
    bookmarks: new Array(),
    countdowns: new Array()
};
/**
 * Events
 */
MWAddonToolbar.Event = {
    deposit: function() {
        var me = $(this);
        if (global.User.cash() > 10) {
            if (!me.hasClass('disabled')) {
                me.addClass('disabled');
                MW.deposit(MW.currentCity(), global.User.cash(), function(text) {
                    MWAddonToolbar.Update();
                    me.removeClass('disabled');
                    MWAddonMessage(text);
                });
            }
        } else {
            MWAddonMessage('You need at least $10 to deposit.');
        }
        return false;
    },
    heal: function() {
        var me = $(this);
        if (global.User.health() < global.User.max_health()) {
            if (!me.hasClass('disabled')) {
                me.addClass('disabled');
                MW.heal(1, function(text) {
                    MWAddonToolbar.Update();
                    me.removeClass('disabled');
                    MWAddonMessage(text);
                });
            }
        } else {
            MWAddonMessage('Your heal is already at maximum.');
        }
        return false;
    },
    new_link: function() {
        showBookmarkEditor(function(data) {
            if (data && Util.isString(data.name) && Util.isString(data.url)) {
                if (data.name.length > 0 && data.url.length > 0) {
                    UserConfig.main.userLinks.push(data);
                    UserConfig.main.save();
                }
            }
        });
        return false;
    },
    watcher_link: function() {
        var msg = 'Link Watcher Active!!<br>Click a link to grab the name and page...';
        $('a.button_menu_open').mouseleave();
        if (MWAddonToolbar.isWatching !== true) {
            MWAddonToolbar.LinkWatcher(msg, function(data) {
                showBookmarkEditor(function(data) {
                    UserConfig.main.userLinks.push(data);
                    UserConfig.main.save();
                }, data);
            });
        }
        return false;
    },
    edit_plugin: function() {
        $('a.button_menu_open').mouseleave();
        PluginManager();
        return false;
    },
    edit_reminder: function() {
        $('a.button_menu_open').mouseleave();
        ReminderEditor();
        return false;
    },
    showMenu: function() {
        var $a = $(this);
        var $ul = $('.toolbarbox > ul', $a);
        var icon = 'run';
        if ($a.hasClass('button_menu_open')) {
            return;
        }
        MWAddonNotify.hideAll();
        function onclick(event) {
            return function() {
                $a.mouseleave();
                setTimeout(function() {
                   event.func.apply(event, event.params); 
                }, 50);
                return false;
            };
        }
        function removeLink(index) {
            return function() {
                $a.mouseleave();
                showAskPopup('Remove Link', 'Are you sure to remove this link?', function() {
                    UserConfig.main.userLinks.splice(index, 1);
                    UserConfig.main.save();
                });
                return false;
            };
        }
        function addReminderButton(i, m) {
            var $li = c$('li','class:mwa_res_'+icon+'_menu_icon');
            c$('span','class:more_in,id:reminder_timer_'+i).appendTo($li);
            c$('div','class:item_name').text(m.name).appendTo($li).width(180);
            $li.appendTo($ul).css('padding-left',21).click(onclick(m.click));
            if (m.remain < 1) {
                $('#reminder_timer_'+i).html(Util.setColor('Ready' ,'green'));
                return;
            }
            if ( m.important ) {
                $li.removeClass().addClass('important');
            }
            (MWAddonToolbar.countdowns[i] = new Countdown({
                selector    : '#reminder_timer_'+i,
                delay       : m.remain,
                format      : [':',':',''],
                stop: function() {
                    $('#reminder_timer_'+i).html(Util.setColor('Ready' ,'green'));
                }
            })).start();
        }
        function addLinkButton(i, m) {
            var $li = c$('li');
            if (m.cbm === true) {
                $li.addClass('mwa_res_chrome_bookmark_icon');
            } else {
                $li.addClass('mwa_res_'+icon+'_menu_icon');
                c$('span','class:remove').appendTo($li).click(removeLink(i));
            }
            c$('div','class:item_name').text(m.name).appendTo($li).width(220);
            $li.appendTo($ul).css('padding-left',21);
            $li.click(onclick(m.click));
        }
        function addButton(i, m) {
            var $li = c$('li','class:mwa_res_'+icon+'_menu_icon').text(m.name);
            $li.appendTo($ul).css('padding-left',21).click(onclick(m.click));
        }
        switch(this.id) {
            case 'mwa_tlbLinks':
                icon = 'link';
                Util.each(MWAddonToolbar.GetLinks(), addLinkButton);
                break; 
            case 'mwa_tlbPlugins':
                Util.each(Plugins.getMenu(), addButton);
                break;
            case 'mwa_tlbReminders':
                icon = 'reminder';
                Util.each(Reminders.getMenu(), addReminderButton);
                break;
        }
        $a.removeClass('button_menu_closed').addClass('button_menu_open');
    },
    hideMenu: function() {
        var $a = $(this).removeClass('button_menu_open').addClass('button_menu_closed');
        var $ul = $('.toolbarbox > ul', $a);
        $('li', $ul).unbind();
        $ul.empty();
        if (MWAddonToolbar.countdowns.length > 0) {
            Util.each(MWAddonToolbar.countdowns, function(i, c) {
                c&&c.clear();
            });
            MWAddonToolbar.countdowns = new Array();
        }
    }
};
MWAddonToolbar.execJS = function(js) {
    if (/^javascript:/i.test(js)) {
        c$('script').text(unescape(js)).appendTo('body');
    }
}
/**
 * Start watching for the link the user was clicked.
 * @param {String} message
 * @param {Function} callback
 */
MWAddonToolbar.LinkWatcher = function(message, callback) {    
    MWAddonToolbar.isWatching = true;
    MWAddonNotify.Show('Link Watcher', message, Util.noop, 30000);
    
    $('a:regex(onclick,xw_controller)').each(function(index, link) {
        link = $(link);
        link.data('onclick', link.attr('onclick'));
        link.addClass('lockedlink');
        link.removeAttr('onclick');
        link.one('click', onclick);
    });
    $('a:regex(href,xw_controller)').each(function(index, link) {
        link = $(link);
        if (link.hasClass('lockedlink')) {
            return;
        }
        link.addClass('lockedlink');
        link.one('click', onclick);
    });
    function closeWatcher() {
        $('a.lockedlink').each(function(index, link) {
            link = $(link);
            link.removeClass('lockedlink');
            if (link.data('onclick')) {
                link.attr('onclick', link.data('onclick'));
                link.removeData('onclick');
            }
            try {
            	link.unbind();
            } catch (e) {
            	Logger.error(e);
            }
        });
        MWAddonToolbar.isWatching = false;
    }
    function onclick(e) {
        closeWatcher();
        callback&&callback({
            name: $(this).text(),
            url: Util.toUrl($(this))
        });
        e.preventDefault();
    }
};
/**
 * Get user defined links.
 */
MWAddonToolbar.GetLinks = function() {
    var excluded =  /xw_controller|xw_action|xw_city/i;
    var menu = new Array();
    var isJS = /^javascript:/i;
    
    function getLinkAction(data) {
        var param = Util.uSplit(data.url);
        var url = 'remote/' + MW.getIntURL(param.xw_controller, param.xw_action);
        Util.each(param, function(name, value) {
            if (!excluded.test(name)) {
                url += '&'+name+'='+value;
            }
        });
        return {'func':MW.goPage,'params':[url, '#inner_page']};
    }
    function getJSAction(data) {
        return {'func':MWAddonToolbar.execJS,'params':[data.js||data.url]};
    }
    
    Util.each(UserConfig.main.userLinks, function(index, data) {
        menu.push({
            'name'  : data.name, 
            'click' : (isJS.test(data.url) ? getJSAction(data) : getLinkAction(data))
        });
    });
    if (UserConfig.main.opt_ChromeBM === true && MWAddonToolbar.bookmarks.length > 0) {
        Util.each(MWAddonToolbar.bookmarks, function(index, data){
            menu.push({
                'cbm'   : true,
                'name'  : data.name, 
                'click' : getJSAction(data)
            });
        });
    }
    
    return menu;
};
/**
 * Initialize and Update the Toolbar.
 */
MWAddonToolbar.Show = function() {
    if (UserConfig.main.get('opt_Toolbar') !== true) {
        return;
    }
    if (e$('#mwa_toolbar') === null) {
        MWAddonToolbar.Init();
    }
    MWAddonToolbar.Update();
};
/**
 * Update user statistics.
 */
MWAddonToolbar.Update = function() {
    if (MWAddonToolbar.initialized !== true) {
        return;
    }
    function fixedInt(n){
        if (n == 'Infinity') { return 0; } else { return n.toFixed(Math.abs(n) < 10 ? 2 : 0); }
    }
    var user = global.User, stats = {
        health: {
            c: user.health(),
            m: user.max_health(),
            p: parseInt(Util.percent(user.health(), user.max_health())),
            o: function() {
                switch(true) {
                    case stats.health.c < 20: return 'red';
                    case stats.health.p < 40: return '#FFD927;';
                    default: return 'white'; 
                }
            }
        },
        energy: {
            c: user.energy(),
            m: user.max_energy(),
            p: parseInt(Util.percent(user.energy(), user.max_energy())),
            r: fixedInt(user.expToNextLevel() / user.energy()),
            o: function() {
                switch(true) {
                    case stats.energy.p > 99: return '#FFD927;';
                    default: return 'white'; 
                }
            }
        },
        stamina: {
            c: user.stamina(),
            m: user.max_stamina(),
            p: parseInt(Util.percent(user.stamina(), user.max_stamina())),
            r: fixedInt(user.expToNextLevel() / user.stamina()),
            o: function() {
                switch(true) {
                    case stats.stamina.p > 99: return '#FFD927;';
                    default: return 'white'; 
                }
            }
        },
        experience: {
            c: user.expToNextLevel(),
            p: 100-parseInt(Util.percent(user.expToNextLevel(), user.expForThisLevel())),
            r: fixedInt(user.expToNextLevel() / (user.energy() + user.stamina())),
            o: function() {
                switch(true) {
                    case stats.experience.p > 80: return '#FFD927;';
                    default: return 'white'; 
                }
            }
        }
    };
    
    $('#mwa_toolbar_hospital').css('display', (stats.health.c<stats.health.m)?'inline':'none' );
    $('#mwa_user_level').text(global.User.level());
    
    Util.each(['health','energy','stamina','experience'], function(i, tag) {
        var t = stats[tag];
        var xpn = ' <span style="font-size:10px">XPN</span>';
        var cur = Util.setColor(t.c,t.o());
        $('#mwa_'+tag+'_bar_info').html((t.m ? cur+' / '+t.m : cur + xpn));
        $('#mwa_'+tag+'_bar div').width((100-Math.min(t.p, 100))+'%');
        $('.'+tag+' .progress_bar').attr('title', Util.formatText(tag) +': '+ t.p+'%');
        if (t.r) {
            $('.'+tag+' .progress_bar .ratio_need').text(t.r);
        }
    });
    
};
/**
 * Initialize Toolbar.
 */
MWAddonToolbar.Init = function() {
    if (global.is_chrome === true) {
        chrome.extension.sendRequest(AppInfo.chmextID, {action:'bookmark'}, function(data) {
            var isJS = /^javascript:/i;
            (function loadChildren(children) {
                try {
	                Util.each(children, function(id, bookmark) {
                        if (bookmark.children) {
                            loadChildren(bookmark.children);
                        } else if (bookmark.url) {
                            var url = Util.trim(bookmark.url);
                            if (isJS.test(url)) {
                                MWAddonToolbar.bookmarks.push({'name':bookmark.title,'js':url});
                            }
                        }
                    });
                } catch (e) { }
            })(data);
        });
    }
    $(Util.render(Base64.decode(
        'PGRpdiBpZD0ibXdhX3Rvb2xiYXIiIHN0eWxlPSJtYXJnaW46IDVweCAycHggMHB4IDJweDsgbWluLWhlaWdodDogNjdweDsiPg0K'+
        'CTxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+DQoJI213YV90b29sYmFyIGRpdi5ib3ggew0KCQlwYWRkaW5nOiAwcHggIWltcG9ydGFu'+
        'dDsNCgl9DQoJI213YV90b29sYmFyIGRpdi5idXR0b24gew0KCQl0ZXh0LWFsaWduOiBjZW50ZXI7DQoJCWN1cnNvcjogcG9pbnRl'+
        'cjsNCgkJYmFja2dyb3VuZC1jb2xvcjogdHJhbnNwYXJlbnQ7DQoJCWNvbG9yOiBncmV5Ow0KCQltYXJnaW46IDBweCAxcHggNXB4'+
        'IDFweDsNCgkJYm9yZGVyLXRvcDogMXB4IHNvbGlkICMyMjI7DQoJCWJvcmRlci1ib3R0b206IDFweCBzb2xpZCAjMjIyOw0KCX0N'+
        'CgkjbXdhX3Rvb2xiYXIgZGl2LmJ1dHRvbjpob3ZlciB7DQoJCWJhY2tncm91bmQtY29sb3I6IGdyZXk7DQoJCWNvbG9yOiBibGFj'+
        'azsNCgl9DQoJI213YV90b29sYmFyICNtd2FfdG9vbGJhcm1lbnUgew0KCQlwb3NpdGlvbjogYWJzb2x1dGU7DQoJCXJpZ2h0OiA1'+
        'cHg7DQoJCWhlaWdodDogMjgwcHg7DQoJCXotaW5kZXg6IDIwOw0KCQlkaXNwbGF5OiBub25lOw0KCX0NCgkjbXdhX3Rvb2xiYXIg'+
        'I213YV90b29sYmFybWVudSBkaXYudG9vbGJhcmFycm93IHsNCgkJcG9zaXRpb246IGFic29sdXRlOw0KCQlyaWdodDogMjVweDsN'+
        'Cgl9DQoJI213YV90b29sYmFyICNtd2FfdG9vbGJhcm1lbnUgZGl2LnRvb2xiYXJib3ggew0KCQlib3JkZXI6IDFweCBzb2xpZCAj'+
        'NDQ0Ow0KCQktbW96LWJvcmRlci1yYWRpdXM6IDE1cHg7DQoJCWJvcmRlci1yYWRpdXM6IDE1cHg7DQoJCXdpZHRoOiAzMDBweDsN'+
        'CgkJaGVpZ2h0OiAyNjBweDsNCgkJcGFkZGluZzogMTBweDsNCgkJZm9udC1zaXplOiAxMnB4Ow0KCQl0ZXh0LWFsaWduOiBsZWZ0'+
        'Ow0KCQliYWNrZ3JvdW5kLWNvbG9yOiAjMTMxMzEzOw0KCQltYXJnaW4tdG9wOiAycHg7DQoJfQ0KCSNtd2FfdG9vbGJhciAjbXdh'+
        'X3Rvb2xiYXJtZW51IGRpdi50b29sYmFyYm94IC5ib3hfdGl0bGUgew0KCQloZWlnaHQ6IDE1cHg7DQoJCXBhZGRpbmc6IDVweCAx'+
        'MHB4Ow0KCQliYWNrZ3JvdW5kOiAjMzMzOw0KCQlib3JkZXItcmFkaXVzOiA1cHg7DQoJCS1tb3otYm9yZGVyLXJhZGl1czogNXB4'+
        'Ow0KCQljb2xvcjogZ3JleTsNCgl9DQoJI213YV90b29sYmFyICNtd2FfdG9vbGJhcm1lbnUgZGl2LnRvb2xiYXJib3ggdWwgew0K'+
        'CQltYXJnaW46IDBweDsNCgkJbGlzdC1zdHlsZTogbm9uZTsNCgkJb3ZlcmZsb3cteTogYXV0bzsNCgkJaGVpZ2h0OiAyMjVweDsN'+
        'CgkJYm9yZGVyOiAxcHggZG90dGVkICMzMzM7DQoJCW1hcmdpbi10b3A6IDVweDsNCgkJcGFkZGluZzogMHB4IDVweCAwcHggNXB4'+
        'Ow0KCQlib3JkZXItcmFkaXVzOiA0cHg7DQoJCS1tb3otYm9yZGVyLXJhZGl1czogNHB4Ow0KCQlwYWRkaW5nLXRvcDogNHB4Ow0K'+
        'CX0NCgkjbXdhX3Rvb2xiYXIgI213YV90b29sYmFybWVudSBkaXYudG9vbGJhcmJveCB1bCBsaSB7DQoJCWNvbG9yOiAjOTk5Ow0K'+
        'CQlib3JkZXI6IDFweCBzb2xpZCB0cmFuc3BhcmVudDsNCgkJd2lkdGg6IGF1dG8gIWltcG9ydGFudDsNCgkJcGFkZGluZy1sZWZ0'+
        'OiAyMXB4ICFpbXBvcnRhbnQ7DQoJCWN1cnNvcjogcG9pbnRlcjsNCgkJYm9yZGVyLXJhZGl1czogM3B4Ow0KCQktbW96LWJvcmRl'+
        'ci1yYWRpdXM6IDNweDsNCgkJYm9yZGVyLWJvdHRvbTogMXB4IGRvdHRlZCAjMjIyOw0KCQlwYWRkaW5nLXRvcDogM3B4Ow0KCQlw'+
        'YWRkaW5nLWJvdHRvbTogMnB4Ow0KCX0NCgkjbXdhX3Rvb2xiYXIgI213YV90b29sYmFybWVudSBkaXYudG9vbGJhcmJveCB1bCBs'+
        'aS5pbXBvcnRhbnQgew0KCQliYWNrZ3JvdW5kLWltYWdlOiB1cmwoJyR7YmFzZV91cmx9Y2F1dGlvblRvcEljb24ucG5nJykgIWlt'+
        'cG9ydGFudDsNCgkJYmFja2dyb3VuZC1wb3NpdGlvbjogMHB4IDUwJSAhaW1wb3J0YW50Ow0KCQliYWNrZ3JvdW5kLXJlcGVhdDog'+
        'bm8tcmVwZWF0ICFpbXBvcnRhbnQ7DQoJfQ0KCSNtd2FfdG9vbGJhciAjbXdhX3Rvb2xiYXJtZW51IGRpdi50b29sYmFyYm94IHVs'+
        'IGxpOmhvdmVyIHsNCgkJYmFja2dyb3VuZC1jb2xvcjogIzI4MjgyODsNCgkJY29sb3I6ICNGRkQ5Mjc7DQoJfQ0KCSNtd2FfdG9v'+
        'bGJhciAjbXdhX3Rvb2xiYXJtZW51IGRpdi50b29sYmFyYm94IHVsIGxpIGRpdi5pdGVtX25hbWUgew0KCQlvdmVyZmxvdzogaGlk'+
        'ZGVuOw0KCX0NCgkjbXdhX3Rvb2xiYXIgI213YV90b29sYmFybWVudSBkaXYudG9vbGJhcmJveCB1bCBsaSBzcGFuLm1vcmVfaW4g'+
        'ew0KCQlmbG9hdDogcmlnaHQ7DQoJCW1hcmdpbi1yaWdodDogNXB4Ow0KCX0NCgkjbXdhX3Rvb2xiYXIgI213YV90b29sYmFybWVu'+
        'dSBkaXYudG9vbGJhcmJveCB1bCBsaSBzcGFuLnJlbW92ZSB7DQoJCWN1cnNvcjogcG9pbnRlcjsNCgkJYmFja2dyb3VuZDogdXJs'+
        'KCcke2Jhc2VfdXJsfXJlcXVlc3RzL2Nyb3NzLnBuZycpIGxlZnQgdG9wIG5vLXJlcGVhdDsNCgkJZmxvYXQ6IHJpZ2h0Ow0KCQl3'+
        'aWR0aDogMThweDsNCgkJaGVpZ2h0OiAxOHB4Ow0KCX0NCgkjbXdhX3Rvb2xiYXIgYS5idXR0b25fbWVudV9vcGVuID4gc3BhbiA+'+
        'IHNwYW4gew0KCQliYWNrZ3JvdW5kOiB1cmwoJyR7YmFzZV91cmx9Y3Jld19tb2R1bGUvcXVldWVfYnRuX2Fycm93X2FjdGl2ZS5w'+
        'bmcnKSA4NSUgNTAlIG5vLXJlcGVhdDsNCgkJdGV4dC1hbGlnbjogbGVmdDsNCgl9DQoJI213YV90b29sYmFyIGEuYnV0dG9uX21l'+
        'bnVfb3BlbiAjbXdhX3Rvb2xiYXJtZW51IHsNCgkJZGlzcGxheTogYmxvY2s7DQoJfQ0KCSNtd2FfdG9vbGJhciBhLmJ1dHRvbl9t'+
        'ZW51X2Nsb3NlZCA+IHNwYW4gPiBzcGFuIHsNCgkJYmFja2dyb3VuZDogdXJsKCcke2Jhc2VfdXJsfWNyZXdfbW9kdWxlL3F1ZXVl'+
        'X2J0bl9hcnJvd19pbmFjdGl2ZS5wbmcnKSA4NSUgNTAlIG5vLXJlcGVhdDsNCgkJdGV4dC1hbGlnbjogbGVmdDsNCgl9DQoJI213'+
        'YV90b29sYmFyIGEuYnV0dG9uX21lbnVfY2xvc2VkICNtd2FfdG9vbGJhcm1lbnUgew0KCQlkaXNwbGF5OiBub25lOw0KCX0NCgkj'+
        'bXdhX3Rvb2xiYXIgLmJ1dHRvbl9iYXIsDQoJI213YV90b29sYmFyIC5idXR0b25fYmFyX2Nhc2ggew0KCQliYWNrZ3JvdW5kLWNv'+
        'bG9yOiB0cmFuc3BhcmVudDsNCgkJY3Vyc29yOiBwb2ludGVyOw0KCX0NCgkjbXdhX3Rvb2xiYXIgLmJ1dHRvbl9iYXI6aG92ZXIs'+
        'DQoJI213YV90b29sYmFyIC5idXR0b25fYmFyX2Nhc2g6aG92ZXIJew0KCQliYWNrZ3JvdW5kLWNvbG9yOiAjMjIyOw0KCX0NCgkj'+
        'bXdhX3Rvb2xiYXIgLmJ1dHRvbl9iYXJfY2FzaCB7DQoJCWZvbnQtc2l6ZTogMTJweDsNCgkJbWFyZ2luOiAwcHggNXB4IDBweCA1'+
        'cHg7DQoJCW1pbi1oZWlnaHQ6IDIwcHg7DQoJfQ0KCSNtd2FfdG9vbGJhciBkaXYucHJvZ3Jlc3NfYmFyIHsNCgkJb3ZlcmZsb3c6'+
        'IGhpZGRlbjsNCgkJbGluZS1oZWlnaHQ6IDE0cHg7DQoJCW1pbi1oZWlnaHQ6IDE2cHg7DQoJfQ0KCSNtd2FfdG9vbGJhciBkaXYu'+
        'cHJvZ3Jlc3NfYmFyIGRpdi5zdGF0X2luZm8gPiBzcGFuOmZpcnN0LWNoaWxkIHsNCgkJZmxvYXQ6IGxlZnQ7DQoJCW1hcmdpbi1s'+
        'ZWZ0OiA1cHg7DQoJfQ0KCSNtd2FfdG9vbGJhciBkaXYucHJvZ3Jlc3NfYmFyIGRpdi5zdGF0X2luZm8gew0KCQlwYWRkaW5nLXJp'+
        'Z2h0OiA1cHg7DQoJCWNvbG9yOiAjRTJFMkUyOw0KCQl0ZXh0LWFsaWduOiByaWdodDsNCgkJZm9udC1zaXplOiAxNHB4Ow0KCQls'+
        'aW5lLWhlaWdodDogMTRweDsNCgkJbWluLWhlaWdodDogMTRweDsNCgl9DQoJI213YV90b29sYmFyIGRpdi5wcm9ncmVzc19iYXIg'+
        'c3Bhbi5jb3VudGRvd24gew0KCQlmb250LXNpemU6IDEycHg7DQoJCWxpbmUtaGVpZ2h0OiAxMnB4Ow0KCQltYXJnaW4tbGVmdDog'+
        'NXB4Ow0KCQljb2xvcjogIzg5ODk4OTsNCgl9DQoJI213YV90b29sYmFyIGRpdi5wcm9ncmVzc19iYXIgLm1hc3RlcnlfYmFyIHsN'+
        'CgkJbWFyZ2luOiAwcHg7DQoJCWhlaWdodDogNXB4Ow0KCQlib3JkZXI6IDFweCBzb2xpZCAjMzMzOw0KCQljb2xvcjogYmxhY2s7'+
        'DQoJCWZvbnQtc2l6ZTogMTJweDsNCgkJcG9zaXRpb246IHJlbGF0aXZlOw0KCQl0ZXh0LWFsaWduOiByaWdodDsNCgkJLXdlYmtp'+
        'dC1ib3JkZXItcmFkaXVzOiAycHg7DQoJCWJvcmRlci1yYWRpdXM6IDJweDsNCgkJLW1vei1ib3JkZXItcmFkaXVzOiAycHg7DQoJ'+
        'CW9wYWNpdHk6IDAuNjsNCgl9DQoJI213YV90b29sYmFyIGRpdi5wcm9ncmVzc19iYXIgLm1hc3RlcnlfYmFyID4gZGl2IHsNCgkJ'+
        'YmFja2dyb3VuZDogdXJsKCIke2Jhc2VfdXJsfW1hcF9iYXNlZF9qb2JzL21hc3RlcnlfYmFyX2JnLmdpZiIpIDAgMCByZXBlYXQt'+
        'eDsNCgkJaGVpZ2h0OiA1cHg7DQoJCW92ZXJmbG93OiBoaWRkZW47DQoJCXBvc2l0aW9uOiBhYnNvbHV0ZTsNCgkJdG9wOiAwOw0K'+
        'CQlyaWdodDogMDsNCgl9DQoJI213YV90b29sYmFyIGRpdi5wcm9ncmVzc19iYXIgLnJhdGlvX25lZWQgew0KCQliYWNrZ3JvdW5k'+
        'OiB1cmwoJyR7YmFzZV91cmx9Y2xhbi9ncm91cF94cC5wbmcnKSAwJSA1MCUgbm8tcmVwZWF0Ow0KCQlmb250LXNpemU6IDExcHg7'+
        'DQoJCXBvc2l0aW9uOiBhYnNvbHV0ZTsNCgkJbWFyZ2luLWxlZnQ6IDEyMHB4Ow0KCQliYWNrZ3JvdW5kLXNpemU6IDE0cHg7DQoJ'+
        'CS1tb3otYmFja2dyb3VuZC1zaXplOiAxNHB4Ow0KCQlsaW5lLWhlaWdodDogMTNweDsNCgkJcGFkZGluZy1sZWZ0OiAxOHB4Ow0K'+
        'CX0NCgk8L3N0eWxlPg0KICAgIDx0YWJsZSBzdHlsZT0iYm9yZGVyLXNwYWNpbmc6IDBweDsgd2lkdGg6IDEwMCU7Ij4NCiAgICAg'+
        'ICAgPHRib2R5Pg0KICAgICAgICAgICAgPHRyPg0KICAgICAgICAgICAgICAgIDx0ZD4NCiAgICA8ZGl2IGNsYXNzPSJib3giIHN0'+
        'eWxlPSJ3aWR0aDoxNTBweDsiPg0KCTxkaXYgY2xhc3M9ImJveF90b3AiPjxkaXYgY2xhc3M9ImJveF90b3BfcmlnaHQiPjwvZGl2'+
        'PjwvZGl2Pg0KCTxkaXYgY2xhc3M9ImJveF9taWRkbGUiPjxkaXYgY2xhc3M9ImJveF9taWRkbGVfcmlnaHQiIHN0eWxlPSJoZWln'+
        'aHQ6IDU0cHg7Ij4NCgkJPGRpdiBpZD0ibXdhX3Rvb2xiYXJDYXNoIiBjbGFzcz0iYnV0dG9uX2Jhcl9jYXNoIj48L2Rpdj4NCgkJ'+
        'PGNlbnRlciBzdHlsZT0icGFkZGluZzogNXB4OyI+DQoJCTxzcGFuIGlkPSJtd2FfdG9vbGJhcl9iYW5rIiBjbGFzcz0iYmFua19k'+
        'ZXBvc2l0Ij4oPGEgaHJlZj0iIyIgb25jbGljaz0icmV0dXJuIGRvX2FqYXgoJ3BvcHVwX2ZvZGRlcicsICdyZW1vdGUvaHRtbF9z'+
        'ZXJ2ZXIucGhwP3h3X2NvbnRyb2xsZXI9YmFuayZhbXA7eHdfYWN0aW9uPXZpZXcnLCAxLCAxLCAwLCAwKTsiIGNsYXNzPSJiYW5r'+
        'X2RlcG9zaXQiPkJhbms8L2E+KTwvc3Bhbj4NCgkJPHNwYW4gaWQ9Im13YV90b29sYmFyX2hvc3BpdGFsIiBjbGFzcz0iYmFua19k'+
        'ZXBvc2l0Ij4oPGEgaHJlZj0iIyIgb25jbGljaz0icmV0dXJuIGRvX2FqYXgoJ3BvcHVwX2ZvZGRlcicsICdyZW1vdGUvaHRtbF9z'+
        'ZXJ2ZXIucGhwP3h3X2NvbnRyb2xsZXI9aG9zcGl0YWwmYW1wO3h3X2FjdGlvbj12aWV3JywgMSwgMSwgMCwgMCk7IiBjbGFzcz0i'+
        'YmFua19kZXBvc2l0Ij5Ib3NwaXRhbDwvYT4pPC9zcGFuPg0KCQk8L2NlbnRlcj4NCgk8L2Rpdj48L2Rpdj4NCgk8ZGl2IGNsYXNz'+
        'PSJib3hfYm90dG9tIj48ZGl2IGNsYXNzPSJib3hfYm90dG9tX3JpZ2h0Ij48L2Rpdj48L2Rpdj4NCgk8L2Rpdj4NCiAgICAgICAg'+
        'ICAgICAgICA8L3RkPg0KICAgICAgICAgICAgICAgIDx0ZD4NCiAgICA8ZGl2IGNsYXNzPSJib3giIHN0eWxlPSJ3aWR0aDo1MTBw'+
        'eDsiPg0KCTxkaXYgY2xhc3M9ImJveF90b3AiPjxkaXYgY2xhc3M9ImJveF90b3BfcmlnaHQiPjwvZGl2PjwvZGl2Pg0KCTxkaXYg'+
        'Y2xhc3M9ImJveF9taWRkbGUiPjxkaXYgY2xhc3M9ImJveF9taWRkbGVfcmlnaHQiIHN0eWxlPSJoZWlnaHQ6IDU0cHg7Ij4NCgk8'+
        'dGFibGUgaWQ9Im1pbmlfbWFzdGVyeSIgc3R5bGU9Im1hcmdpbjowcHggYXV0bzsiPg0KCQk8dGJvZHk+DQoJCTx0cj4NCgkJCTx0'+
        'ZCBpZD0ibXdhX3Rvb2xiYXJIZWFsdGgiIGNsYXNzPSJidXR0b25fYmFyIiB0aXRsZT0iSGVhbHRoIj4NCgkJCQk8ZGl2IGNsYXNz'+
        'PSJoZWFsdGgiIHN0eWxlPSJ3aWR0aDogMjAwcHg7Ij4NCgkJCQkJPGRpdiBjbGFzcz0icHJvZ3Jlc3NfYmFyIj4NCgkJCQkJCTxk'+
        'aXYgY2xhc3M9InN0YXRfaW5mbyI+PHNwYW4gaWQ9Im13YV9oZWFsdGhfYmFyX2luZm8iPjAvMDwvc3Bhbj48c3BhbiBpZD0iY291'+
        'bnRkb3duU3BhbkhlYWx0aCIgY2xhc3M9ImNvdW50ZG93biI+MDowMDwvc3Bhbj48L2Rpdj4NCgkJCQkJCTxkaXYgaWQ9Im13YV9o'+
        'ZWFsdGhfYmFyIiBjbGFzcz0ibWFzdGVyeV9iYXIgenlfcHJvZ3Jlc3NfYmFyX2dyZWVuIj48ZGl2IHN0eWxlPSJ3aWR0aDogMCU7'+
        'Ij48L2Rpdj48L2Rpdj4NCgkJCQkJPC9kaXY+DQoJCQkJPC9kaXY+DQoJCQk8L3RkPg0KCQkJPHRkIHRpdGxlPSJFbmVyZ3kiPg0K'+
        'CQkJCTxkaXYgY2xhc3M9ImVuZXJneSIgc3R5bGU9IndpZHRoOiAyMDBweDsiPg0KCQkJCQk8ZGl2IGNsYXNzPSJwcm9ncmVzc19i'+
        'YXIiPg0KCQkJCQkJPGRpdiBjbGFzcz0ic3RhdF9pbmZvIj48c3BhbiBpZD0ibXdhX2VuZXJneV9iYXJfaW5mbyI+MC8wPC9zcGFu'+
        'PjxkaXYgY2xhc3M9InJhdGlvX25lZWQiIHRpdGxlPSJFbmVyZ3kgZXhwZXJpZW5jZSByYXRpbyB0byBsZXZlbCB1cC4iPjwvZGl2'+
        'Pg0KCQkJCQkJPHNwYW4gaWQ9ImNvdW50ZG93blNwYW5FbmVyZ3kiIGNsYXNzPSJjb3VudGRvd24iPjA6MDA8L3NwYW4+PC9kaXY+'+
        'DQoJCQkJCQk8ZGl2IGlkPSJtd2FfZW5lcmd5X2JhciIgY2xhc3M9Im1hc3RlcnlfYmFyIHp5X3Byb2dyZXNzX2Jhcl9ncmVlbiI+'+
        'PGRpdiBzdHlsZT0id2lkdGg6IDAlOyI+PC9kaXY+PC9kaXY+DQoJCQkJCTwvZGl2Pg0KCQkJCTwvZGl2Pg0KCQkJPC90ZD4NCgkJ'+
        'PC90cj48dHI+DQoJCQk8dGQgaWQ9Im13YV90b29sYmFyRXhwZXJpZW5jZSIgY2xhc3M9ImJ1dHRvbl9iYXIiIHRpdGxlPSJFeHBl'+
        'cmllbmNlIj4NCgkJCQk8ZGl2IGNsYXNzPSJleHBlcmllbmNlIiBzdHlsZT0id2lkdGg6IDIwMHB4OyI+DQoJCQkJCTxkaXYgY2xh'+
        'c3M9InByb2dyZXNzX2JhciI+DQoJCQkJCQk8ZGl2IGNsYXNzPSJzdGF0X2luZm8iPjxzcGFuIGlkPSJtd2FfZXhwZXJpZW5jZV9i'+
        'YXJfaW5mbyI+PC9zcGFuPg0KCQkJCQkJPGRpdiBjbGFzcz0icmF0aW9fbmVlZCIgc3R5bGU9Im1hcmdpbi1sZWZ0OiAxMDBweDsi'+
        'IHRpdGxlPSJHbG9iYWwgZXhwZXJpZW5jZSByYXRpbyB0byBsZXZlbCB1cC4iPjwvZGl2Pg0KCQkJCQkJCTxzcGFuIGNsYXNzPSJj'+
        'b3VudGRvd24iPjxzcGFuIHN0eWxlPSJmb250LXNpemU6IDhweDsgbGluZS1oZWlnaHQ6IDhweDsiPkxWLjwvc3Bhbj48c3BhbiBp'+
        'ZD0ibXdhX3VzZXJfbGV2ZWwiPjA8L3NwYW4+PC9zcGFuPg0KCQkJCQkJPC9kaXY+DQoJCQkJCQk8ZGl2IGlkPSJtd2FfZXhwZXJp'+
        'ZW5jZV9iYXIiIGNsYXNzPSJtYXN0ZXJ5X2JhciB6eV9wcm9ncmVzc19iYXJfZ3JlZW4iPjxkaXYgc3R5bGU9IndpZHRoOiAwJTsi'+
        'PjwvZGl2PjwvZGl2Pg0KCQkJCQk8L2Rpdj4NCgkJCQk8L2Rpdj4NCgkJCTwvdGQ+DQoJCQk8dGQgdGl0bGU9IlN0YW1pbmEiPg0K'+
        'CQkJCTxkaXYgY2xhc3M9InN0YW1pbmEiIHN0eWxlPSJ3aWR0aDogMjAwcHg7Ij4NCgkJCQkJPGRpdiBjbGFzcz0icHJvZ3Jlc3Nf'+
        'YmFyIj4NCgkJCQkJCTxkaXYgY2xhc3M9InN0YXRfaW5mbyI+PHNwYW4gaWQ9Im13YV9zdGFtaW5hX2Jhcl9pbmZvIj4wLzA8L3Nw'+
        'YW4+PGRpdiBjbGFzcz0icmF0aW9fbmVlZCIgdGl0bGU9IlN0YW1pbmEgZXhwZXJpZW5jZSByYXRpbyB0byBsZXZlbCB1cC4iPg0K'+
        'CQkJCQkJPC9kaXY+PHNwYW4gaWQ9ImNvdW50ZG93blNwYW5TdGFtaW5hIiBjbGFzcz0iY291bnRkb3duIj4wOjAwPC9zcGFuPjwv'+
        'ZGl2Pg0KCQkJCQkJPGRpdiBpZD0ibXdhX3N0YW1pbmFfYmFyIiBjbGFzcz0ibWFzdGVyeV9iYXIgenlfcHJvZ3Jlc3NfYmFyX2dy'+
        'ZWVuIj48ZGl2IHN0eWxlPSJ3aWR0aDogMCU7Ij48L2Rpdj48L2Rpdj4NCgkJCQkJPC9kaXY+DQoJCQkJPC9kaXY+DQoJCQk8L3Rk'+
        'Pg0KCQk8L3RyPg0KCQk8L3Rib2R5Pg0KCTwvdGFibGU+DQoJPC9kaXY+PC9kaXY+DQoJPGRpdiBjbGFzcz0iYm94X2JvdHRvbSI+'+
        'PGRpdiBjbGFzcz0iYm94X2JvdHRvbV9yaWdodCI+PC9kaXY+PC9kaXY+DQoJPC9kaXY+DQogICAgICAgICAgICAgICAgPC90ZD4N'+
        'CiAgICAgICAgICAgICAgICA8dGQ+DQoJPGRpdiBjbGFzcz0iYm94IiBzdHlsZT0id2lkdGg6IDkwcHg7Ij4NCgkJPGRpdiBjbGFz'+
        'cz0iYm94X3RvcCI+PGRpdiBjbGFzcz0iYm94X3RvcF9yaWdodCI+PC9kaXY+PC9kaXY+DQoJCTxkaXYgY2xhc3M9ImJveF9taWRk'+
        'bGUiPg0KCQk8ZGl2IGNsYXNzPSJib3hfbWlkZGxlX3JpZ2h0IiBzdHlsZT0iaGVpZ2h0OiA1NHB4O3BhZGRpbmctbGVmdDogM3B4'+
        'OyI+DQoJCQk8YSBpZD0ibXdhX3RsYkxpbmtzIiBjbGFzcz0ic2V4eV9idXR0b25fbmV3IGJ1dHRvbl9tZW51X2Nsb3NlZCI+PHNw'+
        'YW4+PHNwYW4+PHNwYW4gY2xhc3M9Im13YV9yZXNfbGlua19tZW51X2ljb24iIHN0eWxlPSJwYWRkaW5nLWxlZnQ6IDIwcHg7IG1h'+
        'cmdpbi1sZWZ0OiAtNXB4OyI+PC9zcGFuPjwvc3Bhbj48L3NwYW4+PC9hPg0KCQkJPGEgaWQ9Im13YV90bGJSZW1pbmRlcnMiIGNs'+
        'YXNzPSJzZXh5X2J1dHRvbl9uZXcgYnV0dG9uX21lbnVfY2xvc2VkIj48c3Bhbj48c3Bhbj48c3BhbiBjbGFzcz0ibXdhX3Jlc19y'+
        'ZW1pbmRlcl9tZW51X2ljb24iIHN0eWxlPSJwYWRkaW5nLWxlZnQ6IDIwcHg7IG1hcmdpbi1sZWZ0OiAtNXB4OyI+PC9zcGFuPjwv'+
        'c3Bhbj48L3NwYW4+PC9hPg0KCQkJPGEgaWQ9Im13YV90bGJQbHVnaW5zIiBzdHlsZT0id2lkdGg6IDgwcHg7IG1hcmdpbi10b3A6'+
        'IDJweDsiIGNsYXNzPSJzZXh5X2J1dHRvbl9uZXcgYnV0dG9uX21lbnVfY2xvc2VkIj48c3Bhbj48c3Bhbj5QbHVnaW5zPC9zcGFu'+
        'Pjwvc3Bhbj48L2E+DQoJCQk8ZGl2IGlkPSJtd2FfdG9vbGJhcm1lbnUiPg0KCQkJPGRpdiBjbGFzcz0idG9vbGJhcmFycm93Ij48'+
        'aW1nIHNyYz0iJHtiYXNlX3VybH1jcmV3X21vZHVsZS9jcmV3X2hvdmVyX2Fycm93LnBuZyI+PC9kaXY+DQoJCQk8ZGl2IGNsYXNz'+
        'PSJ0b29sYmFyYm94Ij4NCgkJCTxkaXYgY2xhc3M9ImJveF90aXRsZSI+DQoJCQk8ZGl2IHN0eWxlPSJmbG9hdDpsZWZ0OyI+PC9k'+
        'aXY+DQoJCQk8ZGl2IHN0eWxlPSJmbG9hdDpyaWdodDsiPjwvZGl2Pg0KCQkJPGRpdiBzdHlsZT0iY2xlYXI6IGJvdGgiPjwvZGl2'+
        'Pg0KCQkJPC9kaXY+DQoJCQk8dWw+PC91bD4NCgkJCTwvZGl2PjwvZGl2Pg0KCQk8L2Rpdj48L2Rpdj4NCgkJPGRpdiBjbGFzcz0i'+
        'Ym94X2JvdHRvbSI+PGRpdiBjbGFzcz0iYm94X2JvdHRvbV9yaWdodCI+PC9kaXY+PC9kaXY+DQoJPC9kaXY+DQogICAgICAgICAg'+
        'ICAgICAgPC90ZD4NCiAgICAgICAgICAgIDwvdHI+DQogICAgICAgIDwvdGJvZHk+DQogICAgPC90YWJsZT4NCjwvZGl2Pg=='), 
        {base_url: global.zGraphicsURL}
    )).prependTo($('#stats_row').css('margin',0));
    
    $('#game_stats, #user_stats').hide();
    
    var $cash = $('#mwa_toolbarCash').click(MWAddonToolbar.Event.deposit);
    $('#stat_cash_cont > div').each(function(index, elem) {
        var $str = $('.stat_numbers strong', elem);
        var new_elem = c$('div','class:cash,id:'+elem.id).appendTo($cash).css({
            'width': 120,
            'display': elem.style.display
        });
        c$('strong','class:cur_cash,id:'+$str.attr('id')).html($str.html()).appendTo(new_elem);
    });
    var $mp = $('#mwa_toolbarmenu'), $mr = ($ml = $mp.clone()).clone();
    $('.box_title > div:first', $ml)
    .append(c$('a').text('ADD NEW').css('opacity',0.8).click(MWAddonToolbar.Event.new_link))
    .append(c$('span').css('margin','0px 5px 0px 5px').html('|'))
    .append(c$('a').text('LINK WATCHER').css('opacity',0.8).click(MWAddonToolbar.Event.watcher_link));;
    $('.box_title > div:first', $mp).append(c$('a').text('EDIT').css('opacity',0.8).click(MWAddonToolbar.Event.edit_plugin));
    $('.box_title > div:first', $mr).append(c$('a').text('EDIT').css('opacity',0.8).click(MWAddonToolbar.Event.edit_reminder));
    $('.box_title > div:eq(1)', $ml).text('BOOKMARKS');
    $('.box_title > div:eq(1)', $mp).text('PLUGINS');
    $('.box_title > div:eq(1)', $mr).text('REMINDERS');
    $('.toolbararrow', $mr).css('right', 6);
    $('.toolbararrow', $ml).css('right', 48);
    $('#mwa_tlbLinks').append($ml).click(MWAddonToolbar.Event.showMenu).mouseleave(MWAddonToolbar.Event.hideMenu);
    $('#mwa_tlbReminders').append($mr).click(MWAddonToolbar.Event.showMenu).mouseleave(MWAddonToolbar.Event.hideMenu);
    $('#mwa_tlbPlugins').append($mp).click(MWAddonToolbar.Event.showMenu).mouseleave(MWAddonToolbar.Event.hideMenu);
    $('#mwa_toolbarExperience').click(MWAddonToolbar.Event.showRatios);
    $('#mwa_toolbar_slider').mouseenter(MWAddonToolbar.Update);
    $('#mwa_toolbarHealth').click(MWAddonToolbar.Event.heal);
    MWAddonToolbar.initialized = true;
};// ==Script==
// @id        Menu.js
// @author    Dakam
// @memberOf  MWAddon.js
// ==Script==
/**
 * @namespace
 */
var MainMenu = {
    root: [{
        name: 'Configuration',
        click: Configuration,
        resIcon: 'config_menu_icon',
        css: {'border-bottom':'1px solid white'}
    }, {
        name: 'Free Gifts Center',
        click: FreeGiftsCenter,
        resIcon: 'freegifts_menu_icon'
    }, {
        name: 'Home Feed Center',
        click: HomeFeedCenter,
        icon: global.zGraphicsURL + 'v3/icon_mafia_hat_32x25_01.png'
    }, {
        name: 'Collect All Cities',
        click: CollectAllCities,
        icon: global.zGraphicsURL + 'v3/icon_vault_23x27_01.png'
    }, {
        name: 'Battlefield v2!',
        click: Battlefieldv2,
        //menu: function(){ return [{name:'Statistics',click:BattlefieldStats}]; },
        resIcon: 'stamina_menu_icon'
    }, {
        name: 'Plug-In Manager',
        click: PluginManager,
        menu: Plugins.getMenu,
        resIcon: 'plugin_menu_icon'
    }, {
        name: 'Reminder Editor',
        click: ReminderEditor,
        menu: Reminders.getMenu,
        resIcon: 'reminder_menu_icon'
    },/* {
        name: 'Rob Master',
        click: RobMaster,
        resIcon: 'stamina_menu_icon'
    },*/ {
        name: 'Bounty Hunter',
        click: BountyHunter,
        resIcon: 'stamina_menu_icon'
    }, {
        name: 'Craft Manager',
        click: CraftManager,
        resIcon: 'weapon_menu_icon'
    }, {
        name: 'Operations Center',
        click: OperationsCenter,
        icon: global.zGraphicsURL + 'map_based_jobs/expert_view/icon_megaphone.png'
    }, {
        name: 'Multi Gifter',
        click: MultiGifter,
        icon: global.zGraphicsURL + 'v3/icon_gift_27x28_01.png'
    }, {
        name: 'Inventory Analyzer',
        click: InventoryAnalyzer,
        resIcon: 'inventory_menu_icon'
    }, {
        name: 'Mafia Wiper',
        click: MafiaWiper,
        resIcon: 'list_menu_icon'
    }, {
       name: 'Fight Club Buyer',
        click: FightClubBuyer,
        resIcon: 'victory_coin_icon'
    }, {
        name: 'My Links',
        click: UserLinks,
        resIcon: 'link_menu_icon'
    }, {
        name: 'Check Updates',
        click: {'func':Updater.check, 'params':true},
        resIcon: 'update_menu_icon'
    }],
    
    /**
     * Events
     */
    Events: {
        click: function(e) {
            if (e.data.url) {
                unsafeWindow.open(e.data.url,'_blank');
            } else {
                var func = e.data.func;
                var p = Util.isArray(e.data.params) ? e.data.params : [e.data.params];
                if (Util.isFunc(func)) {
                    p = func.apply( this, p );
                }
            }
            e.stopImmediatePropagation();
            return false;
        },
        enter: function(e) {
            var parent, func = e.data.func;
            var p = Util.isArray(e.data.params) ? e.data.params : [e.data.params];
            if (Util.isFunc(func)) {
                var subMenu = func.apply(this, p);
                if (Util.length(subMenu) < 1) {
                    return false;
                }
                if ((parent = e$('.submenu', this)) === null) {
                    parent = c$('div','class:submenu').appendTo(this);
                }
                parent.empty().css({
                    'left': $(this).outerWidth(),
                    'top': $(this).position().top
                });
                MainMenu.create( subMenu, c$('ul').appendTo(parent) );
                parent.show();
            }
            return false;
        },
        leave: function() {
            $('.submenu', this).hide();
        },
        show: function() {
            var b;
            while ((b = $('#fbmw_menu #like_button span')).length > 1) {
                b.last().remove();
            }
            $('#fbmw_menu').stop().animate({'left': '0px'}, 'normal'); 
        },
        hide: function() {
            $('.submenu', '#fbmw_menu').hide();
            $('#fbmw_menu').stop().animate({'left': '-182px'}, 'normal');
        }
    },
    /**
     * Initialize the Menu.
     */
    init: function() {
        if (e$('#fbmw_menu_container') !== null) {
            return;
        }
        $(Base64.decode(
        'PGRpdiBpZD0iZmJtd19tZW51X2NvbnRhaW5lciIgc3R5bGU9ImRpc3BsYXk6bm9uZTsiPjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+'+
        'DQojZmJtd19tZW51X2Fycm93IHsNCglwb3NpdGlvbjogYWJzb2x1dGU7DQoJei1pbmRleDogMjU7DQoJdG9wOiAxMHB4Ow0KCWxl'+
        'ZnQ6IDJweDsNCgl3aWR0aDogNDBweDsNCgloZWlnaHQ6IDQwcHg7DQp9DQojZmJtd19tZW51IHsNCglwb3NpdGlvbjogYWJzb2x1'+
        'dGU7DQoJei1pbmRleDogMzA7DQoJdG9wOiAxMHB4Ow0KCWxlZnQ6IC0xNzhweDsNCgl3aWR0aDogMTgwcHg7DQoJbWluLXdpZHRo'+
        'OiAxODBweDsNCgltYXgtd2lkdGg6IDI0MHB4Ow0KCWJvcmRlcjogMnB4IHJpZGdlICNGRkQ5Mjc7DQoJLXdlYmtpdC1ib3gtc2hh'+
        'ZG93OiA1cHggNXB4IDJweCByZ2JhKDAsIDAsIDAsIDAuNSk7DQoJLW1vei1ib3gtc2hhZG93OiA1cHggNXB4IDJweCByZ2JhKDAs'+
        'IDAsIDAsIDAuNSk7DQoJYm94LXNoYWRvdzogNXB4IDVweCAycHggcmdiYSgwLCAwLCAwLCAwLjUpOw0KCWZvbnQtc2l6ZTogMTJw'+
        'eDsNCglmb250LXdlaWdodDogYm9sZDsNCgl0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KI2ZibXdfbWVudSB1bCB7DQogICAgbGlzdC1z'+
        'dHlsZS10eXBlOiBub25lOw0KICAgIG1hcmdpbjogMHB4Ow0KICAgIHBhZGRpbmc6IDBweDsNCiAgICBiYWNrZ3JvdW5kLWNvbG9y'+
        'OiAjMDAwMDE4Ow0KfQ0KI2ZibXdfbWVudSA+IHVsIHsNCiAgICBtaW4taGVpZ2h0OiAyMDBweDsNCn0NCiNmYm13X21lbnUgZGl2'+
        'LnN1Ym1lbnUgew0KCWRpc3BsYXk6IG5vbmU7DQogICAgei1pbmRleDogMzE7DQogICAgcG9zaXRpb246IGFic29sdXRlOw0KfQ0K'+
        'I2ZibXdfbWVudSBkaXYuc3VibWVudSB1bCB7DQoJd2lkdGg6IDIyMHB4Ow0KCW1pbi13aWR0aDogMTgwcHg7DQoJYm9yZGVyOiAx'+
        'cHggc29saWQgeWVsbG93Ow0KCS13ZWJraXQtYm94LXNoYWRvdzogNXB4IDVweCAycHggcmdiYSgwLCAwLCAwLCAwLjUpOw0KCS1t'+
        'b3otYm94LXNoYWRvdzogNXB4IDVweCAycHggcmdiYSgwLCAwLCAwLCAwLjUpOw0KCWJveC1zaGFkb3c6IDVweCA1cHggMnB4IHJn'+
        'YmEoMCwgMCwgMCwgMC41KTsNCglmb250LXNpemU6IDEycHg7DQoJZm9udC13ZWlnaHQ6IGJvbGQ7DQoJdGV4dC1hbGlnbjogbGVm'+
        'dDsNCiAgICBtYXgtaGVpZ2h0OiA1MDBweDsNCiAgICBvdmVyZmxvdy15OiBhdXRvOw0KfQ0KI2ZibXdfbWVudSAucmlnaHRfYXJy'+
        'b3cgew0KICAgIGJhY2tncm91bmQ6ICMwMDAwMTggdXJsKGh0dHA6Ly9td2ZiLnN0YXRpYy56eW5nYS5jb20vbXdmYi9ncmFwaGlj'+
        'cy9tYXJrZXRwbGFjZV9zbWFsbGVyX2Fycm93X3JpZ2h0LmdpZikgbm8tcmVwZWF0IDk2JSA1MCU7DQp9DQojZmJtd19tZW51IHVs'+
        'IGxpIHsNCgliYWNrZ3JvdW5kLWNvbG9yOiAjMDAwMDE4Ow0KCWNvbG9yOiB3aGl0ZTsNCglkaXNwbGF5OiBibG9jazsNCglvdmVy'+
        'ZmxvdzogaGlkZGVuOw0KCXBhZGRpbmc6IDVweCAxMHB4IDVweCAxMHB4Ow0KCWhlaWdodDogMTZweDsNCgljdXJzb3I6IHBvaW50'+
        'ZXI7DQp9DQojZmJtd19tZW51IHVsIGxpIHNwYW57DQogICAgbGluZS1oZWlnaHQ6IDE4cHg7DQoJYmFja2dyb3VuZC1zaXplOiAx'+
        'NnB4ICFpbXBvcnRhbnQ7DQogICAgLW1vei1iYWNrZ3JvdW5kLXNpemU6IDE2cHggIWltcG9ydGFudDsNCn0NCiNmYm13X21lbnUg'+
        'dWwgbGkuaW1wb3J0YW50IHNwYW57DQoJYmFja2dyb3VuZC1pbWFnZTogdXJsKCdodHRwOi8vbXdmYi5zdGF0aWMuenluZ2EuY29t'+
        'L213ZmIvZ3JhcGhpY3MvY2F1dGlvblRvcEljb24ucG5nJykgIWltcG9ydGFudDsNCgliYWNrZ3JvdW5kLXBvc2l0aW9uOiAwcHgg'+
        'NTAlICFpbXBvcnRhbnQ7DQoJYmFja2dyb3VuZC1yZXBlYXQ6IG5vLXJlcGVhdCAhaW1wb3J0YW50Ow0KfQ0KI2ZibXdfbWVudSB1'+
        'bCBsaS5tYWluIHsNCglib3JkZXItYm90dG9tOiAxcHggc29saWQgd2hpdGU7DQp9DQojZmJtd19tZW51IHVsIGxpOmhvdmVyIHsN'+
        'CgliYWNrZ3JvdW5kLWNvbG9yOiAjMzIzMjMyOw0KCWNvbG9yOiAjRkZEOTI3Ow0KfQ0KI2ZibXdfbWVudSAjbGlrZV9idXR0b24g'+
        'ew0KICAgIGJvcmRlci10b3A6IDFweCBzb2xpZCB3aGl0ZTsNCiAgICBwYWRkaW5nOiA0cHggMHB4IDRweCAxNnB4Ow0KICAgIGJh'+
        'Y2tncm91bmQtY29sb3I6ICMzMzM7DQp9DQo8L3N0eWxlPjxkaXYgaWQ9ImZibXdfbWVudSIgc3R5bGU9ImxlZnQ6IDBweDsgIj48'+
        'dWw+PC91bD4NCjxkaXYgaWQ9Imxpa2VfYnV0dG9uIiBjbGFzcz0iYnV0dG9uX2FjdGlvbiI+DQogICAgPGZiOmxpa2UgaHJlZj0i'+
        'aHR0cDovL3d3dy5mYWNlYm9vay5jb20vTWFmaWFXYXJzQWRkb24iIHNlbmQ9InRydWUiIGxheW91dD0iYnV0dG9uX2NvdW50IiB3'+
        'aWR0aD0iMTUwIiBzaG93X2ZhY2VzPSJmYWxzZSIgY29sb3JzY2hlbWU9ImRhcmsiIGNsYXNzPSIgZmJfZWRnZV93aWRnZXRfd2l0'+
        'aF9jb21tZW50IGZiX2lmcmFtZV93aWRnZXQiPjwvZmI6bGlrZT4NCjwvZGl2Pg0KPC9kaXY+PGRpdiBpZD0iZmJtd19tZW51X2Fy'+
        'cm93IiBjbGFzcz0ibXdhX3Jlc19tZW51X2Fycm93Ij48L2Rpdj48L2Rpdj4='
        )).prependTo('#final_wrapper').hide();
        
        var $menu = $('#fbmw_menu').css('left','-182px');        
        // add green arrow
        //if ($('#appwrapper').outerWidth() < 800) {
            
            $menu.mouseleave(MainMenu.Events.hide);
            $('#fbmw_menu_arrow').mouseenter(MainMenu.Events.show);
            /*
        } else {
            $('#fbmw_menu_arrow').remove();
            $menu.css('left', '-192px');
        }
        */
        // add root menu
        MainMenu.create(MainMenu.root, $('ul', $menu));
        $('#fbmw_menu_container').show();
        MWFB.XFBML.parse();
    },
    /**
     * Add a new menu item.
     * <pre>
     * foo({
     *    'name': 'the name for this menu',
     *    'click': function to execute,
     *    'icon': an icon url link,
     *    'menu': function to retrieve a new submenu
     *})
     * </pre>
     * @param {Object} opt
     */
    add: function(opt) {
        if (!opt.name) {
            return;
        }
        var maxPos = MainMenu.root.length, thisItem = {
            'name'    : opt.name,
            'click'   : opt.action,
            'icon'    : opt.icon,
            'resIcon' : opt.resIcon,
            'menu'    : opt.subMenu||opt.menu
        };
        if (Util.isNumber(opt.position)) {
            if (opt.position < 0)      { opt.position = MainMenu.root.length + opt.position; }
            else if (opt.position < 1) { opt.position = 1;      }
            if (opt.position > maxPos) { opt.position = maxPos; }
        } else {
            opt.position = maxPos;
        }
        MainMenu.root.splice(opt.position, 0, thisItem);
    },

    /**
     * Create a Menu.
     * @param {Object} oMenu Array of key=>value pairs
     * @param {jQuery} oParent Where menu will be generated
     */
    create: function(oMenu, oParent) {
        if (Util.length(oMenu) < 1) {
            return;
        }
        Util.each(oMenu, function(index, menu) {
            var $li = c$('li');
            var $span = c$('span').text(menu.name).appendTo($li);
            
            if (menu.icon) {
                $li.prepend(c$('img').attr('src', menu.icon).css({
                    'float': 'left',
                    'margin-right': 5,
                    'width': 16
                }));
            } else {
                if (menu.resIcon) {
                    $span.addClass(Resources.className(menu.resIcon));
                } else {
                    $span.addClass(Resources.className('run_menu_icon'));
                }
                $span.css('padding-left', 21);
            }
            
            if (menu.css) { $li.css(menu.css); }
            
            if ( menu.important ) {
                $li.prependTo(oParent).addClass('important');
            } else {
                $li.appendTo(oParent);
            }
            
            if (Util.isFunc(menu.click)) {
                menu.click = {'func': menu.click};
            }
            if (Util.isObject(menu.click)) {
                $li.click(menu.click, MainMenu.Events.click);
            }
            else {
                $li.attr('onclick', menu.click);
            }
            if (Util.isFunc(menu.menu)) {
                $li.addClass('right_arrow');
                $li.mouseenter({'func': menu.menu}, MainMenu.Events.enter);
                $li.mouseleave(MainMenu.Events.leave);
            }
        });
    }
    
};

// ==Script==
// @id        Community.js
// @author    Dakam
// @memberOf  MWAddon.js
// ==Script==
/**
 * The Marquee Object.
 */
var MWAddonCommunity = {
    feeds: new Array(),
    current: 0,
    refreshTime: 0,
    linksLoaded: false,
    closed: false,
    isShowing: false
};
/**
 * Marquee Events
 */
MWAddonCommunity.Event = {
    click: function() {
        var data = MWAddonCommunity.feeds[$(this).attr('index')];
        if (data) {
            unsafeWindow.open(data.link, data.target);
        }
        return false;
    },
    stop: function() {
        // For some reason this not work in Firefox 3.x
        // $(this).trigger('pause');
        MWAddonCommunity.Event.pause.apply(this);
        MWAddonCommunity.isShowing = false;
        MWAddonCommunity.Show();
    },
    start: function() {
        var me = this;
        var endPos = parseInt($(this).attr('endPos'));
        var intval = $(this).attr('intval');
        if (intval) {
            clearInterval(intval);
        }
        if (isNaN(endPos)) {
            // Fix for Firefox 3.x compatibility.
            // $(me).trigger('stop');
            MWAddonCommunity.Event.stop.apply(me);
            return;
        }
        $(this).attr('intval', setInterval(function() {
            var prev = me.scrollLeft;
            me.scrollLeft += 2;
            if (me.scrollLeft == prev || me.scrollLeft >= endPos) {
                // Fix for Firefox 3.x compatibility.
                // $(me).trigger('stop');
                MWAddonCommunity.Event.stop.apply(me);
            }
        }, 40));
    },
    pause: function() {
        var n = $(this).attr('intval');
        if (n) {
            clearInterval(n);
            $(this).removeAttr('intval');
        }
    },
    next: function() {
        $('#marquee_outer').trigger('stop');
        return false;
    }
};
/**
 * Show the Marquee.
 */
MWAddonCommunity.Show = function() {
    if (UserConfig.main.get('opt_Community') !== true 
    ||  MWAddonCommunity.isShowing === true 
    ||  MWAddonCommunity.closed === true) 
    {
        return;
    }
    if (MWAddonCommunity.feeds.length < 1) {
        
        MWAddonCommunity.GetData(function(data) {
            if (data && data.length > 0) {
                MWAddonCommunity.Show();
                setInterval(function() {
                    MWAddonCommunity.GetData();
                }, 600000);
            }
        });
        return;
    }
    if (e$('#mwa_marquee') === null) {
        MWAddonCommunity.Init();
    }
    MWAddonCommunity.isShowing = true;
    
    var index = MWAddonCommunity.current;
    var data = MWAddonCommunity.feeds[index];
    var message = String(data.message).replace(/\n/g,' ');
    var $marquee = $('#marquee_outer')
    var marquee = $marquee.get(0);
    
    if ((++MWAddonCommunity.current) > MWAddonCommunity.feeds.length - 1) {
        MWAddonCommunity.current = 0;
    }
    if (data.type === 'post') {
        message += ' -- by <span style="color:gold;">'+data.name+'</span> ';
    } else {
        message += ' -- '+data.action+' <span style="color:gold;">'+data.name+'</span> '+Util.formatText(data.type);
    }
    $('#marquee_outer #marquee_inner').html(message);
    
    marquee.scrollLeft = 0;
    /*
     * $marquee.data('marquee', {
     *     'endPos': marquee.scrollWidth - $marquee.outerWidth(),
     *     'link': data.link,
     *     'target': data.target
     * });
     * 
     * Because Firefox 3.x does not support the jQuery data feature 
     * to store data in DOM elements, we're forced to use "attr".
     */
    $marquee.attr({
        'index': index,
        'title': data.action +' '+ data.name +' '+ Util.formatText(data.type),
        'endPos': marquee.scrollWidth - $marquee.outerWidth()
    });
    $marquee.trigger('start');
};
/**
 * Stop marquee and close Community box.
 */
MWAddonCommunity.Close = function() {
    var $marquee = $('#marquee_outer');
    $marquee.trigger('pause');
    $marquee.unbind().removeData('marquee');
    MWAddonCommunity.isShowing = false;
    MWAddonCommunity.closed = true;
    $('#mwa_marquee').remove();
    $('#mw_like_button').show();
    $('#final_wrapper').css('margin-top', 0);
    return false;
};
/**
 * Get all fan page news.
 * @param {Object} callback
 */
MWAddonCommunity.GetData =  function(callback) {
    if (MWAddonCommunity.closed === true) {
        return;
    }
    var expr = /mwaddon_(plugin|reminder)/i;
    var excl = /tinyurl|3bszve6|add\s?me|mafia\s?wars?\s?(2|two)/i;
    /**
     * Parse a installtion link.
     * @param {String} href
     */
    function parseLink(href) {
        try {
            var url = Util.uSplit(href);
            if (url.next_params) {
                var type = Util.doRgx(expr, url.next_params);
                if (type.$0) {
                    var data = Util.parseJSON(url.next_params);
                    data = Util.parseJSON(Base64.decode(data[type.$0]));
                    return {
                        type: type.$1,
                        action: 'Install',
                        name: data.name
                    };
                } else if (href.match('apps.facebook.com')) {
                    return {
                        type: 'link',
                        action: 'Get'
                    };
                }
            }
        } catch (e) {
            Logger.error('facebook.getLinks.parseLink: '+ e);
        	return {};
        }
    }
    /**
     * Add a new installation link.
     * @param {Object} content
     */
    function addLink(content) {
        if ((content.type==='plugin' && Plugins.exists(content.name))
        ||  (content.type==='reminder' && Reminders.exists(content.name)) ) {
            //Logger.debug(content.type+' '+content.name+' exists.');
            return false;
        }
        var index = MWAddonCommunity.feeds.length;
        Util.each(MWAddonCommunity.feeds, function(i, link) {
            if (content.type===link.type && Util.isSameString(content.name, link.name)) {
                if (content.time > link.time) {
                    index = i;
                } else {
                    index = -1;
                }
            }
        });
        if (index > -1) {
            MWAddonCommunity.feeds[index] = content;
            return true;
        }
        return false;
    }
    /**
     * Fix all feeds, sorted by time and cutted to limited value.
     */
    function fixFeeds() {
        var count = 0, links = new Array();
        MWAddonCommunity.feeds.sort(function(a,b) {
            return b.time - a.time;
        });
        Util.each(MWAddonCommunity.feeds, function(index, data) {
            if (data.type !== 'post') {
                links.push(data);
            } else if (count < UserConfig.main.opt_CommunityLimit) {
                links.push(data);
                count += 1;
            }
        });
        MWAddonCommunity.feeds = links;
    }
    /**
     * Get links.
     */
    function getLinks() {
        // Get and parse plugins and reminders installations
        facebook.getLinks(AppInfo.fanpage, function(response) {
            if (response && response.data) {
                Util.each(response.data, function(index, feed) {
                    var data = parseLink(feed.link);
                    if (data && data.type && (data.name||feed.name||feed.description)) {
                        addLink({
                            'type'    : data.type,
                            'action'  : data.action,
                            'target'  : '_top',
                            'name'    : data.name||feed.name||feed.description,
                            'message' : feed.message,
                            'link'    : feed.link,
                            'time'    : feed.created_time
                        });
                    }
                });
                fixFeeds();
            }
        });
    }
    // Get user answers
    facebook.getFeeds(AppInfo.fanpage, function(response) {
        var count = 0;
        if (response && response.data && response.data.length > 0) {
            Util.each(response.data, function(index, feed) {
                var message = feed.caption||feed.message;
                var link = feed.actions && feed.actions[0] && feed.actions[0].link;
                if ((feed.type === 'status'||feed.type === 'photo') && message && link) {
                    if (!excl.test(message) && count < UserConfig.main.opt_CommunityLimit) {
                        if (addLink({
                            'type'    : 'post',
                            'action'  : 'Go to',
                            'target'  : '_blank',
                            'name'    : feed.from.name,
                            'message' : feed.caption||feed.message,
                            'link'    : link,
                            'time'    : feed.created_time
                        })) {
                            count += 1;
                        }
                    }
                }
            });
            fixFeeds();
        }
        if (MWAddonCommunity.linksLoaded !== true) {
            MWAddonCommunity.linksLoaded = true;
            getLinks();
        }
        if (Util.isFunc(callback)) {
            callback(MWAddonCommunity.feeds);
        }
    });
};
/**
 * Initialize the Marquee.
 */
MWAddonCommunity.Init = function() {
    
    $(Base64.decode(
        'PGRpdiBpZD0ibXdhX21hcnF1ZWUiPg0KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4NCgkjbXdhX21hcnF1ZWUgZGl2Lm1hcnF1ZWVf'+
        'Ym94IHsNCgkJYmFja2dyb3VuZDogYmxhY2sgdXJsKGh0dHBzOi8venluZ2Fwdi5ocy5sbG53ZC5uZXQvZTYvbXdmYi9ncmFwaGlj'+
        'cy9tYXBfYmFzZWRfam9icy9leHBlcnRfdmlldy9leHBlcnR2aWV3X25hdl9taWQuZ2lmKSAwJSAwJSByZXBlYXQteDsNCgkJcG9z'+
        'aXRpb246IGFic29sdXRlOw0KCQltYXJnaW46IDBweDsNCgkJd2lkdGg6IDEwMCU7DQoJCW92ZXJmbG93OiBoaWRkZW47DQoJCWNv'+
        'bG9yOiB3aGl0ZTsNCgkJbGluZS1oZWlnaHQ6IDI4cHg7DQoJCWJvcmRlcjogMXB4IHNvbGlkIGJsYWNrOw0KCQlsZWZ0OiAwcHg7'+
        'DQoJCXBhZGRpbmc6IDFweDsNCgkJaGVpZ2h0OiAyNHB4Ow0KCQl0b3A6IC0yOXB4Ow0KCQl6LWluZGV4OiA0MDsNCgl9DQoJI213'+
        'YV9tYXJxdWVlIGRpdi5tYXJxdWVlX2JveCBkaXYjbWFycXVlZV9oZWFkZXIgew0KCQlmbG9hdDogbGVmdDsNCgkJbWFyZ2luLXJp'+
        'Z2h0OiAycHg7DQoJCWhlaWdodDogMThweDsNCgkJZm9udC1zaXplOiAxMnB4Ow0KCQlwYWRkaW5nOiAycHggMHB4IDBweCA1cHg7'+
        'DQoJCWxpbmUtaGVpZ2h0OiAxOHB4Ow0KCQl0ZXh0LWFsaWduOiBsZWZ0Ow0KCQlwYWRkaW5nOiAycHggMHB4IDBweCA1cHg7DQoJ'+
        'CWNvbG9yOiAjODk4OTg5Ow0KCX0NCgkjbXdhX21hcnF1ZWUgZGl2Lm1hcnF1ZWVfYm94IGRpdiNtYXJxdWVlX291dGVyIHsNCgkJ'+
        'd2lkdGg6IDU0MHB4Ow0KCQltYXJnaW46IDBweDsNCgkJaGVpZ2h0OiAyMHB4Ow0KCQlib3JkZXI6IDFweCBkb3R0ZWQgIzMzMzsN'+
        'CgkJYmFja2dyb3VuZC1jb2xvcjogIzExMTsNCgkJb3ZlcmZsb3c6IGhpZGRlbjsNCgkJcGFkZGluZzogMXB4IDFweCAwcHggMXB4'+
        'Ow0KCQlmb250LXNpemU6IDEycHg7DQoJCWN1cnNvcjogcG9pbnRlcjsNCgkJZmxvYXQ6IGxlZnQ7DQoJfQ0KCSNtd2FfbWFycXVl'+
        'ZSBkaXYubWFycXVlZV9ib3ggZGl2I21hcnF1ZWVfb3V0ZXIgPiBkaXYjbWFycXVlZV9pbm5lciB7DQoJCWN1cnNvcjogcG9pbnRl'+
        'cjsNCgkJd2hpdGUtc3BhY2U6IG5vd3JhcDsNCgkJbWFyZ2luLXRvcDogMnB4Ow0KCQlsaW5lLWhlaWdodDogMTRweDsNCgkJZmxv'+
        'YXQ6IGxlZnQ7DQoJCXBhZGRpbmc6IDBweCA1NzBweCAwcHggNTcwcHg7DQoJfQ0KCSNtd2FfbWFycXVlZSBkaXYubWFycXVlZV9i'+
        'b3ggI21hcnF1ZWVfaGVhZGVyIGEgew0KCQlwb3NpdGlvbjogYWJzb2x1dGU7DQoJCWRpc3BsYXk6IGJsb2NrOw0KCQl3aWR0aDog'+
        'MjBweDsNCgkJaGVpZ2h0OiAyMHB4Ow0KCQl0ZXh0LWRlY29yYXRpb246IG5vbmU7DQoJCWJhY2tncm91bmQ6IHRyYW5zcGFyZW50'+
        'IHVybCgnaHR0cHM6Ly96eW5nYXB2LmhzLmxsbndkLm5ldC9lNi9td2ZiL2dyYXBoaWNzL2JsYWNrX2Nsb3NlX2J1dHRvbl8xNHgx'+
        'NF8wMS5naWYnKSAwJSAwJSBuby1yZXBlYXQ7DQoJfQ0KCSNtd2FfbWFycXVlZSBkaXYubWFycXVlZV9ib3ggI21hcnF1ZWVfaGVh'+
        'ZGVyIGEuY2xvc2VfYnV0dG9uIHsNCgkJYmFja2dyb3VuZDogdHJhbnNwYXJlbnQgdXJsKCdodHRwczovL3p5bmdhcHYuaHMubGxu'+
        'd2QubmV0L2U2L213ZmIvZ3JhcGhpY3MvYmxhY2tfY2xvc2VfYnV0dG9uXzE0eDE0XzAxLmdpZicpIDAgMCBuby1yZXBlYXQ7DQoJ'+
        'CXJpZ2h0OiA1cHg7DQoJCXRvcDogNnB4Ow0KCX0NCgkjbXdhX21hcnF1ZWUgZGl2Lm1hcnF1ZWVfYm94ICNtYXJxdWVlX2hlYWRl'+
        'ciBhLm5leHRfYnV0dG9uICB7DQoJCWJhY2tncm91bmQ6IHRyYW5zcGFyZW50IHVybCgnaHR0cHM6Ly96eW5nYXB2LmhzLmxsbndk'+
        'Lm5ldC9lNi9td2ZiL2dyYXBoaWNzL2VtcGlyZS9idXR0b25fbWluaV9wbGF5LnBuZycpIDAlIDAlIG5vLXJlcGVhdDsNCgkJcmln'+
        'aHQ6IDMwcHg7DQoJCXRvcDogMnB4Ow0KCX0NCjwvc3R5bGU+DQo8ZGl2IGNsYXNzPSJtYXJxdWVlX2JveCI+PGRpdiBpZD0ibWFy'+
        'cXVlZV9oZWFkZXIiPjxzcGFuPk1XQWRkb24gQ29tbXVuaXR5IE5ld3M6PC9zcGFuPg0KPGEgaHJlZj0iIyIgdGl0bGU9IkNsb3Nl'+
        'IENvbW11bml0eSBOZXdzLiIgY2xhc3M9ImNsb3NlX2J1dHRvbiI+PC9hPjxhIGhyZWY9IiMiIHRpdGxlPSJTaG93IG5leHQgbWVz'+
        'c2FnZS4iIGNsYXNzPSJuZXh0X2J1dHRvbiI+PC9hPg0KPC9kaXY+DQo8ZGl2IGlkPSJtYXJxdWVlX291dGVyIj48ZGl2IGlkPSJt'+
        'YXJxdWVlX2lubmVyIj48L2Rpdj48L2Rpdj4NCjwvZGl2Pg0KPC9kaXY+'
    )).prependTo('#final_wrapper');
    
    $('#marquee_header .close_button').click(MWAddonCommunity.Close);
    $('#marquee_header .next_button').click(MWAddonCommunity.Event.next);
    $('#marquee_outer').click(MWAddonCommunity.Event.click)
    .bind('start mouseleave', MWAddonCommunity.Event.start)
    .bind('pause mouseenter', MWAddonCommunity.Event.pause)
    .bind('stop',             MWAddonCommunity.Event.stop);
    
    $('#mw_like_button').hide();
    $('#final_wrapper').css('margin-top', 28);
}
// ==Script==
// @id        Notify.js
// @author    Dakam
// @memberOf  MWAddon.js
// ==Script==

var MWAddonNotify = {
    Initialized: false,
    context: null,
    /**
     * Show a notification.
     * @param {String} title
     * @param {String} message
     * @param {Function} callback
     * @param {Number} [timeOut]
     */
    Show: function(title, message, callback, timeOut) {
        var $booble = c$('div', 'class:notification');
        var icon_res = Resources.className('reminder_menu_icon');
        timeOut = Math.max(5000, timeOut||0);
        
        if (Util.isString(title)) {
            c$('div', 'class:text_title '+icon_res).appendTo($booble).text(title);
        }
    
        if (Util.isString(message)) {
            c$('div').html(message.replace(/\n/g,'<br>')).appendTo($booble);
        } else {
            return;
        }
        
        $booble.click(function() {
            MWAddonNotify.hideAll();
            $booble.remove();
            if (Util.isFunc(callback)) {
                callback(true);
                callback = null;
            }
        }).hover(function() {
            $booble.stop(true).removeAttr('style');
            timeOut = 5000;
        }, function() {
            $booble.delay(timeOut).fadeOut(function(){
                $booble.remove();
                if (Util.isFunc(callback)) {
                    callback(false);
                }
            });
        });
        if (UserConfig.main.notificationOnTop === true) {
            $('#TopField').focus();
        }
        MWAddonNotify.context.prepend($booble.css('display','none'));
        $booble.fadeIn('normal', function() {
            $booble.mouseout();
        });
    },
    /**
     * Set the new position coordinates.
     * @param {Object} pos
     */
    setPosition: function(pos) {
        if (position) {
            MWAddonNotify.context.css({'top':pos.top,'right':pos.right});
        }
    },
    /**
     * Get how many notifications are active.
     */
    Showing: function() {
        return MWAddonNotify.context.find('.notification').length;
    },
    /**
     * Hide all notifications.
     */
    hideAll: function() {
        MWAddonNotify.context.find('.notification').hide();
    },
    /**
     * Init the Notification container.
     */
    init: function() {
        if (MWAddonNotify.Initialized) {
            return;
        }
        MWAddonNotify.context = c$('div', 'id:notify_container').appendTo('#final_wrapper');
        
        c$('style').append(Base64.decode(
        'I25vdGlmeV9jb250YWluZXJ7DQogICAgcG9zaXRpb246IGFic29sdXRlOw0KICAgIHotaW5kZXg6IDEwMDA1OyAvKiB0aGlzIGV2'+
        'ZW4gbW9yZSByaWRpY3Vsb3VzIHRoYW4geW91cnMuLi4gKi8NCiAgICB0b3A6IDE2MXB4Ow0KICAgIHJpZ2h0OiA0cHg7DQp9DQoj'+
        'bm90aWZ5X2NvbnRhaW5lciAubm90aWZpY2F0aW9uIHsNCiAgICBjdXJzb3I6IHBvaW50ZXI7DQogICAgcGFkZGluZzogMTJweCAx'+
        'OHB4Ow0KICAgIG1hcmdpbjogMHB4IDBweCA2cHg7DQogICAgYmFja2dyb3VuZC1jb2xvcjogYmxhY2s7DQogICAgY29sb3I6IHdo'+
        'aXRlOw0KICAgIHdpZHRoOiA0MDBweDsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KICAgIG9wYWNpdHk6IDAuOTsNCiAgICAtd2Vi'+
        'a2l0LWJveC1zaGFkb3c6IHJnYigxNTMsIDE1MywgMTUzKSAwcHggMHB4IDE4cHg7DQogICAgLW1vei1ib3gtc2hhZG93OiByZ2Io'+
        'MTUzLCAxNTMsIDE1MykgMHB4IDBweCAxOHB4Ow0KICAgIGJveC1zaGFkb3c6IHJnYigxNTMsIDE1MywgMTUzKSAwcHggMHB4IDE4'+
        'cHg7DQogICAgLXdlYmtpdC1ib3JkZXItcmFkaXVzOiAzcHg7DQogICAgLW1vei1ib3JkZXItcmFkaXVzOiAzcHg7DQogICAgYm9y'+
        'ZGVyLXJhZGl1czogM3B4Ow0KICAgIGJvcmRlcjogMXB4IHNvbGlkIHdoaXRlOw0KfQ0KI25vdGlmeV9jb250YWluZXIgLm5vdGlm'+
        'aWNhdGlvbjpob3ZlciB7DQogICAgYm94LXNoYWRvdzogcmdiKDAsIDAsIDApIDBweCAwcHggMThweDsNCiAgICBvcGFjaXR5OiAx'+
        'Ow0KfQ0KI25vdGlmeV9jb250YWluZXIgLm5vdGlmaWNhdGlvbiAudGV4dF90aXRsZSB7DQogICAgd2lkdGg6IGF1dG8gIWltcG9y'+
        'dGFudDsNCiAgICBwYWRkaW5nLWxlZnQ6IDIwcHg7DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogICAgZm9udC1zdHlsZTogaXRh'+
        'bGljOw0KICAgIGZvbnQtc2l6ZTogMTRweDsNCiAgICB0ZXh0LXRyYW5zZm9ybTogY2FwaXRhbGl6ZTsNCiAgICB3aGl0ZS1zcGFj'+
        'ZTogbm93cmFwOw0KICAgIG92ZXJmbG93OiBoaWRkZW47DQp9'
        )).appendTo(MWAddonNotify.context);
        
        MWAddonNotify.Initialized = true;
    }
};
// ==Script==
// @id        FriendSelector.js
// @author    Dakam
// @memberOf  MWAddon.js
// @run-at    *xw_action=friend_selector*
// ==Script==
/**
 * Add Tiny button to MW friend_selector form.
 */
function FriendSelectorGetTiny() {
    var _FB = unsafeWindow.FB;
    var _MW = unsafeWindow.MW;

    var tinyUrl = 'http://tinyurl.com/create.php?alias=';
    var time = Math.round(new Date().getTime() / 1000);
    var appElt = document.getElementById('mfs_container').parentNode;
    var divElt = appElt.querySelector('div');
    var ctrlElt = document.createElement('div')
    var btnElt = createButton();
    var gift_name = appElt.querySelector('.request_item_container > div').innerText;
    var url;
     
    ctrlElt.setAttribute('style', 'font-size:22px; font-weight:bold; color:#FFCC00; margin-bottom:5px; padding:4px; text-align:center;');
    appElt.insertBefore(ctrlElt, divElt);
    
    ctrlElt.innerHTML = '<span>MWAddon Get Tiny URL: </span>';
    ctrlElt.appendChild(btnElt);
    btnElt.addEventListener('click', function() {
        var imgElt = document.createElement('img');
        imgElt.setAttribute('src', 'http://mwfb.static.zgncdn.com/mwfb/graphics/socialmissions/ajax-loader.gif');
        appElt.innerHTML = '<br><br>';
        appElt.appendChild(imgElt);
        getLink(function(u) {
            var cUrl = Util.uSplit(u);
            url = MW.getExtURL('freegifts','accept_gift')+'&next_params='+escape(cUrl.data);
            getShortURL(url, showShortenedURL, showManualButton);
        });
    }, false);
    
    function getLink(callback) {
        _FB.ui = function(a,b) {
            b({request:'new_request_id'});
        };
        _MW.Ajax.request = function(a,b) {
            callback(a);
        };
        _MW.Request.setTabFriendLists({"1":[123456789]});
        _MW.Request.setSelectableForTab(1);
        _MW.Request.selectFriend(123456789);
        _MW.Request.submitMFS();
    }

    function createButton() {
        var inputElt = document.createElement('input');
        inputElt.setAttribute('style', 'position: relative; top: -3px; width: 180px; height: 30px; margin-left: 5px;');
        inputElt.setAttribute('type', 'button');
        inputElt.setAttribute('value', 'Get Tiny URL');
        return inputElt;
    }
    
    function showShortenedURL(shortURL) {
        var inputElt = document.createElement('input');
        appElt.innerHTML = ''; 
        ctrlElt.innerHTML = '<br><br><span>Your Tiny URL: </span><br><br>';
        appElt.appendChild(ctrlElt);
        appElt.appendChild(inputElt);   
        inputElt.setAttribute('style', 'heigth: 22px;'
        + 'text-align:center; width: 500px; font-weight: bold; color: rgb(208, 208, 208); '
        + 'border: 1px solid rgb(153, 153, 153); background-color: black; font-size: 14px;');
        inputElt.setAttribute('type', 'text');
        inputElt.setAttribute('readonly', 'readonly');
        
        inputElt.value = (gift_name + ' => ' + shortURL);
    }

    function showManualButton() {
        var btnElt = createButton();
        tinyUrl += time + '-' + gift_name + '&url=' + escape(url);
        appElt.innerHTML = '';
        ctrlElt.innerHTML = '<br><br><span>Click to Open a new Tab: </span><br><br>';
        appElt.appendChild(ctrlElt);
        appElt.appendChild(btnElt);
        btnElt.setAttribute('onclick', "window.open('"+tinyUrl+"'); return false;");
    }

    return false;
}
// ==Script==
// @id        Initialize.js
// @author    Dakam
// @memberOf  MWAddon.js
// ==Script==
/**
 * Set main configuration.
 */
UserConfig.create('main', {
    // exclude some settings when exporting.
    _excludedToExport: ['groups', 'appLastUpdateCheck'],
    
    groups               : {'0': 'My wall'},            
    // publish configuration 
    privacy              : {'value': 'ALL_FRIENDS'},
    publishPreview       : false,
    
    // tooltip language selection
    toolTips             : true,
    toolTipLanguage      : 'none',
    
    // check updates
    checkForUpdates      : true,
    appLastUpdateCheck   : 0,
    
    // Shortener Services Configuration
    shortServiceID       : 'TinyUrl',
    unshortServiceID     : 'unshort.me',
    shortServiceLogin    : new Object(),
    
    // Create a new menu with fan page notes
    helpNotes            : true,
    
    // MWaddon features on/off
    opt_Toolbar          : true,
    opt_ChromeBM         : true,
    opt_Community        : true,
    opt_CommunityLimit   : 3,
    opt_JobRates         : true,
    opt_CollectionPage   : true,
    opt_ProfilePage      : true,
    opt_FamilyPage       : true,
    
    // Others
    debugMode            : false,
    notificationOnTop    : false,
    
    // Timeouts
    fbTimeout            : 60,
    rqTimeout            : 30,
    jsTimeout            : 10,
    
    // Autoheal Configuration
    autoHeal             : false,
    autoHealWhen         : 25,
    autoHealIn           : 1,
	
	// saved user gifts ids
	userGiftsIds         : new Object(),
    userLinks            : new Array()
    
});
/** @namespace */ var MWAddonInit = {};

/**
 * Initialize Storage to save/load data.
 * @param {Function} callback
 */
MWAddonInit.Storage = function(callback) {

    if (typeof GM_deleteValue == 'undefined')
    {
        // make sure we have a localStorage
        if (typeof(localStorage) == 'undefined' || typeof(localStorage.getItem) == 'undefined')
        {
            if (!(localStorage = unsafeWindow.localStorage)) {
                Logger.error('localStorage is undefined, using a temporal storage.');
                localStorage = new (function() {
                    var tmp_array = [];
                    this.setItem = function(name, value){
                        tmp_array[name] = value;
                    };
                    this.getItem = function(name) {
                        return tmp_array[name] ? tmp_array[name] : null;
                    };
                    this.removeItem = function(name) {
                        tmp_array[name] = null;
                    };
                    return this;
                })();
            }
        }
        var timeout = setTimeout(defineAPI, 10000);
        /**
         * @param {Bollean} chromeFileSystem
         */
        function defineAPI(chromeFileSystem) {
            clearTimeout(timeout);
            if ((global.has_filesystem = chromeFileSystem)) {
                Logger.log('INFO', 'Chrome FileSystem detected.');
            } else {
                Logger.log('INFO', 'General LocalStorage detected.');
                Logger.log('INFO', 'CAUTION!!, clear browser cookies will remove all saved data.');
            }
            /**
             * Sets the named preference to the specified value.
             * @param {String} name The name preference.
             * @param {String, Number, Boolean} value Must be strings, booleans, or integers.
             */
            GM_setValue = (chromeFileSystem !== true)
            ? function(name, value) {
                try {
	                localStorage.setItem(name, value);
                } catch (e) {
                	Logger.error('WARNING! Save data is not possible:');
                	Logger.error(e);
                }
            }
            : function(name, value, callback) {
                if (typeof callback !== 'function') {
                    callback = Util.noop;
                };
                chrome.extension.sendRequest(AppInfo.chmextID, {
                    action: 'SaveData',
                    file: name,
                    data: value
                }, callback);
            };
            /**
             * Returns the named preference, or defaultValue if it does not exist.
             * @param {String} name The name preference.
             * @param {String, Number, Boolean} defaultValue To return if it does not exist.
             * @return {String, Number, Boolean}
             */
            GM_getValue = (chromeFileSystem !== true)
            ? function(name, defaultValue) {
                try {
                    var value = localStorage.getItem(name);
                } catch(e) {}
                return value ? value : defaultValue;
            }
            : function(name, callback) {
                if (typeof callback !== 'function') return;
                chrome.extension.sendRequest(AppInfo.chmextID, {
                    action: 'LoadData',
                    file: name
                }, callback);
            };
            /**
             * Deletes the named preference or subtree.
             * @param {String} name The name preference to delete.
             */
            GM_deleteValue = (chromeFileSystem !== true)
            ? function(name) {
                try { localStorage.removeItem(name); } catch(e) {}
            }
            : function(name) {
                // empty
            };
            // return;
            callback&&callback();
        }
        // Check for MWAddon Chrome Extended.
        if (global.is_chrome === true) {
            chrome.extension.sendRequest(AppInfo.chmextID, 'hasFileSystem', defineAPI);
        } else {
            defineAPI(false);
        }
    } else {
        Logger.log('INFO', 'Greasemonkey API detected.');
        callback&&callback();
    }
}
/**
 * Initialize jQuery
 * @param {Function} callback
 */
MWAddonInit.jQuery = function(callback) {
    Util.until({
        retries: 5,
        delay: 2000,
        test: function() { return (typeof($ = unsafeWindow.$) !== 'undefined'); },
        success: function() {
            if (typeof($) === 'undefined') {
                showHelpPopup({
                    icon: 'caution',
                    title: 'MWAddon cannot initializate.',
                    message: 'jQuery is not available.'
                });
            } else {
                Logger.log('INFO', 'jQuery is loaded.');
                jQueryExtend();
                try {
                    $(document).ready(callback);
                } catch(e) {
                    callback();
                }
            }
        }
    });
    
    function jQueryExtend() {
        // add regex selector to jQuery
        $.expr[":"].regex = function(a, i, m, r)
        {
            var p, s_expr, s_member;
            // get member and expr
            if ((p = m[3].split(',')).length > 1) {
                s_member = $.trim(p[0]);
                s_expr = $.trim(p[1]);
            }
            else {
                s_member = 'html';
                s_expr = $.trim(m[3]);
            }
            // create regex expr
            var r = new RegExp(s_expr, 'i');

            // is a jQuery member
            if ($(a)[s_member]) {
                return r.test($(a)[s_member]());
            }
            // is a element member
            if ($(a).attr(s_member)) {
                return r.test($(a).attr(s_member));
            }
            return false;
        };
    }
};
/**
 * Initialize Facebook
 * @param {Function} callback
 */
MWAddonInit.Facebook = function(callback) {
    Util.until({
        retries: 10,
        delay: 3000,
        test: function() { return (typeof(MWFB = unsafeWindow.FB) !== 'undefined'); },
        success: function() {
            if (typeof(MWFB) == 'undefined') {
                Logger.error('Cant load Facebook API.');
                AppInfo.unique_prefix = AppInfo.prefix + 'allUsers_';
                showHelpPopup({
                    icon: 'caution',
                    title: 'Facebook API is not available.',
                    message: 'Personal settings can\'t be loaded for this session.<br><br>'
                    +        '- Click "Unframe MW" if your game is cutted to a half.<br>'
                    +        '- Click "Continue" to load MWAddon using global settings.<br>'
                    +        '- Click "Close" to unload MWAddon.',
                    buttons: [{
                        label: 'Unframe MW',
                        addClass: 'short green',
                        onclick: function() {
                            unsafeWindow.open(global.location.href, '_top');
                        }
                    }, {
                        label: 'Continue',
                        addClass: 'short white',
                        onclick: callback
                    }, {
                        label: 'Close',
                        addClass: 'short red'
                    }]
                });
            } else {
                // wait until ready
                facebook.init(function() {
                    Logger.log('INFO', 'Facebook API is loaded.');
                    AppInfo.unique_prefix = AppInfo.prefix + facebook.session.uid + '_';
                    UserConfig.main.load(callback);
                });
            }
        }
    });
};
/**
 * Get Active Quest
 */
MWAddonInit.QuestBar = function() {
    var $a = e$('#quest_tray a:regex(onclick,showQuestRequestFriendSelector)');
    if ($a) {
        try {
            var q = Util.doRgx(/MW.QuestBar.showTask\((\d+),(\d+)\)/, $a.attr('onclick'));
            if (q.$1 && q.$2) {
				UserFreeGifts.get(441).params = {'quest':q.$1, 'task':q.$2};
                Logger.debug('activeQuest: {"quest":'+q.$1+', "task":'+q.$2+'}');
                return false;
            }
        } catch (e) {}
    }
};
/**
 * Check for a new update.
 */
MWAddonInit.Update = function() {
    var now = (new Date()).getTime();
    var uInt = 4 * 60 * 60 * 1000;
    if (UserConfig.main.checkForUpdates) {
        if ( now >= (UserConfig.main.appLastUpdateCheck + uInt) ) {
            UserConfig.main.set('appLastUpdateCheck', now);
            UserConfig.main.save();
            setTimeout(function(){
                Logger.log('INFO', 'Checking for updates...');
                Updater.check(false);
            }, 1000);
        }
    }
};
/**
 * Get user id and language.
 */
MWAddonInit.User = function() {
    if (!(global.USER_ID = MW.getUserID())) {
        showHelpPopup({
            icon: 'caution',
            title: 'MWAddon cannot initializate.',
            message: 'No user id found.'
        });
        return false;
    }
    // get person ID
    global.PERSON_ID = String(global.USER_ID).match(/(\d+)/)[1];
    
    // set tooltip language
    if ( Tooltips.resourceExists( global.User.locale() ) ) {
        global.mw_locale = global.User.locale();
    }
    return eval(Base64.decode('IS8xMTY3NzczMzl8MTM5OTExNzY3Ly50ZXN0KGdsb2JhbC5VU0VSX0lEKTs='));
};
/**
 * Install a new application from URL (if available).
 * @param {Function} callback
 */
MWAddonInit.Installation = function(callback) {
    var message = 'The "${name}" ${type} is ready to be installed.<br>Do you want to proceed?'
    + '<br><br><span style="font-size:11px">You can disable/remove ${type}s by opening ${editor}.</span>';
    var params = Util.uSplit(global.location.href);
    
    if (!Util.isFunc(callback)) {
        return;
    } else if (!params) {
        callback();
        return;
    }
    try {
        function addToCollection(obj, collection) {
            var index = collection.length;
            obj.active = true;
            Util.each(collection, function(i, o) {
                if (Util.isSameString(obj.name, o.name)) {
                    index = i;
                    return false;
                }
            });
            collection[index] = obj;
        }
        if (params.mwaddon_plugin) {
            UserConfig.plugins.load(function() {
                askToInstall('Plugin', function(data, callback){
                    addToCollection(new MWAddonPlugin(data), UserConfig.plugins.all);
                    UserConfig.plugins.save(callback);
                }, Util.parseJSON( Base64.decode(params.mwaddon_plugin) ) );
            });
        } 
        else if (params.mwaddon_reminder) {
            UserConfig.reminder.load(function() {
                askToInstall('Reminder', function(data, callback){
                    addToCollection(new MWAddonReminder(data), UserConfig.reminder.all);
                    UserConfig.reminder.save(callback);
                }, Util.parseJSON( Base64.decode(params.mwaddon_reminder) ) );
            });
        } else {
            callback();
        }
        function askToInstall(type, handler, data) {
            function success() {
                setTimeout(callback, 1000);
            }
            function install() {
                handler(data, success);
            }
            $('#content_row > div[id^=popup_]').empty();
            showAskPopup('New '+type+' Installation!', Util.render(message, {
                'name': data.name||'Unknow',
                'type': type,
                'editor': (type==='Plugin'?'Plugin Manager':'Reminder Editor')
            }), install, success);
        }
    } catch (e) {
    	callback();
        Logger.error('Plugin Installation: '+e);
    }
};
/**
 * Handle game ajax requests.
 */
MWAddonInit.HandleAjax = function(){
    $('#mainDiv').ajaxComplete(function(e, r, o){
        if (!/xw_controller/.test(o.url)) {
            return;
        }
        MW.updateUri(o.url);
        MainMenu.init();
        AutoActions(r.responseText);
        setTimeout(MWAddonToolbar.Show, 20);
        setTimeout(MWAddonCommunity.Show, 50);
        setTimeout(function(){ Reminders.checker(o.url); }, 2000);
        
        if (o.url.match('xw_action=view_stage_jobs')) {
            setTimeout(PageJob, 200);
            return;
        }
        var m = Util.doRgx(/<!--\s*Current\s*Page:\s*(\w+)/, r.responseText).$1;
        if (m) { global.loadPage(m, r.responseText); }
        
        // Execute all onPageLoad events.
        Util.each(global.onPageLoadArray, function(i, f){
            if (Util.isFunc(f)) {
                try { f(o.url, m); } catch (e) {}
            }
        });
    });
};
/**
 * All ready, Initialize plugins and reminders.
 * @param {Function} callback
 */
MWAddonInit.Ready = function(callback) {
    Logger.log('INFO', 'Initialization Completed.');
    function addHelpNotes() {
        if (UserConfig.main.helpNotes !== true) {
            return;
        }
        var notes = new Array();
        MainMenu.add({
            position: 5,
            name: 'Help Notes',
            resIcon: 'info_icon',
            menu: function() { return notes; }
        });
        facebook.notesGet(AppInfo.fanpage, function(response) {
            Util.each(response.data, function(index, note) {
                notes.push({
                    name: note.subject,
                    click: {'url': 'http://www.facebook.com/note.php?note_id='+note.id},
                    icon: note.icon
                });
            });
        });
    }
    // Initialize plugins and reminders.
    Plugins.init(function() {
        Plugins.each(function(i, p) {
            if (parseInt(p.runat) === Plugins.RUNAT_STARTUP) {
                Plugins.exec(i);
            }
        });
        Reminders.init(function() {
            addHelpNotes();
            MainMenu.init();
            Tooltips.init();
            MWAddonToolbar.Show();
            MWAddonCommunity.Show();
            MWAddonNotify.init();
            MW.updateUri(global.location.href);
            global.loadPage(MW.currentPageName());
            UserFreeGifts.update();
            callback&&callback();
        });
    });
};
MWAddonInit.Title = function() {
    function showNews() {
        if (MWAddonCommunity) {
            MWAddonCommunity.closed = false;
            if (UserConfig.main.get('opt_Community') !== true) {
                showAskPopup('Community News', 
                'Community News is disabled, would you like to enable it?', function() {
                    UserConfig.main.set('opt_Community', true);
                    UserConfig.main.save(MWAddonCommunity.Show)
                });
            } else {
                MWAddonCommunity.Show();
            }
        }
        return false;
    }
    c$('div').prependTo('#mw_masthead')
    .append(c$('span').html(AppInfo.name+' v'+AppInfo.version+'&nbsp;|&nbsp;'))
    .append(c$('a','target:_blank').text('Show Community News').click(showNews))
    .append(c$('span').html('&nbsp;|&nbsp;'))
    .append(c$('a','target:_blank').text('Support MWAddon').attr('href',AppInfo.contributionURL)).css({
        'font-weight':'bold',
        'font-size':10,
        'background-color': 'black',
        'opacity': '0.7',
        'z-index': 18
    });
}
/**
 * Start MWAddon Initialization.
 */
MWAddonInit.Start = function() {
    Logger.log('INFO', 'Initializating...');
    Logger.log('INFO', 'Server Host: '+global.location.host);
    // Add cities
    global.addCity(1, 'New York',    true,  1, '$');
    global.addCity(2, 'Cuba',        false, 1, 'C$');
    global.addCity(3, 'Moscow',      false, 1, 'R$');
    global.addCity(4, 'Bangkok',     false, 1, 'B$');
    global.addCity(5, 'Las Vegas',   true,  1, 'V$');
    global.addCity(6, 'Italy',       false, 1, 'L$');
    global.addCity(7, 'Brazil',      true,  5, 'BRL$',   'brz_real_sm.png');
    global.addCity(8, 'Chicago',     true,  5, '\u00a2', 'chic_clam_sm.png');
    global.addCity(9, 'London',      true,  5, '\u00a3', 'lond_pound_sm.png');
    global.cityNames = (function() {
        var a = new Object();
        global.cities.each(function(id, city) {
            a[id] = city.name;
        });
        return a;
    })();
    UserConfig.main.add('autoDeposit', UserConfig.getSettingFrom(global.cityNames, {
        'active' : false,
        'amount' : 5000
    }));
    MWAddonInit.Storage(function() {
        // init jquery
        MWAddonInit.jQuery(function() {
            // init user
            if (MWAddonInit.User() !== true) {return;}
            // add resources
            Resources.insertCSS();
            // init facebook
            MWAddonInit.Facebook(function() {
                // add header title
                MWAddonInit.Title()
                // check for updates
                MWAddonInit.Update();
                // Load active Mission Crew
                MWAddonInit.QuestBar();
                // check installations from URL
                MWAddonInit.Installation(function() {
                    // for handle page loadings
                    MWAddonInit.Ready(MWAddonInit.HandleAjax);                      
                });
            });
        }); 
    });
};// ==Script==
// @id        AutoActions.js
// @author    Dakam
// @requires  MWAddon.js
// ==Script==
/**
 * Check Cash and auto deposit it.
 */
function AutoActions(htmlText) {
    var user_fields = MW.evaluateUserFields(htmlText);
    if (!Util.isSet(user_fields)) {
        return;
    }
    var healIn = parseInt(UserConfig.main.autoHealIn);
    var cityId = parseInt(user_fields['current_city_id']);
    var cash   = user_fields['user_cash'];
    var health = user_fields['user_health'];

    if (UserConfig.main.autoHeal === true && health < UserConfig.main.autoHealWhen) {
        // autoheal
        MW.heal((healIn===1 ? (healIn===cityId ? 0 : 1 ) : 0), MWAddonMessage);
        
    } else {
        var deposit = UserConfig.main.autoDeposit[cityId];
        if (deposit.active === true) {
            if (cash > deposit.amount) {
                MW.deposit(cityId, cash, MWAddonMessage);
            }
        }
    }
}
function MWAddonMessage(text) {
    var $msg = e$('#deposit_message');
    if ($msg !== null) {
        // if message element exist, clear timeout and fix css.
        clearTimeout($msg.attr('timeout'));
        $msg.stop().css({'opacity': 0, 'left': 0});
        
    } else {
        // create a new message element.
        $msg = c$('div', 'deposit_message').html(text).prependTo('#mainDiv').css({
            'opacity': 0,
            'position': 'absolute',
            'left': 0,
            'width': '70%',
            'height': 20,
            'top': 0,
            'padding': 2,
            'background': 'url('+global.zGraphicsURL+'empire/header_module_gradient.gif) repeat-x',
            'z-index': 999,
            'border': '2px solid #666',
            'text-align': 'center',
            'font-weight': 'bold'
        });
        // fix to center. 
        $msg.css('margin', '0px -'+ String($msg.outerWidth()/2) + 'px');
    }
    // animate element show and hide.
    $msg.animate({'opacity': 1,'left': '50%'}, {
        complete: function() {
            $msg.attr('timeout', setTimeout(function () {
                $msg.animate({'opacity': 0,'left': '100%'}, { 
                	complete: function() {$msg.remove();} 
                });
            }, 5000));
        }
    });
}
// ==Script==
// @id        Battlefield.js
// @author    Dakam
// @requires  MWAddon.js
// ==Script==

UserConfig.create('bfopt', {
    startCity           : 1,
    maxLogLength        : 250,
    showSocialEvents    : false,
    collectClanFightXp  : false,
    epicBattle          : false,
    epicBattleFortress  : false,
    epicBattleRefresh   : 30,
    useBlacklist        : true,
    useStamPack         : false,
    useStamPackWhen     : 2000,
    useHealPack         : false,
    useHealPackWhen     : 1000,
    blackList           : new Object(),
    whiteList           : new Object(),
    frdClanList         : new Object(),
    enmClanList         : new Object(),
    // FILTER
    badgeFilterActive   : false,
    badgeFilterExpr     : '',
    nameFilterActive    : false,
    nameFilterExpr      : '',
    // HEAL
    healActive          : true,
    healCity            : 1,
    healBelow           : 100,
    healTimer           : 60,
    healAttacking       : false, 
    healMinSta          : 0,
    // ATTACK
    attackPwr           : true,
    attackRetries       : 5,
    attackUseMax        : false,
    attackMax           : 60,
    attackUsePerFoe     : false,
    attackPerFoe        : 40,
    //attackToNpc         : false,
    attackRevenge       : false,
    attackRevengeFilter : 1,
    // ATTACK DELAY
    attkdelayUseA       : false,
    attkdelayUseB       : true,
    attkdelayA          : 0,
    attkdelayB          : 0,
    // REFRESH
    refreshOn           : false,
    refreshMax          : 6,
    // PUBLISH
    publishActive       : false,
    publishAfter        : 15,
    publishTo           : '',
    // RANGE FILTERS
    levelRangeActive    : false,
    levelRangeMin       : 0,
    levelRangeMax       : 10000,
    levelRangeMethod    : 'attk',
    mafiaRangeActive    : false,
    mafiaRangeMin       : 100,
    mafiaRangeMax       : 501,
    mafiaRangeMethod    : 'attk',
    compareRangeMethod  : 'AND',
    // SKIP
    skipIced            : true,
    skipIcedByMe        : false,
    skipUseHealth       : false,
    skipHealth          : 100,
    skipHealed          : true,
    skipUnderAttk       : false,
    skipUnderAttkPct    : 15,
    skipWrongCash       : false,
    skipUseMinCash      : false,
    skipByMinCash       : 0,
    // RAPID FIRE
    rapidFireActive     : true,
    rapidFireAttacks    : 40,
    rapidFireHealth     : 50,
    rapidFireTiming     : 500,
    rapidFireAutoTiming : true,
    // RED ICE
    redIceActive        : false,
    redIceMaxAttk       : 25,
    redIceAfterWon      : true,
    redIceEpicBattle    : false,
    // STOP
    stopKeepStaOn       : false,
    stopKeepSta         : 0,
    stopKeepExpOn       : false,
    stopKeepExp         : 0,
    stopByIces          : false,
    stopIceAmount       : 0,
    stopByLevelup       : true,
    stopResume          : true,
    stopResumeDelay     : 5,
    stopAutomatic       : false,
    // TIMERS
    timerStartActive    : false,
    timerStartMins      : 30,
    timerFightActive    : false,
    timerFightMins      : 10,
    timerFightResume    : 5,
    timerCityActive     : false,
    timerCityMins       : 2,
    timerRivalsActive   : false,
    timerRivalsMins     : 1,
    // WHITELIST
    whiteListCountActive: false,
    whiteListCount      : 3,
    randomizeWhiteList  : false,
    // TRAVEL
    travelToStartCity   : false,
    travelAutomatic     : true
});
/**
 * Battlefield for FightV2 system.
 */
function Battlefieldv2() {
    var ERROR_SUCCESS = 0;
    var ERROR_BAD_RESPONSE = 1;
    var ERROR_NO_FIGHT_RESULT = 2;
    /**
     * Global variables.
     */
    var gVar = {
        StartCity          : MW.currentCity(),
        CurrentCity        : 0,
        ReadyToHeal        : 0,
        Aborted            : false,
        AutoPaused         : false,
        AttackWhitelist    : false,
        AttackFamilyList   : false,
        AttackInBattle     : false,
        AttackerUsedBoost  : false,
        ForceHeal          : false,
        SaveFromElements   : true,
        WorstItems         : new Object(),
        IcedPlayerCache    : new Object(),
        WhiteListCache     : new Object(),
        WhiteListArray     : new Array(),
        FamilyListCache    : new Object(),
        FamilyListArrays   : new Object(),
        InventoryData      : new Config('cache_loot_name', null, true),
        /** @type {CSEpicBattle} */  EpicBattle: null,
        /** @type {Object}       */  EpicBattleFortress: null
    };
    var options = UserConfig.bfopt;
    /** @type {TimerMessage}        */ var StatusTimer = new TimerMessage('#msgcontainer');
    /** @type {Countdown}           */ var TravelCountdown;
    /** @type {Countdown}           */ var ResumeCountdown;
    /** @type {Countdown}           */ var FightCountdown;
    /** @type {Countdown}           */ var RivalsCountdown;
    /** @type {CSAutoPublish}       */ var AutoPublish;
    /** @type {CSLootList}          */ var LootCache;
    /** @type {CSSessionStats}      */ var SessionStats;
    /** @type {CSAutoTravelCounter} */ var AutoTravelCounter;

    var logIcon = {
        'loot'     : global.zGraphicsURL + 'achievements/mwach_collector_75x75_01.gif',
        'fight'    : global.zGraphicsURL + 'home/icon_fight_75x75_01.gif',
        'iced'     : global.zGraphicsURL + 'home/icon_hitlist_75x75_01.gif',
        'kill'     : global.zGraphicsURL + 'home/icon_hitlist_75x75_01.gif',
        'heal'     : global.zGraphicsURL + 'red_cross_small.gif',
        'bank'     : global.zGraphicsURL + 'home/icon_loot_75x75_01.gif',
        'whitelist': global.zGraphicsURL + 'DW_feed_grn_01.png',
        'blacklist': global.zGraphicsURL + 'DW_feed_red_01.png',
        'help'     : global.zGraphicsURL + 'home/icon_call_for_help_75x75_01.gif',
        'published': global.zGraphicsURL + 'mw_iced_feed1_90x90.gif'
    };
    /**
     * Attacker Statistics.
     */
    var AttackerStats = {
        health         : 0,
        maxHealth      : 0,
        healthPct      : 0,
        stamina        : 0,
        maxStamina     : 0,
        userCash       : 0,
        atkGained      : 0,
        defGained      : 0,
        expToNextLevel : 0,
        total_ices     : 0,
        season_ices    : 0,
        season_target  : 0,
        session_ices   : 0,
        lastHealth     : 0,
        exp_fight      : 0,
        exp_fight_max  : 0,
        exp_fight_min  : 0,
        exp_per_sta    : 0,
        currCityName   : {toString: function(){
            return '<span class="good">'+global.city(gVar.CurrentCity).name+'</span>';
        }},
        powerPack      : {
            time1: 0,
            time2: 0,
            count: 0,
            on: false,
            avail: function() {
                var me = AttackerStats;
                if (!(me.powerPack.on && me.powerPack.count > 0)) {
                    return {'hospital':false,'stamina':false};
                }
                return {
                    'hospital': options.useHealPack&&(options.useHealPackWhen<me.stamina)&&(me.powerPack.time2==0),
                    'stamina' : options.useStamPack&&(options.useStamPackWhen<me.expToNextLevel)&&(me.powerPack.time1==0)
                };
            },
            toString: function() {
                var avail = AttackerStats.powerPack.avail();
                var count = AttackerStats.powerPack.count;
                count = Util.setColor(count, (count>0?'green':'red'));
                if (avail.hospital&&avail.stamina) { return 'Both ('+count+')';     }
                if (avail.hospital)                { return 'Hospital ('+count+')'; }
                if (avail.stamina)                 { return 'Stamina ('+count+')';  }
                return Util.setColor('Inactive','grey');
            }
        }
    };
    /**
     * Create a new epic battle class.
     * @constructor
     * @param {Object} data
     * @return {CSEpicBattle}
     */
    var CSEpicBattle = function(data) {
        this.inBattle      = data.inBattle;
        this.userName      = data.userName;
        this.opponentName  = data.opponentName;
        this.userScore     = data.userScore;
        this.opponentScore = data.opponentScore;
        this.timeStart     = data.timeStart;
        this.timeEnd       = data.timeEnd;
        this.started       = data.started;
        return this;
        
    }; CSEpicBattle.prototype = {
        toString: function() {
            if (this.inBattle) {
                var title = Util.setColor(this.userName,'red')+' VS '+Util.setColor(this.opponentName,'red');
                title += (this.timeEnd>5 ? ' Started!' : ' Finished!');
                return title + '<br>Score: ' + this.userScore + ' vs ' + this.opponentScore;
            } else {
                return 'Unknow Battle';
            }
        },
        update: function(data) {
            var me = this;
            Util.each(me, function(n) {
                if (data[n] && !Util.isFunc(data[n])) {
                    me[n] = data[n];
                }
            });
        },
        finished: function() {
            return this.timeEnd < 5;
        }
    };
    // POPUP
    var popupElt = new PopupObject('battlefield_popup', {
        type: 'main',
        title: 'BATTLEFIELD',
        width: 750,
        onclose: function() {
            stopAutoFight(); 
            if (gVar.SaveFromElements === true) {
                options.fromDomElements();
            }
            options.save();
            //SessionStats.save();
        }
    });
    /**
     * Create a new Fight stats.
     * @constructor
     * @return {CSFightStats}
     */
    var CSFightStats = function() {
        this.fightCount = 0;
        this.fightWon = new Object();
        this.fightLost = new Object();
        this.icesWon = new Object();
        this.killWon = new Object();
        this.icesLost = new Object();
        this.foesAttacked = new Object();
        this.revenge = new Object();
        this.cash = { won: 0, lost: 0 };
        this.expGained = 0;
        this.staminaSpend = 0;
        this.coinsGained = 0;
        return this;
    };
    /**
     * Create a new Session stats.
     * @constructor
     * @return {CSSessionStats}
     */
    var CSSessionStats = function() {
        var timestamp = String(parseInt((new Date()).getTime() / 1000));
        var statistics = new Config('bf_stats');
        var allSessions = new Object();
        var oppData = new Object();
        var Session = (allSessions[timestamp] = new Object());
        var Total = (Session[0] = {
            fightCount    : 0,
            fightWon      : 0,
            fightLost     : 0,
            icesWon       : 0,
            killWon       : 0,
            icesLost      : 0,
            foesAttacked  : 0,
            revenge       : 0,
            expGained     : 0,
            staminaSpend  : 0,
            coinsGained   : 0,
            cash: {
                won: new Object(),
                lost: new Object(),
                toString: function() {
                    var curCity = global.city(gVar.CurrentCity);
                    var me = this, span = '<span class="cur_cash" title="';
                    global.cities.each(function(id, city) {
                        if (me['won'][id]) { 
                            span += city.name+': '+ Util.formatNum(me['won'][id]) + ' \n'; 
                        }
                    });
                    span += '">';
                    span += curCity.currency + Util.formatNum(me['won'][gVar.CurrentCity]||0); 
                    span += '</span>';
                    return  span;
                }
            }
        });
        /**
         * Experience per stamina
         */
        this.expPerSta = function() {
            var n = Total.expGained / Total.staminaSpend;
            return isNaN(n) ? 0 : n.toFixed(2);
        };
        /**
         * Current session total.
         */
        this.currentTotal = Total;
        /**
         * Get a stat.
         */
        this.get = function(timestamp, city) {
            var select = Session;
            if (timestamp && allSessions[timestamp]) {
                select = allSessions[timestamp];
            }
            if (Util.isSet(city) && select[city]) {
                select = select[city];
            }
            return select;
        };
        /**
         * Add Stolen Ice.
         * @param {Object} thief
         */
        this.addStolenIce = function(thief) {
            oppData[thief.id] = thief.name;
            var oldVal = (Session[gVar.CurrentCity].icesLost[thief.id] || 0) + 1;
            Session[gVar.CurrentCity].icesLost[thief.id] = oldVal;
            Total.icesLost++;
        }
        /**
         * Add count to stat.
         * @param {String} stat_name
         * @param {Object} value
         */
        this.add = function(stat_name, value, city) {
            var city = city || gVar.CurrentCity;
            var opp = PlayerList.current;
            var statval;
            
            if (!Util.isSet(value)) { value = 1; }
            
            if (Util.isObject(Session[city])) {
                if (stat_name==='cash') {
                    if (value > -1) {
                        Total['cash']['won'][city] += value;
                        Session[city]['cash']['won'] += value;
                    } else {
                        Total['cash']['lost'][city] -= value;
                        Session[city]['cash']['lost'] -= value;
                    }
                } else {
                    if (Util.isObject(statval)) {
                        statval = Session[city][stat_name];
                        oppData[opp.id] = opp.name;
                        statval[opp.id] = (parseInt(statval[opp.id])||0) + value;
                    } else {
                        Session[city][stat_name] += value;
                    }
                    Total[stat_name] += value;
                }
            }
        };
        /**
         * Load all sessions.
         * @param {Object} callback
         */
        this.load = function(callback) {
            statistics.load(function() {
                statistics.each(function(timestamp, session) {
                    if (timestamp === 'opp') {
                        oppData = session;
                    } else {
                        allSessions[timestamp] = session;
                    }
                });
                callback && callback(allSessions, timestamp);
            });
        };
        /**
         * Save all sessions.
         * @param {Object} callback
         */
        this.save = function(callback) {
            Logger.debug('SessionStats; (fights:'+Total.fightCount+', save:'+(Total.fightCount > 9)+').');
            if (Total.fightCount < 10) {
                return;
            }
            statistics.add('opp', oppData);
            Util.each(allSessions, function(timestamp, session) {
                statistics.add(timestamp, session);
            });
            statistics.save(callback);
        };
        /**
         * Loops through all stats.
         * @param {Object} callback
         */
        this.each = function(callback) {
            Util.each(Session[gVar.CurrentCity], callback);
        };
        
        // Initialize
        global.cities.each(function(id) {
            Session[id] = new CSFightStats();
            Total.cash.won[id] = Total.cash.lost[id] = 0;
        });
        return this;
    };
    /**
     * Auto Travel class.
     * @constructor
     * @return {CSAutoTravelCounter}
     */
    var CSAutoTravelCounter = function() {
        var counter, me = this;
        var errMsg = {
            a: {
                1: 'Your health fall down very quickly.<br>',
                2: 'You lost 5 times in a row.<br>',
                3: 'You lost 5 ices in a row.<br>',
                4: 'You fought against 15 dead bodies in a row.<br>',
                5: 'Your fightlist has no valid targets for 3 times.<br>'
            },
            b: {
                f1: 'Enable "Automatic" option in Travel line to travel if this happen again.',
                f2: 'Enable "Start City" option in Travel line to travel if this happen again.',
                f3: 'You must select the cities where you want to travel.',
                ok: 'AutoTravel will force to fight in '
            }
        };
        /**
         * Increase a counter only if fighting game list.
         * @param {String} to
         */
        function increase(to) {
            if (gVar.AttackFamilyList!==true && gVar.AttackWhitelist!==true && gVar.AttackInBattle!==true) {
                counter[to]++;
            }
        }
        this.stolenIce = function() { increase('stolenIce'); return check(); };
        this.attack    = function() { increase('attack');    return check(); };
        this.lose      = function() { increase('loses');     return check(); };
        this.noTarget  = function() { increase('noTarget');  return check(); };
        this.heal      = function() { return check(true); };
        /**
         * Reset a counter.
         * @param {Object} to undefined to reset all counters.
         */
        this.reset = function(to)  { 
            if (!to || !Util.isSet(counter[to])) {
                counter = { 'stolenIce': 0,'attack': 0,'loses': 0, 'noTarget': 0 };  
            } else {
                counter[to] = 0;
            }
        };
        /**
         * Check the counters.
         * @param {Boolean} forceHealIssue
         */
        function check(forceHealIssue) {
            var message, is_able, newCity;

            switch(true) {
                case (forceHealIssue===true): message = errMsg['a'][1]; break;
                case (counter.loses     > 4): message = errMsg['a'][2]; PlayerList.clear(); break;
                case (counter.stolenIce > 4): message = errMsg['a'][3]; break;
                case (counter.attack   > 14): message = errMsg['a'][4]; PlayerList.clear(); break;
                case (counter.noTarget  > 2): message = errMsg['a'][5]; break;
                default                     : return false;
            }
            switch(false) {
                case (is_able=options.travelAutomatic)   : message += errMsg['b']['f1']; break;
                case (is_able=options.travelToStartCity) : message += errMsg['b']['f2']; break;
                case (is_able=setNewCurrentCity())       : message += errMsg['b']['f3']; break;
                default                                  : message += errMsg['b']['ok']; break;
            }
            if (is_able) {
                message += global.city(is_able).name+'.';
            }
            addLog(message, 'help', null, 'autotravel');
            me.reset();
            return false;
        }
        me.reset();
        return this;
    };
    /**
     * Create a fight_result from a first attack
     * @constructor
     * @param {Object} htmlText
     * @return {CSQueryResult}
     */
    var CSFightResult = function(htmlText) {
        htmlText = h$(htmlText);
        this.fight_wrapper         = $('#fight_wrapper', htmlText);
        this.atkbtn_req            = $('#fightv2_atkbtn_boost_on a',       htmlText).attr('requirements');
        this.poweratkbtn_req       = $('#fightv2_poweratkbtn_boost_on a',  htmlText).attr('requirements');
        this.atkbtn_boost_on       = $('#fightv2_atkbtn_boost_on a',       htmlText).attr('href');
        this.atkbtn_boost_off      = $('#fightv2_atkbtn_boost_off a',      htmlText).attr('href');
        this.poweratkbtn_boost_on  = $('#fightv2_poweratkbtn_boost_on a',  htmlText).attr('href');
        this.poweratkbtn_boost_off = $('#fightv2_poweratkbtn_boost_off a', htmlText).attr('href');
        this.opponent_icon         = $('#defender_pic img',                htmlText).attr('src');
        try { this.popup = Util.parsePopup(htmlText); } catch(err) {}
        
        var fight_result, boostAskFeed, $b
        ,   scriptText = htmlText.find('script:regex(text,FightV2.init)').text();
        
        if (scriptText) {
            fight_result = Util.substr(scriptText,'fight_result = {',';FightV2',15,0);
            boostAskFeed = Util.substr(scriptText,'var feed','MW.Feed(feed)',0,0);
        }
        if (fight_result) {
            this.fight_result = Util.parseJSON(fight_result);
        }
        if (boostAskFeed && ($b = e$('.fv2_boost_ask_allowed a', this.fight_wrapper))) {
            $b.attr('onclick', boostAskFeed+'MW.Feed(feed);');
        }
        fight_result=scriptText=boostAskFeed=htmlText=$b=null;
        return this;
    };
    /**
     * Create a new opponent class.
     * @constructor
     * @return {CSOpponent}
     */
    var CSOpponent = function() {
        this.reqs = {
            'atk':{'stamina':1,'health':20},
            'pwr':{'stamina':5,'health':20}
        };
        this.id            = 0;
        this.badge         = '';
        this.badge_url     = null;
        this.title         = '';
        this.name          = '';
        this.level         = 0;
        this.mafia         = 0;
        this.clanName      = null;
        this.clanId        = null;
        this.ice_state     = 0;
        this.iced          = false;
        this.fights        = 0;
        this.alive         = false;
        this.won           = 0;
        this.lost          = 0;
        this.isThief       = false;
        this.isRival       = false;
        this.isWList       = false;
        this.isCBttl       = false;
        this.isFList       = false;
        this.retries       = 0;
        this.attack_url    = null;
        this.pwrattack_url = null;
        this.powerAttack   = false;
        return this;
        
    }; CSOpponent.prototype = {
        toString: function() {
            var tag;
            switch(true) {
                case (this.isThief) : tag = ' "Thief" ';      break;
                case (this.isRival) : tag = ' "Rival" ';      break;
                case (this.isWList) : tag = ' "Whitelist" ';  break;
                case (this.isCBttl) : tag = ' "ClanBattle" ';  break;
                default             : tag = ' "Fightlist" ';  break;
            }
            return 'Level '+ Util.setColor(this.level,'yellow') + Util.setColor(tag,'green') + this.anchor();
        },
        /**
         * Set the stamina and health attack requirements.
         * @param {Object} atk
         * @param {Object} pwr
         */
        setReqs: function(atk, pwr) {
            if (atk && atk.stamina && atk.health) {
                this.reqs.atk = atk;
            }
            if (pwr && pwr.stamina && pwr.health) {
                this.reqs.pwr = pwr;
            }
        },
        /**
         * Skip this opponent.
         * @param {String} reason
         */
        skip: function(reason) {
            this.skipped = true;
            if (!this.result_text) {
                this.result_text = reason;
            }
        },
        /**
         * Return true if the opponent has a power attack url.
         * @return {Boolean}
         */
        hasPwrAtkUrl: function() {
            return /xw_controller=fight/i.test(this.pwrattack_url);
        },
        /**
         * Return true if the opponent has an attack url.
         * @return {Boolean}
         */
        hasAtkUrl: function() {
            return /xw_controller=fight/i.test(this.attack_url);
        },
        /**
         * Get a valid attack url.
         * @param {Boolean} bPwrAttack
         */
        getAttackUrl: function(bPwrAttack) {
            var boost = 'use_boost=' + (gVar.AttackerUsedBoost ? '1' : '0');
            if (/xw_controller=fight/i.test(this.attack_url)) {
                this.attack_url = this.attack_url.replace(/use_boost=\d+/, boost);
            }
            if (/xw_controller=fight/i.test(this.pwrattack_url)) {
                this.pwrattack_url = this.pwrattack_url.replace(/use_boost=\d+/, boost);
            }
            if((this.powerAttack || bPwrAttack) && this.pwrattack_url) {
                return this.pwrattack_url;
            } else {
                this.powerAttack = false;
            }
            return this.attack_url;
        },
        /**
         * Get the stamina and health requirements.
         */
        requirements: function() {
            if (this.powerAttack && this.pwrattack_url) {
                return this.reqs.pwr;
            } else {
                return this.reqs.atk;
            }
        },
        /**
         * @return {Number}
         */
        attacksToIce: function() {    
            if (this.fights > 1 && this.startedHealthPct && this.curHealthPct) {
                var pct_deal = this.startedHealthPct - this.curHealthPct;
                return Math.ceil(this.curHealthPct / (pct_deal / this.fights));
            }
            return -1;
        },
        /**
         * Return anchor for name
         */
        anchor: function() {
            return [this.clan(), Util.setAnchor(MW.getProfileLink(this.id),this.name)].join(' ');
        },
        /**
         * Return profile url
         */
        profile: function() {
            return MW.getProfileLink(this.id);
        },
        /**
         * Return anchor for clan name
         */
        clan: function() {
            if (this.clanId && this.clanName) {
                return Util.setAnchor(MW.getFamilyLink(this.clanId),Util.setColor(this.clanName,'red'));
            }
            else if (this.clanName) {
                return Util.setColor(this.clanName,'red');
            }
            return '';
        },
        /**
         * Return true if player is listed in specified list_name
         */
        isListed: function(name) {
            return PlayerList[name].exists(this.id);
        },
        /**
         * @return {CSOpponent}
         */
        clone: function() {
            var opp = new CSOpponent();
            for (m in opp) {
                if (typeof(opp[m]) !== 'function')
                    opp[m] = this[m];
            }
            return opp;
        },
        /**
         * Return true is opponent is valid (not filtered/skipped)
         * @return {Boolean}
         */
        isValid: function() {
            var bInRanges = true;
            function isInLevelRange(level) {
                if (options.levelRangeActive !== true) {
                    return (options.compareRangeMethod==='AND');
                }
                if (level >= options.levelRangeMin && level <= options.levelRangeMax) {
                    return (options.levelRangeMethod === 'attk');
                } else {
                    return (options.levelRangeMethod !== 'attk');
                }
            }
            function isInMafiaRange(mafia) {
                if (options.mafiaRangeActive !== true) {
                    return (options.compareRangeMethod==='AND');
                }
                if (mafia >= options.mafiaRangeMin && mafia <= options.mafiaRangeMax) {
                    return (options.mafiaRangeMethod === 'attk');
                } else {
                    return (options.mafiaRangeMethod !== 'attk');
                }
            }
            function isFiltered(expr, str) {
                if (!Util.isString(expr) || expr.length < 1) {
                    return false;
                }
                return (new RegExp(expr, 'i')).test(str);
            }
            if (parseInt(this.id)===98025069||options.skipIced===true&&this.iced===true) {
                return false;
            }
            if (PlayerList.blackList.exists(this.id)) {
                //addEventLog(this, 'blacklisted.');
                Logger.debug('Skipped blacklisted id: ' + this.id);
                return false;
            }
            if (this.isCBttl === true) {
                return true;
            }
            if (PlayerList.whiteList.exists(this.id)) {
                // whitelist users are always valid opponents.
                return true;
            }
            if (this.isThief === true && options.attackRevengeFilter == 0) {
                // revenge not filtered
                return true;
            }
            if (this.clanId && PlayerList.frdClanList.exists(this.clanId)) {
                //addEventLog(this, 'friendly clan: '+this.clanName);
                Logger.debug('skipping friendly clan: ' + this.clanName);
                return false;
            }
            if (options.badgeFilterActive === true ) {
                if (isFiltered(options.badgeFilterExpr, this.badge)) {
                    //addEventLog(this, 'Badge filtered: '+this.badge);
                    Logger.debug('filtered badge: (' + this.badge +') '+ this.name);
                    return false;
                }
            }
            if (options.nameFilterActive === true) {
                if (isFiltered(options.nameFilterExpr, this.clanName + ' ' + this.name)) {
                    //addEventLog(this, 'Name filtered.');
                    Logger.debug('filtered name: ' + this.clanName + ' ' + this.name);
                    return false;
                }
            }
            if (this.isThief !== true && options.skipIcedByMe && Util.isSet(gVar.IcedPlayerCache[this.id])) {
                //addEventLog(this, 'already iced by you.');
                Logger.debug('skipping "'+this.name+'" because was iced by you.');
                return false;
            }
            if (this.isThief === true && options.attackRevengeFilter == 1) {
                // revenge not filtered by level
                return true;
            }
            if (options.levelRangeActive||options.mafiaRangeActive) {
                if (options.compareRangeMethod==='AND') {
                    bInRanges = (isInLevelRange(this.level) && isInMafiaRange(this.mafia));
                } else {
                    bInRanges = (isInLevelRange(this.level) || isInMafiaRange(this.mafia));
                }
            }
            if (!bInRanges) {
                Logger.debug('Out level/mafia range. level:' + this.level + ', mafia:' + this.mafia);
                return false;
            }
            return true;
        }
    };
    /**
     * Create an ICE Stolen popup class
     * @param {String} popup_html
     * @return {CSStolenIce}
     */
    var CSStolenIce = function(fight_data) {
        this.id       = Util.parseNum(fight_data.thief_id);
        this.name     = Util.trim($('<div>'+fight_data.thief_name+'</div>').text());
        this.level    = Util.parseNum(fight_data.thief_class);
        this.inClan   = fight_data.thief_in_clan;
        this.inMafia  = fight_data.thief_isInMafia;
        this.tyClass  = fight_data.thief_class;
        try {
            this.action   = $(fight_data.thief_btn).attr('href');
            this.pic      = $(fight_data.thief_pic).attr('src');
        } catch (e) { }
        fight_data = null;
        return this;
        
    };  CSStolenIce.prototype.anchor = function() {
        if (this.id && this.name) {
            return Util.setAnchor(MW.getProfileLink(this.id), this.name);
        }
    };
    /**
     * Create a Loot class
     * @param {String} item_html
     * @return {CSItemCard}
     */
    var CSItemCard = function(item_html) {
        var $itm      = c$('div').html(item_html);
        this.title    = $itm.find('#fake_item_card_title').text();
        this.pic      = $itm.find('#fake_item_card_img img').attr('src');
        this.quantity = $itm.find('#fake_item_card_qty, #fake_item_card_subtitle').text();
        if ( /victory/.test(this.pic) ) {
            this.type = 'coins';
        }
        $itm.empty();
        item_html=$itm=null;
        return this;
    };

    /**
     * Create a Loot class
     * @param {String} item_html
     * @return {CSItemLoot}
     */
    var CSItemLoot = function(item_html, count) {
        var loot = gVar.InventoryData.data;
        var $itm = c$('div').html(item_html);
        this.name     = 'Unknow';
        this.type     = 'Unknow'; 
        this.item_id  = $itm.find('.item_card_mini').attr('item_id');
        this.pic      = $itm.find('.item_card_mini img').attr('src');
        this.attack   = Util.parseNum($itm.find('.attack').text());
        this.defense  = Util.parseNum($itm.find('.defense').text());
        this.count    = parseInt(count);
        if (Util.isSet(loot = loot[this.item_id])) {
            this.name = loot.name;
            this.type = loot.type;
        }
        $itm.empty();
        item_html=$itm=loot=null;
        return this;
        
    }; CSItemLoot.prototype.toString = function() {
        var longName = this.name;
        if (this.attack) {
            longName += ' <span class="attack">'+this.attack+'</span>';
        }
        if (this.defense) {
            longName += ' <span class="defense">'+this.defense+'</span>';
        }
        if (this.increaseAttack) {
            longName += ' (<span class="attack" style="color:green;">+'+this.increaseAttack+'</span>)';
        }
        if (this.increaseDefense) {
            longName += ' (<span class="defense" style="color:green;">+'+this.increaseDefense+'</span>)';
        }
        return longName;
    };

    /**
     * Create an Ice Event Loot class
     * @param {String} item_html
     * @return {CSItemLoot}
     */
    var CSItemIceEvent = function(item_html) {
        var $itm      = c$('div').html(item_html);
        this.title    = $itm.find('.ice > div > div').text();
        this.pic      = $itm.find('.ice img').attr('src');
        this.count    = Util.parseNum(this.title);
        this.event    = true;
        this.item_id  = String(this.title).toLowerCase().replace(/\s/g,'');
        if ( this.count === 0 ) {
            this.count = 1;
        }
        $itm.empty();
        item_html=$itm=null;
        return this;
    };

    /**
     * Create an ICE popup class
     * @param {Object} fight_result
     * @param {Boolean} isKilled
     * @return {CSIce}
     */
    var CSIce = function(fight_result) {
        this.isKilled       = fight_result.you_just_killed;  
        this.count          = fight_result.total_ice_count;
        this.season_ices    = fight_result.ices_so_far; 
        this.season_target  = fight_result.ices_target; 
        this.action         = '';
        this.canPublish     = false;
        this.link           = '';
        if (fight_result.feed_js && /MW.Feed/i.test(fight_result.feed_js)) {
            this.canPublish = true;
            this.action  = '<a href="javascript: void(0);"'
            + 'class="sexy_button_new medium white sexy_announce_gray" '
            + 'onclick="'+fight_result.feed_js+'"><span><span>Share</span></span></a>';
            var feed = Util.substr(fight_result.feed_js, 'var feed = {', 'MW.Feed');
            try {
                eval ( feed );
                this.link = feed.link;
                this.description = feed.description;
            }
            catch(err) {
                Logger.error(err);
            }
        }
        fight_result=null;
        return this;
    };

    var CSAutoPublish = function() {
        var publish = new Object();
        /**
         * @param {CSIce} popup
         * @param {String} oppName
         */
        this.add = function(ice, oppName) {
            if ( !ice ) {
                return;
            }
            var date = '['+ (new Date()).toLocaleTimeString() +']';
            if ( ice.action && ice.canPublish === true ) {
                publish[date] = (ice.isKilled ? ' KILL ' : ' ICED ') + '#' + ice.count + ' to ' + oppName;
            }
        };
        this.length = function() {
            return Util.length(publish);
        };
        /**
         * @param {String} target_id
         * @param {Function} callback
         */
        this.publishTo = function(target_id, callback) {
            var success_msg = 'You\'ve ${count} ICES.<br>Auto-Publish has posts all and reset the count.';
            var error_msg = 'Error code #${errorcode}<br>${errormsg}';
            var properties = publish;
            
            publish = new Object();
            
            facebook.streamPublish({
                'target'      : target_id,
                'name'        : '{*actor*} has eliminated some players!',
                'properties'  : properties
            }, function(post_id) {
                if (Util.isString(post_id)) {
                    success_msg = Util.render(success_msg, {
                        'count': Util.length(properties)
                    });
                    addLog(success_msg, 'published', null, 'autopublish');
                }
                else if (post_id.error_code || post_id.error) {
                    error_msg = Util.render(error_msg, {
                        'errorcode' : (post_id.error?post_id.error.code:post_id.error_code),
                        'errormsg'  : (post_id.error?post_id.error.message:post_id.error_msg)
                    });
                    addLog(error_msg, 'published', null, 'autopublish');
                }
                callback && callback();
            });
        };
        /**
         * Clear stored ices
         */
        this.clear = function() {
            publish = new Array();
        };
        return this;
    };
    /**
     * Create a new loot list.
     * @return {CSLootList}
     */
    var CSLootList = function() {
        var me = this;
        var cache = new Object();
        var lootTypes = new Object();
        /**
         * Estimate stats increased by a loot and return true if item is cached.
         * @param {CSItemLoot} loot
         * @param {Number} count
         * @return {Boolean}
         */
        function estimateStatsIncrease(loot, count) {
            var best = gVar.InventoryData.data[loot.item_id];
            var worst = (best&&Util.isObject(gVar.WorstItems)) ? gVar.WorstItems[best.type] : false;
            if (best && worst) {
                if (best.attack && best.attack > worst.att) {
                    AttackerStats.atkGained += (loot.increaseAttack = (best.attack - worst.att))*count;
                }
                if (best.defense && best.defense > worst.def) {
                    AttackerStats.defGained += (loot.increaseDefense = (best.defense - worst.def))*count;
                    
                }
            }
            return Util.isSet(best);
        }
        /**
         * Get a singular loot data.
         * @param {CSItemLoot} loot
         */
        function loadLoot(loot, callback) {
            Logger.debug('getItemInfo: Loading loot...');
            MW.getItemInfo(loot.item_id, function(name, type) {
                loot.name = name;
                if (lootTypes && lootTypes[type]) {
                    loot.type = lootTypes[type];
                }
                if (!Util.isSet(gVar.InventoryData.data[loot.item_id])) {
                    gVar.InventoryData.add('forceReload', true);
                    gVar.InventoryData.data[loot.item_id] = {
                        id: loot.item_id,
                        name: name,
                        type: lootTypes[type],
                        attack: loot.attack,
                        defense: loot.defense
                    };
                    gVar.InventoryData.save(callback);
                }
                Logger.debug('Loot Item "'+name+'" loaded.');
            });
        }
        /**
         * Load the loot name data.
         * @param {Object} callback
         */
        this.load = function(callback) {
            var fields = ['id','name','type','attack','defense'];
            gVar.InventoryData.load(function() {
               if (!gVar.InventoryData.data || gVar.InventoryData.forceReload) {
                   gVar.InventoryData.data = new Object();
                   // Load Inventory
                   MW.getInventoryData(function(data) {
                       if (data && data.Items) {
                           Util.each(data.Items.data, function(id, data) {
                               var cdata = (gVar.InventoryData.data[id] = new Object());
                               Util.each(fields, function(index, field_name) {
                                   cdata[field_name] = data[field_name];
                               });
                           });
                           gVar.InventoryData.forceReload = false;
                           gVar.InventoryData.save(function(){
                               Logger.debug('Inventory Data saved.');
                               callback&&callback(true);
                           });
                       } else {
                           callback&&callback(false);
                       }
                   });
               } else {
                   callback&&callback(true);
               }
            });
        };
        /**
         * @param {CSItemLoot} loot
         */
        this.add = function(loot) {
            var count = loot.count||1;
            if (Util.isSet(cache[loot.item_id])) {
                (loot = cache[loot.item_id]).count += count;
            } else {
                cache[loot.item_id] = loot;
            }
            if (!estimateStatsIncrease(loot, count)) {
                loadLoot(loot, function() {
                    estimateStatsIncrease(loot, count);
                });
            }
            return loot;
        };
        /**
         * @param {Object} item_id
         * @return {CSItemLoot}
         */
        this.get = function(item_id) {
            return cache[item_id];
        };
        /**
         * @param {Object} item_id
         * @return {Boolean}
         */
        this.exists = function(item_id) {
            return Util.isSet(cache[item_id]);
        };
        /**
         * Loops through all loot items.
         * @param {Function} callback
         */
        this.each = function(callback, sortedby) {
            var obj = cache;
            var by = {
                'name': function(a, b) {
                    var x = a.name.toLowerCase();
                    var y = b.name.toLowerCase();
                    return ((x < y) ? -1 : ((x > y) ? 1 : 0));
                },
                'attack': function(a, b) {
                    return (b.attack||0) - (a.attack||0);
                },
                'defense': function(a, b) {
                    return (b.defense||0) - (a.defense||0);
                },
                'best': function(a, b) {
                    var a1 = a.attack  || 0,
                        d1 = a.defense || 0,
                        a2 = b.attack  || 0,
                        d2 = b.defense || 0,
                        t1 = (a.increaseAttack||0)+(a.increaseDefense||0),
                        e1 = (b.increaseAttack||0)+(b.increaseDefense||0);
                    switch(true) {
                        case (t1 != e1): return e1 - t1;
                        case (a1 != a2): return a2 - a1;
                        case (d1 != d2): return d2 - d1;
                        default:         return 0;
                    }
                }
            };
            if (Util.isSet(sortedby) && Util.isSet(by[sortedby])) {
                obj = new Array();
                Util.each(cache, function(i,o){obj.push(o);});
                obj.sort(by[sortedby]);
            }
            Util.each(obj, callback);
        };
        
        if (unsafeWindow.Item && unsafeWindow.Item.Types) {
            lootTypes = new Object();
            Util.each(unsafeWindow.Item.Types, function(i,n) {
                lootTypes[n] = i; 
            });
        }
        return this;
    };

    /**
     * Create a new list
     * @param {String} list_id
     * @return {CSList}
     */
    var CSList = function(list_id) {
        this.id = list_id;
        return this;
        
    }; CSList.prototype = {
        /**
         * Add opponent to a list
         * @param {Object} id
         * @param {CSOpponent} opp
         */
        add: function(opp) {
            var now = (new Date()).toDateString();
            if ( !Util.isSet(opp.id) || opp.id === 0 ) {
                return;
            }
            if ( opp.level ) {
                options.get(this.id)[opp.id] = (opp.name + '  level ' + opp.level + ' at '+now);
            }
            else {
                options.get(this.id)[opp.id] = (opp.name + ' at '+now);
            }
            options.save();
        },
        /**
         * Return true if opponent exists
         * @param {Object} id
         * @return {Boolean}
         */
        exists: function(id) {
            return Util.isSet(id) && Util.isString(options.get(this.id)[id]);
        },
        /**
         * Loops through all entries.
         * @param {Function} callback
         */
        each: function(callback) {
            Util.each(options.get(this.id), callback);
        },
        /**
         * Remove an entry.
         * @param {Object} id
         */
        remove: function(id) {
            if (Util.isSet(id) && options.get(this.id)) {
               delete options.get(this.id)[id]; 
            }
        },
        /**
         * @return {Number}
         */
        length: function() {
            return Util.length(options.get(this.id));
        },
        /**
         * @return {Array}
         */
        toArray: function() {
            var list = options.get(this.id);
            var new_array = [];
            if (Util.length(list) > 0) {
                Util.each(list, function(id, name) {
                    if (parseInt(id) && Util.isString(name));
                        new_array.push({'id':id , 'name':name});
                });
            }
            return new_array;
        }
    };

    var CSPlayerList = function() {
        var me = this;
        var players = new Array();

        /** @type {CSOpponent} */
        this.current = null;
        /** @type {CSOpponent} */
        this.revenge  = null;
        /** @type {CSList} */
        this.blackList = null;
        /** @type {CSList} */
        this.whiteList = null;
        /** @type {CSList} */
        this.frdClanList = null;
        /** @type {CSList} */
        this.enmClanList = null;
        /**
         * @return {Boolean}
         */
        this.addCurrentToBlackList = function() {
            var bNew = !me.blackList.exists(me.current.id);
            me.blackList.add(me.current);
            return bNew;
        };
        /**
         * @return {Boolean}
         */
        this.addCurrentToWhiteList = function() {
            var bNew = !me.whiteList.exists(me.current.id);
            me.whiteList.add(me.current);
            return bNew;
        };
        /**
         * Set the current opponent
         * @param {Number} id
         * @return {CSOpponent}
         */
        this.setCurrent = function(id) {
            me.current = null;
            if ( me.revenge ) {
                me.current = me.revenge;
                me.revenge = null;
                return me.current;
            }
            if (isNaN(id = parseInt(id)) || players.length < 1) {
                return null;
            }
            if (id > 0) {
                return (me.current = players.splice(id, 1).shift());
            } 
            else if (id < 0) {
                return (me.current = players.pop());
            } 
            else {
                return (me.current = players.shift());
            }
        };
        /**
         * @return {CSOpponent}
         */
        this.setRandomCurrent = function() {
            return me.setCurrent(Math.floor(Math.random() * players.length));
        };
        /**
         * @return {CSOpponent}
         */
        this.setLastCurrent = function() {
            return me.setCurrent(-1);
        };
        /**
         * Add a new opponent to list
         * @param {CSOpponent} opponent
         */
        this.add = function(opp) {
            if (!me.exists(opp.id)) {
                players.push(opp);
            }
        };
        /**
         * Add a set of opponents from a list
         * @param {Object} list
         */
        this.addFrom = function(list) {
            var success = false;
            Util.each(list, function(i, opp) {
                if (!me.blackList.exists(opp.id)) {
                    players.push(opp);
                    success = true;
                }
            });
            return success;
        };
        /**
         * @return {Boolean}
         */
        this.exists = function(id) {
            var is_added = false;
            Util.each(players, function(i, p) {
                return !(is_added = (p.id == id));
            });
            return is_added;
        }
        /**
         * @return {Number}
         */
        this.length = function() {
            return players.length;
        };
        /**
         * Clear the specified players.
         * @param {String} criteria
         */
        this.clear = function(criteria) {
            if (Util.isSet(criteria)) {
                var new_array = new Array();
                Util.each(players, function(i, opp) {
                    if (opp[criteria] !== true) {
                        new_array.push(opp);
                    }
                });
                players = new_array;
            } else {
                players = new Array();
            }
        };
        /**
         * Sort all players
         * @param {String} by 'name', 'level', 'mafia' or 'iced'.
         */
        this.sort = function(by) {
            var sortBy = {
                'name': function(a, b) {
                    var x = a.name.toLowerCase();
                    var y = b.name.toLowerCase();
                    return ((x < y) ? -1 : ((x > y) ? 1 : 0));
                },
                'level': function(a, b) {
                    return b.level - a.level;
                },
                'mafia': function(a, b) {
                    return b.mafia - a.mafia;
                },
                'iced': function(a, b) {
                    return a.iced ? -1 : 1 ;
                }
            };
            if ( players.length > 0 ) {
                players.sort(sortBy[by]);
            }
        };
        /**
         * Loops through all players.
         * @param {Object} callback
         */
        this.each = function(callback) {
            if (typeof callback !== 'function')
                return;
            for (var i = players.length - 1; i >= 0; i--) {
                callback.apply(players[i], [i, players[i]]);
            }
        };
        /**
         * Parse and load opponents from fightlist or rivals list.
         * @param {Object} htmlText
         */
        this.load = function(htmlText) {
            var count = 0;
            var $html = h$(htmlText);
            var query = options.skipIced 
            ? '.fight_table tr:has(td.fight_list_player_alive)'
            : '.fight_table tr:has(td[class^=fight_list_player])';
            options.refreshMax = Math.min(Math.max(options.refreshMax, 6), 20);
            // fighters table
            $html.find(query).each(function(index, element) {
                var $el, opp;
                try {
                    if ( ($el = e$('a:regex(href,action=attack)', element)) ) {
                        opp = new CSOpponent();
                        opp.attack_url = $el.attr('href');
                        opp.id = Util.parseNum(Util.uSplit(opp.attack_url).opponent_id);
                        opp.setReqs(Util.parseJSON($el.attr('requirements')));
                    }
                    else {
                        return;
                    }
                    if (!Util.isSet(opp.id) || opp.id === 0 
                    ||   me.exists(opp.id)  || e$('td[id^=npc_]',element) !== null) {
                        return;
                    }
                    if ( ($el = e$('td:eq(0) a:regex(href,controller=clan)', element)) ) {
                        opp.clanId   = Base64.decode(Util.uSplit($el.attr('href')).id);
                        opp.clanName = Util.trim($el.text());
                    }
                    opp.isRival   = (e$('a:regex(href,rivals_remove|rivals_add)', element) !== null);
                    opp.badge     = $('.fight_list_badge_area img', element).attr('title');
                    opp.badge_url = $('.fight_list_badge_area img', element).attr('src');
                    opp.title     = Util.trim($('td:eq(0) span:first', element).text());
                    opp.name      = Util.trim($('td:eq(0) a:regex(href,controller=stats)', element).text());
                    opp.level     = Util.parseNum($('td:eq(0) *[class^=fight_list_level]', element).text());
                    opp.iced      = ($('img:regex(src,iced)', element).length > 0);
                    opp.mafia     = Util.parseNum($('td:eq(1)', element).text());
                    
                    if (opp.isValid()) {
                       opp.isFList = true;
                       players.push(opp);
                       count++;
                    }
                    return (options.refreshOn !== true || count < options.refreshMax);
                }
                catch(err) {
                    Logger.error('PlayerList.load: '+err.message);
                }
                $el=opp=null;
            });
            htmlText=$html=null;
            Logger.debug('Added '+count+' opponents.');
            return count;
        };
        
        this.loadBattle = function(htmlText) {
            var count = 0;
            var $html = h$(htmlText);
            if ($html.find('#battle_target_list .fight_entry .fr a:regex(href,action=attack)').length < 1) {
                var $btn = $('#their_wall a:regex(href,xw_action=attackFortress)', $html);
                gVar.EpicBattleFortress = {
                    url: $btn.attr('href'),
                    req: Util.parseJSON($btn.attr('requirements')).stamina
                }
                return 0;
            } else {
                gVar.EpicBattleFortress = null;
            }
            $html.find('#battle_target_list .fight_entry').each(function(i, e) {
                var opp, $btn = e$('.fr a:regex(href,action=attack)', e);
                if ($btn == null) {
                    return;
                }
                opp = new CSOpponent();
                opp.attack_url = $btn.attr('href');
                opp.id = Util.parseNum(Util.uSplit(opp.attack_url).opponent_id);
                opp.setReqs(Util.parseJSON($btn.attr('requirements')));
                
                if (!Util.isSet(opp.id) || opp.id === 0 || me.exists(opp.id)) {
                    return;
                }
                if ( ($btn = e$('.fl a:regex(href,controller=clan)', e)) ) {
                    opp.clanId   = Base64.decode(Util.uSplit($btn.attr('href')).id);
                    opp.clanName = Util.trim($btn.text());
                }
                opp.isCBttl = true;
                opp.iced    = false;
                opp.name    = Util.trim($('.fl a:regex(href,controller=stats)', e).text());
                (function(text) {
                    var a = Util.doRgx(/Level:\s*(\d+)\s*(\w*)/i, text);
                    opp.level = Util.parseNum(a.$1);
                    opp.mafia = Util.parseNum(Util.doRgx(/Mafia: (\d+)/i, text).$1);
                    opp.title = (a.$2||'');
                })($('.fl', e).text());
                console.log(opp);
                if (opp.isValid()) {
                   players.push(opp);
                   count++;
                }
                $btn=opp=user=null;
            });
            htmlText=$html=null;
            addLog(gVar.EpicBattle+'<br>Found '+count+' opponents.','help',null,'epicbattle');
            return count;
        };
        return this;
    };

    var PlayerList = new CSPlayerList();

    // EVENTS
    var Events = {
        hardLevelRange_click: function() {
            setHardLevelRange();
            return false;
        },
        editFilter: function() {
            var name = $(this).attr('for');
            var elt = $('#'+name, popupElt.content);
            var valStr = elt.val();
            var message = 'Add values delimited by lines:';
            var clPattern = /([\^\$\.\+\?\*\{\}\(\)\\\/\|\[\]])/g;
            var unPattern = /\\([\^\$\.\+\?\*\{\}\(\)\\\/\|\[\]])/g;
            
            if (Util.isString(valStr) && valStr.length > 0) {
                valStr = valStr.replace(/([^\\])\|/g, '$1\n').replace(unPattern, '$1');
            } else {
                valStr = '';
            }
            
            showPromptPopup(message, valStr, function(text) {
                var result = new Array();
                
                if (Util.isString(text) && text.length > 0) {
                    if (clPattern.test(text)) {
                        text = text.replace(clPattern, '\\$1');
                    }
                    Util.each(text.split(/\n/), function(i,line) {
                        if (Util.isString(line) && (line=Util.trim(line)).length > 0) {
                            result.push(line);
                        }
                    });
                }
                if (result.length > 0) {
                    elt.val(result.join('|'));
                } else {
                    elt.val('');
                }
            }, 250);
            return false;
        },
        sortloot_click: function() {
            var by = $(this).attr('href').substring(1);
            $('#lootlistlog', popupElt.content).empty();
            LootCache.each(function(id, loot) {
                addLootLog(loot, true);
            }, by);
            return false;
        },
        revenge_click: function() {
            gVar.Aborted = false;
            if (PlayerList.revenge) {
                PlayerList.current = PlayerList.revenge;
                updateNewOpponent();
                manualAttack();
            }
            return false;
        },
        use_boost_click: function() {
            var state = (this.id === 'fightv2_boost_on') ? 'on' : 'off';
            httpAjaxRequest({
                url: 'remote/'+MW.getIntURL('fight','setBoostToggle')+'&toggle_state='+state,
                success: function() {
                    $('#fightv2_boost_on, #fightv2_boost_off', popupElt.content).removeClass('checked');
                    $('#fightv2_boost_'+state, popupElt.content).addClass('checked');
                    gVar.AttackerUsedBoost = (state === 'on');
                }
            });
            return false;
        },
        showLog_click: function() {
            var name = String($(this).attr('href')).substring(1);
            $('#events_list .buttons a').removeClass('selected');
            $(this).addClass('selected');
            showDiv(name, '_logs');
            return false;
        },
        addNewToList_click: function() {
            var list_name = $(this).attr('name');
            var message = 'Paste all MW profiles OR IDs by line delimited:';
            showPromptPopup(message,'',function(text) {
                addMWProfiles(list_name, text); 
            },250);
            return false;
        },
        deleteSelected_click: function() {
            var name = $(this).attr('name').toLowerCase();
            $('option:selected', '#bfopt_'+name).remove();
            options.fromDomElements();
            options.save();
            return false;
        },
        clearList_click: function() {
            var name = $(this).attr('name');
            $('#bfopt_'+name.toLowerCase()).empty();
            options.set(name, {});
            options.save();
            return false;
        },
        mwProfile_click: function() {
            var name = $(this).attr('name').toLowerCase();
            var selectedElts = $('#bfopt_'+name).find('option:selected');

            if (selectedElts.length === 0) {
                showHelpPopup({
                    icon: 'info',
                    title: 'No opponents selected.',
                    message: 'You need to select at least one opponent for go to his profile page.'
                });
                return;
            }

            if (selectedElts.length < 2 || confirm('Are you sure to open ALL selected users?')) {
                selectedElts.each(function(index, elem) {
                    if ( /clanlist/i.test(name) ) {
                        unsafeWindow.open( MW.getFamilyLink(elem.value) );
                    } else {
                        unsafeWindow.open( MW.getProfileLink(elem.value) );
                    }
                });
            }
            return false;
        },
        addFromList_click: function() {
            var name = $(this).attr('name');
            $('#import_settings').remove();
            c$('input:file','id:import_settings,name:files[],lst:'+name).css({
                'position':'absolute',
                'width':1,
                'left':'-100px',
                'opacity':0
            })
            .appendTo(popupElt.content).change(Events.importData_change).click();
            return false;
        },
        importData_change: function(e) {
            var list = options.get($(this).attr('lst'));
            if (!e.target.files || !list) {
                return;
            }
            FileSystem.fileReader(e.target.files, function(data) {
                try {
                    Util.each(Util.parseJSON(data), function(name, value) {
                        if (parseInt(name))
                            list[name] = value;
                    });
                    options.toDomElements();
                    options.save();
                    showHelpPopup({
                        icon:'info',
                        title:'Import List',
                        message:'List imported successfully.'
                    });
                }
                catch(err) {
                    Logger.error(err);
                }
                finally {
                    $(e.target).remove();
                }
            });
        },
        getList_click: function() {
            var name = $(this).attr('name');
            var sOutput = Util.toJSON(options.get(name));
            if (sOutput.length < 5) {
                return false;
            }
            FileSystem.fileSave('data:application/json;base64,'+Base64.encode(sOutput));
            return true;
        },
        sort_click: function() {
            PlayerList.sort(this.id.match(/sort_by_(\w+)/)[1]);
            genEnemyListDom();
            return false;
        },
        refresh_click: function() {
            $('#opponents_table', popupElt.content).empty();
            $('#ctrlcontainer', popupElt.content).hide();
            sendMessage();
            showFightRewards();
            clearAllTimers();
            options.fromDomElements();
            updateStats();
            setTimeout(function() {
                PlayerList.clear();
                $('#opponents_table').empty();
                refreshPlayerList(function(result) {
                    if (result > 0) {
                        genEnemyListDom();
                    }
                    else {
                        sendMessage('No players found. Try change filters and click "Refresh".');
                    }
                    $('#ctrlcontainer', popupElt.content).show();
                });
            }, 500);
            return false;
        },
        attack_click: function() {
            gVar.Aborted = false;
            options.fromDomElements();
            options.save();
            var id = this.id.match(/attack_id_(\d+)/)[1];
            PlayerList.setCurrent(id);
            if (options.travelToStartCity) {
                gVar.StartCity = options.startCity;
            }
            $('#fight_wrapper', popupElt.content).empty();
            toFightScreen();
            reqSurvey(function() {
                gVar.SaveFromElements = false;
                manualAttack();
            });
            return false;
        },
        powerAttack_click: function() {
            PlayerList.current.powerAttack = true;
            manualAttack();
            return false;
        },
        attackAgain_click: function() {
            manualAttack();
            return false;
        },
        runAway_click: function() {
            stopAutoFight();
            gVar.AttackWhitelist = false;
            gVar.AttackFamilyList = false;
            options.toDomElements();
            options.save();
            gVar.SaveFromElements = true;
            if (PlayerList.length() > 0) genEnemyListDom();
            toStartScreen();
            return false;
        },
        autoMode_click: function() {
            httpAjaxStopRequests();
            gVar.Aborted = false;
            options.fromDomElements();
            options.save();
            toFightScreen();
            if (options.travelToStartCity && gVar.AttackInBattle !== true) {
               gVar.StartCity = options.startCity; 
            } 
            AutoTravelCounter.reset();
            gVar.SaveFromElements = false;
            options.refreshEvery = Math.min(Math.max(options.refreshEvery, 5), 15);
            if (options.timerStartActive === true) {
                addAutoControls(true);
                addFightResumeCountdown(options.timerStartMins);
            } else {
                resetAllTimers()
                reqSurvey(AttackNewOpponent);
                if (gVar.AttackInBattle === true) {
                    addAutoControls(true);
                }
            }
            return false;
        },
        attackWhiteList_click: function() {
            gVar.AttackWhitelist = true;
            gVar.AttackFamilyList = false;
            gVar.Aborted = false;
            PlayerList.whiteList.attack_count = 1;
            gVar.WhiteListArray = PlayerList.whiteList.toArray();
            gVar.WhiteListCache = new Object();
            Events.autoMode_click();
            return false;
        },
        attackFamily_click: function() {
            gVar.AttackWhitelist = false;
            gVar.AttackFamilyList = true;
            gVar.Aborted = false;
            toFightScreen();
            getFamilyOpponents(Events.autoMode_click);
            return false;
        },
        stop_click: function() {
            $(this).remove();
            stopAutoFight('User stopped.');
            setTimeout(function() {
                sendMessage('AutoFight was stopped.');
                addAutoControls(true);
            }, 500);
            return false;
        },
        skip_click: function() {
            $(this).remove();
            PlayerList.current.skip('User skip manually.');
            return false;
        },
        autoHeal_click: function() {
            var instant = $(this).attr('instantheal');
            $(this).remove();
            gVar.ForceHeal = true;
            if ( parseInt(instant) === 1 ) {
                clearAllTimers();
                healPlayer(function() {
                    resetAllTimers();
                    preAttack();
                });
            }
            return false;
        },
        heal_click: function() {
            hideFightControls();
            healPlayer(addManualControls);
            return false;
        }
    };

    // GENERATE ALL MAIN DOM ELEMENTS
    function genMainDom() {

        popupElt.content.css('margin',0);

        var battle_div = c$('div', 'id:fv2_widget_wrapper').appendTo(popupElt.content).css({
            'height': 350,
            'max-height': 350
        });
        c$('div', 'id:fight_wrapper').appendTo(battle_div);
        
        var tabs = new TabObject({
            id: 'fightOpt',
            height: 310,
            background: 'transparent',
            appendTo: c$('div', 'options_wrapper').appendTo(battle_div),
            tabs: ['Config Page 1', 'Config Page 2', 'BlackList', 'WhiteList', 'FriendClanList', 'EnemyClanList']
        });

        // ---------------
        // CONFIG PAGE 1
        // ---------------
        var divListElt = c$('ul').appendTo(tabs.getLayout(0));

        // START CITY, TRAVEL
        c$('li').appendTo(divListElt)
        .append(x$('bfopt_traveltostartcity', 'Start City:'))
        .append(s$('bfopt_startcity', 100))
        .append(c$('span').text('Travel To:'))
        .append(DropDownObject('bfopt_travelto', 'Select cities', global.cityNames))
        .append(x$('bfopt_timercityactive', 'After:'))
        .append(n$('bfopt_timercitymins', 40))
        .append(c$('span').text('min.'))
        .append(x$('bfopt_travelautomatic', 'Automatic.'));

        // HEAL
        c$('li').appendTo(divListElt)
        .append(x$('bfopt_healactive', 'Heal in'))
        .append(s$('bfopt_healcity', 100))
        .append(n$('bfopt_healbelow','Health below', 40))
        .append(n$('bfopt_healminsta', 'Stamina above', 40))
        .append(x$('bfopt_healattacking', 'Attacking. '))
        .append(n$('bfopt_healtimer', 'Timer:', 40))
        .append(c$('span').text('sec.'));

        // RAPID FIRE!
        c$('li').appendTo(divListElt)
        .append(x$('bfopt_rapidfireactive', 'Rapid Fire! If health:'))
        .append(s$('bfopt_rapidfirehealth', 80))
        .append(n$('bfopt_rapidfireattacks', 'Max. Count:', 50))
        .append(n$('bfopt_rapidfiretiming', 'Timing:', 50))
        .append(x$('bfopt_rapidfireautotiming', 'Auto Timing.'));
        
        // ATTACK OPTIONS
        c$('li').appendTo(divListElt)
        .append(x$('bfopt_attackusemax', 'Maximum attacks:'))
        .append(n$('bfopt_attackmax', 60))
        .append(x$('bfopt_attackuseperfoe', 'Attacks Per Foe:'))
        .append(n$('bfopt_attackperfoe', 60))
        .append(n$('bfopt_attackretries', 'Error Retries:', 40))
        .append(x$('bfopt_attackpwr', 'Power Attack!'));
        //.append(x$('bfopt_attacktonpc', 'Attack non-players targets.'));
        
        // REVENGES / NPC
        c$('li').appendTo(divListElt)
        .append(x$('bfopt_attackrevenge', 'Revenge thiefs:'))
        .append(s$('bfopt_attackrevengefilter', 200))
        .append(x$('bfopt_timerrivalsactive', 'Attack Rivals every'))
        .append(n$('bfopt_timerrivalsmins', '(min.):', 40));
        
        c$('li').appendTo(divListElt)
        .append(x$('bfopt_rediceactive', 'RedICE if attacks below:'))
        .append(n$('bfopt_redicemaxattk', 35))
        .append(x$('bfopt_rediceafterwon', 'AutoRedICE if you won.'))
        .append(x$('bfopt_rediceepicbattle', 'AutoRedICE if in Epic Battle.'));

        c$('li').appendTo(divListElt)
        .append(x$('bfopt_attkdelayusea', 'Delay when attack same player:'))
        .append(s$('bfopt_attkdelaya', 70))
        .append(x$('bfopt_attkdelayuseb', 'Delay when changing player'))
        .append(s$('bfopt_attkdelayb', 70));

        c$('li').appendTo(divListElt)
        .append(x$('bfopt_levelrangeactive', 'Level:'))
        .append(n$('bfopt_levelrangemin', 40))
        .append(n$('bfopt_levelrangemax', '-', 40))
        .append(s$('bfopt_levelrangemethod', 'is:', 90))
        .append(s$('bfopt_comparerangemethod', 60))
        .append(x$('bfopt_mafiarangeactive', 'Mafia:'))
        .append(n$('bfopt_mafiarangemin', 40))
        .append(n$('bfopt_mafiarangemax', '-', 40))
        .append(s$('bfopt_mafiarangemethod', 'is:', 90));

        // AUTOPUBLISH
        c$('li').appendTo(divListElt)
        .append(x$('bfopt_publishactive', 'Auto Publish after:'))
        .append(s$('bfopt_publishafter', 80))
        .append(t$('bfopt_publishto', 'ices to page/group (empty for wall):', 180));

        c$('li').appendTo(divListElt)
        .append(x$('bfopt_usestampack', 'Use StaminaPack if exp. above:'))
        .append(n$('bfopt_usestampackwhen', 50))
        .append(x$('bfopt_usehealpack', 'Use HealthPack if stamina above:'))
        .append(n$('bfopt_usehealpackwhen', 50));

        // -----------------
        // CONFIG PAGE 2
        // -----------------

        divListElt = c$('ul').appendTo(tabs.getLayout(1));

        // NAME FILTER
        c$('li').appendTo(divListElt)
        .append(x$('bfopt_namefilteractive', 'Name filter:', 'div').css({'width':90,'float':'left'}))
        .append(c$('input:text', 'id:bfopt_namefilterexpr,readonly:readonly').width(520).css('margin-right',5))
        .append(c$('a', 'for:bfopt_namefilterexpr').text('EDIT').click(Events.editFilter));
        
        // BADGE FILTER
        c$('li').appendTo(divListElt)
        .append(x$('bfopt_badgefilteractive', 'Badge filter:', 'div').css({'width':90,'float':'left'}))
        .append(c$('input:text', 'id:bfopt_badgefilterexpr,readonly:readonly').width(520).css('margin-right',5))
        .append(c$('a', 'for:bfopt_badgefilterexpr').text('EDIT').click(Events.editFilter));

        // SKIP
        c$('li').appendTo(divListElt)
        .append(x$('bfopt_skipiced', 'Skip Iced targets.'))
        .append(x$('bfopt_skipicedbyme', 'Skip iced by me.'))
        .append(x$('bfopt_skipusemincash', 'Skip cash below:'))
        .append(n$('bfopt_skipbymincash', 40))
        .append(x$('bfopt_skipwrongcash', 'Skip wrong cash.'));
        
        // REFRESH FIGHTLIST
        c$('li').appendTo(divListElt)
        .append(x$('bfopt_refreshon', 'Maximum opponents per fightlist:'))
        .append(n$('bfopt_refreshmax', 40))
        .append(x$('bfopt_epicbattle', 'Join family Battles.'))
        .append(n$('bfopt_epicbattlerefresh', 'Refresh:', 40))
        .append(x$('bfopt_epicbattlefortress', 'Attack Fortress.'));
        
        c$('li').appendTo(divListElt)
        .append(x$('bfopt_skipusehealth', 'Skip health:'))
        .append(s$('bfopt_skiphealth', 80))
        .append(x$('bfopt_skipunderattk', 'Skip under attack if damage'))
        .append(s$('bfopt_skipunderattkpct', 80))
        .append(x$('bfopt_skiphealed', 'Skip Healed.'));
        
        // STOP - RESUME
        c$('li').appendTo(divListElt)
        .append(x$('bfopt_stopbyices', 'Stop when ICE amount is more than:'))
        .append(n$('bfopt_stopiceamount', 40))
        .append(x$('bfopt_stopkeepstaon', 'Stop If stamina is less than:'))
        .append(n$('bfopt_stopkeepsta', 40));

        c$('li').appendTo(divListElt)
        .append(x$('bfopt_stopbylevelup', 'Stop after LevelUp.'))
        .append(x$('bfopt_stopkeepexpon', 'Stop before LevelUp and keep:'))
        .append(n$('bfopt_stopkeepexp', 40))
        .append(c$('span').text('Exp.'));

        c$('li').appendTo(divListElt)
        .append(x$('bfopt_stopautomatic', 'Stop AutoFight if health fall down quickly.'))
        .append(x$('bfopt_stopresume', 'Keep and resume after (min):'))
        .append(n$('bfopt_stopresumedelay', 40));
        
        c$('li').appendTo(divListElt)
        .append(x$('bfopt_timerstartactive', 'Start Fighting after (min):'))
        .append(n$('bfopt_timerstartmins', 40))
        .append(x$('bfopt_timerfightactive', 'AutoPause after (min):'))
        .append(n$('bfopt_timerfightmins', 40))
        .append(n$('bfopt_timerfightresume',' resume after (min): ', 40));
        
        // LOG LENGTH
        c$('li').appendTo(divListElt)
        .append(c$('label','for:bfopt_maxloglength').text('Log Length:'))
        .append(s$('bfopt_maxloglength', 100).css('margin-left',5))
        .append(x$('bfopt_showsocialevents', 'Show social events.'))
        .append(x$('bfopt_collectclanfightxp', 'Collect Clan fight XP.'));

        $('a', divListElt).css('margin-left',5);
        
        
        function generateList($parent, sName) {
            var $elt = c$('div').appendTo($parent).css({
                'float':'left',
                'text-align':'left',
                'margin':5,
                'width':500
            });
    
            var table = c$('div',sName+'_table').appendTo($parent).css({
                'float': 'left',
                'padding-top': 50
            });
            
            Util.each([
                {id:'add_new_list',     name:sName, text:'Add New',    evt:Events.addNewToList_click},
                {id:'delete_item_list', name:sName, text:'Delete',     evt:Events.deleteSelected_click},
                {id:'clear_list',       name:sName, text:'Clear List', evt:Events.clearList_click},
                {id:'go_profile',       name:sName, text:'MW Profile', evt:Events.mwProfile_click},
                {id:'add_from_list',    name:sName, text:'Load List',  evt:Events.addFromList_click},
                {id:'get_current_list', name:sName, text:'Save List',  evt:Events.getList_click}
            ], function(i,b) {
                b$(b.text, 'id:'+b.id+',name:'+b.name+',class:short white fightV2AttackBtn')
                .appendTo(c$('dl').appendTo(table).css('margin',5)).click(b.evt);
            });
            
            return $elt;
        }
        
        // -----------------
        // BLACKLIST OPTIONS
        // -----------------
        generateList(tabs.getLayout(2), 'blackList')
        .append(x$('bfopt_useblacklist','Add players who defeat me to BlackList:'))
        .append(c$('a','href:#').text('Hardest Level Range').css('font-weight','bold').click(Events.hardLevelRange_click))
        .append(c$('div').css({'clear':'both','margin-top':5}))
        .append(c$('select', 'id:bfopt_blacklist,multiple:multiple').width(500).height(270));
        
        // -----------------
        // WHITELIST OPTIONS
        // -----------------
        generateList(tabs.getLayout(3), 'whiteList')
        .append(x$('bfopt_whitelistcountactive','Attack whitelist only:'))
        .append(n$('bfopt_whitelistcount', 40))
        .append(c$('span').text(' times.'))
        .append(x$('bfopt_randomizewhitelist','Randomize Whitelist.'))
        .append(c$('div').css({'clear':'both','margin-top':5}))
        .append(c$('select', 'id:bfopt_whitelist,multiple:multiple').width(500).height(270));
        
        // -----------------
        // FRIEND CLANLIST OPTIONS
        // -----------------
        generateList(tabs.getLayout(4), 'frdClanList')
        .append(c$('div').text('Add your friendly families to skip here:'))
        .append(c$('div').css({'clear':'both','margin-top':5}))
        .append(c$('select', 'id:bfopt_frdclanlist,multiple:multiple').width(500).height(270));

        // -----------------
        // ENEMY CLANLIST OPTIONS
        // -----------------
        generateList(tabs.getLayout(5), 'enmClanList')
        .append(c$('div').text('Add your Enemy families to attack here:'))
        .append(c$('div').css({'clear':'both','margin-top':5}))
        .append(c$('select', 'id:bfopt_enmclanlist,multiple:multiple').width(500).height(270));


        // ---------------
        // USER PANELS
        // ---------------
        var wrapper_info = c$('div', 'id:wrapper_info').appendTo(popupElt.content);

        var fightV2_msg_ctrl = c$('div','class:fightV2_msg_ctrl').appendTo(wrapper_info);
        c$('div','class:fightV2_result').appendTo(wrapper_info);
        c$('div').css('clear','both').appendTo(wrapper_info);

        // START PANEL
        c$('center', 'ctrlcontainer').appendTo(fightV2_msg_ctrl)
        .append(b$('Random Attacks!', 'class:short red').click(Events.autoMode_click))
        .append(b$('Attack WhiteList!', 'class:short red').css('margin-left', 5).click(Events.attackWhiteList_click))
        .append(b$('Attack Families!', 'class:short red').css('margin-left', 5).click(Events.attackFamily_click));


        // ACTIVE STATUS
        c$('center', 'msgcontainer').css('margin',5).appendTo(fightV2_msg_ctrl);

        // BOTTOM PART
        var wrapper_log_stats = c$('div', 'id:wrapper_log_stats').appendTo(popupElt.content);

        // STATS
        var fightV2_stats = c$('div', 'class:fightV2_stats').appendTo(wrapper_log_stats);

        // generate stats
        Util.each([
            {name:'Current City:',      cls:'stat_travel',         id:'currcityname'                       },
            {name:'Total Fights:',      cls:'stamina',             id:'fightCount'                         },
            {name:'Won Fights:',        cls:'attack',              id:'fightWon'                           },
            {name:'Lost Fights:',       cls:'defense',             id:'fightLost'                          },
            {name:'Foes Attacked:',     cls:'attack',              id:'foesAttacked'                       },
            {name:'Foes Ice/Kill:',     cls:'stat_kill',           id:['killWon', 'icesWon']               },
            {name:'Session Ices:',      cls:'stat_kill',           id:'session_ices'                       },
            {name:'Stolen Ices:',       res:'thief_icon',          id:'icesLost'                           },
            {name:'Revenges:',          cls:'attack',              id:'revenge'                            },
            {name:'Clan fight XP:',     cls:'stat_iced',           id:['exp_fight_max', 'exp_fight']       },
            {name:'Ice Season:',        cls:'stat_iced',           id:['season_target','season_ices']      },
            {name:'Total Ices:',        cls:'stat_iced',           id:'total_ices'                         },
            {name:'Attack gained:',     cls:'mafia_attack',        id:'atkgained'                          },
            {name:'Defense gained:',    cls:'mafia_defense',       id:'defgained'                          },
            {name:'Exp. gained:',       cls:'experience',          id:['exptonextlevel','expGained']       },
            {name:'Exp. per stamina:',  cls:'experience',          id:'exp_per_sta'                        },
            {name:'Victory Coins:',     cls:'stat_victory',        id:'coinsGained'                        },
            {name:'Cash:',              cls:'cash',                id:'cash'                               },
            {name:'PowerPack:',         cls:'stat_powerpack',      id:'powerpack'                          },
            {name:'Load Rivals:',       res:'timer_icon',          id:'loadrivals'                         },
            {name:'Auto Travel:',       res:'timer_icon',          id:'timetotravel'                       },
            {name:'Auto Pause:',        res:'timer_icon',          id:'timetopause'                        }
        ],
        function(index, stat) {
            var $span = c$('span').text(stat.name);
            var $dl = c$('dl').appendTo(fightV2_stats);

            if (stat.cls) {
                $dl.append(c$('dt', 'class:'+stat.cls).append($span));
            }
            else {
                if (stat.icn) {
                    $dl.append(c$('img').attr('src',global.zGraphicsURL + stat.icn));
                }
                else if (stat.res) {
                    $dl.addClass(Resources.className(stat.res)).css({
                        'width': 'auto',
                        'padding-left': 19
                    });
                }
                $dl.append(c$('dt').append($span));
            }
            if (Util.isString(stat.id)) {
                $dl.append(c$('dd', 'id:mass_stat_'+stat.id).text('0'));
            }
            else {
                $dl.append(c$('dd', 'id:mass_stat_'+stat.id[0]).text('0'));
                $dl.append(c$('dd').text('/'));
                $dl.append(c$('dd', 'id:mass_stat_'+stat.id[1]).text('0'));
            }
        });        
        
        (function($parent) {
            var $div, $elt = c$('div', 'events_list').appendTo($parent);
            
            c$('div', 'class:buttons').appendTo($elt)
            .append(c$('a', 'href:#items,class:selected').text('General').click(Events.showLog_click))
            .append(c$('a', 'href:#loot').text('Loot').click(Events.showLog_click))
            .append(c$('a', 'href:#iced').text('Ices').click(Events.showLog_click))
            .append(c$('a', 'href:#events').text('Events').click(Events.showLog_click));
            
            c$('div', 'id:items_logs,class:player_updates').height(420).appendTo($elt);
            
            c$('div', 'id:loot_logs').appendTo($elt)
            .append(c$('div', 'class:buttons').css('text-align','right')
            .append(c$('a', 'href:#name').text('Name').click(Events.sortloot_click))
            .append(c$('a', 'href:#attack').text('Attack').click(Events.sortloot_click))
            .append(c$('a', 'href:#defense').text('Defense').click(Events.sortloot_click))
            .append(c$('a', 'href:#best').text('Best').click(Events.sortloot_click)))
            .append(c$('ul', 'id:lootlistlog').height(401));
                
            $div = c$('div', 'id:iced_logs').height(420).css('text-align','center').appendTo($elt);
            
            c$('textarea','readonly:readonly,id:text_plain_ice_log').css({
                'width': '90%',
                'height': '90%',
                'color': 'white',
                'background-color': 'transparent',
                'border': 0
            }).appendTo($div);
            
            c$('div').css('clear','both').appendTo($div);
            b$('Select All','class:short green').css({'float':'right','margin-right':15})
            .attr('onclick',"$(this).prevAll('textarea').select(); return false;").appendTo($div);
            
            c$('div', 'id:events_logs').height(420).appendTo($elt).append(c$('ul','id:eventslogger'));
            
            // MANUAL LIST OF FIGHTERS
            $div = c$('div', 'opponents_list').appendTo($parent);
            
            c$('div', 'class:header').appendTo($div)
            .append(c$('a', 'sort_by_name').text('Name').click(Events.sort_click))
            .append(c$('a', 'sort_by_level').text('Level').click(Events.sort_click))
            .append(c$('a', 'sort_by_mafia').text('Mafia').click(Events.sort_click))
            .append(c$('a').text('Refresh').click(Events.refresh_click));
            
            c$('div', 'id:opponents_table,class:player_updates').appendTo($div);
            
        })( c$('div', 'class:fightV2_log').appendTo(wrapper_log_stats) );
        
        popupElt.applyOptions({
            'bfopt_healcity'            : global.cityNames,
            'bfopt_startcity'           : global.cityNames,
            'bfopt_comparerangemethod'  : {'AND': 'AND', 'OR': 'OR'},
            'bfopt_attkdelaya'          : {1:'1-3', 2:'2-4', 3:'3-5', 4:'1-5'},
            'bfopt_attkdelayb'          : {1:'1', 2:'2', 3:'3', 4:'4'},
            'bfopt_levelrangemethod'    : {'attk':'Attacked', 'skip':'Skipped'},
            'bfopt_mafiarangemethod'    : {'attk':'Attacked', 'skip':'Skipped'},
            'bfopt_attackrevengefilter' : {0:'Attack to everyone',1:'Attack to any level/mafia',2:'Use all filters'},
            'bfopt_maxloglength'        : {10:'10', 50:'50', 100:'100', 250:'250', 500:'500', 1000:'1000'},
            'bfopt_publishafter'        : {5:'5', 10:'10', 15:'15', 20:'20'},
            'bfopt_skiphealth'          : {20:'> 20%', 30:'> 30%', 40:'> 40%', 50:'> 50%', 60:'> 60%', 70:'> 70%', 80:'> 80%', 90:'> 90%', 100:'> 100%'},
            'bfopt_skipunderattkpct'    : {5:'> 5%', 10:'> 10%', 15:'> 15%', 20:'> 20%', 25:'> 25%', 30:'> 30%', 40:'> 40%', 50:'> 50%'},
            'bfopt_rapidfirehealth'     : {10:'< 10%', 20:'< 20%', 30:'< 30%', 40:'< 40%', 50:'< 50%', 60:'< 60%', 70:'< 70%', 80:'< 80%', 90:'< 90%', 100:'< 100%'}
        });
    }

    // show specified div element
    function showDiv(name, type, bfade, fn) {
        $('div[id*='+type+'],ul[id*='+type+']', popupElt.content).stop().css('opacity', 1).hide();
        if (bfade === true) {
            $('#' + name + type, popupElt.content).fadeIn(1000, fn);
        }
        else {
            $('#' + name + type, popupElt.content).show();
        }
    }

    function setAttackTimer(fn) {
        if (options.attkdelayUseA !== true) {
            fn();
            return;
        }
        var min = 1, max = 3, delay = 5;
        switch(options.attkdelayA) {
            case 2: min = 2; max = 4; break;
            case 3: min = 3; max = 5; break;
            case 4: max = 5; break;
        }
        delay = Math.floor(Math.random() * (max-min+1)) + min;

        StatusTimer.start('Ready to attack '+PlayerList.current.anchor()+' in %N% seconds...', delay, fn);
    }

    function setAttackNewTimer(fn) {
        if (options.attkdelayUseB !== true) {
            fn();
            return;
        }
        var delay = options.attkdelayB;
        if (!Util.isSet(delay) || delay < 1) {
            delay = 2;
        }
        StatusTimer.start('Ready to attack '+PlayerList.current.anchor()+' in %N% seconds...', delay, fn);
    }

    /**
     * @param {String} list_name
     * @param {String} profilesText
     */
    function addMWProfiles(list_name, profilesText) {

        if (!Util.isSet(PlayerList[list_name])) {
            Logger.error('Attempt to add MW Profiles in unknow list name.');
            return;
        }
        var profileRegex = /(profile|family).php\?id=([^\n]+)|user=([^\n]+)|^([\s\d,\n]+)$/g;
        var rgx, data = [];
        var isClan = /clanList/i.test(list_name);
        var errors = new Array();

        while ((rgx = profileRegex.exec(unescape(profilesText)))) {
            if (rgx[2]) {
                // from profile link
                rgx = Util.trim(rgx[2]);
                if (rgx.indexOf('{') !== -1) {
                    rgx = Util.parseJSON(rgx);
                    rgx = (rgx.id || rgx.user);
                    if (rgx) {
                        data.push(Base64.decode(rgx));
                    }    
                }
            } 
            else if (rgx[3]) {
                data.push(Base64.decode(rgx[3]));
            }
            else if (rgx[4]) {
                data = String(rgx[4]).split(/[\s,\n]+/);
                break;
            }
        }
        if (data.length < 1) {
            return;
        }
        var profiles = new Collection(data);

        profiles.onMove(function(pos, key, id) {
            try {
                var url;
                if ( PlayerList[list_name].exists(Util.parseNum(id)) ) {
                    profiles.MoveNext();
                    return;
                }
                if ( isClan ) {
                    url = 'remote/' + MW.getIntURL('clan') + '&from_red_link=1&id=';
                    url += Base64.encode(id);
                } else {
                    if (String(id).charAt(0) !== 'p') {
                        id = 'p|' + id;
                    }
                    url = 'remote/' + MW.getIntURL('stats') + '&user=' + id;
                }
                httpAjaxRequest({url: url, message: 'Loading id: '+id,
                    success: function(htmlText) {
                        var result;
                        if ( isClan ) {
                            result = parseClanProfile(htmlText);
                            result.id = id;
                        } else {
                            result = getOpponentFromProfile(htmlText);
                        }
                        if (!result.error_msg) {
                            PlayerList[list_name].add(result);
                        }
                        else {
                            errors.push(Util.setColor(id,'red')+': '+result.error_msg);
                        }
                        profiles.MoveNext();
                    }
                });
            }
            catch(err) {
                profiles.MoveNext();
                Logger.error(err);
            }
        });

        profiles.onEnd(function() {
            options.toDomElements();
            options.save();
            profiles.clear();
            if (errors.length > 0) {
                showHelpPopup({
                    icon: 'error',
                    title: 'Adding profiles to '+list_name,
                    message: 'There has been encountered the following errors:<br><br>'+errors.join('<br>')
                });
            }
        });

        profiles.MoveFirst();
    }

    function getOpponentFromProfile(data) {
        var opp = new CSOpponent();
        var $html = h$(data), sText;
        var attexpr = 'a:regex(href,xw_action=attack), a:regex(onclick,xw_action=attack)';
        try {
            if (e$('#change_title', $html) !== null) {
                throw 'You can\'t add yourself';
            }
            if (!(opp.attack_url = Util.toUrl($html.find(attexpr).eq(0)))) {
                throw 'Cant get user url.';
            }
            if (e$('.zy_popup_box_body_dark a:regex(href,xw_action=remove)') !== null) {
                opp.isInMafia = true;
            }
            opp.id = Util.parseNum(Util.uSplit(opp.attack_url).opponent_id);
            sText = Util.htmlDecode($html.find('.stats_title .stats_title_text:first').text());
            opp.name = Util.substr(sText, '"', '"', 1, 0, 0, true);
            opp.level = Util.parseNum(sText.substr(sText.lastIndexOf('"') + 1));
            return opp;
        }
        catch(err) {
            return {'error_msg': err};
        }
    }

    function parseClanProfile(data) {
        var clan = new Object();
        var $html = h$(data);
        try {
            clan.name = $html.find('#clan_main #clan_header > h3').text();
        }
        catch(err) {
            Logger.error(err);
            return {error_msg:err.message};
        }
        return clan;
    }
    
    function getFamilyOpponents(callback) {
        var message = 'Found <span style="color:green;">${added}</span> Players in Family ${family}.<br>${result}...'
        var tempList = PlayerList.enmClanList.toArray();
        gVar.FamilyListArrays.original = new Array();
        
        function getFamily(p) {
            var url = 'remote/' + MW.getIntURL('clan') + '&from_red_link=1&id=';
            
            httpAjaxRequest({url: url + Base64.encode(p.id),
                success: function(htmlText) {
                    var elt = h$(htmlText);
                    var tmpl = {
                        added: 0,
                        family: elt.find('#clan_main #clan_header > h3').text(),
                        result: 'All families loaded, starting'
                    };                    
                    elt.find('ul#member_list li').each(function(index, element) {
                        var user;
                        try {
                            user = Util.uSplit($('.name_n_rank a',element).attr('href')).user;
                            user = Base64.decode(user);
                            gVar.FamilyListArrays.original.push(Util.parseNum(user));
                            tmpl.added++;
                        }
                        catch(err) {
                            Logger.error('AttackFamilies.getFamily:'+ err.message);
                        }
                    });
                    if (tempList.length > 0) {
                        tmpl.result = 'Loading next family';
                        getFamily(tempList.shift());
                    } else {
                        callback();
                    }
                    addLog(Util.render(message,tmpl), 'help', null, 'attackfamily');
                }
            });
        }        
        sendMessage('Loading families...', true);
        PlayerList.clear();
        if (tempList.length < 1) {
            sendMessage('There is no families in your enemy family list.');
            stopAutoFight();
        } else {
            getFamily(tempList.shift());
        }
    }

    function fromFamilyList() {
        if (gVar.FamilyListArrays.original.length < 1) {
            sendMessage('There is no more possible enemies to attack in your family list.');
            stopAutoFight();          
        }
        if (!Util.isArray(gVar.FamilyListArrays.current) || gVar.FamilyListArrays.current.length < 1) {
            gVar.FamilyListArrays.current = gVar.FamilyListArrays.original.slice();
        }        
        var pId;
        if (gVar.FamilyListArrays.current.length > 1) {
            pId = gVar.FamilyListArrays.current.splice(Math.random() * gVar.FamilyListArrays.current.length, 1).shift();
        } else {
            pId = gVar.FamilyListArrays.current.shift();
        }

        if (PlayerList.blackList.exists(pId)) {
            gVar.FamilyListArrays.original.splice(gVar.FamilyListArrays.original.indexOf(pId), 1);
            fromFamilyList();
            Logger.debug('Enemy Family "' + pId + '" is blacklisted.');
            return;
        }
        if ( Util.isSet(gVar.FamilyListCache[pId]) ) {
            PlayerList.current = gVar.FamilyListCache[pId].clone();
            setAttackNewTimer(function() {
                updateNewOpponent();
                preAttack();
            });
        } else {
            sendMessage('Loading profile (' + pId + ')...', true);
            httpAjaxRequest({
                url: 'remote/' + MW.getIntURL('stats') + '&user=p|' + pId,
                success: function(htmlText) {
                    (PlayerList.current = getOpponentFromProfile(htmlText)).isWList = true;
                    gVar.FamilyListCache[pId] = PlayerList.current.clone();
                    updateNewOpponent();
                    preAttack();
                }
            });
        }
    }

    function fromRandomWhiteList() {
        if (gVar.WhiteListArray.length < 1) {
            if (PlayerList.whiteList.length() > 0 && (!options.whiteListCountActive 
                 || PlayerList.whiteList.attack_count < options.whiteListCount)) 
            {
                PlayerList.whiteList.attack_count++;
                gVar.WhiteListArray = PlayerList.whiteList.toArray();
                
            } else {
                if (options.whiteListCountActive) {
                    gVar.AttackWhitelist = false;
                    AttackNewOpponent();
                } else {
                    sendMessage('No enemy in WhiteList. Please add players to attack.');
                    stopAutoFight('No enemy in WhiteList.');
                }
                return; 
            }            
        }
        
        var p;
        if (options.randomizeWhiteList && gVar.WhiteListArray.length > 1) {
            p = gVar.WhiteListArray.splice(Math.random() * gVar.WhiteListArray.length, 1).shift();
        } else {
            p = gVar.WhiteListArray.shift();
        }

        if (PlayerList.blackList.exists(p.id)) {
            PlayerList.whiteList.remove(p.id);
            fromRandomWhiteList();
            Logger.debug('Whitelist enemy "' + p.name + '" is blacklisted.');
            return;
        }
        if ( Util.isSet(gVar.WhiteListCache[p.id]) ) {
            PlayerList.current = gVar.WhiteListCache[p.id].clone();
            setAttackNewTimer(function() {
                updateNewOpponent();
                preAttack();
            });
        } else {
            sendMessage('Loading profile (' + p.name + ')...', true);
            httpAjaxRequest({
                url: 'remote/' + MW.getIntURL('stats') + '&user=p|' + p.id,
                success: function(htmlText) {
                    var opp = getOpponentFromProfile(htmlText);
                    if (opp.isInMafia) {
                        opp.error_msg = 'is in your mafia. Removed from whitelist.';
                    }
                    if (opp.error_msg) {
                        PlayerList.whiteList.remove(p.id);
                        addEventLog(opp, opp.error_msg);
                        fromRandomWhiteList();
                        return;
                    }
                    (PlayerList.current = opp).isWList = true;
                    gVar.WhiteListCache[p.id] = opp.clone();
                    updateNewOpponent();
                    preAttack();
                }
            });
        }
    }

    // Refresh fight table
    function refreshPlayerList(callback) {
        var controller = {};
        if (gVar.AttackInBattle === true) {
            controller.name = 'EpicBattle';
            controller.func = 'loadBattle';
        } else {
            controller.name = 'fight';
            controller.func = 'load';
        }
        sendMessage('Loading '+controller.name+' page...', true);
        httpAjaxRequest({
            url: 'remote/' + MW.getIntURL(controller.name),
            liteLoad: 1,
            success: function(htmlText) {
                var result = 0;
                updateUserFields(htmlText);
                sendMessage();
                if (/<!--[^:]*:\s*(fight|epicbattle)_controller/.test(htmlText)) {
                    result = PlayerList[controller.func](htmlText);
                } else {
                    Logger.error('Unexpected page, looking for a fight or epicbattle page.');
                }
                if (Util.isFunc(callback)) {
                    callback(result);
                }
            }
        });
    }

    // generate a list of opponenets
    function genEnemyListDom() {
        if (PlayerList.length() < 1) {
            return;
        }
        var $table = $('#opponents_table').empty();
        var template = '${title}<br>${user}<br>${badge}Level: ${level}';
        var icon_url = global.zGraphicsURL + 'home/icon_hitlist_75x75_01.gif';
        var $icon = c$('img', {
            'width': 40,
            'height': 40,
            'src': global.zGraphicsURL + 'home/icon_hitlist_75x75_01.gif'
        });
        PlayerList.each(function(index, opp) {
            var color = opp.iced ? '#433' : 'white';
            var text = Util.render(template, {
                title : opp.title || '',
                user  : opp.anchor(),
                badge : (opp.badge&&(opp.badge.length > 0)?'['+opp.badge+'] ':''),
                level : opp.level
            });
            c$('div', 'class:update_item,id:enemy_player_' + index).css('width', '99%')
            .append(c$('div', 'class:update_timestamp'))
            .append(c$('div', 'class:update_icon').append($icon.clone().attr('src', opp.badge_url||icon_url)))
            .append(c$('span', 'id:player_name,class:update_txt').css({'color':color,'width':260}).html(text))
            .append(c$('span', 'id:player_mafia,class:update_txt').width(50).css('padding',5).text(opp.mafia))
            .append(b$('Attack', 'class:short red,id:attack_id_'+ index).click(Events.attack_click).css({
                'float': 'right',
                'margin-right': 15
            })).appendTo($table);
        });
    }
    /**
     * Check logs and remove last log item if it's more than maximum.
     * @param {String} list
     */
    function checkMaximumLogs(list) {
        var $ul = $(list||'#items_logs');
        if ($ul.children().length > options.maxLogLength) {
            $ul.children().last().remove();
        }
    }
    /**
     * Add generic loot
     * @param {CSItemCard} loot
     */
    function addGenericLoot(loot) {
        if (loot.type === 'coins') {
            SessionStats.add('coinsGained', Util.parseNum(loot.quantity));
        }
        if ( !options.get('showSocialEvents') ) {
            return;
        }
        var textMessage = 'Social Event:<br>'+ loot.title+' '+loot.quantity;
        var updateItem = c$('div', 'class:update_item,id:generic_item');

        c$('div', 'class:update_timestamp').appendTo(updateItem).html((new Date()).toUTCString());
        c$('div', 'class:update_icon').appendTo(updateItem)
        .append(c$('img').attr({
            'width': 40,
            'height': 40,
            'src': loot.pic
        }));
        c$('div', 'class:update_txt').appendTo(updateItem).html(textMessage);
        checkMaximumLogs();
        $('#items_logs').prepend(updateItem);
    }
    /**
     * Add loot item to loot log.
     * @param {CSItemLoot} loot
     * @param {Boolean} bLastPos
     */
    function addLootLog(loot, bLastPos) {
        if (!Util.isSet(loot)) {
            return;
        }
        var elt = e$('#loot_'+loot.item_id, '#lootlistlog');
        var lootPic = c$('img').css('vertical-align', 'middle').attr({
            'width': 30,
            'height': 30, 
            'src': loot.pic
        });
        if (elt !== null) {
            $('#item_count', elt).text(loot.count);
        }
        else {
            c$('li', 'loot_'+loot.item_id).append(lootPic)
            .append(c$('span').css('margin-left',10).text('Found '))
            .append(c$('span','item_count').css('color','green').text(loot.count))
            .append(c$('span').css('margin-left',5).append(loot.toString()))
            [bLastPos?'appendTo':'prependTo']('#lootlistlog');
        }
    }
    /**
     * Generate a new log event.
     * @param {String} message
     * @param {String} icon
     * @param {String} pic
     * @param {String} id
     */
    function addLog(message, icon, pic, id) {
        if (!Util.isString(icon)) {
            icon = 'fight';
        }
        checkMaximumLogs();
        var updateItem;
        var updateIcon = c$('img').attr({
            'width': 40,
            'height': 40,
            'src': logIcon[icon]
        });
        if (id) {
            id = icon+'_item_'+id;
            updateItem = e$('#items_logs #'+id, popupElt.content)
            ||           c$('div', 'class:update_item,id:'+id);
        } else {
            id = icon+'_item';
            updateItem = c$('div', 'class:update_item,id:'+id);
        }
        updateItem.empty();
        c$('div', 'class:update_timestamp').appendTo(updateItem).html((new Date()).toUTCString());
        c$('div', 'class:update_icon').appendTo(updateItem).append(updateIcon);
        if (pic) {
            c$('div').addClass('update_pic').appendTo(updateItem).append(c$('img').attr({
                'width': 40,
                'height': 40,
                'src': pic
            }));
        }
        c$('div', 'class:update_txt').appendTo(updateItem).html(message);
        $('a.sexy_button_new', updateItem).css('color', 'black');
        
        // insert
        $('#items_logs').prepend(updateItem);
    }

    function parseLootData(lootData, count) {
        if (Util.isSet(lootData) && lootData.length > 0) {
            var i;
            for (i = 0; i < lootData.length; i++) {
                if ( /fake_item_card/.test(lootData[i]) ) {
                    addGenericLoot(new CSItemCard(lootData[i]));
                }
                else if ( /item_with_preview/.test(lootData[i]) ) {
                    addLootLog( LootCache.add(new CSItemLoot(lootData[i], count)) );
                }
                else if ( /item_card_mini ice/.test(lootData[i]) ) {
                    var evt = new CSItemIceEvent(lootData[i]);
                    checkMaximumLogs('#eventslogger');
                    c$('li').html('&#187; '+Util.setColor('You won: '+evt.title,'green')).prependTo('#eventslogger');
                }
                else {
                    Logger.error('Unknow Loot Found:\n'+lootData[i]);
                }
            }
        }
    }
    /**
     * @param {CSIce} ice
     * @param {CSOpponent} opp
     */
    function addIcedToLog(ice, opp) {
        var ice_tmpl = '${name} ${event} ${oppname} to get ${count}.';
        var season_tmpl = 'Season ice: ${ices} of ${target} (left ${left} ices).';
        var log_tmpl = '# [${time}] ${event} ${count} to ${oppname}';
        var tmpl = {
            time     : (new Date()).toLocaleTimeString(),
            name     : (opp.isCBttl ? (gVar.EpicBattle.userName||'You') : 'You'),
            event    : 'ICED',
            count    : ice.count,
            ices     : ice.season_ices||0,
            target   : ice.season_target||0,
            oppname  : opp.anchor()
        };
        tmpl.left = tmpl.target - tmpl.ices;
        
        gVar.IcedPlayerCache[opp.id] = true;
        AutoTravelCounter.reset('stolenIce');

        if ( ice.isKilled ) {
            SessionStats.add('killWon');
            AttackerStats.session_ices += 2;
            tmpl.event = 'KILLED';
        }
        else {
            SessionStats.add('icesWon');
            AttackerStats.session_ices++;
        }
        if ( opp.isThief ) {
            SessionStats.add('revenge');
            tmpl.event += ' the THIEF';
        }
        if (ice.season_ices && ice.season_target) {
            ice_tmpl += ('<br>' + season_tmpl);
        }        
        addLog( Util.render(ice_tmpl, tmpl) +'<br>'+ ice.action, 'iced', opp.image );
        
        if (ice.description) {
            $('#text_plain_ice_log',popupElt.content)[0].value += '# '+ice.description+'\n';
        } else {
            $('#text_plain_ice_log',popupElt.content)[0].value += Util.render(log_tmpl, tmpl);
        }
        if ( options.publishActive && ice.canPublish ) {
            AutoPublish.add( ice, (opp.clanId ? opp.clanName+' '+opp.name : opp.name) );
        }
    }
    /**
     * @param {CSOpponent} opp
     */
    function addStolenIceToLog(opp) {
        var thief = opp.thief;
        var message = 'ICE STOLEN BY: ';
        var result = thief.tyClass;
        
        SessionStats.addStolenIce(thief);
        AutoTravelCounter.stolenIce();

        function skipRevenge() {
            if (opp.isCBttl === true) {
                result = 'But you\'re in a epic battle.';
                return true;
            }
            if ( thief.inMafia ) {
                result = 'But he/she is in your mafia.';
                return true;
            }
            if ( options.attackRevenge !== true ) {
                result = 'Set Revenge ON if you want to take your revenge!.';
                return true;
            }
            if ( PlayerList.blackList.exists(thief.id) ) {
                result = 'But was skipped because he/she is in your blacklist.';
                return true;
            }
            result = 'When fighting against '+ opp.anchor() +'.';
            return false;            
        }

        if ( skipRevenge() !== true )
        {
            PlayerList.revenge = new CSOpponent();
            PlayerList.revenge.name = thief.name;
            PlayerList.revenge.id = thief.id;
            PlayerList.revenge.attack_url = thief.action;
            PlayerList.revenge.isThief = true;
            PlayerList.revenge.level = thief.level;
            PlayerList.revenge.clanName = thief.clanName;
        }
        addLog( message+thief.anchor()+'<br>'+result, 'iced', thief.pic, thief.id );
    }
    /**
     * @param {CSOpponent} opp
     * @param {String} reason
     */
    function addEventLog(opp, reason) {
        var $li = e$('#eventslogger #skippedlog'+opp.id)||c$('li','id:skippedlog'+opp.id);
        var text = reason||opp.result_text||'Too late, was iced.';
        $li.html(opp.toString() + ':<br>&#187; '+text);
        checkMaximumLogs('#eventslogger');
        $li.prependTo('#eventslogger');
    }

    // update stats from AttackerStats and SessionStats classes.
    function updateStats() {        
        Util.each(AttackerStats, function(name, value) {
            var elem = e$('#mass_stat_' + name.toLowerCase());
            if (elem !== null) {
                elem.html(String(value));
            }
        });
        Util.each(SessionStats.currentTotal, function(name, value) {
            var elem = e$('#mass_stat_' + name);
            if (elem !== null) {
                elem.html(String(value));
            }
        });
    }
    /**
     * @param {String} message
     * @param {Boolean} hasAjax
     */
    function sendMessage(message, hasAjax, continue_fn) {
        var $elt = $('#msgcontainer').empty().show();
        if ( Util.isString(message) ) {
            if ( hasAjax === true ) {
                Resources.getPicture('ajax_loader', 'span')
                .appendTo($elt).html(message).css('padding-left', 18);
            }
            else {
                $elt.html(message);
            }
            if ( Util.isFunc(continue_fn) ) {
                $elt.append(c$('a','href:#').text('click here to continue.').click(continue_fn));
            }
        } else {
            $elt.hide();
        }
    }

    /**
     * @param {Number} toCity
     * @param {Function} callback
     */
    function travelTo(toCity, callback) {
        if (gVar.Aborted) {
            return;
        }
        var trvmsg = 'Traveling to ${city}...';
        var errmsg = 'Unexpected city (#${trycount}), taveling to ${city} in %N% seconds.';
        var tmpl = {trycount: 1};
        
        // fix possible undefined city
        if ( global.city(toCity).enabled!==true ) {
            if ( gVar.CurrentCity === (toCity = (gVar.StartCity = 1)) ) {
                callback && callback();
                return;
            }
        }
        (function tryAgain() {
            if (gVar.Aborted) {
                return;
            }
            tmpl.trycount++;
            tmpl.city = global.city(toCity).name;
            
            sendMessage(Util.render(trvmsg, tmpl), true);
            
            MW.travel(toCity, function(city, htmlText) {
                if ((gVar.CurrentCity = parseInt(city)) === parseInt(toCity)) {
                    sendMessage();
                    addTravelCountdown();
                    callback && callback(htmlText);
                }
                else {
                    // so many tries, removing city and setting a new one.
                    if (tmpl.trycount > 3) {
                        Logger.error('Can\'t travel to '+global.city(toCity).name+' the city will be removed.');
                        global.city(toCity).enabled = false;
                        toCity = setNewCurrentCity() || 1;
                        tmpl.trycount = 0;
                        StatusTimer.start(Util.render(trvmsg, tmpl), 3, tryAgain);
                    } else {
                        StatusTimer.start(Util.render(errmsg, tmpl), 3, tryAgain);
                    }
                }
            }, null, 'fight');
        })();
    }
    /**
     * @param {Function} callback
     */
    function healPlayer(callback) {
        if (gVar.Aborted) {
            return;
        }
        var city, try_count = 0, powerPackTry = false;
        
        function tryHeal() {
            var now = (new Date()).getTime();
            if (gVar.AutoPaused === true) {
                stopAutoFight('AutoPaused.');
                gVar.AutoPaused = false;
                addFightResumeCountdown();
                return;
            }
            if ( (++try_count) < 4 ) {
                city = parseInt(options.healCity);
            }
            else if (city !== 1){
                options.healCity = (city=1);
                options.save();
            } else {
                StatusTimer.start('Error healing. Try again in %N% seconds.', 60, tryHeal);
                return;
            }
            if (gVar.ReadyToHeal > now) {
                if (!gVar.ForceHeal) {
                    gVar.ForceHeal = true;
                    if (options.stopAutomatic === true) {
                        addLog('AutoStop:<br>Your health fall down very quickly.', 'help', null, 'autostop');
                        sendMessage('Seem you\'re under attack!, AutoFight was stopped.');
                        stopAutoFight('AutoStopped.');
                        if (options.stopResume) {
                            addFightResumeCountdown(parseInt(options.stopResumeDelay||5));
                        }
                        return;
                    } 
                    else {
                        AutoTravelCounter.heal();
                    }
                }
                if (options.useHealPack && AttackerStats.powerPack.avail().hospital) {
                    usePowerPack('hospital', callback, function() {
                        AttackerStats.powerPack.on = false;
                        tryHeal();
                    });
                } else {
                    try_count--;
                    StatusTimer.start('Ready to heal in %N% seconds.', Math.ceil((gVar.ReadyToHeal-now)/1000), tryHeal);
                }
                return;
            }
            gVar.ForceHeal = false;
            
            if (city === 1 && city !== gVar.CurrentCity) {
                Heal('remote/' + MW.getIntURL('hospital', 'heal', gVar.CurrentCity) + '&xcity=1');
            }
            else if (city !== 0 && city !== gVar.CurrentCity) {
                travelTo(city, function() { 
                    Heal('remote/' + MW.getIntURL('hospital', 'heal', city)); 
                });
            }
            else {
                Heal('remote/' + MW.getIntURL('hospital', 'heal', city));
            }
        }

        function Heal(url) {
            sendMessage('Healing at ' + global.city(city).name + '...', true);
            httpAjaxRequest({'url':url, 'success': function(jsonData) {
                try {
                    updateUserFields(jsonData);
                    
                    if (AttackerStats.health < Math.min(options.healBelow,AttackerStats.maxHealth-100)) {
                        StatusTimer.start(jsonData.hospital_message+' try again in %N% seconds.', 5, tryHeal);
                    }
                    else {
                        gVar.ReadyToHeal = (new Date()).getTime() + (options.healTimer * 1000);
                        MW.getWaitHealTimer(function(timer) {
                            if (!isNaN(timer)) {
                                Logger.debug('ReadyToHeal set to: '+timer+' secs.');
                                gVar.ReadyToHeal = (new Date()).getTime() + (parseInt(timer)*1000);
                            }
                        });
                        addLog(jsonData.hospital_message, 'heal', null, 'healmessage');
                        setHealth(0, AttackerStats.healthPct);
                        updateAttackerHealthVal(AttackerStats.health);
                        updateStats();
                        if (gVar.CurrentCity !== gVar.StartCity && (options.travelToStartCity||city!==1)) {
                            travelTo(gVar.StartCity, callback);
                        }
                        else {
                            callback && callback();
                        }
                    }
                }
                catch(err) {
                    StatusTimer.start('Error healing. Try again in %N% seconds.', 5, tryHeal);
                }
            }});
        }
        tryHeal();
    }
    /**
     * Uses a PowerPack and attack new opponent if success.
     * @param {Function} success
     * @param {Function} fail
     */
    function usePowerPack(type, success, fail) {
        httpAjaxStopRequests();
        clearAllTimers();
        addAutoControls(true);
        sendMessage('Using PowerPack...', true);
        $('#mass_stat_powerpack').html(Util.setColor('Loading','green'));
        MW.usePack(function(data) {
            $('#mass_stat_powerpack').html(Util.setColor('Inactive','grey'));
            if (data) {
                updateUserFields(data);
                //AttackerStats.powerPack.time = data.timer;
                if (data && data.success) {
                    addLog(data.message,'help',null,'powerpack');
                    resetAllTimers();
                    success&&success();
                } else {
                    addLog('PowerPack:<br>Seem there was a server error.','help',null,'powerpack');
                    fail&&fail();
                }
            } else {
                AttackerStats.powerPack.on = false;
                reqSurvey(fail);
            }
        }, type);
    }
    // -----------------------------
    // MANUAL MODE
    // -----------------------------
    function manualAttack() {
        hideFightControls();

        if (AttackerStats.stamina < getStaminaSpendPerFight()) {
            showHelpPopup({
                icon: 'info',
                title: 'No stamina left',
                message: 'You need to have some stamina to attack.'
            });
            Events.runAway_click();
            return;
        }
        if (AttackerStats.health < PlayerList.current.requirements().health) {
            healPlayer(Attack);
            return;
        }
        Attack(false);
    }
    
    function checkBankMoney(callback) {
        var deposit = UserConfig.main.autoDeposit[gVar.CurrentCity];
        if (deposit && deposit.active === true) {
            if (AttackerStats.userCash > deposit.amount) {
                sendMessage('Depositing '+Util.setColor(Util.formatNum(AttackerStats.userCash),'green')+ '...', true);
                MW.deposit(gVar.StartCity, AttackerStats.userCash, function(result) {
                    if ( result ) {
                        $('#bank_item', '#items_logs').remove();
                        addLog(result, 'bank', null, 'deposit');
                    }
                    if ( Util.isFunc(callback) ) {
                        callback();
                    }
                });
                return;
            }
        } 
        if ( Util.isFunc(callback) ) {
            callback();
        }
    }

    // update player account stats
    function updateUserFields(data) {
        var user_fields;
        try {
            if (data.user_fields) {
                user_fields = data.user_fields;
                if (options.epicBattle===true&&data.questData&&data.questData.inClanBattle) {
                    if (gVar.EpicBattle) {
                        gVar.EpicBattle.update(data.questData.inClanBattle);
                    } else {
                        gVar.EpicBattle = new CSEpicBattle(data.questData.inClanBattle);
                    }
                } else {
                    gVar.EpicBattle = null;
                }
            }
            else if (/var user_fields/.test(data)) {
                eval(Util.substr(String(data),'var user_fields', 'user_fields_update'));
            }
            if (user_fields) {
                gVar.CurrentCity              = parseInt(user_fields['current_city_id']);
                AttackerStats.health          = parseInt(user_fields['user_health']);
                AttackerStats.maxHealth       = parseInt(user_fields['user_max_health']);
                AttackerStats.stamina         = parseInt(user_fields['user_stamina']);
                AttackerStats.maxStamina      = parseInt(user_fields['user_max_stamina']);
                AttackerStats.expToNextLevel  = parseInt(user_fields['exp_to_next_level']);
                AttackerStats.userCash        = parseInt(user_fields['user_cash']);
                AttackerStats.powerPack.ask   = parseInt(user_fields['powerPackAsk']) != 0;
                AttackerStats.powerPack.time1 = parseInt(user_fields['powerPackStaminaUse']);
                AttackerStats.powerPack.time2 = parseInt(user_fields['powerPackHealthUse']);
                AttackerStats.powerPack.count = parseInt(user_fields['powerPackCount']);
                AttackerStats.powerPack.on    = parseInt(user_fields['powerPackOn']) == 1;
                //AttackerStats.is_iced         = (parseInt(user_fields['xp_earned']) < 0);
                AttackerStats.healthPct = parseInt((AttackerStats.health*100)/AttackerStats.maxHealth);
            }
        }
        catch(err) {
            Logger.error(err);
        }
    }

    // request a survey for fast player update
    function reqSurvey(callback) {
        sendMessage('Updating...', true);
        httpAjaxRequest({
            url: 'remote/' + MW.getIntURL('survey', 'show_nps_survey'),
            success: function(jsonData) {
                sendMessage();
                updateUserFields(jsonData);
                updateStats();
                if (Util.isFunc(callback)) {
                    callback();
                }
                else {
                    preAttack();
                }
            }
        });
    }

    // -----------------------------
    // AUTOMATIC MODE
    // -----------------------------
    function getStaminaSpendPerFight() {
        return global.city(gVar.CurrentCity).ratio;
    }
    function setNewCurrentCity() {
        if (gVar.AttackInBattle===true) {
            return (gVar.StartCity = 1);
        }
        var cities = new Array();
        Util.each(options.travelTo, function(id, value) {
            id = Util.parseNum(id);
            if (global.city(id).enabled===true && gVar.StartCity !== id) {
                if ( value === true ) cities.push(id);
            } 
        });
        if (cities.length > 0) {
            gVar.StartCity = parseInt(cities[ Math.floor(Math.random() * cities.length) ]);
            return gVar.StartCity;
        }
        return false;
    }
    function resetAllTimers() {
        addTravelCountdown();
        addFightCountdown();
        addRivalsCountdown();
    }
    function clearAllTimers() {
        StatusTimer.clear();
        TravelCountdown.clear();
        ResumeCountdown.clear();
        FightCountdown.clear();
        RivalsCountdown.clear();
    }
    function addTravelCountdown() {
        TravelCountdown.clear();
        if (options.timerCityActive) {
            TravelCountdown.start(options.timerCityMins*60);
        }
    }
    /** @param {Number} mins_delay minutes. */
    function addFightResumeCountdown(mins_delay) {
        ResumeCountdown.clear();
        ResumeCountdown.start((mins_delay||options.timerFightResume)*60);
    }
    function addFightCountdown() {
        FightCountdown.clear();
        if (options.timerFightActive) {
            FightCountdown.start(options.timerFightMins*60);
        }
    }
    function addRivalsCountdown() {
        RivalsCountdown.clear();
        if (options.timerRivalsActive) {
            RivalsCountdown.start(options.timerRivalsMins*60);
        }
    }

    function publishCurrentIces(callback) {
        var bContinued = false;
        var continue_fn = function() {
            if ( bContinued === false ) {
                bContinued = true;
                sendMessage();
                callback();
            }
        };
        sendMessage('Paused while publishing your ices. ', false, continue_fn);
        AutoPublish.publishTo(options.publishTo, continue_fn);
    }
    
    function isWrongCity(callback) {
        if (options.travelToStartCity && gVar.CurrentCity !== gVar.StartCity) {
            travelTo(gVar.StartCity, function(htmlText) {
                if (gVar.AttackInBattle !== true) {
                    PlayerList.clear('isFList');
                    PlayerList.load(htmlText);
                }
                callback();
            });
            return true;
        } else {
            gVar.StartCity = gVar.CurrentCity;
            return false;
        }
    }
    
    function AttackEpicBattleFortress() {
        if (gVar.EpicBattleFortress.req > AttackerStats.stamina) {
            gVar.EpicBattle == null;
            AttackNewOpponent();
            return;
        }
        sendMessage('Attacking '+gVar.EpicBattle.opponentName+' Fortress...', true);
        httpAjaxRequest({
            url: gVar.EpicBattleFortress.url,
            liteLoad: 1,
            success: function(htmlText) {
                updateUserFields(htmlText);
                gVar.EpicBattleFortress = null;
                var message = h$(htmlText).find('.message_body:first').text();
                if (message) {
                    addLog(message,'help',null,'epicbattlefortress');
                }
                PlayerList.loadBattle(htmlText);
                AttackNewOpponent();
            }
        });
    }

    function AttackNewOpponent() {
        if (gVar.Aborted) {
            return;
        }
        if (options.collectClanFightXp && (AttackerStats.exp_fight>=AttackerStats.exp_fight_max) && 
           (AttackerStats.exp_fight_max > AttackerStats.exp_fight_min)) {
            MW.collectClanXP('exp_fight', function(msg) {
                if (msg) {
                    AttackerStats.exp_fight_min = AttackerStats.exp_fight_max;
                    addLog(msg,'help',null,'clanxp');
                } else {
                    options.collectClanFightXp = false;
                }
            });
        }
        if ( options.publishActive && AutoPublish.length() >= options.publishAfter ) {
            publishCurrentIces(AttackNewOpponent);
            return;
        }
        if (PlayerList.current) {
            updateCurrentOppStats();
            PlayerList.current = null;
        }
        if (gVar.AutoPaused === true) {
            stopAutoFight();
            gVar.AutoPaused = false;
            addFightResumeCountdown();
            return;
        }
        if (isWrongCity(AttackNewOpponent) === true) {
            return;
        }
        if (gVar.AttackInBattle !== true) {
            if (gVar.EpicBattle && gVar.EpicBattle.inBattle && gVar.EpicBattle.started) {
                addLog(gVar.EpicBattle.toString(),'help',null,'epicbattle');
                PlayerList.clear();
                gVar.AttackInBattle = true;
                setNewCurrentCity();
                refreshPlayerList(AttackNewOpponent);
                return;
            } else {
                if (gVar.AttackWhitelist === true) {
                    fromRandomWhiteList();
                    return;
                }
                if (gVar.AttackFamilyList === true) {
                    fromFamilyList();
                    return;
                }
            }
        } else {
            if (gVar.EpicBattle == null || gVar.EpicBattle.finished()) {
                if (gVar.EpicBattle) {
                    addLog(gVar.EpicBattle.toString(),'help',null,'epicbattle');
                }
                PlayerList.clear();
                gVar.AttackInBattle = false;
                refreshPlayerList(AttackNewOpponent);
                return;
            }
        }
        var opponent = PlayerList.setLastCurrent();
        // check if a valid opponent object
        if (gVar.AttackInBattle !== true) {
            if (!opponent) {
                PlayerList.clear();
                AutoTravelCounter.noTarget();
                refreshPlayerList(AttackNewOpponent);
                return;
            } else {
                AutoTravelCounter.reset('noTarget');
            }
        } else {
            if (!opponent) {
                if (options.epicBattleFortress===true && gVar.EpicBattleFortress) {
                    AttackEpicBattleFortress();
                } else {
                    PlayerList.clear();
                    StatusTimer.start('No opponents to attack, reloading epic battle in %N%...',
                    options.epicBattleRefresh, function() {
                        refreshPlayerList(AttackNewOpponent);
                    } );
                }
                return;
            }
        }
        // check if opponent is filtered
        if (opponent.isValid() !== true) {
            AttackNewOpponent();
            return;
        }
        if (PlayerList.length() < 4 && gVar.AttackInBattle !== true) {
            httpAjaxRequest({
                url: 'remote/' + MW.getIntURL('fight'),
                liteLoad: 1,
                success: PlayerList.load
            });
        }
        // attack
        setAttackNewTimer(function() {
            updateNewOpponent();
            preAttack();
        });
    }

    function preAttack() {
        if (gVar.Aborted === true || isWrongCity(preAttack) === true) {
            return;
        }
        if ( !Util.isSet(PlayerList.current) ) {
            AttackNewOpponent();
            return;
        }
        var opp = PlayerList.current;
        var opt = options;
        var lVar = {
            Health       : AttackerStats.health,
            Stamina      : AttackerStats.stamina,
            ToLevel      : AttackerStats.expToNextLevel,
            SessionIces  : AttackerStats.session_ices,
            RapidFire    : false,
            Delay        : (opt.stopResumeDelay > 0 ? opt.stopResumeDelay*60 : 300),
            MinHealth    : opp.requirements().health,
            MinStamina   : opp.requirements().stamina,
            StaminaPack  : AttackerStats.powerPack.avail().stamina,
            ToIce        : opp.attacksToIce()
        };
        opt.healBelow = Math.max(lVar.MinHealth, opt.healBelow);
        lVar.KeepSta  = Math.max((opt.stopKeepStaOn ? opt.stopKeepSta : 0), lVar.MinStamina);
        lVar.KeepHlt  = Math.max((opt.healActive ? opt.healBelow : 0), lVar.MinHealth);
        opp.powerAttack = (opt.attackPwr && opp.hasPwrAtkUrl());
        
        if (opt.attackUsePerFoe && opt.attackPerFoe <= opp.fights) {
            opp.skip('Limited attacks per foe reached: '+opp.fights);
        }
        if (opt.attackUseMax && lVar.ToIce > 0 && opt.attackMax < lVar.ToIce) {
            opp.skip('Out Maximum Attacks: '+lVar.ToIce);
        }
        if (opt.skipHealed && opp.startedHealthPct && opp.curHealthPct > opp.startedHealthPct) {
            opp.skip('Seem that was healed.');
        }
        if (opp.skipped === true) {
            checkBankMoney(AttackNewOpponent);
            return;
        }
        if (opp.powerAttack && (!opt.attackUseMax||opp.fights > 2)) {
            if (opt.rapidFireActive && lVar.ToIce > 4) {
                lVar.RapidFire = ( opp.curHealthPct < opt.rapidFireHealth );
            }
        }
        if ( opt.stopKeepExpOn && lVar.ToLevel < opt.stopKeepExp ) {
            lVar.Stop = 'AutoFight deactivated, you need '+lVar.ToLevel+' exp. points to levelup.';
        }
        else if ( lVar.Stamina < lVar.KeepSta ) {
            if ((lVar.Stamina < lVar.MinStamina) && lVar.StaminaPack) {
                checkBankMoney(function(){
                    usePowerPack('stamina', AttackNewOpponent, function(){
                        AttackerStats.powerPack.on = false;
                        preAttack();
                    });
                });
                return;
            }
            if (opt.stopResume) {
                lVar.Pause = 'No stamina left, continue in %N% seconds.';
            } else {
                lVar.Stop = 'AutoFight deactivated, No stamina left.';
            }
        }
        else if (gVar.ForceHeal) {
            healPlayer(preAttack);
            return;
        }
        else if ( lVar.Health < lVar.KeepHlt ) {
            if ( lVar.Stamina < opt.healMinSta && !lVar.StaminaPack) {
                if (lVar.Health < lVar.MinHealth) {
                    if (opt.stopResume) {
                        lVar.Pause = 'Heal was not possible due your low stamina, continue in %N% seconds.';
                    } else {
                        lVar.Stop = 'AutoFight deactivated, Heal was not possible due your low stamina.';
                    }
                }
            }
            else if ( opt.healActive !== true ) {
                if (opt.stopResume) {
                    lVar.Pause = 'Healing is set off, continue in %N% seconds.';
                } else {
                    lVar.Stop = 'AutoFight deactivated, Healing is set off.';
                }
            } 
            else if (opt.healAttacking || opp.fights===0 || lVar.Health < lVar.MinHealth) {
                healPlayer(preAttack);
                return;
            }
            
        }
        else if (opt.stopByIces && lVar.SessionIces >= opt.stopIceAmount) {
            lVar.Stop = 'AutoFight deactivated, you made '+lVar.SessionIces+' ices.';
        }
        
        if ( lVar.Stop ) {
            clearAllTimers();
            checkBankMoney(function() {
                sendMessage(lVar.Stop);
                stopAutoFight('AutoFight was stopped.');
            });
            return;
        }
        if ( lVar.Pause ) {
            clearAllTimers();
            checkBankMoney();
            addAutoControls(false, true);
            StatusTimer.start(lVar.Pause, lVar.Delay, function() {
                PlayerList.clear();
                reqSurvey(function() {
                    resetAllTimers();
                    AttackNewOpponent();
                });
            });
            return;
        }
        if (!/xw_controller=fight/i.test(opp.getAttackUrl())) {
            AttackNewOpponent();
            return;
        }
        if ( opp.fights > 0 ) {
            addSkipControl();
        }
        if ( lVar.RapidFire === true ) {
            AttackWithRapidFire();
        } else {
            Attack(true);
        }
    }

    function AttackWithRapidFire() {
        if (gVar.Aborted) {
            return;
        }
        var opp = PlayerList.current;
        var opt = options;
        var lVar = {
            Instance     : null,
            Url          : opp.getAttackUrl(),
            LevelUp      : false,
            AttackAgain  : true,
            Stopped      : false,
            Finished     : false,
            AtkMaxHealth : 100,
            DefMaxHealth : 100,
            Stamina      : AttackerStats.stamina,
            Health       : AttackerStats.health,
            MaxCount     : (opt.rapidFireAttacks=Math.max(Math.min(options.rapidFireAttacks, 80), 2)),
            ReqsLeft     : 0,
            TotalSent    : 0,
            AtkDamage    : 0,
            DefDamage    : 0,
            MinHealth    : opp.requirements().health,
            MinStamina   : opp.requirements().stamina,
            ToLevel      : AttackerStats.expToNextLevel,
            GoodRQ       : 0,
            BadRQ        : 0,
            timeout      : null
        };        
        if (options.attackUsePerFoe) {
            lVar.MaxCount = Math.max(parseInt((opt.attackPerFoe - opp.fights)/5), 2);
        }
        if (opt.healActive && opt.healAttacking) {
            lVar.MinHealth = Math.max(opt.healBelow, lVar.MinHealth);
        }
        if (opt.stopKeepStaOn) {
            lVar.MinStamina = Math.max(opt.stopKeepSta, lVar.MinStamina);
        }
        sendNewRFAttack();
        opt.rapidFireTiming = Math.max(Math.min(opt.rapidFireTiming, 1000), 333);
        lVar.Instance = setInterval(sendNewRFAttack, opt.rapidFireTiming);
        
        function sendNewRFAttack() {
            if (gVar.Aborted === true || lVar.TotalSent >= lVar.MaxCount) {
                stopRapidFire();
            }  else {
                lVar.ReqsLeft++;
                showRapidFireTitle(++lVar.TotalSent);
                sendMessage('Rapid Fire x'+lVar.TotalSent+' to '+opp.anchor()+'...', true);
                httpAjaxRequest({'url':lVar.Url,'liteLoad':1,'update':false,'success':rapidFireResponse});
            }
        }
        
        function stopRapidFire() {
            if (lVar.Stopped !== true) {
                lVar.Stopped = true;
                if (lVar.Instance) {
                    clearInterval(lVar.Instance);
                    lVar.Instance = null;
                }
            }
        }
        
        function rapidFireResponse(response) {
            lVar.ReqsLeft--;
            if (lVar.timeout) {
                clearTimeout(lVar.timeout);
                lVar.timeout = null;
            }
            if (gVar.Aborted === true) {
                return;
            }
            var resp = {'success': ERROR_BAD_RESPONSE};
            try {
	            resp = parseAttack( response, true );
                if (lVar.Health > AttackerStats.health) {
                    lVar.Health = AttackerStats.health;
                }
                if (lVar.Stamina > AttackerStats.stamina) {
                    lVar.Stamina = AttackerStats.stamina;
                }
                if (lVar.ToLevel > AttackerStats.expToNextLevel) {
                    lVar.ToLevel = AttackerStats.expToNextLevel;
                }
                if (gVar.ForceHeal || lVar.Stamina < lVar.MinStamina || lVar.Health < lVar.MinHealth
                || (opt.stopKeepExpOn && (lVar.ToLevel < opt.stopKeepExp))) {
                    stopRapidFire();
                }
            } catch (e) {
            	Logger.error(e.message);
            }
            if ( resp.success !== ERROR_SUCCESS ) {
                lVar.BadRQ++;
                
                if ( parseInt((lVar.BadRQ*100)/lVar.MaxCount) > 80 ) {
                    opp.skip('So many server errors.');
                    stopRapidFire();
                }
            }
            else {
                lVar.GoodRQ++;
                var defender = resp.fight_result ? resp.fight_result.defender : false;
                var attacker = resp.fight_result ? resp.fight_result.attacker : AttackerStats;
                
                lVar.AtkDamage += attacker.damage_dealt;
                lVar.DefDamage += defender.damage_dealt;
                
                if (lVar.DefMaxHealth > defender.current_health_pct) {
                    lVar.DefMaxHealth = defender.current_health_pct;
                }
                if (lVar.AtkMaxHealth > attacker.current_health_pct) {
                    lVar.AtkMaxHealth = attacker.current_health_pct;
                }
                
                attacker.damage_dealt = lVar.AtkDamage;
                attacker.current_health_pct = lVar.AtkMaxHealth;
                defender.damage_dealt = lVar.DefDamage;
                defender.current_health_pct = lVar.DefMaxHealth;
                
                if (resp.attack_again === false) {
                    lVar.AttackAgain = false;
                }
                if (opt.skipHealed && opp.startedHealthPct && defender.current_health_pct > opp.startedHealthPct) {
                    opp.skip('Seem that was healed.');
                    lVar.AttackAgain = false;
                }
                if (resp.levelUp_stop === true) {
                    lVar.AttackAgain = false;
                    lVar.LevelUp = true;
                }
                if (lVar.AttackAgain !== true) {
                    stopRapidFire();
                }
            }
            
            // completed
            if (lVar.ReqsLeft < 1 && lVar.Stopped === true && lVar.Finished === false) {
                lVar.Finished = true;
                if (opt.rapidFireAutoTiming === true && lVar.TotalSent > 4 && lVar.Health > 20) {
                    if (lVar.BadRQ > lVar.GoodRQ) {
                        if ((opt.rapidFireTiming += 50) < 1050) {
                            addLog('RapidFire Autotiming:<br>Very bad success rate, timing increased by 50.','help',null,'autotiming');
                        }
                    }
                    else if (parseInt((lVar.GoodRQ*100)/lVar.TotalSent) > 80) {
                        if ((opt.rapidFireTiming -= 50) > 350) {
                            addLog('RapidFire Autotiming:<br>Very good success rate, timing decreased by 50.','help',null,'autotiming');
                        }
                    }
                }
                AttackerStats.health = lVar.Health;
                AttackerStats.stamina = lVar.Stamina;
                AttackerStats.expToNextLevel = lVar.ToLevel;
                updateFightResponse(resp);
                
                if (lVar.AttackAgain && lVar.GoodRQ < 1) {
                    lVar.AttackAgain = false;
                    opp.skip('So many server errors.');
                }
                if (lVar.LevelUp === true) {
                    stopAutoFight('Leveled up.');
                    sendMessage('AutoFight deactivated, you Leveled UP!!.');
                }
                else if (lVar.AttackAgain) {
                    preAttack();
                }
                else {
                    addAutoControls();
                    checkBankMoney(AttackNewOpponent);
                }
            } else {
                lVar.timeout = setTimeout(function() {
                    lVar.timeout = null;
                    updateFightResponse(resp, true);
                }, 100);
            }
        }
    }

    function Attack( bAuto ) {
        if (gVar.Aborted) {
            return;
        }
        var url = PlayerList.current.getAttackUrl();
        var n_ToIce = PlayerList.current.attacksToIce();
        var retry = PlayerList.current.retries;
        var sAttackMessage = 'Attacking ' + PlayerList.current.anchor() + '...';
        if (PlayerList.current.retries > 0) {
            sAttackMessage += '<br>Retry #'+(PlayerList.current.retries+1)+'.';
        }
        if ( n_ToIce == 'Infinity' ) {
            sAttackMessage += '<br>Calculating attacks to ice...';
        }
        else if (n_ToIce > 0) {
            sAttackMessage += '<br>You need ~'+PlayerList.current.attacksToIce()+' attacks to ice.';
        }
        sendMessage(sAttackMessage, true);

        httpAjaxRequest({
            'url': url,
            'liteLoad': 1,
            'update': false,
            'success': function (htmlText) {
                if (gVar.Aborted) {
                    return;
                }
                var fr = {success:ERROR_BAD_RESPONSE};
                try {
                    fr = parseAttack(htmlText, bAuto !== true);
                    updateFightResponse(fr);
                } catch (e) {
                	Logger.error(e.message);
                }
                switch( fr.success ) {
                    case ERROR_BAD_RESPONSE:
                        if (bAuto) {
                            addAutoControls(true);
                            StatusTimer.start('There was an error, try again in %N% seconds.',8, preAttack);
                        }
                        else {
                            sendMessage('There was an error.');
                            addManualControls(true);
                        }
                        break;

                    case ERROR_NO_FIGHT_RESULT:
                        if (bAuto) {
                            if ( PlayerList.current.retries < options.attackRetries) {
                                PlayerList.current.retries++;
                                preAttack();
                            } else {
                                addAutoControls(true);
                                PlayerList.current.skip('So many server errors.');
                                AttackNewOpponent();
                            }
                        }
                        else {
                            sendMessage('This opponent can\'t be attacked, go back and try a different one.');
                            PlayerList.current = null;
                            addManualControls(true);
                        }
                        break;

                    case ERROR_SUCCESS:
                        if (bAuto) {
                            if (fr.levelUp_stop) {
                                stopAutoFight('Leveled up.');
                                sendMessage('AutoFight deactivated, you Leveled UP!!.');
                            }
                            else if (fr.attack_again) {
                                PlayerList.current.retries = 0;
                                setAttackTimer(preAttack);
                            } 
                            else {
                                addAutoControls();
                                checkBankMoney(AttackNewOpponent);
                            }
                        }
                        else {
                            updateCurrentOppStats();
                            //PlayerList.current = null;
                            checkBankMoney(addManualControls);
                        }
                        break;
                }
            },
            'error': function(errorText) {
                if (bAuto) {
                    addAutoControls(true);
                    StatusTimer.start('ERROR: '+errorText+', try again in %N% seconds.',8, preAttack);
                }
                else {
                    sendMessage('ERROR: '+errorText);
                    addManualControls(true);
                }
            }
        });
    }
    
    function updateAttackerHealthVal(val) {
        $('#bfatt_health', popupElt.content).html(val);
    }
    function updateAttackerStaminaVal(s, maxS) {
        $('#bfatt_stamina', popupElt.content).html(s);
        $('#bfatt_max_stamina', popupElt.content).html(maxS);
    }

    function setHealth( player_id, val ) {
        var target_width;
        var target_css;
        var defender_hp;
        var attacker_hp;
        var healthBarWidth = 215;
        var fill_bar;            

        function getHealthBarCss(healthPerc) {
            switch(true) {
                case (healthPerc < 34):  return 'hpbg_low';
                case (healthPerc < 67):  return 'hpbg_mid';
                default:  return 'hpbg_high';
            };
        }
        if( player_id == 0 ) {
            attacker_hp = Math.min(Math.max(val, 0), 100);
            fill_bar = $('#attacker_hp', popupElt.content);
            target_width = (attacker_hp/100) * healthBarWidth;
            target_css = getHealthBarCss(attacker_hp);
        } else {
            defender_hp = Math.min(Math.max(val, 0), 100);
            fill_bar = $('#defender_hp', popupElt.content);
            target_width = (defender_hp/100) * healthBarWidth;
            target_css = getHealthBarCss(defender_hp);
        }
        if (fill_bar.width() == target_width) {
            return;
        }
        if( val > 0 && val < 100) {
            fill_bar.stop(true).animate({ 'width': target_width + 'px' }, 400 );
        } else {
            fill_bar.stop(true).animate({ 'width': target_width + 'px' }, 100 );
        }
        fill_bar.removeClass('hpbg_low hpbg_mid hpbg_high');
        fill_bar.addClass(target_css);
    }

    function updateAttackerAttackVal(val, val_no_boost, used_boost) {
        if ($('#fightv2_boost_on', popupElt.content).hasClass('checked') || used_boost) {
            $('#attacker_attack', popupElt.content).html(val);
        } else {
            $('#attacker_attack', popupElt.content).html(val_no_boost);
        }
    }
    function updateNewOpponent() {
        $('#wrapper_defender', popupElt.content).empty();
        $('#defender_best_items .fightV2ItemList li', popupElt.content).empty();
        $('#divAttackerDmgTaken', popupElt.content).empty();
        $('#attacker_fight_status', popupElt.content).empty();
        $('#wrapper_info .fightV2_result', popupElt.content).empty();
    }

    function showOtherDamageTaken(player_id, val) {
        var damageDiv, labelSpan, damageSpan;
        if( player_id == 0 ) {
            damageDiv = $("#divAttackerOtherAtkId", popupElt.content);
            damageSpan = $("#spanAttackerOtherAtk", popupElt.content);
            labelSpan = $("#spanAttackerOtherAtkLabel", popupElt.content);
        } else {
            damageDiv = $("#divDefenderOtherAtkId", popupElt.content);
            damageSpan = $("#spanDefenderOtherAtk", popupElt.content);
            labelSpan = $("#spanDefenderOtherAtkLabel", popupElt.content);
        }
        if(!Util.isSet(val) || val == 0 ) {
            damageDiv.hide();
            damageSpan.html('');
            labelSpan.html('');
        } else {
            var real_val = -val;
            damageDiv.show();
            damageSpan.removeClass('fv2_dmg_negative');
            damageSpan.removeClass('fv2_dmg_positive');
            if( real_val <= 0 ) {
                damageSpan.html(real_val+'%');
                damageSpan.addClass('fv2_dmg_negative');
                labelSpan.html("other attackers");
            } else {
                /*damageSpan.html('+'+real_val+'%');*/
                damageSpan.html('');
                damageSpan.addClass('fv2_dmg_positive');
                labelSpan.html("healed");
            }
        }
    }
    function showDamageTaken(player_id, val) {

        var damageDiv;
        if( player_id == 0 ) {
            damageDiv = $("#divAttackerDmgTaken", popupElt.content);
        } else {
            damageDiv = $("#divDefenderDmgTaken", popupElt.content);
        }

        damageDiv.fadeIn(500);
        damageDiv.removeClass('fv2_dmg_negative fv2_dmg_positive');
        if( val < 0 ) {
            damageDiv.html(-val);
            damageDiv.css('color', 'green');
        } else {
            damageDiv.html('-'+val);
            damageDiv.css('color', 'red');
        }
    }

    function showBoostUsed(player_id, imgTag) {
        var divEl;
        if(player_id == 0) {
            divEl = $("#attacker_boost_used_tag", popupElt.content);
            $('#attacker_boost_used', popupElt.content).show();
        } else {
            divEl = $("#defender_boost_used_tag", popupElt.content);
            $('#defender_boost_used', popupElt.content).show();
        }

        divEl.html(imgTag);
    }


    function showBoostUsage(boostCount, nextBoostTag, askTimeout) {
        var c = 0;
        if (Util.isSet(boostCount) && boostCount > 0) {
            c = boostCount;
        }
        $('.boost_wgt_boost_count', popupElt.content).html(c);
        if( c > 0 ) {
            $('#fv2_boost_toggle_ask_btns', popupElt.content).hide();
        } else {
            $('#fv2_boost_toggle_ask_btns', popupElt.content).show();
            if( askTimeout > 0) {
                $('.fv2_boost_ask_allowed', popupElt.content).hide();
            } else {
                $('.fv2_boost_ask_allowed', popupElt.content).show();
            }
        }
        $('.boost_wgt_next_boost', popupElt.content).hide();
        if (Util.isSet(nextBoostTag) && nextBoostTag != '') {
            $('.boost_wgt_next_boost', popupElt.content).html(nextBoostTag);
            $('.boost_wgt_next_boost', popupElt.content).show();
        }
    }

    function showTopItems(player_id, topItems) {
        var divPre;
        if(player_id == 0) {
            divPre = 'attacker';
        } else {
            divPre = 'defender';
        }

        for (var i in topItems) {
            if (topItems[i] != '') {
                $('#' + divPre + 'attacker_best_item_' + i, popupElt.content).html(topItems[i]);
            }
        }
    }

    function showFightRewards(msg) {
        if (!Util.isSet(msg)) {
            $('#wrapper_info .fightV2_result', popupElt.content).empty();
            return;
        }
        $("#wrapper_info .fightV2_result", popupElt.content).html(msg)
        .find('.impulse_buy, #figthv2_moreinfo_btn').remove();
        $('#msgcontainer', popupElt.content).empty();
    }

    function showRapidFireTitle(target_count) {
        var divElt = $('#wrapper_info .fightV2_result', popupElt.content).empty();
        var titleElt = c$('div').text('RAPID FIRE!').css({
            'text-align'  : 'center',
            'margin-top'  : 6,
            'height'      : 25,
            'color'       : 'green',
            'font-size'   : 20,
            'font-weight' : 'bold',
            'font-family' : 'tahoma'
        });
        var numElt = c$('div').hide().text('x'+target_count).css({
            'text-align'  : 'center',
            'margin-top'  : 1,
            'color'       : 'yellow',
            'font-size'   : 24,
            'font-weight' : 'bold'
        });
        divElt.append(titleElt).append(numElt);

        numElt.fadeIn(options.rapidFireTiming-100, function() {
            titleElt.fadeOut(2000);
            numElt.fadeOut(2000);
        });
    }

    function showWonLost(isWin) {
        $('#attacker_fight_status', popupElt.content).css('opacity', 0).removeClass('good bad');
        $('#defender_fight_status', popupElt.content).css('opacity', 0).removeClass('good bad');

        if (Util.isSet(isWin) && isWin == true) {
            $('#attacker_fight_status', popupElt.content).html('Won').addClass('good');
            $('#defender_fight_status', popupElt.content).html('Lost').addClass('bad');
        } else {
            $('#attacker_fight_status', popupElt.content).html('Lost').addClass('bad');
            $('#defender_fight_status', popupElt.content).html('Won').addClass('good');
        }
        $('#attacker_fight_status', popupElt.content).stop(true).animate({ 'opacity': 1 }, 300 );
        $('#defender_fight_status', popupElt.content).stop(true).animate({ 'opacity': 1 }, 300 );
    }
    /**
     * Stop AutoFight and skip current fight with the specified reason.
     * @param {Object} reason
     */
    function stopAutoFight(reason) {
        gVar.Aborted = true;
        httpAjaxStopRequests();
        clearAllTimers();
        if (reason) {
            addAutoControls(true);
        }
        if (PlayerList.current) {
            PlayerList.current.skip(reason||'AutoFight stopped.');
            updateCurrentOppStats();
        }
        PlayerList.clear();
    }
    
    function showIcedOverlays(fight_data, opp, first_attack) {
        if( opp.ice_state > 0 ) {
            return true;
        }
        if( fight_data.you_just_killed ) {
            opp.ice_state = 1;
            $('#fv2_defender_overlay_killed', popupElt.content).show();
        } else if( fight_data.you_just_iced ) {
            opp.ice_state = 2;
            $('#fv2_defender_overlay_iced', popupElt.content).show();
        } else if ( fight_data.ice_was_just_stolen ) {
            opp.ice_state = 3;
            $('#fv2_defender_overlay_stolen', popupElt.content).show();
            $('.fv2_defender_overlay_name', popupElt.content).html(fight_data.thief_profile);
            $('.fv2_defender_overlay_level', popupElt.content).html(fight_data.thief_class);
            $('.fv2_defender_overlay_pic', popupElt.content).html(fight_data.thief_pic);
            $(fight_data.thief_btn)
            .removeAttr('onclick').attr('href','#').click(Events.revenge_click)
            .appendTo($("#fv2_defender_overlay_stolen_btn", popupElt.content).empty());
        } else {
            showIceState(fight_data.defender, first_attack);
            return false;
        }
        if( fight_data.show_ice_season ) {
            $('.fv2_defender_overlay_next_title', popupElt.content).html("Next Ice Prize");
            $('.fv2_defender_overlay_next_count', popupElt.content).html(fight_data.ices_so_far + '/' + fight_data.ices_target);
        } else {
            $('.fv2_defender_overlay_next_title', popupElt.content).html("Total Ices");
            $('.fv2_defender_overlay_next_count', popupElt.content).html(fight_data.total_ice_count);
        }
        return true;
    }
    function showIceState(defender, first_attack) {
        var ice_state_set = false;
        if (defender.iced_self) {
            $('#fv2_defender_you_iced', popupElt.content).css('display', 'none');
            $('#fv2_defender_iced_self', popupElt.content).css('display', 'block');
            $('#fv2_defender_was_iced', popupElt.content).css('display', 'none');
            ice_state_set = true;
        } else if (defender.is_iced) {
            $('#fv2_defender_you_iced', popupElt.content).css('display', 'none');
            $('#fv2_defender_iced_self', popupElt.content).css('display', 'none');
            $('#fv2_defender_was_iced', popupElt.content).css('display', 'block');
            ice_state_set = true;
        }
        if( !ice_state_set ) {
            $('#fv2_defender_you_iced', popupElt.content).css('display', 'none');
            $('#fv2_defender_iced_self', popupElt.content).css('display', 'none');
            $('#fv2_defender_was_iced', popupElt.content).css('display', 'none');
        }
    }
    function genFightWrapper(sHtml) {
        var e = $('#fight_wrapper', popupElt.content).html(sHtml);
        $('.fv2_btncontainer', e).empty();
        $('a.close', popupElt.content).remove();
        $('#fv2_wgt_open_button_wrapper, #fv2_wgt_button_open_link, #wrapper_items_won, .fv2_boost_ask_not_allowed', e).remove();
        //$('.boostcontainer', e).removeAttr('style').css('padding-top',15);
        //$('.boost_wgt_next_boost', e).removeAttr('style');
        $('#fightv2_boost_on, #fightv2_boost_off', e).removeAttr('onclick').removeClass('checked').css('cursor','pointer').click(Events.use_boost_click);
        $('#fightv2_boost_'+(gVar.AttackerUsedBoost?'on':'off'), e).addClass('checked');
        $('#attacker_health', popupElt.content).attr('id','bfatt_health').html(AttackerStats.health);
        $('#attacker_stamina', popupElt.content).attr('id','bfatt_stamina').html(AttackerStats.stamina);
        $('#attacker_max_stamina', popupElt.content).attr('id','bfatt_max_stamina').html(AttackerStats.maxStamina);
    }

    function toFightScreen() {
        $('#ctrlcontainer', popupElt.content).hide();
        $('#msgcontainer', popupElt.content).show();
        $('#fightmsgcontainer', popupElt.content).show();
        $('#options_wrapper', popupElt.content).hide();
        $('#opponents_table', popupElt.content).empty();
        $('#fv2_widget_wrapper', popupElt.content).show();
        $('#fight_wrapper', popupElt.content).html(Base64.decode(
            'PGRpdiBpZD0id3JhcHBlcl9hdHRhY2tlciIgY2xhc3M9InVzZXJib3giIHN0eWxlPSJmbG9hdDpsZWZ0O3Bvc2l0aW9uOnJlbGF0'+
            'aXZlOyI+PC9kaXY+CQ0KPGRpdiBpZD0iZmlnaHRfYnRuX3BhbmVsIiBzdHlsZT0iZmxvYXQ6IGxlZnQ7Ij4JDQoJPGRpdiBpZD0i'+
            'YXR0YWNrZXJfYmVzdF9pdGVtcyIgc3R5bGU9ImZsb2F0OiBsZWZ0OyI+DQoJCTxkaXYgY2xhc3M9InN1YmhkciI+VG9wIEl0ZW1z'+
            'PC9kaXY+DQoJPC9kaXY+CQ0KCTxkaXYgaWQ9IndyYXBwZXJfYWN0aW9ucyIgc3R5bGU9ImZsb2F0OiBsZWZ0OyI+DQoJCTxkaXYg'+
            'c3R5bGU9Im1hcmdpbi10b3A6IDEwcHg7dGV4dC1hbGlnbjpyaWdodDsgcGFkZGluZy1yaWdodDoxNXB4OyBoZWlnaHQ6IDIwcHg7'+
            'Ij48L2Rpdj4NCgkJPGRpdiBjbGFzcz0iZnYyX2J0bmNvbnRhaW5lciIgc3R5bGU9InBvc2l0aW9uOnJlbGF0aXZlOyAiPjwvZGl2'+
            'PgkJCQ0KCTwvZGl2Pg0KCTxkaXYgaWQ9ImRlZmVuZGVyX2Jlc3RfaXRlbXMiIHN0eWxlPSJmbG9hdDogbGVmdDsiPg0KCQk8ZGl2'+
            'IGNsYXNzPSJzdWJoZHIiPlRvcCBJdGVtczwvZGl2Pg0KCTwvZGl2PgkJDQoJPGRpdiBzdHlsZT0iY2xlYXI6IGJvdGg7Ij48L2Rp'+
            'dj4NCjwvZGl2PgkNCjxkaXYgaWQ9IndyYXBwZXJfZGVmZW5kZXIiIGNsYXNzPSJ1c2VyYm94IiBzdHlsZT0iZmxvYXQ6bGVmdDtw'+
            'b3NpdGlvbjpyZWxhdGl2ZTsiPjwvZGl2Pg0KPGRpdiBzdHlsZT0iY2xlYXI6IGJvdGg7Ij48L2Rpdj4J'
        )).show();
        $('#attacker_health', popupElt.content).attr('id','bfatt_health').html(AttackerStats.health);
        $('#attacker_stamina', popupElt.content).attr('id','bfatt_stamina').html(AttackerStats.stamina);
        $('#attacker_max_stamina', popupElt.content).attr('id','bfatt_max_stamina').html(AttackerStats.maxStamina);
        showDiv('events', '_list');
        showFightRewards();
    }

    function toStartScreen() {
        $('#ctrlcontainer', popupElt.content).show();
        $('#fightmsgcontainer', popupElt.content).hide();
        $('#fight_wrapper', popupElt.content).hide();
        $('#options_wrapper', popupElt.content).show();
        $('#msgcontainer', popupElt.content).empty().show();
        $('.fightV2_result', popupElt.content).empty();
        $('#attacker_boost_used', popupElt.content).empty();
        showDiv('opponents', '_list');
        hideFightControls();
        updateNewOpponent();
    }

    function addManualControls(bHideHealButton) {
        var wrapper = hideFightControls();
        var curr = PlayerList.current;

        if (curr && curr.alive && curr.hasAtkUrl()) {
            c$('div', 'fv2_button_row1').appendTo(wrapper)
            .append(b$('Attack again','class:short red fightV2AttackBtn').click(Events.attackAgain_click));
        }
        if (options.attackPwr && curr && curr.alive && curr.hasPwrAtkUrl()) {
            c$('div', 'fv2_button_row2').appendTo(wrapper)
            .append(b$('Power Attack','class:short red fightV2AttackBtn').click(Events.powerAttack_click));
        }
        if (wrapper.length < 2 && !bHideHealButton && AttackerStats.healthPct < 80) {
            c$('div', 'fv2_button_row3').appendTo(wrapper)
            .append(b$('Heal','class:short red fightV2AttackBtn').click(Events.heal_click));
        }
        c$('div', 'fv2_button_row3').appendTo(wrapper)
        .append(b$('Run Away', 'class:fightV2AttackBtn').click(Events.runAway_click));
    }

    function addAutoControls(onlyRunAway, bInstantHeal) {
        var wrapper = hideFightControls();
        if (wrapper.length < 1 || wrapper.is(':hidden')) {
            return;
        }
        if (onlyRunAway !== true) {
            c$('div', 'fv2_button_row1').appendTo(wrapper)
            .append(b$('Stop','class:short red fightV2AttackBtn').click(Events.stop_click));
        }
        if (onlyRunAway !== true && AttackerStats.healthPct < 80) {
            c$('div', 'fv2_button_row2').appendTo(wrapper)
            .append(b$('Heal','class:short red fightV2AttackBtn')
                .attr('instantheal', (bInstantHeal===true) ? '1': '0').click( Events.autoHeal_click ));
        }
        c$('div', 'fv2_button_row3').appendTo(wrapper)
        .append(b$('Run Away','class:fightV2AttackBtn').click(Events.runAway_click));
    }

    function addSkipControl() {
        var wrapper = hideFightControls();
        c$('div', 'fv2_button_row3').appendTo(wrapper)
        .append(b$('Stop','class:short red fightV2AttackBtn').click(Events.stop_click));
        c$('div', 'fv2_button_row1').appendTo(wrapper)
        .append(b$('Skip','class:short white fightV2AttackBtn').click(Events.skip_click));
    }

    function hideFightControls() {
        return $('#wrapper_actions .fv2_btncontainer').empty();
    }
    /**
     * Evaluate a fight result.
     * @param {String} htmlText
     * @param {Boolean} disableSkipChecks
     */
    function parseAttack(htmlText, disableSkipChecks) {
        var fight_result, fight_data;
        var bfirstAttack = false;
        var opponent = PlayerList.current;
        var attack_again = false;
        var bLevelUpStop = false;
        var bOpponentGetIced = false;
        
        updateUserFields(htmlText);
        
        if (Util.isSet(htmlText.fight_result)) {
            if (htmlText.clanXp&&htmlText.clanXp.exp_fight) {
                AttackerStats.exp_fight = htmlText.clanXp.exp_fight.xp;
                AttackerStats.exp_fight_max = htmlText.clanXp.exp_fight.xp_max;
            }
            fight_data = htmlText;
            if (fight_data.worstitems) {
                gVar.WorstItems = fight_data.worstitems;
            }
        }
        else if (/fight_controller/.test(htmlText)||gVar.AttackInBattle===true) {
            fight_data = new CSFightResult(htmlText);
        } else {
            return {'success': ERROR_NO_FIGHT_RESULT};
        }
        
        // level up
        if (fight_data.popup && /levelUpBg/.test(fight_data.popup) ) {
            if ( options.stopByLevelup ) {
                $('#popup_fodder').html(fight_data.popup);
                bLevelUpStop = true;
                setTimeout(function() {
                    $('.pop_bg, .pop_box', '#popup_fodder').show();
                    $('.pop_box', '#popup_fodder').css('z-index', 1001);
                },1000);
            }
        }
        // check it has a fight results
        if ( !(fight_result = fight_data.fight_result) ) {
            //Logger.error('parseAttack: ERROR_NO_FIGHT_RESULT');
            return {'success': ERROR_NO_FIGHT_RESULT};
        }

        function getLootQuantityPerCity() {
            if (fight_result.cash_city === gVar.CurrentCity) {
                return getStaminaSpendPerFight();
            }
            return 1;
        }
    
        opponent.alive = attack_again = 
        !( fight_result.defender.was_iced
        || fight_result.defender.is_iced
        || fight_result.defender.is_killed
        || fight_result.defender.iced_self
        || fight_result.defender.you_iced);

        if (fight_data.fight_wrapper) {
            genFightWrapper(fight_data.fight_wrapper.html());
            bfirstAttack = true;
            SessionStats.add('foesAttacked', 1);
            opponent.startedHealthPct = fight_result.defender.current_health_pct;
            opponent.setReqs(Util.parseJSON(fight_data.atkbtn_req)
            ,                Util.parseJSON(fight_data.poweratkbtn_req));
            opponent.attack_url = fight_data.atkbtn_boost_off;
            opponent.pwrattack_url = fight_data.poweratkbtn_boost_off;
            
            if (fight_data.opponent_icon) {
                opponent.image = fight_data.opponent_icon;
            }
        }
        
        var n_done = fight_result.defender.damage_dealt;
        var n_take = fight_result.attacker.damage_dealt;
        var b_goodCash = (fight_result.cash_city === gVar.StartCity);
        var n_grpAttack, n_grpDefense;
        
        if (fight_result.status != 'ok') {
            if (fight_result.status == 'failed') {
                if (Util.isSet(fight_result.error)) {
                    Logger.error('parseAttack: '+fight_result.error);
                }
            }
            return {'success': ERROR_NO_FIGHT_RESULT, 'first_attack': bfirstAttack};
        }
        
        gVar.AttackerUsedBoost = fight_result.use_boost;

        // Add loot
        if (Util.isSet(fight_result.loot)) {
            parseLootData(fight_result.loot, getLootQuantityPerCity());
        }
        if( Util.isSet(fight_result.socialMessageCards)) {
            parseLootData(fight_result.socialMessageCards, getLootQuantityPerCity());
        }

        // check ICE state
        if ( opponent.ice_state === 0 && !opponent.thief && !opponent.just_beated ) {
            if( fight_result.you_just_killed || fight_result.you_just_iced ) {
                bOpponentGetIced = opponent.just_beated = true;
                opponent.result_text = fight_result.you_just_killed
                ? 'You just killed and get x2 iced count.' 
                : 'You just iced and got your ice.';
                addIcedToLog(new CSIce(fight_result), opponent);
            } else if ( fight_result.ice_was_just_stolen ) {
                bOpponentGetIced = true;
                opponent.thief = new CSStolenIce(fight_result);
                opponent.result_text = 'But ice was just stolen by ' + opponent.thief.anchor();
                addStolenIceToLog(opponent);
            }
        }

        // APPLY STATS
        if (fight_result.ices_so_far)     { AttackerStats.season_ices   = fight_result.ices_so_far;     }
        if (fight_result.ices_target)     { AttackerStats.season_target = fight_result.ices_target;     }
        if (fight_result.total_ice_count) { AttackerStats.total_ices    = fight_result.total_ice_count; }
        
        opponent.exp = fight_result.experience;
        opponent.won = fight_result.power_attack.won;
        opponent.lost = fight_result.power_attack.lost;
        opponent.fights = (opponent.lost + opponent.won);
        opponent.cashFrom = fight_result.cash_city;
        opponent.cash = fight_result.cash;
        opponent.curHealthPct = fight_result.defender.current_health_pct;
        opponent.curOtherDamage = fight_result.defender.other_damage;
        
        AttackerStats.healthPct = fight_result.attacker.current_health_pct;
        AttackerStats.health = fight_result.attacker.health;
        AttackerStats.stamina = fight_result.attacker.stamina;
        
        if (fight_result.attacker.other_damage > 0) {
            addLog('You\'re under attack!<br>Trying to heal...','help',null,'underattack');
            gVar.ForceHeal = true;
        }
        
        if (fight_result.isWin) {
            // SKIP CHECKS
            AutoTravelCounter.reset('loses');
            if (gVar.AttackInBattle!==true && disableSkipChecks !== true && attack_again) {
                // skip is health is more than
                if ( options.skipUseHealth && fight_result.defender.current_health_pct > options.skipHealth ) {
                    opponent.skip('Health percentage:'+fight_result.defender.current_health_pct+'%');
                    //Logger.debug('skiping ' + opponent.name + ' health percentage is '+fight_result.defender.current_health_pct+'%');
                    attack_again = false;
                }
                // skip if no minimal cash
                if (options.skipUseMinCash && (opponent.cash < 1 || parseInt(opponent.cash/opponent.fights) < options.skipByMinCash)) {
                    opponent.skip('Cash gained per fight is too low.');
                    //Logger.debug('skiping ' + opponent.name + ' cash of: ' + n_currentCash + ' less than: '+ options.skipByMinCash);
                    attack_again = false;
                }
                // skip if cash from different city
                if (options.skipWrongCash && b_goodCash !== true) {
                    opponent.skip('Cash gained from different city.');
                    //Logger.debug('skiping ' + opponent.name + ' cash from different city.');
                    attack_again = false;
                }
                // skip if other damage
                if ( opponent.ice_state === 0 && options.skipUnderAttk && fight_result.defender.other_damage) {
                    if ( fight_result.defender.current_health_pct > 5 && fight_result.defender.other_damage > options.skipUnderAttkPct ) {
                        opponent.skip('So many damage from others ('+fight_result.defender.other_damage+'%).');
                        //Logger.debug('skiping ' + opponent.name + ' get so many damage from others.');
                        attack_again = false;
                    }
                }
            }
        } else {
            if (gVar.AttackInBattle===true && options.redIceEpicBattle===true) {
                if ( attack_again === true && opponent.autoRedIce !== true ) {
                    opponent.autoRedIce = true;
                    addLog('You lost but RedIce for EpicBattle is active.','blacklist',opponent.image);                    
                }
            }       
            else if ( options.redIceActive === true ) {
                if ( !bfirstAttack && opponent.attacksToIce() >= options.redIceMaxAttk ) {
                    opponent.skip('So many fights for RedIce:'+opponent.attacksToIce());
                    attack_again = false;
                }  
            }
            else if ( opponent.won > 0 && options.redIceAfterWon === true  ) {
                if ( attack_again === true && opponent.autoRedIce !== true ) {
                    opponent.autoRedIce = true;
                    addLog('You lost after win '+opponent.won+' figths.<br>Red Ice mode temporarily activated for '+opponent.anchor(),'blacklist',opponent.image);                    
                }
            } 
            else {
                if ( opponent.won < 1 ) {
                    // add enemy to blacklist
                    if ( options.get('useBlacklist') ) {
                        if (PlayerList.addCurrentToBlackList()) {
                            addLog('"The beast" '+opponent.anchor()+' is too strong!, added to BlackList.','blacklist',opponent.image);
                            if (opponent.fights > 0) opponent.skip('You lost, blacklisted.');
                        }
                    }
                    AutoTravelCounter.lose();
                }
                attack_again = false;
            }
        }
        return {
            'success'      : ERROR_SUCCESS,
            'opponent'     : opponent,
            'iced'         : bOpponentGetIced,
            'fight_result' : fight_result,
            'first_attack' : bfirstAttack,
            'attack_again' : attack_again,
            'levelUp_stop' : bLevelUpStop
        };
    }
    
    function updateCurrentOppStats() {
        var opp = PlayerList.current;
        if (opp.updated !== true) {
            opp.updated = true;
            addEventLog(opp);
            SessionStats.add('fightCount', opp.lost + opp.won);
            SessionStats.add('staminaSpend', getStaminaSpendPerFight() * opp.fights);
            SessionStats.add('expGained', opp.exp);
            SessionStats.add('cash', opp.cash, opp.cashFrom);
            if (opp.won > 0) {
                SessionStats.add('fightWon', opp.won);
            }
            if (opp.lost > 0) {
                SessionStats.add('fightLost', opp.lost);
            }
            if (gVar.AttackWhitelist!==true && gVar.AttackFamilyList!==true && gVar.AttackInBattle!==true) {
                if (opp.isRival !== true) {
                    if (opp.fights > 1 || opp.startedHealthPct > 0 || opp.ice_state > 0) {
                        AutoTravelCounter.reset('attack');
                    } else {
                        AutoTravelCounter.attack();
                    }
                }
            }
        }
        updateStats();
    }

    function updateFightResponse(response, bHideRewards) {
        if (!response || !response.fight_result) {
            return;
        }
        var fight_result = response.fight_result;
        // UPDATE FIGHT DATA
        setHealth(0, fight_result.attacker.current_health_pct);
        setHealth(1, fight_result.defender.current_health_pct);

        updateAttackerHealthVal(fight_result.attacker.health);
        updateAttackerAttackVal(fight_result.attacker.skill_atk, fight_result.attacker.skill_atk_no_boost, gVar.AttackerUsedBoost);
        updateAttackerStaminaVal(fight_result.attacker.stamina, fight_result.attacker.max_stamina);

        showDamageTaken( 0, fight_result.attacker.damage_dealt );
        showDamageTaken( 1, fight_result.defender.damage_dealt );

        showOtherDamageTaken( 0, fight_result.attacker.other_damage );
        showOtherDamageTaken( 1, fight_result.defender.other_damage );

        if (bHideRewards !== true) {
            showFightRewards(fight_result.fight_info_div);  
        }
        //updateFactionBar(fight_result.user_stats.faction_module);
        showWonLost(fight_result.isWin);

        $('#defender_boost_used, #attacker_boost_used', popupElt.content).hide();
        if (Util.isSet(fight_result.attacker.boost_used_tag) && fight_result.attacker.boost_used_tag != '') {
            showBoostUsed(0, fight_result.attacker.boost_used_tag);
        }
        if (Util.isSet(fight_result.defender.boost_used_tag) && fight_result.defender.boost_used_tag != '') {
            showBoostUsed(1, fight_result.defender.boost_used_tag);
        }
        showBoostUsage(fight_result.boost.total_qty, fight_result.boost.next, fight_result.boost.ask_timeout, fight_result.boost.qty_array);
        showIcedOverlays(fight_result, response.opponent, response.first_attack);
        showTopItems(0, fight_result.attacker.top_items);
        showTopItems(1, fight_result.defender.top_items);
    }
    
    function setHardLevelRange() {
        var levels = new Object();
        var level = {min: 0, max: 0};
        var hardest = 0, lowest = 0;
        var messages = {
            insuficient: 'Cant determinate your hardest level range.<br>'
            + 'Keep fighting and adding enemies to blacklist, then try again.',
            result: 'Your hardest level range is <span style="color:red;">${min} - ${max}</span>.'
            + '<br>Would you like to AutoConfigure your level range with this values?'
        };
        if (PlayerList.blackList.length() < 200) {
            showHelpPopup({icon: 'info', message: messages.insuficient});
            return;
        }
        PlayerList.blackList.each(function(id, name) {
            var lv = parseInt(Util.doRgx(/level\s(\d+)/, name).$1);
            if (Util.isSet(levels[lv])) { levels[lv]++; } else { levels[lv] = 1 }
        });
        Util.each(levels, function(lv, num) {
            if (num > hardest) { hardest = num; }
            if (num > lowest && num < hardest) { lowest = num; }
        });
        if (hardest < 3) {
            showHelpPopup({icon: 'info', message: messages.insuficient});
            return;
        }
        Util.each(levels, function(lv, num) {
            lv = parseInt(lv);
            if (num == lowest || num == hardest) {
                if (level.min > lv || level.min == 0) { level.min = lv; }
                if (level.max < lv || level.max == 0) { level.max = lv; }
            }
        });
        showAskPopup('Hardest Level Range', Util.render(messages.result, level), function() {
            options.levelRangeActive = true;
            options.levelRangeMin    = level.min;
            options.levelRangeMax    = level.max;
            options.levelRangeMethod = 'skip';
            options.toDomElements();
        });
    }

    function Initialize() {
        try {
    	    if (unsafeWindow.FightV2) {
                Logger.debug('Killing FightV2 Class.');
                if (unsafeWindow.CloseJS) {
                    unsafeWindow.CloseJS();
                }
                unsafeWindow.FightV2 = null;
            }
        } catch (e) {}
        
        genMainDom();
        PlayerList.blackList = new CSList('blackList');
        PlayerList.whiteList = new CSList('whiteList');
        PlayerList.enmClanList = new CSList('enmClanList');
        PlayerList.frdClanList = new CSList('frdClanList');
        AutoTravelCounter = new CSAutoTravelCounter();
        AutoPublish = new CSAutoPublish();
        SessionStats = new CSSessionStats();
        AttackerStats.exp_per_sta = {toString: SessionStats.expPerSta};
        SessionStats.load();

        TravelCountdown = new Countdown({
            success: function() {
                var new_city = setNewCurrentCity();
                if (new_city !== false) {
                    addLog('AutoTravel will force to fight in '+global.city(new_city).name+'.', 'help', null, 'autotravel');
                }
            } ,
            step: function(n, time) {
                $('#mass_stat_timetotravel').html(time);
            },
            stop: function() {
                $('#mass_stat_timetotravel').html(Util.setColor('Inactive','grey'));
            }
        });
        ResumeCountdown = new Countdown({
            success: function() {
                gVar.Aborted = false;
                sendMessage('Updating...', true);
                reqSurvey(AttackNewOpponent);
                resetAllTimers();
            },
            step: function(n, time) {
                $('#msgcontainer').html('Resume AutoFight in: '+time);
            }
        });
        FightCountdown = new Countdown({
            success: function() {
                gVar.AutoPaused = true;
                $('#mass_stat_timetopause').html(Util.setColor('Waiting','green'));
            },
            step: function(n, time) {
                $('#mass_stat_timetopause').html(time);
            },
            stop: function() {
                $('#mass_stat_timetopause').html(Util.setColor('Inactive','grey'));
            }
        });
        RivalsCountdown = new Countdown({
            success: function() {
                if (gVar.AttackInBattle === true) {
                    return;
                }
                $('#mass_stat_loadrivals').html(Util.setColor('Loading','green'));
                httpAjaxRequest({url: 'remote/' + MW.getIntURL('fight') + '&tab=1',
                    liteLoad: 1,
                    success: function(htmlText) {
                        var message, result = PlayerList.load(htmlText);
                        if (result < 1) {
                            message = 'Rival list successfully loaded:<br>No Rivals has been added.';
                        } else {
                            message = 'Rival list successfully loaded:<br>Added '+result+' rivals to be attacked in next rounds.';
                        }
                        addLog(message, 'help', null, 'rivals');
                        addRivalsCountdown();
                    }
                });
            },
            step: function(n, time) {
                $('#mass_stat_loadrivals').html(time);
            },
            stop: function() {
                $('#mass_stat_loadrivals').html(Util.setColor('Inactive','grey'));
            }
        });
        
        showDiv('items', '_logs');
        toStartScreen();
        sendMessage('Loading...', true);
        
        httpAjaxRequest({
            url: 'remote/' + MW.getIntURL('survey', 'show_nps_survey'),
            success: function(jsonData)
            {
                if (popupElt.content.length < 1) {
                    return;
                }
                try {
                    updateUserFields(jsonData);
                    updateStats();
                    // get players from fight_controller page
                    if ($('#inner_page').attr('class') == 'fight_controller') {
                        PlayerList.load($('#inner_page').html());
                        if (PlayerList.length() > 0) {
                            genEnemyListDom();
                            sendMessage();
                            $('#ctrlcontainer', popupElt.content).show();
                            return;
                        }
                    } else {
                        // if no opponents, refresh.
                        Events.refresh_click();
                    }
                } catch (e) {
                    sendMessage('Error loading stats, please Close and Open again.');
                    return;
                }
            }
        });

        options.toDomElements();
        popupElt.applyCustomClass();
        popupElt.show();
    }

    popupElt.addBase64Style(
        'I2JhdHRsZWZpZWxkX3BvcHVwIC5ibGFja19ib3ggew0KCWZvbnQtd2VpZ2h0OiBib2xkOyANCgljb2xvcjogcmdiKDIwOCwgMjA4'+
        'LCAyMDgpOyANCglib3JkZXI6IDFweCBzb2xpZCByZ2IoMTUzLCAxNTMsIDE1Myk7IA0KCWJhY2tncm91bmQtY29sb3I6IGJsYWNr'+
        'OyANCglmb250LXNpemU6IDE0cHg7DQp9DQojYmF0dGxlZmllbGRfcG9wdXAgLm1hZmlhX2F0dGFjayAgew0KCWJhY2tncm91bmQ6'+
        'IHVybChodHRwOi8vbXdmYi5zdGF0aWMuemduY2RuLmNvbS9td2ZiL2dyYXBoaWNzL2ljb25fbWFmaWFfYXR0YWNrXzIyeDE2XzAx'+
        'LmdpZikgbm8tcmVwZWF0IDBweCA1MCU7DQoJcGFkZGluZy1sZWZ0OiAxOXB4Ow0KICAgIGJhY2tncm91bmQtc2l6ZTogMTZweDsN'+
        'CiAgICAtbW96LWJhY2tncm91bmQtc2l6ZTogMTZweDsNCn0NCiNiYXR0bGVmaWVsZF9wb3B1cCAubWFmaWFfZGVmZW5zZSB7DQoJ'+
        'YmFja2dyb3VuZDogdXJsKGh0dHA6Ly9td2ZiLnN0YXRpYy56Z25jZG4uY29tL213ZmIvZ3JhcGhpY3MvaWNvbl9tYWZpYV9kZWZl'+
        'bnNlXzIyeDE2XzAxLmdpZikgbm8tcmVwZWF0IDBweCA1MCU7DQoJcGFkZGluZy1sZWZ0OiAxOXB4Ow0KICAgIGJhY2tncm91bmQt'+
        'c2l6ZTogMTZweDsNCiAgICAtbW96LWJhY2tncm91bmQtc2l6ZTogMTZweDsNCn0NCiNiYXR0bGVmaWVsZF9wb3B1cCAuc3RhdF90'+
        'cmF2ZWwgIHsgDQoJYmFja2dyb3VuZDogdXJsKGh0dHA6Ly9td2ZiLnN0YXRpYy56eW5nYS5jb20vbXdmYi9ncmFwaGljcy9pY29u'+
        'X3RyYXZlbF8zMngxN18wMS5naWYpIG5vLXJlcGVhdCAwcHggNTAlOw0KCXBhZGRpbmctbGVmdDogMTlweDsNCiAgICBiYWNrZ3Jv'+
        'dW5kLXNpemU6IDE2cHg7DQogICAgLW1vei1iYWNrZ3JvdW5kLXNpemU6IDE2cHg7DQp9DQojYmF0dGxlZmllbGRfcG9wdXAgLnN0'+
        'YXRfdmljdG9yeSB7DQoJYmFja2dyb3VuZDogdXJsKGh0dHA6Ly9td2ZiLnN0YXRpYy56eW5nYS5jb20vbXdmYi9ncmFwaGljcy92'+
        'aWN0b3J5X2ljb24uZ2lmKSBuby1yZXBlYXQgMHB4IDUwJTsNCglwYWRkaW5nLWxlZnQ6IDE5cHg7DQogICAgYmFja2dyb3VuZC1z'+
        'aXplOiAxNnB4Ow0KICAgIC1tb3otYmFja2dyb3VuZC1zaXplOiAxNnB4Ow0KfQ0KI2JhdHRsZWZpZWxkX3BvcHVwIC5zdGF0X3Bv'+
        'd2VycGFjayB7DQogICAgYmFja2dyb3VuZDogdXJsKGh0dHA6Ly9td2ZiLnN0YXRpYy56eW5nYS5jb20vbXdmYi9ncmFwaGljcy9I'+
        'UF9wb3dlcnBhY2sucG5nKSBuby1yZXBlYXQgMHB4IDUwJTsNCiAgICBiYWNrZ3JvdW5kLXNpemU6IDE2cHggMTZweDsNCiAgICAt'+
        'bW96LWJhY2tncm91bmQtc2l6ZTogMTZweDsNCiAgICBwYWRkaW5nLWxlZnQ6IDE5cHg7DQp9DQojYmF0dGxlZmllbGRfcG9wdXAg'+
        'LnN0YXRfaWNlZCB7DQoJYmFja2dyb3VuZDogdXJsKGh0dHA6Ly9td2ZiLnN0YXRpYy56Z25jZG4uY29tL213ZmIvZ3JhcGhpY3Mv'+
        'bWFwX2Jhc2VkX2pvYnMvbWFzdGVyeV9zdGFyc19tZWRfODF4MzBfMDIucG5nKSBuby1yZXBlYXQgLTY0cHggLTEzcHg7DQoJcGFk'+
        'ZGluZy1sZWZ0OiAxOXB4Ow0KfQ0KI2JhdHRsZWZpZWxkX3BvcHVwIC5zdGF0X2tpbGwgew0KCWJhY2tncm91bmQ6IHVybChodHRw'+
        'Oi8vbXdmYi5zdGF0aWMuemduY2RuLmNvbS9td2ZiL2dyYXBoaWNzL21hcF9iYXNlZF9qb2JzL21hc3Rlcnlfc3RhcnNfbWVkXzgx'+
        'eDMwXzAyLnBuZykgbm8tcmVwZWF0IC00N3B4IDJweDsNCglwYWRkaW5nLWxlZnQ6IDE5cHg7DQp9DQojYmF0dGxlZmllbGRfcG9w'+
        'dXAgI2Z2Ml93aWRnZXRfd3JhcHBlciB7DQogICAgYmFja2dyb3VuZDogYmxhY2sgdXJsKCdodHRwOi8vbXdmYi5zdGF0aWMuemdu'+
        'Y2RuLmNvbS9td2ZiL2dyYXBoaWNzL2ZpZ2h0L3YyL2ZpZ2h0X21haW5fYmFja2dyb3VuZC5qcGcnKSB0b3AgbGVmdCBuby1yZXBl'+
        'YXQ7DQogICAgd2lkdGg6IDc1MHB4Ow0KCWhlaWdodDogMzUwcHg7DQoJYm9yZGVyOiAwcHg7DQogICAgdG9wOiAwcHg7DQogICAg'+
        'bGVmdDogMHB4OyANCglwb3NpdGlvbjogcmVsYXRpdmU7DQp9DQojYmF0dGxlZmllbGRfcG9wdXAgI29wdGlvbnNfd3JhcHBlciB7'+
        'DQoJYmFja2dyb3VuZDogYmxhY2sgdXJsKGh0dHA6Ly9td2ZiLnN0YXRpYy56eW5nYS5jb20vbXdmYi9ncmFwaGljcy9zb2NpYWxt'+
        'aXNzaW9ucy9iZ190ZXh0dXJlX21vZHVsZS5qcGcpIG5vLXJlcGVhdCBzY3JvbGwgNTAlIDAlOw0KCXBhZGRpbmc6IDBweCA1cHg7'+
        'DQoJaGVpZ2h0OiAxMDAlOw0KfQ0KI2JhdHRsZWZpZWxkX3BvcHVwICNmaWdodF93cmFwcGVyIGRpdi51c2VyYm94IHsNCgl3aWR0'+
        'aDogMjMycHg7DQoJaGVpZ2h0OiAzNDVweDsNCgliYWNrZ3JvdW5kOiB1cmwoJ2h0dHA6Ly9td2ZiLnN0YXRpYy56Z25jZG4uY29t'+
        'L213ZmIvZ3JhcGhpY3MvZmlnaHQvdjIvZmlnaHRfcGxheWVyYmFja2dyb3VuZC5wbmcnKSAwIDM1cHggbm8tcmVwZWF0Ow0KCXRl'+
        'eHQtYWxpZ246IGNlbnRlcjsNCglmbG9hdDogbGVmdDsNCiAgICBwb3NpdGlvbjogcmVsYXRpdmU7DQp9DQojYmF0dGxlZmllbGRf'+
        'cG9wdXAgI2F0dGFja2VyX2ZpZ2h0X3N0YXR1cywgI2RlZmVuZGVyX2ZpZ2h0X3N0YXR1cyB7DQoJaGVpZ2h0OiAzNnB4Ow0KCWZv'+
        'bnQtc2l6ZTogMjhweDsNCn0NCiNiYXR0bGVmaWVsZF9wb3B1cCAgI2F0dGFja2VyX2ZpZ2h0X3N0YXRzIHsNCgltYXJnaW46IDEw'+
        'cHggYXV0bzsNCgl3aWR0aDogMjAwcHg7DQp9DQojYmF0dGxlZmllbGRfcG9wdXAgICNhdHRhY2tlcl9waWMgew0KCWZsb2F0OiBs'+
        'ZWZ0Ow0KCXRleHQtYWxpZ246IHJpZ2h0Ow0KCXdpZHRoOiAxNDdweDsNCn0NCiNiYXR0bGVmaWVsZF9wb3B1cCAuZGl2SFBCYXIg'+
        'ew0KCWJhY2tncm91bmQ6IHVybCgnaHR0cDovL213ZmIuc3RhdGljLnpnbmNkbi5jb20vbXdmYi9ncmFwaGljcy9maWdodC92Mi9m'+
        'aWdodF9oZWFsdGhfZ3JleS5wbmcnKSB0b3AgbGVmdCByZXBlYXQteDsNCgl3aWR0aDogMjE1cHg7DQoJaGVpZ2h0OiAyM3B4Ow0K'+
        'CW1hcmdpbjogNXB4IGF1dG87DQp9DQojYmF0dGxlZmllbGRfcG9wdXAgI2ZpZ2h0X2J0bl9wYW5lbCB7DQoJaGVpZ2h0OiAzNTBw'+
        'eDsNCglmbG9hdDogbGVmdDsNCn0NCiNiYXR0bGVmaWVsZF9wb3B1cCAjYXR0YWNrZXJfYmVzdF9pdGVtcywgI2RlZmVuZGVyX2Jl'+
        'c3RfaXRlbXMgew0KCXdpZHRoOiA2NXB4Ow0KCXRleHQtYWxpZ246IGNlbnRlcjsNCgltYXJnaW4tdG9wOiA0NXB4Ow0KfQ0KI2Jh'+
        'dHRsZWZpZWxkX3BvcHVwICN3cmFwcGVyX2FjdGlvbnMgew0KCXdpZHRoOiAxMzBweDsNCn0NCiNiYXR0bGVmaWVsZF9wb3B1cCAj'+
        'd3JhcHBlcl9kZWZlbmRlciB7DQoJbWFyZ2luOiA1cHggNXB4IDAgNXB4Ow0KfQ0KI2JhdHRsZWZpZWxkX3BvcHVwICNmaWdodF9i'+
        'dG5fcGFuZWwgZGl2LmJvb3N0Y29udGFpbmVyIHsNCgl3aWR0aDogMTg1cHg7DQoJbWFyZ2luOiAycHggMCAwIDQwcHg7DQoJdGV4'+
        'dC1hbGlnbjogY2VudGVyOw0KCW1pbi1oZWlnaHQ6IDc4cHg7DQp9DQojYmF0dGxlZmllbGRfcG9wdXAgI2ZpZ2h0X2J0bl9wYW5l'+
        'bCBkaXYuYm9vc3Rjb250YWluZXIgew0KCXRleHQtYWxpZ246IGNlbnRlcjsNCn0NCiNiYXR0bGVmaWVsZF9wb3B1cCAubGlzdGVk'+
        'IHsNCgliYWNrZ3JvdW5kOiB0cmFuc3BhcmVudCB1cmwoaHR0cDovL213ZmIuc3RhdGljLnpnbmNkbi5jb20vbXdmYi9ncmFwaGlj'+
        'cy92My9pY29uX3dpc2hsaXN0X2FkZF8xOXgxNF8wMS5naWYpIG5vLXJlcGVhdCAtMXB4IDUwJTsNCglwYWRkaW5nLWxlZnQ6IDE5'+
        'cHg7DQp9DQojYmF0dGxlZmllbGRfcG9wdXAgI2ZpZ2h0T3B0IHsNCgltYXJnaW46IDBweDsNCgl3aWR0aDogNzM4cHg7DQp9DQoj'+
        'YmF0dGxlZmllbGRfcG9wdXAgI2ZpZ2h0T3B0IHVsIHsNCglsaXN0LXN0eWxlLXR5cGU6IG5vbmU7DQoJaGVpZ2h0OiAxMDAlOw0K'+
        'CXdpZHRoOiAxMDAlOw0KCW1hcmdpbjogMTBweCAwcHggMHB4Ow0KCXBhZGRpbmc6IDBweDsNCgl0ZXh0LWFsaWduOiBsZWZ0Ow0K'+
        'CW92ZXJmbG93OiBhdXRvOw0KfQ0KI2JhdHRsZWZpZWxkX3BvcHVwICNmaWdodE9wdCB1bCBsaSB7DQoJZGlzcGxheTogYmxvY2s7'+
        'DQoJbWFyZ2luOiAwcHggMHB4IDBweCA1cHg7DQoJcGFkZGluZzogMHB4Ow0KCWhlaWdodDogMjhweDsNCgltYXgtaGVpZ2h0OiAy'+
        'OHB4Ow0KCW92ZXJmbG93OiBoaWRkZW47DQp9DQojYmF0dGxlZmllbGRfcG9wdXAgI3dyYXBwZXJfaW5mbyB7DQoJYmFja2dyb3Vu'+
        'ZDogdXJsKCdodHRwOi8vbXdmYi5zdGF0aWMuemduY2RuLmNvbS9td2ZiL2dyYXBoaWNzL2ZpZ2h0L3YyL2ZpZ2h0X2xvb3R0cmF5'+
        'X3NsaXZlci5qcGcnKSB0b3AgbGVmdCByZXBlYXQteDsNCgloZWlnaHQ6IDEyMHB4Ow0KCW1hcmdpbi10b3A6IDBweDsNCgltYXJn'+
        'aW4tcmlnaHQ6IGF1dG87DQoJbWFyZ2luLWJvdHRvbTogMHB4Ow0KCW1hcmdpbi1sZWZ0OiBhdXRvOw0KCWJvcmRlci1ib3R0b206'+
        'IDFweCBzb2xpZCAjMzMzOw0KfSANCiNiYXR0bGVmaWVsZF9wb3B1cCAjd3JhcHBlcl9pbmZvIC5maWdodFYyX21zZ19jdHJsIHsN'+
        'CglmbG9hdDogbGVmdDsNCgl3aWR0aDogNDkwcHg7DQoJaGVpZ2h0OiAxMDBweDsNCgltYXJnaW46IDE0cHggMTFweCAwOw0KCW92'+
        'ZXJmbG93OiBoaWRkZW47DQoJcG9zaXRpb246IHJlbGF0aXZlOw0KCXotaW5kZXg6IDA7DQp9DQojYmF0dGxlZmllbGRfcG9wdXAg'+
        'I3dyYXBwZXJfaW5mbyAjY3RybGNvbnRhaW5lciB7DQoJaGVpZ2h0OiA0MHB4Ow0KfQ0KI2JhdHRsZWZpZWxkX3BvcHVwICN3cmFw'+
        'cGVyX2luZm8gLmZpZ2h0VjJfcmVzdWx0IHsNCglmbG9hdDogcmlnaHQ7DQoJYmFja2dyb3VuZDogdXJsKCdodHRwOi8vbXdmYi5z'+
        'dGF0aWMuemduY2RuLmNvbS9td2ZiL2dyYXBoaWNzL2ZpZ2h0L3YyL2ZpZ2h0X2xvb3R0cmF5X2xpbmUucG5nJykgMCA1MCUgbm8t'+
        'cmVwZWF0Ow0KCXdpZHRoOiAyMTBweDsNCgloZWlnaHQ6IDEwMHB4Ow0KCXBhZGRpbmc6IDEwcHggNXB4IDEwcHggMTVweDsNCn0N'+
        'CiNiYXR0bGVmaWVsZF9wb3B1cCAjd3JhcHBlcl9sb2dfc3RhdHMgew0KCWJhY2tncm91bmQtY29sb3I6IGJsYWNrOw0KCWJhY2tn'+
        'cm91bmQtaW1hZ2U6IHVybChodHRwOi8vbXdmYi5zdGF0aWMuenluZ2EuY29tL213ZmIvZ3JhcGhpY3MvaXRhbHlfZDQuanBnKTsN'+
        'CgliYWNrZ3JvdW5kLXBvc2l0aW9uOiA1MCUgMCU7DQoJYmFja2dyb3VuZC1yZXBlYXQ6IG5vLXJlcGVhdDsNCgloZWlnaHQ6IDQ1'+
        'MHB4Ow0KfQ0KI2JhdHRsZWZpZWxkX3BvcHVwICN3cmFwcGVyX2xvZ19zdGF0cyAuZmlnaHRWMl9zdGF0cyB7DQoJYmFja2dyb3Vu'+
        'ZDogdXJsKCdodHRwOi8vbXdmYi5zdGF0aWMuemduY2RuLmNvbS9td2ZiL2dyYXBoaWNzL2ZpZ2h0L3YyL2ZpZ2h0X2xvb3R0cmF5'+
        'X2xpbmUucG5nJykgMCA1MCUgbm8tcmVwZWF0Ow0KCWZsb2F0OiByaWdodDsNCgloZWlnaHQ6IDk3JTsNCgl3aWR0aDogMjEwcHg7'+
        'DQoJcGFkZGluZzogMTBweCA1cHggMTBweCAxNXB4Ow0KCXRleHQtYWxpZ246IGxlZnQ7DQp9DQojYmF0dGxlZmllbGRfcG9wdXAg'+
        'I3dyYXBwZXJfbG9nX3N0YXRzIC5maWdodFYyX3N0YXRzIGRsIHsNCgloZWlnaHQ6IDIwcHg7DQoJbWFyZ2luOiAwcHg7DQp9DQoj'+
        'YmF0dGxlZmllbGRfcG9wdXAgI3dyYXBwZXJfbG9nX3N0YXRzIC5maWdodFYyX3N0YXRzIGRsIGltZyB7DQoJZmxvYXQ6IGxlZnQ7'+
        'DQoJaGVpZ2h0OiAxOXB4Ow0KCXdpZHRoOiAxOXB4Ow0KCW1hcmdpbi1yaWdodDogMnB4Ow0KfQ0KI2JhdHRsZWZpZWxkX3BvcHVw'+
        'ICN3cmFwcGVyX2xvZ19zdGF0cyAuZmlnaHRWMl9zdGF0cyBkbCBkdCB7DQoJZmxvYXQ6IGxlZnQ7DQoJbWFyZ2luOiAwcHg7DQp9'+
        'DQojYmF0dGxlZmllbGRfcG9wdXAgI3dyYXBwZXJfbG9nX3N0YXRzIC5maWdodFYyX3N0YXRzIGRsIGRkIHsNCglmbG9hdDogcmln'+
        'aHQ7DQoJbWFyZ2luOiAwcHg7DQp9DQojYmF0dGxlZmllbGRfcG9wdXAgI3dyYXBwZXJfbG9nX3N0YXRzIC5maWdodFYyX3N0YXRz'+
        'IC5udW1iZXJzIHsNCglmbG9hdDogcmlnaHQ7DQoJZm9udC13ZWlnaHQ6IGJvbGQ7DQoJbWFyZ2luOiAwcHg7DQoJb3ZlcmZsb3c6'+
        'IGhpZGRlbjsNCglwYWRkaW5nOiAwcHg7DQp9DQojYmF0dGxlZmllbGRfcG9wdXAgI3dyYXBwZXJfbG9nX3N0YXRzIC5maWdodFYy'+
        'X2xvZyB7DQoJZmxvYXQ6IGxlZnQ7DQoJaGVpZ2h0OiA5OSU7DQoJd2lkdGg6IDUxNXB4Ow0KfQ0KI2JhdHRsZWZpZWxkX3BvcHVw'+
        'ICN3cmFwcGVyX2xvZ19zdGF0cyAuZmlnaHRWMl9sb2cgI2V2ZW50c19saXN0IHsNCgloZWlnaHQ6IGF1dG87DQoJb3ZlcmZsb3c6'+
        'IGhpZGRlbjsNCgl0ZXh0LWFsaWduOiBsZWZ0Ow0KCWhlaWdodDogMTAwJTsNCn0NCiNiYXR0bGVmaWVsZF9wb3B1cCAjd3JhcHBl'+
        'cl9sb2dfc3RhdHMgLmZpZ2h0VjJfbG9nICNldmVudHNfbGlzdCAjZmlnaHRsb2dzIHsNCglib3JkZXI6IDBweDsNCn0NCiNiYXR0'+
        'bGVmaWVsZF9wb3B1cCAjd3JhcHBlcl9sb2dfc3RhdHMgLmZpZ2h0VjJfbG9nICNldmVudHNfbGlzdCAuYnV0dG9ucyB7DQoJYm9y'+
        'ZGVyLWJvdHRvbTogMXB4IHNvbGlkICMzMzM7DQoJYm9yZGVyLXJpZ2h0OiAxcHggc29saWQgIzMzMzsNCn0JDQojYmF0dGxlZmll'+
        'bGRfcG9wdXAgI3dyYXBwZXJfbG9nX3N0YXRzIC5maWdodFYyX2xvZyAjZXZlbnRzX2xpc3QgLmJ1dHRvbnMgYSB7DQoJYm9yZGVy'+
        'LWxlZnQ6IDFweCBzb2xpZCAjMzMzOw0KCWJvcmRlci1yaWdodDogMXB4IHNvbGlkICMzMzM7DQoJcGFkZGluZy1sZWZ0OiAxMHB4'+
        'Ow0KCXBhZGRpbmctcmlnaHQ6IDEwcHg7DQoJb3BhY2l0eTogMC41Ow0KfQ0KI2JhdHRsZWZpZWxkX3BvcHVwICN3cmFwcGVyX2xv'+
        'Z19zdGF0cyAuZmlnaHRWMl9sb2cgI2V2ZW50c19saXN0IC5idXR0b25zIGEuc2VsZWN0ZWQgew0KCWZvbnQtd2VpZ2h0OiBib2xk'+
        'Ow0KCW9wYWNpdHk6IDE7DQp9DQojYmF0dGxlZmllbGRfcG9wdXAgI3dyYXBwZXJfbG9nX3N0YXRzIC5maWdodFYyX2xvZyAjZXZl'+
        'bnRzX2xpc3QgLmJ1dHRvbnMgYTpmaXJzdC1jaGlsZCB7DQoJbWFyZ2luLWxlZnQ6IDIwcHg7DQp9DQojYmF0dGxlZmllbGRfcG9w'+
        'dXAgI3dyYXBwZXJfbG9nX3N0YXRzIC5maWdodFYyX2xvZyAjZXZlbnRzX2xpc3QgdWwgew0KCWxpc3Qtc3R5bGUtdHlwZTogbm9u'+
        'ZTsNCgltYXJnaW46IDBweDsNCglvdmVyZmxvdzogYXV0bzsNCglwYWRkaW5nOiAwcHg7DQoJdGV4dC1hbGlnbjogbGVmdDsNCgl3'+
        'aWR0aDogMTAwJTsNCn0NCiNiYXR0bGVmaWVsZF9wb3B1cCAjd3JhcHBlcl9sb2dfc3RhdHMgLmZpZ2h0VjJfbG9nICNldmVudHNf'+
        'bGlzdCB1bCBsaSB7DQoJbWFyZ2luOiA4cHggNXB4IDBweCAyMHB4Ow0KfQ0KI2JhdHRsZWZpZWxkX3BvcHVwICN3cmFwcGVyX2xv'+
        'Z19zdGF0cyAuZmlnaHRWMl9sb2cgI2V2ZW50c19saXN0IHVsI2xvb3RsaXN0bG9nIHsNCgltYXJnaW4tdG9wOiA1cHg7DQp9DQoj'+
        'YmF0dGxlZmllbGRfcG9wdXAgI3dyYXBwZXJfbG9nX3N0YXRzIC5maWdodFYyX2xvZyAjZXZlbnRzX2xpc3QgdWwjZXZlbnRzbG9n'+
        'Z2VyIHsNCiAgICBmb250LXNpemU6IDEycHg7DQogICAgbWFyZ2luLXRvcDogNXB4Ow0KICAgIGhlaWdodDogNDAwcHg7DQogICAg'+
        'b3ZlcmZsb3cteTogbm9uZTsNCn0NCiNiYXR0bGVmaWVsZF9wb3B1cCAjd3JhcHBlcl9sb2dfc3RhdHMgLmZpZ2h0VjJfbG9nICNl'+
        'dmVudHNfbGlzdCB1bCNldmVudHNsb2dnZXIgbGkgew0KICAgIGJvcmRlci1ib3R0b206IDFweCBzb2xpZCAjMzMzOw0KfQ0KI2Jh'+
        'dHRsZWZpZWxkX3BvcHVwICNzdGF0c19sb2dzIHRhYmxlIHsNCiAgICB3aWR0aDogOTglOw0KICAgIG1hcmdpbjogMTVweCAwcHgg'+
        'MHB4IDVweDsNCn0NCiNiYXR0bGVmaWVsZF9wb3B1cCAjc3RhdHNfbG9ncyB0YWJsZSB0cjpmaXJzdC1jaGlsZCB0aDpmaXJzdC1j'+
        'aGlsZCB7DQogICAgd2lkdGg6IDEwMHB4Ow0KfQ0KI2JhdHRsZWZpZWxkX3BvcHVwICNzdGF0c19sb2dzIHRhYmxlIHRyOmZpcnN0'+
        'LWNoaWxkIHRoIHsNCiAgICB3aWR0aDogNTBweDsNCn0NCiNiYXR0bGVmaWVsZF9wb3B1cCAjc3RhdHNfbG9ncyB0YWJsZSB0ciB0'+
        'ZDpmaXJzdC1jaGlsZCB7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCiNiYXR0bGVmaWVsZF9wb3B1cCAjc3RhdHNfbG9ncyB0'+
        'YWJsZSB0ciB0ZCB7DQogICAgdGV4dC1hbGlnbjogY2VudGVyOw0KfQ0KI2JhdHRsZWZpZWxkX3BvcHVwICN3cmFwcGVyX2xvZ19z'+
        'dGF0cyAuZmlnaHRWMl9sb2cgI29wcG9uZW50c19saXN0IHsNCgloZWlnaHQ6IDEwMCU7DQp9DQojYmF0dGxlZmllbGRfcG9wdXAg'+
        'I3dyYXBwZXJfbG9nX3N0YXRzIC5maWdodFYyX2xvZyAjb3Bwb25lbnRzX2xpc3QgLmhlYWRlciB7DQoJaGVpZ2h0OiA1JTsNCn0N'+
        'CiNiYXR0bGVmaWVsZF9wb3B1cCAjd3JhcHBlcl9sb2dfc3RhdHMgLmZpZ2h0VjJfbG9nICNvcHBvbmVudHNfbGlzdCAuaGVhZGVy'+
        'IGEgew0KCWZsb2F0OiBsZWZ0Ow0KCW1hcmdpbi1sZWZ0OiAyMHB4Ow0KfQ0KI2JhdHRsZWZpZWxkX3BvcHVwICN3cmFwcGVyX2xv'+
        'Z19zdGF0cyAuZmlnaHRWMl9sb2cgI29wcG9uZW50c19saXN0ICNvcHBvbmVudHNfdGFibGUgew0KCWhlaWdodDogOTUlOw0KfQ0K'+
        'I2JhdHRsZWZpZWxkX3BvcHVwICN3cmFwcGVyX2xvZ19zdGF0cyAuZmlnaHRWMl9sb2cgLnBsYXllcl91cGRhdGVzIHsNCiAgICBv'+
        'dmVyZmxvdy14OiBoaWRkZW47DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCiNiYXR0bGVmaWVsZF9wb3B1cCAjd3JhcHBlcl9s'+
        'b2dfc3RhdHMgLmZpZ2h0VjJfbG9nIC51cGRhdGVfdHh0IHsNCgl3aWR0aDogMzYwcHg7DQoJZm9udC1mYW1pbHk6IHRyZWJ1Y2hl'+
        'dCBNUzsNCglmb250LXdlaWdodDogYm9sZDsNCgljb2xvcjogd2hpdGU7DQoJY2xlYXI6IG5vbmU7DQp9DQojYmF0dGxlZmllbGRf'+
        'cG9wdXAgI3dyYXBwZXJfbG9nX3N0YXRzIC5maWdodFYyX2xvZyAjaXRlbXNfbG9ncyAubG9vdCB7DQoJYmFja2dyb3VuZDogYmxh'+
        'Y2sgdXJsKGh0dHA6Ly9td2ZiLnN0YXRpYy56eW5nYS5jb20vbXdmYi9ncmFwaGljcy9maWdodF9ib251c2VzL3NpZGVfYm9udXNs'+
        'b290LnBuZykgbm8tcmVwZWF0IHNjcm9sbCAxMDAlIDUwJTsNCn0NCiNiYXR0bGVmaWVsZF9wb3B1cCAjd3JhcHBlcl9sb2dfc3Rh'+
        'dHMgLmZpZ2h0VjJfbG9nICNpdGVtc19sb2dzIC5zdGFzaCB7DQoJYmFja2dyb3VuZDogYmxhY2sgdXJsKGh0dHA6Ly9td2ZiLnN0'+
        'YXRpYy56eW5nYS5jb20vbXdmYi9ncmFwaGljcy9maWdodF9ib251c2VzL3NpZGVfc2VjcmV0c3Rhc2gucG5nKSBuby1yZXBlYXQg'+
        'c2Nyb2xsIDEwMCUgNTAlOw0KfQ0KI2JhdHRsZWZpZWxkX3BvcHVwICN3cmFwcGVyX2xvZ19zdGF0cyAuZmlnaHRWMl9sb2cgI2l0'+
        'ZW1zX2xvZ3MgLnN0YXNoIC51cGRhdGVfdHh0IHsNCgliYWNrZ3JvdW5kLWNvbG9yOiBibGFjazsNCglvcGFjaXR5OiAwLjk7DQp9'+
        'DQojYmF0dGxlZmllbGRfcG9wdXAgI3dyYXBwZXJfbG9nX3N0YXRzIC5maWdodFYyX2xvZyAjaXRlbXNfbG9ncyAubG9vdCAudXBk'+
        'YXRlX3R4dCB7DQoJYmFja2dyb3VuZC1jb2xvcjogYmxhY2s7DQoJb3BhY2l0eTogMC45Ow0KfQ=='
    );
    // load options and Initialize
    options.add('travelTo', UserConfig.getSettingFrom(global.cityNames, false));
    options.load(function() {
        LootCache = new CSLootList();
        LootCache.load(function(success) {
            if (success === true) {
                Initialize();
            } else {
                popupElt.destroy();
            }
        });
    });
}
// ==Script==
// @id        BattlefieldStats.js
// @author    Dakam
// @requires  MWAddon.js
// ==Script==

function BattlefieldStats() {
    var statistics = new Config('bf_stats');
    var opponent_data, Sessions =  new Object();
    // POPUP
    var popupElt = new PopupObject('bfstats_popup', {
        type: 'normal',
        title: 'Battlefield Statistics'
    });
    
    
    function genMainDom() {
        popupElt.content.height('auto');
        c$('img').appendTo(popupElt.content).attr({
            'alt'   : 'Under Construction...',
            'title' : 'Under Construction...',
            'src'   :'http://www.icis.com/blogs/green-chemicals/under_construction2.jpg'
        });
        return;
        c$('div', 'id:stats_logs').css('text-align','center').appendTo($elt)
        .append(c$('div').css('margin-top',15))
        .append(s$('oldsession_time_select', 'Select Session:', 300).change(Events.sessionstats_change))
        .append(c$('div').css({'clear':'both','margin-top':5}))
        .append(s$('oldsession_type_select', 'Select Type:', 100).change(Events.sessionstats_change))
        .append(c$('div', 'id:table_stats'));
    }
    
    
    function genStatsTable() {
        var type = $('#oldsession_type_select').val();
        var timestamp = $('#oldsession_time_select').val();
        var session = SessionStats.get(timestamp);
        var rowFileds = Util.clone(global.cityNames);
        rowFileds[0] = 'Total';
        // If no session return.
        if (!session) {
            return;
        }
        function getValue(from) {
            result = 0;
            if (Util.isObject(from)) {
                Util.each(from, function(id, count) {
                    result += count;
                });
                return result
            }
            return from||0;
        }
        /**
         * @param {String} name
         */
        function getStat(name, prct) {
            return function(c) {
                /**
                 * @param {String} curr_name
                 * @param {String} max_name
                 */
                function Percent() {
                    var min = session[c][name], 
                        max = session[c][prct],
                        n   = Util.percent(getValue(min), getValue(max));
                    return n.toFixed(Math.abs(n) < 10 ? 2 : 0);
                }
                if (session[c]) {
                    if (prct) {
                        return '<span title="'+Percent()+'%" style="cursor:pointer;">'
                        +      getValue(session[c][name]) + '</span>';
                    } else {
                        return getValue(session[c][name]);
                    }
                } else {
                    return '0'
                }
            }
        }
        /**
         * Get a table builder
         */
        var Builder = {
            fights: {
                'City'   : rowFileds,
                'Fights' : getStat('fightCount'),
                'Foes'   : getStat('foesAttacked', 'fightCount'),
                'Won'    : getStat('fightWon', 'fightCount'),
                'Lost'   : getStat('fightLost', 'fightCount')
            },
            ices: {
                'City'     : rowFileds,
                'Fights'   : getStat('fightCount'),
                'Ices'     : getStat('icesWon', 'fightCount'),
                'Kill'     : getStat('killWon', 'fightCount'),
                'Stolen'   : getStat('icesLost', 'fightCount'),
                'Revenges' : getStat('revenge', 'fightCount')
                
            },
            other: {
                'City'     : rowFileds,
                'Exp.'     : getStat('expGained'),
                'Stamina'  : getStat('staminaSpend'),
                'CashWon'  : getStat('cashWon'),
                'CashLost' : getStat('cashLost'),
                'Coins'    : getStat('coinsGained')
                
            }
        };
        // ------------------------------------
        // GENERATE TABLE
        // ------------------------------------
        var $parent = $('#stats_logs #table_stats', popupElt.content).empty();
        var tlb = new TableObject($parent, true);
        var row = 1, column = 0;
        tlb.table.attr({
            'cellspacing': 1,
            'border': 1            
        });
        Util.each(Builder[type||'fights'], function(name, bld) {
            tlb.cell(0,column++).html(name);
        });
        column = 0;
        Util.each(rowFileds, function(cityId) {
            Util.each(Builder[type||'fights'], function(name, bld) {
                tlb.cell(row,column++).html( Util.isFunc(bld) ? bld(cityId) : bld[cityId] );
            });
            row++;
        });
    }
    
    function Initialize() {
        statistics.each(function(timestamp, session) {
            if (timestamp === 'opp') {
                opponent_data = session;
            } else {
                Sessions[timestamp] = session;
            }
        });
        genMainDom();
        popupElt.show();
        
        return;
        SessionStats.load(function(sessions, curr) {
            Util.each(sessions, function(timestamp) {
                var $op = c$('option', 'value:'+timestamp);
                if (parseInt(curr) === parseInt(timestamp)) {
                    $op.prependTo('#oldsession_time_select');
                    $op.attr('selected', 'selected').html('Current Session');
                } else {
                    $op.appendTo('#oldsession_time_select');
                    $op.html((new Date(timestamp*1000)).toLocaleString());
                }
            });
        });
    }
    
    statistics.load(Initialize);
}
// ==Script==
// @id        BountyHunter.js
// @author    Dakam
// @requires  MWAddon.js
// ==Script==

UserConfig.create('bhopt', {
    userBlackListOn: true,
    clanBlackListOn: true,
    userBlackList: new Object(),
    clanBlackList: new Object(),
    redBounty: false,
    healOn: true,
    healMin: 100,
    deposit: false,
    delay: 60
});

function BountyHunter() {
    var options = UserConfig.bhopt;
    var StatusTimer = new TimerMessage('#bh_messages');
    var gVar = {
        Aborted: false,
        Paused: false,
        ReadyToHeal: 0,
        DepositAmount: 100000,
        ResumeFunction: null,
        /** @type {CSBountyList} */ BountyList : null,
        /** @type {TabObject}    */ Tabs       : null,
        /** @type {CSBlackList}  */ BlackList  : null,
        /** @type {CSBlackList}  */ ClanList   : null,
        Expressions: {
            'en_US': [{
                expr: /You (LOST) the fight along with .([\d,\.]+), taking\s*(\d+) damage and dealing\s*(\d+)/i,
                fields: ['msg', 'lost', 'cash', 'take', 'deal'],
                convert: [String, Boolean, Util.parseNum, parseInt, parseInt]
            }, {
                expr: /You (WON) the fight, taking\s*(\d+) damage and dealing\s*(\d+) damage to your enemy\.\s*You gained\s*.([\d,\.]+) and\s*(\d+) exp/i,
                fields: ['msg', 'won', 'take', 'deal', 'cash', 'exp'],
                convert: [String, Boolean, parseInt, parseInt, Util.parseNum, parseInt]
            }, {
                expr: /You are too weak to fight/i,
                fields: ['low_health'],      convert: [Boolean]
            }, {
                expr: /This player is currently part of your mafia/i,
                fields: ['in_mafia'],        convert: [Boolean]
            }, {
                expr: /someone else took out/i,
                fields: ['too_late'],        convert: [Boolean]
            }, {
                expr: /have enough stamina to attack anybody on the hitlist/i,
                fields: ['low_stamina'],     convert: [Boolean]
            }, {
                expr: /closer to your level/i,
                fields: ['low_level'],       convert: [Boolean]
            }, {
                expr: /You knocked out/i,
                fields: ['knock_out'],       convert: [Boolean]
            }, {
                expr: / could not be completed\. Please try again/i,
                fields: ['try_again'],       convert: [Boolean]
            }]
        
        }
    };
    /**
     * Attacker Statistics.
     */
    var HunterStats = {
        health         : 0,
        maxHealth      : 0,
        stamina        : 0,
        maxStamina     : 0,
        expToNextLevel : 0,
        cash           : 0,
        city           : MW.currentCity()
    };   
    // POPUP
    var Popup = new PopupObject('bountyhunter_popup', {
        type: 'main',
        title: 'BOUNTY HUNTER',
        width: 700,
        onclose: function() {
            gVar.Aborted = true;
            httpAjaxStopRequests();
            StatusTimer.clear();
            options.fromDomElements();
            options.save();
        }
    });
    /**
     * Create a new fightresult.
     * @constructor
     * @param {Object} data
     * @return {CSBountyFightResult}
     */
    var CSBountyFightResult = function(data) {
        var me = this;
        if ((data = e$('table.messages', h$(data))) === null) {
            this.valid = false;
        }
        else {
            this.valid = true;
            this.response = Util.trim(data.text());
            this.attack_url = data.find('a.sexy_button_new').attr('href');
            this.vCoins = Util.parseNum(data.find('div:has(img:regex(src,victory_icon))').text());
        }
        Util.each(gVar.Expressions, function(index, e) {
    		if (e.expr.test(me.response)) {
    			var expr = Util.doRgx(e.expr, me.response);
    			Util.each(e.fields, function(index, field){
    				me[field] = e.convert[index]( expr['$'+index] );
    			});
    		}
        });
        data = null;
        return this;
    };
    /**
     * Create a new bounty class.
     * @constructor
     * @param {jQuery} tr
     * @return {CSBounty}
     */
    var CSBounty = function(tr) {
        var me = this;
        function user_anchor(id, name) {
            return Util.setAnchor(MW.getProfileLink(id), name);
        }
        function clan_anchor(id, name) {
            return Util.setAnchor(MW.getFamilyLink(id), Util.setColor(name,'red'));
        }
        if ($(tr).is('tr')) {
            this.cash = Util.parseNum($('td:eq(3)', tr).text());
            this.payer = {
                'clan': parseLink($('td:eq(2) a:regex(href,xw_controller=clan)', tr)),
                'user': parseLink($('td:eq(2) a:regex(href,xw_controller=stats)',tr)),
                'anchor': function() {
                    var clan = '';
                    if (me.payer.clan) {
                        clan = clan_anchor(me.payer.clan.id, me.payer.clan.name)+' '; 
                    }
                    return clan + user_anchor(me.payer.user.id, me.payer.user.name);
                }
            };
            this.target = {
                'badge' : $('.fight_badge img', tr).attr('src'),
                'clan'  : parseLink($('td:eq(1) a:regex(href,xw_controller=clan)', tr)),
                'user'  : parseLink($('td:eq(1) a:regex(href,xw_controller=stats)',tr)),
                'link'  : $('a:regex(href,hitlist)',tr).attr('href'),
                'anchor': function() {
                    var clan = '';
                    if (me.target.clan) {
                        clan = clan_anchor(me.target.clan.id, me.target.clan.name)+' '; 
                    }
                    return clan + user_anchor(me.target.user.id, me.target.user.name);
                }
            };
            this.id = (this.target.clan?this.target.clan.id:'')+this.target.user.id;
            this.valid = !(gVar.ClanList.exists(me)||(gVar.BlackList.exists(me)&&options.redBounty!==true));
        } else {
            this.valid = false;
        }
        tr = null;
        return this;
        
    }; CSBounty.prototype.toString = function() {
        if (this.valid) {
            return 'Bounty of <span class="cash">' + Util.formatNum(this.cash) + '</span> to ' + this.target.anchor();
        } else {
            return 'Invalid Bounty';
        }
    };
    /**
     * Create a new bounty collection class.
     * @constructor
     * @return {CSBountyList}
     */
    var CSBountyList = function() {
        var list = new Array();
        
        this.current = null;
        /**
         * @param {CSBounty} bounty
         */
        this.add = function(bounty) {
            if (bounty.valid === true) {
                list.push(bounty);
            }
        };
        /**
         * Get the element from the specified index.
         * @param {Number} index
         * @return {CSBounty}
         */
        this.get = function(index) {
            return (this.current = list.splice(parseInt(index), 1).shift());
        };
        this.each = function(callback) {
            Util.each(list, callback);
        };
        /**
         * @return {CSBounty}
         */
        this.shift = function() {
            return (this.current = list.shift());
        };
        /**
         * Clear the list.
         */
        this.clear = function() {
            list = new Array();
        };
        /**
         * @return {Number}
         */
        this.length = function() {
            return list.length;
        };
        this.sort = function() {
            list.sort(function(a, b) {
               return b.cash - a.cash; 
            });
        }
        return this;
    };
    /**
     * Create a new list
     * @constructor
     * @return {CSList}
     */
    var CSBlackList = function(list) {
        /**
         * Convert the list to a json string format.
         * @return {String}
         */
        this.toJSON = function() {
            return Util.toJSON(options.get(list));
        };
        /**
         * Add an user.
         * @param {Object} id
         * @param {Object} value
         */
        this.addID = function(id, value) {
            options.get(list)[id] = value;
        };
        /**
         * Add a target bounty.
         * @param {CSBounty} bounty
         */
        this.add = function(bounty) {
            if (!this.exists(bounty)) {
                try {
                    Logger.debug('Adding To BlackList:' +bounty.target.user.id);
                    options.get(list)[bounty.target.user.id] = bounty.target.user.name;
                    options.save();
                } catch (e) { 
                    Logger.debug('CSBlackList.add: '+ e.message);
                }
            }
        };
        /**
         * Return true if opponent exists
         * @param {Object} id
         * @return {Boolean}
         */
        this.exists = function(bounty) {
            try {
            	return Util.isString(options.get(list)[bounty.target.user.id]);
            } catch (e) { return false; }
        };
        /**
         * Loops through all entries.
         * @param {Function} callback
         */
        this.each = function(callback) {
            Util.each(options.get(list), callback);
        };
        /**
         * Remove an entry.
         * @param {Object} id
         */
        this.remove = function(id) {
            if (Util.isSet(id) && options.get(list)) {
                if (Util.isArray(id)) {
                    Util.each(id, function(i, id) {
                        delete options.get(list)[id]; 
                    });
                } else {
                    delete options.get(list)[id]; 
                }
            }
        };
        /**
         * Clear the list.
         * @param {Function} callback
         */
        this.clear = function(callback) {
            options.set(list, {});
            options.save(callback);
        }
        /**
         * @return {Number}
         */
        this.length = function() {
            return Util.length(options.get(list));
        };
        /**
         * @return {Array}
         */
        this.toArray = function() {
            var new_array = [];
            if (Util.length(options.get(list)) > 0) {
                Util.each(options.get(list), function(id, name) {
                    if (parseInt(id) && Util.isString(name));
                        new_array.push({'id':id , 'name':name});
                });
            }
            return new_array;
        };

        return this;
    };
    
    var Events = {
        list_click: function() {
            var event = Util.parseJSON($(this).attr('event-data'));
            var $lst = $('#bhopt_'+event['i']+'blacklist');
            var lst = event['i']=='user' ? gVar.BlackList : gVar.ClanList;
            if ($('option', $lst).length < 1) {
                if (event['e'] != 'import') {
                    return false;
                }
            }
            switch(event['e']) {
            case 'clear':
                $lst.empty();
                options.fromDomElements();
                options.save();
                break;
            case 'export':
                options.fromDomElements();
                if (lst.length() < 1) {
                    return false;
                }
                FileSystem.fileSave( 'data:application/json;base64,'+Base64.encode(lst.toJSON()) );
                break;
            case 'import':
                c$('input:file','name:files[]').css({
                    'position':'absolute',
                    'width':1,
                    'left':'-100px',
                    'opacity':0
                }).appendTo('body').change(function(e) {
                    $(this).remove();
                    if (!e.target.files) {
                        return;
                    }
                    FileSystem.fileReader(e.target.files, function(data) {
                        try {
                            Util.each(Util.parseJSON(data), function(id, value) {
                                if (parseInt(id))
                                    lst.addID(id, value);
                            });
                            options.toDomElements();
                            options.save();
                            showHelpPopup({
                                icon:'info',
                                title:'Import List',
                                message:'List imported successfully.'
                            });
                        }
                        catch(err) {
                            Logger.error(err);
                        }
                    });
                }).click();
                break;
            case 'remove':
                var ids = new Array();
                $('option:selected', $lst).each(function(i, e) { ids.push(e.value); });
                if (ids.length > 0) {
                    lst.remove(ids);
                    options.toDomElements();
                    options.save();
                }
                break;
            }
            return false;
        },
        attack_click: createButton(function() {
            var bounty = gVar.BountyList.get($(this).attr('target'));
            if (bounty) {
                gVar.Aborted = false;
                gVar.Tabs.showTab(2);
                gVar.Tabs.buttonEnabled(0, false);
                showButtons('stop_hunting');
                Attack(bounty, function(data) {
                    stopHunting();
                    if (data) {
                        updateBounties(data);
                    }
                });
            }
            return false;
        }),
        refresh_click: createButton(function() {
            $('#main_controls a').addClass('disabled');
            $('.bh_hitlist .bounty_attack a').unbind().addClass('disabled');
            sendMessage('Loading Hitlist...',true);
            getlist(function() {
                gVar.Tabs.showTab(0, false);
                $('#main_controls a').removeClass('disabled');
            });
        }),
        start_click: createButton(function() {
            showButtons('stop_hunting');
            gVar.Aborted = false;
            gVar.Tabs.showTab(2);
            gVar.Tabs.buttonEnabled(0, false);
            AutoAttack();
            return false;
        }),
        stop_click: createButton(function() {
            var msg = $('#attack_messages').html();
    		if (msg) {
                addLog(msg+'<span style="color:grey;float:right;">&nbsp;CANCELLED&nbsp;</span>', 'info');
                $('#attack_messages').empty();
            }
            stopHunting();
            return false;
        })
    };
    /**
     * @param {Object} event
     * @return {Function}
     */
    function createButton(event) {
        return function() {
            var me = $(this);
            if (!me.hasClass('disabled')) {
                event.apply(this);
            }
            return false;
        }
    }
    function showButtons(ids) {
        var expr = new RegExp(ids, 'i');
         $('#main_controls a').each(function(i, e) {
             $(e)[expr.test(e.id)?'show':'hide']();
         });
    }
    /**
     * Parse a profile link
     * @param {jQuery} a
     */
    function parseLink(a) {
        var url = a.attr('href');
        var params = Util.uSplit(url);
        var id = params.id||params.user;
        try {
            if (id) {
                id = Base64.decode(id);
            }
        } catch (e) {}
        return {
            id    : Util.parseNum(id||''),
            name  : Util.trim(a.text()),
            url   : url  
        };
    }
    
    function resultMessage(message) {
        $('#attack_messages').hide().html(message).fadeIn();
    }
    
    function sendMessage(text, ajax) {
        $('#bh_messages').css('padding-left', (ajax===true?20:0))
        .toggleClass('mwa_res_ajax_loader', ajax===true).html(text);
    }
    
    function addLog(m, type, id) {	
        var $ul = Popup.content.find('#content_wrapper .bh_log');
    	var t = Util.setColor('['+(new Date()).toLocaleTimeString()+ ']', 'grey');
        if (!Util.isSet(type) || type == 'info') {
            type = 'mwa_res_info_icon';
        } else {
            type = 'mwa_res_ajax_error';
        }
        if (!id) {
            c$('li','class:'+type).prependTo($ul).html(t + '  ' + m);
        } else {
            (e$('#'+id, $ul)||c$('li','class:'+type+',id:'+id)).prependTo($ul).html(t+'  '+m);
        }
    }
    
    function getlist(callback, message) {
    	httpAjaxRequest({
    		url: 'remote/' + MW.getIntURL('hitlist', 'view', 1),
    		liteLoad: 1,
            message: message,
    		success: function(data) {
                updateStats(data);
                updateBounties(data);
		        callback&&callback();
            }
    	});
    }
    
    function updateBounties(data) {
        gVar.BountyList.clear();
		h$(data).find('table.hit_list tr:has(a:regex(href,hitlist))').each(function(i,e){
            var bounty = new CSBounty(e);
            if (bounty.valid) {
               gVar.BountyList.add(bounty);
            }
		});
        gVar.BountyList.sort();
		addLog('Found '+gVar.BountyList.length()+ ' bounties.');
        sendMessage('Found '+gVar.BountyList.length()+ ' bounties.');
        genBountyDom();
	}
    
    function genBountyDom() {
        var $ul = $('#content_wrapper .bh_hitlist').empty();
        gVar.BountyList.each(function(index, bounty) {
            c$('li').css('background','url("'+bounty.target.badge+'") 0px 50% no-repeat')
            .append(c$('div','class:bounty_user').html(bounty.target.anchor()))
            .append(c$('div','class:bounty_user').html(bounty.payer.anchor()))
            .append(c$('div','class:bounty_cash cash').html(Util.formatNum(bounty.cash)))
            .append(c$('div','class:bounty_attack')
            .append(b$('Hunt!','target:'+index+',class:short red').click(Events.attack_click)))
            .appendTo($ul);
        });
    }
    
    function updateStats(htmlText) {
        var user_fields;
        try {
            if (htmlText.user_fields) {
                user_fields = htmlText.user_fields;
            }
            else {
                var script = h$(htmlText).find('script:regex(text,var user_fields)');
                if (script.length > 0) {
                    eval(Util.substr(script.text(),'var user_fields', 'user_fields_update'));
                    $('#sf_updater').append(script);
                } else {
                    return;
                }
            }
            HunterStats.city            = parseInt(user_fields['current_city_id']);
            HunterStats.cash            = parseInt(user_fields['user_cash']);
            HunterStats.health          = parseInt(user_fields['user_health']);
            HunterStats.maxHealth       = parseInt(user_fields['user_max_health']);
            HunterStats.stamina         = parseInt(user_fields['user_stamina']);
            HunterStats.maxStamina      = parseInt(user_fields['user_max_stamina']);
            HunterStats.expToNextLevel  = parseInt(user_fields['exp_to_next_level']);
        } catch (e) { }
    }
    
    function AutoHeal(callback) {
    	if (gVar.Aborted===true || !Util.isFunc(callback)) return;
        var try_count = 4;
        function tryHeal() {
            try_count--;
            if (try_count > 0) {
                sendMessage('Healing at ' + global.city(1).name + '...', true);
                httpAjaxRequest({
                    'url': 'remote/' + MW.getIntURL('hospital', 'heal') + '&xcity=1',
                    'success': function(jsonData){
                        try {
                            updateStats(jsonData);
                            if (HunterStats.health < HunterStats.maxHealth - 100) {
                                StatusTimer.start(jsonData.hospital_message + ' try again in %N% seconds.', 5, tryHeal);
                            }
                            else {
                                MW.getWaitHealTimer(function(timer) {
                                    if (!isNaN(timer)) {
                                        Logger.debug('ReadyToHeal set to: '+timer+' secs.');
                                        gVar.ReadyToHeal = (new Date()).getTime() + (parseInt(timer)*1000);
                                    }
                                });
                                callback(true, jsonData.hospital_message);
                            }
                        } 
                        catch (err) {
                            StatusTimer.start('Error healing. Try again in %N% seconds.', 5, tryHeal);
                        }
                    }
                });
            } else {
                callback(false, 'Error healing: so many retries.');
            }
        }
        tryHeal();
    }
    /**
     * Deposit cash if user has more then a minimal amount.
     * @param {Object} callback
     * @param {Boolean} [force]
     */
    function depositCash(callback, force) {
        if (HunterStats.city==1 && options.deposit && (HunterStats.cash > gVar.DepositAmount || force)) {
            sendMessage('Depositing <span class="cash">'+Util.formatNum(HunterStats.cash)+'</span> Cash...', true);
            MW.deposit(1, HunterStats.cash, function(msg, data) {
                if (data) {
                    updateStats(data);
                    addLog(msg, 'info');
                } else {
                    addLog(msg, 'error');
                }
                callback();
            });
        } else {
            callback();
        }
    }
    /**
     * Attack a new Bounty.
     */
    function AutoAttack() {
    	if (gVar.Aborted===true) return;
    	if (gVar.BountyList.length() < 1) {
            sendMessage('No bounties left...');
            StatusTimer.start('No bounties left, reload in %N%...',options.delay,function(){
    			getlist(AutoAttack);
    		});		
    		return;
    	}
        var bounty = gVar.BountyList.shift();
        if (bounty.valid !== true) {
            AutoAttack();
            return;
        }
        Attack(bounty, function() {
            if (gVar.Paused===true) {
                sendMessage('Hunting Paused...');
                gVar.ResumeFunction = AutoAttack;
            } else {
                setTimeout(AutoAttack, 1000);
            }
        });
    }
    /**
     * Attack a bounty
     * @param {CSBounty} bounty
     */
    function Attack(bounty, callback) {
        if (gVar.Paused===true) {
            sendMessage('Hunting Paused...');
            gVar.ResumeFunction = function() {
                Attack(bounty, callback);
            };
            return;
        }
        var won_msg = '<span style="color:green;font-weight:bold;">YOU WON!</span>&nbsp;&nbsp;&nbsp;<span class="attack">${deal}</span>&nbsp;<span class="defense" style="color:red;">${take}</span>&nbsp;&nbsp;&nbsp;<span class="experience">${expe}</span>&nbsp;&nbsp;&nbsp;<span class="cash">${format_cash}</span>&nbsp;&nbsp;&nbsp;<span class="victory">${vcns}</span>';
        var lost_msg = '<span style="color:red;font-weight:bold;">YOU LOST!</span>&nbsp;&nbsp;&nbsp;<span class="attack">${deal}</span>&nbsp;<span class="defense" style="color:red;">${take}</span>&nbsp;&nbsp;&nbsp;<span class="cash" style="color:red;">${format_cash}</span>';
        var target = { user: null, expe:0, cash:0, vcns:0, deal:0, take:0, format_cash:'' };
        
        gVar.DepositAmount = Math.max(gVar.DepositAmount, 1000);
        options.healMin = Math.min(options.healMin, 20);
        
		if (bounty && bounty.target && bounty.target.link) {
    	    addLog('Hunting '+bounty);
            target.user = bounty.target.anchor();
            preAttack();
		} else {
            callback();
        }
        
        function preAttack() {
            if (gVar.Aborted===true) return;
            if (gVar.Paused===true) {
                sendMessage('Hunting Paused...');
                gVar.ResumeFunction = preAttack;
                return;
            }
            if (HunterStats.city !== 1) {
                sendMessage('Traveling to '+global.city(1).name,true);
        		httpAjaxRequest({
        			'url': 'remote/'+MW.getIntURL('travel','travel')+'&destination=1&from=hitlist&zone=1',
        			'success': function(data) {
                        gVar.BountyList.clear();
                        updateStats(data);
                        updateBounties(data);
                        preAttack();
                    }
        		});
                return;
            }
            sendMessage('Attacking to '+bounty.target.anchor(),true);
    		httpAjaxRequest({
    			'url': bounty.target.link,
                'liteLoad': 1,
    			'success': postAttack
    		});
        }
        /**
         * Parse a fight response.
         * @param {CSBounty} bounty
         * @param {String} htmlText
         */
        function postAttack(htmlText) {
            if (gVar.Aborted===true) return;
    		var fight = new CSBountyFightResult(htmlText);
            var attack_again = true;
            var message = fight.response;
            
            updateStats(htmlText);
            
            if (fight.attack_url) {
                bounty.target.link = fight.attack_url;
            } else {
                attack_again = false;
            }
            if (fight.low_stamina) {
                stopHunting();
                return;
            }
            if (options.healOn && HunterStats.health < options.healMin) {
                if (options.healOn) {
                    AutoHeal(function(success, response){
                        addLog(response);
                        if (success === true) {
                            preAttack();
                        }
                        else {
                            stopHunting();
                        }
                    });
                }
                return;
            }
            else if (fight.low_health) {
                StatusTimer.start('Health so low... check again in %N%', options.delay, function(){
                    gVar.BountyList.clear();
                    getlist(preAttack);
                });
                return;
            }
            if (fight.won) {
                target.vcns += fight.vCoins;
                target.expe += (fight.exp||0);
                target.cash += (fight.cash||0);
                target.deal += (fight.deal||0);
                target.take += (fight.take||0);
                target.format_cash = Util.formatNum(target.cash);
                resultMessage(message = Util.render(won_msg, target));
            } 
            else if (fight.lost) {
                gVar.BlackList.add(bounty);
                target.cash -= (fight.cash||0);
                target.deal += (fight.deal||0);
                target.take += (fight.take||0);
                target.format_cash = Util.formatNum(target.cash);
                resultMessage(message = Util.render(lost_msg, target));
                attack_again = (options.redBounty === true);
            }
            else if (fight.valid!==true) {
                attack_again = false;
                message = 'Bad luck... someone else took out the bounty.';
            } else {
                attack_again = (fight.try_again===true);
            }
            if (fight.knock_out) {
                addLog(message, 'info');
                attack_again = false;
                target.earned_cash = Util.formatNum(bounty.cash);
                message = Util.render('You knocked out ${user} and earned <span class="cash">${earned_cash}</span>', target);
            }
    		if (fight.in_mafia) {
                attack_again = false;
                message = Util.render('${user} is in your mafia.', target);
    			gVar.BlackList.add(bounty);
    		}
            if (attack_again === true) {
                if (fight.lost && fight.cash && fight.cash > 0) {
                    depositCash(preAttack, true);
                } else {
                    preAttack();
                }
            } else {
                $('#attack_messages').empty();
                addLog(message, 'info');
                depositCash(function() {
                    callback(htmlText);
                });
            }
        }
    }
    /**
     * Stop AutoHunting.
     */
    function stopHunting() {
        gVar.Tabs.buttonEnabled(0, gVar.Aborted = true);
        sendMessage('Hunting Stopped.');
        httpAjaxStopRequests();
        StatusTimer.clear();
        options.toDomElements();
        options.save();
        showButtons('refresh_hitlist|start_hunting');
    }

    function genMainDom() {
        var $content = Popup.content.css({'padding':'0px 5px','text-align':'left'});
        c$('div','id:main_controls')
        .appendTo(c$('div','id:main_panel').append(c$('span','id:bh_messages')).appendTo($content))
        .append(b$('Refresh','id:refresh_hitlist,class:short orange').click(Events.refresh_click))
        .append(b$('Hunt\'em','id:start_hunting,class:short green').click(Events.start_click))
        .append(b$('Take A Rest','id:stop_hunting,class:short red').hide().click(Events.stop_click));
        
        gVar.Tabs = new TabObject({
            id: 'content_wrapper',
            appendTo: $content,
            height: 400, padding: 5,
            tabs: [{
                label: 'Bounty List',
                onclick: genBountyDom
            }, {
                label: 'Settings',
                onclick: function() {
                    Logger.debug('Hunting will be Paused.');
                    gVar.Paused = true;
                    options.toDomElements();
                }
            }, {
                label: 'Hunting Log',
                onclick: function(){
                    Logger.debug('Hunting will be Resumed.');
                    gVar.Paused = false;
                    options.fromDomElements();
                    if (Util.isFunc(gVar.ResumeFunction)) {
                        gVar.ResumeFunction();
                        gVar.ResumeFunction = null;
                    }
                }
            }]
        });
        gVar.Tabs.header.attr('id','attack_messages').css({'width':380,'text-align':'center'});
        c$('ul', 'class:bh_hitlist').appendTo(gVar.Tabs.content[0]);
        c$('ul', 'class:bh_log').appendTo(gVar.Tabs.content[2]);
        genConfig().appendTo(gVar.Tabs.content[1]);
        
        function createButton(args) {
            var $div = c$('div', 'class:ui-right');
            Util.each(args, function(event, id) {
                var data = Util.toJSON({'i':id, 'e':event});
                $div.append(c$('a', 'href:#,class:ui-button').attr('event-data',data).text(event).click(Events.list_click));
            });
            return $div;
        }
        
        function genConfig() {
            var $div = c$('div')
            .append(x$('bhopt_deposit', 'Desposit Cash.'))
            .append(x$('bhopt_healon', 'Heal when:'))
            .append(n$('bhopt_healmin', 40))
            .append(x$('bhopt_redbounty', 'BloodHunting.'))
            .append(n$('bhopt_delay', 'Delay', 40))
            .append(c$('div', 'class:ui-line'));
            
            c$('div', 'class:ui-blacklist ui-left').appendTo($div)
            .append(c$('div', 'class:ui-top')
            .append(x$('bhopt_userblackliston', 'User Blacklist:')).append(createButton({
                'import'  : 'user',
                'export'  : 'user',
                'remove'  : 'user',
                'clear'   : 'user'
            }))).append(c$('select', 'id:bhopt_userblacklist,multiple:multiple'));
            
            c$('div', 'class:ui-blacklist ui-right').appendTo($div)
            .append(c$('div', 'class:ui-top')
            .append(x$('bhopt_clanblackliston','Clan Blacklist:')).append(createButton({
                'import'  : 'clan',
                'export'  : 'clan',
                'remove'  : 'clan',
                'clear'   : 'clan'
            }))).append(c$('select', 'id:bhopt_clanblacklist,multiple:multiple'));
            
            return $div;
        }
    }
    
    function Initialize() {
        if ((gVar.Expressions = gVar.Expressions[global.User.locale()])) {
            gVar.BountyList = new CSBountyList();
            gVar.BlackList = new CSBlackList('userBlackList');
            gVar.ClanList = new CSBlackList('clanBlackList');
            if (UserConfig.main.autoDeposit[1]) {
                gVar.DepositAmount =  UserConfig.main.autoDeposit[1].amount;
            }
            genMainDom();
            options.toDomElements();
            getlist(Popup.show, 'Loading Hitlist...');
        } else {
            Popup.destroy();
            showHelpPopup({
                icon: 'error',
                message: 'Bounty Hunter can\'t work with the current game language.' 
            });
        }
    }
    
    Popup.applyCustomClass();    
    Popup.addBase64Style(
        'I2JvdW50eWh1bnRlcl9wb3B1cCAudmljdG9yeSB7DQogICAgYmFja2dyb3VuZDogdXJsKCR7YmFja2dyb3VuZF91cmx9dmljdG9y'+
        'eV9pY29uLmdpZikgbm8tcmVwZWF0IDBweCA1MCU7DQogICAgcGFkZGluZy1sZWZ0OiAxOXB4Ow0KICAgIGJhY2tncm91bmQtc2l6'+
        'ZTogMTZweDsNCiAgICAtbW96LWJhY2tncm91bmQtc2l6ZTogMTZweDsNCn0NCiNib3VudHlodW50ZXJfcG9wdXAgI21haW5fcGFu'+
        'ZWwgew0KICAgIGJvcmRlci1ib3R0b206IDFweCBkb3R0ZWQgIzMzMzsNCiAgICBtYXJnaW4tYm90dG9tOiA1cHg7DQogICAgaGVp'+
        'Z2h0OiAzMHB4Ow0KICAgIHBhZGRpbmc6IDVweCAwcHggMHB4IDEwcHg7DQp9DQojYm91bnR5aHVudGVyX3BvcHVwICNtYWluX3Bh'+
        'bmVsICNtYWluX2NvbnRyb2xzIHsNCiAgICBmbG9hdDogcmlnaHQ7DQp9DQojYm91bnR5aHVudGVyX3BvcHVwICNtYWluX3BhbmVs'+
        'ICNtYWluX2NvbnRyb2xzIGEgew0KICAgIG1hcmdpbi1yaWdodDogNXB4Ow0KICAgIHdpZHRoOiAxMDBweDsNCn0NCiNib3VudHlo'+
        'dW50ZXJfcG9wdXAgI21haW5fcGFuZWwgI2JoX21lc3NhZ2VzIHsNCiAgICBsaW5lLWhlaWdodDogMzBweDsNCn0NCiNib3VudHlo'+
        'dW50ZXJfcG9wdXAgI2NvbnRlbnRfd3JhcHBlciB1bC5iaF9sb2csDQojYm91bnR5aHVudGVyX3BvcHVwICNjb250ZW50X3dyYXBw'+
        'ZXIgdWwuYmhfaGl0bGlzdCB7DQogICAgbGlzdC1zdHlsZS10eXBlOiBub25lOw0KICAgIG92ZXJmbG93OiBhdXRvOw0KICAgIGZv'+
        'bnQtc2l6ZTogMTJweDsNCiAgICB3aWR0aDogMTAwJTsNCiAgICBoZWlnaHQ6IDEwMCU7DQogICAgbWFyZ2luOiAwcHg7DQogICAg'+
        'cGFkZGluZzogMHB4Ow0KfQ0KI2JvdW50eWh1bnRlcl9wb3B1cCAjY29udGVudF93cmFwcGVyIHVsLmJoX2xvZyBsaSB7DQogICAg'+
        'b3ZlcmZsb3c6IGhpZGRlbjsNCiAgICBib3JkZXItYm90dG9tOiAxcHggZG90dGVkICMyMjI7DQogICAgbWFyZ2luOiAwcHggMHB4'+
        'IDRweDsNCiAgICBwYWRkaW5nOiAwcHggMHB4IDBweCAxOHB4Ow0KICAgIHdpZHRoOiBhdXRvICFpbXBvcnRhbnQ7DQogICAgYmFj'+
        'a2dyb3VuZC1zaXplOiAxNnB4ICFpbXBvcnRhbnQ7DQogICAgLW1vei1iYWNrZ3JvdW5kLXNpemU6IDE2cHggIWltcG9ydGFudDsN'+
        'CiAgICBtaW4taGVpZ2h0OiAxNnB4ICFpbXBvcnRhbnQ7DQp9DQojYm91bnR5aHVudGVyX3BvcHVwICNjb250ZW50X3dyYXBwZXIg'+
        'dWwuYmhfaGl0bGlzdCBsaSB7DQogICAgYm9yZGVyOiAycHggc29saWQgIzFGMUYxRjsNCiAgICBtYXJnaW46IDBweCAwcHggNHB4'+
        'Ow0KICAgIHBhZGRpbmc6IDBweCAwcHggMHB4IDM2cHg7DQogICAgd2lkdGg6IGF1dG8gIWltcG9ydGFudDsNCiAgICBiYWNrZ3Jv'+
        'dW5kLXNpemU6IDMwcHggIWltcG9ydGFudDsNCiAgICAtbW96LWJhY2tncm91bmQtc2l6ZTogMzBweCAhaW1wb3J0YW50Ow0KICAg'+
        'IG1pbi1oZWlnaHQ6IDMwcHggIWltcG9ydGFudDsNCn0NCiNib3VudHlodW50ZXJfcG9wdXAgI2NvbnRlbnRfd3JhcHBlciB1bC5i'+
        'aF9oaXRsaXN0IGxpIC5ib3VudHlfdXNlciB7DQogICAgZmxvYXQ6IGxlZnQ7DQogICAgd2lkdGg6IDIwMHB4Ow0KICAgIGxpbmUt'+
        'aGVpZ2h0OiAzMHB4Ow0KICAgIG92ZXJmbG93OiBoaWRkZW47DQogICAgbWFyZ2luLXJpZ2h0OiAzcHg7DQogICAgYm9yZGVyLXJp'+
        'Z2h0OiAxcHggc29saWQgIzIyMjsNCiAgICB3aGl0ZS1zcGFjZTogbm93cmFwOw0KfQ0KI2JvdW50eWh1bnRlcl9wb3B1cCAjY29u'+
        'dGVudF93cmFwcGVyIHVsLmJoX2hpdGxpc3QgbGkgLmJvdW50eV9jYXNoIHsNCiAgICBmbG9hdDogbGVmdDsNCiAgICB3aWR0aDog'+
        'MTAwcHg7DQogICAgbGluZS1oZWlnaHQ6IDMwcHg7DQogICAgb3ZlcmZsb3c6IGhpZGRlbjsNCn0NCiNib3VudHlodW50ZXJfcG9w'+
        'dXAgI2NvbnRlbnRfd3JhcHBlciB1bC5iaF9oaXRsaXN0IGxpIC5ib3VudHlfYXR0YWNrIHsNCiAgICB3aWR0aDogNzBweDsNCiAg'+
        'ICBmbG9hdDogcmlnaHQ7DQogICAgbGluZS1oZWlnaHQ6IDI2cHg7DQp9DQojYm91bnR5aHVudGVyX3BvcHVwICNjb250ZW50X3dy'+
        'YXBwZXIgLnVpLWJsYWNrbGlzdCB7DQogICAgZmxvYXQ6IGxlZnQ7DQogICAgd2lkdGg6IDQ5JTsNCn0NCiNib3VudHlodW50ZXJf'+
        'cG9wdXAgI2NvbnRlbnRfd3JhcHBlciAudWktYmxhY2tsaXN0IGRpdi51aS10b3Agew0KICAgIGNsZWFyOiBib3RoOw0KICAgIG1h'+
        'cmdpbi1ib3R0b206IDNweDsNCiAgICBsaW5lLWhlaWdodDogMjBweDsNCn0NCiNib3VudHlodW50ZXJfcG9wdXAgI2NvbnRlbnRf'+
        'd3JhcHBlciAudWktYmxhY2tsaXN0IGRpdi51aS10b3Agc3Bhbi5jaGVja2JveCB7DQogICAgbGluZS1oZWlnaHQ6IDI0cHg7DQp9'+
        'DQojYm91bnR5aHVudGVyX3BvcHVwICNjb250ZW50X3dyYXBwZXIgLnVpLWJsYWNrbGlzdCBzZWxlY3Qgew0KICAgIHdpZHRoOiAx'+
        'MDAlOw0KICAgIGhlaWdodDogMzQwcHg7DQp9DQojYm91bnR5aHVudGVyX3BvcHVwIC51aS1saW5lIHsNCiAgICBjbGVhcjogYm90'+
        'aDsNCiAgICBtYXJnaW46IDVweCAwcHg7DQogICAgYm9yZGVyLWJvdHRvbTogMXB4IGRvdHRlZCAjMzMzOw0KfQ0K', 
        {background_url: global.zGraphicsURL}
    );
    
    options.load(Initialize);
}

/**
 * Set configuration.
 */
UserConfig.create('caopt', {
    taskmaster : {
        '7': false,
        '8': false,
        '9': false
    }
});

/**
 * Collect all cities and bank cash
 */
function CollectAllCities() {
    
    var startCity = MW.currentCity();
    var abort_process = false;    
    var c_cities = new Collection(global.cityNames);
    var options = UserConfig.caopt;    
    var cityCodes = {
        7: {
            'skip'        : 'Skip', 
            'Properties'  : 'All',
            'Cash'        : 'Cash',
            'Refinery'    : 'Energy',
            'Barracks'    : 'Stamina'
        }
    };
    var taskMasters = {
        7: 'remote/' + MW.getIntURL('CityCrew','activate', 7) + '&isajax=1&crew_city=7&crew_type=prop&crew_dsp_type=prop&crew_slot=1',
        8: 'remote/' + MW.getIntURL('CityCrew','activate', 8) + '&isajax=1&crew_city=8&crew_type=prop&crew_dsp_type=prop&crew_slot=1',
        9: 'remote/' + MW.getIntURL('CityCrew','activate', 9) + '&isajax=1&crew_city=9&crew_type=prop&crew_dsp_type=prop&crew_slot=1'
    };
    var collectAll_click = function() {
        if ($(this).hasClass('disabled')) {
            return;
        }
        $(this).addClass('disabled');
        options.fromDomElements();
        global.cities.each(function(city) {
            var $city = c$('div','class:city_messages,id:message_city'+city);
            $('.city_booble .city' + city).empty().append($city);
            if (options.collect[city] === 'skip') {
                $city.html('This city is skipped by the user.');
            } else {
                $city.html('Waiting...');
            }
        });
        options.save(c_cities.MoveFirst);
    };

    var popupElt = new PopupObject('collectall_popup', {
        type: 'main',
        title: 'COLLECT ALL CITIES',
        width: 690,
        onclose: function() {
            abort_process = true;
            httpAjaxStopRequests();
            options.fromDomElements();
            options.save();
        },
        buttons: [{
            label: 'Collect All!',
            addClass: 'short white',
            onclick: collectAll_click
        }]
    });
    /**
     * Generate a city booble.
     * @param {String} id
     */
    function genCityBoobleDom(id) {
        
        var $elt = c$('div', 'class:city_booble').appendTo(popupElt.content);
        var $div = c$('div').css({
            'float': 'right',
            'text-align': 'left',
            'width': 250
        });
        
        c$('div').css('margin',5).appendTo(c$('div','class:city'+id).appendTo($elt))
        .append(s$('caopt_collect_'+id, 'Collect: ', 100)).append($div);
        
        x$('caopt_deposit_'+id, 'Deposit cash after collect it.').appendTo($div);
        if (Util.isSet(taskMasters[id])) {
            c$('div').css('clear','both').appendTo($div);
            x$('caopt_taskmaster_'+id, 'Active Taskmaster before collect.').appendTo($div);
        }
        
        $elt = $('#caopt_collect_'+id, popupElt.content);
        Util.each((cityCodes[id] || {'skip':'Skip','all':'All'}), function(v, n) {
            $elt.append(c$('option', 'value:'+v).text(n));
        });
    }
    /**
     * @param {Number} pos
     * @param {Number} city
     * @param {String} city_name
     */
    function nextCity(pos, city, city_name) {
        var $city = $('#message_city'+city, popupElt.content);
        var collectMessage = '';
        
        if (abort_process) {
            return;
        }
        Logger.debug( options.collect[city] );
        if (options.collect[city] === 'skip') {
            c_cities.MoveNext();
            return;
        }
        
        $city.html('Traveling...');
        
        MW.travel(city, function(new_city) {
            if (new_city === parseInt(city)) {
                taskMaster();
            }
            else {
                $city.html('Error traveling.');
                c_cities.MoveNext();
            }
        });
        
        function taskMaster() {
            if (Util.isSet(taskMasters[city]) && options.taskmaster[city] === true) {
                $city.html('Activating Taskmaster...');
                httpAjaxRequest({url: taskMasters[city], success: collectAll});
            } else {
                collectAll();
            }
        }
        
        function depositAll(amount) {
            if (abort_process) {
                return;
            }
            if (isNaN(amount) || amount < 11 || !options.deposit[city]) {
                c_cities.MoveNext();
                return;
            }
            $city.html('Depositing...');
            
            MW.deposit(city, amount, function(result) {
                $city.html(collectMessage + '<br>' + result);
                c_cities.MoveNext();
            });
        }
        function collectAll(obj) {
            if (abort_process) {
                return;
            }
            $city.html('Collecting...');
            
            MW.collect(city, options.collect[city], function(r) {
                $city.html(collectMessage = r.message);
                depositAll(r.cash);
            });
        }    
    }
    
    // Set c_cities collection actions
    c_cities.onMove(nextCity);
    c_cities.onEnd(function(){ MW.travel(startCity); });
    
    // Style
    popupElt.addBase64Style(
        'I2NvbGxlY3RhbGxfcG9wdXAgLmJsYWNrX2JveCB7DQoJZm9udC13ZWlnaHQ6IGJvbGQ7IA0KCWNvbG9yOiByZ2IoMjA4LCAyMDgs'+
        'IDIwOCk7IA0KCWJvcmRlcjogMXB4IHNvbGlkIHJnYigxNTMsIDE1MywgMTUzKTsgDQoJYmFja2dyb3VuZC1jb2xvcjogYmxhY2s7'+
        'IA0KCWZvbnQtc2l6ZTogMTRweDsNCn0NCiNjb2xsZWN0YWxsX3BvcHVwIC5jaXR5X2Jvb2JsZSB7DQoJbWFyZ2luOiAxMHB4Ow0K'+
        'fQ0KI2NvbGxlY3RhbGxfcG9wdXAgLmNpdHlfYm9vYmxlID4gZGl2IHsNCglib3JkZXI6IHRoaW4gc29saWQgIzZCNkI2QjsNCgkt'+
        'd2Via2l0LWJvcmRlci1yYWRpdXM6IDhweDsNCgktbW96LWJvcmRlci1yYWRpdXM6IDhweDsNCglib3JkZXItcmFkaXVzOiA4cHg7'+
        'DQogICAgcGFkZGluZzogNXB4IDVweCAwIDE4MHB4Ow0KICAgIGhlaWdodDogNTFweDsNCn0NCiNjb2xsZWN0YWxsX3BvcHVwIC5j'+
        'aXR5X2Jvb2JsZSAuY2l0eV9tZXNzYWdlcyB7DQogICAgYmFja2dyb3VuZC1jb2xvcjogIzEyMTIxMjsNCgktd2Via2l0LWJvcmRl'+
        'ci1yYWRpdXM6IDRweDsNCgktbW96LWJvcmRlci1yYWRpdXM6IDRweDsNCglib3JkZXItcmFkaXVzOiA0cHg7DQogICAgbWluLWhl'+
        'aWdodDogMzVweDsNCiAgICBib3JkZXI6IHRoaW4gaW5zZXQgIzQ0NDsNCiAgICBmb250LXNpemU6IDEycHg7DQogICAgcGFkZGlu'+
        'ZzogNXB4Ow0KfQ0KI2NvbGxlY3RhbGxfcG9wdXAgLmNpdHlfYm9vYmxlIC5zZWxlY3RlZCB7DQoJYm9yZGVyOiB0aGluIHNvbGlk'+
        'IGdyZWVuOw0KfQ0KI2NvbGxlY3RhbGxfcG9wdXAgLmNpdHkxIHsNCgliYWNrZ3JvdW5kOiBibGFjayB1cmwoaHR0cDovL213ZmIu'+
        'c3RhdGljLnp5bmdhLmNvbS9td2ZiL2dyYXBoaWNzL21hZmlhX3dhcnNfOTI4eDU2XzAzLmpwZykgbm8tcmVwZWF0IHNjcm9sbCAt'+
        'NXB4IDAlOw0KfQkNCiNjb2xsZWN0YWxsX3BvcHVwIC5jaXR5MiB7DQoJYmFja2dyb3VuZC1jb2xvcjogYmxhY2s7DQp9DQojY29s'+
        'bGVjdGFsbF9wb3B1cCAuY2l0eTMgew0KCWJhY2tncm91bmQtY29sb3I6IGJsYWNrOw0KfQ0KI2NvbGxlY3RhbGxfcG9wdXAgLmNp'+
        'dHk0IHsNCgliYWNrZ3JvdW5kLWNvbG9yOiBibGFjazsNCn0NCiNjb2xsZWN0YWxsX3BvcHVwIC5jaXR5NSB7DQoJYmFja2dyb3Vu'+
        'ZDogYmxhY2sgdXJsKGh0dHA6Ly9td2ZiLnN0YXRpYy56eW5nYS5jb20vbXdmYi9ncmFwaGljcy92ZWdhc19oZWFkZXJfNzYweDU2'+
        'XzAxLmdpZikgbm8tcmVwZWF0IHNjcm9sbCAwJSAwJTsNCn0NCiNjb2xsZWN0YWxsX3BvcHVwIC5jaXR5NiB7DQoJYmFja2dyb3Vu'+
        'ZDogYmxhY2sgdXJsKGh0dHA6Ly9td2ZiLnN0YXRpYy56eW5nYS5jb20vbXdmYi9ncmFwaGljcy9pdGFseV9oZWFkZXJfMDEuanBn'+
        'KSBuby1yZXBlYXQgc2Nyb2xsIC0yMHB4IDAlOw0KfQ0KI2NvbGxlY3RhbGxfcG9wdXAgLmNpdHk3IHsNCgliYWNrZ3JvdW5kOiBi'+
        'bGFjayB1cmwoaHR0cDovL213ZmIuc3RhdGljLnp5bmdhLmNvbS9td2ZiL2dyYXBoaWNzL2JyYXppbF9oZWFkZXJfMDEucG5nKSBu'+
        'by1yZXBlYXQgc2Nyb2xsIC01cHggMCU7DQp9DQojY29sbGVjdGFsbF9wb3B1cCAuY2l0eTggew0KCWJhY2tncm91bmQ6IGJsYWNr'+
        'IHVybChodHRwOi8vbXdmYi5zdGF0aWMuenluZ2EuY29tL213ZmIvZ3JhcGhpY3MvY2hpY2Fnb19oZWFkZXJfMDEucG5nKSBuby1y'+
        'ZXBlYXQgc2Nyb2xsIC01cHggMCU7DQp9DQojY29sbGVjdGFsbF9wb3B1cCAuY2l0eTkgew0KCWJhY2tncm91bmQ6IGJsYWNrIHVy'+
        'bChodHRwOi8vbXdmYi5zdGF0aWMuenluZ2EuY29tL213ZmIvZ3JhcGhpY3MvbG9uZG9uX2hlYWRlcl8wMS5wbmcpIG5vLXJlcGVh'+
        'dCBzY3JvbGwgLTVweCAwJTsNCn0='
    );
    options.add('collect',UserConfig.getSettingFrom(global.cityNames, 'all'));
    options.add('deposit',UserConfig.getSettingFrom(global.cityNames, true));
    options.load(function() {
        global.cities.each(genCityBoobleDom);
        options.toDomElements();
        popupElt.applyCustomClass();
        popupElt.show();
    });
}
// ==Script==
// @id        Configuration.js
// @author    Dakam
// @requires  MWAddon.js
// ==Script==
/**
 * Create a new popup with global config options
 */
function Configuration() {
    var inputFileElt, options = UserConfig.main;
    
    var features = [
        {id:'opt_Toolbar',        label:'Show MWAddon Toolbar.'},
        {id:'opt_ChromeBM',       label:'Add Chrome bookmarks to the Bookmarks menu (if available).', padleft:25},
        {id:'opt_Community',      label:'Show MWAddon Community News.'},
        {id:'opt_CommunityLimit', label:'Community News Limit:', numeric:true, padleft:25},
        {id:'opt_JobRates',       label:'Modify jobs stats to show energy/stamita ratios.'},
        {id:'opt_CollectionPage', label:'Modify Collection Page to use Multi Gifter popup for send gifts.'},
        {id:'opt_ProfilePage',    label:'Modify User\'s Profiles to add new actions.'},
        {id:'opt_FamilyPage',     label:'Modify Family\'s Pages to add new actions.'}
    ];
    
    var popupElt = new PopupObject('config_popup', {
        type: 'main',
        title: 'CONFIGURATION',
        width: 650,
        center: false,
        buttons: [{label: 'Save configuration'}],
        onclose: SaveConfiguration
    });
    
    var Events = {
        tooltipEditor: function() {
            Tooltips.EditMode = true;
            showHelpPopup({
                icon: 'info',
                message: 'Tooltip Edit Mode has been activated.<br>Now every time you move your mouse over a control which can be added a tooltip, the editor box will appear.'
            });
            popupElt.destroy();
            return false;
        },
        givepermissions_click: function() {
            var perms = 'read_stream,publish_stream,user_groups,offline_access,read_friendlists';
            facebook.requestPermission(perms, function(success) {
                Logger.debug('Give All Permissions: '+success);
            });
            return false;
        },
        
        savelogin_click: function() {
            var e = $('#short_service_login'), obj = options.shortServiceLogin;
            obj[ $('#login_name_text',e).text() ] = $('#login_name',e).val();
            obj[ $('#login_key_text',e).text()  ] = $('#login_key',e).val();
            options.save(savedLoginMessage);
        },
        
        refreshgroups_click: function() {
            var self = $(this);
            if (!self.hasClass('disabled')) {
                self.addClass('disabled');
                global.Groups.refresh(function() {
                    global.Groups.addToSelect('#groups_list', 0);
                    self.removeClass('disabled');
                });
            }
            return false;
        },
        removegroup_click: function() {
            var self = $(this);
            if (self.hasClass('disabled')) {
                return false;
            }
            self.addClass('disabled');
            
            $('option:selected', '#groups_list').each(function(i,elem) {
                if (parseInt(elem.value) !== 0) {
                    delete options.groups[elem.value];
                }
            });
            
            options.save(function(){
                global.Groups.addToSelect('#groups_list', 0);
                self.removeClass('disabled');
            })
            return false;
        },
        services_change: function() {
            var infoElt = $('#short_service_info', popupElt.content),
                loginElt = $('#short_service_login', popupElt.content),
                slogin = options.shortServiceLogin,
                service = URLServices.shorten[ this.value ];
            
            if ( Util.isSet(service) ) {
                if (service.login) {
                    loginElt.show();
                    infoElt.hide();
                    $('p',loginElt).html(service.description);
                    $('#login_name_text',loginElt).html(service.login.name);
                    $('#login_key_text',loginElt).html(service.login.key);
                    $('#login_name',loginElt).val(slogin[service.login.name] || '');
                    $('#login_key', loginElt).val(slogin[service.login.key]  || '');
                } else {
                    loginElt.hide();
                    infoElt.html(service.description).show();
                }
            } else if ( Util.isSet(service = URLServices.unshorten[ this.value ]) ) {
                $('#unshort_service_info').html(service.description);
            }
            return false;
        },
        import_click: function() {
            $('#import_settings').click();
            return false;
        },
        
        export_click: function() {
            createExportData(function(data) {
                FileSystem.fileSave( data );
            });
            return true;
        },
        
        privacy_change: function() {
            var self = this;
            var privacy = options.privacy;
            var friendListElt = $('#friendlists', popupElt.content);
            
            if(this.options[this.selectedIndex].value != 'CUSTOM') {
                friendListElt.hide();
                return false;
            }
            facebook.needAppPermission('read_friendlists', function(success) {
                if(!success) {
                    self.selectedIndex = 1;
                    return;
                }
                facebook.friendlist(function(resp) {
                    if (!resp || resp.error_code || resp.data.length < 1) {
                        showHelpPopup({
                            icon: 'error',
                            title: 'friendlist length',
                            message: 'Seem that you haven\'t friendlists.'
                        });
                        return;
                    }
                    friendListElt.empty();
                    Util.each(resp.data, function(i, list) {
                        var option = c$('option', 'value:'+list.id).text(list.name);
                        friendListElt.append(option);
                        if (privacy && privacy.allow == list.id) {
                            option.attr('selected', 'selected');
                        }
                    });
                    friendListElt.show();
                });
            });
            return false;
        },
        
        language_change: function() {
            var langID = this.options[this.selectedIndex].value;
            if ( langID === 'none' ) {
                langID = global.mw_locale;
            }
            if (Tooltips.resourceExists(langID)) {
                $('#language_description', popupElt.content).empty()
                .append(c$('p').css('color', 'green').text('Description:'))
                .append(c$('div').html( Tooltips.getResource(langID).description ));
            } else {
                $('#language_description', popupElt.content).empty();
            }
            return false;
        }
    };
    /**
     * Import all settings.
     * @param {Object} e
     */
    function loadImportedData(e) {
        if (!e.target.files) {
            return;
        }
        FileSystem.fileReader(e.target.files, function(data) {
            try {
                popupElt.destroy();
                Util.each(Util.parseJSON(data), function(name, value) {
                    if (!Util.isSet(UserConfig[name])) {
                        return;
                    }
                    UserConfig[name].load(function() {
                        UserConfig[name].fromObject(value);
                        UserConfig[name].save();
                    });
                });
                showHelpPopup({
                    icon:'info',
                    title:'Import Setting',
                    message:'File Settings imported successfully.',
                    buttons: [{
                        label: 'Accept',
                        addClass: 'medium white',
                        onclick: Configuration
                    }]
                });
            }
            catch(err) {
                Logger.error(err);
            }
        });
    }
    
    function savedLoginMessage() {
        showHelpPopup({
            icon:'info',
            title:'Login Saved',
            message:'Your login information has been saved successfully.'
        });
    }
    
    /**
     * Export all settings.
     */
    function createExportData(callback) {
        var values = new Object();
        var c_cat = new Collection(UserConfig);
        
        c_cat.onMove(function(pos, name, item) {
            if (item._isConfigObject !== true) {
                c_cat.MoveNext();
                return;
            }
            var check = Util.isArray(item._excludedToExport)  
            ? new RegExp(item._excludedToExport.join('|'),'i') 
            : false;
            
            item.load(function() {
                values[name] = new Object();
                item.each(function(opt, val) {
                    if (check === false || !check.test(opt)) {
                        values[name][opt] = val;
                    }
                });
                c_cat.MoveNext();
            });
            
        });
        c_cat.onEnd(function() {
            callback( Util.toJSON(values, true) );
        });
        SaveConfiguration(c_cat.MoveFirst);
    }
    /**
     * Return a new input:file element.
     */
    function getInputFile() {
        return c$('input:file','id:import_settings,name:files[]').css({
            'position':'absolute',
            'width':1,
            'left':'-100px',
            'opacity':0
        }).change(loadImportedData);
    }    
    /**
     * Save configuration.
     * @param {Object} callback
     */
    function SaveConfiguration(callback) {
        var value = $('option:selected', '#privacy').val();
        if (value !== 'CUSTOM') {
            options.set('privacy', {'value':value});
        }
        else {
            options.set('privacy', {
                'value': value,
                'friends': 'SOME_FRIENDS',
                'allow': $('option:selected', '#friendlists').val()
            });
        }
        options.fromDomElements();
        options.save(callback);
    }

    function genMainDom() {        
        c$('div').appendTo(popupElt.header).html('Version: '+ AppInfo.version).css({
            'font-family'  : 'Arial',
            'font-size'    : 14,
            'font-weight'  : 'bold',
            'opacity'      : '0.5',
            'position'     : 'absolute',
            'top'          : 50,
            'right'        : 15
        });
        popupElt.content.css('padding', '10px 30px');
        
        var thisTab, tb = new TabObject({
            id: 'config_tabs',
            appendTo: popupElt.content,
            padding: 20,
            height: 300,
            tabs: ['General', 'Features', 'Auto Actions', 'Services', 'Publish', 'Tooltips']
        });
        
        // ----------------------------------
        // GENERAL SETTINGS
        // ----------------------------------
        thisTab = tb.getLayout(0)
        .append(c$('span').text('You need to give '))
        .append(c$('a','class:ui-button,id:give_all_permissions').text('All required Permission')
            .css({'vertical-align':'initial','display':'inline'}).click(Events.givepermissions_click))
        .append(c$('span').text(' to Mafia Wars.'))
        .append(c$('div').css({'clear':'both','margin-top':5}))
        .append(x$('main_checkForUpdates', 'Check for Addon updates.'))
        .append(c$('div').css({'clear':'both','margin-top':5}))
        .append(x$('main_notificationOnTop', 'Bring the screen to the top when a Notification shows.'))
        .append(c$('div').css({'clear':'both','margin-top':5}))
        .append(x$('main_helpNotes', 'Add the Help Notes menu at Startup.'))
        .append(c$('div').css({'clear':'both','margin-top':5}))
        .append(x$('main_publishPreview', 'Use Facebook user interface (UI).'))
        .append(c$('div').css({'clear':'both','margin-top':5}))
        .append(x$('main_debugMode', 'Debug Mode, (CTRL + SHIFT + J to open debug window).'))
        .append(c$('div').css({'clear':'both','margin':'10px 0px 10px 0px','border-bottom':'1px solid #333'}))
        .append(c$('div').append(n$('main_fbtimeout', 'Facebook Requests Timeout (sec):', 50)).css({'padding-right':200,'text-align':'right'}))
        .append(c$('div').css({'clear':'both','margin-top':5}))
        .append(c$('div').append(n$('main_rqtimeout', 'MafiaWars Requests Timeout (sec):', 50)).css({'padding-right':200,'text-align':'right'}))
        .append(c$('div').css({'clear':'both','margin-top':5}))
        .append(c$('div').append(n$('main_jstimeout', 'JSON Services Timeout (sec):', 50)).css({'padding-right':200,'text-align':'right'}))
        .append(c$('div').css({'clear':'both','margin':'10px 0px 10px 0px','border-bottom':'1px solid #333'}));

        c$('center').appendTo(thisTab).css('margin-top', 20).append(getInputFile())
        .append(b$('Import settings', 'class:medium orange').css('margin-right',5).click(Events.import_click))
        .append(b$('Export settings', 'id:export_settings,class:medium orange').click(Events.export_click))
        .append(c$('div').css({'clear':'both','margin-top':5}));


        // ----------------------------------
        // FEATURES
        // ----------------------------------
        thisTab = tb.getLayout(1);
        Util.each(features, function(index, item) {
            var $checkbox; 
            if (item.numeric != true) {
                $checkbox = x$('main_'+item.id, item.label).appendTo(thisTab);
            } else {
                $checkbox = n$('main_'+item.id, item.label, 50).appendTo(thisTab);
            }
            if (item.padleft) {
                $checkbox.css('margin-left', item.padleft);
            }
            c$('div').css({'clear':'both','margin-top':6}).appendTo(thisTab);
        });
        // ----------------------------------
        // AUTOACTIONS SETTINGS
        // ----------------------------------
        thisTab = tb.getLayout(2);
        
        x$('main_autoheal', 'Auto heal,').css('width',150).appendTo(thisTab);
        n$('main_autohealwhen', 'when health goes below:').appendTo(thisTab);
        c$('span').text(' In: ').appendTo(thisTab);
        s$('main_autohealin', 120).appendTo(thisTab);
        
        c$('p').text('Autodeposit in:').appendTo(thisTab);
        thisTab = c$('ul').appendTo(thisTab).css('list-style-type','none');
        
        global.cities.each(function(index, city) {
            var id = 'main_autodeposit_'+index;
            c$('li', 'id:'+id).appendTo(thisTab).height(25)
            .append(x$(id + '_active', city.name, 'div').css({'float':'left','width':100}))
            .append(t$(id + '_amount', ' When cash is more than: ', 150));
        });

        // ----------------------------------
        // SHORTENER SERVICES
        // ----------------------------------
        thisTab = tb.getLayout(3)
        .append(c$('div').text('Select the shortener service:'))
        .append(s$('main_shortserviceid', 548).change(Events.services_change))
        .append(c$('div', 'id:short_service_info').css({'margin-top':5,'min-height':100}))
        .append(c$('div', 'id:short_service_login').css({'margin-top':5,'min-height':100}).hide())
        .append(c$('div').css({'border-top':'1px solid #333','padding-top':5}).text('Select the unshortener service:'))
        .append(s$('main_unshortserviceid', 548).change(Events.services_change))
        .append(c$('div', 'id:unshort_service_info').css({'margin-top':5,'min-height':100}));
        
        
        thisTab = $('#short_service_login');
        b$('Save Login', 'class:medium white').click(Events.savelogin_click).css('float','right').appendTo(thisTab);
        c$('div').css('float','left').width(420).appendTo(thisTab)
        .append(c$('p').css('margin','2px 0px 2px 0px'))
        .append(c$('span','id:login_name_text').css('color','green'))
        .append(c$('input:text','id:login_name').width(320).css('float','right'))
        .append(c$('div').css('clear','both'))
        .append(c$('span','id:login_key_text').css('color','green'))
        .append(c$('input:text','id:login_key').width(320).css('float','right'));
        
        // ----------------------------------
        // PUBLISH SETTINGS
        // ----------------------------------
        thisTab = tb.getLayout(4)
        .append(c$('div').css('margin-bottom',5).text('Wall posts privacy: '))
        .append(s$('privacy', 200).change(Events.privacy_change))
        .append(s$('friendlists', 200).hide());
        
        thisTab.append(c$('div').css({'clear':'both','margin-top':5}))
        .append(c$('div').text('Manage Groups:').css({'clear':'both','margin-top':5,'border-top':'1px solid #333'}))
        .append(c$('select', 'id:groups_list,multiple:multiple').css({'float':'left','width':400,'height':200,'margin-right':5}))
        .append(c$('div').css('float','left')
        .append(b$('Refresh','class:green medium').click(Events.refreshgroups_click))
        .append(c$('div').css({'clear':'both','margin-top':5}))
        .append(b$('Remove','class:red medium').click(Events.removegroup_click)));
               
        // ----------------------------------
        // TOOLTIP HELPER
        // ----------------------------------
        tb.getLayout(5)
        .append(x$('main_tooltips', 'Enable Tooltips helps.'))
        .append(b$('Tooltip Editor','class:short orange').css('float','right').click(Events.tooltipEditor))
        .append(c$('div').text('Language selection').css({'border-bottom': '1px solid #333','margin': '10px 0px 5px 0px'}))
        .append(c$('div').text('Current Mafia Wars language: '+global.mw_locale))
        .append(s$('main_tooltiplanguage', 300).change(Events.language_change)
            .append(c$('option', 'value:none').text('Use Mafia Wars language')))
        .append(c$('div', 'id:language_description').css({'margin-top':8,'border-top':'1px solid #333'}));
        
        
        // ----------------------------------
        // PAYPAL BUTTON
        // ----------------------------------
        c$('a', 'target:_black').appendTo(c$('center').css('margin',5).appendTo(popupElt.content))
        .attr('href', 'https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=CQVWPSUMDKW5N')
        .append(c$('img').attr('src', 'https://www.paypal.com/en_US/i/btn/btn_donate_SM.gif'));
        
        
        // ----------------------------------
        popupElt.applyOptions({
            'main_autohealin': {1:'New York', 0:'Current City'},
            'main_tooltiplanguage': Tooltips.toArray(),
            'main_shortserviceid': URLServices.toArray('shorten'),
            'main_unshortserviceid': URLServices.toArray('unshorten'),
            'privacy': {
                'EVERYONE'            : 'Everyone',
                'ALL_FRIENDS'         : 'All friends',
                'FRIENDS_OF_FRIENDS'  : 'Friends of friends',
                'SELF'                : 'Myself',
                'CUSTOM'              : 'From friend list'
            }
        });
        
        global.Groups.addToSelect('#groups_list', 0);      
          
        if (options.privacy) {
            $('#privacy option[value='+options.privacy.value+']', popupElt.content).attr('selected','selected');
        }
    }

    // add style
    popupElt.addBase64Style(
        'I2NvbmZpZ19wb3B1cCAuYmxhY2tfYm94IHsNCgl3aWR0aDogNDAwcHg7DQoJZm9udC13ZWlnaHQ6IGJvbGQ7IA0KCWNvbG9yOiBy'+
        'Z2IoMjA4LCAyMDgsIDIwOCk7IA0KCWJvcmRlcjogMXB4IHNvbGlkIHJnYigxNTMsIDE1MywgMTUzKTsgDQoJYmFja2dyb3VuZC1j'+
        'b2xvcjogYmxhY2s7IA0KCWZvbnQtc2l6ZTogMTRweDsNCn0NCiNjb25maWdfcG9wdXAgLmZyYW1lX2JveCB7DQoJYm9yZGVyOiAx'+
        'cHggc29saWQgIzRGNEY0RjsNCgltYXJnaW4tYm90dG9tOiAyMHB4Ow0KCXBhZGRpbmc6IDEwcHg7DQoJdGV4dC1hbGlnbjogbGVm'+
        'dDsNCn0NCiNjb25maWdfcG9wdXAgZGl2LmNoZWNrYm94IHsNCgliYWNrZ3JvdW5kOiB1cmwoImh0dHA6Ly9td2ZiLnN0YXRpYy56'+
        'eW5nYS5jb20vbXdmYi9ncmFwaGljcy9mbGFncy9td19tZXNzYWdlYm94X2NoZWNrYm94Ml9ub3JtYWxpemVkLmdpZiIpIG5vLXJl'+
        'cGVhdCBzY3JvbGwgMCUgMCUgdHJhbnNwYXJlbnQ7DQoJdGV4dC1hbGlnbjogbGVmdDsNCgltYXJnaW4tdG9wOiA0cHg7DQoJbWlu'+
        'LWhlaWdodDogMjBweDsNCgl3aWR0aDogYXV0bzsgDQoJcGFkZGluZy1sZWZ0OiAzMHB4OyANCgloZWlnaHQ6IDIwcHg7DQoJY3Vy'+
        'c29yOiBwb2ludGVyOw0KfQ0KI2NvbmZpZ19wb3B1cCBkaXYuY2hlY2tib3guY2hlY2tlZCB7DQoJYmFja2dyb3VuZDogdXJsKCJo'+
        'dHRwOi8vbXdmYi5zdGF0aWMuenluZ2EuY29tL213ZmIvZ3JhcGhpY3MvZmxhZ3MvbXdfbWVzc2FnZWJveF9jaGVja2JveDFfbm9y'+
        'bWFsaXplZC5naWYiKSBuby1yZXBlYXQgc2Nyb2xsIDAlIDAlIHRyYW5zcGFyZW50Ow0KfQ=='
    );
    
    options.load(function() {
        genMainDom();
        options.toDomElements();
        popupElt.applyCustomClass();
        popupElt.show();
        $('select', popupElt.content).change();
    });
}
// ==Script==
// @id        CraftManager.js
// @author    Dakam
// @requires  MWAddon.js
// ==Script==

UserConfig.create('cmopt');
/**
 * Craft Manager.
 */
function CraftManager() {
    /**
     * @type {CSPropertyCollection}
     */
    var Properties;
    var options = UserConfig.cmopt;
    var gVar = {
        DisabledProperties: new Object(),
        cash_by_city: global.User.cash_by_city(),
        nyCashProps: false,
        countdowns: new Object(),
        ny_props: {
            '1'              : {name:'Sports Bar',             icon:'LimitedTimeProperty/SportsBar/flashImage.png'         },
            '2'              : {name:'Venetian Condo',         icon:'LimitedTimeProperty/Condo/condoFlash.png'             },
            '3'              : {name:'Tad\'s Gun Shop',        icon:'LimitedTimeProperty/Gunshop/2_pv2.png'                },
            '4'              : {name:'Biker Clubhouse',        icon:'LimitedTimeProperty/BikerClubhouse/flashImage.png'    },
            '5'              : {name:'Martial Arts Dojo',      icon:'LimitedTimeProperty/MartialArts/flashImage.png'       },
            '6'              : {name:'Botanical Garden',       icon:'LimitedTimeProperty/Garden/flashImage.png'            },
            '7'              : {name:'Cemetery',               icon:'LimitedTimeProperty/Cemetery/flashImage.png'          },
            '8'              : {name:'Cider House',            icon:'LimitedTimeProperty/Ciderhouse/flashImage.png'        },
            '9'              : {name:'Toy Store',              icon:'LimitedTimeProperty/Toystore/flashImage.png'          },
            '10'             : {name:'Assassin\'s Academy',    icon:'LimitedTimeProperty/Assassin/Assassin-flashImage.png' },
            '20'             : {name:'Hangar',                 icon:'LimitedTimeProperty/Hangar/hangar-flashImage.png'     },
            '21'             : {name:'Cage Fight Arena',       icon:'LimitedTimeProperty/CageFightingArena/CageFightArena-flashImage.png' },
            '22'             : {name:'Shamrock Pub',           icon:'LimitedTimeProperty/ShamrockIrishPub/shamrockirishpub-flashImage.png'},
            'chop_shop'      : {name:'Chop Shop',              icon:'PropertiesV2/prop11_chopshop.png'                     },
            'weapons_depot'  : {name:'Weapons Depot',          icon:'PropertiesV2/prop12_weaponsdepot_01.png'              },
            'armory'         : {name:'Armory',                 icon:'PropertiesV2/armory.png'                              },
            'private_zoo'    : {name:'Private Zoo',            icon:'PropertiesV2/zoo.png'                                 },
            '100'            : {name:'School Of Choice',       icon:'LimitedTimeProperty/SchoolOfChoice/flashImage.png'    }
        },
        props: {
            'black_market': {
                type  : 3,
                city  : 7,
                load  : 'brazil'
            },
            'workshop': {
                type  : 2,
                city  : 7,
                load  : 'brazil'
            },
            'warehouse': {
                type  : 3,
                city  : 8,
                load  : 'chicago'
            },
            'speakeasy': {
                type  : 2,
                city  : 8,
                load  : 'chicago'
            },
            'nightclub': {
                type  : 3,
                city  : 9,
                load  : 'london'
            },
            'east_end_pub': {
                type  : 2,
                city  : 9,
                load  : 'london'
            }
        },
        reqs: {
            newyork  : 'remote/' + MW.getIntURL('LimitedTimeProperty', 'showProperties')+'&view_tab=build&page_click=viewV2',
            brazil   : 'remote/' + MW.getIntURL('propertyV2', 'createData') + '&city=7',
            chicago  : 'remote/' + MW.getIntURL('propertyV2', 'createData') + '&city=8',
            london   : 'remote/' + MW.getIntURL('propertyV2', 'createData') + '&city=9',
            ny_cash  : 'remote/' + MW.getIntURL('propertyV2', 'view', 1)
            //'remote/' + MW.getIntURL('travel', 'travel') + '&from=propertyV2&destination=1'
        }
    };
    /**
     * Create a Property Item.
     * @constructor
     * @return {CSPropertyItem}
     */
    var CSPropertyItem = function() {
        this.name = 'Unknow';
        this.level = 0;
        this.attack = '0';
        this.defense = '0';
        this.bonus = '';
        this.requirements = new Array();
        
        this.canBuy = function() {
            if (!Util.isString(this.buy)) {
                return false;
            }
            var result = true;
            
            Util.each(this.requirements, function(i,r) {
                return (result = (!Util.isSet(r.have) || r.have >= r.need));
            });
            return result;
        };
        
        return this;
    };
    /**
     * Create a property class.
     * @constructor
     * @param {Element} masterElt
     * @return {CSProperty}
     */
    var CSProperty = function(id, name, icon) {
        var Items = new Array();
        
        this.id = id;
        this.icon = icon; 
        this.name = (name ? name : 'Unknow');
        this.level = 0;
        this.is_ny = false;
        this.canUpgrade = false;
        
        this.length = function() {
            return Items.length;
        };
        /**
         * @param {Object} index
         * @return {CSPropertyItem}
         */        
        this.get = function(index) {
            return Items[index];
        };
        /**
         * @param {CSPropertyItem} obj
         */
        this.add = function(obj) {
            Items.push(obj);
        };
        /**
         * Loops through all items.
         * @param {Function} callback
         */
        this.each = function(callback) {
            Util.each(Items, callback);
        };
        
        return this;
    };
    /**
     * Create a new property collection.
     * @constructor
     * @return {CSPropertyCollection}
     */
    var CSPropertyCollection = function() {
        var Props = new Object();
        /**
         * @param {CSProperty} prop
         */
        this.add = function(prop) {
            if (prop && prop.id) {
                Props[prop.id] = prop;
            }
        }
        /**
         * @return {Collection} 
         */
        this.getCollection = function() {
            return new Collection(Props);
        };
        /**
         * @param {String} id
         * @return {CSProperty}
         */
        this.get = function(id) {
            return Props[id];
        };
        /**
         * @return {Number}
         */
        this.length = function() {
            return Util.length(Props);
        };
        /**
         * Loops through all properties.
         * @param {Function} callback
         * @param {Boolean} bSorted true to sort properties by name.
         */
        this.each = function(callback, bSorted) {
            var obj = Props;
            if (bSorted) {
                obj = new Array();
                Util.each(Props, function(id, prop) {
                    obj.push(prop);
                });
                obj.sort(function(a, b) {
                    if (a.is_ny_cash && b.is_ny_cash) {
                        return a.upgrade_cost - b.upgrade_cost;
                    }
                    if (a.is_ny_cash || b.is_ny_cash) {
                        return (a.is_ny_cash ? -1 : 1);
                    }
                    if ((a.type || b.type) && (a.type != b.type)) {
                        return (a.type == 5 ? -1 : 1);
                    }
                    var x = a.name.toLowerCase();
                    var y = b.name.toLowerCase();
                    return ((x < y) ? -1 : ((x > y) ? 1 : 0));
                });
            }
            Util.each(obj, callback);
        }
        
        return this;        
    };
    // -------------
    // EVENTS
    // -------------
    var Events = {
        upgrade: function() {
            if ($(this).hasClass('disabled')) {
                return false;
            }
            $('.property_inner a', Popup.content).addClass('disabled');
            var me = $(this);
            var name = me.attr('name');
            var prop = Properties.get(name);
            var url, to_level, $input;
            if (!prop.upgrade) {
                return;
            }
            options.fromDomElements();
            options.save();
            if (prop.is_ny) {
                url = prop.upgrade;
            } else if (prop.is_ny_cash) {
                to_level = parseInt( $('#upgrade_in_'+prop.id, Popup.content).val() );
                $input = $('#level_'+prop.id, Popup.content);
                url = prop.upgrade;
            } else {
                url = 'remote/';
                url += MW.getIntURL('propertyV2','buy', Util.uSplit(prop.upgrade).city) 
                url += prop.upgrade;
            }
            (function upgradeProperty() {
                sendMessage('Upgrading '+prop.name+'...', true);
                httpAjaxRequest({'url': url, 'liteLoad': 1, 
                'success': function(response) {
                    if (Popup.is_closed===true||gVar.autocrafting===true) {return;}
                    var message = parseUpgradeResponse(response);
                    if (prop.is_ny_cash) {
                        if (/success/i.test(message)) {
                            prop.level++;
                        } else if (/bank/i.test(message)) {
                            url += '&fundtype=bank';
                        }
                        if (to_level > prop.level) {
                            $input.text(prop.level);
                            setTimeout(upgradeProperty, 1000);
                        } else {
                          Properties.each(updateProperty);
                        }
                    } else {
                        refreshProperty(prop.id, true);
                    }
                    sendMessage(message);
                    addLog(message, 'info');
                }, 
                'error' : function(errText) {
                    sendMessage(errText);
                    addLog(errText, 'info');
                }});
            })();
            return false;
        },
        build: function() {
            if ($(this).hasClass('disabled')) {
                return false;
            }
            $('.property_inner a', Popup.content).addClass('disabled');
            
            var name = $(this).attr('name');
            var id = $('option:selected', '#cmopt_build_'+name).val();
            var item = Properties.get(name).get(id);
            
            options.fromDomElements();
            options.save();
            
            craftItem(name, item);
            return false;
        },        
        build_change: function() {
            var id = $(this).attr('name');
            if (id) {
                updateProperty(id);
            }
        },
        upgrade_change: function() {
            var id = Util.doRgx(/upgrade_in_(.*)/, this.id).$1;
            if (id) {
                updateProperty(id);
            }
        },
        upgrade_keyup: function() {
            var me = $(this);
            var waiting = me.attr('timeout');
            if (waiting) {
                clearTimeout(waiting);
            }
            me.attr('timeout', setTimeout(function() {
                me.removeAttr('timeout');
                me.change();
            }, 600));
        },
        refresh: function() {
            gVar.restart = true;
            Popup.close(); 
            return false;
        },
        autocraft: function() {
            var $btn = $(this);
            if ($btn.hasClass('disabled')) {
                return false;
            }
            options.fromDomElements();
            options.save();
            if ($btn.hasClass('green')) {
                $btn.removeClass('green').addClass('red disabled');
                gVar.tab.showTab(3);
                gVar.autocrafting = true;
                tabsEnabled(false);
                addLog('AutoCrafting started.', 'info');
                autoCrafting();
            } else {
                $btn.removeClass('red').addClass('green disabled');
                gVar.autocrafting = false;
                tabsEnabled(true);
                addLog('AutoCrafting stopped.', 'info');
                sendMessage('AutoCrafting stopped.');
            }
            setTimeout(function() {
                $btn.removeClass('disabled');
            }, 5000);
            return false;
        }
    };
    // -------------
    // POPUP ELEMENT
    // -------------
    var Popup = new PopupObject('craftmanager_popup', {
        type: 'main',
        title: 'CRAFT MANAGER',
        width: 700,
        background: 'url("'+global.zGraphicsURL+'LimitedTimeProperty/generic.png") 0% 0% no-repeat',
        onclose: function() {
            gVar.autocrafting = false;
            Util.each(gVar.countdowns, function(id, timer) {
                timer.clear();
            });
            httpAjaxStopRequests();
            options.fromDomElements();
            options.save();
            if (gVar.restart === true) {
                CraftManager();
            }
        },
        buttons: [{
             label: 'Refresh',
             addClass: 'medium white',
             onclick: Events.refresh
        }, {
             label: 'AutoCraft',
             addClass: 'medium green',
             onclick: Events.autocraft
        }]
    });
    /**
     * Parse a New York Cash property
     * @param {Object} scriptText
     */
    function getNYCashProperties(scriptText) {
		var flashvars;
		try {
			eval( Util.substr(scriptText, 'var flashvars', 'swfobject') );
			if (flashvars && flashvars.mw_data) {
				parseProps(Util.parseJSON(unescape( flashvars.mw_data )));
			}
		} catch(e) { }
        function parseProps(data) {        
        	Util.each(data, function(index, ny_prop) {
        		if (!Util.isString(ny_prop.type)||!/Cash/i.test(ny_prop.type)||parseInt(ny_prop.maxlevel)!=0) {
        			return;
        		}
                var id = 'ny_cash_' + ny_prop.property_id;
                var prop = new CSProperty(id, ny_prop.name.replace(/\+/g,' '), ny_prop.property_pic);
                prop.is_ny_cash      = true;
                prop.canUpgrade      = parseInt(ny_prop.maxlevel) == 0;
                prop.level           = parseInt(ny_prop.level);
                prop.upgrade_cost    = parseInt(ny_prop.upgrade_cost);
                prop.build_cost      = parseInt(ny_prop.build_cost);
                prop.last_collected  = parseInt(ny_prop.last_collected);
                prop.upgrade         = ny_prop.buy_url;// + '&fundtype=bank';
                // Add property
                Properties.add(prop);
        	});
            if (Util.isArray(data) && data.length > 0) {
                gVar.nyCashProps = true;
                gVar.tab.buttonDisplay(2, true);
            }
        }
    }
    /**
     * Parse a New York property.
     * @param {Object} masterElt
     */
    function getNYProperty(masterElt) {
        var id = (masterElt.id||'').replace('propsDiv','');
        var ny_prop = (gVar.ny_props[id]||{
            name: 'Unknow Property', 
            icon: 'LimitedTimeProperty/button/'+id+'-off.png'
        });
        var prop = new CSProperty(id, ny_prop.name, global.zGraphicsURL+ny_prop.icon);
        
        prop.is_ny = true;
        prop.level = Util.parseNum(Util.textNodes($('.propLevel', masterElt)));
        
        $('.cs_deck_of_cards > div', masterElt).each(function(i1, e1) {
            $('.cs_outer', e1).each(function(i2, e2) {
                var me = new CSPropertyItem();
                var bonuses = new Array();
                
                me.name = $('.cd_item_name', e2).text();
                me.level = Util.parseNum( $('.cs_recipe_card_banner .cs_bigtext', e2).text() );
                me.image = $('.cs_item_image img', e2).attr('src');
                
                $('.cs_item_stats > span, .cs_item_stats > img', e2).each(function(i3, b1) {
                    b1 = $(b1);
                    if (b1.hasClass('cs_attack')) {
                        me.attack = b1.text();
                    }
                    else if (b1.hasClass('cs_defense')) {
                        me.defense = b1.text();
                    }
                    else if (b1.hasClass('cs_bonus')) {
                        bonuses.push(Util.trim(b1.text() + ' ' + (b1.find('img').attr('alt')||'')));
                    }
                    else if (b1.is('img')) {
                        bonuses.push(Util.trim(Util.textNodes(b1.parent()) +' '+ (b1.attr('alt')||'')));
                    }
                    else {
                        bonuses.push(b1.text() + ' ' + b1.attr('class'));
                    }
                });
                
                $('.cs_cs_ingredients > .cs_ingredient', e2).each(function(i, e) {
                    var img = $('img', e);
                    var num = Util.doRgx(/(\d+)\/(\d+)/, $('.num_items', e).text());
                    me.requirements.push({
                        name: img.attr('alt'),
                        icon: img.attr('src'),
                        have: parseInt(num.$1),
                        need: parseInt(num.$2)
                    });
                });
                
                if (bonuses.length > 0) {
                    me.bonus = bonuses.join(',');
                }
                if (prop.level < me.level) {
                    me.requirements.push({
                        name: prop.name + ' level',
                        need: me.level,
                        have: prop.level
                    });
                }
                me.buy = $('.cs_buy_button a:regex(href,xw_action=build|xw_action=craft)', e2).attr('href');
                prop.add(me);
            });
        });
        
        return prop;
    }
    /**
     * Parse a property. 
     * @param {Object} data
     */
    function getProperty(data) {
        if (!data || !data.properties) {
            return;
        }        
        var items = new Object(), prop, now = parseInt((new Date()).getTime() / 1000);
        if (Util.isArray(data.items)) {
            Util.each(data.items, function(index, item) {
                items[item.id] = item;
            });
        }
        Util.each(data.properties, function(index, p) {
            if (!p.purchase_items) {
                return;
            }
            var readyAt = (p.last_purchase_time_stamp + p.purchase_refresh_rate);
            var prop_id = Util.trim(p.name_unlocalized.toLowerCase().replace(/\s/g,'_'));
            var city_id = gVar.props[prop_id].city;
            var prop_url = '&city='+ city_id +'&building_type='+ gVar.props[prop_id].type;
            
            prop = new CSProperty(prop_id, p.name, p.image_url);
            prop.time_left = Math.max(0, readyAt - now);
            prop.level = Util.parseNum(p.level);
            prop.buy_avail = parseInt(p.available_purchases);
            prop.type = p.type;
            if (isNaN(prop.buy_avail)) {
                prop.buy_avail = 0;
            }
            if (prop.time_left > 0) {
                if (gVar.countdowns[prop_id]) {
                    gVar.countdowns[prop_id].start(prop.time_left);
                } else {
                    addCountdown(prop_id, prop.time_left);
                }
            } else if (gVar.countdowns[prop_id]) {
                gVar.countdowns[prop_id].clear();
            }
            Util.each(p.parts_required, function(index, req) {
                if (items[req.id]) {
                    return (prop.canUpgrade = (items[req.id].num_owned > req.quantity));
                } else {
                    return (prop.canUpgrade = false);
                }
            });
            if (prop.canUpgrade && Util.isSet(gVar.props[prop_id])) {
                prop.upgrade = prop_url;
            } else {
                prop.canUpgrade = false;
            }
            Util.each(p.purchase_items, function(item_id, item) {
                var me = new CSPropertyItem();
                var itemProp = (item.property ? data.properties[item.property] : p);
                
                me.name = item.name;
                me.level = parseInt(item.unlock_level);
                me.image = item.image;
                me.attack = item.attack||0;
                me.defense = item.defense||0;
                me.bonus = item.special_ability_text;
                
                if (item.price > 0 && gVar.props[prop_id]) {
                    me.requirements.push({
                        name: 'Cash',
                        type: 'cash',
                        icon: global.zGraphicsURL + global.city(city_id).cashIcon,
                        need: item.price,
                        have: gVar.cash_by_city[city_id]||0
                    });
                }
                if (item.rp > 0) {
                    me.requirements.push({
                        name: 'Reward Points',
                        type: 'rp',
                        icon: global.zGraphicsURL + 'v3/icon-gf-coin.gif',
                        need: item.rp,
                        have: global.User.reward_points()
                    });
                }
                if (itemProp.level < me.level) {
                    me.requirements.push({
                        type: 'level',
                        name: itemProp.name + ' level',
                        need: me.level,
                        have: itemProp.level
                    });
                } else if ((prop.buy_avail&&prop.buy_avail > 0) || now > readyAt && gVar.props[prop_id]) {
                    me.buy = 'remote/' +MW.getIntURL('propertyV2','portBuyItem')+ prop_url +'&id='+ item_id;
                }
                prop.add(me);
            });
            // Add property
            Properties.add(prop);
        });
    }
    
    function genMainDom() {
        c$('div','id:craft_messages').appendTo(c$('div').css({
            'height': 40,
            'border-bottom': '1px solid #333',
            'padding': '5px 5px 5px 20px'
        }).appendTo(Popup.content));
        gVar.tab = new TabObject({
            id: 'craft',
            appendTo: Popup.content,
            overflow: true,
            height: 420,
            tabs: ['New York', 'Other Cities','New York Cash','Event Log']
        });
        gVar.tab.getLayout(3).append(c$('ul', 'id:craftingLogger').css({
            'padding-top': 10,
            'height': 410
        }));
        gVar.tab.buttonDisplay(2, false);
    }
    /**
     * Generate a property interface.
     * @param {String} index
     * @param {CSProperty} prop
     */
    function genPropertyDom(index, prop) {        
        var bestItem = 0, opt_id = 'build_'+prop.id, opt_enabled = prop.id+'_enabled';
        var prop_index = (prop.is_ny?'0':(prop.is_ny_cash?'2':'1'));
        var $outer = c$('div', 'class:property_outer,id:prop_id_'+prop.id);
        
        gVar.tab.getLayout(prop_index).append($outer);
        $outer = c$('div', 'class:property_inner').appendTo($outer);
        
        if (!prop.is_ny_cash) {
            x$('cmopt_'+opt_enabled,'','div').attr('title','Enable/Disable this property for AutoCrafting.').css({
                'margin': 0,
                'width': 50,
                'height': 60,
                'float': 'left'
            }).appendTo($outer);
        }
        
        var $buttons = c$('div','class:buttons').appendTo($outer);
        
        if (prop.is_ny_cash) {
            b$('Upgrade', 'class:medium orange,id:upgrade_'+prop.id+',name:'+prop.id)
            .css('margin-top',15).click(Events.upgrade).appendTo($buttons);
        } else {
            b$('Craft', 'class:short green disabled,id:'+opt_id+',name:'+prop.id).click(Events.build).appendTo($buttons);
            c$('div').css({'clear':'both', 'margin-top':5}).appendTo($buttons);
            b$('Upgrade', 'class:short orange disabled,id:upgrade_'+prop.id+',name:'+prop.id).click(Events.upgrade).appendTo($buttons);
        }
        
        $outer = c$('div', 'class:body,id:'+prop.id+'_body').appendTo($outer);
        $outer.addClass((prop.is_ny_cash||prop.is_ny)?'ny_property_icon':'property_icon');
        $outer.css('background-image','url("'+prop.icon+'")');
        c$('h4').html(prop.name+' (Level <span id="level_'+prop.id+'">0</span>):').appendTo($outer);
        c$('div', 'id:'+prop.id+'_countdown,class:cdn_timer').appendTo($outer);
        
        $outer = c$('p').appendTo($outer);
        
        if (prop.is_ny_cash) {
            n$('upgrade_in_'+prop.id, 'Upgrade to Level:', 100).appendTo($outer);
            $('input', $outer).val(prop.level)
            .change(Events.upgrade_change).keyup(Events.upgrade_keyup);
            return;
        }
        var $select = c$('select', 'id:cmopt_'+opt_id+',name:'+prop.id).width(480).appendTo($outer);
        c$('div','id:item_requirements').appendTo($outer);
        
        prop.each(function(i, item) {
            var name = 'Level ' + item.level + ' - ' + item.name;
            if ( item.attack > 0 || item.defense > 0 ) {
                name += ' [' + item.attack + '/' + item.defense + ']';
            }
            if ( item.bonus ) {
                name += ' [' + item.bonus + ']';
            }
            c$('option', 'value:'+i).text(name).appendTo($select);
            
            if (item.level <= prop.level && item.rp < 1) {
                bestItem = i;
            }
        });
        if (!prop.is_ny_cash) {
            if ( !Util.isSet(options[opt_id]) ) {
                options.add(opt_id, bestItem);
            }
            if ( !Util.isSet(options[opt_enabled]) ) {
                options.add(opt_enabled, true);
            }
        }
        $select.change(Events.build_change);
    }
    
    function autoCrafting() {
        var bNoCraftItems = true;
        if (Popup.is_closed === true) {
            return;
        }
        Properties.each(function(id, prop) {
            if (gVar.DisabledProperties[id]) {
                return true;
            }
            var item_id = $('option:selected', '#cmopt_build_'+id).val();
            var item = prop.get(item_id);
            
            if ( options.get(id+'_enabled') && item && item.canBuy() ) {
                craftItem(id, item);
                return (bNoCraftItems=false);
            } else {
                return bNoCraftItems;
            }
        });
        if (bNoCraftItems) {
            var coutndown;
            Util.each(gVar.countdowns, function(id, timer) {
                if (timer.arguments.delay > 0) {
                    if (!Util.isSet(coutndown)) {
                        coutndown = timer;
                    } else {
                        if (timer.arguments.delay < coutndown.arguments.delay) {
                            coutndown = timer;
                        }
                    }
                }
            });
            if (Util.isSet(coutndown)) {
                sendMessage('<span id="'+coutndown.arguments.id+'_countdown"></span>');
                addLog('AutoCrafting paused: Waiting for the next available item.', 'info');
            } else {
                sendMessage('AutoCrafting stopped.');
                addLog('AutoCrafting stopped: You can\'t craft more items.', 'info');
            }
        }
    }
    /**
     * Enable/Disable tabs.
     * @param {Boolean} is_enabled
     */
    function tabsEnabled(is_enabled) {
        Util.each(gVar.tab.buttons, function(i,b) {
            b.toggleClass('disabled', is_enabled===false);
        });
    }
    /**
     * Craft the specified item.
     * @param {String} prop_id
     * @param {CSPropertyItem} item
     */
    function craftItem(prop_id, item) {
        sendMessage('Crafting '+item.name+'...', true);
        $('#'+prop_id+'_countdown', Popup.content).html(Util.setColor('Loading...','green'));
        httpAjaxRequest({
            url: item.buy, 
            success: function(response) {
                var response = parseResponse(response);
                sendMessage(response.message);
                addLog(response.message);
                if (response.success) {
                    refreshProperty(prop_id);
                } else {
                    gVar.DisabledProperties[prop_id] = true;
                    if (gVar.autocrafting) {
                        autoCrafting();
                    }
                }
            },
            error: function(errMessage) {
                addLog('Error crafting '+item.name+'.');
                refreshProperty(prop_id);
            }
        });
    }
    /**
     * Add a message to the user.
     * @param {Object} message
     * @param {Object} bLoadIcon
     */
    function sendMessage(message, bLoadIcon) {
        var $msg = $('#craft_messages').empty();
        var $span = c$('span').html(message||'');
        
        if (bLoadIcon) {
            Resources.getPicture('ajax_loader').css({
                'float':'left',
                'padding-left': 20,
                'width': 'auto'
            }).append($span).prependTo($msg);
        } else {
            $span.appendTo($msg).hide().fadeIn(500);
        }
    }
    /**
     * @param {String} message
     * @param {String} icon
     */
    function addLog(message, icon) {
        var timestamp = Util.setColor('['+(new Date()).toLocaleTimeString()+ '] ', 'grey');
        var $li = (icon==='error') 
        ? Resources.getPicture('ajax_error', 'li')
        : Resources.getPicture('info_icon', 'li');
        if (icon !== 'error') {
            message = Util.setColor(message, '#E1E1E1');
        } else {
            message = Util.setColor(message, 'red');
        }
        $li.prependTo('#craftingLogger').append(c$('p').html(timestamp+message||''));
    }
    /**
     * Parse a server response after buy an item.
     * @param {String} htmlText
     * @return {String}
     */
    function parseResponse(htmlText) {
        var response = {
            success: false,
            message: ''
        }
        if (!htmlText.data) {       
            htmlText = h$(htmlText).find('.pop_box > div > div:eq(2)').html();
            if ((response.success = (Util.isString(htmlText) && htmlText.indexOf('<br>')>-1))) {
                response.message = htmlText.substr(0, htmlText.indexOf('<br>'));
            } else {
                response.message = 'Error crafting this item.';
            }
        } else {
            var data = Util.parseJSON(htmlText.data);
            response.success = data.result == 'success';
            response.message = data.purchase_message
        }
        return response;
    }
    /**
     * Parse a server response after upgrade a property.
     * @param {String} htmlText
     * @return {String}
     */
    function parseUpgradeResponse(htmlText) {
        if (!htmlText.data) {
            return h$(htmlText).find('.message_body:first').text();
        }
        var data = Util.parseJSON(htmlText.data);
        if (data.result && data.description) {
            return Util.formatText(data.result)+'! '+data.description;
        } else {
            return data.purchase_message||data.success_message||data.result;
        }
    }
    /**
     * Refresh a property after the countdown.
     * @param {String} id
     * @param {Boolean} update
     */
    function refreshProperty(id, update) {
        var url, prop = Properties.get(id);
        try {
	        url = ( prop.is_ny ? gVar.reqs.newyork : gVar.reqs[gVar.props[id].load] );
            loadProperties(url, function(success) {
                Properties.each(updateProperty);
                if (gVar.autocrafting) {
                    autoCrafting();
                }
            });
        } catch (e) {
            Logger.debug('RefreshProperty: ' + e.message);
        	return;
        }
    }
    /**
     * Update a Property.
     * @param {String} prop_id
     * @param {CSProperty} [prop]
     */
    function updateProperty(prop_id, prop) {
        var $outer;
        if (!Util.isObject(prop)) {
            prop = Properties.get(prop_id);
        }
        if (!($outer = e$('#prop_id_'+prop_id, Popup.content))) {
            return;
        }
        $('#level_'+prop_id, $outer).text(prop.level);
        
        if (prop.is_ny_cash) {
            $('#'+prop_id+'_countdown', $outer).empty();
            if (prop && prop.canUpgrade && parseInt($('input', $outer).val()) > prop.level) {
                $('#upgrade_'+prop_id, $outer).removeClass('disabled');
            }
            else {
                $('#upgrade_'+prop_id, $outer).addClass('disabled');
            }
            $('#'+prop_id+'_countdown', $outer).html(calculateNYUpgradeCost());
        } else {
            if (!gVar.countdowns[prop_id] || gVar.countdowns[prop_id].started !== true) {
                $('#'+prop_id+'_countdown', $outer).html(Util.setColor('Ready!', 'green'));
            }
            updateCraftProperty();
        }
        function calculateNYUpgradeCost() {
            var to_level = parseInt($('input', $outer).val());
            var from_level = prop.level;
            var spend_cost = 0;
            while (from_level < to_level) {
                spend_cost += prop.build_cost + (prop.upgrade_cost * from_level);
                from_level++;
            }
            return 'Cost: $' + Util.setColor(Util.formatNum(spend_cost), 'green');
        }
        function updateCraftProperty() {
            var selectedIndex = $('select option:selected', $outer).val();
            var item = prop.get(selectedIndex);
            var reqDiv = $('#item_requirements', $outer);
            var $b = $('a#build_'+prop_id), $u = $('#upgrade_'+prop.id);
            var craftText = 'Craft';
            
            if (prop.buy_avail && prop.buy_avail > 0) {
                craftText += ' ('+prop.buy_avail+')';
            }
            $b.toggleClass('disabled',item.canBuy()===false).find('span:last').text(craftText);
            
            if (prop.canUpgrade) {
                $b.removeClass('medium').addClass('short');
                $u.removeClass('disabled').show();
            } else {
                $b.removeClass('short').addClass('medium').css('margin-top',15);
                $u.addClass('disabled').hide();
            }
            
            if (!prop.selectedIndex || prop.selectedIndex != selectedIndex) {
                prop.selectedIndex = selectedIndex;
                reqDiv.empty();
            } else {
                return;
            }
            
            if (item.requirements.length > 0) {
                c$('div').text('You need:').appendTo(reqDiv).css({
                    'float': 'left',
                    'color': 'grey'
                });
                Util.each(item.requirements, function(i, req) {
                    var reqItemDiv = c$('div','class:req_field').attr('title',req.name).appendTo(reqDiv);
                    if (Util.isSet(req.icon)) {
                        c$('img').appendTo(reqItemDiv).attr({
                            'src': req.icon,
                            'alt': req.name
                        });
                    } else {
                        c$('span').text(req.name+': ').appendTo(reqItemDiv);
                    }
                    if (Util.isSet(req.have)) {
                        c$('span').text(req.need + '/' + req.have).appendTo(reqItemDiv).css({
                            'color': (req.have < req.need ? 'red' : 'green')
                        });
                    } else {
                        c$('span').text(Util.formatNum(req.need)).appendTo(reqItemDiv).css('color','yellow');
                    }
                });
            }
        }
    }
    /**
     * Add a new countdown.
     * @param {String} id
     * @param {Number} timeLeft
     */
    function addCountdown(id, timeLeft) {
        gVar.countdowns[id] = new Countdown({
            id          : id,
            selector    : '#craftmanager_popup #'+id+'_countdown',
            delay       : timeLeft,
            text        : 'Build new item in:',
            success     : refreshProperty
        });
    }
    /**
     * Load properties form the given url.
     * @param {Object} url
     * @param {Object} callback
     */
    function loadProperties(url, callback) {
        /**
         * Parse a countdown from script.
         * @param {Object} script
         */
        function parseCountdown(script) {
            var timeLeft = Util.doRgx(/var timeLeft\s?=\s?(\d+)/i,   script).$1;
            var id = Util.doRgx(/id:\s?'buildTimer?Prop([\w\d]+)'/i, script).$1;
            
            if (timeLeft && (timeLeft = parseInt(timeLeft)) > 0 && id) {
                if (gVar.countdowns[id]) {
                    gVar.countdowns[id].start(timeLeft);
                } else{
                    addCountdown(id, timeLeft);
                }
            } else if (id && gVar.countdowns[id]) {
                gVar.countdowns[id].clear();
            }
        }
        httpAjaxRequest({'url':url,
        'success': function(htmlText) {
            if (htmlText.data) {
                try {
                    htmlText = Util.parseJSON(htmlText.data);
                    if (htmlText.cities) {
                        Util.each(htmlText.cities, function(index, city) {
                            gVar.cash_by_city[city.city] = city.cash_amt;
                        });
                    }
                    getProperty(htmlText);
                } catch (e) {
                    Logger.error(e);
                }
            } else {
                h$(htmlText).find('.master, script:regex(text,addCountdown|mw_collectall)').each(function(i,e) {
                    if ($(e).is('script')) {
                        if (/var flashvars/.test( $(e).text() )) {
                            getNYCashProperties($(e).text());
                        } else {
                            parseCountdown($(e).text()); 
                        }
                    } 
                    else if ( e.id && /propsDiv/.test(e.id) ) {
                        Properties.add(getNYProperty(e));
                    }
                    else if ( e.id && /upgradeDiv/.test(e.id) ) {
                        try {
                            var prop = Properties.get((e.id).replace('upgradeDiv',''));
                            var $btn = e$('div[id^=cs_buy_all_and_upgrade] a', e);
                            prop.canUpgrade = (Util.parseJSON($btn.attr('requirements')).favor == 0);
                            prop.upgrade = Util.toUrl($('div[id^=cs_build_or_upgrade] a', e));
                        } catch (e) {}
                    }
                });
            }
            callback&&callback(true);
        }, 
        'error': function() {
            addLog('There was an error while loading properties.', 'error');
            callback&&callback(false);
        }});
    }
    
    function Initialize() {
        var reqs_sent = 0;
        
        Properties = new CSPropertyCollection();
        genMainDom();
        
        loadingScreen('Loading properties...');
        Util.each(gVar.reqs, function(name, url) {
            reqs_sent++;
            loadProperties(url, function() { reqs_sent--; });
        });
        
        Util.until({
            retries: 300,
            test: function() { return (reqs_sent < 1); },
            success: function() {
                addLog('Properties loaded successfully.', 'info');
                loadingScreen();
                if (Properties.length() > 0) {
                    Properties.each(genPropertyDom, true);
                    options.toDomElements();
                    sendMessage('Default items selected.');
                    Properties.each(function(id, prop) {
                        if (gVar.countdowns[id]) {
                            gVar.countdowns[id].start();
                        } else if (!prop.is_ny_cash) {
                            addCountdown(id, 0);
                        }
                        updateProperty(id);
                    });
                    Popup.applyCustomClass();
                    Popup.show();
                } else {
                    Popup.destroy();
                    showHelpPopup({
                        icon: 'error',
                        title: 'Craft Manager',
                        message: 'Can\'t load your properties. Please, try again later.'
                    });
                }
            }
        });
        
    }
    
    // Style
    Popup.addBase64Style(
        'I2NyYWZ0bWFuYWdlcl9wb3B1cCB1bCwgDQojY3JhZnRtYW5hZ2VyX3BvcHVwIGxpLA0KI2NyYWZ0bWFuYWdlcl9wb3B1cCBwIHsN'+
        'CgltYXJnaW46IDBweDsNCglwYWRkaW5nOiAwcHg7DQp9DQojY3JhZnRtYW5hZ2VyX3BvcHVwIC5ibGFja19ib3ggew0KCWZvbnQt'+
        'd2VpZ2h0OiBib2xkOyANCgljb2xvcjogcmdiKDIwOCwgMjA4LCAyMDgpOyANCglib3JkZXI6IDFweCBzb2xpZCByZ2IoMTUzLCAx'+
        'NTMsIDE1Myk7IA0KCWJhY2tncm91bmQtY29sb3I6IGJsYWNrOyANCglmb250LXNpemU6IDE0cHg7DQp9DQojY3JhZnRtYW5hZ2Vy'+
        'X3BvcHVwIC5wcm9wZXJ0eV9vdXRlciB7DQogICAgbWFyZ2luOiAycHg7DQp9DQojY3JhZnRtYW5hZ2VyX3BvcHVwIC5wcm9wZXJ0'+
        'eV9pbm5lciB7DQogICAgYm9yZGVyOiB0aGluIHNvbGlkICMzMzM7DQogICAgcGFkZGluZzogNXB4Ow0KfQ0KI2NyYWZ0bWFuYWdl'+
        'cl9wb3B1cCAucHJvcGVydHlfaW5uZXIgLm55X3Byb3BlcnR5X2ljb24gew0KICAgIGJhY2tncm91bmQtcG9zaXRpb246IDVweCAt'+
        'NjBweDsNCiAgICBiYWNrZ3JvdW5kLXNpemU6IDc1cHggYXV0bzsNCiAgICAtbW96LWJhY2tncm91bmQtc2l6ZTogNzVweCBhdXRv'+
        'Ow0KICAgIGJhY2tncm91bmQtcmVwZWF0OiBuby1yZXBlYXQ7DQp9DQojY3JhZnRtYW5hZ2VyX3BvcHVwIC5wcm9wZXJ0eV9pbm5l'+
        'ciAucHJvcGVydHlfaWNvbiB7DQogICAgYmFja2dyb3VuZC1wb3NpdGlvbjogNXB4IC0xMHB4Ow0KICAgIGJhY2tncm91bmQtc2l6'+
        'ZTogNzVweCA3NXB4Ow0KICAgIC1tb3otYmFja2dyb3VuZC1zaXplOiA3NXB4IDc1cHg7DQogICAgYmFja2dyb3VuZC1yZXBlYXQ6'+
        'IG5vLXJlcGVhdDsNCn0NCiNjcmFmdG1hbmFnZXJfcG9wdXAgLnByb3BlcnR5X2lubmVyIGRpdi5ib2R5IHsNCiAgICBtaW4taGVp'+
        'Z2h0OiA2MnB4Ow0KICAgIHBhZGRpbmc6IDBweCA4MHB4IDBweCA4MHB4Ow0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQogICAgbWF4'+
        'LWhlaWdodDogNjJweDsNCn0NCiNjcmFmdG1hbmFnZXJfcG9wdXAgLnByb3BlcnR5X2lubmVyIGRpdi5ib2R5IGg0IHsNCiAgICBm'+
        'bG9hdDogbGVmdDsNCiAgICBtYXJnaW46IDJweCAwcHggMnB4IDEwcHg7DQp9DQojY3JhZnRtYW5hZ2VyX3BvcHVwIC5wcm9wZXJ0'+
        'eV9pbm5lciBkaXYuYm9keSBkaXYuY2RuX3RpbWVyIHsNCiAgICBmbG9hdDogcmlnaHQ7DQogICAgbWFyZ2luOiAycHggMzBweCAy'+
        'cHggMHB4Ow0KICAgIGZvbnQtc2l6ZTogMTJweDsNCn0NCiNjcmFmdG1hbmFnZXJfcG9wdXAgLnByb3BlcnR5X2lubmVyIGRpdi5i'+
        'b2R5IHAgew0KICAgIG1hcmdpbjogMHB4Ow0KICAgIHRleHQtYWxpZ246IGNlbnRlcjsNCiAgICBwYWRkaW5nLXRvcDogMjNweDsN'+
        'Cn0NCiNjcmFmdG1hbmFnZXJfcG9wdXAgLnByb3BlcnR5X2lubmVyIGRpdi5idXR0b25zIHsNCiAgICBwYWRkaW5nLXRvcDogMnB4'+
        'Ow0KICAgIGZsb2F0OiByaWdodDsNCn0NCiNjcmFmdG1hbmFnZXJfcG9wdXAgLnByb3BlcnR5X2lubmVyIGRpdi5idXR0b25zIGEg'+
        'ew0KICAgIHdpZHRoOiA3NXB4Ow0KfQ0KI2NyYWZ0bWFuYWdlcl9wb3B1cCAjaXRlbV9yZXF1aXJlbWVudHMgew0KICAgIHBhZGRp'+
        'bmctbGVmdDogMzZweDsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KICAgIGZvbnQtd2VpZ2h0OiBib2xkOw0KfQ0KI2NyYWZ0bWFu'+
        'YWdlcl9wb3B1cCAjaXRlbV9yZXF1aXJlbWVudHMgLnJlcV9maWVsZCB7DQogICAgZmxvYXQ6IGxlZnQ7DQogICAgbWFyZ2luOiAw'+
        'cHggMHB4IDBweCAxNXB4Ow0KfQ0KI2NyYWZ0bWFuYWdlcl9wb3B1cCAjaXRlbV9yZXF1aXJlbWVudHMgLnJlcV9maWVsZCBpbWcg'+
        'ew0KICAgIHdpZHRoOiAxOHB4Ow0KICAgIGhlaWdodDogMThweDsNCiAgICBmbG9hdDogbGVmdDsNCiAgICBtYXJnaW4tcmlnaHQ6'+
        'IDJweDsNCn0NCiNjcmFmdG1hbmFnZXJfcG9wdXAgdWwgew0KCWxpc3Qtc3R5bGUtdHlwZTogbm9uZTsNCglvdmVyZmxvdzogYXV0'+
        'bzsNCgl0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KI2NyYWZ0bWFuYWdlcl9wb3B1cCAjY3JhZnRpbmdMb2dnZXIgbGkgew0KICAgIHBh'+
        'ZGRpbmc6IDNweDsNCiAgICBib3JkZXItYm90dG9tOiAycHggc29saWQgIzIyMjsNCiAgICBtYXJnaW46IDFweDsNCiAgICB3aWR0'+
        'aDogYXV0byAhaW1wb3J0YW50Ow0KICAgIGJhY2tncm91bmQtc2l6ZTogMThweCAhaW1wb3J0YW50Ow0KICAgIC1tb3otYmFja2dy'+
        'b3VuZC1zaXplOiAxOHB4ICFpbXBvcnRhbnQ7DQogICAgbWluLWhlaWdodDogMThweCAhaW1wb3J0YW50Ow0KICAgIGJhY2tncm91'+
        'bmQtcG9zaXRpb246IDVweCA1MCU7DQp9DQojY3JhZnRtYW5hZ2VyX3BvcHVwICNjcmFmdGluZ0xvZ2dlciBsaSBwIHsNCiAgICBm'+
        'b250LXNpemU6IDEycHg7DQogICAgcGFkZGluZy1sZWZ0OiAzMHB4Ow0KfQ0KI2NyYWZ0bWFuYWdlcl9wb3B1cCAjY3JhZnRfbWVz'+
        'c2FnZXMgew0KICAgIGZsb2F0OiBsZWZ0Ow0KICAgIHdpZHRoOiA1NTBweDsNCiAgICBtYXJnaW4tdG9wOiAxMHB4Ow0KICAgIHRl'+
        'eHQtYWxpZ246IGxlZnQ7DQp9DQo='
    );
    
    // load options.
    options.load(Initialize);
}
// ==Script==
// @id        FightClubBuyer.js
// @author    Dakam
// @requires  MWAddon.js
// ==Script==
/**
 * Set configuration.
 */
UserConfig.create('fcopt', {});

function FightClubBuyer() {
    var gVar = {
        Abort: false,
        UserFavor: 0,
        UserTokens: 0,
        /** @type {CSQueue}       */ 
        Queue: null,
        /** @type {CSMarketplace} */ 
        Marketplace: null
    };    
    var Popup = new PopupObject('fightclubbuyer_popup', {
        type: 'main',
        title: 'FIGHTCLUB BUYER',
        width: 650, top: 20,
        onclose: function() {
            gVar.Abort = true;
            httpAjaxStopRequests();
        }
    });
    /**
     * Create a new Marketplace Item class.
     * @constructor
     * @param {Element, jQuery} elem
     * @return {CSMarketplaceItem}
     */
    var CSMarketplaceItem = function(elem) {
        this.icon = $('> div:eq(0) > div:eq(0) img', elem).attr('src');
        this.name = Util.trim($('> div:eq(0) > div:eq(1)', elem).text());
        this.attack = Util.parseNum($('> div:eq(0) > div:last > div:first', elem).text());
        this.defense = Util.parseNum($('> div:eq(0) > div:last > div:last', elem).text());
        this.tokens = Util.parseNum($('> div:eq(1) > div:first > div:eq(0)', elem).text());
        this.favor = Util.parseNum($('> div:eq(1) > div:first > div:eq(1) span', elem).text());
        this.buy = $('> div:eq(1) > div:first > a', elem).attr('href');
        try {
    	    this.id = parseInt(Util.uSplit(this.buy).favor_id);
        } catch (e) {}
        return this;
    };
    /**
     * Create a new Marketplace class.
     * @constructor
     * @return {CSMarketplace}
     */
    var CSMarketplace = function() {
        var Items = new Array();
        var sortBy = {
            'name': function(a, b) {
                var x = a.name.toLowerCase();
                var y = b.name.toLowerCase();
                return ((x < y) ? -1 : ((x > y) ? 1 : 0));
            },
            'att': function(a, b) {
                return b.attack - a.attack;
            },
            'def': function(a, b) {
                return b.defense - a.defense;
            },
            'tokens': function(a, b) {
                if (b.tokens == a.tokens) {
                    return sortBy.name(a, b);
                }
                return b.tokens - a.tokens;
            }
        }
        /**
         * @param {Number} index
         * @return {CSMarketplaceItem}
         */
        this.get = function(index) {
            return Items[parseInt(index)];
        };
        /**
         * @param {Number} id
         * @return {CSMarketplaceItem}
         */
        this.getById = function(id) {
            for(var i=0, item; (item=Items[i]); i++) {
                if (item.id == id) {
                    return item;
                }
            }
        };
        /**
         * @param {Number} i dummy
         * @param {Element, jQuery} elem
         */
        this.add = function(i, elem) {
            if (elem) {
                Items.push(new CSMarketplaceItem(elem));
            }
        };
        /**
         * @param {Function} callback
         */
        this.each = function(callback, sortBy) {
            if (Util.isString(sortBy)) {
                this.sort(sortBy);
            }
            Util.each(Items, callback);
        };
        /**
         * @return {Number}
         */
        this.length = function() {
            return Items.length;
        };
        this.sort = function(id) {
            if (Util.isSet(sortBy[id])) {
                Items.sort(sortBy[id]);
            }
        }
        return this;
    };
    /**
     * Create a new queue class.
     * @constructor
     * @return {CSQueue}
     */
    var CSQueue = function() {
        var me = this;
        var ItemsQuantities = new Object();
        var queue = '#fc_selected';
        
        this.totalCost = {'tokens':0, 'favor':0};
        
        /**
         * @param {CSMarketplaceItem} item
         */
        this.add = function(item, quantity) {
            if (!Util.isSet(item)) return;
            var $fld = e$('li#item_id'+item.id, queue)
            ||         c$('li', 'id:item_id'+item.id).appendTo(queue);
            
            quantity = ItemsQuantities[item.id] = (parseInt(ItemsQuantities[item.id]||0) + parseInt(quantity));
            $fld.html('Buy <span class="quantity">'+quantity+'</span> '+item.name);
            $fld.append(c$('div','class:remove').click(function() {
                ItemsQuantities[item.id] = 0;
                $(this).unbind();
                $('li#item_id'+item.id, queue).remove();
                me.updateCost();
                setBuyerStatus();
                return false;
            }));
            me.updateCost();
            setBuyerStatus();
        };
        /**
         * Update all items cost
         */
        this.updateCost = function() {
            me.totalCost = {'tokens':0, 'favor':0};
            gVar.Marketplace.each(function(index, item) {
                quantity = ItemsQuantities[item.id];
                if (quantity && quantity > 0) {
                    if (item.tokens > 0) {
                        me.totalCost.tokens += item.tokens * quantity;
                    }
                    if (item.favor > 0) {
                        me.totalCost.favor += item.favor * quantity;
                    }
                }
            });
        };
        /**
         * Clear all queue items.
         */
        this.clear = function() {
            me.totalCost = {'tokens':0, 'favor':0};
            ItemsQuantities = new Object();
            $(queue).empty();
            setBuyerStatus();
        }
        /**
         * Returns a new Array with all items and quantities.
         */
        this.toArray = function() {
            var result = new Array();
            Util.each(ItemsQuantities, function(id, quantity) {
                if (quantity && quantity > 0) {
                    result.push({
                        'quantity': quantity,
                        'item': gVar.Marketplace.getById(id)
                    })
                }
            });
            return result;
        }
        return this;
    };
    
    var Events = {
        start: function() {
            var me = $(this);
            if (me.hasClass('disabled')) {
                return false;
            }
            var q = gVar.Queue.toArray();
            if (q.length > 0) {
                if (gVar.Queue.totalCost.favor > gVar.UserFavor) {
                    notEnoughCoins('You\'ve not enough Reward Points.');
                } else if (gVar.Queue.totalCost.tokens > gVar.UserTokens) {
                    notEnoughCoins('You\'ve not enough Victory Coins.');
                } else {
                    gVar.Queue.clear();
                    startBuyer(q);
                }
            }
            function notEnoughCoins(message) {
                showHelpPopup({
                    icon: 'info',
                    title: 'Fight Club Buyer',
                    message: message + '<br>Remove some items and try again.'
                });
            }
            return false;
        },
        sort_click: function() {
            genListItems(this.value);
        },
        selectItem_click: function() {
            if ($(this).hasClass('selected')) {
                return false;
            }
            $('#fc_selectable li', Popup.content).removeClass('selected');
            $(this).addClass('selected');
            scrollTo(gVar.Marketplace.get($(this).attr('index')));
            return false;
        }
    };
    /**
     * Scroll to a new item
     * @param {CSMarketplaceItem} item
     */
    function scrollTo(item) {
        var $context = Popup.content;
        var statistics = '<div>Cost: '+toHtml(item.tokens,'tokens')
        + ((item.favor > 0) ? ('  '+toHtml(item.favor,'favor')) : '') +'</div>'
        + '<div>Quantity: <input type="text" id="item_quantity"></div>';
        
        $('#item_stats_inner a', $context).unbind();
        $('#item_stats_outer', $context).stop(true).animate({'scrollLeft':600},'fast','linear',function(){
            this.scrollLeft = 0;
            $('#item_name', $context).text(item.name);
            $('#item_stats_inner', $context).css('background-image','url('+item.icon+')').html(statistics);
            $('#item_quantity', $context).width(50).val(1);
            $('#item_stats_outer', $context).animate({'scrollLeft':300},'fast','linear', function(){
                var $quantity = $('#item_quantity', $context);
                b$('Add to queue','class:short green').click(function(){
                    gVar.Queue.add(item, $quantity.val());
                }).hide().appendTo('#item_stats_inner').fadeIn('normal');
                $quantity.focus().select();
            });
        });
    }
    
    function startBuyer(items) {
        var $context = Popup.content;
        var c_Items = new Collection(items);
        var selected = gVar.Marketplace.get($('#fc_selectable .selected').attr('index'));
        var $inner = $('#item_stats_inner', $context);
        var $outer = $('#item_stats_outer', $context);
        var $iname = $('#item_name', $context);
        
        $outer.get(0).scrollLeft = 0;
        $inner.html('<div>Progress: <span id="current_count">0</span> of <span id="total_count">0</span></div>');
        
        c_Items.onEnd(function() {
            $('#item_stats_outer').stop(true).animate({'scrollLeft':600},'fast','linear',function(){
                this.scrollLeft = 0;
                $iname.html('COMPLETED!');
                $inner.empty().addClass('completed').attr('style',' ')
                .append(c$('div').text('Process was finished.'))
                .append(b$('Go back','class:short orange').one('click', function() {
                    $('#fc_selected').show();
                    $('#fc_log, #disable_overlay').hide();
                    $inner.removeClass('completed');
                    scrollTo(selected);
                    setBuyerStatus();
                }))
                $outer.animate({'scrollLeft':300},'fast','linear');
            });
        });
        
        c_Items.onMove(function(pos, key, data) {
            if (gVar.Abort === true) return;
            var item = data.item;
            $outer.stop(true).animate({'scrollLeft':600},'fast','linear',function(){
                this.scrollLeft = 0;
                $inner.css('background-image','url('+item.icon+')');
                $('#total_count', $inner).text(data.quantity);
                $('#current_count', $inner).text(0);
                $outer.animate({'scrollLeft':300},'fast','linear', function(){
                    buyItem(item, data.quantity, 0);
                });
            });
        });
        function sendMessage(message) {
            var $span = Resources.getPicture('ajax_loader', 'span').css('padding-left',18);
            $iname.empty().append($span.text(message));
        }
        function buyItem(item, quantity, n_buyed) {
            (function buy() {
                if (gVar.Abort === true) return;
                sendMessage(' Buying '+item.name+'...');
                httpAjaxRequest({
                    url: item.buy,
                    liteLoad: 1,
                    error: function(err_msg) {
                        addLog(err_msg);
                        if (n_buyed < quantity) {
                            setTimeout(buy, 500);
                        } else {
                            c_Items.MoveNext();
                        }
                    },
                    success: function(htmlText) {
                        var $message = h$(htmlText).find('.messages .message_body');
                        n_buyed += 1;
                        gVar.UserTokens -= item.tokens;
                        gVar.UserFavor -= item.favor;
                        addLog($('> span', $message).text());
                        $('#current_count', $inner).text(n_buyed);
                        setBuyerStatus();
                        if (n_buyed < quantity) {
                            setTimeout(buy, 500);
                        } else {
                            c_Items.MoveNext();
                        }
                    }
                });
            })();
        }
        $('#fc_log textarea').val('');
        $('#fc_selected').hide();
        $('#fc_log, #disable_overlay').show();
        c_Items.MoveFirst();
    }
    
    function addLog(message) {
        var timestamp = (new Date()).toLocaleTimeString();
        var textarea = $('#fc_log textarea').get(0);
        textarea.value = '[' + timestamp + '] ' + message + '\n' + textarea.value;
    }
    
    function setBuyerStatus() {
        $('#start_button', Popup.content).toggleClass('disabled', $('#fc_selected li').length < 1);
        $('#total_tokens', Popup.content).text(gVar.UserTokens);
        $('#total_favor', Popup.content).text(gVar.UserFavor);
        $('#to_spend .tokens', Popup.content).text(gVar.Queue.totalCost.tokens);
        $('#to_spend .favor', Popup.content).text(gVar.Queue.totalCost.favor);
    }
    
    function toHtml(val, class_name) {
        return '<span class="'+class_name+'">'+val+'</span>';
    }
    
    function genListItems(sortBy) {
        var $ul = $('#fc_selectable', Popup.content)
        if (sortBy && $ul.attr('sortedby') == sortBy) {
            return;
        }
        $ul.empty().attr('sortedby', sortBy);
        gVar.Marketplace.each(function(index, item) {
            if (item.id == 0 || item.id == 1) {
                return;
            }
            var item_stats = toHtml(item.tokens,'tokens');
            if (item.attack > 0 || item.defense > 0) {
                item_stats += toHtml(item.attack,'attack') + toHtml(item.defense,'defense');
            }
            var $li = c$('li','index:'+index).appendTo($ul)
            .append(c$('img').attr('src', item.icon))
            .append(c$('div', 'class:item_name').text(item.name))
            .append(c$('div', 'class:item_stats').html(item_stats))
            .click(Events.selectItem_click);
            
        }, sortBy);
    }
    
    function genMainDom() {
        var $wrapper = c$('div','id:main_wrapper').appendTo(Popup.content);
        
        c$('div','class:top_header').appendTo($wrapper)
        .append(c$('div').html('Tokens: <span id="total_tokens" class="tokens">0</span>'))
        .append(c$('div').html('Favor: <span id="total_favor" class="favor">0</span>'))
        .append(c$('div','id:to_spend').css('float','right').html('Need: '+toHtml(0,'tokens')+' '+toHtml(0,'favor')));
        
        c$('div','class:left_wrapper').appendTo($wrapper)
        .append(c$('div','id:disable_overlay'))
        .append(c$('div','class:sort_list')
        .append(s$('sort_list','Sorted By:', 150, {click: Events.sort_click})))
        .append(c$('ul','id:fc_selectable'));
        
        $wrapper =  c$('div','class:right_wrapper').appendTo($wrapper);
        c$('div','id:more_info').appendTo($wrapper)
        .append(c$('div', 'id:item_stats_outer')
        .append(c$('div', 'id:item_name'))
        .append(c$('div', 'id:item_stats_inner')))
        .append(c$('ul',  'id:fc_selected'))
        .append(c$('div',  'id:fc_log').append(c$('textarea','readonly:readonly')))
        .append(c$('center')
        .append(b$('START BUYING','class:short green disabled,id:start_button').click(Events.start)));
        
        Popup.applyCustomClass();
        Popup.applyOptions({
			'sort_list': {
				'name'   :'Name', 
				'att'    :'Attack', 
				'def'    :'Defense', 
				'tokens' :'Tokens'
			}
        });
    }
    
    function Initialize() {
        function errorMessage() {
            showHelpPopup({
                icon: 'error',
                message: 'Cannot load the Marketplace Items.'
            });
            Popup.destroy();
        }
        httpAjaxRequest({
            url: 'remote/' + MW.getIntURL('marketplace','marketplace_category') + '&category=8',
            message: 'Loading Marketplace...',
            liteLoad: 1,
            error: errorMessage,
            success: function(htmlText) {
                var $h = h$(htmlText);
                
                gVar.Marketplace = new CSMarketplace();
                gVar.Queue = new CSQueue();
                
                gVar.UserFavor = global.User.reward_points();
                gVar.UserTokens = Util.parseNum($h.find('.marketplace_heading > div:has(img:regex(src,victory_icon))').text());
                
                $h.find('#full_list > div:has(a:regex(href,buy))').each(gVar.Marketplace.add);
                if (gVar.Marketplace.length() > 0) {
                    genMainDom();
                    genListItems('name');
                    Popup.show();
                    setBuyerStatus();
                } else {
                    errorMessage();
                }
            }
        });
    }
    
    Popup.addBase64Style(
        'I2ZpZ2h0Y2x1YmJ1eWVyX3BvcHVwIHNwYW4udG9rZW5zIHsNCiAgICBiYWNrZ3JvdW5kOiB1cmwoIiR7YmFzZV91cmx9dmljdG9y'+
        'eV9pY29uLmdpZiIpIDAgNTAlIG5vLXJlcGVhdDsNCiAgICBwYWRkaW5nLWxlZnQ6IDE2cHg7DQogICAgYmFja2dyb3VuZC1zaXpl'+
        'OiAxOHB4ICFpbXBvcnRhbnQ7DQogICAgLW1vei1iYWNrZ3JvdW5kLXNpemU6IDE4cHggIWltcG9ydGFudDsNCn0NCiNmaWdodGNs'+
        'dWJidXllcl9wb3B1cCBzcGFuLmZhdm9yIHsNCiAgICBiYWNrZ3JvdW5kOiB1cmwoIiR7YmFzZV91cmx9L3YzL2ljb24tZ2YtY29p'+
        'bi5naWYiKSAwIDUwJSBuby1yZXBlYXQ7DQogICAgcGFkZGluZy1sZWZ0OiAxNnB4Ow0KICAgIGJhY2tncm91bmQtc2l6ZTogMTRw'+
        'eCAhaW1wb3J0YW50Ow0KICAgIC1tb3otYmFja2dyb3VuZC1zaXplOiAxNHB4ICFpbXBvcnRhbnQ7DQp9DQojZmlnaHRjbHViYnV5'+
        'ZXJfcG9wdXAgI21haW5fd3JhcHBlciB7DQogICAgcGFkZGluZzogMnB4Ow0KICAgIG1hcmdpbjogNXB4Ow0KICAgIGJvcmRlcjog'+
        'c29saWQgMXB4ICM2NjY7DQogICAgaGVpZ2h0OiA0NDBweDsNCn0NCiNmaWdodGNsdWJidXllcl9wb3B1cCB1bCB7DQogICAgZm9u'+
        'dC1zaXplOiAxMnB4Ow0KICAgIG1hcmdpbjogMHB4Ow0KICAgIGxpc3Qtc3R5bGUtdHlwZTogbm9uZTsNCiAgICBvdmVyZmxvdzog'+
        'YXV0bzsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KICAgIHBhZGRpbmc6IDBweCA1cHg7DQp9DQojZmlnaHRjbHViYnV5ZXJfcG9w'+
        'dXAgdWwjZmNfc2VsZWN0YWJsZSB7DQogICAgaGVpZ2h0OiAzODlweDsNCn0NCiNmaWdodGNsdWJidXllcl9wb3B1cCAjZmNfc2Vs'+
        'ZWN0ZWQsDQojZmlnaHRjbHViYnV5ZXJfcG9wdXAgI2ZjX2xvZyB7DQogICAgaGVpZ2h0OiAyNDhweDsNCiAgICBib3JkZXItdG9w'+
        'OiBkb3R0ZWQgMXB4ICM2NjY7DQogICAgYm9yZGVyLWJvdHRvbTogZG90dGVkIDFweCAjNjY2Ow0KICAgIHBhZGRpbmctdG9wOiAx'+
        'cHg7DQp9DQojZmlnaHRjbHViYnV5ZXJfcG9wdXAgI2ZjX2xvZyB7DQogICAgZGlzcGxheTogbm9uZTsNCn0NCiNmaWdodGNsdWJi'+
        'dXllcl9wb3B1cCAjZmNfbG9nIHRleHRhcmVhIHsNCiAgICB3aWR0aDogOTAlOw0KICAgIGhlaWdodDogMjAwcHg7DQogICAgbWFy'+
        'Z2luLXRvcDogNXB4Ow0KICAgIGJhY2tncm91bmQtY29sb3I6IHRyYW5zcGFyZW50Ow0KfQ0KI2ZpZ2h0Y2x1YmJ1eWVyX3BvcHVw'+
        'IHVsI2ZjX3NlbGVjdGFibGUgbGkgew0KICAgIHBhZGRpbmc6IDNweDsNCiAgICB0ZXh0LWRlY29yYXRpb246IG5vbmU7DQogICAg'+
        'Ym9yZGVyOiAxcHggc29saWQgIzIyMjsNCiAgICBtYXJnaW46IDFweDsNCiAgICBjdXJzb3I6IHBvaW50ZXI7DQogICAgd2lkdGg6'+
        'IGF1dG8gIWltcG9ydGFudDsNCiAgICBtaW4taGVpZ2h0OiAxOHB4ICFpbXBvcnRhbnQ7DQp9DQojZmlnaHRjbHViYnV5ZXJfcG9w'+
        'dXAgI2ZjX3NlbGVjdGFibGUgbGk6aG92ZXIgew0KICAgIGJhY2tncm91bmQtY29sb3I6ICMyMjI7DQp9DQojZmlnaHRjbHViYnV5'+
        'ZXJfcG9wdXAgI2ZjX3NlbGVjdGFibGUgbGkuc2VsZWN0ZWQgew0KICAgIGJvcmRlcjogMXB4IHNvbGlkIHllbGxvdzsNCn0NCiNm'+
        'aWdodGNsdWJidXllcl9wb3B1cCAjZmNfc2VsZWN0ZWQgbGkgew0KICAgIHBhZGRpbmc6IDNweDsNCiAgICB0ZXh0LWRlY29yYXRp'+
        'b246IG5vbmU7DQogICAgYm9yZGVyOiAxcHggc29saWQgIzIyMjsNCn0NCiNmaWdodGNsdWJidXllcl9wb3B1cCAjZmNfc2VsZWN0'+
        'ZWQgbGkgZGl2LnJlbW92ZSB7DQogICAgYmFja2dyb3VuZC1pbWFnZTogdXJsKCR7YmFzZV91cmx9cmVxdWVzdHMvY3Jvc3MucG5n'+
        'KTsNCiAgICBmbG9hdDogcmlnaHQ7DQogICAgd2lkdGg6IDE2cHg7DQogICAgaGVpZ2h0OiAxNnB4Ow0KICAgIGN1cnNvcjogcG9p'+
        'bnRlcjsNCn0NCiNmaWdodGNsdWJidXllcl9wb3B1cCAjZmNfc2VsZWN0ZWQgbGkgc3Bhbi5xdWFudGl0eSB7DQogICAgY29sb3I6'+
        'IGdvbGQ7DQp9DQojZmlnaHRjbHViYnV5ZXJfcG9wdXAgdWwgbGkgaW1nIHsNCiAgICBmbG9hdDogbGVmdDsNCiAgICB3aWR0aDog'+
        'MThweDsNCiAgICBoZWlnaHQ6IDE4cHg7DQp9DQojZmlnaHRjbHViYnV5ZXJfcG9wdXAgdWwgbGkgZGl2Lml0ZW1fbmFtZSB7DQog'+
        'ICAgbWFyZ2luOiAwcHggMHB4IDBweCA1cHg7DQogICAgZmxvYXQ6IGxlZnQ7DQogICAgb3ZlcmZsb3c6IGhpZGRlbjsNCiAgICBt'+
        'YXgtaGVpZ2h0OiAyMHB4Ow0KICAgIG1heC13aWR0aDogMjYycHg7DQogICAgaGVpZ2h0OiAyMHB4Ow0KfQ0KI2ZpZ2h0Y2x1YmJ1'+
        'eWVyX3BvcHVwIHVsIGxpIGRpdi5pdGVtX3N0YXRzIHsNCiAgICBmbG9hdDogcmlnaHQ7DQogICAgbGluZS1oZWlnaHQ6IDE4cHg7'+
        'DQogICAgZm9udC1zaXplOiAxMXB4Ow0KfQ0KI2ZpZ2h0Y2x1YmJ1eWVyX3BvcHVwIHVsIGxpIGRpdi5pdGVtX3N0YXRzIHNwYW4g'+
        'ew0KICAgIG1hcmdpbi1yaWdodDogNXB4Ow0KICAgIHBhZGRpbmctbGVmdDogMTZweDsNCn0NCiNmaWdodGNsdWJidXllcl9wb3B1'+
        'cCBkaXYubGVmdF93cmFwcGVyIHsNCiAgICBmbG9hdDogbGVmdDsNCiAgICB3aWR0aDogMzIwcHg7DQogICAgYm9yZGVyLXJpZ2h0'+
        'OiBzb2xpZCAxcHggIzY2NjsNCn0NCiNmaWdodGNsdWJidXllcl9wb3B1cCBkaXYubGVmdF93cmFwcGVyICNkaXNhYmxlX292ZXJs'+
        'YXkgew0KICAgIHBvc2l0aW9uOiBhYnNvbHV0ZTsNCiAgICB6LWluZGV4OiAxOw0KICAgIGJhY2tncm91bmQtY29sb3I6IGJsYWNr'+
        'Ow0KICAgIG9wYWNpdHk6IDAuNDsNCiAgICB3aWR0aDogMzI1cHg7DQogICAgaGVpZ2h0OiA0MjFweDsNCiAgICBkaXNwbGF5OiBu'+
        'b25lOw0KfQ0KI2ZpZ2h0Y2x1YmJ1eWVyX3BvcHVwIGRpdi5yaWdodF93cmFwcGVyIHsNCiAgICBmbG9hdDogcmlnaHQ7DQogICAg'+
        'd2lkdGg6IDMwMHB4Ow0KICAgIG1hcmdpbjogMnB4IDZweCAwcHggMnB4Ow0KfQ0KI2ZpZ2h0Y2x1YmJ1eWVyX3BvcHVwIGRpdi5z'+
        'b3J0X2xpc3Qgew0KICAgIGJvcmRlci1ib3R0b206IDFweCBkb3R0ZWQgIzQ0NDsNCiAgICBwYWRkaW5nOiA1cHggMHB4Ow0KfQ0K'+
        'I2ZpZ2h0Y2x1YmJ1eWVyX3BvcHVwIGRpdi5uZXdfbGluZSB7DQogICAgY2xlYXI6IGJvdGg7DQogICAgbWFyZ2luLXRvcDogNXB4'+
        'DQp9DQojZmlnaHRjbHViYnV5ZXJfcG9wdXAgZGl2LnJpZ2h0X3dyYXBwZXIgI21vcmVfaW5mbyB7DQogICAgY2xlYXI6IGJvdGg7'+
        'DQogICAgaGVpZ2h0OiA0MTBweDsNCiAgICBib3JkZXI6IGRvdHRlZCAxcHggIzY2NjsNCn0NCiNmaWdodGNsdWJidXllcl9wb3B1'+
        'cCBkaXYucmlnaHRfd3JhcHBlciAjbW9yZV9pbmZvIGNlbnRlciB7DQogICAgcGFkZGluZy10b3A6IDRweDsNCiAgICB3aWR0aDog'+
        'Mjk4cHg7DQp9DQojZmlnaHRjbHViYnV5ZXJfcG9wdXAgZGl2LnJpZ2h0X3dyYXBwZXIgI21vcmVfaW5mbyAjaXRlbV9zdGF0c19v'+
        'dXRlciB7DQogICAgd2lkdGg6IDI5OHB4Ow0KICAgIG92ZXJmbG93OiBoaWRkZW47DQp9DQojZmlnaHRjbHViYnV5ZXJfcG9wdXAg'+
        'ZGl2LnJpZ2h0X3dyYXBwZXIgI21vcmVfaW5mbyAjaXRlbV9zdGF0c19vdXRlciAjaXRlbV9uYW1lIHsNCiAgICBoZWlnaHQ6IDIw'+
        'cHg7DQogICAgYm9yZGVyLWJvdHRvbTogMXB4IHNvbGlkICMyMjI7DQogICAgcGFkZGluZzogMHB4IDMwMHB4Ow0KICAgIGxpbmUt'+
        'aGVpZ2h0OiAyMnB4Ow0KICAgIHdoaXRlLXNwYWNlOiBub3dyYXA7DQogICAgcGFkZGluZzogMHB4IDMwMHB4Ow0KICAgIHdpZHRo'+
        'OiAzMDBweDsNCiAgICBmb250LXdlaWdodDogYm9sZDsNCn0NCiNmaWdodGNsdWJidXllcl9wb3B1cCBkaXYucmlnaHRfd3JhcHBl'+
        'ciAjbW9yZV9pbmZvICNpdGVtX3N0YXRzX291dGVyICNpdGVtX3N0YXRzX2lubmVyIHsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0K'+
        'ICAgIGhlaWdodDogOTBweDsNCiAgICBiYWNrZ3JvdW5kLXJlcGVhdDogbm8tcmVwZWF0Ow0KICAgIGJhY2tncm91bmQtcG9zaXRp'+
        'b246IDMxMHB4IDUwJTsNCiAgICBwYWRkaW5nOiAxMHB4IDMwMHB4IDBweCA0MDBweDsNCiAgICB3aGl0ZS1zcGFjZTogbm93cmFw'+
        'Ow0KICAgIHdpZHRoOiAyMTBweDsNCn0NCiNmaWdodGNsdWJidXllcl9wb3B1cCBkaXYucmlnaHRfd3JhcHBlciAjbW9yZV9pbmZv'+
        'ICNpdGVtX3N0YXRzX291dGVyICNpdGVtX3N0YXRzX2lubmVyLmNvbXBsZXRlZCB7DQogICAgcGFkZGluZy1sZWZ0OiAzNjBweDsN'+
        'CiAgICBiYWNrZ3JvdW5kLWltYWdlOiB1cmwoJHtiYXNlX3VybH0vaW1wdWxzZV9hcnJvd19hdHRhY2tfMDEucG5nKTsNCiAgICBi'+
        'YWNrZ3JvdW5kLXBvc2l0aW9uOiAzMTBweCA1MCU7DQp9DQojZmlnaHRjbHViYnV5ZXJfcG9wdXAgLnRvcF9oZWFkZXIgew0KICAg'+
        'IGJvcmRlci1ib3R0b206IDFweCBzb2xpZCAjMzMzOw0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQogICAgaGVpZ2h0OiAyMHB4Ow0K'+
        'ICAgIG1heC1oZWlnaHQ6IDIwcHg7DQogICAgcGFkZGluZy1yaWdodDogMjBweDsNCiAgICBvdmVyZmxvdzogaGlkZGVuOw0KICAg'+
        'IHdoaXRlLXNwYWNlOiBub3dyYXA7DQp9DQojZmlnaHRjbHViYnV5ZXJfcG9wdXAgLnRvcF9oZWFkZXIgZGl2IHsNCiAgICBmbG9h'+
        'dDogbGVmdDsNCiAgICBtYXJnaW4tbGVmdDogMjBweA0KfQ0KI2ZpZ2h0Y2x1YmJ1eWVyX3BvcHVwIC50b3BfaGVhZGVyIHNwYW4g'+
        'ew0KICAgIG1hcmdpbi1sZWZ0OiA1cHgNCn0=',
        {base_url: global.zGraphicsURL}
    );
    Initialize();
}
// ==Script==
// @id        FreeGiftsCenter.js
// @author    Dakam
// @requires  MWAddon.js
// ==Script==
/**
 * Set configuration.
 */
UserConfig.create('fgopt', {
    // exclude some settings when exporting.
    _excludedToExport: ['excludedGifts', 'today', 'acceptGiftCount'],
    
    today             : 0,
    acceptGiftCount   : 0,
    sendGiftCount     : 0,
    sendBack          : true,
    sendInternally    : true,
    excludedPattern   : 'reached your limit|gold mastery',
    dailyLimit        : 'cannot accept any more Free Gifts today',
    excludedGifts     : new Object(),
    activeFriends     : new Object(),
	customLists       : new Object(),
    useractions       : new Object(),
    activeFRMin       : 1,
    ignoreLimits      : false
});
/**
 * help collecting free gifts
 */
function FreeGiftsCenter() {
    if (facebook.initialized !== true) {
        showHelpPopup({
            icon: 'error',
            title: 'You cant use Free Gifts Center',
            message: 'Free Gifts Center requires Facebook Api and it\'s not loaded.'
        });
        return;
    }
    var options = UserConfig.fgopt;
    var gVar = {
        Aborted: false,
        AcceptGiftLimit: 250,
        ThankSent: new Object(),
        FriendList: new Object(),
        Events: {
    		3013: 'mafia_invite',
    		3012: 'gift',
    		3052: 'energy_pack',
    		3018: 'safehouse',
    		3053: 'none',
    		3054: 'none',
    		3053: 'none',
    		3053: 'none',
    		3066: 'help',
    		3065: 'none',
    		3067: 'none',
    		3070: 'family'
        },
        /** @type {CSRequestCollection} */ 
        Request: null,
        /** @type {CSCollector} */ 
        Collector: null
    };
    // popup
    var Popup = new PopupObject('freegiftscenter_popup', {
        type: 'main',
        title: 'FREE GIFTS CENTER',
        onclose: function() {
            options.fromDomElements();
            options.save();
        }
    });
    /**
     * Create a new RequestV2.
     * @constructor
     * @param {Object} e
     * @return {CSRequestV2}
     */
    var CSRequestV2 = function(e) {
        var params = Util.uSplit(e.acceptUrl);
        var sendBack = {
            to: new Array(),
            message: e.sendbackMsg,
            data: e.sendbackData,
            title: ''
        };
        this.id    = e.event_hash;
        this.type  = gVar.Events[e.event_type];
        this.url   = e.acceptUrl;
        this.rem   = e.ignoreUrl;
        this.icon  = e.sender_pic;
        this.name  = e.sender_name ;
        this.body  = e.request_msg;
        
        if (e.sendbackData) {
            sendBack.to.push(String(e.sender_id));
            this.send = sendBack;
        }
        this.gift = {
			item_id  : e.data.item_id,
			item_cat : e.data.item_cat,
			name     : e.item_name,
			img      : e.item_pic
        };
        this.user_id = Util.parseNum(params.from_user);
        this.profile = MW.getProfileLink(params.from_user);
        
        return this;
    };    
    /**
     * Create a new Request.
     * @constructor
     * @param {jQuery, Element} e
     * @return {CSRequest}
     */
    var CSRequest = function(e) {
        var params, $sendback, $accept, $ignore;
        
        if (e$('.acceptonly', e)) {
            $accept = $('.acceptonly', e);
            $ignore = $('.ignore', e);
            $sendback = $('.state_accept a:first', e);
        } else {
            $accept = $('.state_accept > a:first', e);
            $ignore = $('.state_accept > a:eq(1)', e);
            $sendback = $('.state_sendback .state_sendback_button', e);
        }
        this.id    = e.id.substr(12);
        this.type  = Util.doRgx(/(\w+)/, $(e).attr('class')).$1 || 'gift';
        this.url   = Util.toUrl($accept);
        this.rem   = Util.toUrl($ignore);
        this.icon  = Util.getPicture($('.player_pic',e).attr('style'));
        this.name  = Util.trim( $('.body > h4',e).text() ) ;
        this.body  = Util.trim( $('.body > p[id^=zmc_bodytext]',e).text() );
        this.send  = getSendBackRequest($sendback.attr('onclick'));
        
        var params = Util.uSplit(this.url);
        var gift = UserFreeGifts.getByItemId(params.item_id, params.item_cat);
        
        this.gift = {
			item_id  : params.item_id,
			item_cat : params.item_cat,
			name     : gift.name,
			img      : UserFreeGifts.getImageUrl(gift.img)
        };
		
        this.user_id = Util.parseNum(params.from_user);
        this.profile = MW.getProfileLink(this.user_id);
        
        return this;
    };
    /**
     * Create a new Request Collection.
     * @constructor
     * @return {CSRequestCollection}
     */
    var CSRequestCollection = function() {
        var groupedItemEvents = new Object();
        var requests = new Object();
        /**
         * Add a new request to the collection.
         * @param {CSRequest} req
         */
        this.add = function(req) {
            var groupedId = req.gift.item_id;
            requests[req.id] = req;
            if (!Util.isSet(groupedItemEvents[groupedId])) {
                groupedItemEvents[groupedId] = new Array();
            }
            groupedItemEvents[groupedId].push(req);
        };
        /**
         * Get a request from the collection.
         * @param {String} id
         * @return {CSRequest}
         */
        this.get = function(id) {
            return requests[id];
        };
        /**
         * Remove a request
         * @param {String} id
         */
        this.remove = function(id) {
            delete requests[id];            
        };
        /**
         * Return an array of requests filtered by type.
         * @param {Object} type
		 * @return {Object}
         */
        this.toArray = function(type) {
            var result = new Array();
            Util.each(requests, function(id, req) {
                if (!Util.isSet(type)||req.type === type) {
                    result.push(req);
                }
            });
            return result;
        };
		/**
		 * Return a grouped list of all gifts.
         * @param {Object} type
		 * @return {Object}
		 */
		this.toGroup = function(type) {
			var groupedList = new Object();			
			Util.each(requests, function(id, req) {
                if (!Util.isSet(type)||req.type === type) {
					var grp = groupedList[req.gift.item_id];
					if (!Util.isSet(grp)) {
						groupedList[req.gift.item_id] = (grp = new Array());
					}
                    grp.push(req);
                }
			});
			return groupedList;
		};
        /**
         * Loops through all requests.
         * @param {Function} callback
         */
        this.each = function(callback) {
            Util.each(requests, callback);
        };
        
        return this;
    };
    /**
     * Create a new Collector.
     * @constructor
     * @return {CSCollector}
     */
    var CSCollector = function() {
        var jobs = new Array();
        /**
         * Get the total requests amount
         */
        this.length = function() {
            return jobs.length;
        };
		/**
		 * Return the amount of an specific item_id
		 * @param {Object} iid
		 * @return {Number}
		 */
		this.amountOf = function(iid) {
			var amount = 0;
			Util.each(jobs, function(index, job) {
				if (job.iid && parseInt(job.iid) === parseInt(iid)) {
					amount += job.amount;
				}
			});
			return amount;
		};
        /**
         * @param {String} id
         * @param {Object} job
         */
        this.add = function(job) {
            jobs.push(job);
        };
        /**
         * @param {String} id
         * @return {Object}
         */
        this.get = function(index) {
            return jobs[index];
        };
        /**
         * Get all requests.
         */
        this.all = function() {
            return jobs;
        };
        /**
         * Loops through all gifts.
         * @param {Function} callback foo(gift, amount)
         */
        this.each = function(callback) {
            Util.each(jobs, callback);
        };
        /**
         * remove the specified item.
         */
        this.remove = function(index) {
            jobs.splice(index, 1);
        };
        /**
         * Clear collector.
         */
        this.clear = function() {
            jobs = new Array();
        };

        return this;
    };

    var excluded = {
        /**
         * Check if a gift is excluded
         * @param {Number} gid
         * @return {Boolean}
         */
        is: function(gid) {
            try {
                var nNow   = parseInt((new Date()).getTime()/1000);
                var isEx   = options.excludedGifts[gid];
                var nTime  = isEx ? (isEx - nNow) : 0;

                return (nTime > 0);
            }
            catch(err) {
                Logger.error(err);
            }
            return false;
        },
        /**
         * Add a gift to excluded list
         * @param {Number} gid
         */
        add: function(gid) {
            try {
                var nTime = parseInt((new Date()).getTime()/1000) + (6*60*60);
                options.excludedGifts[gid] = nTime;
                options.save();
            }
            catch(err) {
                Logger.error(err);
            }
        },
        /**
         * Reset the list
         */
        reset: function() {
            options.set('excludedGifts', new Object());
            options.save();
        }
    };

    // EVENTS
    var Events = {
        removeFromCollector: function() {
            var index = $(this).attr('index');
            gVar.Collector.remove(index);
            genMssSelectedDom();
            genMssSelectableDom();
            return false;
        },
        addAllGifts_click: function() {
            var mode = $(this).attr('mode');
            $('ul.mss_selectable li', Popup.content).each(function(index, element) {
                var $li = $(element);
                if (mode == '1') {
                    if (options.useractions[$li.attr('iid')]) {
                        addToCollector($li, null, options.useractions[$li.attr('iid')]);
                        $li.remove();
                    }
                } else {
                    addToCollector($li);
                    $li.remove();
                }
            });
            genMssSelectedDom();
            return false;
        },
        addGift_click: function() {
			var $li = e$('ul.mss_selectable li.selected', Popup.content);
			if (!$li) {
				$('#mss_info', Popup.content).html('Select a gift first!.');
				return false;
			}
			var n_left = addToCollector($li, $('#mass_body #mss_amount').val());
			if (n_left < 1) {
				$li.fadeOut(100, function() { $li.remove(); });
			} else {
                $('.gift_amount', $li).text(n_left+'x');
            }
			genMssSelectedDom();
            return false;
        },
        selectGift_click: function() {
			if ($(this).hasClass('selected')) {
				return;
			}
			$('#mass_body .mss_selectable li').removeClass('selected');
		    $('#mass_body #mss_amount').focus().val($(this).attr('count')||0).select();
            $(this).addClass('selected');
            return false;
        },
        startSending_click: function() {
            if (!$(this).hasClass('disabled') && !$('#friendlists').hasClass('loading')) {
                SendRequests();
            }
            return false;
        },
        friendLists_change: function() {
            if ($(this).hasClass('disabled') || $(this).hasClass('loading')) {
                return false;
            }
            var s = $('option:selected', this).val();
			//$('#save_friends_btn, #remove_list_btn').hide();
            $('#send_body #snd_users').empty();
            
            if (s !== 'none') {
                $('#remove_list_btn')[Util.isSet(options.customLists[s])?'show':'hide']();
                $('#snd_messages').html('Adding Friends...');
                setTimeout(function() {addFriends(s);}, 200);
            } else {
                $('#remove_list_btn').hide();
            }
        },
        editFriends_click: function() {
            if ($(this).hasClass('disabled') || $('#friendlists').hasClass('loading')) {
                return false;
            }
            var added_users = new Array();
            var $sl = $('#send_body #snd_users');
            $('option', $sl).each(function(i, op) {
                added_users.push(op.value);
            });
            showFriendsSelector(function(users) {
                if (users.length > 0) {
                    $sl.empty();
                    Util.each(users, function(i, user) {
                        c$('option','value:'+user.uid).appendTo($sl).text(user.name);
                    });
                }
            }, added_users);
            return false;
        },
		saveFriends_click: function() {
            if ($(this).hasClass('disabled')) {
                return false;
            }
            var added_users = new Array();
            var custom = options.customLists;
            var $sl = $('#send_body #snd_users');
            var $id = $('#friendlists option:selected');
            var id = $id.val();
            if (Util.isSet(options.customLists[id])) {
                update($id.text(), custom[id] = new Array());
            } else {
    			showPromptPopup('Friend List name (SIX char. min.):','Saved List',function(name) {
    				if (Util.isString(name) && name.length > 5) {
    					update(name, custom[name.replace(/\s/g,'_')] = new Array());
    				}
    			});
            }
            function update(list_name, list) {
	            $('option', $sl).each(function(i, op) {
	                list.push(op.value);
	            });
				options.save(loadFriendLists);
				showSenderMessage('Friend list "'+list_name+'" saved.');
            }
            return false;
		},
		removeList_click: function() {
			var $fl = $('#friendlists');
			var id = $('option:selected', $fl).val();
			var nm = $('option:selected', $fl).text();
			if (!Util.isSet(options.customLists[id])) {
                $(this).hide();
                return false;
            }
			showAskPopup('Remove Friendlist', 'Are you sure to remove?', function() {
				delete options.customLists[id];
				delete gVar.FriendList[id];
				options.save();
				$('option:selected', $fl).remove();
				$fl[0].selectedIndex = 0;
				$fl.change();
				showSenderMessage('Friend list "'+nm+'" removed.');
			});
            return false;
		},
        clearFriends_click: function() {
            if (!$(this).hasClass('disabled')) {
                $('#send_body #snd_users').empty();
            }
            return false;
        },
        resetActiveFriends_click: function() {
            options.set('activeFriends', new Object());
            options.save(function() {
                showHelpPopup({
                    icon: 'info',
                    title: 'Reset Active Friends List',
                    message: 'Your active friends list is now empty.'
                });
            });
            return false;
        },
        finishCollector_click: function() {
            $('#mss_info').show();
            $('#mss_messages').hide();
            showDiv('setup', 'mass');
            Events.clearCollector_click();
            genRequestDom();
            $('.tab_bar', Popup.content).removeClass('disabled');
            $(this).unbind().remove();
            return false;
        },
        clearCollector_click: function() {
            $('#mass_body .mss_selected').empty();
            showGiftLimitRemain();
            gVar.Collector.clear();
			genMssSelectableDom();
            return false;
        },
        startCollector_click: function() {
            if ($(this).hasClass('disabled')) {
                return false;
            }
            if (gVar.Collector.length() > 0) {
                showDiv('logger','mass');
                StartCollector();
            }
            return false;
        },
        tab_click: function() {
            if ($('.tab_bar',Popup.content).hasClass('disabled')||$(this).hasClass('active')) {
                return false;
            }
            var tabName = $(this).attr('tb');
            switch(tabName) {
                case 'mass':
                    showGiftLimitRemain();
                    Events.clearCollector_click();
                    break;
                case 'send':
                    showSenderMessage();
                    $('#send_body #snd_users').empty();
                    loadFriendLists();
					$('#friendlists').change();
                    break;
                case 'cnfg':
                    $('#total_activefriends').text(Util.length(options.activeFriends));
                    break;
            }
            $('.tab_bar li', Popup.content).removeClass('active');
            $(this).addClass('active');
            showDiv(tabName, 'body');
            options.fromDomElements();
            options.save();
            return false;
        },
        accept_click: function() {
            if ($(this).hasClass('disabled')) {
                return false;
            }
            $(this).addClass('disabled');
            
            var bAcceptOnly = $(this).hasClass('acceptonly');
            var req_id = $(this).attr('req');
            var req = gVar.Request.get(req_id);
            var $li = $('#reqslist > li#req_' + req_id);
            
            // check if request is valid
            if (!Util.isSet(req)) {
                $li.addClass('failed');
                $('.state_accept', $li).hide();
                $('.state_completed', $li).fadeIn(500);
                $('.body p', $li).html('There was an error with this request.');
                return false;
            }
            
            // check if gift is excluded
            if (options.ignoreLimits !== true && excluded.is(req.gift.item_id) && !$li.hasClass('asking')) {
                $li.addClass('asking');
                $('.state_accept', $li).hide();
                $('.state_retry', $li).fadeIn(500);
                $('.body p', $li).html('You\'ve reached the limit of this gift.<br>Are you sure to continue?');
                return false;
            }
            
            $('.state_accept, .state_completed, .state_retry', $li).hide();
            $li.removeClass('asking').addClass('completed');

            $('.body #loading_overlay', $li).show();
            $('.body p', $li).hide();

            CollectRequest(req, (options.sendBack && !bAcceptOnly), function(data) {
                var image = data.image ? '<img src="'+data.image+'">' : '';
                gVar.Request.remove(req_id);
                options.save();
                if (isLimitReached(data.message)) {
                    excluded.add(req.gift.item_id);
                }
                $('.body', $li).hide().html(image+'<p>'+data.message+'</p>').fadeIn(500);
                $('.state_completed', $li).fadeIn(500);
                
            }, function(response) {
                $('.body', $li).append('<span>'+response+'</span>');
            });
            
            return false
        },
        clear_request_click: function() {
            var id  = $(this).attr('req');
            $('#req_'+id).fadeOut(500, function(){
                $('#req_'+id).remove();
            });
            return false
        },
        ignore_request_click: function() {
            var id  = $(this).attr('req');
            var req = gVar.Request.get(id);
            var $li = $('#reqslist > li#req_' + id);
            
            $('.state_accept, .state_completed, .state_retry', $li).hide();            
            $('.body #loading_overlay', $li).show();
            $('.body p', $li).hide();
            
            if ($(this).hasClass('thanksonly') && req.send && req.send.to) {
                httpAjaxRequest({'url': req.rem, 'liteLoad': 1});
                thanksTo(req.send, function(message) {
                    $li.removeClass('asking').addClass('completed');
					$('.body #loading_overlay', $li).hide();
                    $('.body p', $li).hide().html('<p>'+message+'</p>').fadeIn(500);
                    $('.state_completed', $li).fadeIn(500);
                }, options.sendInternally); 
            } else {
                httpAjaxRequest({ 'url': req.rem, 'liteLoad': 1, 
                    'success': function(json){
                        $('#req_'+id).fadeOut(500, function(){
                            $('#req_'+id).remove();
                        });
                    }
                });
            }
            return false
        },
        refresh_click: function() {
            showDiv('ajaxloader', 'body');
            $('#category_select', Popup.content).val('all').change();
            getUserRequests(function() {
                genRequestDom();
                showDiv('reqs', 'body');
            });
            return false
        },
        clear_all: function() {
            $('#reqslist li.completed').fadeOut(500, function(){
                $('#reqslist li.completed').remove();
            });
        },
        reset_click: function() {
            excluded.reset();
            showHelpPopup({
                icon: 'info',
                title: 'Reset Exclude list',
                message: 'The gifts added to the exclude list has been reset.'
            });
            return false;
        },
        resetDailyLimit_click: function() {
            Logger.debug('Daily Gift Count was reset.');
            options.set('today', (new Date()).getDay());
            options.set('acceptGiftCount', 0);
            options.save();
            return false;
        },
        category_change: function(e) {
            var cls = 'req_box';
            var type = $('option:selected', this).val();
            if (type && type !== 'all') {
                cls += ' '+type;
            }
            $('#reqslist', Popup.content).attr('class', cls);
        },
        filter_change: function(e) {
            var $me = $(this);
            var s = $me.val();
            
            clearTimeout($me.attr('timeout'));
            if (!Util.isString(s) || s.length < 4) {
                $('#reqslist > li.matched', Popup.content).removeClass('matched');
                return true;
            }          
            $me.attr('timeout', setTimeout(function() {
                var regEx = new RegExp(s, 'i');
                $('#reqslist > li', Popup.content).each(function(i, e) {
                    if ( regEx.test($('.body p', e).text()) ) {
                        $(e).addClass('matched');
                    } else {
                        $(e).removeClass('matched');
                    }
                });
                if (!$('#reqslist').hasClass('search')) {
                    $('#category_select', Popup.content).val('search').change();
                }
            }, 500));
        }
    };
    /**
     * Add friends.
     * @param {String} lid
     */
    function addFriends(lid) {
        if (!Util.isSet(gVar.FriendList['all'])) {
            return;
        }        
        if (Util.isSet(gVar.FriendList[lid]) && Util.isSet(gVar.FriendList[lid].uids)) {
            var $sl = $('#send_body #snd_users');
            Util.each(gVar.FriendList[lid].uids, function(i, uid) {
				try {
	                uid = parseInt(uid);
	                if (!isNaN(uid) && Util.isSet(gVar.FriendList['all'][uid])) {
						var $op = e$('option[value='+uid+']', $sl);
						if (!$op) {
							$op = c$('option','value:'+uid).appendTo($sl);
						}
						$op.text(gVar.FriendList['all'][uid]);
	                }
				} catch (e) {}
            });
            showSenderMessage('Friends Loaded.');
        }
    }
    /**
     * Load all friends and MW default lists.
     * @param {Function} callback
     */
    function loadFriendLists(callback) {        
        var active = (gVar.FriendList['active'] = {
            name: 'Most Active Friends',
            uids: new Array()
        });
        
        Util.each(options.activeFriends, function(uid, count) {
           if (count > options.activeFRMin) {
               active.uids.push(uid);
           } 
        });
		
		Util.each(options.customLists, function(id, uids) {
            gVar.FriendList[id] = {
                name: id.replace(/_/g, ' '),
                uids: uids
            };
		});
		
		Util.each(gVar.FriendList, function(id, list) {
			if (id === 'all') {
				return true;
			}
			try {
				var $op = $('option[value='+id+']', '#friendlists');
			    if ($op.length < 1) {
			        $('#friendlists').append(c$('option','value:'+id).text(list.name));
			    } else {
                    $op.text(list.name);
                }
			} catch (e) {}
		});
    }
	/**
	 * Load app users
	 * @param {Function} callback
	 */
	function getAppFriends(callback) {
        facebook.getAppFriends(function(users) {
            var all = (gVar.FriendList['all'] = new Object());
            if (!users.error) {
                Util.each(users, function(index, user) {
                    all[user.uid] = user.name;
                });
				callback && callback();
            }
        });
	}
	/**
	 * Load MAfia Wars lists
	 */
    function getMWLists() {
		$('#snd_messages').empty().append(Resources.getPicture('ajax_loader','span').css('padding-left', 18).text('Loading Friend Lists...'));
		
		var params = 'free_gift_id=100&free_gift_cat=1&ajax=1&fbml_iframe=1';
		
        httpAjaxRequest({
            'url': 'remote/' + MW.getIntURL('requests', 'friend_selector') + params, 
            'liteLoad': 1, 
            'success': function(htmlText) {
                var $j = h$(htmlText);
                var ol = Util.doRgx(/MW.Request.setTabFriendLists\(([^\)]+)/, htmlText).$1;
                if (ol) {
                    ol = Util.parseJSON(ol);
                    Util.each(ol, function(id, uids) {
                        var name = String($j.find('#mfs_tab_'+id).text() || id);
                        gVar.FriendList[id] = {
                            'name': name,
                            'uids': uids
                        };
                    });
					showSenderMessage('All friendlists loaded.');
					if ($('.tab_bar li.active', Popup.content).attr('tb')==='send') {
						loadFriendLists();
					}
                }
            }
        });
    }
    /**
     * Parse a send back request
     * @param {String} text
     */
    function getSendBackRequest(text) {
        var request = {};
        if (text && text.indexOf('sendGiftbackRequest') > -1) {
            try {
	            eval(Util.substr(text, 'sendGiftbackRequest', ';',0, 1));
            } catch (e) {
            	Logger.error('getSendBackRequest: '+e.message);
            }
            return request;
        }
        function sendGiftbackRequest(a1,a2,a3,a4,a5) {
            request.to = new Array();
            request.to.push(String(a1));
            request.message = a2;
            request.data = a3;
            request.title = a4;
        }
        return;
    }
    
    /**
     * Collect a request and send back if specified.
     * @param {CSRequest} req
     * @param {Boolean} bSendBack
     * @param {Function} callback
     */
    function CollectRequest(req, bSendBack, callback, sendBackCallback) {
        var bSuccess = false, item_image, item_message, bIsBackSent = false;
        if (req.send && req.send.to) {
            addActiveFriend(req.send.to[0]);
        }
        httpAjaxRequest({
            'url': req.url, 
            'liteLoad': 1, 
            'success': function(json) {
                bSuccess = Util.isBoolean(json.is_success) ? json.is_success 
                :          (parseInt(json.is_success) === 1);
                
                if ( bSuccess ) {
                    item_image = json.item_image_path;
                    item_message = json.success_message;
                    
                    if (bSendBack === true && req.send && req.send.to) {
						bIsBackSent = true;
                        thanksTo(req.send, sendBackCallback, options.sendInternally);
                    }
                }
                else {
                    item_message = json.fail_message
                    ? Util.setColor(json.fail_message, 'red')
                    : Util.setColor('There was an error with this request.', 'red');
                }
                if (bSuccess && req.type == 'gift') {
                    options.acceptGiftCount += 1;
                    Logger.debug('Gift count increased by 1 ('+options.acceptGiftCount+')');
                }
                callback({
                    'success'    : bSuccess,
                    'image'      : item_image,
                    'message'    : item_message,
                    'isBackSent' : bIsBackSent
                });
            },
            error: function(msg) {
                callback({
                    'success'    : false,
                    'message'    : msg
                });
            }
        });
    }
    /**
     * Add a gift to collector.
     * @param {jQuery} $li
     * @param {Number} n_amount
     * @param {String} action
     */
    function addToCollector($li, n_amount, action) {
        var n_iid = $li.attr('iid');
		var n_total = parseInt($li.attr('count')) - gVar.Collector.amountOf(n_iid);
		n_amount = Math.min(n_total, (n_amount||n_total));
		gVar.Collector.add({
			iid    : n_iid,
			name   : $('.gift_name', $li).text(),
			icon   : $('img', $li).attr('src'),
			action : action || $('#mass_body #mss_action').val(),
			amount : n_amount
		});
        return n_total - n_amount;
    }
    /**
     * Add an user to active friends list.
     * @param {Object} uid
     */
    function addActiveFriend(uid) {
        if (!isNaN(uid = parseInt(uid))) {
            if (Util.isNumber(options.activeFriends[uid]) ) {
                options.activeFriends[uid]++;
            } else {
                options.activeFriends[uid] = 1;
            }
            options.save();
        }
    }
    /**
     * Return true if user reached the limit of the specified gift.
     * @param {String} text collect response.
     */
    function isLimitReached(text) {
        var expr = new RegExp(options.excludedPattern, 'i');
        if (options.excludedPattern) {
            return expr.test(text);
        } else {
            return false;
        }
    }
    /**
     * Return true if user daily limit is reached.
     * @param {String} text
     */
    function isDailyLimitReached(text) {
        if (Util.isString(options.dailyLimit) && options.dailyLimit.length > 3) {
            if ((new RegExp(options.dailyLimit, 'i')).test(text)) {
                options.set('acceptGiftCount', gVar.AcceptGiftLimit);
                options.save();
                return true;
            } else {
                return false;
            }
        }
    }
    /**
     * Show a message about the remaining gifts the user can collect.
     */
    function showGiftLimitRemain() {
        var remain = Math.max(gVar.AcceptGiftLimit - options.acceptGiftCount, 0);
        $('#mss_info', Popup.content).html('You can collect about '+remain+' more gifts today.');
    }
    /**
     * Show a message about the total gifts sent by the user.
     */
    function showSenderMessage(message) {
        if (message) {
            message += '<br>'
        } else {
            message = '';
        }
        message += 'You\'ve sent '+options.sendGiftCount+' gifts today.';
        $('#snd_messages', Popup.content).html(message);
    }
    /**
     * Send request to selected friends.
     */
    function SendRequests() {
        var bCancelled = false;
        var toIds = new Array();
        var toSend = new Array();
        var $sel = $('.snd_selectable li:has(.checked)');
        var c_gifts, gift_id, gift, n_sendPerGift = 50;
        
        $('#send_body .start_ctrl a, #friendlists, .snd_selectable, .tab_bar', Popup.content).addClass('disabled');
        $('#snd_messages').empty();
        
        if ($sel.length < 1) {
            addMessageLog('Check a gift to send.');
            finish();
            return;
        }
        
        $('#send_body #snd_users option').each(function(index, elem) {
            toIds.push(parseInt(elem.value));
        });
        
        if (toIds.length < 1) {
            addMessageLog('Select which friends you want to send to.');
            finish();
            return;
        }
        
        if ($sel.length === 1) {
            gift = UserFreeGifts.get((gift_id = $sel.attr('gid')));
        } else {
            c_gifts = new Array();
            $sel.each(function(index, e) {
                c_gifts.push($(e).attr('gid'));
            });
            n_sendPerGift = Math.min(50, parseInt(toIds.length/c_gifts.length));
            
            c_gifts = new Collection(c_gifts);
            c_gifts.onEnd(c_gifts.MoveFirst);
            c_gifts.onMove(function(a1, a2, gid) {
                gift = UserFreeGifts.get((gift_id = gid));
            });
            c_gifts.MoveFirst();
        }
        
        while (toIds.length > 0) {
            toSend.push(toIds.splice(0, n_sendPerGift));
        }
        
        function loadGift(callback) {
            if (bCancelled||gVar.Aborted) {
                finish();
                return;
            }
            function checkGiftData(data, msg, title) {
                if (data && msg && title) {
                    clearLoadingMessage(true);
                    callback({
                        message : msg,
                        data    : data,
                        title   : title
                    });
                } else {
                    clearLoadingMessage(false);
                    addMessageLog('Error loading the gift data.', 'error');
                    callback();
                }
            } 
            addMessageLog('Loading and preparing "'+gift.name+'" to be send.', 'load');
            if (gift.request_id) {
                MW.getCityRequest(gift.request_id, checkGiftData);
            } else {
                MW.getGiftRequest(gift_id, gift.item_cat, checkGiftData, gift.params);
            }
        }
                
        function startSending() {
            var n_total = toSend.length;
            
            $('#snd_messages').empty();
            addMessageLog(n_total+' Requests To Send. '+n_sendPerGift+' friend(s) per request.');
            
            function nextSend(send) {
                if (bCancelled||gVar.Aborted) {
                    finish();
                    return;
                }
                if (!Util.isSet(send)) {
                    nextLoad();
                    return;
                }
                send.to = toSend.shift();
                addMessageLog('Sending "'+gift.name+'" to '+send.to.length+' friend(s).', 'load');
                thanksTo(send, function(message, success) {
					clearLoadingMessage(success);
					addMessageLog(message);
                    if (success === true) {
                        nextLoad();                        
                    } else {
                        if (toSend.length>0) {
                            showAskPopup('Error Sending Requests', 'Do you want to continue?', nextLoad, finish);
                        } else {
                            finish();
                        }
                    }
                    
                }, options.sendInternally);
            }
            function nextLoad() {
                if (bCancelled||gVar.Aborted||toSend.length < 1) {
                    finish();
                } else if (c_gifts) {
                    c_gifts.MoveNext();
                    setTimeout(function() {loadGift(nextSend);}, 50);
                } else {
                    loadGift(nextSend);
                }
            }
            nextLoad();
        }
        /**
         * @param {String} message
         */
        function clearLoadingMessage(result) {
            var cs = (result !== true ? 'mwa_res_ajax_error' : 'mwa_res_ok_icon');
            $('#snd_messages div[name=load]').attr('name','ok').removeClass().addClass(cs);
        }
        /**
         * @param {String} message
         */
        function addMessageLog(message, icon) {
            var $div = Resources.getPicture('info_icon');
            switch(icon) {
                case 'error':  $div = Resources.getPicture('ajax_error');  break;
                case 'load':   $div = Resources.getPicture('ajax_loader'); break;
            }
            $div.prependTo('#snd_messages').attr('name',(icon?icon:'info')).append(c$('span').html(message));
        }
        
        function finish() {
            addMessageLog('Process Finished.');
            $('#send_body .start_ctrl a, #friendlists, .snd_selectable, .tab_bar', Popup.content).removeClass('disabled');
        }
        
        startSending();
    }
    /**
     * Collect selected gifts
     */
    function StartCollector() {
        var n_Success = 0;
        var bCancelled = false;
        var c_reqs, c_gift = new Collection(gVar.Collector.all());
        var grReqs = gVar.Request.toGroup('gift');
        /**
         * Stop Event
         */
        function stop_click() {
             $(this).unbind().remove(); 
             bCancelled = true;
             finish();
             return false;
        }
		
        c_gift.onEnd(finish);
        c_gift.onMove(function(r1, r2, req_set) {
            if (req_set.iid) {
                options.useractions[req_set.iid] = req_set.action;
            }
            if (bCancelled || gVar.Aborted) {
                // nothing to do.
            }
            else if (req_set.amount < 1 || !Util.isSet(grReqs[req_set.iid])) {
                c_gift.MoveNext();
            }
            else if (options.ignoreLimits !== true && excluded.is(req_set.iid)) {
                addMessageLog('"'+req_set.name+'" skipped. Limits are reached.');
                c_gift.MoveNext();
            }
			else {
				grReqs[req_set.iid].sort(function(a, b) {
					var x = (a.send && a.send.to);
					var y = (b.send && b.send.to);
					if (req_set.action==='ignore' || req_set.action==='okonly') {
						return (x && !y) ? 1 : (!x && y) ? -1 : 0;
					}
					else {
						return (x && !y) ? -1 : (!x && y) ? 1 : 0;
					}
				});
				setGiftName(Util.setColor(req_set.name, 'green')+':');
	            c_reqs = new Collection(grReqs[req_set.iid].splice(0,req_set.amount));
				c_reqs.onEnd(c_gift.MoveNext);
				c_reqs.onMove(nextRequest);
				c_reqs.MoveFirst();
			}
            
            function nextRequest(i1, i2, req) {
                if (bCancelled||gVar.Aborted) {
                    return;
                }
                if (!Util.isSet(req)) {
                    c_reqs.MoveNext();
                    return;
                }
				var gift_title = req_set.name+' ('+(i1+1)+' of '+req_set.amount+')';
				
				if (req_set.action === 'ignore') {
					sendMessage('Ignore to ' + req.name, true);
	                httpAjaxRequest({
						'url': req.rem, 
						'liteLoad': 1, 
						'success': function(json) {
							gVar.Request.remove(req.id);
							addMessageLog('Request ignored by the user.', 'info', req);
							c_reqs.MoveNext();
	                    }
	                });
                    return;
				}
				if (req_set.action === 'thank') {
					sendMessage('Thank to ' + req.name, true);
					thanksTo(req.send, function(message){
						gVar.Request.remove(req.id);
						addMessageLog('Thank only: ' + message, 'info', req);
						c_reqs.MoveNext();
					}, true);
                    return;
				}
                if (options.ignoreLimits !== true && excluded.is(req_set.iid)) {
                    c_gift.MoveNext();
                    return;
                }
				if (req_set.action==='accept') {
					sendMessage('Accept and Thank to ' + req.name, true);
				} else {
					sendMessage('Accept to ' + req.name, true);
				}
                CollectRequest(req, options.sendBack&&(req_set.action==='accept'), function(data) {
                    gVar.Request.remove(req.id);
                    if (bCancelled||gVar.Aborted) {
                        return;
                    }
                    if (data.success) {
                        n_Success += 1;
                        addCollectLog(gift_title, req, data);
	                    if (isLimitReached(data.message) && !excluded.is(req_set.iid)) {
	                        excluded.add(req_set.iid);
	                        addMessageLog('Limit reached. "' + req_set.name + '" added to exclude list.');
	                    }
                    }
                    else {
                        addMessageLog(data.message, 'error', req);
                    }
                    if (isDailyLimitReached(data.message)) {
                        addMessageLog('Daily Limit reached, stopped.', 'info');
                        bCancelled = true;
                        finish();
                        return;
                    }
                    if (data.isBackSent !== true) {
                        c_reqs.MoveNext();
                    }
                    
                }, function(message) {
                    addMessageLog('Accept and Thank: '+message, 'info', req);
                    c_reqs.MoveNext();
                });
            }
        });
        /**
         * @param {String} title
         * @param {MafiaMember} req
         * @param {Object} data
         */
        function addCollectLog(gift_name, req, data) {
            var gift_image = data.image || global.zGraphicsURL+'v3/icon_gift_27x28_01.png';
            var title = gift_name + ' from ' + Util.setAnchor(req.profile, req.name);
            var profile_pic = Util.isString(req.icon)
            ? c$('img').attr('src', req.icon) 
            : c$('span');
            c$('li').prependTo('#mss_successlog')
            .append(c$('h4').html(title)).append(profile_pic)
            .append(c$('img').attr('src', gift_image))
            .append(c$('p').html(data.message));
        }
        /**
         * @param {String} message
         * @param {String} icon
         * @param {MafiaMember} req
         */
        function addMessageLog(message, icon, req) {
            var $li = (icon==='error') 
            ? Resources.getPicture('ajax_error', 'li')
            : Resources.getPicture('info_icon', 'li');
            
            var userProfile = Util.isSet(req) 
            ? Util.setAnchor(req.profile, req.name) + ': ' : '';
            
            if (icon !== 'error') {
                message = Util.setColor(message, '#E1E1E1');
            } else {
                message = Util.setColor(message, 'red');
            }
            $li.prependTo('#mss_messagelog').append(c$('p').html(userProfile + message));
        }

        function setGiftName(giftname) {
            $('#mss_reqname', Popup.content).html(giftname);
        }

        function sendMessage(message, ajax) {
            $('#mss_messages').toggleClass('mwa_res_ajax_loader', ajax===true);
            $('#mss_message', Popup.content).html(message);
        }
        
        function finish() {
            options.save();
            c_gift.clear();
            setGiftName('Finished');
            sendMessage(n_Success+' gifts collected.');
            addMessageLog('Process finished at '+(new Date()).toUTCString(), 'info');
            $('#mss_messages a').remove();
            gVar.Collector.clear();
            b$('Return','class:short green').css('float','right')
            .appendTo('#mss_messages').click(Events.finishCollector_click);
        }
        // start
        $('.tab_bar', Popup.content).addClass('disabled');
        $('#mss_successlog, #mss_messagelog', Popup.content).empty();
        $('#mss_messages a', Popup.content).remove();
        $('#mss_reqname, #mss_message', Popup.content).empty();
        $('#mss_info').hide();
        $('#mss_messages').show();
        
        b$('STOP','class:short red').css('float','right').appendTo('#mss_messages').click(stop_click);
        addMessageLog('Process started at '+(new Date()).toUTCString(), 'info');
        c_gift.MoveFirst();
    }
	/**
	 * Send thanks.
	 * @param {Object} data
	 * @param {Function} callback
	 * @param {Boolean} [internally]
	 */
	function thanksTo(data, callback, internally) {
		var toUsers = new Array()
		if (data && Util.isArray(data.to)) {
			if (data.to.length === 1) {
				if (gVar.ThankSent[data.to[0]] === true) {
					userError();
					return;
				}
			}
			else {
				Util.each(data.to, function(index, uid) {
					if (gVar.ThankSent[uid] !== true) {
						toUsers.push(uid);
					}
				});
				if ((data.to = toUsers).length < 1) {
					userError();
					return;
				}
			}
		} else {
			userError();
			return;
		}
		facebook.sendRequest(data, function(message) {
			Util.each(data.to, function(index, uid) {
				gVar.ThankSent[uid] = true;
			});
			if (message === false) {
				callback(Util.setColor('Error sending request.', 'red'), false);
			} else {
                options.sendGiftCount += Util.parseNum(h$(message).text());
                Logger.debug('Thanks Count increased to '+options.sendGiftCount);
				callback(message, true);
			}
		}, internally);
		
		function userError() {
			callback(Util.setColor('Can\'t sent to this user(s) again.', 'yellow'), false);
		}
	}
	/**
	 * Get all users requests.
	 * @param {Function} callback
	 */
    function getUserRequests(callback) {
        httpAjaxRequest({
            url: 'remote/' + MW.getIntURL('messageCenter', 'view'),
            success: function(htmlText) {
                var $html = h$(htmlText);
                var zmc = $html.find('script:regex(ZmcController)');
                var $li = $html.find('ul#zmc_message_list_ul li');
                
                Popup.content.append(zmc);
                zmc = unsafeWindow.ZmcController;
                gVar.Request = new CSRequestCollection();
                
                if (Util.isSet(zmc.allEvents)) {
                    Util.each(zmc.allEvents, function(index, e) {
                        gVar.Request.add(new CSRequestV2(e)); 
                    });
                } else {
                    $li.each(function(index, elem) {
                        gVar.Request.add(new CSRequest(elem)); 
                    });
                }
                callback && callback(true);
            },
            error: function(msg) {
                callback && callback(false);
                showBadResponse(msg);
            }
        });
    }

    function showDiv(name, type, ms, fn) {
        $('div[id*=_'+type+']', Popup.content).hide();
        var elem = $('#' + name + '_' + type, Popup.content);
        if (ms) {
            elem.fadeIn(ms, fn);
        }
        else {
            elem.show();
        }
    }

    function genMainDom() {
        // fix content height
        Popup.content.height(680);

        // add tab bar
        var $tab = c$('ul', 'class:tab_bar').appendTo(Popup.content);

        c$('li','tb:reqs,class:request active').appendTo($tab).click(Events.tab_click)
        .append(c$('a','href:#').html('<span><em>Requests</em></span>'));
        c$('li','tb:mass,class:collector').appendTo($tab).click(Events.tab_click)
        .append(c$('a','href:#').html('<span><em>Collector</em></span>'));
        c$('li','tb:send,class:sender').appendTo($tab).click(Events.tab_click)
        .append(c$('a','href:#').html('<span><em>Sender</em></span>'));
        c$('li','tb:cnfg').appendTo($tab).click(Events.tab_click)
        .append(c$('a','href:#').html('<span><em>Configuration</em></span>'));

        // loader
        c$('div', 'ajaxloader_body').css('padding-top',40).height(600).appendTo(Popup.content)
        .append(c$('img').attr('src', global.zGraphicsURL+'socialmissions/ajax-loader.gif'));

        function addReqLayout($to) {
            var $frm = c$('div', 'class:frame_box').appendTo($to);
            
            c$('div').css('float','left').appendTo($frm)
            .append(t$('filter_text', 'Search:', 150))
            .append(s$('category_select', 'Category:', 150));
            
            c$('div').css('float','right').appendTo($frm)
            .append(b$('Clear all', 'class:short white').click(Events.clear_all))
            .append(b$('Refresh','class:short orange').css('margin-left',5).click(Events.refresh_click));
    
            c$('ul', 'id:reqslist,class:req_box').height(560).appendTo($to);            
        }
        function addColLayout($to) {
            var $frm = c$('div', 'class:frame_box').appendTo($to);
            c$('div', 'id:mss_info').css('min-height',40).appendTo($frm);
            c$('div', 'id:mss_messages')
            .append(c$('span', 'id:mss_reqname'))
            .append(c$('span', 'id:mss_message').css('margin-left',5))
            .appendTo($frm);
            
            $frm = c$('div', 'id:setup_mass,class:frame_box').appendTo($to).css('height',520);
            
            c$('div','class:li_wrapper').appendTo($frm)
			.append(c$('div','class:top_ctrl').css('height',60)
			.append(s$('mss_action', 'Action:', 260))
			.append(c$('div').css({'clear':'both','margin-top':4}))
			.append(n$('mss_amount', 'Amount:', 50))
			.append(b$('Selected', 'class:short green').click(Events.addGift_click))
			.append(b$('All', 'class:short white').click(Events.addAllGifts_click))
			.append(b$('All (S)', 'class:short white,mode:1').click(Events.addAllGifts_click)))
            .append(c$('ul', 'class:mss_selectable').css('height',430));
            
            c$('div','class:li_wrapper').appendTo($frm)
            .append(c$('ul', 'class:mss_selected'))
            .append(c$('center', 'class:start_ctrl')
            .append(b$('CLEAR','class:medium white').click(Events.clearCollector_click))
            .append(b$('START!','class:medium green').click(Events.startCollector_click)));
            
            $to = c$('div','logger_mass').appendTo($to);
            
            $to.append(c$('ul','mss_messagelog').height(250))
            $to.append(c$('ul','mss_successlog').height(300));
        }
        function addSndLayout($to) {
            var $frm = c$('div', 'class:frame_box').appendTo($to);
            
            c$('div', 'id:snd_messages').appendTo($frm);
            
            $frm = c$('div', 'class:frame_box').appendTo($to).css('height',500);
            
            c$('div','class:li_wrapper').appendTo($frm)
            .append(c$('ul', 'class:snd_selectable').css('height',484));
            
            c$('div','class:li_wrapper').appendTo($frm)
            .append(c$('div','class:top_ctrl').appendTo($frm)
            .append(s$('friendlists', 'List:', 200))
            .append(c$('a', 'id:remove_list_btn').text('Remove').click(Events.removeList_click)))
            .append(c$('select', 'multiple:multiple,id:snd_users').css({'height':406,'width':'100%'}))
            .append(c$('center', 'class:start_ctrl')
            .append(b$('CLEAR','class:medium white').click(Events.clearFriends_click))
            .append(b$('EDIT','class:medium orange').click(Events.editFriends_click))
            .append(b$('SAVE','class:medium orange,id:save_friends_btn').click(Events.saveFriends_click))
            .append(b$('SEND','class:medium green').click(Events.startSending_click)));
            
            $('#friendlists').change(Events.friendLists_change); 
            
        }        
        function addCfgLayout($to) {
            c$('div', 'class:frame_box')
            .append(x$('fgopt_sendback', 'Send requests back (check "Don\'t ask again..." on confirmation box to make it faster).'))
            .append(c$('div').css({'clear':'both','margin-bottom':5}))
            .append(x$('fgopt_sendinternally', 'Send requests internally only (don\'t send facebook request).'))
            .append(c$('div').css({'clear':'both','margin-bottom':5}))
            .append(x$('fgopt_ignorelimits', 'Continue collecting when limits are reached.'))
            .appendTo($to);
            
            c$('div', 'class:frame_box')
            .append(c$('h4').css('margin',0).text('Daily Limit:'))
            .append(c$('a','href:#').text('Reset internal count.').click(Events.resetDailyLimit_click))
            .append(c$('div').css({'clear':'both','margin-bottom':5}))
            .append(c$('input:text','id:fgopt_dailylimit').width('100%'))
            .appendTo($to);
            
            c$('div', 'class:frame_box')
            .append(c$('h4').css('margin',0).text('Excluded Gifts:'))
            .append(c$('a','href:#').text('Reset all gifts in excluded list.').click(Events.reset_click))
            .append(c$('div').css({'clear':'both','margin-bottom':5}))
            .append(c$('input:text','id:fgopt_excludedpattern').width('100%'))
            .appendTo($to);
    
            c$('div', 'class:frame_box')
            .append(c$('h4').css('margin',0).text('Active Friends:'))
            .append(n$('fgopt_activefrmin', 'Active Friends are those who send me', 50))
            .append(c$('span').text('requests or more.  '))
            .append(c$('div').html('Total Active Friends '+Util.setColor(0,'yellow','total_activefriends')))
            .append(c$('div').html('Only those friends which you can send requests will be added when you select "Active Friends" list.'))
            .append(c$('div').css({'clear':'both','margin-bottom':5}))
            .append(c$('a','href:#').text('Reset active friends list.').click(Events.resetActiveFriends_click))
            .appendTo($to);
        }
		
        // ADD BODY ELEMENTS
        addReqLayout( c$('div', 'reqs_body').height(640).appendTo(Popup.content) );
        addColLayout( c$('div', 'mass_body').height(640).appendTo(Popup.content) );
        addSndLayout( c$('div', 'send_body').height(640).appendTo(Popup.content) );
        addCfgLayout( c$('div', 'cnfg_body').height(640).appendTo(Popup.content) );
        
        $('#filter_text', Popup.content).keypress(Events.filter_change).change(Events.filter_change);
        $('#category_select', Popup.content).change(Events.category_change);
        
        UserFreeGifts.each(function(id, gift) {
            c$('li','gid:'+id).appendTo('.snd_selectable')
            .append(x$('checked_gift_'+id, gift.name, 'div').css('float','left')
            .prepend(c$('img').attr('src', UserFreeGifts.getImageUrl(gift.img))));
        }, true);
        
        function getEventOptions() {
            var result = {
                'all'    : 'All',
                'search' : 'Search'
            };
            Util.each(gVar.Events, function(id, name) {
                if (name !== 'none') {
                    result[name] = Util.formatText(name.replace(/_/g,' '));
                }
            });
            return result;
        }
        Popup.applyOptions({
			'mss_action': {
				'accept'  :'Accept And Thank', 
				'okonly'  :'Accept Only', 
				'thank'   :'Thank Only', 
				'ignore'  :'Ignore'
			},
            'category_select' : getEventOptions(),
            'friendlists'     : {'none': 'New List', 'active': 'Most Active'}
        });
    }

    function genRequestDom() {
        var $ul = $('#reqslist', Popup.content).empty();
        var $icon = Resources.getPicture('ajax_loader').text('Loading...').css('padding-left',18).attr('id','loading_overlay');
        
        gVar.Request.each(function(id, req) {
            
            var name  = '<a href="'+req.profile+'" target="_black">'+req.name+'</a>';
            var $img  = c$('span', 'class:player_pic,title:'+req.name);
            var $li   = c$('li', 'id:req_'+id+',class:'+req.type).appendTo($ul);
            $li = c$('div', 'class:li_wrapper clearfix').appendTo($li);
            if (req.icon) {
                $img.css('background', "url('"+req.icon+"') 50% 50% no-repeat");
            }
            
            $img.append(c$('span')).appendTo($li);
            
            if (options.sendBack && req.send && req.send.to) {
                c$('div','class:buttons state_accept').appendTo($li).width(150)
                .append(b$('Accept And Thank', 'class:medium white,req:'+id).click(Events.accept_click))
                .append(c$('a', 'href:#,class:acceptonly,req:'+id).text('accept').click(Events.accept_click))
                .append(c$('span').text(' | '))
                .append(c$('a', 'href:#,class:thanksonly,req:'+id).text('thanks').click(Events.ignore_request_click))
                .append(c$('span').text(' | '))
                .append(c$('a', 'href:#,class:ignore,req:'+id).text('ignore').click(Events.ignore_request_click));
            }
            else {
                c$('div','class:buttons state_accept').appendTo($li)
                .append(b$('Accept', 'class:medium white,req:'+id).click(Events.accept_click))
                .append(c$('a', 'href:#,class:ignore,req:'+id).text('ignore').click(Events.ignore_request_click));
            }
            
            c$('div','class:buttons state_retry').appendTo($li).hide()
            .append(b$('Clear', 'class:medium white,req:'+id).click(Events.clear_request_click))
            .append(c$('a', 'href:#,class:ignore,continue:true,req:'+id).text('continue').click(Events.accept_click));
                        
            c$('div','class:buttons state_completed').appendTo($li).hide()
            .append(b$('Clear', 'class:medium white,req:'+id).click(Events.clear_request_click));
                        
            c$('div','class:body').append('<h4>'+name+'</h4><p>'+req.body+'</p>').appendTo($li).append($icon.clone().hide());
        });

        if ($('li', $ul).length < 1) {
            c$('li').appendTo($ul).append(c$('center').css('padding-top',40).text('You don\'t have request.'));
        }
    }
	
	function genMssSelectableDom() {        
		var $ul = $('.mss_selectable', Popup.content).empty();
		Util.each(gVar.Request.toGroup('gift'), function(item_id, reqs) {
			var gift = reqs[0].gift, reqText = reqs[0].body, length = reqs.length;
            length -= gVar.Collector.amountOf(item_id);
            if (length < 1) {
                return;
            }
            c$('li','iid:'+item_id+',title:'+reqText+',count:'+reqs.length).appendTo($ul)
            .append(c$('img').attr('src', gift.img))
            .append(c$('div', 'class:gift_amount').text(reqs.length+'x'))
            .append(c$('div', 'class:gift_name').text(gift.name))
            .click(Events.selectGift_click);
		});
	}
	
	function genMssSelectedDom() {
		var $ul = $('#mass_body .mss_selected', Popup.content).empty();
		gVar.Collector.each(function(index, job) {
			var jobText = $('#mss_action option[value='+job.action+']', '#mass_body').text();
			
			jobText = Util.setColor(jobText,'green') + ' ';
			jobText += Util.setColor(job.amount,'yellow');
			
	        c$('li').appendTo($ul).click(Events.removeFromCollector)
	        .append(c$('img').attr('src', job.icon))
	        .append(c$('div', 'class:gift_name').html(jobText + ' ' + job.name))
            .append(c$('div', 'class:remove,index:'+index).click(Events.removeFromCollector));
		});
		
	}

    function Initialize() {
        var n_today = new Date().getDay();
        if (options.today !== n_today) {
            options.set('sendGiftCount', 0);
            Events.resetDailyLimit_click();
        }
        loadingScreen('Loading requests...');
        getUserRequests(function(success) {
            loadingScreen();
			if (success !== true) {
                Popup.destroy();
                return;
            }
			gVar.Collector = new CSCollector(); 
			
            genMainDom();
            genRequestDom();

            options.toDomElements();

            showDiv('reqs', 'body');
            showDiv('control', 'panel');
            showDiv('setup', 'mass');
    
			getAppFriends(getMWLists);
            Popup.applyCustomClass();
            Popup.show();
        });
    }

    Popup.addBase64Style(
        'I2ZyZWVnaWZ0c2NlbnRlcl9wb3B1cCBzcGFuLnBsYXllcl9waWMgew0KCWZsb2F0OiBsZWZ0Ow0KCWRpc3BsYXk6IGJsb2NrOw0K'+
        'CXdpZHRoOiA1NHB4Ow0KfQ0KI2ZyZWVnaWZ0c2NlbnRlcl9wb3B1cCBzcGFuLnBsYXllcl9waWMgc3BhbiB7DQoJYmFja2dyb3Vu'+
        'ZDogdXJsKCIke2Jhc2VfdXJsfXYzL3VzZXJwaWNfb3V0bGluZV81NHg1NF8wMS5wbmciKSA1MCUgNTAlIG5vLXJlcGVhdDsNCglk'+
        'aXNwbGF5OiBibG9jazsNCgloZWlnaHQ6IDU0cHg7DQp9DQojZnJlZWdpZnRzY2VudGVyX3BvcHVwIC5mcmFtZV9ib3ggew0KCWNs'+
        'ZWFyOiBib3RoOw0KCWJvcmRlcjogMXB4IHNvbGlkICMzMzM7DQoJbWFyZ2luOiA1cHg7DQoJcGFkZGluZzogMTBweCA4cHg7DQoJ'+
        'bWluLWhlaWdodDogMjdweDsNCgl0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KI2ZyZWVnaWZ0c2NlbnRlcl9wb3B1cCB1bCB7DQoJbGlz'+
        'dC1zdHlsZS10eXBlOiBub25lOw0KCW92ZXJmbG93OiBhdXRvOw0KCXRleHQtYWxpZ246IGxlZnQ7DQp9DQojZnJlZWdpZnRzY2Vu'+
        'dGVyX3BvcHVwIHVsLnRhYl9iYXIgew0KCWJhY2tncm91bmQ6IHVybCgiJHtiYXNlX3VybH16bWMvdGFic19iZ18xeDQ1XzAxLmdp'+
        'ZiIpIDAgMTAwJSByZXBlYXQteDsNCgloZWlnaHQ6IDQ4cHg7DQoJb3ZlcmZsb3c6IHZpc2libGU7DQoJcGFkZGluZy1sZWZ0OiA2'+
        'cHg7DQoJdGV4dC10cmFuc2Zvcm06IHVwcGVyY2FzZTsNCgl0ZXh0LWFsaWduOiBjZW50ZXI7DQp9DQojZnJlZWdpZnRzY2VudGVy'+
        'X3BvcHVwIHVsLnRhYl9iYXIgbGkgew0KCWJhY2tncm91bmQ6IHVybCgiJHtiYXNlX3VybH16bWMvdGFiX2RpdmlkZXJfMngyN18w'+
        'MS5naWYiKSAwIDExcHggbm8tcmVwZWF0Ow0KCWZsb2F0OiBsZWZ0Ow0KCW1hcmdpbi1sZWZ0OiA1cHg7DQoJcGFkZGluZy1sZWZ0'+
        'OiA2cHg7DQoJd2lkdGg6IDE1MHB4Ow0KfQ0KI2ZyZWVnaWZ0c2NlbnRlcl9wb3B1cCB1bC50YWJfYmFyIGxpOmZpcnN0LWNoaWxk'+
        'IHsNCgliYWNrZ3JvdW5kOiBub25lOw0KCW1hcmdpbi1sZWZ0OiAwOw0KCXBhZGRpbmctbGVmdDogMDsNCn0NCiNmcmVlZ2lmdHNj'+
        'ZW50ZXJfcG9wdXAgdWwudGFiX2JhciBsaSBhIHsNCgliYWNrZ3JvdW5kOiB1cmwoIiR7YmFzZV91cmx9em1jL3RhYl9pbmFjdGl2'+
        'ZV8xNTB4OTBfMDEucG5nIikgMCAwIG5vLXJlcGVhdDsNCgljb2xvcjogd2hpdGU7DQoJZGlzcGxheTogYmxvY2s7DQoJaGVpZ2h0'+
        'OiA0NXB4Ow0KCXBhZGRpbmctbGVmdDogM3B4Ow0KfQ0KI2ZyZWVnaWZ0c2NlbnRlcl9wb3B1cCB1bC50YWJfYmFyIGxpLmFjdGl2'+
        'ZSBhIHsNCgliYWNrZ3JvdW5kOiB1cmwoIiR7YmFzZV91cmx9em1jL3RhYl9hY3RpdmVfMTUweDEyMF8wMS5wbmciKSAtNHB4IDFw'+
        'eCBuby1yZXBlYXQ7DQp9DQojZnJlZWdpZnRzY2VudGVyX3BvcHVwIHVsLnRhYl9iYXIgbGkgYSBzcGFuIHsNCgliYWNrZ3JvdW5k'+
        'OiB1cmwoIiR7YmFzZV91cmx9em1jL3RhYl9pbmFjdGl2ZV8xNTB4OTBfMDEucG5nIikgMTAwJSAxMDAlIG5vLXJlcGVhdDsNCglk'+
        'aXNwbGF5OiBibG9jazsNCgloZWlnaHQ6IDQ1cHg7DQp9DQojZnJlZWdpZnRzY2VudGVyX3BvcHVwIHVsLnRhYl9iYXIgbGkuYWN0'+
        'aXZlIGEgc3BhbiB7DQoJYmFja2dyb3VuZDogdXJsKCIke2Jhc2VfdXJsfXptYy90YWJfYWN0aXZlXzE1MHgxMjBfMDEucG5nIikg'+
        'MTAwJSAtNzVweCBuby1yZXBlYXQ7DQp9DQojZnJlZWdpZnRzY2VudGVyX3BvcHVwIHVsLnRhYl9iYXIgbGkgZW0gew0KCWRpc3Bs'+
        'YXk6IGJsb2NrOw0KCWZvbnQtc2l6ZTogMTBweDsNCglmb250LXN0eWxlOiBub3JtYWw7DQoJZm9udC13ZWlnaHQ6IGJvbGQ7DQoJ'+
        'bGluZS1oZWlnaHQ6IDEycHg7DQoJcGFkZGluZy10b3A6IDE2cHg7DQoJaGVpZ2h0OiAyMHB4Ow0KCXBhZGRpbmctbGVmdDogMzVw'+
        'eDsNCgl0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KI2ZyZWVnaWZ0c2NlbnRlcl9wb3B1cCB1bC50YWJfYmFyIGxpLmNvbGxlY3RvciBl'+
        'bSB7DQoJYmFja2dyb3VuZDogdXJsKCIke2Jhc2VfdXJsfXYzL2ljb25fc2hlZXRfMjV4MjRfMDEucG5nIikgMnB4IDEwcHggbm8t'+
        'cmVwZWF0Ow0KfQ0KI2ZyZWVnaWZ0c2NlbnRlcl9wb3B1cCB1bC50YWJfYmFyIGxpLnJlcXVlc3QgZW0gew0KYmFja2dyb3VuZDog'+
        'dXJsKCIke2Jhc2VfdXJsfXYzL2ljb25fZ2lmdF8yN3gyOF8wMS5wbmciKSAycHggNXB4IG5vLXJlcGVhdDsNCn0NCiNmcmVlZ2lm'+
        'dHNjZW50ZXJfcG9wdXAgdWwudGFiX2JhciBsaS5zZW5kZXIgZW0gew0KYmFja2dyb3VuZDogdXJsKCIke2Jhc2VfdXJsfXYzL2lj'+
        'b25fc2VuZGdpZnRfMjJ4MTZfMDEuZ2lmIikgNXB4IDEzcHggbm8tcmVwZWF0Ow0KfQ0KI2ZyZWVnaWZ0c2NlbnRlcl9wb3B1cCAj'+
        'bWFzc19ib2R5IHVsLA0KI2ZyZWVnaWZ0c2NlbnRlcl9wb3B1cCAjc2VuZF9ib2R5IHVsIHsNCgloZWlnaHQ6IDQ2MHB4Ow0KCW1h'+
        'cmdpbi10b3A6IDJweDsNCglib3JkZXI6IHNvbGlkIDFweCAjNjY2Ow0KCXRleHQtYWxpZ246IGxlZnQ7DQoJcGFkZGluZzogNXB4'+
        'Ow0KfQ0KI2ZyZWVnaWZ0c2NlbnRlcl9wb3B1cCAjbWFzc19ib2R5IHVsIGxpIGltZywNCiNmcmVlZ2lmdHNjZW50ZXJfcG9wdXAg'+
        'I3NlbmRfYm9keSB1bCBsaSBpbWcgew0KCWZsb2F0OiBsZWZ0Ow0KICAgIHdpZHRoOiAxOHB4Ow0KICAgIGhlaWdodDogMThweDsN'+
        'Cn0NCiNmcmVlZ2lmdHNjZW50ZXJfcG9wdXAgI21hc3NfYm9keSB1bCBsaSwNCiNmcmVlZ2lmdHNjZW50ZXJfcG9wdXAgI3NlbmRf'+
        'Ym9keSB1bCBsaSB7DQoJcGFkZGluZzogM3B4Ow0KCXRleHQtZGVjb3JhdGlvbjogbm9uZTsNCglib3JkZXI6IDFweCBzb2xpZCAj'+
        'MjIyOw0KCW1hcmdpbjogMXB4Ow0KICAgIHdpZHRoOiBhdXRvICFpbXBvcnRhbnQ7DQogICAgYmFja2dyb3VuZC1zaXplOiAxOHB4'+
        'ICFpbXBvcnRhbnQ7DQogICAgLW1vei1iYWNrZ3JvdW5kLXNpemU6IDE4cHggIWltcG9ydGFudDsNCiAgICBtaW4taGVpZ2h0OiAx'+
        'OHB4ICFpbXBvcnRhbnQ7DQogICAgYmFja2dyb3VuZC1wb3NpdGlvbjogNXB4IDUwJTsNCn0NCiNmcmVlZ2lmdHNjZW50ZXJfcG9w'+
        'dXAgdWwjbXNzX3N1Y2Nlc3Nsb2cgbGkgaDQgew0KCWZvbnQtc2l6ZTogMTRweDsNCglsaW5lLWhlaWdodDogMThweDsNCgltYXJn'+
        'aW4tYm90dG9tOiA0cHg7DQoJcGFkZGluZy10b3A6IDJweDsNCgl0ZXh0LWFsaWduOiBsZWZ0Ow0KCWNvbG9yOiAjRkZEOTI3Ow0K'+
        'fQ0KI2ZyZWVnaWZ0c2NlbnRlcl9wb3B1cCB1bCNtc3Nfc3VjY2Vzc2xvZyBsaSBwIHsNCglmb250LXNpemU6IDEycHg7DQoJbWlu'+
        'LWhlaWdodDogMzVweDsNCglwYWRkaW5nLWxlZnQ6IDQwcHg7DQp9DQojZnJlZWdpZnRzY2VudGVyX3BvcHVwIHVsI21zc19tZXNz'+
        'YWdlbG9nIGxpIHAgew0KCWZvbnQtc2l6ZTogMTJweDsNCgloZWlnaHQ6IDIwcHg7DQoJcGFkZGluZy1sZWZ0OiAzMHB4Ow0KCW1h'+
        'cmdpbi1ib3R0b206IDFweDsNCn0NCiNmcmVlZ2lmdHNjZW50ZXJfcG9wdXAgI21hc3NfYm9keSAjbXNzX21lc3NhZ2VzIHsNCiAg'+
        'ICBwYWRkaW5nLWxlZnQ6IDIwcHg7DQogICAgZGlzcGxheTogbm9uZTsNCiAgICB3aWR0aDogYXV0byAhaW1wb3J0YW50Ow0KICAg'+
        'IGxpbmUtaGVpZ2h0OiAzMHB4Ow0KfQ0KI2ZyZWVnaWZ0c2NlbnRlcl9wb3B1cCAjc2VuZF9ib2R5IHVsIGxpIGltZyB7DQoJbWFy'+
        'Z2luLXJpZ2h0OiA1cHg7DQp9DQojZnJlZWdpZnRzY2VudGVyX3BvcHVwICNzZW5kX2JvZHkgdWwuc25kX3NlbGVjdGFibGUgbGkg'+
        'ZGl2IHsNCgl3aWR0aDogOTAlOw0KfQ0KI2ZyZWVnaWZ0c2NlbnRlcl9wb3B1cCB1bCNtc3Nfc3VjY2Vzc2xvZyBsaSBpbWcgew0K'+
        'CW1hcmdpbi1yaWdodDogNXB4Ow0KCWhlaWdodDogMzBweDsNCgl3aWR0aDogMzBweDsNCn0NCiNmcmVlZ2lmdHNjZW50ZXJfcG9w'+
        'dXAgI3NlbmRfYm9keSAjc25kX21lc3NhZ2VzIHsNCiAgICBtaW4taGVpZ2h0OiA4MHB4Ow0KICAgIG92ZXJmbG93LXk6IGF1dG87'+
        'DQogICAgbWF4LWhlaWdodDogODBweDsNCn0NCiNmcmVlZ2lmdHNjZW50ZXJfcG9wdXAgI3NlbmRfYm9keSAjc25kX21lc3NhZ2Vz'+
        'IGRpdiB7DQogICAgd2lkdGg6IGF1dG8gIWltcG9ydGFudDsNCiAgICBiYWNrZ3JvdW5kLXNpemU6IDE4cHggIWltcG9ydGFudDsN'+
        'CiAgICAtbW96LWJhY2tncm91bmQtc2l6ZTogMThweCAhaW1wb3J0YW50Ow0KICAgIG1pbi1oZWlnaHQ6IDE4cHggIWltcG9ydGFu'+
        'dDsNCiAgICBiYWNrZ3JvdW5kLXBvc2l0aW9uOiAwcHggNTAlOw0KICAgIHBhZGRpbmc6IDJweCA1cHggMnB4IDIwcHg7DQp9DQoj'+
        'ZnJlZWdpZnRzY2VudGVyX3BvcHVwICNtYXNzX2JvZHkgZGl2LmxpX3dyYXBwZXIsDQojZnJlZWdpZnRzY2VudGVyX3BvcHVwICNz'+
        'ZW5kX2JvZHkgZGl2LmxpX3dyYXBwZXIgew0KICAgIHdpZHRoOiAzNDRweDsNCiAgICBmbG9hdDogbGVmdDsNCiAgICBtYXJnaW46'+
        'IDJweDsNCn0NCiNmcmVlZ2lmdHNjZW50ZXJfcG9wdXAgI21hc3NfYm9keSB1bCNtc3Nfc3VjY2Vzc2xvZyBsaSBpbWcgew0KICAg'+
        'IHdpZHRoOiAzMnB4Ow0KICAgIGhlaWdodDogMzJweDsNCn0NCiNmcmVlZ2lmdHNjZW50ZXJfcG9wdXAgI21hc3NfYm9keSB1bC5t'+
        'c3Nfc2VsZWN0YWJsZSBsaSB7DQoJY3Vyc29yOiBwb2ludGVyOw0KfQ0KI2ZyZWVnaWZ0c2NlbnRlcl9wb3B1cCAjbWFzc19ib2R5'+
        'IHVsLm1zc19zZWxlY3RhYmxlIGxpLnNlbGVjdGVkLA0KI2ZyZWVnaWZ0c2NlbnRlcl9wb3B1cCAjc2VuZF9ib2R5IHVsLnNuZF9z'+
        'ZWxlY3RhYmxlIGxpLnNlbGVjdGVkIHsNCglib3JkZXI6IDFweCBzb2xpZCB5ZWxsb3c7DQp9DQojZnJlZWdpZnRzY2VudGVyX3Bv'+
        'cHVwICNtYXNzX2JvZHkgdWwubXNzX3NlbGVjdGFibGUgbGk6aG92ZXIsDQojZnJlZWdpZnRzY2VudGVyX3BvcHVwICNzZW5kX2Jv'+
        'ZHkgdWwuc25kX3NlbGVjdGFibGUgbGk6aG92ZXIgew0KICAgIGJhY2tncm91bmQtY29sb3I6ICMzMzM7DQp9DQojZnJlZWdpZnRz'+
        'Y2VudGVyX3BvcHVwIHVsIGxpIGRpdi5naWZ0X25hbWUgew0KICAgIG1hcmdpbjogMHB4IDBweCAwcHggNXB4Ow0KICAgIGZsb2F0'+
        'OiBsZWZ0Ow0KICAgIG92ZXJmbG93OiBoaWRkZW47DQogICAgbWF4LWhlaWdodDogMjBweDsNCiAgICBtYXgtd2lkdGg6IDI2MnB4'+
        'Ow0KICAgIGhlaWdodDogMjBweDsNCn0NCiNmcmVlZ2lmdHNjZW50ZXJfcG9wdXAgdWwgbGkgZGl2LmdpZnRfYW1vdW50IHsNCglt'+
        'YXJnaW46IDBweCAzcHggMHB4IDhweDsNCglmbG9hdDogbGVmdDsNCglvdmVyZmxvdzogaGlkZGVuOw0KCW1heC1oZWlnaHQ6IDIw'+
        'cHg7DQoJbWF4LXdpZHRoOiAzMHB4Ow0KCW1pbi13aWR0aDogMjBweDsNCgljb2xvcjogeWVsbG93Ow0KfQ0KI2ZyZWVnaWZ0c2Nl'+
        'bnRlcl9wb3B1cCAjbWFzc19ib2R5IHVsLm1zc19zZWxlY3RlZCBsaSBkaXYucXVhbnRpdHkgew0KCWZsb2F0OiByaWdodDsNCiAg'+
        'ICBtYXJnaW4tcmlnaHQ6IDEwcHg7DQp9DQojZnJlZWdpZnRzY2VudGVyX3BvcHVwICNtYXNzX2JvZHkgdWwubXNzX3NlbGVjdGVk'+
        'IGxpIGRpdi5xdWFudGl0eSBpbnB1dCB7DQoJZm9udC13ZWlnaHQ6IGJvbGQ7IA0KCWNvbG9yOiByZ2IoMjA4LCAyMDgsIDIwOCk7'+
        'IA0KCWJvcmRlcjogMXB4IHNvbGlkIHJnYigxNTMsIDE1MywgMTUzKTsgDQoJYmFja2dyb3VuZC1jb2xvcjogYmxhY2s7IA0KCWZv'+
        'bnQtc2l6ZTogMTRweDsNCgl3aWR0aDogMzBweDsNCgloZWlnaHQ6IDE1cHg7DQp9DQojZnJlZWdpZnRzY2VudGVyX3BvcHVwICNt'+
        'YXNzX2JvZHkgdWwubXNzX3NlbGVjdGVkIGxpIGEgew0KCW1hcmdpbi1sZWZ0OiAycHg7DQoJZmxvYXQ6IHJpZ2h0Ow0KfQ0KI2Zy'+
        'ZWVnaWZ0c2NlbnRlcl9wb3B1cCAjbWFzc19ib2R5IHVsLm1zc19zZWxlY3RlZCBsaSAucmVtb3ZlIHsNCiAgICBiYWNrZ3JvdW5k'+
        'LWltYWdlOiB1cmwoaHR0cDovL213ZmIuc3RhdGljLnp5bmdhLmNvbS9td2ZiL2dyYXBoaWNzL3JlcXVlc3RzL2Nyb3NzLnBuZyk7'+
        'DQogICAgZmxvYXQ6IHJpZ2h0Ow0KICAgIHdpZHRoOiAxNnB4Ow0KICAgIGhlaWdodDogMTZweDsNCiAgICBjdXJzb3I6IHBvaW50'+
        'ZXI7DQp9DQojZnJlZWdpZnRzY2VudGVyX3BvcHVwIC50b3BfY3RybCB7DQoJYm9yZGVyOiBzb2xpZCAxcHggIzY2NjsNCgloZWln'+
        'aHQ6IDIwcHg7DQogICAgcGFkZGluZzogNXB4Ow0KICAgIG1hcmdpbi10b3A6IDJweDsNCn0NCiNmcmVlZ2lmdHNjZW50ZXJfcG9w'+
        'dXAgLnRvcF9jdHJsIGEgew0KICAgIGZsb2F0OiByaWdodDsNCiAgICBtYXJnaW4tbGVmdDogNXB4Ow0KfQ0KI2ZyZWVnaWZ0c2Nl'+
        'bnRlcl9wb3B1cCAuc3RhcnRfY3RybCB7DQoJaGVpZ2h0OiAzN3B4Ow0KCWJvcmRlcjogc29saWQgMXB4ICM2NjY7DQoJbWFyZ2lu'+
        'LXRvcDogMnB4Ow0KCXBhZGRpbmctdG9wOiAzcHg7DQp9DQojZnJlZWdpZnRzY2VudGVyX3BvcHVwIC5zdGFydF9jdHJsIGEgew0K'+
        'CW1hcmdpbi1yaWdodDogMTBweDsNCn0NCiNmcmVlZ2lmdHNjZW50ZXJfcG9wdXAgdWwucmVxX2JveCBkaXYubGlfd3JhcHBlciB7'+
        'DQoJYm9yZGVyLXRvcDogMXB4IHNvbGlkICMyMjI7DQoJcGFkZGluZzogNnB4IDE0cHggMCA1MnB4Ow0KfQ0KI2ZyZWVnaWZ0c2Nl'+
        'bnRlcl9wb3B1cCB1bC5yZXFfYm94IGxpOmZpcnN0LWNoaWxkIHsNCglib3JkZXItd2lkdGg6IDBweDsNCn0NCiNmcmVlZ2lmdHNj'+
        'ZW50ZXJfcG9wdXAgdWwucmVxX2JveCBsaSB7DQoJbWluLWhlaWdodDogNzZweDsNCgltYXgtaGVpZ2h0OiAxNjBweDsNCn0NCiNm'+
        'cmVlZ2lmdHNjZW50ZXJfcG9wdXAgdWwucmVxX2JveCBsaS5naWZ0IHsNCgliYWNrZ3JvdW5kOiB1cmwoIiR7YmFzZV91cmx9djMv'+
        'aWNvbl9naWZ0XzI3eDI4XzAxLnBuZyIpIDE3cHggMjBweCBuby1yZXBlYXQ7DQp9DQojZnJlZWdpZnRzY2VudGVyX3BvcHVwIHVs'+
        'LnJlcV9ib3ggbGkubWFmaWFfaW52aXRlIHsNCiAgICBiYWNrZ3JvdW5kOiB1cmwoIiR7YmFzZV91cmx9djMvaWNvbl9tYWZpYV9o'+
        'YXRfMzJ4MjVfMDEucG5nIikgMTRweCAyMHB4IG5vLXJlcGVhdDsNCn0NCiNmcmVlZ2lmdHNjZW50ZXJfcG9wdXAgdWwucmVxX2Jv'+
        'eCBsaS5lbmVyZ3lfcGFjayB7DQogICAgYmFja2dyb3VuZDogdXJsKCIke2Jhc2VfdXJsfXYzL2ljb25fZW5lcmd5X3BhY2tfMjl4'+
        'MjdfMDEucG5nIikgMTZweCAyMHB4IG5vLXJlcGVhdDsNCn0NCiNmcmVlZ2lmdHNjZW50ZXJfcG9wdXAgdWwucmVxX2JveCBsaS5o'+
        'ZWxwIHsNCiAgICBiYWNrZ3JvdW5kOiB1cmwoIiR7YmFzZV91cmx9bWFwX2Jhc2VkX2pvYnMvZXhwZXJ0X3ZpZXcvaWNvbl9tZWdh'+
        'cGhvbmUucG5nIikgMTdweCAyMHB4IG5vLXJlcGVhdDsNCn0NCiNmcmVlZ2lmdHNjZW50ZXJfcG9wdXAgdWwucmVxX2JveCBsaS5m'+
        'YW1pbHkgew0KICAgIGJhY2tncm91bmQ6IHVybCgiJHtiYXNlX3VybH12My9GYW1pbHlfWk1DX0ljb24ucG5nIikgMTVweCAyMHB4'+
        'IG5vLXJlcGVhdCAhaW1wb3J0YW50Ow0KfQ0KI2ZyZWVnaWZ0c2NlbnRlcl9wb3B1cCB1bC5tYWZpYV9pbnZpdGUgbGkgew0KICAg'+
        'IGRpc3BsYXk6IG5vbmU7DQp9DQojZnJlZWdpZnRzY2VudGVyX3BvcHVwIHVsLm1hZmlhX2ludml0ZSBsaS5tYWZpYV9pbnZpdGUg'+
        'ew0KICAgIGRpc3BsYXk6IGJsb2NrOw0KfQ0KI2ZyZWVnaWZ0c2NlbnRlcl9wb3B1cCB1bC5naWZ0IGxpIHsNCiAgICBkaXNwbGF5'+
        'OiBub25lOw0KfQ0KI2ZyZWVnaWZ0c2NlbnRlcl9wb3B1cCB1bC5naWZ0IGxpLmdpZnQgew0KICAgIGRpc3BsYXk6IGJsb2NrOw0K'+
        'fQ0KI2ZyZWVnaWZ0c2NlbnRlcl9wb3B1cCB1bC5lbmVyZ3lfcGFjayBsaSB7DQogICAgZGlzcGxheTogbm9uZTsNCn0NCiNmcmVl'+
        'Z2lmdHNjZW50ZXJfcG9wdXAgdWwuZW5lcmd5X3BhY2sgbGkuZW5lcmd5X3BhY2sgew0KICAgIGRpc3BsYXk6IGJsb2NrOw0KfQ0K'+
        'I2ZyZWVnaWZ0c2NlbnRlcl9wb3B1cCB1bC5zYWZlaG91c2UgbGkgew0KICAgIGRpc3BsYXk6IG5vbmU7DQp9DQojZnJlZWdpZnRz'+
        'Y2VudGVyX3BvcHVwIHVsLnNhZmVob3VzZSBsaS5zYWZlaG91c2Ugew0KICAgIGRpc3BsYXk6IGJsb2NrOw0KfQ0KI2ZyZWVnaWZ0'+
        'c2NlbnRlcl9wb3B1cCB1bC5oZWxwIGxpIHsNCiAgICBkaXNwbGF5OiBub25lOw0KfQ0KI2ZyZWVnaWZ0c2NlbnRlcl9wb3B1cCB1'+
        'bC5oZWxwIGxpLmhlbHAgew0KICAgIGRpc3BsYXk6IGJsb2NrOw0KfQ0KI2ZyZWVnaWZ0c2NlbnRlcl9wb3B1cCB1bC5mYW1pbHkg'+
        'ew0KICAgIGJhY2tncm91bmQ6IHRyYW5zcGFyZW50ICFpbXBvcnRhbnQ7DQp9DQojZnJlZWdpZnRzY2VudGVyX3BvcHVwIHVsLmZh'+
        'bWlseSBsaSB7DQogICAgZGlzcGxheTogbm9uZTsNCn0NCiNmcmVlZ2lmdHNjZW50ZXJfcG9wdXAgdWwuZmFtaWx5IGxpLmZhbWls'+
        'eSB7DQogICAgZGlzcGxheTogYmxvY2s7DQp9DQojZnJlZWdpZnRzY2VudGVyX3BvcHVwIHVsLnNlYXJjaCBsaSB7DQogICAgZGlz'+
        'cGxheTogbm9uZTsNCn0NCiNmcmVlZ2lmdHNjZW50ZXJfcG9wdXAgdWwuc2VhcmNoIGxpLm1hdGNoZWQgew0KICAgIGRpc3BsYXk6'+
        'IGJsb2NrOw0KfQ0KI2ZyZWVnaWZ0c2NlbnRlcl9wb3B1cCB1bC5yZXFfYm94IGxpIGRpdi5idXR0b25zIHsNCglmbG9hdDogcmln'+
        'aHQ7DQoJZm9udC1zaXplOiAxMHB4Ow0KCWZvbnQtd2VpZ2h0OiBib2xkOw0KCW1hcmdpbjogM3B4IDBweCAwcHggMTRweDsNCgl0'+
        'ZXh0LWFsaWduOiByaWdodDsNCgl3aWR0aDogNzBweDsNCn0NCiNmcmVlZ2lmdHNjZW50ZXJfcG9wdXAgdWwucmVxX2JveCBsaSBk'+
        'aXYuYm9keSB7DQoJbWFyZ2luLXJpZ2h0OiAxMDBweDsNCgltYXJnaW4tbGVmdDogNjVweDsNCgltaW4taGVpZ2h0OiA2MHB4Ow0K'+
        'ICAgIGZvbnQtc2l6ZTogMTJweDsNCn0NCiNmcmVlZ2lmdHNjZW50ZXJfcG9wdXAgdWwucmVxX2JveCBsaSBkaXYuYm9keSAjbG9h'+
        'ZGluZ19vdmVybGF5IGltZyB7DQogICAgcG9zaXRpb246IHJlbGF0aXZlOw0KICAgIGJvdHRvbTogLTNweDsNCiAgICBtYXJnaW4t'+
        'cmlnaHQ6IDVweDsNCn0NCiNmcmVlZ2lmdHNjZW50ZXJfcG9wdXAgdWwucmVxX2JveCBsaSBoNCB7DQoJZm9udC1zaXplOiAxNHB4'+
        'Ow0KCWxpbmUtaGVpZ2h0OiAxOHB4Ow0KCW1hcmdpbi1ib3R0b206IDRweDsNCglwYWRkaW5nLXRvcDogMnB4Ow0KfQ0KI2ZyZWVn'+
        'aWZ0c2NlbnRlcl9wb3B1cCB1bC5yZXFfYm94IGxpIHAgew0KCWxpbmUtaGVpZ2h0OiAxNXB4Ow0KCW1hcmdpbi1ib3R0b206IDVw'+
        'eDsNCn0NCiNmcmVlZ2lmdHNjZW50ZXJfcG9wdXAgdWwucmVxX2JveCBsaS5mYWlsZWQgcCB7DQoJY29sb3I6IHJlZDsNCn0NCiNm'+
        'cmVlZ2lmdHNjZW50ZXJfcG9wdXAgdWwucmVxX2JveCBsaS5hc2tpbmcgcCB7DQoJY29sb3I6IHllbGxvdzsNCn0NCiNmcmVlZ2lm'+
        'dHNjZW50ZXJfcG9wdXAgdWwucmVxX2JveCBsaSBpbWcgew0KICAgIGZsb2F0OiBsZWZ0Ow0KICAgIG1heC13aWR0aDogNTRweDsN'+
        'CgltYXgtaGVpZ2h0OiA1NHB4Ow0KICAgIG1hcmdpbi1yaWdodDogNXB4Ow0KfQ0KI2ZyZWVnaWZ0c2NlbnRlcl9wb3B1cCB1bC5y'+
        'ZXFfYm94IGxpIGRpdi5idXR0b25zIGEuc2V4eV9idXR0b25fbmV3IHsNCgltYXJnaW4tYm90dG9tOiA2cHg7DQp9DQojZnJlZWdp'+
        'ZnRzY2VudGVyX3BvcHVwIHVsLnJlcV9ib3ggbGkgZGl2LmJ1dHRvbnMgYS5pZ25vcmUsDQojZnJlZWdpZnRzY2VudGVyX3BvcHVw'+
        'IHVsLnJlcV9ib3ggbGkgZGl2LmJ1dHRvbnMgYS5hY2NlcHRvbmx5LA0KI2ZyZWVnaWZ0c2NlbnRlcl9wb3B1cCB1bC5yZXFfYm94'+
        'IGxpIGRpdi5idXR0b25zIGEudGhhbmtzb25seSB7DQoJdGV4dC10cmFuc2Zvcm06IHVwcGVyY2FzZTsNCn0NCiNmcmVlZ2lmdHNj'+
        'ZW50ZXJfcG9wdXAgdWwsICNmcmVlZ2lmdHNjZW50ZXJfcG9wdXAgbGksICNmcmVlZ2lmdHNjZW50ZXJfcG9wdXAgZGwsICNmcmVl'+
        'Z2lmdHNjZW50ZXJfcG9wdXAgZHQsICNmcmVlZ2lmdHNjZW50ZXJfcG9wdXAgZGQsICNmcmVlZ2lmdHNjZW50ZXJfcG9wdXAgaDIs'+
        'ICNmcmVlZ2lmdHNjZW50ZXJfcG9wdXAgaDMsICNmcmVlZ2lmdHNjZW50ZXJfcG9wdXAgaDQsICNmcmVlZ2lmdHNjZW50ZXJfcG9w'+
        'dXAgcCB7DQoJbWFyZ2luOiAwcHg7DQoJcGFkZGluZzogMHB4Ow0KfQ==', 
        {base_url: global.zGraphicsURL}
    );

    // Initialize
    options.load(Initialize);
}
// ==Script==
// @id        HomeFeedCenter.js
// @author    Dakam
// @requires  MWAddon.js
// ==Script==
/**
 * Set configuration.
 */
UserConfig.create('hfopt', {
    // exclude some settings when exporting.
    _excludedToExport: ['collectedStreams', 'lastPostTime'],
    
    actions                    : {'skipif':new Object(),'fail':new Object()},
    maxLogLength               : 50,
    lastPostTime               : 0,
    collectedStreams           : new Array(),
    feedsLimit                 : 100,
    fbListFilterOn             : false,
    fbListFilter               : new Object(),
    userFilterOn               : false,
    userFilter                 : new Object(),
    doGotoWar                  : false,
    doGiveSocialHelp           : true,
    GiveSocialHelp             : new Object(),
    doSocialMissions           : false,
    doClaimBonusAndReward      : true,
    ClaimBonusAndReward        : new Object(),
    doPropertyHelp             : true,
    PropertyHelp               : new Object(),
    doSendEnergyAndPhones      : true,
    doAcceptGiftEvent          : true,
    doCityCrew                 : true,
    doDailyTake                : true,
    helpDelay                  : 3,
    refreshDelay               : 60,
    joinMission                : 0,
    joinMissionAfter           : 0,
    diff_easy                  : false,
    diff_medium                : false,
    diff_hard                  : true,
    diff_event                 : false,
    maxFreeSlots               : 100,
    warlootLimited             : {'id':'limited', 'enabled':true, 'name':'*', 'att':0, 'def':0},
    warLimitCount              : 0,
    warloot                    : new Object(),
    warlootSecundaryLimit      : false,
    opUseNameFilter            : false, 
    opNameFilter               : '',
    dtCheckMinAtkDef           : false, 
    dtMinAttack                : 30,
    dtMinDefense               : 30,
    dtLootPriority             : 'Attack Point\nDefense Point'
});

var UserStreams = {
    content: new Array(),
    /**
     * Add a new user defined stream.
     * @param {String} id
     * @param {Object} action
     */
    add: function(id, action) {
        UserStreams.content.push({
            'id': id,
            'action': action
        });
    }
};
/**
 * Get feed stories and autohelp all
 */
function HomeFeedCenter() {
    if (facebook.initialized !== true) {
        showHelpPopup({
            icon: 'error',
            title: 'You cant use Home Feed Center',
            message: 'Home Feed Center requires Facebook Api and it\'s not loaded.'
        });
        return;
    }
    const ERROR_BAD_RESPONSE = 'Unexpected server response, check this stream manually.';
    var messageTimer = new TimerMessage('#auto_feed_helper_messages');
    var cancel_process = false;
    /** @type {CSStreamCollection} */ var feedStream;
    /** @type {CSActionCollection} */ var Actions;
    var options = UserConfig.hfopt;
    
    // popup
    var popupElt = new PopupObject('homefeedcenter_popup', {
        type: 'main',
        title: 'HOME FEED CENTER',
        onclose: function() {
            cancel_process = true;
            httpAjaxStopRequests();
            messageTimer.clear();
            options.fromDomElements();
            options.save();
        }
    });

    var groupNames = {
        'none'                    : 'General',
        'doGotoWar'               : 'Go to War',
        'doGiveSocialHelp'        : 'Give Social Help',
        'doDailyTake'             : 'Daily Take',
        'doSocialMissions'        : 'Operations',
        'doClaimBonusAndReward'   : 'Claim Bonus And Reward',
        'doPropertyHelp'          : 'Property Help',
        'doSendEnergyAndPhones'   : 'Send Energy And Phones',
        'doCityCrew'              : 'City Crew'
    };

    /**
     * @constructor
     * @param {Object} feed
     * @return {CSStream}
     */
    var CSStream = function(feed) {
        var me = this;
        var action;
        try {
	        if (!feed.attachment.href) {
	            feed.attachment.href = feed.attachment.media[0].href;
	        }
	        if (!feed.attachment.href) {
	            throw Error('Stream: Invalid href');
	        }
            me.url      = Util.uSplit(feed.attachment.href);
            me.id       = me.url.next_controller + '_' + me.url.next_action;
            me.url      = toInternalUrl(me.url);
            me.action   = Actions.get(me.id, me.url);
            me.isValid  = true;
            me.feed     = feed;
            me.user_id  = feed.source_id;
            me.isNew    = (feed.created_time > options.lastPostTime);
            me.gid      = (me.action ? me.action.group : '')
        }
        catch(err) {
            Logger.error(err);
            me.id = 'Unknow';
            me.isValid = false;
        }
        /**
         * Return true if this was collected.
         */
        me.isCollected = function() {
            var arr = options.collectedStreams;
            if (Util.isArray(arr)) {
                return arr.indexOf(feed.post_id) !== -1;
            } else {
                options.set('collectedStreams', new Array());
                options.save();
            }
            return false;
        };
        /**
         * Set this feed as collected.
         */
        me.collected = function() {
            var arr = options.collectedStreams;
            if (!Util.isArray(arr)) {
                options.set('collectedStreams', (arr = new Array()));
            }
            else if (arr.length > 199) {
                arr = arr.splice(0, 1);
            }
            arr.push(feed.post_id);
            options.save();
        };

        return this;
    };
    /**
     * Create a new Stream collection.
     * @param {Object} feeds
     * @param {Boolean} bOnlyNewFeeds
     * @return {CSStreamCollection}
     */
    var CSStreamCollection = function(feeds, bOnlyNewFeeds) {
        var nLastTime = options.get('lastPostTime');
        var nNewLastTime = nLastTime;
        var streams = new Array();

        /**
         * @param {Number} index
         * @return {CSStream}
         */
        this.get = function(index) {
            return streams[index];
        };
        /** @return {CSStream} */
        this.shift = function() {
            return streams.shift();
        };
        /** @param {Number} index */
        this.remove = function(index) {
            streams[index] = null;
        };
        /** @return {Number} */
        this.length = function() {
            var len = 0;
            for (var i = 0; i < streams.length; i++) {
                if (Util.isSet(streams[i])) { len++; }
            }
            return len;
        };
        /**
         * Loops through all streams. 
         * @param {Function} callback
         */
        this.each = function(callback) {
            Util.each(streams, callback);
        };
        /**
         * Split stream array to the new quantity
         */
        this.slice = function(amount) {
            streams = streams.slice(0, amount);
        }

        Util.each(feeds, function(n, feed) {
            if (feed && feed.created_time) {
                var me  = parseFloat(facebook.session.uid);
                var sid = parseFloat(feed.source_id);
                var tid = feed.target_id ? parseFloat(feed.target_id) : null;

                if ( feed.created_time > nNewLastTime ) {
                    nNewLastTime = feed.created_time;
                }

                if ( sid !== me && (tid == null || tid == me) ) {
                    var strm = new CSStream(feed);
                    if ( strm.isValid && (bOnlyNewFeeds !== true || strm.isNew) ) {
                        streams.push(strm);
                    }
                }
            }
        });

        options.set('lastPostTime',nNewLastTime);
        options.save();

        /** @type Number   */
        this.lastTime = nNewLastTime;

        return this;
    };
    /**
     * @constructor
     * @param {Object} params
     * @return {CSAction}
     */
    var CSAction = function(params) {
        /** @type String   */ this.name      = params.name;
        /** @type Number   */ this.group     = params.group;
        /** @type Object   */ this.next      = params.next;
        /** @type String   */ this.skipif    = params.skipif;
        /** @type String   */ this.fail      = params.fail;
        return this;
    };
    /**
     * @constructor
     * @return {CSActionCollection}
     */
    var CSActionCollection = function() {
        var actions = new Object();
        /**
         * @param {String} id
         * @param {Object} params
         */
        this.add = function(id, params) {
            actions[id] = params;
        };
        /** @return {CSAction, Array} */
        this.get = function(id, url) {
            var action = actions[id];
            if (action && action.match && Util.isString(url)) {
                Util.each(action.match, function(test, a) {
                    if (url.indexOf(test) > -1) {
                        a.skipif = action.skipif;
                        a.group = action.group;
                        a.fail = action.fail;
                        a.next = action.next;
                        action = a;
                        return false;
                    }
                });
            }
            return action;
        };
        /** @param {Function} callback */
        this.each = function(callback) {
            Util.each(actions, callback);
        };
        return this;
    };
    /**
     * @constructor
     * @param {Object} element
     * @return {CSDailyTakeLoot}
     */
    var CSDailyTakeLoot = function(element) {
        var expr = /do_ajax\('popup_fodder','(remote\/html_server.php[^']+)/i;
        var $pic = $('div:first img', element);
        this.pic      = $pic.attr('src');
        this.url      = Util.doRgx(expr, $('.collectbutton a', element).attr('onclick')).$1;
        this.attack   = parseInt($('.attack', element).text()||0);
        this.defense  = parseInt($('.defense', element).text()||0);
        this.name     = $pic.attr('title');
        return this;
    };
    var CSDailyTakeLootCollection = function() {
        var Items = new Array();
        /**
         * @param {CSDailyTakeLoot} loot
         */
        this.add = function(loot) {
            if (loot && loot.url) {
                Items.push(loot);
            }
        };
        this.length = function() {
            return Items.length;
        };
        /**
         * @param {Number} index
         */
        this.get = function(index) {
            return Items[index];
        };
        /**
         * Loops through all loot items and return the matched item.
         * @param {String} name
         * @return {CSDailyTakeLoot}
         */
        this.getByName = function(name) {
            var matched;
            if (Util.isString(name) && name.length > 0) {
                var expr = new RegExp(name, 'i');
                Util.each(Items, function(index,loot) {
                    if (expr.test(loot.name)) {
                        matched = loot;
                        return false;
                    }
                });
            }
            return matched;
        };
        /**
         * Loops through all loot items and return the matched item.
         * @param {Number} attack
         * @param {Number} defense
         * @return {CSDailyTakeLoot}
         */
        this.getByStats = function(attack, defense) {
            var matched;
            if (Util.isString(name) && name.length > 0) {
                Util.each(Items, function(index,loot) {
                    if (loot.attack >= attack || loot.defense >= defense) {
                        matched = loot;
                        return false;
                    }
                });
            }
            return matched;
        };        
        /**
         * Get all items names.
         */
        this.names = function() {
            var names = new Array();
            Util.each(Items, function(index,loot) {
                names.push(loot.name);
            });
            return names.join(', ');
        };
        /**
         * Loops through all loot items.
         * @param {Object} callback
         */
        this.each = function(callback) {
            Util.each(Items, callback);
        };
        return this;
    };

    // EVENTS
    var Events = {
        toggleWarloot: function() {
            Util.each(options.warloot, function(id, data) {
                data.enabled = (data.enabled !== true);
            });
            updateWarLoot();
            return false;
        },
        selectHelp: function() {
            var me = $(this), $ul = $('#logList');
            var p = me.position(), w = me.width();
            var t = $ul.position().top;
            var h = t + $ul.height() - me.outerHeight();
            me.addClass('selected').css({'top':Math.max(t, Math.min(h, p.top)), 'width':w});
        },
        unselectHelp: function() {
            $(this).removeAttr('style').removeClass('selected');
        },
        likeFeed_click: function() {
            var me = $(this);
            me.unbind().css('opacity',0.5);
            facebook.likeAdd(me.attr('postid'),function(success) {
                if (success === true) {
                    me.replaceWith(Util.setColor('Liked!','yellow')).css('opacity',1);
                } else {
                    me.replaceWith(Util.setColor('Fail','red')).css('opacity',1);
                }
            });
            return false;
        },
        removeWarloot: function() {
            var $selection = $('#warloot_data option:selected');
            if ($selection.length > 0) {
                $selection.each(function(index,op) {
                    delete options.warloot[op.value];
                });
                options.save(updateWarLoot);
            }
            return false;
        },
        editWarloot: function() {
            var $selected = $('#warloot_data option:selected:first');
            if ($selected.length > 0) {
                showWarLootEditor();
                warLootData(options.warloot[$selected.val()]);
            }
            return false;
        },
        addWarloot: function() {
            showWarLootEditor();
            warLootData($(this).attr('limited') ? options.warlootLimited : {});
            return false;
        },
        resetLimitedWarloot: function() {
            options.warLimitCount = 0;
            options.save(updateWarLoot);
            return false;
        },
        addFblistToFilter_click: function() {
            showFBListEditor();
            return false;
        },
        addUserToFilter_click: function() {
            var added_users = new Array();
            var listElt = $('#hfopt_userfilter');
            $('option',listElt).each(function(i, e) {
                added_users.push(e.value);
            });
            showFriendsSelector(function(users) {
                if (users && users.length > 0) {
                    listElt.empty();
                    Util.each(users, function(i, user) {
                        c$('option','value:'+user.uid).text(user.name).appendTo(listElt);
                    });
                }
            }, added_users);
            return false;
        },
        clearUserFilter_click: function() {
            $('#hfopt_userfilter', popupElt.content).empty();
            return false;
        },
        clearFblistFilter_click: function() {
            $('#hfopt_fblistfilter', popupElt.content).empty();
            options.set('fbListFilter', {});
            return false;
        },
        helpFeed_click: function() {
            if ($(this).hasClass('disabled')) {
                return false;
            }
            $(this).addClass('disabled');
            var unkActionMsg = 'Unknow feed.<br>You can still help by <a href="${link}" target="_black">click here</a>.';
            var id = $(this).attr('feed');
            var $li = $('li[feed='+id+']');
            var $p = $('.body > p', $li).empty();
            var $div = $('.state_completed',$li);
            var help = feedStream.get(id);
            
            $('.state_accept, .state_completed', $li).hide();
            
            if (help && help.action && isChecked(help.action.enable)) {
                Resources.getPicture('ajax_loader','span').appendTo($p).css('padding-left',18).text('Loading...');
                doHelp(help.url, help.action, function(data, success) {
                    data = cleanResponse(data);
                    help.collected();
                    $li.addClass('collected');
                    if (success === true) {
                        addCommentFeed($div, help.feed.post_id, data);
                    }
                    completed(data);
                });
            }
            else {
                completed(Util.render(unkActionMsg,{'link':help.feed.attachment.href}));
                $li.addClass('collected');
            }
            function completed(message, callback) {
                $p.hide().html(message).fadeIn(500, function(){$div.show();});
            }
            return false
        },
        deleteFeed_click: function() {
            removeFeed( $(this).attr('feed') );
            return false
        },
        skip_click: function() {
            showDiv('control', 'panel', 500, qryAction('#status_panel','empty'));
        },
        refresh_click: function() {
            showDiv('ajaxloader', 'body');
            showDiv('status', 'panel');
            c$('div').css('padding-top',5).appendTo($('#status_panel').empty())
                     .text('Loading... wait a moment please.');
            queryFeeds(function(fs) {
                if (!Util.isArray(fs) || fs.length < 1) {
                    showErrorMessage();
                    return;
                }
                feedStream = new CSStreamCollection(fs);
                genFeedsDom();
                updateWarLoot();
                showDiv('feedlist', 'body');
                showDiv('control', 'panel', 500);
            });
        },
        cancel_click: function() {
            messageTimer.clear();
            cancel_process = true;
            options.save(Events.refresh_click);
            $('#logList, #logSkippedList').empty();
            return false;
        },
        config_click: function() {
            $('#control_panel').fadeOut(500, function() {
                $('#minipopup_overlay').fadeIn(500);
                $('#panel_container').animate({height: 280}, 'normal', function() {
                    $('#config_panel').fadeIn(500);
                });
            });
        },
        save_click: function() {
            options.fromDomElements();
            options.save();
            $('#config_panel').fadeOut(500, function() {
                $('#panel_container').animate({height:35}, 'normal', function() {
                    $('#minipopup_overlay').fadeOut(500);
                    $('#control_panel').fadeIn(500);
                });
            });
        },
        keyup: function() {
            var search = this.value;
            clearTimeout($(this).attr('timeout'));
            $(this).attr('timeout', setTimeout(function() {
                if (Util.isString(search) && search.length > 3) {
                    $('li[feed]', '#feedlist_body').each(function(index, element) {
                        if (Util.doRgxTest(search, $('.body > p',element).text())) {
                            $(element).show();
                        } else {
                            $(element).hide();
                        }
                    });
                } else {
                    $('li[feed]', '#feedlist_body').show();
                }
            }, 1000));
        },
        change: function() {
            var gid = $('option:selected', this).val();
            if (gid === 'none') {
                $('li[feed]', '#feedlist_body').show();
                return;
            }
            $('li[feed]', '#feedlist_body').hide();
            $('li[gid='+gid+']', '#feedlist_body').show();
        },
        clearall: function() {
            $('li.collected', popupElt.content).each(function(index, element) {
                removeFeed( $(element).attr('feed') );
            });
        },
        config_change: function(e) {
            var $prnt = $(this).parent();
            var name = $(this).attr('name');
            
            if (!$(e.target).hasClass('checkbox') && !$(this).hasClass('selected')) {
                $('.selected', $prnt).toggleClass('selected', false);
                $(this).toggleClass('selected', true);
                showDiv(name, 'config');
            }
            return false;
        },
        subconfig_change: function(e) {
            var $div = $(this).parent().parent();
            var name = $(this).attr('name');
            var action = Actions.get(name);
            
            if (!$(e.target).hasClass('checkbox') && !$(this).hasClass('selected')) {
                $('.selected', $div).removeClass('selected');
                $(this).addClass('selected');
                if (action) {
                    $('#action_skipif', $div).attr('name',name).val(action.skipif||'');
                    $('#action_fail',   $div).attr('name',name).val(action.fail||'');
                }
            }
            return false;
        },
        action_issue_change: function() {
            var id = $(this).attr('name');
            var type = Util.doRgx(/action_(\w+)/, this.id).$1;
            var text = this.value;
            if (!Util.isString(id) || !Util.isString(text)) { 
                return; 
            }
            if (text.length < 1) {
                delete Actions.get(id)[type];
                delete options.actions[type][id];
            }
            else if (text !== Actions.get(id)[type]) {
                options.actions[type][id] = text;
                Actions.get(id)[type] = text;
            }
        }
    };
    /**
     * Return a function to execute the defined jQuery action.
     * @param {Object} selector
     * @param {String} action
     * @param {Object} [context]
     */
    function qryAction(selector, action, context) {
        return function() {
            $(selector, context)[action]();
        };
    }
    /**
     * Open a mini-popup to edit some configurations.
     * @param {Object} content
     * @param {Function} callback
     */
    function showMiniEditor(content, callback) {
        var p = new PopupObject({
            appendTo         : $('#minipopup_content').show(),
            type             : 'help',
            autoOpen         : true,
            zIndex           : 99999,
            closeAfterClick  : true,
            hideCloseButton  : true,
            content          : content,
            buttons: [{
                label: 'Save',
                addClass: 'short green',
                onclick: callback
            },{
                label: 'Close',
                addClass: 'short red'
            }]
        });
    }
    /**
     * Show the facebook friendlist mini-editor.
     */
    function showFBListEditor() {
        loadingScreen('Loading Facebook Friend lists...');
        facebook.friendlist(function(response) {
            loadingScreen();
            if (!Util.isArray(response.data)) {
                return;
            }
            var $fl = c$('div', 'class:fb_friendlist');
            Util.each(response.data,function(index, list) {
                if (list.list_type === 'user_created') {
                    x$(list.id, list.name, 'div').appendTo($fl)
                    .toggleClass('checked', Util.isSet(options.fbListFilter[list.id]));
                }
            });
            showMiniEditor(c$('div').css('text-align', 'left')
                .append(c$('center').text('ADDING FRIENDLISTS FILTER'))
                .append($fl), 
            function() {
                options.set('fbListFilter', {});
                $('.checkbox.checked', $fl).each(function(i, e) {
                    options.fbListFilter[e.id] = $(e).text();
                });
                options.toDomElements();
                options.save();
            });
        });
    }
    /**
     * Show the war loot mini-editor.
     */
    function showWarLootEditor() {
        showMiniEditor(c$('div').css('padding-bottom', 10)
            .append(c$('center').text('ADDING WAR LOOT'))
            .append(c$('input:hidden', 'id:warloot_id'))
            .append(c$('div').css({'clear':'both','margin-top':8}))
            .append(t$('warloot_name', 'Name:', 350))
            .append(c$('div').css({'clear':'both','margin-top':8}))
            .append(n$('warloot_att', 'Attack above:', 50))
            .append(n$('warloot_def', 'Defense above:', 50))
            .append(c$('div').css({'clear':'both','margin-top':8}))
            .append(x$('warloot_enabled', 'Enabled')), 
        function() {
            var data = warLootData();
            if (data.id === 'limited') {
                options.warlootLimited = data;
            } else {
                options.warloot[data.id] = data;
            }
            options.save(updateWarLoot);
        });
    }
    /**
     * Set/Get the war loot current data.
     * @param {Object} [data]
     * @return [Object]
     */
    function warLootData(data) {
        var $content = $('#minipopup_content');
        var save = Util.isSet(data);
        data = Util.merge({
            id      : parseInt((new Date()).getTime()/1000),
            enabled : true,
            name    : '*',
            att     : 0,
            def     : 0
        }, data);
        Util.each(data, function(name, value) {
            var $el = $('#warloot_'+name, $content);
            if ($el.length > 0) {
                if ($el.is('input, select')) {
                    if (save) {
                        $el.val(value);
                    } else {
                        data[name] = $el.val();
                    }
                } else {
                    if (save) {
                        $el.toggleClass('checked',value);
                    } else {
                        data[name] = $el.hasClass('checked');
                    }
                }
            }
        });
        if (!Util.isString(data.name) || data.name.length < 1) {
            data.name = '*';
        }
        return data;
    }
    function updateWarLoot() {
        var $list = $('#warloot_data').empty();
        var text = '${enabled}: "${name}" if > "${att}/${def}".';
        var limited = options.warlootLimited;
        $('#warlimit_count').text('('+options.warLimitCount+'/5)');
        $('#warlootlimited_data').val(Util.render(text, {
            enabled: (limited.enabled?'ON':'OFF'),
            name: (limited.name==='*'?'Any':limited.name),
            att: limited.att,
            def: limited.def
        }));
        Util.each(options.warloot, function(id, data) {
            c$('option','value:'+id).appendTo($list).text(Util.render(text, {
                enabled: (data.enabled?'ON':'OFF'),
                name: (data.name==='*'?'Any':data.name),
                att: data.att,
                def: data.def
            }));
        });
    }
    
    /**
     * @param {Element, jQuery, String} selector
     * @return {String}
     */
    function getUrlFromElement(selector) {
        var obj = $(selector);

        if (!obj.length || !obj.attr)
            return '';

        var url = obj.attr('href');

        if (typeof(url) == 'string' && /https?/.test(url)) {
            return url;
        }
        return Util.doRgx(/['"](remote[^'"]*)/, obj.attr('onclick')).$1;
    }
    /**
     * @param {String} url
     * @return {String}
     */
    function toInternalUrl(params) {
        if (!Util.isSet(params)) {
            return '';
        }
        var a, b;
        var sOutUrl = 'remote/' + MW.getIntURL(params.next_controller, params.next_action);
        var unQuote = function(str) {
            return Util.doRgx(/"([^"]+)"/, str).$1 || str;
        };
        for (var uri in params) {
            if (!/next_|from|zy_track|value/.test(uri)) {
                 if (uri==='friend') {
                     sOutUrl += ('&' + uri + '=' + Util.parseNum(params[uri]));
                 } else {
                     sOutUrl += ('&' + uri + '=' + params[uri]);
                 }
            }
        }
        if ( String(params.next_params).charAt(0) === '{' ) {
	        Util.each(Util.parseParam(params.next_params), function(n, v) {
	            sOutUrl += ('&' + n + '=' + v);
	        });
        }
        if ( String(params.value).charAt(0) === '{' ) {
            Util.each(Util.parseParam(params.value), function(n, v) {
                sOutUrl += ('&' + n + '=' + v);
            });
        }

        return sOutUrl;
    }
    /**
     * @param {String} data
     * @return {String}
     */
    function cleanResponse(response) {
        if (typeof(response) !== 'string') {
            return response;
        }
        var obj = $('<div>'+ response.replace(/<div[^>]*>/g, '<div>') +'</div>');
        $('img, .sexy_button_new, .sexy_button, br, div:empty, .msg_energy_but_col, script', obj).remove();
        return obj.html();
    }
    /**
     * Add a comment button.
     * @param {Object} element
     * @param {String} post_id
     * @param {String} message
     */
    function addCommentFeed(element, post_id, message) {
        message = h$('<div>'+message+'</div>').text();
        if (!post_id || !(Util.isString(message) && message.length > 2)) { 
            return;
        }
        c$('a','href:#,class:ignore').text('Comment').appendTo(element).click(function() {
            var me = $(this);
            me.unbind().css('opacity',0.5);
            facebook.commentAdd(post_id, message, function(comment) {
                if (comment && comment.id) {
                    me.replaceWith(Util.setColor('Commented!','yellow')).css('opacity',1);
                } else {
                    me.replaceWith(Util.setColor('Fail','red')).css('opacity',1);
                }
            });
            return false;
        });
    }
    /**
     * Return true if checkbox exist and is cheched. Otherwise false.
     * @param {Object} selector
     * @param {Object} context
     * @return {Boolean}
     */
    function isChecked(selector, context) {
        if (typeof(selector) == 'undefined') {
            return true;
        }
        return $(selector, context).attr('checked');
    }
    /**
     * @return {Number}
     */
    function getRefreshDelay() {
		var refreshDelay = parseInt(options.get('refreshDelay'));
        if ( refreshDelay < 2 ) {
            refreshDelay = 2;
        }
        return refreshDelay;
    }
    /**
     * Add default actions.
     */
    function addDefaultActions() {        
        // Add user defined Steams
        if (UserStreams.content.length > 0) {
            Util.each(UserStreams.content, function(index, data) {
                if (data.id && data.action) {
                    if (data.action.group!=='none' && Util.isSet(groupNames[data.action.group])) {
                        Actions.add(data.id, data.action);
                    }
                }
            });
        }
        Actions.add('DailyTakeV3_collect_stake', { 
            name       : 'Daily Take Reward',
            group      : 'doDailyTake',
            next       : collectDailyTake,
            skipif     : 'You have already collected'
        });
        Actions.add('war_view', {
            name       : 'Go to war',
            group      : 'doGotoWar',
            next       : helpInWar
        });
        Actions.add('war_share_reward_feed_click', {
            name       : 'Claim war reward',
            group      : 'doClaimBonusAndReward',
            next       : warRewardResult,
            fail       : 'You cannot claim this reward'
        });
        Actions.add('socialmission_joinMission', {
            name       : 'Join in a mission',
            group      : 'doSocialMissions',
            next       : joinOperation
        });
        Actions.add('robbing_mastery_bonus', {
            name       : 'Claim robbing mastery bonus',
            group      : 'doClaimBonusAndReward',
            next       : '.message_body a:regex(href,xw_action=mastery_bonus)'
        });
        Actions.add('index_ach_celeb', {
            name       : 'Get archivement reward',
            group      : 'doClaimBonusAndReward',
            next       : '.message_body a:regex(href,xw_action=ach_celeb)',
            fail       : 'You already got the bonus before|This bonus has expired'
        });
        Actions.add('story_claim_boss_bonus', {
            name       : 'Claim a boss bonus',
            group      : 'doClaimBonusAndReward',
            next       : '.message_body a:regex(href,xw_action=claim_boss_bonus)'
        });
        Actions.add('index_crm_levelup_claim', {
            name       : 'Get free Loyalty Points',
            group      : 'doClaimBonusAndReward',
            next       : '.message_body a:regex(href,xw_action=crm_levelup_claim)',
            skipif     : 'have already received the maximum amount',
            fail       : 'You are too late'             
        });
        Actions.add('propertyV2_getBoost', {
            name       : 'Get a property boost',
            group      : 'doPropertyHelp',
            next       : '.message_body a:regex(href,xw_action=getBoost)'
        });
        Actions.add('index_send_energy_mbox', {
            name       : 'Send energy',
            group      : 'doSendEnergyAndPhones',
            next       : '.message_body a:regex(href,xw_action=send_energy_mbox)'
        });
        Actions.add('socialmission_rewardBrag',        { 
            name       : 'Claim social mission reward',
            group      : 'doClaimBonusAndReward',
            fail       : 'You are too late to claim a reward.'
        });
        Actions.add('job_collect_loot', {
            name       : 'Collect a loot',
            group      : 'doPropertyHelp',
            skipif     : 'have to wait'
        });
        Actions.add('limitedTimeProperty_addPropertyPart', {
            name       : 'Send Limited Time Property Parts',     
            group      : 'doPropertyHelp',
            skipif     : 'You have already sent \\d+ parts today',
            fail       : 'You have already sent|feed have been claimed|feed has expired|has all parts needed|could not be completed'
        });
        Actions.add('limitedTimeProperty_addAnyPropertyPart', {
            name       : 'Send "Any" Limited Property Parts',     
            group      : 'doPropertyHelp',
            skipif     : 'You have already sent \\d+ parts today',
            fail       : 'You have already sent|feed have been claimed|feed has expired|You can only send parts to friend'
        });
        Actions.add('limitedTimeProperty_upgradeBragFeed',{ 
            name       : 'Limited Time Property Upgraded',
            group      : 'doPropertyHelp',
            skipif     : 'You already have all parts needed for this level',
            fail       : 'All rewards from this feed have been claimed|You have already collected|feed has expired'
        });
        Actions.add('fight_iced_boost_claim', {
            name       : 'Get iced boost',
            group      : 'doClaimBonusAndReward',
            skipif     : 'You can only receive \\d+ free iced fight boosts per day',
            fail       : 'This boost is no longer available'
        });
        Actions.add('fight_send_boost_from_feed',      { 
            name       : 'Collect Fight Boost',
            group      : 'doClaimBonusAndReward',
            fail       : 'This boost is no longer available|You have already received|You are too late' 
        });
        Actions.add('index_levelUpBonusClaim', {
            name       : 'Get levelup Bonus',
            group      : 'doClaimBonusAndReward',
            skipif     : 'You have already collected the maximum',
            fail       : 'available bonus rewards have already been claimed'
        });
        Actions.add('lootladderevent_share_feed_click', {
            name       : 'Collect a Share Loot Event',
            group      : 'doClaimBonusAndReward',
            skipif     : 'try again tomorrow'
        });
        Actions.add('lootladderevent_ask_feed_click', {
            name       : 'Collect an Ask Loot Event',
            group      : 'doClaimBonusAndReward',
            skipif     : 'try again tomorrow'
        });
        Actions.add('lootladderevent_brag_feed_click', {
            name       : 'Collect a Golden Loot Event',
            group      : 'doClaimBonusAndReward',
            skipif     : 'try again tomorrow'
        });
        Actions.add('Epicclanboss_ask_feed_click', { 
            name       : 'Collect Boss help item',
            group      : 'doClaimBonusAndReward',
            skipif     : 'You have already collected \\d+ Rifle Rounds today|You are at max capacity',
            fail       : 'You are too late|already collected'
        }); 
        Actions.add('job_give_help', { 
            name       : 'Give job help',
            group      : 'doGiveSocialHelp',
            skipif     : 'Try again tomorrow',
            fail       : 'You are too late to help|have already helped|already helped out'
        });
        Actions.add('story_give_help_social', { 
            name       : 'Give social help',
            group      : 'doGiveSocialHelp',
            skipif     : 'Try again tomorrow',
            fail       : 'You are too late to help|have already helped|already helped out'
        });
        Actions.add('story_give_help_moscow_social', { 
            name       : 'Give moscow help',
            group      : 'doGiveSocialHelp',
            skipif     : 'Try again tomorrow',
            fail       : 'You are too late to help|have already helped|already helped out|to be social network friends'
        });
        Actions.add('job_sd_boost_get', { 
            name       : 'Collect 2x loot boosts',
            group      : 'doClaimBonusAndReward',
            skipif     : 'you can only collect \\d+ boosts from feeds per day',
            fail       : 'these boosts have already been claimed|already helped your friend|this request has expired'
        });
        Actions.add('propertyV2_one_click_get', { 
            name       : 'Get Rob Squad',
            group      : 'doPropertyHelp',
            skipif     : 'can only collect \\d+ Rob Squads|you have too many Rob Squads in your inventory',
            fail       : 'already helped your friend|request has expired' 
        });
        Actions.add('propertyV2_collect_all_share', { 
            name       : 'Get Free Property Parts',
            group      : 'doPropertyHelp',
            skipif     : 'you have already collected the maximum amount',
            fail       : 'already helped your friend|you are too late'
        });
        Actions.add('propertyV2_itemFeedHelp', { 
            name       : 'Property item Help',
            group      : 'doPropertyHelp',
            skipif     : 'You have already helped \\d+ people today',
            fail       : 'has already received the maximum amount|You have already helped'
        });
        Actions.add('propertyV2_cs_redeem_special_item_feed',{ 
            name       : 'Property special help',
            group      : 'doPropertyHelp',
            fail       : 'cannot receive any more Exotic Feeds from you today'
        });
        Actions.add('ClanProperty_getPartsFromFeed', {
            match      : {
                'item_id=20000': {name: 'Get Reinforced Concrete'},
                'item_id=13000': {name: 'Get Artillery Shell'}
            },
            name       : 'Get Clan Property Parts',
            group      : 'doPropertyHelp',
            skipif     : 'already collected the maximum number',
            fail       : 'already collected from this request' 
        });
        Actions.add('index_levelup_boost_claim', { 
            name       : 'Get levelup boost',            
            group      : 'doClaimBonusAndReward',
            fail       : 'All of the available bonus rewards have already been claimed.',
        });
        Actions.add('FeedOfTheDay_feed_accept', { 
            name       : 'Collect Don\'s Gift',
            group      : 'doClaimBonusAndReward',
            fail       : 'already helped your friend with this request',
            skipif     : 'You have reached your limit for collecting'
        });
        Actions.add('job_mastery_feed_claim', { 
            name       : 'Collect job Mastery reward',
            group      : 'doClaimBonusAndReward',
            skipif     : 'have claimed the maximum number',
            fail       : 'You can only claim this reward once'
        });
        Actions.add('quest_questFeedReward', { 
            name       : 'Claim quest reward',
            group      : 'doClaimBonusAndReward',
            skipif     : 'you may only accept \\d+ rewards a day',
            fail       : 'the max number of people have already|you may only collect from a feed once|encountered an error'
        });
        Actions.add('job_accept_city_crew_feed', { 
            name       : 'Join in a crew',
            group      : 'doCityCrew',
            fail       : 'Crew queue is full|users already assisted|time limit to assist|You are already'
        });
        Actions.add('bossfightv2_ask_feed_click', { 
            name       : 'Collect a Boss Fight item',
            group      : 'doClaimBonusAndReward',
            fail       : 'feed has expired'
        });
        Actions.add('map_mapboss_reward_claim', { 
            name       : 'Claim boss reward', 
            group      : 'doClaimBonusAndReward',
            fail       : 'You have already received'
        });
        Actions.add('propertyV2_getCustomer', { 
            name       : 'Get a customer',
            group      : 'doPropertyHelp',
            fail       : 'You already helped this user' 
        });
        Actions.add('propertyV2_cs_help_item', { 
            name       : 'Send Property Parts',          
            group      : 'doPropertyHelp',
            fail       : 'has received all the help'
        });
        Actions.add('index_send_energy', { 
            name       : 'Send energy',
            group      : 'doSendEnergyAndPhones',
            fail       : 'You have already helped today|has already got their Energy'
        });
        Actions.add('index_power_pack_get', { 
            name       : 'Send Power Pack',
            group      : 'doSendEnergyAndPhones',
            fail       : 'already helped|feed is expired|you have too many'
        });
        Actions.add('robbing_call_for_help_get_phone', { 
            name       : 'Get robbing phone',
            group      : 'doSendEnergyAndPhones',
            fail       : 'too many friends help|You cannot help'
        });
        Actions.add('robbing_one_click_get',           { 
            name       : 'Get Rob Squad',
            group      : 'doSendEnergyAndPhones',
            fail       : 'you\'ve already helped'
        });
        Actions.add('job_accept_city_crew',            { name: 'Join in a crew',               group: 'doCityCrew' });
        Actions.add('propertyV2_cs_help_initial',      { name: 'Property Started',             group: 'doPropertyHelp' });
        Actions.add('propertyV2_cs_help_final',        { name: 'Property Upgraded',            group: 'doPropertyHelp' });

        
        /*
        Actions.add('freegifts_acceptGiftEvent',       { name: 'Collect Gift Event',           group: 'doClaimBonusAndReward' });
        Actions.add('hitlist_feed_hit', {
            name       : 'Hitlist Bounty',
            group      : 'doHitlistBounty',
            repeat     : '.message_body a:regex(href,action=attack), a:regex(href,action=power_attack)',
            bad        : '.message_body:has(span.bad)',
            success    : '.fight_results, .fightres_stats'
        });
        */
    }
    /**
     * Collect a link.
     * @param {String} url
     * @param {CSAction} action
     * @param {Function} callback 
     */
    function doHelp(url, action, callback)
    {
        httpAjaxRequest({
            url: url,
            success: function(htmlText)
            {
                if (!MW.update(htmlText)) {
                    callback(ERROR_BAD_RESPONSE);
                    return;
                }
                if (Util.isFunc(action.next)) {
                    action.next.apply(action, [htmlText, callback]);
                    return;
                }
                var $r, $html = h$(htmlText);
                
                if (action.next && ($r = $html.find(action.next)).length > 0) {
                    doHelp(getUrlFromElement($r), {}, callback, true);
                    return;
                }
                if (($r = $html.find('.message_body:first, #mbox_generic_1 tr:eq(1)')).length > 0) {
                    callback($r.html(), !Util.doRgxTest(action.fail, $r.text()));
                }
                else {
                    callback(ERROR_BAD_RESPONSE);
                }
            }
        });
    }
    /**
     * @param {Object} id
     */
    function removeFeed(id) {
        var $li = $('li[feed='+id+']');
        $li.fadeOut(500, qryAction($li, 'remove'));
        feedStream.remove(parseInt(id));
        $('#total_feeds', '#feed_center_header').html(feedStream.length());
    }
    /**
     * Add an error message.
     */
    function showErrorMessage() {
        showDiv('feedlist', 'body');
        $('#status_panel').empty().append(
            c$('div').css({'text-align':'center', 'padding-top':5})
            .append(c$('span').text('There is some error in facebook response.'))
            .append(b$('Retry','class:short white').css('margin-left',5).click(Events.refresh_click))
            .append(b$('Cancel','class:short white').css('margin-left',5).click(Events.skip_click))
        );
    }
    /**
     * Check a filter, return false if filter match the text or it's disabled.
     * @param {String} sText text to check.
     * @param {String} sUse use option name to enable/disable this filter
     * @param {String} sFilter filter option name to get the filter.
     * @return {Boolean}
     */
    function checkFilter(sText, sUse, sFilter) {
        sFilter = String( options.get(sFilter) );
        if ( options.get(sUse) && sFilter.length > 1 ) {
            return !( new RegExp(sFilter.replace(/\s*,\s*/g,'|'),'i') ).test(sText);
        }
        return false;
    }
    /**
     * Help in a war.
     * @param {String} htmlText
     * @return {Boolean, String}
     */
    function helpInWar(htmlText, callback) {
        var $html = h$(htmlText);
        var reward = $html.find('.helpers_rewards ul img').attr('title');
        var message = 'Wrong War Loot. ( '+reward+' )';
        var isLimited = false;
        var queries = {
            att: 'a:regex(href,xw_controller=war&xw_action=attack)',
            shr: '.war_attacks div.left a:regex(href,controller=stats)',
            scs: '.post_help_results p, .pop_box > div > div > div:eq(1)',
            msg: '.message_body:first'
        };
        if (!reward) {
            callback( 'Unknow War loot.', false );
            return;
        }
        if (Util.length(options.warloot) < 1 && options.warlootLimited.enabled !== true) {
            callback( 'Your War Loot filter is empty.', false );
            return;
        }
        if ($html.find(queries.att).length < 1) {
            callback( 'This war is already over.', false );
            return;
        }
        /**
         * @param {String} url
         */
        function attackTo(url, loot_id) {
            httpAjaxRequest({
                'url': url, 
                'success': function(htmlText) {
                    if (!MW.update(htmlText)) {
                        callback(ERROR_BAD_RESPONSE);
                        return;
                    }
                    var $pop = Util.parsePopup(htmlText);
                    var $html = h$(htmlText);
                    var $att = $html.find(queries.att);
                    if ($att.length > 0) {
                        attackTo( getUrlFromElement($att) );
                    } else {
                        if ($pop && $pop.length>0 && $pop.find) {
                            message = $pop.find(queries.scs).text();
                        } else {
                            message = $html.find(queries.msg).text();
                        }
                        postWarResult($html, loot_id);
                    }
                }
            });
        }
        /**
         * @param {jQuery} $html
         */
        function postWarResult($html, loot_id) {
            var failed = true;
            $html.find(queries.shr).each(function(i, a){
                try {
                	var id = Base64.decode(Util.uSplit(a.href).user);
                	if (global.USER_ID === id) {
                        if (isLimited) {
                            if (Util.isNumber(options.warLimitCount)) {
                                options.warLimitCount++;
                            } else {
                                options.warLimitCount = 1;
                            }
                            options.save();
                        } else if (options.warlootSecundaryLimit === true && loot_id) {
                            options.warloot[loot_id].enabled = false;
                        }
                        Logger.debug('Helped in War with loot: ( '+reward+' )');
                        callback( message, true );
                	    return (failed=false);
                	}
                } catch(e){}
            });
            if (failed) {
                callback( message, false );
            }
        }
        if (options.warlootLimited.enabled) {
            if (testWarFilter(options.warlootLimited)) {
                isLimited = true;
                attackTo(getUrlFromElement($html.find(queries.att)));
                return;
            }
            if (options.warLimitCount < 5) {
                callback( 'You need to collect +'+(5-options.warLimitCount)+' of your primary loot: ( '+reward+' )', false );
                return;
            }
        }
        for (var id in options.warloot) {
            if (testWarFilter(options.warloot[id])) {
                attackTo(getUrlFromElement($html.find(queries.att)), id);
                return;
            }
        }
        function testWarFilter(data) {
            if (!data.enabled) {
                return false;
            }
            var expr_name = new RegExp(data.name==='*'?'.':data.name, 'i');
            var attack = parseInt(Util.doRgx(/Attack:\s?(\d+)/i,reward).$1||1);
            var defense = parseInt(Util.doRgx(/Defense:\s?(\d+)/i,reward).$1||1);
            return expr_name.test(reward) && (attack>=parseInt(data.att)||defense>=parseInt(data.def));
        }
        
        callback( message, false );
    }
    /**
     * Parse a war reward help.
     * @param {Object} htmlText
     */
    function warRewardResult(htmlText, callback) {
        var $h = h$(htmlText);
        var popup = Util.parsePopup(htmlText);
        var message = String($('.pop_box > div > div > div:eq(1)',popup).text()||$h.find('.message_body:first').text());
        var success = (!Util.doRgxTest(this.fail, message) && message.length > 0)
        if (message.length < 1) {
            message = 'There was some error.';
        }
        callback(message, success);
    }
    /**
     * Collect a Daily Take.
     * @param {String} htmlText
     * @param {Function} callback
     */
    function collectDailyTake(htmlText, callback) {
        var temp = Util.parsePopup(htmlText);
        var rewards = new CSDailyTakeLootCollection();
        /**
         * Return an expression to match loot names.
         * @return {String}
         */
        function createPriorityName() {
            var text = Util.trim(String(options.dtLootPriority));
            if (!Util.isString(text) || text.length < 1) {
                return;
            } 
            else if (/\n/.test(text)) {
                text = text.split(/\n/).join('|');
            }
            return text;
        }
        /**
         * Parse collect popup and return the text. 
         * @param {CSDailyTakeLoot} loot
         */
        function collectAndReturn(loot) {
            httpAjaxRequest({
                'url': loot.url, 
                'success': function(htmlText) {
                    var response = h$(htmlText).find('#collectText');
                    if (response.length > 0) {
                        callback( response.text(), true );
                    } else {
                        callback( 'Seem that an user has received the reward before you.');
                    }
                }
            });
        }
        $('#dt_body_area .collectbox .dt_pop_item',temp).each(function(i, elem) {
            rewards.add(new CSDailyTakeLoot(elem));
        });
        if (rewards.length() < 1) {
            if ((temp = h$(htmlText).find('.message_body')).length > 0) {
               callback(temp.text());
            } else {
               callback('This "Daily Take" was taken.'); 
            }
            return;
        }
        if ( (temp = createPriorityName()) ) {
            if ( (temp = rewards.getByName(temp)) ) {
                collectAndReturn( temp );
                return;  
            }
        }
        if (options.dtCheckMinAtkDef) {
            if ( (temp = rewards.getByStats(options.dtMinAttack, options.dtMinDefense)) ) {
                collectAndReturn( temp );
                return;  
            }
        }
        callback( 'Filtered. ('+ rewards.names() +')' );
    }    
    /**
     * Try join in an operation
     * @param {String} htmlText
     * @param {Function} callback
     */
    function joinOperation(htmlText, callback)
    {
        var qHtml = h$(htmlText);

        if (e$('.socialMissionSelector', qHtml) == null) {
            callback('You can\'t join. Try helping out other mafia members.');
            return;
        }
        try {
            var nFree   = options.get('maxFreeSlots');
            var sName   = $('.missionSelectHeaderTitle', qHtml).text();
            var sDiff   = $('.missionDifficulty > span', qHtml).attr('class');
            var nSpend1 = parseInt($('#hfopt_joinmission').val());
            var nSpend2 = parseInt($('#hfopt_joinmissionafter').val());
            var sQuery  = 'a:regex(onclick,action=selectPosition)';
            var nSlots  = $(sQuery, qHtml).length;
            var cSlots  = new Array();
            
            if ( checkFilter(sName, 'opUseNameFilter', 'opNameFilter') ) {
                callback('Filtered name "'+sName+'".');
                return;
            }
            if (nSlots > nFree) {
                callback('Filtered "'+ sName + '". Too many slots free ('+nSlots+').');
                return;
            }
            if (!options.get('diff_' + sDiff.toLowerCase())) {
                callback('Filtered "'+ sName + '". Difficulty don\'t match.');
                return;
            }
            $('.missionSelectorBox:has('+sQuery+')', qHtml).each(function(index,elem) {
                var nSpendType = 0;
                var hasEnergy  = (e$('dd.energy', elem) !== null);
                var hasStamina = (e$('dd.stamina', elem) !== null);

                if ((hasEnergy || hasStamina) && !Util.isSet(cSlots[4])) {
                    cSlots[4] = $(sQuery, elem);
                }
                if (hasEnergy && !hasStamina) {
                    nSpendType = 1;
                }
                else if (!hasEnergy && hasStamina) {
                    nSpendType = 2;
                }
                else if (hasEnergy && hasStamina) {
                    nSpendType = 3;
                }

                if (!Util.isSet(cSlots[nSpendType])) {
                    Logger.debug('Adding SpendType: '+ nSpendType);
                    cSlots[nSpendType] = $(sQuery, elem);
                }
            });

            var button = (cSlots[nSpend1] || cSlots[nSpend2]);

            if (typeof(button) == 'undefined' || button.length < 1) {
                callback('Filtered "'+ sName + '". Spend type don\'t match.');
                return;
            }
        }
        catch(err) {
            Logger.error(err);
            callback(ERROR_BAD_RESPONSE);
            return;
        }

        httpAjaxRequest({
            url: getUrlFromElement(button),
            success: function(htmlText)
            {
                if (MW.update(htmlText))
                    callback('You has join in "' + sName + '" mission.', true);
                else
                    callback(ERROR_BAD_RESPONSE);
            }
        });
    }

    function showDiv(name, type, ms, fn) {
        $('div[id*=_'+type+']', popupElt.content).hide();
        var elem = $('#' + name + '_' + type, popupElt.content);
        if (ms) {
            elem.fadeIn(ms, fn);
        }
        else {
            elem.show();
        }
    }
    /**
     * Add a new success action.
     * @param {CSStream} stream
     * @param {String} responseText
     */
    function addHelpLog(stream, responseText) {
        var $log = $('#logList');
        var title = stream.action.name;
        var feed = stream.feed;

        if ($log.children().length > options.get('maxLogLength')) {
            $log.children().last().remove();
        }
        var $li = c$('li'), $wrapper, $buttons;
        var atch = feed.attachment;
        var $title = c$('a','target:_black').attr('href',feed.permalink).text(atch.name||atch.description);
        var $img = atch.media[0]
        ? c$('img','class:feed_icon,title:'+title).attr('src',atch.media[0].src)
        : c$('span');
        
        $wrapper = c$('div').addClass('li_wrapper clearfix').appendTo($li).append($img);
        $buttons = c$('div','class:buttons').appendTo($wrapper)
        .append(c$('a','href:#,class:ignore,postid:'+feed.post_id).text('like it').click(Events.likeFeed_click))
        .append(c$('div').css('clear','both'));
        c$('div','class:body').append(c$('h4').append($title)).append(c$('p').html(responseText)).appendTo($wrapper);
        
        $li.prependTo($log);
        $wrapper.mouseenter(Events.selectHelp).mouseleave(Events.unselectHelp);
        addCommentFeed($buttons, feed.post_id, responseText);
    }
    /**
     * Add a new skip/fail action.
     * @param {CSStream} stream
     * @param {String} message
     */
    function addSkippedLog(stream, message) {
        var timestamp = Util.setColor('['+(new Date()).toLocaleTimeString()+ '] ', 'grey');
        var $log = $('#logSkippedList');
        var streamName = Util.setAnchor(stream.feed.permalink, stream.action.name);
         
        if ($log.children().length > options.get('maxLogLength')) {
            $log.children().last().remove();
        }
        Resources.getPicture('info_icon','li').prependTo($log)
        .append(c$('p').html(timestamp+' '+streamName+': '+message));
    }
    /**
     * Send a message when auto collecting
     * @param {String} text
     */
    function sendMessage(text) {
        Resources.getPicture('ajax_loader', 'span').css('padding-left', 18)
        .appendTo($('#auto_feed_helper_messages').empty()).html(text);
    }

    function StartCollector() {
        var nDelay         = options.get('helpDelay');
        var nRefreshDelay  = getRefreshDelay();
        var nCompleted     = 0;
        var nSkipped       = 0;

        function toNextStream(message, delay) {
            if (cancel_process) {
                return;
            }
            $('#completed_helps').text(nCompleted);
            $('#skipped_helps').text(nSkipped);
            $('#total_feeds', '#feed_center_header').html(feedStream.length());
            // no more streams
            if (feedStream.length() < 1) {
                messageTimer.start('All helps finished!. continue in %N% seconds.',nRefreshDelay, updateStreams);
                return;
            }
            // if no delay, skip timer
            if (!delay) {
                collectStream(feedStream.shift());
            } else {
                messageTimer.start(''+message, delay, function() {
                    collectStream(feedStream.shift());
                });
            }
        }
        /**
         * @param {CSStream} cStream
         */
        function collectStream(cStream) {
            if (cancel_process) {
                return;
            }
            /**
             * Skip current stream
             * @param {String} sReason
             */
            function skipHelp(sReason) {
                Logger.debug(cStream.action.name+' skipped.');
                if (Util.isSet(sReason)) {
                    addSkippedLog(cStream, sReason);  
                }
                nSkipped++;
                toNextStream();
            }
            
            // skip if stream or action is undefined
            if ( !Util.isSet(cStream) || !Util.isSet(cStream.action) ) {
                toNextStream();
                return;
            }
            try {
                if ( cStream.isCollected() ) {
                    skipHelp();
                    return;
                }
	            // skip if stream group isn't enabled.
	            if (!options.get(cStream.gid)) {
                    skipHelp();
                    return;
                }
	            // skip if stream isn't enabled.
	            if (Util.isSet(options.get(cStream.gid.substr(2)))) {
	                if (options.get(cStream.gid.substr(2))[cStream.id] === false) {
                        skipHelp();
	                    return;
	                }
	            }
	            // skip if this stream can't be collected today
	            if (cStream.action.skip === true) {
                    skipHelp();
	                return;
	            }
                // collect the stream
                sendMessage('Helping in:  '+cStream.action.name);
                console.log(cStream.action);
                doHelp(cStream.url, cStream.action, function(response, success) {
                    response = cleanResponse(response);
                    cStream.collected();
                    cStream.action.skip = Util.doRgxTest(cStream.action.skipif, response);
                    if (cStream.action.skip || success !== true) {
                        addSkippedLog(cStream, response); 
                    } else {
                        addHelpLog(cStream, response);
                    }
                    nCompleted++;
                    toNextStream('Done!, Next in %N%...',nDelay);
                });
            }
            catch (err) {
                Logger.error('collectStream: ' + err.message);
                toNextStream();
            }
        }

        function updateStreams() {
            if (cancel_process) {
                return;
            }
            function retry(msg) {
                messageTimer.start(msg,nRefreshDelay,updateStreams);
            }
            sendMessage('Refreshing home posts...');

            queryFeeds(function(fs) {
                if (fs && fs.error) {
                    retry(fs.error.message+' try again in %N%...');
                    return;
                }
                if (!Util.isArray(fs) || fs.length < 1) {
                    retry('Error loading posts, try again in %N%...');
                    return;
                }
                // if empty, update again
                if ((feedStream = new CSStreamCollection(fs, true)).length() < 1) {
                    retry('No new posts, try again in %N%...');
                    return;
                }
                // update stream amout
                $('#total_feeds', '#feed_center_header').html(feedStream.length());

                // start collecting first stream
                collectStream(feedStream.shift());   
            });
        }

        //=========
        // Generate code for auto help messages
        $('#status_panel').empty();

        c$('div').appendTo('#status_panel').css({
            'padding':'5px 0 0 15px',
            'float': 'left'
        })
        .append(c$('span').css('color','green').text('Success: '))
        .append(c$('span','completed_helps').text(0))
        .append(c$('span').css({'color':'green','margin-left':5}).text('Skip: '))
        .append(c$('span','skipped_helps').text(0))

        c$('div','auto_feed_helper_messages').appendTo('#status_panel').html('Preparing...').css({
            'padding':'5px 0 0 30px',
            'float': 'left'
        });
        b$('CANCEL', 'class:short red').click(Events.cancel_click).appendTo('#status_panel').css({
            'float': 'right',
            'margin-right': 40
        });

        // toggle layers
        showDiv('automode', 'body');
        showDiv('status', 'panel', 500);

        // update options
        options.fromDomElements();
        options.save(function() {
            // rest the cancel variable
            cancel_process = false;
            // start
            if ( feedStream.length() > 0 ) {
                feedStream.slice( options.feedsLimit );
                collectStream(feedStream.shift());
            } else {
                updateStreams();
            }
        });
    }
    function genMainDom() {
        var $control = c$('div', 'control_panel').height(35)
        var $config = c$('div', 'config_panel');
        var $group = c$('select', 'id:hfopt_grouping').width(152);
        var $cfgselect = c$('div', 'id:main_selector,class:select_tab').css({
            width: 250,
            height: 200
        });
        // fix content height
        popupElt.content.css('margin-top', 5);

        // header popup
        c$('dl', 'class:total_requests')
        .appendTo(c$('div', 'feed_center_header').appendTo(popupElt.header))
        .append('<dt id="total_feeds">'+feedStream.length()+'</dt>')
        .append('<dd>Total Posts</dd>');
        
        c$('div','id:minipopup_overlay').appendTo(popupElt.content);
        c$('div','id:minipopup_content').appendTo(popupElt.content);
        
        // middle, config, buttons
        c$('div', 'panel_container').appendTo(popupElt.content)
        .append(c$('div', 'status_panel').height(35)).append($control).append($config);

        c$('div', 'class:search_box').appendTo($control)
        .append(c$('label', 'for:hfopt_filter').text('Search: '))
        .append(c$('input:text', 'id:hfopt_filter').width(149).keyup(Events.keyup))
        .append($group.change(Events.change));

        c$('div', 'class:control_box').appendTo($control)
        .append(b$('Clear all', 'class:short white').click(Events.clearall))
        .append(b$('Refresh','class:short orange').css('margin-left',5).click(Events.refresh_click))
        .append(b$('Config','class:short orange').css('margin-left',5).click(Events.config_click))
        .append(b$('AutoHelp','class:short orange').css('margin-left',10).click(StartCollector));
        
        // add options checkboxes and group list
        $cfgselect.appendTo($config);
        
        Util.each(groupNames, function(id, name) {
            var $elm = c$('div', 'name:'+id).appendTo($cfgselect).click(Events.config_change);
            // add the group to the filter            
            $group.append(c$('option', 'value:'+id).text(name));
            // add the group to configuration
            if (id !== 'none') {
                $elm.append(x$('hfopt_'+id.toLowerCase()).css('margin',0));
            }
            c$('p').text(name).appendTo($elm);
        });
        $('div[name=none]', $cfgselect).addClass('selected');
        
        $config        
        .append( c$('div', 'class:config_tab,id:none_config')                  .append(genGeneralConfig())                             )
        .append( c$('div', 'class:config_tab,id:doGotoWar_config')             .append(genWarFilter())                                 )
        .append( c$('div', 'class:config_tab,id:doGiveSocialHelp_config')      .append(genMultiCheckboxConfig('GiveSocialHelp'))       )
        .append( c$('div', 'class:config_tab,id:doSocialMissions_config')      .append(genMissionConfig())                             )
        .append( c$('div', 'class:config_tab,id:doClaimBonusAndReward_config') .append(genMultiCheckboxConfig('ClaimBonusAndReward'))  )
        .append( c$('div', 'class:config_tab,id:doPropertyHelp_config')        .append(genMultiCheckboxConfig('PropertyHelp'))         )
        .append( c$('div', 'class:config_tab,id:doAcceptGiftEvent_config')     .append(genEmptyConfig())                               )
        .append( c$('div', 'class:config_tab,id:doCityCrew_config')            .append(genEmptyConfig())                               )
        .append( c$('div', 'class:config_tab,id:doDailyTake_config')           .append(genDailyTakeFilter())                           )
        .append( c$('div', 'class:config_tab,id:doSendEnergyAndPhones_config') .append(genEmptyConfig())                               )
        .append( c$('div', 'class:save_config').append(b$('Save Configuration','class:short orange').click(Events.save_click))         );

        // ADD BODY ELEMENTS
        c$('div').appendTo(popupElt.content).css({'width':'100%','height':35});
        c$('div', 'feedlist_body').height(700).appendTo(popupElt.content);
        c$('div', 'ajaxloader_body').css('padding-top',25).height(675)
        .append(c$('img').attr('src', global.zGraphicsURL+'socialmissions/ajax-loader.gif'))
        .appendTo(popupElt.content);
        c$('div', 'automode_body').height(700)
        .append(c$('ul', 'id:logList,class:feed_box').height(400)).css('border-bottom','2px solid #333;')
        .append(c$('ul', 'id:logSkippedList,class:skipped_log').height(300))
        .appendTo(popupElt.content);
        
        // =====================

        function genEmptyConfig() {
            return c$('center').css('margin-top', 10)
            .html('This category don\'t have any configuration.');
        }
        function genMultiCheckboxConfig(id) {
            var $elm = c$('div');
            var grp = String(id).toLowerCase();
            var opts = options.get(id);
            var $tlb = c$('div', 'class:select_tab,name:checkboxlist,id:hfopt_'+grp);
            
            $tlb.height(150).width(400).appendTo($elm);
            t$('action_skipif', 'Skip Check:', 335, {
                'change'   : Events.action_issue_change,
                'focusout' : Events.action_issue_change
            }).appendTo($elm).css({'float':'right','margin':'5px 0px 0px 0px'});
            t$('action_fail', 'Fail Check:', 335, {
                'change'   : Events.action_issue_change,
                'focusout' : Events.action_issue_change
            }).appendTo($elm).css({'float':'right','margin':'5px 0px 0px 0px'});
            
            Actions.each(function(name, action) {
                var skipif = options.actions.skipif[name];
                var fail = options.actions.fail[name];
                if ( action.group !== 'do'+id ) { return; }
                if ( !Util.isSet(opts[name]) )  { opts[name] = true; }
                if ( Util.isString(skipif)&& skipif.length>0 ) { action.skipif = skipif; }
                if ( Util.isString(fail)  && fail.length > 0 ) { action.fail = fail; }
                c$('div', 'name:'+name).appendTo($tlb).click(Events.subconfig_change)
                .append(x$(name).attr('value',name).css('margin',0))
                .append(c$('p').text(action.name));
            });
            return $elm;
        }
        function genGeneralConfig() {
            var $div = c$('div');
            var tabs = new TabObject({
                id: 'hfc_filters_wrapper',
                appendTo: $div,  height: 177,
                tabs: ['General', 'Friends', 'Friend List']
            });
            c$('div').css('padding',5).appendTo(tabs.content[0])
            .append(n$('hfopt_feedslimit', 'Feeds Limit', 100))
            .append(c$('div').css({'clear':'both','margin-top':5}))
            .append(s$('hfopt_maxloglength', 'Log Length:', 100))
            .append(c$('div').css({'clear':'both','margin-top':5}))
            .append(s$('hfopt_helpdelay', 'Help delay:', 100))
            .append(c$('div').css({'clear':'both','margin-top':5}))
            .append(s$('hfopt_refreshdelay', 'Refresh delay:',100));
            
            c$('div', 'class:ui-filterlist').appendTo(tabs.content[1])
            .append(c$('div', 'class:ui-top') 
            .append(x$('hfopt_fblistfilteron', 'Friendlist filter:'))
            .append(c$('div', 'class:ui-right').css('margin-right',4)
            .append(c$('a', 'href:#,class:ui-button').text('Edit').click(Events.addFblistToFilter_click))
            .append(c$('a', 'href:#,class:ui-button').text('Clear').click(Events.clearFblistFilter_click))))
            .append(c$('select', 'id:hfopt_fblistfilter,multiple:multiple'));
            
            c$('div', 'class:ui-filterlist').appendTo(tabs.content[2])
            .append(c$('div', 'class:ui-top')
            .append(x$('hfopt_userfilteron', 'Friends filter '))
            .append(c$('div', 'class:ui-right').css('margin-right',4)
            .append(c$('a', 'href:#,class:ui-button').text('Edit').click(Events.addUserToFilter_click))
            .append(c$('a', 'href:#,class:ui-button').text('Clear').click(Events.clearUserFilter_click))))
            .append(c$('select', 'id:hfopt_userfilter,multiple:multiple'));
            
            return $div;
        }
        function genWarFilter() {
            var $div = c$('div')
            .append(c$('div').html('Primary Loot <span id="warlimit_count" style="color:green">(0/5)</span>:')
            .append(c$('div', 'class:ui-right')
            .append(c$('a', 'href:#,class:ui-button,limited:1').text('Edit').click(Events.addWarloot))
            .append(c$('a', 'href:#,class:ui-button').text('reset').click(Events.resetLimitedWarloot))))
            .append(c$('div').css('clear','both'))
            .append(c$('input:text', 'id:warlootlimited_data').width(420))
            .append(c$('div').text('Secundary Loot:').css({'clear':'both','margin-top':5})
            .append(x$('hfopt_warlootsecundarylimit', 'One time.').css('margin-left',15))            
            .append(c$('div', 'class:ui-right')
            .append(c$('a', 'href:#,class:ui-button').text('Add').click(Events.addWarloot))
            .append(c$('a', 'href:#,class:ui-button').text('Edit').click(Events.editWarloot))
            .append(c$('a', 'href:#,class:ui-button').text('Remove').click(Events.removeWarloot))
            .append(c$('a', 'href:#,class:ui-button').text('toggle').click(Events.toggleWarloot))))
            .append(c$('select','id:warloot_data,multiple:multiple').css({'width':420,'height':140,'margin-top':2}));
            
            return $div;
        }
        function genDailyTakeFilter() {
            return c$('div')
            .append(c$('div').text('1st CHECK -> Loot Priority/Filter'))
            .append(c$('textarea', 'cols:50,rows:8,id:hfopt_dtlootpriority'))
            .append(c$('div').css('clear','both').css('margin-top',5))
            .append(x$('hfopt_dtcheckminatkdef', '2nd Check -> Minimal Attack/Defense', 'div'))
            .append(n$('hfopt_dtminattack', 'Minimal Attack:', 60))
            .append(n$('hfopt_dtmindefense', '-OR- Minimal Defense:', 60));
        }
        function genMissionConfig() {
            var $elm = c$('div');
            c$('div').css('margin',5)
            .append(c$('span').text('Mission difficulty: '))
            .append(c$('input:checkbox', 'id:hfopt_diff_easy'))
            .append(c$('label', 'for:hfopt_diff_easy').text('Easy'))
            .append(c$('input:checkbox', 'id:hfopt_diff_medium'))
            .append(c$('label', 'for:hfopt_diff_medium').text('Medium'))
            .append(c$('input:checkbox', 'id:hfopt_diff_hard'))
            .append(c$('label', 'for:hfopt_diff_hard').text('Hard'))
            .append(c$('input:checkbox', 'id:hfopt_diff_event'))
            .append(c$('label', 'for:hfopt_diff_event').text('Event'))
            .appendTo($elm);

            c$('div').css('margin',5)
            .append(c$('label', 'for:hfopt_joinmission').text('Join only in: '))
            .append(c$('select', 'id:hfopt_joinmission').width(90))
            .append(c$('label', 'for:hfopt_joinmissionafter').text(' if not possible then: '))
            .append(c$('select', 'id:hfopt_joinmissionafter').width(90))
            .appendTo($elm);

            c$('div').css('margin',5)
            .append(c$('label', 'for:hfopt_maxfreeslots').text('And only if remain: '))
            .append(c$('select', 'id:hfopt_maxfreeslots').width(110))
            .appendTo($elm);

            c$('div').css('margin',5)
            .append(x$('hfopt_opusenamefilter', 'Active Operation Name Filter:'))
            .append(c$('br'))
            .append(c$('textarea', 'cols:45,rows:6,id:hfopt_opnamefilter'))
            .appendTo($elm);

            return $elm;
        }

        popupElt.applyOptions({
            'hfopt_userfilteraction'  : {'skip':'Skipping', 'help':'Helping'},
            'hfopt_maxloglength'      : {50:'50', 200:'200', 500:'500', 1000:'1000'},
            'hfopt_joinmission'       : {4:'Any', 1:'Energy', 2:'Stamina', 3:'Both'},
            'hfopt_joinmissionafter'  : {0:'None', 1:'Energy', 2:'Stamina', 3:'Both', 4:'Any'},
            'hfopt_maxfreeslots'      : {100:'Any slots', 1:'1 slot', 2:'2 slots', 3:'3 slots', 4:'4 slots'},
            'hfopt_helpdelay'         : {0:'None', 1:'1 sec', 2:'2 sec', 3:'3 sec', 5:'5 sec'},
            'hfopt_refreshdelay'      : {2:'2 sec', 5:'5 sec', 15:'15 sec', 30:'30 sec', 60:'1 min', 180:'3 min', 300:'5 min', 600:'10 min'}
        });
    }

    function genFeedsDom() {
        var $li, $ul = c$('ul', 'class:feed_box').height(700);
        // ADD FEEDS
        feedStream.each(function(index, obj) {
            var feed  = obj.feed;
            var atch  = feed.attachment;
            var name  = '<a href="'+feed.permalink+'" target="_black">';
            var desp  = atch.description ? atch.description : atch.caption;
            
            name += (atch.name||atch.caption||atch.description) + '</a>';
            
            $li = c$('li', 'gid:'+(obj.gid||'none')+',feed:'+index).appendTo($ul);
            if (obj.isNew) {
                $li.addClass('new_post');
            }
            $li = c$('div', 'class:li_wrapper clearfix').appendTo($li);
            if (atch.media && atch.media[0] && atch.media[0].src) {
                c$('img', 'class:feed_icon').attr('src', atch.media[0].src).appendTo($li);
            }
            c$('div','class:buttons state_accept').appendTo($li)
            .append(b$('Help', 'class:medium white,feed:'+index).click(Events.helpFeed_click))
            .append(c$('a', 'href:#,class:ignore,feed:'+index).text('ignore').click(Events.deleteFeed_click));
            c$('div','class:buttons state_completed').appendTo($li).css('display','none')
            .append(b$('Clear', 'class:medium white,feed:'+index).click(Events.deleteFeed_click))
            .append(c$('div').css('clear','both'))
            .append(c$('a','href:#,class:ignore,postid:'+feed.post_id).text('like it').click(Events.likeFeed_click))
            .append(c$('div').css('clear','both'));
            
            c$('div','class:body').append('<h4>'+name+'</h4><p>'+desp+'</p>').appendTo($li);
        });
        $('#feedlist_body').empty().append($ul);
        $('#total_feeds', '#feed_center_header').html(feedStream.length());
        if ($('#hfopt_grouping').val() !== 'none') {
            $('#hfopt_grouping').change();
        }
    }
    
    function loadError() {
        var new_limit = Math.max(parseInt(options.feedsLimit/2), 25);
        if (options.feedsLimit > 25) {
            showHelpPopup({
                icon: 'error',
                title: 'No home posts found',
                message: 'There was an error while loading your home posts<br>'
                +        'would you like to reset feeds limit and disable friends filter before try again?.',
                buttons: [{
                    label: 'Yes',
                    addClass: 'short white',
                    onclick: function() {
                        options.fbListFilterOn = false;
                        options.userFilterOn = false;
                        options.feedsLimit = 50;
                        options.save(Initialize);
                    }
                }, {
                    label: 'No'
                }]
            });
        } else {
            showHelpPopup({
                icon: 'error',
                title: 'No home posts found',
                message: 'There was an error while loading your home posts<br>'
                +        'And your feed limit is very low, try again later.'
            });
            popupElt.destroy();
        }
    }
    function queryFeeds(callback) {
        var args = {'limit':options.feedsLimit};
        if (options.fbListFilterOn===true && Util.length(options.fbListFilter) > 0) {
            args.flid = new Array();
            Util.each(options.fbListFilter, function(id) { 
                args.flid.push('"'+id+'"'); 
            });
        }
        if (options.userFilterOn===true && Util.length(options.userFilter) > 0) {
            args.uid = new Array();
            Util.each(options.userFilter, function(id) { 
                args.uid.push('"'+id+'"'); 
            });
        }
        facebook.queryFeed(callback, args);
    }
    
    function Initialize() {
        var overlay = loadingScreen('Loading home posts...');

        queryFeeds(function(fs) {
            overlay.hide();
            if (!Util.isSet(fs) || fs.error || fs.length < 1) {
                loadError();
                return;
            }
            Actions = new CSActionCollection();
	        // add all pre-configured actions
	        addDefaultActions();
            feedStream = new CSStreamCollection(fs);
            Logger.debug('HFC: '+ feedStream.length());

            genMainDom();
            options.toDomElements();
            genFeedsDom();
            updateWarLoot();

            showDiv('feedlist', 'body');
            showDiv('control', 'panel');
            showDiv('none', 'config');

            // show popup
            popupElt.applyCustomClass();
            popupElt.show();
        });
    }

    popupElt.addBase64Style(
        'I2ZlZWRfY2VudGVyX2hlYWRlciB7DQoJZmxvYXQ6IHJpZ2h0Ow0KCWhlaWdodDogNjRweDsNCgltYXJnaW4tcmlnaHQ6IDUwcHg7'+
        'DQoJbWFyZ2luLXRvcDogNnB4Ow0KfQ0KI2ZlZWRfY2VudGVyX2hlYWRlciBkbC50b3RhbF9yZXF1ZXN0cyB7DQoJZmxvYXQ6IGxl'+
        'ZnQ7DQoJZm9udC13ZWlnaHQ6IGJvbGQ7DQoJbWFyZ2luOiAxNHB4IDBweCAwcHg7DQp9DQojZmVlZF9jZW50ZXJfaGVhZGVyIGRs'+
        'LnRvdGFsX3JlcXVlc3RzIGR0IHsNCgliYWNrZ3JvdW5kOiB1cmwoJHtiYXNlX3VybH16bWMvdG90YWxfcmVxdWVzdHNfYnViYmxl'+
        'XzM4eDM4XzAxLnBuZykgbm8tcmVwZWF0IDBweCAwcHg7DQoJZmxvYXQ6IGxlZnQ7DQoJZm9udC1zaXplOiAxOHB4Ow0KCWhlaWdo'+
        'dDogMzhweDsNCglsaW5lLWhlaWdodDogMzhweDsNCgltYXJnaW4tcmlnaHQ6IDZweDsNCgl0ZXh0LWFsaWduOiBjZW50ZXI7DQoJ'+
        'd2lkdGg6IDM4cHg7DQp9DQojZmVlZF9jZW50ZXJfaGVhZGVyIGRsLnRvdGFsX3JlcXVlc3RzIGRkIHsNCgliYWNrZ3JvdW5kOiB1'+
        'cmwoJHtiYXNlX3VybH16bWMvdG90YWxfcmVxdWVzdHNfYmdfMjAweDI0XzAxLnBuZykgbm8tcmVwZWF0IDEwMCUgNTAlOw0KCWZv'+
        'bnQtc2l6ZTogMTJweDsNCgloZWlnaHQ6IDM4cHg7DQoJbGluZS1oZWlnaHQ6IDM4cHg7DQoJbWFyZ2luLWxlZnQ6IDVweDsNCglw'+
        'YWRkaW5nOiAwcHggMjBweCAwcHggMzlweDsNCgl3aGl0ZS1zcGFjZTogbm93cmFwOw0KfQ0KI2hvbWVmZWVkY2VudGVyX3BvcHVw'+
        'IC5ibGFja19ib3ggew0KCWZvbnQtd2VpZ2h0OiBib2xkOyANCgljb2xvcjogcmdiKDIwOCwgMjA4LCAyMDgpOyANCglib3JkZXI6'+
        'IDFweCBzb2xpZCByZ2IoMTUzLCAxNTMsIDE1Myk7IA0KCWJhY2tncm91bmQtY29sb3I6IGJsYWNrOyANCglmb250LXNpemU6IDE0'+
        'cHg7DQp9DQojaG9tZWZlZWRjZW50ZXJfcG9wdXAgdWwuZmVlZF9ib3ggew0KICAgIGxpc3Qtc3R5bGUtdHlwZTogbm9uZTsNCiAg'+
        'ICBvdmVyZmxvdzogYXV0bzsNCn0NCiNob21lZmVlZGNlbnRlcl9wb3B1cCB1bC5za2lwcGVkX2xvZyB7DQogICAgbGlzdC1zdHls'+
        'ZS10eXBlOiBub25lOw0KICAgIG92ZXJmbG93OiBhdXRvOw0KICAgIHBhZGRpbmc6IDNweDsNCiAgICBmb250LXNpemU6IDEycHg7'+
        'DQogICAgYm9yZGVyLWJvdHRvbTogMXB4IHNvbGlkICM0NDQ7DQogICAgYm9yZGVyLXRvcDogMXB4IHNvbGlkICM0NDQ7DQp9DQoj'+
        'aG9tZWZlZWRjZW50ZXJfcG9wdXAgdWwuc2tpcHBlZF9sb2cgbGkgew0KICAgIGJvcmRlcjogMnB4IHNvbGlkICMxRjFGMUY7DQog'+
        'ICAgbWFyZ2luOiAwcHggMHB4IDRweDsNCiAgICBwYWRkaW5nOiAwcHggMHB4IDBweCAxNnB4Ow0KICAgIHdpZHRoOiBhdXRvICFp'+
        'bXBvcnRhbnQ7DQogICAgYmFja2dyb3VuZC1zaXplOiAxNnB4ICFpbXBvcnRhbnQ7DQogICAgLW1vei1iYWNrZ3JvdW5kLXNpemU6'+
        'IDE2cHggIWltcG9ydGFudDsNCiAgICBtaW4taGVpZ2h0OiAxNnB4ICFpbXBvcnRhbnQ7DQp9DQojaG9tZWZlZWRjZW50ZXJfcG9w'+
        'dXAgdWwuc2tpcHBlZF9sb2cgbGkgaW1nIHsNCglmbG9hdDogbGVmdDsNCgl3aWR0aDogMjBweDsNCgloZWlnaHQ6IDIwcHg7DQp9'+
        'DQojaG9tZWZlZWRjZW50ZXJfcG9wdXAgdWwuc2tpcHBlZF9sb2cgbGkgcCB7DQogICAgbWFyZ2luOiAxcHggMHB4IDBweCAzcHg7'+
        'DQogICAgaGVpZ2h0OiAyMHB4Ow0KCWxpbmUtaGVpZ2h0OiAyMHB4Ow0KICAgIG92ZXJmbG93LXk6IGhpZGRlbjsNCn0NCiNob21l'+
        'ZmVlZGNlbnRlcl9wb3B1cCAjZmVlZGxpc3RfYm9keSB1bC5mZWVkX2JveCBsaTpmaXJzdC1jaGlsZCB7DQoJYm9yZGVyLXdpZHRo'+
        'OiAwcHg7DQp9DQojaG9tZWZlZWRjZW50ZXJfcG9wdXAgdWwuZmVlZF9ib3ggbGkgew0KCW1hcmdpbjogMHB4IDBweCA2cHg7DQoJ'+
        'aGVpZ2h0OiA4MHB4Ow0KCW1heC1oZWlnaHQ6IDEwMHB4Ow0KfQ0KI2hvbWVmZWVkY2VudGVyX3BvcHVwIHVsLmZlZWRfYm94IGxp'+
        'Lm5ld19wb3N0IHsNCgliYWNrZ3JvdW5kOiB1cmwoJHtiYXNlX3VybH16bWMvbmV3X21lc3NhZ2VfY2FsbG91dF80NXg0NV8wMS5w'+
        'bmcpIG5vLXJlcGVhdCAwcHggMHB4Ow0KfQ0KI2hvbWVmZWVkY2VudGVyX3BvcHVwIHVsLmZlZWRfYm94IGRpdi5saV93cmFwcGVy'+
        'IHsNCiAgICBwYWRkaW5nOiA0cHggMTRweCAycHggNHB4Ow0KICAgIGJvcmRlci1ib3R0b206IDJweCBzb2xpZCAjMUYxRjFGOw0K'+
        'ICAgIGJvcmRlci10b3A6IDJweCBzb2xpZCAjMUYxRjFGOw0KfQ0KI2hvbWVmZWVkY2VudGVyX3BvcHVwICNhdXRvbW9kZV9ib2R5'+
        'IHVsI2xvZ0xpc3QgZGl2LnNlbGVjdGVkIHsNCiAgICBwb3NpdGlvbjogYWJzb2x1dGU7DQogICAgYmFja2dyb3VuZC1jb2xvcjog'+
        'IzEyMTIxMjsNCiAgICBib3JkZXItdG9wOiAycHggc29saWQgIzQ0NDsNCiAgICBib3JkZXItYm90dG9tOiAycHggc29saWQgIzQ0'+
        'NDsNCn0NCiNob21lZmVlZGNlbnRlcl9wb3B1cCB1bC5mZWVkX2JveCBsaSBpbWcuZmVlZF9pY29uIHsNCglmbG9hdDogbGVmdDsN'+
        'CglkaXNwbGF5OiBibG9jazsNCgl3aWR0aDogNzBweDsNCn0NCiNob21lZmVlZGNlbnRlcl9wb3B1cCB1bC5mZWVkX2JveCBsaS5u'+
        'ZXdfcG9zdCBpbWcuZmVlZF9pY29uIHsNCgltYXJnaW46IDE1cHggMCAwIDE1cHg7DQoJd2lkdGg6IDU1cHg7DQp9DQojaG9tZWZl'+
        'ZWRjZW50ZXJfcG9wdXAgdWwuZmVlZF9ib3ggbGkgZGl2LmJ1dHRvbnMgew0KCWZsb2F0OiByaWdodDsNCglmb250LXNpemU6IDEw'+
        'cHg7DQoJZm9udC13ZWlnaHQ6IGJvbGQ7DQoJbWFyZ2luOiAzcHggMHB4IDBweCAxNHB4Ow0KCXRleHQtYWxpZ246IHJpZ2h0Ow0K'+
        'CXdpZHRoOiA3MHB4Ow0KfQ0KI2hvbWVmZWVkY2VudGVyX3BvcHVwIHVsLmZlZWRfYm94IGxpIGRpdi5ib2R5IHsNCgltYXJnaW46'+
        'IDBweCA4MHB4Ow0KCWhlaWdodDogNzBweDsNCn0NCiNob21lZmVlZGNlbnRlcl9wb3B1cCAjYXV0b21vZGVfYm9keSB1bC5mZWVk'+
        'X2JveCBsaSBkaXYuYm9keSB7DQoJbWFyZ2luLWxlZnQ6IDgwcHg7DQoJaGVpZ2h0OiA3MHB4Ow0KfQ0KI2hvbWVmZWVkY2VudGVy'+
        'X3BvcHVwIHVsLmZlZWRfYm94IGxpIGg0IHsNCglmb250LXNpemU6IDE0cHg7DQoJbGluZS1oZWlnaHQ6IDE4cHg7DQoJbWFyZ2lu'+
        'LWJvdHRvbTogNHB4Ow0KCXBhZGRpbmctdG9wOiAycHg7DQp9DQojaG9tZWZlZWRjZW50ZXJfcG9wdXAgdWwuZmVlZF9ib3ggbGkg'+
        'cCB7DQoJZm9udC1zaXplOiAxMnB4Ow0KCWxpbmUtaGVpZ2h0OiAxNXB4Ow0KfQ0KI2hvbWVmZWVkY2VudGVyX3BvcHVwIHVsLmZl'+
        'ZWRfYm94IGxpIGRpdi5idXR0b25zIGEuc2V4eV9idXR0b25fbmV3IHsNCiAgICBtaW4td2lkdGg6IDYycHg7DQoJbWFyZ2luLWJv'+
        'dHRvbTogNnB4Ow0KfQ0KI2hvbWVmZWVkY2VudGVyX3BvcHVwIHVsLmZlZWRfYm94IGxpIGRpdi5idXR0b25zIGEuaWdub3JlIHsN'+
        'Cgl0ZXh0LXRyYW5zZm9ybTogdXBwZXJjYXNlOw0KfQ0KI2ZlZWRfY2VudGVyX2hlYWRlciBwLCAjaG9tZWZlZWRjZW50ZXJfcG9w'+
        'dXAgdWwsICNob21lZmVlZGNlbnRlcl9wb3B1cCBsaSwgI2hvbWVmZWVkY2VudGVyX3BvcHVwIGRsLCAjaG9tZWZlZWRjZW50ZXJf'+
        'cG9wdXAgZHQsICNob21lZmVlZGNlbnRlcl9wb3B1cCBkZCwgI2hvbWVmZWVkY2VudGVyX3BvcHVwIGgyLCAjaG9tZWZlZWRjZW50'+
        'ZXJfcG9wdXAgaDMsICNob21lZmVlZGNlbnRlcl9wb3B1cCBoNCwgI2hvbWVmZWVkY2VudGVyX3BvcHVwIHAgew0KCW1hcmdpbjog'+
        'MHB4Ow0KCXBhZGRpbmc6IDBweDsNCn0NCiNob21lZmVlZGNlbnRlcl9wb3B1cCAuc2VsZWN0X3RhYiB7DQoJYm9yZGVyOiAxcHgg'+
        'c29saWQgIzRGNEY0RjsNCglmbG9hdDogbGVmdDsNCgltaW4taGVpZ2h0OiAxNDBweDsNCgltYXJnaW46IDJweDsNCglvdmVyZmxv'+
        'dzogYXV0bzsNCglwYWRkaW5nOiA1cHg7DQoJdGV4dC1hbGlnbjogbGVmdDsNCgl3aWR0aDogMjQwcHg7DQp9DQojaG9tZWZlZWRj'+
        'ZW50ZXJfcG9wdXAgLnNlbGVjdF90YWIgZGl2IHsNCglib3JkZXI6IDFweCBzb2xpZCAjMkYyRjJGOw0KCW92ZXJmbG93OiBoaWRk'+
        'ZW47DQoJcGFkZGluZzogMHB4IDJweCAycHggNXB4Ow0KfQ0KI2hvbWVmZWVkY2VudGVyX3BvcHVwIC5zZWxlY3RfdGFiIGRpdi5z'+
        'ZWxlY3RlZCB7DQoJYm9yZGVyOiAxcHggc29saWQgeWVsbG93Ow0KfQ0KI2hvbWVmZWVkY2VudGVyX3BvcHVwIC5zZWxlY3RfdGFi'+
        'IGRpdiBpbnB1dCB7DQoJZmxvYXQ6IGxlZnQ7DQp9DQojaG9tZWZlZWRjZW50ZXJfcG9wdXAgLnNlbGVjdF90YWIgZGl2IHAgew0K'+
        'CWZsb2F0OiBsZWZ0Ow0KCWN1cnNvcjogcG9pbnRlcjsNCgl3aWR0aDogODYlOw0KCW92ZXJmbG93OiBoaWRkZW47DQp9DQojaG9t'+
        'ZWZlZWRjZW50ZXJfcG9wdXAgLmNvbmZpZ190YWIgew0KCWZsb2F0OiBsZWZ0Ow0KCXBhZGRpbmc6IDBweCAxMHB4Ow0KCXRleHQt'+
        'YWxpZ246IGxlZnQ7DQoJd2lkdGg6IDQyMHB4Ow0KfQ0KI2hvbWVmZWVkY2VudGVyX3BvcHVwIC5zYXZlX2NvbmZpZyB7DQoJY2xl'+
        'YXI6IGJvdGg7DQoJcGFkZGluZy10b3A6IDE2cHg7DQp9DQojaG9tZWZlZWRjZW50ZXJfcG9wdXAgI2ZlZWRsaXN0X2JvZHksIA0K'+
        'I2hvbWVmZWVkY2VudGVyX3BvcHVwICNhdXRvbW9kZV9ib2R5ICB7DQoJdGV4dC1hbGlnbjogbGVmdDsNCn0NCiNob21lZmVlZGNl'+
        'bnRlcl9wb3B1cCAjcGFuZWxfY29udGFpbmVyIHsNCgliYWNrZ3JvdW5kOiAjMTExIHVybCgke2Jhc2VfdXJsfXptYy90YWJzX2Jn'+
        'XzF4NDVfMDEuZ2lmKSByZXBlYXQteCAwcHggMTAwJTsNCiAgICB3aWR0aDogMTAwJTsNCiAgICBwb3NpdGlvbjogYWJzb2x1dGU7'+
        'DQogICAgei1pbmRleDogMTA7DQp9DQojaG9tZWZlZWRjZW50ZXJfcG9wdXAgI3BhbmVsX2NvbnRhaW5lciAuc2VhcmNoX2JveCB7'+
        'DQoJZmxvYXQ6IGxlZnQ7DQoJbWFyZ2luOiAwcHggNXB4Ow0KCXRleHQtYWxpZ246IGxlZnQ7DQoJd2lkdGg6IDM3MHB4Ow0KfQ0K'+
        'I2hvbWVmZWVkY2VudGVyX3BvcHVwICNwYW5lbF9jb250YWluZXIgLmNvbnRyb2xfYm94IHsNCglmbG9hdDogcmlnaHQ7DQoJbWFy'+
        'Z2luOiAwcHggNXB4Ow0KCXRleHQtYWxpZ246IGNlbnRlcjsNCgl3aWR0aDogMzQwcHg7DQp9DQojaG9tZWZlZWRjZW50ZXJfcG9w'+
        'dXAgI3N0YXR1c19wYW5lbCwgDQojaG9tZWZlZWRjZW50ZXJfcG9wdXAgI2NvbnRyb2xfcGFuZWwgIHsNCgloZWlnaHQ6IDM1cHg7'+
        'DQogICAgd2lkdGg6IDEwMCU7DQp9DQojaG9tZWZlZWRjZW50ZXJfcG9wdXAgI2NvbmZpZ19wYW5lbCB7DQoJaGVpZ2h0OiAyNTBw'+
        'eDsNCgltYXJnaW46IDVweDsNCiAgICB3aWR0aDogMTAwJTsNCn0NCiNob21lZmVlZGNlbnRlcl9wb3B1cCAjbWluaXBvcHVwX292'+
        'ZXJsYXkgew0KICAgIGRpc3BsYXk6IG5vbmU7DQogICAgd2lkdGg6IDEwMCU7DQogICAgaGVpZ2h0OiA3NTBweDsNCiAgICBwb3Np'+
        'dGlvbjogYWJzb2x1dGU7DQogICAgYmFja2dyb3VuZC1jb2xvcjogYmxhY2s7DQogICAgb3BhY2l0eTogMC41Ow0KICAgIHotaW5k'+
        'ZXg6IDU7DQp9DQojaG9tZWZlZWRjZW50ZXJfcG9wdXAgI21pbmlwb3B1cF9jb250ZW50IGNlbnRlciB7DQogICAgaGVpZ2h0OiAz'+
        'MHB4Ow0KICAgIGJvcmRlci1ib3R0b206IDFweCBkb3R0ZWQgIzMzMzsNCiAgICBtYXJnaW4tYm90dG9tOiAxNXB4Ow0KICAgIGZv'+
        'bnQtc2l6ZTogMThweDsNCiAgICBmb250LXdlaWdodDogYm9sZDsNCiAgICBjb2xvcjogIzk4OTg5ODsNCn0NCiNob21lZmVlZGNl'+
        'bnRlcl9wb3B1cCAjbWluaXBvcHVwX2NvbnRlbnQgLmZiX2ZyaWVuZGxpc3Qgew0KICAgIGhlaWdodDogMjAwcHg7DQogICAgb3Zl'+
        'cmZsb3c6IGF1dG87DQogICAgbWFyZ2luOiAwcHggMTBweCA1cHg7DQogICAgYm9yZGVyOiAycHggc29saWQgIzQ0NDsNCiAgICBw'+
        'YWRkaW5nOiA1cHg7DQp9DQojaG9tZWZlZWRjZW50ZXJfcG9wdXAgLndhcmxvb3RfYnV0dG9ucyB7DQogICAgdGV4dC1hbGlnbjog'+
        'cmlnaHQ7DQp9DQojaG9tZWZlZWRjZW50ZXJfcG9wdXAgLndhcmxvb3RfYnV0dG9ucyBhIHsNCiAgICBtYXJnaW4tbGVmdDogNXB4'+
        'Ow0KfQ0KI2hvbWVmZWVkY2VudGVyX3BvcHVwIC51aS1maWx0ZXJsaXN0IGRpdi51aS10b3Agew0KICAgIGNsZWFyOiBib3RoOw0K'+
        'ICAgIG1hcmdpbi1ib3R0b206IDNweDsNCiAgICBsaW5lLWhlaWdodDogMjBweDsNCn0NCiNob21lZmVlZGNlbnRlcl9wb3B1cCAu'+
        'dWktZmlsdGVybGlzdCBkaXYudWktdG9wIHNwYW4uY2hlY2tib3ggew0KICAgIGxpbmUtaGVpZ2h0OiAyNHB4Ow0KfQ0KI2hvbWVm'+
        'ZWVkY2VudGVyX3BvcHVwIC51aS1maWx0ZXJsaXN0ID4gc2VsZWN0IHsNCiAgICB3aWR0aDogMTAwJTsNCiAgICBoZWlnaHQ6IDE1'+
        'MHB4Ow0KfQ0K', 
        {base_url: global.zGraphicsURL}
    );
    
    // make sure we have permissions and Initialize
    facebook.needAppPermission('read_stream,offline_access', function(success) {
        if (success === true) {
            options.load(Initialize);
        } else {
            popupElt.destroy();
        }
    });
}
// ==Script==
// @id        InventoryAnalyzer.js
// @author    Dakam
// @requires  MWAddon.js
// ==Script==
/**
 * Analize all items from inventory.
 * @param {Boolean} forceFreshInventory
 */
function InventoryAnalyzer(forceFreshInventory) {
    var Snapshots = new Config('LootQuantities',{});
    var Increments = new Object();
    var Groups = ['Weapons', 'Armor', 'Vehicles', 'Animals', 'Henchmen'];
    var Indexes = {'1':0, '2':1, '3':2, '8': 3, '13': 4};
    var Wishlist, Templates = {
        list: Base64.decode(
            'PGxpIGlkeD0iJHtpbmRleH0iPg0KICAgIDxkaXYgY2xhc3M9InF1YWxpdHkiPg0KICAgICAgICA8aW1nIHRpdGxlPSIke2FjdGl2'+
            'ZV90aXRsZX0iIHN0eWxlPSJtYXJnaW4tdG9wOiAxcHg7IiBzcmM9IiR7YWN0aXZlX3VybH0iPg0KICAgIDwvZGl2Pg0KICAgIDxk'+
            'aXYgY2xhc3M9InF1YWxpdHkiPiR7cXVhbGl0eV9pY29ufTwvZGl2Pg0KICAgIDxkaXYgY2xhc3M9InNwYWNlIj48L2Rpdj4NCiAg'+
            'ICA8ZGl2IGNsYXNzPSJpdGVtX25hbWUiPiR7aXRlbV9uYW1lfTwvZGl2Pg0KICAgIDxkaXYgY2xhc3M9InNwYWNlIj48L2Rpdj4N'+
            'CiAgICA8ZGl2IGNsYXNzPSJudW1iZXIgcXVhbnRpdHkiPiR7cXVhbnRpdHl9PC9kaXY+DQogICAgPGRpdiBjbGFzcz0ic3BhY2Ui'+
            'PjwvZGl2Pg0KICAgIDxkaXYgY2xhc3M9Im51bWJlciI+JHt2MX08L2Rpdj4NCiAgICA8ZGl2IGNsYXNzPSJudW1iZXIiPiR7djJ9'+
            'PC9kaXY+DQogICAgPGRpdiBjbGFzcz0ibnVtYmVyIj4ke3YzfTwvZGl2Pg0KICAgIDxkaXYgY2xhc3M9InNwYWNlIj48L2Rpdj4N'+
            'CiAgICA8ZGl2IGNsYXNzPSJudW1iZXIiPiR7djR9PC9kaXY+DQogICAgPGRpdiBjbGFzcz0ibnVtYmVyIj4ke3Y1fTwvZGl2Pg0K'+
            'ICAgIDxkaXYgY2xhc3M9Im51bWJlciI+JHt2Nn08L2Rpdj4NCiAgICA8ZGl2IGNsYXNzPSJzcGFjZSI+PC9kaXY+DQogICAgPGRp'+
            'diBjbGFzcz0idG90YWwiPiR7djd9PC9kaXY+DQogICAgPGRpdiBjbGFzcz0idG90YWwiPiR7djh9PC9kaXY+DQogICAgPGRpdiBj'+
            'bGFzcz0idG90YWwiPiR7djl9PC9kaXY+DQo8L2xpPg=='
        ),
        table: Base64.decode(
            'PGRpdiBjbGFzcz0iaGVhZGVyIj4NCiAgICA8ZGl2IGNsYXNzPSJxdWFsaXR5Ij4ke2FjdGl2ZX08L2Rpdj4NCiAgICA8ZGl2IGNs'+
            'YXNzPSJzcGFjZSI+PC9kaXY+DQogICAgPGRpdiBjbGFzcz0icXVhbGl0eSI+JHtxdWFsaXR5fTwvZGl2Pg0KICAgIDxkaXYgY2xh'+
            'c3M9InNwYWNlIj48L2Rpdj4NCiAgICA8ZGl2IGNsYXNzPSJpdGVtX25hbWUiPiR7aXRlbV9uYW1lfTwvZGl2Pg0KICAgIDxkaXYg'+
            'Y2xhc3M9InNwYWNlIj48L2Rpdj4NCiAgICA8ZGl2IGNsYXNzPSJudW1iZXIgcXVhbnRpdHkiIHRpdGxlPSIke3NxfSI+JHtscX08'+
            'L2Rpdj4NCiAgICA8ZGl2IGNsYXNzPSJzcGFjZSI+PC9kaXY+DQogICAgPGRpdiBjbGFzcz0ibnVtYmVyIiB0aXRsZT0iJHtzMX0i'+
            'PiR7bDF9PC9kaXY+DQogICAgPGRpdiBjbGFzcz0ibnVtYmVyIiB0aXRsZT0iJHtzMn0iPiR7bDJ9PC9kaXY+DQogICAgPGRpdiBj'+
            'bGFzcz0ibnVtYmVyIiB0aXRsZT0iJHtzM30iPiR7bDN9PC9kaXY+DQogICAgPGRpdiBjbGFzcz0ic3BhY2UiPjwvZGl2Pg0KICAg'+
            'IDxkaXYgY2xhc3M9Im51bWJlciIgdGl0bGU9IiR7czR9Ij4ke2w0fTwvZGl2Pg0KICAgIDxkaXYgY2xhc3M9Im51bWJlciIgdGl0'+
            'bGU9IiR7czV9Ij4ke2w1fTwvZGl2Pg0KICAgIDxkaXYgY2xhc3M9Im51bWJlciIgdGl0bGU9IiR7czZ9Ij4ke2w2fTwvZGl2Pg0K'+
            'ICAgIDxkaXYgY2xhc3M9InNwYWNlIj48L2Rpdj4NCiAgICA8ZGl2IGNsYXNzPSJ0b3RhbCIgdGl0bGU9IiR7czd9Ij4ke2w3fTwv'+
            'ZGl2Pg0KICAgIDxkaXYgY2xhc3M9InRvdGFsIiB0aXRsZT0iJHtzOH0iPiR7bDh9PC9kaXY+DQogICAgPGRpdiBjbGFzcz0idG90'+
            'YWwiIHRpdGxlPSIke3M5fSI+JHtsOX08L2Rpdj4NCjwvZGl2Pg0KPHVsIGNsYXNzPSJpdGVtX2xpc3QiPiR7bGlzdH08L3VsPg=='
        )
    };
    var Filter = function() {
        return {
            'top_offense'   : new Array(),
            'top_defense'   : new Array(),
            'top_combined'  : new Array(),
            'weak_offense'  : new Array(),
            'weak_defense'  : new Array(),
            'weak_combined' : new Array(),
            'get_offense'   : new Array(),
            'get_defense'   : new Array(),
            'get_combined'  : new Array(),
            'give_away'     : new Array()
        };
    };
    var Inventory = {
        all        : new Array(),
        Weapons    : Filter(),
        Armor      : Filter(),
        Vehicles   : Filter(),
        Animals    : Filter(),
        Henchmen   : Filter(),
        Qualities  : null,
        ItemTypes  : null,
        Locations  : null,
        global     : {
            'Weapons_offense'    : 0,
            'Weapons_defense'    : 0,
            'Weapons_combined'   : 0,
            'Armor_offense'      : 0,
            'Armor_defense'      : 0,
            'Armor_combined'     : 0,
            'Vehicles_offense'   : 0,
            'Vehicles_defense'   : 0,
            'Vehicles_combined'  : 0,
            'Animals_offense'    : 0,
            'Animals_defense'    : 0,
            'Animals_combined'   : 0,
            'Henchmen_offense'   : 0,
            'Henchmen_defense'   : 0,
            'Henchmen_combined'  : 0,
            'Total_offense'      : 0,
            'Total_defense'      : 0,
            'Total_combined'     : 0,
            'Qualities'          : {
                'Total': 0,
                '1': 0,
                '2': 0,
                '3': 0,
                '4': 0,
                '5': 0
            }
        },
        get: function(type, subtype) {
            return Inventory[type][subtype];
        }
    };

    var popupElt = new PopupObject('ianalyzer_popup', {
        type: 'main',
        title: 'INVENTORY ANALYZER',
        onclose: function() {
            delete Inventory;
        }
    });

    var Events = {
        refresh_click: function() {
            popupElt.close();
            InventoryAnalyzer(true);
            return false;
        },
        takeSnapshot_click: function() {
            var snapshot;
            if (!Snapshots.base) { 
                Snapshots.add('base', (snapshot=new Object()));   
            } else {
                snapshot = Snapshots.base;
            } 
            Util.each(Inventory.all, function(id, loot) {
                snapshot[id] = loot.quantity;
            });
            Snapshots.save(function() {
                showHelpPopup({
                    icon: 'info',
                    title: 'Inventory Analyzer says:',
                    message: 'Snapshot taken successfully.'
                });
            });
            genListDom();
            return false;
        },
        filter_click: function() {
            genListDom();
            return false;
        },
        tab_click: function() {
            $('.cs_tabs', popupElt.content).removeClass('activeTab').addClass('inactiveTab');
            $(this).removeClass('inactiveTab').addClass('activeTab');
            genListDom();
            return false;
        },
        toMGSend_click: function () {
            var link = Util.uSplit(c$(this).attr('gift-link'));
            if ( link.gift_category && link.gift_id ) {
                popupElt.close();
                setTimeout(function() {
                    MultiGifter(link.gift_category, link.gift_id, InventoryAnalyzer);
                }, 1000);
            }
        },
        toMGFav_click: function() {
            addAllToMultiGifter();
            return false;
        },
        remWishlist_click: function() {
            var id = $(this).attr('wid');
            var url = 'remote/' + MW.getIntURL('wishlist', 'remove');

            httpAjaxRequest({'url':url+'&isajax=1&retwl=1&wid='+id, 'success':setWishlist});

            return false;
        },
        addWishlist_click: function() {
            var id = $(this).attr('itemid');
            for(var i = 0; i < Wishlist.length; i++) {
                if(parseInt(id) === parseInt(Wishlist[i].id)) {
                    return false;
                }
            }
            var sText = $(this).text();
            var url = 'remote/' + MW.getIntURL('wishlist', 'add');

            $(this).replaceWith(c$('span').text(sText));
            httpAjaxRequest({'url':url+'&isajax=1&retwl=1&gift_category=1&gift_id='+id, 'success':setWishlist});

            return false;
        },
        item_click: function() {
            var type    = $('#type_filter option:selected', popupElt.content).val();
            var subtype = $('#show_filter option:selected', popupElt.content).val();
            $('.item_list li.selected', popupElt.content).toggleClass('selected', false);
            $(this).toggleClass('selected', true);
            
            genSelectedItemDom(Inventory.get(type,subtype)[$(this).attr('idx')]);
        },
        genTable_click: function() {
            var sOutput = 'data:text/html;base64,';
            sOutput += Base64.encode(genHTMLTable());
            unsafeWindow.open(sOutput, '_black');
        }
    };

    var sortBy = {
        attack   : function(a, b) { return b.attack - a.attack; },
        defense  : function(a, b) { return b.defense - a.defense; },
        combined : function(a, b) { return (b.attack+b.defense) - (a.attack+a.defense); },
        quantity : function(a, b) { return b.quantity - a.quantity; }
    };

    function addAllToMultiGifter() {
        var type = $('#type_filter option:selected', popupElt.content).val();
        var subtype = 'give_away';
        
        showAskPopup('Adding to Multi Gifter',
        'Do you want to add all unnecessary '+type+' to<br>MultiGifter Favorites ?',
        function() { setTimeout(addtoFav, 1000); });
        
        function addtoFav() {
            var options = UserConfig.mgopt;

            options.load(function() {
                var itemId, link, giftFav = options.giftFav;
                Util.each(Inventory.get(type,subtype), function(id, item) {
                    if ( item.tradeable !== 1 || item.quantity < 1  ) {
                        return;
                    }
                    link = Util.uSplit(item.gift_link);
                    if ( link.gift_category && link.gift_id ) {
                        itemId = link.gift_category +'_'+ link.gift_id;
                        if ( giftFav.indexOf(itemId) === -1 ) {
                            giftFav.push(itemId);
                        }
                    }
                });
                options.save();
                showAskPopup('Adding to Multi Gifter',
                'All '+type+' were added.<br>Do you like to open MultiGifter now ?',
                function() {
                    popupElt.close();
                    setTimeout(MultiGifter, 1000);
                });
            });
        }
    }

    function genHTMLTable() {
        var sOutput = '<html><head></head><body>';
        var $lst = $('.item_list', popupElt.content).clone();
        var $hdr = $('.header', popupElt.content).clone();

        $lst.find('.space').remove();
        $hdr.find('.space').remove();

        $lst.find('img').replaceWith(function() {
            var rgx;
            var title = $(this).attr('title');
            var url = $(this).attr('src');
            var span = '<span title="' + title + '">';

            if (/active-icon/.test(url)) {
                return span + 'A</span>';
            }
            if (/inactive-icon/.test(url)) {
                return span + 'N</span>';
            }
            if ((rgx = /Quality:\s*(\w)\w+/.exec(title))) {
                return span + rgx[1] + '</span>';
            }
            return '';
        });

        $lst = $lst.html();
        $hdr = $hdr.html();

        $lst = $lst.replace(/<li[^>]*>/g, '<tr>').replace(/<\/li>/g, '</tr>');
        $lst = $lst.replace(/<div[^>]*>/g, '<td>').replace(/<\/div>/g, '</td>');
        $hdr = $hdr.replace(/<div[^>]*>/g, '<th>').replace(/<\/div>/g, '</th>');
        
        sOutput += '<table cellspacing="0" cellpadding="4" border="1" style="text-align:center;"><tbody>';
        sOutput += '<tr>' + $hdr + '</tr>' + $lst;
        return sOutput + '</tbody></table></body></html>';
    }

    function setWishlist(jsonData) {
        try {
            var data = Util.parseJSON(jsonData.data);
            if (data && data.wldata) {
                Wishlist = data.wldata;
                updateWishlist();
            }
            else {
                console.log(data.wlmsg);
                showHelpPopup({
                    icon: 'error',
                    message: 'Error adding item to wishlist'
                });
            }
        }
        catch(err) {
            Logger.error(err);
        }
    }

    function updateWishlist() {
        var sRemImgSrc = global.zGraphicsURL + 'inventory/ItemCard/icons/Inventory-wishlist2-icon.png';
        var $wl = $('#wishlist').empty();

        Util.each(Wishlist, function(index, item) {
            var $span = c$('div', 'class:attack_defense');
            $span.append(c$('span','class:attack').html(item.attack||0));
            $span.append(c$('span','class:defense').html(item.defense||0));
            
            c$('div', 'class:wl_item,title:'+item.name)
            .append(c$('img', 'class:remove,wid:'+index).attr('src', sRemImgSrc).click(Events.remWishlist_click))
            .append(c$('img').attr('src', item.image).width(50).height(50))
            .append($span).appendTo($wl);
        });
    }

    function genListDom() {
        var sName = $('#type_filter option:selected', popupElt.content).val();
        var sFilter = $('#show_filter option:selected', popupElt.content).val();
        var nLoc = parseInt($('#loc_filter option:selected', popupElt.content).val());
        var bMod = $('#snapshow_filter').hasClass('checked');
        var bNeed = /get/.test(sFilter);
        var html = {
            active     : 'A',
            quality    : 'Q',
            item_name  : 'Item Name',
            list       : '',
            sq: (bNeed ? 'Items needed' : 'Item quantity'),
            lq: 'QT',
            s1: (bNeed ? 'Attack gained if equipped' : 'Items equipped in attack'),
            s2: (bNeed ? 'Defense gained if equipped' : 'Items equipped in defense'),
            s3: (bNeed ? 'Combined gained if equipped' : 'Items equipped in tournaments'),
            s4: 'Attack',
            s5: 'Defense',
            s6: 'Combined',
            s7: (bNeed ? 'Attack gained if total equipped' : 'Total attack from this item'),
            s8: (bNeed ? 'Defense gained if total equipped' : 'Total defense from this item'),
            s9: (bNeed ? 'Combined gained if total equipped' : 'Total combined from this item'),
            l1: (bNeed ? '+A' : 'EA'),
            l2: (bNeed ? '+D' : 'ED'),
            l3: (bNeed ? '+C' : 'ET'),
            l4: 'AT',
            l5: 'DF',
            l6: 'CB',
            l7: (bNeed ? 'GA' : 'TA'),
            l8: (bNeed ? 'GD' : 'TD'),
            l9: (bNeed ? 'GC' : 'TC')
        };
        // List of items
        Util.each(Inventory[sName][sFilter], function(index, item) {
            var increment = Increments[item.id],
                quantity = Util.isSet(increment) ? ('<span style="color:'
                + (increment > 0 ? 'green' : 'red') + ';" title="You ' 
                + (increment > 0 ? 'won ' : 'lost ') + increment
                + ' from your last snapshot.">') : '<span>';
                
            if ((nLoc > 0 && nLoc !== item.location) || (bMod && !increment)) {
                return true;
            }
            if (bNeed) {
                quantity += item['need'+sFilter.substr(sFilter.lastIndexOf('_'))];
            } else {
                quantity += item.quantity;
            }
            html.list += Util.render(Templates.list, {
                index         : index,
                active_title  : (item.active?'Active item - Your Mafia is using this item in fights/robs.': 'Inactive item - Your Mafia is not using this item in fights/robs.'),
                active_url    : global.zGraphicsURL + 'inventory/ItemCard/icons/' + (item.active?'Inventory-active-icon.png':'Inventory-inactive-icon.png'),
                quality_icon  : (Inventory.Qualities[String(item.quality)] || {}).image,
                item_name     : item.name,
                quantity      : quantity + '</span>',
                v1: (bNeed ? item.gain_offense : item.equipped_offense),
                v2: (bNeed ? item.gain_defense : item.equipped_defense),
                v3: (bNeed ? item.gain_combined : item.equipped_combined),
                v4: item.attack,
                v5: item.defense,
                v6: item.combined,
                v7: (bNeed ? item.gained_offense : item.attack * item.quantity),
                v8: (bNeed ? item.gained_defense : item.defense * item.quantity),
                v9: (bNeed ? item.gained_combined : item.combined * item.quantity)
            });
        });
        if (html.list.length < 1) {
            html.list = '<div style="margin-top:25px;">No items match your criteria.</div>';
        }
        $('#info_table').html(Util.render(Templates.table,html)).find('li').click(Events.item_click);
    }

    function genSelectedItemDom(item) {
        var $iteminfo = $('#item_info', popupElt.content).empty();
        if (!Util.isSet(item)) {
            c$('div').text('Select an item to show extended info.').appendTo($iteminfo);
            return;
        }
        var $info = c$('div', 'class:info');
        var iLocation = Inventory.Locations[item.location];
        var sSearchUrl = 'http://mafiawars.wikia.com/index.php?title=Special:Search&search='
        +   ((Util.isString(item.name)&&item.name.length>0)?item.name.replace(/\s+?/g,'+'):'');
        var types = (function() {
            var tp = new Array();
            if (item.subtypes && item.subtypes.length) {
                Util.each(item.subtypes, function(i, index) {
                    var subtype = Inventory.Subtypes[String(index)];
                    if(Util.isSet(subtype)) {
                        tp.push(subtype.name);
                    }
                });
            }
            if (tp.length > 0) {
                return tp.join(', ');
            } else {
                return;
            }
        })();
        $iteminfo.append(c$('div','class:imagen').html(item.image)).append($info);

        if (types) {
            c$('div').css({'float':'left','margin-right':5})
            .html('Item type: '+ types + ' ').appendTo($info);
        }
        if (item.tradeable === 1) {
            c$('div').click(Events.toMGSend_click).css('cursor','pointer')
            .attr({'title':'Open with MultiGifter.','gift-link':item.gift_link})
            .append(c$('img').attr('src',global.zGraphicsURL+'inventory/ItemCard/icons/Inventory-gift-icon.png'))
            .appendTo(c$('div').css({'float':'right','margin-right':5}).appendTo($info));
        }
        if (item.favor_id > 0 && parseInt(item.rp_price) > 0) {
            c$('div').appendTo($info).css({'float':'left','color':'green'}).html('('+item.rp_price + ' RP)');
        }
        else if (item.pawn_shop > 0 && parseInt(item.lp_price) > 0) {
            c$('div').appendTo($info).css({'float':'left','color':'green'}).html('('+item.lp_price + ' LP)');
        }
        else if (item.purchasable && parseInt(item.cash_price) > 0) {
            c$('div').appendTo($info).css({'float':'left','color':'green'}).html('(buy: '+item.cash_price+')');
        }

        c$('div').css('clear', 'both').html('You can search it at ').appendTo($info)
        .append(c$('a', 'target:_black').attr('href', sSearchUrl).text('wikia'));

        if (item.tradeable === 1) {
            c$('div').html('This item can be added to ').appendTo($info)
            .append(c$('a', 'href:#,itemid:'+item.id).text('wishlist')
            .click(Events.addWishlist_click));
        }

        if (iLocation && iLocation.link && iLocation.name) {
            c$('div').html('You can get this item in ').appendTo($info)
            .append(c$('a', 'target:_black').attr('href', iLocation.link).text(iLocation.name));
        }
    }

    function genMainDom() {
        var $snap = b$('', 'class:short white button_icon');
        var $tbl = b$('', 'class:short white button_icon');
        var $snd = b$('', 'class:short white sexy_send_gift_new button_icon');
        var $rfh = b$('', 'class:short white sexy_send_gift_new button_icon');
        
        $snap.find('span:last').replaceWith(Resources.getPicture('snapshot_icon','div'));
        $snap.attr('title','Take a new snapshot of your current inventory loot amounts.');
        $tbl.find('span:last').replaceWith(Resources.getPicture('createtable_icon','div'));
        $tbl.attr('title','Generate a html table to open in a new tab.');
        $snd.attr('title','Send unused loot to the MultiGifter favourites.');
        $rfh.find('span:last').replaceWith(Resources.getPicture('refresh_icon','div'));
        $rfh.attr('title','Refresh your inventory (use it to get a fresh copy of your inventory).');
        
        c$('div', 'class:frame_box').height(80).appendTo(popupElt.content)
        .append(c$('div', 'item_info').text('Select an item to show extended info.'))
        .append(c$('div', 'wishlist'));
        c$('div', 'class:frame_box middle_bar')
        .append(c$('select', 'id:type_filter').width(120).change(Events.filter_click))
        .append(c$('select', 'id:show_filter').width(160).change(Events.filter_click))
        .append(c$('select', 'id:loc_filter').width(160).change(Events.filter_click))
        .append(x$('snapshow_filter', 'Modified.').click(Events.filter_click).attr('title','Show only modified items from the last snapshot.'))
        .append($snd.click(Events.toMGFav_click)).append($tbl.click(Events.genTable_click))
        .append($snap.click(Events.takeSnapshot_click)).append($rfh.click(Events.refresh_click))
        .appendTo(popupElt.content);
        
        c$('div', 'info_table').appendTo(popupElt.content).css({
            'margin': '0px 2px 2px 2px',
            'padding': '0px 2px 2px 2px'
        });

        function buildStrengthBox(title, type) {
            var elem = c$('div', 'class:mafia_strength')
            .append(c$('div', 'class:total_title').append(c$('span').text(title)));

            Util.each(['Weapons', 'Armor', 'Vehicles', 'Animals', 'Henchmen', 'Total'], function(index, name) {
                var nQuantity = Inventory.global[name+'_'+type];
                var sClassFix = (type == 'offense') ? 'attack' : type;

                c$('div', 'class:mafia_strength_spacing').appendTo(elem)
                .append(c$('div', 'class:stat_name').text(name + ':'))
                .append(c$('span','class:text_positioning '+sClassFix).text(nQuantity));
            });
            return elem;
        }

        function buildQualitiesBox() {
            var cQualities = Inventory.global.Qualities;
            var elem = c$('div', 'class:mafia_strength qualities')
            .append(c$('div', 'class:total_title').append(c$('span').text('Qualities equipped')));

            $.each(Inventory.Qualities, function(name, cQuality) {
                var nQuantity = cQualities[name];
                c$('div', 'class:mafia_strength_spacing').appendTo(elem)
                .append(c$('div').css('float', 'left').html(cQuality.image).css('margin-left',5))
                .append(c$('div', 'class:stat_name').text(cQuality.name + ':'))
                .append(c$('span','class:text_positioning').text(nQuantity));
            });

            c$('div', 'class:mafia_strength_spacing').appendTo(elem)
            .append(c$('div', 'class:stat_name').text('Total:'))
            .append(c$('span','class:text_positioning').text(cQualities['Total']));

            return elem;
        }

        // global stats
        c$('div', 'class:frame_box').css({'padding-left':38,'height':210})
        .append(buildStrengthBox('Attack Strength', 'offense'))
        .append(buildStrengthBox('Defense Strength', 'defense'))
        .append(buildStrengthBox('Combined Strength', 'combined'))
        .append(buildQualitiesBox()).appendTo(popupElt.content);
        
        // apply options
        popupElt.applyOptions({
            'type_filter': {
                'Weapons'  : 'Weapons',
                'Armor'    : 'Armor',
                'Vehicles' : 'Vehicles',
                'Animals'  : 'Animals',
                'Henchmen' : 'Henchmen'
            },
            'show_filter': {
                'top_offense'         : 'Top offense',
                'top_defense'         : 'Top defense',
                'top_combined'        : 'Top combined',
                'weak_offense'        : 'Weak offense',
                'weak_defense'        : 'Weak defense',
                'weak_combined'       : 'Weak combined',
                'get_offense'         : 'Need for offense',
                'get_defense'         : 'Need for defense',
                'get_combined'        : 'Need for tournament',
                'give_away'           : 'To given away.'
            },
            'loc_filter': (function locs() {
                var result = {0:'All Locations'};
                Util.each(Inventory.Locations, function(id, loc) {
                    result[id] = loc.name;
                });
                return result;
            })()
        });
    }

    function Initialize(Items) {
        var cQualities = Inventory.global['Qualities'];
        var snapshot = Snapshots.base;
        if (!snapshot)   { 
            Snapshots.add('base', (snapshot=new Object())); 
        }
        // get all items
        Util.each(Items, function(id, obj) {
            var gItems = Inventory[Groups[Indexes[obj.type]]];
            var bValid = (obj.attack + obj.defense) > 0;
            var bAvailable = (obj.unique !== 1);
            var baseQuantity = snapshot[id];
            if (!Util.isSet(baseQuantity)) {
                snapshot[id] = 0;
            }
            if (obj.quantity != baseQuantity) {
                Increments[id] = obj.quantity - baseQuantity;
            }
            try {
                if (gItems) {
                    // add new stats
                    obj['equipped_combined'] = 0;
                    obj['need_offense']      = 0;
                    obj['need_defense']      = 0;
                    obj['need_combined']     = 0;
                    obj['gained_offense']    = 0;
                    obj['gained_defense']    = 0;
                    obj['gained_combined']   = 0;
                    obj['need_total']        = 0;
                    obj['gain_offense']      = 0;
                    obj['gain_defense']      = 0;
                    obj['gain_combined']     = 0;
                    obj['combined']          = obj['attack'] + obj['defense'];
                    obj['offense']           = obj['attack'];
                    obj['is_equipped']       = (obj['equipped_offense']+obj['equipped_defense'] > 0);
                    obj['equipped_quantity'] = Math.max(obj['equipped_offense'], obj['equipped_defense']);

                    // add equiped items
                    if (obj['is_equipped']) {
                        gItems['top_combined'].push(obj);
                        cQualities[obj.quality] += obj['equipped_quantity'];
                        cQualities['Total']     += obj['equipped_quantity'];
                        if(obj['equipped_offense'] > 0) {
                            gItems['top_offense'].push(obj);
                        }
                        if (obj['equipped_defense'] > 0) {
                            gItems['top_defense'].push(obj);
                        }
                    } else if(!obj['is_equipped'] && bValid && obj.quantity > 0) {
                        gItems['top_combined'].push(obj);
                        if(obj['attack'] > obj['defense']) {
                            gItems['weak_offense'].push(obj);
                        }
                        else {
                            gItems['weak_defense'].push(obj);
                        }
                    }
                    
                    // add needed items
                    if(bValid && bAvailable) {
                        gItems['get_combined'].push(obj);
                        gItems['get_offense'].push(obj);
                        gItems['get_defense'].push(obj);
                    }
                }
            }
            catch(err) {
                Logger.error(err);
            }
        });
        Snapshots.save();
        
        // calculate all
        Util.each(Groups, function(index, groupTag) {
            var gItems = Inventory[groupTag];
            gItems['top_offense'].sort(sortBy.attack);
            gItems['top_defense'].sort(sortBy.defense);
            gItems['top_combined'].sort(sortBy.combined);
            gItems['weak_offense'].sort(sortBy.attack);
            gItems['weak_defense'].sort(sortBy.defense);

            // calculate combined
            var top_combined = [];
            var nMaxCombined = 501;

            Util.each(gItems['top_combined'], function(index, item) {
                if (nMaxCombined > 0) {
                    top_combined.push(item);
                    nMaxCombined -= item['quantity'];
                    item['equipped_combined'] = item['quantity'];
                    if (nMaxCombined < 0) {
                        item['equipped_combined'] += nMaxCombined;
                    }
                }
                else {
                    gItems['weak_combined'].push(item);
                }
            });
             gItems['weak_combined'].sort(sortBy.combined);
            (gItems['top_combined'] = top_combined).sort(sortBy.combined);

            // calculate need, gain, total, etc..
            var getList = {'get_offense':[], 'get_defense':[], 'get_combined':[]};

            Util.eac