camamba-utils

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

Version vom 03.06.2016. Aktuellste Version

Dieses Skript sollte nicht direkt installiert werden. Es handelt sich hier um eine Bibliothek für andere Skripte, welche über folgenden Befehl in den Metadaten eines Skriptes eingebunden wird // @require https://update.greasyfork.ip-ddns.com/scripts/20132/129445/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 (!_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 uriRoute = /^\/(.+?)(?:_de)?\.php.*/g.exec(location.pathname)[1];
    /**
     * Verifies an uri, if it loads the German version of camamba.
     * @param {string} uri The uri for a camamba Page.
     * @returns {boolean} <code>true</code> if the uri will request a camamba Page in German.
     */
    var uriIsGerman = function(uri) {
        return (uri.indexOf('www.de.camamba.com') >= 0);
    };
    /**
     * Transforms the uri of a camamba Page wether to request that Page in English or German, depending the language of the current Page.
     * @param {string} uri - The uri for a cammaba Page.
     * @param {Object.<string,string>[]} [queryParamsObj] - A key-value Object for additional query parameter to be attached to the uri.
     * @returns {string} The localized uri
     */
    var uriLocalized = function(uri, queryParamsObj) {
        var localizedUri = uri;
        if (isGerman && !uriIsGerman(uri)) {
            localizedUri = uri
                .replace("www.camamba.com", "www.de.camamba.com")
                .replace(".php", "_de.php");
        } else if (!isGerman && uriIsGerman(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 uri.
         */
        route : { value: uriRoute },
        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, 10); // camamba user ID (readonly)
    var _key = 'uid' + _uid, // key for storing (readonly)
        _uname = "", // camamba username
        _name = "", // custom username
        _note = "", // custom annotation
        _hasChanged = true; // is any value is unsaved (readonly)
    /**
     * @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; } },
        uname : {
            get : function() { return _uname; },
            set : function(val) {
                if (_uname != val) {
                    _uname = val;
                    _hasChanged = true;
                }
            }
        },
        name : {
            get : function() { return _name || _uname || _uid.toString(); },
            set : function(val) {
                if (_name != val) {
                    _name = val;
                    _hasChanged = true;
                }
            }
        },
        note : {
            get : function() { return _note; },
            set : function(val) {
                if (_note != val) {
                    _note = val;
                    _hasChanged = true;
                }
            }
        }
    });
    /**
     * 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
            }));
        }
    },
    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')
    };
})();