/**
 * Plani javascript module
 *
 * jquery 3.1.0
 *
 * @package	Plani javascript Api Module
 * @author	kim seung beom
 * @copyright	Copyright (c) 2016, Plani, Inc.
 * @link	http://plani.co.kr
 * @since	 Version 2.0
 * @filesource
 */

/**
 * jquery form
 */
$.module['form'] = function( form, options ) {
	$.extend( this, {
		form : form.eq(0),
		enctype : form.attr('enctype')=='multipart/form-data',
		options : $.extend({
			'ajax' : true, 
			'error' : null,
			'before' : null,
			'success' : null,
			'message' : __('처리 되었습니다'), 
			'jgrow' : false,
			'page_exit' : false,
			'page_exit_check' : true
			}, 
			options
		)
	});

	this.__construct();
}

$.extend( $.module['form'].prototype, {
	__construct : function() {
		var self = this;

		self.form.addClass('plani-form')
			.prepend('<div class="alert-error alert-result"></div><div class="alert-success alert-result"></div>')
			.end()
			.on( 'submit', function() {
				self.options['page_exit_check'] = true;
				
				if( self.options['before'] instanceof Function ) {	// before
					if( self.options['before'](self) != true ) {
						return false;
					}
				}

				if( self.options['ajax'] == true ) { // ajax
					self.request();
					return false;
				}
			});

		$('.submit', self.form)
			.on('click', function(){
				self.form.trigger('submit');
				return false;
			});

		if( self.options['page_exit'] == true ) {
			$.window
				.on( 'beforeunload', function() {
					if( self.options['page_exit_check'] == false ) {
						self.form.trigger('editor_update');
						if( $('[detect-check=false]', self.form).length > 0 ) {
							return __('변경사항이 저장되지 않았을 수 있습니다.');
						}
					}
				});

			self.detect();
		}

		self.form.hook();
	},
	request : function() {
		var self = this;

		self._disposal(true);
		self.form.ajaxSubmit({
			data: { HTTP_X_REQUESTED_WITH : 'XMLHttpRequest' },
			beforeSubmit : function(a, f, o){ o.dataType	= 'html' },
			success: function ( msg ) {
				var msgSplit = msg.split('::'),
					arg1 = 'failed',
					arg2 = '';

				if( msgSplit[0] == 'true' ) {
					arg1 = 'true';
					arg2 = msgSplit[1] ? msgSplit[1] : '';
				}else if( msgSplit.length > 1 ) {
					arg1 = msgSplit[0];
					arg2 = msgSplit[1];
				}else {
					arg2 = msgSplit[0];
				}

				self.response( arg1, arg2 );
			}
		});
	},
	response : function( result, msg ) {
		this['%s_%s'.sprintf( this.options['jgrow'] == true ? '_jgrow' : '', result != 'failed' ? 'success' : 'error' )]( result, msg );
	},
	detect : function() {
		var self = this;

		$('input[type=text], input[type=radio], input[type=checkbox], select, textarea', self.form)
			.each( function(){
				var choice = 'radio|checkbox'.indexOf(this.type) > -1;

				$(this).attr( { 'detect' : choice == true ? this.checked : this.value, 'detect-check' : 'true' } )
					.on( 'change', function(){
						var detect = $(this).attr('detect'),
							v = choice == true ? this.checked : this.value;

						$(this).attr('detect-check', detect == v.toString() );
						self.options['page_exit_check'] = false;
					});
			});
	},
	_disposal : function( method ) {
		if( method == true ) {
			$('.alert-result', this.form).html('');
		}

		$(':image, :submit, .submit', this.form)
			.each( function(){
				if( this.tagName == 'BUTTON' ) {
					if( method == true ) {
						$(this).attr('disabled', true)
							.find('span').hide().end().append('<span class="saveing"><i class="xi-spinner-3 xi-spin"></i> &nbsp;&nbsp;' +__('처리중')+ '...</span>');
					}else {
						$(this).attr('disabled', false)
							.find('span').eq(0).show().end().eq(1).remove();
					}
				}else{
					if( method == true ) {
						$(this).hide()
							.after('<span class="saveing"><i class="xi-spinner-3 xi-spin"></i> &nbsp;&nbsp;' +__('처리중')+ '...</span>');	
					}else {
						$(this).show()
							.next().remove();
					}
				}
			});
	},
	_focus : function( msg ) {
		var focus = msg.match(/<!--focus>(.+)<\/focus-->/g),
			p = msg.match(/<p>(.+)<\/p>/g);

		try {
			for( var i=0; i < focus.length; i++ ) {
				var item = focus[i].match(/<!--focus>(.+)<\/focus-->/),
					obj = $('[name='+item[1]+']'),
					text = p[i].match(/<p>(.+)<\/p>/),
					text_message = text[1].split(' : ');

				if( obj.length > 0 ) {
					obj.eq(0).addClass('tooltip').helper_tooltip({
						message : text_message[1], 
						open : false,
						form_element : true
					});

					if( i == 0 ){
						obj.focus();
					}
				}
			}

		}catch( e ) {
			// return null
		}
	},
	_success : function( result, msg ) {
		var self = this,
			msg = msg !='' ? msg : this.options['message'];

		$('.alert-success', this.form).html('<p>' +this.options['message']+ '</p>')
			.fadeIn()
			.find('p')
			.append('<i class="fa fa-times"></i>')
			.find('i')
			.on( 'click', function(){
				$(this).closest('p').remove();
			});
		
		setTimeout( function(){
			self._process(msg, true);
		},
		1000);			
	},
	_error : function( result, msg ) {
		if( msg.indexOf('</p>') < 0 ) {
			msg='<p>' +msg+ '</p>';
		}

		$('.alert-error', this.form).html(msg)
			.fadeIn('slow')
			.find('p')
			.append('<i class="fa fa-times"></i>')
			.find('i')
			.one( 'click', function(){
				$(this).closest('p').remove();
			});
		
		this._process( msg, false );
		this._disposal( false );
		this._focus( msg );
	},
	_jgrow_success : function( result, msg ) {
		var self = this,
			msg = msg !='' && result !='redirect' ? msg : this.options['message'];
		
		$.jGrowl( self.options['message'], {
			life	: 1000,
			header  : 'SUCCESS',
			close	: function(e, m) { 
				self._process(msg, true);
			}
		});

	},
	_jgrow_error : function( result, msg ) {
		var self = this;

		$.jGrowl( msg,  {
			life	: 2000,
			header : 'ERROR',
			close	: function(e, m) { 
				self._process( msg, false );
				self._disposal( false );
				self._focus( msg );
			}
		});
	},
	_process : function( msg, is ) {
		if( is == true ) {
			if( typeof this.options['success'] == 'function' ) {
				this.options['success']( this, msg );
			}else{
				location.reload();
			}
		}else {
			if( typeof this.options['error'] == 'function' ) {
				this.options['error']( this, msg );
			}		
		}
	}
});

/**
 * jquery modal
 */
$.static.modal = null;
$.module['modal'] = function( obj, options ) {	
	$.extend( this, {
		obj : obj,
		layer : $('.plani-modal'),
		bg : $('.plani-modal-bg'),
		progress : $('.plani-modal-progress'),
		selector : null,
		selector_size : {
			width : 0,
			heigth : 0		
		},
		timer : null,
		status : {
			'open' : false,
			'mode' : 'img',
			'index' : 0
		},
		options : $.extend( {
			'title' : 'untitle',
			'start' : false,
			'close' : true,
			'iframe' : false,
			'width' : 'auto',
			'destory' : false,
			'onclose' : function(){ }
			}, 
			options
		)
	});

	if( typeof this.obj == 'string' ) {
		this.obj = $('<a />', { 'href' : this.obj });
		this.options.start = true;
		this.options.destory = true;
	}

	this.__construct();
}

$.extend( $.module['modal'].prototype, {
	__construct : function() {
		var self = this;

		if( self.layer.length < 1 ) {
			self.layer = $('<div />', {'class' : 'plani-modal', 'tabindex' : 0}).html('<div class="modal-title"></div><div class="modal-frame"></div><iframe src="about:blank" class="modal-iframe" title="modal-iframe" />').appendTo('body');
			self.bg = $('<div />', {'class' : 'plani-modal-bg'}).appendTo('body');
			self.progress = $('<i />', {'class' : 'xi-spinner-1 xi-spin plani-modal-progress'}).appendTo(self.bg);
		}

		self.obj
			.on( 'click', function() {
				self['_%sbox'.sprintf(this.tagName  == 'IMG' ? 'image' : 'page')]( this );
				return false;
			});

		self.bg
			.on( 'click', function() {
				if( self.options.close == true ) {
					self.end();
				}
			});

		self.layer
			.undelegate('.close-button', 'click')
			.delegate( '.close-button', 'click', function() {
				self.end();
				return false;
			})
			.undelegate('.prev-button', 'click')
			.delegate( '.prev-button', 'click', function() {
				self.change( self.status.index-1 );
				return false;
			})
			.undelegate('.next-button', 'click')
			.delegate( '.next-button', 'click', function() {
				self.change( self.status.index+1 );
				return false;
			});

		$.window
			.on( 'keydown', function(e) {
				if( e.which == 27 && self.status.open == true ) {
					self.end();
					return false;
				}
			})
			.on( 'resize', function() {
				if( self.status.open == true ) {
					var size = self._resize(self.selector);
					self.layer.stop()
						.animate( {'left' : size.end.left, 'top' : size.end.top, 'min-width' : size.width, 'min-height' : size.height, 'max-width' : size.width}, 'normal' );
				}
			});

		if( self.options.start == true ){
			self.obj.eq(0).trigger('click');
		}
	},
	_pagebox : function( obj ) {
		var self = this,
			img = new Image(),
			iframe = $('.modal-iframe', self.layer),
			frame = $('.modal-frame', self.layer),
			iframe_src = iframe.attr('src'),
			obj_src = obj.getAttribute('href')+'/layout/modal';

		self.status.open = true;
		self.status.mode = 'iframe';

		self.bg.fadeIn('slow');
		frame.hide();
		iframe.hide();

		title = self.options.title;
		if( title == 'untitle' && $(obj).attr('title') != '' ) {
			 title = $(obj).attr('title');
		}

		$('.modal-title', self.layer).html(title + self._panel(obj));
		if( self.options.iframe == true ) {
			iframe.css({'height' : 'auto'})
				.attr('src', obj_src)
				.off('load')
				.on( 'load', function() {
					var frame = $(this),
						framebody = $(this.contentWindow.document);

					self.layer.css( {'width' : self.options.width+20} ).show();
					frame.show();
					img.width = self.options.width =='auto'? framebody.width() : self.options.width;
					img.height = framebody.height()+50;
					frame.hide();

					$('body', framebody).css('overflow', 'hidden');
					self.open( self._resize(img, true), true, function() {
						self.timer = setInterval( function(){	
							iframe.stop().animate({'height' : framebody.height()}, 'fast');
						}, 
						100
						);
					});
				});
		} else {
			$.post( obj.getAttribute('href'),  {}, function( data ) {
				var target = $('.modal-frame', self.layer);
				
				target.html(data);
				self.timer = setTimeout(function() {
					img.width = self.options.width =='auto'? target.width() : self.options.width;
					img.height=target.height();

					self.layer.css({'width' : img.width+20}).show();
					self.open( self._resize(img, true) );
				},
				500);
			});
		}
	},
	_imagebox : function( obj ) {
		var self = this,
			img = $('<img />', {'src' : obj.getAttribute('src'), 'alt' :  obj.getAttribute('alt') || self.options.title }),
			frame = $('.modal-frame', self.layer);

		self.status.open = true;
		self.status.mode = 'img';

		self.bg.fadeIn('slow');
		frame.hide();

		img
			.off('load')
			.on( 'load', function(){
				self.open( self._resize(this, true) );
			});

		$('.modal-frame', self.layer).html(img);
		$('.modal-title', self.layer).html(img.attr('alt') + self._panel(obj));

	},
	_resize : function( obj, isnew ){
		var width = isnew == true ? obj.width : this.selector_size.width,
			height = isnew == true ? obj.height : this.selector_size.height,
			maxWidth = $.window.width(),
			maxHeight = $.window.height(),
			layer = this.layer,
			resize = {width : 0, height : 0, start : {}, end : {}},
			margin = 50;

		if( height <= 0 ) {
			height = 150;
		}

		resize.width = width > maxWidth-margin ? maxWidth-margin : width;
		if( resize.width < 150 ){
			resize.width = 150;
		}

		resize.height = Math.round(height-height*0.01*Math.round((width-resize.width) / (width*0.01)));
		if( resize.height > (maxHeight-margin-100) && this.status.mode == 'img' ) {
			resize.height = (maxHeight-margin-100);
			resize.width = Math.round(width-width*0.01*Math.round((height-resize.height) / (height*0.01)));
		}

		resize.start.width = layer.width();
		resize.start.height = layer.height();
		resize.start.left = ((maxWidth - layer.width()) / 2);
		resize.start.top = ((maxHeight- layer.height()) / 3) + $.window.scrollTop();

		resize.end.left = ((maxWidth - resize.width) / 2);
		resize.end.top	= ((maxHeight- resize.height) / 3) + $.window.scrollTop();
		
		if( resize.end.top < 50 ) {
			resize.end.top = 50;
		}

		if( isnew == true ) {
			this.selector=obj;
			this.selector_size.width= width;
			this.selector_size.height= height;
		}

		return resize;
	},
	_panel : function( obj ) {
		var text = [],
			size = this.obj.length,
			index = this.obj.index(obj)+1;
		
		text.push('<span class="modal-panel-close"><a href="#" class="close-button"><i class="xi-close-circle bg-radius"></i></a></span>');

		if( size > 1 ){
			text.push('<span class="modal-panel">');
			if( index > 1 ){
				text.push('<i class="fas fa-chevron-circle-left prev-button"></i>');
			}

			text.push(' <span style="padding:0px 4px"> file of ' + size+ ' / ' + index + '</span> ');

			if( index < size ){
				text.push('<i class="fas fa-chevron-circle-right next-button"></i>');
			}
			text.push('</span>');
		}

		this.status.index=index-1;

		return text.join('');
	},
	open : function( size, iframemode, callback ) {
		var self = this;
		
		$.static.modal = self;

		$('.modal-title', self.layer).hide();
		self.layer.css( {'left' : size.end.left, 'top' : size.end.top, 'min-width' : size.width, 'min-height' : size.height, 'max-width' : size.width} )
			.animate( {'opacity' : 'show'}, 'normal', function() {
				$(this).animate( {'top' : size.end.top} );

				if( iframemode == true ){
					$('.modal-iframe', self.layer).height(size.height)
						.fadeIn('fast', function(){
							if( typeof callback == 'function' ) {
								callback();
							}
						});
				}else{
					$('.modal-frame', self.layer).fadeIn('fast');
				}

				self.layer.focus();
				$('.modal-title', self.layer).show();
				self.progress.hide();
			});
		
		if( $.window.width() < size.start.width ) {
			var size = self._resize( self.selector );
			self.layer.css( {'left' : size.end.left, 'top' : size.end.top, 'min-width' : size.width, 'min-height' : size.height, 'max-width' : size.width} );
		}
	},
	end : function() {
		this.bg.stop().fadeOut();
		this.layer.stop().fadeOut();
		this.progress.show();
		this.status.open = false;
		this.obj.focus();

		if(typeof this.options.onclose == 'function') {
			this.options.onclose();
			if( this.options.destory == true ) {
				this.options.onclose = null;
			}
		}

		try {
			clearInterval(this.timer);
			this.timer = null;
		}
		catch (e) {
		}
	},
	change : function(index) {
		this.layer.stop().hide();
		this.progress.show();
		this.status.open=false;

		if(typeof this.timer != 'undefined' ){
			clearInterval(this.timer);
			this.timer = null;
		}

		if( typeof index == 'string' ) {
			$('.modal-iframe', this.layer).attr('src', index)
		}else {
			this.obj.eq(index).trigger('click');
		}
	}
});

