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  })()