      1 /*
      2 SG framework
      3 */
      4
      5 /**
      6  * @fileOverview 웹 콘텐츠제작용 프레임워크
      7  * @version 1.0
      8  * @author taejin (drumtj@gmail.com)
      9  */
     10
     11 /**
     12  * @see (http://jquery.com/).
     13  * @name jQuery
     14  * @class
     15  * See the jQuery Library  (http://jquery.com/) for full details.  This just
     16  * documents the function and classes that are added to jQuery by this plug-in.
     17  */
     18
     19
     20 /**
     21  * @see (http://jquery.com/)
     22  * @name fn
     23  * @class
     24  * See the jQuery Library  (http://jquery.com/) for full details.  This just
     25  * documents the function and classes that are added to jQuery by this plug-in.
     26  * @memberOf jQuery
     27  */
     28
     29 (function(){
     30
     31 	if( !("jQuery" in window) ){
     32 		throw new Error("sg framework is require jQuery");
     33 	}
     34
     35 	var toString = {}.toString
     36 		, slice = [].slice;
     37
     38 	var //readyCallbackList = []
     39 		initCallbackList = []
     40 		, customTagList = {}
     41 		, customAttrList = {}
     42 		, setStageFunc
     43 		, setScaleModeFunc
     44 		, progressImg;
     45
     46 	//private
     47 	var version = "1.0"
     48 		, scaleMode = "none"
     49 		, scaleX = 0
     50 		, scaleY = 0
     51 		, stageWidth = 0
     52 		, stageHeight = 0
     53 		, $stage = null
     54 		, $content = null
     55 		, $body = null
     56 		, $window = $( window );
     57
     58 	//support
     59 	if( !("forEach" in Array.prototype) ){
     60 		Array.prototype.forEach = function ( callback ){
     61 			for( var i=0; i<this.length; i++ ){
     62 				callback.call( window, this[ i ] );
     63 			}
     64 		}
     65 	}
     66
     67 	/**
     68 	 * change cursor
     69 	 * @example
     70 	 * $( element ).pointer();
     71 	 * @example
     72 	 * $( element ).pointer( false );
     73 	 * @param {boolean} bool
     74 	 * @returns jQuery object
     75 	 * @type jQuery Object
     76 	 */
     77 	jQuery.fn.pointer = function(){
     78 		return $( this ).css( "cursor", arguments[0] == false ? "auto" : "pointer" );
     79 	};
     80
     81 	/**
     82 	 * Returns the numeric value of the css
     83 	 * @example
     84 	 * $( element ).cssVal( "margin-top" );
     85 	 * @param {string} cssName
     86 	 * @returns remove the "px" character number
     87 	 * @type number
     88 	 */
     89 	jQuery.fn.cssVal = function( cssName ){
     90 		var val = $( this ).css( cssName );
     91 		if( val ) num = parseFloat( val.replace( "px", "") );
     92 		else num = 0;
     93
     94 		return isNaN( num ) ? 0 : num;
     95 	};
     96
     97 	/**
     98 	 * get custom tag
     99 	 * @example
    100 	 * $( 'p' ).getCustomTag( "sg-btn-hide" );
    101 	 * @param {string} tagName
    102 	 * @returns jQuery object
    103 	 * @type jQuery Object
    104 	 */
    105 	jQuery.fn.getCustomTag = function( tagName ){
    106 		var isRoot = (this == $);
    107 		for( var o in customTagList ){
    108 			if( tagName == o ){
    109 				if( isRoot ) return $( "[" + customTagList[ o ].identifier + "]" );
    110 				else return $( this ).children( "[" + customTagList[ o ].identifier + "]" );
    111 			}
    112 		}
    113 		return $;
    114 	};
    115
    116 	/**
    117 	 * get custom tag
    118 	 * @function
    119 	 * @memberOf jQuery
    120 	 * @example
    121 	 * $.getCustomTag( "sg-btn-hide" );
    122 	 * @param {string} tagName
    123 	 * @returns jQuery object
    124 	 * @type jQuery Object
    125 	 */
    126 	jQuery.getCustomTag = jQuery.fn.getCustomTag;
    127
    128
    129
    130 	function swapTag( obj, findTagName, swapTagName, identifier ) {
    131 		if ( document.querySelector( findTagName ) ) {
    132 			identifier = identifier ? ' ' + identifier + ' ' : '';
    133 			obj.html = obj.html
    134 				.replace( new RegExp( '<' + findTagName, 'g' ), '<' + swapTagName + identifier )
    135 				.replace( new RegExp( '</' + findTagName + '>', 'g' ), '</' + swapTagName + '>' );
    136 		}else{
    137 			customTagList[ findTagName ].nothing = true;
    138 		}
    139 	}
    140
    141 	function replaceTagList(){
    142 		var obj = {	html: document.body.innerHTML }
    143 			, customTagName
    144 			, customAttrName
    145 			, ctInfo, caInfo;
    146
    147 		//customTagList.forEach(function(o) {
    148 		for( customTagName in customTagList ){
    149 			ctInfo = customTagList[ customTagName ];
    150 			swapTag(obj, customTagName, ctInfo.originTag, ctInfo.identifier);
    151 		};
    152
    153 		//add custom attribute prefix : data-
    154 		var sgAttrNameReg = /\ssg([\-][\w]*){1,5}(\s)?=/gi,
    155 			s1 = obj.html.split('<'),
    156 			s2, matchArr, r2 = [],
    157 			i;
    158
    159 		for ( i = 0; i < s1.length; i++ ) {
    160 			s2 = s1[ i ].split( '>' );
    161 			for ( var j = 0; j < s2.length; j++ ) {
    162 				if ( j % 2 == 0 ) {
    163 					matchArr = s2[ j ].match( sgAttrNameReg );
    164 					if ( matchArr ) {
    165 						for ( var k = 0; k < matchArr.length; k++ ) {
    166 							s2[ j ] = s2[ j ].replace( matchArr[ k ], " data-" + matchArr[ k ].substr( 1 ) );
    167 						}
    168 					}
    169 				}
    170 			}
    171 			r2.push( s2.join( '>' ) );
    172 		}
    173
    174 		obj.html = r2.join( '<' );
    175 		document.body.innerHTML = obj.html;
    176
    177 		//support placeholder bug
    178 		$( "[placeholder]" ).each(function(i,e){
    179 			$( this ).text("").attr( "placeholder", $( this ).attr("placeholder") );
    180 		});
    181
    182 		//function apply
    183 		var $customTag;
    184 		for( customTagName in customTagList ){
    185 			ctInfo = customTagList[ customTagName ];
    186 			if( ctInfo.nothing ) continue;
    187
    188 			$customTag = $.getCustomTag( customTagName );//$( "[" + ctInfo.identifier + "]" );
    189
    190 			if( sg.isFunction( ctInfo[ "initFunc" ] ) ){
    191 				ctInfo[ "initFunc" ].call( sg );
    192 			}
    193
    194 			//execute to init function of custom attribute for each custom tag
    195 			$customTag.each(function(index, element) {
    196 				$( element ).data( "_customTagName", customTagName );
    197 				sg.setOption( element );
    198 				if( /^sg-btn-/.test( customTagName ) ) $( element ).pointer();
    199 				if( sg.isFunction( ctInfo[ "eachInitFunc" ] ) ){
    200 					ctInfo[ "eachInitFunc" ].call( element, element );
    201 				}
    202 				sg.initAttr( element );
    203 			});
    204
    205 			//execute to action function of custom attribute when called event handle
    206 			if( ctInfo[ "eventName" ] ){//sg.isFunction( ctInfo[ "eventFunc" ] )
    207 				$customTag.bind( ctInfo[ "eventName" ], function( e ){
    208 					var ctname = $( this ).data( "_customTagName" );
    209 					if ( !$( this ).data( "options" ).enabled ) return;
    210 					if( sg.isFunction( customTagList[ ctname ][ "eventFunc" ] ) ) customTagList[ ctname ][ "eventFunc" ].call( this, this, e );
    211 					sg.actionAttr( this );
    212 				});
    213 			};
    214 			$customTag = null;
    215 		};
    216
    217 		//apply custom attribute for all tag
    218 		for( customAttrName in customAttrList ){
    219 			caInfo = customAttrList[ customAttrName ];
    220 			if( caInfo.isForEveryTags ){
    221 				if( caInfo && sg.isFunction( caInfo[ "init" ] ) ){
    222 					$( "[data-" + customAttrName + "]" ).each(function( i, element ){
    223 						sg.setOption( element );
    224 						caInfo[ "init" ].call( element, element, element.getAttribute( "data-" + customAttrName ) );
    225 					});
    226 				}
    227 			}
    228 		}
    229 	}
    230
    231
    232 	function applyScaleMode(){
    233 		var ww = $window.width();
    234 		var hh = $window.height();
    235 		var ch = $content.height();
    236 		ch += $content.cssVal( "border-top-width" ) + $content.cssVal( "border-bottom-width" );
    237 		ch += $content.cssVal( "margin-top" ) + $content.cssVal( "margin-bottom" );
    238 		ch += $content.cssVal( "padding-top" ) + $content.cssVal( "padding-bottom" );
    239
    240 		switch( scaleMode ){
    241 			case "showall":
    242 				var msc = Math.min(ww / stageWidth, hh / stageHeight);
    243 				if(ch - stageHeight <= 1){
    244 					if(ch * msc > hh){
    245 						//console.log("top 0, overflow:visible");
    246 						$stage.css({
    247 							"transform" : "scale(" + msc + ")",
    248 							"transform-origin" : "0 0",
    249 							"left" : ((ww - stageWidth * msc) * 0.5) + "px",
    250 							"top" : 0
    251 						});
    252
    253 						$stage.parent().css({
    254 							"overflow-y" : "visible"
    255 						});
    256 					}else{
    257 						//console.log("top center, overflow:hidden");
    258 						$stage.css({
    259 							"transform" : "scale(" + msc + ")",
    260 							"transform-origin" : "0 0",
    261 							"left" : ((ww - stageWidth * msc) * 0.5) + "px",
    262 							"top" : ((hh - ch * msc) * 0.5) + "px"
    263 						});
    264
    265 						$stage.parent().css({
    266 							"overflow-y" : "hidden"
    267 						});
    268 					}
    269 				}else{
    270 					//console.log("top center, overflow:auto");
    271 					$stage.css({
    272 						"transform" : "scale(" + msc + ")",
    273 						"transform-origin" : "0 0",
    274 						"left" : ((ww - stageWidth * msc) * 0.5) + "px",
    275 						"top" : ((hh - stageHeight * msc) * 0.5) + "px"
    276 					});
    277
    278 					$stage.parent().css({
    279 						"overflow-y" : "auto"
    280 					});
    281 				}
    282 				scaleX = scaleY = msc;
    283 			break;
    284
    285 			case "noscale":
    286 				scaleX = scaleY = 0;
    287 			break;
    288
    289 			case "exactfit":
    290 				var mscx = ww / stageWidth;
    291 				var mscy = hh / stageHeight;
    292 				$stage.css({
    293 					"transform" : "scale(" + mscx + ", " + mscy + ")",
    294 					"transform-origin" : "0 0"
    295 				});
    296 				scaleX = mscx
    297 				scaleY = mscy;
    298 			break;
    299 		}
    300 	}
    301
    302
    303 	/**
    304 	 * @namespace 웹 콘텐츠제작용 프레임 워크
    305 	 * @author taejin (drumtj@gmail.com)
    306 	 * @name sg
    307 	 * @version 1.0
    308 	 * @since 2014.09.22
    309 	 * @description
    310 	 * jQuery를 필요로 하는 library입니다.<br>
    311 	 * - stage scale control<br>
    312 	 * - custom attribute and custom tag<br>
    313 	 */
    314 	var sg = {
    315 		//Variables defined for codehint
    316 		/**
    317 		 * [Read Only] sg framework의 버전정보.
    318 		 * @name version
    319 		 * @memberOf sg
    320 		 * @type string
    321 		 */
    322 		get version() { return version },
    323
    324 		/**
    325 		 * [Read Only] stage의 scaleMode값. sg.setScaleMode 함수로 설정되며 "showall", "exactfit", "none"의 값이 설정될 수 있다.
    326 		 * @name scaleMode
    327 		 * @memberOf sg
    328 		 * @type string
    329 		 */
    330 		get scaleMode() { return scaleMode },
    331
    332 		/**
    333 		 *[Read Only]  stage의 X축 scale값. sg.setScaleMode 함수로 scaleMode가 설정되면 값이 계산된다. 이 비율은 stage의 width를 기준으로 계산된다.
    334 		 * @name scaleX
    335 		 * @memberOf sg
    336 		 * @type number 0.0 ~ 1.0
    337 		 */
    338 		get scaleX() { return scaleX },
    339
    340 		/**
    341 		 * [Read Only] stage의 Y축 scale값. sg.setScaleMode 함수로 scaleMode가 설정되면 값이 계산된다. 이 비율은 stage의 height를 기준으로 계산된다.
    342 		 * @name scaleY
    343 		 * @memberOf sg
    344 		 * @type number 0.0 ~ 1.0
    345 		 */
    346 		get scaleY() { return scaleY },
    347
    348 		/**
    349 		 * [Read Only] stage의 width값. setStage함수로 stage설정시 설정된 stage의 width값을 읽어온다.
    350 		 * @name stageWidth
    351 		 * @memberOf sg
    352 		 * @type number
    353 		 */
    354 		get stageWidth() { return stageWidth },
    355
    356 		/**
    357 		 * [Read Only] stage의 height값. setStage함수로 stage설정시 설정된 stage의 height값을 읽어온다.
    358 		 * @name stageHeight
    359 		 * @memberOf sg
    360 		 * @type number
    361 		 */
    362 		get stageHeight() { return stageHeight },
    363
    364 		/**
    365 		 * [Read Only] 스테이지로 설정된 엘리먼트. sg.setStage함수로 설정된다.
    366 		 * @name $stage
    367 		 * @memberOf sg
    368 		 * @type jQuery Object
    369 		 */
    370 		get $stage() { return $stage },
    371
    372 		/**
    373 		 * [Read Only] stage의 자식요소들을 감싼 컨테이너. $stage가 설정될 때 설정된다.
    374 		 * @name $content
    375 		 * @memberOf sg
    376 		 * @type jQuery Object
    377 		 */
    378 		get $content() { return $content },
    379
    380 		/**
    381 		 * [Read Only] $("body")의 참조
    382 		 * @name $body
    383 		 * @memberOf sg
    384 		 * @type jQuery Object
    385 		 */
    386 		get $body() { return $body },
    387
    388 		/**
    389 		 * [Read Only] jQuery Object of window object.
    390 		 * @name $window
    391 		 * @memberOf sg
    392 		 * @type jQuery Object
    393 		 */
    394 		get $window() { return $window },
    395
    396 		/**
    397 		 * [Read Only] sg에 새로운 함수를 추가하거나 기존 함수에 덮어쓰거나 함수를 상속받아 확장한다.
    398 		 * @function
    399 		 * @name extend
    400 		 * @memberOf sg
    401 		 * @example
    402 		 * sg.extend({
    403 		 * 	myfunc: function( ){
    404 		 *		//TO DO
    405 		 * 	}
    406 		 * });
    407 		 * @param {object} property sg에 추가할 함수를 가진 Object
    408 		 */
    409 		extend: function( prop ){
    410 			if(typeof prop !== "object"){ throw new Error("sg.extend arguments is not Object!"); }
    411
    412 			for( var name in prop ){
    413 				//sg.hasOwnProperty( name )
    414 				if( name in sg ) prop[ name ]._super = sg[ name ];
    415 				sg[ name ] = prop[ name ];
    416 			}
    417 		},
    418
    419 		/**
    420 		 * sg.extend를 통해 확장한 함수에서 원래 함수를 호출할 때 사용
    421 		 * @function
    422 		 * @name super
    423 		 * @memberOf sg
    424 		 * @example
    425 		 * sg.extend({
    426 		 * 	myfunc: function( str ){
    427 		 *		return '(' + str + ')';
    428 		 * 	}
    429 		 * });
    430 		 *
    431 		 * sg.myfunc( 'abc' ); //return "(abc)"
    432 		 *
    433 		 * sg.extend({
    434 		 * 	myfunc: function( str ){
    435 		 *		return sg.super( str.toUpperCase() );
    436 		 * 	}
    437 		 * });
    438 		 *
    439 		 * sg.myfunc( 'abc' ); //return "(ABC)"
    440 		 * @param {object} arguments 원래 함수의 arguments.
    441 		 * @return {*} 원래 함수의 반환 값.
    442 		 * @type *
    443 		 */
    444 		super: function(){
    445 			var _super = arguments.callee.caller._super;
    446 			return sg.isFunction( _super ) ? _super.apply( this, arguments ) : null;
    447 		}
    448 	};
    449
    450
    451 	sg.extend({
    452 		/**
    453 		 * 대상이 함수인지 확인. jQuery의 isFunction 함수를 사용.
    454 		 * @function
    455 		 * @name isFunction
    456 		 * @memberOf sg
    457 		 * @param {object} object
    458 		 * @returns true or false
    459 		 * @type boolean
    460 		 */
    461 		isFunction: $.isFunction,
    462
    463 		/**
    464 		 * 대상이 배열인지 확인. jQuery의 isArray 함수를 사용.
    465 		 * @function
    466 		 * @name isArray
    467 		 * @memberOf sg
    468 		 * @param {object} object
    469 		 * @returns true or false
    470 		 * @type boolean
    471 		 */
    472 		isArray: $.isArray,
    473
    474 		/**
    475 		 * 대상이 window객체인지 확인. jQuery의 isWindow 함수를 사용.
    476 		 * @function
    477 		 * @name isWindow
    478 		 * @memberOf sg
    479 		 * @param {object} object
    480 		 * @returns true or false
    481 		 * @type boolean
    482 		 */
    483 		isWindow: $.isWindow,
    484
    485 		/**
    486 		 * 대상이 숫자인지 확인. jQuery의 isNumeric 함수를 사용.
    487 		 * @function
    488 		 * @name isNumeric
    489 		 * @memberOf sg
    490 		 * @param {object} object
    491 		 * @returns true or false
    492 		 * @type boolean
    493 		 */
    494 		isNumeric: $.isNumeric,
    495
    496 		/**
    497 		 * 대상이 빈 오브젝트인지 확인. jQuery의 isEmptyObject 함수를 사용.
    498 		 * @function
    499 		 * @name isEmptyObject
    500 		 * @memberOf sg
    501 		 * @param {object} object
    502 		 * @returns true or false
    503 		 * @type boolean
    504 		 */
    505 		isEmptyObject: $.isEmptyObject,
    506
    507 		/**
    508 		 * 대상이 배열에 포함되어 있는지 확인. jQuery의 inArray 함수를 사용.
    509 		 * @function
    510 		 * @name inArray
    511 		 * @memberOf sg
    512 		 * @param {object} element
    513 		 * @param {array} array
    514 		 * @param {number} index
    515 		 * @returns true or false
    516 		 * @type string
    517 		 */
    518 		inArray: $.inArray,
    519
    520 		/**
    521 		 * 대상의 data type을 반환. jQuery의 type 함수를 사용.
    522 		 * @function
    523 		 * @name type
    524 		 * @memberOf sg
    525 		 * @param {object}
    526 		 * @returns true or false
    527 		 * @type string
    528 		 */
    529 		type: $.type
    530 	});
    531
    532
    533
    534 	sg.extend({
    535
    536 		/**
    537 		 * 초기실행 callback함수를 추가한다. 이렇게 추가된 callback함수들은 초기 실행과정 진행 후에 실행된다.
    538 		 * @function
    539 		 * @name addInit
    540 		 * @memberOf sg
    541 		 * @example
    542 		 * sg.addInit( function(){} );
    543 		 * @param {function} callback
    544 		 */
    545 		addInit: function( callback ){
    546 			initCallbackList.push( callback );
    547 		},
    548
    549 		/**
    550 		 * progress image의 경로를 설정한다. 설정된 image는 page loading하는 동안 화면에 나타난다.
    551 		 * @function
    552 		 * @name setLoadingImage
    553 		 * @memberOf sg
    554 		 * @example
    555 		 * sg.setLoadingImage( '../common/img/progress.gif' );
    556 		 * @param {string} path
    557 		 */
    558 		setLoadingImage: function( path ){
    559 			progressImg = new Image();
    560 			progressImg.setAttribute("data-sg-id", "progressImg");
    561 			progressImg["onload"] = function( e ){
    562 				this.style.left = (((window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth) - this.width) * 0.5) + "px";
    563 				this.style.top = (((window.innerHeight || document.documentElement.clientWidth || document.body.clientWidth) - this.height) * 0.5) + "px";
    564 				if( !sg.isReady ) document.body.appendChild( progressImg );
    565 				else delete progressImg;
    566 			};
    567 			progressImg.src = path;
    568 			progressImg.style.position = "fixed";
    569 			progressImg.style.visibility = "visible";
    570 		},
    571
    572 		/**
    573 		 * stage를 설정한다. 설정된 stage는 sg.$stage로 접근 할 수 있다. stage는 scale조절의 기준이 된다.
    574 		 * @function
    575 		 * @name setStage
    576 		 * @memberOf sg
    577 		 * @example
    578 		 * sg.setStage( "#stage" );
    579 		 * @param {object} elementOrSelector
    580 		 */
    581 		setStage: function( elementOrSelector ){
    582 			setStageFunc = function(){
    583 				var $temp = $( elementOrSelector );
    584 				if( $temp.length == 0 ) throw new Error( "setStage : $stage is not select. (selector : " + elementOrSelector + ")" );
    585
    586 				$temp.attr( "data-sg-id", "stage" );
    587 				$content = $( "<div data-sg-id='content'></div>" );
    588 				$temp.contents().each(function( i, e ){
    589 					$content.append( this );
    590 				});
    591
    592 				$temp.append( $content ).css( "position", "absolute" );
    593 				$temp.parent().css( "overflow-x", "hidden" );
    594
    595 				//border가 없으면, 때로는 크기가 잘 못 계산되기 때문에
    596 				if( $temp.cssVal( "border-top-width" ) == 0 ){
    597 					$temp.css( "border", "solid 1px rgba(255,255,255,0)" );
    598 				}
    599
    600 				stageWidth = $temp.width();
    601 				stageHeight = $temp.height();
    602
    603 				$content.css({
    604 					"min-height": stageHeight,
    605 					"visibility": "hidden"
    606 				});
    607
    608 				$stage = $temp;
    609
    610 				//apply scaleMode
    611 				if( setScaleModeFunc ){
    612 					setScaleModeFunc.apply( sg );
    613 					setScaleModeFunc = null;
    614 				}
    615 			}
    616
    617 			if( sg.isReady ){
    618 				setStageFunc.apply( sg );
    619 				setStageFunc = null;
    620 			}
    621 		},
    622
    623 		/**
    624 		 * scaleMode를 설정한다. stage가 설정되어야 기능이 작동하며, "showall", "exactfit", "none"의 모드가 있다.
    625 		 * @function
    626 		 * @name setScaleMode
    627 		 * @memberOf sg
    628 		 * @example
    629 		 * sg.setStage( "#stage" );
    630 		 * sg.setScaleMode( "showall" );
    631 		 * @example
    632 		 * sg.setStage( "#stage" );
    633 		 * sg.setScaleMode( "exactfit" );
    634 		 * @param {string} scaleMode
    635 		 */
    636 		setScaleMode: function( _scaleMode ){
    637 			scaleMode = _scaleMode;
    638 			setScaleModeFunc = function(){
    639 				$window.bind( "resize" , function(){
    640 					applyScaleMode();
    641 				});
    642 				applyScaleMode();
    643 			}
    644
    645 			if( $stage ){
    646 				setScaleModeFunc.apply( this );
    647 				setScaleModeFunc = null;
    648 			}
    649 		},
    650
    651 		//options setting for custom tags
    652 		/**
    653 		 * $(target).data("options")에 Custom tag 처리시 사용할 값을 저장한다.
    654 		 * @function
    655 		 * @name setOption
    656 		 * @memberOf sg
    657 		 * @example
    658 		 * sg.setOption( element );
    659 		 * @example
    660 		 * sg.setOption( element , {myClicked: false} );
    661 		 * @param {object} elementOrSelector element나 jQuery selector
    662 		 * @param {object} options options값이 전달되지 않았다면 기본값을 넣는다.
    663 		 */
    664 		setOption: function ( target, options ){
    665 			var op = { enabled: true };
    666 			for( var o in options ) op[ o ] = options[ o ];
    667 			$( target ).data( "options", op );
    668 		},
    669
    670 		/**
    671 		 * register custom attribute.<br>
    672 		 * @function
    673 		 * @name addCustomAttr
    674 		 * @memberOf sg
    675 		 * @param {string} option.name name of custom attribute.
    676 		 * @param {function} option.init initialize function. 초기 설정 시점에 실행
    677 		 * @param {function} option.action action function. 이 커스텀 속성을 사용하는 커스텀 태그의 이벤트 처리시 실행
    678 		 * @param {boolean} option.applyAll 모든 기본 태그에서도 이 속성의 기능을 적용 할 것인지 여부. true or false
    679 		 * @example
    680 		 * sg.addCustomAttr({
    681 		 *	//attribute name
    682 		 *	name: "sg-attrname",
    683 		 * 	//initialize
    684 		 *	init: function( element, attrValue ){},
    685 		 * 	//action
    686 		 * 	action: function( element, attrValue ){},
    687 		 * 	//apply to every tag - default value : true
    688 		 * 	applyAll: false
    689 		 * });
    690 		 * @example
    691 		 * sg.addCustomAttr({
    692 		 *	name: "sg-color",
    693 		 *	init: function( element, attrValue ){
    694 		 * 		$( element ).css( "color", attrValue );
    695 		 * 	},
    696 		 * });
    697 		 * @example
    698 		 * sg.addCustomAttr({
    699 		 *	name: "sg-alert",
    700 		 *	action: function( element, attrValue ){
    701 		 * 		if( attrValue ) alert( attrValue );
    702 		 * 	}
    703 		 * });
    704 		 */
    705 		addCustomAttr: function( obj ){
    706 			customAttrList[ obj.name ] = {
    707 				init: obj.init,
    708 				action: obj.action,
    709 				isForEveryTags: obj.applyAll
    710 			}
    711 		},
    712
    713 		/**
    714 		 * register custom tag.<br>
    715 		 * custom tag를 select 하려면 $.getCustomTag( custom tag name )을 사용한다.
    716 		 * @function
    717 		 * @name addCustomTag
    718 		 * @memberOf sg
    719 		 * @param {string} option.name name of custom tag.
    720 		 * @param {string} option.originTag 교체할 진짜 태그의 이름. 기본값은 'div'
    721 		 * @param {string} option.id custom tag의 식별자로 특수한 경우가 아니면 기본값을 사용. 기본값은 "[data-sg-id='name of custom tag']"
    722 		 * @param {array} option.attr custom tag에서 사용 될 custom attribute 이름을 나열한 배열
    723 		 * @param {function} option.init 초기설정 때 실행 될 내용이 있다면 이곳에 함수를 정의한다.
    724 		 * @param {function} option.tagInit 초기설정 때 각 태그마다 실행 될 내용이 있다면 이곳에 함수를 정의한다.
    725 		 * @param {string} option.event 특정 이벤트로 동작한다면 이벤트 이름을 넣는다.
    726 		 * @param {function} option.eventHandle 이벤트의 처리 함수를 정의한다.
    727 		 * @example
    728 		 * sg.addCustomTag({
    729 		 *	//tag name
    730 		 *	name: "sg-tagname",
    731 		 *	//swap tag name (default tag) - default value : "div"
    732 		 *	originTag: "div",
    733 		 *	//identifier - default value : "data-sg-id=__custom tag name__"
    734 		 *	id: "data-myid='ID'",
    735 		 *	//used attributes
    736 		 *	attr: [ "sg-color" ],
    737 		 * 	//initialize
    738 		 *	init: function(){},
    739 		 * 	//init each tag
    740 		 * 	tagInit: function( element ){},
    741 		 * 	//event
    742 		 * 	event: "eventName",
    743 		 * 	//handle
    744 		 * 	eventHandle: function( element, event ){}
    745 		 * });
    746 		 * @example
    747 		 * sg.addCustomTag({
    748 		 *	name: "sg-h1",
    749 		 *	attr: [ "sg-color", "sg-alert" ],
    750 		 * 	tagInit: function( element ){
    751 		 * 		$( element ).css( "font-size", "40pt" );
    752 		 * 	},
    753 		 * 	event: "click",
    754 		 * 	eventHandle: function( element ){
    755 		 * 		$( element ).animate( {"font-size": "80pt"} );
    756 		 * 	}
    757 		 * });
    758 		 */
    759 		addCustomTag: function( obj ){
    760 			customTagList[ obj.name ] = {
    761 				originTag: obj.originTag ? obj.originTag : "div",
    762 				identifier: obj.id ? obj.id : "data-sg-id='" + obj.name + "'",
    763 				eventName: obj.event,
    764 				attrList: (function( list ){
    765 					if( !list ) return null;
    766 					else list = slice.call( list );//copy array
    767 					var i=0, attrName;
    768 					list.forEach( function(attrName){
    769 						if( !(attrName in customAttrList) ) list.splice( i, 1 );
    770 						else i++;
    771 					});
    772 					return list;
    773 				})( obj.attr ),
    774 				initFunc: obj.init,
    775 				eachInitFunc: obj.tagInit,
    776 				eventFunc: obj.eventHandle
    777 			}
    778 		},
    779
    780 		//initialize about custom attribute in custom tag
    781 		/**
    782 		 * 커스텀 태그 정의시 설정한, 사용되는 속성에 대한 초기화 함수.
    783 		 * @function
    784 		 * @name initAttr
    785 		 * @memberOf sg
    786 		 * @example sg.initAttr( element );
    787 		 * @param {object} element
    788 		 */
    789 		initAttr: function( element ){
    790 			//console.log( element, arguments.callee );
    791 			var customTagName = $( element ).data( "_customTagName" )
    792 				, attrList, attr, attrValue;
    793
    794 			if( customTagName ){
    795 				attrList = customTagList[ customTagName ].attrList;
    796 				if( attrList ){
    797 					attrList.forEach(function( attrName ){
    798 						attr = customAttrList[ attrName ];
    799 						attrValue = element.getAttribute( "data-" + attrName );
    800 						if( attr && attr[ "init" ] && !attr.isForEveryTags ){
    801 							attr[ "init" ].call( element, element, attrValue );
    802 						}
    803 					});
    804 				}
    805 			}
    806 		},
    807
    808 		//execute function about custom attribute in custom tag
    809 		/**
    810 		 * 커스텀 태그 정의시 설정한, 사용되는 커스텀 속성에 대한 기능실행 함수.
    811 		 * @function
    812 		 * @name actionAttr
    813 		 * @memberOf sg
    814 		 * @example sg.actionAttr( element );
    815 		 * @param {object} element
    816 		 */
    817 		actionAttr: function( element ){
    818 			var customTagName = $( element ).data( "_customTagName" )
    819 				, attrList, attr, attrValue;
    820
    821 			if( customTagName ){
    822 				attrList = customTagList[ customTagName ].attrList;
    823 				if( attrList ){
    824 					attrList.forEach(function( attrName ){
    825 						attr = customAttrList[ attrName ];
    826 						attrValue = element.getAttribute( "data-" + attrName );
    827 						if( attr && attr[ "action" ] ){
    828 							attr[ "action" ].call( element, element, attrValue );
    829 						}
    830 					});
    831 				}
    832 			}
    833 		},
    834
    835
    836
    837 		/**
    838 		 * jQuery.hide
    839 		 * @function
    840 		 * @name hide
    841 		 * @memberOf sg
    842 		 * @example
    843 		 * sg.hide( 'p' );	//$( 'p' ).hide();
    844 		 * sg.hide( this ); //$( this ).hide();
    845 		 * @param {object} elementOrSelector
    846 		 */
    847 		hide: function( elementOrSelector ){
    848 			$( elementOrSelector ).hide();
    849 		},
    850
    851 		/**
    852 		 * jQuery.show
    853 		 * @function
    854 		 * @name show
    855 		 * @memberOf sg
    856 		 * @example
    857 		 * sg.show( 'p' );	//$( 'p' ).show();
    858 		 * sg.show( this ); //$( this ).show();
    859 		 * @param {object} elementOrSelector
    860 		 */
    861 		show: function( elementOrSelector ){
    862 			$( elementOrSelector ).show();
    863 		},
    864
    865 		/**
    866 		 * jQuery.fadeIn("slow")
    867 		 * @function
    868 		 * @name fadeIn
    869 		 * @memberOf sg
    870 		 * @example
    871 		 * sg.fadeIn( 'p' );	//$( 'p' ).fadeIn( 'slow' );
    872 		 * sg.fadeIn( this );	//$( this ).fadeIn( 'slow' );
    873 		 * @param {object} elementOrSelector
    874 		 */
    875 		fadeIn: function( elementOrSelector ){
    876 			$( elementOrSelector ).fadeIn("slow");
    877 		},
    878
    879 		/**
    880 		 * jQuery.fadeOut("slow")
    881 		 * @function
    882 		 * @name fadeOut
    883 		 * @memberOf sg
    884 		 * @example
    885 		 * sg.fadeOut( 'p' );	//$( 'p' ).fadeOut( 'slow' );
    886 		 * sg.fadeOut( this );	//$( this ).fadeOut( 'slow' );
    887 		 * @param {object} elementOrSelector
    888 		 */
    889 		fadeOut: function( elementOrSelector ){
    890 			$( elementOrSelector ).fadeOut("slow");
    891 		},
    892
    893 		/**
    894 		 * 커스텀 태그가 이벤트에 동작하도록 허용하고 cursor를 pointer로 설정.
    895 		 * @function
    896 		 * @name enabled
    897 		 * @memberOf sg
    898 		 * @example
    899 		 * sg.enabled( this );
    900 		 * sg.enabled( '#myButton' );
    901 		 * sg.enabled( $.getCustomTag( 'sg-btn-hide' ) );
    902 		 * @param {object} elementOrSelector
    903 		 * @returns jQuery Object
    904 		 * @type jQuery Object
    905 		 */
    906 		enabled: function ( elementOrSelector ){
    907 			return $(elementOrSelector).each(function(i,e){
    908 				var $this = $(this);
    909 				$this.css("cursor","pointer");
    910 				var options = $this.data("options") || {};
    911 				options.enabled = true;
    912 				$this.data("options", options);
    913 			});
    914 		},
    915
    916 		/**
    917 		 * 커스텀 태그가 이벤트에 동작하지 않도록 하고 cursor를 default로 설정.
    918 		 * @function
    919 		 * @name disabled
    920 		 * @memberOf sg
    921 		 * @example
    922 		 * sg.disabled( this );
    923 		 * sg.disabled( '#myButton' );
    924 		 * sg.disabled( $.getCustomTag( 'sg-btn-hide' ) );
    925 		 * @param {object} elementOrSelector
    926 		 * @returns jQuery Object
    927 		 * @type jQuery Object
    928 		 */
    929 		disabled: function ( elementOrSelector ){
    930 			return $(elementOrSelector).each(function(i,e){
    931 				var $this = $(this);
    932 				$this.css("cursor","default");
    933 				var options = $this.data("options") || {};
    934 				options.enabled = false;
    935 				$this.data("options", options);
    936 			});
    937 		}
    938 	});
    939
    940 	sg.extend({
    941 		/**
    942 		 * 초기 실행 함수. parameter로 callback함수가 전달 된다면 초기 설정 진행 후에 callback함수 실행.
    943 		 * @function
    944 		 * @name init
    945 		 * @memberOf sg
    946 		 * @example
    947 		 * sg.setStage( "#stage" );
    948 		 * sg.setScaleMode( "showall" );
    949 		 * sg.init();
    950 		 * @example
    951 		 * sg.setStage( "#stage" );
    952 		 * sg.setScaleMode( "showall" );
    953 		 * sg.init( function(){} );
    954 		 * @param {function} callback
    955 		 */
    956 		init: function ( callback ) {
    957 			function _init(){
    958 				//////setting
    959 				//console.log("setting");
    960 				$body = $( document.body );
    961
    962 				replaceTagList();
    963
    964 				if( setStageFunc ){
    965 					setStageFunc.apply( sg );
    966 					setStageFunc = null;
    967 				}
    968
    969 				///compatibility for old versions
    970 				if( "init" in window && sg.isFunction( window[ "init" ] ) ){
    971 					window[ "init" ].apply( window );
    972 				}
    973
    974 				if( "initList" in window ){
    975 					initCallbackList = initCallbackList.concat( window[ "initList" ] );
    976 				}
    977 				///
    978
    979 				initCallbackList.forEach( function( initFunc ){
    980 					if( sg.isFunction( initFunc ) ) initFunc.apply( window );
    981 				});
    982
    983
    984 				$("[data-sg-id='progressImg']").remove();
    985 				$content = $("[data-sg-id='content']").css( "visibility", "visible" );
    986 				$stage = $("[data-sg-id='stage']");
    987 				sg.isReady = true;
    988
    989 				setTimeout(function(){
    990 					$window.trigger("resize");
    991 				}, 0);
    992 			}
    993
    994 			if( $.isReady ){
    995 				_init.apply( window );
    996 				if( sg.isFunction( callback ) ) callback.apply( window );
    997 			}else{
    998 				$( document ).ready(function(e) {
    999                     _init.apply( window );
    1000 					if( sg.isFunction( callback ) ) callback.apply( window );
    1001                 });
    1002 			}
    1003 		}
    1004 	});
    1005
    1006 	window.sg = sg;
    1007
    1008 	return sg;
    1009 })();