/**
 * jquery print
 */
$.module['print'] = function( obj, options ) {
	$.extend( this, {
		obj : obj,
		iframe : $('.plani-print-iframe'),
		options : $.extend({ 
				'layer' : 'body',
				'javascript' : false,
				'style' : true
			}, 
			options
		)
	});

	this.__construct();
}

$.extend( $.module['print'].prototype, {
	__construct : function() {
		var self = this;

		if( self.iframe.length < 1 ){
			self.iframe = $('<iframe src="about:blank" class="plani-print-iframe">').insertBefore('body');
		}

		self.obj
			.on( 'click', function(){
				self.area_print();
			});
	},
	area_print : function(){
		var self = this,
			head_clone = $('head').clone(),
			body_clone = $(self.options.layer),
			doc = [];

		if( self.options.javascript == false ){
			head_clone.find('script').remove();
			body_clone.find('script').remove();
		}

		if( self.options.style == false ){
			head_clone.find('link, style').remove();
			body_clone.find('link, style').remove();
		}

		doc.push('<!doctype html>');
		doc.push('<html lang="ko-KR">');
		doc.push('<head>');
		doc.push(head_clone.html());
		doc.push('</head>');
		doc.push('<body>');
		doc.push(body_clone.html());
		doc.push('</body>');
		doc.push('</html>');

		var framebody = self.iframe.get(0).contentWindow.document;
		with(framebody) {
			designMode='on';
			open();
			writeln(doc.join(''));
			close();
		}

		self.iframe
			.one('load', function(){
				framebody.body.focus();
				framebody.execCommand('print', 0, 0);

				try {
					$.static.modal.end();
				} catch (e) {

				}
			});
	}
});

/**
 * jquery nested sortable
 */
$.module['nested.sortable'] = function( form, options ) {
	$.extend( this, {
		form : form,
		ol : $('ol:eq(0)', form),
		begin : false,
		removesData : [],
		not_visible_remove : true,
		options : $.extend({	
			'title' : '',
			'obj' : {
				'group' : $('button:eq(0)', form),
				'message' : $('.message', form)
			},
			'item-level' : 3,
			'item-name' : '그룹메뉴',
			'item-sub-name' : '서브메뉴',
			'item-id' : 0,
			'isAllowed' : function(){
				return true;
			},
			'axis' : 'xy',
			'submit_jgrow' : true,
			'submit_success' : null,
			'event' : { 'click' : null }
			},
			options
		)
	});

	this.__construct();
}

$.extend( $.module['nested.sortable'].prototype, {
	__construct : function() {
		var self = this;

		self.form.addClass('plani-nested-wrap');

		if( self.options.title != '' ) {
			self.form.prepend('<h2 class="title">' +self.options.title+ '</h2>');
		}

		new $.module['form'] ( self.form, {
			before : function( module ) {
				self._serialize();
				return true;
			},
			success : function( module, result ) {
				self.options.obj.message.hide();
				module._disposal(false);

				if( typeof self.options.submit_success == 'function' ){
					self.options.submit_success(module);
				}
			},
			jgrow : self.options.submit_jgrow
		});

		self.options.obj.group
			.on( 'click', function() {
				self._import_group();
			});

		self.ol
			.delegate( 'div', 'mouseenter', function(){
				if( $(this).hasClass('not-remove') == false ) {
					$('<i class="fa fa-times tmp-button" style="right:14px"></i>').appendTo(this)
						.on( 'click', function(){
							self._remove( this );
						});
				}

				if( $(this).parents('ol').length < self.options['item-level'] ) {
					$('<i class="fa fa-plus tmp-button" style="right:34px"></i>').appendTo(this)
						.on( 'click', function(){
							self._import( this );
						});
				}
			})
			.delegate( 'div', 'mouseleave', function(){
				$('.tmp-button', this).remove();
			})
			.delegate( 'div', 'click', function( e ) {
				if( e.target == this || $(e.target).hasClass('tmp-button') == false ){
					self._hook( 'click', this );
				}
			})
			.delegate( 'div', 'dblclick', function( e ) {
				if( e.target.tagName.toLowerCase() == 'span' ) {
					var target = $('span', this);

					$(this).find('.tmp-button').remove();
					target.html('<input type="text" value="' +target.text().trim()+ '"/>')
						.find('input')
						.focus()
						.one( 'focusout', function(){
							var value=$(this).val().replace(/[\+]/g,'＋');
							target.text(value? value : 'Empty');
						})
						.keyenter('focusout');
				}
			});

		self.ol.nestedSortable( {
			axis: self.options['axis'],
			isAllowed: self.options['isAllowed'],
			forcePlaceholderSize: true,
			handle: '.move',
			helper:	'clone',
			items: 'li',
			opacity: .8,
			placeholder: 'placeholder',
			revert: 250,
			tabSize: 25,
			tolerance: 'pointer',
			toleranceElement: '> div',
			maxLevels: self.options['item-level'],
			isTree: true,
			expandOnHover: 700,
			startCollapsed: true,
			complate : function(){
				self._depth();
			}
		});

		self._depth();
	},
	_depth : function(){
		var self = this,
			target = $('li', self.obj);

		setTimeout(function() {
			if( self.not_visible_remove == true ) {
				$('li:not(:visible)', self.ol).remove();
			}

			target
				.each(function(){
					var icon = $(this).has('li').length > 0? 'fa-folder-open' : 'fa-folder',
						div = $('div:eq(0)', this);

					if( $('.move', div).length  < 1 ) {
						div.html('<i class="far fa-eye-slash"></i><i class="far ' +icon+ ' move"></i><span>' +div.text()+ '</span>');
					} else {
						$('.move', div).attr('class', 'far ' +icon+ ' move');
					}
				});

			if( self.begin == true ) {
				self.options.obj.message.show();
			}

			if( self.begin == false ) {
				self.begin=true;
			}
		}, 
		300);
	},
	_import_group : function() {
		var self = this,
			target = self.ol,
			newid = self._latest();

		target.append('<li id="list_' +newid+ '"><div class="new">' +self.options['item-name']+ '</div></li>');
		
		self._depth();	
	},
	_import : function( obj ) {
		var self = this,
			target = $(obj).closest('li'),
			newid = self._latest();

		
		if( target.is(':has(> ol)') == false ){
			target = $('<ol />').appendTo(target);
		}else{
			target = target.find('> ol');
		}

		target.append('<li id="list_' +newid+ '"><div class="new">' +self.options['item-sub-name']+ '</div></li>');
		
		self._depth();	
	},
	_latest : function(){
		var no = parseInt(this.options['item-id']);
		$('li', this.obj)
			.each(
			function(){
				var id = parseInt(this.id.replace('list_', ''));
				if( id > no ) {
					no = id;
				}
			});

		return no+1;
	},
	_remove : function( obj ) {
		var self = this;

		$(obj).closest('li')
			.slideUp(
			function(){
				self.removesData.push(this.id.replace('list_', ''));

				$(this).find('li')
					.each(function(){
						if( typeof this.id != 'undefined' ){
							self.removesData.push(this.id.replace('list_', ''));
						}
					});

				$(this).remove();
				self._depth();
			});
	},
	_serialize : function(){
		var self = this,
			serialized = self.ol.nestedSortable('serialize').split('&'),
			hash = [];

		$('.postdata', self.form).remove();

		postdata = $('<div />', {'class' : 'postdata'}).appendTo(self.form);

		for(var i=0; i< serialized.length; i++){
			if( serialized[i] == '' ){
				continue;
			}

			var item = serialized[i].replace('list[', '').split(']='),
				id = item[0],
				pid = item[1]=='null'? 0 : item[1],
				text = $('#list_' + id + ' > div', self.ol).text().trim(),
				sort = 1,
				str =''

				if( typeof hash[pid] == 'undefined'){
					hash[pid]=1;
				}else{
					hash[pid]+=1;
				}

				sort = hash[pid];
				postdata.append($('<input />', {'type' : 'hidden', 'name' : 'sitemap['+id+'][id]', 'value' : id}));
				postdata.append($('<input />', {'type' : 'hidden', 'name' : 'sitemap['+id+'][pid]', 'value' : pid}));
				postdata.append($('<input />', {'type' : 'hidden', 'name' : 'sitemap['+id+'][text]', 'value' : text}));
				postdata.append($('<input />', {'type' : 'hidden', 'name' : 'sitemap['+id+'][sort]', 'value' : sort}));
		}

		for(var i=0; i< self.removesData.length; i++) {
			postdata.append($('<input />', {'type' : 'hidden','name' : 'sitemap_remove[]', 'value' : self.removesData[i]}));
		}
	},
	_hook : function( action, target ) {
		if( typeof this.options.event[action]  == 'function' ) {
			this.options.event[action](target);
		}
	}
});

/**
 * tree nodes
 */
$.module['nested.tree'] = function( obj, options ) {
	$.extend( this, {
		obj : obj,
		ul : $('ul', obj),
		options : $.extend({	
			'title' : 'nodes',
			'open' : false
			},
			options
		)
	});

	this.__construct();
}
$.extend( $.module['nested.tree'].prototype, {
	__construct : function() {
		var self = this;

		self.obj.addClass('plani-tree-wrap');

		if( self.options.title != '' ) {
			self.obj.prepend('<h2 class="title">' +self.options.title+ '</h2>');
		}

		self.ul
			.each(function(){
				var li = $(this).find('>li');

				li.eq(-1)
					.addClass('joinend')
					.end()
					.each(function(){
						$(this).prepend( $(this).is(':has(ul)') == true ? '<span class="plus button"></span><span class="folder"></span>' : '<span class="page"></span>' );
					});
			});

		$('li:has(ul) .button', self.ul)
			.on(
				'click', self._open
			);

		if( self.options.open == true ) {
			$('li:has(ul) .button', self.ul).trigger('click');
		}
	},
	_open : function( e ) {
		var target = $(this).closest('li'), 
			ul = target.find('>ul'), 
			span = target.find('span'), 
			hide = ul.is(':hidden');

		if( hide == true ){
			ul.show();
		}else{
			ul.hide();		
		}

		span.eq(0)
			.attr('class', hide? 'minus' : 'plus')
			.end()
			.eq(1)
			.attr('class', hide? 'folder-open' : 'folder');
	}
});

/**
 * animate flip
 */
$.module['animate.flip'] = function( obj, options ) {
	$.extend( this, {
		obj : obj,
		btn : {
			'flip' : $('.flip', obj),
			'unflip' : $('.unflip', obj)
		},
		layer : {
			'front' : $('.front', obj),
			'back' : $('.back', obj)
		},
		queue : true,
		options : $.extend({
			trigger : 'menual',
			reverse: true,
			axis: 'y'
			}, 
			options
		)
	});

	this.__construct();
}

$.extend( $.module['animate.flip'].prototype, {
	__construct : function() {
		var self = this

		self.obj.addClass('plani-flip')
			.flip(self.options);

		self.btn.flip
			.on( 'click', function(){
				self._flip();
			});

		self.btn.unflip
			.on( 'click', function(){
				self._unflip();
			});

		setInterval(function() {
			var layer = self.queue == true ? self.layer.front : self.layer.back;
			self.obj.height(layer.height())
		},
		100
		);

		self._unflip();
	},
	_flip : function(){
		this.queue = false;
		this.obj.flip(true);
		this.layer.back.css('opacity', '1');
		this.layer.front.css('opacity', '0');
	},
	_unflip : function(){
		this.queue = true;
		this.obj.flip(false);
		this.layer.front.css('opacity', '1');
		this.layer.back.css('opacity', '0');
	}
});

/**
 * animate tabs
 */
