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 })();