/*
 * pettanR peta.apps.js
 *   version 0.5.49
 *   
 * author:
 *   itozyun
 * licence:
 *   3-clause BSD
 */

( function( pettanr, gOS, window, document, undefined ){
/*
 * PettanR service driver.
 */
	var MyAuthorID = 'current_author' in window ? current_author.id : ( pettanr.CONST.SERVER_SUPPORT === false ? 1 : -1 ),
		MyArtistID = 'current_artist' in window ? current_artist.id : ( pettanr.CONST.SERVER_SUPPORT === false ? 1 : -1 ),
		Driver     = null,
		FileAPI    = gOS.registerDriver( function(){
			var self = Driver = this,
				unregisteredFileDataJsonList = [];
			
			function onLoadJson( file, json ){
				var access = FileAPI.getFileDataAccess( file ),
					data   = access !== null ? access.DATA : null,
					i, l, args;
				if( data === null ){
					onErrorJson( file );
					return;
				};
				data.state = Const.FILE.STATE.OK;
				if( Type.isArray( json ) === true ){
					for( i = 0, l = json.length; i < l; ++i ){
						registerFileData( json[ i ], data );
					};
				} else
				if( Type.isNumber( json.id ) === true ){
					registerFileData( json, data );
				};
				while( 0 < unregisteredFileDataJsonList.length ){
					args = unregisteredFileDataJsonList.shift();
					registerFileData( args[ 0 ], args[ 1 ] );
					//alert( unregisteredFileDataJsonList.length )
				};
				file.dispatchEvent( FileAPI.createFileEvent( Const.FILE.EVENT.GET_SEQENTIAL_FILES, file, 'children', null ) );
			};
			function onErrorJson( file ){ 
				var data = FileAPI.getFileData( file );
				if( data !== null){
					data.state = Const.FILE.STATE.ERROR;
				};
				file.dispatchEvent( FileAPI.createFileEvent( Const.FILE.EVENT.GET_SEQENTIAL_FILES, file, 'children', null ) );
			};
			function registerFileData( json, parent ){
				var data;
				switch( parent ){
					// Comic
					case FILE_DATA_COMICS_ROOT    :
					case FILE_DATA_MY_COMICS_ROOT :
					case COMIC_ARRAY              :
						json.type = FILE_TYPE.COMIC;
						data = createFileData( json, COMIC_ARRAY, 'title' );
						if( data.json !== null ) data.json = true;
						if( data.author ){
							addChildData( data.author, data );
							data.author.id === MyAuthorID && addChildData( FILE_DATA_MY_COMICS_ROOT, data );
						};
						parent === FILE_DATA_COMICS_ROOT && addChildData( FILE_DATA_LATEST_COMICS, data );
						break;
					
					case STORY_ARRAY :
						json.type = FILE_TYPE.STORY;
						data = createFileData( json, STORY_ARRAY, 'x,y,z,t' );
						addChildData( FILE_DATA_STORY_ROOT, data );
						break;
					
					// Lisence
					case FILE_DATA_LISENCE :
					case LICENSE_ARRAY :
						json.type = FILE_TYPE.LICENSE;
						data = createFileData( json, LICENSE_ARRAY, 'name,caption,url,system_picture_id,settings,credit_pictures' );
						addChildData( FILE_DATA_LISENCE, data );
						break;
					// License Group
					case FILE_DATA_LISENCE_GROUP :
					case LICENSE_GROUP_ARRAY     :
						json.type = FILE_TYPE.LICENSE_GROUP;
						data = createFileData( json, LICENSE_GROUP_ARRAY, 'name,caption,url,classname' );
						addChildData( FILE_DATA_LISENCE_GROUP, data );
						break;
					
					// Speech Balloon Templete
					case FILE_DATA_BALLOON_ROOT :
					case BALLOON_TEMPLETE_ARRAY :
						json.type = FILE_TYPE.BALLOON_TEMPLETE;
						// register pettanr.balloon
						pettanr.newBalloon.register( json.settings );
						data = createFileData( json, BALLOON_TEMPLETE_ARRAY, 'name,caption,classname,settings' );
						addChildData( FILE_DATA_BALLOON_ROOT, data );
						break;
					
					// Author
					case FILE_DATA_AUTHOR_ROOT :
					case AUTHOR_ARRAY          :
						json.type = FILE_TYPE.AUTHOR;
						data = createFileData( json, AUTHOR_ARRAY, 'name,email,homepage_url' );
						addChildData( FILE_DATA_AUTHOR_ROOT, data );
						break;
					
					// Artist
					case FILE_DATA_ARTIST_ROOT :
					case ARTIST_ARRAY          :
						json.type = FILE_TYPE.ARTIST;
						data = createFileData( json, ARTIST_ARRAY, 'name,email,homepage_url' );
						addChildData( FILE_DATA_ARTIST_ROOT, data );
						break;

					case PICTURE_ARRAY :
						json.type = FILE_TYPE.PICTURE;
						data = createFileData( json, PICTURE_ARRAY, 'ext,revision,credit,settings' );
						break;
				
					// Resource Picture
					case FILE_DATA_RESOURCE_PICTURES_ROOT	 :
					case FILE_DATA_MY_RESOURCE_PICTURES_ROOT :
					case RESOURCE_PICTURE_ARRAY              :
						json.type = FILE_TYPE.RESOURCE_PICTURE;
						data = createFileData( json, RESOURCE_PICTURE_ARRAY, 'ext' );
						if( data.artist ){
							addChildData( data.artist, data );
							data.artist.id === MyArtistID && addChildData( FILE_DATA_MY_RESOURCE_PICTURES_ROOT, data );
						};
						break;

					// Original Picture
					case FILE_DATA_MY_ORIGINAL_PICTURES_ROOT :
					case ORIGINAL_PICTURE_ARRAY              :
						json.type = FILE_TYPE.ORIGINAL_PICTURE;
						data = createFileData( json, ORIGINAL_PICTURE_ARRAY, 'ext,filesize,width,height,md5' );
						if( data.artist ){
							// addChildData( data.artist, data );
							data.artist.id === MyArtistID && addChildData( FILE_DATA_MY_ORIGINAL_PICTURES_ROOT, data );
						};
						break;
					// Panel
					case FILE_DATA_PANELS_ROOT    :
					case FILE_DATA_MY_PANELS_ROOT :
					case PANEL_ARRAY              :
							json.type = FILE_TYPE.PANEL;
							data = createFileData( json, PANEL_ARRAY, 'border,publish,width,height' );
							addChildData( FILE_DATA_LATEST_PANELS, data );
							data.author.id === MyAuthorID && addChildData( FILE_DATA_MY_PANELS_ROOT, data );						
						break;
						
					// Panel Element
					case PANEL_ELEMENT_ARRAY :
						json.type = FILE_TYPE.PANEL_ELEMENT;
						data = createFileData( json, PANEL_ELEMENT_ARRAY, 'caption,url,width,height,x,y,z,t' ); // 画像の分
						// 噴出しの分
						// 景色の分
						break;
						
					default :
						if( parent.type === FILE_TYPE.COMIC ){
							//alert( 'comicstory' )
							data = registerFileData( json, STORY_ARRAY );
							//addChildData( parent, data );
							break;
						};
						throw new Error( 'build file error!' );	
				};
				return data;
			};
			/*
			 * 1. すでに一度以上ファイルを取得している場合、そのオブジェクトを取得．または、新規に作成．
			 * 2. オブジェクトに値をコピー・上書き
			 * 3. 
			 */
			function createFileData( json, array, copyProps ){
				copyProps = copyProps + ',id,type';
				
				var getIndex  = Util.getIndex,
					id        = json.id,
					data      = getResource( array, id ),
					p, i, l,
					histories, history,
					stories, story,
					elements;
				
				// Copy Props
				copyProps = copyProps.split( ',' );
				for( p in json ){
					if( getIndex( copyProps, p ) !== -1 ) data[ p ] = json[ p ];
				};
				
				// Common
				data.driver = Driver;				
				if( Type.isString( json.created_at ) === true ) data.created_at = Math.floor( new Date( json.created_at ).getTime() / 1000 );
				if( Type.isString( json.updated_at ) === true ) data.updated_at = Math.floor( new Date( json.updated_at ).getTime() / 1000 );
				
				// 実素材履歴
				histories = json.pictures;
				if( Type.isArray( histories ) === true ){
					for( i = 0, l = histories.length; i < l; ++i ){
						history = getResource( PICTURE_ARRAY, histories[ i ] );
						addChildData( data, history );
					};
					data.picture = history;
				};				
				
				// ストーリー
				stories = json.stories;
				if( Type.isArray( stories ) === true ){
					for( i = 0, l = stories.length; i < l; ++i ){
						story = getResource( STORY_ARRAY, stories[ i ] );
						/*
						 * 間違い！ t 順に格納
						 */
						addChildData( data, story );
					};
				};

				// パネル要素
				elements = json.elements;
				if( Type.isArray( elements ) === true ){
					for( i = 0, l = elements.length; i<l; ++i ){
						/*
						 * 間違い！ t 順に格納
						 */
						addChildData( data, getResource( PANEL_ELEMENT_ARRAY, elements[ i ] ) );
					};
				};
				
				// Artist
				if( json.artist || json.artist_id ){
					data.artist = getResource( ARTIST_ARRAY, json.artist || json.artist_id );
				};
					
				// Author
				if( json.author || json.author_id ){
					data.author = getResource( AUTHOR_ARRAY, json.author || json.author_id );
				};

				// Comic
				if( json.comic || json.comic_id ){
					data.comic = getResource( COMIC_ARRAY, json.comic || json.comic_id );
				};

				// Panel
				if( json.panel || json.panel_id ){
					data.panel = getResource( PANEL_ARRAY, json.panel || json.panel_id );
				};

				// 実素材
				if( json.picture || json.picture_id ){
					data.picture = getResource( PICTURE_ARRAY, json.picture || json.picture_id );
				};

				// License
				if( json.license || json.license_id ){
					data.license = getResource( LICENSE_ARRAY, json.license || json.license_id );
				};

				// License Group
				if( json.license_group || json.license_group_id ){
					data.license_group = getResource( LICENSE_GROUP_ARRAY, json.license_group || json.license_group_id );
				};
				
				// 原画
				if( json.original_picture || json.original_picture_id ){
					data.original_picture = getResource( ORIGINAL_PICTURE_ARRAY, json.original_picture || {
						id        : json.original_picture_id,
						ext       : json.ext,
						filesize  : json.filesize,
						width     : json.width,
						height    : json.height,
						md5       : json.md5,
						artist    : json.artist,
						artist_id : json.artist_id
					} );						
				};
				
				return data;
			};
			function addChildData( parent, child ){
				if( Type.isArray( parent.children ) === false ){
					parent.children = [ child ];
					return;
				};
				Util.getIndex( parent.children, child ) === -1 && parent.children.push( child );
			};
			function getResource( list, IDorOBJECT ){
				var data, id, obj;
				if( Type.isNumber( IDorOBJECT ) === true ){
					id = IDorOBJECT;
				} else
				if( IDorOBJECT && Type.isNumber( IDorOBJECT.id ) === true ){
					obj = IDorOBJECT;
					id  = obj.id;
					unregisteredFileDataJsonList.push( [ obj, list ] );
				} else {
					alert( 'getResource error' + IDorOBJECT.toString() );
					return undefined;
				};
				data = list[ id ];
				if( !data ) data = list[ id ] = { id : id };
				return data;
			};
			
			this.getSeqentialFiles = function( file ){
				var data = FileAPI.getFileData( file ),
					json = data.json || null;
				if( data.type === FILE_TYPE.COMIC && json === true ){
					if( pettanr.CONST.SERVER_SUPPORT === false ){
						json = [ 'json\/comics_', data.id, '.json' ].join( '' );
					} else {
						json = [ pettanr.CONST.PETTANR_ROOT_PATH, 'stories\/', data.id, '\/comic.json' ].join( '' );
					};
					data.json = null;
				};
				if( typeof json === 'string' ){
					FileAPI.getJson( file, json, onLoadJson, onErrorJson );
					data.state = Const.FILE.STATE.LOADING;
					if( data.json !== null ) delete data.json;
					return;
				};
			};
			this.getName = function( file ){
				var data = FileAPI.getFileData( file ),
					type = data !== null ? data.type : null;
				switch( type ){
					case FILE_TYPE.BALLOON :
						break;
					case FILE_TYPE.PANEL_ELEMENT :
						return 'パネル要素';
					case FILE_TYPE.RESOURCE_PICTURE :
						return [ '素材 ' + data.id, '.', data.ext ].join( '' );
					case FILE_TYPE.PICTURE  :
						return [ '実素材 ' + data.id, '.', data.ext ].join( '' );
					case FILE_TYPE.ORIGINAL_PICTURE :
						return [ '原画 ', data.id, '.', data.ext ].join( '' );
					case FILE_TYPE.COMIC :
						return data.title;
					case FILE_TYPE.STORY :
						return [ 'story id:', data.id, ' ', data.comic ? data.comic.title : 'no comic', ':', data.t ].join( '' );
					case FILE_TYPE.PANEL :
						return [ 'panel id:', data.id ].join( '' );
					case FILE_TYPE.AUTHOR :
						return [ data.name, '先生' ].join( '' );
					case FILE_TYPE.ARTIST :
						return [ data.name, '画伯' ].join( '' );
					case FILE_TYPE.BALLOON_TEMPLETE :
						return [ data.id, data.caption ].join( ':' );
					case FILE_TYPE.FOLDER :
				};
				return data.name;
			};
			this.getThumbnail = function( file ){
				var data = FileAPI.getFileData( file ),
					type = data !== null ? data.type : null;
				if( data === FILE_DATA_COMICS_ROOT ) return { className: 'file-type-cabinet' };
				switch( type ){
					
					case FILE_TYPE.PANEL_ELEMENT :
						return { className: 'file-type-charactor' };
					case FILE_TYPE.RESOURCE_PICTURE :
						return { image: [ pettanr.CONST.THUMBNAIL_PATH, data.id, '.', data.ext ].join( '' )};
					case FILE_TYPE.PICTURE  :
						//data = data.original_picture;
						return { image: [ pettanr.CONST.THUMBNAIL_PATH, data.id, '.', data.ext ].join( '' )};
					case FILE_TYPE.ORIGINAL_PICTURE :
						return { className: 'file-type-charactor' };			
						// return { image: [ pettanr.CONST.THUMBNAIL_PATH, data.id, '.', data.ext ].join( '' )};
					case FILE_TYPE.BALLOON :
						break;
					case FILE_TYPE.COMIC :
						return { className: 'file-type-comic' };
					case FILE_TYPE.STORY :
						data = data.panel;
					case FILE_TYPE.PANEL :
						return { className: 'file-type-panel' };
					case FILE_TYPE.AUTHOR :
						return { className: 'file-type-author' };
					case FILE_TYPE.ARTIST :
						return { className: 'file-type-artist' };
					case FILE_TYPE.BALLOON_TEMPLETE :
						return { className: 'file-type-balloon' };
					case FILE_TYPE.FOLDER :
						return { className: 'file-type-folder' };
				};
				return { className: 'file-type-broken' };
			};
			this.getSummary = function( file ){
				var data = FileAPI.getFileData( file ),
					type = data !== null ? data.type : null;
				if( data === FILE_DATA_COMICS_ROOT ) return 'cabinet file';
				switch( type ){
					case FILE_TYPE.BALLOON :
						break;
					case FILE_TYPE.PANEL_ELEMENT :
						return 'caption:' + data.caption + ' url:' + data.url + ' ' + data.width + 'x' + data.height + ' x:' + data.x + ' y:' + data.y + ' z:' + data.z + ' t:' + data.t;
					case FILE_TYPE.RESOURCE_PICTURE :
						data = data.picture;
					case FILE_TYPE.PICTURE :
						return '実素材情報 revision:' + data.revision + ' ' + data.credit + ' ' + data.settings;
					case FILE_TYPE.ORIGINAL_PICTURE :
						return [ '原画情報 ', data.width, 'x', data.height, ', filesize:', data.filesize, ' md5', data.md5 ].join( '' );
					case FILE_TYPE.COMIC :
						return 'comic id:' + data.id;
					case FILE_TYPE.STORY :
						data = data.panel;
					case FILE_TYPE.PANEL :
						return [ 'panel id:', data.id, ', width:', data.width, ', height:', data.height, ', 枠線:', data.border, ', 公開:', data.publish ].join( '' );
					case FILE_TYPE.AUTHOR :
						return 'author id:' + data.id;
					case FILE_TYPE.ARTIST :
						return [ 'artist id:', data.id, ' Email:', data.email || 'empty' , ', HP:', data.homepage_url || 'empty' ].join( '' );
					case FILE_TYPE.BALLOON_TEMPLETE :
						return data.name + ', ' + data.settings;
					case FILE_TYPE.FOLDER :
						return 'pettanR folder';
				};
				return 'pettanR unknown file';
			};
			this.read = function( file ){
				var data = FileAPI.getFileData( file ),
					type = data !== null ? data.type : null,
					ret, i, elm;
				switch( type ){
					case FILE_TYPE.COMIC :
						ret = Util.copy( data );
						ret.panels = ret.children;
						if( Type.isArray( ret.panels ) === true ){
							for( i = ret.panels.length; i; ){
								elm = ret.panels[ --i ];
								elm.elements = elm.children;
							};
						};
						return ret;
					case FILE_TYPE.STORY :
						ret = Util.copy( data );
						ret.panel.elements = ret.panel.children;
						return ret;
					case FILE_TYPE.PANEL :
						ret = Util.copy( data );
						ret.elements = ret.children;
						return ret;
					case FILE_TYPE.PANEL_ELEMENT :
					case FILE_TYPE.BALLOON :
					case FILE_TYPE.ORIGINAL_PICTURE :
				};
			};
			this.write = function( file, newData, onUpdate ){
				var data = FileAPI.getFileData( file ),
					type = data !== null ? data.type : null;
				switch( type ){
					case FILE_TYPE.COMIC :
					case FILE_TYPE.PANEL :
					case FILE_TYPE.PANEL_ELEMENT :
					case FILE_TYPE.BALLOON :
					case FILE_TYPE.ORIGINAL_PICTURE :
				};
			};
			this.viewerApplicationList = function( file ){
				var data = FileAPI.getFileData( file ),
					type = data !== null ? data.type : null;
				if( data === FILE_DATA_MY_ORIGINAL_PICTURES_ROOT ) return [ PremiumSatge ];
				switch( type ){
					case FILE_TYPE.COMIC :
					case FILE_TYPE.PANEL :
					case FILE_TYPE.STORY :
						return [ Reader ];
					case FILE_TYPE.PANEL_ELEMENT :
					case FILE_TYPE.BALLOON :
					case FILE_TYPE.ORIGINAL_PICTURE :
						break;
					case FILE_TYPE.ARTIST :
						return [ PremiumSatge ];
					default :						
				};
				return [];
			};
			this.editorApplicationList = function( file ){
				var data = FileAPI.getFileData( file ),
					type = data !== null ? data.type : null;
				switch( type ){
					case FILE_TYPE.COMIC :
						return [ Editor, ComicConsole ];
					case FILE_TYPE.PANEL :
						return [ Editor ];
					case FILE_TYPE.PANEL_ELEMENT :
					case FILE_TYPE.BALLOON :
					case FILE_TYPE.ORIGINAL_PICTURE :
					case FILE_TYPE.ARTIST :
					default :						
				};
				return [];
			}
		}),
		Const = FileAPI.getConst(),
		FILE_TYPE = Util.extend(
			Const.FILE.TYPE,
			{
				COMIC            : FileAPI.createFileTypeID(),
				STORY            : FileAPI.createFileTypeID(),
				PANEL            : FileAPI.createFileTypeID(),
				PANEL_ELEMENT    : FileAPI.createFileTypeID(),
				BALLOON          : FileAPI.createFileTypeID(),
				BALLOON_TEMPLETE : FileAPI.createFileTypeID(),	
				ORIGINAL_PICTURE : FileAPI.createFileTypeID(),
				RESOURCE_PICTURE : FileAPI.createFileTypeID(),
				PICTURE          : FileAPI.createFileTypeID(),
				AUTHOR           : FileAPI.createFileTypeID(),
				ARTIST           : FileAPI.createFileTypeID(),
				LICENSE          : FileAPI.createFileTypeID(),
				LICENSE_GROUP    : FileAPI.createFileTypeID(),
			}
		),
		AUTHOR_ARRAY  = [],
		ARTIST_ARRAY  = [],
		COMIC_ARRAY   = [],
		STORY_ARRAY   = [],
		PANEL_ARRAY   = [],
		LICENSE_ARRAY = [],
		PICTURE_ARRAY = [],
		LICENSE_GROUP_ARRAY    = [],
		PANEL_ELEMENT_ARRAY    = [],
		ORIGINAL_PICTURE_ARRAY = [],
		RESOURCE_PICTURE_ARRAY = [],
		BALLOON_TEMPLETE_ARRAY = [],
		BASIC_LICENSES = 'cc_by,cc_nc,cc_nd,cc_sa,keep_aspect_ratio,no_convert,no_flip,no_resize'.split( ','),
		FILE_DATA_SERVICE_ROOT = {
			name:		'PettanR root',
			type:		FILE_TYPE.FOLDER,
			children:	[]
		},
		FILE_DATA_COMICS_ROOT = {
			name:		'Comics',
			type:		FILE_TYPE.FOLDER,
			children:	[],
			driver:		Driver,
			json:		pettanr.CONST.URL_COMICS_JSON
		},
		FILE_DATA_PANELS_ROOT = {
			name:		'Panels',
			type:		FILE_TYPE.FOLDER,
			children:	[],
			driver:		Driver,
			json:		pettanr.CONST.URL_PANELS_JSON
		},
		FILE_DATA_RESOURCE_PICTURES_ROOT = {
			name:		'素材',
			type:		FILE_TYPE.FOLDER,
			children:	[],
			driver:		Driver,
			json:		pettanr.CONST.URL_RESOURCE_PICTURES_JSON
		},
		FILE_DATA_ORIGINAL_PICTURES_ROOT = {
			name:		'原画',
			type:		FILE_TYPE.FOLDER,
			children:	[],
			driver:		Driver
		},
		FILE_DATA_MY_COMICS_ROOT = {
			name:		'My Comics',
			type:		FILE_TYPE.FOLDER,
			children:	[],
			driver:		Driver,
			id:			MyAuthorID,
			json:       pettanr.CONST.URL_MY_COMICS_JSON
		},
		FILE_DATA_LATEST_COMICS = {
			name:		'Latest Comics',
			type:		FILE_TYPE.FOLDER,
			children:	[]
		},
		FILE_DATA_STORY_ROOT = {
			name:		'Stories',
			type:		FILE_TYPE.FOLDER,
			children:	[],
			driver:		Driver
		},
		FILE_DATA_LATEST_PANELS = {
			name:		'Latest Panels',
			type:		FILE_TYPE.FOLDER,
			children:	[]
		},
		FILE_DATA_MY_PANELS_ROOT = {
			name:		'My Panels',
			type:		FILE_TYPE.FOLDER,
			children:	[],
			driver:		Driver,
			json:		pettanr.CONST.URL_MY_PANELS_JSON
		},
		FILE_DATA_MY_RESOURCE_PICTURES_ROOT = {
			name:		'My 素材画像',
			type:		FILE_TYPE.FOLDER,
			children:	[],
			driver:		Driver,
			// json:		pettanr.CONST.URL_RESOURCE_PICTURES_JSON,
			id:			MyArtistID
		},
		FILE_DATA_MY_ORIGINAL_PICTURES_ROOT = {
			name:		'My 原画',
			type:		FILE_TYPE.FOLDER,
			children:	[],
			driver:		Driver,
			json:		pettanr.CONST.URL_MY_ORIGINAL_PICTURES_JSON,
			id:			MyArtistID
		},
		FILE_DATA_AUTHOR_ROOT = {
			name:		'Authors',
			type:		FILE_TYPE.FOLDER,
			children:	[]
		},
		FILE_DATA_ARTIST_ROOT = {
			name:		'Artists',
			type:		FILE_TYPE.FOLDER,
			children:	[]
		},
		FILE_DATA_LISENCE_ROOT = {
			name:		'Lisence Root',
			type:		FILE_TYPE.FOLDER,
			children:	[]
		},
		FILE_DATA_LISENCE = {
			name:		'Lisence',
			type:		FILE_TYPE.FOLDER,
			children:	[]
		},
		FILE_DATA_LISENCE_GROUP = {
			name:		'Lisence Group',
			type:		FILE_TYPE.FOLDER,
			children:	[]
		},
		FILE_DATA_BALLOON_ROOT = {
			name:		'Balloon Templetes',
			type:		FILE_TYPE.FOLDER,
			children:	[],
			driver:		Driver,
			json:		pettanr.CONST.SPEECH_BALOON_TEMPLETE
		};
	FILE_DATA_SERVICE_ROOT.children.push( FILE_DATA_COMICS_ROOT, FILE_DATA_RESOURCE_PICTURES_ROOT, FILE_DATA_ORIGINAL_PICTURES_ROOT, FILE_DATA_LISENCE_ROOT, FILE_DATA_BALLOON_ROOT );
	FILE_DATA_COMICS_ROOT.children.push( FILE_DATA_MY_COMICS_ROOT, FILE_DATA_LATEST_COMICS, FILE_DATA_AUTHOR_ROOT, FILE_DATA_STORY_ROOT, FILE_DATA_PANELS_ROOT );
	FILE_DATA_PANELS_ROOT.children.push( FILE_DATA_LATEST_PANELS, FILE_DATA_MY_PANELS_ROOT );
	FILE_DATA_RESOURCE_PICTURES_ROOT.children.push( FILE_DATA_MY_RESOURCE_PICTURES_ROOT, FILE_DATA_ARTIST_ROOT );
	FILE_DATA_LISENCE_ROOT.children.push( FILE_DATA_LISENCE_GROUP, FILE_DATA_LISENCE );
	FILE_DATA_ORIGINAL_PICTURES_ROOT.children.push( FILE_DATA_MY_ORIGINAL_PICTURES_ROOT );
	
	FileAPI.createFolderUnderRoot( FILE_DATA_SERVICE_ROOT );

	Driver.isPettanrFileInstance = function( file ){
		if( FileAPI.isFileInstance( file ) === true ){
			var _data = FileAPI.getFileData( file.getUID() );// file でなく  file.getUID()
			return _data !== null && _data.driver === Driver;
		};
		return false;
	};

var Cabinet = gOS.registerApplication( function(){
	var self         = this,
		finder       = null,
		tree         = null,
		nodeClose    = null,
		nodePath     = null,
		nodeBody     = null,
		headerH      = 0,
		eventRoot    = null;

	this.bgColor     = '#FFFFFF';
	this.MIN_WIDTH   = 500;
	this.MIN_HEIGHT  = 300;
	this.onInit = function(){
		self.rootElement.id = 'cabinet-root';
		self.rootElement.innerHTML = [
			'<div id="cabinet-header">',
				'<div class="header-title">Cabinet</div>',
				'<div id="cabinet-close-button">x</div>',
				'<div id="cabinet-path" class="finder-path"></div>',
			'</div>',
			'<div id="cabinet-container" class="finder-container"></div>'
		].join( '' );
		
		self.fetchCSS( pettanr.CONST.URL_PETA_APPS_CSS );
		
		tree      = FileAPI.createTree( FILE_DATA_SERVICE_ROOT );
		eventRoot = self.getPointingDeviceEventTreeRoot();
		
		delete self.onInit;
	};
	this.onOpen = function( _w, _h ){
		headerH = Util.getElementSize( document.getElementById( 'cabinet-header' ) ).height;
		
		nodeClose = eventRoot.createNode( document.getElementById( 'cabinet-close-button' ), false, false, 'close-button-hover', 'pointer' );
		nodeClose.addEventListener( 'click', Cabinet.shutdown );
		nodePath  = eventRoot.createNode( document.getElementById( 'cabinet-path' ), false, false );
		nodeBody  = eventRoot.createNode( document.getElementById( 'cabinet-container' ), false, true, null, '', true );
		
		finder = self.createFinder( nodeBody, tree );
		finder.createPath( nodePath );
		self.onPaneResize( _w, _h );
	};
	this.onClose = function(){
		finder.destroy();
		tree.destroy();
		finder = tree = null;
	};
	this.onPaneResize = function( w, h ){
		nodePath.width( w );
		nodeBody.update( 0, headerH, w, h - headerH );
		finder.resize( w, h - headerH );
	};
}, false, true, 'Cabinet', 'cabinet', null, '#1C1C1C' );

var Gallery = gOS.registerApplication( function(){
	var self         = this,
		finder       = null,
		tree         = null,
		nodeClose    = null,
		nodePath     = null,
		nodeBody     = null,
		headerH      = 0,
		eventRoot    = null;

	this.bgColor     = '#FFFFFF';
	this.MIN_WIDTH   = 500;
	this.MIN_HEIGHT  = 300;
	this.onInit = function(){
		self.rootElement.id        = 'gallery-root';
		self.rootElement.innerHTML = [
			'<div id="gallery-header">',
				'<div class="header-title">Gallery</div>',
				'<div id="gallery-close-button">x</div>',
				'<div id="gallery-path" class="finder-path"></div>',
			'</div>',
			'<div id="gallery-container" class="finder-container"></div>'
		].join( '' );
		
		self.fetchCSS( pettanr.CONST.URL_PETA_APPS_CSS );
		
		tree = FileAPI.createTree( FILE_DATA_RESOURCE_PICTURES_ROOT );
		var	_root  = tree.getRootFile(),
			_myPic = _root.getChildFileAt( 0 ),
			_pic   = _root.getChildFileAt( 1 );
		_myPic.getSeqentialFiles();
		_pic.getSeqentialFiles();
		_myPic.destroy();
		_pic.destroy();
		
		eventRoot = self.getPointingDeviceEventTreeRoot();
	};
	this.onOpen = function( w, h ){
		headerH = Util.getElementSize( document.getElementById( 'gallery-header' ) ).height;
		
		nodeClose = eventRoot.createNode( document.getElementById( 'gallery-close-button' ), false, false, 'close-button-hover', 'pointer' );
		nodeClose.addEventListener( 'click', Gallery.shutdown );
		nodePath = eventRoot.createNode( document.getElementById( 'gallery-path' ), false, false );
		nodeBody = eventRoot.createNode( document.getElementById( 'gallery-container' ), false, true, null, '', true );
		
		finder = self.createFinder( nodeBody, tree );
		finder.createPath( nodePath );
		self.onPaneResize( w, h );
	};
	this.onClose = function(){
		// finder.destroy();
		// tree.destroy();
		finder = tree = null;
	};
	this.onPaneResize = function( w, h ){
		nodePath.width( w );
		nodeBody.update( 0, headerH, w, h - headerH );
		finder.resize( w, h - headerH );
	};
}, false, true, 'Gallery', 'gallery', null, '#01A31C' );

var Backyard = gOS.registerApplication( function(){
	var self         = this;
	
	this.bgColor     = '#FFFFFF';
	this.MIN_WIDTH   = 500;
	this.MIN_HEIGHT  = 300;
	this.onInit = function(){
	};
	this.onOpen = function( _w, _h, _option ){
	}
	this.onClose = function(){
	}
	this.onPaneResize = function( _w, _h){
	}
}, false, false, 'Settings', 'settings', null, '#DDDDDD' );

if( pettanr.DEBUG === true){
	var Debug = gOS.registerApplication( function(){
		var self = this,
			elmDl,
			data = ( function(){
				var data = {
					pettanR:       pettanr.version,
					ua:            navigator.userAgent,
					platform:      navigator.platform,
					appVersion:    navigator.appVersion,
					appCodeName:   navigator.appCodeName,
					appName:       navigator.appName,
					language:      navigator.browserLanguage || navigator.language,
					ActiveX:       UA.ACTIVEX,
					RenderingMode: UA.isStanderdMode === true ? 'Standerd' : 'Quirks'
				};
				if( UA.IE ){
					data.version = UA.IE;
					if( UA.ieVersion >= 8 ) data.RenderingVersion = UA.ieRenderingVersion;
					data.browserType = UA.STANDALONE === true ? 'Standalone' : 'bundle';
					if( UA.ieVersion < 9 ) {
						data.vml = UA.VML;
					} else {
						data.svg = UA.SVG;
					}
				};
				return data;
			})();

		this.bgColor     = '#FFFFFF';
		this.MIN_WIDTH   = 500;
		this.MIN_HEIGHT  = 300;
		this.onInit = function(){
			self.rootElement.id = 'debug-root';
			self.rootElement.innerHTML = '<dl id="useragent" class="dl-table clearfix"></dl>';
		};
		this.onOpen = function( _w, _h, _option ){
			elmDl = document.getElementById( 'useragent' );
			var elmDt, elmDd;
			for( var key in data ){
				elmDt = document.createElement( 'dt' );
				elmDt.innerHTML = key;
				elmDd = document.createElement( 'dd' );
				elmDd.innerHTML = '' + data[ key];
				if( !data[ key ] ) elmDd.style.color = 'red';
				elmDl.appendChild( elmDt );
				elmDl.appendChild( elmDd );
			}
		}
		this.onClose = function(){
			
		}
		this.onPaneResize = function( _w, _h ){
			
		}
	}, false, true, 'Debug', 'debug', null, '#01A31C' );
}

/* ----------------------------------------
 * Image Group Exproler
 *  - overlay
 */
var PremiumSatge = gOS.registerApplication( function(){
	var BASE_PATH      = pettanr.CONST.RESOURCE_PICTURE_PATH,
		THUMB_PATH     = pettanr.CONST.THUMBNAIL_PATH,
		LIMIT_FILESIZE = 1024 * 100,
		ICON_ARRAY     = [];
		
	var self             = this,
		tree, rootFile,
		winW, winH, wrapX,
		elmContainer, elmIconOrigin, elmName, elmButton,
		containerW, containerH,	
		itemW, itemH, buttonW,
		onUpdate        = null,
		onUpdateData    = null,
		onUpdateContext = null,
		artistID        = -1;

	var ImageGroupIconClass = function( index, data ){
		var elmIconWrap     = elmIconOrigin.cloneNode( true ),
			elmIconTitle    = Util.getElementsByClassName( elmIconWrap, 'image-group-item-title' )[ 0 ],
			originalPicture = data.original_picture || data,
			SRC             = [ BASE_PATH, data.id, '.', data.ext ].join( ''),
			LOW_SRC         = originalPicture.filesize && originalPicture.filesize > LIMIT_FILESIZE ? [ THUMB_PATH, data.id, '.', data.ext ].join( '' ) : null,
			reversibleImage = null,
			timer           = null,
			onEnterFlag     = false,
			instance        = this;
		elmContainer.appendChild( elmIconWrap );
		elmIconWrap.style.left = ( index * itemW ) + 'px';
		elmIconTitle.appendChild( document.createTextNode( originalPicture.filesize + 'bytes' ) );
		
		function onImageLoad( url, _imgW, _imgH ){
			data.width  = _imgW = _imgW || originalPicture.width  || 64;
			data.height = _imgH = _imgH || originalPicture.height || 64;
			elmIconTitle.firstChild.data = _imgW + 'x' + _imgH;
			var zoom = 128 /( _imgW > _imgH ? _imgW : _imgH ),
				MATH_FLOOR = Math.floor,
				h = MATH_FLOOR( _imgH * zoom ),
				w = MATH_FLOOR( _imgW * zoom );
			reversibleImage.elm.style.cssText = [
				'width:',  w, 'px;',
				'height:', h, 'px;',
				'margin:', MATH_FLOOR( itemH / 2 - h / 2 ), 'px ', MATH_FLOOR( itemW / 2 - w / 2 ), 'px 0'
			].join('');
			reversibleImage.resize( w, h );
			self.addEventListener( elmIconWrap, 'click', onClick );
		};
		
		function onClick(){
			onUpdateData = data;
			PremiumSatge.shutdown();
		};
		
		function asyncDraw(){
			reversibleImage = pettanr.image.createReversibleImage( LOW_SRC || SRC, itemW, itemH, onImageLoad );
			elmIconWrap.appendChild( reversibleImage.elm );
			onEnterFlag = true;
			timer = null;
		};
		
		this.onEnter = function( delay ){
			self.addTimer( asyncDraw, delay, true );
			delete instance.onEnter;
		};
		this.destroy = function(){
			delete instance.destroy;
			// timer && window.clearTimeout( timer );
			self.removeTimer( asyncDraw );
			self.removeEventListener( elmIconWrap );
			reversibleImage !== null && reversibleImage.destroy();
			// Util.removeAllChildren( elmIconWrap );
			// elmContainer.removeChild( elmIconWrap );
			reversibleImage = elmIconWrap = elmIconTitle = data = timer = null;
		};
	};
	
	function onEnterShowImage(){
		var l = ICON_ARRAY.length,
			_start = -wrapX /itemW -1,
			_end = _start + winW /itemW +1,
			_icon;
		for( var i=0, c = 0; i<l; ++i){
			_icon = ICON_ARRAY[ i ];
			if( _start < i && i < _end && _icon.onEnter ){
				_icon.onEnter( c * 100 );
				c++;
			}
		}
		//onEnterInterval !== null && window.clearTimeout( onEnterInterval );
		//onEnterInterval = null;
		self.removeTimer( onEnterShowImage );
	};
	function clickClose(){
		PremiumSatge.shutdown();
	};
	function onMouseWheel( e ){
		if( winW < containerW ){
			wrapX += e.wheelDelta / 2;
			wrapX = wrapX > 0 ? 0 : wrapX < winW -containerW ? winW -containerW : wrapX;
			elmContainer.style.left = wrapX + 'px';
			
			self.removeTimer( onEnterShowImage );
			self.addTimer( onEnterShowImage, 500 );
		};
		return false;			
	};
	
	function drawIcons(){
		while( ICON_ARRAY.length > 0 ){
			ICON_ARRAY.shift().destroy();
		};
		var _index = rootFile.search( {
				id   : artistID,
				type : FILE_TYPE.ARTIST
			})[ 0 ],
			_artistFile = rootFile.getChildFileAt( _index ),
			file;
		if( _artistFile !== null ){
			for( var i=0, l=_artistFile.getChildFileLength(); i<l; ++i ){
				file = _artistFile.getChildFileAt( i );
				ICON_ARRAY.push( new ImageGroupIconClass( i, FileAPI.getFileData( file ) ));
				file.destroy();
			};
			elmName.firstChild.data = _artistFile.getName();
			_artistFile.destroy();
		};
	};
	
	function onFadeout(){
		while( ICON_ARRAY.length > 0 ){
			ICON_ARRAY.shift().destroy();
		};
		onUpdate !== null && onUpdateData !== null && onUpdate.call( onUpdateContext, onUpdateData );
		onUpdate = onUpdateData = onUpdateContext = null;
		PremiumSatge.shutdown();
	};
	
	
	this.MIN_WIDTH   = 320;
	this.MIN_HEIGHT  = 320;
	this.onInit = function(){
		self.rootElement.id = 'image-group-wrapper';

		self.rootElement.innerHTML = [
			'<div id="image-group-icon-container"></div>',
			'<div id="image-group-name">NO DATA...</div>',
			'<div id="image-group-button" class="button">close</div>'
		].join( '' );
		
		self.fetchCSS( pettanr.CONST.URL_PETA_APPS_CSS );
		
		tree      = FileAPI.createTree( FILE_DATA_ARTIST_ROOT );
		rootFile  = tree.getRootFile();
	};
	this.onOpen = function( _windowW, _windowH, _ARTISTIDorFILE, _onUpdate, opt_thisObj ){
		elmContainer  = document.getElementById( 'image-group-icon-container' );
		containerH    = Util.getElementSize( elmContainer ).height;
		
		elmIconOrigin = ( function(){
			var ret  = document.createElement( 'div' ),
				data = document.createElement( 'div' );
			ret.appendChild( data );
			ret.className  = 'image-group-item';
			data.className = 'image-group-item-title';
			return ret;
		})();

		var size      = Util.getElementSize( elmIconOrigin );
		itemW         = size.width;
		itemH         = size.height;

		elmName       = document.getElementById( 'image-group-name' );
		elmButton     = document.getElementById( 'image-group-button' );
		
		buttonW       = Util.getElementSize( elmButton ).width;
		
		self.addEventListener( elmContainer, 'mousewheel', onMouseWheel );
		self.addEventListener( elmButton, 'click', clickClose );
		tree.addTreeEventListener( Const.TREE.EVENT.UPDATE, drawIcons );
		
		var data = FileAPI.getFileData( _ARTISTIDorFILE );
		if( !data ){
			artistID = MyArtistID || -1;
		} else
		if( data.type === FILE_TYPE.ARTIST || FILE_DATA_MY_ORIGINAL_PICTURES_ROOT === data ){
			artistID = data.id || -1;
		} else
		if( Type.isNumber( _ARTISTIDorFILE ) === true ){
			artistID = _ARTISTIDorFILE;
		};
		
		onUpdate = _onUpdate || null;
		onUpdateContext = opt_thisObj || null;
		onUpdateData = null;
		
		drawIcons();
		
		wrapX = 0;
		containerW = ICON_ARRAY.length * itemW;
		
		winW = _windowW;
		winH = _windowH;
		var w = winW > containerW ? winW : containerW,
			h = _windowH > containerH ? containerH : _windowH,
			MATH_FLOOR = Math.floor;

		$( elmContainer ).css( {
			width:		w,
			height:		0,
			left:		0,
			top:		MATH_FLOOR( _windowH /2 )
		}).stop().animate( {
			height: 	h,
			top:		MATH_FLOOR( _windowH /2 - h /2 )
		}, onEnterShowImage );
		
		elmButton.style.cssText = [
			'left:', MATH_FLOOR( _windowW /2 - buttonW /2 ), 'px;',
			'top:',  MATH_FLOOR( _windowH /2 + containerH /2 +10 ), 'px'
		].join('');
	}
	this.onPaneResize = function( _windowW, _windowH ){
		var w = _windowW > containerW ? _windowW : containerW,
			h = _windowH > containerH ? containerH : _windowH,
			MATH_FLOOR = Math.floor,
			offsetW = MATH_FLOOR( _windowW /2 -winW /2 );
			
		winW = _windowW;
		winH = _windowH;
		if( offsetW <= 0 ){ // smaller
			$( elmContainer ).stop().css( {
				left:				offsetW,
				width:				w
			}).animate( {
				left:				0,
				top:				MATH_FLOOR( _windowH /2 -h /2 )
			});					
		} else {
			$( elmContainer ).stop().css( { // bigger
				left:				0,
				width:				w,
				borderLeftWidth:	offsetW
			}).animate( {
				top:				MATH_FLOOR( _windowH /2 -h /2 ),
				borderLeftWidth:	0
			});
		}
		elmButton.style.cssText = [
			'left:', MATH_FLOOR( _windowW /2 -buttonW /2 ), 'px;',
			'top:',  MATH_FLOOR( _windowH /2 +containerH /2 + 10 ), 'px'
		].join('');
		onEnterShowImage();
	}
	this.onClose = function(){
		if( tree === null ) return true;
		$( elmContainer ).stop().animate( {
				height:	0,
				top:	Math.floor( winH / 2 )
			}, onFadeout );
		// onEnterInterval !== null && window.clearTimeout( onEnterInterval );
		// onEnterInterval = null;
		self.removeTimer();
		
		tree.removeTreeEventListener( Const.TREE.EVENT.UPDATE, drawIcons );
		tree.destroy();
		tree = rootFile = null;
		
		return false;
	}
}, true, true, 'Premium Stage', 'premiumStage', null, '#C3325F' );


/* ----------------------------------------
 * Text Editor
 *  - overlay
 */
var TextEditor = gOS.registerApplication( function(){
	var elmTextarea, elmButton,
		textElement, onUpdate,
		ID = 'textEditor',
		panelX, panelY,
		self = this;
	
	function clickOK(){
		textElement && textElement.text( elmTextarea.value );
		self.addAsyncCall( asyncCallback );
	};
	
	function asyncCallback(){
		onUpdate && onUpdate( textElement );
		onUpdate = textElement = null;
		TextEditor.shutdown();
	};
	

	function textareaFitHeight(){
		var rows = 0;
		while( elmTextarea.offsetHeight < textElement.h ){
			rows++;
			elmTextarea.rows = rows;
		};
		if( rows > 1 ) elmTextarea.rows = --rows;
	};
	
	/* grobal method */
	
	this.MIN_WIDTH   = 320;
	this.MIN_HEIGHT  = 320;
	this.onInit = function(){
		self.rootElement.id        = 'speach-editor-wrapper';
		self.rootElement.innerHTML = '<textarea id="speach-editor"></textarea><div id="speach-edit-complete-button" class="button">OK</div>';
	};
	this.onOpen = function( _w, _h, _panelX, _panelY, _textElement, _onUpdate ){
		elmTextarea = document.getElementById( 'speach-editor' );
		elmButton   = document.getElementById( 'speach-edit-complete-button' );
		
		self.addKeyEventListener( 'keydown', new Function( 'return false' ), 69, false, true );
		self.addEventListener( elmButton, 'click', clickOK );
		
		panelX = _panelX;
		panelY = _panelY;
		textElement = _textElement;
		onUpdate = _onUpdate || null;
		
		self.onPaneResize( _w, _h );
		elmTextarea.value = _textElement.content;
		elmTextarea.focus();
		
		/*
		 * ie6,7は、textarea { width:100%}でも高さが変わらない。rowsを設定。
		 */
		UA.isIE === true && UA.ieVersion <= 7 && self.addAsyncCall( textareaFitHeight );
	};
	this.onPaneResize = function( _w, _h ){
		self.rootElement.style.cssText = [
			'left:', textElement.x + panelX, 'px;',
			'top:',  textElement.y + panelY, 'px;',
			'width:', textElement.w, 'px;',
			'height:', textElement.h, 'px;'
		].join( '' );
	};
	this.onClose = function(){
		self.removeKeyEventListener();
		self.removeEventListener( elmButton );
		
		elmTextarea = elmButton = onUpdate = textElement = self = null;
	};
}, true, false, 'Tetxt Editor', 'texteditor', null, '#DDDDDD' );


var Reader = gOS.registerApplication( function(){
	var windowW, windowH,
		headerH,
		consoleH,
		panelMargin,
		elmContainer, elmTitle, elmAuthor, elmBackButton, elmNextButton,
		bindWorker    = null,
		currentFile   = null,
		comicData     = null,
		currentPanel  = null,
		currentIndex  = 0,
		numPanel      = 0,
		self          = this;

	function onBackClick(){
		currentIndex -= ( currentIndex > 0 ? 1 : 0 );
		slide();
		return false;
	}
	function onNextClick(){
		currentIndex += ( currentIndex < numPanel - 1 ? 1 : 0 );
		slide();
		return false;
	}
	function slide(){
		var elm    = elmContainer.childNodes[ currentIndex ],
			h      = windowH - headerH - consoleH,
			top    = headerH;
		if( elm ){
			top =  headerH - elm.offsetTop + Math.floor( ( h - elm.offsetHeight ) / 2 );
		}
		
		$( elmContainer ).stop().animate( {
			top:	top
		});
	}
	function getCurrentTopPosition(){

	}
	function draw(){
		var fileData, title, author, story;
		
		if( Driver.isPettanrFileInstance( currentFile ) === true ){
			switch( currentFile.getType() ){
				case FILE_TYPE.COMIC :
					fileData    = currentFile.read();
					title       = fileData.title;
					author      = fileData.author.name;
					comicData   = fileData;
					numPanel    = currentFile.getChildFileLength();
					break;
				case FILE_TYPE.STORY :
					story       = currentFile.read();
					fileData    = story.panel;
					title       = story.comic.title;
					author      = fileData.author.name;
					comicData   = fileData;
					numPanel    = 1;
					break;
				case FILE_TYPE.PANEL :
					fileData    = currentFile.read();
					title       = 'No comic';
					author      = fileData.author.name;
					comicData   = fileData;
					numPanel    = 1;
					break;			
			};
		} else {
			
		};
		if( comicData !== null ){
			elmTitle.data  = title;
			elmAuthor.data = author;
			// bindWorker.json( comicData );
			bindWorker.file( currentFile );
			self.addAsyncCall( asyncResize );
		};
	}
	function asyncResize(){
		self.onPaneResize( windowW, windowH );
	};
	
	/* grobal method */
	
	this.MIN_WIDTH   = 320;
	this.MIN_HEIGHT  = 320;
	this.onInit = function(){
		self.rootElement.id = 'comic-reader-wrapper';
		self.rootElement.innerHTML = [
			'<div id="comic-reader-panel-container"></div>',
			'<div class="comic-reader-shadow" style="top:0;height:40px;"></div>',
			'<div id="comic-reader-header">',
				'<div id="comic-reader-header-content">',
					'<span id="comic-reader-title">NO DATA...</span>',
					'<span id="comic-reader-author">NO DATA...</span>',
				'</div>',
			'</div>',
			'<div class="comic-reader-shadow" style="bottom:0;height:100px;"></div>',
			'<div id="comic-reader-console">',
				'<div id="comic-reader-button-centering">',
					'<a href="#" id="comic-reader-back-button">▲</da>',
					'<a href="#" id="comic-reader-forward-button">▼</a>',
				'</div>',
			'</div>'
		].join( '' );
		
		self.fetchCSS( pettanr.CONST.URL_PETA_APPS_CSS );
		
	};
	this.onOpen = function( _w, _h, file ){
		headerH       = Util.getElementSize( document.getElementById( 'comic-reader-header' ) ).height;
		consoleH      = Util.getElementSize( document.getElementById( 'comic-reader-console' ) ).height;
		elmContainer  = document.getElementById( 'comic-reader-panel-container' );
		elmTitle      = document.getElementById( 'comic-reader-title' ).firstChild;
		elmAuthor     = document.getElementById( 'comic-reader-author' ).firstChild;
		elmBackButton = document.getElementById( 'comic-reader-back-button' );
		elmNextButton = document.getElementById( 'comic-reader-forward-button' );

		bindWorker = pettanr.bind.createBindWorker( elmContainer, null, false, false );
		
		self.addEventListener( elmBackButton, 'click', onBackClick );
		self.addEventListener( elmNextButton, 'click', onNextClick );
		
		numPanel = currentIndex = 0;
		
		elmContainer.style.cssText = 'left:' + ( _w / 2 )  + 'px;' + 'top:' + _h + 'px;';
		
		windowW = _w;
		windowH = _h;
		if( FileAPI.isFileInstance( file ) === true ){
			currentFile = file;
			file.addEventListener( Const.FILE.EVENT.GET_SEQENTIAL_FILES, draw );
			file.getSeqentialFiles();
			draw();
		};
	};
	this.onPaneResize = function( _windowW, _windowH ){
		windowW = _windowW;
		windowH = _windowH;
		var panelH = elmContainer.offsetHeight,
			panelW = elmContainer.offsetWidth,
			h      = _windowH - headerH - consoleH;
		$( elmContainer ).stop().animate(
			{
				left:	Math.floor( ( _windowW - panelW ) / 2 ),
				top:	headerH + ( panelH < h ? Math.floor( ( h - panelH ) / 2 ) : 0 )
			}
		);
	};
	this.onClose = function(){
		self.removeEventListener( elmBackButton );
		self.removeEventListener( elmNextButton );
		
		bindWorker.destroy();
		bindWorker = null;
		
		currentFile && currentFile.removeEventListener( Const.FILE.EVENT.GET_SEQENTIAL_FILES, draw );
		currentFile = comicData = currentPanel = null;
		
		elmContainer = elmTitle = elmAuthor = elmBackButton = elmNextButton = null;
	};
}, true, true, 'Comic Reader', 'comicreader', null, '#01A31C' );


var Editor = gOS.registerApplication( function(){

	var PANEL_ELEMENT_TYPE_IMAGE = 0,
		PANEL_ELEMENT_TYPE_TEXT  = 1,
		MODULE_ARRAY             = [],
		PANEL_ELEMENT_ARRAY      = [],
		MIN_PANEL_HEIGHT         = 20,
		MIN_ELEMENT_SIZE         = 19,
		MOUSE_HIT_AREA           = 10,
		windowW, windowH,
		app                      = this,
		eventRoot                = null,
		option,
		comicID                  = -1,
		panelID                  = -1,
		panelTimming             = -1,
		phase                    = -1;

	var kill = function(){
		var o = this, v;
		for( var p in o ){
			if( o.hasOwnProperty && !o.hasOwnProperty( p ) ) continue;
			v = o[ p ];
			delete o[ p ];
		};
	};
/* ----------------------------------------
 * MENU BAR
 *  - mouseEventListener
 *  - controler
 * 
 * div
 *   div.title
 *   ul
 *     li
 *        a
 *          span
 *          kbd shortcut
 */
	var MENU_BAR_CONTROL = ( function(){
		var ELM_ITEM_CLASSNAME = 'menubar-item',
			currentMenu        = null,
			elmBar, elmBox,
			nodeBar, nodeBox, layerBox,
			barH, menuW;
	/** -----------------------------------------
	 *  PrivateOptionDataClass
	 */
		var PrivateOptionDataClass = function( menuData, option, title, shortcut, callback, visible, separateAfter, thisObject ){
			this.menuData      = menuData;
			this.option        = option;
			this.title         = title;
			this.shortcut      = shortcut || '';
			this.callback      = callback;
			this.thisObject    = thisObject;
			this.visible       = visible;
			this.separateAfter = separateAfter;
		};
		PrivateOptionDataClass.prototype = {
			menuData      : null,
			option        : null,
			elm           : null,
			node          : null,
			visible       : undefined,
			border        : false,
			title         : null,
			shortcut      : null,
			callback      : null,
			thisObject    : null,
			separateAfter : false,
			show : function( elm ){
				if( this.elm === elm ) return;
				if( !elm ){
					elm = document.createElement( 'div' );
					elmBox.appendChild( elm );
					elm.appendChild( document.createElement( 'span' ) );
					elm.appendChild( document.createElement( 'kbd' ) );
				};
				this.elm      = elm;
				this.option.title( this.title );
				this.option.visible( !!this.visible );
				elm.lastChild.innerHTML = this.shortcut;
				elm.style.borderStyle = this.border === true ? 'solid' : 'none';
				
				this.node     = nodeBox.createNode( elm, false, true, 'menubar-option-hover', 'pointer' );
				this.node.disabled( !this.visible );
			},
			hide : function(){
				this.node.remove();
				delete this.elm;
				delete this.node;
			},
			fire : function(){
				this.callback.call( this.thisObject || this.option, Util.getIndex( this.menuData.optionDataList, this ) );
			},
			remove : function(){
				var list = PrivateOptionDataClass.list;
				list.splice( Util.getIndex( list, this ), 1 );
			}
		};
		PrivateOptionDataClass.list = [];
		PrivateOptionDataClass.get = function( OptionOrElm ){
			var list = PrivateOptionDataClass.list,
				i    = list.length,
				data;
			for( ; i; ){
				data = list[ --i ];
				if( data.option === OptionOrElm || data.elm === OptionOrElm ) return data;
			};
			return null;
		};

	/** -----------------------------------------
	 *  OptionClass
	 */	
		var OptionClass = function( menuData, title, shortcut, callback, visible, separateAfter, thisObject ){
			PrivateOptionDataClass.list.push( new PrivateOptionDataClass( menuData, this, title, shortcut, callback, visible, separateAfter, thisObject ) );
		};
		OptionClass.prototype = {
			title: function( v ){
				var data = PrivateOptionDataClass.get( this );
				if( Type.isString( v ) === true ){
					data.title = v;
					if( data.elm ) data.elm.firstChild.innerHTML = v;
				};
				return data.title;
			},
			visible : function( v ){
				var data = PrivateOptionDataClass.get( this );
				if( Type.isBoolean( v ) === true ){
					data.visible = v;
					data.elm && Util.toggleClass( data.elm, 'menubar-option-disabled', !v );
				};
				return data.visible;
			}
		};
	/** -----------------------------------------
	 *  AsyncOptionClass
	 */
		var AsyncOptionClass = function( menuData, callback, visible, separateAfter, thisObject ){
			var data  = new PrivateOptionDataClass( menuData, this, null, null, callback, visible, separateAfter, thisObject );
			data.show = AsyncOptionClass.show;
			data.hide = AsyncOptionClass.hide;			
			PrivateOptionDataClass.list.push( data );
		};
		AsyncOptionClass.prototype = {
			title    : function(){},
			visible  : OptionClass.prototype.visible
		};
		AsyncOptionClass.show = function( elm ){
			if( this.elm === elm ) return;
			if( !elm ){
				elm = document.createElement( 'div' );
				elmBox.appendChild( elm );
				elm.appendChild( document.createElement( 'span' ) );
				elm.appendChild( document.createElement( 'kbd' ) );		
			};
			this.elm         = elm;
			elm.className    = 'loading';
			elm.style.height = '90px';
			elm.firstChild.innerHTML = this.elm.lastChild.innerHTML = '';
			elm.style.borderStyle    = this.border === true ? 'solid' : 'none';
			
			this.callback();			
		};
		AsyncOptionClass.hide = function(){
			this.elm.className    = '';
			this.elm.style.height = '';
			delete this.elm;
		};
		
	/** -----------------------------------------
	 *  MenuPrivateDataClass
	 */
		var MenuPrivateDataClass = function( menu, title ){
			this.menu           = menu;
			this.elm            = document.createElement( 'div' );
			this.optionDataList = [];
			
			elmBar.appendChild( this.elm );
			this.elm.className  = ELM_ITEM_CLASSNAME;
			this.elm.innerHTML  = title;
		};
		MenuPrivateDataClass.prototype = {
			menu           : null,
			elm            : null,
			node           : null,
			visible        : false,
			currentOption  : -1,
			index          : -1,
			optionDataList : null,
			open : function(){
				this.elm.style.left = ( menuW * Util.getIndex( MenuPrivateDataClass.list, this ) ) + 'px';
				this.node           = nodeBar.createNode( this.elm, false, false, ELM_ITEM_CLASSNAME + '-hover', 'pointer' );
				// this.node.addEventListener( 'click', this.onClick, this );
				this.node.addEventListener( 'click', this.onClick, this );			
			},			
			close : function(){
				var o;
				while( o = this.optionDataList.shift() ) o.remove();
				this._kill = kill;
				this._kill();
			},
			onClick : function( e ){
				if( currentMenu !== this.menu ){
					currentMenu && currentMenu.hide();
					currentMenu = this.menu;
					this.menu.show();
				};
			},
			onOptionClick : function( e ){
				var target = e.target,
					i      = target.nodeIndex(),
					option = this.optionDataList[ i ];
				if( target === nodeBox ) return true;
				option.fire();
			}
		};
		MenuPrivateDataClass.list = [];
		MenuPrivateDataClass.get  = function( menu ){
			var list = MenuPrivateDataClass.list,
				i    = list.length;
			for( ; i; ){
				if( list[ --i ].menu === menu ) return list[ i ];
			};
			return null;
		};
	
	/** -----------------------------------------
	 *  MenuClass
	 */
		var MenuClass = function( title ){
			MenuPrivateDataClass.list.push( new MenuPrivateDataClass( this, title ) );
		};
		MenuClass.prototype = {
			show: function(){
				var data = MenuPrivateDataClass.get( this );
				if( data.visible === true ) return;
				
				data.elm.className = ELM_ITEM_CLASSNAME + '-focus';
				
				if( !elmBox ){
					elmBox   = document.createElement( 'div' );
					elmBar.appendChild( elmBox );
					elmBox.className = 'menubar-option-box';
					nodeBox  = nodeBar.createNode( elmBox, false, false, 'menubar-option-box-hover' );
				};
				nodeBox.disabled( false );
				elmBar.parentNode.insertBefore( elmBox, elmBar.nextSibling ); // ie6 では　elmBar の 子にすると 選択肢が表示されない
				
				nodeBox.setPosition( data.node.x(), barH );
				
				var i,
					children = elmBox.childNodes,
					list     = data.optionDataList
					l        = list.length;
				while( l < children.length ){
					elmBox.removeChild( elmBox.firstChild );
				};
				for( i = 0; i < l; ++i ){
					list[ i ].show( children[ i ] );
				};
				nodeBox.mesure();

				nodeBar.addEventListener( 'mouseout', this.hide, this );
				nodeBox.addEventListener( 'click', data.onOptionClick, data );
				data.visible = true;								
			},
			hide: function(){
				data = MenuPrivateDataClass.get( this );
				if( data.visible === false ) return;
				
				data.elm.className = ELM_ITEM_CLASSNAME;
				for( var i = data.optionDataList.length; i; ){
					data.optionDataList[ --i ].hide();
				};
				elmBar.parentNode.removeChild( elmBox );
				nodeBox.disabled( true );

				nodeBar.removeEventListener( 'mouseout', this.hide );
				nodeBox.removeEventListener( 'click', data.onOptionClick );
				data.visible = false;
				currentMenu  = null;
			},
			createOption: function( title, shortcut, callback, visible, separateBefore, separateAfter, thisObject ){
				var data       = MenuPrivateDataClass.get( this ),
					before     = data.optionDataList[ data.optionDataList.length - 1 ],
					ret        = new OptionClass( data, title, shortcut, callback, visible, separateAfter, thisObject ),
					dataOption = PrivateOptionDataClass.get( ret );
				if( before ) before.border = separateBefore === true || before.separateAfter === true;
				data.optionDataList.push( dataOption );
				if( data.visible === true ){
					dataOption.show();
					nodeBox.mesure();
				};
				return ret;
			},
			createAsyncOption: function( onOpen, visible, separateBefore, separateAfter, thisObject ){
				var data       = MenuPrivateDataClass.get( this ),
					before     = data.optionDataList[ data.optionDataList.length -1 ],
					ret        = new AsyncOptionClass( data, onOpen, visible, separateAfter, thisObject ),
					dataOption = PrivateOptionDataClass.get( ret );
				if( before ) before.border = separateBefore === true || before.separateAfter === true;
				data.optionDataList.push( dataOption );
				data.visible === true && dataOption.show();
				return ret;
			},
			remove : function( option ){
				var data       = MenuPrivateDataClass.get( this ),
					optionData = PrivateOptionDataClass.get( option ),
					i          = Util.getIndex( data.optionDataList, optionData );
				if( i === -1 ) return;
				
				data.optionDataList.splice( i, 1 );
				
				data.visible === true && elmBox.removeChild( optionData.elm ) && optionData.hide();
				optionData.remove();
				
				!( option instanceof AsyncOptionClass ) && data.optionDataList.length === 0 && this.hide();
			}
		};
		
		return {
			id : 'MENU_BAR_CONTROL',
			h  : 0,
			init : function(){
				elmBar   = document.getElementById( 'menu-bar' );
				nodeBar  = eventRoot.createNode( elmBar, false, false, 'menu-bar-hover' );
				
				MENU_BAR_CONTROL.QUIT   = MENU_BAR_CONTROL.createItem( 'Quit' );
				MENU_BAR_CONTROL.EDIT   = MENU_BAR_CONTROL.createItem( 'Edit' );
				MENU_BAR_CONTROL.WINDOW = MENU_BAR_CONTROL.createItem( 'Window' );
				MENU_BAR_CONTROL.HELP   = MENU_BAR_CONTROL.createItem( 'Help' );
				
				var size = Util.getElementSize( MenuPrivateDataClass.list[ 0 ].elm );
				menuW    = size.width;
				barH     = MENU_BAR_CONTROL.h = size.height;
				
				elmBar.style.top = ( - barH ) + 'px';
				$( elmBar ).animate( { top: 0 } );				
				
				delete MENU_BAR_CONTROL.init;
			},
			open : function(){
				for( var i = MenuPrivateDataClass.list.length; i; ) MenuPrivateDataClass.list[ --i ].open();
				delete MENU_BAR_CONTROL.open;
			},
			close : function(){
				var data;
				while( data = MenuPrivateDataClass.list.shift() ) data.close();
				nodeBox.remove();
				MenuPrivateDataClass.list = elmBar = layerBox = elmBox = null;
				MENU_BAR_CONTROL.kill = kill;
				MENU_BAR_CONTROL.kill();
			},
			createItem : function( title ){
				return new MenuClass( title );
			},
			busy : function( _busy ){
				return false;
			},
			onWindowResize: function( _windowW, _windowH ){
				
			},
			QUIT:   null,
			EDIT:   null,
			WINDOW: null,
			HELP:   null
		};
	})();


/* ----------------------------------------
 * HISTORY_CONTROL
 *  - controler
 */
	var HISTORY_CONTROL = ( function() {
		var	stackBack    = [],
			stackForward = [],
			menubarBack,
			menubarForward;

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

			var s = stackBack.pop();
			s.callback.apply( s.thisObj || {}, s.argBack );
			menubarBack.visible( stackBack.length !== 0 );
			SAVE_CONTROL.panelUpdated( stackBack.length !== 0 );
			
			stackForward.push( s );
			menubarForward.visible( true );
		};
		function forward(){
			if( stackForward.length === 0 ) return;
			
			var s = stackForward.pop();
			s.callback.apply( s.thisObj || {}, s.argForword );
			menubarForward.visible( stackForward.length !== 0 );
			
			stackBack.push( s );
			menubarBack.visible( true );
			SAVE_CONTROL.panelUpdated( true );
		};
		var RecordClass = function( callback, argBack, argForword, destroy, opt_thisObject ){
			this.callback   = callback;
			this.argBack    = argBack;
			this.argForword = argForword;
			this.destroy    = !!destroy;
			this.thisObj    = opt_thisObject;
		};
		RecordClass.prototype.kill = function( _callDestroy ){
			var	_argBack    = this.argBack,
				_argForword = this.argForword,
				v;
			this._kill = kill;
			this._kill();
			
			if( _callDestroy !== true ) return;
			
			if( Type.isArray( _argBack ) === true ){ // isArray
				while( v = _argBack.shift() ){
					_callDestroy === true && Type.isFunction( v.destroy ) === true && v.destroy();
				};
			};
			if( Type.isArray( _argForword ) === true ){
				while( v = _argForword.shift() ){
					_callDestroy === true && Type.isFunction( v.destroy ) === true && v.destroy();
				};
			};
		};
		return {
			init: function(){
				app.addKeyEventListener( 'keydown', back,    90, false, true );	// ctrl + Z
				app.addKeyEventListener( 'keydown', forward, 90, true,  true );	// ctrl + shift + Z
				app.addKeyEventListener( 'keydown', forward, 89, false, true ); // ctrl + Y
				
				delete HISTORY_CONTROL.init;
			},
			open: function(){
				menubarBack    = MENU_BAR_CONTROL.EDIT.createOption( 'back',    'ctrl + z', back, false );
				menubarForward = MENU_BAR_CONTROL.EDIT.createOption( 'forward', 'ctrl + y', forward, false, false, true );				
				
				delete HISTORY_CONTROL.open;
			},
			close: function(){
				var s;
		       	while( s = stackBack.shift() )    s.kill( true );
		       	while( s = stackForward.shift() ) s.kill( true );
		       	menubarBack = menubarForward = stackBack = stackForward = null;
			},
		    saveState: function( _function, _argBack, _argForword, _onRecordDestroy, opt_thisObject ){
		        stackBack.push( new RecordClass( _function, _argBack, _argForword, _onRecordDestroy, opt_thisObject ));
		        menubarBack.visible( true );
				SAVE_CONTROL.panelUpdated( true );
				
				var s;
		       	while( s = stackForward.shift() ) s.kill( s.destroy );
				menubarForward.visible( false );
		    }		
		};
	})();

/* ----------------------------------------
 * SAVE_CONTROL
 *  - controler
 */
	var SAVE_CONTROL = ( function(){
		var save, saveQuit, eXport, quit,
			updated  = false;
		
		function quit(){
			Editor.shutdown();
		};
		function onSave(){
			PanelConsole.boot( Model.createPanel( {
				comicID           : comicID,
				panelID           : panelID,
				panelTimming      : panelTimming,
				panelW            : PANEL_CONTROL.w,
				panelH            : PANEL_CONTROL.h,
				borderSize        : 2,
				panelElementArray : PANEL_ELEMENT_ARRAY,
				publish           : true
			} ) );
		};
		function onSaveQuit(){
			// Editor.shutdown();
			onSave();
		};
		function onExport(){
			OutputConsole.boot(
				comicID, panelID, panelTimming,
				PANEL_CONTROL.w, PANEL_CONTROL.h,
				2, // border, BackgroundImage
				PANEL_ELEMENT_ARRAY
			);
		};
		return {
			init: function(){
				delete SAVE_CONTROL.init;
			},
			open: function(){
				save     = MENU_BAR_CONTROL.QUIT.createOption( 'save', 'ctrl + S', onSave, false );
				saveQuit = MENU_BAR_CONTROL.QUIT.createOption( 'save & quit', null, onSaveQuit, false, false, true );
				eXport   = MENU_BAR_CONTROL.QUIT.createOption( 'export', null, onExport, true, false, true );
				quit     = MENU_BAR_CONTROL.QUIT.createOption( 'quit', null, quit, true, true );				
				
				delete SAVE_CONTROL.open;
			},
			close: function(){
				save = saveQuit = eXport = quit = null;
				SAVE_CONTROL.kill = kill;
				SAVE_CONTROL.kill();
			},
			quit: quit,
			panelUpdated: function( _updated ){
				if( Type.isBoolean( _updated ) === true ){
					save.visible( _updated );
					saveQuit.visible( _updated );
					updated = _updated;
				}
				return updated;
			},
			save: function(){
				
			}
		};
	})();

/* ----------------------------------------
 * WINDOWS_CONTROL
 *  - contloler
 *  - mouseEventListener
 */	
	var WINDOWS_CONTROL = ( function(){
	/*
	 *  表示上手前にあるwindowは、WINDOW_DATA_LISTの先頭にあり、htmlでは後ろにある。
	 */
		var DEFAULT_MIN_WINDOW_WIDTH  = 200,
			DEFAULT_MIN_WINDOW_HEIGHT = 200,
			WINDOW_DATA_LIST          = [],
			WINDOW_BODY_BODER_SIZE    = 1,
			currentWindowData,
			elmRoot,
			nodeContainer,
			elmWindowOrigin,
			closeButtonWidth;
	/**
	 * WindowPrivateData
	 */
		var WindowPrivateData = function(){};
		WindowPrivateData.prototype = {
			window        : null,
			menubarOption : null,
			elm           : null,
			elmHead       : null,
			elmBody       : null,
			nodeWindow    : null,
			nodeHead      : null,
			nodeBody      : null,
			nodeFoot      : null,
			nodeResize    : null,
			visible       : false,
			isDragging    : false,
			isResizing    : false,
			title         : null,
			x             : 0,
			y             : 0,
			w             : 0,
			h             : 0,
			minWindowW    : 200,
			minWindowH    : 200,
			startX        : 0,
			startY        : 0,
			startW        : 0,
			startH        : 0,
			offsetX       : 0,
			offsetY       : 0,
			headerH       : 0,
			bodyH         : 0,
			footerH       : 0,
			init : function( win, bodyTempleteID, title, x, y, w, h, visible, closeEnabled, resizeEnabled, minWindowW, minWindowH ){
				this.window         = win;
				this.bodyTempleteID = bodyTempleteID;
				this.title          = title;
				this.x              = x;
				this.y              = y;
				this.w              = w;
				this.h              = h;
				this.visible        = visible;
				this.closeEnabled   = closeEnabled;
				this.resizeEnabled  = resizeEnabled;
				this.minWindowW     = minWindowW;
				this.minWindowH     = minWindowH;
				
				WINDOW_DATA_LIST.push( this );
			},
			create : function(){
				var win = this.window;
				this.elm = win.elm = elmWindowOrigin.cloneNode( true );
				this.menubarOption = MENU_BAR_CONTROL.WINDOW.createOption( 
					( this.visible !== true ? 'show ' : 'hide ' ) + this.title,
					null, this.onMenubarClick,
					true, false, false,
					this
				);
				if( win.onInit ){
					win.onInit();
					delete win.onInit;
				};
			},
			onMenubarClick : function(){
				this.window[ this.visible === true ? 'close' : 'open' ]();
			},
			update : function( x, y, w, h ){
				var win = this.window, bodyH;
					
				x = x !== undefined ? x : this.x;
				y = y !== undefined ? y : this.y;
				y = y > MENU_BAR_CONTROL.h ? y : MENU_BAR_CONTROL.h;
				w = w !== undefined ? w : this.w;
				h = h !== undefined ? h : this.h;
				
				this.nodeWindow.update( x, y, w, h );
				this.nodeHead && this.nodeHead.update( 0, 0, w, this.headerH );
				console.log( '************ hewader' + this.headerH )
				this.nodeBody.update( 0, this.headerH, w, this.bodyH = h - this.headerH - this.footerH );
				( this.w !== w || this.h !== h ) && win.onResize && win.onResize( w, this.bodyH );

				this.x = x;
				this.y = y;
				this.w = w;
				this.h = h;
			},
			firstOpen : function(){
				var win       = this.window,
					elmHead   = this.elmHead = Util.getElementsByClassName( this.elm, 'window-header' )[ 0 ],
					elmBody   = this.elmBody = Util.getElementsByClassName( this.elm, 'window-body' )[ 0 ],
					elmClose  = Util.getElementsByClassName( this.elm, 'window-close-button' )[ 0 ],
					elmFoot   = Util.getElementsByClassName( this.elm, 'window-footer' )[ 0 ],
					elmResize = Util.getElementsByClassName( this.elm, 'window-resize-button' )[ 0 ],
					replaceID = this.bodyTempleteID;
				
				this.nodeWindow = nodeContainer.createNode( this.elm, false, true, 'window-wrapper-hover' );
				this.nodeWindow.addEventListener( 'mousemove', this.mousemove, this );
				this.nodeWindow.addEventListener( 'mousedown', this.mousedown, this );
				this.nodeWindow.addEventListener( 'mouseup',   this.mouseup,   this );
				this.nodeWindow.addEventListener( 'mouseout',  this.mouseup,   this );
				
				// this.nodeHead   = this.nodeWindow.createNode( elmHead );
				win.title( this.title );

				this.nodeBody   = this.nodeWindow.createNode( elmBody, false, true, null, '', true );
				replaceID && elmBody.appendChild( document.getElementById( replaceID ) );
				
				if( this.closeEnabled === true ){
					// this.nodeClose = this.nodeHead.createNode( elmClose );
					// this.nodeClose.addEventListener( 'mousedown', win.close, data );
				} else {
					elmClose.parentNode.removeChild( elmClose );
				};
				
				if( this.resizeEnabled === true ){
					// this.nodeFoot = this.nodeWindow.createNode( elmFoot );
					this.footerH  = Util.getElementSize( elmFoot ).height; // this.nodeFoot.height();
					
					// this.nodeResize = this.nodeFoot.createNode( elmResize );
					// this.nodeResize.addEventListener( 'mousedrag', this.resizeDrag, data );
				} else {
					elmFoot.parentNode.removeChild( elmFoot );
				};
				
				this.update( this.x, this.y, this.w, this.h );
				if( win.onFirstOpen ){
					win.onFirstOpen( this.w, this.bodyH, this.nodeBody );
					delete win.onFirtOpen;
				};
				
				this.firstOpen = null;
			},
			onFadeIn : function(){
				var data = WindowPrivateData.get( this ),
					win  = data.window;
				data.firstOpen && data.firstOpen();
				win.onOpen && win.onOpen( data.w, data.bodyH );
				data.nodeWindow.disabled( false );
				data.goFront();
			},
			onFadeOut : function(){
				var data = WindowPrivateData.get( this ),
					win  = data.window;
				elmRoot.removeChild( data.elm );
				win.onClose && app.addAsyncCall( win.onClose, null, win );
			},
			mousedown : function( e ){
				currentWindowData !== this && this.goFront();
				
				var x   = e.layerX,
					y   = e.layerY;
				if( this.resizeEnabled === true && this.w - 20 <= x && x < this.w && this.headerH + this.bodyH < y && y <= this.h ){
					this.isResizing = true;
					//this.startX     = this.x;
					//this.startY     = this.y;
					this.startW     = this.w;
					this.startH     = this.h;
					this.offsetX    = x;
					this.offsetY    = y;
					// app.updateCoursor( 'nw-resize' );
					this.nodeWindow.cursor( 'nw-resize' );
					return true;
				};
				
				// if( x < 0 || y < 0 || this.w < x || this.headerH < y ) return false;
				if( this.closeEnabled === true && this.w - closeButtonWidth < x && y < this.headerH ){
					this.window.close();
					return;
				};
				
				if( y < this.headerH ){
					this.isDragging = true;
					this.startX     = this.x;
					this.startY     = this.y;
					this.startW     = this.w;
					this.startH     = this.h;
					this.offsetX    = x;
					this.offsetY    = y;
					
					// app.updateCoursor( 'move' );
					this.nodeWindow.cursor( 'move' );
					return true;					
				}
			},
			mouseup : function( e ){
				if( this.isResizing === true || this.isDragging === true ){
					this.isDragging = this.isResizing = false;
					this.update();
				};
				this.nodeWindow.cursor( '' );
			},
			mousemove : function( e ){
				currentWindowData !== this && this.goFront();
				
				var x   = e.layerX,
					y   = e.layerY,
					w, h;
				if( this.isResizing === true ){
					w = this.startW + x - this.offsetX;
					h = this.startH + y - this.offsetY;
					this.w = w = w < this.minWindowW ? this.minWindowW : w;
					this.h = h = h < this.minWindowH ? this.minWindowH : h;
					this.elm.style.width  = w + 'px';
					this.elm.style.height = h + 'px';
					return true;				
				} else
				if( this.isDragging === true ){
					this.x = x = this.startX + x - this.offsetX;
					this.y = y = this.startY + y - this.offsetY;
					this.elm.style.left = x + 'px';
					this.elm.style.top  = y + 'px';
					return true;
				};
				// if( e.hit === false || ( this.headerH < layerY && layerY < this.headerH + this.bodyH ) ) return false;
				this.nodeWindow.cursor( ( /*0 < layerX && layerX < this.w && 0 <= layerY &&*/ y <= this.headerH ) ? 'pointer' : '' );
			},
			goFront : function(){
				currentWindowData = this;
				var i = nodeContainer.numNode() - 1;
				// console.log( this.nodeWindow.nodeIndex() + ' , ' + this.nodeWindow.numNode() )
				if( this.nodeWindow.nodeIndex() !== i ){
					this.nodeWindow.nodeIndex( i );
					elmRoot.appendChild( this.elm );
				};
			},
			busy : function(){
				return this.isDragging === true || this.isResizing === true;
			},
			destroy : function(){
				
			}
		};
		WindowPrivateData.get = function( windowOrElement ){
			if( windowOrElement instanceof WindowPrivateData ) return windowOrElement;
			var list = WINDOW_DATA_LIST,
				i    = list.length,
				data;
			for( ; i; ){
				data = list[ --i ];
				if( data.window === windowOrElement || data.elm === windowOrElement ) return data;
			};
			return null;
		};
		
	/**
	 * WindowClass
	 */
		var WindowClass = function( bodyTempleteID, title, x, y, w, h, visible, closeEnabled, resizeEnabled, minWindowW, minWindowH ){
			( new WindowPrivateData() ).init( this, bodyTempleteID, title, x, y, w, h, visible, closeEnabled, resizeEnabled, minWindowW, minWindowH );
		};
		WindowClass.prototype = {
			elm        : null,
			open : function(){
				var data = WindowPrivateData.get( this );
				if( data.visible === true ) return;
				
				data.visible = true;
				openWindow( data );
				data.menubarOption.title( 'hide ' + data.title );
				
				// WINDOW_DATA_LIST.splice( Util.getIndex( WINDOW_DATA_LIST, data ), 1 );
				// WINDOW_DATA_LIST.unshift( data );
				currentWindowData  = null;
			},
			close : function(){
				var data = WindowPrivateData.get( this );
				if( data.visible === false ) return;
				
				data.visible = false;
				$( data.elm ).fadeOut( data.onFadeOut );
				data.menubarOption.title( 'show ' + data.title );
				data.nodeWindow.disabled( true );
			},
			title : function( _title ){
				if( Type.isString( _title ) === true ){
					var data = WindowPrivateData.get( this );
					data.elmHead.firstChild.innerHTML = data.title = _title;
					// data.nodeHead.mesure();
					//data.headerH = data.nodeHead.height();
					data.headerH = Util.getElementSize( data.elmHead ).height;
				};
				return data.title;
			},
			createHeaderItem : function(){
				var data = WindowPrivateData.get( this ),
					elm  = document.createElement( 'div' ),
					node;
				if( !data.nodeHead ) data.nodeHead = data.nodeWindow.createNode( data.elmHead, true, false );
				data.elmHead.appendChild( elm );
				elm.className = 'header-item finder-path';
				node = data.nodeHead.createNode( elm, false, true, 'header-item-hover', '' )
				// data.nodeHead.mesure();
				// data.headerH = data.nodeHead.height();
				
				data.headerH = Util.getElementSize( data.elmHead ).height;
				data.update();
				
				return node;
			}
		};
		
		function openWindow( data ){
			if( data.visible !== true ) return;
			elmRoot.appendChild( data.elm );// appendした後に fadeIn() しないと ie で filterが適用されない．
			$( data.elm ).fadeIn( data.onFadeIn );
			return;
		};
		
		return {
			id   : 'WINDOWS_CONTROL',
			init : function(){
				elmRoot          = document.getElementById( 'window-container' );
				nodeContainer    = eventRoot.createNode( elmRoot, true, false );
				elmWindowOrigin  = app.fetchHTMLElement( 'windowTemplete' );
				closeButtonWidth = Util.getElementSize( Util.getElementsByClassName( elmWindowOrigin, 'window-close-button' )[ 0 ] ).width;
				
				delete WINDOWS_CONTROL.init;
			},
			open : function(){
				for( var i = WINDOW_DATA_LIST.length, data; i; ){
					data = WINDOW_DATA_LIST[ --i ];
					data.create();
					openWindow( data );
				};
				delete WINDOWS_CONTROL.open;
			},
			close : function(){
			},
			onWindowResize : function( _windowW, _windowH ){
				/*
				 * 画面外に出るwindowの移動
				 */
			},
			createWindow : function( EXTENDS, bodyTempleteID, title, x, y, w, h, opt_visible, opt_closeEnabled, opt_resizeEnabled, opt_minWindowW, opt_minWindowH ){
				opt_visible       = opt_visible !== false;
				opt_closeEnabled  = opt_closeEnabled === true;
				opt_resizeEnabled = opt_resizeEnabled === 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 win = new WindowClass( bodyTempleteID, title, x, y, w, h, opt_visible, opt_closeEnabled, opt_resizeEnabled, opt_minWindowW, opt_minWindowH ),
					data;
				for( var key in EXTENDS ){
					win[ key ] = EXTENDS[ key ];
				};
				if( Type.isUndefined( WINDOWS_CONTROL.init ) === true ){
					data = WindowPrivateData.get( win );
					data.create();
					openWindow( data );
				};
				return win;
			}
		};
	})();

/* ----------------------------------------
 * TOOL_BOX_WINDOW
 * - window
 */
	var TOOL_BOX_WINDOW = ( function(){
			
		app.addKeyEventListener( 'keydown', addImage,   73, false, true );
		app.addKeyEventListener( 'keydown', addText,    84, false, true );
		app.addKeyEventListener( 'keydown', switchGrid, 71, false, true );

		function addImage(){
			// IMAGE_EXPLORER_WINDOW.open();
			app.addAsyncCall( IMAGE_EXPLORER_WINDOW.open, null, IMAGE_EXPLORER_WINDOW );
			//TOOL_BOX_WINDOW.bodyBackOrForward( true );
		};
		function addText(){
			app.addAsyncCall( PANEL_ELEMENT_CONTROL.createTextElement );
		};
		function switchGrid(){
			app.addAsyncCall( GRID_CONTROL.update, null, GRID_CONTROL );
		};
		function popupHelp(){
			//.bodyBackOrForward( true );
			app.addAsyncCall( HELP_DOCUMENTS_WINDOW.open, null, HELP_DOCUMENTS_WINDOW );
		};
		function editBG( e ){
			//TOOL_BOX_WINDOW.bodyBackOrForward( true );
			app.addAsyncCall( INFOMATION_WINDOW.open, null, INFOMATION_WINDOW );
		};
		
		return WINDOWS_CONTROL.createWindow(
			{
				onInit: function(){
					MENU_BAR_CONTROL.EDIT.createOption( 'Add Image', 'ctrl + I', addImage, true, true, false );
					MENU_BAR_CONTROL.EDIT.createOption( 'Add Text',  'ctrl + T', addText, true, false, true );
					MENU_BAR_CONTROL.EDIT.createOption( 'show Grid', 'ctrl + G', switchGrid, true, true, true );
				},
				onFirstOpen: function( x, y, nodeBody ){
					nodeBody.createNode( document.getElementById( 'toolbox-add-image-button'  ), false, true, 'button-hover', 'pointer' ).addEventListener( 'click', addImage );
					nodeBody.createNode( document.getElementById( 'toolbox-add-text-button'   ), false, true, 'button-hover', 'pointer' ).addEventListener( 'click', addText );
					nodeBody.createNode( document.getElementById( 'toolbox-edit-bg-button'    ), false, true, 'button-hover', 'pointer' ).addEventListener( 'click', editBG );
					nodeBody.createNode( document.getElementById( 'toolbox-switch-grid'       ), false, true, 'button-hover', 'pointer' ).addEventListener( 'click', switchGrid );
					nodeBody.createNode( document.getElementById( 'toolbox-popup-help-button' ), false, true, 'button-hover', 'pointer' ).addEventListener( 'click', popupHelp );
					// nodeBody.createNode( document.getElementById( 'toolbox-add-text-button'   ), false, true, 'button-hover', 'pointer' ).addEventListener( 'click', addText );
				}
			},
			'toolbox-window', 'Tool box', 0, 215, 110, 290, true
		);
	})();
	
	
/* ----------------------------------------
 * IMAGE_EXPROLER
 *  - window
 */
	var IMAGE_EXPLORER_WINDOW = ( function(){
		var tree, finder;
		
		function onFileSelect( file ){
			// 他の image ファイルも許可する？
			if( Driver.isPettanrFileInstance( file ) === true ){
				if( file.getType() === FILE_TYPE.RESOURCE_PICTURE ){
					PANEL_ELEMENT_CONTROL.onImageSelect( FileAPI.getFileData( file ) );
				};
			};
		};
		
		return WINDOWS_CONTROL.createWindow(
			{
				onInit: function(){},
				onFirstOpen: function( _w, _h, nodeBody ){
					tree = FileAPI.createTree( FILE_DATA_RESOURCE_PICTURES_ROOT );
					var	_root  = tree.getRootFile(),
						_myPic = _root.getChildFileAt( 0 ),
						_pic   = _root.getChildFileAt( 1 );
					_myPic.getSeqentialFiles();
					_pic.getSeqentialFiles();
					_myPic.destroy();
					_pic.destroy();	
			
					finder = app.createFinder(
						nodeBody,
						tree,
						onFileSelect
					);
					finder.createPath( IMAGE_EXPLORER_WINDOW.createHeaderItem() );
				},
				onOpen: function( _w, _h ){
					finder.resize( _w, _h );
				},
				onResize: function( _w, _h ){
					finder.resize( _w, _h );
				}
			},
			null, 'Album', 0, 215, 600, 350, false, true, true, 300, 300
		);
	})();
	
	
/* ----------------------------------------
 * INFOMATION_WINDOW
 *  - window
 */			
	var INFOMATION_WINDOW = ( function(){
		var backgroundInfomationElm;

		return WINDOWS_CONTROL.createWindow(
			{
				onFirstOpen: function( _w, _h ){
					backgroundInfomationElm = $( '#panel-background-information');
				},
				onResize: function(  _w, _h ){
				},
				update: function( currentElement ){

				}
			},
			'infomation-window', 'Infomation', 0, 30, 200, 180, true
		);
	})();

/* ----------------------------------------
 * HELP_WINDOW
 *  - window
 */
	var HELP_DOCUMENTS_WINDOW = ( function(){
		var visible          = true,
			jqAjaxContents,
			jqNaviItems,
			jqPages,
			currentPageIndex = 0,
			numPage          = 0,
			asyncOption      = null;

		function onAjaxStart( _pageIndex ){
			delete asyncOption.callback;
			
			currentPageIndex = _pageIndex || currentPageIndex;
			if( onHelpLoad !== null ){
				$.ajax({
					url:		'help/jp.xml',
					dataType:	'xml',
					success:	onHelpLoad
				});
				onHelpLoad = null;
			}
			onAjaxStart = null;
		};
		var onHelpLoad = function( _xml ){
			var jqXML          = $( _xml ),
				helpTitle      = jqXML.find( 'pages' ).eq( 0 ).attr( 'title' ),
				elmRoot        = document.createElement( 'div' ),
				elmNavi        = document.createElement( 'div' ),
				elmItemOrigin  = document.createElement( 'a' ),
				elmPages       = document.createElement( 'div' ),
				elmPageOrigin  = document.createElement( 'div' ),
				elmTitleOrigin = document.createElement( 'h2' ),
				elmPage;
			elmRoot.className       = 'multi-page-container clearfix';
			elmNavi.className       = 'sidenavi';
			elmItemOrigin.className = 'sidenavi-item';
			elmItemOrigin.href      = '#';
			elmPages.className      = 'page-contents';
			elmPageOrigin.className = 'page-content main';
			elmPageOrigin.appendChild( elmTitleOrigin);
			
			MENU_BAR_CONTROL.HELP.remove( asyncOption );
			asyncOption = null;			
			
			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;
				
				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 );
				
				MENU_BAR_CONTROL.HELP.createOption( title, null, onSelectionClick, true );
				++numPage;
			});
			elmRoot.appendChild( elmNavi );
			elmRoot.appendChild( elmPages );
			jqAjaxContents.removeClass( 'loading' ).append( elmRoot );
			
			jqNaviItems = jqAjaxContents.find( 'a.' + elmItemOrigin.className ).click( onNaviClick );
			jqPages     = jqAjaxContents.find( '.page-content' );
			jqPages.find( 'a' ).click( onInnerLinkClick );
			
			app.addAsyncCall( jumpPage );
		};
		function onSelectionClick( _pageIndex ){
			HELP_DOCUMENTS_WINDOW.open();
			jumpPage( _pageIndex );
		};
		function jumpPage( _index ){
			if( Type.isNumber( _index ) === true && 0 <= _index && _index < numPage && currentPageIndex !== _index ){
				currentPageIndex = _index;
			};
			jqNaviItems.removeClass( 'current' ).eq( currentPageIndex ).addClass( 'current' );
			jqPages.hide().eq( currentPageIndex ).show();
		};
		function onNaviClick( e ){
			// this は <a>
			jumpPage( Util.getChildIndex( this.parentNode, this ) );
			return false;
		};
		function onInnerLinkClick( e ){
			var jump = ( this.href || '' ).split( '#jump' ),
				n = jump[ 1 ];
			if( !n ) return;
			jumpPage( '' + parseFloat( n ) === n ? parseFloat( n ) : -1 );
			return false;				
		};
		return WINDOWS_CONTROL.createWindow(
			{
				onInit: function(){
					asyncOption    = MENU_BAR_CONTROL.HELP.createAsyncOption( onAjaxStart );
					jqAjaxContents = $( HELP_DOCUMENTS_WINDOW.elm ).find( '.window-body' ).addClass( 'loading' ).css( { overflow: 'auto' } );
				},
				onFirstOpen: function( _w, _h ){
					jqAjaxContents.css( { height: _h } );
					onAjaxStart !== null && onAjaxStart();
				},
				onResize: function( _w, _h ){
					jqAjaxContents && jqAjaxContents.css( { height: _h } );
				}
			},
			null, 'Help', 0, 215, 400, 350, false, true, true, 300, 300
		);
	})();

