/*
 * pettanR work.js
 *   version 0.3.0
 *   
 * author:
 *   itozyun
 * licence:
 *   new BSD
 *
 * 
 * ----------------------------------------
 * - naming of comic's elements -
 * 
 *  0.comic strip(en) : koma manga(jp)
 *
 *  1.panel(en) : koma(jp)
 *    +------------+
 *    |            |
 *    |            |
 *    |            |
 *    |            |
 *    +------------+
 *  
 *  2.speach balloon(en) : fukidasi(jp)
 *      ________
 *     /        \
 *     |         |
 *    <  Hello!! |
 *     |         |
 *     \________/
 *   
 *  3.img
 * 
 * 
 * ----------------------------------------
 * window
 * +----------------------------+
 * |      CANVAS_CONTROL        |  
 * |       - panel              |
 * |        +----------+        | 
 * |        | +----------+      | 
 * |        | |          |      | 
 * |        | |          |      | 
 * |        +-|          |      |
 * |          +----------+      | 
 * |        - WHITE_GLASS_CONTROL | 
 * +----------------------------+
 * 
 * ----------------------------------------
 * naming rules
 * 
 *  Class
 *    ThisIsClass
 *  
 *  const & Singleton Class
 *    THIS_IS_CONST = 'this is const';
 *  
 *  var
 *    thisIsVar
 *   
 *  value of jquery
 *    jqWrapper, JQ_WRAPPER
 *  
 *  value of dom element
 *    elmWrapper, ELM_WRAP
 * 
 * 	value of vml element
 *    vmkImg, VML_SHAPE
 * 
 */

var h2c = ( function(){
	var	FUNCTION_ARRAY = [],
		URL = document.location.href.split( '#')[ 0],
		IS_LOCAL = URL.indexOf( 'file:') === 0,
		URL_PARAMS = ( function(){
			var search = document.location.search,
				l = search.length;
		    if( 1 < l){
		        var	query = search.substring( 1),
					params = query.split( '&'),
					ret = {}, elm, name;
		        while( params.length > 0){
		            elm = params.shift().split( '=');
					name = decodeURIComponent( elm[ 0]);
					if( elm.length === 2){
			            ret[ name] = ( function( v){
							if( '' + parseFloat( v) === v) return parseFloat( v);
							if( v === 'true') return true;
							if( v === 'false') return false;
							if( v === 'null') return null;
							if( v === 'undefined') return undefined;
							return v;
						})( decodeURIComponent( elm[ 1]));
					} else
					if( elm.length === 1){
						ret[ name] = true;
					}
		        }
		        return ret;
		    }
		    return {};
		})(),
		IS_DEBUG = IS_LOCAL === true || URL_PARAMS.debug === true,
		isIE = navigator.userAgent.toLowerCase().indexOf( 'msie') !== -1,
		ieVersion = isIE === true ? parseInt( navigator.appVersion.toLowerCase().replace( /.*msie[ ]/, '').match( /^[0-9]+/)) : 0,
		ieMode = ieVersion === 8 ? document.documentMode : 0,
		isStanderdMode = document.compatMode === 'CSS1Compat',
		jqWindow , jqDocument , jqBody;
	return {
		version: '0.3.0',
		init: function(){
			jqWindow = $( window);
			jqDocument = $( document);
			jqBody = $( document.body);
			
			var l = FUNCTION_ARRAY.length,
				_fn;
			for( var i=0; i<l; i++){
				_fn = FUNCTION_ARRAY[ i];
				_fn.init && _fn.init( _fn === h2c.view ? FUNCTION_ARRAY : undefined);
			}
			
			delete h2c.init;
		},
		fn: function( _fn){
			FUNCTION_ARRAY.push( _fn);
			h2c.init === undefined && _fn.init && _fn.init();
		},
		isIE: isIE,
		ieVersion: ieVersion,
		ieRenderingVersion: ieMode !== 0 ? ieMode : ieVersion,
		isStanderdMode: isStanderdMode,
		VENDER_PREFIX: ( function() {
			var ua = navigator.userAgent.toLowerCase();
			if ( ua.indexOf('opera') !== -1) {
				return 'O';
			} else if ( ua.indexOf('msie') !== -1) {
				return 'ms';
			} else if ( ua.indexOf('webkit') !== -1) {
				return 'webkit';
			} else if ( navigator.product === 'Gecko') {
				return 'Moz';
			}
			return '';
		})(),
		ACTIVEX: ( function(){
			if( isIE === false || ieVersion > 8) return false;
			var className = document.body.className,
				test;
			if( className && className.indexOf( 'h2c-ActiveX-enabled') !== -1) return true;
			if( className && className.indexOf( 'h2c-ActiveX-disabled') !== -1) return false;
			try {
				test = new ActiveXObject('DXImageTransform.Microsoft.gradient');
			} catch( e){
				return false;
			}
			return !!test;
		})(),
		VML: ( function(){
			if( isIE === false || ieVersion > 8) return false;
			var globalObjectName = '_global',
				script;
			document.write( [ '<!--[if gte vml 1]><script>window', globalObjectName, '=1;<\/script><![endif]-->'].join( ''));
			if( window[globalObjectName] === 1){
				//script = document.getElementById( globalObjectName);
				//script.parentNode.removeChild( script);
				window[globalObjectName] = undefined;
				return true;
			}
			return false;
		})(),
		startVML: function(){
			if( h2c.VML !== true) return false;
			if (!document.namespaces["v"]) {
		        document.namespaces.add("v", "urn:schemas-microsoft-com:vml", "#default#VML");
		    }
		    document.createStyleSheet().cssText = "v\:shape,v\:image{behavior:url(#default#VML);display:block;};";
		    delete this.startVML;
		},
		jqWindow: function(){
			return jqWindow;
		},
		jqDocument: function(){
			return jqDocument;
		},
		jqBody: function(){
			return jqBody;
		},
		URL_PARAMS: URL_PARAMS,
		LOCAL: IS_LOCAL,
		DEBUG: IS_DEBUG,
		LINE_FEED_CODE_TEXTAREA : ( function(){
			var text = document.getElementById( 'shadowTxtarea'),
				form = text.parentNode;
			form.parentNode.removeChild( form);
			return text.value;
		})(),
		LINE_FEED_CODE_PRE : ( function(){
			var pre = document.getElementById( 'shadowPre');
			pre.parentNode.removeChild( pre);
			return isIE === true ? this.LINE_FEED_CODE_TEXTAREA : pre.innerHTML; // ie ??
		})()
	}
})();

/*
 * h2c.util
 * 
 * getElementSize( _elm)
 * getImageSize()
 * 
 */
h2c.util = ( function(){
	var ELM_SIZE_GETTER = ( function(){
			var ret = document.createElement( 'DIV'),
				style = ret.style;
			ret.id = 'elmSizeGetter';
			style.position = 'absolute';
			style.left = '0px';
			style.top = '-9999px';
			style.visibility = 'hidden';
			document.body.appendChild( ret);
			return ret;
		})(),
		IMG_SIZE_GETTER = ( function(){
			var ret = ELM_SIZE_GETTER.cloneNode( true);
			ret.id = 'imgSizeGetter';
			document.body.appendChild( ret);
			return ret;
		})(),
		CLEAN_TARGET_ELEMENT = 'script,style,object,applet,embed,iframe,frame,base,bgsound,frameset,listing'.split( ',');
		
	// cleanCommnetNode();
	function cleanCommnetNode( _targetElm){
		var _elms = ( _targetElm || document).getElementsByTagName( '*'),
			_elm,
			i = 0;
		while( i < _elms.length){
			_elm = _elms[ i];
			if( _elm.nodeType === 8 && _elm.parentNode){
				_elm.parentNode.removeChild( _elm);
			} else {
				++i;
			}
		}
	}
	return {
		extend: function( baseInstance, extend){
			for( var key in extend){
				baseInstance[ key] = extend[ key];
			}
			return baseInstance;
		},
		cleanCommnetNode: cleanCommnetNode,
		cleanElement: function( _targetElm){
			var l = CLEAN_TARGET_ELEMENT.length,
				elms, elm, j;
			for( var i=0; i<l; ++i){
				elms = _targetElm.getElementsByTagName( CLEAN_TARGET_ELEMENT[ i]);
				if( elms.length > 0){
					for( j=0; i<elms.length; ++j){
						elm = elms[ 0];
						elm.parentNode && elm.parentNode.removeChild( elm);
					}
				}
			}
			cleanCommnetNode( _targetElm);
			if( h2c.isIE === false) return;
			elms = document.getElementsByName( '*');
			l = elms.length;
			for(i=0; i<l; ++i){
				elm = elms[ i];
				elm.style.filter = '';
				elm.style.behavior = '';
			}
		},
		getElementSize: function( _elm){
			if( !_elm){
				return {
					width:	0,
					height:	0
				}
			}
			var	parentElm = _elm.parentNode,
				prevElm = _elm.previousSibling,
				nextElm = _elm.nextSibling,
				displayNone = _elm.style.display === 'none';
			if( displayNone === true) _elm.style.display = '';
			ELM_SIZE_GETTER.appendChild( _elm);
			var ret = {
				width:		_elm.offsetWidth,
				height:		_elm.offsetHeight
			}
			if( displayNone === true) _elm.style.display = 'none';
			if( nextElm){
				parentElm.insertBefore( _elm, nextElm);
			} else		
			if( prevElm && prevElm.nextSibling){
				parentElm.insertBefore( _elm, prevElm.nextSibling);
			} else {
				parentElm && parentElm.appendChild( _elm);
			}			
			return ret;
		},
		getImageSize: function( img){
			var	parentElm = img.parentNode,
				prevElm = img.previousSibling,
				nextElm = img.nextSibling,
				displayNone = img.style.display === 'none';
			if( displayNone === true) img.style.display = '';
			IMG_SIZE_GETTER.appendChild( img);
			var size = getActualDimension( img);
			IMG_SIZE_GETTER.removeChild( img);
			if( displayNone === true) img.style.display = 'none';
			if( nextElm){
				parentElm.insertBefore( img, nextElm);
			} else		
			if( prevElm && prevElm.nextSibling){
				parentElm.insertBefore( img, prevElm.nextSibling);
			} else {
				parentElm && parentElm.appendChild( img);
			}
			return size;
			
		/* LICENSE: MIT
		 * AUTHOR: uupaa.js@gmail.com
		 */
			function getActualDimension(image) {
				var run, mem, w, h, key = "actual";
			
			// for Firefox, Safari, Google Chrome
				if ("naturalWidth" in image) {
					return {
						width:	image.naturalWidth,
						height:	image.naturalHeight
					};
				}
			
				if ("src" in image) { // HTMLImageElement
					if (image[key] && image[key].src === image.src) {
						return image[key];
					}
					if (document.uniqueID) { // for IE
						run = image.runtimeStyle;
						mem = { w: run.width, h: run.height }; // keep runtimeStyle
						run.width  = "auto"; // override
						run.height = "auto";
						w = image.width;
						h = image.height;
						run.width  = mem.w; // restore
						run.height = mem.h;
					} else { // for Opera and Other
					/*
						function fn() {
							w = image.width;
							h = image.height;
						}
						mem = { w: image.width, h: image.height }; // keep current style
						image.removeAttribute("width");
						image.addEventListener("DOMAttrModified", fn, false);
						image.removeAttribute("height");
						// call fn
						image.removeEventListener("DOMAttrModified", fn, false);
						image.width  = mem.w; // restore
						image.height = mem.h;
					*/
						mem = { w: image.width, h: image.height }; // keep current style
						image.removeAttribute("width");
						image.removeAttribute("height");
						w = image.width;
						h = image.height;
						image.width  = mem.w; // restore
						image.height = mem.h;
					}
					return image[key] = { width: w, height: h, src: image.src }; // bond
				}
				// HTMLCanvasElement
				return { width: image.width, height: image.height };
			}
		},
		loadImage: function( URLorELM, onLoad, onError, delay, timeout){
			delay = delay || 250;
			timeout = timeout || 5000;
			var type = typeof URLorELM,
				img, images, src, abstractPath;
			if( type === 'string'){
				src = URLorELM;
				// images = [];
			} else
			// http://d.hatena.ne.jp/hottolinkblog/20090228/1235823487
			if( type === 'object' && typeof URLorELM.hspace !== 'undefined' && typeof URLorELM.vspace !== 'undefined'){
				img = URLorELM;
				images = [ img];
				src = img.src;
			} else {
				return;
			}
			abstractPath = this.getAbsolutePath( src);
			
			loadImage( images, abstractPath,
				function( abspath, actualW, actualH){
					if( abstractPath !== abspath) return;
					onLoad && setTimeout( function(){ // 一度読み込まれた画像は即座にonLoadされてしまうので遅延
						onLoad( src, actualW, actualH);
					}, 0);
				},
				function( abspath){
					if( abstractPath !== abspath) return;
					onError && setTimeout( function(){
						onError( src);
					}, 0);
				}, delay, timeout
			);
			
		/*  LICENSE: MIT?
		 *  URL: http://d.hatena.ne.jp/uupaa/20080413/1208067631
		 *  AUTHOR: uupaa.js@gmail.com
		 * 
		 *  fixed for ie6-8 by h2c
		 *   new Image -> document.createElement( 'IMG')
		 */
			function loadImage( images, abspath, onLoad, onError, delay, timeout) {
				images = images || document.images;
				var img,
					i = 0, l = images.length,
					tick = 0;
				for(; i < l; ++i) {
					img = images[i];
					if ( img.src === abspath && img.complete) {
						var size = h2c.util.getImageSize( img);
						onLoad( abspath, size.width, size.height);
						return;
					}
				}
				img = document.createElement( 'IMG'); //var img = new Image(); ではieでimgのsizeが取れない、、、removeChildも失敗し、imgSizeGetterにimgが残る
				img.finish = false;
				img.onabort = img.onerror = function() {
					if (img.finish) { return; }
					img.finish = true;
					onError(abspath);
					img.onload = img.onabort = img.onerror = "";
				};
				img.onload  = function() {
					img.finish = true;
					if (window.opera && !img.complete) {
						onError(abspath);
						img.onload = img.onabort = img.onerror = "";
						return;
					}
					var size = h2c.util.getImageSize( img);
					onLoad( abspath, size.width, size.height);
					img.onload = img.onabort = img.onerror = "";
					//img = void 0;
				};
				img.src = abspath;
				if (!img.finish && timeout) {
					setTimeout(function() {
						if (img.finish) { return; }
						if (img.complete) {
							img.finish = true;
							if (img.width) { return; }
							onError(abspath);
							img.onload = img.onabort = img.onerror = "";
							return;
						}
						if ((tick += delay) > timeout) {
							img.finish = true;
							onError(abspath);
							img.onload = img.onabort = img.onerror = "";
							return;
						}
						setTimeout(arguments.callee, delay);
					}, 0);
				}
			}
		},
		getAbsolutePath: function( path) {
			var e = document.createElement("div");
			e.innerHTML = '<a href=\"' + path + '\" />';
			return e.firstChild.href;
		}
	}
})();

/*
 * window resize event, overlay と currentなviewに流す
 * view modeの保持
 * 	 editor, overlay, comic-viewer, image-explorer
 * fadeIn, faseOut
 */
h2c.view = ( function(){
	var jqWindow,
		funcArray,
		HOME_ID = 'home',
		elmCurrent,
		viewID = ( function(){
			var _viewID = h2c.URL_PARAMS.view || HOME_ID,
				_elmView = document.getElementById( _viewID),
				_elmHome = document.getElementById( HOME_ID);
			if( _elmView){
				_elmHome.style.display = '';
				_elmView.style.display = 'block';
				elmCurrent = _elmView;
				return _viewID;
			}
			elmCurrent = _elmHome;
			return HOME_ID;
		})(),
		currentView;

	function onWindowResize(){
		var _fn,
			l = funcArray.length,
			w = jqWindow.width(),
			h = jqWindow.height();
		for( var i=0; i<l; ++i){
			_fn = funcArray[ i];
			_fn.onWindowResize && _fn.onWindowResize( w, h);
		}
	}
	return {
		init: function( _funcArray){
			funcArray = _funcArray;
			jqWindow = h2c.jqWindow();
			jqWindow.resize( onWindowResize);
			currentView = h2c[ viewID];
			setTimeout( onWindowResize, 100);
			
			delete h2c.view.init;
		},
		show: function( _view){
			for( var key in h2c){
				if( h2c[ key] === _view){
					var elm = document.getElementById( key);
					if( currentView !== _view && elm){
						this.currentID = viewID = key;
						currentView = _view;
						elmCurrent.style.display = '';
						elm.style.display = 'block';
						elmCurrent = elm;
					}
					return;
				}
			}
			alert( 'fault!!');
		},
		currentID: viewID,
		HOME: HOME_ID,
		COMICS: 'comic-list',
		IMAGES: 'image-list',
		SETTING: 'setting',
		EDITOR: 'editor',
		OVERLAY: 'overlay'
	}
})();

h2c.overlay = ( function(){
	var SHADOW_OPACITY = 0.5,
		jqBody, jqConteiner, jqShadow, jqCloseButton,
		currentOverlay = null,
		visible = false;
	
	function close(){
		currentOverlay && currentOverlay.onClose && currentOverlay.onClose();
		h2c.overlay.hide();		
	}
	return {
		init: function(){
			jqBody = h2c.jqBody();
			jqConteiner = $( '#overlay-container');
			jqShadow = $( '#overlay-shadow');
			jqCloseButton = $( '#overlay-close-button').click( function( e){
				close();
				e.preventDefault();
				return false;
			});
			
			h2c.key.addKeyDownEvent( h2c.view.OVERLAY, 27, false, false, close); // 27.esc
			
			delete h2c.overlay.init;
		},
		show: function( _currentOverlay){
			if( visible === true && currentOverlay === _currentOverlay) return;
			jqConteiner.stop().css( {
				filter:		'',
				opacity:	''
			}).fadeIn();
			this.visible = visible = true;
			currentOverlay = _currentOverlay;
			this.currentID = _currentOverlay.ID;
			jqCloseButton.toggle( !!_currentOverlay.onClose);
		},
		hide: function(){
			currentOverlay = null;
			if( visible === false) return;
			jqConteiner.stop().css( {
				filter:		'',
				opacity:	''
			}).fadeOut();
			this.visible = visible = false;
			this.currentID = null;
		},
		visible: visible,
		currentID: null,
		onWindowResize: function( _windowW, _windowH){
			jqConteiner.css( { height:	_windowH});
			jqShadow.css( { height:	_windowH});
			currentOverlay && currentOverlay.onWindowResize && setTimeout( function(){ // 先にeditorのcanvasを確定する。
				currentOverlay.onWindowResize( _windowW, _windowH);
			}, 0);
		}
	}
})();

/* ----------------------------------------
 * KEY
 * 
 *  - EDITABLE_TEXT_CONTROL
 *    - EditableTextClass
 *      .update: function,
 *      .show: function,
 *      .hide: function,
 *      .start: function,
 *      .finish: finish,
 *      .enabled: function,
 *      .index: function,
 *      .instance: function
 * 
 *    .SHIFT_DOWN_EVENT:	'shiftDown',
 *    .SHIFT_UP_EVENT:		'shiftUp',
 *    .CTRL_DOWN_EVENT:		'ctrlDown',
 *    .CTRL_UP_EVENT:		'ctrlUp',
 *    .SPACE_DOWN_EVENT:	'spaceDown',
 *    .SPACE_UP_EVENT:		'spaceUp',
 *    .init:				function,
 *    .addKeyDownEvent:			function,
 *    .keyEventDispatcher:	function,
 *    .createEditableText:	function,
 * 
 * ショートカットキーの監視とテキスト入力(input, textarea)、チェックボックスを管理する。
 * キー入力はdocumentで受けて、テキスト編集中(input, textarea)はそちらにキーイベント流す。
 * 
 */