$.module['animate.tabs'] = function( obj, options ) {
	$.extend( this, {
		obj : obj,
		btn : $(' > ol > li', obj),
		layer : $(' > div', obj),
		queue : true,
		options : $.extend({
			start : '0',
			prefix : '<i class="fa fa-angle-right" aria-hidden="true"></i>'
			}, 
			options
		)
	});

	this.__construct();
}

$.extend( $.module['animate.tabs'].prototype, {
	__construct : function() {
		var self = this;

		self.btn.prepend(self.options.prefix);
		self.btn.attr('tabindex', 0)
			.on( 'click', function( e ){
				var no = self.btn.index(this);
				self.btn.removeClass('on').eq(no).addClass('on');
				self.layer.hide().eq(no).fadeIn('fast');
			})
			.keyenter('click')
			.eq(self.options.start)
			.trigger('click');
	}
});

/**
 * animate scroll
 */
$.module['animate.scroll'] = function( obj, options ) {
	$.extend( this, {
		obj : obj
	});

	//bug
	var self = this;
	$.window
		.on( 'load', function() {
			self.fix = self.obj.position().top;
			self.__construct();	
		});
}

$.extend( $.module['animate.scroll'].prototype, {
	__construct : function() {
		var self = this;

		$.window
			.on('scroll', function(){
				var top = $(document).scrollTop(),
					move = top > self.fix ? (top-self.fix) : 0;

				if( $.window.height() > (self.obj.height()+100) || move == 0 ) {
					self.obj.stop()
						.animate( { 'top' : move } );
				}
			})
			.on('resize', function(){
				self.obj.css('top', 0);
				$.window.trigger('scroll');
			});

		setTimeout(function(){
			$.window.trigger('scroll');
		},
		100);
	}
});


/**
 * board lists
 */
$.module['board.lists'] = function( form, options ){
	$.extend( this, {
		form : form,
		layer : null,
		pos_left : 0,
		options : $.extend( {
				'layer' : '.request',
				'grid' : false,
				'autoload' : false,
				'callback' : function(){}
			}, 
			options
		),
		grid_options : {
			'able' : false,
			'paging' : null,
			'col_width' : [],
			'min_height' : 200
		}	
	});

	this.grid_options.able = this.options.grid;
	this.layer = $(this.options.layer, form);

	this.__construct();
}