/* ----------------------------------------
 * GRID_CONTROL
 *  - control
 *  - panelResizeListener
 */
	var GRID_CONTROL = ( function(){
		var elmGrid,
			urlBG   = "url('images/grid.gif')",
			visible = false;

		return {
			init: function(){
				elmGrid = document.getElementById( 'grid' );
				delete GRID_CONTROL.init;
			},
			open: function(){
				delete GRID_CONTROL.open;
			},
			close: function(){
				
			},
			onPanelResize: function( _panelX, _panelY ){
				elmGrid.style.backgroundPosition = [ _panelX % 10, 'px ', _panelY % 10, 'px' ].join( '' );
				elmGrid.style.height = windowH +'px';
			},
			enabled: function(){
				return visible;
			},
			update: function(){
				$( elmGrid ).stop().css( {
					opacity:	'',
					fliter:		''
				})[ visible === true ? 'fadeOut' : 'fadeIn' ]();
				
				visible = !visible;
				
				if( visible === true && urlBG !== null ){
					elmGrid.style.backgroundImage = urlBG;
					urlBG = null;
				}
				return visible;
			}
		}
	})();
		
/* ----------------------------------------
 * WHITE_GLASS_CONTROL
 *  - panelResizeListener
 */
	var WHITE_GLASS_CONTROL = ( function(){
		var styleTop, styleLeft, styleRight, styleBottom;

		return {
			init: function(){
				styleTop    = document.getElementById( 'whiteGlass-top' ).style;
				styleLeft   = document.getElementById( 'whiteGlass-left' ).style;
				styleRight  = document.getElementById( 'whiteGlass-right' ).style;
				styleBottom = document.getElementById( 'whiteGlass-bottom' ).style;
				delete WHITE_GLASS_CONTROL.init;
			},
			onPanelResize: function( _panelX, _panelY, _panelW, _panelH ){
				var	_w             = _panelW,
					_h             = _panelH,
					marginTop      = _panelY,
					marginBottom   = windowH -_h -marginTop,
					marginX        = _panelX,
					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';
			}
		};
	})();


