(function (window, nodeGlobal) { var global = { jquery: (typeof(jQuery) != "undefined") ? jQuery : null }, globalFunc = {}, globalClass = {}; // JUI의 기본 설정 값 (향후 더 추가될 수 있음) var globalOpts = { template: { evaluate: /<\!([\s\S]+?)\!>/g, interpolate: /<\!=([\s\S]+?)\!>/g, escape: /<\!-([\s\S]+?)\!>/g }, logUrl: "tool/debug.html" }; /** * @class util.base * * jui 에서 공통적으로 사용하는 유틸리티 함수 모음 * * ``` * var _ = jui.include("util.base"); * * console.log(_.browser.webkit); * ``` * * @singleton */ var utility = global["util.base"] = { /** * @property browser check browser agent * @property {Boolean} browser.webkit Webkit 브라우저 체크 * @property {Boolean} browser.mozilla Mozilla 브라우저 체크 * @property {Boolean} browser.msie IE 브라우저 체크 */ browser: { webkit: ('WebkitAppearance' in document.documentElement.style) ? true : false, mozilla: (typeof window.mozInnerScreenX != "undefined") ? true : false, msie: (window.navigator.userAgent.indexOf("Trident") != -1) ? true : false }, /** * @property {Boolean} isTouch * check touch device */ isTouch: /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(window.navigator.userAgent), /** * @method inherit * * 프로토타입 기반의 상속 제공 * * @param {Function} ctor base Class * @param {Function} superCtor super Class */ inherit: function (ctor, superCtor) { if (!this.typeCheck("function", ctor) || !this.typeCheck("function", superCtor)) return; ctor.parent = superCtor; ctor.prototype = new superCtor; ctor.prototype.constructor = ctor; ctor.prototype.parent = ctor.prototype; /** * @method super * call parent method * @param {String} method parent method name * @param {Array} args * @returns {Mixed} */ ctor.prototype.super = function (method, args) { return this.constructor.prototype[method].apply(this, args); } }, /** * @method extend * * implements object extend * * @param {Object|Function} origin * @param {Object|Function} add * @param {Boolean} skip * @return {Object} */ extend: function (origin, add, skip) { if (!this.typeCheck([ "object", "function" ], origin)) origin = {}; if (!this.typeCheck([ "object", "function" ], add)) return origin; for (var key in add) { if (skip === true) { if (isRecursive(origin[key])) { this.extend(origin[key], add[key], skip); } else if (this.typeCheck("undefined", origin[key])) { origin[key] = add[key]; } } else { if (isRecursive(origin[key])) { this.extend(origin[key], add[key], skip); } else { origin[key] = add[key]; } } } function isRecursive(value) { return utility.typeCheck("object", value); } return origin; }, /** * convert px to integer * @param {String or Number} px * @return {Number} */ pxToInt: function (px) { if (this.typeCheck("string", px) && px.indexOf("px") != -1) { return parseInt(px.split("px").join("")); } return px; }, /** * @method clone * implements object clone * @param {Array/Object} obj 복사할 객체 * @return {Array} */ clone: function (obj) { var clone = (this.typeCheck("array", obj)) ? [] : {}; for (var i in obj) { if (this.typeCheck("object", obj[i])) clone[i] = this.clone(obj[i]); else clone[i] = obj[i]; } return clone; }, /** * @method deepClone * implements object deep clone * @param obj * @param emit * @return {*} */ deepClone: function (obj, emit) { var value = null; emit = emit || {}; if (this.typeCheck("array", obj)) { value = new Array(obj.length); for (var i = 0, len = obj.length; i < len; i++) { value[i] = this.deepClone(obj[i], emit); } } else if (this.typeCheck("date", obj)) { value = obj; } else if (this.typeCheck("object", obj)) { value = {}; for (var key in obj) { if (emit[key]) { value[key] = obj[key]; } else { value[key] = this.deepClone(obj[key], emit); } } } else { value = obj; } return value; }, /** * @method sort * use QuickSort * @param {Array} array * @return {QuickSort} */ sort: function (array) { var QuickSort = jui.include("util.sort"); return new QuickSort(array); }, /** * @method runtime * * caculate callback runtime * * @param {String} name * @param {Function} callback */ runtime: function (name, callback) { var nStart = new Date().getTime(); callback(); var nEnd = new Date().getTime(); console.log(name + " : " + (nEnd - nStart) + "ms"); }, /** * @method template * parsing template string * @param html * @param obj */ template: function (html, obj) { var tpl = jui.include("util.template"); if (!obj) return tpl(html, null, globalOpts.template); else return tpl(html, obj, globalOpts.template); }, /** * @method resize * add event in window resize event * @param {Function} callback * @param {Number} ms delay time */ resize: function (callback, ms) { var after_resize = (function () { var timer = 0; return function () { clearTimeout(timer); timer = setTimeout(callback, ms); } })(); if (window.addEventListener) { window.addEventListener("resize", after_resize); } else if (object.attachEvent) { window.attachEvent("onresize", after_resize); } else { window["onresize"] = after_resize; } }, /** * @method index * * IndexParser 객체 생성 * * @return {IndexParser} */ index: function () { var KeyParser = jui.include("util.keyparser"); return new KeyParser(); }, /** * @method chunk * split array by length * @param {Array} arr * @param {Number} len * @return {Array} */ chunk: function (arr, len) { var chunks = [], i = 0, n = arr.length; while (i < n) { chunks.push(arr.slice(i, i += len)); } return chunks; }, /** * @method typeCheck * check data type * @param {String} t type string * @param {Object} v value object * @return {Boolean} */ typeCheck: function (t, v) { function check(type, value) { if (typeof(type) != "string") return false; if (type == "string") { return (typeof(value) == "string"); } else if (type == "integer") { return (typeof(value) == "number" && value % 1 == 0); } else if (type == "float") { return (typeof(value) == "number" && value % 1 != 0); } else if (type == "number") { return (typeof(value) == "number"); } else if (type == "boolean") { return (typeof(value) == "boolean"); } else if (type == "undefined") { return (typeof(value) == "undefined"); } else if (type == "null") { return (value === null); } else if (type == "array") { return (value instanceof Array); } else if (type == "date") { return (value instanceof Date); } else if (type == "function") { return (typeof(value) == "function"); } else if (type == "object") { // typeCheck에 정의된 타입일 경우에는 object 체크시 false를 반환 (date, array, null) return ( typeof(value) == "object" && value !== null && !(value instanceof Array) && !(value instanceof Date) && !(value instanceof RegExp) ); } return false; } if (typeof(t) == "object" && t.length) { var typeList = t; for (var i = 0; i < typeList.length; i++) { if (check(typeList[i], v)) return true; } return false; } else { return check(t, v); } }, typeCheckObj: function (uiObj, list) { if (typeof(uiObj) != "object") return; var self = this; for (var key in uiObj) { var func = uiObj[key]; if (typeof(func) == "function") { (function (funcName, funcObj) { uiObj[funcName] = function () { var args = arguments, params = list[funcName]; for (var i = 0; i < args.length; i++) { if (!self.typeCheck(params[i], args[i])) { throw new Error("JUI_CRITICAL_ERR: the " + i + "th parameter is not a " + params[i] + " (" + name + ")"); } } return funcObj.apply(this, args); } })(key, func); } } }, /** * @method dataToCsv * * data 를 csv 로 변환한다. * * @param {Array} keys * @param {Array} dataList * @param {Number} dataSize * @return {String} 변환된 csv 문자열 */ dataToCsv: function (keys, dataList, dataSize) { var csv = "", len = (!dataSize) ? dataList.length : dataSize; for (var i = -1; i < len; i++) { var tmpArr = []; for (var j = 0; j < keys.length; j++) { if (keys[j]) { if (i == -1) { tmpArr.push('"' + keys[j] + '"'); } else { var value = dataList[i][keys[j]]; tmpArr.push(isNaN(value) ? '"' + value + '"' : value); } } } csv += tmpArr.join(",") + "\n"; } return csv; }, /** * @method dataToCsv2 * * @param {Object} options * @return {String} */ dataToCsv2: function (options) { var csv = ""; var opts = this.extend({ fields: null, // required rows: null, // required names: null, types: null, count: (this.typeCheck("integer", options.count)) ? options.count : options.rows.length }, options); for (var i = -1; i < opts.count; i++) { var tmpArr = []; for (var j = 0; j < opts.fields.length; j++) { if (opts.fields[j]) { if (i == -1) { if (opts.names && opts.names[j]) { tmpArr.push('"' + opts.names[j] + '"'); } else { tmpArr.push('"' + opts.fields[j] + '"'); } } else { var value = opts.rows[i][opts.fields[j]]; if (this.typeCheck("array", opts.types)) { if(opts.types[j] == "string") { tmpArr.push('"' + value + '"'); } else if(opts.types[j] == "integer") { tmpArr.push(parseInt(value)); } else if(opts.types[j] == "float") { tmpArr.push(parseFloat(value)); } else { tmpArr.push(value); } } else { tmpArr.push(isNaN(value) ? '"' + value + '"' : value); } } } } csv += tmpArr.join(",") + "\n"; } return csv; }, /** * @method fileToCsv * * file 에서 csv 컨텐츠 로드 * * @param {File} file * @param {Function} callback */ fileToCsv: function (file, callback) { var reader = new FileReader(); reader.onload = function (readerEvt) { if (utility.typeCheck("function", callback)) { callback(readerEvt.target.result); } }; reader.readAsText(file); }, /** * @method csvToBase64 * * csv 다운로드 링크로 변환 * * @param {String} csv * @return {String} */ csvToBase64: function (csv) { var Base64 = jui.include("util.base64"); return "data:application/octet-stream;base64," + Base64.encode(csv); }, /** * @method csvToData * * @param {Array} keys * @param {String} csv * @param {Number} csvNumber * @return {Array} */ csvToData: function (keys, csv, csvNumber) { var dataList = [], tmpRowArr = csv.split("\n") for (var i = 1; i < tmpRowArr.length; i++) { if (tmpRowArr[i] != "") { var tmpArr = tmpRowArr[i].split(","), // TODO: 값 안에 콤마(,)가 있을 경우에 별도로 처리해야 함 data = {}; for (var j = 0; j < keys.length; j++) { data[keys[j]] = tmpArr[j]; // '"' 로 감싸져있는 문자열은 '"' 제거 if (this.startsWith(tmpArr[j], '"') && this.endsWith(tmpArr[j], '"')) { data[keys[j]] = tmpArr[j].split('"').join(''); } else { data[keys[j]] = tmpArr[j]; } if (this.inArray(keys[j], csvNumber) != -1) { data[keys[j]] = parseFloat(tmpArr[j]); } } dataList.push(data); } } return dataList; }, /** * @method getCsvFields * * csv 에서 필드 얻어오기 * * @param {Array} fields * @param {Array} csvFields * @return {Array} */ getCsvFields: function (fields, csvFields) { var tmpFields = (this.typeCheck("array", csvFields)) ? csvFields : fields; for (var i = 0; i < tmpFields.length; i++) { if (!isNaN(tmpFields[i])) { tmpFields[i] = fields[tmpFields[i]]; } } return tmpFields; }, /** * @method svgToBase64 * * xml 문자열로 svg datauri 생성 * * @param {String} xml * @return {String} 변환된 data uri 링크 */ svgToBase64: function (xml) { var Base64 = jui.include("util.base64"); return "data:image/svg+xml;base64," + Base64.encode(xml); }, /** * @method dateFormat * * implements date format function * * yyyy : 4 digits year * yy : 2 digits year * y : 1 digit year * * @param {Date} date * @param {String} format date format string * @param utc * @return {string} */ dateFormat: function (date, format, utc) { var MMMM = ["\x00", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]; var MMM = ["\x01", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; var dddd = ["\x02", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]; var ddd = ["\x03", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; function ii(i, len) { var s = i + ""; len = len || 2; while (s.length < len) s = "0" + s; return s; } var y = utc ? date.getUTCFullYear() : date.getFullYear(); format = format.replace(/(^|[^\\])yyyy+/g, "$1" + y); format = format.replace(/(^|[^\\])yy/g, "$1" + y.toString().substr(2, 2)); format = format.replace(/(^|[^\\])y/g, "$1" + y); var M = (utc ? date.getUTCMonth() : date.getMonth()) + 1; format = format.replace(/(^|[^\\])MMMM+/g, "$1" + MMMM[0]); format = format.replace(/(^|[^\\])MMM/g, "$1" + MMM[0]); format = format.replace(/(^|[^\\])MM/g, "$1" + ii(M)); format = format.replace(/(^|[^\\])M/g, "$1" + M); var d = utc ? date.getUTCDate() : date.getDate(); format = format.replace(/(^|[^\\])dddd+/g, "$1" + dddd[0]); format = format.replace(/(^|[^\\])ddd/g, "$1" + ddd[0]); format = format.replace(/(^|[^\\])dd/g, "$1" + ii(d)); format = format.replace(/(^|[^\\])d/g, "$1" + d); var H = utc ? date.getUTCHours() : date.getHours(); format = format.replace(/(^|[^\\])HH+/g, "$1" + ii(H)); format = format.replace(/(^|[^\\])H/g, "$1" + H); var h = H > 12 ? H - 12 : H == 0 ? 12 : H; format = format.replace(/(^|[^\\])hh+/g, "$1" + ii(h)); format = format.replace(/(^|[^\\])h/g, "$1" + h); var m = utc ? date.getUTCMinutes() : date.getMinutes(); format = format.replace(/(^|[^\\])mm+/g, "$1" + ii(m)); format = format.replace(/(^|[^\\])m/g, "$1" + m); var s = utc ? date.getUTCSeconds() : date.getSeconds(); format = format.replace(/(^|[^\\])ss+/g, "$1" + ii(s)); format = format.replace(/(^|[^\\])s/g, "$1" + s); var f = utc ? date.getUTCMilliseconds() : date.getMilliseconds(); format = format.replace(/(^|[^\\])fff+/g, "$1" + ii(f, 3)); f = Math.round(f / 10); format = format.replace(/(^|[^\\])ff/g, "$1" + ii(f)); f = Math.round(f / 10); format = format.replace(/(^|[^\\])f/g, "$1" + f); var T = H < 12 ? "AM" : "PM"; format = format.replace(/(^|[^\\])TT+/g, "$1" + T); format = format.replace(/(^|[^\\])T/g, "$1" + T.charAt(0)); var t = T.toLowerCase(); format = format.replace(/(^|[^\\])tt+/g, "$1" + t); format = format.replace(/(^|[^\\])t/g, "$1" + t.charAt(0)); var tz = -date.getTimezoneOffset(); var K = utc || !tz ? "Z" : tz > 0 ? "+" : "-"; if (!utc) { tz = Math.abs(tz); var tzHrs = Math.floor(tz / 60); var tzMin = tz % 60; K += ii(tzHrs) + ":" + ii(tzMin); } format = format.replace(/(^|[^\\])K/g, "$1" + K); var day = (utc ? date.getUTCDay() : date.getDay()) + 1; format = format.replace(new RegExp(dddd[0], "g"), dddd[day]); format = format.replace(new RegExp(ddd[0], "g"), ddd[day]); format = format.replace(new RegExp(MMMM[0], "g"), MMMM[M]); format = format.replace(new RegExp(MMM[0], "g"), MMM[M]); format = format.replace(/\\(.)/g, "$1"); return format; }, /** * @method createId * * 유니크 아이디 생성 * * @param {String} key prefix string * @return {String} 생성된 아이디 문자열 */ createId: function (key) { return [key || "id", (+new Date), Math.round(Math.random() * 100) % 100].join("-"); }, /** * @method btoa * * Base64 인코딩 * * @return {String} */ btoa: function(input) { var Base64 = jui.include("util.base64"); return Base64.encode(input); }, /** * @method atob * * Base64 디코딩 * * @return {String} */ atob: function(input) { var Base64 = jui.include("util.base64"); return Base64.decode(input); }, /** * implement async loop without blocking ui * * @param total * @param context * @returns {Function} */ timeLoop : function(total, context) { return function(callback, lastCallback) { function TimeLoopCallback (i) { if (i < 1) return; if (i == 1) { callback.call(context, i) lastCallback.call(context); } else { setTimeout(function() { if (i > -1) callback.call(context, i--); if (i > -1) TimeLoopCallback(i); }, 1); } } TimeLoopCallback(total); }; }, /** * @method loop * * 최적화된 루프 생성 (5단계로 나눔) * * @param {Number} total * @param {Object} [context=null] * @return {Function} 최적화된 루프 콜백 (index, groupIndex 2가지 파라미터를 받는다.) */ loop: function (total, context) { var start = 0, end = total, unit = Math.ceil(total / 5); return function (callback) { var first = start, second = unit * 1, third = unit * 2, fourth = unit * 3, fifth = unit * 4, firstMax = second, secondMax = third, thirdMax = fourth, fourthMax = fifth, fifthMax = end; while (first < firstMax && first < end) { callback.call(context, first, 1); first++; if (second < secondMax && second < end) { callback.call(context, second, 2); second++; } if (third < thirdMax && third < end) { callback.call(context, third, 3); third++; } if (fourth < fourthMax && fourth < end) { callback.call(context, fourth, 4); fourth++; } if (fifth < fifthMax && fifth < end) { callback.call(context, fifth, 5); fifth++; } } }; }, /** * @method loopArray * * 배열을 사용해서 최적화된 루프로 생성한다. * * * @param {Array} data 루프로 생성될 배열 * @param {Object} [context=null] * @return {Function} 최적화된 루프 콜백 (data, index, groupIndex 3가지 파라미터를 받는다.) */ loopArray: function (data, context) { var total = data.length, start = 0, end = total, unit = Math.ceil(total / 5); return function (callback) { var first = start, second = unit * 1, third = unit * 2, fourth = unit * 3, fifth = unit * 4, firstMax = second, secondMax = third, thirdMax = fourth, fourthMax = fifth, fifthMax = end; while (first < firstMax && first < end) { callback.call(context, data[first], first, 1); first++; if (second < secondMax && second < end) { callback.call(context, data[second], second, 2); second++; } if (third < thirdMax && third < end) { callback.call(context, data[third], third, 3); third++; } if (fourth < fourthMax && fourth < end) { callback.call(context, data[fourth], fourth, 4); fourth++; } if (fifth < fifthMax && fifth < end) { callback.call(context, data[fifth], fifth, 5); fifth++; } } }; }, /** * @method makeIndex * * 배열의 키 기반 인덱스를 생성한다. * * 개별 값 별로 멀티 인덱스를 생성한다. * * @param {Array} data * @param {String} keyField * @return {Object} 생성된 인덱스 */ makeIndex: function (data, keyField) { var list = {}, func = this.loopArray(data); func(function (d, i) { var value = d[keyField]; if (typeof list[value] == 'undefined') { list[value] = []; } list[value].push(i); }); return list; }, /** * @method startsWith * Check that it matches the starting string search string. * * @param {String} string * @param {String} searchString * @return {Integer} position */ startsWith: function (string, searchString, position) { position = position || 0; return string.lastIndexOf(searchString, position) === position; }, /** * @method endsWith * Check that it matches the end of a string search string. * * @param {String} string * @param {String} searchString * @return {Integer} position */ endsWith: function (string, searchString, position) { var subjectString = string; if (position === undefined || position > subjectString.length) { position = subjectString.length; } position -= searchString.length; var lastIndex = subjectString.indexOf(searchString, position); return lastIndex !== -1 && lastIndex === position; }, inArray: function (target, list) { if(this.typeCheck([ "undefined", "null" ], target) || !this.typeCheck("array", list)) return -1; for(var i = 0, len = list.length; i < len; i++) { if(list[i] == target) return i; } return -1; }, trim: function(text) { var whitespace = "[\\x20\\t\\r\\n\\f]", rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ); return text == null ? "" : ( text + "" ).replace( rtrim, "" ); }, ready: (function() { var readyList, DOMContentLoaded, class2type = {}; class2type["[object Boolean]"] = "boolean"; class2type["[object Number]"] = "number"; class2type["[object String]"] = "string"; class2type["[object Function]"] = "function"; class2type["[object Array]"] = "array"; class2type["[object Date]"] = "date"; class2type["[object RegExp]"] = "regexp"; class2type["[object Object]"] = "object"; var ReadyObj = { // Is the DOM ready to be used? Set to true once it occurs. isReady: false, // A counter to track how many items to wait for before // the ready event fires. See #6781 readyWait: 1, // Hold (or release) the ready event holdReady: function( hold ) { if ( hold ) { ReadyObj.readyWait++; } else { ReadyObj.ready( true ); } }, // Handle when the DOM is ready ready: function( wait ) { // Either a released hold or an DOMready/load event and not yet ready if ( (wait === true && !--ReadyObj.readyWait) || (wait !== true && !ReadyObj.isReady) ) { // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). if ( !document.body ) { return setTimeout( ReadyObj.ready, 1 ); } // Remember that the DOM is ready ReadyObj.isReady = true; // If a normal DOM Ready event fired, decrement, and wait if need be if ( wait !== true && --ReadyObj.readyWait > 0 ) { return; } // If there are functions bound, to execute readyList.resolveWith( document, [ ReadyObj ] ); // Trigger any bound ready events //if ( ReadyObj.fn.trigger ) { // ReadyObj( document ).trigger( "ready" ).unbind( "ready" ); //} } }, bindReady: function() { if ( readyList ) { return; } readyList = ReadyObj._Deferred(); // Catch cases where $(document).ready() is called after the // browser event has already occurred. if ( document.readyState === "complete" ) { // Handle it asynchronously to allow scripts the opportunity to delay ready return setTimeout( ReadyObj.ready, 1 ); } // Mozilla, Opera and webkit nightlies currently support this event if ( document.addEventListener ) { // Use the handy event callback document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); // A fallback to window.onload, that will always work window.addEventListener( "load", ReadyObj.ready, false ); // If IE event model is used } else if ( document.attachEvent ) { // ensure firing before onload, // maybe late but safe also for iframes document.attachEvent( "onreadystatechange", DOMContentLoaded ); // A fallback to window.onload, that will always work window.attachEvent( "onload", ReadyObj.ready ); // If IE and not a frame // continually check to see if the document is ready var toplevel = false; try { toplevel = window.frameElement == null; } catch(e) {} if ( document.documentElement.doScroll && toplevel ) { doScrollCheck(); } } }, _Deferred: function() { var // callbacks list callbacks = [], // stored [ context , args ] fired, // to avoid firing when already doing so firing, // flag to know if the deferred has been cancelled cancelled, // the deferred itself deferred = { // done( f1, f2, ...) done: function() { if ( !cancelled ) { var args = arguments, i, length, elem, type, _fired; if ( fired ) { _fired = fired; fired = 0; } for ( i = 0, length = args.length; i < length; i++ ) { elem = args[ i ]; type = ReadyObj.type( elem ); if ( type === "array" ) { deferred.done.apply( deferred, elem ); } else if ( type === "function" ) { callbacks.push( elem ); } } if ( _fired ) { deferred.resolveWith( _fired[ 0 ], _fired[ 1 ] ); } } return this; }, // resolve with given context and args resolveWith: function( context, args ) { if ( !cancelled && !fired && !firing ) { // make sure args are available (#8421) args = args || []; firing = 1; try { while( callbacks[ 0 ] ) { callbacks.shift().apply( context, args );//shifts a callback, and applies it to document } } finally { fired = [ context, args ]; firing = 0; } } return this; }, // resolve with this as context and given arguments resolve: function() { deferred.resolveWith( this, arguments ); return this; }, // Has this deferred been resolved? isResolved: function() { return !!( firing || fired ); }, // Cancel cancel: function() { cancelled = 1; callbacks = []; return this; } }; return deferred; }, type: function( obj ) { return obj == null ? String( obj ) : class2type[ Object.prototype.toString.call(obj) ] || "object"; } } // The DOM ready check for Internet Explorer function doScrollCheck() { if ( ReadyObj.isReady ) { return; } try { // If IE is used, use the trick by Diego Perini // http://javascript.nwbox.com/IEContentLoaded/ document.documentElement.doScroll("left"); } catch(e) { setTimeout( doScrollCheck, 1 ); return; } // and execute any waiting functions ReadyObj.ready(); } // Cleanup functions for the document ready method if ( document.addEventListener ) { DOMContentLoaded = function() { document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false ); ReadyObj.ready(); }; } else if ( document.attachEvent ) { DOMContentLoaded = function() { // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). if ( document.readyState === "complete" ) { document.detachEvent( "onreadystatechange", DOMContentLoaded ); ReadyObj.ready(); } }; } function ready( fn ) { // Attach the listeners ReadyObj.bindReady(); var type = ReadyObj.type( fn ); // Add the callback readyList.done( fn );//readyList is result of _Deferred() } return ready; })(), param: function(data) { var r20 = /%20/g, s = [], add = function(key, value) { // If value is a function, invoke it and return its value value = utility.typeCheck("function", value) ? value() : (value == null ? "" : value); s[s.length] = encodeURIComponent(key) + "=" + encodeURIComponent(value); }; for(var key in data) { add(key, data[key]); } return s.join("&").replace(r20, "+"); }, ajax: function(data) { var xhr = null, paramStr = "", callback = null; var opts = utility.extend({ url: null, type: "GET", data: null, async: true, success: null, fail: null }, data); if(!this.typeCheck("string", opts.url) || !this.typeCheck("function", opts.success)) return; if(this.typeCheck("object", opts.data)) paramStr = this.param(opts.data); if(!this.typeCheck("undefined", XMLHttpRequest)) { xhr = new XMLHttpRequest(); } else { var versions = [ "MSXML2.XmlHttp.5.0", "MSXML2.XmlHttp.4.0", "MSXML2.XmlHttp.3.0", "MSXML2.XmlHttp.2.0", "Microsoft.XmlHttp" ]; for(var i = 0, len = versions.length; i < len; i++) { try { xhr = new ActiveXObject(versions[i]); break; } catch(e) {} } } if(xhr != null) { xhr.open(opts.type, opts.url, opts.async); xhr.send(paramStr); callback = function() { if (xhr.readyState === 4 && xhr.status == 200) { opts.success(xhr); } else { if (utility.typeCheck("function", opts.fail)) { opts.fail(xhr); } } } if (!opts.async) { callback(); } else { xhr.onreadystatechange = callback; } } }, scrollWidth: function() { var inner = document.createElement("p"); inner.style.width = "100%"; inner.style.height = "200px"; var outer = document.createElement("div"); outer.style.position = "absolute"; outer.style.top = "0px"; outer.style.left = "0px"; outer.style.visibility = "hidden"; outer.style.width = "200px"; outer.style.height = "150px"; outer.style.overflow = "hidden"; outer.appendChild(inner); document.body.appendChild(outer); var w1 = inner.offsetWidth; outer.style.overflow = "scroll"; var w2 = inner.offsetWidth; if (w1 == w2) w2 = outer.clientWidth; document.body.removeChild(outer); return (w1 - w2); } } /* * Module related functions * */ var getDepends = function (depends) { var args = []; for (var i = 0; i < depends.length; i++) { var module = global[depends[i]]; if (!utility.typeCheck([ "function", "object" ], module)) { var modules = getModules(depends[i]); if (modules == null) { console.log("JUI_WARNING_MSG: '" + depends[i] + "' is not loaded"); args.push(null); } else { args.push(modules); } } else { args.push(module); } } return args; } var getModules = function (parent) { var modules = null, parent = parent + "."; for (var key in global) { if (key.indexOf(parent) != -1) { if (utility.typeCheck([ "function", "object" ], global[key])) { var child = key.split(parent).join(""); if (child.indexOf(".") == -1) { if (modules == null) { modules = {}; } modules[child] = global[key]; } } } } return modules; } /** * @class jui * * Global Object * * @singleton */ window.jui = nodeGlobal.jui = { /** * @method ready * * ready 타임에 실행될 callback 정의 * * @param {Function} callback */ ready: function () { var args = [], callback = (arguments.length == 2) ? arguments[1] : arguments[0], depends = (arguments.length == 2) ? arguments[0] : null; if (!utility.typeCheck([ "array", "null" ], depends) || !utility.typeCheck("function", callback)) { throw new Error("JUI_CRITICAL_ERR: Invalid parameter type of the function"); } utility.ready(function() { if (depends) { args = getDepends(depends); } else { // @Deprecated 기존의 레거시를 위한 코드 var ui = getModules("ui"), uix = {}; utility.extend(uix, ui); utility.extend(uix, getModules("grid")); args = [ ui, uix, utility ]; } callback.apply(null, args); }); }, /** * @method defineUI * * 사용자가 실제로 사용할 수 있는 UI 클래스를 정의 * * @param {String} name 모듈 로드와 상속에 사용될 이름을 정한다. * @param {Array} depends 'define'이나 'defineUI'로 정의된 클래스나 객체를 인자로 받을 수 있다. * @param {Function} callback UI 클래스를 해당 콜백 함수 내에서 클래스 형태로 구현하고 리턴해야 한다. */ defineUI: function (name, depends, callback, parent) { if (!utility.typeCheck("string", name) || !utility.typeCheck("array", depends) || !utility.typeCheck("function", callback) || !utility.typeCheck([ "string", "undefined" ], parent)) { throw new Error("JUI_CRITICAL_ERR: Invalid parameter type of the function"); } if (utility.typeCheck("function", globalClass[name])) { throw new Error("JUI_CRITICAL_ERR: '" + name + "' is already exist"); } if (utility.typeCheck("undefined", parent)) { // 기본적으로 'event' 클래스를 상속함 parent = "event"; } if (!utility.typeCheck("function", globalClass[parent])) { throw new Error("JUI_CRITICAL_ERR: Parents are the only function"); } else { if (globalFunc[parent] !== true) { throw new Error("JUI_CRITICAL_ERR: UI function can not be inherited"); } } var args = getDepends(depends), uiFunc = callback.apply(null, args); // 상속 utility.inherit(uiFunc, globalClass[parent]); // TODO: 차트 빌더를 제외하고, 무조건 event 클래스에 정의된 init 메소드를 호출함 global[name] = globalClass[parent != "core" ? "event" : "core"].init({ type: name, "class": uiFunc }); globalClass[name] = uiFunc; globalFunc[name] = true; /** * @deprecated // support AMD module if (typeof define == "function" && define.amd) { define(name, function () { return global[name] }); } */ }, createUIObject: function (UI, selector, index, elem, options, afterHook) { var mainObj = new UI["class"](); // Check Options var opts = jui.defineOptions(UI["class"], options || {}); // Public Properties mainObj.init.prototype = mainObj; /** @property {String/HTMLElement} selector */ mainObj.init.prototype.selector = selector; /** @property {HTMLElement} root */ mainObj.init.prototype.root = elem; /** @property {Object} options */ mainObj.init.prototype.options = opts; /** @property {Object} tpl Templates */ mainObj.init.prototype.tpl = {}; /** @property {Array} event Custom events */ mainObj.init.prototype.event = new Array(); // Custom Event /** @property {Integer} timestamp UI Instance creation time*/ mainObj.init.prototype.timestamp = new Date().getTime(); /** @property {Integer} index Index of UI instance*/ mainObj.init.prototype.index = index; /** @property {Class} module Module class */ mainObj.init.prototype.module = UI; // UI 객체 프로퍼티를 외부에서 정의할 수 있음 (jQuery 의존성 제거를 위한 코드) if(utility.typeCheck("function", afterHook)) { afterHook(mainObj, opts); } // Script-based Template Settings for (var name in opts.tpl) { var tplHtml = opts.tpl[name]; if (utility.typeCheck("string", tplHtml) && tplHtml != "") { mainObj.init.prototype.tpl[name] = utility.template(tplHtml); } } var uiObj = new mainObj.init(); // Custom Event Setting for(var key in opts.event) { uiObj.on(key, opts.event[key]); } // 엘리먼트 객체에 jui 속성 추가 elem.jui = uiObj; return uiObj; }, /** * @method define * * UI 클래스에서 사용될 클래스를 정의하고, 자유롭게 상속할 수 있는 클래스를 정의 * * @param {String} name 모듈 로드와 상속에 사용될 이름을 정한다. * @param {Array} depends 'define'이나 'defineUI'로 정의된 클래스나 객체를 인자로 받을 수 있다. * @param {Function} callback UI 클래스를 해당 콜백 함수 내에서 클래스 형태로 구현하고 리턴해야 한다. * @param {String} parent 상속받을 클래스 */ define: function (name, depends, callback, parent) { if (!utility.typeCheck("string", name) || !utility.typeCheck("array", depends) || !utility.typeCheck("function", callback) || !utility.typeCheck([ "string", "undefined", "null" ], parent)) { throw new Error("JUI_CRITICAL_ERR: Invalid parameter type of the function"); } if (utility.typeCheck("function", globalClass[name])) { throw new Error("JUI_CRITICAL_ERR: '" + name + "' is already exist"); } var args = getDepends(depends), uiFunc = callback.apply(null, args); if (utility.typeCheck("function", globalClass[parent])) { if (globalFunc[parent] !== true) { throw new Error("JUI_CRITICAL_ERR: UI function can not be inherited"); } else { utility.inherit(uiFunc, globalClass[parent]); } } // 함수 고유 설정 global[name] = uiFunc; globalClass[name] = uiFunc; // original function globalFunc[name] = true; // support AMD module // @deprecated /* if (typeof define == "function" && define.amd) { define(name, function () { return global[name] }); }*/ }, /** * @method redefine * * UI 클래스에서 사용될 클래스를 정의하고, 자유롭게 상속할 수 있는 클래스를 정의 * * @param {String} name 모듈 로드와 상속에 사용될 이름을 정한다. * @param {Array} depends 'define'이나 'defineUI'로 정의된 클래스나 객체를 인자로 받을 수 있다. * @param {Function} callback UI 클래스를 해당 콜백 함수 내에서 클래스 형태로 구현하고 리턴해야 한다. * @param {String} parent 상속받을 클래스 */ redefine: function (name, depends, callback, parent, skip) { if (!skip && globalFunc[name] === true) { global[name] = null; globalClass[name] = null; globalFunc[name] = false; } if (!skip || (skip && globalFunc[name] !== true)) { this.define(name, depends, callback, parent); } }, /** * @method defineOptions * * 모듈 기본 옵션 정의 * * @param {Object} Module * @param {Object} options * @param {Object} exceptOpts * @return {Object} */ defineOptions: function (Module, options, exceptOpts) { var defOpts = getOptions(Module, {}); var defOptKeys = Object.keys(defOpts), optKeys = Object.keys(options); // 정의되지 않은 옵션 사용 유무 체크 for (var i = 0; i < optKeys.length; i++) { var name = optKeys[i]; if (utility.inArray(name, defOptKeys) == -1 && utility.inArray(name, exceptOpts) == -1) { throw new Error("JUI_CRITICAL_ERR: '" + name + "' is not an option"); } } // 사용자 옵션 + 기본 옵션 utility.extend(options, defOpts, true); // 상위 모듈의 옵션까지 모두 얻어오는 함수 function getOptions(Module, options) { if (utility.typeCheck("function", Module)) { if (utility.typeCheck("function", Module.setup)) { var opts = Module.setup(); for (var key in opts) { if (utility.typeCheck("undefined", options[key])) { options[key] = opts[key]; } } } getOptions(Module.parent, options); } return options; } return options; }, /** * define과 defineUI로 정의된 클래스 또는 객체를 가져온다. * * @param name 가져온 클래스 또는 객체의 이름 * @return {*} */ include: function (name) { if (!utility.typeCheck("string", name)) { throw new Error("JUI_CRITICAL_ERR: Invalid parameter type of the function"); } var module = global[name]; if (utility.typeCheck(["function", "object"], module)) { return module; } else { var modules = getModules(name); if (modules == null) { console.log("JUI_WARNING_MSG: '" + name + "' is not loaded"); return null; } else { return modules; } } }, /** * define과 defineUI로 정의된 모든 클래스와 객체를 가져온다. * * @return {Array} */ includeAll: function () { var result = []; for (var key in global) { result.push(global[key]); } return result; }, /** * 설정된 jui 관리 화면을 윈도우 팝업으로 띄운다. * * @param logUrl * @return {Window} */ log: function (logUrl) { var jui_mng = window.open( logUrl || globalOpts.logUrl, "JUIM", "width=1024, height=768, toolbar=no, menubar=no, resizable=yes" ); jui.debugAll(function (log, str) { jui_mng.log(log, str); }); return jui_mng; }, setup: function (options) { if (utility.typeCheck("object", options)) { globalOpts = utility.extend(globalOpts, options); } return globalOpts; } }; if (typeof module == 'object' && module.exports) { module.exports = window.jui || global.jui; } })(window, (typeof(global) !== "undefined") ? global : window);