$.extend( $.module['board.lists'].prototype, {
	__construct : function() {
		var self = this;

		self.layer.addClass('plani-board-lists');
		new $.module['form']( self.form, {
			'ajax' : false,
			'before' : function(){
				self.request();
				return false;
			}
		});

		self.form
			.delegate('.paging-area a', 'click', function(){
				if( $(this).attr('href') != '#' ){
					self.request( $(this).attr('href') );
				}
				return false;
			});

		self[self.options.autoload == true ? 'request' : '_trigger']();
	},
	request : function( url ) {
		var self = this;

		if( !url ){
			url = self.form.attr('action');
		}

		try {
			var scrollbar = $('.mCustomScrollbar'),
				dragger = scrollbar.find('.mCSB_dragger').eq(1),
				dragger_left = dragger.position().left,
				scroll_width =  scrollbar.find('.mCSB_container').width()

			self.pos_left = (dragger_left / (scrollbar.width() - dragger.width()) * (scroll_width - scrollbar.width()));
		} catch (e) {
		}

		self.layer.append('<div class="progress"><i class="xi-spinner-1 xi-spin"></i></div>');

		$.request( { 'url' : url, 'param' : self.form.serialize() }, function( body ) {

			var $html = $(body);

			history.pushState({}, '', $html.find('input[name=history]').val());
			if( self.grid_options.able == true ) {
				self._grid_reset();
			}

			$html.filter(':not(' +self.options.layer+ ')').find('[update-id]').each(function(){
				var obj1 = $(this),
					obj2 = $('[update-id=' +obj1.attr('update-id')+ ']', self.form);

				if( obj2.length > 0 ){
					obj2.after(obj1).remove();
				}
			});

			self.layer.html($html.find(self.options.layer).html());

			self._trigger();
		});
	},
	_trigger : function(){
		var self = this,
			order_by = $('[name=search_order]', self.form).val(),
			sort = typeof order_by != 'undefined' ? order_by.split(' ') : ['', 'asc'],
			th = $('th[sort]', self.form);

		if( typeof self.options.callback == 'function' ) {
			this.options.callback(self.form);
		}

		th
		.each(function(){
			var target = $(this);
			target.append('<span class="sort"></span>').addClass('cursor-hand');

			if( target.attr('sort') == sort[0] ){
				$('span', target).attr('class', 'sort ' + (sort.length < 2 ? 'asc' : sort[1]) );
			}

			target
				.on('click', function(){
					var span = $('span', target),
						order_by = span.hasClass('asc') == true? 'desc' : 'asc',
						val = target.attr('sort') + ' ' + order_by;

					$('[name=search_order]', self.form).val(val);

					self.form.trigger('submit');
				});
		});

		if( self.grid_options.able == true ) {
			self._grid_set();
		}
	},
	_grid_set : function() {
		this.layer.addClass('plani-grid');

		if( this.grid_options.paging == null ){
			this.grid_options.paging = $('<div />', {'class' : 'plani-grid-paging'});
			this.layer.after(this.grid_options.paging);
		}

		this.grid_options.paging.html(this.layer.children().not('table'));

		this._grid();
	},
	_grid_reset : function() {
		this.layer.removeClass('plani-grid').mCustomScrollbar("destroy");
	},
	_grid_scroll : function() {
		var top = parseInt($('.mCSB_container', this.layer).css('top'))*-1;

		$('.col-resize', this.layer).css('top', top);

		try {
			$('tfoot', this.layer).css('top', (top + this.layer.height() - $('tfoot', this.layer).height())-2);
		}catch (e) {
		}
	},
	_grid_resize : function( cols, base, extend ) {
		var self = this,
			table_width = $('table', self.layer).width(),
			base_col_size = base.length;

		base
		.each(function( no ){
			var w = $(this).width();

			if( extend == true ){
				if( typeof self.grid_options.col_width[no] != 'undefined' ){
					w = self.grid_options.col_width[no];
				}
				if( w < 60 ) {
					w = 60;
				}
			}

			if( base_col_size == (no+1) ) {
				w = table_width;
			}else {
				table_width-= w;
			}

			for( var key in cols ) {
				var col = cols[key];
				try {
					col.eq(no).width(w);
				}catch (e) {
				}
			}

			self.grid_options.col_width[no] = w;
		});	
	},
	_grid_resize_overap : function( overap, base ) {
		var self = this;

		for( var i=0; i < overap.length; i++ ) {
			var target= overap[i],
				w = 0,
				l = 0

			for( var x=target.start; x<=target.end; x++ ) {
				w+=base.eq(x).outerWidth();

				if( x == target.start ){
					l =	base.eq(x).position().left;
				}
			}

			target.obj.css({'left': l+($.info.isie == true ? -1 : 0), 'width' : w+1});
		}
	},
	_grid : function() {
		var self = this,
			_table = $('table', self.layer),
			_thead = $('thead', _table), 
			_tbody = $('tbody', _table), 
			_tfoot = $('tfoot', _table), 
			_trows = $('trows name', self.form), 
			_trows_cols = [],
			_ths = $('tr:eq(0) th', _thead), 
			_tds = $('tr:eq(0) td', _tbody),
			_tfoot_tds = $('td', _tfoot),
			_col_size = _ths.length

		if( _tds.length > 0 ) {
			_tbody.append('<tr><td colspan="' +_col_size+ '" class="end-tr">&nbsp;</td></tr>');
		}

		if( _trows.length > 0 ) {
			var eq_no = 0;
			_trows
				.each(function(){
					var colspan = parseInt($(this).attr('colspan')),
						text = $(this).attr('value'),
						bg = $(this).attr('bg')

					if(  colspan > 1 ) {
						_ths.eq(eq_no).prepend('<div class="grp">'+text+'</div>');
						for(var i=1; i<colspan; i++ ){
							_ths.eq(eq_no+i).prepend('<div class="grp">&nbsp;</div>')
						}
						_trows_cols.push({'obj' : $('<div />', {'class' : 'grp-overap'}).html(text).css('background', bg) , 'start' : eq_no, 'end' : eq_no+(i-1)});
					}else {
						_ths.eq(eq_no).prepend('<div class="grp-text">&nbsp;</div>');						
					}
					eq_no+=colspan;
				});
		}

		self._grid_resize( [_ths], _ths, true );

		self.layer.mCustomScrollbar({
			theme : 'minimal-dark', //rounded-dark
			axis:"xy",
			scrollButtons:{
			  enable : true
			},
			setLeft : (self.pos_left + ((self.pos_left / 500)*10)) + 'px',
			callbacks:{
				 onInit : function(){
					self._grid_scroll();
				 },
				 whileScrolling : function(){
					 self._grid_resize(
						[_ths, _tfoot_tds], _ths, true	 
					 );
					 self._grid_scroll();
				}
			}
		});
		self.pos_left = 0;

		$.window
			.off('resize.grid')
			.on('resize.grid', function() {
				$('.col-resize', _table).remove();

				var clone = _thead.clone().height(_thead.height()+1).addClass('col-resize'),
					clone_th = $('th', clone).height(_thead.height()+1);

				clone_th.each
				(
					function(no){
						$(this)
						.on
						(
							'click', function(){
								_ths.eq(no).trigger('click');
							}
						);
					}
				);

				for( var i=0; i < _trows_cols.length; i++ )
				{
					clone.append(_trows_cols[i].obj);
				}

				_table.prepend(clone);


				var layer_height = $(this).height() - self.layer.offset().top - self.grid_options.min_height;
				if( layer_height > _table.height()+50 ) {
					layer_height = _table.height()+50;
				}

				if( layer_height < self.grid_options.min_height ) {
					layer_height = self.grid_options.min_height;
				}

				self.layer.height(layer_height);

				_table.removeAttr('id').colResizable({
					'minWidth' : 60,
					'onResize' : function(e){
						self._grid_resize_overap( _trows_cols, clone_th );
						self._grid_resize( [_ths,  _tfoot_tds], clone_th, false );
					}
				});

				self._grid_resize( [_ths,  _tfoot_tds], clone_th, false );
				self._grid_resize( [_ths,  _tfoot_tds], clone_th, true );
				self._grid_resize_overap( _trows_cols, _ths );
				self._grid_scroll();

			});

		setTimeout(function(){
				$.window.trigger('resize.grid');
			}, 
			500);
	}
});

/**
 * board masonry
 */
$.module['board.masonry'] = function( form, options ) {
	$.extend( this, {
		form : form,
		params : { num : 0, width : 0, margin : 20 },
		layer : null,
		items : null,
		pageing : null,
		progress : null,
		empty : null,
		options : $.extend({
				'layer' : '.request',
				'paging' : '.paging-area',
				'items' : '.masonry_item',
				'callback' : function(){ }
			}, 
			options
		)
	});

	this.layer = $(this.options.layer, form);
	this.items = $(this.options.items, this.layer);
	this.pageing = $(this.options.paging, form).hide();
	this.progress = $('<div />', {'class' : 'progress' }).html('<i class="xi-spinner-1 xi-spin"></i>').appendTo(this.layer);
	this.empty = $('<div />', {'class' : 'empty-data' }).html(__('검색 결과가 없습니다')).appendTo(this.layer);

	this.__construct();
}