/* --------------------------------------------------------------------------------------------
 * PanelResizerClass
 *  - mouseEventListener
 */
	var PanelResizerClass = function( id, isTop ){
		this.id    = id;
		this.isTop = isTop;
	};
	PanelResizerClass.BORDER_SIZE = 2;
	PanelResizerClass.HEIGHT      = 30;
	PanelResizerClass.prototype = {
		id             : null,
		node           : null,
		style          : null,
		isTop          : false,
		x              : - PanelResizerClass.BORDER_SIZE / 2,
		y              : 0,
		w              : 0,
		h              : PanelResizerClass.HEIGHT,
		panelX         : 0,
		panelY         : 0,
		panelW         : 0,
		panelH         : 0,
		offsetY        : 0,
		startY         : 0,
		startH         : 0,
		isDragging     : false,
		init : function(){
			this.node  = PANEL_CONTROL.node.createNode( document.getElementById( this.id ), false, false, 'panel-resizer-hover', 'pointer' );
			this.node.addEventListener( 'mousedown', this.mousedown, this );
			this.style = document.getElementById( this.id ).style;
			this.y     = this.isTop === true ? ( -5 - PanelResizerClass.HEIGHT - PanelResizerClass.BORDER_SIZE ) : 0;
		},	
		mousedown : function( e ){
			
			var x = e.layerX, // - this.panelX,
				y = e.layerY; // - this.panelY;
			this.offsetY    = y;
			this.startY     = this.panelY;
			this.startH     = this.panelH;
			this.isDragging = true;
			// app.updateCoursor( 'n-resize' );
			this.node.addEventListener( 'mousemove', this.mousemove, this );
			this.node.addEventListener( 'mouseup',   this.mouseup,   this );
			this.node.cursor( 'n-resize' );
			return true;
		},
		mousemove : function( e ){
			var move = e.layerY - this.offsetY,
				h;

			if( this.isTop === true ){
				if( this.panelH - move < MIN_PANEL_HEIGHT ){
					move = this.panelH - MIN_PANEL_HEIGHT;
				};
				PANEL_CONTROL.resizeElement( true, this.panelX, this.panelY + move, this.panelW, this.panelH - move );
			} else {
				h = this.startH + move;
				if( 0 < h && h < windowH - this.panelY - PanelResizerClass.HEIGHT - 5 - PanelResizerClass.BORDER_SIZE ){
					PANEL_CONTROL.resizeElement( false, this.panelX, this.panelY, this.panelW, h < MIN_PANEL_HEIGHT ? MIN_PANEL_HEIGHT : h );
				};
			};
			return true;
		},
		mouseup : function( e ){
			if( this.isDragging !== true ) return;
			( this.startY !== this.panelY || this.startH !== this.panelH ) &&
				HISTORY_CONTROL.saveState(
					this.restoreState,
					[ undefined, this.startY, undefined, this.startH ],
					[ undefined, this.panelY, undefined, this.panelH ],
					null, this
				);
			this.isDragging = false;
			var move = e.layerY - this.offsetY,
				h;

			if( this.isTop === true ){
				if( this.panelH - move < MIN_PANEL_HEIGHT ){
					move = this.panelH - MIN_PANEL_HEIGHT;
				};
				PANEL_CONTROL.resize( true, this.panelX, this.panelY + move, this.panelW, this.panelH - move );
			} else {
				h = this.startH + move;
				if( 0 < h && h < windowH - this.panelY - PanelResizerClass.HEIGHT - 5 - PanelResizerClass.BORDER_SIZE ){
					PANEL_CONTROL.resize( false, this.panelX, this.panelY, this.panelW, h < MIN_PANEL_HEIGHT ? MIN_PANEL_HEIGHT : h );
				};
			};
			// app.updateCoursor( '' );
			this.node.removeEventListener( 'mousemove', this.mousemove );
			this.node.removeEventListener( 'mouseup',   this.mouseup );
			this.node.cursor( 'pointer' );
		},
		restoreState : function( x, y, w, h ){
			PANEL_CONTROL.resize( this.isTop, x || this.panelX, y || this.panelY, w || this.panelW, h || this.panelH );
		},
		busy : function(){
			return this.isDragging;
		},
		onPanelResize : function( x, y, w, h ){
			this.panelX = x;
			this.panelY = y;
			if( this.panelW !== w ){
				this.style.width = ( w + 2 ) + 'px';
				this.panelW      = w;
			};
			this.panelH = h;
			this.y = this.isTop === true ? this.y : ( this.panelH + 5 + PanelResizerClass.BORDER_SIZE );
			this.w = this.panelW + 2;
			this.node.update( undefined, undefined, this.w );
		}	
	};

/* ----------------------------------------
 * PANEL_CONTROL
 *  - controler
 *  - mouseEventListener
 * 
 * panel-border の表示と onPanelResize の通知．
 * panel drag.
 * 
 */
	var PANEL_CONTROL = ( function(){
		var elmPanel, stylePanel,
			nodePanel            = null,
			DEFAULT_PANEL_WIDTH  = 400,
			DEFAULT_PANEL_HEIGHT = 300,
			borderSize           = 2,
			offsetX, offsetY, startX, startY,
			isDragging           = false,
			isDraggable          = false,
			resizerTop           = new PanelResizerClass( 'panel-resizer-top',    true ),
			resizerBottom        = new PanelResizerClass( 'panel-resizer-bottom', false );
		
		app.addKeyEventListener( 'keychange', onSpaceUpdate, 32, false, false );
		
		function onSpaceUpdate( e ){
			if( e.type === 'keyup' ){
				app.isCurrentInteractiveEventListener( null ) === true && app.updateCoursor( '' );
				isDraggable = false;
			} else {
				app.isCurrentInteractiveEventListener( null ) === true && app.updateCoursor( 'move' );
				isDraggable = true;
			};
			return false;
		};
		
		return {
			id   : 'PANEL_CONTROL',
			x    : 0,
			y    : 0,
			w    : 0,
			h    : 0,
			elm  : null,
			node : null,
			init : function(){
				elmPanel      = this.elm  = document.getElementById( 'panel-tools-container' );
				nodePanel     = this.node = eventRoot.createNode( elmPanel, true, false );
				
				resizerTop.init();
				resizerBottom.init();
				
				stylePanel = elmPanel.style;
				delete PANEL_CONTROL.init;
			},
			open: function( _panelW, _panelH, _borderSize ){
				PANEL_CONTROL.w = Type.isFinite( _panelW ) === true ? _panelW : DEFAULT_PANEL_WIDTH;
				PANEL_CONTROL.h = Type.isFinite( _panelH ) === true ? _panelH : DEFAULT_PANEL_HEIGHT;
				borderSize      = Type.isFinite( _borderSize ) === true ? _borderSize : borderSize;
				
				delete PANEL_CONTROL.open;
			},
			close: function(){
				
			},
			resize: function( isResizerTopAction, x, y, w, h ){
				PANEL_CONTROL.x = x = x !== undefined ? x : PANEL_CONTROL.x;
				PANEL_CONTROL.y = y = y !== undefined ? y : PANEL_CONTROL.y;
				PANEL_CONTROL.w = w = w !== undefined ? w : PANEL_CONTROL.w;
				PANEL_CONTROL.h = h = h !== undefined ? h : PANEL_CONTROL.h;
				
				nodePanel.update( x - borderSize, y - borderSize, w, h );

				
				resizerTop.onPanelResize( x, y, w, h );
				resizerBottom.onPanelResize( x, y, w, h );
				GRID_CONTROL.onPanelResize( x, y );
				WHITE_GLASS_CONTROL.onPanelResize( x, y, w, h );
				PANEL_ELEMENT_CONTROL.onPanelResize( x, y, w, h, isResizerTopAction === true );
			},
			resizeElement : function( isResizerTopAction, x, y, w, h ){
				stylePanel.cssText = [ 'left:',  ( x - borderSize ), 'px;',
									 'top:',    ( y - borderSize ), 'px;',
									 'width:',  w, 'px;',
									 'height:', h, 'px' ].join( '' );
				// PANEL_RESIZER_TOP.onPanelResize( x, y, w, h );
				// PANEL_RESIZER_BOTTOM.onPanelResize( x, y, w, h );
				GRID_CONTROL.onPanelResize( x, y );
				WHITE_GLASS_CONTROL.onPanelResize( x, y, w, h );
				PANEL_ELEMENT_CONTROL.onPanelResize( x, y, w, h, isResizerTopAction );			
			},
			onWindowResize: function( _windowW, _windowH ){
				PANEL_CONTROL.x = Math.floor( ( _windowW - PANEL_CONTROL.w ) / 2 );
				PANEL_CONTROL.y = Math.floor( ( _windowH - PANEL_CONTROL.h ) / 2 );
				PANEL_CONTROL.resize();
			},
			mousemove: function( _mouseX, _mouseY ){
				if( isDraggable === true && isDragging === true ){
					PANEL_CONTROL.resize( false, startX + _mouseX - offsetX, startY + _mouseY - offsetY );
				};
			},
			mouseup: function( _mouseX, _mouseY ){
				if( isDraggable === true ){
					isDragging = false;
					app.updateCoursor( '' );
				};
			},
			mousedown: function( _mouseX, _mouseY ){
				if( isDraggable === true ){
					offsetX    = _mouseX;
					offsetY    = _mouseY;
					startX     = PANEL_CONTROL.x;
					startY     = PANEL_CONTROL.y;
					isDragging = true;
					app.updateCoursor( 'move' );
					return true;
				};
			},
			busy: function(){
				return isDragging === true;
			}				
		};
	})();

