1 /**
  2  *  @fileOverview pmio.js
  3  *  production managemaent io
  4  *
  5  *  nas.Pm は 管理情報を分離するためのオブジェクト群
  6  *
  7  *  PmUnitを中核にしてそれに含まれる被管理情報をオブジェクトとして保持する
  8  */
  9 /*
 10  *
 11  *  PmUnitは Production(or Project) Management Unit(マネジメントユニット)を表す
 12  *  =カット袋に相当するオブジェクトの拡張機能=
 13  *
 14  *  制作ライン及びステージングを管理するためのオブジェクト
 15  *  カレントの ライン、ステージ、ジョブの値をひとまとめに保持する
 16  *  制作上の既経路を保持
 17  *  エレメントグループに対する変更権の保持(ラインが持つ情報)
 18  *  及びこれらの情報に対するアクセスを受け持つ
 19  *  開始時間 伝票番号 担当ユーザなどを参照・設定・変更が可能
 20  *  オブジェクト初期化時点では、空のオブジェクトを作成する
 21  *
 22  *  カット管理を行う場合は ALLアセットを、他の個別素材の場合は個別アセットを引数にして初期化のこと
 23 
 24 nas.Pm配下のDB通信オブジェクトは、アクセスポイントとして
 25 nas.pmdb オブジェクトを置いてその配下に参照を配置する
 26 配置されたオブジェクト群は基本的な情報テンプレートとして働く
 27 
 28 ClassObject nas.Pm がアプリケーションとしてのテンプレートキャリア
 29 初期状態ではnas.pmdbを実アクセスポイントとして参照を置く
 30 nas.pmdb  は、リポジトリ切り替え毎に各リポジトリの.pmdbに参照先が切り替えられる?
 31 
 32     nas.pmdb.organizations
 33          関連組織一覧 組織情報コレクション
 34             プライマリエントリーとしてpmdbの組織情報をエントリーする
 35             他組織のエントリは、接続情報のみでusersには通常自身のエントリのみを複製する
 36     nas.pmdb.users
 37          関連ユーザ一覧 ユーザ情報コレクション
 38     nas.pmdb.staff
 39         スタッフ一覧 スタッフコレクション
 40 
 41     nas.pmdb.lines
 42         ライン一覧テーブル   ラインコレクション
 43     nas.pmdb.stages
 44         ステージ一覧テーブル  ステージコレクション
 45     nas.pmdb.pmTemplates
 46         制作管理テンプレートコレクション
 47             ラインテンプレート(ライン定義)
 48                 ラインテンプレートの内容は自分自身と自分で保持するステージコレクション
 49     nas.pmdb.jobNames
 50         ジョブテンプレートコレクション
 51         
 52     nas.pmdb.workTitles
 53         .workTitles[titleIndex].episodes
 54             .episodes[episodeIndex].works ?
 55 
 56 	nas.pmdb.products
 57 
 58 	nas.pmdb.assets
 59 		アセット情報コレクション
 60 			制作時に管理対象となるアセットの定義テーブル
 61     nas.pmdb.medias
 62     	制作メディアコレクション
 63     		制作に供されるメディア情報のトレーラー
 64 
 65     等々 その際にparent  経由で相互の参照を行うので初期化時のパラメータ注意    
 66     nas オブジェクト内では以下の相互関係を持つ
 67 
 68     nas.Pm.~    マスターとなるクラスデータ
 69     nas.Repository.pmd.~    サーバごとのカスタマイズデータ
 70     nas.pmdb.~    実アクセスポイント
 71     配下の各オブジェクトのparentは、それぞれの親をポイントして初期化
 72 
 73     Pmモジュールに設定パーサを実装
 74     設定パーサは設定ストリームを入力として、リジョン毎に分離
 75     各リジョンを適切のパーサに振り分けて、自身のコレクションDBを再初期化する
 76     各パーサは、追加処理を行うが、設定パーサ側でデータのクリアを行い、再初期化動作とする
 77 
 78     nas.Pm.parseConfig(ストリーム)
 79 
 80     nas.pmdb.users
 81     nas.pmdb.staff
 82 
 83     nas.pmdb.assets
 84     nas.pmdb.medias
 85 
 86     nas.pmdb.lines
 87     nas.pmdb.stages
 88     nas.pmdb.jobNames
 89 
 90 
 91 // products/workTitles はPmクラスのみに存在するキャッシュオブジェクトなので要注意
 92    nas.Pm.products	リポジトリ内に記録されたエピソード単位のキャッシュ
 93    nas.Pm.workTitles	同、作品単位のデータコレクション
 94 
 95 Object PmDomain
 96     nas.Pm.WorkTitle.pmd
 97      ・
 98      ・
 99 
100 pmdbオブジェクトは親オブジェクトへの参照 pmdb.parent を持つ
101 このプロパティは、pmdbが持つ情報の親ノードへの参照
102 親ノードは以下のオブジェクトに対応する
103 
104 organization   = Repository.pmdb                 //pmdb.parent = Repository      ; Repository.parent='.';
105 product(title) = products/product.pmdb           //product.pmdb.parent = product ; product.parent = Repository;
106 episode(opus)  = product/episodes/episode.pmdb   //episode.pmdb.parent = episode ; episode.parent = product;
107 cut(work)      = episode/cuts/cut.pmdb           //cut.pmdb.parent = cut         ; cut.parent = episode
108 各ノードはツリー内を相互にアクセスするための .parentプロパティをもつ
109 pmdbはノードに対する.parent参照を持つ
110 pmdb からツリー上位のpmdbにアクセスするためには this.parent.parent.pmdbをアクセスする必要がある OK?
111 
112 organization:
113 repository:
114    product
115     title:
116      opus:
117       pmu:
118       cut:
119     
120 
121 pmdbの各オブジェクトにはユニークなプロパティを格納するunique配列をもたせる
122 この配列に値がある場合、新規メンバー登録の比較条件としてそのプロパティを参照する
123 RDBMのuniqueインデックスの付いたフィールドに同じ
124 
125 */
126 /**
127  * @class
128  */
129 nas.Pm = {};
130 //nas.Pm.organization = new nas.Pm.Organization() 
131 nas.Pm.users        = new nas.UserInfoCollection();
132 nas.pmdb            = nas.Pm;
133 
134 /*
135     PmDomain オブジェクトは、制作管理上の基礎データを保持するキャリアオブジェクト
136     制作管理ディレクトリノード毎に保持される。
137     基礎データを必要とするプログラムに基礎データをサービスする
138     基本データが未登録の場合は親オブジェクトの同データを参照してサービスを行う
139 
140 case:localRepository    
141     localRepository.pmdb = new nas.Pm.PmDomain(localRepository);
142 case:NetworkRepository
143     NetworkRepository.pmdb = new nas.Pm.PmDomain(NetworkRepository);
144 */
145 nas.Pm.PmDomain=new function(myParent){
146     this.parent=myParent;
147     this.users;     //
148     this.staff;     //
149     this.lines;     //
150     this.stages;    //
151     this.jobNames;  //
152     this.organization
153     this.medias;
154 
155 }
156 
157 /**
158  * @method
159  * クラスメソッド
160  * @desc
161  * ターゲットコレクション内にkeywordに一致するプロパティを持っているメンバーがあればコレクションメンバーのキー値を返す
162  * keyword がメンバーキーだった場合はそのまま返す
163  * 検索に失敗したらfalse
164  * オブジェクト本体が必要な場合は、Object.members[key]またはこの検索関数を間接的にコールする_getMemberメソッドを使用
165  * タイトル/エピソード/メディア/アセット/ライン/ステージ 共用
166  * @param {string} keyword
167  * @return {property}
168  * memberProp 
169  * キーワードは、各コレクションの共通プロパティで、検索対象となるもの
170     id          DBアクセス用のキー値(予約)
171     projectName 作品としてのタイトル タイトルに所属する情報の場合に有効だが、検索キーとしてはタイトルコレクション以外では無効
172     name        コレクションメンバーの一般名称
173     shortName   コレクションメンバーの省略表記
174     fullName    コレクションメンバーの正式表記
175     code        コレクションメンバーの短縮アイテムコード
176  *
177  */
178 nas.Pm.searchProp = function(keyword,target){
179     if(target.members[keyword]) return keyword;
180     for (var prp in target.members){
181         if( (target.members[prp].id          ==keyword)||
182             (target.members[prp].name        ==keyword)||
183             (target.members[prp].projectName ==keyword)||
184             (target.members[prp].episodeName ==keyword)||
185             (target.members[prp].mediaName   ==keyword)||
186             (target.members[prp].shortName   ==keyword)||
187             (target.members[prp].fullName    ==keyword)||
188             (target.members[prp].code        ==keyword) ) return prp;
189     }
190     return false;
191 }
192 /*
193     コレクションメンバーキャリアが配列の場合は以下を使用
194     使えないかも
195 */
196 nas.Pm.searchPropA = function(keyword,target){
197     if(! target.unique) return false;
198     //メンバー総当たり
199     for (var mix = 0 ; mix < target.menbers.length ; mix ++){
200     //オブジェクトのプロパティ内で unique情報のあるプロパティのみを検索
201         for (var uix = 0 ; uix < target.unique.length ; uix ++){
202             if(
203                 ((target.members[mix][target.unique[uix]].sameAs)&&(target.members[mix][target.unique[uix]].sameAs(keyword))) ||
204                 (target.members[mix][target.unique[uix]].toString()==keyword)
205             ) return target.members[mix]
206         }
207     }
208     return null;
209 }
210 
211 /**
212     クラスメソッド nas.Pm.searchPropを使ってキーを検索して対応するメンバーを返すオブジェクトメソッド
213     検索に失敗したケースではnullを戻す
214     引数を与えない場合に限り、メンバー内の最初のエントリを戻す
215     これは デフォルトエントリとして使用される
216     デフォルトエントリを必ず最初に登録する必要がある
217     通常は各コレクションの.entryメソッドにマッピングされる
218 */
219 nas.Pm._getMember = function(keyword){
220     if(typeof keyword=='undefined'){for (itm in this.members){return this.members[itm];break;}}
221     if(this.members[keyword]) return this.members[keyword];
222     var prp = nas.Pm.searchProp(keyword,this);
223     if(prp){return this.members[prp]}else{return null}
224 }
225 
226 /**
227     コレクションメンバーをテキストとしてダンプ出力するメソッド 汎用
228     対象コレクション
229  nas.Pm.OrganizationCollection //nas.pmdb.Organizations.dump();
230  nas.Pm.WorkTitleCollection //nas.pmdb.workTitles.dump();
231  nas.Pm.MediaCollection     //nas.pmdb.medias.dump();
232  nas.Pm.AssetCollection     //nas.pmdb.assets.dump();
233  nas.Pm.StageCollection     //nas.pmdb.stages.dump();
234  nas.Pm.LineCollection      //nas.pmdb.lines.dump();
235 
236  nsa.Pm.//nas.pmdb.jobNames.dump(true); これは別わけ コレクションの構造が異なる
237  nas.pmdb.//nas.pmdb.pmTemplates.dump(true);
238     
239     引数なし        メンバーあたり1要素のカンマ区切りテキスト  改行なし
240                     代表値 例えばステージならばステージ名を単独でコンマ区切りで戻す
241 
242      plain/text     プレーンテキスト 文章形式 config.pmdb用
243                     可読性の高い平文フォーマット改行あり
244                     1要素1行とは限らないので注意
245 
246     full/dump      プレーンテキスト設定ファイル用のダンプストリーム config.pmdb用
247                    コレクションの addMember メソッドで直接処理可能なテキストデータの配列を改行区切りで出力する
248                    1要素1レコード
249 
250     JSON        JSONによるダンプ 汎用的なデータ交換用
251                 オブジェクトごとに戻りデータは構造が異なる
252 
253 データ形式の複雑なものは汎用メソッドを使用せずに専用メソッドを持つ
254 ただし仕様は汎用メソッドに準ずる
255 */
256 nas.Pm._dumpList = function(form){
257     switch (form){
258     case "JSON":
259         //コレクションのキャリアが配列ベースの場合
260         if(this.members instanceof Array){
261         var result = [];
262             for (var ix =0 ; ix<this.members.length;ix++){
263                 result.push(JSON.parse((this.members[ix].dump)?this.members[ix].dump(form):this.members[ix].toString(form)));
264             }
265        }else{
266         //キャリアがオブジェクトベースの場合
267         var result = {};
268             for (var prp in this.members){
269                 result[prp]=JSON.parse((this.members[prp].dump)?this.members[prp].dump(form):this.members[prp].toString(form));
270             }
271         }
272         return JSON.stringify(result);
273         break;
274     case "full-dump":
275     case "full":
276     case "dump":
277         var result="";
278         //コレクションのキャリアが配列ベースの場合
279         if(this.members instanceof Array){
280             for (var ix =0 ; ix<this.members.length;ix++){
281 //                if (ix > 0) result +=",\n";
282                 if (ix > 0) result +="\n";
283                 result += (this.members[ix].dump )? this.members[ix].dump('full'):this.members[ix].toString('full');
284             }
285             result += '\n';
286        }else{
287          //キャリアがオブジェクトベースの場合
288             for (var prp in this.members){
289                 result += '"'+prp+'",';
290                 result += (this.members[prp].dump)? this.members[prp].dump('full') : this.members[prp].toString('full');
291                 result += '\n';
292             }
293         }
294         return result;
295         break;
296     case 'plain-text':
297     case 'plain':
298     case 'text':
299     default:
300         var result = new Array;
301         //コレクションのキャリアが配列ベースの場合
302         if(this.members instanceof Array){
303             for (var ix =0 ; ix<this.members.length;ix++){
304                 result.push((this.members[ix].dump)? this.members[ix].dump(form) : this.members[ix].toString(form));
305             }
306        }else{
307         //キャリアがオブジェクトベースの場合
308             for (var prp in this.members){
309                 result.push((this.members[prp].dump)? this.members[prp].dump(form) : this.members[prp].toString(form));
310             }
311         }
312         return result.join((form)? '\n':',');
313     }
314 }
315 /*      コレクションオブジェクトのメンバ追加オブジェクト
316     引数  メンバオブジェクトの配列
317     戻値  追加に成功したエントリ数
318     重複メンバーは登録しない
319     重複の条件は、Collection.unique配列を参照 いずれかのバッティングを(_getMember() で)検出
320 
321 */
322 nas.Pm._addMembers = function(members){
323     var result = 0;
324     if(!(members instanceof Array)) members = [members];
325 if (this.members instanceof Array){
326     for (var ix = 0 ; ix < members.length ; ix++ ){
327         var tempMember = members[ix];
328         var conflict = false;
329         if((this.unique)&&(this.entry)){
330             for (var uix = 0 ; uix < this.members.length ; uix++ ){
331                 if (this.entry(tempMember[this.unique[uix]])!=null){ conflict = true;break;}
332             }
333         }
334         if(! conflict){
335             var idx = this.members.add(tempMember)>=0;
336             if(ix == idx) result++;
337         }
338     }
339 }else{
340     for (var ix = 0 ; ix < members.length ; ix++ ){
341         var tempMember = members[ix];
342         var conflict = false;
343         for (var uix = 0 ; uix < this.unique.length ; uix++ ){
344             if (this.entry(tempMember[this.unique[uix]])!=null){ conflict = true;break;}
345         }
346         if(! conflict){
347             this.members[tempMember[this.unique[0]]]=tempMember;
348             result++;
349         }
350     }
351 }
352     return result;
353 }
354 /*
355  コレクションオブジェクトの設定読み込みメソッド
356     不正データの排除と重複データの排除はコレクションのaddMembersメソッドが受け持つ
357     これは使用されない メンバーごとのオブジェクトの相関が記述できていない 9/3
358 */
359 /*
360 nas.Pm._parseConfig = function(dataStream,form){
361     var myMembers =[];
362     // 形式が指定されない場合は、第一有効レコードで判定
363     if(! form ){
364             if (dataStream.match(/\[\s*(\{[^\}]+\}\s*,\s*)+(\{[^\}]+\})?\s*\]/)) form='JSON';//配列JSON
365             else if (dataStream.match(/(\n|^)\s*\[\s*.+\]($|\n)/)) form='full-dump';
366             else  form='plain-text';
367     }
368     switch(form){
369     case    'JSON':
370         var tempObject=JSON.parse(dataStream);
371         for (var rix=0;rix<tempObject.length;rix++){
372             var currentMember=new nas.Pm.Object(
373                 tempObject[rix].
374                 
375             );
376             currentMember[]=tempObject[rix][];
377             
378             myMembers.push(currentMember);
379         }
380     break;
381     case    'full-dump':	
382         dataStream = String(dataStream).split("\n");
383         for (var rix=0;rix<dataStream.length;rix++){
384             if((dataStream[rix].indexOf('#')==0)||(dataStream[rix].length == 0)) continue;
385             var currentMember=new nas.Pm.Object(
386             );
387             currentMember.parse(dataStream[rix]);
388             if (currentMember) myMembers.push(currentMember);
389         }
390     break;
391     case    'plain-text':
392     default:
393         dataStream = String(dataStream).split("\n");
394       var currentMember=false;
395       for (var rix=0;rix<dataStream.length;rix++) {
396         if((dataStream[rix].indexOf('#')==0)||(dataStream[rix].length == 0)) continue;
397         var currentField=dataStream[rix];
398 plainフォーマット
399 entryName
400 	prop:value
401 	prop:value
402 
403         if((currentMember)&&(currentField.match(/^\t([^:])+:(.+)/))){
404         	currentMember[RegExp.$1]=RegExp.$2;
405         } else if(currentField.match(/^[a-z].*$/)) {
406         	if(currentMember) myMembers.push(currentMember);
407         	currentMember=new nas.Pm.Object(currentField);
408         }
409       }
410       myMembers.push(currentMember);
411     }
412     return this.addStaff(myMembers);
413 }
414 */
415 //test 上記共用メソッドの関与するコレクションの出力確認
416 // nas.pmdb.workTitles.toString(true)
417 // nas.pmdb.medelias
418 /** ProductionManagementNode
419  * 制作管理オブジェクト
420  * @constractor
421  * 制作管理オブジェクトは、それぞれの管理単位(PmUnit=カット袋)についての制作管理部分を抽出したオブジェクトである。
422  * BANK等の管理移管時データ独立性を持たせるために分離される
423  * ラインごとに各PmUnitのプロパティとして登録され、ラインの開始条件及び終了条件の判定を含む
424  *  カット袋よりも上位となるエピソード・タイトルについては、このオブジェクトでなくnas.Pm.PmUnitをコレクションメンバーとする上層ノードオブジェクトを作る
425  >>管理がノードベースでありアセット単位にならないため
426  */
427 nas.Pm.PmNode = function PmNode(targetAsset,myName){
428     this.target     = targetAsset;//管理単位ごとのゴールを設定
429     this.name       = myName;
430     this.jobID      ;
431     this.jobs       = [];//空配列で初期化 コレクションが望ましい
432     this.stageID    ;
433     this.stages     = [];//空配列で初期化 コレクションが望ましい
434     this.lineID     ;
435     this.line       ;
436 }
437 /*
438 Pmuコンストラクタ
439 
440 PMU = new PmUnit(scObjects);
441 カット袋に相当するワークキャリア(ワークトレーラー)
442     SCiCオブジェクトを配列で与えて初期化する。
443 複数オブジェクトを持つ場合は 兼用カットとして処理する
444 クラスメソッドを使って後から編集可能
445 カット番号等のムービー内での識別情報が複数入っている
446 また、集合全体の進捗情報が格納されている
447 作品情報・管理フラグ・カットコレクション・進捗情報・素材DBを持つ
448 素材DBはPMUと関連付けられたxMapが保持する。
449 管理情報をこのユニットが受け持つ
450 */
451 
452 nas.Pm.PmUnit=function(mySCs){
453     //初期パラメータの中の第一要素の情報で識別名を作る
454     this.body           = mySCs;//
455     this.cut            = this.body[0].cut;//
456     this.scene          = this.body[0].myScene;//
457     this.subtitle       = mySubTitle;//文字列又は参照
458 //サブタイトル記述はDBと接続のない場合は別途入力するか、又は空白のまま保留
459     this.opus           = myOpus;//識別文字列(リレーション または ProductOpusオブジェクト)
460     this.title          = myTitle;//識別文字列(リレーション または ProductTitleオブジェクト)
461     this.inherit        =   mySCs;
462     this.pmNode         =   new nas.PmNode();
463 }
464 /*
465 制作管理単位の内容ダンプメソッド
466 引数: form 文字列可形式 html,plain,
467 指定がない場合は Sciオブジェクトのリストを"//(ダブルスラッシュ)"で区切って戻す
468 
469 
470 */
471 nas.Pm.PmUnit.prototype.toString=function(form){
472     if(! form){
473         return this.body.reverse().join("//");
474     }else{
475         return "yet coding";
476     }
477 //toString()メソッドは、出力用に調整する
478 //
479 }
480 //制作管理用 Organizationオブジェクト 各Repositoryに対応する
481 /*
482 nas.Pm.Organization(組織名)
483 
484     name        =;//識別名             eg."nekomataya"
485     fullName    =;//正式名称            eg.'ねこまたや'
486     code        =;//省略コード          eg.'nkmt'
487     id          =;//DB接続用Index      eg.'0001'
488     serviceUrl  =;//サービス接続情報    eg.'localRepository:info.nekomataya.pmdb'
489     shortName   =;//表示用短縮名       eg.'(ね)'
490     contact     =;//コンタクト情報     eg.'ねこまたや;//nekomataya@nekomataya.info'
491     description =;//説明 所在住所等自由記述
492 
493 オブジェクトメソッドで初期化する
494 戻り値は組織情報オブジェクト
495 実運用上はDBとリンクして動作するように調整
496 初期化段階ではプライマリオブジェクトとしてRepositoryに関連付けられた組織一つだけが登録される
497 
498 Organization.usersには、pmdbのusersへの参照か またはカレントのuserのみを登録した一時的ユーザコレクションを用いる? 
499 */
500 nas.Pm.Organization = function(repoitoryName){
501     this.name        =repoitoryName;
502     this.fullName    =repoitoryName;
503     this.code        =String(repoitoryName).slice(0,4);
504     this.id          ;
505     this.serviceUrl  ='localRepository:info.nekomataya.pmdb';
506     this.shortName   =String(repoitoryName).slice(0,2);
507     this.contact     =repoitoryName;
508     this.description =""; 
509 }
510 nas.Pm.Organization.prototype.toString = function(form){
511     switch(form){
512     case 'full-dump':
513     case 'full':
514     case 'dump':
515         return JSON.stringify([
516             this.fullName,
517             this.code,
518             this.id,
519             this.serviceUrl,
520             this.shortName,
521             this.contact,
522             this.description
523         ]);
524     break;
525     case    'plain-text':
526     case    'plain':
527     case    'text':
528         var result=[
529             this.name,
530             "\tfullName:"+this.fullName,
531             "\tcode:"+this.code,
532             "\tid:"+this.id,
533             "\tserviceUrl:"+this.serviceUrl,
534             "\tshortName:"+this.shortName,
535             "\tcontact:"+this.contact,
536             "\tdescription:"+this.description
537         ];
538             return result.join('\n');
539     break;
540     case    'JSON':
541         return JSON.stringify({
542             "name":this.name,
543             "fullName":this.fullName,
544             "code":this.code,
545             "id":this.id,
546             "serviceUrl":this.serviceUrl,
547             "shortName":this.shortName,
548             "contact":this.contact,
549             "description":this.contact
550         });
551     break;
552     default:
553         if(this[form]){
554           return this[form]
555         }else{
556             return this.name;
557         }
558     }
559 }
560 /**
561 組織コレクション
562 プライマリの組織はデータベースを維持する組織本体の情報
563 
564 */
565 nas.Pm.OrganizationCollection = function(myParent){
566     this.parent = myParent;
567     this.members = {};
568     this.unique =["name","id","fullName","serviceUrl","shortName","code"];
569 }
570 nas.Pm.OrganizationCollection.prototype.entry = nas.Pm._getMember;
571 nas.Pm.OrganizationCollection.prototype.addMembers = nas.Pm._addMembers;
572 nas.Pm.OrganizationCollection.prototype.dump = nas.Pm._dumpList;
573 /*
574     設定パーサ
575 */
576 nas.Pm.OrganizationCollection.prototype.parseConfig = function(configStream){
577     if(String(configStream).length==0) return false;
578     var newMembers=[];
579     this.members = {};//clear
580     var form = 'plain-text';
581     if(configStream.match(/\{[^\}]+\}/)){
582         form = 'JSON';
583     } else if(configStream.match(/.+\,\[.+\]/)){
584         form = 'full-dump';
585     }
586     switch(form){
587     case 'JSON':
588         var configData=JSON.parse(configStream);
589         for(prp in configData){
590             var tempData = configData[prp];
591             var newEntry         = new nas.Pm.Organization(prp);
592             newEntry.fullName    = tempData.fullName;
593             newEntry.code        = tempData.code;
594             newEntry.id          = tempData.id;
595             newEntry.serviceUrl  = tempData.serviceUrl;
596             newEntry.shortName   = tempData.shortName;
597             newEntry.contact     = tempData.contact;
598             newEntry.description   = tempData.description;
599             newMembers.push(newEntry);
600         }
601     break;
602     case 'full-dump':
603         configStream=String(configStream).split('\n');
604         for(var ir = 0;ir<configStream.length;ir++){
605             if((configStream[ir].indexOf("#")==0)||(configStream[ir].length==0)) continue;//コメント/空行スキップ
606             var tempData = JSON.parse("["+configStream[ir]+"]");
607             var newEntry         = new nas.Pm.Organization(tempData[0]);
608             newEntry.fullName    = tempData[1][0];
609             newEntry.code        = tempData[1][1];
610             newEntry.id          = tempData[1][2];
611             newEntry.serviceUrl  = tempData[1][3];
612             newEntry.shortName   = tempData[1][4];
613             newEntry.contact     = tempData[1][5];
614             newEntry.description = tempData[1][6];
615             newMembers.push(newEntry);
616         }
617     break;
618     default:
619         configStream=String(configStream).split('\n');
620         var currentEntry=null;
621         for(var ir = 0;ir<configStream.length;ir++){
622             if((configStream[ir].indexOf("#")==0)||(configStream[ir].length==0)) continue;//コメント/空行スキップ
623             if((configStream[ir].match( /^\t([a-z]+)\:(.+)$/i ))&&(currentEntry)){
624                 currentEntry[RegExp.$1]=RegExp.$2;//プロパティ設定
625             }else{
626                 if (currentEntry) newMembers.push(currentEntry);
627                 currentEntry=new nas.Pm.Organization(configStream[ir]);
628             }
629         }
630         newMembers.push(currentEntry);
631     }
632     return this.addMembers(newMembers);
633 }
634 
635 nas.Pm.organizations = new nas.Pm.OrganizationCollection(nas.Pm);
636 
637 //制作管理用 WorkTitelオブジェクト サーバ上のProductに対応する
638 /*
639 nas.Pm.newWorkTitle(タイトル識別子)
640 オブジェクトメソッドで初期化する
641 戻り値はタイトル情報オブジェクト
642 実運用上はDBとリンクして動作するように調整
643 
644 クラスメソッドとしての初期化機能は保留
645 タイトル情報及びタイトルの制作母体となる組織(Organization)へのペアレントリンクを保持する
646 親オブジェクト内のタイトルコレクションのメンバー
647 タイトル内にOpusコレクションを持たせる
648 */
649 nas.Pm.WorkTitle = function(){
650     this.id;   //DB接続用index - UATサーバの場合はtoken tokenはidへの参照
651     this.projectName; //タイトル - UATサーバの場合はname name はprojectNameへの参照
652     this.fullName;  //完全なタイトル文字列(なるべく公式に)
653     this.shortName;  //表示用短縮名
654     this.code;   //ファイル名使用の略号2~3文字アルファベット限定
655     this.framerate;  //Object nas.Framerate フレームレート
656     this.length;  //String 納品定尺フレーム数 または nasTC
657     this.inputMedia; //Object nas.AnimationField スタンダードフレーム
658     this.outputMedia; //Object nas.AnimationField 編集スペック
659 //****************************************************************
660 //    this.pmTemplates;    //作品内の標準工程テンプレート 不要
661 //   this.staff; //作品のスタッフ一覧 スタッフコレクションオブジェクト 不要
662 //    this.opuses = new nas.Pm.OpusCollection(this);    //Object nas.Pm.OpusCollection タイトル配下の話数コレクション 不要
663 //****************************************************************
664 //UATサーバのためのプロパティ
665     this.token = this.id;
666     this.name = this.projectName;
667     this.updated_at;
668     this.created_at;  
669     this.description;//タイトル識別子として使用?
670 }
671 /* タイトル文字列化
672 引数
673     なし          プロジェクト名で返す
674     propName      一致したプロパティを単独で返す 文字列またはオブジェクト
675     "full"        設定ダンプ形式 
676     "plain"       設定ダンプ形式 プレーンテキスト ダンプと同形式?
677     "JSON"          データ交換用JSONフォーマット
678 */
679 nas.Pm.WorkTitle.prototype.toString = function(form){
680     switch (form){
681     case    'full-dump':
682     case    'full':
683     case    'dump':
684         return JSON.stringify([
685             this.id,
686             this.fullName,
687             this.shortName,
688             this.code,
689             this.framerate.toString(true),
690             nas.Frm2FCT(this.length,2),
691             this.inputMedia,
692             this.outputMedia
693         ]);
694     break;
695     case    'plain-text':
696     case    'plain':
697     case    'text':
698         var result=[
699             this.projectName,
700             "\tid:"+this.id,
701             "\tfullName:"+this.fullName,
702             "\tshortName:"+this.shortName,
703             "\tcode:"+this.code,
704             "\tframerate:"+this.framerate.toString(true),
705             "\tformat:"+nas.Frm2FCT(this.length,2),
706             "\tinputMedia:"+this.inputMedia,
707             "\toutputMedia:"+this.outputMedia
708         ];
709             return result.join('\n');
710     break;
711     case    'JSON':
712         return JSON.stringify({
713             "projectName":this.projectName,
714             "id":this.id,
715             "fullName":this.fullName,
716             "shortName":this.shortName,
717             "code":this.code,
718             "framerate":this.framerate.toString(true),
719             "format":nas.Frm2FCT(this.length,2),
720             "inputMedia":this.inputMedia,
721             "outputMedia":this.outputMedia
722         });
723     break;
724     default:
725         if(this[form]){
726             return this[form];
727         }else{
728             return this.projectName;
729         }
730     }
731 }
732 nas.Pm.WorkTitle.prototype.valueOf=function(){return this.id;}
733 /**
734        ワークタイトルコレクションオブジェクト
735        一般に組織の配下に入るが、システム配下のリセント情報としても利用される
736 */
737 nas.Pm.WorkTitleCollection = function(myParent){
738     this.parent  = myParent;
739     this.members = {};
740     this.unique =["projectName","id","fullName","shortName","code"];
741 }
742 nas.Pm.WorkTitleCollection.prototype.entry = nas.Pm._getMember;
743 nas.Pm.WorkTitleCollection.prototype.addMembers = nas.Pm._addMembers;
744 nas.Pm.WorkTitleCollection.prototype.dump = nas.Pm._dumpList;
745 /*
746 function(keyword){
747     if(keyword){  return this.entry(keyword)};
748     return JSON.stringify(this.members);
749 }
750 */
751 /*
752     タイトル登録メソッド
753     引数  メンバーオブジェクトの配列
754     戻値  エントリに成功したメンバー数
755 
756     重複メンバーは登録しない
757     重複の条件は、projectName,id,fullName,shortName,code いずれかのバッティングを検出(_getMember)
758     他のプロパティは比較対象外
759     propListの形式は
760     projectName,[id,fullName,shortName,code,framerate,format,inputMedia,outputMedia]
761 */
762 /*
763 function(members){
764     var result = 0;
765     if(!(members instanceof Array)) members = [members];
766     for (var ix = 0 ; ix < members.length ; ix++ ){
767         var tempTitle = members[ix];
768         if( (this.entry(tempTitle.projectName)==null)&&
769             (this.entry(tempTitle.id)==null)&&
770             (this.entry(tempTitle.fullName)==null)&&
771             (this.entry(tempTitle.shortName)==null)&&
772             (this.entry(tempTitle.code)==null)
773         ){
774             this.members[tempTitle.projectName]=tempTitle;
775             result++;
776         }
777     }
778     return result;
779 }
780 */
781 /*
782     設定パーサ
783 */
784 nas.Pm.WorkTitleCollection.prototype.parseConfig = function(configStream){
785     if(String(configStream).length==0) return false;
786     var newMembers=[];
787     this.members = {};//clear
788     var form = 'plain-text';
789     if(configStream.match(/\{[^\}]+\}/)){
790         form = 'JSON';
791     } else if(configStream.match(/.+\,\[.+\]/)){
792         form = 'full-dump';
793     }
794     switch(form){
795     case 'JSON':
796         var configData=JSON.parse(configStream);
797         for(prp in configData){
798             var tempData = configData[prp];
799             var newTitle         = new nas.Pm.WorkTitle();
800             newTitle.projectName = prp;
801             newTitle.id          = tempData.id;
802             newTitle.fullName    = tempData.fullName;
803             newTitle.shortName   = tempData.shortName;
804             newTitle.code        = tempData.code;
805             newTitle.framerate   = new nas.Framerate(tempData.framerate);
806             newTitle.length      = nas.FCT2Frm(tempData.format);
807             newTitle.inputMedia  = tempData.inputMedia;
808             newTitle.outputMedia = tempData.outputMedia;
809             newMembers.push(newTitle);
810         }
811     break;
812     case 'full-dump':
813         configStream=String(configStream).split('\n');
814         for(var ir = 0;ir<configStream.length;ir++){
815             if((configStream[ir].indexOf("#")==0)||(configStream[ir].length==0)) continue;//コメント/空行スキップ
816             var tempData = JSON.parse("["+configStream[ir]+"]");
817             var newTitle         = new nas.Pm.WorkTitle();
818             newTitle.projectName = tempData[0];
819             newTitle.id          = tempData[1][0];
820             newTitle.fullName    = tempData[1][1];
821             newTitle.shortName   = tempData[1][2];
822             newTitle.code        = tempData[1][3];
823             newTitle.framerate   = new nas.Framerate(tempData[1][4]);
824             newTitle.length      = nas.FCT2Frm(tempData[1][5]);
825             newTitle.inputMedia  = tempData[1][6];
826             newTitle.outputMedia = tempData[1][7];
827             newMembers.push(newTitle);
828         }
829     break;
830     default:
831         configStream=String(configStream).split('\n');
832         var currentTitle=null;
833         for(var ir = 0;ir<configStream.length;ir++){
834             if((configStream[ir].indexOf("#")==0)||(configStream[ir].length==0)) continue;//コメント/空行スキップ
835             if((configStream[ir].match( /^\t([a-z]+)\:(.+)$/i ))&&(currentTitle)){
836                 var prop = RegExp.$1;var value = RegExp.$2;
837                 switch(prop){
838                 case 'format':currentTitle['length']=nas.FCT2Frm(value);//納品フォーマット尺
839                 break;
840                 case 'framerate':currentTitle['framerate'] = new nas.Framerate(value);
841                 break;
842                 default:currentTitle[prop] = value;
843                 }
844             }else{
845                 if (currentTitle) newMembers.push(currentTitle);
846                 currentTitle=new nas.Pm.WorkTitle();
847                 currentTitle.projectName=String(configStream[ir]);
848             }
849         }
850         newMembers.push(currentTitle);
851     }
852     return this.addMembers(newMembers);
853 }
854 /*  タイトル登録メソッド 試験用
855     パーサのfull-dumpの部分
856 */
857 nas.Pm.WorkTitleCollection.prototype.addTitle = function(titleName,propList){
858     var newTitle         = new nas.Pm.WorkTitle();
859     newTitle.projectName = titleName;
860     newTitle.id          = propList[0];
861     newTitle.fullName    = propList[1];
862     newTitle.shortName   = propList[2];
863     newTitle.code        = propList[3];
864     newTitle.framerate   = new nas.Framerate(propList[4]);
865     newTitle.length      = nas.FCT2Frm(propList[5]);
866     newTitle.inputMedia  = propList[6];
867     newTitle.outputMedia = propList[7];
868 
869     this.addMembers(newTitle);
870 }
871 
872 //テンプレート用コレクション
873 /*
874     UI上で参照されるコレクション
875     使用したタイトルを記録してテンプレートとして利用
876     recentTitles
877     プロダクションオブジェクトの配下のコレクションは別に設定される
878 */
879 nas.Pm.workTitles = new nas.Pm.WorkTitleCollection(nas.Pm);
880 
881 nas.Pm.activeTitle = nas.Pm.workTitles.entry();
882 //制作管理用 Opusオブジェクト サーバ上のEpisodeに対応する
883 /*
884  *nas.Pm.newOpus(タイトル識別子)
885  * // nas.Pm.newOpus(識別ID)
886  *nas.Pm.newOpus(管理話数名,タイトル)
887  *オブジェクトメソッドで初期化する
888  *戻り値は管理単位情報オブジェクト
889  *実運用上はDBとリンクして動作するように調整
890  *
891  *クラスメソッドとしての初期化機能は保留
892  制作話数(Opus/Episode)が所属するタイトル(Title/Product)へのリンクを持つ
893  
894 */
895 nas.Pm.Opus = function Opus(myID,myOpus,mySubtitle,myTitle){
896     this.id         = myID          ;//DB接続用index UATtoken
897     this.name       = myOpus        ;//表示名 話数/制作番号等 UATname
898     this.subtitle   = mySubtitle    ;//サブタイトル文字列 UATdescription
899     this.title      = myTitle       ;//String タイトルキー または Object nas.Pm.WorkTitle
900     this.valueOf    = function(){return this.id};
901 //    this.pmunits ;//カット袋コレクション 不要
902 }
903 
904 nas.Pm.newOpus = function(identifier,index){
905     if(! identifier) return false;
906     var arg = Xps.parseIdentifier(identifier);
907     if(arg){
908         return new nas.Pm.Opus(index,arg.opus,arg.subtitle,arg.title);
909     }else{
910         return arg;
911     }
912 }
913 /**
914 引数
915     なし          識別名を返す
916     propName      一致したプロパティを単独で返す 文字列またはオブジェクト
917     "full"        設定ダンプ形式 
918     "plain"       設定ダンプ形式 プレーンテキスト ダンプと同形式?
919     JSON          データ交換用JSONフォーマット
920 toStringメソッド 引数がなければ識別子用の文字列を返す
921 引数を与えると設定ファイル形式のJSONを返す
922 */
923 nas.Pm.Opus.prototype.toString   = function(form){
924     switch (form){
925     case 'full':
926         return JSON.stringify([
927             this.id,
928             this.name,
929             this.subtitle,
930             this.title
931         ]);
932     break;
933     case    'plain':
934         var result=[
935             this.name,
936             "\tid:"+this.id,
937             "\tname:"+this.name,
938             "\tsubTitle:"+this.subtitle,
939             "\ttitle:"+this.title.toString()
940         ];
941             return result.join('\n');
942     break;
943     case    'JSON':
944         return JSON.stringify({
945             "id":this.id,
946             "name":this.name,
947             "subTitle":this.subtitle,
948             "title":this.title.toString()
949         });
950     break;
951     default:
952     //デフォルトは識別子を組んで返す
953     return this.title+"#"+this.name+(this.subtitle)?"["+this.subtitle+"]":"";
954     }
955 };
956 /**
957     各話(エピソード)コレクションオブジェクト OpusCorrection
958     一般にタイトルの配下に入るが、システム配下でキャッシュとしても利用
959 */
960 nas.Pm.OpusCollection = function(myParent){
961     this.parent  = myParent;//parentTitle
962     this.members = {};
963     this.unique =["name","id"];
964 }
965 nas.Pm.OpusCollection.prototype.entry = nas.Pm._getMember;
966 nas.Pm.OpusCollection.prototype.addMembers = nas.Pm._addMembers;
967 nas.Pm.OpusCollection.prototype.dump =  nas.Pm._dumpList;
968 /*
969 
970 
971 nas.Pm.OpusCollection.prototype.addMembers = function(members){
972     var result = 0;
973     if(!(members instanceof Array)) members = [members];
974     for (var ix = 0 ; ix < members.length ; ix++ ){
975         var tempOpus = members[ix];
976         if( (this.entry(tempOpus.name)==null)&&
977             (this.entry(tempOpus.id)==null)
978         ){
979             this.members[tempOpus.name]=tempOpus;
980             result++;
981         }
982     }
983     return result;
984 
985 }
986 */
987 /*
988     設定パーサ
989 */
990 nas.Pm.OpusCollection.prototype.parseConfig = function(configStream){
991     if(String(configStream).length==0) return false;
992     var newMembers=[];
993     this.members = {};//clear
994     var form = 'plain-text';
995     if(configStream.match(/\{[^\}]+\}/)){
996         form = 'JSON';
997     } else if(configStream.match(/.+\,\[.+\]/)){
998         form = 'full-dump';
999     }
1000     switch(form){
1001     case 'JSON':
1002         var configData=JSON.parse(configStream);
1003         for(prp in configData){
1004             var tempData = configData[prp];
1005             var newOpus  = new nas.Pm.Opus(tempData.id,prp,tmpData.subtitle,this.parent.entry(tempData.title));
1006             newMembers.push(newTitle);
1007         }
1008     break;
1009     case 'full-dump':
1010         configStream=String(configStream).split('\n');
1011         for(var ir = 0;ir<configStream.length;ir++){
1012             if((configStream[ir].indexOf("#")==0)||(configStream[ir].length==0)) continue;//コメント/空行スキップ
1013             var tempData = JSON.parse("["+configStream[ir]+"]");
1014             var newOpus  = new nas.Pm.Opus(tempData.id,prp,tmpData.subtitle,this.parent.entry(tempData.title));
1015             newMembers.push(newTitle);
1016         }
1017     break;
1018     default:
1019         configStream=String(configStream).split('\n');
1020         var currentOpus=null;
1021         for(var ir = 0;ir<configStream.length;ir++){
1022             if((configStream[ir].indexOf("#")==0)||(configStream[ir].length==0)) continue;//コメント/空行スキップ
1023             if((configStream[ir].match( /^\t([a-z]+)\:(.+)$/i ))&&(currentOpus)){
1024                 currentOpus[RegExp.$1]=RegExp.$2;//プロパティ設定
1025             }else{
1026                 if (currentOpus) newMembers.push(currentOpus);
1027                 currentOpus=new nas.Pm.WorkTitle();
1028                 currentOpus.projectName=String(configStream[ir]);
1029             }
1030         }
1031         newMembers.push(currentOpus);
1032     }
1033     return this.addMembers(newMembers);
1034 }
1035 
1036 
1037 nas.Pm.opuses= new nas.Pm.OpusCollection(nas.Pm);
1038 
1039 //メディアDB
1040 /*
1041 メディアDBは、入出力のメディアスペックを記述するための複合オブジェクト
1042 MAP内部ではワークタイトルに付属する情報として処理する
1043 animationField,framerate,baseResolution等は、オブジェクトで保持
1044 初期化時は、デフォルトの値で作成 再初期化が必用
1045 idは初期化時は未設定
1046 コレクション加入時に設定される
1047 DBとの連結時は連結時に再設定
1048 */
1049 nas.Pm.ProductionMedia = function(mediaName,animationField,framerate){
1050     this.id             ;
1051     this.animationField = new nas.AnimationField(animationField);
1052     this.mediaName      = mediaName;//
1053     this.baseResolution = new nas.UnitResolution();//
1054     this.type           ;//mediaType drawing/video
1055     this.baseWidth      = this.animationField.baseWidth;
1056     this.frameAspect    = this.animationField.frameAspect;
1057     this.framerate      = nas.newFramerate(framerate);
1058     this.tcType         ;//string tradJA/SMPTE/TC/frame
1059     this.pegForm        = this.animationField.peg;//animationField.peg
1060     this.pegOffset      = this.animationField.pegOffset;
1061     this.pixelAspect    ;//float
1062     this.description    ;
1063 }
1064 /*
1065 
1066 */
1067 nas.Pm.ProductionMedia.prototype.toString = function(form){
1068     switch (form){
1069     case 'JSON':
1070         return JSON.stringify({
1071             "mediaName"     :this.mediaName,
1072             "id"            :this.id,
1073             "animationField":this.animationField.toString(),
1074             "baseResolution":this.baseResolution.toString(),
1075             "mediaType"     :this.mediaType,
1076             "tcType"        :this.tcType,
1077             "pegForm"       :this.pegForm.toString(),
1078             "pixelAspect"   :this.pixelAspect,
1079             "description"   :this.description
1080         });
1081     break;
1082     case 'dump':
1083     case 'full':
1084         return JSON.stringify([
1085             this.id,
1086             this.animationField.toString(),
1087             this.baseResolution.toString(),
1088             this.mediaType,
1089             this.tcType,
1090             this.pegForm.toString(),
1091             this.pixelAspect,
1092             this.description
1093         ]);
1094     break;
1095     case 'plain':
1096     case 'text':
1097         return ([
1098             this.mediaName,
1099             "\tid:"+this.id,
1100             "\tanimationField:"+this.animationField.toString(),
1101             "\tbaseResolution:"+this.baseResolution.toString(),
1102             "\tmediaType:"+this.mediaType,
1103             "\ttcType:"+this.tcType,
1104             "\tpegForm:"+this.pegForm.toString(),
1105             "\tpixelAspect:"+this.pixelAspect,
1106             "\tdescription:"+this.description
1107         ]).join('\n');
1108     break;
1109     default:
1110         return this.mediaName;
1111     }
1112 }
1113 //
1114 nas.Pm.MediaCollection= function(myParent){
1115     this.parent  = myParent;
1116     this.members = {};
1117 //    this.unique =["mediaName","id"];
1118     this.unique =["mediaName"];
1119 }
1120 nas.Pm.MediaCollection.prototype.entry = nas.Pm._getMember;
1121 nas.Pm.MediaCollection.prototype.addMembers= nas.Pm._addMembers;
1122 nas.Pm.MediaCollection.prototype.dump = nas.Pm._dumpList;
1123 /*
1124     コレクションメンバー登録メソッド
1125     引数  メンバーオブジェクト配列
1126     戻値  エントリに成功したメンバー数
1127     重複メンバーは登録しない
1128     重複の条件は、mediaName,id いずれかのバッティングを検出(_getMember)
1129     他のプロパティは比較対象外
1130     full-dump の形式は
1131     mediaName,[id,animationField,baseResolution,mediaType,tcType,pegForm,pixelAspect,description]
1132 nas.Pm.MediaCollection.prototype.addMembers=function(members){
1133     var result = 0;
1134     if(!(members instanceof Array)) members = [members];
1135     for (var ix = 0 ; ix < members.length ; ix++ ){
1136         var tempOpus = members[ix];
1137         if( (this.entry(tempOpus.mediaName)==null)&&
1138             (this.entry(tempOpus.id)==null)
1139         ){
1140             this.members[tempOpus.name]=tempOpus;
1141             result++;
1142         }
1143     }
1144     return result;
1145 }
1146 */
1147 /*
1148 */
1149 nas.Pm.MediaCollection.prototype.parseConfig = function(configStream){
1150     if((! configStream)||(String(configStream).length==0)) return false;
1151     var newMembers=[];
1152     this.members = {};//clear
1153     var form = 'plain-text';
1154     if(configStream.match(/\{[^\}]+\}/)){
1155         form = 'JSON';
1156     } else if(configStream.match(/.+\,\[.+\]/)){
1157         form = 'full-dump';
1158     }
1159     switch(form){
1160     case 'JSON':
1161         var configData=JSON.parse(configStream);
1162         for(prp in configData){
1163             var tempData = configData[prp];
1164             var newMedia  = new nas.Pm.ProductionMedia(tempData.mediaName,tempData.animationField,tempData.framerate);
1165                 newMedia.id             = tempData.id;
1166 //              newMedia.mediaName      = tempData.mediaName;//
1167 //              newMedia.animationField = tempData.new nas.AnimationField(tempData.animationField);
1168 //              newMedia.baseWidth      = newMedia.animationField.baseWidth;
1169 //              newMedia.frameAspect    = newMedia.animationField.frameAspect;
1170 //              newMedia.pegForm        = newMedia.animationField.peg;//animationField.peg
1171 //              newMedia.pegOffset      = newMedia.animationField.pegOffset;
1172                 newMedia.baseResolution = new nas.UnitResolution(tempData.baseResolution);//
1173                 newMedia.type           = tempData.type;//mediaType drawing/video
1174 //              newMedia.framerate      = nas.newFramerate(tempData.framerate);
1175                 newMedia.tcType         = tempData.tcType;//string tradJA/SMPTE/TC/frame
1176                 newMedia.pixelAspect    = parseFloat(tempData.pixelAspect);//float
1177                 newMedia.description    = tempData.description;
1178             newMembers.push(newMedia);
1179         }
1180     break;
1181     case 'full-dump':
1182         configStream=String(configStream).split('\n');
1183         for(var ir = 0;ir<configStream.length;ir++){
1184             if((configStream[ir].indexOf("#")==0)||(configStream[ir].length==0)) continue;//コメント/空行スキップ
1185             var tempData = JSON.parse("["+configStream[ir]+"]");
1186             var newMedia  = new nas.Pm.ProductionMedia(tempData[0],tempData[1][1]);
1187     newMedia.id             = tempData[1][0];
1188 //    newMedia.animationField = tempData[1][1];//new nas.AnimationField(animationField);
1189 //    newMedia.mediaName      = tempData[0];// mediaName;//
1190     newMedia.baseResolution = tempData[1][2];// new nas.UnitResolution();//
1191     newMedia.type           = tempData[1][3];// ;//mediaType drawing/video
1192 //    newMedia.baseWidth      = ;// newMedia.animationField.baseWidth;
1193 //    newMedia.frameAspect    = ;// newMedia.animationField.frameAspect;
1194 //    newMedia.framerate      = ;// nas.newFramerate(framerate);
1195     newMedia.tcType         = tempData[1][4];// ;//string tradJA/SMPTE/TC/frame
1196     newMedia.pegForm        = tempData[1][5];// newMedia.animationField.peg;//animationField.peg
1197 //    newMedia.pegOffset      = newMedia.animationField.pegOffset;
1198     newMedia.pixelAspect    = parseFloat(tempData[1][6])  ;//float
1199     newMedia.description    = tempData[1][7];
1200 
1201             newMembers.push(newMedia);
1202         }
1203     break;
1204     case 'plain-text':
1205     default:
1206         configStream=String(configStream).split('\n');
1207         var currentMedia=false;
1208         for(var ir = 0;ir<configStream.length;ir++){
1209             if((configStream[ir].indexOf("#")==0)||(configStream[ir].length==0)) continue;//コメント/空行スキップ
1210             if((configStream[ir].match( /^\t([a-z]+)\:(.+)$/i ))&&(currentMedia)){
1211                 currentMedia[RegExp.$1]=RegExp.$2;//プロパティ設定
1212             }else{
1213                 if (currentMedia) newMembers.push(currentMedia);
1214                 currentMedia=new nas.Pm.ProductionMedia(configStream[ir]);
1215             }
1216         }
1217         newMembers.push(currentMedia);
1218     }
1219     return this.addMembers(newMembers);
1220 
1221 
1222     if(   (this.entry(mediaName)==null)&&
1223         (this.entry(propList[0])==null)
1224     ){
1225         var tempMedia = new nas.Pm.ProductionMedia(mediaName);
1226         var tempField = new nas.AnimationField(
1227             name,
1228             basewidth,
1229             frameaspect,
1230             scale,
1231             pegform,
1232             pegOffset
1233         );
1234         tempMedia.id              = (propList[0]=='')? nas.Zf(Object.keys(this.members).length,4):propList[0];
1235         tempMedia.animationField  = propList[1];//現在は文字列のまま
1236     // 本日は仕様変更が主眼なのでこのまま保留 12/04
1237         tempMedia.baseResolution  = propList[2];
1238         tempMedia.mediaType       = propList[3];
1239         tempMedia.tcType          = propList[4];//nas.Framerate Objectする場合は nas.newFramerate(this.tcType)
1240         tempMedia.pegForm         = propList[5];
1241         tempMedia.pixelAspect     = propList[6];
1242         tempMedia.description     = propList[7];
1243         this.members[mediaName]=tempMedia;
1244     }
1245 }
1246 
1247 
1248 nas.Pm.MediaCollection.prototype.addMedia = function(mediaName,propList){
1249     
1250     this.members[mediaName]                 = new nas.Pm.ProductionMedia();
1251     this.members[mediaName].mediaName       = mediaName;
1252     this.members[mediaName].id              = propList[0];
1253     this.members[mediaName].animationField  = propList[1];//現在は文字列のまま
1254     // 本日は仕様変更が主眼なのでこのまま保留 12/04
1255     this.members[mediaName].baseResolution  = propList[2];
1256     this.members[mediaName].mediaType       = propList[3];
1257     this.members[mediaName].tcType          = propList[4];//nas.Framerate Objectする場合は nas.newFramerate(this.tcType)
1258     this.members[mediaName].pegForm         = propList[5];
1259     this.members[mediaName].pixelAspect     = propList[6];
1260     this.members[mediaName].description     = propList[7];
1261 }
1262 /*
1263 nas.Pm.MediaCollection.prototype.addMembers = function (members){
1264     if(!(members instanceof Array)) members =[members];
1265     for (var ix=0 ;ix< members.length;ix++) this.addMember(members[ix])
1266 }
1267 */
1268 nas.Pm.medias = new nas.Pm.MediaCollection(nas.Pm);
1269 
1270 /*制作管理用 Assetオブジェクト
1271  *アセットベースの管理を行う
1272  *このシステム上のアセットは、通常XPSを介して時間/空間的に配置された再利用可能データ群を指す
1273  *XPSを持たない場合もある(時間構造を持たない)
1274  *
1275  *作品内でユニークな識別名を持つ管理用のキーオブジェクトに結合されたデータ群を総称するもの、
1276  *管理用オブジェクトは以下のプロパティを持つ
1277  * name String 識別名称:作品内での一意性を求められる
1278  * hasXPS Boolean アセットがXPS(時間構造)を持つかのフラグ
1279  * code String 省略表記用短縮コード 2〜3バイトを推奨 ユニークであること
1280  * shortName String 画面表示用略称 8文字程度までを推奨 指定のない場合はnameを転用
1281  * description String アセットの説明 ユーザのために必用
1282  * endNode Boolean アセットがラインを終了させうるか否かのフラグ このフラグのあるアセットは、制作ラインの目標となりラインを収束させる
1283  * callStage Array ステージ識別名配列 当該アセットを受けて(入力として)開始することが可能なステージ群 ユーザが選択する 一つのアセットを受けて2つ以上のステージを開始する場合、ライン分岐が発生する
1284  *
1285 */
1286 nas.Pm.Asset = function(){
1287     this.assetName      ;
1288     this.name           ;
1289     this.hasXPS         ;
1290     this.code           ;
1291     this.shortName      ;
1292     this.description    ;
1293     this.endNode        ;
1294     this.callStage      ;
1295 }
1296 
1297 nas.Pm.Asset.prototype.toString = function(form){
1298     switch (form) {
1299     case 'JSON':
1300         return JSON.stringify({
1301             name:this.name,
1302             hasXPS:this.hasXPS,
1303             code:this.code,
1304             shortName:this.shortName,
1305             descripion:this.description,
1306             endNode:this.endNode,
1307             callStage:this.callStage
1308         });
1309     case 'full-dump':
1310     case 'full':
1311     case 'dump':
1312         return JSON.stringify([
1313             this.name,
1314             this.hasXPS,
1315             this.code,
1316             this.shortName,
1317             this.description,
1318             this.endNode,
1319             this.callStage
1320         ]);
1321     case 'plain-text':
1322     case 'plain':
1323     case 'text':
1324         return ([
1325             this.assetName,
1326             '\tname:'+this.name,
1327             '\thasXPS:'+this.hasXPS,
1328             '\tcode:'+this.code,
1329             '\tshortName:'+this.shortName,
1330             '\tdescription:'+this.description,
1331             '\tendNode:'+this.endNode,
1332             '\tcallStage:'+this.callStage
1333         ]).join('\n');
1334     default:
1335         return this.name;
1336 //        return nas.Pm.searchProp(this.name,nas.pmdb.assets);
1337     }
1338 }
1339 /**
1340     アセットコレクション
1341 */
1342 nas.Pm.AssetCollection = function(myParent){
1343     this.parent  = myParent;
1344     this.members = {};
1345     this.unique = ["assetName","name","code","shortName"];
1346 }
1347 nas.Pm.AssetCollection.prototype.entry = nas.Pm._getMember;
1348 nas.Pm.AssetCollection.prototype.addMembers = nas.Pm._addMembers;
1349 nas.Pm.AssetCollection.prototype.dump = nas.Pm._dumpList;
1350 
1351 //アセット登録メソッド
1352 nas.Pm.AssetCollection.prototype.addAsset = function(assetName,propList){
1353     this.members[assetName]             = new nas.Pm.Asset();
1354     this.members[assetName].assetName   = assetName;
1355     this.members[assetName].name        = propList[0];
1356     this.members[assetName].hasXPS      = (propList[1])?true:false;
1357     this.members[assetName].code        = propList[2];
1358     this.members[assetName].shortName   = propList[3];
1359     this.members[assetName].description = propList[4];
1360     this.members[assetName].endNode     = (propList[5])?true:false;
1361     this.members[assetName].callStage   = propList[6];
1362 }
1363 /* 
1364     return [ this[assetName].name,
1365     this[assetName].hasXPS,
1366     this[assetName].code,
1367     this[assetName].shortName,
1368     this[assetName].description,
1369     this[assetName].endNode,
1370     "["+(this[assetName].callStage).join()+"]"
1371     ];
1372 */
1373 /*データパーサ
1374 
1375 */
1376 nas.Pm.AssetCollection.prototype.parseConfig =function(configStream){
1377     if(String(configStream).length==0) return false;
1378     var newMembers=[];
1379     this.members = {};//clear
1380     var form = 'plain-text';
1381     if(configStream.match(/\{[^\}]+\}/)){
1382         form = 'JSON';
1383     } else if(configStream.match(/.+\,\[.+\]/)){
1384         form = 'full-dump';
1385     }        
1386     switch(form){
1387     case    'JSON':
1388         var configData=JSON.parse(configStream);
1389         for(prp in configData){
1390             var tempData = configData[prp];
1391             var newEntry        = new nas.Pm.Asset();
1392             newEntry.assetName   = prp;
1393             newEntry.name        = tempData.name;
1394             newEntry.hasXPS      = tempData.hasXPS;
1395             newEntry.code        = tempData.code;
1396             newEntry.shortName   = tempData.shortName;
1397             newEntry.description = tempData.description;
1398             newEntry.endNode     = tempData.endNode;
1399             newEntry.callStage   = tempData.callStage;
1400             newMembers.push(newEntry);
1401         }
1402     break;
1403     case    'full-dump':
1404     case    'full':
1405     case    'dump':
1406         configStream=String(configStream).split('\n');
1407         for(var ir = 0;ir<configStream.length;ir++){
1408             if((configStream[ir].indexOf("#")==0)||(configStream[ir].length==0)) continue;//コメント/空行スキップ
1409             var tempData = JSON.parse("["+configStream[ir]+"]");
1410             var newEntry         = new nas.Pm.Asset();
1411             newEntry.assetName    = tempData[0];
1412             newEntry.name        = tempData[1][0];
1413             newEntry.hasXPS      = tempData[1][1];
1414             newEntry.code        = tempData[1][2];
1415             newEntry.shortName   = tempData[1][3];
1416             newEntry.description = tempData[1][4];
1417             newEntry.endNode     = tempData[1][5];
1418             newEntry.callStage   = tempData[1][6];
1419             newMembers.push(newEntry);
1420         }
1421     break;
1422     case    'plain-text':
1423     case    'plain':
1424     case    'text':
1425     default:
1426         configStream=String(configStream).split('\n');
1427         var currentEntry=false;
1428         for(var ir = 0;ir<configStream.length;ir++){
1429             if((configStream[ir].indexOf("#")==0)||(configStream[ir].length==0)) continue;//コメント/空行スキップ
1430             if((configStream[ir].match( /^\t([a-z]+)\:(.+)$/i ))&&(currentEntry)){
1431                 currentEntry[RegExp.$1]=(RegExp.$1=='callStage')?(RegExp.$2).split(','):RegExp.$2;//プロパティ設定
1432             }else{
1433                 if (currentEntry) newMembers.push(currentEntry);
1434                 currentEntry=new nas.Pm.Asset();
1435                 currentEntry.assetName=String(configStream[ir]);
1436             }
1437         }
1438         newMembers.push(currentEntry);
1439     }
1440     return this.addMembers(newMembers)
1441 }
1442 
1443 nas.Pm.assets = new nas.Pm.AssetCollection(nas.Pm);
1444 
1445 /*制作管理用 PmTemplateオブジェクト
1446  *プロパティ すべて配列
1447  *  lineNames   ライン名称コレクション
1448  *  stageNames  ステージ名称コレクション
1449  *  jobName     ジョブ名称コレクション
1450  *  
1451  *  .getLines()             設定されているラインのリストを返す
1452  *  .getStageName(myLine)   ラインごとのステージ候補セットを設定順で戻す
1453  *  .getJobNames(myStage)   指定ステージのジョブ候補セットを設定順で戻す
1454  *  
1455  *  タイトルごとに設定される工程テンプレート
1456  *  ユーザが管理情報を入力する際に提示される参考データとして提示される
1457  *  記録データ的には、コレクション外の入力はOK
1458  *  コレクション外の入力は入力時にコレクションに追加されて必要に従ってマスターDBへ送信される
1459  *  アクセスメソッドを介して情報セットを引き出す
1460  *    
1461 lineNames[line]=[stage1,stage2,stage3];
1462 stageNames[stage]=[line1,line2];
1463 jobNames[job]=[stage1,stage2]
1464 line null,ALL,trunk,backgroundArt,
1465  */
1466 //ラインテンプレートコレクション配列
1467 nas.Pm.PmTemplateCollection   = function(myParent){
1468         this.parent  = myParent;
1469         this.members = [];
1470         this.unique =["line"];
1471 };
1472 /*テンプレートコレクションメンバー追加メソッド
1473 配列型のみを受け取る
1474 重複チェックはなし 上書き
1475 */
1476 nas.Pm.PmTemplateCollection.prototype.addTemplate = function(templates){
1477         if(! templates[0] instanceof Array){templates = [templates];}
1478     for (var eid = 0;eid<templates.length ; eid ++){
1479         //引数: トレーラーオブジェクトの参照,ライン識別名,ステージコレクションの内容配列
1480         this.members[eid] = new nas.Pm.LineTemplate(this,templates[eid][0],templates[eid][1]);
1481     }
1482 };
1483 nas.Pm.PmTemplateCollection.prototype.entry = nas.Pm._getMember;
1484 nas.Pm.PmTemplateCollection.prototype.addMembers = nas.Pm._addMembers;
1485 /*
1486     設定データストリームパーサ
1487 */
1488 nas.Pm.PmTemplateCollection.prototype.parseConfig = function(dataStream,form){
1489     if(! dataStream) return false;
1490     var myMembers =[];
1491     // 形式が指定されない場合は、第一有効レコードで判定
1492     if(! form ){
1493             if (dataStream.match(/\[\s*(\{[^\}]+\}\s*,\s*)+(\{[^\}]+\})?\s*\]/)) form='JSON';//配列JSON
1494             else if (dataStream.match(/(\n|^)\[.+\]($|\n)/)) form='full-dump';
1495             else  form='plain-text';
1496     }
1497     switch(form){
1498     case    'JSON':
1499         var tempObject=JSON.parse(dataStream);
1500         for (var rix=0;rix<tempObject.length;rix++){
1501             var currentMember=new nas.Pm.LineTemplate(
1502                 this,
1503                 tempObject[rix].line,
1504                 tempObject[rix].stages
1505             );
1506             myMembers.push(currentMember);
1507         }
1508     break;
1509     case    'full-dump':	
1510         dataStream = String(dataStream).split("\n");
1511         for (var rix=0;rix<dataStream.length;rix++){
1512         if((dataStream[rix].indexOf('#')==0)||(dataStream[rix].length == 0)) continue;
1513             var currentRecord=JSON.parse(dataStream[rix]);
1514             var currentMember=new nas.Pm.LineTemplate(
1515                 this,
1516                 currentRecord[0],
1517                 currentRecord[1]
1518             );
1519             if (currentMember) myMembers.push(currentMember);
1520         }
1521     break;
1522     case    'plain-text':
1523     default:
1524         dataStream = String(dataStream).split("\n");
1525       var currentMember=false;
1526       for (var rix=0;rix<dataStream.length;rix++) {
1527         if((dataStream[rix].indexOf('#')==0)||(dataStream[rix].length == 0)) continue;
1528         var currentField=dataStream[rix];
1529 /*plainフォーマット
1530 entryName
1531 	prop:value
1532 	prop:value
1533 */
1534         if((currentMember)&&(currentField.match( /^\t([a-z]+)\:(.+)$/i ))){
1535             if(RegExp.$1=='stages'){
1536                 var stages=(RegExp.$2).split(',');
1537                 for (var sid=0;sid<stages.length;sid++){
1538                    currentMember.stages.addStage(stages[sid],currentMember.parent.parent.stages.entry(stages[sid]));
1539                 }
1540             }else{
1541         	    currentMember[RegExp.$1]=RegExp.$2;//追加プロパティ用
1542         	}
1543         } else if(currentField.match( /^.+$/i )) {
1544         	if(currentMember) myMembers.push(currentMember);
1545         	currentMember = new nas.Pm.LineTemplate(this,currentField,[]);
1546         }
1547       }
1548       myMembers.push(currentMember);
1549     }
1550     return this.addMembers(myMembers);
1551 }
1552 nas.Pm.PmTemplateCollection.prototype.dump = nas.Pm._dumpList;
1553 /**
1554     ラインテンプレート ステージデータコレクションを持つ
1555 引数
1556 myParent    
1557 lineName    ライン識別名称
1558 [myStarges]   ラインの標準的なステージ並びを配列で与える 空配列で初期化可能
1559 
1560 */
1561 nas.Pm.LineTemplate = function(myParent,lineName,myStages){
1562     if (!(myStages instanceof Array)) myStages = [myStages];
1563     this.parent = myParent;//親参照は不要?
1564     this.line   = this.parent.parent.lines.getLine(lineName);
1565     this.stages = new nas.Pm.StageCollection(this);
1566     for (var ix=0;ix< myStages.length;ix++){
1567         var stageKey= nas.Pm.searchProp(myStages[ix],this.parent.parent.stages)
1568         this.stages.addStage(stageKey,this.parent.parent.stages.entry(stageKey));
1569     }
1570 };
1571 /*
1572 toString(true) でテキスト設定形式で書き出す
1573 
1574 */
1575 nas.Pm.LineTemplate.prototype.toString = function(form){
1576     switch(form){
1577     case 'JSON':
1578         return JSON.stringify({
1579            line: this.line.toString(),
1580            stages:(this.stages.dump()).split(',')
1581         });
1582     case 'full-dump':
1583     case 'full':
1584     case 'dump':
1585       return JSON.stringify([
1586         this.line.toString(),
1587         (this.stages.dump()).split(',')
1588       ]);
1589     break;
1590     case 'plain-text':
1591     case 'plain':
1592     case 'text':
1593       return ([
1594         this.line.toString(),
1595         '\tstages:'+this.stages.dump()
1596       ]).join('\n');
1597     break;
1598     default:
1599         return this.line.toString();
1600     }
1601 };
1602 
1603 nas.Pm.pmTemplates = new nas.Pm.PmTemplateCollection(nas.Pm);
1604 
1605 /*制作管理用 Jobオブジェクト
1606  *プロパティ
1607  * name String: ジョブ名
1608  * // line Object:Line 所属ライン<<不要 stage にライン情報が含まれるので不用
1609  * stage Object:Stage 所属ステージ
1610  * type Number:typeID 0:init/1:primary/2~:check/ 当該Jobのタイプ
1611  * id Number:Index ステージ内でのユニークID 自己アクセスのための配列インデックスを内部保持
1612  * jobId生成規則
1613  * 管理単位所属ステージ内部でユニークな整数ID 重複不可 飛び番等は許容される
1614  * DB連結時はDBへの照合プロパティになるので初期化時には引数として外部から与えるのが基本
1615  * 引数が与えられない(=DB連結が無い)場合は、その場での自動生成を行う
1616  * その際のルールは、同PmStage内部での出現順連番 0はStartupJobとして予約 
1617  * currentStatus String:ステータス startup|active<>hold|fixed
1618  * createUser String:UID
1619  * createDate String:DATE
1620  * updateUser String:UID
1621  * updateDate String:DATE
1622  * slipNumber String:伝票番号
1623  *new Job(jobName?)
1624 */
1625 nas.Pm.ProductionJob = function ProductionJob(myName,myStage,myIndex,mySlipNumber){
1626     this.name           = myName;//*
1627     this.stage          = myStage;
1628 // if(! myStage){alert("stage Argument is :"+myStage)}
1629     this.type           ;
1630     this.id             = (typeof myIndex == "undefined")? null:myIndex;//*
1631     this.currentStatus  ;//
1632     this.createUser     ;//
1633     this.createDate     = new Date();//
1634     this.updateUser     ;//
1635     this.updateDate     ;//
1636     this.slipNumber     ;//*
1637 };
1638 nas.Pm.ProductionJob.prototype.getPath = function(){return [this.name,this.stage.getPath()].join(".");}
1639 
1640 nas.Pm.ProductionJob.prototype.toString = function(){
1641     var myResult        = "";
1642 // myResult            += "##["+this.stage.name+"][["+this.name+"]"+"id:"+this.id+"]\n";
1643     myResult            += "##[["+this.name+"]"+"id:"+this.id+"]\n";
1644     myResult            += "##created="+this.createDate+"/"+this.createUser+";\n";
1645     myResult            += "##updated="+this.createDate+"/"+this.createUser+";\n";
1646 
1647     if(this.manager)    myResult += "##manager="+this.manager+";\n";
1648     if(this.worker)     myResult += "##worker="+this.worker+";\n";
1649     if(this.slipNumber) myResult += "##slipNumber="+this.slipNumber+";\n";
1650 
1651     var myGroups        = new Array();
1652     var myMapElements   = this.stage.line.parent.elementStore;
1653     //エレメント総当りで ジョブに対応するグループを抽出
1654     for (var eID=0 ; eID < myMapElements.length ; eID++){
1655      if((myMapElements[eID] instanceof nas.xMapGroup)&&(myMapElements[eID].link===this.stage)){
1656       myGroups.push(myMapElements[eID].link); 
1657      }
1658     }
1659     //登録グループごとにエレメント総当りで ジョブ内のグループに対応するエレメントを抽出して出力に加算
1660     for (var gID=0;gID<myGroups.length;gID++){
1661         myResult+="["+myGroup[gID].name+"\t"+myGroup[gID].type+"]\n";
1662         for (var eID=0;eID<myMapElements.length;eID++){
1663             if((myMapElements[eID] instanceof nas.xMapElement)&&(myMapElements[eID].link===this)){
1664                 myResult+=myMapElements[eID].toString();//
1665             }
1666         }
1667 //  myResult+="["+myGroup[gID].name+"]/\n";//グループ終了子は省略可
1668     }
1669 //    myResult+="##[["+this.name+"]]/\n";//終了子をここでは出力しない 呼び出し側で処置 
1670     return myResult;
1671 };
1672 /**
1673     JOB名称ストア
1674     クラス内でDBとして働くコレクション
1675     このオブジェクト(配列)がDBと通信を行う
1676     引数:   jobName,targetStage,jobType
1677             ジョブ名,所属ステージ名,ジョブタイプ
1678     配列要素は引数の配列である必要あり。
1679     実際のジョブは定義されるものではなく、名称をその場で決めて開始することが可能
1680     これらの設定は、
1681  */
1682 nas.Pm.JobTemplate = function(jobName,targetStage,jobType){
1683     this.jobName   = jobName    ;
1684     this.targetStage  = targetStage;
1685     this.jobType   = jobType    ;
1686 };
1687 nas.Pm.JobTemplate.prototype.toString = function(form){
1688     switch(form){
1689     case    'JSON':
1690         return JSON.stringify({
1691             jobName:this.jobName,
1692             targetStage:this.targetStage,
1693             jobType:this.jobType
1694         });
1695     break;
1696     case    'full-dump':
1697     case    'full':
1698     case    'dump':
1699         return JSON.stringify([this.jobName,this.targetStage,this.jobType]);
1700     break;
1701     case    'plain-text':
1702     case    'plain':
1703     case    'text':
1704         return ([
1705             this.jobName,
1706             "\ttargetStage:"+this.targetStage,
1707             "\tjobType:"+this.jobType
1708         ]).join('\n');
1709     break;
1710     default:
1711         return this.jobName;
1712     }
1713 };
1714 nas.Pm.JobTemplateCollection = function(myParent){
1715     this.parent  = myParent ;
1716     this.members = [];
1717 }
1718 /**
1719     ジョブテンプレートコレクション
1720     一括登録メソッド
1721     
1722 */
1723 nas.Pm.JobTemplateCollection.prototype.addNames = function(names){
1724     if(! names[0] instanceof Array){names = [names];}
1725     for (var eid = 0;eid<names.length ; eid ++){
1726         this.members[eid] = new nas.Pm.JobTemplate(names[eid][0],names[eid][1],names[eid][2]);
1727     }
1728 }
1729 nas.Pm.JobTemplateCollection.prototype.addMembers = nas.Pm._addMembers;
1730 /**
1731     テンプレート取得
1732     引数に従ってJobテンプレートから必要な集合を抽出して返す
1733 引数:
1734     ステージキーワード   layout LO レイアウト 等
1735     ジョブタイプ  init/primary/check/* ジョブタイプ'*'は primary+check (! init)
1736 */
1737 nas.Pm.JobTemplateCollection.prototype.getTemplate = function(stage,type){
1738     if((! stage)||(! type)){return []};
1739     var result=[];
1740     for (var eid = 0;eid<this.members.length ; eid ++){
1741         if((this.members[eid].jobType == type)||(this.members[eid].jobType == "*")||(type == "*")&&(this.members[eid].jobType != "init")){
1742             if((this.parent.stages.getStage(this.members[eid].targetStage) === this.parent.stages.getStage(stage))||(this.members[eid].targetStage == "*")){
1743                 var jobName         = this.members[eid].jobName;
1744                 var parentStage = this.parent.stages.getStage(stage);
1745                 if(( jobName.indexOf("*") >= 0)&&(parentStage)){
1746                     var myString = jobName.replace(/\*/,parentStage.name);
1747                 }else{
1748                     var myString = jobName;
1749                 }
1750                 result.push(myString);
1751             }
1752         }
1753     }
1754     return result;
1755 }
1756 nas.Pm.JobTemplateCollection.prototype.dump = nas.Pm._dumpList;
1757 /*  設定パーサ
1758 nas.Pm.JobTemplate (jobName,targetStage,jobType)
1759 */
1760 nas.Pm.JobTemplateCollection.prototype.parseConfig = function(dataStream,form){
1761     var myMembers =[];
1762     // 形式が指定されない場合は、第一有効レコードで判定
1763     if(! form ){
1764             if (dataStream.match(/\[\s*(\{[^\}]+\}\s*,\s*)+(\{[^\}]+\})?\s*\]/)) form='JSON';//配列JSON
1765             else if (dataStream.match(/(\n|^)\[.+\]($|\n)/)) form='full-dump';
1766             else  form='plain-text';
1767     }
1768     switch(form){
1769     case    'JSON':
1770         var tempObject=JSON.parse(dataStream);
1771         for (var rix=0;rix<tempObject.length;rix++){
1772             var currentMember=new nas.Pm.JobTemplate(
1773                 tempObject[rix].jobName,
1774                 tempObject[rix].targetStage,
1775                 tempObject[rix].jobType
1776             );
1777             myMembers.push(currentMember);
1778         }
1779     break;
1780     case    'full-dump':	
1781         dataStream = String(dataStream).split("\n");
1782         for (var rix=0;rix<dataStream.length;rix++){
1783         if((dataStream[rix].indexOf('#')==0)||(dataStream[rix].length == 0)) continue;
1784             var currentRecord=JSON.parse(dataStream[rix]);
1785             var currentMember=new nas.Pm.JobTemplate(
1786                 currentRecord[0],
1787                 currentRecord[1],
1788                 currentRecord[2]
1789             );
1790             if (currentMember) myMembers.push(currentMember);
1791         }
1792     break;
1793     case    'plain-text':
1794     default:
1795         dataStream = String(dataStream).split("\n");
1796       var currentMember=false;
1797       for (var rix=0;rix<dataStream.length;rix++) {
1798         if((dataStream[rix].indexOf('#')==0)||(dataStream[rix].length == 0)) continue;
1799         var currentField=dataStream[rix];
1800 /*plainフォーマット
1801 entryName
1802 	prop:value
1803 	prop:value
1804 */
1805         if((currentMember)&&(currentField.match( /^\t([a-z]+)\:(.+)$/i ))){
1806         	currentMember[RegExp.$1]=RegExp.$2;
1807         } else if(currentField.match( /^.+$/i )) {
1808         	if(currentMember) myMembers.push(currentMember);
1809         	currentMember = new nas.Pm.JobTemplate(currentField);
1810         }
1811       }
1812       myMembers.push(currentMember);
1813     }
1814     return this.addMembers(myMembers);
1815 }
1816 /*
1817 function(form){
1818     if(form == 'JSON'){
1819         return JSON.stringify(this.members);//JSON.stringify不能なオブジェクトがあるので注意
1820     }else if(form == 'dump'){
1821         var result="[";
1822         for (var ix =0 ; ix<this.members.length;ix++){
1823             result += this.members[ix].toString('dump');
1824             result += ((ix+1)<this.members.length)? ",\n":"]\n";
1825         }
1826         return result;
1827     }else{
1828         var result="[";
1829         for (var ix =0 ; ix<this.members.length;ix++){
1830             result += this.members[ix].toString(true);
1831             result += ((ix+1)<this.members.length)? ",\n":"]\n";
1832         }
1833         return result;
1834     }
1835 }
1836 */
1837 nas.Pm.jobNames = new nas.Pm.JobTemplateCollection(nas.Pm);
1838 
1839 
1840 
1841 /*制作管理用 Stageオブジェクト
1842  *
1843  * name String 識別名称:作品内での一意性を求められる
1844  * line Object ステージが所属するラインへの参照
1845  * code String 省略表記用短縮コード 2〜3バイトを推奨 ユニークであること
1846  * shortName String 画面表示用略称 8文字程度までを推奨 指定のない場合はnameを転用
1847  * description String ステージの説明 ユーザのために必用
1848  * output Asset ステージの出力アセット
1849  * staffs Object スタッフリスト(リスト)
1850  * ステージは必ずステージコレクションを介してラインに所属するので、親ラインの参照はコレクション側のline属性で保持する。
1851  * ステージ内では、コレクションを parent プロパティで示す 従って親のラインを参照するパスは this.parent.line
1852 */
1853 nas.Pm.ProductionStage=function(myName,myParent){
1854     this.parent=myParent;
1855     this.name=myName;
1856     this.code;
1857     this.shortName;
1858     this.description;
1859     this.output;
1860     this.stageName;
1861 }
1862 //nas.Pm.ProductionStage.prototype.getPath=function(){return [this.name,this.parent.line.getPath()].join(".")}
1863 nas.Pm.ProductionStage.prototype.getPath=function(){return [this.name,this.line.getPath()].join(".")}
1864 nas.Pm.newStage=function(myStage,myLine){
1865     var newStage= nas.Pm.stages.getStage(myStage);//参照をとっているが、これは複製?
1866     if(newStage){
1867         newStage.line=myLine;
1868         return newStage;
1869     }else{
1870   //ステージは未登録なので、新規ステージ編集?
1871         return new nas.Pm.ProductionStage(myStage,myLine);
1872    }
1873 }
1874 nas.Pm.ProductionStage.prototype.toString=function(form){
1875     
1876     switch(form){
1877     case 'JSON':
1878         return JSON.stringify({
1879             name:this.name,
1880             code:this.code,
1881             shortName:this.shortName,
1882             description:this.description,
1883             output:this.output,
1884             stageName:this.stageName
1885         });
1886     break;
1887     case 'full-dump':
1888     case 'full':
1889     case 'dump':
1890         return JSON.stringify([
1891             this.name,
1892             this.code,
1893             this.shortName,
1894             this.description,
1895             this.output
1896         ]);
1897     break;
1898     case 'plain-text':
1899     case 'plain':
1900     case 'text':
1901         return ([
1902             this.stageName,
1903             "\tname:"+this.name,
1904             "\tcode:"+this.code,
1905             "\tshortName:"+this.shortName,
1906             "\tdescription:"+this.description,
1907             "\toutput:"+this.output
1908         ]).join('\n');
1909 
1910     default:
1911     return this.name;
1912     }
1913 };
1914 /*    ステージコレクション
1915  *
1916  *クラス内でDBとして働くオブジェクト
1917  *このオブジェクトがDBと通信する
1918  ステージにテンプレートとしてスタッフコレクションを持たせる拡張を行う
1919 */
1920 nas.Pm.StageCollection = function(myParent){
1921     this.parent  = myParent;
1922     this.members = {};
1923     this.unique =["stageName","name","code","shortName"];
1924 }
1925 
1926 nas.Pm.StageCollection.prototype.dump = nas.Pm._dumpList;
1927 nas.Pm.StageCollection.prototype.getStage = nas.Pm._getMember;
1928 nas.Pm.StageCollection.prototype.entry = nas.Pm._getMember;
1929 nas.Pm.StageCollection.prototype.addMembers = nas.Pm._addMembers
1930 //ステージコレクション追加メソッド
1931 /*
1932 引数:
1933 stageName
1934 myStage ステージオブジェクト または プロパティリスト配列
1935 */
1936 nas.Pm.StageCollection.prototype.addStage=function(stageName,myStage){
1937     if(myStage instanceof nas.Pm.ProductionStage){
1938         this.members[stageName]= myStage;
1939     }else if(myStage instanceof Array){
1940     this.members[stageName] = new nas.Pm.ProductionStage(myStage[0],null);
1941 //    this.members[stageName].name=myStage[0];
1942     this.members[stageName].code        = myStage[1];
1943     this.members[stageName].shortName   = myStage[2];
1944     this.members[stageName].description = myStage[3];
1945     this.members[stageName].output      = myStage[4];
1946     this.members[stageName].stageName   = stageName;
1947     }
1948 }
1949 
1950 /*
1951 設定パーサ
1952 */
1953 nas.Pm.StageCollection.prototype.parseConfig = function(configStream){
1954     if(String(configStream).length==0) return false;
1955     var newMembers=[];
1956     this.members = {};//clear
1957     var form = 'plain-text';
1958     if(configStream.match(/\{[^\}]+\}/))          form = 'JSON';
1959     else if(configStream.match(/.+\,\[.+\]/)) form = 'full-dump';
1960     switch(form){
1961     case 'JSON':
1962         var configData=JSON.parse(configStream);
1963         for(prp in configData){
1964             var tempData = configData[prp];
1965             var newStage         = new nas.Pm.ProductionStage(prp,this);
1966             newStage.stageName   = prp;
1967             newStage.name        = tempData.name;
1968             newStage.code        = tempData.code;
1969             newStage.shortName   = tempData.shortName;
1970             newStage.description = tempData.description;
1971             newStage.output      = tempData.output;
1972             newMembers.push(newStage);
1973         }
1974     break;
1975     case 'full-dump':
1976         configStream=String(configStream).split('\n');
1977         for(var ir = 0;ir<configStream.length;ir++){
1978             if((configStream[ir].indexOf("#")==0)||(configStream[ir].length==0)) continue;//コメント/空行スキップ
1979             var tempData = JSON.parse("["+configStream[ir]+"]");
1980             var newStage         = new nas.Pm.ProductionStage(tempData[1][0],this);
1981             newStage.stageName   = tempData[0];
1982             newStage.name        = tempData[1][0];
1983             newStage.code        = tempData[1][1];
1984             newStage.shortName   = tempData[1][2];
1985             newStage.description = tempData[1][3];
1986             newStage.output      = tempData[1][4];
1987             newMembers.push(newStage);
1988         }
1989     break;
1990     default:
1991         configStream=String(configStream).split('\n');
1992         var currentStage=false;
1993         for(var ir = 0;ir<configStream.length;ir++){
1994             if((configStream[ir].indexOf("#")==0)||(configStream[ir].length==0)) continue;//コメント/空行スキップ
1995             if((configStream[ir].match( /^\t([a-z]+)\:(.+)$/i ))&&(currentStage)){
1996                 currentStage[RegExp.$1]=RegExp.$2;//プロパティ設定
1997             }else{
1998                 if (currentStage) newMembers.push(currentStage);
1999                 currentStage=new nas.Pm.ProductionStage(configStream[ir],this);
2000                 currentStage.stageName=String(configStream[ir]);
2001             }
2002         }
2003         newMembers.push(currentStage);
2004     }
2005     return this.addMembers(newMembers);
2006 }
2007 /**
2008     次のステージの候補を抽出する関数
2009 引数:
2010     ライン識別子
2011     ステージ識別子
2012 基本構造
2013     引数のステージの出力アセットから、そのアセットが呼び出し可能なステージを得て展開する
2014     
2015 */
2016 nas.Pm.StageCollection.prototype.getTemplate = function(stageName){
2017     var result=[];
2018     var myStageAsset =this.parent.assets.entry(this.getStage(stageName).output);
2019     var newStageList=(myStageAsset)? myStageAsset.callStage:[];
2020     for (var idx = 0 ;idx < newStageList.length ; idx ++){
2021         var myStage = this.getStage(newStageList[idx]);//null可能性あり
2022         if(myStage) result.push(myStage.name);
2023     }
2024     return result;
2025 }
2026 /**
2027     ステージコレクション内からスタートアップ候補(開始デフォルト)のステージを取得するメソッド
2028     第一ステージとなるアイテムはステージコレクションに最初に置かれたステージ
2029     for( itm in this.menbers) で最初に出てくるステージのこと
2030     ↑これはgetStage=_getMember に統合 したので不要
2031 
2032 nas.Pm.StageCollection.prototype.getStartup =function(){
2033     for(itm in this.members){return itm;break;}
2034 }
2035 */
2036 
2037 nas.Pm.stages = new nas.Pm.StageCollection(nas.Pm);
2038 /*
2039 定義テーブルからテンプレートを取得するための機能
2040 名前と検索先(指定がない場合はcallerから判定)を与えて、その定義テーブル内のオブジェクト引数を返す
2041 あたるべきプロパティはname,code,shortName,fullName オブジェクトによってはいくつかのプロパティを持たないものものある
2042 
2043 */
2044 
2045 /*制作管理用 ProductionLineオブジェクト
2046 
2047 name String 識別名称:作品内での一意性を求められる
2048 shortName String 画面表示用略称 8文字程度までを推奨 指定のない場合はnameを転用
2049 outputAsset Asset ラインの出力アセット
2050 initAsset Asset ラインの入力アセット
2051 code String 省略表記用短縮コード 2〜3バイトを推奨 ユニークであること
2052 description String ラインの説明 ユーザのために必用
2053 
2054 */
2055 
2056 nas.Pm.ProductionLine=function(lineName){
2057     this.lineName;
2058     this.name;
2059     this.shortName;
2060     this.outputAsset;
2061     this.initAsset;
2062     this.code;
2063     this.description;
2064 }
2065 
2066 nas.Pm.ProductionLine.prototype.getPath = function(){return this.name;}
2067 
2068 nas.Pm.ProductionLine.prototype.toString = function(form){
2069     switch (form){
2070     case 'JSON':
2071         return JSON.stringify({
2072             name:this.name,
2073             shortName:this.shortName,
2074             outputAsset:((this.outputAsset)?this.outputAsset.toString():this.outputAsset),
2075             initAsset:((this.initAsset)?this.initAsset.toString():this.initAsset),
2076             code:this.code,
2077             description:this.description
2078         });
2079     break;
2080     case 'full-dump':
2081     case 'full':
2082     case 'dump':
2083         return JSON.stringify([
2084             this.name,
2085             this.shortName,
2086             (this.outputAsset)?this.outputAsset.toString():this.outputAsset,
2087             (this.initAsset)?this.initAsset.toString():this.initAsset,
2088             this.code,
2089             this.description
2090         ]);
2091     break;
2092     case 'plain-text':
2093     case 'plain':
2094     case 'text':
2095         return ([
2096             this.lineName,
2097             '\tname:'+this.name,
2098             '\tshortName:'+this.shortName,
2099             '\toutoputAsset:'+((this.outputAsset)?this.outputAsset.toString():null),
2100             '\tinitAsset:'+((this.initAsset)?this.initAsset.toString():null),
2101             '\tcode:'+this.code,
2102             '\tdescription:'+this.description
2103         ]).join('\n');
2104     break;
2105     default:
2106         return this.name
2107     }
2108 };
2109 /*    ラインストア
2110 
2111 クラス内でDBとして働くコレクションオブジェクト
2112 このオブジェクトがタイトルの下に入り上位オブジェクトがDBと通信する
2113 */
2114 nas.Pm.LineCollection = function(myParent){
2115     this.parent  = myParent;
2116     this.members = {};
2117     this.unique =["lineName","name","code","shortName"];
2118 }
2119 
2120 nas.Pm.LineCollection.prototype.dump = nas.Pm._dumpList;
2121 //function(){    return JSON.stringify(this.members);}
2122 nas.Pm.LineCollection.prototype.addMembers = nas.Pm._addMembers;
2123 
2124 /**
2125 ラインテンプレートの中から指定された名前と一致するオブジェクトを戻す
2126 lineNameと一致していればそのまま、一致するものがない場合はname/shortName/codeを検索してその順で最初に一致したものを戻す
2127 */
2128 nas.Pm.LineCollection.prototype.getLine = nas.Pm._getMember;
2129 nas.Pm.LineCollection.prototype.entry = nas.Pm._getMember;
2130 
2131 /*設定パーサ
2132 
2133 */
2134 
2135 nas.Pm.LineCollection.prototype.parseConfig =function(configStream){
2136     if(String(configStream).length==0) return false;
2137     var newMembers=[];
2138     this.members = {};//clear
2139     var form = 'plain-text';
2140     if(configStream.match(/\{[^\}]+\}/)){
2141         form = 'JSON';
2142     } else if(configStream.match(/.+\,\[.+\]/)){
2143         form = 'full-dump';
2144     }        
2145     switch(form){
2146     case    'JSON':
2147         var configData=JSON.parse(configStream);
2148         for(prp in configData){
2149             var tempData = configData[prp];
2150             var newLine         = new nas.Pm.ProductionLine();
2151             newLine.lineName    = prp;
2152             newLine.name        = tempData.name;
2153             newLine.shortName   = tempData.shortName;
2154             newLine.outputAsset = tempData.outputAsset;
2155             newLine.initAsset   = tempData.initAsset;
2156             newLine.code        = tempData.code;
2157             newLine.description  = tempData.description;
2158             newMembers.push(newLine);
2159         }
2160     break;
2161     case    'full-dump':
2162     case    'full':
2163     case    'dump':
2164         configStream=String(configStream).split('\n');
2165         for(var ir = 0;ir<configStream.length;ir++){
2166             if((configStream[ir].indexOf("#")==0)||(configStream[ir].length==0)) continue;//コメント/空行スキップ
2167             var tempData = JSON.parse("["+configStream[ir]+"]");
2168             var newLine         = new nas.Pm.ProductionLine();
2169             newLine.lineName    = tempData[0];
2170             newLine.name        = tempData[1][0];
2171             newLine.shortName   = tempData[1][1];
2172             newLine.outputAsset = tempData[1][2];
2173             newLine.initAsset   = tempData[1][3];
2174             newLine.code        = tempData[1][4];
2175             newLine.description = tempData[1][5];
2176             newMembers.push(newLine);
2177         }
2178     break;
2179     case    'plain-text':
2180     case    'plain':
2181     case    'text':
2182     default:
2183         configStream=String(configStream).split('\n');
2184         var currentLine=false;
2185         for(var ir = 0;ir<configStream.length;ir++){
2186             if((configStream[ir].indexOf("#")==0)||(configStream[ir].length==0)) continue;//コメント/空行スキップ
2187             if((configStream[ir].match( /^\t([a-z]+)\:(.+)$/i ))&&(currentLine)){
2188                 currentLine[RegExp.$1]=RegExp.$2;//プロパティ設定
2189             }else{
2190                 if (currentLine) newMembers.push(currentLine);
2191                 currentLine=new nas.Pm.ProductionLine();
2192                 currentLine.lineName=String(configStream[ir]);
2193             }
2194         }
2195         newMembers.push(currentLine);
2196     }
2197     return this.addMembers(newMembers)
2198 }
2199 /*
2200     ライン編集メソッド
2201 */
2202 
2203 nas.Pm.LineCollection.prototype.addLine =function(lineName,propList){
2204     this.members[lineName]              =new nas.Pm.ProductionLine();
2205     this.members[lineName].lineName     =lineName;
2206     this.members[lineName].name         =propList[0];
2207     this.members[lineName].shortName    =propList[1];
2208     this.members[lineName].outputAsset  =nas.Pm.assets.entry(propList[2]);
2209     this.members[lineName].initAsset    =nas.Pm.assets.entry(propList[3]);
2210     this.members[lineName].code         =propList[4];
2211     this.members[lineName].description  =propList[5];
2212 }
2213 
2214 
2215 nas.Pm.lines = new nas.Pm.LineCollection(nas.Pm);
2216 
2217 //制作管理用 SCオブジェクト
2218 /*
2219 新規オブジェクト作成は以下のクラスメソッドを利用されたし
2220 
2221 nas.Pm.newSC(カット識別子,時間指定文字列);
2222 
2223 カット識別子を与えて初期化
2224 分解はクラスメソッドで行う
2225 格納はプロパティを分けて、可能ならばDBと比較対照して校正する?
2226 識別子がフルで与えられなかった場合は、現在アクティブなPmでポイントしている作品で補う
2227 
2228 識別子はMapフォーマットドキュメントを参照
2229 [TITLE(セパレータ)][Oo#]OPUS(セパレータ)][[Ss]SCENE(セパレータ)?[Cc]CUT
2230 例:
2231 nas.Pm.newSC("ktc#01.s-c123","3+12,OL(1+12),--(0+0)",framerate)
2232 
2233 */
2234 /**
2235  * SCオブジェクトコンストラクタ
2236  * コンストラクタの引数は、分離を終えた状態で与える
2237  * プロパティの不足は呼び出し側(newSCi)で行う
2238  * コンストラクタ内でのチェックはしない
2239  */
2240 nas.Pm.SCi = function SC(cutName,sceneName,myOpus,myTitle,myTime,myTRin,myTRout,myRate,myFrate,myId){
2241     this.id         = myId ;//DB連結用 DBに接続していない場合はundefined
2242     this.cut        = cutName;//
2243     this.scene      = sceneName;//
2244     this.opus       = myOpus;//Object nas.Pm.Opus
2245     this.title      = myTitle;//Object nsa.Pm.WorkTitle参照
2246     this.time       = myTime;//ここでは静的プロパティ  フレーム数で記録
2247     this.trin       = myTRin;//[0,"trin"];//後で初期化
2248     this.trout      = myTRout;//[0,"trout"];//後で初期化
2249     this.framerate  = myFrate; //Object nas.Framerate;
2250 }
2251 nas.Pm.newSCi = function(idString,index){
2252     var mySCi=Xps.parseIdentifier(idString)
2253     var mySC= new nas.PM.SC(
2254         mySCi.cut,
2255         mySCi.scene,
2256         mySCi.opus,
2257         mySCi.title,
2258         nas.FCT2Frm(mySCi.time),
2259         "",
2260         "",
2261         mySCi.framerate
2262     )
2263     return mySC
2264 }
2265 
2266 
2267 nas.Pm.SCi.prototype.toString =function(){
2268     var myResult="";
2269     if(arguments.length){
2270         myResult+= "##CUT="+this.cut+"\n";
2271         myResult+= "##SCENE="+this.scene+"\n";
2272         myResult+= this.opus.toString(true)+"\n";
2273         myResult+= "##TIME="+this.time+"\n";
2274         myResult+= "##TRIN="+this.trin+"\n";
2275         myResult+= "##TROUT="+this.trout+"\n";
2276         myResult+= "##FRAME_RATE="+this.cut+"\n";
2277     }else{
2278         myResult+=["s",this.scene,"-c",this.cut].join("");
2279     }
2280        return myResult;
2281 };//
2282 nas.Pm.SCi.prototype.valueOf =function(){return this.id;};//
2283 
2284 /**
2285        引数をまとめて解釈してSCiオブジェクトを返すPmクラスメソッド
2286 
2287 
2288 */
2289 nas.Pm.newSC=function(myTitleString,myTimeString,myRateString){
2290     var myInfo      = nas.separateTitleString(myTitleString)
2291     var myOpus      = nas.newOpus(myInfo[2],myInfo[3]);
2292     var myTimeInfo  = nas.perseTimeString(myTimeString);
2293     var myRate      = (myRateString)? new nas.Framerate(myRateString):myOpus.workTitle.framerate;
2294     var myTime      = myTimeInfo[0];
2295     var myTrin      = myTimeInfo[1];
2296     return new nas.Pm.SCi(myInfo[0],myInfo[1],myOpus,myTimeInfo[0],myTimeInfo[1],myTimeInfo[2],myRate);
2297 }
2298 //Test
2299 // A=nas.Pm.newSC("mte02")
2300 
2301 /*
2302     管理ライン
2303 
2304 new nas.Pm.Issue(Line or LineName,IssueID)
2305 発行されたライン情報
2306 .lineId; String/ 識別ID  文字列処理 専用のパーサを作る
2307 .lineName; String/ 識別名
2308 .line Object/Line 
2309 .checkOut; string/ date / user
2310 .checkOutDate; string/ date / user
2311 .checkIn; String/ date / user
2312 .checkInDate; String/ date / user
2313 .currentStatus; String/ Startup,Active,Hold,Fixed,Aborted
2314 制御関連は各ステージの持つアセットがステージ内で完結する構造により無用の概念となる
2315 更新権利の概念は消失したので不要 これを持って制御する事項が無い
2316 アセット(ステージ)間の衝突の検知は必用
2317 
2318 作業状態の遷移
2319      startup 初期化状態(未着手)
2320      ↓(一方通行)
2321      active ⇐⇒ hold
2322      ↓    ↓
2323         fixed/aborted
2324 activeには本作業中とチェック作業中が含まれる
2325 holdは、作業をサーバ側で預かっている状態 作業権限の無いユーザはアクティブに遷移出来ない
2326 fixedは、ラインの作業が完成した状態
2327 abortedは、ライン自体が中断(破棄)された状態 中断からの復帰が可能なので reject.discard,destruct 等では無いが実質同等
2328 ステータス属性はラインの状態
2329 
2330 */
2331 nas.Pm.Issue=function(myLine,myID){
2332     this.line=(myLine instanceof nas.Pm.ProductionLine)? myLine:undefined;//Object:Pm.line if exists link
2333     //名前指定時は 次の拡張では初期化時点でシステム上の既存ラインを検索、存在しない場合はライン新規登録用の機能を呼び出す
2334     this.lineId             = new String(myID);//String:index 
2335     this.lineName           = (myLine instanceof nas.Pm.ProductionLine)? this.line.name:myLine;//String:name
2336     this.lineCheckOut       = nas.CURRENTUSER;//String:uid
2337     this.lineCheckOutDate   = new Date();//Date;
2338     this.lineCheckIn        ;//String:uid undefined
2339     this.lineCheckInDate    ;//Date: undefined
2340     this.currentStatus      = "startup";//String:startup active hold fixed aborted 
2341 }
2342 /*
2343     IssueにJSONの入出力を設置する必用あり
2344 */
2345 nas.Pm.Issue.prototype.toString = function(){
2346     var myResult = "";
2347     myResult     += "##CHECK_OUT=("+this.lineName+"):"+this.lineId+" / "+this.lineCheckOutDate.toNASString()+" / "+this.lineCheckOut +";\n";
2348     if(this.lineCheckInDate)
2349     myResult     += "##CHECK_IN=("+this.lineName+"):"+this.lineId+" / "+this.lineCheckInDate+" / "+this.lineCheckIn +";\n";
2350     return myResult
2351 }
2352 /*
2353     LineIssues
2354     issue  コレクション
2355     発行されたライン記述をパースする機能と文字列化する機能をオブジェクトメソッドで加える
2356     支線の発行/合流機能を持たせる
2357     LineIssues.branch(newIssue) : boranchedLines
2358      自分自身の現在のコピーをつくって新たなIssues オブジェクトで返す
2359     LineIssues.merge(LineIssues) : mergedLines
2360      与えられたIssuesをマージする。コンフリクト判定が必用
2361     LineIssues.parse(LineString) : initLinesItself
2362     
2363 
2364 
2365     本体にチェックインされてクローズされたブランチを戻す
2366     引数無しでコールされた場合、条件をチェックして可能なら本体をクローズする
2367     
2368     LineIssues.toString() : LineString
2369     LineIssues.valueOf() : currentIssue
2370 
2371 これらのメソッドは、さらに外側のxMapにも同名メソッドが必用
2372 このメソッドはそちらから呼ばれるのが前提でありユーザやアプリケーションが直接呼び出すことは無いとしておく
2373 不正引数のハンドリングはしない
2374 
2375 ライン発行コレクションはラインの作業状態を保持するコレクション
2376 作業状態はアクティブなラインの作業状態を保存する>>ステージ/ジョブの作業状態が反映される
2377 日付情報は、チェックアウト・チェックインを独自に保存
2378 */
2379 nas.Pm.LineIssues=function LineIssues(myIssue,myParent){
2380     this.currentId=0;//Int currentLine Array index
2381     this.body=[myIssue];// Object Array;
2382     this.parent=myParent;//Object xMap;
2383 }
2384 nas.Pm.LineIssues.prototype.valueOf =function(){return this.body[this.id]};
2385 /*
2386     Issue
2387 */
2388 nas.Pm.LineIssues.prototype.toString=function(){
2389     var myResult="";
2390     myResult+="##LINE_ID=("+ this.body[this.currentId].lineName +"):"+this.body[this.currentId].lineId+"\n";
2391     myResult+="##currentStatus="+ this.body[this.currentId].currentStatus+"\n";
2392     for (var iix=0;iix<this.body.length;iix++){myResult+=this.body[iix].toString();}
2393     return myResult;
2394 }
2395 /* branch(新規ライン名)
2396     ブランチ
2397     既存のラインと同名のブランチが指定された場合ブランチは失敗 false を戻す
2398     ただし現在の実装だと、支線側で親のラインを把握していないので 重複の可能性を排除できない
2399     DB接続時は、マスターDBに問い合わせを行う処理が必用
2400     最終的には同名のラインは許容される
2401 */
2402 nas.Pm.LineIssues.prototype.branch=function(newLineName){
2403     for(var ix =0;ix<this.body.length;ix++){if(this.body[ix].lineName==newLineName) return false;};//重複をリジェクト
2404     var currentDepth    = this.body[this.currentId].lineId.split(":").length;//現在の分岐深度
2405     var branchCount     = 0;
2406     for(var ix =0;ix<this.body.length;ix++){if(this.body[ix].lineId.split(":").length==currentDepth) branchCount++;};
2407     var newBranchId     = (this.body[this.currentId].lineId=="0")? branchCount :this.body[this.currentId].lineId+":"+branchCount;
2408     var newIssue        = new nas.Pm.Issue(newLineName,newBranchId);
2409     this.body.push(newIssue);
2410     var newIssues       = new nas.Pm.LineIssues(newIssue);
2411     return newIssues;
2412 }
2413 /* merge(Issueオブジェクト)
2414 //支線のIssues配列をマージする 今日は検査は保留20160505
2415 マージの手順
2416 マージされる側のラインのステータスを検査 startup,active,hold のラインはマージ不可 処理失敗
2417 fixed,abortedのラインのみがマージ可能
2418 マージ側のトランクに対する被マージ(親)側の該当するラインを閉じる(チェックイン)
2419 同時にマージ側のラインを同じタイムスタンプで閉じる
2420 親ラインに未登録のサブラインは、ここでマージされる。
2421 この時点で発給されたラインにマージ(チェックイン)されていないラインはこれ以降のマージは親ラインに対して行なわれる。
2422 =クローズした子ラインに対するマージは、データの整合性を脅かすので禁止
2423 
2424 */
2425 nas.Pm.statuses={"startup":0,"active":1,"hold":2,"fixed":3,"aborted":4};
2426 
2427 nas.Pm.LineIssues.prototype.merge = function(myBranch){
2428     if(nas.Pm.statuses[myBranch.body[myBranch.currentId].currentStatus]<3) return false;
2429     //カレントラインがフィックスしていない場合失敗
2430     for(var ix=0;ix<this.body.length;ix++){
2431         if(this.body[ix].lineId==myBranch.body[0].lineId){
2432       //マージ側のラインが被マージ側にあるか否か確認
2433             if(typeof this.body[ix].lineCheckIn !="undefined"){
2434                 return false;//既にマージ済みの場合もリジェクト
2435             }else{
2436                 this.body[ix].lineCheckIn=nas.CURRENTUSER;
2437                 this.body[ix].lineCheckInDate   =new Date();
2438                 myBranch.body[0].lineCheckIn    =this.body[ix].lineCheckIn;//転記
2439                 myBranch.body[0].lineCheckInDate=this.body[ix].lineCheckInDate;//転記
2440                 for(var mix=1;mix<myBranch.body.length;mix++){
2441                     this.body.push(myBranch.body[mix]);//残り情報を転記
2442                 }
2443             }
2444         }
2445     }
2446     return myBranch;
2447 }
2448 //クラスメソッド
2449 nas.Pm.parseIssue = function(datastream){
2450     if(! datastream.match){return false};
2451 //ラインで分割して配列に取り込み
2452     var SrcData     = new Array();
2453     if(datastream.match(/\r/)){datastream=datastream.replace(/\r\n?/g,("\n"))};
2454     SrcData=datastream.split("\n");
2455     var newIssues   = false;
2456     for (l=0;l<SrcData.length;l++){
2457         if(SrcData[l].match(/^\#\#([\[<A-Z][^=]+)=?(.*)$/)){var nAme=RegExp.$1;var vAlue=RegExp.$2;}
2458         switch (nAme){
2459         case "LINE_ID":
2460             if(! newIssues) {
2461                 var myContent   = vAlue.split(":");
2462                 var myLineName  = myContent[0].replace(/^\(|\)$/g,"");
2463        // alert("setupLine :"+myLineName);
2464                 var myLineId    = myContent.slice(1,myContent.length).join(":");
2465                 newIssues       = new nas.Pm.LineIssues(new nas.Pm.Issue(myLineName,myLineId));
2466             }else{continue;}
2467         break;
2468         case "CHECK_OUT":
2469         case "CHECK_IN":
2470             var myContentIssue  = vAlue.split(";")[0].split("/");
2471             var myIndex = myContentIssue[0].split(":");myIndex[1] = nas.chopString(myIndex[1]);
2472             var myDate          = myContentIssue.slice(1,myContentIssue.length-1).join("/");
2473             var myUser          = nas.chopString(myContentIssue[myContentIssue.length-1]);
2474             if((newIssues)&&(newIssues.body[newIssues.body.length-1].lineId==myIndex[1])){
2475                 var myIssue     = newIssues.body[newIssues.body.length-1];
2476             }else{
2477                 var myIssue     = new nas.Pm.Issue(myIndex[0].replace(/^\(|\)$/g,""),myIndex[1]);
2478                 newIssues.body.push(myIssue);
2479             }
2480             if(nAme=="CHECK_OUT"){
2481                 myIssue.lineCheckOut    = myUser;
2482                 myIssue.lineCheckOutDate= new Date(myDate);
2483             }else{
2484                 myIssue.lineCheckIn     = myUser;
2485                 myIssue.lineCheckInDate = new Date(myDate);
2486             }
2487         break;
2488         case "currentStatus":
2489             newIssues.currentStatus  = nas.chopString(vAlue)  ;
2490         break;
2491         }
2492     }
2493     return newIssues;
2494 }
2495 /*
2496 ライン情報は、1セットのxMap/Xpsに対して1種類発行される
2497 Pm.PmUには同時に複数セットのラインが記録され 複数の
2498 
2499 A= "##LINE_ID=(本線):0\n##CHECK_OUT=(本線):0/ 2016/01/31 18:45:22 / kiyo;"
2500 B=nas.Pm.parseIssue(A);
2501 C=B.branch("BG").toString();
2502 B.body[B.currentId].lineId
2503 
2504 */
2505 /*========================================================================この下は整理が済んだらcommonライブラリへ移行*/
2506 /**
2507     カット表記用時間文字列オブジェクト
2508     名前とフレーム数で初期化する
2509     toString()メソッドは 秒+コマ 又は 名前(秒+コマ)型式文字列
2510     valueOf() メソッドは フレーム数を返す
2511     toStringにXps保存用の形式も必要 ","区切りで倒置
2512 */
2513 nas.TimeUnit = function(myName,myFrames){
2514     this.name   = myName;
2515     this.frames = myFrames;
2516 this.toString   = function(myForm){
2517     if(myForm){
2518         return (this.name)?([nas.Frm2FCT(this.frames,3) , this.name ]).join(","):nas.Frm2FCT(this.frames,3);
2519     }else{ 
2520         return (this.name)? this.name+" ( "+nas.Frm2FCT(this.frames,3)+" )":nas.Frm2FCT(this.frames,3);
2521     }
2522 }
2523 this.valueOf = function(){return this.frames;}
2524 }
2525 /**
2526      カット用の時間記述を解釈してTimeUnitオブジェクトの配列で返す
2527     nas.parseTimeString("timeString")
2528     "1+12,OL(3+0),4+0" コンマ区切りでいくつでも配列で返す
2529     ⇒[{name:"time",frames:Frames-Int},{name:"tr-in",frames:Frames-Int},{name:"tr-out",frames:Frames-Int}]
2530  **解釈の幅を広げてパターン調整が必要
2531 */
2532 nas.parseTimeString = function(myTimeString){
2533     var myTarget    = myTimeString.split(",");
2534     var myResult    = new Array();
2535     for (var t = 0; t < myTarget.length; t ++){
2536         myResult[t]   =new nas.TimeUnit();
2537         if(myTarget[t].match(/(.*)\(([^\)]+)\)$/)){
2538             myResult[t]   =new nas.TimeUnit(RegExp.$1,nas.FCT2Frm(RegExp.$2));
2539         }else{
2540             myResult[t]=new nas.TimeUnit("",nas.FCT2Frm(myTarget[t]));
2541         }
2542     }
2543     return myResult;
2544 }
2545 //test nas.pareseTimeString("12+6,trin(0),tr-out(0)");
2546 
2547 /* nas.separateTitleString(titleString)
2548      識別文字列を分解して配列戻し
2549      カット識別文字列の詳細はドキュメントを参照
2550 引数:カット識別文字列 "title.opus.scene.cut"
2551 戻値:配列[cut,scene,opus,title]
2552 例:
2553 nas.separateTitleString("Title#12_s12-c#123B") == ["123B","12","12","Title"]
2554 nas.separateTitleString("XAv/10/s-c0023") == ["0023","","10","XAv"]
2555 セパレータは . / _ -
2556 プレフィックスは Oo#Ss#Cc#
2557 ポストフィックスはカット番号に含まれるものとします。>必要にしたがって別途パースすること
2558 */
2559 nas.separateTitleString = function(myName){
2560 // alert(myName);
2561     var regSep      = new RegExp("[\\.\/\-]+","g");//セパレータ("_"以外)
2562     var myString    = myName.toString().replace(regSep,"_");//セパレータを統一
2563      myString       = myString.replace(/[cCcCsSsSoOoO##№][##№]?([0-9]+)/g,"_$1")
2564 // プレフィクスをセパレータに変換
2565 // alert(myString);
2566      myString       = myString.replace(/_+/g,"_");//連続セパレータを縮小
2567 // alert(myString);
2568     var myParse     = myString.split("_").reverse();
2569     var newCut      = (myParse.length>0)?myParse[0]:"";
2570 // var newCut=(myParse.length>0)?new String(myParse[0]).replace(/^[cCcC]?[##№]?/,""):"";
2571     var newScene    = (myParse.length>1)?new String(myParse[1]).replace(/^[sSsS]?[##№]?/g,""):myScene;
2572 // var newOpus      = (myParse.length>2)?new String(myParse[2]).replace(/^[oOoO]?[##№]?/g,""):myOpus;
2573     var newOpus     = (myParse.length>2)?myParse[2]:myOpus;
2574     var newTitle    = (myParse.length>3)?myParse[3]:myTitle;
2575     return [newCut,newScene,newOpus,newTitle];
2576 }
2577 // nas.separateTitleString("Title#12_s12-c#123B");
2578 // nas.separateTitleString("TitleO#12s#12c#123B");
2579 // timeString
2580 // 1+12 (trin:0+00)(trout:0+00)
2581 // test
2582 // var A=new SC("c012","s")
2583 /** nas.Pm.Orgnization
2584     システム内で参照される組織の
2585 */
2586 /** nas.Pm.Staff
2587 作業許可/拒否の判定基準となるスタッフオブジェクト
2588 .type   String  //自動設定スタッフエントリのタイプ識別名
2589 .user   Object nas.UserInfo or null or * //
2590 .duty   Object dutyName or null or * //
2591 .section  Object sectionName or null or * //
2592 .access  bool  //アクセス権
2593 .alias   String  //スタッフユーザの表示名称 ユーザ指定可能 デフォルトは"" データがある場合は、優先的にユーザハンドルと置換される
2594 
2595 ""(ヌルストリング) nullエントリとして扱う
2596 nullエントリは、自身を含む全てのエントリとマッチしない
2597 
2598 特殊なエントリとして"*"(スターエントリ)を扱う
2599 アクセス権を判定する場合、設定可能な全てのユーザとマッチする特殊なエントリ
2600 *同士は判定対象外(マッチがおきない)
2601  
2602     以下のエントリは全ての部門、役職及びユーザのアクセスを禁止する (一般に指定されない)
2603 false,"*","*","*"
2604 
2605     以下のエントリは演出部のユーザ全てのアクセスを許す
2606 true,"*","*","演出"
2607     以下のエントリ役職が演出である演出部所属のユーザのアクセスを許す
2608 true,"*","演出","演出"
2609     以下のエントリは役職が演出である全てのユーザのアクセスを許す、このエントリは上記のエントリの内容を包括する
2610 true,"*","演出","*"
2611     以下のエントリは役職が演出でいずれの部門にも属さないユーザのアクセスを許す
2612 true,"*","演出",""
2613 
2614     以下のエントリは演出部の全て役職のアクセスを許す
2615 true,"*","*","演出"
2616 */
2617 nas.Pm.Staff = function(user,duty,section,access,alias){
2618     this.type ;                             //String  タイプ識別名 section/duty/user
2619 //    if(!(user instanceof nas.UserInfo)) user =new nas.UserInfo(user);
2620     this.user    = (user)? user:null;//Object nas.UserInfo or * or null
2621     this.duty    = (duty)? duty:null;       //StringID of duty
2622     this.section = (section)? section:null; //StringID of section
2623     this.access  = (typeof access == "undefined")? true:(access); //bool  アクセス権
2624     this.alias   = (alias)? alias:"";  //String  表示名称 ユーザ指定可能 デフォルトは""
2625     this.typeSet();
2626 }
2627 /*
2628   テキスト形式の指定を受けてスタッフオブジェクトを再初期化するオブジェクトメソッド
2629   palin形式の文字列は、単一レコードでは初期化に必要な情報に欠けるのでここでは扱わない
2630   StaffCollectionのメソッドのみが受け付ける
2631   ここではdump形式のみを判定 それ以外はfullフォーマットとして扱う
2632 */
2633 nas.Pm.Staff.prototype.parseStaff = function(staffString){
2634     if (staffString.match(/^\[([^\[\]]+)\]$/)) {;
2635 /*
2636 [access ,alias  ,user   ,duty   ,section]
2637     dump format
2638 */
2639         var myProps=JSON.parse(staffString);
2640         if (myProps.length!=5) return false;
2641         this.access  = (String(myProps[0]).match(/-|false/i))?false:true;
2642         this.alias   = myProps[1];
2643         this.user    = (myProps[2])? new nas.UserInfo(myProps[2]):null;
2644         this.duty    = myProps[3];
2645         this.section = myProps[4];
2646     } else {
2647 //full format
2648 /*
2649 Access *SECTION* [DUTY] handle:email ALIAS
2650 
2651     Access 以外は順不同
2652     ALIAS は、スタッフユーザの表示エイリアスなのでuserエントリがnullの場合は意味を持たないことに注意
2653 */
2654         staffString= staffString.replace(/\s+/g,'\t');//空白をタブに置換
2655         var myProps = staffString.split('\t');//配列化
2656         if ((myProps.length<2)||(myProps.length>6)) return false;//フィールド数0,1,6~は不正データ
2657         this.access=(myProps[0].match( /-|false/i ))?false:true;//第一フィールドは、固定でアクセス可否 bool
2658         //第二フィールド〜ラストまでループでチェック
2659         for (var ix=1;ix<myProps.length;ix++){
2660             if(myProps[ix].match(/^\*([^\*]+)\*$/)){
2661                 this.section=RegExp.$1;// *SECTION*
2662             }else if(myProps[ix].match(/^\[([^\]]+)\]$/)){
2663                 this.duty=RegExp.$1;// [duty]
2664             }else if(myProps[ix].match(/^[^:]+:[^:]+/)){
2665                 this.user=new nas.UserInfo(myProps[ix]);// Handle:email
2666             }else{
2667                 this.alias = myProps[ix]
2668             }
2669         }
2670     }
2671     this.typeSet();
2672     return this;
2673 }
2674 /*TEST
2675 var A = new nas.Pm.Staff();
2676 A.parseStaff('[false,"","","プロデューサ","制作管理"]');
2677 A.parseStaff(' *うなぎ* [海遊館] ハンドル:sample@example.com ほげら');
2678 
2679 */
2680 /*
2681     データの内容を確認してtypeプロパティをセットする。
2682     同時に必要なエントリにスタープロパティを補う
2683     初期化以後プロパティの変更の際にコールする必要がある
2684 */
2685 nas.Pm.Staff.prototype.typeSet = function(){
2686     if((this.user)&&(this.user!="*")){
2687         this.type = "user";
2688     }else{
2689         if((this.duty)&&(this.duty!="*")){
2690             this.type = "duty";
2691         }else if((this.section)&&(this.section!="*")){
2692             this.type = "section"; 
2693         }else{
2694             this.type = null;
2695         }
2696     }
2697     return this.type;
2698 }
2699 /*  .samaAs
2700     同値判定用メソッド
2701     アクセス可否判定を含めてエントリが完全に一致した場合のみtrueを返す
2702     ユーザ情報はメールアドレスのみでなくハンドルまで一致した場合にtrue
2703     null,"" は、いずれのエントリとも一致しない
2704     マッチングの順位あり
2705 タイプ    部署 役職 ユーザ ハンドル アクセス可否
2706 user        全マッチ以外はfalse
2707 section
2708 duty
2709 */
2710 nas.Pm.Staff.prototype.sameAs = function(target){
2711     if(!(target instanceof nas.Pm.Staff)) return false; 
2712     var result = 0;
2713     //user プロパティに値がある 双方がUserInfoオブジェクトだった場合のみ文字列化して比較 それ以外は直接比較
2714     if(this.user){
2715       if ((this.user instanceof nas.UserInfo)&&(target.user instanceof nas.UserInfo)){
2716             if(this.user.toString()==target.user.toString()) result += 4;
2717       } else {
2718          if(this.user==target.user) result +=4;
2719       }
2720     }else{
2721     //値がない>相手先に値がない場合のみマッチ (nullが"",0,false等とマッチする)
2722         if(! target.user) result += 4;
2723     }
2724     if (this.duty){
2725         if(String(this.duty)==String(target.duty)) result += 2;//文字列比較
2726     }else{
2727         if(! target.duty) result += 2;        
2728     }
2729     if (this.section){
2730         if(String(this.section)==String(target.section)) result += 1;//文字列比較
2731     }else{
2732         if(! target.section) result += 1;        
2733     }
2734     if ((this.access)==(target.access)) result += 8 ;//比較先にアクセス権がなければ負数へ
2735     return (result==15);
2736 }
2737 /*TEST
2738  var A = new nas.Pm.Staff("*","*","作画");
2739  var B = new nas.Pm.Staff("","作画監督","作画");
2740  var C = new nas.Pm.Staff("","演出","");
2741  var D = new nas.Pm.Staff(new nas.UserInfo("kiyo:kiyo@nekomataya.info"),"作画監督","作画");
2742  D.alias="ねこまたや";
2743 
2744 A.sameAs(A);
2745 A.sameAs(B);
2746 A.sameAs(C);
2747 A.sameAs(D);
2748 A.sameAs();
2749 A.sameAs("kjsadhjakshdjkh");
2750 
2751 B.parseStaff(A.toString("dump"))
2752 */
2753 
2754 /*
2755     .compareWith(target)
2756     比較用に与えられたオブジェクトとの比較係数を返す
2757 特殊エントリ "*"
2758     比較先が"*"エントリの場合、"*","",nullを除く全てのエントリに対してマッチが発生する
2759     比較元が"*"の場合は、 比較先"*"を含めてなにものにもマッチしない
2760 
2761     片方向判定を行うので、thisとtargetを入れ替えた場合の戻り値は一致しない
2762 
2763 特殊エントリ "" == null
2764     比較元が及び比較先が""またはnullの場合""同士、null同士を含めてマッチが発生しない
2765 「比較先」のアクセス可否情報を見てfalseの場合 得られた係数を正負反転させて戻す。
2766 (自身の可否情報は見ない 必要があれば戻り値に対して自身のアクセス可否情報を乗せる)
2767 
2768 var A=[true   *      *     演出]
2769 var B=[false  *      監督  演出]
2770 var C=[true   タヌキ 監督  演出]
2771     として
2772     A.cpmpareWith(A)    result 1
2773     A.cpmpareWith(B)    result -1
2774     A.cpmpareWith(C)    result 1
2775     B.cpmpareWith(A)    result 3
2776     B.cpmpareWith(B)    result -3
2777     B.cpmpareWith(C)    result 3
2778     C.compareWith(A)  result 7
2779     C.compareWith(B)  result -7
2780     C.compareWith(C)  result 7
2781     
2782 用途: 自身に対する相手の比較係数を得て自身のアクセスの可否を判定するのが主
2783 副用途として、エントリコレクション中の完全一致するエントリを検出して重複エントリの排除を行う?
2784 --完全一致の判定が出来ない?
2785 */
2786 nas.Pm.Staff.prototype.compareWith = function(target){
2787     if(!(target instanceof nas.Pm.Staff)) return false; 
2788     var result = 0;
2789 //  一致条件 
2790 //  相手先が nullと*以外の場合で自身が* >マッチ
2791 //  自身の値が存在して(null以外) >相手を判定 相手先が
2792 //   >相手先問わずアンマッチ
2793 //  自身と相手先の判別
2794     if (
2795         (this.user!="*")&&
2796         (((this.user)&&(target.user=="*"))||
2797         ((this.user instanceof nas.UserInfo)&&(this.user.sameAs(target.user))))
2798     )        result += 4;
2799     if (
2800         (this.duty!="*")&&
2801         (((this.duty)&&(target.duty=="*"))||
2802         (this.duty == target.duty))
2803     )       result += 2;
2804     if (
2805         (this.section!="*")&&
2806         ((this.section)&&(target.section=="*"))||
2807         ((this.section)&&(this.section == target.section))
2808     )       result += 1;
2809 //比較先にアクセス権がなければ負数へ(自身のアクセス権は問わない)
2810     if (! target.access) result *= -1 ;
2811     return result;
2812 }
2813 /*TEST (user,duty,section,access,alias)
2814 var A=new nas.Pm.Staff("*","*","演出");
2815 var B=new nas.Pm.Staff("*","監督","演出",false);
2816 var C=new nas.Pm.Staff(new nas.UserInfo("タヌキ:tanuki@animal.example.com"),"監督","演出",true);
2817 //    として
2818     console.log(A.compareWith(A))    ;//result 1
2819     console.log(A.compareWith(B))    ;//result -1
2820     console.log(A.compareWith(C))    ;//result 1
2821     console.log(B.compareWith(A))    ;//result 3
2822     console.log(B.compareWith(B))    ;//result -3
2823     console.log(B.compareWith(C))    ;//result 3
2824     console.log(C.compareWith(A))  ;//result 7
2825     console.log(C.compareWith(B))  ;//result -7
2826     console.log(C.compareWith(C))  ;//result 7
2827 */
2828 /*
2829      文字列化して返す
2830      formオプション
2831 plain-textフォーマット
2832         'plain-text'
2833         'plain'
2834         'text'
2835 この書式は、スタッフコレクションから呼び出された時のみに意味を持つので注意
2836      sction
2837 部門                  \t部門名
2838      duty
2839 役職                  \t\t役職名
2840      user
2841 ユーザ                \t\t\tハンドル:e-メール
2842 
2843 スタッフコレクションの'plain'オプションに対応する機能
2844 
2845 
2846 full-dumpフォーマット     
2847         'full-dump'
2848         'full'
2849         'dump'
2850 
2851 アクセス可否  UID [役職] *部門* 別名
2852 
2853     スペース区切りで、第一フィールドはアクセス可否
2854     最終フィールドは別名
2855     UIDは通常文字列
2856     役職はブラケットで囲む
2857     部門はスターで囲む
2858     それぞれのエントリが無い場合はフィールドごと省略するのでフィールド数は可変
2859    
2860 dumpフォ−マット
2861         'dump'テキスト記録用文字列で返す
2862 
2863     [アクセス可否,"別名","UID","役職","部門"]
2864 配列型文字列 フィールド数固定
2865         
2866     エントリーの全情報をカンマ区切りで出力する。
2867     コレクションのエントリ追加メソッドの引数形式
2868 
2869         
2870      上記以外の返り値の文字列はtypeにより異なる
2871      section(部門)エントリーはセクション名の前後に'*'を付けて返す
2872         ex:*演出* *作画*
2873     duty(役職)エントリーは役職名の前後をブラケットで囲んで返す
2874         ex:[監督][作画監督]
2875     部門エントリがあればそれを添付する
2876     
2877     ユーザエントリーは、ユーザの表示名を返す オブジェクトに設定されたALIASまたはユーザ情報オブジェクトのハンドル
2878 
2879 JSONフォーマット
2880     他のDBとのデータ交換用にJSON文字列化したデータを返す
2881 */
2882 nas.Pm.Staff.prototype.toString = function(form){
2883     switch(form){
2884     case 'JSON':
2885         return JSON.stringify({
2886             acsess:this.access,
2887             type:this.type,
2888             alias:this.alias,
2889             user:((this.user)?this.user.toString():null),
2890             duty:this.duty,
2891             section:this.section
2892         });
2893     break;
2894     case    'plain-text':
2895     case    'plain':
2896     case    'text':
2897         var result=(this.access)?"\t":"-\t";
2898         switch(this.type){
2899         case "section":
2900             result += this.section;
2901         break;
2902         case "duty":
2903             result += "\t";
2904             result += this.duty;
2905         break;
2906         case "user":
2907             if(this.alias.length){this.user.handle=this.alias}
2908                 result += "\t\t";
2909                 result += this.user.toString(true);
2910         break;
2911         }
2912         return result;
2913     break
2914     case    'full-dump':
2915     case    'full':
2916     case    'dump':
2917         var result=(this.access)?[true]:[false];
2918         result.push(this.alias);
2919         result.push((this.user)?this.user.toString():'');
2920         result.push(this.duty);
2921         result.push(this.section);
2922         return JSON.stringify(result);
2923     break;
2924 /*    case 'void':
2925         var result='';
2926         result +=(this.access)? "":"-";
2927         if(this.user){
2928             result +="\t";
2929             result += (this.user instanceof nas.UserInfo)? this.user.toString():String(this.user);
2930         }
2931         if(this.duty){
2932             result +="\t";
2933             result += "["+String(this.duty)+"]";
2934         }
2935         if (this.section){
2936             result +="\t";
2937             result += "*"+String(this.section)+"*"  ;
2938         }
2939         if (this.alias){
2940             result +="\t";
2941             result += String(this.alias)  ;
2942         }
2943         return result;
2944 */
2945     default:
2946         var result=(this.access)?'':'-';
2947         switch(this.type){
2948         case "duty"   :
2949             result += "["+String(this.duty)+"]";
2950         break;
2951         case "section":
2952             result += "*"+String(this.section)+"*"  ;
2953         break;
2954         case "user"   :
2955             if(this.alias.length){
2956                 result += String(this.alias);
2957             }else{
2958                 result += String(this.user.handle);
2959             }
2960         break;
2961         default:
2962             return false;
2963         }
2964         return result;
2965     }
2966 }
2967 //test 初期化引数 user,duty,section,access,alias
2968 /*
2969  var A = new nas.Pm.Staff("*","*","作画");
2970  var B = new nas.Pm.Staff("","作画監督","作画");
2971  var C = new nas.Pm.Staff("","演出","");
2972  var D = new nas.Pm.Staff(new nas.UserInfo("kiyo:kiyo@nekomataya.info"),"作画監督","作画");
2973  D.alias="ねこまたや";
2974 F= new nas.Pm.StaffCollection(nas.pm);
2975 F.addStaff([A,B,C,D]);
2976 
2977 //console.log(F)
2978 //A.sameAs(B);
2979 D.sameAs(C);
2980 */
2981 /** nas.Pm.StaffCollection
2982 スタッフコレクションオブジェクト
2983 スタッフを収集したスタッフコレクションをエントリノード毎に保持する
2984 問い合わせに対して権利の解決を行う
2985 ペアレント属性には、自身が所属するノードが格納される
2986 ノードのペアレント属性に親子関係にあるノードがあるので、継承及び参照の解決は当該の情報パスをたどる。
2987 コレクションのメンバー数が0の場合、コレクションは上位ディレクトリの内容を返す
2988 .parent     Object      所属するノード 親ノードのstaffをアクセスするパスは this.parent.parent.staffs
2989 .members    Array       オブジェクトトレーラー配列
2990 .add()      Function    メンバー追加メソッド 戻り値 追加成功時 Object staff 失敗時 false
2991 .parseConfig() Function    設定ファイルのストリームからメンバーを入れ替え
2992 .dump() Functio         ダンプリストを取得
2993 .toString() Function    
2994 .remove()   エントリを削除
2995 
2996 
2997 */
2998 nas.Pm.StaffCollection = function(myParent){
2999     this.parent = myParent;
3000     this.members = [];
3001 }
3002 /*
3003 toStringは、二種の出力フォーマットを持つ
3004  full/引数なし または dump
3005 フルフォーマットは可読テキストとして出力
3006     第1フィールドに何らかのデータのあるレコードは拒否エントリになる
3007     第4フィールドはalias 個々にデータがある場合、そのエントリの表示名称として優先して使用される
3008         例 \t演出\t監督\t\tbigBoss
3009         例 \t作画\t原画\tcitten:cat@animals.example.com\tキティちゃん
3010     各フィールドの値として、h-tabは使用できない
3011 ダンプフォーマットは、機械読み取り用のフォーマットでaddStaffメソッドの引数形式
3012     
3013 
3014 nas.Pm.StaffCollection.prototype.toString = function(form){
3015     var result="";
3016     switch (form){
3017     case "full":
3018             for (var ix =0 ; ix<this.members.length;ix++){
3019                 if (ix > 0) result +="\n";
3020                 result += this.members[ix].toString('full');
3021             }
3022             result += '\n';
3023         return result;
3024         break;
3025     case "plain":
3026             for (var ix =0 ; ix<this.members.length;ix++){
3027                 if (ix > 0) result +="\n";
3028                 result += this.members[ix].toString('plain');
3029             }
3030             result += '\n';
3031         return result;
3032         break;
3033     case "dump":
3034             for (var ix =0 ; ix<this.members.length;ix++){
3035                 if (ix > 0) result +=",\n";
3036                 result += this.members[ix].toString('dump');
3037             }
3038             result += '\n';
3039         return result;
3040         break;
3041     default:
3042         var result = new Array;
3043             for (var ix =0 ; ix<this.members.length;ix++){
3044                 result.push(this.members[ix].toString());
3045             }
3046         return result.toString();
3047     }
3048 }
3049 */
3050 nas.Pm.StaffCollection.prototype.dump=nas.Pm._dumpList;
3051 /*
3052   コレクションをソートする
3053   ソート基準は
3054   部門 役職 ユーザ
3055   メンバーをタイプ別にわける
3056   タイプごとに部門でソートする
3057     部門エントリを抽出して 辞書ソート
3058     部門1エントリ毎に役職エントリを抽出して辞書ソート
3059     役職1エントリ毎にユーザエントリを抽出して
3060     役職エントリ  部門ソート 辞書ソート
3061     ユーザエントリ 部門ソート 役職ソート 辞書ソート
3062 
3063 */
3064 nas.Pm.StaffCollection.prototype.sort = function(){
3065     
3066 };
3067 /*
3068     コレクション内の指定条件にマッチするエントリを新たなコレクションで返すメソッド
3069     引数 staffString
3070     フルフォーマット文字列
3071     "*演出*"    部門・演出 のエントリをタイプ問わず
3072     "*演出* [演出助手]","user" 部門・演出 && 役職・演出助手 のユーザエントリ
3073     " [作画監督]","section" 役職・作画監督 を含む部門エントリ
3074     '馬:hose@animal.example.com','duty' ユーザ・馬が所属する役職エントリ
3075 エントリの問い合わせがあった場合、コレクションメンバーを検索してアクセスの可否を返す。
3076 コレクションのエントリ数が0の場合のみ、親オブジェクトの持つスタッフコレクションに問い合わせを行いその結果を返す。
3077 */
3078 nas.Pm.StaffCollection.prototype.getMember = function(staffString,type){
3079     var result=new nas.Pm.staffCollection(this.parent);
3080     var sect='';    var dut ='';    var usr ='';
3081 }
3082 /*    .parseConfig
3083     設定ファイルのスタッフ初期化文字列をパースしてスタッフコレクションを更新するオブエジェクトメソッド
3084     引数はレコード改行区切りテキストストリーム
3085     受け入れ形式は3つ
3086     ストリームの第一有効レコードで判定する
3087     
3088     いずれも行頭 '#'はコメント行 空行は無視 
3089     JSON   データ交換用JSON
3090 {access:<ACESS>,alilas:<ALIAS>,user:<USER>,duty:<DUTY>,section:<SECTION>,type:<TYPE>}
3091     full-dump 引数配列形式
3092 [アクセス可否,"別名","UID","役職","部門"]
3093 
3094     plain-text    タブ区切りフィールド
3095 アクセス可否\t部門\t役職\tユーザ\t別名
3096 
3097     free-form スペース分離 不定フィールドテキスト
3098 アクセス可否	handle:UID	[役職]	*部門*	別名
3099 例:
3100 true	*演出*
3101 false	*作画*	[原画]
3102 
3103 ** Free-Formは、スタッフDB記述の独自記法なので充分に留意のこと これに類する記法は他に Line,Stage,Job にみられる
3104 
3105 
3106 */
3107 nas.Pm.StaffCollection.prototype.parseConfig = function(dataStream,form){
3108     var myMembers =[];
3109     // 形式が指定されない場合は、第一有効レコードで判定
3110     if(! form ){
3111             if (dataStream.match(/\[\s*(\{[^\}]+\}\s*,)\s*(\{[^\}]+\})?\s*\]/)) form='JSON';//配列JSON
3112             else if (dataStream.match(/(\n|^)\[.+\]($|\n)/)) form='full-dump';
3113             else if (dataStream.match(/\*[^\*]+\*|\[[^\[\]]+\]/)) form='free-form';//]
3114             else  form='plain-text';
3115     }
3116     switch(form){
3117     case    'JSON':
3118         var tempObject=JSON.parse(dataStream);
3119         for (var rix=0;rix<tempObject.length;rix++){
3120             var currentStaff=new nas.Pm.Staff(
3121                 tempObject[rix].user,
3122                 tempObject[rix].duty,
3123                 tempObject[rix].section,
3124                 tempObject[rix].access,
3125                 tempObject[rix].alias
3126             );
3127             myMembers.push(currentStaff);
3128         }
3129     break;
3130     case    'full-dump':
3131     case    'free-form':
3132         dataStream = String(dataStream).split("\n");
3133         for (var rix=0;rix<dataStream.length;rix++){
3134             if(dataStream[rix].indexOf('#')==0) continue;
3135             var currentStaff=new nas.Pm.Staff();
3136             currentStaff.parseStaff(dataStream[rix]);
3137             if (currentStaff) myMembers.push(currentStaff);
3138         }
3139     break;
3140     case    'plain-text':
3141     default:
3142         dataStream = String(dataStream).split("\n");
3143       var currentSection=null;var currentDuty=null;
3144       for (var rix=0;rix<dataStream.length;rix++) {
3145         if((dataStream[rix].indexOf('#')==0)||(dataStream[rix].length == 0)) continue;
3146         var currentRecord=dataStream[rix].split('\t');
3147         var currentAccess=true;var currentUser=null;var currentAlias="";
3148 //plainフォーマットはタブ区切り タブ1つは部門 2つで役職 3つでユーザ ユーザ指定のレコードには別名の指定も可
3149 //例:  ^access  section duty user alias
3150         if(currentRecord[0]) currentAccess = (String(currentRecord[0]).match( /-|false/i ))?false:true;
3151         if(currentRecord[1]) {
3152             var mySection = currentRecord[1].replace(/\/$/,"");
3153             if(mySection != currentSection) {currentSection=mySection;currentDuty=null;}
3154             //myMembers.push(new nas.Pm.Staff(null,null,currentSection,currentAccess,""));
3155         }
3156         if(currentRecord[2]) {
3157             currentDuty    = currentRecord[2];
3158 //            myMembers.push(new nas.Pm.Staff(null,currentDuty,currentSection,currentAccess,""));
3159         }
3160         if(currentRecord[3]) {
3161             var currentUser    = new nas.UserInfo(currentRecord[3]);
3162             var currentAlias   = (currentRecord[4])? currentRecord[4]:"";
3163 //            myMembers.push(new nas.Pm.Staff(currentUser,currentDuty,currentSection,currentAccess,currentAlias));
3164         }
3165         myMembers.push(new nas.Pm.Staff(currentUser,currentDuty,currentSection,currentAccess,currentAlias));
3166       }
3167     }
3168     return this.addStaff(myMembers);
3169 }
3170 /*TEST
3171 
3172 */
3173 
3174 
3175 
3176 /*
3177       ターゲットになるユーザまたはスタッフとコレクションの内容を比較して、
3178       一致したエントリIDを返すメソッド
3179       ヒットしなかった場合は -1
3180 */
3181 nas.Pm.StaffCollection.prototype.indexOf = function(target){
3182     for (var ix =0 ;ix <this.members.length;ix ++){
3183         if(this.members[ix].sameAs(target)) return ix;
3184     }
3185     return -1;
3186 }
3187 /*  スタッフの追加メソッド
3188     引数は nas.Pm.Staff オブジェクト
3189     引数形式は、Staffオブジェクトまたはオブジェクトの配列
3190     可読テキストの再ロードはparseConfigメソッドを利用
3191     parseConfigメソッドは、可読テキストをdump形式にコンバートしてこのメソッドを内部で呼び出す
3192     同内容のエントリがあった場合は追加されない。
3193     
3194     追加時に既存のsection/dutyエントリに存在しないプロパティを持ったuserエントリがあった場合は、
3195    当該のエントリを新規に作成して追加する? ユーザの設定を変更することになるのでコレは行わない 
3196     戻り値は、追加に成功したエントリ数(エントリ配列か?)
3197     
3198 */
3199 nas.Pm.StaffCollection.prototype.addStaff = function(members){
3200     var result=0;
3201     if(!( members instanceof Array)) members = [members];
3202     for(var ix =0 ; ix<members.length;ix++){
3203       if(!(members[ix] instanceof nas.Pm.Staff)){
3204 /*
3205         member[0]// access
3206         member[1]// alias
3207         member[2]// user
3208         member[3]// duty
3209         member[4]// section
3210 */
3211         var member=new nas.Pm.Staff();
3212         member.parseStaff(members[ix]);//文字列としてパースする 不正データの場合は初期化できないのでスキップ
3213       }else{
3214         var member = members[ix]
3215       }
3216       if(! member) continue;
3217       var checkHint = this.indexOf(member);
3218 //console.log("checkHint : " + checkHint)  ;   
3219 //一致エントリがないので追加
3220       if (checkHint < 0){
3221         this.members.push(member);
3222         result ++;
3223 //console.log('push member :'+member.toString('dump'));
3224         continue;
3225       }
3226     }
3227     return result;
3228 }
3229 
3230 nas.Pm.staff=new nas.Pm.StaffCollection(nas.Pm);
3231 /*TEST
3232 新設が必要な設定群
3233 ユーザDB
3234     U-AT の場合はサーバから取得   Repository.pmd.users? この管理はサーバに任せて、スタッフだけもらうべき
3235     ローカルストレージ等の
3236 スタッフDB
3237     部門、役職、ユーザを合成したスタッフDB
3238         Repository.pmd.staff ~ タイトル、エピソード、カット(ライン、ステージ)までのツリー状の構造の各所でそれぞれのデータを参照可能にするための構造
3239 
3240 nas.Pm.inportDB(settingStream)
3241  
3242 DBとの通信は基本的に serviceAgent配下で各ServiceNodeが行う
3243 読み出しは低レベル関数をそれぞれのオブジェクトが受け持ち
3244 設定ファイル読み出しに相当するひとまとまりのアクションを親オブジェクト側で実装する
3245 
3246 統一形式
3247 Object.parseConfig(dataStream)
3248 
3249 perseStaff等もリネーム
3250 
3251 */
3252