/*
 *	Xpsオブジェクト生成
 *		2007.04.03 エラーメッセージ分離
 *		2013.04.02 外部フォーマット解析部分分離
 *
 *		2015.06.12 Xps及びMap関連オブジェクトをnas.配下に移動
 *
 */
/*
	Xpsオブジェクト初期化手順
		Xpsオブジェクトの新規作成
		コンストラクタ
	myXPS=new nas.Xps([layer count][,frame length]);

Xpsクラスオブジェクトコンストラクタ

引数は、省略可能。
省略時はレイヤ数０，フレーム数０
セルの初期値は全て""
マップの設定はなし(ダミーマップも参照していない)

このオブジェクトはフレーム優先。
フレームレートを変えるとコマうちが変わるのではなく、カットの継続時間が変わる。
アニメーターの振ったコマ打ち優先!(これでいいのかなぁ?)


		XPSオブジェクトの再初期化
	method	[object Xps].init([layer count][,frame length])
自分自身を再初期化する。
すべてのプロパティをリセット
指定されたレイヤ数とフレーム長で空の値のテーブルを作成する。
以前のデータは消去。new_XPSは、内部でこのメソッドを呼ぶ。

		MAPオブジェクトの参照メソッド
	method	[object Xps].getMap([object Map])
このメソッドに引数としてマップオブジェクト[省略不可]を与える。
マップファイルがない場合は、falseを戻す。
現在の使用には、明示的にダミーオブジェクトを与える。
戻り値は取り込み成功時に true 
マップオブジェクトを参照して得られるプロパティは、
レイヤ情報初期値群
タイトル・サブタイトル 初期値

		現在の継続時間を返す
	method	[object Xps].duration()
このメソッドは、プロパティに変更予定
		
		現在のカット尺を返す
	method	[object Xps].time()
このメソッドは、プロパティに変更予定

		カット尺をフレーム数で返す
	method	[object Xps].getTC(フレーム数)
暫定メソッド、消えそう


		テキスト形式データを読み込んでオブジェクトに反映
	method	[object Xps].readIN(テキストデータ)
init()と一体化した方が良いかも

		テキスト形式で出力
	method	[object Xps].toString(セパレータ)
そのうち拡張
と思っていたが、コンバータは別立てにしてXpsオブジェクトの汎用性を高めるが吉

	method	[object Xps].mkAEKey(レイヤID)
モードよっては不要ぽい
同上

メソッド一覧

Xps.newLayers= function(layerCount)://レイヤプロパティトレーラを作成して返す

*/
/*
	XpsStageオブジェクトコンストラクタ
ステージプロパティとして参照される
ステージのツリー構造はペアレントツリーをたどることで再構成される
各ステージ・ジョブは一連の識別名で分離される
ステージボディとしてストリームを配列で持つ

各識別子は制作単位内で一意であることを期待される

将来的に、実運用上ステージオブジェクトは親プロセスに問い合わせの上DBとリンクした親オブジェクトを受け取るケースの方が多い？
ただしカットごとのステージングは優先される
基本的には、カット制作ルートから組み上げたステージングで物事が決定される？
ステージ識別名称が新規に作成された場合は、親DBの更新が必要


ステージ.bodyの配列

*/
function XpsStage(myParent,myStage,myJob)
{
	this.body =myParent;//ステージ・ジョブストリームを記録する配列
	this.name   =  myStage;//String　ステージ識別名称（任意文字列）
	this.job   =  myJob;//String　ジョブ識別名称（任意文字列）
	this.stageIndex	= 0;//Int
	this.jobIndex = 0;//Int
}
/*
	XpsLayerオブジェクトコンストラクタ
	ラベルを与えて初期化
*/
function XpsLayer(myName,myOpt){
		this.name	=  myName	;//識別用タイムラインラベル
		this.sizeX	=   "640"	;//デフォルト幅 point
		this.sizeY	=   "480"	;//デフォルト高 point
		this.aspect	=     "1"	;//デフォルトのpixelAspect
		this.lot	="=AUTO="	;
		this.blmtd	=  "wipe"	;
		this.blpos	=   "end"	;
		this.option	=(!myOpt)? "timing":myOpt;
		this.link	=     "."	;
		this.parent	=     "."	;
}
 function getMapDefault(myOption){
 	if(myOption==undefined){myOption=this.option}
		switch (myOption){
			case "dialog":;
			case "sound":;
				return new XpsSound();
			break;
			case "camerawork":;
			case "camera":;
				return new XpsCamerawork();
			break;
			case "effect":;
			case "composit":;
				return new XpsComposit();
			break;
			case "timing":;
			default:;
				return new XpsReplacement();				
		}
	}

/*
	XpsTimelineTrackオブジェクトコンストラクタ
	タイムラインの各トラックとなるオブジェクト
	タイムライントラックのプロパティを保持する
	contentStreamを要求された場合は、ボディテーブルからコンテントストリームを生成して返す（オブジェクトメソッド）
	contentSectionsを要求された場合は、同様にセクションコレクションを生成して戻す（オブジェクトメソッド）
	トラックは、セクションで構成される
	ラベルとトラック種別を与えて初期化する
	ジオメトリを持たないオブジェクトもあるので
	タイムライントラックを抽象化するためのレイヤーとして機能させること
	現行のXPSLayerの代替となるのである程度の互換を考慮すること
*/
function XpsTimelineTrack(myName,myKind){
		this.id	=  myName	;//識別用タイムラインid(文字列)
		this.option	=(!myKind)? "timing":myKind;//timing/dialog/sound/camera/camerawork/effect/composit　のいずれか
		this.sizeX	=   "640"	;//デフォルト幅 point
		this.sizeY	=   "480"	;//デフォルト高 point
		this.aspect	=     "1"	;//デフォルトのpixelAspect
		this.lot	="=AUTO="	;//旧オブジェクト互換
		this.blmtd	=  "file"	;//旧オブジェクト互換
		this.blpos	=   "end"	;//旧オブジェクト互換
		this.link	=     "."	;
		this.parent	=     "."	;
		this.getDefaultValue	=	getMapDefault;//
//		this.getContentSections=getTimelineSections;
//		this.getCententStream=getTimelineStream;
//		this.getCentent=getTimelineStream;
}
/*
	XpsTimelineSectionCollection
	トラック・セクションオブジェクトのプロパティとなるセクショントレーラー配列
	セクションオブジェクトは、内包サブセクションを持つことができる
*/
function XpsTimelineSections(){
}
XpsTimelineSections.prototype=Array.prototype;
XpsTimelineSections.valueOf=function(){
	return this.getDefaultValue().valueOf();
}
/*
	XpsTimelineSection
	セクションオブジェクト
	りまぴんではセクション編集時に都度生成される
	セクションはトラックの要素でありセクションコレクションに格納される
	中間値生成セクションはそのプロパティとしてCollectionを持ち　中間値生成サブセクションを内包する

*/
function XpsTimelineSection(parent,index){
	this.parent		=	parent;
	this.id			=	index;	
	this.startOffset	=	0;
	this.duration		=	function(){return this.parent.duration;}
	this.value		=	this.parent.value;//valueはオブジェクトで初期値は親の値を参照
	this.subSections	=	undefined;
}

