camamba-utils

utils for modifying the DOM and fetching info of a webpage of camamba

Tính đến 30-06-2016. Xem phiên bản mới nhất.

Script này sẽ không được không được cài đặt trực tiếp. Nó là một thư viện cho các script khác để bao gồm các chỉ thị meta // @require https://update.greasyfork.ip-ddns.com/scripts/20132/134452/camamba-utils.js

/**
 * Extension for HtmlFactory
 */
(function() {
    /**
     * Creates a 'button' Html Element.
     * The initial class attribute is <code>class='smallbutton'</code>.
     * @param {function} [callback] - The callback function for the <code>onClick<code> event
     * @param {string} [text] - The content text of the element (text shown on the button)
     * @param {string} [id] - The value for the <code>id</code> attribute
     * @returns {ElType} The 'button' HTML element
     */
    var createButtonSmall = function(callback, text, id) {
        return HtmlFactory.newButton('smallbutton', callback, text, id);
    };
    /**
     * Creates a 'button' Html Element.
     * The initial class attribute is <code>class='tinybutton'</code>.
     * @param {function} [callback] - The callback function for the <code>onClick<code> event
     * @param {string} [text] - The content text of the element (text shown on the button)
     * @param {string} [id] - The value for the <code>id</code> attribute
     * @returns {ElType} The 'button' HTML element
     */
    var createButtonTiny = function ButtonTiny(callback, text, id) {
        var elBtn = HtmlFactory.newButton('tinybutton', callback, text, id);
        elBtn.style = 'margin:0;padding:0;width:auto;';
        return elBtn;
    };
    /**
     * Creates a 'Select' HTML element for selecting camamba users.
     * The options will be generated by the list of users.
     * The initial class attribute is <code>class='smallselect'</code>.
     * @param {Object<string, User>[]} users - The list of users shown as options
     * @param {string} [id] - The value for the <code>id</code> attribute
     * @param {boolean} [isShowGivenNames] - <code>true</code> has options shown by the custom name if given instead of the camamba username
     * @returns {ElType} The 'select' HTML element
     */
    var createSelectUsers = function(users, id, isShowGivenNames) {
        var elSelect = HtmlFactory.newSelect('smallselect', id);
        var _users = users, _isShowGivenNames = isShowGivenNames;
        var _onchangeCallback;
        /**
         * Recreates the options by the current list of users.
         */
        var updateOptions = function() {
            var sortUsers = [];
            var remainingUsers = {};
            for (var i = 0; i <= elSelect.length - 1; i++) {
                var userInSelect = elSelect[i].user;
                if (userInSelect) {
                    if (!_users[userInSelect.uid]) { // deleted users
                        elSelect.remove(i);
                    } else { // remaining users
                        remainingUsers[userInSelect.uid] = true;
                        sortUsers.push({ user:userInSelect, selected:elSelect[i].selected });
                    }
                }
            }
            Object.keys(_users).forEach(function(uid){
                if (!remainingUsers[uid]) { // additional users
                    var user = users[uid];
                    sortUsers.push({ user:user, selected:false });
                    /**
                     * Html 'Option' Child of a Html 'Select' Element that holds a User
                     * @type {HTMLOptionElement}
                     * @property {User} user - The User related to the option
                     */
                    elSelect.add(document.createElement('OPTION'));
                }
            });
            elSelect.length = sortUsers.length;
            var optionTextProp = _isShowGivenNames ? "name" : "uname";
            sortUsers.sort(function (a, b) {
                var nameA = a.user[optionTextProp].toLowerCase();
                var nameB = b.user[optionTextProp].toLowerCase();
                if (nameA < nameB) { return  -1; }
                if (nameA > nameB) { return 1; }
                return 0;
            });
            sortUsers.forEach(function (opt, i) {
                elSelect[i].text = opt.user[optionTextProp];
                elSelect[i].value = opt.user.uid;
                elSelect[i].user = opt.user;
                elSelect[i].selected = opt.selected;
            });
            // refresh select
            elSelect.value = elSelect.options[elSelect.selectedIndex].value;
        };
        /**
         * Returns the user of the selected option
         * @returns {User} The current selected user
         */
        var getSelectedUser = function() {
            return elSelect.options[elSelect.selectedIndex].user;
        };
        /**
         * Sets a callback function triggered by the events <code>OnChange</code>, <code>OnKeyUp</code> and <code>OnFocus</code>.
         * Removes any former callback function registered to these events.
         * @param {function} callback
         */
        var setOnChangeKeyUpFocus = function(callback) {
            if (_onchangeCallback) {
                elSelect.removeEventListener("focus", _onchangeCallback);
                elSelect.removeEventListener("change", _onchangeCallback);
                elSelect.removeEventListener("keyup", _onchangeCallback);
            }
            if (typeof callback === 'function') {
                _onchangeCallback = callback;
                elSelect.addEventListener("focus", callback);
                elSelect.addEventListener("change", callback);
                elSelect.addEventListener("keyup", callback);
            }
        };
        Object.defineProperties(elSelect, {
            /**
             * list of users of type {{uid:User}}
             */
            users : {
                get : function() { return _users },
                set : function(value) {
                    _users = value;
                    updateOptions();
                }
            },
            /**
             * <code>true</code> shows the custom given names
             * <code>false</code> shows the camamba names
             */
            isShowGivenNames : {
                get : function() { return _isShowGivenNames },
                set : function(value) {
                    if (value != _isShowGivenNames) {
                        _isShowGivenNames = value;
                        updateOptions();
                    }
                }
            },
            refresh : { value : updateOptions },
            selectedUser : { value : getSelectedUser },
            setOnChangeKeyUpFocus : { value : setOnChangeKeyUpFocus }
        });
        updateOptions();
        return elSelect;
    };
    Object.defineProperties(HtmlFactory, {
        newButtonSmall : { value: createButtonSmall },
        newButtonTiny : { value: createButtonTiny },
        newSelectUsers : { value: createSelectUsers }
    });
})();