/* --------------------------------------------------------------------------------------------
 * TailOperator
 *  - panelElementOperator
 */
	var TailOperator = ( function(){
		var	styleMover,
			SIZE,
			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,
			w, h,
			balloonW, balloonH, balloonA, radA,
			visible = false,
			startA;
		
		return {
			init: function(){
				var elm    = document.getElementById( 'balloon-tail-mover' );
				SIZE       = Util.getElementSize( elm ).width;
				styleMover = elm.style;
				delete TailOperator.init;
			},
			update: function ( _w, _h, _a ){
				balloonW = _w !== undefined ? _w : balloonW;
				balloonH = _h !== undefined ? _h : balloonH;
				balloonA = _a !== undefined ? _a : balloonA;
				radA = ( balloonA - 90 ) * 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 );
				styleMover.left = tailX + 'px';
				styleMover.top  = tailY + 'px';
			},
			show: function( _currentText ){
				/**
				 * visibilityのほうがいい, display:none だと ie で描画が狂う
				 */
				styleMover.visibility = '';
				currentText = _currentText;
				TailOperator.update( _currentText.w, _currentText.h, _currentText.a );
			},
			hide: function(){
				styleMover.visibility = 'hidden';
				currentText = null;
			},			
			hitTest: function( _mouseX, _mouseY ){
				var _x  = tailX -SIZE / 2,
					_y  = tailY -SIZE / 2,
					ret = _x <= _mouseX && _y <= _mouseY && _x + SIZE >= _mouseX && _y + SIZE >= _mouseY;
				ret === true && app.updateCoursor( 'move' );
				return ret;
			},
			onStart: function( _mouseX, _mouseY ){
				if( currentText.type !== PANEL_ELEMENT_TYPE_TEXT ) return false;
				if( TailOperator.hitTest( _mouseX, _mouseY ) === true ){
					w = currentText.w;
					h = currentText.h;
					startA = currentText.a;
					return true;
				};
				return false;
			},
			onDrag: function( _mouseX, _mouseY ){
				_mouseX = _mouseX - w / 2;
				_mouseY = _mouseY - h / 2; //Balloonの中心を0,0とする座標系に変換
				TailOperator.update( w, h,
					_mouseX !== 0 ?
						ATAN( _mouseY / _mouseX ) * RAD_TO_DEG + ( _mouseX > 0 ? 90 : 270 ) :
						_mouseY > 0 ? 180 : 0
				);
				currentText && currentText.angle( FLOOR( balloonA + 0.5 ) );
				CONSOLE_CONTROLER.update( currentText );
			},
			onFinish: function(){
				startA !== currentText.a && PanelElementOperatorManager.saveStatus( undefined, undefined, w, h, startA );
				startA !== currentText.a && PanelElementOperatorManager.resize( undefined, undefined, w, h, currentText.a );
			},
			onCancel: function(){
				currentText.angle( startA );
				PanelElementOperatorManager.resize( undefined, undefined, w, h, startA );
			}
		}
	})();

/* --------------------------------------------------------------------------------------------
 * ResizeOperator
 *  - panelElementOperator
 */
	var ResizeOperator = ( function(){
		var	HIT_AREA        = MOUSE_HIT_AREA,
			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 }
			],
			styleInner,
			elmInner,
			styleResizerTop,
			styleResizerLeft,
			styleResizerRight,
			styleResizerBottom,
			x, y, w, h,
			currentIndex = -1,
			currentElement,
			isSpeach = false;
		
		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,
            flipV, flipH, startFilpV, startFilpH, startAspect,
			baseX, baseY, baseW, baseH,
			currentX, currentY, currentW, currentH,
			offsetX, offsetY,
			error = 0;
		
		function update( _x, _y, _w, _h ){
			var __w, __h;
			_x = _x !== undefined ? _x : currentX;
			_y = _y !== undefined ? _y : currentY;
			_w = _w !== undefined ? _w : currentW;
			_h = _h !== undefined ? _h : currentH;
			
			if( isSpeach === false && currentIndex > 3 && app.shiftEnabled() === true ){
				if( startAspect >= 1 ){
					__w = _w;
					_w  = FLOOR( startAspect * _h );
					_x  = _x + ( currentIndex % 2 === 0 ? __w - _w : 0 );
				} else {
					__h = _h;
					_h  = FLOOR( _w / startAspect );
					_y  = _y + ( currentIndex <= 5 ? __h - _h : 0 );
				};
			};
			ResizeOperator.update( x = _x, y = _y, w = _w, h = _h );
			currentElement.resize( _x, _y, _w, _h );
			isSpeach === true && TailOperator.update( _w, _h );
			CONSOLE_CONTROLER.show( currentElement, _w, _h );
			// CONSOLE_CONTROLER.update( currentElement );
		};
		
		function flip( _flipH, _flipV ){
			var p = CURSOR_AND_FLIP[ currentIndex ];
			currentIndex = _flipH === true || _flipV === true ? p[
					_flipH === true && _flipV === true ? 'vh' : ( _flipH === true ? 'h' : 'v' )
				] : currentIndex;
			app.updateCoursor( CURSOR_AND_FLIP[ currentIndex ].cursor );
			elmInner.className = 'current-resizer-is-' + currentIndex;
			currentElement.flip( _flipH, _flipV );
		};
		return {
			init: function(){
				elmInner           = document.getElementById( 'comic-element-resizer-container-inner' );
				styleInner         = elmInner.style;
				
				styleResizerTop    = document.getElementById( 'comic-element-resizer-top' ).style;
				styleResizerLeft   = document.getElementById( 'comic-element-resizer-left' ).style;
				styleResizerRight  = document.getElementById( 'comic-element-resizer-right' ).style;
				styleResizerBottom = document.getElementById( 'comic-element-resizer-bottom' ).style;
				
				delete ResizeOperator.init;
			},
			update: function( _x, _y, _w, _h ){
				x = _x = _x !== undefined ? _x : x;
				y = _y = _y !== undefined ? _y : y;
				w = _w = _w !== undefined ? _w : w;
				h = _h = _h !== undefined ? _h : h;
				
				PanelElementOperatorManager.resizeElement( _x, _y, _w, _h );
				
				styleInner.width  = _w + 'px';
				styleInner.height = _h + 'px';
				
				styleResizerTop.left = styleResizerBottom.left = FLOOR( _w / 2 - 5 ) + 'px';
				styleResizerLeft.top = styleResizerRight.top   = FLOOR( _h / 2 - 5 ) + 'px';
	
				POSITION_ARRAY.length = 0;
				POSITION_ARRAY.push(
					{x:	5,				y:	-HIT_AREA,		w:	_w - 5,			h:	HIT_AREA * 2 }, // top
					{x: -HIT_AREA,		y:	5,				w:	HIT_AREA * 2,	h:	_h - 5 },   // left
					{x: _w - 5,			y:	HIT_AREA + 5,	w:	HIT_AREA * 2,	h:	_h - 5 },   // right
					{x:	5,				y:	_h - 5,			w:	_w - 5,			h:	HIT_AREA * 2 }, // bottom
					{x:	-HIT_AREA,		y:	-HIT_AREA,		w:	HIT_AREA + 5,	h:	HIT_AREA + 5}, // top left
					{x: _w - 5,			y:	-HIT_AREA,		w:	HIT_AREA + 5,	h:	HIT_AREA + 5}, // top right
					{x:	-HIT_AREA,		y:	_h - 5,			w:	HIT_AREA + 5,	h:	HIT_AREA + 5}, // bottom left
					{x:	_w - 5,			y:	_h - 5,			w:	HIT_AREA + 5,	h:	HIT_AREA + 5}  // bottom right
				);
			},
			index: function( _mouseX, _mouseY ){
				var	p, i;
				for( i = 4; i < 8; ++i ){
					p = POSITION_ARRAY[ i ];
					if( p.x <= _mouseX && p.y <= _mouseY && p.x + p.w >= _mouseX && p.y + p.h >= _mouseY ){
						app.updateCoursor( CURSOR_AND_FLIP[ i ].cursor );
						elmInner.className  = 'current-resizer-is-' + i;
						return currentIndex = i;
					};
				};
				for( i = 0; i < 4; ++i ){
					p = POSITION_ARRAY[ i ];
					if( p.x <= _mouseX && _mouseX <= p.x + p.w && p.y <= _mouseY && _mouseY <= p.y + p.h ){
						app.updateCoursor( CURSOR_AND_FLIP[ i ].cursor );
						elmInner.className  = 'current-resizer-is-' + i;
						return currentIndex = i;
					};
				};
				app.updateCoursor( '' );
				elmInner.className = '';
				return -1;
			},
			show: function( _currentElement ){
				currentElement = _currentElement;
				isSpeach = _currentElement.type === PANEL_ELEMENT_TYPE_TEXT;
				ResizeOperator.update( _currentElement.x, _currentElement.y, _currentElement.w, _currentElement.h );
			},
			hide: function(){
				currentElement = null;
			},
			onStart: function( _mouseX, _mouseY ){
				isSpeach = currentElement.type === PANEL_ELEMENT_TYPE_TEXT;
				if( currentElement.keepSize === true ) return false;
				currentIndex = this.index( _mouseX, _mouseY );
				if( currentIndex === -1 ) return false;
				offsetX = _mouseX;
				offsetY = _mouseY;
				startX = baseX = currentElement.x;
				startY = baseY = currentElement.y;
				startW = baseW = currentElement.w;
				startH = baseH = currentElement.h;
				startFilpV = currentElement.flipV;
				startFilpH = currentElement.flipH;
				startAspect = startW / startH;
				return true;
			},
			onDrag: function( _mouseX, _mouseY ){
				var com      = RESIZE_WORK_ARRAY[ currentIndex ],
					moveX    = _mouseX - offsetX,
					moveY    = _mouseY - offsetY,
					_updated = moveX !== 0 || moveY !== 0,
					_x, _y, _w, _h,
					_thisError = 0;
					
				var _memoryX = 0,
					_memoryY = 0;
				/*
				 * Opera 11+ often forget values, why ??
				 */
				while( _x === undefined || _y === undefined || _w === undefined || _h === undefined ){
					_x = _x !== undefined ? _x : baseX + moveX * com.x;
					_y = _y !== undefined ? _y : baseY + moveY * com.y;
					_w = _w !== undefined ? _w : baseW + moveX * com.w;
					_h = _h !== undefined ? _h : baseH + moveY * com.h;
					error += _thisError === 0 ? 0 : 1;
					++_thisError;
					if( _thisError > 9999 ){
						++error
						//alert( 'opera error' +error);
						this.onCancel();
						return;
					};
				};
				
				if( _w >= MIN_ELEMENT_SIZE && _h >= MIN_ELEMENT_SIZE ){
					
				} else 
				if( _w >= -MIN_ELEMENT_SIZE && _h >= -MIN_ELEMENT_SIZE ){
					//return;
					if( _w < MIN_ELEMENT_SIZE){
						//_x += Math.abs( MIN_ELEMENT_SIZE -_w);
						_x = currentX;
						_w = MIN_ELEMENT_SIZE;
					}
					if( _h < MIN_ELEMENT_SIZE){
						//_y += Math.abs( MIN_ELEMENT_SIZE -_h);
						_y = currentY;
						_h = MIN_ELEMENT_SIZE;
					}
				} else 
				if( currentElement.type === PANEL_ELEMENT_TYPE_TEXT ){
					return;
				} else 
				if( _w < -MIN_ELEMENT_SIZE || _h < -MIN_ELEMENT_SIZE ){

					if( _w < -MIN_ELEMENT_SIZE && _h > MIN_ELEMENT_SIZE ){
					// flipH
						_memoryX = _x;
						baseX    = _x = _x +_w;
						baseY    = _y;
						baseW    = _w = _memoryX -_x;
						baseH    = _h;
						flip( true, false );
						flipV    = currentElement.flipV;
					} else 
					if( _w > MIN_ELEMENT_SIZE && _h < -MIN_ELEMENT_SIZE ){
					// flipV
						_memoryY = _y;
						baseX    = _x;
						baseY    = _y = _y +_h;
						baseW    = _w;
						baseH    = _h = _memoryY -_y;
						flip( false, true);
						flipH    = currentElement.flipH;
					} else {
					// flipVH
						_memoryX = _x;
						_memoryY = _y;
						baseX    = _x = _x + _w;
						baseY    = _y = _y + _h;
						baseW    = _w = _memoryX - _x;
						baseH    = _h = _memoryY - _y;
						flip( true, true );
						flipV    = currentElement.flipV;
						flipH    = currentElement.flipH;
					};
					_updated = true;
					offsetX  = _mouseX;
					offsetY  = _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>',
						'result', y, h,
						'err', error
				].join( ' , ')); */
			},
			onFinish: function(){
				app.updateCoursor( '' );
				if( w === startW && h === startH && x === startX && y === startY ) return;
				PanelElementOperatorManager.resize( x, y, w, h );
				currentElement.resize( x, y, w, h );
				PanelElementOperatorManager.saveStatus( startX, startY, startW, startH, undefined, startFilpV, startFilpH );
			},
			onCancel: function(){
				app.updateCoursor( '' );
				PanelElementOperatorManager.resize( startX, startY, startW, startH );
				currentElement.type === PANEL_ELEMENT_TYPE_IMAGE ?
					currentElement.animate( startX, startY, startW, startH, startFilpV, startFilpH ) :
					currentElement.animate( startX, startY, startW, startH, angle );
			},
			onShiftUpdate: update,
			onCtrlUpdate: update
		}
	})();

/* --------------------------------------------------------------------------------------------
 * PositionOperator
 *  - panelElementOperator
 */
	var PositionOperator = ( function(){
		var HIT_AREA = MOUSE_HIT_AREA,
			currentElement,
			startX, startY,
			x, y,
			offsetX, offsetY,
			isCopy = false;
		function update( _x, _y ){
			x = _x !== undefined ? _x : x;
			y = _y !== undefined ? _y : y;
			// ResizeOperator.update( x, y );
			
			PanelElementOperatorManager.resizeElement( x, y );
			
			currentElement.resize( x, y );
			// CONSOLE_CONTROLER.update( currentElement );
		};
		return {
			init: function(){
				delete PositionOperator.init;
			},
			show : function( _currentElement ){
				currentElement = _currentElement;
			},
			hide : function(){
				currentElement = null;
			},
			onStart: function( _mouseX, _mouseY ){
				offsetX = _mouseX;
				offsetY = _mouseY;
				startX  = x = currentElement.x;
				startY  = y = currentElement.y;
				app.updateCoursor( 'move' );
			},
			onDrag: function( _mouseX, _mouseY ){
				var moveX = _mouseX - offsetX,
					moveY = _mouseY - offsetY,
					_x    = startX + moveX,
					_y    = startY + moveY;
				if( GRID_CONTROL.enabled() === true ){
					_x = Math.floor( _x / 10 ) * 10;
					_y = Math.floor( _y / 10 ) * 10;
				};
				update( _x, _y );
			},
			onFinish: function(){
				app.updateCoursor( '' );
				if( x === startX && y === startY ) return;
				PanelElementOperatorManager.resize( x, y );
				currentElement.resize( x, y );
				PanelElementOperatorManager.saveStatus( startX, startY );
			},
			onCancel: function(){
				app.updateCoursor( '' );
				PanelElementOperatorManager.resize( startX, startY );
				currentElement.animate( startX, startY );
			},
			onShiftUpdate: update,
			onCtrlUpdate: update
		};
	})();


/* --------------------------------------------------------------------------------------------
 * PanelElementOperatorManager
 */
	var PanelElementOperatorManager = ( function(){
		var	HIT_AREA        = MOUSE_HIT_AREA,
			isSpeach        = false,
			currentOperator = null,
			currentElement  = null,
			node            = null,
			styleContainer  = null,
			currentX, currentY, currentW, currentH, angle, flipV, flipH;
		
		function mousedown( e ){
			var x = e.layerX - HIT_AREA / 2 - 5,
				y = e.layerY - HIT_AREA / 2 - 5;
			if( isSpeach === true && TailOperator.onStart( x, y ) === true ){
				currentOperator = TailOperator;
			} else
			if( ResizeOperator.onStart( x, y ) === true ){
				currentOperator = ResizeOperator;
			} else {
				PositionOperator.onStart( x, y );
				currentOperator = PositionOperator;
			};
			// return true;
		};
		function mousemove( e ){
			var x = e.layerX - HIT_AREA / 2 - 5,
				y = e.layerY - HIT_AREA / 2 - 5;
			if( currentOperator !== null ){
				currentOperator.onDrag( x, y );
				return true;
			} else
			if( currentElement !== null ){
				( isSpeach === false || TailOperator.hitTest( x, y ) === false ) && ResizeOperator.index( x, y );
			};
		};
		function mouseup( e ){
			currentOperator !== null && currentOperator.onFinish();
			currentOperator = null;
		};
		
		return {
			elm  : null,
			node : null,
			init : function(){
				TailOperator.init();
				ResizeOperator.init();
				PositionOperator.init();
				CONSOLE_CONTROLER.init();
				
				app.addKeyEventListener( 'keychange', function( e ){
					currentOperator !== null && currentOperator.onShiftUpdate && currentOperator.onShiftUpdate();
					return false;
				}, 16 );
				app.addKeyEventListener( 'keychange', function( e ){
					currentOperator !== null && currentOperator.onCtrlUpdate && currentOperator.onCtrlUpdate();
					return false;
				}, 17 );
				app.addKeyEventListener( 'keydown', function( e ){
					currentOperator !== null && currentOperator.onCancel && currentOperator.onCancel();
					currentOperator = null;
					return false;
				}, 27, false, false );
				
				delete PanelElementOperatorManager.init;
			},
			open: function(){
				var elm = document.getElementById( 'comic-element-resizer-container' );
				PanelElementOperatorManager.elm  = elm;
				PanelElementOperatorManager.node = eventRoot.createNode( elm, false, false );
				PanelElementOperatorManager.node.addEventListener( 'mouseout', PanelElementOperatorManager.hide, PanelElementOperatorManager );
				styleContainer = elm.style;
				PanelElementOperatorManager.node.disabled( true );
				PanelElementOperatorManager.node.addEventListener( 'mousemove', mousemove );
				PanelElementOperatorManager.node.addEventListener( 'mousedown', mousedown );
				PanelElementOperatorManager.node.addEventListener( 'mouseup', mouseup );
				
				CONSOLE_CONTROLER.open();
				PanelElementOperatorManager.hide();
				
				delete PanelElementOperatorManager.open;
			},
			close: function(){
				
			},
			show : function( _currentElement ){
				if( currentElement === null ){
					styleContainer.display = '';
					PanelElementOperatorManager.node.disabled( false );
				};
				if( currentElement !== _currentElement ){
					currentElement = _currentElement;
					
					ResizeOperator.show( _currentElement );
					PositionOperator.show( _currentElement );
					
					isSpeach = ( _currentElement.type === PANEL_ELEMENT_TYPE_TEXT );
					isSpeach === true ? TailOperator.show( _currentElement ) : TailOperator.hide();
					
					flipV = _currentElement.flipV;
					flipH = _currentElement.flipH;
					
					PanelElementOperatorManager.resize(
						_currentElement.x, _currentElement.y, _currentElement.w, _currentElement.h,
						isSpeach === true ? _currentElement.a : 0
					);
				};
			},
			hide: function(){
				if( currentElement !== null ){
					styleContainer.display = 'none';
					PanelElementOperatorManager.node.disabled( true );
				};
				currentElement = null;
				app.updateCoursor( '' );
				TailOperator.hide();
				ResizeOperator.hide();
				PositionOperator.hide();
				CONSOLE_CONTROLER.hide();
			},
			resizeElement : function( _x, _y, _w, _h, _angle ){
				_x = _x !== undefined ? _x : currentX;
				_y = _y !== undefined ? _y : currentY;
				_w = _w !== undefined ? _w : currentW;
				_h = _h !== undefined ? _h : currentH;
				
				var style    = PanelElementOperatorManager.elm.style;
				style.left   = ( PANEL_CONTROL.x + _x - HIT_AREA / 2 - 5 ) + 'px';
				style.top    = ( PANEL_CONTROL.y + _y - HIT_AREA / 2 - 5 ) + 'px';
				style.width  = ( _w + HIT_AREA + 5 ) + 'px';
				style.heught = ( _h + HIT_AREA + 5 ) + 'px';
				
				// ResizeOperator.update( _x, _y, _w, _h );
				isSpeach === true && TailOperator.update( _w, _h, angle );
				CONSOLE_CONTROLER.show( currentElement, _w, _h );
			},
			resize: function( _x, _y, _w, _h, _angle ){
				currentX = _x = _x !== undefined ? _x : currentX;
				currentY = _y = _y !== undefined ? _y : currentY;
				currentW = _w = _w !== undefined ? _w : currentW;
				currentH = _h = _h !== undefined ? _h : currentH;
				angle = _angle = _angle !== undefined ? _angle : angle;

				// ResizeOperator.update( _x, _y, _w, _h );
				isSpeach === true && TailOperator.update( _w, _h, angle );
				CONSOLE_CONTROLER.show( currentElement, _w, _h );
				//CONSOLE_CONTROLER.update( currentElement );
				PanelElementOperatorManager.node.update( PANEL_CONTROL.x + _x - HIT_AREA / 2 - 5, PANEL_CONTROL.y + _y - HIT_AREA / 2 - 5, _w + HIT_AREA + 5, _h + HIT_AREA + 5 );
			},
			/* history */
			restoreState: function( _currentElement, _x, _y, _w, _h, _a, _flipV, _flipH ){
				if( arguments.length !== 8 ) return;
				if( !_currentElement && !currentOperator ) return;
				_currentElement.type === PANEL_ELEMENT_TYPE_IMAGE ?
					_currentElement.animate( _x, _y, _w, _h, _flipV, _flipH ) :
					_currentElement.animate( _x, _y, _w, _h, _a );
				currentOperator !== null && currentOperator.onCancel && currentOperator.onCancel();
				currentOperator = null;
				currentElement === _currentElement ? PanelElementOperatorManager.resize( _x, _y, _w, _h, _a ) : PanelElementOperatorManager.show( _currentElement );
			},
			saveStatus: function( startX, startY, startW, startH, startA, startFilpV, startFilpH ){
				startX = startX !== undefined ? startX : currentX;
				startY = startY !== undefined ? startY : currentY;
				startW = startW !== undefined ? startW : currentW;
				startH = startH !== undefined ? startH : currentH;
				startA = startA !== undefined ? startA : angle;
				startFilpV = startFilpV !== undefined ? startFilpV : flipV;
				startFilpH = startFilpH !== undefined ? startFilpH : flipH;
				currentElement && HISTORY_CONTROL.saveState( PanelElementOperatorManager.restoreState,
					[ currentElement, startX, startY, startW, startH, startA, startFilpV, startFilpH ],
					[ currentElement, currentX, currentY, currentW, currentH, angle, flipV, flipH ]
				);
			}
		};
	})();
	/*
	 *  // PanelElementOperatorManager
	 */

