Added the init.js file to make certain bits of JS run sooner than others.
The template_*.js files should get cached now. The phrase API results might get cached now. Solved an edge case in the attachment parser where a redirect is triggered on a HTTPS site when the attachment is posted as a HTTP URL. Fixed runHook in the JS files.
This commit is contained in:
parent
317ab8856b
commit
3fc2d6a867
|
@ -22,7 +22,10 @@
|
||||||
},
|
},
|
||||||
"globals": {
|
"globals": {
|
||||||
"$": true,
|
"$": true,
|
||||||
"session": true,
|
"addHook": true,
|
||||||
"siteURL": true
|
"runHook": true,
|
||||||
|
"addInitHook": true,
|
||||||
|
"runInitHook": true,
|
||||||
|
"loadScript": true
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -758,7 +758,7 @@ func parseMediaBytes(data []byte) (media MediaEmbed, ok bool) {
|
||||||
hostname = strings.Split(Site.URL, ":")[0]
|
hostname = strings.Split(Site.URL, ":")[0]
|
||||||
// ?- Test this as I'm not sure it'll do what it should. If someone's running SSL on port 80 or non-SSL on port 443 then... Well... They're in far worse trouble than this...
|
// ?- Test this as I'm not sure it'll do what it should. If someone's running SSL on port 80 or non-SSL on port 443 then... Well... They're in far worse trouble than this...
|
||||||
port = Site.Port
|
port = Site.Port
|
||||||
if scheme == "" && Site.EnableSsl {
|
if Site.EnableSsl {
|
||||||
scheme = "https"
|
scheme = "https"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
120
public/global.js
120
public/global.js
|
@ -1,32 +1,15 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
var formVars = {};
|
var formVars = {};
|
||||||
var tmplInits = {};
|
|
||||||
var tmplPhrases = []; // [key] array of phrases indexed by order of use
|
|
||||||
var phraseBox = {};
|
|
||||||
var alertList = [];
|
var alertList = [];
|
||||||
var alertCount = 0;
|
var alertCount = 0;
|
||||||
var moreTopicCount = 0;
|
var moreTopicCount = 0;
|
||||||
var conn;
|
var conn;
|
||||||
var me = {};
|
|
||||||
var selectedTopics = [];
|
var selectedTopics = [];
|
||||||
var attachItemCallback = function(){}
|
var attachItemCallback = function(){}
|
||||||
var hooks = {
|
|
||||||
"start_init": [],
|
|
||||||
"end_init": [],
|
|
||||||
};
|
|
||||||
|
|
||||||
// Topic move
|
// Topic move
|
||||||
var forumToMoveTo = 0;
|
var forumToMoveTo = 0;
|
||||||
|
|
||||||
function runHook(name, ...args) {
|
|
||||||
if(!(name in hooks)) return;
|
|
||||||
|
|
||||||
let hook = hooks[name];
|
|
||||||
for (const callback in hook) {
|
|
||||||
callback(...args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Write a friendlier error handler which uses a .notice or something, we could have a specialised one for alerts
|
// TODO: Write a friendlier error handler which uses a .notice or something, we could have a specialised one for alerts
|
||||||
function ajaxError(xhr,status,errstr) {
|
function ajaxError(xhr,status,errstr) {
|
||||||
console.log("The AJAX request failed");
|
console.log("The AJAX request failed");
|
||||||
|
@ -193,6 +176,10 @@ function runWebSockets() {
|
||||||
conn = new WebSocket("wss://" + document.location.host + "/ws/");
|
conn = new WebSocket("wss://" + document.location.host + "/ws/");
|
||||||
} else conn = new WebSocket("ws://" + document.location.host + "/ws/");
|
} else conn = new WebSocket("ws://" + document.location.host + "/ws/");
|
||||||
|
|
||||||
|
conn.onerror = (err) => {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
|
||||||
conn.onopen = () => {
|
conn.onopen = () => {
|
||||||
console.log("The WebSockets connection was opened");
|
console.log("The WebSockets connection was opened");
|
||||||
conn.send("page " + document.location.pathname + '\r');
|
conn.send("page " + document.location.pathname + '\r');
|
||||||
|
@ -201,10 +188,12 @@ function runWebSockets() {
|
||||||
Notification.requestPermission();
|
Notification.requestPermission();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
conn.onclose = () => {
|
conn.onclose = () => {
|
||||||
conn = false;
|
conn = false;
|
||||||
console.log("The WebSockets connection was closed");
|
console.log("The WebSockets connection was closed");
|
||||||
}
|
}
|
||||||
|
|
||||||
conn.onmessage = (event) => {
|
conn.onmessage = (event) => {
|
||||||
//console.log("WSMessage:", event.data);
|
//console.log("WSMessage:", event.data);
|
||||||
if(event.data[0] == "{") {
|
if(event.data[0] == "{") {
|
||||||
|
@ -270,102 +259,29 @@ function runWebSockets() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Temporary hack for templates
|
|
||||||
function len(item) {
|
|
||||||
return item.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadScript(name, callback) {
|
|
||||||
let url = "/static/"+name
|
|
||||||
$.getScript(url)
|
|
||||||
.done(callback)
|
|
||||||
.fail((e,xhr,settings,ex) => {
|
|
||||||
console.log("Unable to get script '"+url+"'");
|
|
||||||
console.log("e: ", e);
|
|
||||||
console.log("xhr: ", xhr);
|
|
||||||
console.log("settings: ", settings);
|
|
||||||
console.log("ex: ",ex);
|
|
||||||
console.trace();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function DoNothingButPassBack(item) {
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
|
|
||||||
function fetchPhrases() {
|
|
||||||
fetch("/api/phrases/?query=status,topic_list")
|
|
||||||
.then((resp) => resp.json())
|
|
||||||
.then((data) => {
|
|
||||||
console.log("loaded phrase endpoint data");
|
|
||||||
console.log("data:",data);
|
|
||||||
Object.keys(tmplInits).forEach((key) => {
|
|
||||||
let phrases = [];
|
|
||||||
let tmplInit = tmplInits[key];
|
|
||||||
for(let phraseName of tmplInit) {
|
|
||||||
phrases.push(data[phraseName]);
|
|
||||||
}
|
|
||||||
console.log("Adding phrases");
|
|
||||||
console.log("key:",key);
|
|
||||||
console.log("phrases:",phrases);
|
|
||||||
tmplPhrases[key] = phrases;
|
|
||||||
});
|
|
||||||
|
|
||||||
let prefixes = {};
|
|
||||||
Object.keys(data).forEach((key) => {
|
|
||||||
let prefix = key.split(".")[0];
|
|
||||||
if(prefixes[prefix]===undefined) {
|
|
||||||
prefixes[prefix] = {};
|
|
||||||
}
|
|
||||||
prefixes[prefix][key] = data[key];
|
|
||||||
});
|
|
||||||
Object.keys(prefixes).forEach((prefix) => {
|
|
||||||
console.log("adding phrase prefix '"+prefix+"' to box");
|
|
||||||
phraseBox[prefix] = prefixes[prefix];
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
(() => {
|
(() => {
|
||||||
runHook("pre_iife");
|
addInitHook("pre_init", () => {
|
||||||
let loggedIn = document.head.querySelector("[property='x-loggedin']").content;
|
// We can only get away with this because template_alert has no phrases, otherwise it too would have to be part of the "dance", I miss Go concurrency :(
|
||||||
|
loadScript("template_alert.js", () => {
|
||||||
fetch("/api/me/")
|
console.log("Loaded template_alert.js");
|
||||||
.then((resp) => resp.json())
|
$(document).ready(() => {
|
||||||
.then((data) => {
|
|
||||||
console.log("loaded me endpoint data");
|
|
||||||
console.log("data:",data);
|
|
||||||
me = data;
|
|
||||||
runHook("pre_init");
|
|
||||||
|
|
||||||
// We can only get away with this because template_alert has no phrases, otherwise it too would have to be part of the "dance", I miss Go concurrency :(
|
|
||||||
loadScript("template_alert.js", () => {
|
|
||||||
console.log("Loaded template_alert.js");
|
|
||||||
alertsInitted = true;
|
alertsInitted = true;
|
||||||
var alertMenuList = document.getElementsByClassName("menu_alerts");
|
var alertMenuList = document.getElementsByClassName("menu_alerts");
|
||||||
for(var i = 0; i < alertMenuList.length; i++) {
|
for(var i = 0; i < alertMenuList.length; i++) {
|
||||||
loadAlerts(alertMenuList[i]);
|
loadAlerts(alertMenuList[i]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if(window["WebSocket"]) runWebSockets();
|
|
||||||
else conn = false;
|
|
||||||
|
|
||||||
$(document).ready(mainInit);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if(loggedIn) {
|
if(window["WebSocket"]) runWebSockets();
|
||||||
let toLoad = 1;
|
else conn = false;
|
||||||
loadScript("template_topics_topic.js", () => {
|
|
||||||
console.log("Loaded template_topics_topic.js");
|
$(document).ready(mainInit);
|
||||||
toLoad--;
|
});
|
||||||
if(toLoad===0) fetchPhrases();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})();
|
})();
|
||||||
|
|
||||||
function mainInit(){
|
function mainInit(){
|
||||||
runHook("start_init");
|
runInitHook("start_init");
|
||||||
|
|
||||||
$(".more_topics").click((event) => {
|
$(".more_topics").click((event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
@ -839,5 +755,5 @@ function mainInit(){
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
runHook("end_init");
|
runInitHook("end_init");
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,146 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var me = {};
|
||||||
|
var phraseBox = {};
|
||||||
|
var tmplInits = {};
|
||||||
|
var tmplPhrases = []; // [key] array of phrases indexed by order of use
|
||||||
|
var hooks = {
|
||||||
|
"pre_iffe": [],
|
||||||
|
"pre_init": [],
|
||||||
|
"start_init": [],
|
||||||
|
"end_init": [],
|
||||||
|
};
|
||||||
|
var ranInitHooks = {}
|
||||||
|
|
||||||
|
function runHook(name, ...args) {
|
||||||
|
if(!(name in hooks)) {
|
||||||
|
console.log("Couldn't find hook '" + name + "'");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log("Running hook '"+name+"'");
|
||||||
|
|
||||||
|
let hook = hooks[name];
|
||||||
|
for (const index in hook) {
|
||||||
|
hook[index](...args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addHook(name, callback) {
|
||||||
|
hooks[name].push(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
function runInitHook(name) {
|
||||||
|
runHook(name);
|
||||||
|
ranInitHooks[name] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addInitHook(name, callback) {
|
||||||
|
addHook(name, callback);
|
||||||
|
if(name in ranInitHooks) {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Temporary hack for templates
|
||||||
|
function len(item) {
|
||||||
|
return item.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
const asyncGetScript = (source) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let script = document.createElement('script');
|
||||||
|
script.async = true;
|
||||||
|
|
||||||
|
const onloadHander = (haha, isAbort) => {
|
||||||
|
if (isAbort || !script.readyState || /loaded|complete/.test(script.readyState)) {
|
||||||
|
script.onload = null;
|
||||||
|
script.onreadystatechange = null;
|
||||||
|
script = undefined;
|
||||||
|
|
||||||
|
isAbort ? reject(haha) : resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
script.onerror = (haha) => {
|
||||||
|
reject(haha);
|
||||||
|
};
|
||||||
|
script.onload = onloadHander;
|
||||||
|
script.onreadystatechange = onloadHander;
|
||||||
|
script.src = source;
|
||||||
|
|
||||||
|
const prior = document.getElementsByTagName('script')[0];
|
||||||
|
prior.parentNode.insertBefore(script, prior);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
function loadScript(name, callback) {
|
||||||
|
let url = "/static/"+name
|
||||||
|
asyncGetScript(url)
|
||||||
|
.then(callback)
|
||||||
|
.catch((haha) => {
|
||||||
|
console.log("Unable to get script '"+url+"'");
|
||||||
|
console.log("haha: ", haha);
|
||||||
|
console.trace();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function DoNothingButPassBack(item) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetchPhrases() {
|
||||||
|
fetch("/api/phrases/?query=status,topic_list")
|
||||||
|
.then((resp) => resp.json())
|
||||||
|
.then((data) => {
|
||||||
|
console.log("loaded phrase endpoint data");
|
||||||
|
console.log("data:",data);
|
||||||
|
Object.keys(tmplInits).forEach((key) => {
|
||||||
|
let phrases = [];
|
||||||
|
let tmplInit = tmplInits[key];
|
||||||
|
for(let phraseName of tmplInit) {
|
||||||
|
phrases.push(data[phraseName]);
|
||||||
|
}
|
||||||
|
console.log("Adding phrases");
|
||||||
|
console.log("key:",key);
|
||||||
|
console.log("phrases:",phrases);
|
||||||
|
tmplPhrases[key] = phrases;
|
||||||
|
});
|
||||||
|
|
||||||
|
let prefixes = {};
|
||||||
|
Object.keys(data).forEach((key) => {
|
||||||
|
let prefix = key.split(".")[0];
|
||||||
|
if(prefixes[prefix]===undefined) {
|
||||||
|
prefixes[prefix] = {};
|
||||||
|
}
|
||||||
|
prefixes[prefix][key] = data[key];
|
||||||
|
});
|
||||||
|
Object.keys(prefixes).forEach((prefix) => {
|
||||||
|
console.log("adding phrase prefix '"+prefix+"' to box");
|
||||||
|
phraseBox[prefix] = prefixes[prefix];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
(() => {
|
||||||
|
runInitHook("pre_iife");
|
||||||
|
let loggedIn = document.head.querySelector("[property='x-loggedin']").content;
|
||||||
|
|
||||||
|
fetch("/api/me/")
|
||||||
|
.then((resp) => resp.json())
|
||||||
|
.then((data) => {
|
||||||
|
console.log("loaded me endpoint data");
|
||||||
|
console.log("data:",data);
|
||||||
|
me = data;
|
||||||
|
runInitHook("pre_init");
|
||||||
|
});
|
||||||
|
|
||||||
|
if(loggedIn) {
|
||||||
|
let toLoad = 1;
|
||||||
|
loadScript("template_topics_topic.js", () => {
|
||||||
|
console.log("Loaded template_topics_topic.js");
|
||||||
|
toLoad--;
|
||||||
|
if(toLoad===0) fetchPhrases();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})();
|
|
@ -101,10 +101,17 @@ func routeAPI(w http.ResponseWriter, r *http.Request, user common.User) common.R
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Remove this line after we move routeAPIPhrases to the routes package
|
||||||
|
var cacheControlMaxAge = "max-age=" + strconv.Itoa(int(common.Day))
|
||||||
|
|
||||||
// TODO: Be careful with exposing the panel phrases here, maybe move them into a different namespace? We also need to educate the admin that phrases aren't necessarily secret
|
// TODO: Be careful with exposing the panel phrases here, maybe move them into a different namespace? We also need to educate the admin that phrases aren't necessarily secret
|
||||||
|
// TODO: Move to the routes package
|
||||||
func routeAPIPhrases(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
func routeAPIPhrases(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||||
// TODO: Don't make this too JSON dependent so that we can swap in newer more efficient formats
|
// TODO: Don't make this too JSON dependent so that we can swap in newer more efficient formats
|
||||||
w.Header().Set("Content-Type", "application/json")
|
h := w.Header()
|
||||||
|
h.Set("Content-Type", "application/json")
|
||||||
|
h.Set("Cache-Control", cacheControlMaxAge) //Cache-Control: max-age=31536000
|
||||||
|
|
||||||
err := r.ParseForm()
|
err := r.ParseForm()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.PreErrorJS("Bad Form", w, r)
|
return common.PreErrorJS("Bad Form", w, r)
|
||||||
|
|
|
@ -6,12 +6,13 @@
|
||||||
{{range .Header.Stylesheets}}
|
{{range .Header.Stylesheets}}
|
||||||
<link href="/static/{{.}}" rel="stylesheet" type="text/css">
|
<link href="/static/{{.}}" rel="stylesheet" type="text/css">
|
||||||
{{end}}
|
{{end}}
|
||||||
|
<meta property="x-loggedin" content="{{.CurrentUser.Loggedin}}" />
|
||||||
|
<script type="text/javascript" src="/static/init.js"></script>
|
||||||
<script type="text/javascript" src="/static/jquery-3.1.1.min.js"></script>
|
<script type="text/javascript" src="/static/jquery-3.1.1.min.js"></script>
|
||||||
<script type="text/javascript" src="/static/chartist/chartist.min.js"></script>
|
<script type="text/javascript" src="/static/chartist/chartist.min.js"></script>
|
||||||
{{range .Header.Scripts}}
|
{{range .Header.Scripts}}
|
||||||
<script type="text/javascript" src="/static/{{.}}"></script>
|
<script type="text/javascript" src="/static/{{.}}"></script>
|
||||||
{{end}}
|
{{end}}
|
||||||
<meta property="x-loggedin" content="{{.CurrentUser.Loggedin}}" />
|
|
||||||
<script type="text/javascript" src="/static/global.js"></script>
|
<script type="text/javascript" src="/static/global.js"></script>
|
||||||
<meta name="viewport" content="width=device-width,initial-scale = 1.0, maximum-scale=1.0,user-scalable=no" />
|
<meta name="viewport" content="width=device-width,initial-scale = 1.0, maximum-scale=1.0,user-scalable=no" />
|
||||||
{{if .Header.MetaDesc}}<meta name="description" content="{{.Header.MetaDesc}}" />{{end}}
|
{{if .Header.MetaDesc}}<meta name="description" content="{{.Header.MetaDesc}}" />{{end}}
|
||||||
|
|
Loading…
Reference in New Issue