/**
 * Extension for Page
 */
(function() {
    var isGerman = window.location.hostname.indexOf("de.camamba.com") >= 0;
    var urlRoute = /^\/(.+?)(?:_de)?\.php.*/g.exec(location.pathname)[1];
    /**
     * Verifies an url, if it loads the German version of camamba.
     * @param {string} url The url for a camamba Page.
     * @returns {boolean} <code>true</code> if the url will request a camamba Page in German.
     */
    var urlIsGerman = function(url) {
        return (url.indexOf('www.de.camamba.com') >= 0);
    };
    /**
     * Transforms the url of a camamba Page wether to request that Page in English or German, depending the language of the current Page.
     * @param {string} uri - The url for a cammaba Page.
     * @param {Object.<string,string>[]} [queryParamsObj] - A key-value Object for additional query parameter to be attached to the url.
     * @returns {string} The localized url
     */
    var uriLocalized = function(uri, queryParamsObj) {
        var localizedUri = uri;
        if (isGerman && !urlIsGerman(uri)) {
            localizedUri = uri
                .replace("www.camamba.com", "www.de.camamba.com")
                .replace(".php", "_de.php");
        } else if (!isGerman && urlIsGerman(uri)) {
            localizedUri = uri
                .replace("www.de.camamba.com", "www.camamba.com")
                .replace("_de.php", "php");
        }
        var queryParams = '';
        if (queryParamsObj) {
            var hasParams = uri.indexOf('.php?') >= 1;
            Object.keys(queryParamsObj).forEach(function (key) {
                var sep = (hasParams ? '&' : '?');
                var value = queryParamsObj[key];
                queryParams += sep + key + '=' + value;
                hasParams = true;
            });
        }
        return localizedUri + queryParams;
    };
    Object.defineProperties(Page, {
        /**
         * Indicates the localization of the current camamba Page.
         */
        isGerman : { value: isGerman },
        /**
         * The current path in camamba according to the url.
         */
        route : { value: urlRoute },
        localizeUri : { value: uriLocalized }
    });
})();


/**
 * Represents a camamba user.
 * Intitially tries to load the User from the database by the given uid.
 * @constructor
 * @param {string|number} uid   Identifies the User with its camaba uid.
 */