/* --------------------------------------------------------------------------------------------
 * CONSOLE_CONTROLER
 */
	var CONSOLE_CONTROLER = ( function(){
		var LAYER_BACK_BUTTON, LAYER_FORWARD_BUTTON, DELETE_BUTTON, EDIT_BUTTON, CHANGE_BUTTON,
			PUSH_OUT_RATIO  = 0.5,
			tailSize        = 10,
			elmRoot, elmContainer, elmPushout, elmTail,
			pushoutW        = 0,
			pushoutH        = 0,
			pushout         = false,
			currentType     = -1,
            currentElement  = null,
			visible         = false,
			node            = null,
			ui, inputX, inputY, inputZ, inputA, inputW, inputH, inputPercentW, inputPercentH, inputAspectRatio,
			buttonBack, buttonForward, buttonDel, buttonEdit, butonChange;
			
		function layerBack(){
			if( currentElement === null ) return;
			if( PANEL_ELEMENT_CONTROL.replace( currentElement, false ) === false ) return;
			CONSOLE_CONTROLER.update( currentElement );
			HISTORY_CONTROL.saveState( PANEL_ELEMENT_CONTROL.replace, [ currentElement, true ], [ currentElement, false ] );
			var _z = currentElement.z;
			LAYER_BACK_BUTTON.visible( _z > 0 );
			LAYER_FORWARD_BUTTON.visible( _z < PANEL_ELEMENT_ARRAY.length - 1 );
		};
		function layerForward(){
			if( currentElement === null ) return;
			if( PANEL_ELEMENT_CONTROL.replace( currentElement, true ) === false ) return;
			CONSOLE_CONTROLER.update( currentElement );
			HISTORY_CONTROL.saveState( PANEL_ELEMENT_CONTROL.replace, [ currentElement, false ], [ currentElement, true ] );
			var _z = currentElement.z;
			LAYER_BACK_BUTTON.visible( _z > 0 );
			LAYER_FORWARD_BUTTON.visible( _z < PANEL_ELEMENT_ARRAY.length - 1 );
		};
		function del(){
			if( currentElement === null ) return;
			HISTORY_CONTROL.saveState( PANEL_ELEMENT_CONTROL.restore, [ true, currentElement ], [ false, currentElement ], false ); // true
			PANEL_ELEMENT_CONTROL.remove( currentElement );
			PanelElementOperatorManager.hide();
		};
		function edit(){
			if( currentElement === null || currentElement.type !== PANEL_ELEMENT_TYPE_TEXT ) return;
			TextEditor.boot( PANEL_CONTROL.x, PANEL_CONTROL.y, currentElement );
		};
		function change(){
			if( currentElement === null ) return;
			PremiumSatge.boot( currentElement.artistID, currentElement.realPicture, currentElement );
		};

		return {
			x: 0,
			y: 0,
			w: 0,
			h: 0,
			init: function(){
				app.addKeyEventListener( 'keydown', layerBack, 66, false, true );
				app.addKeyEventListener( 'keydown', layerForward, 70, false, true );
				app.addKeyEventListener( 'keydown', del, 68, false, true );
				app.addKeyEventListener( 'keydown', edit, 69, false, true );
				app.addKeyEventListener( 'keydown', change, 85, false, true );
				
				elmContainer = document.getElementById( 'comic-element-consol-container' );
				elmRoot      = elmContainer.parentNode;
				elmPushout   = document.getElementById( 'comic-element-consol-pushout-wrapper' );
				elmTail      = document.getElementById( 'comic-element-consol-pushout-tail' );
				delete CONSOLE_CONTROLER.init;
			},
			open: function(){
				LAYER_BACK_BUTTON    = MENU_BAR_CONTROL.EDIT.createOption( 'layer back', 'ctrl + B', layerBack, false, true, false );
				LAYER_FORWARD_BUTTON = MENU_BAR_CONTROL.EDIT.createOption( 'layer forward', 'ctrl + F', layerForward, false, false, false );
				DELETE_BUTTON        = MENU_BAR_CONTROL.EDIT.createOption( 'delete', 'ctrl + D', del, false, true, true );
				EDIT_BUTTON          = MENU_BAR_CONTROL.EDIT.createOption( 'Edit Text', 'ctrl + E', edit, false, true, false );
				CHANGE_BUTTON        = MENU_BAR_CONTROL.EDIT.createOption( 'change', 'ctrl + U', change, false, false, true );
				// inputAspectRatio = $( '#comic-element-keep-aspect' );
				
				delete CONSOLE_CONTROLER.open;
			},
			onMouseover : function( e ){
				node.mesureChildren();
				//node.mesure();
			},
			show: function( _currentElement, w, h ){
				if( node === null ){
					node = CONSOLE_CONTROLER.node = PanelElementOperatorManager.node.createNode( elmContainer, false, false, 'comic-element-consol-container-hover' );
					node.addEventListener( 'mouseover', CONSOLE_CONTROLER.onMouseover, CONSOLE_CONTROLER );
					ui            = app.createUIGroup( node );
					inputX        = ui.createInputText( document.getElementById( 'comic-element-x' ), null );
					inputY        = ui.createInputText( document.getElementById( 'comic-element-y' ), null );
					inputZ        = ui.createInputText( document.getElementById( 'comic-element-z' ), null );
					inputA        = ui.createInputText( document.getElementById( 'comic-element-a' ), null );
					inputW        = ui.createInputText( document.getElementById( 'comic-element-w' ), null );
					inputH        = ui.createInputText( document.getElementById( 'comic-element-h' ), null );
					inputPercentW = ui.createInputText( document.getElementById( 'comic-element-w-percent' ), null );
					inputPercentH = ui.createInputText( document.getElementById( 'comic-element-h-percent' ), null );
					butonChange   = ui.createButton( document.getElementById( 'change-image-button' ), change ),
					buttonBack    = ui.createButton( document.getElementById( 'layer-back-button' ), layerBack ),
					buttonDel     = ui.createButton( document.getElementById( 'delete-button' ), del ),
					buttonForward = ui.createButton( document.getElementById( 'layer-forward-button' ), layerForward ),
					buttonEdit    = ui.createButton( document.getElementById( 'edit-text-button' ), edit );
				};
				
				currentElement = _currentElement;
				
				var type = currentElement.type,
					z    = currentElement.z;
				
				LAYER_BACK_BUTTON.visible( z > 0 );
				buttonBack.enabled( z > 0 );
				LAYER_FORWARD_BUTTON.visible( z < PANEL_ELEMENT_ARRAY.length - 1 );
				buttonForward.enabled( z < PANEL_ELEMENT_ARRAY.length - 1 )
				DELETE_BUTTON.visible( true );
				EDIT_BUTTON.visible( type === PANEL_ELEMENT_TYPE_TEXT );
				CHANGE_BUTTON.visible( false );
				
				//CONSOLE_CONTROLER.x = Math.floor( ( _w - CONSOLE_CONTROLER.w ) / 2 );
				CONSOLE_CONTROLER.w = w;
				CONSOLE_CONTROLER.h = elmContainer.offsetHeight;
				CONSOLE_CONTROLER.y = h - CONSOLE_CONTROLER.h;
				
				if( h * PUSH_OUT_RATIO < CONSOLE_CONTROLER.h ){
					if( pushout === false ){
						pushout = true;
						elmPushout.lastChild.appendChild( elmContainer );
						elmPushout.style.display = 'block';
						pushoutW = elmPushout.offsetWidth;
						pushoutH = elmPushout.offsetHeight;
						elmTail.style.top = ( pushoutH / 2 - tailSize / 2 ) + 'px';
					};
					elmPushout.style.left = ( -pushoutW ) + 'px';
					elmPushout.style.top  = ( h / 2 - pushoutH / 2 ) + 'px';
					elmPushout.className  = 'satellite-left';
				} else
				if( pushout === true ){
					pushout = false;
					elmRoot.insertBefore( elmContainer, elmRoot.firstChild );
					elmPushout.style.cssText = '';
				};
				
				CONSOLE_CONTROLER.update( currentElement );
				ui.visible( true );
				node.mesure();
			},
			update : function( _currentElement ){
				if( _currentElement === null ){
					visible = false;
					return;
				};
				currentElement = _currentElement;
				var type = currentElement.type,
					x    = currentElement.x,
					y    = currentElement.y,
					z    = currentElement.z,
					a    = type === PANEL_ELEMENT_TYPE_TEXT ? Math.floor( currentElement.a ) : 0,
					w    = currentElement.w,
					h    = currentElement.h,
					actualW    = type === PANEL_ELEMENT_TYPE_IMAGE ? currentElement.actualW : 1,
					actualH    = type === PANEL_ELEMENT_TYPE_IMAGE ? currentElement.actualH : 1,
					wPercent   = type === PANEL_ELEMENT_TYPE_IMAGE ? Math.floor( w / actualW * 100 ) : 0,
					hPercent   = type === PANEL_ELEMENT_TYPE_IMAGE ? Math.floor( h / actualH * 100 ) : 0,
					keepAspect = currentElement.keepAspect;
					
				if( currentType !== type ){
					if( type === PANEL_ELEMENT_TYPE_TEXT ){
						inputA.visible( true );
						inputPercentW.visible( false );
						inputPercentH.visible( false );
						buttonEdit.enabled( true );
						//inputAspectRatio.hide();
					} else {
						inputA.visible( false );
						inputPercentW.visible( true );
						inputPercentH.visible( true );
						buttonEdit.enabled( false );
						//inputAspectRatio.show();
					};
					currentType = type;
				};

				inputX.value( x );
				inputY.value( y );
				inputZ.value( z );
				type === 1 && inputA.value( a );
				inputW.value( w );
				inputH.value( h );
				type === 0 && inputPercentW.value( wPercent );
				type === 0 && inputPercentH.value( hPercent );
			},
			hide: function(){
				// if( visible === true ) styleConsoleWrapper.display = 'none';
				ui && ui.visible( false );
				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 );
			}
		};
	})();



	var AbstractPanelElement = function( COMIC_ELM_TYPE ){
		this.type = COMIC_ELM_TYPE;
	};
	AbstractPanelElement.prototype = {
		$       : null,
		data    : null,
		node    : null,
		x       : 0,
		y       : 0,
		w       : 0,
		h       : 0,
		z       : 0,
		timing  : 0,
		actualW : 0,
		actualH : 0,
		flipV   : 0,
		flipH   : 0,
		shift : function( shiftX, shiftY ){
			this.resize( this.x + shiftX, this.y + shiftY );
		},
		mouseover : function( e ){
			PanelElementOperatorManager.show( this );
		}
	};

/* --------------------------------------------------------------------------------------------
 * ImageElementClass
 */
	var	jqImageElementOrigin;
	var ImageElementClass = function( data ){
		jqImageElementOrigin = jqImageElementOrigin || $( app.fetchHTMLElement( 'imgElementTemplete' ) );
		
		this.$        = jqImageElementOrigin.clone( true );
		this.data     = data;
		this.z        = data.z;
		this.timing   = data.t || PANEL_ELEMENT_ARRAY.length;
		this.keepSize = false;
		this.flipV    = data.height < 0 ? -1 : 1;
		this.flipH    = data.width  < 0 ? -1 : 1;
		this.rPicture = data.picture;
		
		var self = this;
		function animeComplete(){
			self.node.mesure();
			self._flipReversibleImage();
		};
		this.animeComplete = animeComplete;
	};
	ImageElementClass.prototype = Util.extend(
		new AbstractPanelElement( PANEL_ELEMENT_TYPE_IMAGE ),
		{
			reversibleImage : null,
			init : function(){
				this._updateResourcePicture( this.rPicture );
				this.node = PANEL_ELEMENT_CONTROL.node.createNode( this.$.get( 0 ), false, true );
				this.node.addEventListener( 'mouseover', this.mouseover, this );				
				this.resize( this.data.x, this.data.y, Math.abs( this.data.width ), Math.abs( this.data.height ) );
				this.init = null;
			},
			flip : function( updateH, updateV ){
				if( updateH !== true && updateV !== true ) return;
				this.flipH = updateH === true ? -this.flipH : this.flipH;
				this.flipV = updateV === true ? -this.flipV : this.flipV;
				this.reversibleImage.resize( this.flipH * this.w, this.flipV * this.h );
			},
			realPicture : function( _rPicture ){
				if( _rPicture && _rPicture !== this.rPicture ){
					HISTORY_CONTROL.saveState( this._updateResourcePicture, this.rPicture, _rPicture, this );
					this._updateResourcePicture( _rPicture );
				};
				return this.rPicture;
			},
			resize : function( x, y, w, h, animate ){
				this.x = x = Type.isFinite( x ) === true ? x : this.x;
				this.y = y = Type.isFinite( y ) === true ? y : this.y;
				this.w = w = Type.isFinite( w ) === true ? w : this.w;
				this.h = h = Type.isFinite( h ) === true ? h : this.h;
				if( animate === true ){
					this.$.animate( { 
						left:	x,
						top:	y,
						width:	w,
						height:	h
					}, 250 , this.animeComplete );					
				} else {
					this.node.update( x, y, w, h );
					this._flipReversibleImage();
				};
			},
			animate : function ( x, y, w, h, flipH, flipV ){
				this.flipH = flipH !== undefined ? flipH : this.flipH;
				this.flipV = flipV !== undefined ? flipV : this.flipV;
				this.resize( x, y, w, h, true );
			},
			destroy : function(){
				this.reversibleImage.destroy();
				this.$.stop().remove();
				this.node.remove();
				
				this.destroy = null;
			},
			_updateResourcePicture : function( _rPicture ){
				this.rPicture = _rPicture;
				this.oPicture = this.rPicture.original_picture;
				this.artistID = this.oPicture.artist.id || -1;

				this.actualW = this.oPicture.width;
				this.actualH = this.oPicture.height;
				
				var _reversibleImage = pettanr.image.createReversibleImage( 
						[ pettanr.CONST.PICTURE_PATH, this.rPicture.id, '.', this.rPicture.ext ].join( '' ),
						this.flipH * this.w, this.flipV * this.h
					);
				if( this.reversibleImage !== null ){
					this.$.children( this.reversibleImage.elm ).replaceWith( _reversibleImage.elm );
					this.reversibleImage.destroy();
				} else {
					this.$.append( _reversibleImage.elm );
				};
				this.reversibleImage = _reversibleImage;
			},
			_flipReversibleImage : function(){
				this.reversibleImage && this.reversibleImage.resize( this.flipH * this.w, this.flipV * this.h );
			}
		}
	);

	
/*
 * / ImageElementClass
 * --------------------------------------------------------------------------------------------
 */


/* --------------------------------------------------------------------------------------------
 * TextElementClass
 * 
 * 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( data ){
		jqTextElementOrigin = jqTextElementOrigin || ( function(){
			var _OLD_IE = $( app.fetchHTMLElement( 'textElementTempleteForOldIE' ) ),
				_MODERN = $( app.fetchHTMLElement( 'textElementTemplete' ) );
			return UA.isIE === true && UA.ieRenderingVersion < 8 ? _OLD_IE : _MODERN;
		})();
		
		this.$       = jqTextElementOrigin.clone( true );
		this.data    = data;
		this.elmText = this.$.find( 'td,.speach-inner' ).get( 0 );
		this.type    = data.balloon_template_id;
		this.content = ( function(){
			var _speachs = data.speeches_attributes;
			for( var k in _speachs ){
				return _speachs[ k ].content || '';
			}
			return '';
		})();
		this.balloon = pettanr.balloon.createBalloon( data.width, data.height, data.tail, this.type );
		this.z       = data.z;
		this.timing  = data.t || PANEL_ELEMENT_ARRAY.length;
		
		this.$.find( 'img' ).eq( 0 ).replaceWith( this.balloon.elm );
		
		var self = this;
		function animeComplete(){
			self.node.mesure();
			self._resizeBalloon();
		};
		this.animeComplete = animeComplete; 
	};
	TextElementClass.prototype = Util.extend(
		new AbstractPanelElement( PANEL_ELEMENT_TYPE_TEXT ),
		{
			init : function(){
				this._updateText();
				this.node = PANEL_ELEMENT_CONTROL.node.createNode( this.$.get( 0 ), false, true );
				this.node.addEventListener( 'mouseover', this.mouseover, this );				
				this.resize( this.data.x, this.data.y, this.data.width, this.data.height, this.data.tail );
				this.init = null;
			},
			_updateType : function( _type ){
				if( this.type !== _type ){
					this.type = _type || this.type;
					this.balloon.type( this.type );
				};
			},
			_updateAngle : function( _a ){
				if( _a !== undefined && a !== _a ){
					this.a = _a !== undefined ? _a : this.a;
					this.balloon.angle( this.a );
				};
			},
			/* history */
			_updateText : function( _text ){
				this.content = _text || this.content || '';
				this.elmText.firstChild.data = this.content;
			},
			_resizeBalloon : function(){
				this.balloon && this.balloon.resize( this.a, this.w, this.h );
			},
			angle : function( _a ){
				_a !== undefined && this.resize( this.x, this.y, this.w, this.h, _a );
				return this.a;
			},
			text : function( _text ){
				if( _text && this.content !== _text ){
					HISTORY_CONTROL.saveState( this._updateText, this.content || '', _text, this );
					this._updateText( _text );
				};
				return this.content;
			},
			resize : function( x, y, w, h, a, animate ){
				this.x = x = x !== undefined ? x : this.x;
				this.y = y = y !== undefined ? y : this.y;
				this.w = w = w !== undefined ? w : this.w;
				this.h = h = h !== undefined ? h : this.h;
				this.a = a !== undefined ? a : this.a;
				
				if( animate === true ){
					this.$.animate( { 
						left:	x,
						top:	y,
						width:	w,
						height:	h
					}, 250 , this.animeComplete );					
				} else {
					this.node.update( x, y, w, h );
					this._resizeBalloon();
				};
			},
			animate : function ( _x, _y, _w, _h, _a ){
				this.resize( _x, _y, _w, _h, _a, true );
			},
			destroy : function(){
				this.$.stop().remove();
				this.balloon.destroy();
				this.node.remove();
				
				this.destroy = null;
			}
		}
	);

