您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
utils for modifying the DOM and fetching info of a webpage of camamba
当前为
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.greasyfork.icu/scripts/20132/144683/camamba-utils.js
window.onload = function () { var hmtlUtils = APP.htmlUtils; var camambaUtils = APP.camambaUtils; var btn = new camambaUtils.ButtonSmall(function() { console.log("Du hast mich gedrückt."); }, "myButton", ""); }; var APP = APP || {}; APP.camambaUtils = (function(){ var htmlUtils = APP.htmlUtils; /** * Wrapper for 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 * @constructor */ function ButtonSmall(callback, text, id) { return new htmlUtils.Button('smallbutton', callback, text, id); } /** * Wrapper for 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 * @constructor */ function ButtonTiny(callback, text, id) { if (!(this instanceof ButtonTiny)) { return new ButtonTiny(callback, text, id); } htmlUtils.Button.call(this, 'tinybutton', callback, text, id); domElement.style = 'margin:0;padding:0;width:auto;'; } /** * Wrapper for a 'Select' html element for selecting camamba users. * The options will be generated from 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 from the custom name if given instead of the camamba username * @constructor */ function SelectUsers(users, isShowGivenNames, callback, id) { if (!(this instanceof SelectUsers)) { return new SelectUsers(users, isShowGivenNames, callback, id); } htmlUtils.Select.call(this, "smallselect", callback, id); Object.defineProperties(this, { /** * list of users of type {{uid:User}} */ users: { get: function () { return users; }, set: function (value) { users = value; SelectUsers.prototype.updateOptions.call(this); }, enumerable: true, configurable: true }, /** * <code>true</code> shows the custom given names * <code>false</code> shows the camamba names */ isShowGivenNames: { get: function () { return isShowGivenNames; }, set: function (value) { var booleanValue = !!value; if (booleanValue !== keeper.get(_idx).isShowGivenNames) { isShowGivenNames = booleanValue; SelectUsers.prototype.updateOptions.call(this); } }, enumerable: true, configurable: true } }); SelectUsers.prototype.updateOptions.call(this); } SelectUsers.prototype = { constructor: SelectUsers, /** * Recreates the options from the current list of users. */ updateOptions: function() { var sortUsers = []; var remainingUsers = {}; for (var i = 0; i <= this.domElement.length - 1; i++) { var userInSelect = this.domElement[i].user; if (userInSelect) { if (!this.users[userInSelect.uid]) { // deleted users this.domElement.remove(i); } else { // remaining users remainingUsers[userInSelect.uid] = true; sortUsers.push({user: userInSelect, selected: this.domElement[i].selected}); } } } Object.keys(this.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 */ this.domElement.add(document.createElement('OPTION')); } }); this.domElement.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) { this.domElement[i].text = opt.user[optionTextProp]; this.domElement[i].value = opt.user.uid; this.domElement[i].user = opt.user; this.domElement[i].selected = opt.selected; }); // refresh select this.domElement.value = this.domElement.options[this.domElement.selectedIndex].value; }, /** * Returns the user of the selected option * @returns {User} The current selected user */ getSelectedUser: function() { return this.domElement.options[this.domElement.selectedIndex].user; } }; return { ButtonSmall : ButtonSmall } })(); if(false) { /** * 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 from 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 getField("lastSeen"); }, set: function (val) { setField("lastSeen", val); } }, roomId: { get: function () { return getField("roomId"); }, set: function (val) { setField("roomId", val); } }, roomName: { get: function () { return getField("roomName"); }, set: function (val) { setField("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') }; })(); /** * Represents a search using the page "/search.php". * @constructor */ var UserSearch = (function () { var doSearch = function (paramString, pageNr) { pageNr = pageNr || 0; var searchHref = '/search.php' + (paramString ? paramString + '&' : '?') + 'page=' + pageNr; var onlineUsers = {}; Page.getAsync(searchHref, function (resp) { var elTablesNormal = resp.html.getElementsByClassName('searchNormal'); var elTablesSuper = resp.html.getElementsByClassName('searchSuper'); var timeStampInMs = Date.now(); [elTablesNormal, elTablesSuper].forEach(function (userTables, index) { var uIsSuper = index === 1; [].forEach.call(userTables, function (table) { var user, uId, uName, uGender, uAge, uIsPremium, uIsReal, uLocation = {}, uOnline = {isOnlineNow: true, lastSeen: timeStampInMs}; var links = table.getElementsByTagName('a'); for (var i = 0; i <= links.length - 1; i++) { var link = links[i]; if (!uId) { var uidSearch = /javascript:sendMail\('(.+)'\)/g.exec(link.href); if (uidSearch && uidSearch.length >= 2) { uId = uidSearch[1]; uIsPremium = uIsReal = false; if (link.nextSibling && link.nextSibling.nextSibling && link.nextSibling.nextSibling.nextSibling) { var elPremiumReal = link.nextSibling.nextSibling.nextSibling; if (elPremiumReal) { if (elPremiumReal.getAttribute('href') === '/premium.php') { uIsPremium = true; if (elPremiumReal.nextSibling && elPremiumReal.nextSibling.nextSibling) { elPremiumReal = elPremiumReal.nextSibling.nextSibling; } } } if (elPremiumReal && elPremiumReal.getAttribute('src') === '/gfx/real.png') { uIsReal = true; } } } } if (!uName || !uGender) { var unameSearch = /javascript:openProfile\('(.+)'\)/g.exec(link.href); if (unameSearch && unameSearch.length >= 2) { uName = unameSearch[1]; if (link.nextSibling && link.nextSibling.nextSibling) { var genderAgeSearch = link.nextSibling.nextSibling.textContent; var uGenderSearch = /(male|female)/g.exec(genderAgeSearch); if (uGenderSearch && uGenderSearch.length >= 2) { uGender = uGenderSearch[1]; var uAgeTxt = genderAgeSearch.replace(/\D/g, ""); if (uAgeTxt) { uAge = parseInt(uAgeTxt); } } } } } if (!uOnline.roomId) { var onlineSearch = /javascript:openChat\('(.+)'\)/g.exec(link.href); if (onlineSearch && onlineSearch.length >= 2) { uOnline.roomId = onlineSearch[1]; uOnline.roomName = link.innerHTML; } } if (!uLocation.gps) { var locationSearch = /javascript:openMap\((.+)\)/g.exec(link.href); if (locationSearch && locationSearch.length >= 2) { uLocation = { gps: locationSearch[1], place: link.innerHTML }; var distanceTxt = link.nextSibling.textContent.replace(/\D/g, ""); if (distanceTxt) { uLocation.distanceInKm = parseInt(distanceTxt); } } } } if (uId) { user = new User(uId); if (uAge) { user.age = uAge; } if (uGender) { user.gender = uGender; } user.isPremium = uIsPremium; user.isReal = uIsReal; user.isSuper = uIsSuper; if (uLocation.distanceInKm !== undefined) { user.location.distanceInKm = uLocation.distanceInKm; } if (uLocation.gps) { user.location.gps = uLocation.gps; } if (uLocation.place) { user.location.place = uLocation.place; } if (uName) { user.uname = uName; } user.online.isOnlineNow = uOnline.isOnlineNow; user.online.lastSeen = uOnline.lastSeen; user.online.roomId = uOnline.roomId; user.online.roomName = uOnline.roomName; onlineUsers[uId] = user; } }); }); pageNr++; var links = resp.html.getElementsByTagName('a'); for (var i = links.length - 1; i >= 0; i--) { var linkHref = links[i].getAttribute('href'); if (linkHref && linkHref.toLowerCase().indexOf(searchHref) >= 0) { console.log('Link:', linkHref); setTimeout(function () { doSearch(paramString, pageNr); }, 750); break; } } }); return onlineUsers; }; /** @typedef {{MALE: string, FEMALE: string, ANY: string}} GendersType */ /** * @readonly * @enum {string} * @type {GendersType} */ Params.prototype.genders = {MALE: "male", FEMALE: "female", ANY: "any"}; Object.freeze(Params.prototype.genders); /** * Represents a search using the page "/search.php". * The parameters restrict the search ... * @param {string} [nick] - to users whose username to start with a given string * @param {GendersType} [gender=any] - to users with a given gender * @param {boolean} [isOnline=true] - to users currently online * @param {boolean} [isReal=false] - to users with a real tag * @param {boolean} [hasPicture=false] - to users with a picture uploaded to their profile * @param {boolean} [isPremium=false] - to users with premium * @param {boolean} [isSuper=false] - to users with super premium * @constructor */ var Constructor = function (nick, gender, isOnline, isReal, hasPicture, isPremium, isSuper) { gender = gender || Params.prototype.genders.ANY; isOnline |= (typeof isOnline === "undefined"); /** * parameter restricting the results of the search * @type {Params} */ this.queryObject = new Params(nick, gender, isOnline, hasPicture, isPremium, isReal, isSuper); /** * List with users resulting from the last executed search * @type {{}<string|number,User>[]} */ this.users = {}; }; Constructor.prototype = { constructor: Constructor, /** executes the search */ execute: function () { this.users = doSearch(this.queryObject.toString()); } }; return Constructor; })(); }