/*
				object Xps コンストラクタ
*/
function Xps(Layers,Length)
{
	if(! Layers) Layers=4;	//標準的なA,B,C,D の4レイヤで初期化
	if(! Length) Length=(!nas)? 24: nas.FRATE;//現状のレートで1秒を初期化
///////////　デフォルト値がレイヤなし継続長なしだと弊害があるのでデフォルト値を変更 2009/10/12
///////////　旧オブジェクトに存在したエラーメッセージ群はUIオブジェクトへ分離
//Xps標準のプロパティ設定
/*
	すべてのプロパティでXpsを初期化するのは、基準値トレーラーとしての基底オブジェクトのみ
	(ステージ＝null)
	以降のステージは、基底オブジェクトをprototypeとして作成される。
*/
	this.parent	=	null;//親Xps参照用プロパティ　初期値はnull（参照無し）
	this.stage	=	new XpsStage(this,"","");//オブジェクトでないほうが良いかも
	this.mapfile	=	"";//ファイルパスでなく参照オブジェクトに変更予定　オブジェクト側に参照可能なパスがあるものとする
	this.opus	=	(myOpus)? myOpus :"--";
	this.title	=	(myTitle)? myTitle:"noTitle";
	this.subtitle	=	(mySubTitle)? mySubTitle:"--";
	this.scene	=	(myScene)? myScene:"";
	this.cut	=	(myCut)? myCut:"";

	this.trin	=	[0,"trin"];
	this.trout	=	[0,"trout"];
	this.rate	=	(!nas)? "24FPS":nas.RATE;
	this.framerate	=	(!nas)? 24:nas.FRATE;
		var Now= new Date();
	this.create_time	=	(!nas)? Now.toString():Now.toNASString();
	this.create_user	=	myName;
	this.update_time	=	"";
	this.update_user	=	myName;
//		レイヤプロパティ設定()　:コレは別オブジェクトにする
	this.layers	=	this.newLayers(Layers);
//	this.timelines	=	this.newTracks(Layers);//メソッド側でデフォルトトラックを1加えて初期化するので指定はこのまま
	this.memo	=	"";

//	XPS配列の作成
	this.xpsBody= this.newBody(Layers+2,Length);
}
//Xps.のメソッドを定義
//レイヤプロパティトレーラを作成して返す
Xps.prototype.newLayers= function(layerCount,camCount){
	if(!camCount){camCount=0};
	var myLayers=new Array();
	var defaultNames="ABCDEFGHIJKLMNOPQRSTUVWXYZ";
	var cellCount=layerCount-camCount;
	for (var idx=0;idx<cellCount;idx++){
		myLayers.push(new XpsLayer(defaultNames.charAt(idx),"timing"));
	}
	for (var idx=0;idx<camCount;idx++){
		myLayers.push(new XpsLayer(defaultNames.charAt(idx),"camera"));
	}
	return myLayers;
}
//初期トラックプロパティトレーラを作成して返す
Xps.prototype.newTracks= function(layerCount){
	var myTracks=new Array();
		myTracks.push(new XpsTimelineTarck("dialog"));//default dialog track
	var defaultNames="ABCDEFGHIJKLMNOPQRSTUVWXYZ";
	var cellCount=layerCount-camCount;
	for (var idx=0;idx<cellCount;idx++){
		myLayers.push(new XpsTimelineTrack(defaultNames.charAt(idx),"timing"));
	}
	return myTracks;
}
/*
	新規タイムライントレーラを作成
	タイムライントレーラーは、レイヤプロパティトレーラーの数+2
	加えられた2つのタイムラインは、固定のダイアログタイムライン及びフレームコメントタイムラインとして使用される。
	このタムラインは、レコードの開始及び終了マーカーを兼ねるので削除できないので注意
	レイヤ数0のシートのタイムライン数は2である。
*/
Xps.prototype.newBody=function(myLayers,myFrames){
	if(! myLayers) myLayers=this.layers.length+2;//+2
//	if(! myLayers) myLayers=this.timelines.length+1;//+1
	if(! myFrames) myFrames=this.framerate;
	var myBody=new Array(myLayers);
	for (var lid=0;lid<myBody.length;lid++){
		myBody[lid]=new Array();
		for(var fid=0;fid<myFrames;fid++){myBody[lid][fid]="";}
	}
	return myBody;
}
//再初期化
Xps.prototype.init=function(Layers,Length)
{
	if(! Layers) Layers=0;
	if(! Length) Length=0;
	///////////
//Xps標準のプロパティ設定
	this.mapfile	=	"";
	this.opus	=	myOpus;
	this.title	=	myTitle;
	this.subtitle	=	mySubTitle;
	this.scene	=	myScene;
	this.cut	=	myCut;

	this.trin	=	[0,"trin"];
	this.trout	=	[0,"trout"];

	this.rate	=	(!nas)? "24FPS":nas.RATE;
	this.framerate	=	(!nas)? 24:nas.FRATE;
		var Now= new Date();
	this.create_time	=(!nas)?Now.toString():Now.toNASString();
	this.create_user	=	myName;
	this.update_time	=	"";
	this.update_user	=	myName;
//		レイヤプロパティ設定()
	this.layers=this.newLayers(Layers);
	this.memo	=	"";

//	XPS配列の作成
	this.xpsBody= this.newBody(Layers+2,Length);
}

//	マップオブジェクトを与えて初期化
Xps.prototype.getMap=function(MAP)
{
//マップ無ければ失敗
	if(! MAP) {
			xUI.errorCode=5;return false;
			}

//		レイヤプロパティ設定()
	this.layers.length	=(MAP.mapBody.length-2 >this.layers.length)?
	MAP.mapBody.length-2 : this.layers.length;//大きいほうを採る
//		レイヤプロパティを取得する。マップに無い情報はパスする。
//	マップが小さい場合は元に情報が残る?不足分はデフォルトで埋めるか?
	for (id=0;id<(MAP.mapBody.length-2);id++)
	{
//レイヤのプロパティをMAPオブジェクトのプロパティから読み込む(ダミーだけど)
//	読み込んだジオメトリは、セル(グループ)のジオメトリであって、
//	個々のエントリのジオメトリではない点に注意。
if(id+1<MAP.groups.length){
		var name	=MAP.groups[id+1][0];
		var sizeX	=MAP.getgeometry(id+1,"sizeX");
		var sizeY	=MAP.getgeometry(id+1,"sizeY");
		var aspect	=MAP.getgeometry(id+1,"aspect");
		var lot		=MAP.getmaxlot(id+1);
}else{
		var name	="noValue";
		var sizeX	=1280;
		var sizeY	=720;
		var aspect	=1;
		var lot		=10;
};

		var blmtd	="wipe";	//りまぴん専用?
		var blpos	="end";	//
		var option	="timing";	//
		var link	=".";	//リンクする親オブジェクト
			//"."区切りでIDナンバでパスを(暫定)

		this["layers"][id]=new Array();

			this["layers"][id]["name"]	=name	;
			this["layers"][id]["sizeX"]	=sizeX	;
			this["layers"][id]["sizeY"]	=sizeY	;
			this["layers"][id]["aspect"]	=aspect	;
			this["layers"][id]["lot"]	=lot	;
			this["layers"][id]["blmtd"]	=blmtd	;
			this["layers"][id]["blpos"]	=blpos	;
			this["layers"][id]["option"]	=option	;
			this["layers"][id]["link"]	=link	;
	}
}
//
/*
	継続時間をフレーム数で返す
	ダイアログタイムラインの要素数で返す
	初期状態でボディの存在しないシートが存在しないように注意
	未記述でも空ボディのタイムラインが存在する。
	エラー関連コードは排除の方向で
*/
//Xps.prototype.duration=function(){if(xUI.errorCode){xUI.errorCode=0};return this.xpsBody[0].length;};
Xps.prototype.duration=function(){return this.xpsBody[0].length;};
//
//	カット尺をフレーム数で返す
//Xps.prototype.time=function(){if(xUI.errorCode){xUI.errorCode=0};return (this.duration()-Math.ceil((this.trin[0]+this.trout[0])/2));};
Xps.prototype.time=function(){return (this.duration()-Math.ceil((this.trin[0]+this.trout[0])/2));};
//
//	フレーム数からTCを返す
//Xps.prototype.getTC=function(mtd){if(xUI.errorCode){xUI.errorCode=0};return nas.Frm2FCT(mtd,3,0,this.framerate);}
Xps.prototype.getTC=function(mtd){return (nas)? nas.Frm2FCT(mtd,3,0,this.framerate):Math.floor(mtd/this.framerate)+"+"+mtd%this.framerate+".";}
//仮メソッドアトでキチンとカケ
//
//	編集関連メソッド