/* --------------------------------------------------------------------------------------------
 * PANEL_ELEMENT_CONTROL
 *  - mouseEventListener
 */
	var PANEL_ELEMENT_CONTROL = ( function(){
		var	elmContainer   = null,
			currentElement = null,
			node           = null,
			panelX, panelY, panelW, panelH,
			startX, startY;
	/*
	 * append, remove, replace
	 * 
	 * panelElement には、z-position と dom-index がある。
	 *   z-position は 表示上の位置。大きいほど前に表示される（ z-index）
	 *   dom-index は 意味上の順番。htmlタグの登場順で、検索結果や音声読み上げブラウザで正しく意味が取れる順番。
	 * 
	 * editerでは、実際には z-index は使わず、htmlの順序で前後を表現する。
	 * dom-index は、数値のみ保持して、投稿時にpanelElementを適宜に並び替える。
	 * 
	 * append panelElement
	 * 1. 新しい panelElement の z-position を得る
	 * 2. ｚ の同じ panelElementを見つけ、その前に加える。または一番先頭へ。（PANEL_ELEMENT_ARRAY）
	 *    zが大きいほど、PANEL_ELEMENT_ARRAYの先頭へ。
	 * 3. dom位置は、PANEL_ELEMENT_ARRAY とは反対に、前のものほど後ろへ。
	 * 
	 * 
	 * remove panelElement
	 * 1. remove
	 * 2. renumber z
	 */
		function onFadeOut(){
			this.parentNode.removeChild( this );
		};
		/*
		 * PANEL_ELEMENT_ARRAY の順番を基準に、zの再計算
		 * jqElmの並び替え。
		 */
		function renumber(){
			var _panelElement, jqElm, jqAfter,
				i = PANEL_ELEMENT_ARRAY.length;
			for( ; i; ){
				_panelElement = PANEL_ELEMENT_ARRAY[ --i ];
				jqElm = _panelElement.$;
				!jqAfter && elmContainer.appendChild( jqElm.get( 0 ) );
				jqAfter  && jqAfter.before( jqElm );
				if( phase === 1 ){
					_panelElement.z = i;
					_panelElement.node.nodeIndex( i );
				};
				jqAfter = jqElm;
			};
		};
		function onTextInput( _panelElement ){
			PANEL_ELEMENT_CONTROL.add( _panelElement );
			_panelElement.animate( undefined, undefined, _panelElement.w, _panelElement.h );
			HISTORY_CONTROL.saveState( PANEL_ELEMENT_CONTROL.restore, [ false, _panelElement ], [ true, _panelElement ], true, PANEL_ELEMENT_CONTROL );
		};
		
		return {
			id : 'PANEL_ELEMENT_CONTROL',
			node : null,
			init : function(){
				elmContainer = document.getElementById( 'comic-element-container' );
				node         = PANEL_ELEMENT_CONTROL.node = eventRoot.createNode( elmContainer, true, false );
				node.nodeIndex( 0 );
				delete PANEL_ELEMENT_CONTROL.init;
			},
			open : function(){
				delete PANEL_ELEMENT_CONTROL.open;
			},
			close : function(){
				var panelElm;
				while( panelElm = PANEL_ELEMENT_ARRAY.shift() ){
					panelElm.destroy && panelElm.destroy();
				};
			},
			add : function( _panelElement ){
				var z = Type.isFinite( _panelElement.z ) === true ? _panelElement.z : -1,
					i = PANEL_ELEMENT_ARRAY.length,
					_jqElm = _panelElement.$.stop().css( {
						filter:		'',
						opacity:	''
					}),
					j;
				if( z < 0 ){
					PANEL_ELEMENT_ARRAY.push( _panelElement );
				} else {
					for( ; i; ){
						if( PANEL_ELEMENT_ARRAY[ --i ].z < z ){
							j = i;
							break;
						};
					};
					if( j !== i ){
						PANEL_ELEMENT_ARRAY.unshift( _panelElement );
					} else {
						PANEL_ELEMENT_ARRAY.splice( i + 1, 0, _panelElement );
					};
				};
				renumber();
				_jqElm.fadeIn();
				_panelElement.node.disabled( false );	
			},
			remove : function( _panelElement ){
				for( var i = PANEL_ELEMENT_ARRAY.length; i; ){
					if( PANEL_ELEMENT_ARRAY[ --i ] === _panelElement ){
						console.log( PANEL_ELEMENT_ARRAY.length );
						PANEL_ELEMENT_ARRAY.splice( i, 1 );
						_panelElement.node.disabled( true );
						_panelElement.node.nodeIndex( PANEL_ELEMENT_ARRAY.length );
						renumber();
						_panelElement.$.stop().css( {
							filter:		'',
							opacity:	''
						}).fadeOut( onFadeOut );
						if( currentElement === _panelElement ) currentElement = null;
						return;
					};
				};
			},
			/* history */
			restore : function( isAppend, panelElement ){
				PANEL_ELEMENT_CONTROL[ isAppend === true ? 'add' : 'remove' ]( panelElement );
			},
			replace: function( _panelElement, goForward ){
				// PANEL_ELEMENT_ARRAYの再構築
				var l = PANEL_ELEMENT_ARRAY.length,
					i,
					j = l;
				for( ; j; ){
					if( PANEL_ELEMENT_ARRAY[ --j ] === _panelElement ){
						i = j;
						break;
					};
				};
				if( i !== j ) return false;
				if( goForward === true ){
					if( i === 0 ) return false;
					PANEL_ELEMENT_ARRAY.splice( i, 1 );
					PANEL_ELEMENT_ARRAY.splice( i + 1, 0, _panelElement );
				} else {
					if( i === l - 1 ) return false;
					PANEL_ELEMENT_ARRAY.splice( i, 1 );
					PANEL_ELEMENT_ARRAY.splice( i - 1, 0, _panelElement );
				};
				renumber( true );
				return true;
			},
			onPanelResize : function ( _panelX, _panelY, _panelW, _panelH, isResizerTopAction ){
			/*
			 * リサイズが、ResizerTopによって行われた場合、panelElementのyを動かして見かけ上動かないようにする。
			 */					
				if( isResizerTopAction === true ){
					var	_shiftX = _panelW - panelW,
						_shiftY = _panelH - panelH;
					for( var i = PANEL_ELEMENT_ARRAY.length; i; ){
						PANEL_ELEMENT_ARRAY[ --i ].shift( _shiftX, _shiftY );
					};
				};
				node.update( panelX = _panelX, panelY = _panelY, panelW = _panelW, panelH = _panelH );
			},
			createImageElement: function( data ){
				if( Type.isObject( data ) === false ){
					PremiumSatge.boot( 1, PANEL_ELEMENT_CONTROL.onImageSelect );
				} else {
					PANEL_ELEMENT_CONTROL.onImageSelect( data, true );
				};
			},
			onImageSelect: function( data, isPanelPictureData ){
				var _panelElement;
				if( isPanelPictureData !== true ){
					_panelElement = new ImageElementClass( {
						picture : data.picture,
						x       : Math.floor( panelW / 2 - data.width / 2 ),
						y       : Math.floor( panelH / 2 - data.height / 2 ),
						z       : -1,
						t       : 0,
						width   : 1,
						height  : 1
					});
					_panelElement.init();
					PANEL_ELEMENT_CONTROL.add( _panelElement );
					_panelElement.animate( undefined, undefined, Math.abs( data.picture.original_picture.width ), Math.abs( data.picture.original_picture.height ) );
				} else {
					_panelElement = new ImageElementClass( data );
					_panelElement.init();
					PANEL_ELEMENT_CONTROL.add( _panelElement );
				};
				HISTORY_CONTROL.saveState( PANEL_ELEMENT_CONTROL.restore, [ false, _panelElement ], [ true, _panelElement ], true );
			},
			createTextElement: function( data ){
				var _panelElement;
				if( Type.isObject( data ) === false ){
					data = {
						balloon_template_id:1,
						size:               1,
						tail:               90,
						x:					Math.floor( panelW / 2 - 100 + Math.random() * 10 ),
						y:                  Math.floor( panelH / 2 - 100 + Math.random() * 10 ),
						z:                  -1,
						t:                  0,
						width:              200,
						height:             200,
						speeches_attributes: {
							text1: {
								content:    'Hello'
							}
						}
					};
					_panelElement = new TextElementClass( data );
					_panelElement.init();
					TextEditor.boot( PANEL_CONTROL.x, PANEL_CONTROL.y, _panelElement, onTextInput );
				} else {
					_panelElement = new TextElementClass( data );
					_panelElement.init();
					onTextInput( _panelElement );
				};
			}
		};
	})();

	/*
	 * end of PANEL_ELEMENT_CONTROL
	 */

	function centering(){
		app.onPaneResize( windowW, windowH );
	};

	/* grobal method */
	
	this.MIN_WIDTH   = 320;
	this.MIN_HEIGHT  = 320;
	this.onInit = function(){
		app.rootElement.id = 'editor';
		app.rootElement.innerHTML = [
			'<div id="grid" style="display:none;"></div>',
			'<div id="comic-element-container"></div>',
			'<div id="whiteGlass-container">',
				'<div id="whiteGlass-top"></div>',
				'<div id="whiteGlass-left"></div>',
				'<div id="whiteGlass-right"></div>',
				'<div id="whiteGlass-bottom"></div>',
			'</div>',
			'<div id="panel-tools-container">',
				'<div id="panel-resizer-top">▲</div>',
				'<div id="panel-resizer-bottom">▼</div>',
			'</div>',
			'<div id="window-container"></div>',
			'<div id="comic-element-resizer-container">',
				'<div id="comic-element-resizer-container-inner">',
					'<div id="comic-element-consol-wrapper">',
						'<div id="comic-element-consol-container" class="clearfix">',
							'<div class="comic-element-consol-item">',
								'<div id="comic-element-x">',
									'<span class="comic-element-attribute-label">x:</span>',
									'<span id="comic-element-x-value" class="comic-element-attribute-value editable-value">0</span>',
								'</div>',
								'<div id="comic-element-y">',
									'<span class="comic-element-attribute-label">y:</span>',
									'<span id="comic-element-y-value" class="comic-element-attribute-value editable-value">0</span>',
								'</div>',
							'</div>',
							'<div class="comic-element-consol-item">',						
								'<div id="comic-element-z">',
									'<span class="comic-element-attribute-label">z:</span>',
									'<span id="comic-element-z-value" class="comic-element-attribute-value editable-value">0</span>',
								'</div>',
								'<div id="comic-element-a">',
									'<span id="comic-element-a-value" class="comic-element-attribute-value editable-value">0</span>',
									'<span class="comic-element-attribute-label">°</span>',
								'</div>',
							'</div>',
							'<div class="comic-element-consol-item">',
								'<div id="comic-element-w">',
									'<span class="comic-element-attribute-label">w:</span>',
									'<span id="comic-element-w-value" class="comic-element-attribute-value editable-value">0</span>',
								'</div>',
								'<div id="comic-element-h">',
									'<span class="comic-element-attribute-label">h:</span>',
									'<span id="comic-element-h-value" class="comic-element-attribute-value editable-value">0</span>',
								'</div>',
							'</div>',
							'<div class="comic-element-consol-item">',
								'<!-- <div id="comic-element-keep-aspect"></div> -->',
								'<div id="comic-element-w-percent">',
									'<span id="comic-element-w-percent-value" class="comic-element-attribute-value editable-value">0</span>',
									'<span class="comic-element-attribute-label">%</span>',
								'</div>',
								'<div class="comic-element-consol-item" id="comic-element-h-percent">',
									'<span id="comic-element-h-percent-value" class="comic-element-attribute-value editable-value">0</span>',
									'<span class="comic-element-attribute-label">%</span>',
								'</div>',
							'</div>',
			                '<div class="comic-element-consol-item-small">',
								'<div class="button" id="change-image-button">change</div>',
							'</div>',
							'<div class="comic-element-consol-item-small">',
								'<div class="button" id="layer-back-button">back</div>',
							'</div>',
							'<div class="comic-element-consol-item-small">',
								'<div class="button" id="delete-button">delete</div>',
							'</div>',
							'<div class="comic-element-consol-item-small">',
								'<div class="button" id="layer-forward-button">forward</div>',
							'</div>',
							'<div class="comic-element-consol-item-small">',
								'<div class="button" id="edit-text-button">edit</div>',
							'</div>',
							'<!-- <div class="comic-element-consol-item-small">',
								'<div class="button" id="hide-text-tail-button">tail</div>',
							'</div> -->',
						'</div>',
					'</div>',				
					'<div class="comic-element-resizer" id="comic-element-resizer-top"></div>',
					'<div class="comic-element-resizer" id="comic-element-resizer-left"></div>',
					'<div class="comic-element-resizer" id="comic-element-resizer-right"></div>',
					'<div class="comic-element-resizer" id="comic-element-resizer-bottom"></div>',
					'<div class="comic-element-resizer" id="comic-element-resizer-top-left"></div>',
					'<div class="comic-element-resizer" id="comic-element-resizer-top-right"></div>',
					'<div class="comic-element-resizer" id="comic-element-resizer-bottom-left"></div>',
					'<div class="comic-element-resizer" id="comic-element-resizer-bottom-right"></div>',
					'<div id="comic-element-consol-pushout-wrapper">',
						'<div id="comic-element-consol-pushout-tail"></div>',
						'<div id="comic-element-consol-pushout-inner"></div>',
					'</div>',
					'<div id="balloon-tail-mover"></div>',
				'</div>',	
			'</div>',
			'<div id="menu-bar"></div>',
			
			'<div id="templete-container" style="display: none;">',
				'<div id="imgElementTemplete" class="comic-element-wrapper image-element"></div>',
				
				'<div id="textElementTemplete" class="comic-element-wrapper text-element">',
					'<img>',
					'<div class="speach">',
						'<div class="speach-inner">&nbsp;</div>',
					'</div>',
				'</div>',
				
				'<div id="textElementTempleteForOldIE" class="comic-element-wrapper text-element">',
					'<img>',
					'<div class="speach">',
						'<table><tr><td>&nbsp;</td></tr></table>',
					'</div>',
				'</div>',
				
				'<div id="imageGroupItemTemplete" class="image-group-item">',
					'<div class="image-group-item-title">img-title</div>',
				'</div>',
				
				'<div id="windowTemplete" class="window-wrapper">',
					'<div class="window-header">',
						'<div class="window-header-title">window title</div>',
						'<div class="window-close-button">x</div>',
					'</div>',
					'<div class="window-body"></div>',
					'<div class="window-footer">',
						'<div class="window-resize-button"></div>',
					'</div>',
				'</div>',
				
				'<div id="infomation-window">',
					'<div id="panel-background-information">',
						'<div id="bg-pattern"></div>',
						'<div id="select-bg-pattern-button">pattern</div>',
						'<div id="reset-bg-pattern-button">x</div>',
						'<div id="bg-color"></div>',
						'<div id="select-bg-color-button">color</div>',
						'<div id="reset-bg-color-button">x</div>',
						'<!-- <div id="bg-pattern-x"></div>',
						'<div id="bg-pattern-y"></div>',
						'<div id="bg-pattern-repeat-x"></div>',
						'<div id="bg-pattern-repeat-y"></div> -->',
					'</div>',
				'</div>',
				
				'<div id="toolbox-window">',
					'<div id="toolbox-add-image-button" class="button">add image</div>',
					'<div id="toolbox-add-text-button" class="button">add text</div>',
					'<div id="toolbox-edit-bg-button" class="button">edit bg</div>',
					'<div id="toolbox-switch-grid" class="button">grid</div>',
					'<div id="toolbox-popup-help-button" class="button">?</div>',
					'<div id="toolbox-post-button" class="button">post</div>',
				'</div>',
							
			'</div>'
		].join( '' );
		
		app.fetchCSS( pettanr.CONST.URL_PETA_APPS_CSS );
		eventRoot = app.getPointingDeviceEventTreeRoot();
		
		delete app.onInit;
	};
	this.onOpen = function( _w, _h, file ){
		// 表示奥の物から順に init() していく
		PANEL_ELEMENT_CONTROL.init();
		PANEL_CONTROL.init();
		// PANEL_RESIZER_BOTTOM.init();
		// PANEL_RESIZER_TOP.init();
		WINDOWS_CONTROL.init();
		MENU_BAR_CONTROL.init();
		
		// レイヤーにあまり関係ないモジュール	
		HISTORY_CONTROL.init();
		SAVE_CONTROL.init();
		GRID_CONTROL.init();
		WHITE_GLASS_CONTROL.init();
		PanelElementOperatorManager.init();
		
		comicID      = -1;
		panelID      = -1;
		panelTimming = -1;
		phase        = 0;
		
		var panelW, panelH,
			borderSize,
			fileData, panelElements, panelElm;

		if( FileAPI.isFileInstance( file ) === true ){
			if( Driver.isPettanrFileInstance( file ) === true ){
				if( file.getType() === FILE_TYPE.COMIC ){
					fileData = file.read();
					panelW   = fileData.width;
					panelH   = fileData.height;
					comicID  = fileData.id || -1;
				} else
				if( file.getType() === FILE_TYPE.PANEL ){
					fileData      = file.read();
					panelW        = fileData.width;
					panelH        = fileData.height;
					borderSize    = fileData.border;
					panelElements = fileData.elements;
					comicID       = fileData.comic ? fileData.comic.id || -1 : -1;
					panelID       = fileData.id || -1;
					panelTimming  = fileData.t  || -1;
				};
			} else {
			};
		} else {
		};
		
		// open() は各モジュールの init() 後に実施可能になる． 
		HISTORY_CONTROL.open();
		SAVE_CONTROL.open();
		WINDOWS_CONTROL.open();
		
		GRID_CONTROL.open();
		PANEL_CONTROL.open( panelW, panelH, borderSize );
		PanelElementOperatorManager.open();
		PANEL_ELEMENT_CONTROL.open();
		
		// last
		MENU_BAR_CONTROL.open();
		
		windowW = _w;
		windowH = _h;
		app.onPaneResize( _w, _h );
		

		if( Type.isArray( panelElements ) === true ){
			for( var i=0; i<panelElements.length; ++i ){
				panelElm = panelElements[ i ];
				if( panelElm.picture ){
					PANEL_ELEMENT_CONTROL.createImageElement( panelElm );
				} else
				if( panelElm.balloon_template_id ){
					PANEL_ELEMENT_CONTROL.createTextElement( panelElm );
				};
			};
		};
		
	/*
	 * centering
	 */
		app.addKeyEventListener( 'keydown', centering, 96, false, true );	// ctrl + 0
		app.addKeyEventListener( 'krydown', centering, 48, false, true );	// ctrl + 0
		MENU_BAR_CONTROL.EDIT.createOption( 'centering', 'ctrl + 0', centering, true, true, true);
		
		phase   = 1;

		delete app.onOpen;
	};
	this.onClose = function(){
		phase   = 2;
		HISTORY_CONTROL.close();
		
		WINDOWS_CONTROL.close();
		
		GRID_CONTROL.close();
		PANEL_CONTROL.close();
		
		PanelElementOperatorManager.close();
		PANEL_ELEMENT_CONTROL.close();
		
		// last
		MENU_BAR_CONTROL.close();
		
		phase = -1;
	};
	this.onPaneResize = function( _windowW, _windowH ){
		windowW = _windowW || windowW;
		windowH = _windowH || windowH;

		app.rootElement.style.height = _windowH + 'px';
		
		WINDOWS_CONTROL.onWindowResize( _windowW, _windowH );
		MENU_BAR_CONTROL.onWindowResize( _windowW, _windowH );
		PANEL_CONTROL.onWindowResize( _windowW, _windowH );
	};
}, false, true, 'Panel Editor', 'paneleditor', null, '#2D89F0' );

var FormApplicationHelper = function( app ){
	app.isUploading   = false;
	app.elmProgress   = null;
	app.elmUploader   = null;
	app.elmScript     = null;
	app.elmIframeWrap = null;
	app.elmIframe     = null;
	app.elmForm       = null;
	app.fetchScript = function(){
		app.elmProgress = document.getElementById( app.elmProgressID );
		
		if( !( app.elmUploader = document.getElementById( app.elmUploaderID ) ) ){
			app.elmUploader    = document.createElement( 'div' );
			app.rootElement.appendChild( app.elmUploader );
			app.elmUploader.id = app.elmUploaderID;
			if( app.hideUploader === true ){
				app.elmUploader.style.cssText = 'height:1px;line-height:1px;visibility:hidden;overflow:hidden;';
			};			
		};
		
		app.elmIframeWrap    = document.getElementById( app.iframeWrapID );
		app.elmScript        = document.createElement( 'script' );
		document.body.appendChild( app.elmScript );
		app.elmScript.type   = 'text\/javascript';
		app.elmScript.src    = app.scriptSrc;
		
		app.elmProgress.innerHTML = 'loading form.';
		
		app.addTimer( app.detectForm, 250 );
		
		delete app.fetchScript;
	};
	app.detectForm = function(){
		app.elmForm = app.elmUploader.getElementsByTagName( 'form' )[ 0 ];
		if( !app.elmForm ) return;
		app.removeTimer( app.detectForm );
		
		Util.createIframe( 'targetFrame', app.onCreateIframe );
		app.elmProgress.innerHTML = 'create iframe';
		delete app.detectForm;
	};
	app.onCreateIframe = function( _iframe ){
		app.elmIframeWrap.appendChild( _iframe );
		_iframe.className         = 'form-iframe';
		app.elmIframe             = _iframe;
		app.elmForm.target        = _iframe.name;
		app.elmProgress.innerHTML = '';
		app.onFormReady && app.onFormReady();
		
		delete app.onCreateIframe;
	};
	app.submit = function(){
		app.elmProgress.innerHTML = 'submit!';
		try {
			app.elmForm.submit();
			app.isUploading = true;
		} catch( e ){
			app.elmProgress.innerHTML = 'submit err..';
			app.submitError && app.submitError();
			return;
		};
		if( app.detectIframe ){
			app.elmIframe.onreadystatechange = app.detectIframe;
		} else {
			app.elmIframe.onload = app.onIframeUpdate;
		};
		app.elmProgress.innerHTML = 'uploading..';
		
		delete app.submit;
	};
	if( UA.isIE ){
		app.detectIframe = function(){
	        if ( this.readyState !== 'complete' ) return;
	        this.onreadystatechange = new Function();
	        this.onreadystatechange = null;
	        app.onIframeUpdate();
	        delete app.detectIframe;
		};
	};
	app.onIframeUpdate = function(){
		app.elmIframe.onload = null;
		try {
			console.log( ( app.elmIframe.contentWindow || app.elmIframe.contentDocument.parentWindow ).document.body.innerHTML );
			console.log( ( app.elmIframe.contentWindow || app.elmIframe.contentDocument.parentWindow )[ 'current_author' ] );
		} catch(e){
			
		};
		( app.elmIframe.contentWindow || app.elmIframe.contentDocument.parentWindow ).close();
		app.elmIframe = null;
		app.elmProgress.innerHTML = 'success!';
		app.isUploading = false;
		// app.submitSuccess && app.submitSuccess();
		delete app.onIframeUpdate;
	};
	app.destroyHelper = function(){
		app.elmUploader.parentNode.removeChild( app.elmUploader );
		app = null;
	};
};

var ComicConsole = gOS.registerApplication( function(){
	var elmHeader, elmProgress,
		windowW, windowH,
		inputTitle, inputW, inputH,
		eventRoot, node,
		comboboxVisible, // comboboxEditable,
		buttonSubmit, buttonCancel,
		app         = this;

	function clickOK(){
		if( !app.elmForm || !app.elmIframe || app.isUploading === true ) return false;
		// validate

		var _inputList = app.elmForm.getElementsByTagName( 'input' ),
			_input, _name;
		for( var i = _inputList.length; i; ){
			_input = _inputList[ --i ];
			_name = _input.name;
			if( _name === 'comic[title]' ){
				_input.value = inputTitle.value();
			} else
			if( _name === 'comic[width]' ){
				_input.value = inputW.value();
			} else
			if( _name === 'comic[height]' ){
				_input.value = inputH.value();
			};
		};
		var _selectList = app.elmForm.getElementsByTagName( 'select' ),
			_select, _optionList;
		for( i = _selectList.length; i; ){
			_select = _selectList[ --i ];
			_name = _select.name;
			_optionList = _select.getElementsByTagName( 'option' )
			if( _name === 'comic[visible]' ){
				_select.selectedIndex = comboboxVisible.selectIndex();
			}/* else
			if( _name === 'comic[editable]' ){
				_select.selectedIndex = comboboxEditable.selectIndex();
			} */;
		};
		buttonSubmit.enabled( false );
		app.submit();
	};
	function clickCancel(){
		if( app.isUploading === true ) return false;
		ComicConsole.shutdown();
	};

	/* grobal method */
	this.MIN_WIDTH   = 320;
	this.MIN_HEIGHT  = 320;
	this.onInit = function(){
		app.rootElement.id = 'comic-console-wrapper';
		app.rootElement.className = 'console-wrapper';
		app.rootElement.innerHTML = [
			'<div id="comic-console-header" class="console-header">Create New Comic</div>',
			'<div id="comic-console" class="console-inner">',
				'<div id="comic-console-title" class="field">',
					'<span class="field-label">Title:</span>',
					'<span id="comic-console-title-value" class="comic-console-value editable-value">No Title</span>',
				'</div>',
				'<div id="comic-console-width" class="field">',
					'<span class="field-label">Default Width:</span>',
					'<span id="comic-console-width-value" class="comic-console-value editable-value">300</span>',
				'</div>',
				'<div id="comic-console-height" class="field">',
					'<span class="field-label">Default Height:</span>',
					'<span id="comic-console-height-value" class="comic-console-value editable-value">200</span>',
				'</div>',
				'<div id="comic-console-visible" class="field">',
					'<span class="field-label">Visible:</span>',
					'<span id="comic-console-visible-value" class="comic-console-value combobox"></span>',
				'</div>',
				//'<div id="comic-console-editable" class="field">',
				//	'<span class="field-label">Editable:</span>',
				//	'<span id="comic-console-editable-value" class="comic-console-value combobox"></span>',
				//'</div>',
				'<div class="console-button-container">',
					'<div id="comic-console-post-button" class="button console-submit-button">create</div>',
					'<div id="comic-console-cancel-button" class="button console-cancel-button">cancel</div>',
				'</div>',
				'<div id="comic-console-progress" class="console-progress">&nbsp;</div>',
				'<div id="comic-console-iframe-container"></div>',
			'</div>'
		].join( '' );
		
		app.fetchCSS( pettanr.CONST.URL_PETA_APPS_CSS );
		eventRoot = app.getPointingDeviceEventTreeRoot();
		
		delete app.onInit;
	};
	this.elmProgressID   = 'comic-console-progress';
	this.elmUploaderID   = 'newcomic';
	this.iframeWrapID    = 'comic-console-iframe-container';
	this.elmIframeName   = 'targetFrameCreateComic'
	this.scriptSrc       = pettanr.CONST.CREATE_COMIC_JS;
	this.hideUploader    = true;
	
	FormApplicationHelper( this );
	
	this.onOpen = function( w, h ){
		node             = eventRoot.createNode( app.rootElement, true, true );
		
		var ui           = app.createUIGroup( node );
		
		inputTitle       = ui.createInputText( document.getElementById( 'comic-console-title') );
		inputW           = ui.createInputText( document.getElementById( 'comic-console-width') );
		inputH           = ui.createInputText( document.getElementById( 'comic-console-height') );
		comboboxVisible  = ui.createCombobox( document.getElementById( 'comic-console-visible') );
		// comboboxEditable = ui.createCombobox( document.getElementById( 'comic-console-editable') );
		buttonSubmit     = ui.createButton( document.getElementById( 'comic-console-post-button'), clickOK );
		buttonCancel     = ui.createButton( document.getElementById( 'comic-console-cancel-button'), clickCancel );
		
		app.onPaneResize( w, h );
		app.fetchScript();
		delete app.onOpen;
	};
	this.onPaneResize = function( w, h ){
		windowW = w;
		windowH = h;
		node.update( w / 2 - node.width() / 2, h / 2 - node.height() / 2 );
	};
	this.onClose = function(){
		app.destroyHelper();
		app = inputTitle = inputW = inputH = comboboxVisible = buttonSubmit = buttonCancel = null;
	};
	this.onFormReady     = function(){
		var selectList = app.elmForm.getElementsByTagName( 'select' ),
			select,
			j, m,
			optionList, option;
		for( var i=0, l=selectList.length; i<l; ++i ){
			select = selectList[ i ];
			optionList = select.getElementsByTagName( 'option' );
			for( j=0, m=optionList.length; j<m; ++j ){
				option = optionList[ j ];
				if( select.name === 'comic[visible]' ){
					comboboxVisible.createOption( option.innerHTML, option.value, option.selected );
				}/* else
				if( select.name === 'comic[editable]' ){
					comboboxEditable.createOption( option.innerHTML, option.value, option.selected );
				}*/;
			};
		};
		inputTitle.focus();
		
		node.mesure();
		app.onPaneResize( windowW, windowH );
		
		delete app.onFormReady;
	};
	this.submitError = function(){
		app.addTimer( clickCancel , 5000, true );
	};
	this.submitSuccess = function(){
		app.addTimer( clickCancel , 5000, true );
	};
}, true, true, 'Comic Console', 'comicConsole', null, '#D44A26' );

var UploadConsole = gOS.registerApplication( function(){
	var windowW, windowH,
		eventRoot, node, nodeForm,
		buttonSubmit, buttonCancel,
		elmFile,
		app = this;

	function clickOK(){
		if( !app.elmForm || !app.elmIframe || app.isUploading === true ) return false;
		if( elmFile.value.length === 0 ) return false;
		app.submit();
		buttonSubmit.enabled( false );
		return false;
	};
	function clickCancel(){
		if( app.isUploading === true ) return false;
		UploadConsole.shutdown();
		return false;
	};

	/* grobal method */
	this.MIN_WIDTH   = 320;
	this.MIN_HEIGHT  = 320;
	this.onInit = function(){
		app.rootElement.id = 'upload-console-wrapper';
		app.rootElement.className = 'console-wrapper';
		app.rootElement.innerHTML = [
			'<div id="upload-console-header" class="console-header">Upload Picture</div>',
			'<div id="upload-console" class="console-inner">',
				'<div id="upload-console-uiform"></div>',
				'<div class="console-button-container">',
					'<div id="upload-console-post-button" class="button console-submit-button">upload</div>',
					'<div id="upload-console-cancel-button" class="button console-cancel-button">cancel</div>',
				'</div>',
				'<div id="upload-console-progress" class="console-progress">&nbsp;</div>',
				'<div id="upload-console-iframe-container"></div>',
			'</div>'
		].join( '' );
		
		app.fetchCSS( pettanr.CONST.URL_PETA_APPS_CSS );
		
		eventRoot = app.getPointingDeviceEventTreeRoot();
		document.body.appendChild( Util.pullHtmlAsTemplete( '<div id="uploader"></div>' ) );
		delete app.onInit;
	};
	this.elmProgressID   = 'upload-console-progress';
	this.elmUploaderID   = 'uploader';
	this.iframeWrapID    = 'upload-console-iframe-container';
	this.elmIframeName   = 'targetFrameUpload';
	this.scriptSrc       = pettanr.CONST.UPLOAD_PICTURE_JS;
	this.hideUploader    = false;
	FormApplicationHelper( this );
	this.onOpen = function( w, h ){
		node             = eventRoot.createNode( app.rootElement, true, true );
		nodeForm         = node.createNode( document.getElementById( 'upload-console-uiform' ), false, true );
		var ui           = app.createUIGroup( node );
		
		buttonSubmit     = ui.createButton( document.getElementById( 'upload-console-post-button' ), clickOK );
		buttonCancel     = ui.createButton( document.getElementById( 'upload-console-cancel-button' ), clickCancel );

		app.onPaneResize( w, h );
		app.fetchScript();
	};
	this.onPaneResize = function( w, h ){
		windowW = w;
		windowH = h;
		node.update( w / 2 - node.width() / 2, h / 2 - node.height() / 2 );
	};
	this.onClose = function(){
		app.destroyHelper();
		app = elmFile = buttonSubmit = buttonCancel = null;
	};
	this.onFormReady = function(){
		var elmForm    = app.elmForm,
			_inputList = elmForm.getElementsByTagName( 'input' ),
			_input;
		for( var i = _inputList.length; i; ){
			_input = _inputList[ --i ];
			if( _input.type === 'file' ){
				elmFile = _input;
			} else
			if( _input.type === 'submit' ){
				_input.style.display = 'none';
			};
		};
		
		app.createUIForm( nodeForm, elmForm );
		node.mesure();
		node.mesureChildren();
		app.onPaneResize( windowW, windowH );
		delete app.onFormReady;
	};
	this.submitError = function(){
		app.addTimer( clickCancel , 5000, true );
	};
	this.submitSuccess = function(){
		app.addTimer( clickCancel , 5000, true );
	};
}, true, true, 'Upload Console', 'uploadConsole', null, '#01A31C' );