h2c.key = ( function(){
	var log,
		jqWindow,
		keyOperationChatcher,
		KEYEVENT_ARRAY = [],
		shiftEnabled = false,
		ctrlEnabled = false;
	
	var EDITABLE_TEXT_CONTROL = ( function(){
		var	EDITABLE_TEXT_TABLE = {},
			currentText = null;

		var EditableTextClass = function( WRAPPER_ELM, ON_UPDATE_FUNCTION, GROUP_ID){
			var ELM = WRAPPER_ELM.children( '.editable-value').eq( 0),
				value = ELM.html(),
				jqInput,
				index = EDITABLE_TEXT_TABLE[ GROUP_ID].length,
				instance,
				enabled = true,
				A = $( '<a href="#" onclick="return false;"></a>').html( value).click( function(e){
					instance = instance || EDITABLE_TEXT_TABLE[ GROUP_ID][ index];
					EDITABLE_TEXT_CONTROL.start( instance);								
					A.hide();
					jqInput = $( '<input type="text"/>').val( value)//.keydown( finish).keypress( finish);
					ELM.append( jqInput);
					jqInput.focus().select();
					e.preventDefault();
					return false;
				});
			ELM.addClass( 'editable-text').html( A);
			
			function finish(e){
				if(e){
					var keyCode = e.keyCode;
					if( keyCode === 13 || keyCode === 27 || keyCode === 9 || keyCode === 18 || e.altKey === true){ // 13.return 27.esc 9.tab 18.alt
						_finish( keyCode !== 27 ? jqInput.val() : value);
						keyCode === 9 && GROUP_ID && EDITABLE_TEXT_CONTROL.tabShift( GROUP_ID, index, e.shiftKey === true ? -1 : 1);
					}
				} else {
					_finish( jqInput.val());
				}
				function _finish( VALUE_NEW){
					value = VALUE_NEW;
					A.html( VALUE_NEW).show();
					jqInput.remove();
					jqInput = null;
					ON_UPDATE_FUNCTION && VALUE_NEW !== value && ON_UPDATE_FUNCTION( VALUE_NEW, value);
					EDITABLE_TEXT_CONTROL.finish( instance);
				}
			}
			return {
				update: function( _value){
					value = _value !== undefined ? _value : value;
					A.html( value);
					jqInput && jqInput.val( value);
					currentText === instance && finish();
				},
				show: function(){
					enabled === false && WRAPPER_ELM.show();
					enabled= true;
				},
				hide: function(){
					enabled === true && WRAPPER_ELM.hide();
					enabled = false;
				},
				start: function(){
					!jqInput && A.click();
				},
				finish: finish,
				enabled: function(){
					return enabled;
				}
			}
		}
		var CheckboxClass = function( WRAPPER_ELM, ON_UPDATE_FUNCTION, GROUP_ID){
			var ELM = WRAPPER_ELM.children( '.checkbox-value').eq( 0),
				value = ELM.html(),
				jqInput,
				index = EDITABLE_TEXT_TABLE[ GROUP_ID].length,
				instance,
				enabled = true,
				A = $( '<a href="#" onclick="return false;"></a>').html( value).click( function(e){
					instance = instance || EDITABLE_TEXT_TABLE[ GROUP_ID][ index];
					EDITABLE_TEXT_CONTROL.start( instance);								
					A.hide();
					jqInput = $( '<input type="text"/>').val( value)//.keydown( finish).keypress( finish);
					ELM.append( jqInput);
					jqInput.focus().select();
					e.preventDefault();
					return false;
				});
			ELM.addClass( 'editable-text').html( A);
			
			function finish(e){
				if(e){
					var keyCode = e.keyCode;
					if( keyCode === 13 || keyCode === 27 || keyCode === 9 || keyCode === 18 || e.altKey === true){ // 13.return 27.esc 9.tab 18.alt
						_finish( keyCode !== 27 ? jqInput.val() : value);
						keyCode === 9 && GROUP_ID && EDITABLE_TEXT_CONTROL.tabShift( GROUP_ID, index, e.shiftKey === true ? -1 : 1);
					}
				} else {
					_finish( jqInput.val());
				}
				function _finish( VALUE_NEW){
					value = VALUE_NEW;
					A.html( VALUE_NEW).show();
					jqInput.remove();
					jqInput = null;
					ON_UPDATE_FUNCTION && VALUE_NEW !== value && ON_UPDATE_FUNCTION( VALUE_NEW, value);
					EDITABLE_TEXT_CONTROL.finish( instance);
				}
			}
			return {
				update: function( _value){
					value = _value !== undefined ? _value : value;
					A.html( value);
					jqInput && jqInput.val( value);
					currentText === instance && finish();
				},
				show: function(){
					enabled === false && WRAPPER_ELM.show();
					enabled= true;
				},
				hide: function(){
					enabled === true && WRAPPER_ELM.hide();
					enabled = false;
				},
				start: function(){
					!jqInput && A.click();
				},
				finish: finish,
				enabled: function(){
					return enabled;
				}
			}
		}
		return {
			createEditableText: function( ELM, ON_UPDATE_FUNCTION, GROUP_ID){
				if( GROUP_ID && !EDITABLE_TEXT_TABLE[ GROUP_ID]) EDITABLE_TEXT_TABLE[ GROUP_ID] = [];
				var ret = EditableTextClass.apply( {}, [ ELM, ON_UPDATE_FUNCTION, GROUP_ID]);
				GROUP_ID && EDITABLE_TEXT_TABLE[ GROUP_ID].push( ret);
				return ret;
			},
			createCheckbox: function( ELM, ON_UPDATE_FUNCTION, GROUP_ID){
				
			},
			start: function( _currentText){
				currentText !== _currentText && currentText && currentText.finish();
				currentText = _currentText;
			},
			finish: function( _currentText){
				if( currentText !== _currentText) return;
				currentText = null 
			},
			keyEventRellay: function( e){
				currentText && currentText.finish( e);
				return !!currentText;
			},
			tabShift: function( _groupID, _index, _way){
				var GROUP_ARRAY = EDITABLE_TEXT_TABLE[ _groupID] || [],
					l = GROUP_ARRAY.length,
					i = _index +_way;
				if( l < 2) return;
				while( i !== _index){
					i = i < 0 ?
						l -1 :
						i === l ? 0 : i; // 0 < i < l
					if( GROUP_ARRAY[ i].enabled() === true) break;
					i += _way;
				}
				if( i === _index) return;
				setTimeout( function(){ GROUP_ARRAY[ i].start();}, 0);
			}
		}
	})();
	
	function keyHit( e){
		log.html( [ e.keyCode, e.shiftKey, e.ctrlKey, e.altKey, e.type].join( ','));
		//keyOperationChatcher.val( '');
		var cancel = false,
			callback = ( function(){
				var type = e.type;
				if( type === 'keypress') return 'keydown';
				return type;
			})(),
			key = e.keyCode,
			overlayEnabled = h2c.overlay.visible === true,
			currentViewID = overlayEnabled === true ? h2c.overlay.currentID : h2c.view.currentID;
		if( callback === 'keyup' || EDITABLE_TEXT_CONTROL.keyEventRellay( e) === false){
			var shift = e.shiftKey,
				ctrl = e.ctrlKey,
				l = KEYEVENT_ARRAY.length,
				d;
			
			shiftEnabled = callback !== 'keyup' ?
							e.shiftKey :
							key !== 16 ? shiftEnabled : false;
			ctrlEnabled = callback !== 'keyup' ?
							e.ctrlKey :
							key !== 17 ? ctrlEnabled : false;;			

			for( var i=0; i<l; i++){
				d = KEYEVENT_ARRAY[ i];
				if( (
						d.viewID === currentViewID ||
							( overlayEnabled === true && d.viewID === h2c.view.OVERLAY)
					) &&
					d.keyCode === key &&
					( d.shift === undefined || d.shift === shift) &&
					( d.ctrl === undefined || d.ctrl === ctrl)
				){
					( function( func, e){
						func && setTimeout( function(){
							func( e);
							func = e = null;
						}, 0);
					})( d[callback], e);
					cancel = true;
				}
			}
		}
		if( cancel === true || key === 18 || key === 9 || key === 27 || e.altKey === true){ // 13.enter 18.esc 9.tab 27.esc   || ( key === 13 && overlayEnabled === false)
			e.preventDefault();
	        e.keyCode = 0;
	        e.cancelBubble = true;
	        e.returnValue = false;
			return false;
		}
	}

	return {
		init: function(){
			log = $( '#key-event-log').html( 'ready');

			jqWindow = h2c.jqWindow().focus();
			keyOperationChatcher = h2c.jqDocument()
				.keydown( keyHit)
				.keyup( keyHit)
				.mouseenter( function(){
					jqWindow.focus();
				});
			h2c.isIE === true && h2c.ieRenderingVersion < 8 && keyOperationChatcher.keypress( function( e){
				var key = e.keyCode;
				if( key === 13 || key === 27){
					keyHit( e);
					return false;
				}
			});

			delete h2c.key.init;
		},
		addKeyDownEvent: function( _viewID, _keyCode, _shift, _ctrl, _callbackFunction){
			KEYEVENT_ARRAY.push( {
				viewID:			_viewID,
				keyCode:		_keyCode,
				shift:			_shift,
				ctrl:			_ctrl,
				keydown:		_callbackFunction
			});
		},
		addKeyUpdateEvent: function( _viewID, _keyCode, _shift, _ctrl, _callbackFunction){
			KEYEVENT_ARRAY.push( {
				viewID:		_viewID,
				keyCode:	_keyCode,
				shift:		_shift,
				ctrl:		_ctrl,
				keydown:	_callbackFunction,
				keyup:		_callbackFunction
			});
		},
		addCursorEvent: function( _viewID, _shift, _ctrl, _callbackFunction){
			
		},
		keyEventDispatcher: function(){
			return keyOperationChatcher;
		},
		createEditableText: EDITABLE_TEXT_CONTROL.createEditableText,
		createCheckbox: EDITABLE_TEXT_CONTROL.createCheckbox,
		shiftEnabled: function(){
			return shiftEnabled;
		},
		ctrlEnabled: function(){
			return ctrlEnabled;
		}
	}
})();

/* ----------------------------------------
 * Vector Support
 * 
 *              __________
 *             /          \
 *            /            \
 *            |,startX,Y    |
 * tailX,Y - <              |
 *            |'endX,Y      |
 *            \            /
 * 	           \__________/
 * 
 * SVG
 * -----------------------
 * ie9, other modern browser
 * 
 * XML
 * -----------------------
 * ie5.5-8
 * 
 * 内部の角度計算は radian で統一したい。
 * 当初 vectorEnabled = true で一度書いてみる。
 * 駄目なら、代替のイメージのsrcの用意もここで担当。
 * 閲覧と編集両方で使う。
 * 
 */
h2c.balloon = ( function() {
	var MIN_BALLOON_WIDTH = 30,
		MIN_BALLOON_HEIGHT = 30,
		TAIL_WIDTH = 6,
		TAIL_HEIGHT = 10,
		STROKE_WIDTH = 1.2,
		PADDING_TOP = TAIL_HEIGHT,
		PADDING_LEFT = TAIL_HEIGHT,
		ACCURACY = 1, // 有効少数桁	
		IS_VML = h2c.isIE === true && h2c.ieVersion < 9,
		ELM_BALLOON_ORIGIN = ( function(){
			var ret;
			try {
				if( IS_VML === true){
					ret = document.createElement( 'DIV');
					var shape = document.createElement( 'v:shape');
					shape.coordorigin = "0,0";
					shape.strokecolor = "black";
					shape.strokeweight = STROKE_WIDTH;
					shape.fillcolor = "white";
					ret.appendChild( shape);
				} else {
					var kSVGNS = 'http://www.w3.org/2000/svg';
					ret = document.createElementNS( kSVGNS, 'svg');
					var path = document.createElementNS( kSVGNS, 'path');
					path.setAttribute( 'fill', "white");
					path.setAttribute( 'stroke', "black");
					path.setAttribute( 'strokeWidth', STROKE_WIDTH);
					ret.appendChild( path);
				}
				return ret;	
			} catch( e){
				return null;
			}
		})(),
		NUM_BALLOON_IMAGE = 24,
		vectorEnabled = ELM_BALLOON_ORIGIN !== null && h2c.URL_PARAMS.vector !== false;
	if( IS_VML === true && h2c.VML === false) vectorEnabled = false;

	var XBROWSER_BALLOON_CLASS = function( w, h, a){
		var balloonElm = vectorEnabled === true ? ELM_BALLOON_ORIGIN.cloneNode( true) : document.createElement( 'IMG'), // h2c.imageに変更
			path = balloonElm.getElementsByTagName( 'path')[ 0],
			shape = balloonElm.getElementsByTagName( 'shape')[ 0],
			cos = Math.cos, sin = Math.sin,
			abs = Math.abs, pow = Math.pow,
			round = Math.round,
			floor = Math.floor,
			TARGET = TAIL_WIDTH * TAIL_WIDTH,
			DEG_TO_RAD = Math.PI / 180,
			l = ',';
		
		draw( a, w, h);
		
		function draw( _a, _w, _h){
			a = _a !== undefined ? _a : a;
			w = _w !== undefined ? _w -PADDING_TOP *2 : w;
			h = _h !== undefined ? _h -PADDING_LEFT *2 : h;

			if( vectorEnabled === false){
				balloonElm.setAttribute( 'src', balloonUrlBuilder( a));
				return;
			}
			
			var rx = w /2,
				ry = h /2,
				tailRad = a * DEG_TO_RAD,
				tailX = rx +( rx +TAIL_HEIGHT) * cos( tailRad),
				tailY = ry +( ry +TAIL_HEIGHT) * sin( tailRad),
				startX, startY, endX, endY;
		/*
		 * tailの太さをTAIL_WIDTHに一致させるため、角度を絞りつつ計算
		 */
			var startRad, endRad,
				_startX, _startY, _endX, _endY,
				tailDeg = 0, d;
			
			for( var i = 45; i > 0.01; i /= 2){
				d = ( tailDeg +i) /2;
				startRad = ( a +d) * DEG_TO_RAD;
				endRad = ( a -d) * DEG_TO_RAD;
				
				_startX = rx +cos( startRad) *rx;
				_startY = ry +sin( startRad) *ry;
				_endX = rx +cos( endRad) *rx;
				_endY = ry +sin( endRad) *ry;	//円弧上のY位置＝円中心Y＋sin(角度×PI÷180)×円半径
					
				if( pow( ( _startX -_endX), 2) + pow( ( _startY -_endY), 2) < TARGET){
					tailDeg += i;
					startX = _startX;
					startY = _startY;
					endX = _endX;
					endY = _endY;						
				}
			}

		/*
		 * 
		 */			
			if( IS_VML === true){
				var _tailX = tailX *10,
					_tailY = tailY *10,
					__startX = startX *10,
					__startY = startY *10,
					__endX = endX *10,
					__endY = endY *10,
					__w = w *10,
					__h = h *10;
				
				shape.style.width = w +'px';
				shape.style.height = h +'px';
				shape.coordsize = [ __w, __h].join( l);
				shape.path = [
					' ar ', 0, l, 0, l, __w, l, __h, l,
					round( __endX), l, round( __endY), l,
					round( __startX), l, round( __startY),
					' l ', round( _tailX), l, round( _tailY),
					' x e'
				].join( '');

				balloonElm.style.marginTop =  _tailY < 0 ? floor( ( 60 +_tailY) /10) : 10;
				balloonElm.style.marginLeft = _tailX < 0 ? floor( ( 60 +_tailX) /10) : 10;
			} else {
				balloonElm.setAttribute( 'width', w +PADDING_LEFT *2);
				balloonElm.setAttribute( 'height', h +PADDING_TOP *2);
				path.setAttribute( 'd', [
					'M', cround( tailX + PADDING_LEFT), l, cround( tailY +PADDING_TOP),
					'L', cround( startX +PADDING_LEFT), l, cround( startY +PADDING_TOP),
					'A', rx, l, ry,
					'0 1 1',			// flag
					cround( endX +PADDING_LEFT), l, cround( endY +PADDING_TOP),
					'z'
				].join( ' '));
			}
			function cround( v, r){
				r = r || ACCURACY;
				return round( v *pow( 10.0, r)) /pow( 10.0, r);
			}
		}
		
		function balloonUrlBuilder( _a){
			var d = 360 /NUM_BALLOON_IMAGE;
			_a += 90 +d /2;
			return [ 'balloon\/_w', _a < 360 -d /2 ? floor( _a /d) : 0, '.gif'].join( '');
		}
		return {
			elm: balloonElm,
			resize: draw,
			angle: function( _a){
				_a !== undefined && _a !== a &&
					vectorEnabled === false ? balloonUrlBuilder( _a) : draw( _a);
				return a;
			},
			type: function( _type){
				//draw( _a);
			},
			getURL: function(){
				return balloonUrlBuilder( a);
			},
			destroy: function(){
				balloonElm.parentNode && balloonElm.parentNode.removeChild( balloonElm);
				balloonElm = null;
				delete this.destroy;
			}
		}
	};
	
	IS_VML === false && vectorEnabled === true && ( function(){
		var detect = XBROWSER_BALLOON_CLASS.apply( {}, [ 100, 100, 0]),
			size = h2c.util.getElementSize( detect.elm);
		vectorEnabled = size.width !== 0 && size.height !== 0;
		detect.destroy();
		detect = size = null;
	})();

	return {
		init: function(){
			delete h2c.balloon.init;
		},
	    enabled: function() {
	        return vectorEnabled;
	    },
		VML: IS_VML === true && vectorEnabled === true,
	    createBalloon: function( _w, _h, _a){
	    	return XBROWSER_BALLOON_CLASS.apply( {}, [ _w, _h, _a]);
	    },
		TYPE_NONE:				0,
		TYPE_SPEACH_BALLOON:	1,
		TYPE_THINKING:			2,
		TYPE_BOM:				3,
		TYPE_BLACK_BOX:			4,
		TYPE_BLUE_BOX:			5
	}
})();

/* ----------------------------------------
 *  h2c.image
 *  
 *   xBackendな画像反転、画像描画機能。
 *   
 *   画像の反転
 *     - css3
 *     - ActiveX (ie)
 *     - VML (ie)
 *     - canvas ??
 *     - flash(lite)
 *     - silverlight
 *     - pettan server
 *   
 *   png画像の表示(アルファpngをサポートしないie6以下のため)
 *     - ActiveX
 *     - VML
 *     - flash(lite)
 *     - silverlight
 *     
 *     -moz-transform:scale( -1, -1);
 */
h2c.image = ( function(){
	var REG_PNG = /\.png?/i,
		IS_CSS3 = 0,
		IS_VML = 1,
		IS_ACTIVEX = 2,
		IS_CANVAS = 3,
		IS_FLASH = 4,
		IS_SILVERLIGHT = 5,
		IS_SERVER = 6,
		IS_ACTIVEX_SERVER = 7,
		BACKEND = ( function(){
			if( h2c.DEBUG === true && h2c.URL_PARAMS.rimg){
				var rimg = h2c.URL_PARAMS.rimg.toLowerCase();
				if( rimg === 'css3') return IS_CSS3;
				if( rimg === 'activex') return IS_ACTIVEX;
				if( rimg === 'vml') return IS_VML;
			}
			if( h2c.isIE === false || h2c.ieVersion >= 9) return IS_CSS3; // 不十分！
			if( h2c.VML === true) return IS_VML;
			if( h2c.ACTIVEX === true) return IS_ACTIVEX;
			if( h2c.FLASH === true) return IS_FLASH;
			if( h2c.SILVERLIGHT === true) return IS_SILVERLIGHT;
			return IS_SERVER;
		})(),
		PNG_FIX = h2c.isIE === true && h2c.ieVersion <= 6,
		BACKEND_WHEN_PNG = PNG_FIX === false ? BACKEND : ( function(){
			if( h2c.balloon.VML === true) return IS_VML;
			if( h2c.FLASH === true) return IS_FLASH;
			if( h2c.SILVERLIGHT === true) return IS_SILVERLIGHT;
			if( h2c.ACTIVEX === true) return IS_ACTIVEX_SERVER;
			return IS_SERVER;
		})();
	
	var XBackendReversibleImageClass = ( function(){
		var CLASS_NAME = 'reversible-image-container',
			CLASS_NAME_LOADING = CLASS_NAME + ' loading',
			CLASS_NAME_ERROR = CLASS_NAME +' error',
			RETRY_DELAY = 5000;
			NUM_RETRY = 3;
		
		var css3Image = function( url, w, h, onLoadCallback){
			var elmWrap = document.createElement( 'div'),
				elmImg,
				loaded = false,
				retryTimer = null;
			elmWrap.className = CLASS_NAME_LOADING;
			h2c.util.loadImage( url, onLoad, onError, 100, 10000);
			function onLoad( _url, _actualW, _actualH){
				if( elmWrap === null) return;
				elmImg = new Image;
				/*
				 * createElement 直後に append しないと、ie(ActiveX)で img が正しく表示されない．
				 */
				elmWrap.appendChild( elmImg);
				elmImg.setAttribute( 'src', url);
				elmWrap.className = CLASS_NAME;
				onLoadCallback && onLoadCallback( _url, _actualW, _actualH);
				onLoadCallback = null;
				loaded = true;
				resize( w, h);
			}
			function onError( _url){
				if( elmWrap === null) return;
				elmWrap.className = CLASS_NAME_ERROR;
				retryTimer = setTimeout( function(){
					elmWrap.className = CLASS_NAME_LOADING;
					h2c.util.loadImage( url, onLoad, onError, 100, 10000);
				}, RETRY_DELAY);
			}
			function resize( _w, _h){
				w = _w !== undefined ? _w : w;
				h = _h !== undefined ? _h : h;
				if( loaded === false) return;
				elmImg.className = w < 0 || h < 0 ? ( 'img-flip-' + ( w < 0 && h < 0 ? 'vh' : ( w < 0 ? 'v' : 'h'))) : '';
			}
			return {
				elm : elmWrap,
				resize: resize,
				destroy: function(){
					loaded === true && elmWrap.removeChild( elmImg);
					retryTimer !== null && clearTimeout( retryTimer);
					elmWrap = vmlImg = onLoadCallback = retryTimer = null;
					elmWrap = elmImg = onLoadCallback = null;
					delete this.destroy;
				}
			}
		}
		var activexImage = css3Image;
		var vmlImage = function( url, w, h, onLoadCallback){
			var elmWrap = document.createElement( 'div'),
				vmlImg,
				loaded = false,
				retryTimer = null;
			elmWrap.className = CLASS_NAME_LOADING;
			h2c.util.loadImage( url, onLoad, onError, 100, 10000);
			function onLoad( _url, _actualW, _actualH){
				if( elmWrap === null) return;
				elmWrap.className = CLASS_NAME;
				vmlImg = document.createElement( 'v:image');
				vmlImg.src = url;
				loaded = true;
				resize( w, h);
				onLoadCallback && onLoadCallback( _url, _actualW, _actualH);
				onLoadCallback = null;
			}
			function onError( _url){
				if( elmWrap === null) return;
				elmWrap.className = CLASS_NAME_ERROR;
				retryTimer = setTimeout( function(){
					elmWrap.className = CLASS_NAME_LOADING;
					h2c.util.loadImage( url, onLoad, onError, 100, 10000);
				}, RETRY_DELAY);
			}
			function resize( _w, _h){
				w = _w !== undefined ? _w : w;
				h = _h !== undefined ? _h : h;
				if( loaded !== true) return;
				vmlImg.style.width = w < 0 ? -w : w +'px';
				vmlImg.style.height = h < 0 ? -h : h +'px';
				//if( flipH !== _flipH || flipV !== _flipV){
					vmlImg.parentNode === elmWrap && elmWrap.removeChild( vmlImg);
				//}
					vmlImg.className = w < 0 || h < 0 ? ( 'img-flip-' + ( w < 0 && h < 0 ? 'vh' : ( w < 0 ? 'v' : 'h'))) : '';
					elmWrap.appendChild( vmlImg);
			}
			return {
				elm : elmWrap,
				resize: resize,
				destroy: function(){
					loaded === true && elmWrap.removeChild( vmlImg);
					retryTimer !== null && clearTimeout( retryTimer);
					elmWrap = vmlImg = onLoadCallback = retryTimer = null;
					delete this.destroy;
				}
			}
		}
		var serverImage = function( url, w, h, onLoadCallback){
			
		}
		
		return function( url, w, h, onLoadCallback){
				var flipH = w < 0,
					flipV = h < 0,
					onLoadCallbackAsync = onLoadCallback,// ? function(){ setTimeout( onLoadCallback, 0);} : undefined,// 一度読み込んだ画像は即座にonLoadになるため遅延
					xBackendImage = ( function( urlIsXDomain){
						if( BACKEND === IS_CSS3) return css3Image.apply( {}, [ url, w, h, onLoadCallbackAsync]);
						if( BACKEND === IS_VML) return vmlImage.apply( {}, [ url, w, h, onLoadCallbackAsync]);
						if( BACKEND === IS_ACTIVEX) return activexImage.apply( {}, [ url, w, h, onLoadCallbackAsync]);
						return serverImage.apply( {}, [ url, w, h, onLoadCallbackAsync]);
					})();
				return {
					elm: xBackendImage.elm,
					w: function( _w){
						_w !== undefined && xBackendImage.resize( _w, h);
						return w;
					},
					h: function( _h){
						_h !== undefined && xBackendImage.resize( w, _h);
						return h;
					},
					resize: xBackendImage.resize,
					destroy: function(){
						xBackendImage.destroy && xBackendImage.destroy();
						xBackendImage = onLoadCallback = onLoadCallbackAsync = null;
						delete this.destroy;
					}
				}
			}
	})();
	return {
		init: function(){
			
		},
		createReversibleImage: function( url, w, h, onLoadCallback){
			return XBackendReversibleImageClass.apply( {}, [ url, w, h, onLoadCallback]);
		}
	}
})();

/* ----------------------------------------
 *   h2c.editor
 *    - HISTORY
 *    - WINDOW_CONTROL
 *       - WINDOW_CLASS
 *    - INFOMATION_WINDOW
 *    - TOOL_BOX_WINDOW
 *    - CANVAS_CONTROL
 *       - GRID_CONTROL
 *       - WHITE_GLASS_CONTROL
 *       - PANEL_BORDER_CONTROL
 *       - COMIC_ELEMENT_CONTROL
 *          - PanelResizerClass
 *          - COMIC_ELEMENT_OPERATOR
 *             - TAIL_CONTROLER
 *          - ImageElementClass
 *          - TextElementClass
 * 
 */
h2c.editor = ( function(){
	var COMIC_ELEMENT_TYPE_IMAGE = 0,
		COMIC_ELEMENT_TYPE_TEXT = 1,
		MOUSE_LISTENER_ARRAY = [],
		currentListener = null,
		ELM_MOUSE_EVENT_CHATCHER = document.getElementById( 'mouse-operation-catcher'),
		jqMouseEventChacher,
		jqEditor,
		windowW, windowH,
		currentCursor = '';

/* ----------------------------------------
 * MENU BAR
 * div
 *   div.title
 *   ul
 *     li
 *        a
 *          span
 *          kbd shortcut
 */
	var MENU_BAR_CONTROL = ( function(){
		var BAR_ID = 'menu-bar',
			ELM_BAR = document.getElementById( BAR_ID),
			ELM_ITEM_CLASSNAME = 'menu-bar-item',
			ELM_ITEM_ORIGN = ( function(){
				var ret = document.createElement( 'DIV'),
					div = document.createElement( 'DIV'),
					ul = document.createElement( 'UL');
				ret.className = ELM_ITEM_CLASSNAME;
				ret.appendChild( div);
				ret.appendChild( ul);
				return ret;
			})(),
			ELM_SELECTION_ORIGN = ( function(){
				var ret = document.createElement( 'LI'),
					a = document.createElement( 'A'),
					span = document.createElement( 'SPAN'),
					key = document.createElement( 'KBD');
				a.href = '#';
				a.appendChild( span);
				a.appendChild( key);
				ret.appendChild( a);
				return ret;
			})(),
			EMPTY_FUNCTION = new Function,
			ITEM_ARRAY = [],
			barH = h2c.util.getElementSize( ELM_BAR).height,
			itemW = h2c.util.getElementSize( ELM_ITEM_ORIGN).width,
			selectionW = h2c.util.getElementSize( ELM_ITEM_ORIGN.getElementsByTagName( 'UL')[ 0]).width,
			jqStage, jqBar;
		ELM_BAR.style.top = ( -barH) +'px';

		var MenubarSelectionClass = function( container, title, shortcut, visible, separateAfter){
			var ELM_WRAPPER = ELM_SELECTION_ORIGN.cloneNode( true),
				ELM_TITLE = ELM_WRAPPER.getElementsByTagName( 'SPAN')[ 0];
			updateTitle( title);
			updateVisible( visible);
			
			( function(){
				var ELM_SHORTCUT = ELM_WRAPPER.getElementsByTagName( 'KBD')[ 0];
				if( shortcut){
					ELM_SHORTCUT.innerHTML = shortcut;
				} else {
					ELM_SHORTCUT.parentNode.removeChild( ELM_SHORTCUT);
				}				
			})();

			container.appendChild( ELM_WRAPPER);
			
			function updateTitle( _title){
				ELM_TITLE.innerHTML = title = _title;
			}
			function updateVisible( _visible){
				_visible !== undefined && ( function(){
					visible = !!_visible;
					ELM_WRAPPER.className = visible === true ? '' : 'disabled';
				})();
			}
			return {
				elm: ELM_WRAPPER,
				title: function( _title){
					_title !== undefined && updateTitle( _title);
					return title;
				},
				visible: function( _visible){
					visible !== !!_visible && updateVisible( _visible);
					return visible;
				},
				separateAfter: separateAfter
			}
		}

		var MenuBarItemClass = function( title, opt_callbackArray){
			var ELM_WRAPPER = ELM_ITEM_ORIGN.cloneNode( true),
				ELM_TITLE = ELM_WRAPPER.getElementsByTagName( 'DIV')[ 0],
				ELM_SELECTION = ELM_WRAPPER.getElementsByTagName( 'UL')[ 0],
				INDEX = ITEM_ARRAY.length,
				SELECTION_CALLBACK_ARRAY = opt_callbackArray || [],
				numSelection = 0,
				visible = false;
			ELM_TITLE.innerHTML = title;
			
			ELM_WRAPPER.style.left = ( itemW * INDEX) +'px';
			ELM_BAR.appendChild( ELM_WRAPPER);
			
			function onClick( e){
				var that = this,
					i = ( function(){
						var parent = that.parentNode,
							children = parent.getElementsByTagName( 'LI'),
							l = children.length;
						for(var i=0; i<l; ++i){
							if( children[ i] === that) return i;
						}
						return -1;
					})();
				i !== -1 && this.className !== 'disabled' && SELECTION_CALLBACK_ARRAY[ i]();
				e.stopPropagation();
				return false;				
			}
			return {
				elm: ELM_WRAPPER,
				onClick: onClick,
				init: function(){
					$( ELM_SELECTION).children( 'li').click( onClick);
					delete this.init;
				},
				show: function(){
					if( visible === true) return;
					jqStage.append( ELM_WRAPPER);
					ELM_WRAPPER.className = ELM_ITEM_CLASSNAME +'-focus';
					this.onShow && setTimeout( this.onShow, 0);
					visible = true;
				},
				hide: function(){
					if( visible === false) return;
					ELM_BAR.appendChild( ELM_WRAPPER);
					ELM_WRAPPER.className = ELM_ITEM_CLASSNAME;
					this.onHide && setTimeout( this.onHide, 0);
					visible = false;
				},
				createSelection: function( title, shortcut, callback, visible, separateBefore, separateAfter){
					var ret = MenubarSelectionClass.apply( {}, [ ELM_SELECTION, title, shortcut, visible, separateAfter]),
						before = SELECTION_CALLBACK_ARRAY.length > 0 ? SELECTION_CALLBACK_ARRAY[ SELECTION_CALLBACK_ARRAY.length -1] : null;
					SELECTION_CALLBACK_ARRAY.push( callback);
					if( ( separateBefore === true && before) || ( before && before.separateAfter === true)){
						ret.elm.style.borderTop = '1px solid #ccc';
					}
					return ret;
				}
			}
		}

		
		function createMenubarItem( title){
			var _item = MenuBarItemClass.apply( {}, [ title]);
			ITEM_ARRAY.push( _item);
			return _item;
		}
		return {
			init: function(){
				jqStage = jqEditor;
				jqBar = $( '#' +BAR_ID).animate( { top: 0});

				var l = ITEM_ARRAY.length;
				for( var i=0; i<l; ++i){
					ITEM_ARRAY[ i].init();
				}

				delete MENU_BAR_CONTROL.init;
			},
			h: barH,
			onMouseMove: function( _mouseX, _mouseY){
				if( barH >= _mouseY){
					return true;
				}
				var l = ITEM_ARRAY.length;
				for( var i=0; i<l; ++i){
					ITEM_ARRAY[ i].hide();
				}
				return false;
			},
			onMouseUp: function( _mouseX, _mouseY){
				return false;
			},
			onMouseDown: function( _mouseX, _mouseY){
				var l = ITEM_ARRAY.length;
				if( barH < _mouseY || itemW * l < _mouseX) return false;
				for( var i=0; i<l; ++i){
					if( i * itemW <= _mouseX && _mouseX < ( i +1) * itemW){
						ITEM_ARRAY[ i].show();
					} else {
						ITEM_ARRAY[ i].hide();
					}
				}
				return true;
			},
			busy: function( _busy){
				return false;
			},
			onWindowResize: function( _windowW, _windowH){
				
			},
			QUIT: createMenubarItem( 'Quit'),
			EDIT: createMenubarItem( 'Edit'),
			WINDOW: createMenubarItem( 'Window'),
			HELP: h2c.util.extend( createMenubarItem( 'Help'), {
					createAjaxSelection: function( callback){
						var elmLoading = document.createElement( 'li'),
							that = this,
							elmSelection = this.elm.getElementsByTagName( 'UL')[ 0];
						elmSelection.appendChild( elmLoading);
						elmLoading.className = 'loading';
						elmLoading.style.height = '90px';							

						this.onShow = callback;
						callback = null;
						
						delete this.createAjaxSelection;
						return function(){
							elmSelection.removeChild( elmLoading);
							$( elmSelection).children( 'li').click( that.onClick);
							elmLoading = elmSelection = null;
							delete that.onShow;
							that = null;
						}
					}
				})
		}
	})();


/* ----------------------------------------
 * HISTORY
 */
	var HISTORY = ( function() {
		var	STACK_BACK = [],
			STACK_FORWARD = [],
			MENUBAR_BACK = MENU_BAR_CONTROL.EDIT.createSelection( 'back', 'ctrl + z', back, false),
			MENUBAR_FORWARD = MENU_BAR_CONTROL.EDIT.createSelection( 'forward', 'ctrl + y', forward, false, false, true),
			log;
			
		h2c.key.addKeyDownEvent( h2c.view.EDITOR, 90, false, true, back);	// ctrl + Z
		h2c.key.addKeyDownEvent( h2c.view.EDITOR, 90, true, true, forward);	// ctrl + shift + Z
		h2c.key.addKeyDownEvent( h2c.view.EDITOR, 89, false, true, forward); // ctrl + Y

		function back(){
			/*
			 * currentを控えてSTACK_FORWARD.push(current)
			 * STACK_BACK.pop()を実行してcurrentに
			 */
			if( STACK_BACK.length === 0) return;

			var state = STACK_BACK.pop();
			state && state.fn( state.argBack);
			MENUBAR_BACK.visible( STACK_BACK.length !== 0);
			SAVE_CONTROL.panelUpdated( STACK_BACK.length !== 0);
			
			STACK_FORWARD.push( state);
			MENUBAR_FORWARD.visible( true);
		}
		function forward(){
			if( STACK_FORWARD.length === 0) return;
			
			var state = STACK_FORWARD.pop();
			state.fn( state.argForword);
			MENUBAR_FORWARD.visible( STACK_FORWARD.length !== 0);
			
			STACK_BACK.push( state);
			MENUBAR_BACK.visible( true);
			SAVE_CONTROL.panelUpdated( true);
		}
		return {
			init: function(){
				log = $( '#history-log');
				delete HISTORY.init;
			},
		    saveState: function( _function, _argBack, _argForword, _destory) {
		        STACK_BACK.push( {
		        	fn:			_function,
		        	argBack:	_argBack,
					argForword:	_argForword,
					destroy:	_destroy
		        });
		        MENUBAR_BACK.visible( true);
				SAVE_CONTROL.panelUpdated( true);

		       	while( STACK_FORWARD.length > 0){
					_argBack = _stack.argBack;
					_argForword = _stack.argForword;					
					var _stack = STACK_FORWARD.shift(),
						_destroy = _stack.destroy,
						_value;
					_stack.fn = null;
					if( typeof _argBack === 'array'){
						while( _argBack.length > 0){
							_value = _argBack.shift();
							_destroy === true && _value.destroy && _value.destroy();
						}
					}
					if( typeof _argForword === 'array'){
						while( _argForword.length > 0){
							_value = _argForword.shift();
							_destroy === true && _value.destroy && _value.destroy();
						}						
					}
				}
				MENUBAR_FORWARD.visible( false);
		    }		
		}
	})();


/* ----------------------------------------
 *     Save Control
 */

	var SAVE_CONTROL = ( function(){
		var SAVE = MENU_BAR_CONTROL.QUIT.createSelection( 'save', 'ctrl + S', quit, false),
			SAVE_AND_QUIT = MENU_BAR_CONTROL.QUIT.createSelection( 'save & quit', null, quit, false, false, true),
			SAVE_AS_HTML = MENU_BAR_CONTROL.QUIT.createSelection( 'get as html', null, outputAsHtml, true, false, true),
			SAVE_AS_JSON_STRING = MENU_BAR_CONTROL.QUIT.createSelection( 'get JsonStr', null, outputAsJsonString, true, false, true),
			QUIT = MENU_BAR_CONTROL.QUIT.createSelection( 'quit', null, quit, true, true),
			updated = false;
		
		function quit(){
		}
		
		function outputAsHtml(){
			alert( CANVAS_CONTROL.getAsHTML( true, false));
		}
		function outputAsJsonString(){
			alert( CANVAS_CONTROL.getAsJsonString());
		}
		return {
			quit: quit,
			panelUpdated: function( _updated){
				if( _updated !== undefined && updated !== _updated){
					SAVE.visible( !!_updated);
					SAVE_AND_QUIT.visible( !!_updated);
					updated = !!_updated;
				}
				return updated;
			},
			save: function(){
				
			}
		}
	})();

/* ----------------------------------------
 *     Text Editor (Overlay)
 */
	
	var TEXT_EDITOR_CONTROL = ( function(){
		var jqWrap, jqTextarea, jqButton,
			textElement, onUpdateFunction;
		
		h2c.key.addKeyDownEvent( this.ID, 69, false, false, clickOK);
		
		function close(){
			jqWrap.hide();
			textElement = onUpdateFunction = null;		
		}
		function clickOK(){
			h2c.overlay.hide();
			textElement && textElement.text( jqTextarea.val());
			onUpdateFunction && onUpdateFunction( textElement);
			close();			
		}
		return {
			init: function(){
				this.jqWrap = jqWrap = $( '#speach-editor-wrapper').hide();
				jqTextarea = $( '#speach-editor').keydown( function( e){
					if( e.keyCode === 69 && e.shiftKey === false && e.ctrlKey === true){
						clickOK();
						e.preventDefault();
				        e.keyCode = 0;
				        e.cancelBubble = true;
				        e.returnValue = false;
						return false;
					}
				});
				jqButton = $( '#speach-edit-complete-button').click( clickOK);
				delete TEXT_EDITOR_CONTROL.init;
			},
			jqWrap: null,
			show: function( _textElement, _onUpdateFunction){
				textElement = _textElement;
				onUpdateFunction = _onUpdateFunction || null;
				h2c.overlay.show( this);
				var h = _textElement.h;
				jqWrap.show().css( {
					left:			_textElement.x +CANVAS_CONTROL.x(),
					top:			_textElement.y +CANVAS_CONTROL.y(),
					width:			_textElement.w,
					height:			h
				});
				jqTextarea.val( _textElement.text()).focus();
				
				/*
				 * ie6,7は、textarea { width:100%}でも高さが変わらない。rowsを設定。
				 */
				h2c.isIE === true && h2c.ieVersion <= 7 && setTimeout( function(){
					var rows = 0;
					while( jqTextarea.height() < h){
						rows++;
						jqTextarea.attr( 'rows', rows);
					}
					rows > 1 && jqTextarea.attr( 'rows', rows -1);
				}, 0);
			},
			onWindowResize: function(){
				textElement && this.show( textElement);
			},
			onClose: close,
			ID: 'textEditor'
		}
	})();

/* ----------------------------------------
 *     Image Group Exproler (Overlay)
 */
	var IMAGE_GROUP_EXPROLER = ( function(){
		var ICON_ARRAY = [],
			WHEEL_DELTA = 64,
			containerW, containerH, wrapX,
			jqWrap, jqContainer, jqItemOrigin,
			itemW, itemH,
			jqName, jqButton, buttonW,
			onUpdateFunction,
			winW,
			onEnterInterval = null;
		
		var BASE_PATH = h2c.LOCAL === false ? 'http://pettan.heroku.com/images/' : 'images/',
			THUMB_PATH = BASE_PATH, // + 'thumbnail/',
			LIMIT_FILESIZE = 1024 * 10; // 10KB
		var IMAGE_DATA = {
				pen001: [
				    {
				        "created_at": "2011-11-13T08:57:39Z", 
				        "ext": "png", 
				        "filesize": 9969, 
				        "height": 463, 
				        "id": 1, 
				        "updated_at": "2011-11-13T08:57:39Z", 
				        "width": 441
				    }, 
				    {
				        "created_at": "2011-11-13T08:57:54Z", 
				        "ext": "gif", 
				        "filesize": 5418, 
				        "height": 500, 
				        "id": 2, 
				        "updated_at": "2011-11-13T08:57:54Z", 
				        "width": 500
				    }, 
				    {
				        "created_at": "2011-11-13T08:58:06Z", 
				        "ext": "gif", 
				        "filesize": 8758, 
				        "height": 464, 
				        "id": 3, 
				        "updated_at": "2011-11-13T08:58:06Z", 
				        "width": 366
				    }, 
				    {
				        "created_at": "2011-11-13T08:58:23Z", 
				        "ext": "gif", 
				        "filesize": 9383, 
				        "height": 480, 
				        "id": 4, 
				        "updated_at": "2011-11-13T08:58:23Z", 
				        "width": 392
				    }, 
				    {
				        "created_at": "2011-11-13T08:58:33Z", 
				        "ext": "gif", 
				        "filesize": 11061, 
				        "height": 500, 
				        "id": 5, 
				        "updated_at": "2011-11-13T08:58:33Z", 
				        "width": 500
				    }, 
				    {
				        "created_at": "2011-11-20T09:50:43Z", 
				        "ext": "gif", 
				        "filesize": 1131, 
				        "height": 126, 
				        "id": 6, 
				        "updated_at": "2011-11-20T09:50:43Z", 
				        "width": 259
				    }, 
				    {
				        "created_at": "2011-11-20T09:50:55Z", 
				        "ext": "gif", 
				        "filesize": 1125, 
				        "height": 126, 
				        "id": 7, 
				        "updated_at": "2011-11-20T09:50:55Z", 
				        "width": 259
				    }, 
				    {
				        "created_at": "2011-11-20T11:33:12Z", 
				        "ext": "gif", 
				        "filesize": 17919, 
				        "height": 600, 
				        "id": 8, 
				        "updated_at": "2011-11-20T11:33:12Z", 
				        "width": 800
				    },
				    {
				        "created_at": "2011-11-20T11:33:12Z", 
				        "ext": "gif", 
				        "filesize": 17919, 
				        "height": 600, 
				        "id": 9, 
				        "updated_at": "2011-11-20T11:33:12Z", 
				        "width": 800
				    },
				    {
				        "created_at": "2011-11-20T11:33:12Z", 
				        "ext": "gif", 
				        "filesize": 17919, 
				        "height": 600, 
				        "id": 10, 
				        "updated_at": "2011-11-20T11:33:12Z", 
				        "width": 800
				    },
				    {
				        "created_at": "2011-11-20T11:33:12Z", 
				        "ext": "gif", 
				        "filesize": 17919, 
				        "height": 600, 
				        "id": 11, 
				        "updated_at": "2011-11-20T11:33:12Z", 
				        "width": 800
				    },
				    {
				        "created_at": "2011-11-22T09:17:20Z", 
				        "ext": "gif", 
				        "filesize": 9055, 
				        "height": 473, 
				        "id": 12, 
				        "updated_at": "2011-11-22T09:17:20Z", 
				        "width": 405
				    }, 
				    {
				        "created_at": "2011-11-22T10:11:07Z", 
				        "ext": "gif", 
				        "filesize": 8758, 
				        "height": 464, 
				        "id": 13, 
				        "updated_at": "2011-11-22T10:11:07Z", 
				        "width": 366
				    }, 
				    {
				        "created_at": "2011-11-24T09:05:12Z", 
				        "ext": "gif", 
				        "filesize": 6431, 
				        "height": 386, 
				        "id": 16, 
				        "updated_at": "2011-11-24T09:05:12Z", 
				        "width": 453
				    }, 
				    {
				        "created_at": "2011-11-26T04:52:12Z",
				        "ext": "gif", 
				        "filesize": 6421, 
				        "height": 426, 
				        "id": 17, 
				        "updated_at": "2011-11-26T04:52:12Z", 
				        "width": 306
				    }, 
				    {
				        "created_at": "2011-11-26T04:52:12Z",
				        "ext": "gif", 
				        "filesize": 6421, 
				        "height": 426, 
				        "id": 18, 
				        "updated_at": "2011-11-26T04:52:12Z", 
				        "width": 306
				    }, 
				    {
				        "created_at": "2011-11-26T04:52:12Z",
				        "ext": "gif", 
				        "filesize": 6421, 
				        "height": 426, 
				        "id": 19, 
				        "updated_at": "2011-11-26T04:52:12Z", 
				        "width": 306
				    }, 
				    {
				        "created_at": "2011-11-26T04:52:12Z",
				        "ext": "gif", 
				        "filesize": 6421, 
				        "height": 426, 
				        "id": 20, 
				        "updated_at": "2011-11-26T04:52:12Z", 
				        "width": 306
				    }, 
				    {
				        "created_at": "2011-11-26T04:52:12Z",
				        "ext": "gif", 
				        "filesize": 6421, 
				        "height": 426, 
				        "id": 21, 
				        "updated_at": "2011-11-26T04:52:12Z",
				        "width": 306
				    }
				]
			}
		
		var ImageGroupIconClass = function( INDEX, data){
			var JQ_ICON_WRAP = jqItemOrigin.clone( true),
				SRC = [ BASE_PATH, data.id, '.', data.ext].join( ''),
				LOW_SRC = data.filesize && data.filesize > LIMIT_FILESIZE ? [ THUMB_PATH, data.id, '.', data.ext].join( '') : null,
				reversibleImage = null,
				onEnterFlag = false;
			JQ_ICON_WRAP.children( 'div').eq( 0).html( data.filesize + 'bytes');
			jqContainer.append( JQ_ICON_WRAP.css( { left: INDEX * itemW}));
			
			return {
				onEnter: function(){
					if( onEnterFlag === true) return;
					reversibleImage = h2c.image.createReversibleImage( LOW_SRC || SRC, itemW, itemH, function( url, imgW, imgH){
						if( reversibleImage === null) {
							alert( url);
							return;
						}
						/*
						 * ieでサイズが取れない、、、
						 */
						imgW = imgW || data.width || 64;
						imgH = imgH || data.height || 64;
						JQ_ICON_WRAP.children( 'div').eq( 1).html( imgW +'x' +imgH);
						var zoom = 128 /( imgW > imgH ? imgW : imgH),
							h = Math.floor( imgH *zoom),
							w = Math.floor( imgW *zoom);
						reversibleImage.elm.style.width = w +'px';
						reversibleImage.elm.style.height = h +'px';
						reversibleImage.elm.style.margin = Math.floor( itemH /2 -h /2)+'px 0 0';
						reversibleImage.resize( w, h);
						JQ_ICON_WRAP.click( function( e){
							h2c.overlay.hide();
							if (onUpdateFunction) {
								if( LOW_SRC === null){
									onUpdateFunction( SRC, imgW, imgH);
								} else {
									( function( onUpdate){
										h2c.util.loadImage( SRC,
											function( _abspath, imgW, imgH){
												onUpdate( SRC, imgW, imgH);
												onUpdate = null;
											},
											function( _abspath){
												onUpdate( SRC, data.width || 64, data.height || 64);
												onUpdate = null;
											}
										);										
									})( onUpdateFunction); // close()で値が消えるので、クロージャに保持
								}
							}
							close();
						});
					});
					JQ_ICON_WRAP.children( 'img').replaceWith( reversibleImage.elm);
					onEnterFlag = true;				
				},
				destroy: function(){
					reversibleImage && reversibleImage.destroy();
					JQ_ICON_WRAP.remove();
					reversibleImage = JQ_ICON_WRAP = null;
					delete this.destroy;
				}
			}
		}
		
		function close(){
			jqContainer.stop().animate( {
					height:	0,
					top:	Math.floor( windowH /2)
				}, function(){
					jqWrap.hide()
				});
			while( ICON_ARRAY.length > 0){
				ICON_ARRAY.shift().destroy();
			}
			onEnterInterval !== null && window.clearTimeout( onEnterInterval);
			onUpdateFunction = onEnterInterval = null;
		}
		function onEnterShowImage(){
			var l = ICON_ARRAY.length,
				_start = -wrapX /itemW -1,
				_end = _start + winW /itemW +1;
			for( var i=0; i<l; ++i){
				_start < i && i < _end && ICON_ARRAY[ i].onEnter();
			}
			onEnterInterval !== null && window.clearTimeout( onEnterInterval);
			onEnterInterval = null;
		}
		function clickOK(){
			h2c.overlay.hide();
			// onUpdateFunction && onUpdateFunction( textElement);
			close();
		}
		return {
			init: function(){
				this.jqWrap = jqWrap = $( '#image-gruop-wrapper').hide();
				jqContainer = $( '#image-icon-container').mousewheel(
					function( e, delta){
						if( winW < containerW){
							wrapX += delta *WHEEL_DELTA;
							wrapX = wrapX > 0 ? 0 : wrapX < winW -containerW ? winW -containerW : wrapX;
							jqContainer.css( { left: wrapX});
							
							onEnterInterval !== null && window.clearTimeout( onEnterInterval);
							onEnterInterval = window.setTimeout( onEnterShowImage, 500);
						}
						//e.stopPropagation();
						return false;
					});
				containerH = h2c.util.getElementSize( jqContainer.get( 0)).height;
				jqItemOrigin = $( $( '#imageGruopItemTemplete').remove().html());
				var itemSize = h2c.util.getElementSize( jqItemOrigin.get( 0));
				itemW = itemSize.width;
				itemH = itemSize.height;
				jqName = $( '#gruop-name-display');
				jqButton = $( '#image-gruop-button').click( clickOK);
				buttonW = h2c.util.getElementSize( jqButton.get( 0)).width;
				
				delete IMAGE_GROUP_EXPROLER.init;
			},
			jqWrap: null,
			show: function( _onUpdateFunction){
				onUpdateFunction = _onUpdateFunction;
				h2c.overlay.show( this);
				
				var CURRENT_GROUP_ARRAY = IMAGE_DATA[ 'pen001'] || [],
					l = CURRENT_GROUP_ARRAY.length;
				for( var i=0; i<l; ++i){
					ICON_ARRAY.push( ImageGroupIconClass.apply( {}, [ i, CURRENT_GROUP_ARRAY[ i]]));
				}
				wrapX = 0;
				containerW = l * itemW;
				
				winW = windowW;
				var w = winW > containerW ? winW : containerW,
					h = windowH > containerH ? containerH : windowH;
				
				jqWrap.show();
				jqContainer.css( {
					width:		w,
					height:		0,
					left:		0,
					top:		Math.floor( windowH /2)
				}).stop().animate( {
					height: 	h,
					top:		Math.floor( windowH /2 -h /2)
				});
				
				jqButton.css( {
					left:		Math.floor( winW /2 -buttonW /2),
					top:		Math.floor( windowH /2 +containerH /2 +10)
				});
				
				onEnterShowImage();
			},
			onWindowResize: function( _windowW, _windowH){
				var w = _windowW > containerW ? _windowW : containerW,
					h = _windowH > containerH ? containerH : _windowH,
					offsetW = Math.floor( _windowW /2 -winW /2);
				winW = _windowW;
				if( offsetW <= 0){ // smaller
					jqContainer.css( {
						left:				offsetW,
						width:				w
					}).animate( {
						left:				0,
						top:				Math.floor( _windowH /2 -h /2)
					});					
				} else {
					jqContainer.css( { // bigger
						left:				0,
						width:				w,
						borderLeftWidth:	offsetW
					}).animate( {
						top:				Math.floor( _windowH /2 -h /2),
						borderLeftWidth:	0
					});
				}
				jqButton.css( {
					left:		Math.floor( _windowW /2 -buttonW /2),
					top:		Math.floor( _windowH /2 +containerH /2 +10)
				});
				onEnterShowImage();
			},
			onClose: close,
			ID: 'imageGroupExproler'
		}
	})();

/* ----------------------------------------
 * WINDOWS_CONTROL
 */	
	var WINDOWS_CONTROL = ( function(){
		/*
		 *  表示上手前にあるwindowは、WINDOW_ARRAYの先頭にあり、htmlでは後ろにある。
		 */
		var DEFAULT_MIN_WINDOW_WIDTH = 200,
			DEFAULT_MIN_WINDOW_HEIGHT = 200,
			WINDOW_ARRAY = [],
			jqContainer,
			currentWindow,
			currentWindowIndex = -1,
			log;

		var jqWindowOrigin,
			closeButtonWidth;
		var WindowClass = function( bodyTempleteID, title, x, y, w, h, visible, CLOSE_BUTTON_ENABLED, RESIZE_BUTTON_ENABLED, minWindowW, minWindowH){
			var MOUSE_CURSOR = updateMouseCursor,
				MENUBAR_SELWCTION = MENU_BAR_CONTROL.WINDOW.createSelection( 
					( visible !== true ? 'show ' : 'hide ') +title,
					null,
					function(){
						visible === true ? instance.close() : instance.open();
					}, true
				),
				jqStage,
				jqWrapper, jqHeader, elmBody, elmBodyStyle,
				startX, startY, startW, startH,
				xOffset, yOffset,
				headerH, bodyH,
				isDragging = false,
				isResizing = false,
				bodyIsTachable = false,
				instance;
			
			function update( _x, _y, _w, _h){
				( w !== _w || h !== _h) && instance.onResize && instance.onResize( _w, _h);
				x = _x !== undefined ? _x : x;
				y = _y !== undefined ? _y : y;
				w = _w !== undefined ? _w : w;
				h = _h !== undefined ? _h : h;
				y = y > MENU_BAR_CONTROL.h ? y : MENU_BAR_CONTROL.h;
				jqWrapper.css( {
					left:		x,
					top:		y,
					width:		w,
					height:		h
				});
				bodyH = h -headerH;
				elmBodyStyle.height = bodyH +'px';
			}
			function bodyBackOrForward( isBack){
				if( !instance) return;
				if( bodyIsTachable === !isBack) return;
				elmBodyStyle.position =	isBack === true ? 'relative' : 'absolute';
				elmBodyStyle.left =		isBack === true ? 0  : x +'px';
				elmBodyStyle.top =		isBack === true ? 0  : y +headerH +'px';
				elmBodyStyle.width =	isBack === true ? '' : w +'px';
				bodyIsTachable === isBack && isBack === true ? jqWrapper.append( elmBody) : jqStage.append( elmBody);
				bodyIsTachable = !isBack;
			}
			function onWindowResize( e){
				bodyBackOrForward( true);
				isResizing = true;
				startX = x;
				startY = y;
				startW = w;
				startH = h;
				xOffset = e.pageX;
				yOffset = e.pageY;
				MOUSE_CURSOR( 'nw-resize');
				e.stopPropagation();
				return false;
			}
			return {
				init: function( jqContainer){
					/*
					 * setTimeout で呼ばれるグローバルメソッドは、this でなく instance を使う.
					 */
					instance = this;
					
					jqWindowOrigin = jqWindowOrigin || ( function(){
						return $( $( '#windowTemplete').remove().html());
					})();
					closeButtonWidth = closeButtonWidth || ( function(){
						return h2c.util.getElementSize( jqWindowOrigin.clone( true).find( '.window-close-button').get( 0)).width;
					})();
					
					jqStage = jqEditor;
					this.$ = jqWrapper = jqWindowOrigin.clone( true);
					jqHeader = jqWrapper.children( '.window-header').eq( 0).html( title);
					headerH = h2c.util.getElementSize( jqHeader.get( 0)).height;
					elmBody = jqWrapper.children( '.window-body').get( 0);
					elmBodyStyle = elmBody.style;
					if( bodyTempleteID) {
						jqWrapper.find( '.window-body-insert-position').replaceWith( $( $( '#' +bodyTempleteID).remove().html()));
					} else {
						jqWrapper.find( '.window-body-insert-position').remove();
					}
					CLOSE_BUTTON_ENABLED !== true && jqWrapper.find( '.window-close-button').remove();
					
					this.onInit && this.onInit();
					delete this.init;
				},
				x: function(){ return x;},
				y: function(){ return y;},
				w: function(){ return w;},
				h: function(){ return h;},
				$: null,
				title: function( _title){
					_title !== undefined && jqHeader.html( _title);
					title = _title || title;
					return title;
				},
				visible: visible,
				firstOpen: function(){
					if( RESIZE_BUTTON_ENABLED === true){
						jqWrapper.find( '.window-resize-button').eq( 0).mousedown( onWindowResize);
					} else {
						jqWrapper.find( '.window-resize-button').remove();
					}
					update( x, y, w, h);
					
					this.onFirstOpen && this.onFirstOpen();
					
					delete this.firstOpen;
				},
				open: function(){
					if( visible === true) return;
					instance.visible = visible = true;
					openWindow( instance);
					MENUBAR_SELWCTION.title( 'hide ' +title);
					
					for( var i=0, l = WINDOW_ARRAY.length; i<l; ++i){
						if( WINDOW_ARRAY[ i] === instance){
							WINDOW_ARRAY.splice( i, 1);
							WINDOW_ARRAY.unshift( instance);
							currentWindow = null;
							currentWindowIndex = -1;
						}
					}
				},
				close: function(){
					if( visible === false) return;
					instance.visible = visible = false;
					instance.onClose && setTimeout( instance.onClose, 0);
					closeWindow( instance);
					MENUBAR_SELWCTION.title( 'show ' +title);
				},
				bodyBackOrForward: bodyBackOrForward,
				onMouseDown: function( _mouseX, _mouseY){
					if( x > _mouseX || y > _mouseY || x +w < _mouseX || y +headerH < _mouseY ) return;
					if( CLOSE_BUTTON_ENABLED === true && x +w -closeButtonWidth < _mouseX){
						this.close();
						return;
					}
					isDragging = true;
					MOUSE_CURSOR( 'move');				
					startX = x;
					startY = y;
					startW = w;
					startH = h;
					xOffset = _mouseX;
					yOffset = _mouseY;
				},
				onMouseUp: function( _mouseX, _mouseY){
					isDragging = isResizing = false;
					MOUSE_CURSOR( '');
				},
				onMouseMove: function( _mouseX, _mouseY){
					var _updateX = _mouseX -xOffset,
						_updateY = _mouseY -yOffset;
					
					if( isResizing === true){
						var _w = startW +_updateX,
							_h = startH +_updateY;
						update( startX, startY, _w < minWindowW ? minWindowW : _w, _h < minWindowH ? minWindowH : _h);
						return;
					} else
					if( isDragging === true) {
						update( startX +_updateX, startY +_updateY);
						return;
					} else
					if( x > _mouseX || x +w < _mouseX ) return;
	
					( y <= _mouseY && y +headerH >= _mouseY ) ?
						MOUSE_CURSOR( 'pointer') :	// hit to header
						MOUSE_CURSOR( '');
					bodyBackOrForward( y +headerH > _mouseY || y +headerH +bodyH < _mouseY);
				},
				onMouseOut: function( _mouseX, _mouseY){
					bodyIsTachable === true && bodyBackOrForward( true);
					isDragging = false;
					MOUSE_CURSOR( '');
				},
				busy: function(){
					return isDragging === true || isResizing === true;
				},
				bodyHeight: function(){
					return	bodyH;
				}
			}
		};
		
		function getCurrentWindow( _mouseX, _mouseY){
			if( currentWindow && currentWindow.busy() === true) return currentWindowIndex;
			var l = WINDOW_ARRAY.length,
				_currentWindow = null,
				_win, _x, _y;
			currentWindowIndex = -1;
			for( var i=0; i<l; i++){
				_win = WINDOW_ARRAY[ i];
				if( _win.visible !== true) continue;
				_x = _win.x();
				_y = _win.y();
				if( _x <= _mouseX && _y <= _mouseY && _x +_win.w() >= _mouseX && _y +_win.h() >= _mouseY){
					_currentWindow = _win;
					currentWindowIndex = i;
					break;
				}
			}
			currentWindow && currentWindow !== _currentWindow && currentWindow.onMouseOut( _mouseX, _mouseY);
			currentWindow = _currentWindow;
			return currentWindowIndex;
		}
		function openWindow( _window){
			if( _window.visible !== true) return;
			var _jqWindow = _window.$;
			jqContainer.append( _jqWindow);// appendした後に fadeIn() しないと ie で filterが適用されない．
			_jqWindow.fadeIn(
				function(){
					_window.firstOpen && _window.firstOpen();
					_window.onOpen && setTimeout( _window.onOpen, 0);
				}
			);
			return;
		}
		function closeWindow( _window){
			if( _window.visible !== false) return;
			var l = WINDOW_ARRAY.length;
			for( var i=0; i<l; ++i){
				if( WINDOW_ARRAY[ i] === _window){
					//WINDOW_ARRAY.splice( i, 1);
					//WINDOW_ARRAY.push( _window);
					_window.$.stop().fadeOut( function(){
						this.parentNode.removeChild( this);
					});
					return;
				}
			}
		}
		
		return {
			init: function(){
				jqContainer = $( '#window-container');
				
				var l = WINDOW_ARRAY.length,
					_window;
				for( var i=l-1; i >= 0; --i){
					_window = WINDOW_ARRAY[ i];
					_window.init && _window.init( jqContainer);
					_window.visible === true && openWindow( _window);
				}
				log = $( '#window-log');
				
				delete WINDOWS_CONTROL.init;
			},
			onMouseMove: function( _mouseX, _mouseY){
				var _index = getCurrentWindow( _mouseX, _mouseY);
				if( _index === 0){
					currentWindow.onMouseMove( _mouseX, _mouseY);
					return true;
				} else
				if( _index !== -1){ // 先頭のクリックでない場合
				// Array を前に
					WINDOW_ARRAY.splice( currentWindowIndex, 1);
					WINDOW_ARRAY.unshift( currentWindow);
				// Domを最後に
					jqContainer.append( currentWindow.$);
					currentWindowIndex = 0;
					return true;
				}
				return false;
			},
			onMouseUp: function( _mouseX, _mouseY){
				if( getCurrentWindow( _mouseX, _mouseY) === 0){
					currentWindow.onMouseUp( _mouseX, _mouseY);
					return true;
				}
				return false;
			},
			onMouseDown: function( _mouseX, _mouseY){
				if( getCurrentWindow( _mouseX, _mouseY) === 0){
					currentWindow.onMouseDown( _mouseX, _mouseY);
					return true;
				}
				return false;
			},
			onMouseClick: function( _mouseX, _mouseY){
				return false;
			},
			busy: function(){
				return currentWindow !== null;
			},
			onWindowResize: function( _windowW, _windowH){
				/*
				 * 画面外に出るwindowの移動
				 */
			},
			createWindow: function( scope, EXTENDS, bodyTempleteID, title, x, y, w, h, opt_visible, opt_closeButtonEnabled, opt_resizeButtonEnabled, opt_minWindowW, opt_minWindowH){
				opt_visible = opt_visible !== false;
				opt_closeButtonEnabled = opt_closeButtonEnabled === true;
				opt_resizeButtonEnabled = opt_resizeButtonEnabled === true;
				opt_minWindowW = opt_minWindowW || ( w < DEFAULT_MIN_WINDOW_WIDTH) ? w : DEFAULT_MIN_WINDOW_WIDTH;
				opt_minWindowH = opt_minWindowH || ( h < DEFAULT_MIN_WINDOW_HEIGHT) ? h : DEFAULT_MIN_WINDOW_HEIGHT;
				
				var _window = h2c.util.extend(
					WindowClass.apply( scope, [ bodyTempleteID, title, x, y, w, h, opt_visible, opt_closeButtonEnabled, opt_resizeButtonEnabled, opt_minWindowW, opt_minWindowH]),
					EXTENDS
				);
				WINDOW_ARRAY.unshift( _window);
				WINDOWS_CONTROL.init === undefined && _window.init( jqContainer);
				WINDOWS_CONTROL.init === undefined && openWindow( _window);
				return _window;
			}
		}
	})();

/* ----------------------------------------
 * TOOL_BOX_WINDOW
 */
	var TOOL_BOX_WINDOW = ( function(){
		var addImageButton, addTextButton, editBgButton, switchGridButton, popupHelpButton, postButton,
			gridSwitchFunction,
			instance;
			
		h2c.key.addKeyDownEvent( h2c.view.EDITOR, 73, false, true, addImage);
		MENU_BAR_CONTROL.EDIT.createSelection( 'Add Image', 'ctrl + I', addImage, true, true, false);
		
		h2c.key.addKeyDownEvent( h2c.view.EDITOR, 84, false, true, addText);
		MENU_BAR_CONTROL.EDIT.createSelection( 'Add Text', 'ctrl + T', addText, true, false, true);

		h2c.key.addKeyDownEvent( h2c.view.EDITOR, 71, false, true, switchGrid);
		MENU_BAR_CONTROL.EDIT.createSelection( 'show Grid', 'ctrl + G', switchGrid, true, true, true);

		function addImage( e){
			setTimeout( CANVAS_CONTROL.createImageElement, 0);
			e && e.preventDefault();
			return false;
		}
		function addText( e){
			setTimeout( CANVAS_CONTROL.createTextElement, 0);
			e && e.preventDefault();
			return false;
		}
		function switchGrid( e){
			setTimeout( gridSwitchFunction, 0);
			e && e.preventDefault();
			return false;
		}
		function popupHelp( e){
			instance.bodyBackOrForward( true);
			setTimeout( HELP_DOCUMENTS_WINDOW.open, 0); // setTimeout で囲むと window が開かない
			e && e.preventDefault();
			return false;
		}
		function editBG( e){
			instance.bodyBackOrForward( true);
			setTimeout( INFOMATION_WINDOW.open, 0);	
			e && e.preventDefault();
			return false;
		}
		
		return WINDOWS_CONTROL.createWindow(
			this,
			{
				onInit: function(){
					instance = this;
					delete this.onInit;
				},
				onFirstOpen: function(){
					addImageButton = $( '#toolbox-add-image-button').click( addImage);
					addTextButton = $( '#toolbox-add-text-button').click( addText);
					editBgButton = $( '#toolbox-edit-bg-button').click( editBG);
					switchGridButton = $( '#toolbox-switch-grid').click( switchGrid);
					popupHelpButton = $( '#toolbox-popup-help-button').click( popupHelp);
					
					postButton = $( '#toolbox-post-button');
					
					delete this.onFirstOpen;
				},
				setGridSwitchFunction: function( _gridSwitchFunction){
					gridSwitchFunction = _gridSwitchFunction || gridSwitchFunction;
				}
			},
			'toolbox-window', 'Tool box', 0, 215, 110, 290, true
		);
	})();
/* ----------------------------------------
 * INFOMATION_WINDOW
 */			
	var INFOMATION_WINDOW = ( function(){
		var FADE_EFFECT_ENABLED = h2c.isIE === false || h2c.ieVersion >= 8,
			FADE_IN_EFFECT = FADE_EFFECT_ENABLED === true ? 'fadeIn' : 'show',
			FADE_OUT_EFFECT = FADE_EFFECT_ENABLED === true ? 'fadeOut' : 'hide',
			backgroundInfomationElm,
			jqComicElementInformation,
			elmValueX, elmValueY, elmValueZ, elmValueA, elmValueW, elmValueH, elmAspect,
			elmPercentW, elmPercentH,
			currentComicElement = null,
			currentElementType = -1;

		return WINDOWS_CONTROL.createWindow(
			this,
			{
				onFirstOpen: function(){
					backgroundInfomationElm = $( '#panel-background-information');
					
					jqComicElementInformation = $( '#comic-element-infomation').hide().css( {
						height:		this.bodyHeight()
					});
					var TAB_GROUP_ID = 'comic-element-attribute';
					var CREATER = h2c.key.createEditableText;
					elmValueX = CREATER( $( '#comic-element-x'), null, TAB_GROUP_ID);
					elmValueY = CREATER( $( '#comic-element-y'), null, TAB_GROUP_ID);
					elmValueZ = CREATER( $( '#comic-element-z'), null, TAB_GROUP_ID);
					elmValueA = CREATER( $( '#comic-element-a'), null, TAB_GROUP_ID);
					elmValueW = CREATER( $( '#comic-element-w'), null, TAB_GROUP_ID);
					elmValueH = CREATER( $( '#comic-element-h'), null, TAB_GROUP_ID);
					elmPercentW = CREATER( $( '#comic-element-w-percent'), null, TAB_GROUP_ID);
					elmPercentH = CREATER( $( '#comic-element-h-percent'), null, TAB_GROUP_ID);
					elmAspect = $( '#comic-element-keep-aspect');
					delete this.onFirstOpen;
				},
				onResize: function( w, h){
					jqComicElementInformation && jqComicElementInformation.css( {
						height: this.bodyHeight()
					});
				},
				update: function( _elementType, x, y, z, a, w, h, wPercent, hPercent, keepAspect){
					if( !backgroundInfomationElm) return; // なぜか !backgroundInfomationElm が必要
					if( currentElementType !== _elementType){
						if( _elementType !== -1){
							if( _elementType === 1){
								elmValueA.show();
								elmPercentW.hide();
								elmPercentH.hide();
								elmAspect.hide();
							} else {
								elmValueA.hide();
								elmPercentW.show();
								elmPercentH.show();
								elmAspect.show();
							}
							currentElementType === -1 && jqComicElementInformation.stop().css( {
								filter:		'',
								opacity:	''
							})[ FADE_IN_EFFECT]();
						} else {
							currentElementType !== -1 && jqComicElementInformation.stop().css({
								filter:		'',
								opacity:	''
							})[ FADE_OUT_EFFECT]();
						}
						currentElementType = _elementType;
					}
					if( currentElementType !== -1){
						elmValueX.update( x);
						elmValueY.update( y);
						elmValueZ.update( z);
						_elementType === 1 && elmValueA.update( a);
						elmValueW.update( w);
						elmValueH.update( h);
						_elementType === 0 && elmPercentW.update( wPercent);
						_elementType === 0 && elmPercentH.update( hPercent);					
					} else {
						
					}
				}
			},
			'infomation-window', 'Infomation', 0, 30, 200, 180, true
		);
	})();

/* ----------------------------------------
 * HELP_WINDOW
 */
	var HELP_DOCUMENTS_WINDOW = ( function(){
		var visible = true,
			hasAjaxContents = false,
			jqAjaxContents,
			jqNaviItems,
			jqPages,
			HELP = MENU_BAR_CONTROL.HELP,
			onLoadFunction = HELP.createAjaxSelection( onFirstOpen),
			instance;
		function jumpPage( _index){
			
		}
		function onFirstOpen( _pageIndex){
			_pageIndex = _pageIndex || 0;
			if( hasAjaxContents === false){
				$.ajax({
					url:		'help/jp.xml',
					dataType:	'xml',
					success:	function( _xml){
						var jqXML = $( _xml),
							helpTitle = jqXML.find( 'pages').eq( 0).attr( 'title'),
							elmNavi = document.createElement( 'DIV'),
							elmItemOrigin = document.createElement( 'A'),
							elmPages = document.createElement( 'DIV'),
							elmPageOrigin = document.createElement( 'DIV'),
							elmTitleOrigin = document.createElement( 'H2'),
							elmPage,
							numPage = 0;
						elmNavi.className = 'sidenavi';
						elmItemOrigin.className = 'sidenavi-item';
						elmItemOrigin.href = '#';
						elmPages.className = 'page-contents';
						elmPageOrigin.className = 'page-content';
						elmPageOrigin.appendChild( elmTitleOrigin);
						
						// helpTitle && instance.title( helpTitle);
						
						jqXML.find( 'page').each( function(){
							var xmlPage = $( this),
								title = xmlPage.attr( 'title'),
								content = xmlPage.text();
							
							elmItemOrigin.innerHTML = title;
							elmNavi.appendChild( elmItemOrigin.cloneNode( true));
							
							elmTitleOrigin.innerHTML = title;
							elmPage = elmPageOrigin.cloneNode( true);
							elmPage.innerHTML = content;
							
							h2c.util.cleanElement( elmPage);
							
							if( elmPage.childNodes.length > 0){
								elmPage.insertBefore( elmTitleOrigin.cloneNode( true), elmPage.childNodes[0]);
							} else {
								elmPage.appendChild( elmTitleOrigin.cloneNode( true));
							}
							elmPages.appendChild( elmPage);
							
							HELP.createSelection( title, null, ( function( _pageIndex){
								return function(){
									HELP_DOCUMENTS_WINDOW.open();
									onOpen( _pageIndex);									
								}
							})( numPage), true);
							++numPage;
						});
						onLoadFunction();
						onLoadFunction = null;
						
						jqAjaxContents.removeClass( 'loading').append( elmNavi, elmPages);
						
						jqNaviItems = jqAjaxContents.find( 'a.' +elmItemOrigin.className)
							.click( function( e){
								var that = this,
									parent = this.parentNode,
									i = ( function(){
										var children = parent.getElementsByTagName( 'A'),
											l = children.length;
										for( var i=0; i<l; ++i){
											if( children[ i] === that) return i;
										}
										return -1;
									})();
								e.stopPropagation();
								if( i === -1) return false;
								jqNaviItems.removeClass( 'current').eq( i).addClass( 'current');
								jqPages.hide().eq( i).show();
								
								return false;
							});
						jqAjaxContents.find( '.' +elmPageOrigin.className).find( 'a')
							.click( function( e){
								var that = this,
									i = ( function(){
										var href = that.href,
											i = href.split( '#jump'),
											n = i[1]
										if( n && '' +parseFloat( n) === n){
											return parseFloat( n)
										}
										return -1;
									})();
								e.stopPropagation();
								if( i === -1) return false;
								jqNaviItems.removeClass( 'current').eq( i).addClass( 'current');
								jqPages.hide().eq( i).show();
								
								return false;
							});
						jqNaviItems.eq( _pageIndex).addClass( 'current');
						jqPages = jqAjaxContents.find( '.page-content');
						jqPages.eq( _pageIndex).show();
					}
				});
				hasAjaxContents = true;
			}
			function onOpen( _pageIndex){
				_pageIndex = _pageIndex || 0;
				jqNaviItems.removeClass( 'current').eq( _pageIndex).addClass( 'current');
				jqPages.hide().eq( _pageIndex).show();
			}
		}
		return WINDOWS_CONTROL.createWindow(
			this,
			{
				onInit: function(){
					instance = this;
					jqAjaxContents = this.$.find( '.window-body').addClass( 'loading').css( { height: this.bodyHeight()});
					delete this.onInit;
				},
				onFirstOpen: onFirstOpen,
				onResize: function( w, h){
					jqAjaxContents && jqAjaxContents.css( { height: this.bodyHeight()});
				},
				setAjaxContent: function( html){
					
					delete this.onLoadAjaxContent;
				}
			},
			null, 'Help', 0, 215, 400, 350, false, true, true, 300, 300
		);
	})();

/* ----------------------------------------
 *    - CANVAS_CONTROL
 *       - GRID_CONTROL
 *       - WHITE_GLASS_CONTROL
 *       - PANEL_BORDER_CONTROL
 *       - COMIC_ELEMENT_CONTROL
 *          - PanelResizerClass
 *          - COMIC_ELEMENT_OPERATOR
 *          - ImageElementClass
 *          - TextElementClass
 */	
	var CANVAS_CONTROL = ( function(){
		var DEFAULT_PANEL_WIDTH = 400,
			DEFAULT_PANEL_HEIGHT = 300,
			MIN_PANEL_HEIGHT = 20,
			BORDER_WIDTH = 2,
			RESIZER_BORDER_WIDTH = 1,
			SPACE_ENABLED = h2c.key.spaceEnabled,
			canvasW, canvasH, canvasX, canvasY,
			xOffset, yOffset, startCanvasX, startCanvasY,
			isDragging = false,
			isCanvasDraggble = false;
		
		var GRID_CONTROL = ( function(){
			var elmGrid = document.getElementById( 'grid'),
				jQGrid,
				visible = false;
			
			function update(){
				jQGrid.css( {
					opacity:	'',
					fliter:		''
				}).stop()[ visible === true ? 'fadeOut' : 'fadeIn']();
				visible = !visible;
				if( visible === true){
					elmGrid.style.backgroundImage = "url('grid.gif')";
				}
				return visible;
			}
			return {
				init: function(){
					jQGrid = $( elmGrid);
					this.resize();
					TOOL_BOX_WINDOW.setGridSwitchFunction( update);
					delete GRID_CONTROL.init;
				},
				resize: function(){
					elmGrid.style.backgroundPosition = [ canvasX % 10, 'px ', canvasY % 10, 'px'].join( '');
					elmGrid.style.height = windowH +'px';				
				},
				enabled: function(){
					return visible;
				}
			}
		})();
		
	/*
	 *	WHITE_GLASS_CONTROL
	 */	
		var WHITE_GLASS_CONTROL = ( function(){
			var styleTop = document.getElementById( 'whiteGlass-top').style,
				styleLeft = document.getElementById( 'whiteGlass-left').style,
				styleRight = document.getElementById( 'whiteGlass-right').style,
				styleBottom = document.getElementById( 'whiteGlass-bottom').style;
			function resize(){
				var	_w = canvasW,
					_h = canvasH,
					marginTop = canvasY,
					marginBottom = windowH -_h -marginTop,
					marginX = canvasX,
					rightWidth = windowW -_w -marginX;
				
				styleTop.height = ( marginTop < 0 ? 0 : marginTop) +'px';
				
				styleLeft.top =	marginTop +'px';
				styleLeft.width = ( marginX < 0 ? 0 : marginX) +'px';
				styleLeft.height = ( _h + marginBottom) +'px';
				
				styleRight.top = marginTop +'px';
				styleRight.left = _w +marginX +'px';
				styleRight.width = ( rightWidth < 0 ? 0 : rightWidth) +'px';
				styleRight.height = ( _h + marginBottom) +'px';
				
				styleBottom.top = ( _h +marginTop) +'px';
				styleBottom.left = marginX +'px';
				styleBottom.width = _w +'px';
				styleBottom.height = ( marginBottom < 0 ? 0 : marginBottom) +'px';
			}
			return {
				resize: resize
			}
		})();

	/*
	 * PANEL_BORDER_CONTROL
	 */
		var PANEL_BORDER_CONTROL = ( function(){
			var	panelElm, borderWidth;
			
			function resize(){
				panelElm.css( {
					left:	canvasX -BORDER_WIDTH,
					top:	canvasY -BORDER_WIDTH,
					width:	canvasW,
					height:	canvasH
				});
			}
						
			return {
				init: function(){
					panelElm = $( '#panel-border');
					borderWidth = panelElm.css( 'border-width');
					
					delete PANEL_BORDER_CONTROL.init;
				},
				resize: resize
			}
		})();
	
	/*
	 * COMIC_ELEMENT_CONTROL
	 *   - PanelResizerClass
	 *   - COMIC_ELEMENT_OPERATOR
	 *   - ImageElementClass
	 *   - TextElementClass
	 */
		var COMIC_ELEMENT_CONTROL = ( function( _onResizeFunction){
			var	MIN_OBJECT_SIZE = 19,
				log,
				currentElement = null,
				_canvasX, _canvasY, _canvasW, _canvasH,
				MOUSE_HIT_AREA = 10,
				SAVE = HISTORY.saveState;
		/*
		 * --------------------------------------------------------------------------------------------
		 * panel resizer
		 */
			var PanelResizerClass = function( ELM, onPanelResizeFunction, isTop){
				var	BORDER_WIDTH = 2,
					yOffset, y, _canvasY, _canvasH,
					RESIZER_HEIGHT = 30,
					isDragging = false,
					SAVE = HISTORY.saveState,
					MOUSE_CURSOR = updateMouseCursor;
					
				function restoreState( arg){
					if( arg && arg.length > 3){
						canvasX = arg[ 0] || canvasX;
						canvasY = arg[ 1] || canvasY;
						canvasW = arg[ 2] || canvasW;
						canvasH = arg[ 3] || canvasH;
						onPanelResizeFunction( isTop);
					}
				}
					
				return {
					init: function( _elm){
						this.$ = $( ELM);
						ELM.style.width = ( canvasW +2)+'px';
					},
					$: null,
					hitareaX: function(){ return -1;},
					hitareaY: function(){ return isTop === true ? ( -35 -BORDER_WIDTH) : ( canvasH +5 +BORDER_WIDTH);},
					hitareaW: function(){ return canvasW +2;},
					hitareaH: function(){ return RESIZER_HEIGHT;},
					resize: resize,
					busy: function(){
						return isDragging;
					},
					onMouseDown: function( _mouseX, _mouseY){
						yOffset = _mouseY;
						_canvasY = canvasY;
						_canvasH = canvasH;
						isDragging = true;
					},
					onMouseMove: function( _mouseX, _mouseY){
						if( isDragging !== true){
							COMIC_ELEMENT_OPERATOR.hide();
							MOUSE_CURSOR( 'n-resize');
						} else {
							var move = _mouseY -yOffset;
							if( isTop === true){	
								//if( 0 < _canvasH - move){
									if( _canvasH - move < MIN_PANEL_HEIGHT){
										move = _canvasH -MIN_PANEL_HEIGHT;
									}
									canvasY = _canvasY +move;
									canvasH = _canvasH -move;
									log.html( move)
									onPanelResizeFunction( true);
								//}								
							} else {
								var _h = _canvasH +move;
								if( 0 < _h && _h < windowH -canvasY -RESIZER_HEIGHT -5 -BORDER_WIDTH){
									if( _h < MIN_PANEL_HEIGHT){
										_h = MIN_PANEL_HEIGHT;
									}
									canvasH = _h;
									onPanelResizeFunction( false);
								}
							}
						}
					},
					onMouseUp: function( _mouseX, _mouseY){
						( _canvasY !== canvasY || _canvasH !== canvasH) && SAVE( restoreState, [ NaN, _canvasY, NaN, _canvasH], [ NaN, canvasY, NaN, canvasH]);
						isDragging = false;
					}
				}
			};
			
			var	PANEL_RESIZER_TOP = PanelResizerClass.apply( {}, [ document.getElementById( 'panel-resizer-top'), _onResizeFunction, true]),
				PANEL_RESIZER_BOTTOM = PanelResizerClass.apply( {}, [ document.getElementById( 'panel-resizer-bottom'), _onResizeFunction, false]),
				DRAGGABLE_ELEMENT_ARRAY = [ PANEL_RESIZER_TOP, PANEL_RESIZER_BOTTOM],
				NUM_RESIZER = DRAGGABLE_ELEMENT_ARRAY.length;
			
			_onResizeFunction = PanelResizerClass = undefined;
		
		/*
		 * --------------------------------------------------------------------------------------------
		 * COMIC_ELEMENT_OPERATOR
		 */
			var COMIC_ELEMENT_OPERATOR = ( function(){
				var	MOUSE_CURSOR = updateMouseCursor,
					SAVE = HISTORY.saveState,
					INFOMATION = INFOMATION_WINDOW.update,
					GRID_ENABLED = GRID_CONTROL.enabled,
					HIT_AREA = MOUSE_HIT_AREA,
					currentIsTextElement = false,
					currentControler = null,
					currentElement = null,
					x, y, w, h, angle, flipV, flipH,
					COMIC_ELEMENT_CONSOLE = ( function(){
						var LAYER_BACK_BUTTON = MENU_BAR_CONTROL.EDIT.createSelection( 'layer back', 'ctrl + B', layerBack, false, true, false),
							LAYER_FORWARD_BUTTON = MENU_BAR_CONTROL.EDIT.createSelection( 'layer forward', 'ctrl + F', layerForward, false, false, false),
							DELETE_BUTTON = MENU_BAR_CONTROL.EDIT.createSelection( 'delete', 'ctrl + D', del, false, true, true),
							EDIT_BUTTON = MENU_BAR_CONTROL.EDIT.createSelection( 'Edit Text', 'ctrl + E', edit, false, true, false),
							CHANGE_BUTTON = MENU_BAR_CONTROL.EDIT.createSelection( 'change', 'ctrl + U', change, false, false, true),
							jqStage,
							jqConsoleParent,
							jqConsoleWrapper,
							jqConsoleTail,
							jqImgConsole, jqTextConsole,
							currentElement = null,
							currentType = -1,
							visible = false,
							imgConsoleWidth, imgConsoleHeight,
							textConsoleWidth, textConsoleHeight,
							consoleWidth, consoleHeight,
							consoleX, consoleY,
							tailSize = 10,
							buttonClickable = false;
						
						h2c.key.addKeyDownEvent( h2c.view.EDITOR, 66, false, true, layerBack);
						h2c.key.addKeyDownEvent( h2c.view.EDITOR, 70, false, true, layerForward);
						
						h2c.key.addKeyDownEvent( h2c.view.EDITOR, 68, false, true, del);
						h2c.key.addKeyDownEvent( h2c.view.EDITOR, 69, false, true, edit);
						h2c.key.addKeyDownEvent( h2c.view.EDITOR, 85, false, true, change);
						
						function buttonBackOrForward( isBack){
							var	offest = jqConsoleWrapper.offset();
							jqConsoleWrapper.css( {
								position:	isBack === true ? '' : 'absolute',
								left:		isBack === true ? consoleX  : offest.left,
								top:		isBack === true ? consoleY  : offest.top
							});
							buttonClickable === isBack && ( isBack === true ? jqConsoleParent : jqStage).append( jqConsoleWrapper);
							buttonClickable = !isBack;
						}
						function layerBack(){
							if( currentElement === null) return;
							replaceComicElement( currentElement, false);
							updateInfomation();
							SAVE( restoreReplaceObject, [ currentElement, true], [ currentElement, false]);
							var _z = currentElement.z;
							LAYER_BACK_BUTTON.visible( _z > 0);
							LAYER_FORWARD_BUTTON.visible( _z < DRAGGABLE_ELEMENT_ARRAY.length -NUM_RESIZER -1);
						}
						function layerForward(){
							if( currentElement === null) return;
							replaceComicElement( currentElement, true);
							updateInfomation();
							SAVE( restoreReplaceObject, [ currentElement, false], [ currentElement, true]);
							var _z = currentElement.z;
							LAYER_BACK_BUTTON.visible( _z > 0);
							LAYER_FORWARD_BUTTON.visible( _z < DRAGGABLE_ELEMENT_ARRAY.length -NUM_RESIZER -1);
						}
						function del(){
							if( currentElement === null) return;
							buttonBackOrForward( true);
							removeComicElement( currentElement);
							SAVE( restoreComicElement, [ true, currentElement], [ false, currentElement], true);
							COMIC_ELEMENT_OPERATOR.hide();
						}
						function edit(){
							if( currentElement === null) return;
							TEXT_EDITOR_CONTROL.show( currentElement);
							buttonBackOrForward( true);
						}
						function change(){
							if( currentElement === null) return;
							buttonBackOrForward( true);
							IMAGE_GROUP_EXPROLER.show( currentElement.url);
						}
						return {
							init: function(){
								jqStage = jqEditor;
								jqConsoleTail = $( '#comic-element-consol-tail');
								jqImgConsole = $( '#image-element-consol').hide();
								var imgConsoleSize = h2c.util.getElementSize( jqImgConsole.get( 0));
								imgConsoleWidth = imgConsoleSize.width;
								imgConsoleHeight = imgConsoleSize.height;
								
								jqTextConsole = $( '#text-element-consol').hide();
								var textConsoleSize = h2c.util.getElementSize( jqTextConsole.get( 0));
								textConsoleWidth = textConsoleSize.width;
								textConsoleHeight = textConsoleSize.height;
								
								jqConsoleWrapper = $( '#comic-element-consol-wrapper').hide();
								jqConsoleParent = jqConsoleWrapper.parent();
								
								$( '#edit-text-button').click( edit);
								$( '#delete-image-button, #delete-text-button').click( del);
								$( '#change-image-button').click( change);
								$( '#layer-forward-button, #forward-text-button').click( layerForward);
								$( '#layer-back-button, #back-text-button').click( layerBack);
															
								delete COMIC_ELEMENT_CONSOLE.init;
							},
							show: function( _currentElement, _w, _h){
								visible === false && jqConsoleWrapper.show();
								visible = true;
								currentElement = _currentElement;
								var _currentType = _currentElement.type,
									_z = _currentElement.z;
								if( currentType !== _currentType){
									currentType = _currentType;
									jqImgConsole.toggle( _currentType === COMIC_ELEMENT_TYPE_IMAGE);
									jqTextConsole.toggle( _currentType === COMIC_ELEMENT_TYPE_TEXT);
									consoleWidth = _currentType === COMIC_ELEMENT_TYPE_IMAGE ? imgConsoleWidth : textConsoleWidth;
									consoleHeight = _currentType === COMIC_ELEMENT_TYPE_IMAGE ? imgConsoleHeight : textConsoleHeight;
								}
								consoleX = Math.floor( ( _w -consoleWidth) /2);
								
								LAYER_BACK_BUTTON.visible( _z > 0);
								LAYER_FORWARD_BUTTON.visible( _z < DRAGGABLE_ELEMENT_ARRAY.length -NUM_RESIZER -1);
								DELETE_BUTTON.visible( true);
								EDIT_BUTTON.visible( _currentType === COMIC_ELEMENT_TYPE_TEXT);
								CHANGE_BUTTON.visible( false);
								
								if( _w > consoleWidth * 1.5 && _h > consoleHeight * 1.5){
									consoleY = Math.floor( ( _h -consoleHeight) /2);
									jqConsoleWrapper.css( {
										left:			consoleX,
										top:			consoleY
									}).removeClass( 'console-out');
								} else {
									consoleY = _h +tailSize;
									jqConsoleWrapper.css( {
										left:			consoleX,
										top:			consoleY
									}).addClass( 'console-out');
								}
							},
							hide: function (){
								visible === true && jqConsoleWrapper.hide();
								visible = false;
								currentElement = null;
								LAYER_BACK_BUTTON.visible( false);
								LAYER_FORWARD_BUTTON.visible( false);
								DELETE_BUTTON.visible( false);
								EDIT_BUTTON.visible( false);
								CHANGE_BUTTON.visible( false);
							},
							x: function(){ return consoleX;},
							y: function(){ return consoleY;},
							w: function(){ return consoleWidth;},
							h: function(){ return consoleHeight;},
							onMouseMove: function( _mouseX, _mouseY){
								if( consoleX > _mouseX || consoleY > _mouseY || consoleX +consoleWidth < _mouseX || consoleY +consoleHeight < _mouseY){
									buttonClickable === true && buttonBackOrForward( true);
									return false;
								}
								buttonClickable === false && buttonBackOrForward( false);
								return true;
							},
							onMouseOut: function( _mouseX, _mouseY){
								buttonClickable === true && buttonBackOrForward( true);
							}
						}
					})(),
					TAIL_CONTROLER = ( function(){
						var	ELM_MOVER = document.getElementById( 'balloon-tail-mover'),
							SIZE = h2c.util.getElementSize( ELM_MOVER).width,
							SIN = Math.sin,
							COS = Math.cos,
							ATAN = Math.atan,
							FLOOR = Math.floor,
							DEG_TO_RAD = Math.PI / 180,
							RAD_TO_DEG = 1 /DEG_TO_RAD,
							currentText = null,
							tailX, tailY,
							balloonW, balloonH, balloonA, radA,
							visible = false,
							startA;
						
						function draw( _w, _h, _a){
							balloonW = _w !== undefined ? _w : balloonW;
							balloonH = _h !== undefined ? _h : balloonH;
							balloonA = _a !== undefined ? _a : balloonA;
							radA = balloonA *DEG_TO_RAD;
							tailX = FLOOR( ( ( COS( radA) /2 +0.5) *( balloonW +SIZE)) -SIZE /2);
							tailY = FLOOR( ( ( SIN( radA) /2 +0.5) *( balloonH +SIZE)) -SIZE /2);
							ELM_MOVER.style.left = tailX +'px';
							ELM_MOVER.style.top = tailY +'px';
							log.html( [ balloonW, balloonH, balloonA].join());
						}
						function hitTest( _mouseX, _mouseY){
							var _x = tailX -SIZE /2,
								_y = tailY -SIZE /2;
								ret = currentIsTextElement === true && _x <= _mouseX && _y <= _mouseY && _x +SIZE >= _mouseX && _y +SIZE >= _mouseY;
							ret === true && MOUSE_CURSOR( 'move');
							return ret;
						}
						return {
							update: draw,
							show: function( _currentText){
								/*
								 * visibilityのほうがいい, display:none だと ie で描画が狂う
								 */
								ELM_MOVER.style.visibility = '';
								draw( _currentText.w, _currentText.h, _currentText.angle());
								currentText = _currentText;
							},
							hitTest: hitTest,
							hide: function(){
								ELM_MOVER.style.visibility = 'hidden';
								currentText = null;
							},
							onStart: function( _currentText, _mouseX, _mouseY){
								if( hitTest( _mouseX -x, _mouseY -y) === true){
									currentText = _currentText;
									startA = _currentText.angle();
									return true;
								}
								currentText = null;
								return false;
							},
							onDrag: function( _mouseX, _mouseY){
								_mouseX = _mouseX -currentText.x -w /2;
								_mouseY = _mouseY -currentText.y -h /2; //Balloonの中心を0,0とする座標系に変換
								
								draw( w, h,
									_mouseX !== 0 ?
										ATAN( _mouseY /_mouseX) *RAD_TO_DEG +( _mouseX < 0 ? 180 : 0) :
										_mouseY > 0 ? 90 : -90
								);
								currentText && currentText.angle( balloonA);
								updateInfomation( undefined, undefined, undefined, balloonA);
							},
							onFinish: function(){
								startA !== currentText.angle() && saveComicElementStatus( x, y, w, h, startA);
								currentText = null;
							},
							onCancel: function(){
								resize( undefined, undefined, undefined, undefined, startA);
							}
						}
					})(),
					RESIZE_CONTROLER = ( function(){
						var POSITION_ARRAY = [],
							FLOOR = Math.floor,
							CURSOR_AND_FLIP = [
								{ cursor:	'n-resize',		v: 3},
								{ cursor:	'e-resize',		h: 2},
								{ cursor:	'e-resize',		h: 1},
								{ cursor:	'n-resize',		v: 0},
								{ cursor:	'nw-resize',	h: 5, v: 6, vh: 7},
								{ cursor:	'ne-resize',	h: 4, v: 7, vh: 6},
								{ cursor:	'ne-resize',	h: 7, v: 4, vh: 5},
								{ cursor:	'nw-resize',	h: 6, v: 5, vh: 4}
							],	
							elmResizerContainerStyle = document.getElementById( 'comic-element-resizer-container').style,
							elmResizerTopStyle = document.getElementById( 'comic-element-resizer-top').style,
							elmResizerLeftStyle = document.getElementById( 'comic-element-resizer-left').style,
							elmResizerRightStyle = document.getElementById( 'comic-element-resizer-right').style,
							elmResizerBottomStyle = document.getElementById( 'comic-element-resizer-bottom').style,
							x, y, w, h,
							currentIndex = -1,
							currentElement,
							currentIsTextElement = false;
						
						elmResizerContainerStyle.display = 'none';
						
						var RESIZE_WORK_ARRAY = [
								{ x:	0, w:	 0, y:	1, h:	-1}, //top
								{ x:	1, w:	-1, y:	0, h:	 0}, //left
								{ x:	0, w:	 1, y:	0, h:	 0}, //right
								{ x:	0, w:	 0, y:	0, h:	 1}, //bottom
								{ x:	1, w:	-1, y:	1, h:	-1}, //top-left
								{ x:	0, w:	 1, y:	1, h:	-1}, //top-right
								{ x:	1, w:	-1, y:	0, h:	 1}, //bottom-left
								{ x:	0, w:	 1, y:	0, h:	 1}  //bottom-right
							],
							startX, startY, startW, startH, startFilpV, startFilpH, startAspect,
							baseX, baseY, baseW, baseH,
							currentX, currentY, currentW, currentH,
							transX, transY, transW, transH,
							xOffset, yOffset,
							error = 0;
						
						function draw( _x, _y, _w, _h){
							x = _x !== undefined ? _x : x;
							y = _y !== undefined ? _y : y;
							w = _w !== undefined ? _w : w;
							h = _h !== undefined ? _h : h;
							elmResizerContainerStyle.left = x +'px';
							elmResizerContainerStyle.top = y +'px';
							elmResizerContainerStyle.width = w +'px';
							elmResizerContainerStyle.height = h +'px';
							elmResizerTopStyle.left = FLOOR( w /2 -10 /2) +'px';
							elmResizerLeftStyle.top = FLOOR( h /2 -10 /2) +'px';
							elmResizerRightStyle.top = FLOOR( h /2 -10 /2) +'px';
							elmResizerBottomStyle.left = FLOOR( w /2 -10 /2) +'px';
							
							POSITION_ARRAY.splice( 0, POSITION_ARRAY.length);
							POSITION_ARRAY.push(
								{x:	x +5,					y:	y -HIT_AREA,		w:	w -5 *2,		h:	HIT_AREA +5},
								{x: x -HIT_AREA,			y:	y +HIT_AREA +5,		w:	HIT_AREA +5,	h:	h -5 *2},
								{x: x +w -5,				y:	y +HIT_AREA +5,		w:	HIT_AREA +5,	h:	h -5 *2},
								{x:	x +5,					y:	y +h -5,			w:	w -5 *2,		h:	HIT_AREA +5},
								{x:	x -HIT_AREA,			y:	y -HIT_AREA,		w:	HIT_AREA +5,	h:	HIT_AREA +5},
								{x: x +w -HIT_AREA,			y:	y -HIT_AREA,		w:	HIT_AREA +5,	h:	HIT_AREA +5},
								{x:	x -HIT_AREA,			y:	y +h -5,			w:	HIT_AREA +5,	h:	HIT_AREA +5},
								{x:	x +w -5,				y:	y +h -5,			w:	HIT_AREA +5,	h:	HIT_AREA +5}
							);
						}
						
						function update( _x, _y, _w, _h){
							transX = _x !== undefined ? _x : currentX;
							transY = _y !== undefined ? _y : currentY;
							transW = _w !== undefined ? _w : currentW;
							transH = _h !== undefined ? _h : currentH;
							
							if( currentIsTextElement === false && currentIndex > 3 && h2c.key.shiftEnabled() === true){
								if( startAspect >= 1){
									_w = transW;
									transW = Math.floor( startAspect * transH);
									transX = transX +( currentIndex % 2 === 0 ? _w -transW : 0);
								} else {
									_h = transH;
									transH = Math.floor( transW / startAspect);
									transY = transY + ( currentIndex <= 5 ? _h -transH : 0);
								}
							}
							draw( transX, transY, transW, transH);
							currentElement.resize( transX, transY, transW, transH);
							currentIsTextElement === true && TAIL_CONTROLER.update( transW, transH);
							COMIC_ELEMENT_CONSOLE.show( currentElement, transW, transH);
							updateInfomation( transX, transY, undefined, undefined, transW, transH);
						}
						function getIndex( _mouseX, _mouseY){
							var	p,
								l = POSITION_ARRAY.length;
							for( var i=0; i<l; i++){
								p = POSITION_ARRAY[ i];
								if( p.x <= _mouseX && p.y <= _mouseY && p.x + p.w >= _mouseX && p.y +p.h >= _mouseY){
									MOUSE_CURSOR( CURSOR_AND_FLIP[ i].cursor);
									return currentIndex = i;
								}
							}
							return -1;
						}
						function flip( _flipV, _flipH){
							var p = CURSOR_AND_FLIP[ currentIndex];
							currentIndex = _flipH === true || _flipV === true ? p[
								_flipH === true && _flipV === true ? 'vh' : ( _flipH === true ? 'h' : 'v')
							] : currentIndex;
							MOUSE_CURSOR( CURSOR_AND_FLIP[ currentIndex].cursor);
							currentElement.flip( _flipV, _flipH);
						}
						return {
							x: function(){ return x;},
							y: function(){ return y;},
							w: function(){ return w;},
							h: function(){ return h;},
							update: draw,
							index: getIndex,
							show: function( _currentElement){
								currentElement = _currentElement;
								currentIsTextElement = _currentElement.type === COMIC_ELEMENT_TYPE_TEXT;
								elmResizerContainerStyle.display = '';
							},
							hide: function(){
								currentElement = null;
								elmResizerContainerStyle.display = 'none';
							},
							onStart: function( _currentElement, _mouseX, _mouseY){
								currentElement = _currentElement;
								currentIsTextElement = _currentElement.type === COMIC_ELEMENT_TYPE_TEXT;
								if( _currentElement.keepSize === true) return false;
								currentIndex = getIndex( _mouseX, _mouseY);
								if( currentIndex === -1) return false;
								xOffset = _mouseX;
								yOffset = _mouseY;
								startX = baseX = _currentElement.x;
								startY = baseY = _currentElement.y;
								startW = baseW = _currentElement.w;
								startH = baseH = _currentElement.h;
								startFilpV = flipV;
								startFilpH = flipH;
								startAspect = startW /startH;
								return true;
							},
							onDrag: function( _mouseX, _mouseY){
								var com = RESIZE_WORK_ARRAY[ currentIndex],
									moveX = _mouseX -xOffset,
									moveY = _mouseY -yOffset,
									_x = baseX +moveX *com.x,
									_y = baseY +moveY *com.y,
									_w = baseW +moveX *com.w,
									_h = baseH +moveY *com.h,
									_updated = moveX !== 0 || moveY !== 0;
								
								// opera　がときどき baseH の値を忘れる　？？
								if( _x === undefined || _y === undefined || _w === undefined || _h === undefined){
									++error;
									return;
								}
								
								if( _w >= MIN_OBJECT_SIZE && _h >= MIN_OBJECT_SIZE){
									
								} else 
								if( _w >= -MIN_OBJECT_SIZE && _h >= -MIN_OBJECT_SIZE){
									return;
								} else 
								if( currentElement.type === COMIC_ELEMENT_TYPE_TEXT){
									return;
								} else 
								if( _w < -MIN_OBJECT_SIZE || _h < -MIN_OBJECT_SIZE){
									var __x = 0,
										__y = 0;
									if( _w < -MIN_OBJECT_SIZE && _h > MIN_OBJECT_SIZE){
									// flipH
										__x = _x;
										baseX = _x = _x +_w;
										baseY = _y;
										baseW = _w = __x -_x;
										baseH = _h;
										flip( false, true);
										flipV = currentElement.flipV();
									} else 
									if( _w > MIN_OBJECT_SIZE && _h < -MIN_OBJECT_SIZE){
									// flipV
										__y = _y;
										baseX = _x;
										baseY = _y = _y +_h;
										baseW = _w;
										baseH = _h = __y -_y;
										flip( true, false);
										flipH = currentElement.flipH();
									} else {
									// flipVH
										__x = _x;
										__y = _y;
										baseX = _x = _x +_w;
										baseY = _y = _y +_h;
										baseW = _w = __x -_x;
										baseH = _h = __y -_y;
										flip( true, true);
										flipV = currentElement.flipV();
										flipH = currentElement.flipH();
									}
									_updated = true;
									xOffset = _mouseX;
									yOffset = _mouseY;	
								}
								currentX = _x;
								currentY = _y;
								currentW = _w;
								currentH = _h;
								_updated === true && update( _x, _y, _w, _h);
								
								log.html( [
										'currentIndex:', currentIndex, 
										'baseW', baseW, 'baseH', baseH,'<br>',
										'mouse', _mouseX, _mouseY,'<br>',
										'move', moveX, moveY,'<br>',
										'xy', _x, _y, 'wh',_w, _h,'<br>',
										'com.w', com.w, 'com.h', com.h,'<br>',
										'current',currentW, currentH,'<br>',
										'trans', transY, transH,
										'err', error
								].join( ' , '));
							},
							onFinish: function(){
								MOUSE_CURSOR( '');
								if( currentW === w && currentH === h && currentX === x && currentY === y) return;
								resize( transX, transY, transW, transH);
								currentElement.resize( transX, transY, transW, transH);
								saveComicElementStatus( startX, startY, startW, startH, angle, startFilpV, startFilpH);
							},
							onCancel: function(){
								MOUSE_CURSOR( '');
								resize( startX, startY, startW, startH);
								currentElement.type === COMIC_ELEMENT_TYPE_IMAGE ?
									currentElement.animate( startX, startY, startW, startH, startFilpV, startFilpH) :
									currentElement.animate( startX, startY, startW, startH, angle);
							},
							onShiftUpdate: update,
							onCtrlUpdate: update
						}
					})(),
					POSITION_CONTROLER = ( function(){
						var currentElement,
							startX, startY,
							currentX, currentY,
							xOffset, yOffset,
							isCopy = false;
						
						function update( _x, _y){
							_x = currentX = _x !== undefined ? _x : currentX;
							_y = currentY = _y !== undefined ? _y : currentY;
							RESIZE_CONTROLER.update( _x, _y);
							currentElement.resize( _x, _y);
							updateInfomation( _x, _y);							
						}
						
						return {
							onStart: function( _currentElement, _mouseX, _mouseY){
								currentElement = _currentElement;
								xOffset = _mouseX;
								yOffset = _mouseY;
								startX = currentX = x;
								startY = currentY = y;
								MOUSE_CURSOR( 'move');
							},
							onDrag: function( _mouseX, _mouseY){
								var moveX = _mouseX -xOffset,
									moveY = _mouseY -yOffset,
									_x = x +moveX,
									_y = y +moveY;
								if( GRID_ENABLED() === true){
									_x = Math.floor( _x / 10) * 10;
									_y = Math.floor( _y / 10) * 10;
								}
								update( _x, _y);
							},
							onFinish: function(){
								MOUSE_CURSOR( '');
								if( currentX === startX && currentY === startY) return;
								resize( currentX, currentY);
								currentElement.resize( currentX, currentY);
								saveComicElementStatus( startX, startY);
							},
							onCancel: function(){
								MOUSE_CURSOR( '');
								resize( startX, startY);
								currentElement.animate( startX, startY);
							},
							onShiftUpdate: update,
							onCtrlUpdate: update
						}
					})();

					function resize( _x, _y, _w, _h, _angle){
						x = _x !== undefined ? _x : x;
						y = _y !== undefined ? _y : y;
						w = _w !== undefined ? _w : w;
						h = _h !== undefined ? _h : h;
						angle = _angle !== undefined ? _angle : angle;

						RESIZE_CONTROLER.update( x, y, w, h);
						currentIsTextElement === true && TAIL_CONTROLER.update( w, h, angle);
						COMIC_ELEMENT_CONSOLE.show( currentElement, w, h);
						updateInfomation( x, y, currentElement.z, angle, w, h);
					}
					function updateInfomation( _x, _y, _z, _a, _w, _h, keepAspect){
						_w = _w !== undefined ? _w : w;
						_h = _h !== undefined ? _h : h;
						INFOMATION(
							currentElement === null ? -1 : currentElement.type,
							_x !== undefined ? _x : x,
							_y !== undefined ? _y : y,
							_z !== undefined ? _z : ( currentElement === null ? '*' : currentElement.z),
							_a !== undefined ? Math.floor( _a) : ( currentIsTextElement === true ? Math.floor( angle) : '-'),
							_w,
							_h,
							currentElement === null || currentIsTextElement === true ? '-' : ( currentElement.actualW() === 0 ? '?' : Math.floor( _w / currentElement.actualW() *100)),
							currentElement === null || currentIsTextElement === true ? '-' : ( currentElement.actualH() === 0 ? '?' : Math.floor( _h / currentElement.actualH() *100)),
							currentElement === null || currentIsTextElement === true ? '-' : currentElement.keepAspect
						);
					}
					function show( _currentElement){
						currentElement === null && RESIZE_CONTROLER.show( _currentElement);
						if( currentElement !== _currentElement){
							currentElement = _currentElement;
							
							currentIsTextElement = ( _currentElement.type === COMIC_ELEMENT_TYPE_TEXT);
							currentIsTextElement === true ? TAIL_CONTROLER.show( _currentElement) : TAIL_CONTROLER.hide();
							
							flipV = currentIsTextElement === false ? _currentElement.flipV() : 0;
							flipH = currentIsTextElement === false ? _currentElement.flipH() : 0;
							
							resize(
								_currentElement.x, _currentElement.y, _currentElement.w, _currentElement.h,
								currentIsTextElement === true ? _currentElement.angle() : 0
							);
						}
					}
					function hide(){
						currentElement !== null && RESIZE_CONTROLER.hide();
						currentElement = null;
						MOUSE_CURSOR( '');
						TAIL_CONTROLER.hide();
						COMIC_ELEMENT_CONSOLE.hide();
						updateInfomation();
					}
					function restoreState( arg){
						if( arg && arg.length !== 8) return;
						var _currentElement = arg[ 0],
							_x = arg[ 1], _y = arg[ 2], _w = arg[ 3], _h = arg[ 4],
							_a = arg[ 5],
							_flipV = arg[ 6], _flipH = arg[ 7];
						if( !_currentElement) return;
						_currentElement.type === COMIC_ELEMENT_TYPE_IMAGE ?
							_currentElement.animate( _x, _y, _w, _h, _flipV, _flipH) :
							_currentElement.animate( _x, _y, _w, _h, _a);
						currentControler = null;
						currentElement === _currentElement ? resize( _x, _y, _w, _h, _a) : show( _currentElement);
					}
					function saveComicElementStatus( startX, startY, startW, startH, startA, startFilpV, startFilpH){
						startX = startX !== undefined ? startX : x;
						startY = startY !== undefined ? startY : y;
						startW = startW !== undefined ? startW : w;
						startH = startH !== undefined ? startH : h;
						startA = startA !== undefined ? startA : angle;
						startFilpV = startFilpV !== undefined ? startFilpV : flipV;
						startFilpH = startFilpH !== undefined ? startFilpH : flipH;
						currentElement && SAVE( restoreState,
							[ currentElement, startX, startY, startW, startH, startA, startFilpV, startFilpH],
							[ currentElement, x, y, w, h, angle, flipV, flipH]
						);
					}
					h2c.key.addKeyUpdateEvent( h2c.view.EDITOR, 16, true, undefined, function( e){
						currentControler !== null && currentControler.onShiftUpdate && currentControler.onShiftUpdate();
					});
					h2c.key.addKeyUpdateEvent( h2c.view.EDITOR, 17, undefined, true, function(){
						currentControler !== null && currentControler.onCtrlUpdate && currentControler.onCtrlUpdate();
					});
					h2c.key.addKeyDownEvent( h2c.view.EDITOR, 27, false, false, function(){
						currentControler !== null && currentControler.onCancel && currentControler.onCancel();
						currentControler = null;
					});
				return {
					init: function(){
						COMIC_ELEMENT_CONSOLE.init();
						hide();
						delete COMIC_ELEMENT_OPERATOR.init;
					},
					hide: hide,
					busy: function(){
						return currentControler !== null;
					},
					hitareaX: function( _comicElement, _x){
						if( _comicElement === currentElement){
							var _consoleX = COMIC_ELEMENT_CONSOLE.x();
							return x +( _consoleX < 0 ? _consoleX : 0) -HIT_AREA;
						}
						return _x -HIT_AREA;
					},
					hitareaY: function( _comicElement, _y){
						return _y -HIT_AREA;
					},
					hitareaW: function( _comicElement, _w){
						if( _comicElement === currentElement){
							var _consoleW = COMIC_ELEMENT_CONSOLE.w();
							return ( _consoleW < w ? w : _consoleW) +HIT_AREA *2;
						}
						return _w +HIT_AREA *2;
					},
					hitareaH: function( _comicElement, _h){
						if( _comicElement === currentElement){
							var _consoleY = COMIC_ELEMENT_CONSOLE.y();
							return ( _consoleY < h ? h : _consoleY +COMIC_ELEMENT_CONSOLE.h()) +HIT_AREA *2;
						}
						return _h +HIT_AREA *2;
					},
					onMouseDown: function( _currentElement, _mouseX, _mouseY){
						//show( _currentElement);
						if( currentIsTextElement === true && TAIL_CONTROLER.onStart( _currentElement, _mouseX, _mouseY) === true){
							currentControler = TAIL_CONTROLER;
						} else
						if( RESIZE_CONTROLER.onStart( _currentElement, _mouseX, _mouseY) === true){
							currentControler = RESIZE_CONTROLER;
						} else {
							POSITION_CONTROLER.onStart( _currentElement, _mouseX, _mouseY)
							currentControler = POSITION_CONTROLER;
						}
					},
					onMouseMove: function( _currentElement, _mouseX, _mouseY){
						show( _currentElement);
						if( currentControler !== null){
							currentControler.onDrag( _mouseX, _mouseY);						
						} else {
							currentElement && COMIC_ELEMENT_CONSOLE.onMouseMove( _mouseX -x, _mouseY -y);
							TAIL_CONTROLER.hitTest( _mouseX -x, _mouseY -y) === false && RESIZE_CONTROLER.index( _mouseX, _mouseY) === -1 && MOUSE_CURSOR( '');
						}
					},
					onMouseUp: function( _currentElement, _mouseX, _mouseY){
						currentControler !== null && currentControler.onFinish();
						currentControler = null;
					},
					onMouseClick: function( _mouseX, _mouseY){
						//return currentElement ? COMIC_ELEMENT_CONSOLE.onMouseClick( _mouseX -x, _mouseY -y) : false;
					}
				}
			})();
			/*
			 *  // COMIC_ELEMENT_OPERATOR
			 */
		
			var AbstractComicElement = function( JQ_WAPPER, COMIC_ELM_TYPE, update, x, y, w, h, z, timing){
				var OPERATOR = COMIC_ELEMENT_OPERATOR;
				return {
					$: JQ_WAPPER,
					type: COMIC_ELM_TYPE,
					x: x,
					y: y,
					w: w,
					h: h,					
					z: z,
					timing: timing,
					hitareaX: function(){ return OPERATOR.hitareaX( this, this.x);},
					hitareaY: function(){ return OPERATOR.hitareaY( this, this.y);},
					hitareaW: function(){ return OPERATOR.hitareaW( this, this.w);},
					hitareaH: function(){ return OPERATOR.hitareaH( this, this.h);},
					shift: function( _shiftX, _shiftY){
						update( this.x +_shiftX, this.y +_shiftY);
					},
					busy: function(){
						return OPERATOR.busy();
					},
					onMouseMove: function( _mouseX, _mouseY){
						OPERATOR.onMouseMove( this, _mouseX, _mouseY);
					},
					onMouseUp: function( _mouseX, _mouseY){
						OPERATOR.onMouseUp( this, _mouseX, _mouseY);
					},
					onMouseDown: function( _mouseX, _mouseY){
						OPERATOR.onMouseDown( this, _mouseX, _mouseY);
					}
				}
			};
		/*
		 * --------------------------------------------------------------------------------------------
		 * ImageElementClass
		 */
			var	jqImageElementOrigin;
			var ImageElementClass = function( url, IMAGE_SET_ID, x, y, z, w, h, timing){
				jqImageElementOrigin = jqImageElementOrigin || $( $( '#imgElementTemplete').remove().html());
				
				var JQ_WRAPPER = jqImageElementOrigin.clone( true),
					OPERATOR = COMIC_ELEMENT_OPERATOR,
					SAVE = HISTORY.saveState,
					HIT_AREA = MOUSE_HIT_AREA,
					reversibleImage = null,
					actualW = 0, actualH = 0,
					flipH = w < 0 ? -1 : 1,
					flipV = h < 0 ? -1 : 1,
					instance;
				w = Math.floor( w);
				h = Math.floor( h);
				
				function update( _x, _y, _w, _h, animate){
					instance.x = x = _x !== undefined ? _x : x;
					instance.y = y = _y !== undefined ? _y : y;
					instance.w = w = _w !== undefined ? _w : w;
					instance.h = h = _h !== undefined ? _h : h;
					JQ_WRAPPER[ animate === true ? 'animate' : 'css']( { 
						left:	x,
						top:	y,
						width:	w,
						height:	h
					}, 250, function(){ reversibleImage.resize( flipH * w, flipV * h);});
					animate !== true && reversibleImage.resize( flipH * w, flipV * h);
				}
				
				function updateUrl( _url){
					if( url === _url) return;
					url = _url || url;
					var _reversibleImage = h2c.image.createReversibleImage( url, flipH * w, flipV * h, function( _url, _actualW, _actualH){
						actualW = _actualW;
						actualH = _actualH;
					});
					if( reversibleImage !== null){
						JQ_WRAPPER.children( reversibleImage.elm).replaceWith( _reversibleImage.elm);
						reversibleImage.destroy();
					} else {
						JQ_WRAPPER.append( _reversibleImage.elm);
					}
					reversibleImage = _reversibleImage;
				}
				return h2c.util.extend(
					AbstractComicElement.apply( this, [ JQ_WRAPPER, COMIC_ELEMENT_TYPE_IMAGE, update, x, y, w, h, z, timing]),
					{
						init: function(){
							instance = this;
							updateUrl();
							update();
							delete this.init;
						},
						flip: function( _flipH, _flipV){
							if( _flipH !== true && _flipV !== true) return;
							flipH = _flipH === true ? -flipH : flipH;
							flipV = _flipV === true ? -flipV : flipV;
							reversibleImage.resize( flipH * w, flipV * h);
						},
						flipV: function(){
							return flipV;
						},
						flipH: function(){
							return flipH;
						},
						url: function( _url, _actualW, _actualH){
							if( _url && _url !== url){
								SAVE( updateUrl, url, _url);
								actualW = _actualW;
								actualH = _actualH;
								updateUrl( _url);
							}
							return url;
						},
						actualW: function(){ return actualW;},
						actualH: function(){ return actualH;},
						keepSize: false,
						resize: update,
						animate: function ( _x, _y, _w, _h, _flipH, _flipV){
							flipH = _flipH !== undefined ? _flipH : flipH;
							flipV = _flipV !== undefined ? _flipV : flipV;
							update( _x, _y, _w, _h, true);
						},
						getAsHTML: function( isAbsoluteUrl, isXHTML){
							return [
								'<img ',
									'src="', isAbsoluteUrl !== true ? url : h2c.util.getAbsolutePath( url), '" ',
									'width="', w, '" ',
									'height="', h, '" ',
									'style="',									
										'left:', x, 'px;',
										'top:', y, 'px;',
										'z-index:', z, ';',
									'"',
								isXHTML !== true ? '>' : ' \/>'
							].join( '');
						},
						getAsJsonString: function(){
							var cr = h2c.LINE_FEED_CODE_TEXTAREA;
							return [
								'"new', this.timing, '": {', cr,
									'"resource_picture_id": "', url, '",', cr,
									'"x": ', x, ',', cr,
									'"y": ', y, ',', cr,
									'"z": ', z, ',', cr,
									'"w": ', w, ',', cr,
									'"h": ', h, ',', cr,
									'"flipv": ', flipV === true ? 1 : 0, ',', cr,
									'"fliph": ', flipH === true ? 1 : 0, ',', cr,
									'"t": ', this.timing, cr,
								'}'
							].join( '');
						},
						destroy: function(){
							reversibleImage.destroy();
							JQ_WRAPPER.remove();
							JQ_WRAPPER = reversibleImage = OPERATOR = null;
							delete this.destroy;
						}
					}
				);
			}
		/*
		 * / ImageElementClass
		 * --------------------------------------------------------------------------------------------
		 */


		/*
		 * --------------------------------------------------------------------------------------------
		 * TextElementClass
		 * 
		 * ELM はh2c.domで書き出したものを突っ込むcreateの場合
		 * 
		 * type
		 * 0.none
		 * 1.speach balloon
		 * 2.think
		 * 3.bom
		 * 4.black-box( dq style)
		 * 5.blue-box( ff style)
		 * 
		 */
			var jqTextElementOrigin;
			var TextElementClass = function( type, a, text, x, y, z, w, h, timing){
				jqTextElementOrigin = jqTextElementOrigin || ( function(){
					var _OLD_IE = $( $( '#textElementTempleteForOldIE').remove().html()),
						_MODERN = $( $( '#textElementTemplete').remove().html());
					return h2c.isIE === true && h2c.ieRenderingVersion < 8 ? _OLD_IE : _MODERN;
				})();
				
				var JQ_WRAPPER = jqTextElementOrigin.clone( true),
					XBROWSER_BALLOON = h2c.balloon.createBalloon( w, h, a, type),
					TEXT_ELM = JQ_WRAPPER.find( 'td,.speach-inner').eq( 0),
					OPERATOR = COMIC_ELEMENT_OPERATOR,
					HIT_AREA = MOUSE_HIT_AREA,
					SAVE = HISTORY.saveState,
					instance;
					
				JQ_WRAPPER.find( 'img').eq( 0).replaceWith( XBROWSER_BALLOON.elm);
				
				function update( _x, _y, _w, _h, _a, animate){
					instance.x = x = _x !== undefined ? _x : x;
					instance.y = y = _y !== undefined ? _y : y;
					instance.w = w = _w !== undefined ? _w : w;
					instance.h = h = _h !== undefined ? _h : h;
					a = _a !== undefined ? _a : a;
					
					JQ_WRAPPER[ animate === true ? 'animate' : 'css']( {
							left:		x,
							top:		y,
							width:		w,
							height:		h
						}, 250,
						function(){
							XBROWSER_BALLOON.resize( a, w, h);
						}
					);		
					animate !== true && XBROWSER_BALLOON.resize( a, w, h);
				}
				
				function updateType( _type){
					if( type !== _type){
						type = _type || type;
						XBROWSER_BALLOON.type( type);
					}
				}
				function updateAngle( _a){
					if( _a !== undefined && a !== _a){
						a = _a !== undefined ? _a : a;
						XBROWSER_BALLOON.angle( a);
					}
				}
				function updateText( _text){
					text = _text || text || '';
					TEXT_ELM.html( text);
				}
				
				return h2c.util.extend(
					AbstractComicElement.apply( this, [ JQ_WRAPPER, COMIC_ELEMENT_TYPE_TEXT, update, x, y, w, h, z, timing]),
					{
						init: function(){
							instance = this;
							updateText();
							update();
							delete this.init;
						},
						angle: function( _a){
							_a !== undefined && update( undefined, undefined, undefined, undefined, _a);
							return a;
						},
						text: function( _text){
							if( _text && text !== _text) {
								SAVE( updateText, text || '', _text);
								updateText( _text);
							}
							return text;
						},
						resize: update,
						animate: function ( _x, _y, _w, _h, _a){
							update( _x, _y, _w, _h, _a, true);
						},
						destroy: function(){
							JQ_WRAPPER.remove();
							XBROWSER_BALLOON.destroy();
							OPERATOR = null;
							delete this.destroy;
						},
						getAsJSON: function(){
							
						},
						getAsJsonString: function(){
							var cr = h2c.LINE_FEED_CODE_TEXTAREA;
							return [
								'"new', this.timing, '": {', cr,
									'"balloon_template_id": ', 1, ',', cr,
									'"resource_picture_id": ', 1, ',', cr,
									'"border": ', 1, ',', cr,
									'"tail": ', a, ',', cr,
									'"x": ', x, ',', cr,
									'"y": ', y, ',', cr,
									'"z": ', z, ',', cr,
									'"w": ', w, ',', cr,
									'"h": ', h, ',', cr,
									'"t": ', this.timing, ',', cr,
        							'"speaches_attributes": {', cr,
	          							'"newf', this.timing, '": {', cr,
		            						'"content": "', text, '",', cr,
											'"x": ', x, ',', cr,
											'"y": ', y, ',', cr,
											'"w": ', w, ',', cr,
											'"h": ', h, cr,
										'}', cr,
									'}', cr,
								'}'
							].join( '');
						},
						getAsHTML: function( isAbsoluteUrl, isXHTML){
							var url = XBROWSER_BALLOON.getURL();
							return [
								'<img ',
									'src="', isAbsoluteUrl !== true ? url : h2c.util.getAbsolutePath( url), '" ',
									'width="', w, '" ',
									'height="', h, '" ',
									'style="',									
										'left:', x, 'px;',
										'top:', y, 'px;',
										'z-index:', z, ';',
									'"',
								isXHTML !== true ? '>' : ' \/>',
								h2c.LINE_FEED_CODE_TEXTAREA,
								'<div class="balloon" style="',
									'left:', x, 'px;',
									'top:', y, 'px;',
									'width:', w, 'px;',
									'height:', h, 'px;',
									'z-index:', z,
								'"><span>', text, '<\/span>', '<\/div>'
									
							].join( '');
						},
						getAsXML: function(){}
						
					}
				);
			}
		/*
		 * リサイズが、ResizerTopによって行われた場合、comicElementのyを動かして見かけ上動かないようにする。
		 */	
			function resize( isResizerTopAction){
				if( isResizerTopAction === true){
					var	_shiftX = canvasW -_canvasW,
						_shiftY = canvasH -_canvasH,
						l = DRAGGABLE_ELEMENT_ARRAY.length;
					for( var i = NUM_RESIZER; i < l; i++){
						DRAGGABLE_ELEMENT_ARRAY[ i].shift( _shiftX, _shiftY);
					}
				}
				_canvasW = canvasW;
				_canvasH = canvasH;
				
				comicElementContainer.css( {
					width:	_canvasW,
					height:	_canvasH,
					top:	canvasY,
					left:	canvasX
				});
			}
		
		/*
		 * append, remove, replace
		 * 
		 * comicElement には、z-position と dom-index がある。
		 *   z-position は 表示上の位置。大きいほど前に表示される（ z-index）
		 *   dom-index は 意味上の順番。htmlタグの登場順で、検索結果や音声読み上げブラウザで正しく意味が取れる順番。
		 * 
		 * editerでは、実際には z-index は使わず、htmlの順序で前後を表現する。
		 * dom-index は、数値のみ保持して、投稿時にcomicElementを適宜に並び替える。
		 * 
		 * append comicElement
		 * 1. 新しい comicElement の z-position を得る
		 * 2. ｚ の同じ comicElementを見つけ、その前に加える。または一番先頭へ。（DRAGGABLE_ELEMENT_ARRAY）
		 *    zが大きいほど、DRAGGABLE_ELEMENT_ARRAYの先頭へ。但しNUM_RESIZER番目より下。
		 * 3. dom位置は、DRAGGABLE_ELEMENT_ARRAY とは反対に、前のものほど後ろへ。
		 * 
		 * 
		 * remove comicElement
		 * 1. remove
		 * 2. renumber z
		 */
			function appendComicElement( _comicElement) {
				_comicElement.init && _comicElement.init();
				var z = _comicElement.z,
					l = DRAGGABLE_ELEMENT_ARRAY.length;
				_comicElement.$.stop().css( {
					filter:		'',
					opacity:	''
				});
				if( z === undefined || z === NaN || z < 0 || z >= l -NUM_RESIZER){
					DRAGGABLE_ELEMENT_ARRAY.splice( NUM_RESIZER, 0, _comicElement);
					comicElementContainer.append( _comicElement.$.fadeIn());
				} else {
					var insertIndex = ( function(){
							for( var ret = NUM_RESIZER; ret < l; ++ret){
								if( DRAGGABLE_ELEMENT_ARRAY[ ret].z <= z) return ret +1;
							}
							return NUM_RESIZER;
						})();
					DRAGGABLE_ELEMENT_ARRAY[ insertIndex -1].$.after( _comicElement.$.fadeIn());
					DRAGGABLE_ELEMENT_ARRAY.splice( insertIndex, 0, _comicElement);
				}
				sortComicElement();
			}
			function removeComicElement( _comicElement) {
				var l = DRAGGABLE_ELEMENT_ARRAY.length;
				for( var i=NUM_RESIZER; i<l; ++i){
					if( DRAGGABLE_ELEMENT_ARRAY[ i] === _comicElement){
						DRAGGABLE_ELEMENT_ARRAY.splice( i, 1);
						sortComicElement();
						_comicElement.$.stop().css( {
							filter:		'',
							opacity:	''
						}).fadeOut( function(){
							this.parentNode.removeChild( this);
						});
						currentElement = currentElement === _comicElement ? null : currentElement;
						return;
					}
				}
			}
			function restoreComicElement( arg){
				var isAppend = arg[ 0],
					comicElement = arg[ 1];
				isAppend === true ? appendComicElement( comicElement) : removeComicElement( comicElement);
			}
			/*
			 * DRAGGABLE_ELEMENT_ARRAY の順番を基準に、zの再計算
			 * jqElmの並び替え。
			 */
			function sortComicElement(){
				var l = DRAGGABLE_ELEMENT_ARRAY.length,
					_comicElement, jqElm, jqNext;
				for( var i=NUM_RESIZER; i < l; ++i){
					_comicElement = DRAGGABLE_ELEMENT_ARRAY[ i];
					jqElm = _comicElement.$;
					jqNext && jqNext.before( jqElm);
					_comicElement.z = l -i -NUM_RESIZER +1;
					jqNext = jqElm;
				}
			}
			function replaceComicElement( _comicElement, goForward){
				// DRAGGABLE_ELEMENT_ARRAYの再構築
				var l = DRAGGABLE_ELEMENT_ARRAY.length,
					i = ( function(){
						for( var ret = NUM_RESIZER; ret < l; ++ret){
							if( DRAGGABLE_ELEMENT_ARRAY[ ret] === _comicElement) return ret;
						}
						return -1;
					})();
				if( i === -1) return;
				if( goForward === true){
					if( i === NUM_RESIZER) return;
					DRAGGABLE_ELEMENT_ARRAY.splice( i, 1);
					DRAGGABLE_ELEMENT_ARRAY.splice( i -1, 0, _comicElement);
				} else {
					if( i === l -1) return;
					DRAGGABLE_ELEMENT_ARRAY.splice( i, 1);
					DRAGGABLE_ELEMENT_ARRAY.splice( i +1, 0, _comicElement);
				}
				sortComicElement();
			}
			function restoreReplaceObject( arg){
				replaceComicElement( arg[ 0], arg[ 1]);
			}
			
			return {
				init: function(){
				/*
				 * Resizer
				 */					
					PANEL_RESIZER_TOP.init();
					PANEL_RESIZER_BOTTOM.init();
				/*
				 * comic-element
				 */
					comicElementContainer = $( '#comic-element-container');
					
					appendComicElement( ImageElementClass.apply( {}, [ 'images/13.gif', 'penchan', 10, 10, 0, 100, 140, 0]));
					appendComicElement( TextElementClass.apply( {}, [ 0, 270, 'Hello', 50, 70, 1, 200, 160, 1]));
					
					COMIC_ELEMENT_OPERATOR.init( updateMouseCursor);
				/*
				 * 
				 */
					log = $( '#operation-catcher-log');

					resize();
					log.html( 'vector' +h2c.balloon.enabled());
					delete COMIC_ELEMENT_CONTROL.init;
				},
				resize: resize,
				onMouseMove: function( _mouseX, _mouseY){
					var l = DRAGGABLE_ELEMENT_ARRAY.length,
						_X = _mouseX -( _canvasX || canvasX),
						_Y = _mouseY -( _canvasY || canvasY),
						_elm, _x, _y;
						
					if( currentElement !== null && currentElement.busy() === true){
						currentElement.onMouseMove( _X, _Y);
						return true;
					}
					if( currentElement !== null){
						_x = currentElement.hitareaX();
						_y = currentElement.hitareaY();
						if( _x <= _X && _y <= _Y && _x + currentElement.hitareaW() >= _X && _y +currentElement.hitareaH() >= _Y){
							currentElement.onMouseMove( _X, _Y); // cursor
							return true;
						}
					}
					for( var i=0; i<l; i++){
						_elm = DRAGGABLE_ELEMENT_ARRAY[ i];
						_x = _elm.hitareaX();
						_y = _elm.hitareaY();
						// hitTest
						if( _x <= _X && _y <= _Y && _x + _elm.hitareaW() >= _X && _y +_elm.hitareaH() >= _Y){
							currentElement = _elm;
							currentElement.onMouseMove( _X, _Y); // cursor
							log.html( [ _X, _Y, _x, _y, i].join( ','));
							return true;
						}
					}
					currentElement = null;							
					COMIC_ELEMENT_OPERATOR.hide();
					log.html( [ _X, _Y, _x, _y].join( ','));
					return false;
				},
				onMouseUp: function( _mouseX, _mouseY){
					var ret = currentElement !== null && currentElement.busy() === true;
					ret === true && currentElement.onMouseUp( _mouseX -_canvasX || canvasX, _mouseY -_canvasY || canvasY);
					_canvasX = _canvasY = NaN;
					return ret;
				},
				onMouseDown: function( _mouseX, _mouseY){
					_canvasX = canvasX;
					_canvasY = canvasY;
					currentElement !== null && currentElement.onMouseDown( _mouseX -canvasX, _mouseY -canvasY);
					return currentElement !== null;
				},
				onMouseClick: function( _mouseX, _mouseY){
					return currentElement !== null &&
						currentElement !== PANEL_RESIZER_TOP &&
						currentElement !== PANEL_RESIZER_BOTTOM &&
						COMIC_ELEMENT_OPERATOR.onMouseClick( _mouseX -canvasX, _mouseY -canvasY);
				},
				onMouseOut: function( _mouseX, _mouseY){
					currentElement !== null && currentElement.busy() === true && currentElement.onMouseUp( _mouseX -canvasX, _mouseY -canvasY);
					_canvasX = _canvasY = NaN;
					currentElement = null;
					COMIC_ELEMENT_OPERATOR.hide();
					return false;
				},
				busy: function(){
					return currentElement !== null ? currentElement.busy() : false;
				},
				createImageElement: function( url, imagesetID, x, y, z, w, h){
					w = w || 200; //ActualWidth
					h = h || 150; //ActualHeight
					x = x || Math.floor( canvasW /2 -w /2);
					y = y || Math.floor( canvasH /2 -h /2);
					IMAGE_GROUP_EXPROLER.show( function( _url, _w, _h){
						var _comicElement = ImageElementClass.apply( {}, [ _url, imagesetID, x, y, z || -1, w, h, DRAGGABLE_ELEMENT_ARRAY.length -NUM_RESIZER]);
						appendComicElement( _comicElement);
						_comicElement.animate( undefined, undefined, _w, _h);
						SAVE( restoreComicElement, [ false, _comicElement], [ true, _comicElement], true);
					});
				},
				createTextElement: function( type, angle, text, x, y, z, w, h){
					type = type || 0;
					angle = angle || 0;
					text = text || '';
					w = w || 200;
					h = h || 150;
					x = x || Math.floor( canvasW /2 -w /2 +Math.random() *10);
					y = y || Math.floor( canvasH /2 -h /2 +Math.random() *10);
					var _comicElement = TextElementClass.apply( {}, [ type, angle, text, x, y, z || -1, w, h, DRAGGABLE_ELEMENT_ARRAY.length -NUM_RESIZER]);
					TEXT_EDITOR_CONTROL.show( _comicElement, function( _comicElement){
						appendComicElement( _comicElement);
						SAVE( restoreComicElement, [ false, _comicElement], [ true, _comicElement], true);
					});
				},
				getAsHTML: function( isAbsoluteUrl, isXHTML){
					var HTML_ARRAY = [],
						l = DRAGGABLE_ELEMENT_ARRAY.length,
						_timing = 0,
						_comicElement;

					while( HTML_ARRAY.length < l -NUM_RESIZER){
						_comicElement = getComicElementByTiming();
						if( _comicElement === null) break;
						HTML_ARRAY.push( _comicElement.getAsHTML( isAbsoluteUrl, isXHTML));
					}
					function getComicElementByTiming(){
						while( _timing < l *2){
							for(var i=NUM_RESIZER; i<l; ++i){
								if( _timing === DRAGGABLE_ELEMENT_ARRAY[ i].timing){
									++_timing;
									return DRAGGABLE_ELEMENT_ARRAY[ i];
								}
							}
							++_timing;
						}
						return null;
					}
					HTML_ARRAY.unshift(
						[
							'<div class="panel" ',
								'style="',
									'height:', canvasH, 'px;',
									'background-color:', ';',
								'"',
							'>'
						].join( '')
					);		
					HTML_ARRAY.push( '</div>');
					
					return HTML_ARRAY.join( h2c.LINE_FEED_CODE_TEXTAREA);
				},
				getAsJsonString: function(){
					var JSON_STRING_ARRAY = [],
						IMAGE_ARRAY = [],
						BALLOON_ARRAY = [],
						l = DRAGGABLE_ELEMENT_ARRAY.length,
						_timing = 0,
						_comicElement,
						cr = h2c.LINE_FEED_CODE_TEXTAREA;
						
					while( IMAGE_ARRAY.length + BALLOON_ARRAY.length < l -NUM_RESIZER){
						_comicElement = getComicElementByTiming();
						if( _comicElement === null) break;
						_comicElement.type === COMIC_ELEMENT_TYPE_IMAGE ? 
							IMAGE_ARRAY.push( _comicElement.getAsJsonString()) :
							BALLOON_ARRAY.push( _comicElement.getAsJsonString());
					}
					function getComicElementByTiming(){
						while( _timing < l *2){
							for(var i=NUM_RESIZER; i<l; ++i){
								if( _timing === DRAGGABLE_ELEMENT_ARRAY[ i].timing){
									++_timing;
									return DRAGGABLE_ELEMENT_ARRAY[ i];
								}
							}
							++_timing;
						}
						return null;
					}
					return [
						'{', cr,
							'"panel": {', cr,
							    '"border": 1,', cr,
							    '"comic_id": 5,', cr,
							    '"resource_picture_id": 1,', cr,
							    '"width": ', canvasW, ',', cr,
							    '"height": ', canvasH, ',', cr,
							    '"panel_pictures_attributes": {', cr,
							    	IMAGE_ARRAY.join( ',' +cr), cr,
							    '},', cr,
							    '"balloons_attributes": {', cr,
							    	BALLOON_ARRAY.join( ',' +cr), cr,
							    '}', cr,
							'}', cr,
						'}'
					].join( '');
				}
			}
		})( resize);
	
	/*
	 * 
	 */
		function resize( isResizerTopAction){
			GRID_CONTROL.resize();
			WHITE_GLASS_CONTROL.resize();
			PANEL_BORDER_CONTROL.resize();
			COMIC_ELEMENT_CONTROL.resize( isResizerTopAction);
		}
		return {
			init: function( _canvasW, _canvasH){
				canvasW = _canvasW || DEFAULT_PANEL_WIDTH;
				canvasH = _canvasH || DEFAULT_PANEL_HEIGHT;
				canvasX = Math.floor( ( windowW -canvasW) /2);
				canvasY = Math.floor( ( windowH -canvasH) /2);
				
				GRID_CONTROL.init();
				// WHITE_GLASS_CONTROL.init();
				PANEL_BORDER_CONTROL.init();
				COMIC_ELEMENT_CONTROL.init();
				
				resize();
				
				h2c.key.addKeyUpdateEvent( h2c.view.EDITOR, 32, false, false, function( e){
					if( e.type === 'keyup'){
						currentListener === null && COMIC_ELEMENT_CONTROL.busy() === false && updateMouseCursor( '');	
						isCanvasDraggble = false;
					} else {
						currentListener === null && COMIC_ELEMENT_CONTROL.busy() === false && updateMouseCursor( 'move');
						isCanvasDraggble = true;
					}
				});
				delete CANVAS_CONTROL.init;
			},
			x: function(){ return canvasX;},
			y: function(){ return canvasY;},
			onWindowResize: function( _windowW, _windowH){
				canvasX = Math.floor(( _windowW - canvasW) / 2);
				canvasY = Math.floor(( _windowH - canvasH) / 2);
				resize();
			},
			onMouseMove: function( _mouseX, _mouseY){
				if( currentListener === null && COMIC_ELEMENT_CONTROL.busy() === false && isCanvasDraggble === true && isDragging === true){
					canvasX = startCanvasX +_mouseX -xOffset;
					canvasY = startCanvasY +_mouseY -yOffset;
					resize();
				} else {
					COMIC_ELEMENT_CONTROL.onMouseMove( _mouseX, _mouseY)
				}
			},
			onMouseUp: function( _mouseX, _mouseY){
				if( COMIC_ELEMENT_CONTROL.onMouseUp( _mouseX, _mouseY) === false && isCanvasDraggble === true){
					isDragging = false;
					updateMouseCursor( '');
				}
			},
			onMouseDown: function( _mouseX, _mouseY){
				if( COMIC_ELEMENT_CONTROL.onMouseDown( _mouseX, _mouseY) === false && isCanvasDraggble === true){
					xOffset = _mouseX;
					yOffset = _mouseY;
					startCanvasX = canvasX;
					startCanvasY = canvasY;
					isDragging = true;
					updateMouseCursor( 'move');
				}
			},
			onMouseClick: function( _mouseX, _mouseY){
				COMIC_ELEMENT_CONTROL.onMouseClick( _mouseX, _mouseY)
			},
			onMouseOut: function( _mouseX, _mouseY){
				if( COMIC_ELEMENT_CONTROL.onMouseOut( _mouseX, _mouseY) === false){
					
				}
			},
			busy: function(){
				return isDragging === true || COMIC_ELEMENT_CONTROL.busy();
			},
			createImageElement: function( url, imagesetID, x, y, z, w, h){
				COMIC_ELEMENT_CONTROL.createImageElement( url, imagesetID, x, y, z, w, h);
			},
			createTextElement: function( type, angle, text, x, y, w, h, z){
				COMIC_ELEMENT_CONTROL.createTextElement( type, angle, text, x, y, w, h, z);
			},
			getAsHTML: COMIC_ELEMENT_CONTROL.getAsHTML,
			getAsJsonString: COMIC_ELEMENT_CONTROL.getAsJsonString
		}
	})();


	function updateMouseCursor( _cursor){
		if( currentCursor !== _cursor){
			currentCursor = _cursor;
			setTimeout(
				function(){
					ELM_MOUSE_EVENT_CHATCHER.style.cursor = _cursor;
				}, 0
			);
		}
	}
	function centering(){
		h2c.editor.onWindowResize( windowW, windowH);
	}	
	function mouseEventRellay( rellayMethod, e){
		var _mouseX = e.pageX,
			_mouseY = e.pageY;
		if( currentListener !== null && currentListener.busy() === true){
			currentListener[ rellayMethod]( _mouseX, _mouseY);
		} else {
			currentListener = null;
			var l = MOUSE_LISTENER_ARRAY.length,
				_listener;
			for( var i=0; i<l; ++i){
				_listener = MOUSE_LISTENER_ARRAY[ i];
				if( typeof _listener[ rellayMethod] === 'function' && _listener[ rellayMethod]( _mouseX, _mouseY) === true){
					currentListener = _listener;
					break;
				}
			}
		}
		e.stopPropagation();
		return false;
	}

	return {
		init: function(){
			var jqWindow = h2c.jqWindow();
			windowW = jqWindow.width();
			windowH = jqWindow.height();
			
			jqEditor = $( '#editor');

			h2c.key.addKeyDownEvent( h2c.view.EDITOR, 96, false, true, centering);
			h2c.key.addKeyDownEvent( h2c.view.EDITOR, 48, false, true, centering);

			HISTORY.init();
			MENU_BAR_CONTROL.EDIT.createSelection( 'centering', 'ctrl + 0', centering, true, true, true);
			
			WINDOWS_CONTROL.init();
			CANVAS_CONTROL.init();
			
			// last
			MENU_BAR_CONTROL.init();
			
			TEXT_EDITOR_CONTROL.init();
			IMAGE_GROUP_EXPROLER.init();
		/*
		 * jqMouseEventChacher は透明な要素で、
		 * マウスイベントをcurrentElement(currentElement)に伝えるのが仕事
		 * このような実装になるのは、ここの表示オブジェクトにイベントを設定した場合、表示が追いつかずマウスカーソルが外れたタイミングでイベントが終わってしまうため。
		 */
		/*
		 * MOUSE_LISTENER_ARRAY は、表示順に格納．手前の要素が最初
		 * MENU_BAR_CONTROL,
		 * WINDOW_CONTROL,
		 * CANVAS_CONTROL
		 * .busy() === true なら、そのままonMouseMove()にイベントを流す．これはArrayの後ろから、奥の表示要素から
		 * onMouseMove()に流してみて、false が帰れば、次にリスナーにも流す．
		 */
			MOUSE_LISTENER_ARRAY.push( MENU_BAR_CONTROL, WINDOWS_CONTROL, CANVAS_CONTROL);
			
			jqMouseEventChacher = $( ELM_MOUSE_EVENT_CHATCHER)
				.mousemove( function( e){
					return mouseEventRellay( 'onMouseMove', e);
				}).mousedown( function( e){
					return mouseEventRellay( 'onMouseDown', e);
				}).mouseup( function( e){
					return mouseEventRellay( 'onMouseUp', e);
				}).mouseout( function( e){
					return mouseEventRellay( 'onMouseUp', e);
				}).click( function( e){
					var _mouseX = e.pageX,
						_mouseY = e.pageY;
					if( CANVAS_CONTROL.busy() === true || WINDOWS_CONTROL.onMouseClick( _mouseX, _mouseY) === false){
						CANVAS_CONTROL.onMouseClick( _mouseX, _mouseY);
					}
					e.stopPropagation();
					return false;
				}).css( {
					height:	windowH // ie6
				});
			
			delete h2c.editor.init;
		},
		onWindowResize: function( _windowW, _windowH){
			windowW = _windowW;
			windowH = _windowH;
			
			/*
			 * ieは +'px'が不要みたい
			 */
			jqEditor.get( 0).style.height = _windowH +'px';
			ELM_MOUSE_EVENT_CHATCHER.style.height = _windowH +'px';
			
			WINDOWS_CONTROL.onWindowResize( _windowW, _windowH);
			MENU_BAR_CONTROL.onWindowResize( _windowW, _windowH);
			CANVAS_CONTROL.onWindowResize( _windowW, _windowH);
		},
		MIN_WIDTH:	320,
		MIN_HEIGHT:	320
	}
})();

h2c.log = ( function(){
	return {
		init: function(){}
	}
})();

h2c.io = ( function(){
	
	return {
		init: function(){}
	}
})();


/*
 * 画像一覧は
 * 	お気に入り画像一覧 > tag:ペン次郎 > ペン次郎：笑う
 *  最近アップロードされた画像 > images
 *  最近使われた画像 > images
 *  キャラクター画像庫 > アニマル系 > tag:ペン次郎 > ペン次郎：笑う
 *  風景画像庫 >
 *  効果画像庫 >
 *  アイテム画像庫 >
 *  
 * 画像一覧を読み込むタイミング
 */
h2c.file = ( function(){
	var TREE_TYPE_IS_COMIC = 1,
		TREE_TYPE_IS_IMAGE = 2,
		TREE_TYPE_IS_SETTING = 3,
		TREE_TYPE_IS_HELP = 4,
		FILE_TYPE_IS_FOLDER = 1,
		FILE_TYPE_IS_IMAGE = 2,
		FILE_TYPE_IS_PANEL = 3,
		FILE_TYPE_IS_SETTING = 4,
		FILE_TYPE_IS_HTML = 5,
		FILE_STATE_IS_UNKNOWN = 0,
		FILE_STATE_IS_OK = 1,
		FILE_STATE_IS_LOADING = 2,
		FILE_STATE_IS_ERROR = 3,
		FILE_STATE_IS_BROKEN = 4,
		TREE_EVENT_UPDTE = 'onUpdate',
		FILE_EVENT_UPDATED_ATTRIVUTE = 'onUpdate',
		FILE_EVENT_GET_SEQENTIAL_FILES = 'gotSeqentilFiles',
		ROLE_IS_SUPER_USER = 2^4,
		ROLE_IS_OWNER = 2^3,
		ROLE_IS_CREATOR = 2^2,
		ROLE_IS_ARTIST = 2^1,
		ROLE_IS_VISITOR = 2^0,
		ROLE_IS_UNKROWN = 2^-1,
		UPDATE_POLICY_SOCAV = 0x11111,// s: super user
		UPDATE_POLICY_SOCA_ = 0x11110,// o: owner( comic || panel || picture )
		UPDATE_POLICY_SOC__ = 0x11100,// c: creator
		UPDATE_POLICY_SO_A_ = 0x11010,// a: artist
		UPDATE_POLICY_SO___ = 0x11000,// v: visitor
		UPDATE_POLICY__O___ = 0x01000,// l: lisence manager
		UPDATE_POLICY_S____ = 0x10000,
		UPDATE_POLICY______ = 0x00000,
		FILEDATA_RESITER = [],
		FILEDATA_ACCESS = [];
		
	var REQUEST_CONTROLER = ( function(){
		var REQUEST_TICKET_RESISTER = [],
			DATA_IS_JSON = 1;
		
		var RequestTicketClass = ( function(){
			
		})();
		
		return {
			init: function(){
				delete REQUEST_CONTROLER.init;
			},
			getJson: function( _url, _onLoad, _onError){
				REQUEST_TICKET_RESISTER.push( RequestTicketClass.apply( {}, [ _url, _onLoad, _onError]));
			}
		}
	})();
	/* ----------------------
	 * folder
	 *  name
	 *  file [],
	 *  ID( imgGroupID, comicID)
	 * ----------------------
	 * 
	 * img-group(folder)
	 *  name - rename,
	 *  groupID,
	 *  img []
	 *  author, licence,
	 *  create, update
	 * 
	 * img
	 *  name - rename
	 *  src(id)
	 *  groupID - updateGropuID
	 *  actualWidth, actualHeight,
	 *  author, licence( 'by,no-resize'),
	 *  create, update, tag,
	 *  actualWidth, actualHeight
	 * 
	 * ----------------------
	 * 
	 * comic
	 *  id
	 *  name - rename
	 *  width
	 *  editmode
	 *  panel []
	 *  
	 * panel
	 *  comicID,
	 *  seqno,
	 * 	height,
	 *  border,
	 *  elments [
	 *    height,
	 *    bg-color,
	 *    bg-image,
	 *    bg-position,
	 *    bg-repeate,
	 *    border,  
	 *  ]
	 *  
	 * ----------------------
	 */
	var FILE_CONTROLER = ( function(){
		var FILE_EVENT_LISTENER_RESISTER = [],
			TREE_ACCESS = [];

		var TreeClass = function( TREE_ROOTFILE){
			var UID = TREE_ACCESS.length,
				currentFile = TREE_ROOTFILE,
				PARENT_FILE_RESITER = [],
				ACCESS = {
					fileEventChatcher:	dispatchFileEvent,
					destroy:			onDestroy
				};
			
			function dispatchFileEvent( fileEvent){
				
			}
			function onDestroy(){
				
			}
			
			TREE_ACCESS.push( ACCESS);
			
			FILE_CONTROLER.getSeqentialFiles( currentFile);
			return {
				ROOT_FILE :function(){
					return TREE_ROOTFILE;
				},
				currentFile: function(){
					return currentFile;
				},
				hierarky: function(){
					return PARENT_FILE_RESITER.length;
				},
				down: function( _index){
					if( typeof _index !== 'number' || _index < 0 || _index >= currentFile.getChildFileLength()) return;
					PARENT_FILE_RESITER.unshift( currentFile);
					currentFile = currentFile.getChildFileByIndex( _index);
					FILE_CONTROLER.getSeqentialFiles( currentFile);
					return currentFile;
				},
				up: function(){
					if( PARENT_FILE_RESITER.length === 0) return;
					currentFile = PARENT_FILE_RESITER.shift();
					FILE_CONTROLER.getSeqentialFiles( currentFile);
					return currentFile;
				},
				addEventListener: function( UID, _eventType, _callback){
					FILE_CONTROLER.addEventListener( UID, _eventType, _callback);
				},
				removeEventListener: function( UID, _eventType, _callback){
					FILE_CONTROLER.removeEventListener( UID, _eventType, _callback);
				},
				createSearchResultFolder: function( _searchParam){
					
				},
				destroySearchResultFolder: function( _searchParam){
					
				},
				destroy: function(){
					FILE_CONTROLER.destroyTree( UID);
				}
			}
		};

		var FileEventTicketClass = function( UID, _eventType, _callback){
			return {
				fileUID:	UID,
				eventType:	_eventType,
				callBack:	_callback
			}
		}
		
		var FileEventClass = function( eventType, file, key, value){
			return {
				eventType:			eventType,
				targetFile:			file,
				updatedAttribute:	key,
				updatedValue:		value
			}
		}

		function disptchFileEvent( eventType, fileUID, key, value){
			var _fileEvent = FileEventClass.apply( {}, [ eventType, fileUID, key, value]),
				l = TREE_ACCESS.length,
				_tree;
			for( var i=0; i<l; ++i){
				_tree = TREE_ACCESS[ i];
				_tree !== null && _tree.fileEventChatcher( _fileEvent);
			}
		}
		function getFileDataAccess( UIDorFILE){
			var l = FILEDATA_ACCESS.length,
				i = typeof UIDorFILE === 'number' ?
						( UIDorFILE > 0 && UIDorFILE > l ? UIDorFILE : -1) :
						( function(){
							for( var i=0; i<l; ++i){
								if( FILEDATA_ACCESS[ i] === UIDorFILE) return i;
							}
							return -1;
						})();
			return i !== -1 ? FILEDATA_ACCESS[ i] : null;
		}
		
		return {
			init: function(){
				delete FILE_CONTROLER.init;
			},
			createTree: function( _rootFileData){
				return TreeClass.apply( {}, [ _rootFileData]);
			},
			getFileData: function( _file){
				var _access = getFileDataAccess( _file);
				return _access !== null ? _access.DATA : null;
			},
			getSeqentialFiles: function( _uid){

			},
			updateFileAttribute: function( _uid, key, _value, _opt_callback){
				var _fileData = getFileDataAccess( _uid),
					_type = _fileData.TYPE;
				
			},			
			getFileAttribute: function( _uid, KEYorKEYARRAY){
				var _fileData = getFileDataAccess( _uid),
					_type = _fileData.TYPE;
			},
			move: function( _prentUID, _targetfile, _newFolder, _newIndex, _opt_callback){
				var _parentData = getFileDataAccess( _prentUID),
					_parentType = _parentData.TYPE,
					_targetData =  getFileDataAccess( _targetfile),
					_targetType = _targetData.TYPE;
			},
			replace: function( _uid, _file, _newIndex){
				
			}
		}
	})();
	
	/*
	 * fileのdataはobjectで保持している。
	 * h2c.file.の外からファイルをみるときは、FileClassを通して操作する。
	 * fileの変更、それに付随して追加されたイベントは、treeで管理される。
	 * treeがdestryされると、fileのイベントリスナーも全て削除される。
	 */
	
	var FileClass = function( TREE, PARENT_FILE, DATA){
		var TYPE = DATA.type,
			UID = FILEDATA_ACCESS.length,
			CHILDREN = DATA.children;
		
		FILEDATA_ACCESS.push(
			{
				TYPE:		TYPE,
				DATA:		DATA,
				CHILDREN:	CHILDREN,
				destroy:	function(){
								PARENT_FILE = DATA = CHILDREN = null;
								delete this.destroy;
							}
			}
		);
		return {
			TYPE: function(){ return TYPE;},
			state: function(){
				return DATA.state !== undefined ? DATA.state : FILE_STATE_IS_OK;
			},
			childFileLength: function(){
				return typeof CHILDREN === 'array' ? CHILDREN.length : 0;
			},
			getChildFileByIndex: function( _index){
				if( typeof _index !== 'number' || _index < 0 || typeof CHILDREN !== 'array' || _index >= CHILDREN.length) return;
				return FileClass.apply( {}, [ TREE, PARENT_FILE, this, CHILDREN[ _index]]);
			},
			getChildFileIndex: function( _FILEorFILEDATA){
				if( typeof CHILDREN !== 'array') return -1;
				var l = CHILDREN.length,
					_fileData = ( function(){
						var l = FILEDATA_RESITER.length;
						for( var i=0; i<l; ++i){
							if( _FILEorFILEDATA === FILEDATA_RESITER[ i]) return _FILEorFILEDATA;
						}
						return FILE_CONTROLER.getFileData( _FILEorFILEDATA);
					})();
				if( _fileData === null) return -1;
				for( var i=0; i<l; ++i){
					if( CHILDREN[ i] === _fileData) return i;
				}
				return -1;
			},
			isChildFile: function( _FILEorFILEDATA){
				return this.getChildFileIndex( _FILEorFILEDATA) !== -1;
			},			
			getAttribute: function( KEYorKEYARRAY){
				return FILE_CONTROLER.getFileAttribute( UID, KEYorKEYARRAY);
			},
			getSeqentialFiles: function(){
				FILE_CONTROLER.getSeqentialFiles( this);
			},
			updateAttribute: function( key, value, opt_callback){
				TREE.updateFileAttribute( UID, key, value, opt_callback);
			},
			move: function( _newFolder, _newIndex, opt_callback){
				TREE.move( PARENT_FILE, UID, _newFolder, _newIndex, opt_callback);
			},
			replace: function( _newIndex, opt_callback){
				TREE.replace( PARENT_FILE, UID, _newIndex, opt_callback);
			},
			addEventListener: function( _eventType, _callback){
				TREE.addEventListener( UID, _eventType, _callback);
			},
			removeEventListener: function( _eventType, _callback){
				TREE.removeEventListener( UID, _eventType, _callback);
			}
		}
	};
	
	var ROOT_FILEDATA = {
			name: 		'root',
			type:		FILE_TYPE_IS_FOLDER,
			children:	[]
		},
		IMAGE_FILEDATA = {
			name:		'image root',
			type:		FILE_TYPE_IS_FOLDER
		},
		COMIC_FILEDATA = {
			name:		'comic root',
			type:		FILE_TYPE_IS_FOLDER
		},
		SETTING_FILEDATA = {
			name:		'setting root',
			type:		FILE_TYPE_IS_FOLDER
		},
		HELP_FILEDATA = {
			name:		'help root',
			type:		FILE_TYPE_IS_FOLDER
		};
	FILEDATA_RESITER.push( ROOT_FILEDATA, IMAGE_FILEDATA, COMIC_FILEDATA, SETTING_FILEDATA, HELP_FILEDATA);
	ROOT_FILEDATA.children.push( IMAGE_FILEDATA, COMIC_FILEDATA, SETTING_FILEDATA, HELP_FILEDATA);

	var SYSTEM_TREE = FILE_CONTROLER.createTree( FileClass.apply( {}, [ null, null, ROOT_FILEDATA])),
		ROOT_FILE = SYSTEM_TREE.ROOT_FILE(),
		COMIC_ROOT_INDEX = ROOT_FILE.getChildFileByIndex( COMIC_FILEDATA),
		COMIC_ROOT_FILE = ROOT_FILE.getChildFileByIndex( COMIC_ROOT_INDEX),
		IMAGE_ROOT_INDEX = ROOT_FILE.getChildFileByIndex( IMAGE_FILEDATA),
		IMAGE_ROOT_FILE = ROOT_FILE.getChildFileByIndex( IMAGE_ROOT_INDEX),		
		SETTING_ROOT_INDEX = ROOT_FILE.getChildFileByIndex( SETTING_FILEDATA),
		SETTING_ROOT_FILE = ROOT_FILE.getChildFileByIndex( SETTING_ROOT_INDEX),
		HELP_ROOT_INDEX = ROOT_FILE.getChildFileByIndex( HELP_FILEDATA),
		HELP_ROOT_FILE = ROOT_FILE.getChildFileByIndex( HELP_ROOT_INDEX);

	return {
		init: function(){
			REQUEST_CONTROLER.init();
			FILE_CONTROLER.init();
			delete h2c.file.init;
		},
		createTree: function( _treeType){
			var _rootFile;
			if( _treeType === TREE_TYPE_IS_COMIC) _rootFile = COMIC_ROOT_FILE;
			if( _treeType === TREE_TYPE_IS_IMAGE) _rootFile = IMAGE_ROOT_FILE;
			if( _treeType === TREE_TYPE_IS_SETTING) _rootFile = SETTING_ROOT_FILE;
			if( _treeType === TREE_TYPE_IS_HELP) _rootFile = HELP_ROOT_FILE;
			if( _rootFile === undefined) return;
			return FILE_CONTROLER.createTree( _rootFile);
		},
		TREE_TYPE_IS_COMIC:		TREE_TYPE_IS_COMIC,
		TREE_TYPE_IS_IMAGE:		TREE_TYPE_IS_IMAGE,
		TREE_TYPE_IS_SETTING:	TREE_TYPE_IS_SETTING,
		FILE_TYPE_IS_FOLDER:	FILE_TYPE_IS_FOLDER,
		FILE_TYPE_IS_IMAGE:		FILE_TYPE_IS_IMAGE,
		FILE_TYPE_IS_PANEL:		FILE_TYPE_IS_PANEL,
		FILE_TYPE_IS_SETTING:	FILE_TYPE_IS_SETTING
	}
})();

// i18n
// login
// lib

h2c.fn( h2c.view);
h2c.fn( h2c.overlay);
h2c.fn( h2c.key);
h2c.fn( h2c.balloon);
h2c.fn( h2c.editor);
h2c.fn( h2c.file);

$(window).ready( h2c.init);
