(function($, window, nodeGlobal) {
var global = { jquery: $ }, globalFunc = {}, globalClass = {} ;
var navigator = window.navigator;
// JUI의 기본 설정 값 (향후 더 추가될 수 있음)
var globalOpts = {
template: {
evaluate : /<\!([\s\S]+?)\!>/g,
interpolate : /<\!=([\s\S]+?)\!>/g,
escape : /<\!-([\s\S]+?)\!>/g
},
logUrl: "tool/debug.html"
};
/**
* @class QuickSort
*
* 퀵 정렬
*
* @param {Array} array
* @param {Boolean} isClone isClone 이 true 이면, 해당 배열을 참조하지 않고 복사해서 처리
* @constructor
* @private
*/
var QuickSort = function(array, isClone) { //
var compareFunc = null,
array = (isClone) ? array.slice(0) : array;
function swap(indexA, indexB) {
var temp = array[indexA];
array[indexA] = array[indexB];
array[indexB] = temp;
}
function partition(pivot, left, right) {
var storeIndex = left, pivotValue = array[pivot];
swap(pivot, right);
for(var v = left; v < right; v++) {
if(compareFunc(array[v], pivotValue) || !compareFunc(pivotValue, array[v]) && v%2 == 1) {
swap(v, storeIndex);
storeIndex++;
}
}
swap(right, storeIndex);
return storeIndex;
}
this.setCompare = function(func) {
compareFunc = func;
}
this.run = function(left, right) {
var pivot = null;
if (typeof left !== 'number') {
left = 0;
}
if (typeof right !== 'number') {
right = array.length - 1;
}
if (left < right) {
pivot = left + Math.ceil((right - left) * 0.5);
newPivot = partition(pivot, left, right);
this.run(left, newPivot - 1);
this.run(newPivot + 1, right);
}
return array;
}
}
/**
* @class IndexParser
*
* 0.0.1 형식의 키 문자열을 제어하는 클래스
*
* @private
* @constructor
*/
var IndexParser = function() {
/**
* @method isIndexDepth
*
* @param {String} index
* @return {Boolean}
*/
this.isIndexDepth = function(index) {
if(typeof(index) == "string" && index.indexOf(".") != -1) {
return true;
}
return false;
}
/**
* @method getIndexList
*
* @param {String} index
* @return {Array}
*/
this.getIndexList = function(index) { // 트리 구조의 모든 키를 배열 형태로 반환
var resIndex = [], strIndex = "" + index;
if(strIndex.length == 1) {
resIndex[0] = parseInt(index);
} else {
var keys = strIndex.split(".");
for(var i = 0; i < keys.length; i++) {
resIndex[i] = parseInt(keys[i]);
}
}
return resIndex;
}
/**
* @method changeIndex
*
*
* @param {String} index
* @param {String} targetIndex
* @param {String} rootIndex
* @return {String}
*/
this.changeIndex = function(index, targetIndex, rootIndex) {
var rootIndexLen = this.getIndexList(rootIndex).length,
indexList = this.getIndexList(index),
tIndexList = this.getIndexList(targetIndex);
for(var i = 0; i < rootIndexLen; i++) {
indexList.shift();
}
return tIndexList.concat(indexList).join(".");
}
/**
* @method getNextIndex
*
* @param {String} index
* @return {String}
*/
this.getNextIndex = function(index) { // 현재 인덱스에서 +1
var indexList = this.getIndexList(index),
no = indexList.pop() + 1;
indexList.push(no);
return indexList.join(".");
}
/**
* @method getParentIndex
*
*
* @param {String} index
* @returns {*}
*/
this.getParentIndex = function(index) {
if(!this.isIndexDepth(index)) return null;
var keys = this.getIndexList(index);
if(keys.length == 2) {
return "" + keys[0];
} else if(keys.length > 2) {
keys.pop();
return keys.join(".");
}
}
}
/**
* Private Static Classes
*
*/
var Base64 = {
// private property
_keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
// public method for encoding
encode : function (input) {
var output = "";
var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
var i = 0;
input = Base64._utf8_encode(input);
while (i < input.length) {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output = output +
Base64._keyStr.charAt(enc1) + Base64._keyStr.charAt(enc2) +
Base64._keyStr.charAt(enc3) + Base64._keyStr.charAt(enc4);
}
return output;
},
// public method for decoding
decode : function (input) {
var output = "";
var chr1, chr2, chr3;
var enc1, enc2, enc3, enc4;
var i = 0;
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
while (i < input.length) {
enc1 = Base64._keyStr.indexOf(input.charAt(i++));
enc2 = Base64._keyStr.indexOf(input.charAt(i++));
enc3 = Base64._keyStr.indexOf(input.charAt(i++));
enc4 = Base64._keyStr.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output = output + String.fromCharCode(chr1);
if (enc3 != 64) {
output = output + String.fromCharCode(chr2);
}
if (enc4 != 64) {
output = output + String.fromCharCode(chr3);
}
}
output = Base64._utf8_decode(output);
return output;
},
// private method for UTF-8 encoding
_utf8_encode : function (string) {
string = string.replace(/\r\n/g,"\n");
// BOM 코드 적용 (UTF-8 관련)
var utftext = String.fromCharCode(239) + String.fromCharCode(187) + String.fromCharCode(191);
for (var n = 0; n < string.length; n++) {
var c = string.charCodeAt(n);
if (c < 128) {
utftext += String.fromCharCode(c);
}
else if((c > 127) && (c < 2048)) {
utftext += String.fromCharCode((c >> 6) | 192);
utftext += String.fromCharCode((c & 63) | 128);
}
else {
utftext += String.fromCharCode((c >> 12) | 224);
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
utftext += String.fromCharCode((c & 63) | 128);
}
}
return utftext;
},
// private method for UTF-8 decoding
_utf8_decode : function (utftext) {
var string = "";
var i = 0;
var c = c1 = c2 = 0;
while ( i < utftext.length ) {
c = utftext.charCodeAt(i);
if (c < 128) {
string += String.fromCharCode(c);
i++;
}
else if((c > 191) && (c < 224)) {
c2 = utftext.charCodeAt(i+1);
string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
i += 2;
}
else {
c2 = utftext.charCodeAt(i+1);
c3 = utftext.charCodeAt(i+2);
string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
i += 3;
}
}
return string;
}
}
/**
* Private Functions
*
*/
var template = function(text, data, settings) {
var _ = {},
breaker = {};
var ArrayProto = Array.prototype,
slice = ArrayProto.slice,
nativeForEach = ArrayProto.forEach;
var escapes = {
'\\' : '\\',
"'" : "'",
'r' : '\r',
'n' : '\n',
't' : '\t',
'u2028' : '\u2028',
'u2029' : '\u2029'
};
for (var p in escapes)
escapes[escapes[p]] = p;
var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g,
unescaper = /\\(\\|'|r|n|t|u2028|u2029)/g,
noMatch = /.^/;
var unescape = function(code) {
return code.replace(unescaper, function(match, escape) {
return escapes[escape];
});
};
var each = _.each = _.forEach = function(obj, iterator, context) {
if (obj == null)
return;
if (nativeForEach && obj.forEach === nativeForEach) {
obj.forEach(iterator, context);
} else if (obj.length === +obj.length) {
for (var i = 0, l = obj.length; i < l; i++) {
if ( i in obj && iterator.call(context, obj[i], i, obj) === breaker)
return;
}
} else {
for (var key in obj) {
if (_.has(obj, key)) {
if (iterator.call(context, obj[key], key, obj) === breaker)
return;
}
}
}
};
_.has = function(obj, key) {
return hasOwnProperty.call(obj, key);
};
_.defaults = function(obj) {
each(slice.call(arguments, 1), function(source) {
for (var prop in source) {
if (obj[prop] == null)
obj[prop] = source[prop];
}
});
return obj;
};
_.template = function(text, data, settings) {
settings = _.defaults(settings || {}, globalOpts.template);
var source = "__p+='" + text.replace(escaper, function(match) {
return '\\' + escapes[match];
}).replace(settings.escape || noMatch, function(match, code) {
return "'+\n_.escape(" + unescape(code) + ")+\n'";
}).replace(settings.interpolate || noMatch, function(match, code) {
return "'+\n(" + unescape(code) + ")+\n'";
}).replace(settings.evaluate || noMatch, function(match, code) {
return "';\n" + unescape(code) + "\n;__p+='";
}) + "';\n";
if (!settings.variable)
source = 'with(obj||{}){\n' + source + '}\n';
source = "var __p='';" + "var print=function(){__p+=Array.prototype.join.call(arguments, '')};\n" + source + "return __p;\n";
var render = new Function(settings.variable || 'obj', '_', source);
if (data)
return render(data, _);
var template = function(data) {
return render.call(this, data, _);
};
template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}';
return template;
};
return _.template(text, data, settings);
}
/**
* @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: (typeof window.webkitURL != "undefined") ? true : false,
mozilla: (typeof window.mozInnerScreenX != "undefined") ? true : false,
msie: (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(navigator.userAgent),
//-- Functions
/**
* @method scrollWidth
* returns scroll width for body
* @return {Number}
*/
scrollWidth: function() {
var isJUI = ($(".jui").size() > 0 && this.browser.webkit) ? true : false;
var div = $('
');
$('body').append(div);
var w1 = $('div', div).innerWidth();
div.css('overflow-y', 'auto');
var w2 = $('div', div).innerWidth();
$(div).remove();
return (isJUI) ? 10 : (w1 - w2);
},
/**
* @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 origin
* @param add
* @param skip
* @return {Object}
*/
extend: function(origin, add, skip) {
if(!this.typeCheck("object", origin)) origin = {};
if(!this.typeCheck("object", 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(typeof(px) == "string" && 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 = ($.isArray(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) {
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) {
if(!obj) return template(html);
else return template(html, obj);
},
/**
* @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);
}
})();
$(window).resize(function() {
after_resize();
});
},
/**
* @method index
*
* IndexParser 객체 생성
*
* @return {IndexParser}
*/
index: function() {
return new IndexParser();
},
/**
* @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 = $.extend({
fields: null, // required
rows: null, // required
names: 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]];
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(typeof(callback) == "function") {
callback(readerEvt.target.result);
}
};
reader.readAsText(file);
},
/**
* @method csvToBase64
*
* csv 다운로드 링크로 변환
*
* @param {String} csv
* @return {String}
*/
csvToBase64: function(csv) {
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($.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) {
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: Base64.encode,
/**
* @method atob
*
* Base64 디코딩
*
* @return {String}
*/
atob: Base64.decode,
/**
* @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;
}
}
/*
* 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) {
throw new Error("JUI_CRITICAL_ERR: '" + depends[i] + "' is not loaded");
} 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");
}
$(function() {
if(depends) {
args = getDepends(depends);
} else {
args = [ getModules("ui"), getModules("uix"), utility ];
}
callback.apply(null, args);
});
},
/**
* @method defineUI
*
* 사용자가 실제로 사용할 수 있는 UI 클래스를 정의
*
* @param {String} name 모듈 로드와 상속에 사용될 이름을 정한다.
* @param {Array} depends 'define'이나 'defineUI'로 정의된 클래스나 객체를 인자로 받을 수 있다.
* @param {Function} callback UI 클래스를 해당 콜백 함수 내에서 클래스 형태로 구현하고 리턴해야 한다.
* @param {String} parent 'depends'와 달리 'define'으로 정의된 클래스만 상속받을 수 있다.
*/
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)) { // 기본적으로 'core' 클래스를 상속함
parent = "core";
}
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]);
// UI 고유 설정
global[name] = globalClass["core"].init({
type: name,
"class": uiFunc
});
globalClass[name] = uiFunc;
globalFunc[name] = true;
// support AMD module
if(typeof define == "function" && define.amd) {
define(name, function() { return global[name] });
}
},
/**
* @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" ], 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
if(typeof define == "function" && define.amd) {
define(name, function() { return global[name] });
}
},
/**
* @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($.inArray(name, defOptKeys) == -1 && $.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) {
throw new Error("JUI_CRITICAL_ERR: '" + name + "' is not loaded");
} 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;
}
};
})(jQuery || $, window, (typeof global !== "undefined") ? global : window);
jui.define("core", [ "jquery", "util.base" ], function($, _) {
/**
* @class core.UIManager
* @private
* @singleton
*/
var UIManager = new function() {
var instances = [], classes = [];
/**
* @method add
* Adds a component object created
*
* @param {Object} ui UI instance
*/
this.add = function(uiIns) {
instances.push(uiIns);
}
/**
* @method emit
* Generates a custom event to an applicable component
*
* @param {String} key Selector or UI type
* @param {String} type Event type
* @param {Array} args Event arguments
*/
this.emit = function(key, type, args) {
var targets = [];
for(var i = 0; i < instances.length; i++) {
var uiSet = instances[i];
if(key == uiSet.selector || key == uiSet.type) {
targets.push(uiSet);
}
}
for(var i = 0; i < targets.length; i++) {
var uiSet = targets[i];
for(var j = 0; j < uiSet.length; j++) {
uiSet[j].emit(type, args);
}
}
}
/**
* @method get
* Gets a component currently created
*
* @param {Integer/String} key
* @returns {Object/Array} UI instance
*/
this.get = function(key) {
if(_.typeCheck("integer", key)) {
return instances[key];
} else if(_.typeCheck("string", key)) {
// 셀렉터 객체 검색
for(var i = 0; i < instances.length; i++) {
var uiSet = instances[i];
if(key == uiSet.selector) {
return (uiSet.length == 1) ? uiSet[0] : uiSet;
}
}
// 모듈 객체 검색
var result = [];
for(var i = 0; i < instances.length; i++) {
var uiSet = instances[i];
if(key == uiSet.type) {
result.push(uiSet);
}
}
return result;
}
}
/**
* @method getAll
* Gets all components currently created
*
* @return {Array} UI instances
*/
this.getAll = function() {
return instances;
}
/**
* @method remove
* Removes a component object in an applicable index from the list
*
* @param {Integer} index
* @return {Object} Removed instance
*/
this.remove = function(index) {
if(_.typeCheck("integer", index)) { // UI 객체 인덱스
return instances.splice(index, 1)[0];
}
}
/**
* @method shift
* Removes the last component object from the list
*
* @return {Object} Removed instance
*/
this.shift = function() {
return instances.shift();
}
/**
* @method pop
* Removes the first component object from the list
*
* @return {Object} Removed instance
*/
this.pop = function() {
return instances.pop();
}
/**
* @method size
* Gets the number of objects currently created
*
* @return {Number}
*/
this.size = function() {
return instances.length;
}
/**
* @method debug
*
* @param {Object} uiObj UI instance
* @param {Number} i
* @param {Number} j
* @param {Function} callback
*/
this.debug = function(uiObj, i, j, callback) {
if(!uiObj.__proto__) return;
var exFuncList = [ "emit", "on", "addEvent", "addValid", "callBefore",
"callAfter", "callDelay", "setTpl", "setVo", "setOption" ];
for(var key in uiObj) {
var func = uiObj[key];
if(typeof(func) == "function" && $.inArray(key, exFuncList) == -1) {
(function(funcName, funcObj, funcIndex, funcChildIndex) {
uiObj.__proto__[funcName] = function() {
var nStart = Date.now();
var resultObj = funcObj.apply(this, arguments);
var nEnd = Date.now();
if(typeof(callback) == "function") {
callback({
type: jui.get(i).type,
name: funcName,
c_index: funcIndex,
u_index: funcChildIndex,
time: nEnd - nStart
}, arguments);
} else {
if(!isNaN(funcIndex) && !isNaN(funcChildIndex)) {
console.log(
"TYPE(" + jui.get(i).type + "), " +
"NAME(" + funcName + "), " +
"INDEX(" + funcIndex + ":" + funcChildIndex + "), " +
"TIME(" + (nEnd - nStart) + "ms), " +
"ARGUMENTS..."
);
} else {
console.log(
"NAME(" + funcName + "), " +
"TIME(" + (nEnd - nStart) + "ms), " +
"ARGUMENTS..."
);
}
console.log(arguments);
console.log("");
}
return resultObj;
}
})(key, func, i, j);
}
}
}
/**
* @method debugAll
* debugs all component objects currently existing
*
* @param {Function} callback
*/
this.debugAll = function(callback) {
for(var i = 0; i < instances.length; i++) {
var uiList = instances[i];
for(var j = 0; j < uiList.length; j++) {
this.debug(uiList[j], i, j, callback);
}
}
}
/**
* @method addClass
* Adds a component class
*
* @param {Object} uiCls UI Class
*/
this.addClass = function(uiCls) {
classes.push(uiCls);
}
/**
* @method getClass
* Gets a component class
*
* @param {String/Integer} key
* @return {Object}
*/
this.getClass = function(key) {
if(_.typeCheck("integer", key)) {
return classes[key];
} else if(_.typeCheck("string", key)) {
for(var i = 0; i < classes.length; i++) {
if(key == classes[i].type) {
return classes[i];
}
}
}
return null;
}
/**
* @method getClassAll
* Gets all component classes
*
* @return {Array}
*/
this.getClassAll = function() {
return classes;
}
/**
* @method create
* It is possible to create a component dynamically after the ready point
*
* @param {String} type UI type
* @param {String/DOMElement} selector
* @param {Object} options
* @return {Object}
*/
this.create = function(type, selector, options) {
var cls = UIManager.getClass(type);
if(_.typeCheck("null", cls)) {
throw new Error("JUI_CRITICAL_ERR: '" + type + "' does not exist");
}
return cls["class"](selector, options);
}
}
var UIListener = function() {
var list = [];
function settingEventAnimation(e) {
var pfx = [ "webkit", "moz", "MS", "o", "" ];
for(var p = 0; p < pfx.length; p++) {
var type = e.type;
if(!pfx[p]) type = type.toLowerCase();
$(e.target).on(pfx[p] + type, e.callback);
}
list.push(e);
}
function settingEvent(e) {
if(e.callback && !e.children) {
$(e.target).on(e.type, e.callback);
} else {
$(e.target).on(e.type, e.children, e.callback);
}
list.push(e);
}
function settingEventTouch(e) {
if(e.callback && !e.children) {
$(e.target).on(getEventTouchType(e.type), e.callback);
} else {
$(e.target).on(getEventTouchType(e.type), e.children, e.callback);
}
list.push(e);
}
function getEventTouchType(type) {
return {
"click": "touchstart",
"dblclick": "touchend",
"mousedown": "touchstart",
"mousemove": "touchmove",
"mouseup": "touchend"
}[type];
}
this.add = function(args) {
var e = { target: args[0], type: args[1] };
if(_.typeCheck("function", args[2])) {
e = $.extend(e, { callback: args[2] });
} else if(_.typeCheck("string", args[2])) {
e = $.extend(e, { children: args[2], callback: args[3] });
}
// 이벤트 유형을 배열로 변경
var eventTypes = _.typeCheck("array", e.type) ? e.type : [ e.type ];
// 이벤트 유형에 따른 이벤트 설정
for(var i = 0; i < eventTypes.length; i++) {
e.type = eventTypes[i]
if (e.type.toLowerCase().indexOf("animation") != -1)
settingEventAnimation(e);
else {
if (e.target != "body" && e.target != window) { // body와 window일 경우에만 이벤트 중첩이 가능
$(e.target).off(e.type);
}
if (_.isTouch) {
settingEventTouch(e);
} else {
settingEvent(e);
}
}
}
}
this.trigger = function(selector, type) {
$(selector).trigger((_.isTouch) ? getEventTouchType(type) : type);
}
this.get = function(index) {
return list[index];
}
this.getAll = function() {
return list;
}
this.size = function() {
return list.length;
}
}
var UICoreSet = function(type, selector, options, list) {
this.type = type;
this.selector = selector;
this.options = options;
this.destroy = function() {
for(var i = 0; i < list.length; i++) {
list[i].destroy();
}
}
for(var i = 0; i < list.length; i++) {
this.push(list[i]);
}
}
// 배열 클래스 상속
UICoreSet.prototype = Object.create(Array.prototype);
/**
* @class core
* Core classes for all of the components
*
* @alias UICore
*/
var UICore = function() {
var vo = null;
/**
* @method find
* Get the child element of the root element
*
* @param {String/HTMLElement} Selector
* @returns {*|jQuery}
*/
this.find = function(selector) {
return $(this.root).find(selector);
}
/**
* @method emit
* Generates a custom event. The first parameter is the type of a custom event. A function defined as an option or on method is called
*
* @param {String} type Event type
* @param {Function} args Event Arguments
* @return {Mixed}
*/
this.emit = function(type, args) {
if(typeof(type) != "string") return;
var result;
for(var i = 0; i < this.event.length; i++) {
var e = this.event[i];
if(e.type == type.toLowerCase()) {
var arrArgs = (typeof(args) == "object" && args.length) ? args : [ args ];
result = e.callback.apply(this, arrArgs);
}
}
return result;
}
/**
* @method on
* A callback function defined as an on method is run when an emit method is called
*
* @param {String} type Event type
* @param {Function} callback
*/
this.on = function(type, callback) {
if(typeof(type) != "string" || typeof(callback) != "function") return;
this.event.push({ type: type.toLowerCase(), callback: callback, unique: false });
}
/**
* @method off
* Removes a custom event of an applicable type or callback handler
*
* @param {String} type Event type
*/
this.off = function(type) {
var event = [];
for(var i = 0; i < this.event.length; i++) {
var e = this.event[i];
if ((typeof(type) == "function" && e.callback != type) ||
(typeof(type) == "string" && e.type != type.toLowerCase())) {
event.push(e);
}
}
this.event = event;
}
/**
* @method addEvent
* Defines a browser event of a DOM element
*
* @param {String/HTMLElement} selector
* @param {String} type Dom event type
* @param {Function} callback
*/
this.addEvent = function() {
this.listen.add(arguments);
}
/**
* @method addTrigger
* Generates an applicable event to a DOM element
*
* @param {String/HTMLElement} Selector
* @param {String} Dom event type
*/
this.addTrigger = function(selector, type) {
this.listen.trigger(selector, type);
}
/**
* @method addValid
* Check the parameter type of a UI method and generates an alarm when a wrong value is entered
*
* @param {String} name Method name
* @param {Array} params Parameters
*/
this.addValid = function(name, params) {
if(!this.__proto__) return;
var ui = this.__proto__[name];
this.__proto__[name] = function() {
var args = arguments;
for(var i = 0; i < args.length; i++) {
if(!_.typeCheck(params[i], args[i])) {
throw new Error("JUI_CRITICAL_ERR: the " + i + "th parameter is not a " + params[i] + " (" + name + ")");
}
}
return ui.apply(this, args);
}
}
/**
* @method callBefore
* Sets a callback function that is called before a UI method is run
*
* @param {String} name Method name
* @param {Function} callback
* @return {Mixed}
*/
this.callBefore = function(name, callback) {
if(!this.__proto__) return;
var ui = this.__proto__[name];
this.__proto__[name] = function() {
var args = arguments;
if(typeof(callback) == "function") {
// before 콜백이 false가 이날 경우에만 실행 한다.
if(callback.apply(this, args) !== false) {
return ui.apply(this, args);
}
} else {
return ui.apply(this, args);
}
}
}
/**
* @method callAfter
* Sets a callback function that is called after a UI method is run
*
* @param {String} name Method name
* @param {Function} callback
* @return {Mixed}
*/
this.callAfter = function(name, callback) {
if(!this.__proto__) return;
var ui = this.__proto__[name];
this.__proto__[name] = function() {
var args = arguments,
obj = ui.apply(this, args);
// 실행 함수의 리턴 값이 false일 경우에는 after 콜백을 실행하지 않는다.
if(typeof(callback) == "function" && obj !== false) {
callback.apply(this, args);
}
return obj;
}
}
/**
* @method callDelay
* Sets a callback function and the delay time before/after a UI method is run
*
* @param {String} name Method name
* @param {Function} callback
*/
this.callDelay = function(name, callObj) { // void 형의 메소드에서만 사용할 수 있음
if(!this.__proto__) return;
var ui = this.__proto__[name],
delay = (!isNaN(callObj.delay)) ? callObj.delay : 0;
this.__proto__[name] = function() {
var self = this,
args = arguments;
if(typeof(callObj.before) == "function") {
callObj.before.apply(self, args);
}
if(delay > 0) {
setTimeout(function() {
callFunc(self, args);
}, delay);
} else {
callFunc(self, args);
}
}
function callFunc(self, args) {
var obj = ui.apply(self, args);
if(typeof(callObj.after) == "function" && obj !== false) { // callAfter와 동일
callObj.after.apply(self, args);
}
}
}
/**
* @method setTpl
* Dynamically defines the template method of a UI
*
* @param {String} name Template name
* @param {String} html Template markup
*/
this.setTpl = function(name, html) {
this.tpl[name] = _.template(html);
}
/**
* @method setVo
* Dynamically defines the template method of a UI
*
* @deprecated
*/
this.setVo = function() { // @Deprecated
if(!this.options.vo) return;
if(vo != null) vo.reload();
vo = $(this.selector).jbinder();
this.bind = vo;
}
/**
* @method setOption
* Dynamically defines the options of a UI
*
* @param {String} key
* @param {Mixed} value
*/
this.setOption = function(key, value) {
if(typeof(key) == "object") {
for(var k in key) {
this.options[k] = key[k];
}
} else {
this.options[key] = value;
}
}
/**
* @method destroy
* Removes all events set in a UI obejct and the DOM element
*
*/
this.destroy = function() {
if(!this.__proto__) return;
for(var i = 0; i < this.listen.size(); i++) {
var obj = this.listen.get(i);
$(obj.target).off(obj.type);
}
for(var key in this.__proto__) {
delete this.__proto__[key];
}
}
};
UICore.build = function(UI) {
return function(selector, options) {
var $root = $(selector || "");
var list = [];
$root.each(function(index) {
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 = $root.selector;
/** @property {HTMLElement} root */
mainObj.init.prototype.root = this;
/** @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 {Object} listen Dom events */
mainObj.init.prototype.listen = new UIListener(); // DOM 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;
// Template Setting (Markup)
$("script").each(function(i) {
if(selector == $(this).data("jui") || selector == $(this).data("vo") || selector instanceof HTMLElement) {
var tplName = $(this).data("tpl");
if(tplName == "") {
throw new Error("JUI_CRITICAL_ERR: 'data-tpl' property is required");
}
opts.tpl[tplName] = $(this).html();
}
});
// Template Setting (Script)
for(var name in opts.tpl) {
var tplHtml = opts.tpl[name];
if(_.typeCheck("string", tplHtml) && tplHtml != "") {
mainObj.init.prototype.tpl[name] = _.template(tplHtml);
}
}
var uiObj = new mainObj.init();
// Event Setting
for(var key in opts.event) {
uiObj.on(key, opts.event[key]);
}
list[index] = uiObj;
// 엘리먼트 객체에 jui 속성 추가
this.jui = uiObj;
});
// UIManager에 데이터 입력
UIManager.add(new UICoreSet(UI.type, selector, options, list));
// 객체가 없을 경우에는 null을 반환 (기존에는 빈 배열을 반환)
if(list.length == 0) {
return null;
} else if(list.length == 1) {
return list[0];
}
return list;
}
}
UICore.init = function(UI) {
var uiObj = null;
if(typeof(UI) === "object") {
uiObj = UICore.build(UI);
UIManager.addClass({ type: UI.type, "class": uiObj });
}
return uiObj;
}
UICore.setup = function() {
return {
/**
* @cfg {Object} [tpl={}]
* Defines a template markup to be used in a UI
*/
tpl: {},
/**
* @cfg {Object} [event={}]
* Defines a DOM event to be used in a UI
*/
event: {},
/**
* @cfg {Object} [vo=null]
* Configures a binding object of a markup
*
* @deprecated
*/
vo: null
}
}
/**
* @class jui
*
* @extends core.UIManager
* @singleton
*/
window.jui = (typeof(jui) == "object") ? $.extend(jui, UIManager) : UIManager;
return UICore;
});
jui.define("util.math", [], function() {
/**
* @class util.math
*
* Math Utility
*
* @singleton
*/
var self = {
/**
* @method rotate
*
* 2d rotate
*
* @param {Number} x
* @param {Number} y
* @param {Number} radian roate 할 radian
* @return {Object}
* @return {Number} return.x 변환된 x
* @return {Number} return.y 변환된 y
*
*/
rotate : function(x, y, radian) {
return {
x : x * Math.cos(radian) - y * Math.sin(radian),
y : x * Math.sin(radian) + y * Math.cos(radian)
}
},
resize : function(maxWidth, maxHeight, objectWidth, objectHeight) {
var ratio = objectHeight / objectWidth;
if (objectWidth >= maxWidth && ratio <= 1) {
objectWidth = maxWidth;
objectHeight = maxHeight * ratio;
} else if (objectHeight >= maxHeight) {
objectHeight = maxHeight;
objectWidth = maxWidth / ratio;
}
return { width : objectWidth, height : objectHeight};
},
/**
* @method radian
*
* convert degree to radian
*
* @param {Number} degree
* @return {Number} radian
*/
radian : function(degree) {
return degree * Math.PI / 180;
},
/**
* @method degree
*
* convert radian to degree
*
* @param {Number} radian
* @return {Number} degree
*/
degree : function(radian) {
return radian * 180 / Math.PI;
},
angle : function(x1, y1, x2, y2) {
var dx = x2 - x1,
dy = y2 - y1;
return Math.atan2(dy, dx);
},
/**
* @method interpolateNumber
*
* a, b 의 중간값 계산을 위한 callback 함수 만들기
*
* @param {Number} a first value
* @param {Number} b second value
* @return {Function}
*/
interpolateNumber : function(a, b) {
var dist = (b - a);
return function(t) {
return a + dist * t;
}
},
// 중간값 round 해서 계산하기
interpolateRound : function(a, b) {
var dist = (b - a);
return function(t) {
return Math.round(a + dist * t);
}
},
/**
* 특정 구간의 값을 자동으로 계산
*
* @param {Object} min
* @param {Object} max
* @param {Object} ticks
* @param {Object} isNice
*/
nice : function(min, max, ticks, isNice) {
isNice = isNice || false;
if (min > max) {
var _max = min;
var _min = max;
} else {
var _min = min;
var _max = max;
}
var _ticks = ticks;
var _tickSpacing = 0;
var _range = [];
var _niceMin;
var _niceMax;
function niceNum(range, round) {
var exponent = Math.floor(Math.log(range) / Math.LN10);
var fraction = range / Math.pow(10, exponent);
var nickFraction;
//console.log(range, exponent, fraction, _ticks);
if (round) {
if (fraction < 1.5)
niceFraction = 1;
else if (fraction < 3)
niceFraction = 2;
else if (fraction < 7)
niceFraction = 5;
else
niceFraction = 10;
} else {
if (fraction <= 1)
niceFraction = 1;
else if (fraction <= 2)
niceFraction = 2;
else if (fraction <= 5)
niceFraction = 5;
else
niceFraction = 10;
//console.log(niceFraction)
}
return niceFraction * Math.pow(10, exponent);
}
function caculate() {
_range = (isNice) ? niceNum(_max - _min, false) : _max - _min;
_tickSpacing = (isNice) ? niceNum(_range / _ticks, true) : _range / _ticks;
_niceMin = (isNice) ? Math.floor(_min / _tickSpacing) * _tickSpacing : _min;
_niceMax = (isNice) ? Math.floor(_max / _tickSpacing) * _tickSpacing : _max;
}
caculate();
return {
min : _niceMin,
max : _niceMax,
range : _range,
spacing : _tickSpacing
}
}
}
return self;
});
jui.define("util.time", [ "util.base" ], function(_) {
/**
* time 객체
*
*/
var self = {
// unit
years : 0x01,
months : 0x02,
days : 0x03,
hours : 0x04,
minutes : 0x05,
seconds : 0x06,
milliseconds : 0x07,
weeks : 0x08,
/**
* 시간 더하기
* var date = new Date();
*
* time.add(date, time.hours, 1); // 현재시간에서 1시간 추가
* time.add(date, time.hours, 1, time.minutes, 2); // 현재시간에서 1시간 2분 추가
*
* @param {Object} date
*/
add : function(date) {
if (arguments.length <= 2) {
return date;
}
if (arguments.length > 2) {
var d = new Date(+date);
for (var i = 1; i < arguments.length; i += 2) {
var split = typeof arguments[i] == 'string' ? this[arguments[i]] : arguments[i];
var time = arguments[i + 1];
if (this.years == split) {
d.setFullYear(d.getFullYear() + time);
} else if (this.months == split) {
d.setMonth(d.getMonth() + time);
} else if (this.days == split) {
d.setDate(d.getDate() + time);
} else if (this.hours == split) {
d.setHours(d.getHours() + time);
} else if (this.minutes == split) {
d.setMinutes(d.getMinutes() + time);
} else if (this.seconds == split) {
d.setSeconds(d.getSeconds() + time);
} else if (this.milliseconds == split) {
d.setMilliseconds(d.getMilliseconds() + time);
} else if (this.weeks == split) {
d.setDate(d.getDate() + time * 7);
}
}
return d;
}
},
/**
* jui.util.dateFormat 의 alias
*
* @param {Object} date
* @param {Object} format
* @param {Object} utc
*/
format: function(date, format, utc) {
return _.dateFormat(date, format, utc);
}
}
return self;
});
jui.define("util.scale", [ "util.math", "util.time" ], function(math, _time) {
/**
* scale utility
* @class util.scale
* @singleton
*/
var self = {
/**
* 원형 좌표에 대한 scale
*
*/
circle : function() {// 원형 radar
var that = this;
var _domain = [];
var _range = [];
var _rangeBand = 0;
function func(t) {
}
func.domain = function(values) {
if ( typeof values == 'undefined') {
return _domain;
}
for (var i = 0; i < values.length; i++) {
_domain[i] = values[i];
}
return this;
}
func.range = function(values) {
if ( typeof values == 'undefined') {
return _range;
}
for (var i = 0; i < values.length; i++) {
_range[i] = values[i];
}
return this;
}
func.rangePoints = function(interval, padding) {
padding = padding || 0;
var step = _domain.length;
var unit = (interval[1] - interval[0] - padding) / step;
var range = [];
for (var i = 0; i < _domain.length; i++) {
if (i == 0) {
range[i] = interval[0] + padding / 2 + unit / 2;
} else {
range[i] = range[i - 1] + unit;
}
}
_range = range;
_rangeBand = unit;
return func;
}
func.rangeBands = function(interval, padding, outerPadding) {
padding = padding || 0;
outerPadding = outerPadding || 0;
var count = _domain.length;
var step = count - 1;
var band = (interval[1] - interval[0]) / step;
var range = [];
for (var i = 0; i < _domain.length; i++) {
if (i == 0) {
range[i] = interval[0];
} else {
range[i] = band + range[i - 1];
}
}
_rangeBand = band;
_range = range;
return func;
}
func.rangeBand = function() {
return _rangeBand;
}
return func;
},
/**
*
* 순서를 가지는 리스트에 대한 scale
*
*/
ordinal : function() {// 순서
var that = this;
var _domain = [];
var _range = [];
var _rangeBand = 0;
var _cache = {};
function func(t) {
var key = "" + t;
if (typeof _cache[key] != 'undefined') {
return _cache[key];
}
var index = -1;
for (var i = 0; i < _domain.length; i++) {
if (typeof t == 'string' && _domain[i] === t) {
index = i;
break;
}
}
if (index > -1) {
_cache[key] = _range[index];
return _range[index];
} else {
if ( typeof _range[t] != 'undefined') {
_domain[t] = t;
_cache[key] = _range[t];
return _range[t];
}
return null;
}
}
func.domain = function(values) {
if ( typeof values == 'undefined') {
return _domain;
}
for (var i = 0; i < values.length; i++) {
_domain[i] = values[i];
}
return this;
}
func.range = function(values) {
if ( typeof values == 'undefined') {
return _range;
}
for (var i = 0; i < values.length; i++) {
_range[i] = values[i];
}
return this;
}
func.rangePoints = function(interval, padding) {
padding = padding || 0;
var step = _domain.length;
var unit = (interval[1] - interval[0] - padding) / step;
var range = [];
for (var i = 0; i < _domain.length; i++) {
if (i == 0) {
range[i] = interval[0] + padding / 2 + unit / 2;
} else {
range[i] = range[i - 1] + unit;
}
}
_range = range;
_rangeBand = unit;
return func;
}
func.rangeBands = function(interval, padding, outerPadding) {
padding = padding || 0;
outerPadding = outerPadding || 0;
var count = _domain.length;
var step = count - 1;
var band = (interval[1] - interval[0]) / step;
var range = [];
for (var i = 0; i < _domain.length; i++) {
if (i == 0) {
range[i] = interval[0];
} else {
range[i] = band + range[i - 1];
}
}
_rangeBand = band;
_range = range;
return func;
}
func.rangeBand = function() {
return _rangeBand;
}
func.invert = function(x) {
return Math.ceil(x / _rangeBand);
}
return func;
},
/**
* 시간에 대한 scale
*
*/
time : function() {// 시간
var that = this;
var _domain = [];
var _range = [];
var _rangeBand;
var func = self.linear();
var df = func.domain;
func.domain = function(domain) {
if (!arguments.length)
return df.call(func);
for (var i = 0; i < domain.length; i++) {
_domain[i] = +domain[i];
}
return df.call(func, _domain);
}
func.min = function() {
return Math.min(_domain[0], _domain[_domain.length - 1]);
}
func.max = function() {
return Math.max(_domain[0], _domain[_domain.length - 1]);
}
func.rate = function(value, max) {
return func(func.max() * (value / max));
}
func.ticks = function(type, step) {
var start = _domain[0];
var end = _domain[1];
var times = [];
while (start < end) {
times.push(new Date(+start));
start = _time.add(start, type, step);
}
times.push(new Date(+start));
var first = func(times[0]);
var second = func(times[1]);
_rangeBand = second - first;
return times;
}
func.realTicks = function(type, step) {
var start = _domain[0];
var end = _domain[1];
var times = [];
var date = new Date(+start)
var realStart = null;
if (type == _time.years) {
realStart = new Date(date.getFullYear(), 0, 1);
} else if (type == _time.months) {
realStart = new Date(date.getFullYear(), date.getMonth(), 1);
} else if (type == _time.days || type == _time.weeks) {
realStart = new Date(date.getFullYear(), date.getMonth(), date.getDate());
} else if (type == _time.hours) {
realStart = new Date(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), 0, 0, 0);
} else if (type == _time.minutes) {
realStart = new Date(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), 0, 0);
} else if (type == _time.seconds) {
realStart = new Date(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds(), 0);
} else if (type == _time.milliseconds) {
realStart = new Date(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds());
}
realStart = _time.add(realStart, type, step);
while (+realStart < +end) {
times.push(new Date(+realStart));
realStart = _time.add(realStart, type, step);
}
var first = func(times[1]);
var second = func(times[2]);
_rangeBand = second - first;
return times;
}
func.rangeBand = function() {
return _rangeBand;
}
func.invert = function(y) {
var f = self.linear().domain(func.range()).range(func.domain());
return new Date(f(y));
}
return func;
},
/**
* log scale
*
* var log = _.scale.log(10).domain([0, 1000000]).range([0, 300]);
*
* log(0) == 0
* log.ticks(4) == [0, 100, 10000, 1000000]
*
* @param base
*/
log : function(base) {
var that = this;
var _base = base || 10;
var func = self.linear();
var _domain = [];
var _domainMax = null;
var _domainMin = null;
function log(value) {
if (value < 0) {
return -(Math.log(Math.abs(value)) / Math.log(_base));
} else if (value > 0) {
return Math.log(value) / Math.log(_base);
}
return 0;
}
function pow(value) {
if (value < 0) {
return - Math.pow(_base, Math.abs(value));
} else if (value > 0) {
return Math.pow(_base, value);
}
return 0;
}
function checkMax(value) {
return Math.pow(_base, (value+"").length-1) < value;
}
function getNextMax(value) {
return Math.pow(_base, (value+"").length);
}
var newFunc = function(x) {
var value = x;
if (x > _domainMax) {
value = _domainMax;
} else if (x < _domainMin) {
value = _domainMin;
}
return func(log(value));
}
$.extend(newFunc, func);
newFunc.log = function() {
var newDomain = [];
for (var i = 0; i < _domain.length; i++) {
newDomain[i] = log(_domain[i]);
}
return newDomain;
}
newFunc.domain = function(values) {
if (!arguments.length) {
return _domain;
}
for (var i = 0; i < values.length; i++) {
_domain[i] = values[i];
}
_domainMax = Math.max.apply(Math, _domain);
_domainMin = Math.min.apply(Math, _domain);
if (checkMax(_domainMax)) {
_domain[1] = _domainMax = getNextMax(_domainMax);
}
if (checkMax(Math.abs(_domainMin))) {
var value = getNextMax(Math.abs(_domainMin));
_domain[0] = _domainMin = _domainMin < 0 ? -value : value ;
}
func.domain(newFunc.log());
return newFunc;
}
newFunc.base = function(base) {
func.domain(newFunc.log());
return newFunc;
}
newFunc.invert = function(y) {
return pow(func.invert(y));
}
newFunc.ticks = function(count, isNice, intNumber) {
var arr = func.ticks(count, isNice, intNumber || 100000000000000000000, true);
if (arr[arr.length-1] < func.max()) {
arr.push(func.max());
}
var newArr = [];
for(var i = 0, len = arr.length; i < len; i++) {
newArr[i] = pow(arr[i]);
}
return newArr;
}
return newFunc;
},
/**
* 범위에 대한 scale
*
*/
linear : function() {// 선형
var that = this;
var _domain = [0, 1];
var _range = [0, 1];
var _isRound = false;
var _isClamp = false;
var _cache = {};
var roundFunction = null;
var numberFunction = null;
var domainMin = null;
var domainMax = null;
var rangeMin = null;
var rangeMax = null;
var distDomain = null;
var distRange = null;
var callFunction = null;
var _rangeBand = null;
function func(x) {
if (domainMax < x) {
if (_isClamp) {
return func(domainMax);
}
return _range[0] + Math.abs(x - _domain[1]) * distDomain / distRange;
} else if (domainMin > x) {
if (_isClamp) {
return func(domainMin);
}
return _range[0] - Math.abs(x - _domain[0]) * distDomain / distRange;
} else {
var pos = (x - _domain[0]) / (distDomain);
return callFunction(pos);
}
}
func.cache = function() {
return _cache;
}
func.min = function() {
return Math.min.apply(Math, _domain);
}
func.max = function() {
return Math.max.apply(Math, _domain);
}
func.rangeMin = function() {
return Math.min.apply(Math, _range);
}
func.rangeMax = function() {
return Math.max.apply(Math, _range);
}
func.rate = function(value, max) {
return func(func.max() * (value / max));
}
func.clamp = function(isClamp) {
_isClamp = isClamp || false;
}
func.domain = function(values) {
if (!arguments.length) {
return _domain;
}
for (var i = 0; i < values.length; i++) {
_domain[i] = values[i];
}
domainMin = func.min();
domainMax = func.max();
distDomain = _domain[1] - _domain[0];
return this;
}
func.range = function(values) {
if (!arguments.length) {
return _range;
}
for (var i = 0; i < values.length; i++) {
_range[i] = values[i];
}
roundFunction = math.interpolateRound(_range[0], _range[1]);
numberFunction = math.interpolateNumber(_range[0], _range[1]);
rangeMin = func.rangeMin();
rangeMax = func.rangeMax();
distRange = Math.abs(rangeMax - rangeMin);
callFunction = _isRound ? roundFunction : numberFunction;
return this;
}
func.rangeRound = function(values) {
_isRound = true;
return func.range(values);
}
func.rangeBand = function() {
return _rangeBand;
}
func.invert = function(y) {
var f = self.linear().domain(_range).range(_domain);
return f(y);
}
func.ticks = function(count, isNice, intNumber, reverse) {
intNumber = intNumber || 10000;
reverse = reverse || false;
var max = func.max();
if (_domain[0] == 0 && _domain[1] == 0) {
return [];
}
var obj = math.nice(_domain[0], _domain[1], count || 10, isNice || false);
var arr = [];
var start = (reverse ? obj.max : obj.min) * intNumber;
var end = (reverse ? obj.min : obj.max) * intNumber;
while ((reverse ? end <= start : start <= end)) {
arr.push(start / intNumber);
var unit = obj.spacing * intNumber;
if (reverse) {
start -= unit;
} else {
start += unit;
}
}
if (reverse) {
if (arr[0] != max) {
arr.unshift(max);
}
for(var i = 0, len = arr.length; i < len; i++) {
arr[i] = Math.abs(arr[i] - max);
}
//arr.reverse();
} else {
if (arr[arr.length - 1] * intNumber != end && start > end) {
arr.push(end / intNumber);
}
if (_domain[0] > _domain[1]) {
arr.reverse();
}
}
var first = func(arr[0]);
var second = func(arr[1]);
_rangeBand = Math.abs(second - first);
return arr;
}
return func;
}
}
return self;
});
jui.define("util.color", [], function() {
/**
* @class util.color
* color parser for chart
* @singleton
*/
var self = {
regex : /(linear|radial)\((.*)\)(.*)/i,
trim : function (str) {
return (str || "").replace(/^\s+|\s+$/g, '');
},
/**
* @method lighten
*
* rgb 컬러 밝은 농도로 변환
*
* @param {String} color RGB color code
* @param {Number} rate 밝은 농도
* @return {String}
*/
lighten : function(color, rate) {
color = color.replace(/[^0-9a-f]/gi, '');
rate = rate || 0;
var rgb = [], c, i;
for (i = 0; i < 6; i += 2) {
c = parseInt(color.substr(i,2), 16);
c = Math.round(Math.min(Math.max(0, c + (c * rate)), 255)).toString(16);
rgb.push(("00"+c).substr(c.length));
}
return "#" + rgb.join("");
},
/**
* @method darken
*
* rgb 컬러 어두운 농도로 변환
*
* @param {String} color RGB color code
* @param {Number} rate 어두운 농도
* @return {String}
*/
darken : function(color, rate) {
return this.lighten(color, -rate)
},
/**
* @method parse
*
* color 파싱
*
* @param color
* @returns {*}
*/
parse : function(color) {
return this.parseGradient(color);
},
/**
* @method parseGrident
*
* gradient parser
*
* @example
* linear(left) #fff,#000
* linear(right) #fff,50 yellow,black
* radial(50%,50%,50%,50,50)
*
* @param {String} color
*/
parseGradient : function(color) {
var matches = color.match(this.regex);
if (!matches) return color;
var type = this.trim(matches[1]);
var attr = this.parseAttr(type, this.trim(matches[2]));
var stops = this.parseStop(this.trim(matches[3]));
var obj = { type : type + "Gradient", attr : attr, children : stops };
return obj;
},
parseStop : function(stop) {
var stop_list = stop.split(",");
var stops = [];
for(var i = 0, len = stop_list.length; i < len; i++) {
var stop = stop_list[i];
var arr = stop.split(" ");
if (arr.length == 0) continue;
if (arr.length == 1) {
stops.push({ type : "stop", attr : {"stop-color" : arr[0] } })
} else if (arr.length == 2) {
stops.push({ type : "stop", attr : {"offset" : arr[0], "stop-color" : arr[1] } })
} else if (arr.length == 3) {
stops.push({ type : "stop", attr : {"offset" : arr[0], "stop-color" : arr[1], "stop-opacity" : arr[2] } })
}
}
var start = -1;
var end = -1;
for(var i = 0, len = stops.length; i < len; i++) {
var stop = stops[i];
if (i == 0) {
if (!stop.offset) stop.offset = 0;
} else if (i == len - 1) {
if (!stop.offset) stop.offset = 1;
}
if (start == -1 && typeof stop.offset == 'undefined') {
start = i;
} else if (end == -1 && typeof stop.offset == 'undefined') {
end = i;
var count = end - start;
var endOffset = stops[end].offset.indexOf("%") > -1 ? parseFloat(stops[end].offset)/100 : stops[end].offset;
var startOffset = stops[start].offset.indexOf("%") > -1 ? parseFloat(stops[start].offset)/100 : stops[start].offset;
var dist = endOffset - startOffset
var value = dist/ count;
var offset = startOffset + value;
for(var index = start + 1; index < end; index++) {
stops[index].offset = offset;
offset += value;
}
start = end;
end = -1;
}
}
return stops;
},
parseAttr : function(type, str) {
if (type == 'linear') {
switch(str) {
case "":
case "left": return { x1 : 0, y1 : 0, x2 : 1, y2 : 0, direction : str || "left" };
case "right": return { x1 : 1, y1 : 0, x2 : 0, y2 : 0, direction : str };
case "top": return { x1 : 0, y1 : 0, x2 : 0, y2 : 1, direction : str };
case "bottom": return { x1 : 0, y1 : 1, x2 : 0, y2 : 0, direction : str };
case "top left": return { x1 : 0, y1 : 0, x2 : 1, y2 : 1, direction : str };
case "top right": return { x1 : 1, y1 : 0, x2 : 0, y2 : 1, direction : str };
case "bottom left": return { x1 : 0, y1 : 1, x2 : 1, y2 : 0, direction : str };
case "bottom right": return { x1 : 1, y1 : 1, x2 : 0, y2 : 0, direction : str };
default :
var arr = str.split(",");
for(var i = 0, len = arr.length; i < len; i++) {
if (arr[i].indexOf("%") == -1)
arr[i] = parseFloat(arr[i]);
}
return { x1 : arr[0], y1 : arr[1],x2 : arr[2], y2 : arr[3] };
}
} else {
var arr = str.split(",");
for(var i = 0, len = arr.length; i < len; i++) {
if (arr[i].indexOf("%") == -1)
arr[i] = parseFloat(arr[i]);
}
return { cx : arr[0], cy : arr[1],r : arr[2], fx : arr[3], fy : arr[4] };
}
}
}
return self;
});
jui.define("util.svg.element", [], function() {
/**
* @class util.svg.element
* Create SVG Element
* @constructor
*/
var Element = function() {
var events = [];
/**
* 엘리먼트 생성 및 조회 메소드
*
*/
this.create = function(type, attr) {
// 퍼블릭 프로퍼티
this.element = document.createElementNS("http://www.w3.org/2000/svg", type);
this.children = [];
this.parent = null;
this.styles = {};
this.attributes = {};
// 기본 속성 설정
this.attr(attr);
};
this.each = function(callback) {
if(typeof(callback) != "function") return;
for(var i = 0, len = this.children.length; i < len; i++) {
var self = this.children[i];
callback.apply(self, [ i, self ]);
}
return this.children;
};
this.get = function(index) {
if(this.children[index]) {
return this.children[index];
}
return null;
}
this.index = function(obj) {
for(var i = 0; i < this.children.length; i++) {
if(obj == this.children[i]) {
return i;
}
}
return -1;
}
/**
* 엘리먼트 관계 메소드
*
*/
this.append = function(elem) {
if(elem instanceof Element) {
if (elem.parent) {
elem.remove();
}
this.children.push(elem);
elem.parent = this;
}
return this;
}
this.prepend = function(elem) {
return this.insert(0, elem);
}
this.insert = function(index, elem) {
if(elem.parent) {
elem.remove();
}
this.children.splice(index, 0, elem);
elem.parent = this;
return this;
}
this.remove = function() {
var index = 0,
nChild = [],
pChild = this.parent.children;
for(var i = 0; i < pChild.length; i++) {
if (pChild[i] == this) {
index = i;
break;
}
nChild.push(pChild[i]);
}
this.parent.children = nChild;
return this;
}
/**
* 엘리먼트 DOM 조작 메소드
*
*/
this.attr = function(attr) {
if(typeof attr == "undefined" || !attr) return;
if(typeof attr == "string") {
return this.attributes[attr] || this.element.getAttribute(attr);
}
for(var k in attr) {
this.attributes[k] = attr[k];
if(k.indexOf("xlink:") != -1) {
this.element.setAttributeNS("http://www.w3.org/1999/xlink", k, attr[k]);
} else {
this.element.setAttribute(k, attr[k]);
}
}
return this;
}
this.css = function(css) {
var list = [];
for(var k in css) {
this.styles[k] = css[k];
}
for(var k in css) {
list.push(k + ":" + css[k]);
}
this.attr({ style: list.join(";") });
return this;
}
this.html = function(html) {
this.element.innerHTML = html;
return this;
}
this.text = function(text) {
this.element.innerHTML = "";
this.element.appendChild(document.createTextNode(text));
return this;
}
/**
* 엘리먼트 DOM 이벤트 메소드
*
*/
this.on = function(type, handler) {
var callback = function(e) {
if(typeof(handler) == "function") {
handler.call(this, e);
}
}
this.element.addEventListener(type, callback, false);
events.push({ type: type, callback: callback });
return this;
}
this.off = function(type) {
var newEvents = [];
for(var i = 0, len = events.length; i < len; i++) {
var event = events[i];
if(event.type != type) {
newEvents.push(event);
} else {
this.element.removeEventListener(type, event.callback, false);
}
}
events = newEvents;
return this;
}
this.hover = function(overHandler, outHandler) {
var callback1 = function(e) {
if(typeof(overHandler) == "function") {
overHandler.call(this, e);
}
}
var callback2 = function(e) {
if(typeof(outHandler) == "function") {
outHandler.call(this, e);
}
}
this.element.addEventListener("mouseover", callback1, false);
this.element.addEventListener("mouseout", callback2, false);
events.push({ type: "mouseover", callback: callback1 });
events.push({ type: "mouseout", callback: callback2 });
return this;
}
/**
* 그 외 메소드
*
*/
this.size = function() {
var size = { width: 0, height: 0 },
rect = this.element.getBoundingClientRect();
if(!rect || (rect.width == 0 && rect.height == 0)) {
var height_list = [ "height", "paddingTop", "paddingBottom", "borderTopWidth", "borderBottomWidth" ],
width_list = [ "width", "paddingLeft", "paddingRight", "borderLeftWidth", "borderRightWidth" ];
var computedStyle = window.getComputedStyle(this.element);
for (var i = 0; i < height_list.length; i++) {
size.height += parseFloat(computedStyle[height_list[i]]);
}
for (var i = 0; i < width_list.length; i++) {
size.width += parseFloat(computedStyle[width_list[i]]);
}
size.width = size.width || this.element.getAttribute('width');
size.height = size.height || this.element.getAttribute('height');
} else {
size.width = rect.width;
size.height = rect.height;
}
if(isNaN(size.width)) size.width = 0;
if(isNaN(size.height)) size.height = 0;
return size;
}
this.is = function(moduleId) {
return this instanceof jui.include(moduleId);
}
}
return Element;
});
jui.define("util.svg.element.transform", [ "util.base" ], function(_) { // polygon, polyline
/**
* @class util.svg.element.transform
*
* implement Transform Element
*
* @extends util.svg.element
* @constructor
*/
var TransElement = function() {
var orders = {
translate: null,
scale: null,
rotate: null,
skew: null,
matrix: null
};
function applyOrders(self) {
var orderArr = [];
for(var key in orders) {
if(orders[key]) orderArr.push(orders[key]);
}
self.attr({ transform: orderArr.join(" ") });
}
function getStringArgs(args) {
var result = [];
for(var i = 0; i < args.length; i++) {
result.push(args[i]);
}
return result.join(",");
}
this.translate = function() {
orders["translate"] = "translate(" + getStringArgs(arguments) + ")";
applyOrders(this);
return this;
}
this.rotate = function(angle, x, y) {
if(arguments.length == 1) {
var str = angle;
} else if(arguments.length == 3) {
var str = angle + " " + x + "," + y;
}
orders["rotate"] = "rotate(" + str + ")";
applyOrders(this);
return this;
}
this.scale = function() {
orders["scale"] = "scale(" + getStringArgs(arguments) + ")";
applyOrders(this);
return this;
}
this.skew = function() {
orders["skew"] = "skew(" + getStringArgs(arguments) + ")";
applyOrders(this);
return this;
}
this.matrix = function() {
orders["matrix"] = "matrix(" + getStringArgs(arguments) + ")";
applyOrders(this);
return this;
}
this.data = function(type) {
var text = this.attr("transform"),
regex = {
translate: /[^translate()]+/g,
rotate: /[^rotate()]+/g,
scale: /[^scale()]+/g,
skew: /[^skew()]+/g,
matrix: /[^matrix()]+/g
};
if(_.typeCheck("string", text)) {
return text.match(regex[type])[0];
}
return null;
}
}
return TransElement;
}, "util.svg.element");
jui.define("util.svg.element.path", [ "util.base" ], function(_) { // path
var PathElement = function() {
var orders = [];
this.moveTo = function(x, y, type) {
orders.push( (type || "m") + x + "," + y );
return this;
}
this.MoveTo = function(x, y) {
return this.moveTo(x, y, "M");
}
this.lineTo = function(x, y, type) {
orders.push( (type || "l") + x + "," + y );
return this;
}
this.LineTo = function(x, y) {
return this.lineTo(x, y, "L");
}
this.hLineTo = function(x, type) {
orders.push( (type || "h") + x );
return this;
}
this.HLineTo = function(x) {
return this.hLineTo(x, "H");
}
this.vLineTo = function(y, type) {
orders.push( (type || "v") + y );
return this;
}
this.VLineTo = function(y) {
return this.vLineTo(y, "V");
}
this.curveTo = function(x1, y1, x2, y2, x, y, type) {
orders.push( (type || "c") + x1 + "," + y1 + " " + x2 + "," + y2 + " " + x + "," + y );
return this;
}
this.CurveTo = function(x1, y1, x2, y2, x, y) {
return this.curveTo(x1, y1, x2, y2, x, y, "C");
}
this.sCurveTo = function(x2, y2, x, y, type) {
orders.push( (type || "s") + x2 + "," + y2 + " " + x + "," + y );
return this;
}
this.SCurveTo = function(x2, y2, x, y) {
return this.sCurveTo(x2, y2, x, y, "S");
}
this.qCurveTo = function(x1, y1, x, y, type) {
orders.push( (type || "q") + x1 + "," + y1 + " " + x + "," + y );
return this;
}
this.QCurveTo = function(x1, y1, x, y) {
return this.qCurveTo(x1, y1, x, y, "Q");
}
this.tCurveTo = function(x1, y1, x, y, type) {
orders.push( (type || "t") + x1 + "," + y1 + " " + x + "," + y );
return this;
}
this.TCurveTo = function(x1, y1, x, y) {
return this.tCurveTo(x1, y1, x, y, "T");
}
this.arc = function(rx, ry, x_axis_rotation, large_arc_flag, sweep_flag, x, y, type) {
large_arc_flag = (large_arc_flag) ? 1 : 0;
sweep_flag = (sweep_flag) ? 1 : 0;
orders.push( (type || "a") + rx + "," + ry + " " + x_axis_rotation + " " + large_arc_flag + "," + sweep_flag + " " + x + "," + y );
return this;
}
this.Arc = function(rx, ry, x_axis_rotation, large_arc_flag, sweep_flag, x, y) {
return this.arc(rx, ry, x_axis_rotation, large_arc_flag, sweep_flag, x, y, "A");
}
this.closePath = function(type) {
orders.push( (type || "z") );
return this;
}
this.ClosePath = function() {
return this.closePath("Z");
}
this.join = function() {
if(orders.length > 0) {
this.attr({ d: orders.join(" ") });
orders = [];
}
}
this.length = function() {
var id = _.createId(),
d = orders.join(" ");
var svg = document.createElement("svg"),
path = document.createElementNS("http://www.w3.org/2000/svg", "path");
path.setAttributeNS(null, "id", id);
path.setAttributeNS(null, "d", d);
svg.appendChild(path);
document.body.appendChild(svg);
var length = document.getElementById(id).getTotalLength();
document.body.removeChild(svg);
return length;
}
}
return PathElement;
}, "util.svg.element.transform");
jui.define("util.svg.element.path.symbol", [ "util.base" ], function(_) { // symbol
var PathSymbolElement = function() {
var ordersString = "";
/**
* 심볼 템플릿
*
*/
this.template = function(width, height) {
var r = width,
half_width = half_r = width / 2,
half_height = height / 2;
var start = "a" + half_r + "," + half_r + " 0 1,1 " + r + ",0",
end = "a" + half_r + "," + half_r + " 0 1,1 " + -r + ",0";
var obj = {
triangle : ["m0," + -half_height, "l" + (half_width) + "," + height, "l" + (-width) + ",0", "l" + (half_width) + "," + (-height)].join(" "),
rect : ["m" + (-half_width) + "," + (-half_height), "l" + (width) + ",0", "l0," + (height) , "l" + (-width) + ',0', "l0," + (-height)].join(" "),
cross : ["m" + (-half_width) + ',' + (-half_height), "l" + (width) + "," + (height), "m0," + (-height), "l" + (-width) + "," + (height)].join(" "),
circle : ["m" + (-r) + ",0", start, end ].join(" ")
}
obj.rectangle = obj.rect;
return obj;
}
this.join = function() {
if(ordersString.length > 0) {
this.attr({ d: ordersString });
ordersString = "";
}
}
/**
* 심볼 추가 하기 (튜닝)
*/
this.add = function(cx, cy, tpl) {
ordersString += " M" + (cx) + "," + (cy) + tpl;
}
/**
* path 내 심볼 생성
*
*/
this.triangle = function(cx, cy, width, height) {
return this.MoveTo(cx, cy).moveTo(0, -height/2).lineTo(width/2,height).lineTo(-width, 0).lineTo(width/2, -height);
}
this.rect = this.rectangle = function(cx, cy, width, height) {
return this.MoveTo(cx, cy).moveTo(-width/2, -height/2).lineTo(width,0).lineTo(0, height).lineTo(-width, 0).lineTo(0, -height);
}
this.cross = function(cx, cy, width, height) {
return this.MoveTo(cx, cy).moveTo(-width/2, -height/2).lineTo(width, height).moveTo(0, -height).lineTo(-width, height);
}
this.circle = function(cx, cy, r) {
return this.MoveTo(cx, cy).moveTo(-r, 0).arc(r/2, r/2, 0, 1, 1, r, 0).arc(r/2, r/2, 0, 1, 1, -r, 0);
}
}
return PathSymbolElement;
}, "util.svg.element.path");
jui.define("util.svg.element.path.rect", [ "util.math" ], function(math) {
var PathRectElement = function() {
this.round = function(width, height, tl, tr, br, bl) {
tl = (!tl) ? 0 : tl;
tr = (!tr) ? 0 : tr;
br = (!br) ? 0 : br;
bl = (!bl) ? 0 : bl;
this.MoveTo(0, tl)
.Arc(tl, tl, 0, 0, 1, tl, 0)
.HLineTo(width - tr)
.Arc(tr, tr, 0, 0, 1, width, tr)
.VLineTo(height - br)
.Arc(br, br, 0, 0, 1, width - br, height)
.HLineTo(bl)
.Arc(bl, bl, 0, 0, 1, 0, height - bl)
.ClosePath()
.join();
}
}
return PathRectElement;
}, "util.svg.element.path");
jui.define("util.svg.element.poly", [], function() { // polygon, polyline
var PolyElement = function() {
var orders = [];
this.point = function(x, y) {
orders.push(x + "," + y);
return this;
}
this.join = function() {
if(orders.length > 0) {
// Firefox 처리
var start = orders[0];
orders.push(start);
// 폴리곤 그리기
this.attr({ points: orders.join(" ") });
orders = [];
}
}
}
return PolyElement;
}, "util.svg.element.transform");
jui.define("util.svg.base",
[ "util.base", "util.math", "util.color", "util.svg.element", "util.svg.element.transform",
"util.svg.element.path", "util.svg.element.path.symbol", "util.svg.element.path.rect", "util.svg.element.poly" ],
function(_, math, color, Element, TransElement, PathElement, PathSymbolElement, PathRectElement, PolyElement) {
var globalObj = null;
var SVGBase = function() {
this.create = function(obj, type, attr, callback) {
obj.create(type, attr);
return obj;
}
this.createChild = function(obj, type, attr, callback) {
return this.create(obj, type, attr, callback);
}
/**
* @method custom
*
* return custom element
*
* @param {String} name
* @param {Object} attr
* @param {Function} callback
* @return {util.svg.element}
*/
this.custom = function(name, attr, callback) {
return this.create(new Element(), name, attr, callback);
}
/**
* @method defs
*
* return defs element
*
* @param {Function} callback
* @return {util.svg.element}
*/
this.defs = function(callback) {
return this.create(new Element(), "defs", null, callback);
}
/**
* @method symbol
*
* return symbol element
*
* @param {Object} attr
* @param {Function} callback
* @return {util.svg.element}
*/
this.symbol = function(attr, callback) {
return this.create(new Element(), "symbol", attr, callback);
}
/**
* @method g
*
* return defs element
*
* @alias group
* @param {Object} attr
* @param {Function} callback
* @return {util.svg.element.transform}
*/
this.g = this.group = function(attr, callback) {
return this.create(new TransElement(), "g", attr, callback);
}
/**
* @method marker
*
* return marker element
*
* @param {Object} attr
* @param {Function} callback
* @return {util.svg.element}
*/
this.marker = function(attr, callback) {
return this.create(new Element(), "marker", attr, callback);
}
/**
* @method a
*
* return a element
*
* @param {Object} attr
* @param {Function} callback
* @return {util.svg.element.transform}
*/
this.a = function(attr, callback) {
return this.create(new TransElement(), "a", attr, callback);
}
/**
* @method switch
*
* return switch element
*
* @param {Object} attr
* @param {Function} callback
* @return {util.svg.element}
*/
this.switch = function(attr, callback) {
return this.create(new Element(), "switch", attr, callback);
}
/**
* @method use
*
* return use element
*
* @param {Object} attr
* @return {util.svg.element}
*/
this.use = function(attr) {
return this.create(new Element(), "use", attr);
}
/**
* @method rect
*
* return rect element
*
* @param {Object} attr
* @param {Function} callback
* @return {util.svg.element.transform}
*/
this.rect = function(attr, callback) {
return this.create(new TransElement(), "rect", attr, callback);
}
/**
* @method line
*
* return line element
*
* @param {Object} attr
* @param {Function} callback
* @return {util.svg.element.transform}
*/
this.line = function(attr, callback) {
return this.create(new TransElement(), "line", attr, callback);
}
this.circle = function(attr, callback) {
return this.create(new TransElement(), "circle", attr, callback);
}
this.text = function(attr, textOrCallback) {
if(arguments.length == 2) {
if (_.typeCheck("function", textOrCallback)) {
return this.create(new TransElement(), "text", attr, textOrCallback);
}
return this.create(new TransElement(), "text", attr).text(textOrCallback);
}
return this.create(new TransElement(), "text", attr);
}
this.textPath = function(attr, text) {
if(_.typeCheck("string", text)) {
return this.create(new Element(), "textPath", attr).text(text);
}
return this.create(new Element(), "textPath", attr);
}
this.tref = function(attr, text) {
if(_.typeCheck("string", text)) {
return this.create(new Element(), "tref", attr).text(text);
}
return this.create(new Element(), "tref", attr);
}
this.tspan = function(attr, text) {
if(_.typeCheck("string", text)) {
return this.create(new Element(), "tspan", attr).text(text);
}
return this.create(new Element(), "tspan", attr);
}
this.ellipse = function(attr, callback) {
return this.create(new TransElement(), "ellipse", attr, callback);
}
this.image = function(attr, callback) {
return this.create(new TransElement(), "image", attr, callback);
}
this.path = function(attr, callback) {
return this.create(new PathElement(), "path", attr, callback);
}
this.pathSymbol = function(attr, callback) {
return this.create(new PathSymbolElement(), "path", attr, callback);
}
this.pathRect = function(attr, callback) {
return this.create(new PathRectElement(), "path", attr, callback);
}
this.polyline = function(attr, callback) {
return this.create(new PolyElement(), "polyline", attr, callback);
}
this.polygon = function(attr, callback) {
return this.create(new PolyElement(), "polygon", attr, callback);
}
this.pattern = function(attr, callback) {
return this.create(new Element(), "pattern", attr, callback);
}
this.mask = function(attr, callback) {
return this.create(new Element(), "mask", attr, callback);
}
this.clipPath = function(attr, callback) {
return this.create(new Element(), "clipPath", attr, callback);
}
this.linearGradient = function(attr, callback) {
return this.create(new Element(), "linearGradient", attr, callback);
}
this.radialGradient = function(attr, callback) {
return this.create(new Element(), "radialGradient", attr, callback);
}
this.filter = function(attr, callback) {
return this.create(new Element(), "filter", attr, callback);
}
/**
* 엘리먼트 관련 메소드 (그라데이션)
*
*/
this.stop = function(attr) {
return this.createChild(new Element(), "stop", attr);
}
/**
* 엘리먼트 관련 메소드 (애니메이션)
*
*/
this.animate = function(attr) {
return this.createChild(new Element(), "animate", attr);
}
this.animateColor = function(attr) {
return this.createChild(new Element(), "animateColor", attr);
}
this.animateMotion = function(attr) {
return this.createChild(new Element(), "animateMotion", attr);
}
this.animateTransform = function(attr) {
return this.createChild(new Element(), "animateTransform", attr);
}
this.mpath = function(attr) {
return this.createChild(new Element(), "mpath", attr);
}
this.set = function(attr) {
return this.createChild(new Element(), "set", attr);
}
/**
* 엘리먼트 관련 메소드 (필터)
*
*/
this.feBlend = function(attr) {
return this.createChild(new Element(), "feBlend", attr);
}
this.feColorMatrix = function(attr) {
return this.createChild(new Element(), "feColorMatrix", attr);
}
this.feComponentTransfer = function(attr) {
return this.createChild(new Element(), "feComponentTransfer", attr);
}
this.feComposite = function(attr) {
return this.createChild(new Element(), "feComposite", attr);
}
this.feConvolveMatrix = function(attr) {
return this.createChild(new Element(), "feConvolveMatrix", attr);
}
this.feDiffuseLighting = function(attr) {
return this.createChild(new Element(), "feDiffuseLighting", attr);
}
this.feDisplacementMap = function(attr) {
return this.createChild(new Element(), "feDisplacementMap", attr);
}
this.feFlood = function(attr) {
return this.createChild(new Element(), "feFlood", attr);
}
this.feGaussianBlur = function(attr) {
return this.createChild(new Element(), "feGaussianBlur", attr);
}
this.feImage = function(attr) {
return this.createChild(new Element(), "feImage", attr);
}
this.feMerge = function(attr, callback) {
return this.createChild(new Element(), "feMerge", attr, callback);
}
this.feMergeNode = function(attr) {
return this.createChild(new Element(), "feMergeNode", attr);
}
this.feMorphology = function(attr) {
return this.createChild(new Element(), "feMorphology", attr);
}
this.feOffset = function(attr) {
return this.createChild(new Element(), "feOffset", attr);
}
this.feSpecularLighting = function(attr) {
return this.createChild(new Element(), "feSpecularLighting", attr);
}
this.feTile = function(attr) {
return this.createChild(new Element(), "feTile", attr);
}
this.feTurbulence = function(attr) {
return this.createChild(new Element(), "feTurbulence", attr);
}
}
SVGBase.create = function(name, attr, callback) {
if(globalObj == null) {
globalObj = new SVGBase();
}
return globalObj.custom(name, attr, callback);
}
return SVGBase;
});
jui.define("util.svg.3d", [ "util.base", "util.math", "util.color" ], function(_, math, color) {
var SVG3d = function() {
// 3D 사각형 그리기
this.rect3d = function(fill, width, height, degree, depth) {
var self = this;
var radian = math.radian(degree),
x1 = 0,
y1 = 0,
w1 = width,
h1 = height;
var x2 = Math.cos(radian) * depth,
y2 = Math.sin(radian) * depth,
w2 = width + x2,
h2 = height + y2;
var g = self.group({}, function() {
self.path({
fill: color.lighten(fill, 0.15),
stroke: color.lighten(fill, 0.15)
}).MoveTo(x2, x1)
.LineTo(w2, y1)
.LineTo(w1, y2)
.LineTo(x1, y2);
self.path({
fill: fill,
stroke: fill
}).MoveTo(x1, y2)
.LineTo(x1, h2)
.LineTo(w1, h2)
.LineTo(w1, y2);
self.path({
fill: color.darken(fill, 0.2),
stroke: color.darken(fill, 0.2)
}).MoveTo(w1, h2)
.LineTo(w2, h1)
.LineTo(w2, y1)
.LineTo(w1, y2);
});
return g;
}
// 3D 타원 그리기
this.cylinder3d = function(fill, width, height, degree, depth, rate) {
var self = this;
var radian = math.radian(degree),
rate = (rate == undefined) ? 1 : (rate == 0) ? 0.01 : rate,
r = width / 2,
tr = r * rate,
l = (Math.cos(radian) * depth) / 2,
d = (Math.sin(radian) * depth) / 2,
key = _.createId("cylinder3d");
var g = self.group({}, function() {
self.ellipse({
fill: color.darken(fill, 0.05),
"fill-opacity": 0.85,
stroke: color.darken(fill, 0.05),
rx: r,
ry: d,
cx: r,
cy: height
}).translate(l, d);
self.path({
fill: "url(#" + key + ")",
"fill-opacity": 0.85,
stroke: fill
}).MoveTo(r - tr, d)
.LineTo(0, height)
.Arc(r, d, 0, 0, 0, width, height)
.LineTo(r + tr, d)
.Arc(r + tr, d, 0, 0, 1, r - tr, d)
.translate(l, d);
self.ellipse({
fill: color.lighten(fill, 0.2),
"fill-opacity": 0.95,
stroke: color.lighten(fill, 0.2),
rx: r * rate,
ry: d * rate,
cx: r,
cy: d
}).translate(l, d);
self.linearGradient({
id: key,
x1: "100%",
x2: "0%",
y1: "0%",
y2: "0%"
}, function() {
self.stop({
offset: "0%",
"stop-color": color.lighten(fill, 0.15)
});
self.stop({
offset: "33.333333333333336%",
"stop-color": color.darken(fill, 0.2)
});
self.stop({
offset: "66.66666666666667%",
"stop-color": color.darken(fill, 0.2)
});
self.stop({
offset: "100%",
"stop-color": color.lighten(fill, 0.15)
});
});
});
return g;
}
}
return SVG3d;
}, "util.svg.base");
jui.define("util.svg",
[ "util.base", "util.math", "util.color", "util.svg.element", "util.svg.element.transform",
"util.svg.element.path", "util.svg.element.path.symbol", "util.svg.element.path.rect", "util.svg.element.poly" ],
function(_, math, color, Element, TransElement, PathElement, PathSymbolElement, PathRectElement, PolyElement) {
/**
* @class util.svg
*
* SVG Util
*
* @param {jQuery/Element} rootElem
* @param {Object} rootAttr
* @constructor
* @alias SVG
*/
var SVG = function(rootElem, rootAttr) {
var self = this,
root = null,
main = null,
sub = null,
parent = {},
depth = 0;
var isFirst = false; // 첫번째 렌더링 체크
function init() {
self.root = root = new Element();
main = new TransElement();
sub = new TransElement();
root.create("svg", rootAttr);
main.create("g");
sub.create("g");
main.translate(0.5, 0.5);
sub.translate(0.5, 0.5);
rootElem.appendChild(root.element);
root.append(main);
root.append(sub);
}
function appendAll(target) {
var len = target.children.length;
for(var i = 0; i < len; i++) {
var child = target.children[i];
if(child) {
if(child.children.length > 0) {
appendAll(child);
}
// PathElement & PathSymbolElement & PathRectElement & PolyElement auto join
if(child instanceof PathElement || child instanceof PolyElement) {
child.join();
}
if(child.parent == target) {
target.element.appendChild(child.element);
}
}
}
}
this.create = function(obj, type, attr, callback) {
obj.create(type, attr);
if(depth == 0) {
main.append(obj);
} else {
parent[depth].append(obj);
}
if(_.typeCheck("function", callback)) {
depth++;
parent[depth] = obj;
callback.call(obj);
depth--;
}
return obj;
}
this.createChild = function(obj, type, attr, callback) {
if(obj.parent == main) {
throw new Error("JUI_CRITICAL_ERR: Parents are required elements of the '" + type + "'");
}
return this.create(obj, type, attr, callback);
}
/**
* @method size
*
* if arguments.length is 2, set attribute width, height to root element
* if arguments.length is zero, return svg size
*
* @return {Object}
* @return {Integer} width
* @return {Integer} height
*/
this.size = function() {
if(arguments.length == 2) {
var w = arguments[0],
h = arguments[1];
root.attr({ width: w, height: h });
} else {
return root.size();
}
}
/**
* @method clear
* @param isAll
*/
this.clear = function(isAll) {
main.each(function() {
if(this.element.parentNode) {
main.element.removeChild(this.element);
}
});
if(isAll === true) {
sub.each(function() {
if(this.element.parentNode) {
sub.element.removeChild(this.element);
}
});
}
}
/**
* @method reset
* @param isAll
*/
this.reset = function(isAll) {
this.clear(isAll);
main.children = [];
if(isAll === true) {
sub.children = [];
}
}
/**
* @method render
* @param isAll
*/
this.render = function(isAll) {
this.clear();
if(isFirst === false || isAll === true) {
appendAll(root);
} else {
appendAll(main);
}
isFirst = true;
}
/**
* @method
* implements svg image file download used by canvas
* @param name
*/
this.download = function(name) {
if(_.typeCheck("string", name)) {
name = name.split(".")[0];
}
var a = document.createElement("a");
a.download = (name) ? name + ".svg" : "svg.svg";
a.href = this.toDataURI()//;_.svgToBase64(rootElem.innerHTML);
document.body.appendChild(a);
a.click();
a.parentNode.removeChild(a);
}
this.downloadImage = function(name, type) {
type = type || "image/png";
var img = new Image();
var size = this.size();
var uri = this.toDataURI()
.replace('width="100%"', 'width="' + size.width + '"')
.replace('height="100%"', 'height="' + size.height + '"');
img.onload = function(){
var canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
var context = canvas.getContext('2d');
context.drawImage(img, 0, 0);
var png = canvas.toDataURI(type);
if(_.typeCheck("string", name)) {
name = name.split(".")[0];
}
var a = document.createElement('a');
a.download = (name) ? name + ".png" : "svg.png";
a.href = png;
document.body.appendChild(a);
a.click();
a.parentNode.removeChild(a);
}
img.src = uri;
}
/**
* @method exportCanvas
*
* convert svg image to canvas
*
* @param {Canvas} canvas
*/
this.exportCanvas = function(canvas) {
var img = new Image(),
size = this.size();
var uri = this.toDataURI()
.replace('width="100%"', 'width="' + size.width + '"')
.replace('height="100%"', 'height="' + size.height + '"');
img.onload = function() {
canvas.width = img.width;
canvas.height = img.height;
var context = canvas.getContext('2d');
context.drawImage(img, 0, 0);
}
img.src = uri;
}
/**
* @method toXML
*
* convert xml string
*
* @return {String} xml
*/
this.toXML = function() {
var text = rootElem.innerHTML;
text = text.replace('xmlns="http://www.w3.org/2000/svg"', '');
return [
'',
text.replace("