github.com/Azareal/Gosora@v0.0.0-20210729070923-553e66b59003/public/init.js (about) 1 'use strict'; 2 var me={}; 3 var phraseBox={}; 4 if(tmplInits===undefined) var tmplInits={}; 5 var tmplPhrases=[]; // [key] array of phrases indexed by order of use 6 var hooks={}; 7 var ranInitHooks={} 8 var log=console.log; 9 var pre="/s/"; 10 11 function runHook(name,...args) { 12 if(!(name in hooks)) { 13 log("Couldn't find hook "+name); 14 return; 15 } 16 log("Running hook "+name); 17 18 let hook = hooks[name]; 19 let o; 20 for(const index in hook) o = hook[index](...args); 21 return o; 22 } 23 function addHook(name,h) { 24 log("Add hook "+name); 25 if(hooks[name]===undefined) hooks[name]=[]; 26 hooks[name].push(h); 27 } 28 29 // InitHooks are slightly special, as if they are run, then any adds after the initial run will run immediately, this is to deal with the async nature of script loads 30 function runInitHook(name,...args) { 31 ranInitHooks[name]=true; 32 return runHook(name,...args); 33 } 34 function addInitHook(name,h) { 35 addHook(name,h); 36 if(name in ranInitHooks) { 37 log("Delay running "+name); 38 h(); 39 } 40 } 41 42 // Temporary hack for templates 43 function len(d) {return d.length} 44 45 function asyncGetScript(src) { 46 return new Promise((resolve,reject) => { 47 let script = document.createElement('script'); 48 script.async = true; 49 50 const onloadHandler = (e,isAbort) => { 51 if (isAbort||!script.readyState||/loaded|complete/.test(script.readyState)) { 52 script.onload=null; 53 script.onreadystatechange=null; 54 script=undefined; 55 56 isAbort ? reject(e) : resolve(); 57 } 58 } 59 script.onerror = e => { 60 reject(e); 61 }; 62 script.onload = onloadHandler; 63 script.onreadystatechange = onloadHandler; 64 script.src = src; 65 66 const prior = document.getElementsByTagName('script')[0]; 67 prior.parentNode.insertBefore(script,prior); 68 }); 69 } 70 71 function notifyOnScript(src) { 72 src = pre+src; 73 return new Promise((resolve,reject) => { 74 let ss = src.replace(pre,""); 75 try { 76 let ssp = ss.charAt(0).toUpperCase() + ss.slice(1) 77 log("ssp",ssp) 78 if(window[ssp]) { 79 resolve(); 80 return; 81 } 82 } catch(e) {} 83 84 log("src",src) 85 let script = document.querySelectorAll(`[src^="${src}"]`)[0]; 86 log("script",script); 87 if(script===undefined) { 88 reject("no script found"); 89 return; 90 } 91 92 const onloadHandler = e => { 93 script.onload=null; 94 script.onreadystatechange=null; 95 resolve(); 96 } 97 script.onerror = e => { 98 reject(e); 99 }; 100 script.onload = onloadHandler; 101 script.onreadystatechange = onloadHandler; 102 }); 103 } 104 105 function notifyOnScriptW(name,complete,success) { 106 notifyOnScript(name) 107 .then(() => { 108 log(`Loaded ${name}.js`); 109 complete(); 110 if(success!==undefined) success(); 111 }).catch(e => { 112 log("Unable to get "+name,e); 113 console.trace(); 114 complete(e); 115 }); 116 } 117 118 // TODO: Send data at load time so we don't have to rely on a fallback template here 119 function loadScript(name,h,fail) { 120 let fname = name; 121 let value = "; "+document.cookie; 122 let parts = value.split("; current_theme="); 123 if(parts.length==2) fname += "_"+parts.pop().split(";").shift(); 124 125 let url = pre+fname+".js" 126 let iurl = pre+name+".js" 127 asyncGetScript(url) 128 .then(h).catch(e => { 129 log("Unable to get "+url,e); 130 if(fname!=name) { 131 asyncGetScript(iurl) 132 .then(h).catch(e => { 133 log("Unable to get "+iurl,e); 134 console.trace(); 135 }); 136 } 137 console.trace(); 138 fail(e); 139 }); 140 } 141 142 function RelativeTime(date) {return date} 143 144 function initPhrases(member,acp=false) { 145 log("initPhrases") 146 log("tmlInits",tmplInits) 147 let e = ""; 148 if(member && !acp) e=",status,topic_list,topic"; 149 else if(acp) e=",analytics,panel"; // TODO: Request phrases for just one section of the acp? 150 else e=",status,topic_list"; 151 fetchPhrases("alerts,paginator"+e) // TODO: Break this up? 152 } 153 154 function fetchPhrases(plist) { 155 fetch("/api/phrases/?q="+plist,{cache:"no-cache"}) 156 .then(r => r.json()) 157 .then(d => { 158 log("loaded phrase endpoint data",d); 159 Object.keys(tmplInits).forEach(key => { 160 let phrases=[]; 161 let tmplInit = tmplInits[key]; 162 for(let phraseName of tmplInit) phrases.push(d[phraseName]); 163 log("Adding phrases for "+key,phrases); 164 tmplPhrases[key] = phrases; 165 }); 166 167 let prefixes={}; 168 Object.keys(d).forEach(key => { 169 let prefix = key.split(".")[0]; 170 if(prefixes[prefix]===undefined) prefixes[prefix]={}; 171 prefixes[prefix][key] = d[key]; 172 }); 173 Object.keys(prefixes).forEach(prefix => { 174 log(`adding phrase prefix ${prefix} to box`); 175 phraseBox[prefix] = prefixes[prefix]; 176 }); 177 178 runInitHook("after_phrases"); 179 }); 180 } 181 182 (() => { 183 runInitHook("pre_iife"); 184 let member = document.head.querySelector("[property='x-mem']")!=null; 185 let acp = window.location.pathname.startsWith("/panel/"); 186 187 let toLoad = 1; 188 // TODO: Shunt this into member if there aren't any search and filter widgets? 189 let q = f => { 190 toLoad--; 191 if(toLoad===0) initPhrases(member,acp); 192 if(f) throw("tmpl func not found"); 193 }; 194 let l = (n,h) => notifyOnScriptW("tmpl_"+n,h); 195 196 if(!acp) { 197 toLoad += 2; 198 if(member) { 199 toLoad += 3; 200 l("topic_c_edit_post", () => q(!Tmpl_topic_c_edit_post)); 201 l("topic_c_attach_item", () => q(!Tmpl_topic_c_attach_item)); 202 l("topic_c_poll_input", () => q(!Tmpl_topic_c_poll_input)); 203 } 204 l("topics_topic", () => q(!Tmpl_topics_topic)); 205 l("paginator", () => q(!Tmpl_paginator)); 206 } 207 l("notice", () => q(!Tmpl_notice)); 208 209 if(member) { 210 fetch("/api/me/") 211 .then(r => r.json()) 212 .then(d => { 213 log("me",d); 214 me=d; 215 pre=d.StaticPrefix; 216 runInitHook("pre_init"); 217 }); 218 } else { 219 me={User:{ID:0,S:""},Site:{"MaxReqSize":0}}; 220 runInitHook("pre_init"); 221 } 222 })()