litejs
Version:
Full-stack web framework in a tiny package
374 lines (336 loc) • 11.2 kB
JavaScript
!function(exports, Object, Function) {
// jshint newcap:false
"use strict";
var currentLang, currentMap
, isArray = Array.isArray
, cache = {}
, formatRe = /{(?!;)({[\s\S]*}|\[[\s\S]*]|(?:("|')(?:\\\2|.)*?\2|[^;{}])+?)(?:;((?:(['"\/])(?:\\\4|.)*?\4[gim]*|[^}])*))?}/g
, exprRe = /(['"\/])(?:\\\1|.)*?\1[gim]*|\b(?:[$_]|false|in|null|true|typeof|void)\b|\.\w+|\w+\s*:|\s+/g
, wordRe = /(\$?)([a-z_][\w$]*)/ig
, pattRe = /(\w+)(?::((?:(['"\/])(?:\\\3|.)*?\3[gim]*|[^;])*))?/g
, pointerRe = /^([\w ]+)\.([\w ]+)$/
, globalTexts = {}
, globalVals = i18n.vals = {}
// you can use Unicode's fraction slash (U+2044) with superscript and subscript numerals: e.g. ³⁄₄₇
// 2^53-1= 9007199254740991 == Number.MAX_SAFE_INTEGER
, list = i18n.list = []
, ext = i18n.ext = {}
, fnScope = {}
exports.i18n = i18n
i18n.add = add
i18n.get = get
i18n.use = use
function i18n(str, data) {
if (typeof str === "number") return "" + str
var out = cache[str] || (
cache[str] = makeFn(get(str) || str)
)
return isString(out) ? out : out(data || {}, i18n, globalVals)
}
function get(str, fallback) {
var tmp
return isString(str) ? (
isString(currentMap[str]) ? currentMap[str] :
typeof currentMap[str] === "object" ? currentMap[str][""] :
(tmp = pointerRe.exec(str)) && (
typeof currentMap[tmp[1]] === "object" &&
currentMap[tmp[1]][tmp[2]] ||
currentMap[tmp[2]]
) || fallback
) :
isArray(str) ?
get(str[0], get(str[1], get(str[2], fallback))) :
fallback
}
function makeFn(str) {
var tmp, m, expr, pattern
, args = ""
, fn = ""
, lastIndex = 0
for (; m = formatRe.exec(str); ) {
for (expr = m[1].replace(exprRe, ""); tmp = wordRe.exec(expr); ) {
args += (args ? "," : "var ") + tmp[0] + (
tmp[1] ? "=" : "=$['" + tmp[0] + "']!=null?$['" + tmp[0] + "']:"
) + "$g['" + tmp[2] + "']!=null?$g['" + tmp[2] + "']:''"
}
expr = m[1]
if (pattern = get(m[3], m[3])) {
if (ext[tmp = pattern.charAt(0)]) {
expr = "_." + ext[tmp] + "(" + expr + "," + quote(pattern.slice(tmp == "#" ? 0 : 1)) + ")"
} else {
for (; tmp = pattRe.exec(pattern); ) {
expr = "_." + tmp[1] + ".call($," + expr + (tmp[2] ? "," + tmp[2] : "") + ")"
}
}
}
fn += (fn ? "+" : "") + (
lastIndex < m.index ?
quote(str.slice(lastIndex, m.index)) + "+(" : "("
) + expr + ")"
lastIndex = m.index + m[0].length
}
if (fn) try {
return Function("$,_,$g", args + ";return(" + fn + (
lastIndex < str.length ? ")+" + quote(str.slice(lastIndex)) : ")"
))
} catch (e) {
/*** debug
console.log("makeFn", str, args, fn)
console.log(e)
/**/
}
return str.replace(/{;/g, "{")
}
function add(lang, texts) {
if (list.indexOf(lang) < 0) {
i18n[lang] = Object.create(globalTexts)
list.push(lang)
if (!currentLang) use(lang)
}
merge(i18n[lang], texts)
}
function merge(target, map) {
for (var k in map) {
target[k] = map[k] && map[k].constructor === Object ? merge(Object.create(target), map[k]) : map[k]
}
return target
}
i18n.def = function(map) {
for (var k in map) {
add(k, map)
}
}
function getLang(lang) {
return lang && (
i18n[lang = ("" + lang).toLowerCase()] ||
i18n[lang = lang.split("-")[0]]
) && lang
}
function use(lang) {
lang = getLang(lang)
if (lang && currentLang != lang) {
cache = {}
currentMap = i18n[currentLang = i18n.current = lang] = i18n[currentLang]
}
return currentLang
}
function isString(str) {
return typeof str === "string"
}
function isObject(obj) {
return obj && obj.constructor === Object
}
function getStr(sub, word, fallback) {
return currentMap && (
isObject(currentMap[word]) && currentMap[word][sub] ||
isObject(currentMap[sub]) && currentMap[sub][word] ||
currentMap[word || sub]
) || isString(fallback) && fallback || ""
}
function quote(str) {
return "'" + (str || "").replace(/'/g, "\\'").replace(/\n/g, "\\n") + "'"
}
/*** i18n.date ***/
// Software should only ever deal with UTC except when displaying times to the user.
// P3Y6M4DT12H30M5S - P is the duration designator (referred to as "period")
//
var dateRe = /([Md])\1\1\1?|([yMdHhmswSZ])(\2?)|[uUaSeoQ]|'((?:''|[^'])*)'|(["\\\n\r\u2028\u2029])/g
, fns = Object.create(null)
, tmp1 = new Date()
, tmp2 = new Date()
, map = {
e: "Day()||7",
M: "Month()+1",
d: "Date()",
H: "Hours()",
h: "Hours()%12||12",
m: "Minutes()",
s: "Seconds()",
S: "Milliseconds()"
}
i18n[ext["@"] = "date"] = date
function date(input, _mask, _zone) {
var offset, undef
, d = typeof input === "number" ? input : isNaN(input) ? Date.parse(input) : +input
, locale = currentMap["@"]
, mask = locale[_mask] || _mask || locale.iso
, zone = _zone != undef ? _zone : Date._tz != undef ? Date._tz : undef
, utc = mask.slice(0, 4) == "UTC:"
if (zone != undef && !utc) {
offset = 60 * zone
tmp1.setTime(d + offset * 6e4)
utc = mask = "UTC:" + mask
} else {
tmp1.setTime(d)
offset = utc ? 0 : -tmp1.getTimezoneOffset()
}
return isNaN(d) ? "" + d : (
fns[mask] || (fns[mask] = Function("d,a,o,l", "var t;return \"" + dateStr(mask, utc) + "\"")))(
tmp1,
tmp2,
offset,
locale
)
}
date.dateStr = dateStr
function dateStr(mask, utc) {
var get = "d.get" + (utc ? "UTC" : "")
, setA = "a.setTime(+d+((4-(" + get + map.e + "))*864e5))"
return (utc ? mask.slice(4) : mask).replace(dateRe, function(match, MD, single, pad, text, esc) {
var str = (
esc ? escape(esc).replace(/%u/g, "\\u").replace(/%/g, "\\x") :
text !== void 0 ? text.replace(/''/g, "'") :
MD ? "l.names[" + get + (MD == "M" ? "Month" : "Day" ) + "()+" + (match == "ddd" ? 24 : MD == "d" ? 31 : match == "MMM" ? 0 : 12) + "]" :
match == "u" ? "(d/1000)>>>0" :
match == "U" ? "+d" :
match == "Q" ? "((" + get + "Month()/3)|0)+1" :
match == "a" ? "l[" + get + map.H + ">11?'pm':'am']" :
match == "o" ? setA + ",a" + get.slice(1) + "FullYear()" :
single == "y" ? get + "FullYear()" + (pad == "y" ? "%100" : "") :
single == "Z" ? "(t=o)?(t<0?((t=-t),'-'):'+')+(t<600?'0':'')+(0|(t/60))" + (pad ? "" : "+':'") + "+((t%=60)>9?t:'0'+t):'Z'" :
single == "w" ? "Math.ceil(((" + setA + "-a.s" + get.slice(3) + "Month(0,1))/864e5+1)/7)" :
get + map[single || match]
)
return text !== void 0 || esc ? str : "\"+(" + (
match == "SS" ? "(t=" + str + ")>9?t>99?t:'0'+t:'00'+t" :
pad && single != "Z" ? "(t=" + str + ")>9?t:'0'+t" :
str
) + ")+\""
})
}
/**/
/*** i18n.detect ***/
i18n.detect = function(fallback) {
var navigator = exports.navigator || exports
// navigator.userLanguage for IE, navigator.language for others
return use([navigator.language, navigator.userLanguage].concat(
navigator.languages, fallback, list[0]
).filter(getLang)[0])
}
/**/
/*** i18n.number ***/
var numRe1 = /([^\d#]*)([\d# .,_·']*\/?\d+)(?:(\s*)([a-z%]+)(\d*))?(.*)/
, numRe2 = /([.,\/])(\d*)$/
i18n[ext["#"] = ext["+"] = "number"] = number
function number(input, _format) {
var format = getStr("#", _format.slice(1), _format)
return (cache[format] || (cache[format] = Function(
"d,g",
"var N=d<0&&(d=-d),n,r,o;return " + numStr(format)
)))(input, fnScope)
}
number.pre = {
a: "(o+=d<1e3?'':d<1e6?(d/=1e3,'k'):d<1e9?(d/=1e6,'M'):d<1e12?(d/=1e9,'G'):d<1e15?(d/=1e12,'T'):d<1e18?(d/=1e15,'P'):(d/=1e18,'E')),"
}
number.post = {
}
function numStr(format) {
// format;NaN;negFormat;0;Infinity;-Infinity;roundPoint
var conf = format.split(";")
, nan_value = conf[1] || "-"
, m2 = numRe1.exec(conf[0])
, m3 = numRe2.exec(m2[2])
, decimals = m3 && m3[2].length || 0
, full = m3 ? m2[2].slice(0, m3.index) : m2[2]
, num = full.replace(/\D+/g, "")
, sLen = num.length
, step = decimals ? +(m3[1] === "/" ? 1 / m3[2] : num + "." + m3[2]) : num
, decSep = m3 && m3[1]
, fn = "d===Infinity?(N?" + quote(conf[5]||nan_value) + ":" + quote(conf[4]||nan_value) + "):d>0||d===0?(o=" + quote(m2[3]) + "," + (number.pre[m2[4]] || "") + "n=" + (
// Use exponential notation to fix float rounding
// Math.round(1.005*100)/100 = 1 instead of 1.01
decimals ?
"d>1e-" + (decimals + 1) + "?(n=(d+'e" + decimals + "')/" + (step + "e" + decimals) + "":
"d>"+num+"e-1?(n=d/" + num
) + ",Math.floor(n" + (
conf[6] == 1 ? "%1?n+1:n" : "+" + (conf[6] || 0.5)
) + ")*" + step + "):0,r=" + (
m2[5] ? "(''+(+n.toPrecision(" + (m2[5]) + ")))" :
decimals ? "n.toFixed(" + decimals + ")" :
"n+''"
)
if (decimals) {
if (decSep == "/") {
fn += ".replace(/\\.\\d+/,'" + (
m3[2] == 5 ?
"⅕⅖⅗⅘'.charAt(5" :
"⅛¼⅜½⅝¾⅞'.charAt(8"
) + "*(n%1)-1))"
} else if (decSep != ".") {
fn += ".replace('.','" + decSep + "')"
}
if (sLen === 0) {
fn += ",n<1&&(r=r.slice(1)||'0')"
}
}
if (sLen > 1) {
if (decimals) sLen += decimals + 1
fn += ",r=(r.length<" + sLen + "?(1e15+r).slice(-" + sLen + "):r)"
}
if (num = full.match(/[^\d#][\d#]+/g)) {
fn += ",r=" + numJunk(num, num.length - 1, 0, decimals ? decimals + 1 : 0)
}
if (m2[4] == "o") {
number.post.o = "r+(o=g.o," + (
fnScope.o = get("ordinal").split(";")
).pop() + ")"
}
fn += (
(m2[4] ? ",r=" + (number.post[m2[4]] || "r+o") : "") +
// negative format
",N&&n>0?" + quote(conf[2] || "-#").replace("#", "'+r+'") + ":" +
(conf[3] ? "n===0?" + quote(conf[3]) + ":" : "") +
(m2[1] ? quote(m2[1]) + "+r" : "r") +
(m2[6] ? "+" + quote(m2[6]) : "")
)
return fn + "):" + quote(nan_value)
}
function numJunk(arr, i, lastLen, dec) {
var len = lastLen + arr[i].length - 1
return "(n<1e" + len + (
lastLen ? "?r.slice(0,-" + (lastLen + dec) + "):" : "?r:"
) + (
len < 16 ? numJunk(arr, i?i-1:i, len, dec) : "r.slice(0,-" + (lastLen + dec) + ")"
) + "+" + quote(arr[i].charAt(0)) + "+r.slice(-" + (len + dec) + (
lastLen ? ",-" + (lastLen + dec) : ""
) + "))"
}
/**/
/*** i18n.pick ***/
var pickRe1 = /(\w+)\?/g
, pickRe2 = /[;=,]/
i18n[ext["?"] = "pick"] = pick
function pick(val, word) {
for (var arr = getStr("?", word, word).replace(pickRe1, "$1=$1;").split(pickRe2), i = 1|arr.length; i > 0; ) {
if ((i-=2) < 0 || arr[i] && (arr[i] == "" + val || +arr[i] <= val)) {
return arr[i + 1] || ""
}
}
}
/**/
/*** i18n.plural ***/
i18n[ext["*"] = "plural"] = plural
function plural(n, word) {
var expr = getStr("*", "", "n!=1")
return (cache[expr] || (cache[expr] = Function(
"a,n",
"return (a[+(" + expr + ")]||a[0]).replace('#',n)"
)))((getStr("*", word, "# " + word)).split(";"), n)
}
/**/
i18n.map = function(input, str, sep, lastSep) {
if (!isArray(input)) return input
var arr = input.map(function(data) {
return i18n(str, data)
})
, end = lastSep && arr.length > 1 ? lastSep + arr.pop() : ""
return arr.join(sep || ", ") + end
}
i18n.upcase = function(str) {
return isString(str) ? str.toUpperCase() : "" + str
}
i18n.locase = function(str) {
return isString(str) ? str.toLowerCase() : "" + str
}
i18n.json = JSON.stringify
}(this, Object, Function) // jshint ignore:line