function User(uid) {
    var _uid = parseInt(uid); // camamba user ID (readonly)
    var _key = 'uid' + _uid; // key for storing (readonly)
    var _hasChanged = true;
    var backingFields = {
        uname : "", name : "", note : "",
        age : Number.NaN, gender : "",
        isPremium : false, isReal : false, isSuper : false,
        location : {}, distanceInKm : Number.NaN, gps : "", place : "",
        online : {}, isOnlineNow : false, lastSeen : new Date("invalid"),
        room : {}, roomId : "", roomName : "",
        lastUpdated : Date.now(), lastChanged : Date.now()
    };
    var setField = function(fieldName, val) {
        _lastUpdated = Date.now();
        if (backingFields[fieldName] !== val) {
            backingFields[fieldName] = val;
            _lastChanged = _lastUpdated;
            _hasChanged = true;
        }
    };
    var getField = function(fieldName) { return backingFields[fieldName]; };

    Object.defineProperties(getField("location"), {
        distanceInKm : {
            get: function() { return getField('distanceInKm'); },
            set: function(val) { setField("distanceInKm", val); }
        },
        gps : {
            get: function() { return getField("gps"); },
            set: function(val) { setField("gps", val); }
        },
        place : {
            get: function() { return getField("place"); },
            set: function(val) { setField("place", val); }
        }
    });
    Object.defineProperties(getField("online"), {
        isOnlineNow : {
            get: function() { return getField("isOnlineNow"); },
            set: function(val) { setField("isOnlineNow", val); }
        },
        lastSeen : {
            get: function() { return setField("lastSeen"); },
            set: function(val) { getField("lastSeen", val); }
        },
        roomId: {
            get: function() { return setField("roomId"); },
            set: function(val) { getField("roomId", val); }
        },
        roomName: {
            get: function() { return setField("roomName"); },
            set: function(val) { getField("roomName", val); }
        }
    });

    /**
     * @name User#uid
     * @type number
     * @readonly
     */
    /**
     * @name User#key
     * @type String
     * @readonly
     */
    /**
     * @name User#hasChanged
     * @type Boolean
     * @readonly
     */
    /**
     * @name User#note
     * @type String
     */
    Object.defineProperties(this, {
        uid : { value : _uid, writable : false },
        key : { value : _key, writable : false },
        hasChanged : { get : function() { return _hasChanged; } },
        lastChanged : { get : function() { return getField("lastChanged"); } },
        lastUpdated : { get : function() { return getField("lastUpdated"); } },
        uname : {
            get : function() { return getField("uname"); },
            set : function(val) { setField("uname", val); }
        },
        name : {
            get : function() { return getField("name") || getField("uname") || _uid.toString(); },
            set : function(val) { setField("name", val); }
        },
        note : {
            get : function() { return getField("note"); },
            set : function(val) { setField("note", val); }
        },
        age : {
            get : function() { return getField("age"); },
            set : function(val) { setField("age", val); }
        },
        gender : {
            get: function() { return getField("gender"); },
            set: function(val) { setField("gender", val); }
        },
        isPremium : {
            get: function() { return getField("isPremium"); },
            set: function(val) { setField("isPremium", val); }
        },
        isReal : {
            get: function() { return getField("isReal"); },
            set: function(val) { setField("isReal", val); }
        },
        isSuper : {
            get: function() { return getField(isSuper); },
            set: function(val) { setField("isSuper", val); }
        },
        location : { get: function() { return getField("location"); } },
        online : { get: function() { return getField("online"); } }
    });

    /**
     * Saves or updates the user in the database of this script.
     * Overwrites an existing entry or creates a new entry if it doesn't alread exist.
     */
    this.save = function() {
        User.prototype.save.call(this);
        _hasChanged = false;
    };
    /**
     * Loads the User from the database of this script.
     * @returns {Boolean} <code>true</code> if the user was found and could be sucessfully loaded from db
     */
    this.load = function() {
        var isSuccess = User.prototype.load.call(this);
        if (isSuccess) {_hasChanged = false; }
        return isSuccess;
    };
    /**
     * Removes the User from the database of this script.
     */
    this.remove = function () {
        User.prototype.remove.call(this);
        _hasChanged = true;
    };
    this.load();
}
User.prototype = {
    constructor : User,
    save : function() {
        if (this.hasChanged) {
            GM_setValue("uid" + this.uid, JSON.stringify({
                uname : this.uname,
                name : this.name,
                note : this.note,
                age : this.age,
                gender : this.gender,
                isPremium : this.isPremium,
                isReal : this.isReal,
                isSuper : this.isSuper,
                location : this.location,
                online : this.online,
                lastChanged : this.lastChanged,
                lastUpdated : this.lastUpdated
            }));
        }
    },
    load : function() {
        var isScuccess = false;
        var loadedString = GM_getValue("uid" + this.uid);
        if (loadedString) {
            var loadedObj = JSON.parse(loadedString);
            var uname = loadedObj.uname;
            var name = loadedObj.name;
            var note = loadedObj.note;
            if (uname !== undefined && name !== undefined && note !== undefined){
                this.uname = uname;
                this.name = name;
                this.note = note;
                isScuccess = true;
            }
        }
        return isScuccess;
    },
    remove : function() {
        GM_deleteValue("uid" + this.uid);
    },
    /**
     * Gets all users stored from the database determined for this Script.
     * @returns {{}<string|number,User>[]} List with all Stored Users
     */
    loadAllUsers : function() {
        var users = {};
        var storedKeys = GM_listValues();
        for (var i = 0; i <= storedKeys.length - 1; i++) {
            var key = storedKeys[i].toString();
            if (key.indexOf('uid') === 0) {
                var uid = key.substr(3);
                users[uid] = new User(uid);
            }
        }
        return users;
    },
    /**
     * Has the browser open the profile Page of this user.
     * @param {boolean} [asNewTab=false]
     *      <code>true</code>, Page is opened in a new tab.
     *      <code>false</code>, replaces the current Page.
     */
    openProfilePage : function(asNewTab) {
        var profPageLocPath = location.protocol + '//www.camamba.com/profile_view.php';
        var queryParamsObj = { uid : this.uid, m : 'start' };
        var uri = Page.localizeUri(profPageLocPath, queryParamsObj);
        var target = asNewTab ? '_blank' : '_self';
        window.open(uri, target);
    }
};

/**
 * Represents the veewards
 * @type {{lol, drama, banHammer, cheese}}
 */
var veewards = (function() {
    function Vee(idx, name) {
        this.index = idx;
        this.name = name;
    }

    /**
     * This callback is displayed as part of the Requester class.
     * @callback Vee~sendCallback
     * @param {Date} timeSend - Time the veeward beeing send
     */

    /**
     * Tries to send a veeward.
     * @param {User} [user] - The user to whom the veeward may be send
     * @param {number} [coolDownPeriodSec] - Period in seconds for which no veeward will be send since last sent
     * @param {Vee~sendCallback} [callback] - A function to be called when the veeward got tried to be send.
     */
    Vee.prototype.send = function (user, coolDownPeriodSec, callback) {
        if (!(user && user instanceof User)) { user = new User(602175); }
        coolDownPeriodSec = parseInt(coolDownPeriodSec||0, 10);
        if (typeof callback !== 'function') {
            callback = function(now) {
                console.info(new Date(now * 1000), 'try to veeward ', user.name, ' with ', thisVee.name);
            };
        }

        var now = function() {
            var now = Date.now ? Date.now() : new Date().getTime();
            return Math.floor(now / 1000);
        }();
        var lastVeeTime = GM_getValue('autoVeeTimestamp', 0);
        if (lastVeeTime > now || lastVeeTime + coolDownPeriodSec < now) {
            var thisVee = this, vhttp;
            try {
                vhttp = new ActiveXObject('Msxml2.XMLHTTP');
            } catch (e) {
                try {
                    vhttp = new ActiveXObject('Microsoft.XMLHTTP');
                } catch (E) {
                    vhttp = false;
                }
            }
            if (!vhttp && typeof XMLHttpRequest != 'undefined') {
                try {
                    vhttp = new XMLHttpRequest();
                } catch (e) {
                    vhttp = false;
                }
            }
            vhttp.open('POST', '/extras/setdata.php', true);
            vhttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
            vhttp.onreadystatechange = function() {
                GM_setValue('autoVeeTimestamp', now);
                callback(now);
            };
            vhttp.send('sendvee=' + this.index + '&to=' + user.uid);
        }
    };
    return {
        lol: new Vee(1, 'LoL'),
        drama: new Vee(2, 'Drama'),
        banHammer: new Vee(3, 'Ban Hammer'),
        cheese: new Vee(4, 'Cheese')
    };
})();