hexo-theme-sukax
Version:
Modern, powerful and simple theme for Hexo
335 lines (309 loc) • 12.4 kB
JavaScript
/*!
* Hexo Theme Suka | lsloader.js
* Patched by SukkaW on 2018/7/14 https://github.com/SukkaW
* Created by EYHN on 2017/4/17. https://github.com/EYHN
* From sexdevil/LSLoader https://github.com/sexdevil/LSLoader
*/
(function () {
window.lsloader = {
jsRunSequence: [], //js 运行队列 {name:代码name,code:代码,status:状态 failed/loading/comboJS,path:线上路径}
jsnamemap: {}, //js name map 防fallback 重复请求资源
cssnamemap: {} //css name map 防fallback 重复请求资源
};
/*
* 封装localStorage get set remove 方法
* try catch保证ls写满或者不支持本地缓存环境能继续运行js
* */
lsloader.removeLS = function (key) {
try {
localStorage.removeItem(key)
} catch (e) { }
};
lsloader.setLS = function (key, val) {
try {
localStorage.setItem(key, val);
} catch (e) {
}
}
lsloader.getLS = function (key) {
var val = ''
try {
val = localStorage.getItem(key);
} catch (e) {
val = '';
}
return val
}
versionString = "/*" + (window.lsversion || 'noVersion') + "*/";
lsloader.clean = function () {
try {
var keys = [];
for (var i = 0; i < localStorage.length; i++) {
keys.push(localStorage.key(i))
}
keys.forEach(function (key) {
var data = lsloader.getLS(key);
if (window.oldVersion) {
var remove = window.oldVersion.reduce(function (p, c) {
return p || data.indexOf('/*' + c + '*/') !== -1
}, false)
if (remove) {
lsloader.removeLS(key);
}
}
})
} catch (e) {
}
}
lsloader.clean();
/*
* load资源
* name 作为key path/分割符/代码值 作为value ,存储资源
* 如果value值中取出的版本号和线上模版的一致,命中缓存用本地,
* 否则 调用requestResource 请求资源
* jsname 文件name值,取相对路径,对应存在localStroage里的key
* jspath 文件线上路径,带md5版本号,用于加载资源,区分资源版本
* cssonload css加载成功时候调用,用于配合页面展现
* */
lsloader.load = function (jsname, jspath, cssonload, isJs) {
if (typeof cssonload === 'boolean') {
isJs = cssonload;
cssonload = undefined;
}
isJs = isJs || false;
cssonload = cssonload || function () { };
var code;
code = this.getLS(jsname);
if (code && code.indexOf(versionString) === -1) { //ls 版本 codestartv* 每次换这个版本 所有ls作废
this.removeLS(jsname);
this.requestResource(jsname, jspath, cssonload, isJs);
return
}
//取出对应文件名下的code
if (code) {
var versionNumber = code.split(versionString)[0]; //取出路径版本号 如果要加载的和ls里的不同,清理,重写
if (versionNumber != jspath) {
console.log("reload:" + jspath)
this.removeLS(jsname);
this.requestResource(jsname, jspath, cssonload, isJs);
return
}
code = code.split(versionString)[1];
if (isJs) {
this.jsRunSequence.push({ name: jsname, code: code })
this.runjs(jspath, jsname, code);
} else {
document.getElementById(jsname).appendChild(document.createTextNode(code));
cssonload();
}
} else {
//null xhr获取资源
this.requestResource(jsname, jspath, cssonload, isJs);
}
};
/*
* load请求资源
* 根据文件名尾部不同加载,js走runjs方法,加入运行队列中
* css 直接加载并且写入对应的<style>标签,根据style的顺序
* 保证css能正确覆盖规则 css 加载成功后调用cssonload 帮助控制
* 异步加载样式造成的dom树渲染错乱问题
* */
lsloader.requestResource = function (name, path, cssonload, isJs) {
var that = this
if (isJs) {
this.iojs(path, name, function (path, name, code) {
that.setLS(name, path + versionString + code)
that.runjs(path, name, code);
})
} else {
this.iocss(path, name, function (code) {
document.getElementById(name).appendChild(document.createTextNode(code));
that.setLS(name, path + versionString + code)
}, cssonload)
}
};
/*
* iojs
* 请求js资源,失败后调用jsfallback
* */
lsloader.iojs = function (path, jsname, callback) {
var that = this;
that.jsRunSequence.push({ name: jsname, code: '' })
try {
var xhr = new XMLHttpRequest();
xhr.open("get", path, true);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
if (xhr.response != '') {
callback(path, jsname, xhr.response);
return;
}
}
that.jsfallback(path, jsname);
}
};
xhr.send(null);
} catch (e) {
that.jsfallback(path, jsname);
}
};
/*
* iocss
* 请求css资源,失败后调用cssfallback
* */
lsloader.iocss = function (path, jsname, callback, cssonload) {
var that = this;
try {
var xhr = new XMLHttpRequest();
xhr.open("get", path, true);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
if (xhr.response != '') {
callback(xhr.response);
cssonload();
return;
}
}
that.cssfallback(path, jsname, cssonload);
}
};
xhr.send(null);
} catch (e) {
that.cssfallback(path, jsname, cssonload);
}
};
lsloader.iofonts = function (path, jsname, callback, cssonload) {
var that = this;
try {
var xhr = new XMLHttpRequest();
xhr.open("get", path, true);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
if (xhr.response != '') {
callback(xhr.response);
cssonload();
return;
}
}
that.cssfallback(path, jsname, cssonload);
}
};
xhr.send(null);
} catch (e) {
that.cssfallback(path, jsname, cssonload);
}
};
/*
* runjs
* 运行js主方法
* path js线上路径
* name js相对路径
* code js代码
* */
lsloader.runjs = function (path, name, code) {
//如果有 name code ,xhr来的结果,写入ls 否则是script.onload调用
if (!!name && !!code) {
for (var k in this.jsRunSequence) {
if (this.jsRunSequence[k].name == name) {
this.jsRunSequence[k].code = code;
}
}
}
if (!!this.jsRunSequence[0] && !!this.jsRunSequence[0].code && this.jsRunSequence[0].status != 'failed') {
//每次进入runjs检查jsRunSequence,如果第一项有代码并且状态没被置为failed,执行并剔除队列,回调
var script = document.createElement('script');
script.appendChild(document.createTextNode(this.jsRunSequence[0].code));
script.type = 'text/javascript';
document.getElementsByTagName('head')[0].appendChild(script);
this.jsRunSequence.shift();
//如果jsSequence还有排队的 继续运行
if (this.jsRunSequence.length > 0) {
this.runjs();
}
} else if (!!this.jsRunSequence[0] && this.jsRunSequence[0].status == 'failed') {
/*每次进入runjs检查jsRunSequence,如果第一项存在并且状态为failed,用script标签异步加载,
* 并且该项status置为loading 其他资源加载调用runjs时候就不会通过这个js项,等候完成
*/
var that = this;
var script = document.createElement('script');
script.src = this.jsRunSequence[0].path;
script.type = 'text/javascript';
this.jsRunSequence[0].status = 'loading'
script.onload = function () {
that.jsRunSequence.shift();
//如果jsSequence还有排队的 继续运行
if (that.jsRunSequence.length > 0) {
that.runjs();
}
};
document.body.appendChild(script);
}
}
/*
* tagLoad 用script标签加载不支持xhr请求的js资源
* 方法时jsRunSequence队列中添加一项name path为该资源,但是status=failed的项
* runjs调用检查时就会把这个项当作失败取用script标签请求
* */
lsloader.tagLoad = function (path, name) {
this.jsRunSequence.push({ name: name, code: '', path: path, status: 'failed' });
this.runjs();
}
//js回退加载 this.jsnamemap[name] 存在 证明已经在队列中 放弃
lsloader.jsfallback = function (path, name) {
if (!!this.jsnamemap[name]) {
return;
} else {
this.jsnamemap[name] = name;
}
//jsRunSequence队列中 找到fail的文件,标记他,等到runjs循环用script请求
for (var k in this.jsRunSequence) {
if (this.jsRunSequence[k].name == name) {
this.jsRunSequence[k].code = '';
this.jsRunSequence[k].status = 'failed';
this.jsRunSequence[k].path = path;
}
}
this.runjs();
};
/*cssfallback 回退加载
* path 同上
* name 同上
* cssonload 同上
* xhr加载css失败的话 使用link标签异步加载样式,成功后调用cssonload
*/
lsloader.cssfallback = function (path, name, cssonload) {
if (!!this.cssnamemap[name]) {
return;
} else {
this.cssnamemap[name] = 1;
}
var link = document.createElement('link');
link.type = 'text/css';
link.href = path;
link.rel = 'stylesheet';
link.onload = link.onerror = cssonload;
var root = document.getElementsByTagName('script')[0];
root.parentNode.insertBefore(link, root)
}
/*runInlineScript 运行行内脚本
* 如果有依赖之前加载的js的内联脚本,用该方法执行,
* scriptId js队列中的name值,可选
* codeId 包含内连脚本的textarea容器的id
* js队列中添加name code值进入,运行到该项时runjs函数直接把代码append到顶部运行
*/
lsloader.runInlineScript = function (scriptId, codeId) {
var code = document.getElementById(codeId).innerText;
this.jsRunSequence.push({ name: scriptId, code: code })
this.runjs()
}
// css & js 定义向页面引用资源
lsloader.css = function (key, uri) {
if(typeof window.lsLoadCSSMaxNums === "undefined")window.lsLoadCSSMaxNums = 0;window.lsLoadCSSMaxNums++;lsloader.load(key, uri ,function(){if(typeof window.lsLoadCSSNums === "undefined")window.lsLoadCSSNums = 0;window.lsLoadCSSNums++;if(window.lsLoadCSSNums == window.lsLoadCSSMaxNums)document.documentElement.style.display="";}, false);
}
lsloader.js = function (key, uri) {
lsloader.load(key, uri , true);
}
})()