Instantly share code, notes, and snippets.
Created
December 10, 2012 10:40
-
Star
(0)
0
You must be signed in to star a gist -
Fork
(0)
0
You must be signed in to fork a gist
-
Save tsmallfield/4249877 to your computer and use it in GitHub Desktop.
loader.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
///////////////////////////////////////////////// | |
var directories = createNamespaceDirectoryNameArray(), | |
jsPrefix, jsSuffix; | |
///////////////////////////////////////////////// | |
/** | |
* 名前空間を作成します。 | |
* @return {Array.<string>} | |
*/ | |
function createNamespaceDirectoryNameArray() { | |
var arr = HOST_NAME.split("."), | |
i; | |
arr.reverse(); | |
arr.push(PROJECT_NAME); | |
i = arr.length; | |
while (i--) { | |
arr[i] = arr[i].replace(/-(.)/g, function(_, letter) { | |
return letter.toUpperCase(); | |
}); | |
} | |
return arr; | |
} | |
/** | |
* 名前空間を作成します。 | |
*/ | |
function createNamespace() { | |
var arr = directories, | |
i = 0, | |
len = arr.length, | |
cur = win, | |
itm; | |
jsPrefix = "!function(app){"; | |
jsSuffix = "}.call(this," + arr.join(".") + ");"; | |
for (; i < len; ++i) { | |
itm = arr[i]; | |
cur = cur[itm] = cur[itm] || {}; | |
cur.toString = (function(namespaceString) { | |
return function() { | |
return namespaceString; | |
}; | |
}(arr.slice(0, i + 1).join('.'))); | |
} | |
} | |
///////////////////////////////////////////////// | |
/** | |
* 相対パスを絶対パスに変換します。 | |
* | |
* @param {string} url | |
* @return {string} | |
*/ | |
function abs(url) { | |
var a = doc.createElement("a"); | |
a.href = url; | |
return a.href; | |
} | |
////////////////////////////////////////////////// | |
/** | |
* @type {boolean} | |
*/ | |
var supportsStorage = (function() { | |
var sto = win.localStorage, | |
key, val, ret; | |
if (!sto) { | |
return false; | |
} | |
key = "_" + (Math.random() * 10000 | 0); | |
val = "_" + (Math.random() * 10000 | 0); | |
// throws 'QUOTA_EXCEEDED_ERR' on Safari Private Mode | |
try { | |
sto.removeItem(key); | |
sto.setItem(key, val); | |
ret = sto.getItem(key) === val; | |
sto.removeItem(key); | |
} catch(err) { | |
ret = false; | |
} | |
return ret; | |
}()); | |
////////////////////////////////////////////////// | |
/** | |
* @type {Object.<function>} | |
*/ | |
var Storage = (function createStorage() { | |
/*-------------------------------------------- | |
PRIVATE | |
--------------------------------------------*/ | |
var WRITE_DELAY = 100, | |
support = supportsStorage, | |
localStorage = win.localStorage, | |
_ = function() {}, | |
ROOT_KEY = directories.join('.') + '.loader', | |
VER_KEY = ROOT_KEY + '.version', | |
DATA_KEY = ROOT_KEY + '.data', | |
data, writeTimerId; | |
/*-------------------------------------------- | |
PRIVATE | |
--------------------------------------------*/ | |
if (supportsStorage) { | |
data = open(); | |
} | |
/*------------------------------------------- | |
PRIVATE | |
-------------------------------------------*/ | |
/** | |
* | |
*/ | |
function open() { | |
var ver = localStorage.getItem(VER_KEY), | |
val; | |
if (ver !== VERSION) { | |
clear(); | |
localStorage.setItem(VER_KEY, VERSION); | |
return {}; | |
} else { | |
val = localStorage.getItem(DATA_KEY); | |
return val ? JSON.parse(val) : {}; | |
} | |
} | |
/** | |
* @see http://d.hatena.ne.jp/uupaa/20100106/1262781846 | |
* @return {boolean} | |
*/ | |
function save() { | |
localStorage.setItem(DATA_KEY, ''); | |
try { | |
localStorage.setItem( | |
DATA_KEY, | |
JSON.stringify(data) | |
); | |
return true; | |
} catch (err) { | |
return false; | |
} | |
} | |
function clear() { | |
localStorage.setItem(DATA_KEY, ''); | |
localStorage.removeItem(DATA_KEY); | |
} | |
/*------------------------------------------- | |
PUBLIC | |
-------------------------------------------*/ | |
/** | |
* @param {string} key | |
* @param {string} val | |
*/ | |
function setItem(key, val) { | |
data[key] = val; | |
// localStorage に書き込む回数を最小にするため | |
// 遅延させる | |
clearTimeout(writeTimerId); | |
writeTimerId = setTimeout(save, WRITE_DELAY); | |
} | |
/** | |
* @param {string} key | |
* @param {string} val | |
*/ | |
function getItem(key) { | |
return data[key]; | |
} | |
/*-------------------------------------------- | |
EXPORT | |
--------------------------------------------*/ | |
return { | |
setItem : support ? setItem : _, | |
getItem : support ? getItem : _ | |
}; | |
}()); | |
///////////////////////////////////////////////// | |
/** | |
* @return {Object.<function>} | |
*/ | |
function createDeferred(queue) { | |
/*------------------------------------------- | |
INIT | |
-------------------------------------------*/ | |
queue = []; | |
/*------------------------------------------- | |
PUBLIC | |
-------------------------------------------*/ | |
function resolve(arr, i, len) { | |
len = (arr = queue).length; | |
i = queue = 0; | |
for (; i < len; ++i) { | |
arr[i](); | |
} | |
} | |
function done(fnc) { | |
queue ? fnc() : queue.push(fnc); | |
} | |
/*------------------------------------------- | |
EXPORT | |
-------------------------------------------*/ | |
return { | |
resolve : resolve, | |
done : done | |
}; | |
} | |
///////////////////////////////////////////////// | |
/** | |
* @param {Array.<string>} queue | |
* @return {undeifned} | |
*/ | |
function main(queue) { | |
/*------------------------------------------- | |
PRIVATE | |
-------------------------------------------*/ | |
var CSS = "css", | |
JS = "js", | |
TMPL = "tmpl", | |
ver = VERSION, | |
max = queue.length, | |
dfd = createDeferred(); | |
/*------------------------------------------- | |
INIT | |
-------------------------------------------*/ | |
shift(); | |
/*-------------------------------------------- | |
PRIVATE | |
--------------------------------------------*/ | |
function shift() { | |
var url; | |
if (!queue.length) { | |
dfd.resolve(); | |
return; | |
} | |
loadFile( | |
url = abs( | |
queue. | |
shift(). | |
replace(/^\s+/, ""). | |
replace(/\s+$/, "") | |
), | |
shift, | |
shift, | |
getExtention(url) | |
); | |
} | |
function isSameDomain(url) { | |
var m = /https?:\/\/(?:[^@]+@)?([^\/]+)/.exec(url); | |
return m && m[1] === location.hostname; | |
} | |
function evaluateJS(js, src) { | |
var script = doc.createElement("script"); | |
script.text = jsPrefix + js + jsSuffix; | |
//script.setAttribute("data-src", src); | |
doc.body.appendChild(script); | |
setTimeout(function() { | |
var p = script.parentNode; | |
// s.text = ""; | |
p && p.removeChild(script); | |
script = p = null; | |
}, 100); | |
} | |
function evaluateCSS(css, src) { | |
var style = doc.createElement("style"), | |
head = doc.head || doc.getElementsByTagName("head")[0]; | |
style.type = "text/css"; | |
style.media = "screen,print"; | |
style.setAttribute("data-src", src); | |
head.appendChild(style);// IE8 先にDOMツリーに挿入する必要あり | |
if (style.styleSheet) { | |
style.styleSheet.cssText = css; | |
} else { | |
style.innerHTML = css; | |
} | |
} | |
function evaluateTMPL(tmpl, src) { | |
var script = doc.createElement("script"), | |
id = abs(src).replace(/[^-_0-9a-z$]+/g, "_");// escape illegal characters | |
if (doc.getElementById(id)) { | |
return; | |
} | |
script.id = id; | |
script.type = "text/template"; | |
script.innerHTML = tmpl; | |
doc.body.appendChild(script); | |
} | |
function preload(src, success, error, ext) { | |
if (!DISABLE_STORAGE && isSameDomain(src)) { | |
loadFromStorage(src, success, error, ext); | |
} | |
} | |
function loadFile(src, success, error, ext) { | |
if (!DISABLE_STORAGE && isSameDomain(src)) { | |
loadFromStorage(src, success, error, ext); | |
} else { | |
switch (ext) { | |
case JS: | |
loadWithScriptTag(src, success, error); | |
break; | |
case CSS: | |
loadWithLinkTag(src, success, error); | |
break; | |
case TMPL: | |
loadWithAJAX(src, success, error, ext); | |
break; | |
default: | |
break; | |
} | |
} | |
} | |
function getExtention(src) { | |
return src. | |
replace(/^.*\.(.*)$/, "$1"). | |
replace(/[?#].*$/, ""). | |
toLowerCase(); | |
} | |
function loadWithScriptTag(src, success, error) { | |
var script = doc.createElement("script"); | |
script.onload = handleSuccess; | |
script.onerror = handleError; | |
script.onreadystatechange = handleReadyStateChange; | |
script.async = 1; | |
script.src = src + (ADD_TIMESTAMP ? ("?" + (+new Date)) : ""); | |
doc.body.appendChild(script); | |
function handleReadyStateChange() { | |
var readyState = script.readyState; | |
if (readyState === "loaded" || readyState === "complete") { | |
handleSuccess(); | |
} | |
} | |
function handleSuccess() { | |
success(); | |
clean(); | |
} | |
function handleError() { | |
error(); | |
clean(); | |
} | |
function clean() { | |
script = | |
script.onload = | |
script.onerror = | |
script.onreadystatechange = null; | |
} | |
} | |
function loadWithLinkTag(src, success, error) { | |
var link = doc.createElement("link"), | |
head = doc.head || doc.getElementsByTagName("head")[0]; | |
link.rel = "stylesheet";// required | |
link.href = src + (ADD_TIMESTAMP ? ("?" + (+new Date)) : ""); | |
head.appendChild(link); | |
success(); | |
} | |
function loadFromStorage(src, success, error, ext) { | |
var key = encodeURIComponent(src), | |
code = Storage.getItem(key); | |
if (code) { | |
setTimeout(function() { | |
switch (ext) { | |
case JS: | |
evaluateJS(code, src); | |
break; | |
case CSS: | |
evaluateCSS(code, src); | |
break; | |
case TMPL: | |
evaluateTMPL(code, src); | |
break; | |
default: | |
break; | |
} | |
success(); | |
}, 0); | |
} else { | |
loadWithAJAX(src, success, error, ext); | |
} | |
} | |
/** | |
* 非同期通信を行います。 | |
* | |
* @param {string} src | |
* @param {function} success | |
* @param {function} error | |
* @return {undefined} | |
*/ | |
function ajax(src, success, error) { | |
var xhr = new XMLHttpRequest; | |
xhr.open("GET", src); | |
xhr.onreadystatechange = handleReadyStateChange; | |
xhr.send(null); | |
function handleReadyStateChange() { | |
var x = xhr, | |
stat; | |
if (+x.readyState === 4) { | |
stat = +x.status; | |
if ((200 <= stat && stat < 300) || stat === 304) { | |
handleSuccess(); | |
} else { | |
handleError(); | |
} | |
} | |
} | |
function handleSuccess() { | |
success && success(xhr); | |
clean(); | |
} | |
function handleError() { | |
error && error(xhr); | |
clean(); | |
} | |
function clean() { | |
xhr = xhr.onreadystatechange = null; | |
} | |
} | |
/** | |
* @param {string} src | |
* @param {function} success | |
* @param {function} error | |
* @param {string} ext | |
* @return {undefined} | |
*/ | |
function loadWithAJAX(src, success, error, ext) { | |
ajax( | |
//src + (ADD_TIMESTAMP ? ("?" + (+new Date)) : ""), | |
src + "?" + (+new Date), | |
handleSuccess, | |
handleError | |
); | |
function handleSuccess(xhr) { | |
var code = xhr.responseText, | |
key = encodeURIComponent(src); | |
// eval | |
switch (ext) { | |
case JS: | |
evaluateJS(code, src); | |
success(); | |
break; | |
case CSS: | |
evaluateCSS(code, src); | |
success(); | |
break; | |
case TMPL: | |
evaluateTMPL(code, src); | |
success(); | |
break; | |
default: | |
error(); | |
throw new Error("Error: INVALID FILE TYPE " + src); | |
break; | |
} | |
Storage.setItem(key, code); | |
} | |
function handleError() { | |
switch (ext) { | |
case JS: | |
loadWithScriptTag(src, success, error); | |
break; | |
case CSS: | |
loadWithLinkTag(src, success, error); | |
break; | |
case TMPL: | |
success(); | |
throw new Error("Error: COULD NOT LOAD TMPL " + src); | |
break; | |
default: | |
error(); | |
throw new Error("Error: INVALID FILE TYPE " + src); | |
break; | |
} | |
} | |
} | |
return dfd; | |
} | |
///////////////////////////////////////////////// | |
/** | |
* @param {Array.<Object>} | |
*/ | |
function classify(arr) { | |
var priorList = [], | |
deferredList = [], | |
// optionalList = [], | |
href = location.href, | |
len = arr.length, | |
i = 0, | |
itm; | |
for (; i < len; ++i) { | |
itm = arr[i]; | |
if (itm.prior && itm.prior.test(href)) { | |
priorList.push(itm.src); | |
} else { | |
deferredList.push(itm.src); | |
} | |
} | |
return { | |
prior : priorList, | |
deferred : deferredList/*, | |
optional : optionalList */ | |
}; | |
} | |
///////////////////////////////////////////////// | |
createNamespace(); | |
LIST = classify(LIST); | |
main(LIST.prior).done(function() { | |
setTimeout(function() { | |
main(LIST.deferred); | |
}, LOAD_DELAY); | |
}); | |
}(this, document)); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;(function(win, doc) { | |
"use strict"; | |
///////////////////////////////////////////////// |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment