You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3848 lines
136 KiB
3848 lines
136 KiB
'use strict';
|
|
|
|
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
|
|
|
|
(function () {
|
|
'use strict';
|
|
|
|
// root of function
|
|
|
|
var root = this,
|
|
win = this,
|
|
doc = win ? win.document : null,
|
|
docElem = win ? win.document.documentElement : null,
|
|
reIsJson = /^(["'](\\.|[^"\\\n\r])*?["']|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/,
|
|
reMs = /^-ms-/,
|
|
reSnakeCase = /[\-_]([\da-z])/gi,
|
|
reCamelCase = /([A-Z])/g,
|
|
reDot = /\./,
|
|
reInt = /[-|+]?[\D]/gi,
|
|
reNotNum = /\D/gi,
|
|
reMoneySplit = new RegExp('([0-9])([0-9][0-9][0-9][,.])'),
|
|
reAmp = /&/g,
|
|
reEq = /=/,
|
|
reClassNameSplit = /[ ]+/g,
|
|
|
|
|
|
/** @namespace {Object} ax5 */
|
|
ax5 = {},
|
|
info = void 0,
|
|
U = void 0,
|
|
dom = void 0;
|
|
|
|
/**
|
|
* guid
|
|
* @member {Number} ax5.guid
|
|
*/
|
|
ax5.guid = 1;
|
|
/**
|
|
* ax5.guid를 구하고 증가시킵니다.
|
|
* @method ax5.getGuid
|
|
* @returns {Number} guid
|
|
*/
|
|
ax5.getGuid = function () {
|
|
return ax5.guid++;
|
|
};
|
|
|
|
/**
|
|
* 상수모음
|
|
* @namespace ax5.info
|
|
*/
|
|
ax5.info = info = function () {
|
|
var _arguments = arguments;
|
|
|
|
/**
|
|
* ax5 version
|
|
* @member {String} ax5.info.version
|
|
*/
|
|
var version = "1.4.126";
|
|
|
|
/**
|
|
* ax5 library path
|
|
* @member {String} ax5.info.baseUrl
|
|
*/
|
|
var baseUrl = "";
|
|
|
|
/**
|
|
* ax5 에러 출력메세지 사용자 재 정의
|
|
* @member {Object} ax5.info.onerror
|
|
* @examples
|
|
* ```
|
|
* ax5.info.onerror = function(){
|
|
* console.log(arguments);
|
|
* }
|
|
* ```
|
|
*/
|
|
var onerror = function onerror() {
|
|
console.error(U.toArray(_arguments).join(":"));
|
|
};
|
|
|
|
/**
|
|
* event keyCodes
|
|
* @member {Object} ax5.info.eventKeys
|
|
* @example
|
|
* ```
|
|
* {
|
|
* BACKSPACE: 8, TAB: 9,
|
|
* RETURN: 13, ESC: 27, LEFT: 37, UP: 38, RIGHT: 39, DOWN: 40, DELETE: 46,
|
|
* HOME: 36, END: 35, PAGEUP: 33, PAGEDOWN: 34, INSERT: 45, SPACE: 32
|
|
* }
|
|
* ```
|
|
*/
|
|
var eventKeys = {
|
|
BACKSPACE: 8, TAB: 9,
|
|
RETURN: 13, ESC: 27, LEFT: 37, UP: 38, RIGHT: 39, DOWN: 40, DELETE: 46,
|
|
HOME: 36, END: 35, PAGEUP: 33, PAGEDOWN: 34, INSERT: 45, SPACE: 32
|
|
};
|
|
|
|
/**
|
|
* week names
|
|
* @member {Object[]} weekNames
|
|
* @member {string} weekNames[].label
|
|
*
|
|
* @example
|
|
* ```
|
|
* [
|
|
* {label: "SUN"},{label: "MON"},{label: "TUE"},{label: "WED"},{label: "THU"},{label: "FRI"},{label: "SAT"}
|
|
* ]
|
|
* console.log( weekNames[0] );
|
|
* console.log( ax5.info.weekNames[(new Date()).getDay()].label )
|
|
* ```
|
|
*/
|
|
var weekNames = [{ label: "SUN" }, { label: "MON" }, { label: "TUE" }, { label: "WED" }, { label: "THU" }, { label: "FRI" }, { label: "SAT" }];
|
|
|
|
/**
|
|
* 사용자 브라우저 식별용 오브젝트
|
|
* @member {Object} ax5.info.browser
|
|
* @example
|
|
* ```
|
|
* console.log( ax5.info.browser );
|
|
* //Object {name: "chrome", version: "39.0.2171.71", mobile: false}
|
|
* ```
|
|
*/
|
|
var browser = function (ua, mobile, browserName, match, browser, browserVersion) {
|
|
if (!win || !win.navigator) return {};
|
|
|
|
ua = navigator.userAgent.toLowerCase(), mobile = ua.search(/mobile/g) != -1, browserName, match, browser, browserVersion;
|
|
|
|
if (ua.search(/iphone/g) != -1) {
|
|
return { name: "iphone", version: 0, mobile: true };
|
|
} else if (ua.search(/ipad/g) != -1) {
|
|
return { name: "ipad", version: 0, mobile: true };
|
|
} else if (ua.search(/android/g) != -1) {
|
|
match = /(android)[ \/]([\w.]+)/.exec(ua) || [];
|
|
browserVersion = match[2] || "0";
|
|
return { name: "android", version: browserVersion, mobile: mobile };
|
|
} else {
|
|
browserName = "";
|
|
match = /(opr)[ \/]([\w.]+)/.exec(ua) || /(chrome)[ \/]([\w.]+)/.exec(ua) || /(webkit)[ \/]([\w.]+)/.exec(ua) || /(msie) ([\w.]+)/.exec(ua) || ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || [];
|
|
browser = match[1] || "";
|
|
browserVersion = match[2] || "0";
|
|
|
|
if (browser == "msie") browser = "ie";
|
|
return {
|
|
name: browser,
|
|
version: browserVersion,
|
|
mobile: mobile
|
|
};
|
|
}
|
|
ua = null, mobile = null, browserName = null, match = null, browser = null, browserVersion = null;
|
|
}();
|
|
|
|
/**
|
|
* 브라우저 여부
|
|
* @member {Boolean} ax5.info.isBrowser
|
|
*/
|
|
var isBrowser = !!(typeof window !== 'undefined' && typeof navigator !== 'undefined' && win.document);
|
|
|
|
/**
|
|
* 브라우저에 따른 마우스 휠 이벤트이름
|
|
* @member {Object} ax5.info.wheelEnm
|
|
*/
|
|
var wheelEnm = win && /Firefox/i.test(navigator.userAgent) ? "DOMMouseScroll" : "mousewheel";
|
|
|
|
/**
|
|
* 첫번째 자리수 동사 - (필요한것이 없을때 : 4, 실행오류 : 5)
|
|
* 두번째 자리수 목적어 - 문자열 0, 숫자 1, 배열 2, 오브젝트 3, 함수 4, DOM 5, 파일 6, 기타 7
|
|
* 세번째 자리수 옵션
|
|
* @member {Object} ax5.info.errorMsg
|
|
*/
|
|
var errorMsg = {};
|
|
|
|
/**
|
|
* 현재 페이지의 Url 정보를 리턴합니다.
|
|
* @method ax5.info.urlUtil
|
|
* @returns {Object}
|
|
* @example
|
|
* ```
|
|
* console.log( ax5.util.toJson( ax5.info.urlUtil() ) );
|
|
* {
|
|
* "baseUrl": "http://ax5:2018",
|
|
* "href": "http://ax5:2018/samples/index.html?a=1&b=1#abc",
|
|
* "param": "a=1&b=1",
|
|
* "referrer": "",
|
|
* "pathname": "/samples/index.html",
|
|
* "hostname": "ax5",
|
|
* "port": "2018",
|
|
* "url": "http://ax5:2018/samples/index.html",
|
|
* "hashdata": "abc"
|
|
* }
|
|
* ```
|
|
*/
|
|
function urlUtil(url, urls) {
|
|
url = {
|
|
href: win.location.href,
|
|
param: win.location.search,
|
|
referrer: doc.referrer,
|
|
pathname: win.location.pathname,
|
|
hostname: win.location.hostname,
|
|
port: win.location.port
|
|
}, urls = url.href.split(/[\?#]/);
|
|
url.param = url.param.replace("?", "");
|
|
url.url = urls[0];
|
|
if (url.href.search("#") > -1) {
|
|
url.hashdata = U.last(urls);
|
|
}
|
|
urls = null;
|
|
url.baseUrl = U.left(url.href, "?").replace(url.pathname, "");
|
|
return url;
|
|
}
|
|
|
|
/**
|
|
* ax5-error-msg.js 에 정의된 ax5 error를 반환합니다.
|
|
* @method ax5.info.getError
|
|
* @returns {Object}
|
|
* @example
|
|
* ```
|
|
* console.log( ax5.info.getError("single-uploader", "460", "upload") );
|
|
*
|
|
* if(!this.selectedFile){
|
|
* if (cfg.onEvent) {
|
|
* var that = {
|
|
* action: "error",
|
|
* error: ax5.info.getError("single-uploader", "460", "upload")
|
|
* };
|
|
* cfg.onEvent.call(that, that);
|
|
* }
|
|
* return this;
|
|
* }
|
|
* ```
|
|
*/
|
|
function getError(className, errorCode, methodName) {
|
|
if (info.errorMsg && info.errorMsg[className]) {
|
|
return {
|
|
className: className,
|
|
errorCode: errorCode,
|
|
methodName: methodName,
|
|
msg: info.errorMsg[className][errorCode]
|
|
};
|
|
} else {
|
|
return { className: className, errorCode: errorCode, methodName: methodName };
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 브라우져의 터치 기능 유무를 확인합니다.
|
|
* @method ax5.info.supportTouch
|
|
* @returns {boolean}
|
|
* @example
|
|
* ```
|
|
* var chkFlag = ax5.info.supportTouch;
|
|
*/
|
|
var supportTouch = win ? 'ontouchstart' in win || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0 : false;
|
|
|
|
var supportFileApi = win ? win.FileReader && win.File && win.FileList && win.Blob : false;
|
|
|
|
return {
|
|
errorMsg: errorMsg,
|
|
version: version,
|
|
baseUrl: baseUrl,
|
|
onerror: onerror,
|
|
eventKeys: eventKeys,
|
|
weekNames: weekNames,
|
|
browser: browser,
|
|
isBrowser: isBrowser,
|
|
supportTouch: supportTouch,
|
|
supportFileApi: supportFileApi,
|
|
wheelEnm: wheelEnm,
|
|
urlUtil: urlUtil,
|
|
getError: getError
|
|
};
|
|
}();
|
|
|
|
/**
|
|
* Refer to this by {@link ax5}.
|
|
* @namespace ax5.util
|
|
*/
|
|
ax5['util'] = U = function () {
|
|
var _toString = Object.prototype.toString;
|
|
|
|
/**
|
|
* Object나 Array의 아이템으로 사용자 함수를 호출합니다.
|
|
* @method ax5.util.each
|
|
* @param {Object|Array} O
|
|
* @param {Function} _fn
|
|
* @example
|
|
* ```js
|
|
* var axf = ax5.util;
|
|
* axf.each([0,1,2], function(){
|
|
* // with this
|
|
* });
|
|
* axf.each({a:1, b:2}, function(){
|
|
* // with this
|
|
* });
|
|
* ```
|
|
*/
|
|
function each(O, _fn) {
|
|
if (isNothing(O)) return [];
|
|
var key = void 0,
|
|
i = 0,
|
|
l = O.length,
|
|
isObj = l === undefined || typeof O === "function";
|
|
if (isObj) {
|
|
for (key in O) {
|
|
if (typeof O[key] != "undefined") if (_fn.call(O[key], key, O[key]) === false) break;
|
|
}
|
|
} else {
|
|
for (; i < l;) {
|
|
if (typeof O[i] != "undefined") if (_fn.call(O[i], i, O[i++]) === false) break;
|
|
}
|
|
}
|
|
return O;
|
|
}
|
|
|
|
// In addition to using the http://underscorejs.org : map, reduce, reduceRight, find
|
|
/**
|
|
* 원본 아이템들을 이용하여 사용자 함수의 리턴값으로 이루어진 새로운 배열을 만듭니다.
|
|
* @method ax5.util.map
|
|
* @param {Object|Array} O
|
|
* @param {Function} _fn
|
|
* @returns {Array}
|
|
* @example
|
|
* ```js
|
|
* var myArray = [0,1,2,3,4];
|
|
* var myObject = {a:1, b:"2", c:{axj:"what", arrs:[0,2,"3"]},
|
|
* fn: function(abcdd){
|
|
* return abcdd;
|
|
* }
|
|
* };
|
|
*
|
|
* var _arr = ax5.util.map( myArray, function(index, I){
|
|
* return index+1;
|
|
* });
|
|
* console.log(_arr);
|
|
* // [1, 2, 3, 4, 5]
|
|
*
|
|
* var _arr = ax5.util.map( myObject, function(k, v){
|
|
* return v * 2;
|
|
* });
|
|
* console.log(_arr);
|
|
* // [2, 4, NaN, NaN]
|
|
* ```
|
|
*/
|
|
function map(O, _fn) {
|
|
if (isNothing(O)) return [];
|
|
var key = void 0,
|
|
i = 0,
|
|
l = O.length,
|
|
results = [],
|
|
fnResult = void 0;
|
|
if (isObject(O)) {
|
|
for (key in O) {
|
|
if (typeof O[key] != "undefined") {
|
|
fnResult = undefined;
|
|
if ((fnResult = _fn.call(O[key], key, O[key])) === false) break;else results.push(fnResult);
|
|
}
|
|
}
|
|
} else {
|
|
for (; i < l;) {
|
|
if (typeof O[i] != "undefined") {
|
|
fnResult = undefined;
|
|
if ((fnResult = _fn.call(O[i], i, O[i++])) === false) break;else results.push(fnResult);
|
|
}
|
|
}
|
|
}
|
|
return results;
|
|
}
|
|
|
|
/**
|
|
* 원본 아이템들을 이용하여 사용자 함수의 리턴값이 참인 아이템의 위치나 키값을 반환합니다.
|
|
* @method ax5.util.search
|
|
* @param {Object|Array} O
|
|
* @param {Function|String|Number} _fn - 함수 또는 값
|
|
* @returns {Number|String}
|
|
* @example
|
|
* ```js
|
|
* var myArray = [0,1,2,3,4,5,6];
|
|
* var myObject = {a:"123","b":"123",c:123};
|
|
*
|
|
* ax5.util.search(myArray, function(){
|
|
* return this > 3;
|
|
* });
|
|
* // 4
|
|
* ax5.util.search(myObject, function(k, v){
|
|
* return v === 123;
|
|
* });
|
|
* // "c"
|
|
* ax5.util.search([1,2,3,4], 3);
|
|
* // 2
|
|
* ax5.util.search([1,2], 4);
|
|
* // -1
|
|
* ax5.util.search(["name","value"], "value");
|
|
* // 1
|
|
* ax5.util.search(["name","value"], "values");
|
|
* // -1
|
|
* ax5.util.search({k1:"name",k2:"value"}, "value2");
|
|
* // -1
|
|
* ax5.util.search({k1:"name",k2:"value"}, "value");
|
|
* // "k2"
|
|
* ```
|
|
*/
|
|
function search(O, _fn) {
|
|
if (isNothing(O)) return -1;
|
|
if (isObject(O)) {
|
|
for (var key in O) {
|
|
if (typeof O[key] != "undefined" && isFunction(_fn) && _fn.call(O[key], key, O[key])) {
|
|
return key;
|
|
break;
|
|
} else if (O[key] == _fn) {
|
|
return key;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
for (var i = 0, l = O.length; i < l; i++) {
|
|
if (typeof O[i] != "undefined" && isFunction(_fn) && _fn.call(O[i], i, O[i])) {
|
|
return i;
|
|
break;
|
|
} else if (O[i] == _fn) {
|
|
return i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* @method ax5.util.sum
|
|
* @param {Array|Object} O
|
|
* @param {Number} [defaultValue]
|
|
* @param {Function} _fn
|
|
* @returns {Number}
|
|
* @example
|
|
* ```js
|
|
* var arr = [
|
|
* {name: "122", value: 9},
|
|
* {name: "122", value: 10},
|
|
* {name: "123", value: 11}
|
|
* ];
|
|
*
|
|
* var rs = ax5.util.sum(arr, function () {
|
|
* if(this.name == "122") {
|
|
* return this.value;
|
|
* }
|
|
* });
|
|
* console.log(rs); // 19
|
|
*
|
|
* console.log(ax5.util.sum(arr, 10, function () {
|
|
* return this.value;
|
|
* }));
|
|
* // 40
|
|
* ```
|
|
*/
|
|
function sum(O, defaultValue, _fn) {
|
|
var i = void 0,
|
|
l = void 0,
|
|
tokenValue = void 0;
|
|
if (isFunction(defaultValue) && typeof _fn === "undefined") {
|
|
_fn = defaultValue;
|
|
defaultValue = 0;
|
|
}
|
|
if (typeof defaultValue === "undefined") defaultValue = 0;
|
|
|
|
if (isArray(O)) {
|
|
i = 0;
|
|
l = O.length;
|
|
for (; i < l; i++) {
|
|
if (typeof O[i] !== "undefined") {
|
|
if ((tokenValue = _fn.call(O[i], O[i])) === false) break;else if (typeof tokenValue !== "undefined") defaultValue += tokenValue;
|
|
}
|
|
}
|
|
return defaultValue;
|
|
} else if (isObject(O)) {
|
|
for (i in O) {
|
|
if (typeof O[i] != "undefined") {
|
|
if ((tokenValue = _fn.call(O[i], O[i])) === false) break;else if (typeof tokenValue !== "undefined") defaultValue += tokenValue;
|
|
}
|
|
}
|
|
return defaultValue;
|
|
} else {
|
|
console.error("argument error : ax5.util.sum - use Array or Object");
|
|
return defaultValue;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @method ax5.util.avg
|
|
* @param {Array|Object} O
|
|
* @param {Number} [defaultValue]
|
|
* @param {Function} _fn
|
|
* @returns {Number}
|
|
* @example
|
|
* ```js
|
|
* var arr = [
|
|
* {name: "122", value: 9},
|
|
* {name: "122", value: 10},
|
|
* {name: "123", value: 11}
|
|
* ];
|
|
*
|
|
* var rs = ax5.util.avg(arr, function () {
|
|
* return this.value;
|
|
* });
|
|
*
|
|
* console.log(rs); // 10
|
|
* ```
|
|
*/
|
|
function avg(O, defaultValue, _fn) {
|
|
var i = void 0,
|
|
l = void 0,
|
|
tokenValue = void 0;
|
|
if (isFunction(defaultValue) && typeof _fn === "undefined") {
|
|
_fn = defaultValue;
|
|
defaultValue = 0;
|
|
}
|
|
if (typeof defaultValue === "undefined") defaultValue = 0;
|
|
|
|
if (isArray(O)) {
|
|
i = 0;
|
|
l = O.length;
|
|
for (; i < l; i++) {
|
|
if (typeof O[i] !== "undefined") {
|
|
if ((tokenValue = _fn.call(O[i], O[i])) === false) break;else if (typeof tokenValue !== "undefined") defaultValue += tokenValue;
|
|
}
|
|
}
|
|
return defaultValue / l;
|
|
} else if (isObject(O)) {
|
|
l = 0;
|
|
for (i in O) {
|
|
if (typeof O[i] != "undefined") {
|
|
if ((tokenValue = _fn.call(O[i], O[i])) === false) break;else if (typeof tokenValue !== "undefined") defaultValue += tokenValue;++l;
|
|
}
|
|
}
|
|
return defaultValue / l;
|
|
} else {
|
|
console.error("argument error : ax5.util.sum - use Array or Object");
|
|
return defaultValue;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 배열의 왼쪽에서 오른쪽으로 연산을 진행하는데 수행한 결과가 왼쪽 값으로 반영되어 최종 왼쪽 값을 반환합니다.
|
|
* @method ax5.util.reduce
|
|
* @param {Array|Object} O
|
|
* @param {Function} _fn
|
|
* @returns {Alltypes}
|
|
* @example
|
|
* ```js
|
|
* var aarray = [5,4,3,2,1];
|
|
* result = ax5.util.reduce( aarray, function(p, n){
|
|
* return p * n;
|
|
* });
|
|
* console.log(result, aarray);
|
|
* // 120 [5, 4, 3, 2, 1]
|
|
*
|
|
* ax5.util.reduce({a:1, b:2}, function(p, n){
|
|
* return parseInt(p|0) + parseInt(n);
|
|
* });
|
|
* // 3
|
|
* ```
|
|
*/
|
|
function reduce(O, _fn) {
|
|
var i, l, tokenItem;
|
|
if (isArray(O)) {
|
|
i = 0, l = O.length, tokenItem = O[i];
|
|
for (; i < l - 1;) {
|
|
if (typeof O[i] != "undefined") {
|
|
if ((tokenItem = _fn.call(root, tokenItem, O[++i])) === false) break;
|
|
}
|
|
}
|
|
return tokenItem;
|
|
} else if (isObject(O)) {
|
|
for (i in O) {
|
|
if (typeof O[i] != "undefined") {
|
|
if ((tokenItem = _fn.call(root, tokenItem, O[i])) === false) break;
|
|
}
|
|
}
|
|
return tokenItem;
|
|
} else {
|
|
console.error("argument error : ax5.util.reduce - use Array or Object");
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 배열의 오른쪽에서 왼쪽으로 연산을 진행하는데 수행한 결과가 오른쪽 값으로 반영되어 최종 오른쪽 값을 반환합니다.
|
|
* @method ax5.util.reduceRight
|
|
* @param {Array} O
|
|
* @param {Function} _fn
|
|
* @returns {Alltypes}
|
|
* @example
|
|
* ```js
|
|
* var aarray = [5,4,3,2,1];
|
|
* result = ax5.util.reduceRight( aarray, function(p, n){
|
|
* console.log( n );
|
|
* return p * n;
|
|
* });
|
|
* console.log(result, aarray);
|
|
* 120 [5, 4, 3, 2, 1]
|
|
* ```
|
|
*/
|
|
function reduceRight(O, _fn) {
|
|
var i = O.length - 1,
|
|
tokenItem = O[i];
|
|
for (; i > 0;) {
|
|
if (typeof O[i] != "undefined") {
|
|
if ((tokenItem = _fn.call(root, tokenItem, O[--i])) === false) break;
|
|
}
|
|
}
|
|
return tokenItem;
|
|
}
|
|
|
|
/**
|
|
* 배열또는 오브젝트의 각 아이템을 인자로 하는 사용자 함수의 결과가 참인 아이템들의 배열을 반환합니다.
|
|
* @method ax5.util.filter
|
|
* @param {Object|Array} O
|
|
* @param {Function} _fn
|
|
* @returns {Array}
|
|
* @example
|
|
* ```js
|
|
* var aarray = [5,4,3,2,1];
|
|
* result = ax5.util.filter( aarray, function(){
|
|
* return this % 2;
|
|
* });
|
|
* console.log(result);
|
|
* // [5, 3, 1]
|
|
*
|
|
* var filObject = {a:1, s:"string", oa:{pickup:true, name:"AXISJ"}, os:{pickup:true, name:"AX5"}};
|
|
* result = ax5.util.filter( filObject, function(){
|
|
* return this.pickup;
|
|
* });
|
|
* console.log( ax5.util.toJson(result) );
|
|
* // [{"pickup": , "name": "AXISJ"}, {"pickup": , "name": "AX5"}]
|
|
* ```
|
|
*/
|
|
function filter(O, _fn) {
|
|
if (isNothing(O)) return [];
|
|
var k,
|
|
i = 0,
|
|
l = O.length,
|
|
results = [],
|
|
fnResult;
|
|
if (isObject(O)) {
|
|
for (k in O) {
|
|
if (typeof O[k] != "undefined") {
|
|
if (fnResult = _fn.call(O[k], k, O[k])) results.push(O[k]);
|
|
}
|
|
}
|
|
} else {
|
|
for (; i < l;) {
|
|
if (typeof O[i] != "undefined") {
|
|
if (fnResult = _fn.call(O[i], i, O[i])) results.push(O[i]);
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
return results;
|
|
}
|
|
|
|
/**
|
|
* Object를 JSONString 으로 반환합니다.
|
|
* @method ax5.util.toJson
|
|
* @param {Object|Array} O
|
|
* @returns {String} JSON
|
|
* @example
|
|
* ```js
|
|
* var ax = ax5.util;
|
|
* var myObject = {
|
|
* a:1, b:"2", c:{axj:"what", arrs:[0,2,"3"]},
|
|
* fn: function(abcdd){
|
|
* return abcdd;
|
|
* }
|
|
* };
|
|
* console.log( ax.toJson(myObject) );
|
|
* ```
|
|
*/
|
|
function toJson(O) {
|
|
var jsonString = "";
|
|
if (ax5.util.isArray(O)) {
|
|
var i = 0,
|
|
l = O.length;
|
|
jsonString += "[";
|
|
for (; i < l; i++) {
|
|
if (i > 0) jsonString += ",";
|
|
jsonString += toJson(O[i]);
|
|
}
|
|
jsonString += "]";
|
|
} else if (ax5.util.isObject(O)) {
|
|
jsonString += "{";
|
|
var jsonObjectBody = [];
|
|
each(O, function (key, value) {
|
|
jsonObjectBody.push('"' + key + '": ' + toJson(value));
|
|
});
|
|
jsonString += jsonObjectBody.join(", ");
|
|
jsonString += "}";
|
|
} else if (ax5.util.isString(O)) {
|
|
jsonString = '"' + O + '"';
|
|
} else if (ax5.util.isNumber(O)) {
|
|
jsonString = O;
|
|
} else if (ax5.util.isUndefined(O)) {
|
|
jsonString = "undefined";
|
|
} else if (ax5.util.isFunction(O)) {
|
|
jsonString = '"{Function}"';
|
|
} else {
|
|
jsonString = O;
|
|
}
|
|
return jsonString;
|
|
}
|
|
|
|
/**
|
|
* 관용의 JSON Parser
|
|
* @method ax5.util.parseJson
|
|
* @param {String} JSONString
|
|
* @param {Boolean} [force] - 강제 적용 여부 (json 문자열 검사를 무시하고 오브젝트 변환을 시도합니다.)
|
|
* @returns {Object}
|
|
* @example
|
|
* ```
|
|
* console.log(ax5.util.parseJson('{"a":1}'));
|
|
* // Object {a: 1}
|
|
* console.log(ax5.util.parseJson("{'a':1, 'b':'b'}"));
|
|
* // Object {a: 1, b: "b"}
|
|
* console.log(ax5.util.parseJson("{'a':1, 'b':function(){return 1;}}", true));
|
|
* // Object {a: 1, b: function}
|
|
* console.log(ax5.util.parseJson("{a:1}"));
|
|
* // Object {a: 1}
|
|
* console.log(ax5.util.parseJson("[1,2,3]"));
|
|
* // [1, 2, 3]
|
|
* console.log(ax5.util.parseJson("['1','2','3']"));
|
|
* // ["1", "2", "3"]
|
|
* console.log(ax5.util.parseJson("[{'a':'99'},'2','3']"));
|
|
* // [Object, "2", "3"]
|
|
* ```
|
|
*/
|
|
function parseJson(str, force) {
|
|
if (force || reIsJson.test(str)) {
|
|
try {
|
|
return new Function('', 'return ' + str)();
|
|
} catch (e) {
|
|
return { error: 500, msg: 'syntax error' };
|
|
}
|
|
} else {
|
|
return { error: 500, msg: 'syntax error' };
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 인자의 타입을 반환합니다.
|
|
* @method ax5.util.getType
|
|
* @param {Object|Array|String|Number|Element|Etc} O
|
|
* @returns {String} window|element|object|array|function|string|number|undefined|nodelist
|
|
* @example
|
|
* ```js
|
|
* var axf = ax5.util;
|
|
* var a = 11;
|
|
* var b = "11";
|
|
* console.log( axf.getType(a) );
|
|
* console.log( axf.getType(b) );
|
|
* ```
|
|
*/
|
|
function getType(O) {
|
|
var typeName;
|
|
if (O != null && O == O.window) {
|
|
typeName = "window";
|
|
} else if (!!(O && O.nodeType == 1)) {
|
|
typeName = "element";
|
|
} else if (!!(O && O.nodeType == 11)) {
|
|
typeName = "fragment";
|
|
} else if (O === null) {
|
|
typeName = "null";
|
|
} else if (typeof O === "undefined") {
|
|
typeName = "undefined";
|
|
} else if (_toString.call(O) == "[object Object]") {
|
|
typeName = "object";
|
|
} else if (_toString.call(O) == "[object Array]") {
|
|
typeName = "array";
|
|
} else if (_toString.call(O) == "[object String]") {
|
|
typeName = "string";
|
|
} else if (_toString.call(O) == "[object Number]") {
|
|
typeName = "number";
|
|
} else if (_toString.call(O) == "[object NodeList]") {
|
|
typeName = "nodelist";
|
|
} else if (typeof O === "function") {
|
|
typeName = "function";
|
|
}
|
|
return typeName;
|
|
}
|
|
|
|
/**
|
|
* 오브젝트가 window 인지 판단합니다.
|
|
* @method ax5.util.isWindow
|
|
* @param {Object} O
|
|
* @returns {Boolean}
|
|
*/
|
|
function isWindow(O) {
|
|
return O != null && O == O.window;
|
|
}
|
|
|
|
/**
|
|
* 오브젝트가 HTML 엘리먼트여부인지 판단합니다.
|
|
* @method ax5.util.isElement
|
|
* @param {Object} O
|
|
* @returns {Boolean}
|
|
*/
|
|
function isElement(O) {
|
|
return !!(O && (O.nodeType == 1 || O.nodeType == 11));
|
|
}
|
|
|
|
/**
|
|
* 오브젝트가 Object인지 판단합니다.
|
|
* @method ax5.util.isObject
|
|
* @param {Object} O
|
|
* @returns {Boolean}
|
|
*/
|
|
function isObject(O) {
|
|
return _toString.call(O) == "[object Object]";
|
|
}
|
|
|
|
/**
|
|
* 오브젝트가 Array인지 판단합니다.
|
|
* @method ax5.util.isArray
|
|
* @param {Object} O
|
|
* @returns {Boolean}
|
|
*/
|
|
function isArray(O) {
|
|
return _toString.call(O) == "[object Array]";
|
|
}
|
|
|
|
/**
|
|
* 오브젝트가 Function인지 판단합니다.
|
|
* @method ax5.util.isFunction
|
|
* @param {Object} O
|
|
* @returns {Boolean}
|
|
*/
|
|
function isFunction(O) {
|
|
return typeof O === "function";
|
|
}
|
|
|
|
/**
|
|
* 오브젝트가 String인지 판단합니다.
|
|
* @method ax5.util.isString
|
|
* @param {Object} O
|
|
* @returns {Boolean}
|
|
*/
|
|
function isString(O) {
|
|
return _toString.call(O) == "[object String]";
|
|
}
|
|
|
|
/**
|
|
* 오브젝트가 Number인지 판단합니다.
|
|
* @method ax5.util.isNumber
|
|
* @param {Object} O
|
|
* @returns {Boolean}
|
|
*/
|
|
function isNumber(O) {
|
|
return _toString.call(O) == "[object Number]";
|
|
}
|
|
|
|
/**
|
|
* 오브젝트가 NodeList인지 판단합니다.
|
|
* @method ax5.util.isNodelist
|
|
* @param {Object} O
|
|
* @returns {Boolean}
|
|
*/
|
|
function isNodelist(O) {
|
|
return !!(_toString.call(O) == "[object NodeList]" || typeof O !== "undefined" && O && O[0] && O[0].nodeType == 1);
|
|
}
|
|
|
|
/**
|
|
* 오브젝트가 undefined인지 판단합니다.
|
|
* @method ax5.util.isUndefined
|
|
* @param {Object} O
|
|
* @returns {Boolean}
|
|
*/
|
|
function isUndefined(O) {
|
|
return typeof O === "undefined";
|
|
}
|
|
|
|
/**
|
|
* 오브젝트가 undefined이거나 null이거나 빈값인지 판단합니다.
|
|
* @method ax5.util.isNothing
|
|
* @param {Object} O
|
|
* @returns {Boolean}
|
|
*/
|
|
function isNothing(O) {
|
|
return typeof O === "undefined" || O === null || O === "";
|
|
}
|
|
|
|
/**
|
|
* 오브젝트가 날자값인지 판단합니다.
|
|
* @method ax5.util.isDate
|
|
* @param {Date} O
|
|
* @returns {Boolean}
|
|
* @example
|
|
* ```js
|
|
* ax5.util.isDate('2016-09-30');
|
|
* // false
|
|
* ax5.util.isDate( new Date('2016-09-30') );
|
|
* // true
|
|
* ```
|
|
*/
|
|
function isDate(O) {
|
|
return O instanceof Date && !isNaN(O.valueOf());
|
|
}
|
|
|
|
function isDateFormat(O) {
|
|
var result = false;
|
|
if (!O) {} else if (O instanceof Date && !isNaN(O.valueOf())) {
|
|
result = true;
|
|
} else {
|
|
if (O.length > 7) {
|
|
if (date(O) instanceof Date) {
|
|
return true;
|
|
}
|
|
}
|
|
O = O.replace(/\D/g, '');
|
|
if (O.length > 7) {
|
|
var mm = O.substr(4, 2),
|
|
dd = O.substr(6, 2);
|
|
O = date(O);
|
|
if (O.getMonth() == mm - 1 && O.getDate() == dd) {
|
|
result = true;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* 오브젝트의 첫번째 아이템을 반환합니다.
|
|
* @method ax5.util.first
|
|
* @param {Object|Array} O
|
|
* @returns {Object}
|
|
* @example
|
|
* ```js
|
|
* ax5.util.first({a:1, b:2});
|
|
* // Object {a: 1}
|
|
* ax5.util.first([1,2,3,4]);
|
|
* // 1
|
|
* ```
|
|
*/
|
|
function first(O) {
|
|
if (isObject(O)) {
|
|
var keys = Object.keys(O);
|
|
var item = {};
|
|
item[keys[0]] = O[keys[0]];
|
|
return item;
|
|
} else if (isArray(O)) {
|
|
return O[0];
|
|
} else {
|
|
console.error("ax5.util.object.first", "argument type error");
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 오브젝트의 마지막 아이템을 반환합니다.
|
|
* @method ax5.util.last
|
|
* @param {Object|Array} O
|
|
* @returns {Object}
|
|
* @example
|
|
* ```js
|
|
* ax5.util.last({a:1, b:2});
|
|
* // Object {b: 2}
|
|
* ax5.util.last([1,2,3,4]);
|
|
* // 4
|
|
* ```
|
|
*/
|
|
function last(O) {
|
|
if (isObject(O)) {
|
|
var keys = Object.keys(O);
|
|
var item = {};
|
|
item[keys[keys.length - 1]] = O[keys[keys.length - 1]];
|
|
return item;
|
|
} else if (isArray(O)) {
|
|
return O[O.length - 1];
|
|
} else {
|
|
console.error("ax5.util.object.last", "argument type error");
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 쿠키를 설정합니다.
|
|
* @method ax5.util.setCookie
|
|
* @param {String} cname - 쿠키이름
|
|
* @param {String} cvalue - 쿠키값
|
|
* @param {Number} [exdays] - 쿠키 유지일수
|
|
* @param {Object} [opts] - path, domain 설정 옵션
|
|
* @example
|
|
* ```js
|
|
* ax5.util.setCookie("jslib", "AX5");
|
|
* ax5.util.setCookie("jslib", "AX5", 3);
|
|
* ax5.util.setCookie("jslib", "AX5", 3, {path:"/", domain:".axisj.com"});
|
|
* ```
|
|
*/
|
|
function setCookie(cn, cv, exdays, opts) {
|
|
var expire;
|
|
if (typeof exdays === "number") {
|
|
expire = new Date();
|
|
expire.setDate(expire.getDate() + exdays);
|
|
}
|
|
opts = opts || {};
|
|
return doc.cookie = [escape(cn), '=', escape(cv), expire ? "; expires=" + expire.toUTCString() : "", // use expires attribute, max-age is not supported by IE
|
|
opts.path ? "; path=" + opts.path : "", opts.domain ? "; domain=" + opts.domain : "", opts.secure ? "; secure" : ""].join("");
|
|
}
|
|
|
|
/**
|
|
* 쿠키를 가져옵니다.
|
|
* @method ax5.util.getCookie
|
|
* @param {String} cname
|
|
* @returns {String} cookie value
|
|
* @example
|
|
* ```js
|
|
* ax5.util.getCookie("jslib");
|
|
* ```
|
|
*/
|
|
function getCookie(cname) {
|
|
var name = cname + "=";
|
|
var ca = doc.cookie.split(';'),
|
|
i = 0,
|
|
l = ca.length;
|
|
for (; i < l; i++) {
|
|
var c = ca[i];
|
|
while (c.charAt(0) == ' ') {
|
|
c = c.substring(1);
|
|
}if (c.indexOf(name) != -1) return unescape(c.substring(name.length, c.length));
|
|
}
|
|
return "";
|
|
}
|
|
|
|
/**
|
|
* jsonString 으로 alert 합니다.
|
|
* @method ax5.util.alert
|
|
* @param {Object|Array|String|Number} O
|
|
* @returns {Object|Array|String|Number} O
|
|
* @example ```js
|
|
* ax5.util.alert({a:1,b:2});
|
|
* ax5.util.alert("정말?");
|
|
* ```
|
|
*/
|
|
function alert(O) {
|
|
win.alert(toJson(O));
|
|
return O;
|
|
}
|
|
|
|
/**
|
|
* 문자열의 특정 문자열까지 잘라주거나 원하는 포지션까지 잘라줍니다.
|
|
* @method ax5.util.left
|
|
* @param {String} str - 문자열
|
|
* @param {String|Number} pos - 찾을 문자열 또는 포지션
|
|
* @returns {String}
|
|
* @example
|
|
* ```js
|
|
* ax5.util.left("abcd.efd", 3);
|
|
* // abc
|
|
* ax5.util.left("abcd.efd", ".");
|
|
* // abcd
|
|
* ```
|
|
*/
|
|
function left(str, pos) {
|
|
if (typeof str === "undefined" || typeof pos === "undefined") return "";
|
|
if (isString(pos)) {
|
|
return str.indexOf(pos) > -1 ? str.substr(0, str.indexOf(pos)) : "";
|
|
} else if (isNumber(pos)) {
|
|
return str.substr(0, pos);
|
|
} else {
|
|
return "";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 문자열의 특정 문자열까지 잘라주거나 원하는 포지션까지 잘라줍니다.
|
|
* @method ax5.util.right
|
|
* @param {String} str - 문자열
|
|
* @param {String|Number} pos - 찾을 문자열 또는 포지션
|
|
* @returns {String}
|
|
* @example
|
|
* ```js
|
|
* ax5.util.right("abcd.efd", 3);
|
|
* // efd
|
|
* ax5.util.right("abcd.efd", ".");
|
|
* // efd
|
|
* ```
|
|
*/
|
|
function right(str, pos) {
|
|
if (typeof str === "undefined" || typeof pos === "undefined") return "";
|
|
str = '' + str;
|
|
if (isString(pos)) {
|
|
return str.lastIndexOf(pos) > -1 ? str.substr(str.lastIndexOf(pos) + 1) : "";
|
|
} else if (isNumber(pos)) {
|
|
return str.substr(str.length - pos);
|
|
} else {
|
|
return "";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* css형 문자열이나 특수문자가 포함된 문자열을 카멜케이스로 바꾸어 반환합니다.
|
|
* @method ax5.util.camelCase
|
|
* @param {String} str
|
|
* @returns {String}
|
|
* @example
|
|
* ```js
|
|
* ax5.util.camelCase("inner-width");
|
|
* ax5.util.camelCase("innerWidth");
|
|
* // innerWidth
|
|
* ```
|
|
*/
|
|
function camelCase(str) {
|
|
return str.replace(reMs, "ms-").replace(reSnakeCase, function (all, letter) {
|
|
return letter.toUpperCase();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* css형 문자열이나 카멜케이스문자열을 스네이크 케이스 문자열로 바꾸어 반환합니다.
|
|
* @method ax5.util.snakeCase
|
|
* @param {String} str
|
|
* @returns {String}
|
|
* @example
|
|
* ```js
|
|
* ax5.util.snakeCase("innerWidth");
|
|
* ax5.util.snakeCase("inner-Width");
|
|
* ax5.util.snakeCase("innerWidth");
|
|
* // inner-width
|
|
* ```
|
|
*/
|
|
function snakeCase(str) {
|
|
return camelCase(str).replace(reCamelCase, function (all, letter) {
|
|
return "-" + letter.toLowerCase();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 문자열에서 -. 을 제외한 모든 문자열을 제거하고 숫자로 반환합니다. 옵션에 따라 원하는 형식의 숫자로 변환 할 수 도 있습니다.
|
|
* @method ax5.util.number
|
|
* @param {String|Number} str
|
|
* @param {Object} cond - 옵션
|
|
* @returns {String|Number}
|
|
* @example
|
|
* ```js
|
|
* var cond = {
|
|
* round: {Number|Boolean} - 반올림할 자릿수,
|
|
* money: {Boolean} - 통화,
|
|
* abs: {Boolean} - 절대값,
|
|
* byte: {Boolean} - 바이트
|
|
* }
|
|
*
|
|
* console.log(ax5.util.number(123456789.678, {round:1}));
|
|
* console.log(ax5.util.number(123456789.678, {round:1, money:true}));
|
|
* console.log(ax5.util.number(123456789.678, {round:2, byte:true}));
|
|
* console.log(ax5.util.number(-123456789.8888, {abs:true, round:2, money:true}));
|
|
* console.log(ax5.util.number("A-1234~~56789.8~888PX", {abs:true, round:2, money:true}));
|
|
*
|
|
* //123456789.7
|
|
* //123,456,789.7
|
|
* //117.7MB
|
|
* //123,456,789.89
|
|
* //123,456,789.89
|
|
* ```
|
|
*/
|
|
function number(str, cond) {
|
|
var result,
|
|
pair = ('' + str).split(reDot),
|
|
isMinus,
|
|
returnValue;
|
|
|
|
isMinus = Number(pair[0].replace(/,/g, "")) < 0 || pair[0] == "-0";
|
|
returnValue = 0.0;
|
|
pair[0] = pair[0].replace(reInt, "");
|
|
|
|
if (pair[1]) {
|
|
pair[1] = pair[1].replace(reNotNum, "");
|
|
returnValue = Number(pair[0] + "." + pair[1]) || 0;
|
|
} else {
|
|
returnValue = Number(pair[0]) || 0;
|
|
}
|
|
result = isMinus ? -returnValue : returnValue;
|
|
|
|
each(cond, function (k, c) {
|
|
if (k == "round") {
|
|
if (isNumber(c)) {
|
|
if (c < 0) {
|
|
result = +(Math.round(result + "e-" + Math.abs(c)) + "e+" + Math.abs(c));
|
|
} else {
|
|
result = +(Math.round(result + "e+" + c) + "e-" + c);
|
|
}
|
|
} else {
|
|
result = Math.round(result);
|
|
}
|
|
}
|
|
if (k == "floor") {
|
|
result = Math.floor(result);
|
|
}
|
|
if (k == "ceil") {
|
|
result = Math.ceil(result);
|
|
} else if (k == "money") {
|
|
result = function (val) {
|
|
var txtNumber = '' + val;
|
|
if (isNaN(txtNumber) || txtNumber == "") {
|
|
return "";
|
|
} else {
|
|
var arrNumber = txtNumber.split('.');
|
|
arrNumber[0] += '.';
|
|
do {
|
|
arrNumber[0] = arrNumber[0].replace(reMoneySplit, '$1,$2');
|
|
} while (reMoneySplit.test(arrNumber[0]));
|
|
if (arrNumber.length > 1) {
|
|
return arrNumber.join('');
|
|
} else {
|
|
return arrNumber[0].split('.')[0];
|
|
}
|
|
}
|
|
}(result);
|
|
} else if (k == "abs") {
|
|
result = Math.abs(Number(result));
|
|
} else if (k == "byte") {
|
|
result = function (val) {
|
|
val = Number(result);
|
|
var nUnit = "KB";
|
|
var myByte = val / 1024;
|
|
if (myByte / 1024 > 1) {
|
|
nUnit = "MB";
|
|
myByte = myByte / 1024;
|
|
}
|
|
if (myByte / 1024 > 1) {
|
|
nUnit = "GB";
|
|
myByte = myByte / 1024;
|
|
}
|
|
return number(myByte, { round: 1 }) + nUnit;
|
|
}(result);
|
|
}
|
|
});
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* 배열 비슷한 오브젝트를 배열로 변환해줍니다.
|
|
* @method ax5.util.toArray
|
|
* @param {Object|Elements|Arguments} O
|
|
* @returns {Array}
|
|
* @example
|
|
* ```js
|
|
* ax5.util.toArray(arguments);
|
|
* //
|
|
* ```
|
|
*/
|
|
function toArray(O) {
|
|
if (typeof O.length != "undefined") return Array.prototype.slice.call(O);
|
|
return [];
|
|
}
|
|
|
|
/**
|
|
* 첫번째 인자에 두번째 인자 아이템을 합쳐줍니다. concat과 같은 역할을 하지만. 인자가 Array타입이 아니어도 됩니다.
|
|
* @method ax5.util.merge
|
|
* @param {Array|ArrayLike} first
|
|
* @param {Array|ArrayLike} second
|
|
* @returns {Array} first
|
|
* @example
|
|
* ```
|
|
*
|
|
* ```
|
|
*/
|
|
function merge(first, second) {
|
|
var l = second.length,
|
|
i = first.length,
|
|
j = 0;
|
|
|
|
if (typeof l === "number") {
|
|
for (; j < l; j++) {
|
|
first[i++] = second[j];
|
|
}
|
|
} else {
|
|
while (second[j] !== undefined) {
|
|
first[i++] = second[j++];
|
|
}
|
|
}
|
|
|
|
first.length = i;
|
|
|
|
return first;
|
|
}
|
|
|
|
/**
|
|
* 오브젝트를 파라미터형식으로 또는 파라미터를 오브젝트 형식으로 변환합니다.
|
|
* @method ax5.util.param
|
|
* @param {Object|Array|String} O
|
|
* @param {String} [cond] - param|object
|
|
* @returns {Object|String}
|
|
* @example
|
|
* ```
|
|
* ax5.util.param({a:1,b:'1232'}, "param");
|
|
* ax5.util.param("a=1&b=1232", "param");
|
|
* // "a=1&b=1232"
|
|
* ax5.util.param("a=1&b=1232");
|
|
* // {a: "1", b: "1232"}
|
|
* ```
|
|
*/
|
|
function param(O, cond) {
|
|
var p;
|
|
if (isString(O) && typeof cond !== "undefined" && cond == "param") {
|
|
return O;
|
|
} else if (isString(O) && typeof cond !== "undefined" && cond == "object" || isString(O) && typeof cond === "undefined") {
|
|
p = {};
|
|
each(O.split(reAmp), function () {
|
|
var item = this.split(reEq);
|
|
if (!p[item[0]]) p[item[0]] = item[1];else {
|
|
if (isString(p[item[0]])) p[item[0]] = [p[item[0]]];
|
|
p[item[0]].push(item[1]);
|
|
}
|
|
});
|
|
return p;
|
|
} else {
|
|
p = [];
|
|
each(O, function (k, v) {
|
|
p.push(k + "=" + escape(v));
|
|
});
|
|
return p.join('&');
|
|
}
|
|
}
|
|
|
|
function encode(s) {
|
|
return encodeURIComponent(s);
|
|
}
|
|
|
|
function decode(s) {
|
|
return decodeURIComponent(s);
|
|
}
|
|
|
|
function error() {
|
|
ax5.info.onerror.apply(this, arguments);
|
|
}
|
|
|
|
function localDate(yy, mm, dd, hh, mi, ss) {
|
|
var utcD, localD;
|
|
localD = new Date();
|
|
if (mm < 0) mm = 0;
|
|
if (typeof hh === "undefined") hh = 12;
|
|
if (typeof mi === "undefined") mi = 0;
|
|
utcD = new Date(Date.UTC(yy, mm, dd || 1, hh, mi, ss || 0));
|
|
|
|
if (mm == 0 && dd == 1 && utcD.getUTCHours() + utcD.getTimezoneOffset() / 60 < 0) {
|
|
utcD.setUTCHours(0);
|
|
} else {
|
|
utcD.setUTCHours(utcD.getUTCHours() + utcD.getTimezoneOffset() / 60);
|
|
}
|
|
return utcD;
|
|
}
|
|
|
|
/**
|
|
* 날짜 형식의 문자열이나 Date객체를 조건에 맞게 처리 한 후 원하는 return 값으로 반환합니다.
|
|
* @method ax5.util.date
|
|
* @param {String|Date} d
|
|
* @param {Object} cond
|
|
* @returns {Date|String}
|
|
* @example
|
|
* ```js
|
|
* ax5.util.date('2013-01-01'); // Tue Jan 01 2013 23:59:00 GMT+0900 (KST)
|
|
* ax5.util.date((new Date()), {add:{d:10}, return:'yyyy/MM/dd'}); // "2015/07/01"
|
|
* ax5.util.date('1919-03-01', {add:{d:10}, return:'yyyy/MM/dd hh:mm:ss'}); // "1919/03/11 23:59:00"
|
|
* ```
|
|
*/
|
|
function date(d, cond) {
|
|
var yy = void 0,
|
|
mm = void 0,
|
|
dd = void 0,
|
|
hh = void 0,
|
|
mi = void 0,
|
|
aDateTime = void 0,
|
|
aTimes = void 0,
|
|
aTime = void 0,
|
|
aDate = void 0,
|
|
va = void 0,
|
|
ISO_8601 = /^\d{4}(-\d\d(-\d\d(T\d\d:\d\d(:\d\d)?(\.\d+)?(([+-]\d\d:\d\d)|Z)?)?)?)?$/i,
|
|
ISO_8601_FULL = /^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d(\.\d+)?(([+-]\d\d:\d\d)|Z)?$/i;
|
|
|
|
if (isString(d)) {
|
|
if (d.length == 0) {
|
|
d = new Date();
|
|
} else if (d.length > 15) {
|
|
if (ISO_8601_FULL.test(d) || ISO_8601.test(d)) {
|
|
d = new Date(d);
|
|
} else {
|
|
aDateTime = d.split(/ /g), aTimes, aTime, aDate = aDateTime[0].split(/\D/g), yy = aDate[0];
|
|
mm = parseFloat(aDate[1]);
|
|
dd = parseFloat(aDate[2]);
|
|
aTime = aDateTime[1] || "09:00";
|
|
aTimes = aTime.substring(0, 5).split(":");
|
|
hh = parseFloat(aTimes[0]);
|
|
mi = parseFloat(aTimes[1]);
|
|
if (right(aTime, 2) === "AM" || right(aTime, 2) === "PM") hh += 12;
|
|
d = localDate(yy, mm - 1, dd, hh, mi);
|
|
}
|
|
} else if (d.length == 14) {
|
|
va = d.replace(/\D/g, "");
|
|
d = localDate(va.substr(0, 4), va.substr(4, 2) - 1, number(va.substr(6, 2)), number(va.substr(8, 2)), number(va.substr(10, 2)), number(va.substr(12, 2)));
|
|
} else if (d.length > 7) {
|
|
va = d.replace(/\D/g, "");
|
|
d = localDate(va.substr(0, 4), va.substr(4, 2) - 1, number(va.substr(6, 2)));
|
|
} else if (d.length > 4) {
|
|
va = d.replace(/\D/g, "");
|
|
d = localDate(va.substr(0, 4), va.substr(4, 2) - 1, 1);
|
|
} else if (d.length > 2) {
|
|
va = d.replace(/\D/g, "");
|
|
return localDate(va.substr(0, 4), va.substr(4, 2) - 1, 1);
|
|
} else {
|
|
d = new Date();
|
|
}
|
|
}
|
|
if (typeof cond === "undefined" || typeof d === "undefined") {
|
|
return d;
|
|
} else {
|
|
if ("add" in cond) {
|
|
d = function (_d, opts) {
|
|
var yy = void 0,
|
|
mm = void 0,
|
|
dd = void 0,
|
|
mxdd = void 0,
|
|
DyMilli = 1000 * 60 * 60 * 24;
|
|
|
|
if (typeof opts["d"] !== "undefined") {
|
|
_d.setTime(_d.getTime() + opts["d"] * DyMilli);
|
|
} else if (typeof opts["m"] !== "undefined") {
|
|
yy = _d.getFullYear();
|
|
mm = _d.getMonth();
|
|
dd = _d.getDate();
|
|
yy = yy + parseInt(opts["m"] / 12);
|
|
mm += opts["m"] % 12;
|
|
mxdd = daysOfMonth(yy, mm);
|
|
if (mxdd < dd) dd = mxdd;
|
|
_d = new Date(yy, mm, dd, 12);
|
|
} else if (typeof opts["y"] !== "undefined") {
|
|
_d.setTime(_d.getTime() + opts["y"] * 365 * DyMilli);
|
|
} else if (typeof opts["h"] !== "undefined") {
|
|
_d.setTime(_d.getTime() + opts["h"] * 1000 * 60 * 60);
|
|
}
|
|
|
|
return _d;
|
|
}(new Date(d), cond["add"]);
|
|
}
|
|
if ("set" in cond) {
|
|
d = function (_d, opts) {
|
|
var yy = void 0,
|
|
mm = void 0,
|
|
dd = void 0,
|
|
processor = {
|
|
"firstDayOfMonth": function firstDayOfMonth(date) {
|
|
yy = date.getFullYear();
|
|
mm = date.getMonth();
|
|
dd = 1;
|
|
return new Date(yy, mm, dd, 12);
|
|
},
|
|
"lastDayOfMonth": function lastDayOfMonth(date) {
|
|
yy = date.getFullYear();
|
|
mm = date.getMonth();
|
|
dd = daysOfMonth(yy, mm);
|
|
return new Date(yy, mm, dd, 12);
|
|
}
|
|
};
|
|
if (opts in processor) {
|
|
return processor[opts](_d);
|
|
} else {
|
|
return _d;
|
|
}
|
|
}(new Date(d), cond["set"]);
|
|
}
|
|
if ("return" in cond) {
|
|
return function () {
|
|
|
|
var fStr = cond["return"],
|
|
nY = void 0,
|
|
nM = void 0,
|
|
nD = void 0,
|
|
nH = void 0,
|
|
nMM = void 0,
|
|
nS = void 0,
|
|
nDW = void 0,
|
|
yre = void 0,
|
|
regY = void 0,
|
|
mre = void 0,
|
|
regM = void 0,
|
|
dre = void 0,
|
|
regD = void 0,
|
|
hre = void 0,
|
|
regH = void 0,
|
|
mire = void 0,
|
|
regMI = void 0,
|
|
sre = void 0,
|
|
regS = void 0,
|
|
dwre = void 0,
|
|
regDW = void 0;
|
|
|
|
nY = d.getUTCFullYear();
|
|
nM = setDigit(d.getMonth() + 1, 2);
|
|
nD = setDigit(d.getDate(), 2);
|
|
nH = setDigit(d.getHours(), 2);
|
|
nMM = setDigit(d.getMinutes(), 2);
|
|
nS = setDigit(d.getSeconds(), 2);
|
|
nDW = d.getDay();
|
|
|
|
yre = /[^y]*(yyyy)[^y]*/gi;
|
|
yre.exec(fStr);
|
|
regY = RegExp.$1;
|
|
mre = /[^m]*(MM)[^m]*/g;
|
|
mre.exec(fStr);
|
|
regM = RegExp.$1;
|
|
dre = /[^d]*(dd)[^d]*/gi;
|
|
dre.exec(fStr);
|
|
regD = RegExp.$1;
|
|
hre = /[^h]*(hh)[^h]*/gi;
|
|
hre.exec(fStr);
|
|
regH = RegExp.$1;
|
|
mire = /[^m]*(mm)[^i]*/g;
|
|
mire.exec(fStr);
|
|
regMI = RegExp.$1;
|
|
sre = /[^s]*(ss)[^s]*/gi;
|
|
sre.exec(fStr);
|
|
regS = RegExp.$1;
|
|
dwre = /[^d]*(dw)[^w]*/gi;
|
|
dwre.exec(fStr);
|
|
regDW = RegExp.$1;
|
|
|
|
if (regY === "yyyy") {
|
|
fStr = fStr.replace(regY, right(nY, regY.length));
|
|
}
|
|
if (regM === "MM") {
|
|
if (regM.length == 1) nM = d.getMonth() + 1;
|
|
fStr = fStr.replace(regM, nM);
|
|
}
|
|
if (regD === "dd") {
|
|
if (regD.length == 1) nD = d.getDate();
|
|
fStr = fStr.replace(regD, nD);
|
|
}
|
|
if (regH === "hh") {
|
|
fStr = fStr.replace(regH, nH);
|
|
}
|
|
if (regMI === "mm") {
|
|
fStr = fStr.replace(regMI, nMM);
|
|
}
|
|
if (regS === "ss") {
|
|
fStr = fStr.replace(regS, nS);
|
|
}
|
|
if (regDW == "dw") {
|
|
fStr = fStr.replace(regDW, info.weekNames[nDW].label);
|
|
}
|
|
return fStr;
|
|
}();
|
|
} else {
|
|
return d;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 인자인 날짜가 오늘부터 몇일전인지 반환합니다. 또는 인자인 날짜가 가까운 미래에 몇일 후인지 반환합니다.
|
|
* @method ax5.util.dday
|
|
* @param {String|Data} d
|
|
* @param {Object} cond
|
|
* @returns {Number}
|
|
* @example
|
|
* ```js
|
|
* ax5.util.dday('2016-01-29');
|
|
* // 1
|
|
* ax5.util.dday('2016-01-29', {today:'2016-01-28'});
|
|
* // 1
|
|
* ax5.util.dday('1977-03-29', {today:'2016-01-28', age:true});
|
|
* // 39
|
|
* ```
|
|
*/
|
|
function dday(d, cond) {
|
|
var memoryDay = date(d),
|
|
DyMilli = 1000 * 60 * 60 * 24,
|
|
today = new Date(),
|
|
diffnum,
|
|
thisYearMemoryDay;
|
|
|
|
function getDayTime(_d) {
|
|
return Math.floor(_d.getTime() / DyMilli) * DyMilli;
|
|
}
|
|
|
|
if (typeof cond === "undefined") {
|
|
diffnum = number((getDayTime(memoryDay) - getDayTime(today)) / DyMilli, { floor: true });
|
|
return diffnum;
|
|
} else {
|
|
diffnum = number((getDayTime(memoryDay) - getDayTime(today)) / DyMilli, { floor: true });
|
|
if (cond["today"]) {
|
|
today = date(cond.today);
|
|
diffnum = number((getDayTime(memoryDay) - getDayTime(today)) / DyMilli, { floor: true });
|
|
}
|
|
if (cond["thisYear"]) {
|
|
thisYearMemoryDay = new Date(today.getFullYear(), memoryDay.getMonth(), memoryDay.getDate());
|
|
diffnum = number((getDayTime(thisYearMemoryDay) - getDayTime(today)) / DyMilli, { floor: true });
|
|
if (diffnum < 0) {
|
|
thisYearMemoryDay = new Date(today.getFullYear() + 1, memoryDay.getMonth(), memoryDay.getDate());
|
|
diffnum = number((getDayTime(thisYearMemoryDay) - getDayTime(today)) / DyMilli, { floor: true });
|
|
}
|
|
}
|
|
if (cond["age"]) {
|
|
thisYearMemoryDay = new Date(today.getFullYear(), memoryDay.getMonth(), memoryDay.getDate());
|
|
diffnum = thisYearMemoryDay.getFullYear() - memoryDay.getFullYear();
|
|
}
|
|
|
|
return diffnum;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 인자인 날짜가 몇년 몇월의 몇번째 주차인지 반환합니다.
|
|
* @method ax5.util.weeksOfMonth
|
|
* @param {String|Data} d
|
|
* @returns {Object}
|
|
* @example
|
|
* ```js
|
|
* ax5.util.weeksOfMonth("2015-10-01"); // {year: 2015, month: 10, count: 1}
|
|
* ax5.util.weeksOfMonth("2015-09-19"); // {year: 2015, month: 9, count: 3}
|
|
* ```
|
|
*/
|
|
function weeksOfMonth(d) {
|
|
var myDate = date(d);
|
|
return {
|
|
year: myDate.getFullYear(),
|
|
month: myDate.getMonth() + 1,
|
|
count: parseInt(myDate.getDate() / 7 + 1)
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 년월에 맞는 날자수를 반환합니다.
|
|
* (new Date()).getMonth() 기준으로 월값을 보냅니다. "2월" 인경우 "1" 을 넘기게 됩니다.
|
|
* @method ax5.util.daysOfMonth
|
|
* @param {Number} y
|
|
* @param {Number} m
|
|
* @returns {Number}
|
|
* @examples
|
|
* ```js
|
|
* ax5.util.daysOfMonth(2015, 11); // 31
|
|
* ax5.util.daysOfMonth(2015, 1); // 28
|
|
* ```
|
|
*/
|
|
function daysOfMonth(y, m) {
|
|
if (m == 3 || m == 5 || m == 8 || m == 10) {
|
|
return 30;
|
|
} else if (m == 1) {
|
|
return y % 4 == 0 && y % 100 != 0 || y % 400 == 0 ? 29 : 28;
|
|
} else {
|
|
return 31;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 원하는 횟수 만큼 자릿수 맞춤 문자열을 포함한 문자열을 반환합니다.
|
|
* 문자열 길이보다 작은값을 보내면 무시됩니다.
|
|
* @method ax5.util.setDigit
|
|
* @param {String|Number} num
|
|
* @param {Number} length
|
|
* @param {String} [padder=0]
|
|
* @param {Number} [radix]
|
|
* @returns {String}
|
|
* @example
|
|
* ```
|
|
* ax5.util.setDigit(2016, 6)
|
|
* // "002016"
|
|
* ax5.util.setDigit(2016, 2)
|
|
* // "2016"
|
|
* ```
|
|
*/
|
|
function setDigit(num, length, padder, radix) {
|
|
var s = num.toString(radix || 10);
|
|
return times(padder || '0', length - s.length) + s;
|
|
}
|
|
|
|
/**
|
|
* 문자열을 지정된 수만큼 반복 합니다.
|
|
* @param {String} s
|
|
* @param {Number} count
|
|
* @returns {string}
|
|
* @example
|
|
* ```
|
|
* ax5.util.times(2016, 2)
|
|
* //"20162016"
|
|
* ```
|
|
*/
|
|
function times(s, count) {
|
|
return count < 1 ? '' : new Array(count + 1).join(s);
|
|
}
|
|
|
|
/**
|
|
* 타겟엘리먼트의 부모 엘리멘트 트리에서 원하는 조건의 엘리먼트를 얻습니다.
|
|
* @method ax5.util.findParentNode
|
|
* @param {Element} _target - target element
|
|
* @param {Object|Function} cond - 원하는 element를 찾을 조건
|
|
* @returns {Element}
|
|
* @example
|
|
* ```
|
|
* // cond 속성정의
|
|
* var cond = {
|
|
* tagname: {String} - 태그명 (ex. a, div, span..),
|
|
* clazz: {String} - 클래스명
|
|
* [, 그 외 찾고 싶은 attribute명들]
|
|
* };
|
|
* console.log(
|
|
* console.log(
|
|
* ax5.util.findParentNode(e.target, {tagname:"a", clazz:"ax-menu-handel", "data-custom-attr":"attr_value"})
|
|
* );
|
|
* // cond 함수로 처리하기
|
|
* jQuery('#id').bind("click.app_expand", function(e){
|
|
* var target = ax5.util.findParentNode(e.target, function(target){
|
|
* if($(target).hasClass("aside")){
|
|
* return true;
|
|
* }
|
|
* else{
|
|
* return true;
|
|
* }
|
|
* });
|
|
* //client-aside
|
|
* if(target.id !== "client-aside"){
|
|
* // some action
|
|
* }
|
|
* });
|
|
* ```
|
|
*/
|
|
|
|
function findParentNode(_target, cond) {
|
|
if (_target) {
|
|
while (function () {
|
|
var result = true;
|
|
if (typeof cond === "undefined") {
|
|
_target = _target.parentNode ? _target.parentNode : false;
|
|
} else if (isFunction(cond)) {
|
|
result = cond(_target);
|
|
} else if (isObject(cond)) {
|
|
for (var k in cond) {
|
|
if (k === "tagname") {
|
|
if (_target.tagName.toLocaleLowerCase() != cond[k]) {
|
|
result = false;
|
|
break;
|
|
}
|
|
} else if (k === "clazz" || k === "class_name") {
|
|
if ("className" in _target) {
|
|
var klasss = _target.className.split(reClassNameSplit);
|
|
var hasClass = false;
|
|
for (var a = 0; a < klasss.length; a++) {
|
|
if (klasss[a] == cond[k]) {
|
|
hasClass = true;
|
|
break;
|
|
}
|
|
}
|
|
result = hasClass;
|
|
} else {
|
|
result = false;
|
|
break;
|
|
}
|
|
} else {
|
|
// 그외 속성값들.
|
|
if (_target.getAttribute) {
|
|
if (_target.getAttribute(k) != cond[k]) {
|
|
result = false;
|
|
break;
|
|
}
|
|
} else {
|
|
result = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return !result;
|
|
}()) {
|
|
if (_target.parentNode && _target.parentNode.parentNode) {
|
|
_target = _target.parentNode;
|
|
} else {
|
|
_target = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return _target;
|
|
}
|
|
|
|
/**
|
|
* @method ax5.util.cssNumber
|
|
* @param {String|Number} val
|
|
* @returns {String}
|
|
* @example
|
|
* ```
|
|
* console.log(ax5.util.cssNumber("100px"))
|
|
* console.log(ax5.util.cssNumber("100%"))
|
|
* console.log(ax5.util.cssNumber("100"))
|
|
* console.log(ax5.util.cssNumber(100))
|
|
* console.log(ax5.util.cssNumber("!!100@#"))
|
|
* ```
|
|
*/
|
|
function cssNumber(val) {
|
|
var re = /\D?(\d+)([a-zA-Z%]*)/i,
|
|
found = ('' + val).match(re),
|
|
unit = found[2] || "px";
|
|
|
|
return found[1] + unit;
|
|
}
|
|
|
|
/**
|
|
* css string 및 object 를 넘기면 object 및 string 으로 변환되어 리턴됩니다.
|
|
* @method ax5.util.css
|
|
* @param {Object|String} val - CSS String or CSS Object
|
|
* @returns {String|Object}
|
|
* @example
|
|
* ```
|
|
* console.log(ax5.util.css({background: "#ccc", padding: "50px", width: "100px"}));
|
|
* //"background:#ccc;padding:50px;width:100px;"
|
|
* console.log(ax5.util.css('width:100px;padding: 50px; background: #ccc'));
|
|
* // object {width: "100px", padding: "50px", background: "#ccc"}
|
|
* ```
|
|
*/
|
|
function css(val) {
|
|
var returns;
|
|
if (isObject(val)) {
|
|
returns = '';
|
|
for (var k in val) {
|
|
returns += k + ':' + val[k] + ';';
|
|
}
|
|
return returns;
|
|
} else if (isString(val)) {
|
|
returns = {};
|
|
var valSplited = val.split(/[ ]*;[ ]*/g);
|
|
valSplited.forEach(function (v) {
|
|
if ((v = v.trim()) !== "") {
|
|
var vSplited = v.split(/[ ]*:[ ]*/g);
|
|
returns[vSplited[0]] = vSplited[1];
|
|
}
|
|
});
|
|
return returns;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @method ax5.util.stopEvent
|
|
* @param {Event} e
|
|
* @example
|
|
* ```
|
|
* ax5.util.stopEvent(e);
|
|
* ```
|
|
*/
|
|
function stopEvent(e) {
|
|
// 이벤트 중지 구문
|
|
if (!e) var e = window.event;
|
|
|
|
//e.cancelBubble is supported by IE -
|
|
// this will kill the bubbling process.
|
|
e.cancelBubble = true;
|
|
e.returnValue = false;
|
|
|
|
//e.stopPropagation works only in Firefox.
|
|
if (e.stopPropagation) e.stopPropagation();
|
|
if (e.preventDefault) e.preventDefault();
|
|
|
|
return false;
|
|
// 이벤트 중지 구문 끝
|
|
}
|
|
|
|
/**
|
|
* @method ax5.util.selectRange
|
|
* @param {Element} el
|
|
* @param {Element} offset
|
|
* @example
|
|
* ```
|
|
* ax5.util.selectRange($("#select-test-0")); // selectAll
|
|
* ax5.util.selectRange($("#select-test-0"), "selectAll"); //selectAll
|
|
* ax5.util.selectRange($("#select-test-0"), "start"); // focus on start
|
|
* ax5.util.selectRange($("#select-test-0"), "end"); // focus on end
|
|
* ax5.util.selectRange($("#select-test-0"), [1, 5]); // select 1~5
|
|
* ```
|
|
*/
|
|
var selectRange = function () {
|
|
var processor = {
|
|
'textRange': {
|
|
'selectAll': function selectAll(el, range, offset) {},
|
|
'arr': function arr(el, range, offset) {
|
|
range.moveStart("character", offset[0]); // todo ie node select 체크필요
|
|
range.collapse();
|
|
range.moveEnd("character", offset[1]);
|
|
},
|
|
'start': function start(el, range, offset) {
|
|
range.moveStart("character", 0);
|
|
range.collapse();
|
|
},
|
|
'end': function end(el, range, offset) {
|
|
range.moveStart("character", range.text.length);
|
|
range.collapse();
|
|
}
|
|
},
|
|
'range': {
|
|
'selectAll': function selectAll(el, range, offset) {
|
|
range.selectNodeContents(el);
|
|
},
|
|
'arr': function arr(el, range, offset) {
|
|
if (isObject(offset[0])) {
|
|
range.setStart(offset[0].node, offset[0].offset);
|
|
range.setEnd(offset[1].node, offset[1].offset);
|
|
} else {
|
|
range.setStart(el.firstChild, offset[0]);
|
|
range.setEnd(el.firstChild, offset[1]);
|
|
}
|
|
},
|
|
'start': function start(el, range, offset) {
|
|
range.selectNodeContents(el);
|
|
range.collapse(true);
|
|
},
|
|
'end': function end(el, range, offset) {
|
|
range.selectNodeContents(el);
|
|
range.collapse(false);
|
|
}
|
|
}
|
|
};
|
|
return function (el, offset) {
|
|
var range, rangeType, selection;
|
|
|
|
if (el instanceof jQuery) {
|
|
el = el.get(0);
|
|
}
|
|
if (!el) return;
|
|
|
|
// 레인지 타입 선택
|
|
if (doc.body.createTextRange) {
|
|
range = document.body.createTextRange();
|
|
range.moveToElementText(el);
|
|
rangeType = "textRange";
|
|
} else if (window.getSelection) {
|
|
selection = window.getSelection();
|
|
range = document.createRange();
|
|
rangeType = "range";
|
|
}
|
|
|
|
// range 적용
|
|
if (typeof offset == "undefined") {
|
|
processor[rangeType].selectAll.call(this, el, range, offset);
|
|
} else if (isArray(offset)) {
|
|
processor[rangeType].arr.call(this, el, range, offset);
|
|
} else {
|
|
for (var key in processor[rangeType]) {
|
|
if (offset == key) {
|
|
processor[rangeType][key].call(this, el, range, offset);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 포커스 및 셀렉트
|
|
if (doc.body.createTextRange) {
|
|
range.select();
|
|
el.focus();
|
|
} else if (window.getSelection) {
|
|
el.focus();
|
|
selection.removeAllRanges();
|
|
selection.addRange(range);
|
|
}
|
|
};
|
|
}();
|
|
|
|
/**
|
|
* 지정한 시간을 지연시켜 함수를 실행합니다.
|
|
* @method ax5.util.debounce
|
|
* @param {Function} func
|
|
* @param {Number} wait
|
|
* @param {Object} options
|
|
* @returns {debounced}
|
|
* @example
|
|
* ```js
|
|
* var debounceFn = ax5.util.debounce(function( val ) { console.log(val); }, 300);
|
|
* $(document.body).click(function(){
|
|
* debounceFn(new Date());
|
|
* });
|
|
* ```
|
|
*/
|
|
// https://github.com/lodash/lodash/blob/master/debounce.js
|
|
function debounce(func, wait, options) {
|
|
var lastArgs = void 0,
|
|
lastThis = void 0,
|
|
maxWait = void 0,
|
|
result = void 0,
|
|
timerId = void 0,
|
|
lastCallTime = void 0;
|
|
|
|
var lastInvokeTime = 0;
|
|
var leading = false;
|
|
var maxing = false;
|
|
var trailing = true;
|
|
|
|
if (typeof func != 'function') {
|
|
throw new TypeError('Expected a function');
|
|
}
|
|
wait = +wait || 0;
|
|
if (isObject(options)) {
|
|
leading = !!options.leading;
|
|
maxing = 'maxWait' in options;
|
|
maxWait = maxing ? Math.max(+options.maxWait || 0, wait) : maxWait;
|
|
trailing = 'trailing' in options ? !!options.trailing : trailing;
|
|
}
|
|
|
|
function invokeFunc(time) {
|
|
var args = lastArgs;
|
|
var thisArg = lastThis;
|
|
|
|
lastArgs = lastThis = undefined;
|
|
lastInvokeTime = time;
|
|
result = func.apply(thisArg, args);
|
|
return result;
|
|
}
|
|
|
|
function leadingEdge(time) {
|
|
// Reset any `maxWait` timer.
|
|
lastInvokeTime = time;
|
|
// Start the timer for the trailing edge.
|
|
timerId = setTimeout(timerExpired, wait);
|
|
// Invoke the leading edge.
|
|
return leading ? invokeFunc(time) : result;
|
|
}
|
|
|
|
function remainingWait(time) {
|
|
var timeSinceLastCall = time - lastCallTime;
|
|
var timeSinceLastInvoke = time - lastInvokeTime;
|
|
var result = wait - timeSinceLastCall;
|
|
|
|
return maxing ? Math.min(result, maxWait - timeSinceLastInvoke) : result;
|
|
}
|
|
|
|
function shouldInvoke(time) {
|
|
var timeSinceLastCall = time - lastCallTime;
|
|
var timeSinceLastInvoke = time - lastInvokeTime;
|
|
|
|
// Either this is the first call, activity has stopped and we're at the
|
|
// trailing edge, the system time has gone backwards and we're treating
|
|
// it as the trailing edge, or we've hit the `maxWait` limit.
|
|
return lastCallTime === undefined || timeSinceLastCall >= wait || timeSinceLastCall < 0 || maxing && timeSinceLastInvoke >= maxWait;
|
|
}
|
|
|
|
function timerExpired() {
|
|
var time = Date.now();
|
|
if (shouldInvoke(time)) {
|
|
return trailingEdge(time);
|
|
}
|
|
// Restart the timer.
|
|
timerId = setTimeout(timerExpired, remainingWait(time));
|
|
}
|
|
|
|
function trailingEdge(time) {
|
|
timerId = undefined;
|
|
|
|
// Only invoke if we have `lastArgs` which means `func` has been
|
|
// debounced at least once.
|
|
if (trailing && lastArgs) {
|
|
return invokeFunc(time);
|
|
}
|
|
lastArgs = lastThis = undefined;
|
|
return result;
|
|
}
|
|
|
|
function cancel() {
|
|
if (timerId !== undefined) {
|
|
clearTimeout(timerId);
|
|
}
|
|
lastInvokeTime = 0;
|
|
lastArgs = lastCallTime = lastThis = timerId = undefined;
|
|
}
|
|
|
|
function flush() {
|
|
return timerId === undefined ? result : trailingEdge(Date.now());
|
|
}
|
|
|
|
function debounced() {
|
|
var time = Date.now();
|
|
var isInvoking = shouldInvoke(time);
|
|
|
|
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
|
|
args[_key] = arguments[_key];
|
|
}
|
|
|
|
lastArgs = args;
|
|
lastThis = this;
|
|
lastCallTime = time;
|
|
|
|
if (isInvoking) {
|
|
if (timerId === undefined) {
|
|
return leadingEdge(lastCallTime);
|
|
}
|
|
if (maxing) {
|
|
// Handle invocations in a tight loop.
|
|
timerId = setTimeout(timerExpired, wait);
|
|
return invokeFunc(lastCallTime);
|
|
}
|
|
}
|
|
if (timerId === undefined) {
|
|
timerId = setTimeout(timerExpired, wait);
|
|
}
|
|
return result;
|
|
}
|
|
debounced.cancel = cancel;
|
|
debounced.flush = flush;
|
|
return debounced;
|
|
}
|
|
|
|
/**
|
|
* @method ax5.util.throttle
|
|
* @param func
|
|
* @param wait
|
|
* @param options
|
|
* @return {debounced}
|
|
*/
|
|
//https://github.com/lodash/lodash/blob/master/throttle.js
|
|
function throttle(func, wait, options) {
|
|
var leading = true;
|
|
var trailing = true;
|
|
|
|
if (typeof func != 'function') {
|
|
throw new TypeError('Expected a function');
|
|
}
|
|
if (isObject(options)) {
|
|
leading = 'leading' in options ? !!options.leading : leading;
|
|
trailing = 'trailing' in options ? !!options.trailing : trailing;
|
|
}
|
|
return debounce(func, wait, {
|
|
'leading': leading,
|
|
'maxWait': wait,
|
|
'trailing': trailing
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @method ax5.util.deepCopy
|
|
* @param {Object} obj
|
|
* @returns {Object}
|
|
* @example
|
|
* ```js
|
|
* var obj = [
|
|
* {name:"A", child:[{name:"a-1"}]},
|
|
* {name:"B", child:[{name:"b-1"}], callBack: function(){ console.log('callBack'); }}
|
|
* ];
|
|
* var copiedObj = ax5.util.deepCopy(obj)
|
|
* ```
|
|
*/
|
|
function deepCopy(obj) {
|
|
var r, l;
|
|
if ((typeof obj === 'undefined' ? 'undefined' : _typeof(obj)) == 'object') {
|
|
if (U.isArray(obj)) {
|
|
l = obj.length;
|
|
r = new Array(l);
|
|
for (var i = 0; i < l; i++) {
|
|
r[i] = deepCopy(obj[i]);
|
|
}
|
|
return r;
|
|
} else {
|
|
return jQuery.extend({}, obj);
|
|
}
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
/**
|
|
* HTML 문자열을 escape 처리합니다.
|
|
* "<" represents the < sign.
|
|
* ">" represents the > sign.
|
|
* "&" represents the & sign.
|
|
* "" represents the " mark.
|
|
* [Character entity references](https://www.w3.org/TR/html401/charset.html#h-5.3)
|
|
* @method ax5.util.escapeHtml
|
|
* @param {String} s
|
|
* @returns {string}
|
|
* @example
|
|
* ```
|
|
* ax5.util.escapeHtml('HTML <span>string</span> & "escape"')
|
|
* //"HTML <span>string</span> & "escape""
|
|
* ```
|
|
*/
|
|
function escapeHtml(s) {
|
|
if (_toString.call(s) != "[object String]") return s;
|
|
if (!s) return "";
|
|
return s.replace(/[\<\>\&\"]/gm, function (match) {
|
|
switch (match) {
|
|
case "<":
|
|
return "<";
|
|
case ">":
|
|
return ">";
|
|
case "&":
|
|
return "&";
|
|
case "\"":
|
|
return """;
|
|
default:
|
|
return match;
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* HTML 문자열을 unescape 처리합니다.
|
|
* escapeHtml를 참고하세요.
|
|
* @method ax5.util.unescapeHtml
|
|
* @param {String} s
|
|
* @returns {string}
|
|
* @example
|
|
* ```
|
|
* ax5.util.unescapeHtml('HTML <span>string</span> & "escape"')
|
|
* //"HTML <span>string</span> & "escape""
|
|
* ```
|
|
*/
|
|
function unescapeHtml(s) {
|
|
if (_toString.call(s) != "[object String]") return s;
|
|
if (!s) return "";
|
|
return s.replace(/(<)|(>)|(&)|(")/gm, function (match) {
|
|
switch (match) {
|
|
case "<":
|
|
return "<";
|
|
case ">":
|
|
return ">";
|
|
case "&":
|
|
return "&";
|
|
case """:
|
|
return "\"";
|
|
default:
|
|
return match;
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @method ax5.util.string
|
|
* @param {String} tmpl
|
|
* @param {*} args
|
|
* @return {ax5string}
|
|
* @example
|
|
* ```js
|
|
* ax5.util.string("{0} is dead, but {1} is alive! {0} {2}").format("ASP", "ASP.NET");
|
|
* ax5.util.string("{0} is dead, but {1} is alive! {0} {2}").format(["ASP", "ASP.NET"]);
|
|
* ax5.util.stinrg("{0} counts").format(100);
|
|
* ```
|
|
*/
|
|
function string(_string) {
|
|
return new function (_string) {
|
|
this.value = _string;
|
|
this.toString = function () {
|
|
return this.value;
|
|
};
|
|
/**
|
|
* @method ax5.util.string.format
|
|
* @returns {*}
|
|
*/
|
|
this.format = function () {
|
|
var args = [];
|
|
for (var i = 0, l = arguments.length; i < l; i++) {
|
|
args = args.concat(arguments[i]);
|
|
}
|
|
return this.value.replace(/{(\d+)}/g, function (match, number) {
|
|
return typeof args[number] != 'undefined' ? args[number] : match;
|
|
});
|
|
};
|
|
/**
|
|
* @method ax5.util.string.escape
|
|
* @returns {*}
|
|
*/
|
|
this.escape = function () {
|
|
return escapeHtml(this.value);
|
|
};
|
|
/**
|
|
* @method ax5.util.string.unescape
|
|
* @returns {*}
|
|
*/
|
|
this.unescape = function () {
|
|
return unescapeHtml(this.value);
|
|
};
|
|
/**
|
|
* @method ax5.util.string.encode
|
|
* @returns {*}
|
|
*/
|
|
this.encode = function () {
|
|
return encode(this.value);
|
|
};
|
|
/**
|
|
* @method ax5.util.string.decode
|
|
* @returns {*}
|
|
*/
|
|
this.decode = function () {
|
|
return decode(this.value);
|
|
};
|
|
/**
|
|
* @method ax5.util.string.left
|
|
* @param {String|Number} pos - 찾을 문자열 또는 포지션
|
|
* @returns {*}
|
|
*/
|
|
this.left = function (_pos) {
|
|
return left(this.value, _pos);
|
|
};
|
|
/**
|
|
* @method ax5.util.string.right
|
|
* @param {String|Number} pos - 찾을 문자열 또는 포지션
|
|
* @returns {*}
|
|
*/
|
|
this.right = function (_pos) {
|
|
return right(this.value, _pos);
|
|
};
|
|
/**
|
|
* @method ax5.util.string.camelCase
|
|
* @returns {*}
|
|
*/
|
|
this.camelCase = function () {
|
|
return camelCase(this.value);
|
|
};
|
|
/**
|
|
* @method ax5.util.string.snakeCase
|
|
* @returns {*}
|
|
*/
|
|
this.snakeCase = function () {
|
|
return snakeCase(this.value);
|
|
};
|
|
}(_string);
|
|
}
|
|
|
|
/**
|
|
* @method ax5.util.color
|
|
* @param _hexColor
|
|
* @return {ax5color}
|
|
* @example
|
|
* ```js
|
|
* ax5.util.color("#ff3300").lighten(10).getHexValue()
|
|
* console.log(ax5.util.color("#ff3300").darken(10).getHexValue());
|
|
* ```
|
|
*/
|
|
function color(_hexColor) {
|
|
|
|
var matchers = function () {
|
|
|
|
// <http://www.w3.org/TR/css3-values/#integers>
|
|
var CSS_INTEGER = "[-\\+]?\\d+%?";
|
|
|
|
// <http://www.w3.org/TR/css3-values/#number-value>
|
|
var CSS_NUMBER = "[-\\+]?\\d*\\.\\d+%?";
|
|
|
|
// Allow positive/negative integer/number. Don't capture the either/or, just the entire outcome.
|
|
var CSS_UNIT = "(?:" + CSS_NUMBER + ")|(?:" + CSS_INTEGER + ")";
|
|
|
|
// Actual matching.
|
|
// Parentheses and commas are optional, but not required.
|
|
// Whitespace can take the place of commas or opening paren
|
|
var PERMISSIVE_MATCH3 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?";
|
|
var PERMISSIVE_MATCH4 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?";
|
|
|
|
return {
|
|
CSS_UNIT: new RegExp(CSS_UNIT),
|
|
rgb: new RegExp("rgb" + PERMISSIVE_MATCH3),
|
|
rgba: new RegExp("rgba" + PERMISSIVE_MATCH4),
|
|
hsl: new RegExp("hsl" + PERMISSIVE_MATCH3),
|
|
hsla: new RegExp("hsla" + PERMISSIVE_MATCH4),
|
|
hsv: new RegExp("hsv" + PERMISSIVE_MATCH3),
|
|
hsva: new RegExp("hsva" + PERMISSIVE_MATCH4),
|
|
hex3: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
|
|
hex6: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,
|
|
hex4: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
|
|
hex8: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/
|
|
};
|
|
}();
|
|
|
|
var convertObject = function convertObject(_color) {
|
|
var match = void 0;
|
|
if (match = matchers.rgb.exec(_color)) {
|
|
return { r: match[1], g: match[2], b: match[3] };
|
|
}
|
|
if (match = matchers.rgba.exec(_color)) {
|
|
return { r: match[1], g: match[2], b: match[3], a: match[4] };
|
|
}
|
|
if (match = matchers.hsl.exec(_color)) {
|
|
return { h: match[1], s: match[2], l: match[3] };
|
|
}
|
|
if (match = matchers.hsla.exec(_color)) {
|
|
return { h: match[1], s: match[2], l: match[3], a: match[4] };
|
|
}
|
|
if (match = matchers.hsv.exec(_color)) {
|
|
return { h: match[1], s: match[2], v: match[3] };
|
|
}
|
|
if (match = matchers.hsva.exec(_color)) {
|
|
return { h: match[1], s: match[2], v: match[3], a: match[4] };
|
|
}
|
|
if (match = matchers.hex8.exec(_color)) {
|
|
return {
|
|
r: parseInt(match[1], 16),
|
|
g: parseInt(match[2], 16),
|
|
b: parseInt(match[3], 16),
|
|
a: parseInt(match[4] / 255, 16),
|
|
format: "hex8"
|
|
};
|
|
}
|
|
if (match = matchers.hex6.exec(_color)) {
|
|
return {
|
|
r: parseInt(match[1], 16),
|
|
g: parseInt(match[2], 16),
|
|
b: parseInt(match[3], 16),
|
|
format: "hex"
|
|
};
|
|
}
|
|
if (match = matchers.hex4.exec(_color)) {
|
|
return {
|
|
r: parseInt(match[1] + '' + match[1], 16),
|
|
g: parseInt(match[2] + '' + match[2], 16),
|
|
b: parseInt(match[3] + '' + match[3], 16),
|
|
a: parseInt(match[4] + '' + match[4], 16),
|
|
format: "hex8"
|
|
};
|
|
}
|
|
if (match = matchers.hex3.exec(_color)) {
|
|
return {
|
|
r: parseInt(match[1] + '' + match[1], 16),
|
|
g: parseInt(match[2] + '' + match[2], 16),
|
|
b: parseInt(match[3] + '' + match[3], 16),
|
|
format: "hex"
|
|
};
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
function isOnePointZero(n) {
|
|
return typeof n == "string" && n.indexOf('.') != -1 && parseFloat(n) === 1;
|
|
}
|
|
|
|
function isPercentage(n) {
|
|
return typeof n === "string" && n.indexOf('%') != -1;
|
|
}
|
|
|
|
function convertToPercentage(n) {
|
|
if (n <= 1) {
|
|
n = n * 100 + "%";
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
function convertTo255(n) {
|
|
return ax5.util.number(Math.min(255, Math.max(n, 0)), { 'round': 2 });
|
|
}
|
|
|
|
function convertToHex(n) {
|
|
return setDigit(Math.round(n).toString(16), 2);
|
|
}
|
|
|
|
function bound01(n, max) {
|
|
if (isOnePointZero(n)) {
|
|
n = "100%";
|
|
}
|
|
|
|
var processPercent = isPercentage(n);
|
|
n = Math.min(max, Math.max(0, parseFloat(n)));
|
|
|
|
// Automatically convert percentage into number
|
|
if (processPercent) {
|
|
n = parseInt(n * max, 10) / 100;
|
|
}
|
|
|
|
// Handle floating point rounding errors
|
|
if (Math.abs(n - max) < 0.000001) {
|
|
return 1;
|
|
}
|
|
|
|
// Convert into [0, 1] range if it isn't already
|
|
return n % max / parseFloat(max);
|
|
}
|
|
|
|
function rgbToHsl(r, g, b) {
|
|
r = bound01(r, 255);
|
|
g = bound01(g, 255);
|
|
b = bound01(b, 255);
|
|
|
|
var max = Math.max(r, g, b),
|
|
min = Math.min(r, g, b);
|
|
var h,
|
|
s,
|
|
l = (max + min) / 2;
|
|
|
|
if (max == min) {
|
|
h = s = 0; // achromatic
|
|
} else {
|
|
var d = max - min;
|
|
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
switch (max) {
|
|
case r:
|
|
h = (g - b) / d + (g < b ? 6 : 0);
|
|
break;
|
|
case g:
|
|
h = (b - r) / d + 2;
|
|
break;
|
|
case b:
|
|
h = (r - g) / d + 4;
|
|
break;
|
|
}
|
|
|
|
h /= 6;
|
|
}
|
|
|
|
return { h: h, s: s, l: l };
|
|
}
|
|
|
|
function hslToRgb(h, s, l) {
|
|
var r = void 0,
|
|
g = void 0,
|
|
b = void 0;
|
|
|
|
h = bound01(h, 360);
|
|
s = bound01(s, 100);
|
|
l = bound01(l, 100);
|
|
|
|
function hue2rgb(p, q, t) {
|
|
if (t < 0) t += 1;
|
|
if (t > 1) t -= 1;
|
|
if (t < 1 / 6) return p + (q - p) * 6 * t;
|
|
if (t < 1 / 2) return q;
|
|
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
|
|
return p;
|
|
}
|
|
|
|
if (s === 0) {
|
|
r = g = b = l; // achromatic
|
|
} else {
|
|
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
|
var p = 2 * l - q;
|
|
r = hue2rgb(p, q, h + 1 / 3);
|
|
g = hue2rgb(p, q, h);
|
|
b = hue2rgb(p, q, h - 1 / 3);
|
|
}
|
|
|
|
return { r: r * 255, g: g * 255, b: b * 255 };
|
|
}
|
|
|
|
return new function (_color) {
|
|
this._originalValue = _color;
|
|
_color = convertObject(_color);
|
|
this.r = _color.r;
|
|
this.g = _color.g;
|
|
this.b = _color.b;
|
|
this.a = _color.a || 1;
|
|
this._format = _color.format;
|
|
this._hex = convertToHex(this.r) + convertToHex(this.g) + convertToHex(this.b);
|
|
|
|
this.getHexValue = function () {
|
|
return this._hex;
|
|
};
|
|
|
|
this.lighten = function (amount) {
|
|
amount = amount === 0 ? 0 : amount || 10;
|
|
var hsl = rgbToHsl(this.r, this.g, this.b),
|
|
rgb = {};
|
|
|
|
hsl.l += amount / 100;
|
|
hsl.l = Math.min(1, Math.max(0, hsl.l));
|
|
hsl.h = hsl.h * 360;
|
|
|
|
rgb = hslToRgb(hsl.h, convertToPercentage(hsl.s), convertToPercentage(hsl.l));
|
|
|
|
return color('rgba(' + convertTo255(rgb.r) + ', ' + convertTo255(rgb.g) + ', ' + convertTo255(rgb.b) + ', ' + this.a + ')');
|
|
};
|
|
|
|
this.darken = function (amount) {
|
|
amount = amount === 0 ? 0 : amount || 10;
|
|
var hsl = rgbToHsl(this.r, this.g, this.b),
|
|
rgb = {};
|
|
|
|
hsl.l -= amount / 100;
|
|
hsl.l = Math.min(1, Math.max(0, hsl.l));
|
|
hsl.h = hsl.h * 360;
|
|
|
|
rgb = hslToRgb(hsl.h, convertToPercentage(hsl.s), convertToPercentage(hsl.l));
|
|
|
|
return color('rgba(' + convertTo255(rgb.r) + ', ' + convertTo255(rgb.g) + ', ' + convertTo255(rgb.b) + ', ' + this.a + ')');
|
|
};
|
|
|
|
this.getBrightness = function () {
|
|
return (this.r * 299 + this.g * 587 + this.b * 114) / 1000;
|
|
};
|
|
|
|
this.isDark = function () {
|
|
return this.getBrightness() < 128;
|
|
};
|
|
|
|
this.isLight = function () {
|
|
return !this.isDark();
|
|
};
|
|
|
|
this.getHsl = function () {
|
|
var hsl = rgbToHsl(this.r, this.g, this.b);
|
|
hsl.l = Math.min(1, Math.max(0, hsl.l));
|
|
hsl.h = hsl.h * 360;
|
|
return {
|
|
h: hsl.h,
|
|
s: hsl.s,
|
|
l: hsl.l
|
|
};
|
|
};
|
|
}(_hexColor);
|
|
}
|
|
|
|
return {
|
|
alert: alert,
|
|
each: each,
|
|
map: map,
|
|
search: search,
|
|
reduce: reduce,
|
|
reduceRight: reduceRight,
|
|
filter: filter,
|
|
sum: sum,
|
|
avg: avg,
|
|
toJson: toJson,
|
|
parseJson: parseJson,
|
|
first: first,
|
|
last: last,
|
|
deepCopy: deepCopy,
|
|
|
|
left: left,
|
|
right: right,
|
|
getType: getType,
|
|
isWindow: isWindow,
|
|
isElement: isElement,
|
|
isObject: isObject,
|
|
isArray: isArray,
|
|
isFunction: isFunction,
|
|
isString: isString,
|
|
isNumber: isNumber,
|
|
isNodelist: isNodelist,
|
|
isUndefined: isUndefined,
|
|
isNothing: isNothing,
|
|
setCookie: setCookie,
|
|
getCookie: getCookie,
|
|
camelCase: camelCase,
|
|
snakeCase: snakeCase,
|
|
number: number,
|
|
toArray: toArray,
|
|
merge: merge,
|
|
param: param,
|
|
error: error,
|
|
date: date,
|
|
dday: dday,
|
|
daysOfMonth: daysOfMonth,
|
|
weeksOfMonth: weeksOfMonth,
|
|
setDigit: setDigit,
|
|
times: times,
|
|
findParentNode: findParentNode,
|
|
cssNumber: cssNumber,
|
|
css: css,
|
|
isDate: isDate,
|
|
isDateFormat: isDateFormat,
|
|
stopEvent: stopEvent,
|
|
selectRange: selectRange,
|
|
debounce: debounce,
|
|
throttle: throttle,
|
|
escapeHtml: escapeHtml,
|
|
unescapeHtml: unescapeHtml,
|
|
|
|
string: string,
|
|
color: color
|
|
};
|
|
}();
|
|
|
|
if ((typeof module === 'undefined' ? 'undefined' : _typeof(module)) === "object" && _typeof(module.exports) === "object") {
|
|
module.exports = ax5;
|
|
} else {
|
|
root.ax5 = function () {
|
|
return ax5;
|
|
}(); // ax5.ui에 연결
|
|
}
|
|
}).call(typeof window !== "undefined" ? window : undefined);
|
|
|
|
ax5.def = {};
|
|
ax5.info.errorMsg["ax5dialog"] = {
|
|
"501": "Duplicate call error"
|
|
};
|
|
|
|
ax5.info.errorMsg["ax5picker"] = {
|
|
"401": "Can not find target element",
|
|
"402": "Can not find boundID",
|
|
"501": "Can not find content key"
|
|
};
|
|
|
|
ax5.info.errorMsg["single-uploader"] = {
|
|
"460": "There are no files to be uploaded.",
|
|
"461": "There is no uploaded files."
|
|
};
|
|
|
|
ax5.info.errorMsg["ax5calendar"] = {
|
|
"401": "Can not find target element"
|
|
};
|
|
|
|
ax5.info.errorMsg["ax5formatter"] = {
|
|
"401": "Can not find target element",
|
|
"402": "Can not find boundID",
|
|
"501": "Can not find pattern"
|
|
};
|
|
|
|
ax5.info.errorMsg["ax5menu"] = {
|
|
"501": "Can not find menu item"
|
|
};
|
|
|
|
ax5.info.errorMsg["ax5select"] = {
|
|
"401": "Can not find target element",
|
|
"402": "Can not find boundID",
|
|
"501": "Can not find option"
|
|
};
|
|
|
|
ax5.info.errorMsg["ax5combobox"] = {
|
|
"401": "Can not find target element",
|
|
"402": "Can not find boundID",
|
|
"501": "Can not find option"
|
|
};
|
|
// 필수 Ployfill 확장 구문
|
|
(function () {
|
|
'use strict';
|
|
|
|
var root = this,
|
|
re_trim = /^\s*|\s*$/g;
|
|
|
|
// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
|
|
if (!Object.keys) {
|
|
Object.keys = function () {
|
|
var hwp = Object.prototype.hasOwnProperty,
|
|
hdeb = !{ toString: null }.propertyIsEnumerable('toString'),
|
|
de = ['toString', 'toLocaleString', 'valueOf', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'constructor'],
|
|
del = de.length;
|
|
|
|
return function (obj) {
|
|
if ((typeof obj === 'undefined' ? 'undefined' : _typeof(obj)) !== 'object' && (typeof obj !== 'function' || obj === null)) throw new TypeError('type err');
|
|
var r = [],
|
|
prop,
|
|
i;
|
|
for (prop in obj) {
|
|
if (hwp.call(obj, prop)) r.push(prop);
|
|
}if (hdeb) {
|
|
for (i = 0; i < del; i++) {
|
|
if (hwp.call(obj, de[i])) r.push(de[i]);
|
|
}
|
|
}
|
|
return r;
|
|
};
|
|
}();
|
|
}
|
|
|
|
// ES5 15.4.4.18 Array.prototype.forEach ( callbackfn [ , thisArg ] )
|
|
// From https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/forEach
|
|
if (!Array.prototype.forEach) {
|
|
Array.prototype.forEach = function (fun /*, thisp */) {
|
|
if (this === void 0 || this === null) {
|
|
throw TypeError();
|
|
}
|
|
var t = Object(this);
|
|
var len = t.length >>> 0;
|
|
if (typeof fun !== "function") {
|
|
throw TypeError();
|
|
}
|
|
var thisp = arguments[1],
|
|
i;
|
|
for (i = 0; i < len; i++) {
|
|
if (i in t) {
|
|
fun.call(thisp, t[i], i, t);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
// ES5 15.3.4.5 Function.prototype.bind ( thisArg [, arg1 [, arg2, ... ]] )
|
|
// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind
|
|
if (!Function.prototype.bind) {
|
|
Function.prototype.bind = function (o) {
|
|
if (typeof this !== 'function') {
|
|
throw TypeError("function");
|
|
}
|
|
var slice = [].slice,
|
|
args = slice.call(arguments, 1),
|
|
self = this,
|
|
bound = function bound() {
|
|
return self.apply(this instanceof nop ? this : o, args.concat(slice.call(arguments)));
|
|
};
|
|
|
|
function nop() {}
|
|
|
|
nop.prototype = self.prototype;
|
|
bound.prototype = new nop();
|
|
return bound;
|
|
};
|
|
}
|
|
|
|
/*global document */
|
|
/**
|
|
* define document.querySelector & document.querySelectorAll for IE7
|
|
*
|
|
* A not very fast but small hack. The approach is taken from
|
|
* http://weblogs.asp.net/bleroy/archive/2009/08/31/queryselectorall-on-old-ie-versions-something-that-doesn-t-work.aspx
|
|
*
|
|
*/
|
|
(function () {
|
|
if (document.querySelectorAll || document.querySelector) {
|
|
return;
|
|
}
|
|
if (!document.createStyleSheet) return;
|
|
var style = document.createStyleSheet(),
|
|
select = function select(selector, maxCount) {
|
|
var all = document.all,
|
|
l = all.length,
|
|
i,
|
|
resultSet = [];
|
|
|
|
style.addRule(selector, "foo:bar");
|
|
for (i = 0; i < l; i += 1) {
|
|
if (all[i].currentStyle.foo === "bar") {
|
|
resultSet.push(all[i]);
|
|
if (resultSet.length > maxCount) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
style.removeRule(0);
|
|
return resultSet;
|
|
};
|
|
|
|
document.querySelectorAll = function (selector) {
|
|
return select(selector, Infinity);
|
|
};
|
|
document.querySelector = function (selector) {
|
|
return select(selector, 1)[0] || null;
|
|
};
|
|
})();
|
|
|
|
if (!String.prototype.trim) {
|
|
(function () {
|
|
String.prototype.trim = function () {
|
|
return this.replace(re_trim, '');
|
|
};
|
|
})();
|
|
}
|
|
|
|
if (!window.JSON) {
|
|
window.JSON = {
|
|
parse: function parse(sJSON) {
|
|
return new Function('', 'return ' + sJSON)();
|
|
},
|
|
stringify: function () {
|
|
var r = /["]/g,
|
|
_f;
|
|
return _f = function f(vContent) {
|
|
var result, i, j;
|
|
switch (result = typeof vContent === 'undefined' ? 'undefined' : _typeof(vContent)) {
|
|
case 'string':
|
|
return '"' + vContent.replace(r, '\\"') + '"';
|
|
case 'number':
|
|
case 'boolean':
|
|
return vContent.toString();
|
|
case 'undefined':
|
|
return 'undefined';
|
|
case 'function':
|
|
return '""';
|
|
case 'object':
|
|
if (!vContent) return 'null';
|
|
result = '';
|
|
if (vContent.splice) {
|
|
for (i = 0, j = vContent.length; i < j; i++) {
|
|
result += ',' + _f(vContent[i]);
|
|
}return '[' + result.substr(1) + ']';
|
|
} else {
|
|
for (i in vContent) {
|
|
if (vContent.hasOwnProperty(i) && vContent[i] !== undefined && typeof vContent[i] != 'function') result += ',"' + i + '":' + _f(vContent[i]);
|
|
}return '{' + result.substr(1) + '}';
|
|
}
|
|
}
|
|
};
|
|
}()
|
|
};
|
|
}
|
|
|
|
// splice ie8 <= polyfill
|
|
(function () {
|
|
if (!document.documentMode || document.documentMode >= 9) return false;
|
|
var _splice = Array.prototype.splice;
|
|
Array.prototype.splice = function () {
|
|
var args = Array.prototype.slice.call(arguments);
|
|
if (typeof args[1] === "undefined") args[1] = this.length - args[0];
|
|
return _splice.apply(this, args);
|
|
};
|
|
})();
|
|
|
|
/**
|
|
* Shim for "fixing" IE's lack of support (IE < 9) for applying slice
|
|
* on host objects like NamedNodeMap, NodeList, and HTMLCollection
|
|
* (technically, since host objects have been implementation-dependent,
|
|
* at least before ES6, IE hasn't needed to work this way).
|
|
* Also works on strings, fixes IE < 9 to allow an explicit undefined
|
|
* for the 2nd argument (as in Firefox), and prevents errors when
|
|
* called on other DOM objects.
|
|
*/
|
|
(function () {
|
|
'use strict';
|
|
|
|
var _slice = Array.prototype.slice;
|
|
|
|
try {
|
|
// Can't be used with DOM elements in IE < 9
|
|
_slice.call(document.documentElement);
|
|
} catch (e) {
|
|
// Fails in IE < 9
|
|
// This will work for genuine arrays, array-like objects,
|
|
// NamedNodeMap (attributes, entities, notations),
|
|
// NodeList (e.g., getElementsByTagName), HTMLCollection (e.g., childNodes),
|
|
// and will not fail on other DOM objects (as do DOM elements in IE < 9)
|
|
Array.prototype.slice = function (begin, end) {
|
|
// IE < 9 gets unhappy with an undefined end argument
|
|
end = typeof end !== 'undefined' ? end : this.length;
|
|
|
|
// For native Array objects, we use the native slice function
|
|
if (Object.prototype.toString.call(this) === '[object Array]') {
|
|
return _slice.call(this, begin, end);
|
|
}
|
|
|
|
// For array like object we handle it ourselves.
|
|
var i,
|
|
cloned = [],
|
|
size,
|
|
len = this.length;
|
|
|
|
// Handle negative value for "begin"
|
|
var start = begin || 0;
|
|
start = start >= 0 ? start : Math.max(0, len + start);
|
|
|
|
// Handle negative value for "end"
|
|
var upTo = typeof end == 'number' ? Math.min(end, len) : len;
|
|
if (end < 0) {
|
|
upTo = len + end;
|
|
}
|
|
|
|
// Actual expected size of the slice
|
|
size = upTo - start;
|
|
|
|
if (size > 0) {
|
|
cloned = new Array(size);
|
|
if (this.charAt) {
|
|
for (i = 0; i < size; i++) {
|
|
cloned[i] = this.charAt(start + i);
|
|
}
|
|
} else {
|
|
for (i = 0; i < size; i++) {
|
|
cloned[i] = this[start + i];
|
|
}
|
|
}
|
|
}
|
|
|
|
return cloned;
|
|
};
|
|
}
|
|
})();
|
|
|
|
// Console-polyfill. MIT license. https://github.com/paulmillr/console-polyfill
|
|
// Make it safe to do console.log() always.
|
|
(function (con) {
|
|
var prop, method;
|
|
var empty = {};
|
|
var dummy = function dummy() {};
|
|
var properties = 'memory'.split(',');
|
|
var methods = ('assert,clear,count,debug,dir,dirxml,error,exception,group,' + 'groupCollapsed,groupEnd,info,log,markTimeline,profile,profiles,profileEnd,' + 'show,table,time,timeEnd,timeline,timelineEnd,timeStamp,trace,warn').split(',');
|
|
while (prop = properties.pop()) {
|
|
con[prop] = con[prop] || empty;
|
|
}while (method = methods.pop()) {
|
|
con[method] = con[method] || dummy;
|
|
}
|
|
})(window.console || {}); // Using `this` for web workers.
|
|
|
|
|
|
// Modernizr style test
|
|
if (!(window.webkitMatchMedia || window.mozMatchMedia || window.oMatchMedia || window.msMatchMedia || window.matchMedia)) {
|
|
var root = document.getElementsByTagName('html')[0];
|
|
root.className += ' no-matchmedia';
|
|
}
|
|
|
|
/*! matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas, David Knight. Dual MIT/BSD license */
|
|
window.matchMedia || (window.matchMedia = function () {
|
|
"use strict";
|
|
|
|
// For browsers that support matchMedium api such as IE 9 and webkit
|
|
|
|
var styleMedia = window.styleMedia || window.media;
|
|
|
|
// For those that don't support matchMedium
|
|
if (!styleMedia) {
|
|
var style = document.createElement('style'),
|
|
script = document.getElementsByTagName('script')[0],
|
|
info = null;
|
|
|
|
style.type = 'text/css';
|
|
style.id = 'matchmediajs-test';
|
|
|
|
script.parentNode.insertBefore(style, script);
|
|
|
|
// 'style.currentStyle' is used by IE <= 8 and 'window.getComputedStyle' for all other browsers
|
|
info = 'getComputedStyle' in window && window.getComputedStyle(style, null) || style.currentStyle;
|
|
|
|
styleMedia = {
|
|
matchMedium: function matchMedium(media) {
|
|
var text = '@media ' + media + '{ #matchmediajs-test { width: 1px; } }';
|
|
|
|
// 'style.styleSheet' is used by IE <= 8 and 'style.textContent' for all other browsers
|
|
if (style.styleSheet) {
|
|
style.styleSheet.cssText = text;
|
|
} else {
|
|
style.textContent = text;
|
|
}
|
|
|
|
// Test if media query is true or false
|
|
return info.width === '1px';
|
|
}
|
|
};
|
|
}
|
|
|
|
return function (media) {
|
|
return {
|
|
matches: styleMedia.matchMedium(media || 'all'),
|
|
media: media || 'all'
|
|
};
|
|
};
|
|
}());
|
|
|
|
/*! matchMedia() polyfill addListener/removeListener extension. Author & copyright (c) 2012: Scott Jehl. Dual MIT/BSD license */
|
|
(function () {
|
|
// Bail out for browsers that have addListener support
|
|
if (window.matchMedia && window.matchMedia('all').addListener) {
|
|
return false;
|
|
}
|
|
|
|
var localMatchMedia = window.matchMedia,
|
|
hasMediaQueries = localMatchMedia('only all').matches,
|
|
isListening = false,
|
|
timeoutID = 0,
|
|
// setTimeout for debouncing 'handleChange'
|
|
queries = [],
|
|
// Contains each 'mql' and associated 'listeners' if 'addListener' is used
|
|
handleChange = function handleChange(evt) {
|
|
// Debounce
|
|
clearTimeout(timeoutID);
|
|
|
|
timeoutID = setTimeout(function () {
|
|
for (var i = 0, il = queries.length; i < il; i++) {
|
|
var mql = queries[i].mql,
|
|
listeners = queries[i].listeners || [],
|
|
matches = localMatchMedia(mql.media).matches;
|
|
|
|
// Update mql.matches value and call listeners
|
|
// Fire listeners only if transitioning to or from matched state
|
|
if (matches !== mql.matches) {
|
|
mql.matches = matches;
|
|
|
|
for (var j = 0, jl = listeners.length; j < jl; j++) {
|
|
listeners[j].call(window, mql);
|
|
}
|
|
}
|
|
}
|
|
}, 30);
|
|
};
|
|
|
|
window.matchMedia = function (media) {
|
|
var mql = localMatchMedia(media),
|
|
listeners = [],
|
|
index = 0;
|
|
|
|
mql.addListener = function (listener) {
|
|
// Changes would not occur to css media type so return now (Affects IE <= 8)
|
|
if (!hasMediaQueries) {
|
|
return;
|
|
}
|
|
|
|
// Set up 'resize' listener for browsers that support CSS3 media queries (Not for IE <= 8)
|
|
// There should only ever be 1 resize listener running for performance
|
|
if (!isListening) {
|
|
isListening = true;
|
|
window.addEventListener('resize', handleChange, true);
|
|
}
|
|
|
|
// Push object only if it has not been pushed already
|
|
if (index === 0) {
|
|
index = queries.push({
|
|
mql: mql,
|
|
listeners: listeners
|
|
});
|
|
}
|
|
|
|
listeners.push(listener);
|
|
};
|
|
|
|
mql.removeListener = function (listener) {
|
|
for (var i = 0, il = listeners.length; i < il; i++) {
|
|
if (listeners[i] === listener) {
|
|
listeners.splice(i, 1);
|
|
}
|
|
}
|
|
};
|
|
|
|
return mql;
|
|
};
|
|
})();
|
|
|
|
// extend innerWidth ..
|
|
var html = document.getElementsByTagName('html')[0];
|
|
var body = document.getElementsByTagName('body')[0];
|
|
|
|
/*
|
|
if (!window.innerWidth) window.innerWidth = html.clientWidth;
|
|
if (!window.innerHeight) window.innerHeight = html.clientHeight;
|
|
if (!window.scrollX) window.scrollX = window.pageXOffset || html.scrollLeft;
|
|
if (!window.scrollY) window.scrollY = window.pageYOffset || html.scrollTop;
|
|
*/
|
|
}).call(window);
|
|
/**
|
|
* Refer to this by {@link ax5}.
|
|
* @namespace ax5.ui
|
|
*/
|
|
|
|
/**
|
|
* @class ax5.ui.root
|
|
* @classdesc ax5 ui class
|
|
* @author tom@axisj.com
|
|
* @example
|
|
* ```
|
|
* var myui = new ax5.ui.root();
|
|
* ```
|
|
*/
|
|
ax5.ui = function () {
|
|
|
|
function axUi() {
|
|
this.config = {};
|
|
this.name = "root";
|
|
|
|
/**
|
|
* 클래스의 속성 정의 메소드 속성 확장후에 내부에 init 함수를 호출합니다.
|
|
* @method ax5.ui.root.setConfig
|
|
* @param {Object} config - 클래스 속성값
|
|
* @param {Boolean} [callInit=true] - init 함수 호출 여부
|
|
* @returns {ax5.ui.axUi}
|
|
* @example
|
|
* ```
|
|
* var myui = new ax5.ui.root();
|
|
* myui.setConfig({
|
|
* id:"abcd"
|
|
* });
|
|
* ```
|
|
*/
|
|
this.setConfig = function (cfg, callInit) {
|
|
jQuery.extend(true, this.config, cfg);
|
|
if (typeof callInit == "undefined" || callInit === true) {
|
|
this.init();
|
|
}
|
|
return this;
|
|
};
|
|
this.init = function () {
|
|
console.log(this.config);
|
|
};
|
|
|
|
this.bindWindowResize = function (callBack) {
|
|
setTimeout(function () {
|
|
jQuery(window).resize(function () {
|
|
if (this.bindWindowResize__) clearTimeout(this.bindWindowResize__);
|
|
this.bindWindowResize__ = setTimeout(function () {
|
|
callBack.call(this);
|
|
}.bind(this), 10);
|
|
}.bind(this));
|
|
}.bind(this), 100);
|
|
};
|
|
|
|
this.stopEvent = function (e) {
|
|
if (e.preventDefault) e.preventDefault();
|
|
if (e.stopPropagation) e.stopPropagation();
|
|
e.cancelBubble = true;
|
|
return false;
|
|
};
|
|
|
|
this.toString = function () {
|
|
return this.name + '@' + this.version;
|
|
};
|
|
|
|
// instance init
|
|
this.main = function () {}.apply(this, arguments);
|
|
}
|
|
|
|
/**
|
|
* @method ax5.ui.addClass
|
|
* @param {Object} config
|
|
* @param {String} config.className - name of Class
|
|
* @param {Object} [config.classStore=ax5.ui] - 클래스가 저장될 경로
|
|
* @param {Function} [config.superClass=ax5.ui.root]
|
|
* @param {Function} cls - Class Function
|
|
*/
|
|
function addClass(config, cls) {
|
|
if (!config || !config.className) throw 'invalid call';
|
|
var classStore = config.classStore ? config.classStore : ax5.ui;
|
|
if (!classStore) throw 'invalid classStore';
|
|
|
|
// make ui definition variable
|
|
ax5.def[config.className] = {
|
|
version: ax5.info.version
|
|
};
|
|
|
|
var factory = function factory(cls, arg) {
|
|
switch (arg.length) {
|
|
case 0:
|
|
return new cls();
|
|
break;
|
|
case 1:
|
|
return new cls(arg[0]);
|
|
break;
|
|
case 2:
|
|
return new cls(arg[0], arg[1]);
|
|
break;
|
|
case 3:
|
|
return new cls(arg[0], arg[1], arg[2]);
|
|
break;
|
|
}
|
|
};
|
|
var initInstance = function initInstance(name, version, instance) {
|
|
instance.name = name;
|
|
instance.version = version;
|
|
instance.instanceId = ax5.getGuid();
|
|
return instance;
|
|
};
|
|
var initPrototype = function initPrototype(cls) {
|
|
var superClass = config.superClass ? config.superClass : ax5.ui.root;
|
|
if (!ax5.util.isFunction(superClass)) throw 'invalid superClass';
|
|
superClass.call(this); // 부모호출
|
|
cls.prototype = new superClass(); // 상속
|
|
};
|
|
var wrapper = function wrapper() {
|
|
if (!this || !(this instanceof wrapper)) throw 'invalid call';
|
|
var instance = factory(cls, arguments);
|
|
return initInstance(config.className, config.version || "", instance);
|
|
};
|
|
initPrototype.call(this, cls);
|
|
classStore[config.className] = wrapper;
|
|
}
|
|
|
|
return {
|
|
root: axUi,
|
|
addClass: addClass
|
|
};
|
|
}();
|
|
|
|
/*!
|
|
* mustache.js - Logic-less {{mustache}} templates with JavaScript
|
|
* http://github.com/janl/mustache.js
|
|
* https://github.com/thomasJang/mustache.js -- imporove some variables
|
|
*/
|
|
|
|
(function defineMustache(global, factory) {
|
|
|
|
factory(global.mustache = {});
|
|
})(window.ax5, function mustacheFactory(mustache) {
|
|
|
|
var objectToString = Object.prototype.toString;
|
|
var isArray = Array.isArray || function isArrayPolyfill(object) {
|
|
return objectToString.call(object) === '[object Array]';
|
|
};
|
|
|
|
function isFunction(object) {
|
|
return typeof object === 'function';
|
|
}
|
|
|
|
/**
|
|
* More correct typeof string handling array
|
|
* which normally returns typeof 'object'
|
|
*/
|
|
function typeStr(obj) {
|
|
return isArray(obj) ? 'array' : typeof obj === 'undefined' ? 'undefined' : _typeof(obj);
|
|
}
|
|
|
|
function escapeRegExp(string) {
|
|
return string.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&');
|
|
}
|
|
|
|
/**
|
|
* Null safe way of checking whether or not an object,
|
|
* including its prototype, has a given property
|
|
*/
|
|
function hasProperty(obj, propName) {
|
|
return obj != null && (typeof obj === 'undefined' ? 'undefined' : _typeof(obj)) === 'object' && propName in obj;
|
|
}
|
|
|
|
// Workaround for https://issues.apache.org/jira/browse/COUCHDB-577
|
|
// See https://github.com/janl/mustache.js/issues/189
|
|
var regExpTest = RegExp.prototype.test;
|
|
|
|
function testRegExp(re, string) {
|
|
return regExpTest.call(re, string);
|
|
}
|
|
|
|
var nonSpaceRe = /\S/;
|
|
|
|
function isWhitespace(string) {
|
|
return !testRegExp(nonSpaceRe, string);
|
|
}
|
|
|
|
var entityMap = {
|
|
'&': '&', '<': '<', '>': '>', '"': '"', "'": ''', '/': '/'
|
|
};
|
|
|
|
function escapeHtml(string) {
|
|
return String(string).replace(/[&<>"'\/]/g, function fromEntityMap(s) {
|
|
return entityMap[s];
|
|
});
|
|
}
|
|
|
|
var whiteRe = /\s*/;
|
|
var spaceRe = /\s+/;
|
|
var equalsRe = /\s*=/;
|
|
var curlyRe = /\s*\}/;
|
|
var tagRe = /#|\^|\/|>|\{|&|=|!/;
|
|
|
|
/**
|
|
* Breaks up the given `template` string into a tree of tokens. If the `tags`
|
|
* argument is given here it must be an array with two string values: the
|
|
* opening and closing tags used in the template (e.g. [ "<%", "%>" ]). Of
|
|
* course, the default is to use mustaches (i.e. mustache.tags).
|
|
*
|
|
* A token is an array with at least 4 elements. The first element is the
|
|
* mustache symbol that was used inside the tag, e.g. "#" or "&". If the tag
|
|
* did not contain a symbol (i.e. {{myValue}}) this element is "name". For
|
|
* all text that appears outside a symbol this element is "text".
|
|
*
|
|
* The second element of a token is its "value". For mustache tags this is
|
|
* whatever else was inside the tag besides the opening symbol. For text tokens
|
|
* this is the text itself.
|
|
*
|
|
* The third and fourth elements of the token are the start and end indices,
|
|
* respectively, of the token in the original template.
|
|
*
|
|
* Tokens that are the root node of a subtree contain two more elements: 1) an
|
|
* array of tokens in the subtree and 2) the index in the original template at
|
|
* which the closing tag for that section begins.
|
|
*/
|
|
function parseTemplate(template, tags) {
|
|
if (!template) return [];
|
|
|
|
var sections = []; // Stack to hold section tokens
|
|
var tokens = []; // Buffer to hold the tokens
|
|
var spaces = []; // Indices of whitespace tokens on the current line
|
|
var hasTag = false; // Is there a {{tag}} on the current line?
|
|
var nonSpace = false; // Is there a non-space char on the current line?
|
|
|
|
// Strips all whitespace tokens array for the current line
|
|
// if there was a {{#tag}} on it and otherwise only space.
|
|
function stripSpace() {
|
|
if (hasTag && !nonSpace) {
|
|
while (spaces.length) {
|
|
delete tokens[spaces.pop()];
|
|
}
|
|
} else {
|
|
spaces = [];
|
|
}
|
|
|
|
hasTag = false;
|
|
nonSpace = false;
|
|
}
|
|
|
|
var openingTagRe, closingTagRe, closingCurlyRe;
|
|
|
|
function compileTags(tagsToCompile) {
|
|
if (typeof tagsToCompile === 'string') tagsToCompile = tagsToCompile.split(spaceRe, 2);
|
|
|
|
if (!isArray(tagsToCompile) || tagsToCompile.length !== 2) throw new Error('Invalid tags: ' + tagsToCompile);
|
|
|
|
openingTagRe = new RegExp(escapeRegExp(tagsToCompile[0]) + '\\s*');
|
|
closingTagRe = new RegExp('\\s*' + escapeRegExp(tagsToCompile[1]));
|
|
closingCurlyRe = new RegExp('\\s*' + escapeRegExp('}' + tagsToCompile[1]));
|
|
}
|
|
|
|
compileTags(tags || mustache.tags);
|
|
|
|
var scanner = new Scanner(template);
|
|
|
|
var start, type, value, chr, token, openSection;
|
|
while (!scanner.eos()) {
|
|
start = scanner.pos;
|
|
|
|
// Match any text between tags.
|
|
value = scanner.scanUntil(openingTagRe);
|
|
|
|
if (value) {
|
|
for (var i = 0, valueLength = value.length; i < valueLength; ++i) {
|
|
chr = value.charAt(i);
|
|
|
|
if (isWhitespace(chr)) {
|
|
spaces.push(tokens.length);
|
|
} else {
|
|
nonSpace = true;
|
|
}
|
|
|
|
tokens.push(['text', chr, start, start + 1]);
|
|
start += 1;
|
|
|
|
// Check for whitespace on the current line.
|
|
if (chr === '\n') stripSpace();
|
|
}
|
|
}
|
|
|
|
// Match the opening tag.
|
|
if (!scanner.scan(openingTagRe)) break;
|
|
|
|
hasTag = true;
|
|
|
|
// Get the tag type.
|
|
type = scanner.scan(tagRe) || 'name';
|
|
scanner.scan(whiteRe);
|
|
|
|
// Get the tag value.
|
|
if (type === '=') {
|
|
value = scanner.scanUntil(equalsRe);
|
|
scanner.scan(equalsRe);
|
|
scanner.scanUntil(closingTagRe);
|
|
} else if (type === '{') {
|
|
value = scanner.scanUntil(closingCurlyRe);
|
|
scanner.scan(curlyRe);
|
|
scanner.scanUntil(closingTagRe);
|
|
type = '&';
|
|
} else {
|
|
value = scanner.scanUntil(closingTagRe);
|
|
}
|
|
|
|
// Match the closing tag.
|
|
if (!scanner.scan(closingTagRe)) throw new Error('Unclosed tag at ' + scanner.pos);
|
|
|
|
token = [type, value, start, scanner.pos];
|
|
tokens.push(token);
|
|
|
|
if (type === '#' || type === '^') {
|
|
sections.push(token);
|
|
} else if (type === '/') {
|
|
// Check section nesting.
|
|
openSection = sections.pop();
|
|
|
|
if (!openSection) throw new Error('Unopened section "' + value + '" at ' + start);
|
|
|
|
if (openSection[1] !== value) throw new Error('Unclosed section "' + openSection[1] + '" at ' + start);
|
|
} else if (type === 'name' || type === '{' || type === '&') {
|
|
nonSpace = true;
|
|
} else if (type === '=') {
|
|
// Set the tags for the next time around.
|
|
compileTags(value);
|
|
}
|
|
}
|
|
|
|
// Make sure there are no open sections when we're done.
|
|
openSection = sections.pop();
|
|
|
|
if (openSection) throw new Error('Unclosed section "' + openSection[1] + '" at ' + scanner.pos);
|
|
|
|
return nestTokens(squashTokens(tokens));
|
|
}
|
|
|
|
/**
|
|
* Combines the values of consecutive text tokens in the given `tokens` array
|
|
* to a single token.
|
|
*/
|
|
function squashTokens(tokens) {
|
|
var squashedTokens = [];
|
|
|
|
var token, lastToken;
|
|
for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) {
|
|
token = tokens[i];
|
|
|
|
if (token) {
|
|
if (token[0] === 'text' && lastToken && lastToken[0] === 'text') {
|
|
lastToken[1] += token[1];
|
|
lastToken[3] = token[3];
|
|
} else {
|
|
squashedTokens.push(token);
|
|
lastToken = token;
|
|
}
|
|
}
|
|
}
|
|
|
|
return squashedTokens;
|
|
}
|
|
|
|
/**
|
|
* Forms the given array of `tokens` into a nested tree structure where
|
|
* tokens that represent a section have two additional items: 1) an array of
|
|
* all tokens that appear in that section and 2) the index in the original
|
|
* template that represents the end of that section.
|
|
*/
|
|
function nestTokens(tokens) {
|
|
var nestedTokens = [];
|
|
var collector = nestedTokens;
|
|
var sections = [];
|
|
|
|
var token, section;
|
|
for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) {
|
|
token = tokens[i];
|
|
|
|
switch (token[0]) {
|
|
case '#':
|
|
case '^':
|
|
collector.push(token);
|
|
sections.push(token);
|
|
collector = token[4] = [];
|
|
break;
|
|
case '/':
|
|
section = sections.pop();
|
|
section[5] = token[2];
|
|
collector = sections.length > 0 ? sections[sections.length - 1][4] : nestedTokens;
|
|
break;
|
|
default:
|
|
collector.push(token);
|
|
}
|
|
}
|
|
|
|
return nestedTokens;
|
|
}
|
|
|
|
/**
|
|
* A simple string scanner that is used by the template parser to find
|
|
* tokens in template strings.
|
|
*/
|
|
function Scanner(string) {
|
|
this.string = string;
|
|
this.tail = string;
|
|
this.pos = 0;
|
|
}
|
|
|
|
/**
|
|
* Returns `true` if the tail is empty (end of string).
|
|
*/
|
|
Scanner.prototype.eos = function eos() {
|
|
return this.tail === '';
|
|
};
|
|
|
|
/**
|
|
* Tries to match the given regular expression at the current position.
|
|
* Returns the matched text if it can match, the empty string otherwise.
|
|
*/
|
|
Scanner.prototype.scan = function scan(re) {
|
|
var match = this.tail.match(re);
|
|
|
|
if (!match || match.index !== 0) return '';
|
|
|
|
var string = match[0];
|
|
|
|
this.tail = this.tail.substring(string.length);
|
|
this.pos += string.length;
|
|
|
|
return string;
|
|
};
|
|
|
|
/**
|
|
* Skips all text until the given regular expression can be matched. Returns
|
|
* the skipped string, which is the entire tail if no match can be made.
|
|
*/
|
|
Scanner.prototype.scanUntil = function scanUntil(re) {
|
|
var index = this.tail.search(re),
|
|
match;
|
|
|
|
switch (index) {
|
|
case -1:
|
|
match = this.tail;
|
|
this.tail = '';
|
|
break;
|
|
case 0:
|
|
match = '';
|
|
break;
|
|
default:
|
|
match = this.tail.substring(0, index);
|
|
this.tail = this.tail.substring(index);
|
|
}
|
|
|
|
this.pos += match.length;
|
|
|
|
return match;
|
|
};
|
|
|
|
/**
|
|
* Represents a rendering context by wrapping a view object and
|
|
* maintaining a reference to the parent context.
|
|
*/
|
|
function Context(view, parentContext) {
|
|
this.view = view;
|
|
this.cache = {
|
|
'.': this.view,
|
|
'@each': function each() {
|
|
var returns = [];
|
|
for (var k in this) {
|
|
returns.push({ '@key': k, '@value': this[k] });
|
|
}
|
|
return returns;
|
|
}
|
|
};
|
|
this.parent = parentContext;
|
|
}
|
|
|
|
/**
|
|
* Creates a new context using the given view with this context
|
|
* as the parent.
|
|
*/
|
|
Context.prototype.push = function push(view) {
|
|
return new Context(view, this);
|
|
};
|
|
|
|
/**
|
|
* Returns the value of the given name in this context, traversing
|
|
* up the context hierarchy if the value is absent in this context's view.
|
|
*/
|
|
Context.prototype.lookup = function lookup(name) {
|
|
var cache = this.cache;
|
|
|
|
var value;
|
|
if (cache.hasOwnProperty(name)) {
|
|
value = cache[name];
|
|
} else {
|
|
var context = this,
|
|
names,
|
|
index,
|
|
lookupHit = false;
|
|
|
|
while (context) {
|
|
if (name.indexOf('.') > 0) {
|
|
value = context.view;
|
|
names = name.split('.');
|
|
index = 0;
|
|
|
|
/**
|
|
* Using the dot notion path in `name`, we descend through the
|
|
* nested objects.
|
|
*
|
|
* To be certain that the lookup has been successful, we have to
|
|
* check if the last object in the path actually has the property
|
|
* we are looking for. We store the result in `lookupHit`.
|
|
*
|
|
* This is specially necessary for when the value has been set to
|
|
* `undefined` and we want to avoid looking up parent contexts.
|
|
**/
|
|
while (value != null && index < names.length) {
|
|
if (index === names.length - 1) lookupHit = hasProperty(value, names[index]);
|
|
|
|
value = value[names[index++]];
|
|
}
|
|
} else {
|
|
value = context.view[name];
|
|
lookupHit = hasProperty(context.view, name);
|
|
}
|
|
|
|
if (lookupHit) break;
|
|
|
|
context = context.parent;
|
|
}
|
|
|
|
cache[name] = value;
|
|
}
|
|
|
|
if (isFunction(value)) value = value.call(this.view);
|
|
|
|
return value;
|
|
};
|
|
|
|
/**
|
|
* A Writer knows how to take a stream of tokens and render them to a
|
|
* string, given a context. It also maintains a cache of templates to
|
|
* avoid the need to parse the same template twice.
|
|
*/
|
|
function Writer() {
|
|
this.cache = {};
|
|
}
|
|
|
|
/**
|
|
* Clears all cached templates in this writer.
|
|
*/
|
|
Writer.prototype.clearCache = function clearCache() {
|
|
this.cache = {};
|
|
};
|
|
|
|
/**
|
|
* Parses and caches the given `template` and returns the array of tokens
|
|
* that is generated from the parse.
|
|
*/
|
|
Writer.prototype.parse = function parse(template, tags) {
|
|
var cache = this.cache;
|
|
var tokens = cache[template];
|
|
|
|
if (tokens == null) tokens = cache[template] = parseTemplate(template, tags);
|
|
|
|
return tokens;
|
|
};
|
|
|
|
/**
|
|
* High-level method that is used to render the given `template` with
|
|
* the given `view`.
|
|
*
|
|
* The optional `partials` argument may be an object that contains the
|
|
* names and templates of partials that are used in the template. It may
|
|
* also be a function that is used to load partial templates on the fly
|
|
* that takes a single argument: the name of the partial.
|
|
*/
|
|
Writer.prototype.render = function render(template, view, partials) {
|
|
var tokens = this.parse(template);
|
|
var context = view instanceof Context ? view : new Context(view);
|
|
return this.renderTokens(tokens, context, partials, template);
|
|
};
|
|
|
|
/**
|
|
* Low-level method that renders the given array of `tokens` using
|
|
* the given `context` and `partials`.
|
|
*
|
|
* Note: The `originalTemplate` is only ever used to extract the portion
|
|
* of the original template that was contained in a higher-order section.
|
|
* If the template doesn't use higher-order sections, this argument may
|
|
* be omitted.
|
|
*/
|
|
Writer.prototype.renderTokens = function renderTokens(tokens, context, partials, originalTemplate) {
|
|
var buffer = '';
|
|
var token, symbol, value;
|
|
for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) {
|
|
value = undefined;
|
|
token = tokens[i];
|
|
symbol = token[0];
|
|
|
|
if (symbol === '#') value = this.renderSection(token, context, partials, originalTemplate);else if (symbol === '^') value = this.renderInverted(token, context, partials, originalTemplate);else if (symbol === '>') value = this.renderPartial(token, context, partials, originalTemplate);else if (symbol === '&') value = this.unescapedValue(token, context);else if (symbol === 'name') value = this.escapedValue(token, context);else if (symbol === 'text') value = this.rawValue(token);
|
|
|
|
if (value !== undefined) buffer += value;
|
|
}
|
|
|
|
return buffer;
|
|
};
|
|
|
|
Writer.prototype.renderSection = function renderSection(token, context, partials, originalTemplate) {
|
|
var self = this;
|
|
var buffer = '';
|
|
|
|
var value = context.lookup(token[1]);
|
|
|
|
// This function is used to render an arbitrary template
|
|
// in the current context by higher-order sections.
|
|
function subRender(template) {
|
|
return self.render(template, context, partials);
|
|
}
|
|
|
|
if (!value) return;
|
|
|
|
if (isArray(value)) {
|
|
for (var j = 0, valueLength = value.length; j < valueLength; ++j) {
|
|
if (value[j]) {
|
|
if (_typeof(value[j]) === 'object') {
|
|
value[j]['@i'] = j;
|
|
value[j]['@first'] = j === 0;
|
|
}
|
|
|
|
buffer += this.renderTokens(token[4], context.push(value[j]), partials, originalTemplate);
|
|
}
|
|
}
|
|
} else if ((typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object' || typeof value === 'string' || typeof value === 'number') {
|
|
buffer += this.renderTokens(token[4], context.push(value), partials, originalTemplate);
|
|
} else if (isFunction(value)) {
|
|
if (typeof originalTemplate !== 'string') throw new Error('Cannot use higher-order sections without the original template');
|
|
|
|
// Extract the portion of the original template that the section contains.
|
|
value = value.call(context.view, originalTemplate.slice(token[3], token[5]), subRender);
|
|
|
|
if (value != null) buffer += value;
|
|
} else {
|
|
buffer += this.renderTokens(token[4], context, partials, originalTemplate);
|
|
}
|
|
return buffer;
|
|
};
|
|
|
|
Writer.prototype.renderInverted = function renderInverted(token, context, partials, originalTemplate) {
|
|
var value = context.lookup(token[1]);
|
|
|
|
// Use JavaScript's definition of falsy. Include empty arrays.
|
|
// See https://github.com/janl/mustache.js/issues/186
|
|
if (!value || isArray(value) && value.length === 0) return this.renderTokens(token[4], context, partials, originalTemplate);
|
|
};
|
|
|
|
Writer.prototype.renderPartial = function renderPartial(token, context, partials) {
|
|
if (!partials) return;
|
|
|
|
var value = isFunction(partials) ? partials(token[1]) : partials[token[1]];
|
|
if (value != null) return this.renderTokens(this.parse(value), context, partials, value);
|
|
};
|
|
|
|
Writer.prototype.unescapedValue = function unescapedValue(token, context) {
|
|
var value = context.lookup(token[1]);
|
|
if (value != null) return value;
|
|
};
|
|
|
|
Writer.prototype.escapedValue = function escapedValue(token, context) {
|
|
var value = context.lookup(token[1]);
|
|
if (value != null) return mustache.escape(value);
|
|
};
|
|
|
|
Writer.prototype.rawValue = function rawValue(token) {
|
|
return token[1];
|
|
};
|
|
|
|
mustache.name = 'mustache.js';
|
|
mustache.version = '2.1.3';
|
|
mustache.tags = ['{{', '}}'];
|
|
|
|
// All high-level mustache.* functions use this writer.
|
|
var defaultWriter = new Writer();
|
|
|
|
/**
|
|
* Clears all cached templates in the default writer.
|
|
*/
|
|
mustache.clearCache = function clearCache() {
|
|
return defaultWriter.clearCache();
|
|
};
|
|
|
|
/**
|
|
* Parses and caches the given template in the default writer and returns the
|
|
* array of tokens it contains. Doing this ahead of time avoids the need to
|
|
* parse templates on the fly as they are rendered.
|
|
*/
|
|
mustache.parse = function parse(template, tags) {
|
|
return defaultWriter.parse(template, tags);
|
|
};
|
|
|
|
/**
|
|
* Renders the `template` with the given `view` and `partials` using the
|
|
* default writer.
|
|
*/
|
|
mustache.render = function render(template, view, partials) {
|
|
if (typeof template !== 'string') {
|
|
throw new TypeError('Invalid template! Template should be a "string" ' + 'but "' + typeStr(template) + '" was given as the first ' + 'argument for mustache#render(template, view, partials)');
|
|
}
|
|
|
|
return defaultWriter.render(template, view, partials);
|
|
};
|
|
|
|
// This is here for backwards compatibility with 0.4.x.,
|
|
/*eslint-disable */ // eslint wants camel cased function name
|
|
mustache.to_html = function to_html(template, view, partials, send) {
|
|
/*eslint-enable*/
|
|
|
|
var result = mustache.render(template, view, partials);
|
|
|
|
if (isFunction(send)) {
|
|
send(result);
|
|
} else {
|
|
return result;
|
|
}
|
|
};
|
|
|
|
// Export the escaping function so that the user may override it.
|
|
// See https://github.com/janl/mustache.js/issues/244
|
|
mustache.escape = escapeHtml;
|
|
|
|
// Export these mainly for testing, but also for advanced usage.
|
|
mustache.Scanner = Scanner;
|
|
mustache.Context = Context;
|
|
mustache.Writer = Writer;
|
|
});
|