var ArtistConsole = gOS.registerApplication( function(){
	var windowW, windowH,
		eventRoot, node,
		elmName, elmLicense,
		inputName, inputLicense,
		buttonSubmit, buttonCancel,
		app = this;

	function clickOK(){
		if( !app.elmForm || !app.elmIframe || app.isUploading === true ) return false;
		inputUpdate();
		app.submit();
		buttonSubmit.enabled( false );
		return false;
	};
	function clickCancel(){
		if( app.isUploading === true) return false;
		ArtistConsole.shutdown();
		return false;
	};
	function inputUpdate( v ){
		elmName.value    = inputName.value();
		elmLicense.value = inputLicense.value();
	};

	/* grobal method */
	this.MIN_WIDTH   = 320;
	this.MIN_HEIGHT  = 320;
	this.onInit = function(){
		app.rootElement.id = 'artist-console-wrapper';
		app.rootElement.className = 'console-wrapper';
		app.rootElement.innerHTML = [
			'<div id="artist-console-header" class="console-header">Register Artist</div>',
			'<div id="artist-console" class="console-inner">',
				'<div id="artist-console-name" class="field">',
					'<span class="field-label">Name:</span>',
					'<span id="artist-console-name-value" class="comic-console-value editable-value">artist name here.</span>',
				'</div>',
				'<div id="artist-console-license" class="field">',
					'<span class="field-label">License:</span>',
					'<span id="artist-console-license-value" class="comic-console-value editable-value">license here.</span>',
				'</div>',
				'<div class="console-button-container">',
					'<div id="artist-console-post-button" class="button console-submit-button">register</div>',
					'<div id="artist-console-cancel-button" class="button console-cancel-button">cancel</div>',
				'</div>',
				'<div id="artist-console-progress" class="console-progress">&nbsp;</div>',
				'<div id="register" style="display:none;"></div>',
				'<div id="artist-console-iframe-container"></div>',
			'</div>'
		].join( '' );
		
		app.fetchCSS( pettanr.CONST.URL_PETA_APPS_CSS );
		eventRoot = app.getPointingDeviceEventTreeRoot();
		
		delete app.onInit;
	};
	this.elmProgressID   = 'artist-console-progress';
	this.elmUploaderID   = 'register';
	this.iframeWrapID    = 'artist-console-iframe-container';
	this.elmIframeName   = 'targetFrameArtistRegister'
	this.scriptSrc       = pettanr.CONST.REGISTER_ARTIST_JS;
	this.hideUploader    = false;
	FormApplicationHelper( this );
	this.onFormReady     = function(){
		var _inputList = app.elmForm.getElementsByTagName( 'input' ),
			_input;
		for( var i = _inputList.length; i; ){
			_input = _inputList[ --i ];
			if( _input.type === 'submit' ){
				_input.style.display = 'none';
			};
			if( _input.name === 'artist[name]' ){
				elmName = _input;
			};
			if( _input.name === 'artist[default_license_id]' ){
				elmLicense = _input;
			};
		};
		
		node.mesure();
		app.onPaneResize( windowW, windowH );
		node.mesureChildren();
		
		delete app.onFormReady;
	};
	this.submitError = function(){
		app.addTimer( clickCancel , 5000, true );
	};
	this.submitSuccess = function(){
		app.addTimer( clickCancel , 5000, true );
	};
	this.onOpen = function( w, h ){
		node             = eventRoot.createNode( app.rootElement, true, true );
		var ui           = app.createUIGroup( node );
		
		inputName        = ui.createInputText( document.getElementById( 'artist-console-name' ), inputUpdate );
		inputLicense     = ui.createInputText( document.getElementById( 'artist-console-license' ), inputUpdate );
		buttonSubmit     = ui.createButton( document.getElementById( 'artist-console-post-button' ), clickOK );
		buttonCancel     = ui.createButton( document.getElementById( 'artist-console-cancel-button' ), clickCancel );

		app.onPaneResize( w, h );
		app.fetchScript();
	};
	this.onPaneResize = function( w, h ){
		windowW = w;
		windowH = h;
		//app.rootElement.style.cssText = [
		//	'left:', Math.floor( ( _w - app.rootElement.offsetWidth  ) /2 ), 'px;',
		//	'top:',  Math.floor( ( _h- app.rootElement.offsetHeight ) /2 ), 'px;'
		//].join( '' );
		node.update( w / 2 - node.width() / 2, h / 2 - node.height() / 2 );
	};
	this.onClose = function(){
		app.destroyHelper();
		app = buttonSubmit = buttonCancel = null;
	};
}, true, true, 'Artist Console', 'artistConsole', null, '#FFC40D' );

var PanelConsole = gOS.registerApplication( function(){
	var windowW, windowH,
		eventRoot, node, inputData,
		comboboxPublish, buttonSubmit, buttonClose,
		elmInput,
		app         = this,
		model       = null;
		
	/*
	 * upload ボタンが押されたらまず iframe をつくる．
	 */
	function clickOK(){
		if( !app.elmForm || !app.elmIframe || app.isUploading === true ) return false;
		inputData.value();
		app.submit();
		buttonSubmit.enabled( false );
		return false;
	}

	function clickCancel(){
		if( app.isUploading === true ) return false;
		PanelConsole.shutdown();
		return false;
	};
	function publishUpdate(){
		if( model ){
			model.publish( comboboxPublish.selectIndex() === 1 );
			elmInput.value = model.getJsonPostString().replace( /\n/g, '' );
			inputData.value( elmInput.value );		
		} else {
			elmInput.value = inputData.value();
		};
	};

	/* grobal method */
	this.MIN_WIDTH   = 320;
	this.MIN_HEIGHT  = 320;
	this.onInit = function(){
		app.rootElement.id = 'panel-console-wrapper';
		app.rootElement.className = 'console-wrapper';
		app.rootElement.innerHTML = [
			'<div id="panel-console-header" class="console-header">Create New Panel (dev)</div>',
			'<div id="panel-console" class="console-inner">',
				'<div id="panel-console-data" class="field">',
					'<span class="field-label">POST DATA:</span>',
					'<span id="panel-console-data-value" class="comic-console-value editable-value">panel json here.</span>',
				'</div>',
				'<div id="panel-console-publish" class="field">',
					'<span class="field-label">Publish:</span>',
					'<span id="panel-console-publish-value" class="combobox"></span>',
				'</div>',
				'<div class="console-button-container">',
					'<div id="panel-console-post-button" class="button console-submit-button">post</div>',
					'<div id="panel-console-cancel-button" class="button console-cancel-button">cancel</div>',
				'</div>',
				'<div id="panel-console-progress" class="console-progress">&nbsp;</div>',
				'<div id="newpanel" style="display:none;"></div>',
				'<div id="panel-console-iframe-container"></div>',
			'</div>'
		].join( '' );

		app.fetchCSS( pettanr.CONST.URL_PETA_APPS_CSS );
		eventRoot = app.getPointingDeviceEventTreeRoot();

		delete app.onInit;
	};
	this.elmProgressID   = 'panel-console-progress';
	this.elmUploaderID   = 'newpanel';
	this.iframeWrapID    = 'panel-console-iframe-container';
	this.elmIframeName   = 'targetFrameNewPanel';
	this.scriptSrc       = pettanr.CONST.CREATE_PANEL_JS;
	this.hideUploader    = false;
	FormApplicationHelper( this );

	this.onOpen = function( w, h, _model ){
		node    = eventRoot.createNode( app.rootElement, true, true );
		var ui  = app.createUIGroup( node ),
			elm = document.getElementById( 'panel-console-publish' );
			
		inputData        = ui.createInputText( document.getElementById( 'panel-console-data' ), publishUpdate );
		
		if( _model ){
			comboboxPublish = ui.createCombobox( elm, publishUpdate );
			comboboxPublish.createOption( 'only me', '0', _model.publish() === false );
			comboboxPublish.createOption( 'publish', '1', _model.publish() === true );
			model = _model;
		} else {
			elm.parentNode.removeChild( elm );
		};
		
		buttonSubmit     = ui.createButton( document.getElementById( 'panel-console-post-button' ), clickOK );
		buttonClose      = ui.createButton( document.getElementById( 'panel-console-cancel-button' ), clickCancel );
		
		app.onPaneResize( w, h );
		app.fetchScript();
	};
	this.onPaneResize = function( w, h ){
		windowW = w;
		windowH = h;
		node.update( w / 2 - node.width() / 2, h / 2 - node.height() / 2 );
	};
	this.onClose = function(){
		app.destroyHelper();
		model && model.destroy();
		app = model = comboboxPublish = buttonSubmit = buttonClose = elmInput = null;
	};
	this.onFormReady     = function(){
		var _inputList = app.elmForm.getElementsByTagName( 'input' ),
			_input;
		for( var i = _inputList.length; i; ){
			_input = _inputList[ --i ];
			if( _input.type === 'submit' ){
				_input.style.display = 'none';
			};
			if( _input.name === 'json' ){
				elmInput     = _input;
				publishUpdate();
			};
		};
		
		node.mesure();
		node.mesureChildren();
		app.onPaneResize( windowW, windowH );
		
		delete app.onFormReady;
	};
	this.submitError = function(){
		app.addTimer( clickCancel , 5000, true );
	};
	this.submitSuccess = function(){
		app.addTimer( clickCancel , 5000, true );
	};
}, true, true, 'Panel Console', 'panelConsole', null, '#603CBA' );

var Model = ( function(){
	
	var PanelModelClass = function( panel ){
		var comicID           = panel.comicID || -1,
			panelID           = panel.panelID || -1,
			panelTimming      = panel.panelTimming || -1,
			panelW            = panel.panelW,
			panelH            = panel.panelH,
			borderSize        = panel.borderSize,
			panelElementArray = panel.panelElementArray,
			publish           = panel.publish,
			timing            = 0;
			
		function getPanelElementByTiming(){
			var i, l = panelElementArray.length;
			while( timing < l * 2 ){
				for( i = 0; i < l; ++i ){
					if( timing === panelElementArray[ i ].timing ){
						// console.log( timing + ' , ' + panelElementArray[ i ].timing );
						++timing;
						return panelElementArray[ i ];
					};
				};
				++timing;
			};
			return null;
		};
		function panelElementToHtml( _panelElement, isAbsoluteUrl, isXHTML ){
			var rPic, url;
			if( _panelElement.type === 0 ){
				rPic = _panelElement.realPicture();
				url  = [ pettanr.CONST.RESOURCE_PICTURE_PATH, rPic.id, '.', rPic.ext ].join( '' );
				return [
					'<img ',
						'src="',        isAbsoluteUrl !== true ? url : Util.getAbsolutePath( url ), '" ',
						'width="',      _panelElement.w, '" ',
						'height="',     _panelElement.h, '" ',
						'style="',
							'left:',    _panelElement.x, 'px;',
							'top:',     _panelElement.y, 'px;',
							'z-index:', _panelElement.z, ';',
						'"',
					isXHTML !== true ? '>' : ' \/>'
				].join( '');				
			} else {
				url = pettanr.balloon.getBalloonUrl( _panelElement.w, _panelElement.h, _panelElement.a );
				return [
					'<img ',
						'src="',        isAbsoluteUrl !== true ? url : Util.getAbsolutePath( url ), '" ',
						'width="',      _panelElement.w, '" ',
						'height="',     _panelElement.h, '" ',
						'style="',									
							'left:',    _panelElement.x, 'px;',
							'top:',     _panelElement.y, 'px;',
							'z-index:', _panelElement.z, ';',
						'"',
					isXHTML !== true ? '>' : ' \/>',
					pettanr.LINE_FEED_CODE_TEXTAREA,
					'<div class="balloon" style="',
						'left:',        _panelElement.x, 'px;',
						'top:',         _panelElement.y, 'px;',
						'width:',       _panelElement.w, 'px;',
						'height:',      _panelElement.h, 'px;',
						'z-index:',     _panelElement.z,
					'"><span>', _panelElement.content, '<\/span>', '<\/div>'
						
				].join( '');				
			};
		};
		function getImageJsonGET( _imageElement ){
			var cr = pettanr.LINE_FEED_CODE_TEXTAREA,
				rPic = _imageElement.realPicture();
			return [
				'{', cr,
					'"resource_picture": {', cr,
						'"id": ',              rPic.id, ',', cr,
						'"ext": ',             '"', rPic.ext, '"', cr,
					'},', cr,
					'"x": ',                   _imageElement.x, ',', cr,
					'"y": ',                   _imageElement.y, ',', cr,
					'"z": ',                   _imageElement.z, ',', cr,
					'"width": ',               _imageElement.flipH * _imageElement.w, ',', cr,
					'"height": ',              _imageElement.flipV * _imageElement.h, ',', cr,
					'"t": ',                   timing, cr,
				'}'
			].join( '');
		};
		function imageToJson( _imageElement, t ){
			var cr = pettanr.LINE_FEED_CODE_TEXTAREA;
			return [
				'{', cr,
					'"picture_id": ', _imageElement.realPicture().id, ',', cr,
					'"x": ',          _imageElement.x, ',', cr,
					'"y": ',          _imageElement.y, ',', cr,
					'"z": ',          _imageElement.z + 1, ',', cr,
					'"t": ',          t, ',', cr,
					'"width": ',      _imageElement.flipH * _imageElement.w, ',', cr,
					'"height": ',     _imageElement.flipV * _imageElement.h, cr,
				'}'
			].join( '');
		};

		function balloonToJson( _textElement, t ){
			var cr = pettanr.LINE_FEED_CODE_TEXTAREA;
			return [
				'{', cr,
					'"speech_balloon_template_id": ', 1, ',', cr,
					'"classname": "Square",',
					'"z": ',                _textElement.z + 1, ',', cr,
					'"t": ',                t, ',', cr,
					'"settings": "{\'tail\':' + _textElement.a + '}",',
		            '"balloons_attributes": {', cr,
		                '"newb', t, '": {', cr,
							'"system_picture_id": ',  2, ',', cr,
    						'"caption": "alt text",', cr,
							'"x": ',        _textElement.x, ',', cr,
							'"y": ',        _textElement.y, ',', cr,
							'"width": ',    _textElement.w, ',', cr,
							'"height": ',   _textElement.h, cr,
						'}', cr,
					'},', cr,
					'"speeches_attributes": {', cr,
						'"news', t, '": {', cr,
    						'"content": "', _textElement.content, '",', cr,
							'"x": ',        Math.floor( _textElement.w * 0.16 ), ',', cr,
							'"y": ',        Math.floor( _textElement.w * 0.16 ), ',', cr,
							'"width": ',    Math.floor( _textElement.w * 0.66 ), ',', cr,
							'"height": ',   Math.floor( _textElement.h * 0.66 ), cr,
						'}', cr,
					'}', cr,
				'}'
			].join( '');
		};
			
		this.getJsonPostString = function(){
			timing = 0;
			
			var JSON_STRING_ARRAY = [],
				IMAGE_ARRAY       = [],
				BALLOON_ARRAY     = [],
				l = panelElementArray.length,
				_panelElement, n,
				cr = pettanr.LINE_FEED_CODE_TEXTAREA;
	
			while( IMAGE_ARRAY.length + BALLOON_ARRAY.length <= l ){
				_panelElement = getPanelElementByTiming();
				if( _panelElement === null) break;
				n = IMAGE_ARRAY.length + BALLOON_ARRAY.length;
				_panelElement.type === 0 ? 
					IMAGE_ARRAY.push( [ '"new', n, '": ', imageToJson( _panelElement, n ) ].join( '' ) ) :
					BALLOON_ARRAY.push( [ '"new', n, '": ', balloonToJson( _panelElement, n ) ].join( '' ) );
			};
			return [
				'{', cr,
					panelID !== -1 ? ( '"id": ' + panelID + ',' + cr ) : '',
					// comicID !== -1 ? ( '"comic_id": ' + comicID + ',' + cr ) : '',
				    '"width": ',            panelW, ',', cr,
				    '"height": ',           panelH, ',', cr,
				    '"border": ',           borderSize, ',', cr,
				    
				    // '"picture_id": 1,', cr,
					'"x": ',                0, ',', cr,
					'"y": ',                0, ',', cr,
					'"z": ',                1, ',', cr,
					panelTimming !== -1 ? ( '"t": ' + panelTimming + ',' + cr ) : '',
				    '"panel_pictures_attributes": {', cr,
				    	IMAGE_ARRAY.join( ',' + cr ), cr,
				    '},', cr,
				    '"speech_balloons_attributes": {', cr,
				    	BALLOON_ARRAY.join( ',' + cr ), cr,
				    '}', ',', cr,
				    '"publish": ',           ( publish === true ? 1 : 0 ), cr,
				'}'
			].join( '' );
		};
		this.getJsonGetString = function(){
			timing = 0;
			
			var JSON_STRING_ARRAY = [],
				ELEMENT_ARRAY     = [],
				l                 = panelElementArray.length,
				cr                = pettanr.LINE_FEED_CODE_TEXTAREA,
				_panelElement;
	
			while( ELEMENT_ARRAY.length <= l){
				_panelElement = getPanelElementByTiming();
				if( _panelElement === null ) break;
				 
				ELEMENT_ARRAY.push( _panelElement.type === 0 ? getImageJsonGET( _panelElement ) : balloonToJson( _panelElement ));
			};
			return [
				'{', cr,
					//'"panel": {', cr,
						//'"id": ',               panelID, ',', cr,
					    '"border": ',           borderSize, ',', cr,
					    // '"comic_id": ',         comicID, ',', cr,
					    // '"picture_id": 1,', cr,
						'"x": ',                0, ',', cr,
						'"y": ',                0, ',', cr,
						'"z": ',                1, ',', cr,
						// panelTimming !== -1 ? ( '"t": ' + panelTimming + ',' + cr ) : '',
					    '"width": ',            panelW, ',', cr,
					    '"height": ',           panelH, ',', cr,
					    '"elements": [', cr,
					    	ELEMENT_ARRAY.join( ',' + cr ), cr,
					    ']', cr,
					//'}', cr,
				'}'
			].join( '' );
		};
		this.getAsHtmlString = function( isAbsoluteUrl, isXHTML ){
			timing = 0;
			
			var HTML_ARRAY = [],
				l = panelElementArray.length,
				_panelElement;
	
			while( HTML_ARRAY.length < l ){
				_panelElement = getPanelElementByTiming();
				if( _panelElement === null) break;
				HTML_ARRAY.push( panelElementToHtml( _panelElement, isAbsoluteUrl, isXHTML ));
			};
	
			HTML_ARRAY.unshift(
				[
					'<div class="panel" ',
						'style="',
							'height:', panelH, 'px;',
							'background-color:', ';',
						'"',
					'>'
				].join( '')
			);		
			HTML_ARRAY.push( '</div>');
			
			return HTML_ARRAY.join( pettanr.LINE_FEED_CODE_TEXTAREA );
		};
		this.publish = function( v ){
			return publish = Type.isBoolean( v ) === true ? v : publish;
		};
		this.destroy = function(){
			panel = panelElementArray = null;
		};
	};
	
	return {
		createPanel: function( panelData ){
			return new PanelModelClass( panelData );
		}
	};
} )();


var OutputConsole = gOS.registerApplication( function(){
	var FORMAT_LIST = [ 'json[POST]', 'json[GET]', 'XML', 'HTML', 'XHTML', 'MT export', 'Blogger ATOM' ];
	var elmOutputArea,
		eventRoot, node,
		comboboxFormat, inputOption,
		buttonSubmit, buttonClose,
		windowW, windowH,
		comicID, panelID, panelTimming, panelW, panelH, borderSize, panelElementArray,
		app      = this,
		model    = null;
	
	function clickOK(){
		OutputConsole.shutdown();
	};

	function formatUpdate(){
		var i = comboboxFormat.selectIndex(),
			text = 'sorry...';
		// buttonSubmit.enabled( false );
		if( i === 0 ){
			text = model.getJsonPostString();
			// buttonSubmit.enabled( true );
		} else
		if( i === 1 ){
			text = model.getJsonGetString();
		} else
		if( i === 3 ){
			text = model.getAsHtmlString( false, false );
		} else {
			
		};
		elmOutputArea.value = text;
	};
	function clickClose(){
		OutputConsole.shutdown();
		return false;
	};
	
	function clickPost(){
		// PanelConsole.boot( elmOutputArea.value );
		return false;
	}
	
	/* grobal method */
	this.MIN_WIDTH   = 320;
	this.MIN_HEIGHT  = 320;
	this.onInit = function(){
		app.rootElement.id = 'output-console-wrapper';
		app.rootElement.className = 'console-wrapper';
		app.rootElement.innerHTML = [
			'<div id="output-console-header" class="console-header">Output Console</div>',
			'<div id="output-console" class="console-inner">',
				'<div id="output-console-format" class="field">',
					'<span class="field-label">Format:</span>',
					'<span id="output-console-format-value" class="output-console-value combobox"></span>',
				'</div>',
				'<div id="output-console-option" class="field">',
					'<span class="field-label">Options:</span>',
					'<span id="output-console-option-value" class="output-console-value editable-value">absolute-path</span>',
				'</div>',
				'<textarea id="output-area" readonly></textarea>',
				'<div id="output-console-close-button" class="button">close</div>',
			'</div>'
		].join( '' );

		app.fetchCSS( pettanr.CONST.URL_PETA_APPS_CSS );
		eventRoot = app.getPointingDeviceEventTreeRoot();

		delete app.onInit;
	};
	this.onOpen = function( _w, _h, _comicID, _panelID, _panelTimming, _panelW, _panelH, _borderSize, _panelElementArray ){
		elmOutputArea = document.getElementById( 'output-area' );
		
		node   = eventRoot.createNode( app.rootElement, true, true );
		var ui = app.createUIGroup( node );
		comboboxFormat = ui.createCombobox( document.getElementById( 'output-console-format' ), formatUpdate );
		
		for( var i=0; FORMAT_LIST[ 0 ]; ++i ){
			comboboxFormat.createOption( FORMAT_LIST.shift(), null, i === 0 );
		};
		inputOption    = ui.createInputText( document.getElementById( 'output-console-option' ), null );
		// buttonSubmit     = ui.createButton( document.getElementById( 'output-console-post-button' ), clickPost );
		buttonClose    = ui.createButton( document.getElementById( 'output-console-close-button' ), clickClose );
		
		app.onPaneResize( _w, _h );
		
		comboboxFormat.focus( true );
		
		model = Model.createPanel( {
			comicID           : _comicID,
			panelID           : _panelID,
			panelTimming      : _panelTimming,
			panelW            : _panelW,
			panelH            : _panelH,
			borderSize        : _borderSize,
			panelElementArray : _panelElementArray,
			publish           : true
		} );
		
		formatUpdate();
	};
	this.onPaneResize = function( w, h ){
		windowW = w;
		windowH = h;
		//app.rootElement.style.cssText = [
		//	'left:', Math.floor( ( _windowW - app.rootElement.offsetWidth  ) /2 ), 'px;',
		//	'top:',  Math.floor( ( _windowH - app.rootElement.offsetHeight ) /2 ), 'px;'
		//].join( '' );
		node.update( w / 2 - node.width() / 2, h / 2 - node.height() / 2 );
	};
	this.onClose = function(){
		elmOutputArea.value = '';
		model.destroy();
		elmOutputArea = comboboxFormat = inputOption = buttonSubmit = buttonClose = panelElementArray = model = null;
	};
}, true, false, 'Output Console', 'outputConsole', null, '#2D89F0' );

})( pettanr, gOS, window, document );