//Xpsにタイムラインを挿入
/*
	Xps.insertTL(id,Timelines)
	Timelines(複数可・配列渡し)
	idの前方に引数のタイムラインを挿入
	idが未指定・範囲外の場合、後方へ挿入
	0番タイムラインの前方へは挿入不能(固定のデフォルトタイムライン)
	現状ではデータを持ったままタイムラインを挿入することはできない。
	必ず空のタイムラインが挿入される。
*/
Xps.prototype.insertTL=function(myId,myTimelines){
	if(!(myTimelines instanceof Array)){myTimelines=[myTimelines]};
	if((! myId )||(myId < 1)||( myId >= this.layers.length)){myId=this.layers.length+1};
	var myBodys =[];
	for (var idx=0;idx<myTimelines.length;idx++){
		//挿入データの検査をする　挿入データが文字列ならそのラベルを持つtimingタイムラインと置き換える
	  if(!(myTimelines[idx] instanceof XpsLayer)){
//alert(myTimelines[idx].length)
		if(myTimelines[idx]){
	     myTimelines[idx]=new XpsLayer(myTimelines[idx],"timing");
		}else{
	     myTimelines[idx]=new XpsLayer(nas.Zf(idx+myId,2),"timing");
		}
	  }
	myBodys[idx]=[];
	  for(var ix=0;ix<this.xpsBody[0].length;ix++){myBodys[idx][ix]=""}
	//挿入データを揃えて挿入
	  this.layers.splice (myId+idx-1,0,myTimelines[idx]);
	  this.xpsBody.splice(myId+idx  ,0,myBodys[idx]     );
	}
}	
/*	Xps.deleteTL([id])
	指定idのタイムラインを削除する。1～
	デフォルトの音声タイムラインとフレームコメントの削除はできない
	IDを単独又は配列渡しで

XpsLayer　と　xpsBody はそのうちタイムラインとして統合すべきかと思う。

*/
Xps.prototype.deleteTL=function(args){
	if (! (args instanceof Array)){args=[args]};
	args.sort().reverse();
	for (var idx=0;idx<args.length;idx++){
	      //操作範囲外の値は無視
		var targetIndex=args[idx];
	   if(isNaN(targetIndex)){continue;}
	   if((targetIndex >0)&&(targetIndex <this.xpsBody.length-1)){
		this.layers.splice(targetIndex-1,1);
		this.xpsBody.splice(targetIndex,1);
	   }
	}

}
/*	Xps.reInitBody(newTimelines:int,newDuration:int)

	Xps本体データのサイズを変更する。
	元あったデータ内容は可能な限り保存
	切り捨て分はなくなる。
	新たに出来たレコードは、ヌルストリングデータで埋める。
*/
Xps.prototype.reInitBody=function(newTimelines,newDuration){
	var oldWidth=this.xpsBody.length;
		if(! newTimelines) newTimelines=oldWidth;
	var oldDuration=this.duration();
		if(! newDuration ) newDuration =oldDuration;
	if(newTimelines<3 || newDuration<=0){return false;};
	var widthUp=(newTimelines > oldWidth)?	true:false;
	var durationUp=(newDuration > oldDuration)?	true:false;

	this.xpsBody.length=newTimelines;//配列長(タイムライン数)の設定

//延長したらカラデータで埋めとく
		if(widthUp){
			for (i=0;i<oldWidth;i++)
			{
				this.xpsBody[i].length=newDuration;
				if(durationUp){
					for(f=oldDuration;f<newDuration;f++){this.xpsBody[i][f]='';};
				};
			};
			for (i=oldWidth;i<newTimelines;i++)
			{
				this.xpsBody[i]=new Array(newDuration);
//if(durationUp)
				for(f=0;f<newDuration;f++){this.xpsBody[i][f]='';};
			};
		}else{
			for (i=0;i<newTimelines;i++)
			{
				this.xpsBody[i].length=newDuration;
				if(durationUp)
				{
					for(f=oldDuration;f<newDuration;f++){this.xpsBody[i][f]='';};
				};
			};
		};
//タイムラインが増えた場合は、再描画前にグループ情報の追加が必要・空データを自動生成してやる必要あり
//現在はラベル名以外は直前タイムラインの複製

	this["layers"].length=(newTimelines-2);//(現在決め打ち)
	if(widthUp){
		for(i=oldWidth-2;i<(newTimelines-2);i++){
		this["layers"][i]=new Array();//これ、今はarrayだけど後でコンストラクタ書くこと
 
		this["layers"][i]["name"]	=("00"+i).slice(-2);
		this["layers"][i]["sizeX"]	=this["layers"][oldWidth-3]["sizeX"]	;
		this["layers"][i]["sizeY"]	=this["layers"][oldWidth-3]["sizeY"]	;
		this["layers"][i]["aspect"]	=this["layers"][oldWidth-3]["aspect"]	;
		this["layers"][i]["lot"]	=this["layers"][oldWidth-3]["lot"]	;
		this["layers"][i]["blmtd"]	=this["layers"][oldWidth-3]["blmtd"]	;
		this["layers"][i]["blpos"]	=this["layers"][oldWidth-3]["blpos"]	;
		this["layers"][i]["option"]	=this["layers"][oldWidth-3]["option"]	;
		this["layers"][i]["link"]	=this["layers"][oldWidth-3]["link"]	;
		this["layers"][i]["parent"]	=this["layers"][oldWidth-3]["parent"]	;

		};
	};
	return true;
};
/*	Xps.getRange(Range:[[startC,startF],[endC,endF]])
	範囲内のデータをストリームで返す	
	xpsのメソッドに移行 2013.02.23
	範囲外のデータは、ヌルストリングを返す　2015.09.18
	負のアドレスを許容　150919
	全てシートの範囲外を指定された場合は、範囲のサイズの空ストリームを返す
	チェックはない（不要）空ストリームを得る場合に使用可能
	開始と終了のアドレスが一致している場合は、該当セルの値を返す
	第一象限と第三象限の指定は無効
*/
Xps.prototype.getRange	=function(Range)
{
	if (typeof Range=="undefined") {Range=[[0,0],[this.xpsBody.length-1,this.xpsBody[0].length-1]]}//指定がなければ全体をストリーム変換
	StartAddress	=Range[0];
	EndAddress	=Range[1];
//	if(StartAddress==EndAddress){return xpsBody[StartAddress[0]][StartAddress[1]]}
	var xBUF=new Array();var yBUF=new Array();var zBUF=new Array();
//ループして拾い出す
	for (var r=StartAddress[0];r<=EndAddress[0];r++)
	{
	  if(r<this.xpsBody.length && r>=0){
		for (var f=StartAddress[1];f<=EndAddress[1];f++)
			xBUF.push((f<this.xpsBody[r].length && f>=0)? this.xpsBody[r][f]:"");
		yBUF.push(xBUF.join(","));xBUF.length=0;
	  }else{
		yBUF.push(new Array(EndAddress[1]-StartAddress[1]+1).join(","));
	  }
	};
	zBUF=yBUF.join("\n");
// ストリームで返す
	return zBUF;
};
/*	Xps.put(書込開始アドレス:[startC,startF],データストリーム)
	書込開始アドレスを起点にストリームでデータ置き換え
	Xpsオブジェクトメソッド
	undo/redo等はUIレベルの実装なのでここでは関知しない
	書込開始アドレスに負の数を与えると、書込アドレスが負の場合レンジ外となる
	レンジオーバーしたデータは無視される
	このメソッドでは本体データとしてセパレータの",""\n"を与えることはできない（禁則事項）
	リザルトとして書き込みに成功したベクトル（左上、右下）、書き換え前のデータストリーム、書き込みに成功したデータを返す
*/
Xps.prototype.put =function(myAddress,myStream)
{
	if ((! myAddress)||(typeof myStream == "undefined")){return false}//指定がなければ操作失敗
	//データストリームが空文字列の場合は要素数１の配列に展開する
//データストリームを配列に展開
	var srcData=new Array(myStream.toString().split("\n").length);
	for (var n=0;n<srcData.length;n++){
	srcData[n]=myStream.toString().split("\n")[n].split(",");
	}
//指定アドレスから書き込み可能な範囲をクリップする
	var writeRange=[myAddress.slice(),add(myAddress,[srcData.length-1,srcData[0].length-1])];
		if(writeRange[0][0]<0) writeRange[0][0]=0;
		if(writeRange[0][0]>=this.xpsBody.length)    writeRange[0][1]=this.xpsBody.length-1;
		if(writeRange[0][1]<0) writeRange[0][1]=0;
		if(writeRange[0][1]>=this.xpsBody[0].length) writeRange[1][1]=this.xpsBody[0].length-1;
		if(writeRange[1][0]<writeRange[0][0]) writeRange[1][0]=writeRange[0][0];
		if(writeRange[1][0]>=this.xpsBody.length)    writeRange[1][0]=this.xpsBody.length-1;
		if(writeRange[1][1]<writeRange[0][1]) writeRange[1][1]=writeRange[0][1];
		if(writeRange[1][1]>=this.xpsBody[0].length) writeRange[1][1]=this.xpsBody[0].length-1;
//書き込み範囲をバックアップ
	var currentData=this.getRange(writeRange);
//ループして置き換え
	//var updatedRange=new Array();
	for (var c=0;c<srcData.length;c++)
	{
		for (var f=0;f<srcData[0].length;f++)
		{
			var writeColumn= c+myAddress[0];
			var writeFrame = f+myAddress[1];
			if(
			    (writeColumn>=0)&&(writeColumn<this.xpsBody.length)&&
			    (writeFrame>=0)&&(writeFrame<this.xpsBody[0].length)
			){
			    this.xpsBody[writeColumn][writeFrame]=srcData[c][f];
			    //updatedRange.push([writeColumn,writeFrame]);
			}
		}
	};
	//戻り値は、書き込みに成功したレンジ
//	return this.getRange([updatedRange[0],updatedRange[updatedRange.length-1]]);
	return [writeRange,this.getRange(writeRange),currentData];
};