$.extend( $.module['board.masonry'].prototype, {
	__construct : function(){
		var self = this;

		self.layer.addClass('plani-board-masonry');
		self.form
			.delegate( '.paging-area a', 'click', function(){
				if( $(this).attr('href') != '#' ){
					self.request( $(this).attr('href') );
				}
				return false;
			});

		new $.module['form']( self.form, {
			'ajax' : false,
			'before' : function(){
				self.request();
				return false;
			}
		});

		setTimeout(function(){
			self.progress.remove();
			self.pageing.show();
			self.resizer( self.layer.width(), 'fadein' );
			$(this)
				.on( 'resize', function(){
					if( typeof this.resizedFinished != 'undefined' ){
						clearTimeout(this.resizedFinished);
					}

					this.resizedFinished = setTimeout(function(){
							self.resizer( self.layer.width(), 'sort' );
						}, 
						200);
				});
		}, 300);
	},
	resizer : function( width, method ){
		var self = this,
			num = 6

		if( width < 600)
			num = 1;
		else if( width <= 800)
			num = 2;
		else if( width <= 1100 )
			num = 3;
		else if( width <= 1400 )
			num = 4;
		else if( width <= 1700 )
			num = 5;

		self.params.num = num;
		self.params.width = width;
		self.items = $(self.options.items, this.form);
		
		setTimeout
		(
			function(){
				self.netsort(method);
			}, 100
		)
	},
	netsort : function(method){
		var self = this,
			positioning = self._position( method ),
			original_method = method,
			myno = 0;

		self.items
			.each(function(no){
				var target = $(this),
					item_pos = positioning[myno];

				if( original_method == 'reload' ) {
					if(typeof target.attr('data-load') != 'undefined' ) {
						myno++;
						method = target.attr('data-load');
					}else {
						method = 'fadeout';
					}
				}else {
					myno++;
				}

				target.stop();

				if( method == 'fadein') {
					target.css( {'left' : item_pos.end_left, 'top' : item_pos.end_top, 'transform' : 'scale(0.5, 0.5)', 'opacity' : 0} )
						.transition( {'opacity' : 1, 'transform' : 'scale(1.0, 1.0)', 'duration' : 800} );

				} else if( method == 'fadeout' ) {
					target
						.transition( {'opacity' : 0, 'transform' : 'scale(0.5, 0.5)', 'duration' : 800 }, function() {
							target.remove();
						});

				} else {
					target
						.animate( {'left' : item_pos.end_left, 'top' : item_pos.end_top, 'opacity' : 1}, {duration : 500 + (no*50)}, 'slow', 'easeOutCirc' );
				}
			});
	},
	request : function(url){
		var self = this;

		if( !url ){
			url = self.form.attr('action');
		}

		$.request( {'url' : url, 'param' : self.form.serialize() }, function( html ){
			self._parse(html);
		});
	},
	_parse : function( html ){
		var self = this,
			text_obj = $(html),
			new_items = $(self.options.items, text_obj).clone(),
			paging = $(self.options.paging, text_obj).clone(),
			sorts = [];

		self.items = $(self.options.items, self.layer);
		self.items.removeAttr('data-load');

		new_items
			.each(function(no){
				var new_target = $(this),
					new_target_data = new_target.data(),
					old_target = self.items.filter('[data-id=' +new_target_data.id+ ']');

				old_target.find('a').attr('href', new_target_data.href);
				
				if( self.items.eq(no).length == 0 ) {
					if( old_target.length < 1 ) {
						sorts[no]=new_target.attr('data-load', 'fadein');
					} else {
						sorts[no]=old_target.attr('data-load', 'sort');
					}
				} else {
					if( old_target.length < 1 ) {
						sorts[no]=new_target.attr('data-load', 'fadein');
					} else {
						sorts[no]=old_target.attr('data-load', 'sort');
					}
				}
			});

		for(var i=0; i<sorts.length; i++) {
			self.layer.append(sorts[i]);
		}

		if( sorts.length == 0 ) {
			self.empty.show();
			self.pageing.html('');
		} else {
			self.empty.hide();
			self.pageing.html(paging.html());
		}

		self.resizer( self.layer.width(), 'reload' );
	},
	_position : function( method ){
		var self = this,
			$params = self.params,
			$return = {},
			$items = method == 'reload'? self.items.filter('[data-load]') : self.items,
			item_width = ($params.width - (($params.num+1)*$params.margin)) / $params.num,
			latest_height = [];


		$items.stop()
			.width(item_width)
			.removeAttr('data-no')
			.each(function( no ){
				var item_sort_num = no==0? 0 : no % $params.num,
					item_sort_top_target = $items.filter('[data-no=' +item_sort_num+ ']'),
					item_left = ($params.margin / 2) + (item_sort_num * $params.margin) + (item_width * item_sort_num),
					item_top = item_sort_top_target.length < 1? 0 : self._top_sum(item_sort_top_target, method),
					target = $(this);


				target.attr({ 'data-no' : item_sort_num });
				$return[no] = {
					'end_width' : item_width,
					'end_height' : target.height(),
					'end_left' : item_left,
					'end_top' : item_top
				}

				latest_height.push(item_top + target.height());
			});

		self.layer.stop().animate( {'height' : Math.max.apply(null, latest_height) + 50} );

		return $return;
	},
	_top_sum : function( item_sort_top_target ){
		var h=0;

		item_sort_top_target
			.each(function(){
				h+=$(this).height();
			});		

		return h + (this.params.margin * item_sort_top_target.length);
	}
});