var chattableLoadingLoop; var chattable = { user: { flair: localStorage.flair ? localStorage.flair : null, name: null, }, loaded: false, commands : false, settings: { visible: true, theme: false, pendingMessages: [], // queue for storing messages that need to be sent when iframe is ready initialized: false }, reload() { let frame = document.getElementById('chattable'); frame.src = frame.src; }, returnFrame() { return document.getElementById('chattable'); }, // resolves when the iframe is ready frameReadyPromise: null, frameReadyResolve: null, initializeFramePromise() { if (!this.frameReadyPromise) { this.frameReadyPromise = new Promise(resolve => { this.frameReadyResolve = resolve; }); } return this.frameReadyPromise; }, async sendMessageToFrame(message) { await this.frameReadyPromise; // waiting for frame to be ready const frame = this.returnFrame(); if (frame && frame.contentWindow) { frame.contentWindow.postMessage(message, "*"); return true; } return false; }, async redraw() { if (window.chattableStylesheet) { await this.sendMessageToFrame(window.chattableStylesheet); } else { console.warn("Unable to redraw, no previous styles available"); } }, async loadStyle(source) { try { console.log("Loading CSS source file..."); const response = await fetch(source); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const css = await response.text(); window.chattableStylesheet = css; await this.sendMessageToFrame(css); } catch (error) { console.error("Error loading stylesheet:", error); return false; } }, async loadTheme(name) { var themes = { "hacker terminal": "/demo/hacker.css", "amber": "/demo/amber.css", "comment section": "/demo/comment.css", "confidential": "/demo/confidential.css", "notepad": "/demo/notepad.css", "pastel pink": "/demo/pastel.css", "moderno": "/demo/moderno.css", "retrowave red": "/demo/red.css", "windows xp": "/demo/xp.css", "wannabe xp": "/demo/xp.css", "winbows": "/demo/xp.css", "winbows xp": "/demo/xp.css", "kick": "/demo/kick.css", "tendo": "/demo/tendo.css", "amdroid" : "/demo/amdroid.css", "android" : "/demo/amdroid.css", "glass" : "/demo/glass.css", "professional": "/demo/dashboard-chat.css", "bytechat": "/demo/bytechat.css", }; const themeName = `load_theme:${themes[name]}`; window.chattableStylesheet = themeName; await this.sendMessageToFrame(themeName); }, reinitialize: function(parameters){ if (parameters) { if (parameters.stylesheet) { chattable.loadStyle(parameters.stylesheet); } else if (parameters.theme) { chattable.loadTheme(parameters.theme.toLowerCase()); } else { chattable.sendMessageToFrame("body {}"); } } if(this.commands){ let db = {}; for(command in this.commands){ db[command] = true; } this.sendMessageToFrame({ type: "storeCommands", commands: db }); } }, initialize(parameters) { const handlers = []; let completed = false; let result = false; console.log("Initializing..."); chattable.parameters = parameters; chattableLoadingLoop = setInterval(function(){ if(chattable.loaded){ clearInterval(chattableLoadingLoop); } else { if(chattable.returnFrame()){ if (parameters) { if (parameters.stylesheet) { chattable.loadStyle(parameters.stylesheet); } else if (parameters.theme) { chattable.loadTheme(parameters.theme.toLowerCase()); } else { chattable.sendMessageToFrame("body {}"); } } if(chattable.commands){ let db = {}; for(command in chattable.commands){ db[command] = true; } chattable.sendMessageToFrame({ type: "storeCommands", commands: db }); } result = true; let loop = setInterval(function(){ if(chattable.settings.initialized){ clearInterval(loop); if(!completed){ completed = true; handlers.forEach(function(item){ item(result); }); } } }, 250); if(localStorage.flair){ chattable.setFlair(localStorage.flair); } } } }, 500); return { then: function(callback){ if(result && chattable.settings.initialized){ callback(result); } else { handlers.push(callback); } } } }, minimize: function() { var ChatUI = chattable.returnFrame(); var defaultStyle = ChatUI.style.transition; chattable.settings.oldHeight = ChatUI.style.height ? ChatUI.style.height : ChatUI.offsetHeight; chattable.settings.oldStyle = ChatUI.style; ChatUI.style.transition = "all 500ms ease-out"; ChatUI.style.minHeight = "0"; ChatUI.style.height = "0"; setTimeout(function() { ChatUI.style.transition = defaultStyle; chattable.settings.visible = false; }, 500); }, maximize: function() { var ChatUI = chattable.returnFrame(); ChatUI.style.transition = "all 500ms ease-out"; ChatUI.style.height = parseInt(chattable.settings.oldHeight) + "px"; setTimeout(function() { ChatUI.style = chattable.settings.oldStyle; chattable.settings.visible = true; }, 500); }, changeRoom: function(chat_id) { var newURL = "https://iframe.chat/embed?chat=" + chat_id; chattable.returnFrame().src = newURL; }, setFlair : function (string) { chattable.sendMessageToFrame({ "type": "setFlair", "flair": string.toString() }); localStorage.flair = string; chattable.user.flair = string; }, sendMessage: function(text, name, flair, perms, obj) { if(text && typeof(text) == 'string'){ chattable.sendMessageToFrame({ "type": "sendMessage", "text": text, "name" : name ? name : undefined, "flair" : flair ? flair : undefined, "perms" : !!perms, "object" : obj ? obj : undefined }); } else { console.error("Failed to call chattable.sendMessage(), you must provide a string as a first parameter."); } }, request: function(reqObj){ chattable.sendMessageToFrame({ "type": "request", "reqObj": reqObj }); }, setName: function(newName) { let q = confirm("This site wants to change your chat handle to \"" + newName + "\". Do you accept?"); if(q){ chattable.sendMessageToFrame({ "type": "setName", "value": newName.toString() }); chattable.user.name = newName.toString(); } }, handlers : { message: [], payload: [], connection: [], load: [] }, on: function(event, callback){ switch(event){ case "message": chattable.handlers.message.push(callback); /* chattable.sendMessageToFrame({ type: "requestOnlineCount" }); */ break; case "payload": chattable.handlers.payload.push(callback); break; case "connection": chattable.handlers.connection.push(callback); break; case "load": chattable.handlers.load.push(callback); break; default: break; } }, off: function(event, callback){ switch(event){ case "message": if(chattable.handlers.message.includes(callback)){ const index = chattable.handlers.message.indexOf(callback); if (index > -1) { chattable.handlers.message.splice(index, 1); } } else { console.error("Can't turn off that handler, it doesn't exist."); } break; case "payload": if(chattable.handlers.payload.includes(callback)){ const index = chattable.handlers.payload.indexOf(callback); if (index > -1) { chattable.handlers.payload.splice(index, 1); } } else { console.error("Can't turn off that handler, it doesn't exist."); } break; case "connection": if(chattable.handlers.connection.includes(callback)){ const index = chattable.handlers.connection.indexOf(callback); if (index > -1) { chattable.handlers.connection.splice(index, 1); } } else { console.error("Can't turn off that handler, it doesn't exist."); } break; case "load": if(chattable.handlers.load.includes(callback)){ const index = chattable.handlers.load.indexOf(callback); if (index > -1) { chattable.handlers.load.splice(index, 1); } } else { console.error("Can't turn off that handler, it doesn't exist."); } break; default: break; } }, sendPayload: function(obj){ try { obj = JSON.stringify(obj); chattable.sendMessageToFrame({ type: "sendPayload", value: obj }); } catch(err){ console.error(err); } } }; window.addEventListener("message", function(e){ let data = e.data; if(typeof(data) == 'object'){ switch(data.type){ case "runCommand": if(data.name){ if(chattable.commands[data.name]){ chattable.commands[data.name](data.full, data); } else { console.error(`Can not execute command, this command doesn't exist. Chattable.commands['${data.name}']`); } } else { console.error("Can not execute command, no command name provided."); } break; case "init": console.log("Initializing Library..."); let n = data.name; if (n.match(/^\&\&(.*?)\$$/ig)) { n = new String(n).replace(/^\&\&|\$$/ig, ""); } chattable.user.name = n; chattable.user.uid = data.uid; chattable.settings.initialized = true; if(chattable.handlers.load.length > 0){ for(let i = 0; i < chattable.handlers.load.length; i++){ try { chattable.handlers.load[i](); } catch(error){ console.error(error); continue; } } } break; case "updateName": chattable.user.name = data.name; break; case "confirmation": switch(data.value){ case "css_is_rendered": console.log("Chattable is ready to be interacted with."); chattable.loaded = true; chattable.returnFrame().addEventListener("load", function(){ chattable.reinitialize(chattable.parameters); if(localStorage.flair){ chattable.setFlair(localStorage.flair); } }); break; default: console.error("Unrecognized Confirmation Code.") break; } break; case "payload": if(data.value && data.uid){ if(chattable.handlers.payload.length > 0){ for(let i = 0; i < chattable.handlers.payload.length; i++){ try { chattable.handlers.payload[i](JSON.parse(data.value)); } catch(error){ console.error(error); continue; } } } else { console.log("Payload dropped, no handlers are defined."); } } else { console.error("payload failed, data.value and/or data.uid are missing."); } break; case "message": if(data){ if(chattable.handlers.message.length > 0){ for(let i = 0; i < chattable.handlers.message.length; i++){ try { chattable.handlers.message[i](data); } catch(error){ console.error(error); continue; } } } } else { console.error("Something went wrong. (Critical)"); } break; case "connection": if(data){ if(chattable.handlers.connection.length > 0){ for(let i = 0; i < chattable.handlers.connection.length; i++){ try { chattable.handlers.connection[i](data.value); } catch(error){ console.error(error); continue; } } } else { console.log("An update to the user list was detected but you do not have events set up to handle them."); } } else { console.error("Something went wrong. (Critical)"); } break; case "setFlair": localStorage.flair = data.value; break; default: // ignore break; } } else { // ignore } return; }); // Export to window window.chattable = chattable; // Demo Payload for Poking Game/Feature /* function poke(name){ if(name == chattable.user.name){ alert("You have been Poked!"); } else { alert(name + " got Poked!"); } } chattable.on("payload", function(data){ switch(data.type){ case "poke": poke(data.value); break; default: break; } }); chattable.commands = { "poke" : function(fullCommand){ let name = fullCommand.replace(/^\!poke\s/i, ""); chattable.sendPayload({ type: "poke", value: name }); } }; */