//	読み込みメソッド
/*
	ラッパとして残置されるが、内部には他フォーマットの判定部分を置かない。
	インポーターとして使用する場合は、更にこの外側にデータ前処理部分をおくか、
	このメソッドをオーバーライドして使用すること。
	戻り値として、parseXps の戻り値を返すこと。2013.04.06
*/
Xps.prototype.readIN=function(datastream){
	if(datastream instanceof Boolean) return datastream;
	return this.parseXps(datastream);
}
//	読み込みメソッドのXpsパーサを分離
/*
	元の読み込みメソッドは、このパーサのラッパとして残置
	他フォーマットのデータパーサはライブラリに分離される。
	このメソッドはXpsのパース専用になる
	(将来の拡張用として必須)2013.04.06
*/
Xps.prototype.parseXps=function(datastream)
{
//	xUI.errorCode=0;//読み込みメソッドが呼ばれたので最終のエラーコードを捨てる。
//	if(!(datastream instanceof String)){alert (datastream.toString());return datastream;}
//	if(! datastream.toString().length ){
//		xUI.errorCode=1;return false;
//"001:データ長が0です。読み込みに失敗したかもしれません",
//	};
	if(! datastream.match){return false};
//ラインで分割して配列に取り込み
	var SrcData=new Array();
	if(datastream.match(/\r/)){datastream=datastream.replace(/\r\n?/g,("\n"))};
	SrcData=datastream.split("\n");
//		var AEK=true;//AEKey read-formatTestFlag
//データストリーム判別プロパティ
	SrcData.startLine	=0;//データ開始行
//	SrcData.dataClass	="";//データバージョン識別用に流用？
//データ種別判定は、削除　作業開始2013.04.04
//ソースデータのプロパティ
	SrcData.layerHeader	=0;//レイヤヘッダ開始行
	SrcData.layerProps	=0;//レイヤプロパティエントリ数
	SrcData.layerCount	=0;//レイヤ数
	SrcData.layers= new Array();//レイヤ情報トレーラー
	SrcData.layerBodyEnd	=0;//レイヤ情報終了行
	SrcData.frameCount	=0;//読み取りフレーム数
//第一パス
//データ冒頭の空白行を無視して、データ開始行を取得
//識別行の確認
//冒頭ラインが識別コードまたは空行でなかった場合は、さようなら御免ね
//IEのデータの検証もここでやっといたほうが良い?
	for (l=0;l<SrcData.length;l++)
	{
		if(SrcData[l].match(/^\s*$/))
		{
			continue;
		}else{

		if(MSIE){
	var choped=SrcData[l].charCodeAt(SrcData[l].length-1);
	if(choped<=32)
	SrcData[l] = SrcData[l].slice(0,-1);
		}
		//なぜだかナゾなぜに一文字多いのか?

/*
	データ処理中に含まれていた他フォーマットの解析部分は、
	別ライブラリで吸収するのでこのソースからは削除
	TSX AEK 部分は未処理　2013.0405
	どうしましょったら、どーしましょ まだ思案中 シアンは赤の補色です。
*/
if(SrcData[l].match(/^nasTIME-SHEET\ 0\.[1-4]$/))
{
	SrcData.startLine =l;//データ開始行
	break;
}else{
	this.errorMsg[10]=SrcData[l];//message10に当該トークンを格納
	xUI.errorCode=2;return false;
//	"002:どうもすみません。このデータは読めないみたいダ\n"
}
		}
	};
//第一パスおしまい。なんにもデータが無かったらサヨナラ
//"読み取るデータがないのです。";
	if(SrcData.startLine==0 && SrcData.length==l){ xUI.errorCode=3;return false;}
/*
	if(! SrcData.dataClass){
		this.errorMsg[10]=("009:想定外エラー\n"+SrcData.dataClass + "error!");
		xUI.errorCode=9;return false;
	}
*/	
//##変数名とプロパティ名の対照テーブル//
	var varNames=[
"MAPPING_FILE",
"TITLE",
"SUB_TITLE",
"OPUS",
"SCENE",
"CUT",
"TIME",
"TRIN",
"TROUT",
"FRAME_RATE",
"CREATE_USER",
"UPDATE_USER",
"CREATE_TIME",
"UPDATE_TIME"
	];
	var propNames=[
"mapfile",
"title",
"subtitle",
"opus",
"scene",
"cut",
"time",
"trin",
"trout",
"framerate",
"create_user",
"update_user",
"create_time",
"update_time"
	];
	var props =new Array(varNames.length);
for (i=0;i<varNames.length;i++){props[varNames[i]]=propNames[i];};
//	データ走査第二パス
//		時間プロパティ欠落時のために初期値設定
//		SrcData.time="6+0";
		SrcData.trin=[0,"trin"];
		SrcData.trout=[0,"trout"];

	for(line=SrcData.startLine;line<SrcData.length;line++){
			//前置部分を読み込みつつ、本体情報の確認
		if(MSIE){
	var choped=SrcData[line].charCodeAt(SrcData[line].length-1);
	if(choped<=32)
	SrcData[line] = SrcData[line].slice(0,-1);
		}
		//なぜだかナゾなぜに一文字多いのか?
//			シートプロパティにマッチ
		if(SrcData[line].match(/^\#\#([A-Z].*)=(.*)$/))
		{
			nAme=RegExp.$1;vAlue=RegExp.$2;
//	時間関連プロパティを先行して評価。
//	読み取ったフレーム数と指定時間の長いほうでシートを初期化する。
switch (nAme)
{
case	"TRIN":			;//トランシットイン
case	"TROUT":		;//トランシットアウト
		var tm=nas.FCT2Frm(vAlue.split(",")[0]);
		if(isNaN(tm)){tm=0};
		var nm=vAlue.split(",")[1];
		if(! nm){nm=props[nAme]};
			  SrcData[props[nAme]]=[tm,nm];
			break	;
case	"TIME":			;//カット尺
		var tm=nas.FCT2Frm(vAlue);
		if(isNaN(tm)){tm=0}
			  SrcData[props[nAme]]=tm;

			break	;
default:				;//時間関連以外
			SrcData[props[nAme]]=vAlue;
//					判定した値をプロパティで控える
}
		}
//			レイヤヘッダまたは終了識別にマッチ
		if(SrcData[line].match(/^\[(([a-zA-Z]+)\t?.*)\]$/))
		{
//シート終わっていたらメモを取り込んで終了
			if(SrcData[line].match(/\[END\]/))
			{
//	シートボディ終了ライン控え
				SrcData.layerBodyEnd=line;
				SrcData["memo"]='';
				for(li=line+1;li<SrcData.length;li++)
				{
					SrcData["memo"]+=SrcData[li];
					if((li+1) < SrcData.length){SrcData["memo"]+="\n"};//最終行以外は改行を追加
				}

					break ;
			}else{
//各レイヤの情報を取得
//	レイヤヘッダの開始行を記録
				if(SrcData.layerHeader==0)
					{SrcData.layerHeader=line};
//	ロットを記録(最大の行を採る)
				var LayerCount =
				SrcData[line].split("\t").length-3;
				SrcData.layerCount=
				(SrcData.layerCount<LayerCount)?
				LayerCount	:	SrcData.layerCount;
//	エントリ数を記録
				SrcData.layerProps++;
			}
		}else{
//	シートデータ本体の行数を加算
	if(! SrcData[line].match(/^\#.*$/))
	{
		SrcData.frameCount++;	//読み取りフレーム数
	}
		}
	}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
//	第二パス終了・読み取った情報でXPSオブジェクトを再初期化(共通)
	SrcData.duration=
	Math.ceil(SrcData.time+(SrcData.trin[0]+SrcData.trout[0])/2);
//		トランシット時間の処理は要再考。現状は切り上げ
	var SheetDuration=(SrcData.duration>(SrcData.frameCount-1))?
	SrcData.duration : (SrcData.frameCount-1)	;//大きいほう

//	///////////////////////
//	if(dbg) dbgPut("count/duration:"+SrcData.layerCount+":"+SheetDuration);

	this.init(SrcData.layerCount,SheetDuration);//再初期化

//	第二パスで読み取ったプロパティをXPSに転記
//					タイム以外はそのまま転記
	for(id=0;id<propNames.length;id++)
	{
		prpName=propNames[id];
		if(SrcData[prpName] && prpName!="time"){this[prpName]=SrcData[prpName];}
	}

//	読み取りデータを調べて得たキーメソッドとブランク位置を転記
for (var lyr=0;lyr<SrcData.layers.length;lyr++)
{
  this.layers[lyr].blmtd	=SrcData.layers[lyr].blmtd;
  this.layers[lyr].blpos	=SrcData.layers[lyr].blpos;
  this.layers[lyr].lot	=SrcData.layers[lyr].lot;
}

	if(SrcData["memo"]) this["memo"]=SrcData["memo"];//memoがあれば転記


// ///// 各エントリのレイヤプロパティとシート本体情報を取得(第三パス)

		var frame_id=0;//読み取りフレーム初期化

	for(line=SrcData.layerHeader;line<SrcData.layerBodyEnd;line++)
	{
//角括弧で開始するデータはレイヤプロパティ
		if(SrcData[line].match(/^\[(([a-zA-Z]+)\t.*)\]$/))
		{
				var layerProps= RegExp.$1.split("\t");
				var layerPropName=RegExp.$2;
					if(layerPropName=="CELL"){layerPropName="name"};
//	これ(CELL)だけシート表記とプロパティ名が一致していないので置換 一致が少ない場合はテーブルが必要になる
				for (c=0;c<SrcData.layerCount;c++)
				{	this["layers"][c][layerPropName]=layerProps[c+2]	}
		}else{
//ほかコメント以外はすべてシートデータ
			if(!SrcData[line].match(/^\#.*$/))
			{
				myLineAry=(SrcData[line].match(/\t/))? SrcData[line].split("\t"):SrcData[line].replace(/[\;\:\,]/g,"\t").split("\t");
				for (col=1;col<=(SrcData.layerCount+2);col++)
				{
//シート本体データの取得
					this.xpsBody[col-1][frame_id]=
					(myLineAry[col]!=undefined)?
					myLineAry[col].replace(/(^\s*|\s*$)/,""):"";
				}
				frame_id++;
			}
		}
	}


// ///// 読み取ったデータを検査する(データ検査は別のメソッドにしろ!??)
/*
//	マップファイルは、現在サポート無し
//		サポート開始時期未定
//この情報は、他の情報以前に判定して、マップオブジェクトの生成が必要。
//マップ未設定状態では、代用マップを作成して使用。
//代用マップは、デフォルトで存在。
<<
	現在は、代用MAPオブジェクトを先行して作成してあるが、
	本来のマップが確定するのはこのタイミングなので、注意!
>>
*/
if(false){
//MAPPING_FILE=(no file)//値は未設定時の予約値?nullで初期化すべきか?
			if(! this.mapfile) this.mapfile='(no file)';

//マップファイルが未設定ならば、代用マップを使用
//この判定はあまりに雑なので後でなんとかすれ
if(false){
	if(this.mapfile=='(no file)')
	{
		MapObj=MAP;	//とりあえず既存のダミーマップを代入しておく。
	}
}
//マップファイルを読み込んでマップオブジェクトを初期化
	//	そのうちね、今はまだない
//日付関連

//制作日付と制作者が無い場合は、空白で初期化?無視したほうが良いかも
//CREATE_USER=''
//CREATE_TIME=''
			if(! this.create_time) this.create_time='';
			if(! this.create_user) this.create_user='';
//最終更新日付と最終更新者が無い場合は、空白で初期化?
//(これは、どのみち保存時に現在のデータで上書き)
//UPDATE_USER=''
//UPDATE_TIME=''
			if(! this.update_time) this.update_time='';
			if(! this.update_user) this.update_user='';
//
//FRAME_RATE=24//
//フレームレート読み取れてなければ、現在の値で初期化(組み込み注意)
			if(! this.framerate)
			{	this.framerate=nas.FRATE;
			}else{
				nas.FRATE=this.framerate;
			}
//トランシット展開しておく
//TRIN=(時間文字列),(トランシット種別)

if(! this.trin){
	this.trin=[0,"trin"]
}else{
	time=nas.FCT2Frm(this.trin[0]);
	if(isNaN(time)){time=0};
	name=(this.trin[1])?this.trin[1]:"trin";
	this.trin=[time,name];
}
//TROUT=(時間文字列),(トランシット種別)
if(! this.trout){
	this.trout=[0,"trout"];
}else{
	time=nas.FCT2Frm(this.trout[0]);
	if(isNaN(time)){time=0};
	name=(this.trout[1])?this.trout[1]:"trout";
	this.trout=[time,name];
}
//TIMEも一応取り込んでおく。
//実際のデータの継続時間とこの情報の「長いほう」を採る
//TIME=(時間文字列)
//			this.time=nas.FCT2Frm(this.time);
//			if(isNaN(this.time)){this.time=0}

//作品データ
//情報が無い場合は、空白で初期化。マップをみるようになったら。
//マップの情報を参照
//最終作業情報(クッキー)を参照
//ユーザ設定によるデフォルトを参照 などから選択


//TITLE=(未設定とかのほうが良いかも)
			if(! this.title) this.title='';
//SUB_TITLE=(未設定とかのほうが良いかも)
			if(! this.subtitle) this.subtitle='';
//OPUS=()
			if(! this.opus) this.opus='';
//SCENE=()
			if(! this.scene) this.scene='';
//CUT=()
			if(! this.cut) this.cut='';

//シーン?・カット番号は最終状態でもデフォルトは空白に。紛らわしいから。

}
if(xUI.errorCode){alert("error :"+xUI.errorMsg[xUI.errorCode]);
//	xUI.errorCode=0;
	};return true;
};

//
/*
 *	書きだしメソッド
 *
 */
Xps.prototype.toString= function()
{
	var Now=new Date();
//セパレータ文字列調整
	var	bold_sep='\n#';
		for(n=this.layers.length+2;n>0;n--) bold_sep+='========';
	var	thin_sep='\n#';
		for(n=this.layers.length+2;n>0;n--) thin_sep+='--------';
//	ヘッダで初期化
	var result='nasTIME-SHEET 0.4';//出力変数初期化
//	##共通プロパティ変数設定
	result+='\n##MAPPING_FILE='	+ this.mapfile	;
	result+='\n##TITLE='	+ this.title	;
	result+='\n##SUB_TITLE='	+ this.subtitle	;
	result+='\n##OPUS='	+ this.opus	;
	result+='\n##SCENE='	+ this.scene	;
	result+='\n##CUT='	+ this.cut	;
	result+='\n##TIME='	+ nas.Frm2FCT(this.time(),3,0)	;
	result+='\n##TRIN='	+nas.Frm2FCT(this.trin[0],3,0)+","+ this.trin[1];
	result+='\n##TROUT='	+nas.Frm2FCT(this.trout[0],3,0)+","+ this.trout[1];
	result+='\n##CREATE_USER='	+ this.create_user	;
	result+='\n##UPDATE_USER='	+ this.update_user	;
	result+='\n##CREATE_TIME='	+ this.create_time	;
	result+='\n##UPDATE_TIME='	+ Now.toNASString()	;
	result+='\n##FRAME_RATE='	+ this.framerate	;
//result+='\n##FOCUS='	+11//
//result+='\n##SPIN='	+S3//
//result+='\n##BLANK_SWITCH='	+File//
	result+='\n#';
	result+=bold_sep;//セパレータ####################################
//	レイヤ別プロパティをストリームに追加
	var Lprops=["sizeX","sizeY","aspect","lot","blmtd","blpos","option","link","name"];
//	var Lprops=["sizeX","sizeY","aspect","lot","blmtd","blpos","option","link","CELL"];
	for (var prop=0;prop<Lprops.length;prop++)
	{
		var propName=Lprops[prop];
		var lineHeader=(propName=="name")? 
		'\n[CELL\tN' : '\n[' + propName + '\t';
		result+=lineHeader;
	for (id=0;id<this.layers.length;id++)
	{
		result+="\t"+this["layers"][id][propName];
	}
	result+='\t]';//
		}
//セパレータ
	result+=bold_sep;//セパレータ####################################
//シートボディ
	for(line=0;line<this.duration();line++)
	{
		result+='\n.';//改行＋ラインヘッダ
		for(column=0;column<(this.layers.length+2);column++)
		{
			address=column+'_'+line;
//			if(! Separator){}else{};

				result+='\t'+this.xpsBody[column][line];
//				result+=Separator+this.xpsBody[column][line];

		}
//	1/4秒おきにサブセパレータ/秒セパレータを出力
		if ((line+1)%Math.round(this.framerate/4)==0)
		{
			if((line+1)%Math.round(this.framerate)==0)
			{
				result+=bold_sep;
			}else{
				result+=thin_sep;
			}
		}
	}
//ボディ終了セパレータ
	result+=bold_sep;//セパレータ####################################
//ENDマーク
	result+='\n[END]\n';
//メモ
	result+=this.memo;

// // // // //返す(とりあえず)
//引数を認識していくつかの形式で返すように拡張予定
//セパレータを空白に変換したものは必要
//変更前(開始時点)のバックアップを返すモード必要/ゼロスクラッチの場合は、カラシートを返す。
	if(xUI.errorCode) xUI.errorCode=0;
	return result;
}
/* =====================================機能分割 20130221
レイヤストリームを正規化する
内部処理用
レイヤのデータ並びと同じ要素数の有効データで埋まった配列を返す

キー作成に必要な機能だが、汎用性があるので分離してXpsのメソッドに
キー作成はXPSのメソッドとして独立させる
中間値補間サインはオプションでその挙動を制御する？

タイムラインの種別によってデータが変化するのでその仕組みを組み込む
正規化されたストリーム形成は、同時にセクションの解析でもあるので、
このルーチンに組み込むか否か判断が必要

取扱タイムライン種別が"timing"限定でなくなるので、他種別の処置を設定
引数の種別を最初に判定する
引数範囲をシフトする
*/
Xps.prototype.getNormarizedStream=function(layer_id){
var	layerDataArray	=this.xpsBody[layer_id+1];
		layerDataArray.label=(layer_id<0)?"N":this.layers[layer_id].name;
if(layer_id<0){
var	blank_pos	="end";
var	key_max_lot	=0;
}else{
var	blank_pos	=this.layers[layer_id].blpos;
var	key_max_lot	=(isNaN(this.layers[layer_id].lot))?
			0 : this.layers[layer_id].lot;
}
var	bflag=(blank_pos)? false : true ;//ブランク処理フラグ
//

	var layer_max_lot=0;//レイヤロット変数の初期化

//シートのタイムラインからフルフレーム有効データの配列を作る
//全フレーム分のバッファ配列を作る
	var bufDataArray=new Array(layerDataArray.length);
//第一フレーム評価・エントリが無効な場合空フレームを設定
	bufDataArray[0]= (dataCheck(layerDataArray[0],layerDataArray.label,bflag))?
	dataCheck(layerDataArray[0],layerDataArray.label,bflag):"blank";

//2>>ラストフレーム ループ
	for (f=1;f<layerDataArray.length;f++){
//有効データを判定して無効データエントリを直前のコピーで埋める
	var currentValue=dataCheck(layerDataArray[f],layerDataArray.label,bflag);
		if(currentValue=="interp") currentValue=false;//キー変換 && timing 限定
	bufDataArray[f]=(currentValue)? currentValue:bufDataArray[f-1];

		if ((bufDataArray[f]!="blank")&&(bufDataArray[f]!="interp"))
		{
			layer_max_lot=(layer_max_lot>bufDataArray[f]) ?
			layer_max_lot : bufDataArray[f] ;
		}
	}
	max_lot = (layer_max_lot>key_max_lot)?
	layer_max_lot:key_max_lot;

//あらかじめ与えられた最大ロット変数と有効データ中の最大の値を比較して
//大きいほうをとる
//ここで、layer_max_lot が 0 であった場合変換すべきデータが無いので処理中断
// >全部ブランクであってもリザルトは返すように変更　

	if(false){if(layer_max_lot==0){
	xUI.errorCode=4;return	;
// "変換すべきデータがありません。\n処理を中断します。";
	}}

if(xUI.errorCode) xUI.errorCode=0;
return bufDataArray;
}
//2016改装用オブジェクト追加記述　2016/01/05
/*
XpsReplacement オブジェクト
置きかえトラックのセクションの値となるオブジェクト（現状、文字列で代用するのが良いか？）
通常に値を求めた場合は、セルの値が戻値
グループが異なる場合はグループラベル付き原動画番号が、ブランクのケースではプランク値が戻る。

XpsReplacement.id
XpsReplacement.name
XpsReplacement.group
XpsReplacement.size
XpsReplacement.offset
XpsReplacement.pegOffset
resolution
*/
XpsReplacement=function(name){
}

/*

XpsComposit オブジェクト？
エフェクト・合成トラックのセクションの値
通常に値を求める場合は、比率が戻値
以下の各プロパティを参照可能
.id
.
.T
.animationTiming
.compositMode

XpsEffect オブジェクト
下部構造としてXpsCompositeオブジェクトに展開可能な効果オブジェクト
エフェクト・合成トラックのセクションの値
通常に値を求める場合は、比率が戻値
以下の各プロパティを参照可能
.id
.
.T
.animationTiming
.compositMode

XpsCamerawork オブジェクト
カメラワークトラック・セクションの値
通常に値を求める場合XpsCameraworkオブジェクト自体が戻値

XpsCameraworkオブジェクトは、識別子を持ち　位置、オフセット、サイズ、スケール、ペグオフセット等のプロパティを持った複合オブジェクト
アニメーション補間可能なプロパティは多岐に渡るが、タイムシート上は単一の代表タイミングのみが表示される
オフセット（2D/3D）スケールの影響を受けない
ペグオフセット（2D/3D）スケールの影響を受ける
位置（2D/3D）スケールの影響を受けない
サイズ（2D/3D) スケールが適用されたサイズ(=読出専用)
スケール（2D/3D）
*/

// Xpsオブジェクト定義終了

/*
//MacOSでシートテキストを読みやすくする為の空白の追加 このせいでデータ量がやたら増える
この関数不要
function spcFill(string,Span)
{
	var charSpan=0;

	for(n=0;n<string.length;n++){
//エントリーの占有幅仮算定、すごく雑、さらにフォント確認していないのでもっと雑
//無いよりマシ程度だね
if(nas.isAdobe){
	if (isWindows){
if(string.charCodeAt(n)<127){charSpan+=1;}else{charSpan+=2;}
	}else{
if(string.charCodeAt(n)<127){charSpan+=2;}else{charSpan+=3;}
	}
}else{
	charSpan+=1;
}
	};
	if(charSpan>Span){charSpan=Span};
	preSpc="";postSpc="";
for (p=0;p<Math.floor((Span-charSpan)/2);p++){preSpc+="\x20"}
for (p=0;p<Span-Math.floor((Span-charSpan)/2)-charSpan;p++){postSpc+="\x20"}
	return preSpc+string+postSpc;
}
*/
